Una app de meditación minimalista, construida completamente en Jetpack Compose, con un selector de tiempo circular único, estadísticas detalladas y experiencia visual cuidada al detalle.
Zen Meditation es una aplicación Android nativa desarrollada íntegramente en Kotlin y Jetpack Compose que permite a los usuarios cronometrar y gestionar sus sesiones de meditación sin fricciones. Diseñada con una estética zen coherente -paleta dorada sobre fondos oscuros y naturales-, combina una UX minimalista con funcionalidades avanzadas como presets personalizados, estadísticas históricas, campanas configurables y sistema de logros.
Toda la información se almacena localmente en el dispositivo: sin cuentas, sin servidores, sin publicidad.
La mayoría de apps de meditación del mercado pecan de dos extremos: o son demasiado simples (un timer básico) o están sobrecargadas de suscripciones, anuncios y funcionalidades que distraen del objetivo principal. Zen Meditation cubre el punto medio: ofrece una experiencia visualmente cuidada y funcionalmente completa, sin comprometer la privacidad ni la simplicidad.
Además, implementa un selector de tiempo circular táctil con cuenco tibetano animado —un componente custom desarrollado desde cero en Compose Canvas— que hace que el acto de elegir la duración sea en sí mismo una experiencia meditativa.
- Selector circular táctil con cuenco y mallet animados (Canvas custom)
- Inicio, pausa, reanudación y reset
- Cuenta atrás configurable antes de iniciar (5, 10 o 15 segundos)
- Modo pantalla encendida durante la sesión
- Servicio en primer plano con notificación persistente y controles de media
- Campana al inicio, a mitad de sesión y al final
- Campanas de intervalo configurables (cada N minutos)
- 4 sonidos disponibles: Cuenco Zen, Campana Templo, Gong, Singing Bowl
- Vibración al inicio y fin de sesión
- Historial de hasta 500 sesiones
- Racha actual y mejor racha
- Tiempo total, sesión más larga, promedio y hora habitual
- Desglose por semana, mes y año
- Gráfico de actividad mensual (barras, modo landscape)
- Sistema de logros con 15 badges desbloqueables
- Objetivo diario de minutos con barra de progreso
- 7 temas visuales (fondo + cuenco): Montañas, Playa, Bosque, Desierto, Templo Zen, Estrellas, Galaxia
- Presets guardables con nombre, tiempo, sonido y configuración de campanas
- Opción de compartir presets
- Recordatorio diario configurable por hora y días de la semana
- Modo No Molestar automático durante la meditación
- Long press en play para resetear (opcionalmente)
- Idioma: Español e Inglés
- Sección de consejos y curiosidades científicas sobre meditación
- Contenido cargado desde JSON en assets (editable sin tocar código)
| Categoría | Tecnología |
|---|---|
| Lenguaje | Kotlin 1.9+ |
| UI | Jetpack Compose (Material 3) |
| Arquitectura | Single Activity + ForegroundService + StateFlow |
| Estado | StateFlow + collectAsState (patrón MVI ligero) |
| Persistencia | SharedPreferences + JSON en assets |
| Concurrencia | Coroutines + CoroutineScope en Service |
| Notificaciones | NotificationManager + AlarmManager |
| Animaciones | animateFloatAsState, Canvas, AnimatedVisibility |
| Gráficos | Compose Canvas (barras custom) |
| Audio | MediaPlayer |
| Gestos táctiles | combinedClickable, detectDragGestures, pointerInput |
| DND | NotificationPolicy API |
| Background | ForegroundService |
# 1. Clona el repositorio
git clone https://github.com/adrisdev/zen-meditation.git
# 2. Abre el proyecto en Android Studio (Hedgehog o superior)
# 3. Conecta un dispositivo físico o inicia un emulador (API 26+)
# 4. Ejecuta el proyecto
# Build > Run 'app' / Shift + F10
⚠️ Requisitos mínimos: Android 8.0 (API 26) · Android Studio Hedgehog o superior · JDK 17
- Elige la duración — arrastra el mallet alrededor del cuenco para seleccionar los minutos
- Pulsa ▶ — la sesión comienza (con cuenta atrás opcional si está configurada)
- Pausa / Reanuda — con el mismo botón central; la notificación también tiene controles
- Finaliza — suena la campana configurada y la sesión se guarda automáticamente en estadísticas
- Explora el menú — desliza hacia arriba desde las tres líneas para acceder a campanas, presets, estadísticas, consejos y ajustes
app/src/main/
├── java/com/example/zenmeditation/
│ ├── MainActivity.kt # Entry point, modo inmersivo
│ ├── MeditationScreen.kt # Pantalla principal, lógica de UI
│ ├── MeditationService.kt # ForegroundService: timer, campanas, DND
│ ├── TimerState.kt # StateFlow global (Service ↔ UI)
│ ├── CircularTimePicker.kt # Componente Canvas custom: cuenco + mallet
│ ├── MenuDrawer.kt # Bottom sheet animado con tabs
│ ├── BellScreen.kt # Configuración de campanas
│ ├── PresetsScreen.kt # Gestión de presets (CRUD + compartir)
│ ├── StatsScreen.kt # Estadísticas, logros y gráfico
│ ├── TipsScreen.kt # Consejos cargados desde JSON
│ ├── SettingsScreen.kt # Ajustes, temas, recordatorio, DND
│ ├── BackupScreen.kt # Exportar/importar datos
│ └── ReminderReceiver.kt # BroadcastReceiver para recordatorio diario
│
├── assets/
│ └── tips.json # Contenido de consejos (editable sin recompilar)
│
└── res/
├── drawable/ # Fondos y cuencos por tema (bg_*, bowl_*)
└── raw/ # Sonidos de campanas (zen_bell, gong, etc.)
Este proyecto no requiere API keys ni variables de entorno. Todos los datos son locales.
Para añadir o editar consejos sin recompilar:
app/src/main/assets/tips.json
El proyecto actualmente no incluye tests automatizados. La hoja de ruta incluye:
// Tests unitarios prioritarios
- TimerState: emisión correcta de StateFlow en START / PAUSE / RESUME / STOP
- saveCompletedSession(): cálculo de racha, acumulados y JSON de historial
- loadPresets() / savePresets(): serialización y deserialización de presets
- scheduleReminder(): selección correcta del próximo día válido
// Tests de UI con Compose Testing
- CircularTimePicker: onMinutesChange al detectar drag gestures
- PresetsScreen: flujo completo guardar → activar → borrar
- StatsScreen: renderizado condicional de objetivo diario y logrosDependencias necesarias para añadir testing:
testImplementation 'junit:junit:4.13.2'
androidTestImplementation 'androidx.compose.ui:ui-test-junit4'
androidTestImplementation 'androidx.test.espresso:espresso-core:3.5.1'El timer vive en MeditationService, no en la UI. Esto garantiza que la sesión continúa aunque el usuario minimice la app o el sistema presione la UI. La comunicación con Compose se realiza a través de TimerState -objeto singleton con MutableStateFlow- siguiendo un patrón similar a MVI sin la complejidad de un ViewModel completo. El tiempo se ancla a SystemClock.elapsedRealtime() en lugar de incrementos delta, lo que lo hace resistente a delays de coroutine.
CircularTimePicker está construido íntegramente con Canvas de Compose y detectDragGestures. Calcula el ángulo del toque respecto al centro del componente, lo normaliza a minutos y actualiza el estado en tiempo real. El mallet se anima con animateFloatAsState para transiciones suaves entre estados (editando / corriendo / pausado). El cuenco reproduce un sonido de bowl al arrastrar, con fade out automático al detenerse.
El MenuDrawer usa una doble capa de pointerInput: el overlay exterior captura taps con detectTapGestures para cerrar el menú, mientras el sheet interior usa awaitPointerEventScope para absorber el ciclo completo DOWN+UP antes de propagarlo, evitando que los eventos lleguen al CircularTimePicker subyacente y bloqueen la UI.
Se optó por SharedPreferences + JSONArray para el historial de sesiones dado el volumen de datos manejado (máx. 500 sesiones ≈ ~50KB). Esta decisión prioriza la simplicidad y la ausencia de dependencias externas sobre la escalabilidad. Una migración a Room está contemplada en el roadmap.
Los consejos de la sección Tips se cargan desde assets/tips.json en runtime. Esto permite actualizar el contenido sin recompilar ni publicar una nueva versión, y sienta las bases para soportar múltiples idiomas en ese contenido de forma independiente al sistema de localización de Android.
La app oculta las barras del sistema con WindowInsetsControllerCompat y las restaura con BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE. Se reaplica en onWindowFocusChanged para que Samsung y otros fabricantes con capas de personalización agresivas no lo cancelen al volver de segundo plano.
- Modo meditación infinita — contador ascendente sin límite de tiempo
- Room Database — migración del historial desde SharedPreferences
- Widget — acceso rápido al timer desde la pantalla de inicio
- Sonido ambiente — lluvia, naturaleza, cuencos en loop durante la sesión
- Exportar/Importar datos — backup completo en JSON
- Google Play Billing — donaciones nativas (compras únicas)
- Localización completa — EN/ES con soporte i18n en tips.json
- Tests unitarios y de UI — cobertura de lógica de negocio y componentes clave
- ViewModel + Hilt — refactor de arquitectura para mayor testabilidad
Las contribuciones son bienvenidas. Para cambios importantes:
- Haz fork del repositorio
- Crea una rama:
git checkout -b feature/nueva-funcionalidad - Commitea tus cambios:
git commit -m 'Add: descripción clara' - Haz push:
git push origin feature/nueva-funcionalidad - Abre un Pull Request
Por favor, sigue las convenciones de Kotlin y mantén la coherencia visual con la paleta y los componentes existentes.
Adrián — Desarrollador Android
Proyecto desarrollado como parte de mi portfolio Android. Construido con Kotlin y Jetpack Compose, enfocado en UX minimalista, calidad de código y experiencia de usuario real.
Hecho con 🧘 y Kotlin · Si te resulta útil, considera apoyar el proyecto en Ko-fi