docs: alinear CLAUDE.md con estándares PapuTec + análisis Flutter vs Kotlin
This commit is contained in:
@@ -10,32 +10,68 @@ Este archivo orienta a cualquier agente de IA (Claude, Copilot, etc.) o desarrol
|
||||
|
||||
**Rachita** es una app de hábitos y creatividad para tablet Android, dirigida a niñas/niños de 6–10 años. Mascota virtual + hábitos diarios + rachas + museo de dibujos + sistema de monitoreo de batería (la mascota "se cansa" con poca batería y "duerme" mientras la tablet carga, bloqueando el uso).
|
||||
|
||||
Prototipo de referencia visual/UX: [Rachita.html](Rachita.html) (bundle React exportado, no es el código fuente final — úsalo solo como referencia de diseño, animaciones, copy y flujos, ver §9).
|
||||
Prototipo de referencia visual/UX: [Rachita.html](Rachita.html) (bundle React exportado, no es el código fuente final — úsalo solo como referencia de diseño, animaciones, copy y flujos, ver §10).
|
||||
|
||||
---
|
||||
|
||||
## 2. Decisiones de arquitectura (confirmadas)
|
||||
## 2. Elección de plataforma cliente — Flutter vs Kotlin/Compose
|
||||
|
||||
**Decisión: Kotlin + Jetpack Compose (nativo Android).** Justificación abajo — si en el futuro se requiere iPadOS (el PRD lo menciona a nivel aspiracional), se hará como app SwiftUI **separada** que consume el mismo backend, no un port Flutter.
|
||||
|
||||
### 2.1 Por qué Kotlin nativo gana aquí
|
||||
|
||||
Rachita **no es una app CRUD** — su valor está en integración profunda con el SO Android. La mayoría de los P0 del PRD son APIs nativas que en Flutter obligarían a escribir *platform channels* en Kotlin de todas formas:
|
||||
|
||||
| Feature P0 del PRD | En Kotlin/Compose | En Flutter |
|
||||
|---|---|---|
|
||||
| **Battery Status API** (BA-05) — lectura de nivel y estado real, no polling | `BatteryManager` + `Intent.ACTION_BATTERY_CHANGED` directo | Plugin `battery_plus` o platform channel a Kotlin |
|
||||
| **Overlay batería baja** (BA-01/02) sobre cualquier app | `WindowManager` + `TYPE_APPLICATION_OVERLAY` nativo | Platform channel obligado (Flutter no dibuja fuera de su Activity) |
|
||||
| **Bloqueo total durante carga** (CA-01..05) — pantalla completa sin back | `Activity` full-screen + Screen Pinning / Kiosk API | Kotlin nativo obligado |
|
||||
| **Verificación real del estado** ("Ya la desconecté", D-24) | Directo con `BatteryManager` | Platform channel |
|
||||
| **Notificaciones locales** (AL-01..04) en lockscreen | `NotificationManager` + canales | Plugin `flutter_local_notifications` (soporta lockscreen pero configuración de canal aún es nativa) |
|
||||
| **App Widgets 2×2 y 4×2** (WI-01..05) con toque directo sin abrir app | `AppWidgetProvider` + `RemoteViews`/Glance nativo | **Flutter no soporta widgets** — se hacen 100% en Kotlin con plugin `home_widget` que solo pasa datos |
|
||||
| **Servicio en segundo plano** (AL-01) | `Foreground Service` nativo | Plugin + configuración nativa |
|
||||
| **Ilustraciones vectoriales por código** (mascota) | Compose `Canvas` + `drawPath` | Flutter `CustomPainter` (paridad) |
|
||||
|
||||
De ~9 features P0 con dependencia de SO, **7 requieren código Kotlin obligatorio** aunque el resto sea Flutter. La ventaja de "un solo codebase" se diluye: quedaría un proyecto Flutter con una capa gruesa de Kotlin, más los bugs de sincronización entre ambas capas (state en Flutter vs. en el servicio Android).
|
||||
|
||||
### 2.2 Cuándo sí elegiría Flutter
|
||||
|
||||
Solo si el proyecto fuera:
|
||||
- Cross-platform desde v1 con **iPadOS** como target real (no aspiracional), Y
|
||||
- Poco integrado con APIs de sistema (una app de dashboards, listas, formularios).
|
||||
|
||||
Ninguna de las dos aplica: v1 es Android tablet only, y la integración con SO es el corazón del producto.
|
||||
|
||||
### 2.3 Sobre iPadOS a futuro
|
||||
|
||||
Cuando (si) se haga la versión iPadOS: SwiftUI nativo, misma API .NET compartida. Los widgets WidgetKit y el equivalente iOS del monitoreo de batería/carga tienen APIs muy distintas a Android — un port Flutter no ahorraría tanto como parece.
|
||||
|
||||
---
|
||||
|
||||
## 3. Decisiones de arquitectura (confirmadas)
|
||||
|
||||
| Decisión | Valor |
|
||||
|---|---|
|
||||
| **Plataforma cliente** | Android nativo (Kotlin) — sin iOS en v1 |
|
||||
| **UI Android** | Jetpack Compose |
|
||||
| **Build system Android** | Gradle con Kotlin DSL (`build.gradle.kts`) |
|
||||
| **SDK mínimo Android** | API 26 (Android 8.0) — `minSdk = 26` |
|
||||
| **Plataforma cliente** | Android nativo (Kotlin) — sin iOS en v1 (ver §2) |
|
||||
| **UI Android** | Jetpack Compose (Material 3) |
|
||||
| **Build system Android** | Gradle con Kotlin DSL (`build.gradle.kts`) + Version Catalog (`libs.versions.toml`) |
|
||||
| **SDK mínimo Android** | API 26 (Android 8.0) — `minSdk = 26`; `targetSdk = 34` |
|
||||
| **Package name** | `mx.paputec.rachita` |
|
||||
| **Backend** | ASP.NET Core Web API, **.NET 10** |
|
||||
| **Arquitectura backend** | REST clásico versionado (`/api/v1/...`) con capas **Controller → Service → Repository** |
|
||||
| **Inyección de dependencias** | Hilt |
|
||||
| **Persistencia local** | Room (DAO por feature) + DataStore (preferencias) — offline-first |
|
||||
| **Networking** | Retrofit + OkHttp + kotlinx.serialization |
|
||||
| **Concurrencia** | Kotlin Coroutines + `StateFlow` |
|
||||
| **Ilustración de mascota** | Vectorial por código con `Canvas` de Compose (sin assets externos) |
|
||||
| **Backend** | ASP.NET Core Web API, **.NET 10** (mismo stack que [[gastosai]] y [[pitchmusicadmin]]) |
|
||||
| **Arquitectura backend** | Clean Architecture: `Api → Application → Domain ← Infrastructure`. REST versionado `/api/v1/...` |
|
||||
| **ORM** | Entity Framework Core (Code First + Migrations) |
|
||||
| **Base de datos** | PostgreSQL |
|
||||
| **Autenticación** | Sin login para la niña. Perfil local vinculado a un **código de familia** (family code) para emparejar Rachas en equipo. Gate parental simple (no es "auth" real) para pantallas sensibles (Ajustes, invitaciones) |
|
||||
| **Persistencia local Android** | v1: la app funciona con almacenamiento local simple (DataStore/Room mínimo) para hábitos/museo offline-first; sincronización real con backend se agrega cuando se implemente "Rachas en equipo". Room completo con sync es deseable, no bloqueante |
|
||||
| **Hosting backend** | Servidor Ubuntu propio, vía **Docker** (contenedor de la API) + **Nginx** como reverse proxy + **systemd** para gestionar el stack |
|
||||
| **Repositorio** | Monorepo único: `git.paputec.mx/johannvelazquez/h-bitosInfantiles` con carpetas `/app` (Android) y `/backend` (.NET) |
|
||||
| **CI/CD** | No configurado todavía. Se agrega más adelante (posible runner de build dedicado, ver skill `migrate-ci-build-runner` si aplica) |
|
||||
| **Testing** | Sí, desde el inicio: JUnit/Compose UI tests en Android, xUnit en .NET |
|
||||
| **Assets de la mascota** | Se mantiene el enfoque ilustrativo **por código** (formas vectoriales/Compose Canvas), igual que el prototipo HTML — no hay assets de diseño gráfico todavía |
|
||||
| **Base de datos** | PostgreSQL 16 (Docker `postgres:16-alpine`, consistente con el resto del servidor) |
|
||||
| **Autenticación** | Sin login para la niña. Perfil local + **código de familia** para "rachas en equipo". Gate parental simple (matemática) para pantallas sensibles |
|
||||
| **Repositorio** | Monorepo `git.paputec.mx/johannvelazquez/h-bitosInfantiles` con `/app` (Android) y `/backend` (.NET) |
|
||||
| **Testing** | Desde el inicio: JUnit 5 + Compose UI Test en Android, xUnit + FluentAssertions en .NET |
|
||||
|
||||
### 2.1 Alcance del primer sprint (MVP real, no todo el PRD)
|
||||
### 3.1 Alcance del primer sprint (MVP real, no todo el PRD)
|
||||
|
||||
Construir **solo** estos 3 módulos primero, de punta a punta (UI + lógica + persistencia local, sin backend aún):
|
||||
1. **Onboarding / Selección de compañero (Avatar)**
|
||||
@@ -46,139 +82,204 @@ Los demás módulos del PRD (Museo, Galería, Rachas en equipo, Alertas/notifica
|
||||
|
||||
---
|
||||
|
||||
## 3. Estructura del repositorio (monorepo)
|
||||
## 4. Infraestructura y despliegue (servidor PapuTec)
|
||||
|
||||
El servidor de producción es el mismo que aloja [[gastosai]], [[pitchmusicadmin]] y [[landing-cms]]. **Stack real:** Apache 2.4 + Plesk + Docker. **NO hay Nginx** — cualquier documentación previa que lo mencionara está corregida aquí.
|
||||
|
||||
### 4.1 Topología objetivo del backend
|
||||
|
||||
- **Contenedor:** `rachita-backend` (imagen build desde `./backend/Dockerfile`, base `mcr.microsoft.com/dotnet/aspnet:10.0-alpine`).
|
||||
- **Compose file:** `/opt/rachita/docker-compose.prod.yml` en el servidor, `.env` con perms 600.
|
||||
- **Puerto expuesto:** `127.0.0.1:8002` (siguiente disponible después de gastosai en 8001; NO exponer `0.0.0.0`).
|
||||
- **DB:** `rachita-postgres` (postgres:16-alpine), volumen `rachita_pgdata`, red interna `rachita_default`. Solo accesible dentro de la red del compose.
|
||||
- **Migraciones:** se ejecutan automáticamente al arrancar el contenedor (`dotnet ef database update` en el entrypoint, patrón de pitchmusicadmin).
|
||||
|
||||
### 4.2 Enrutamiento HTTP — dos opciones (a decidir)
|
||||
|
||||
**Opción A (recomendada): subdominio custom `rachita.paputec.mx`** — sigue el patrón de `finanzas.paputec.mx` y `git.paputec.mx`, todos con vhosts Apache propios en `/etc/apache2/sites-enabled/` (fuera de Plesk):
|
||||
|
||||
```apache
|
||||
# /etc/apache2/sites-enabled/rachita.paputec.mx.conf (:80 → redirect https + acme-challenge)
|
||||
# /etc/apache2/sites-enabled/rachita.paputec.mx-le-ssl.conf (:443 → ProxyPass / http://127.0.0.1:8002/)
|
||||
```
|
||||
|
||||
Cert Let's Encrypt vía `certbot certonly --webroot -w /var/www/rachita-acme -d rachita.paputec.mx` (NO `--apache`, sigue patrón finanzas).
|
||||
|
||||
**Opción B: subruta `paputec.mx/rachita/api/`** — como `paputec.mx/admin/api/` (pitchmusicadmin) o `paputec.mx/cms/api/` (landing-cms). Requiere editar **ambos** `vhost.conf` y `vhost_ssl.conf` de `paputec.mx` bajo `/var/www/vhosts/system/paputec.mx/conf/`, aplicar con `sudo -n plesk sbin httpdmng --reconfigure-domain paputec.mx && sudo -n service apache2 reload`.
|
||||
|
||||
> Para una app móvil, **A es la elección natural** — la URL base la consume solo el cliente Android, no un browser navegando desde otra ruta. Definir subdominio antes del primer despliegue.
|
||||
|
||||
### 4.3 CI/CD — Gitea Actions
|
||||
|
||||
El servidor tiene un `act_runner` con label **`android:docker://paputec/android-runner:latest`** ya disponible ([[paputec_deployment]]). Plan:
|
||||
|
||||
- **`.gitea/workflows/backend.yml`** — build imagen `rachita-backend`, push a registry local o `docker load` directo, `docker compose up -d --no-deps rachita-backend` contra el host (network host + socket Docker montado en el runner).
|
||||
- **`.gitea/workflows/android.yml`** — usar el label `android`, ejecutar `./gradlew testDebugUnitTest lintDebug assembleRelease` sobre `runs-on: android`. Firma de APK con keystore en Gitea Secrets. Publicar APK como artifact de release; instalación en la tablet vía ADB o URL directa (no Play Store en v1).
|
||||
|
||||
**Trigger:** push a `main` para deploy backend; tag `v*.*.*` para release APK.
|
||||
|
||||
### 4.4 SSH al servidor
|
||||
|
||||
Siempre `ssh johann@paputec.mx` + `sudo -n` cuando sea necesario ([[paputec_ssh]]). Nunca `root`. `johann` **no** está en el grupo `docker` — comandos Docker manuales requieren `sudo -n docker ...`.
|
||||
|
||||
---
|
||||
|
||||
## 5. Estructura del repositorio (monorepo)
|
||||
|
||||
```
|
||||
h-bitosInfantiles/
|
||||
├── CLAUDE.md ← este archivo
|
||||
├── PRD-Rachita.md ← requisitos de producto completos
|
||||
├── Rachita.html ← prototipo visual (referencia, no se ejecuta en prod)
|
||||
├── CLAUDE.md ← este archivo
|
||||
├── PRD-Rachita.md ← requisitos de producto completos
|
||||
├── Rachita.html ← prototipo visual (referencia, ver §10)
|
||||
├── README.md
|
||||
├── app/ ← proyecto Android (Kotlin + Jetpack Compose)
|
||||
│ ├── build.gradle.kts
|
||||
├── STATUS.md ← estado vivo (agregar cuando arranque desarrollo)
|
||||
├── .gitea/workflows/ ← Gitea Actions (backend.yml, android.yml)
|
||||
├── app/ ← proyecto Android (Kotlin + Jetpack Compose)
|
||||
│ ├── settings.gradle.kts
|
||||
│ ├── gradle/libs.versions.toml ← version catalog
|
||||
│ └── app/
|
||||
│ ├── build.gradle.kts
|
||||
│ └── src/main/java/mx/paputec/rachita/
|
||||
│ ├── ui/ ← pantallas Compose por feature
|
||||
│ ├── data/ ← repos locales, DataStore/Room, modelos
|
||||
│ ├── domain/ ← casos de uso / lógica de negocio
|
||||
│ └── di/ ← inyección de dependencias (Hilt)
|
||||
└── backend/ ← proyecto .NET 10 Web API
|
||||
│ ├── ui/ ← Composables por feature (onboarding, dashboard, battery, museum…)
|
||||
│ │ └── theme/ ← Color.kt, Type.kt, Shape.kt (§6.1)
|
||||
│ ├── data/ ← Room DAOs, DataStore, Retrofit services, DTOs
|
||||
│ ├── domain/ ← entidades puras + use cases
|
||||
│ ├── di/ ← módulos Hilt
|
||||
│ ├── platform/ ← receivers, services, widgets (código Android específico)
|
||||
│ └── RachitaApplication.kt
|
||||
└── backend/ ← proyecto .NET 10 Web API
|
||||
├── Rachita.sln
|
||||
├── Dockerfile
|
||||
├── docker-compose.dev.yml ← db + api local
|
||||
├── docker-compose.prod.yml ← plantilla de despliegue (copiar a /opt/rachita/)
|
||||
├── src/
|
||||
│ ├── Rachita.Api/ ← Controllers, Program.cs, DI, middlewares
|
||||
│ ├── Rachita.Application/ ← Services, DTOs, interfaces
|
||||
│ ├── Rachita.Domain/ ← Entidades, reglas de negocio puras
|
||||
│ └── Rachita.Infrastructure/ ← EF Core, DbContext, Repositories, Migrations
|
||||
│ ├── Rachita.Api/ ← Controllers, Program.cs, middlewares
|
||||
│ ├── Rachita.Application/ ← Services, DTOs, interfaces
|
||||
│ ├── Rachita.Domain/ ← Entidades y reglas puras
|
||||
│ └── Rachita.Infrastructure/ ← EF Core, DbContext, Repositories, Migrations
|
||||
└── tests/
|
||||
├── Rachita.Application.Tests/
|
||||
└── Rachita.Api.Tests/
|
||||
```
|
||||
|
||||
**Regla:** el código Android nunca depende directamente de EF Core ni del backend; se comunica solo vía API REST (DTOs propios en el cliente).
|
||||
**Regla dura:** el código Android nunca depende directamente de EF Core ni del backend; se comunica solo vía API REST (DTOs propios en el cliente, mapeo explícito).
|
||||
|
||||
---
|
||||
|
||||
## 4. Convenciones de código
|
||||
## 6. Convenciones de código
|
||||
|
||||
### 4.1 Android / Kotlin
|
||||
### 6.1 Android / Kotlin
|
||||
- Arquitectura por **feature** dentro de `ui/` (ej. `ui/onboarding`, `ui/dashboard`, `ui/battery`), no por tipo de archivo.
|
||||
- Patrón **MVVM**: `Screen.kt` (Composable) + `ViewModel.kt` (`StateFlow` de UI state) + `UiState` data class.
|
||||
- Inyección de dependencias con **Hilt**.
|
||||
- Patrón **MVVM**: `Screen.kt` (Composable) + `ViewModel.kt` (`StateFlow<UiState>`) + `UiState` data class inmutable.
|
||||
- DI con **Hilt**: `@HiltAndroidApp`, `@HiltViewModel`, módulos en `di/`.
|
||||
- Nombres: `PascalCase` para Composables y clases, `camelCase` para funciones/variables, `UPPER_SNAKE_CASE` para constantes.
|
||||
- Un Composable por archivo cuando sea una pantalla completa; sub-componentes pequeños pueden compartir archivo si son privados (`private fun`).
|
||||
- Preview de Compose (`@Preview`) obligatorio en componentes visuales reutilizables.
|
||||
- Colores, tipografías y espaciados centralizados en `ui/theme/` (`Color.kt`, `Type.kt`, `Shape.kt`) siguiendo la guía de estilo del PRD (§6).
|
||||
- Animaciones con `animateFloatAsState`, `rememberInfiniteTransition`, etc. — replicar timings del prototipo (ver PRD §6.4).
|
||||
- Un Composable por archivo cuando sea pantalla completa; sub-componentes pequeños pueden compartir archivo si son `private fun`.
|
||||
- `@Preview` obligatorio en componentes visuales reutilizables (con estados: default, cargando, error).
|
||||
- Colores, tipografías y espaciados centralizados en `ui/theme/` siguiendo la guía de estilo del PRD §6 (paleta cream/coral/lila/verde).
|
||||
- Animaciones con `animateFloatAsState`, `rememberInfiniteTransition`, `Animatable` — replicar timings del prototipo (PRD §6.4).
|
||||
- Código Android específico (BroadcastReceivers, Services, Widgets, WindowManager) va en `platform/`, no en `ui/`.
|
||||
|
||||
### 4.2 Backend / .NET
|
||||
- Separación estricta de capas: `Api` (controllers, DTOs de request/response) → `Application` (servicios, interfaces `IXxxService`) → `Domain` (entidades y reglas puras, sin dependencias externas) → `Infrastructure` (EF Core, `DbContext`, implementaciones de repositorios).
|
||||
- Controllers **delgados**: solo reciben request, llaman al service, devuelven response. Nada de lógica de negocio en el controller.
|
||||
- Repositorios detrás de interfaces (`IHabitRepository`, etc.) definidas en `Application` o `Domain`, implementadas en `Infrastructure`.
|
||||
- DTOs distintos de las entidades de dominio (nunca exponer entidades EF directamente en la API).
|
||||
- Migraciones EF Core versionadas y con nombre descriptivo (`dotnet ef migrations add AddTeamStreaks`).
|
||||
- Endpoints versionados: `/api/v1/...`.
|
||||
- Convención REST: sustantivos en plural, verbos HTTP correctos (`GET /api/v1/habits`, `POST /api/v1/habits/{id}/complete`).
|
||||
### 6.2 Backend / .NET
|
||||
- Separación estricta de capas: `Api` → `Application` → `Domain` ← `Infrastructure`. `Domain` no depende de nada; `Infrastructure` implementa interfaces declaradas en `Application`.
|
||||
- Controllers **delgados**: request → service → response. Nada de lógica de negocio.
|
||||
- Repositorios detrás de interfaces (`IHabitRepository`, `IProfileRepository`) definidas en `Application`.
|
||||
- DTOs distintos de las entidades de dominio — nunca exponer entidades EF en la API.
|
||||
- Migraciones EF Core con nombre descriptivo: `dotnet ef migrations add AddTeamStreaks --project src/Rachita.Infrastructure --startup-project src/Rachita.Api`.
|
||||
- Endpoints versionados: `/api/v1/...`. Sustantivos en plural, verbos HTTP correctos (`GET /api/v1/habits`, `POST /api/v1/habits/{id}/complete`).
|
||||
- Logging: `Serilog` a stdout (para que Docker/journalctl lo capture). Nunca a archivo dentro del contenedor.
|
||||
- Configuración por `IOptions<T>` + `appsettings.json` + variables de entorno (`ASPNETCORE_ENVIRONMENT=Production`). Secretos solo en `.env` del host, nunca en el repo.
|
||||
|
||||
### 4.3 General
|
||||
- Idioma del código (nombres de variables, clases): **inglés**. Idioma de la UI/copy visible al usuario: **español (MX)**, tal como en el PRD y el prototipo.
|
||||
- Commits: mensajes claros en español o inglés consistente por PR, formato libre (Conventional Commits deseable pero no obligatorio).
|
||||
- No se necesita documentar cada cambio en archivos markdown nuevos salvo que se pida explícitamente.
|
||||
### 6.3 General
|
||||
- Idioma del código (nombres de variables, clases, comentarios técnicos): **inglés**. Idioma de la UI/copy visible al usuario: **español (MX)**, tal como en el PRD y el prototipo.
|
||||
- Commits: mensajes claros en español o inglés consistente por PR. Conventional Commits deseable pero no obligatorio. **No agregar `Co-Authored-By: Claude`** ([[no_coauthor_commits]]).
|
||||
- No crear archivos markdown de resumen/plan salvo que se pida explícitamente.
|
||||
|
||||
---
|
||||
|
||||
## 5. Modelo de datos de referencia
|
||||
## 7. Modelo de datos de referencia
|
||||
|
||||
Basado en PRD §9, adaptado a EF Core (backend) y su equivalente local (Android):
|
||||
Basado en PRD §9, adaptado a EF Core (backend) y su equivalente Room local (Android):
|
||||
|
||||
```
|
||||
Profile (childName, avatarKind, customAvatarUri, level, xp, streak, familyCode)
|
||||
Habit (id, name, subtitle, icon, color, active, createdAt)
|
||||
Profile (id, childName, avatarKind, customAvatarUri, level, xp, streak, familyCode)
|
||||
Habit (id, profileId, name, subtitle, icon, color, active, createdAt)
|
||||
HabitLog (habitId, date, done) -- 1 registro por hábito por día
|
||||
Artwork (id, date, imageUri, title, note, medium)
|
||||
Artwork (id, profileId, date, imageUri, title, note, medium)
|
||||
TeamStreak (id, ownerProfileId, partnerProfileId, sharedHabitId, streak, week[7])
|
||||
TeamStreakDay (teamStreakId, date, myDone, theirDone, nudgedByMe, nudgedByThem)
|
||||
Settings (profileId, notifyLowBattery, notifyWhileCharging, habitReminderAt, confettiOn)
|
||||
```
|
||||
|
||||
Reglas clave a implementar (ver PRD §5.2 y §5.7):
|
||||
- La racha global sube +1 solo si **todos** los hábitos activos del día quedaron `done = true` antes de medianoche.
|
||||
- La racha global sube +1 solo si **todos** los hábitos activos del día quedaron `done = true` antes de medianoche (hora local del dispositivo).
|
||||
- En `TeamStreak`, la racha crece solo si `myDone && theirDone` el mismo día calendario.
|
||||
- Sincronización: cliente autoritativo para `HabitLog` local hasta que el usuario active "Rachas en equipo"; a partir de ahí el backend reconcilia por `(profileId, habitId, date)`.
|
||||
|
||||
---
|
||||
|
||||
## 6. Sistema de Monitoreo de Batería — notas técnicas
|
||||
## 8. Sistema de Monitoreo de Batería — notas técnicas
|
||||
|
||||
- Usar `BatteryManager` / `Intent.ACTION_BATTERY_CHANGED` (o `registerReceiver` con `IntentFilter(Intent.ACTION_BATTERY_CHANGED)`) para leer nivel y estado de carga en tiempo real — **no hacer polling manual**.
|
||||
- Umbral batería baja: `< 20%` → mostrar overlay (Composable con `Dialog` o superposición a pantalla completa), no bloqueante, con opción "Ahora no".
|
||||
- Detección de carga: `BatteryManager.EXTRA_STATUS == BATTERY_STATUS_CHARGING` (o `plugged != 0`) → activar pantalla de bloqueo total (Activity/Composable a pantalla completa, sin back button funcional, ver PRD CA-01).
|
||||
- El botón "Ya la desconecté" debe **verificar el estado real** del sistema antes de desbloquear (no confiar ciegamente en el tap del usuario) — mitigación de riesgo listada en PRD §13.
|
||||
- Evaluar `Screen Pinning` (modo kiosco, PRD D-24) cuando se implemente el bloqueo, para impedir salir de la app durante la carga.
|
||||
- Usar `Intent.ACTION_BATTERY_CHANGED` (sticky broadcast) registrado con `registerReceiver` para leer nivel y estado de carga en tiempo real — **no hacer polling manual**.
|
||||
- Umbral batería baja: `< 20%` → mostrar overlay Compose (`Dialog` o `Popup` a pantalla completa), no bloqueante, con opción "Ahora no". Reaparición en 15/10/5% (PRD BA-03).
|
||||
- Detección de carga: `BatteryManager.EXTRA_STATUS == BATTERY_STATUS_CHARGING` (o `plugged != 0`) → activar `Activity` de bloqueo a pantalla completa, sin back button funcional (PRD CA-01).
|
||||
- El botón "Ya la desconecté" debe **verificar el estado real** del sistema antes de desbloquear (no confiar en el tap) — mitigación de riesgo listada en PRD §13.
|
||||
- Evaluar `startLockTask()` (Screen Pinning, PRD D-24) o modo COSU si la tablet es dedicada, para impedir salir de la app durante la carga.
|
||||
- Overlay sobre otras apps (si se requiere fuera de Rachita): `WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY` + permiso `SYSTEM_ALERT_WINDOW`. En v1 basta con overlay dentro de la propia app.
|
||||
|
||||
---
|
||||
|
||||
## 7. Cómo ejecutar el proyecto (a completar conforme se construya)
|
||||
## 9. Cómo ejecutar el proyecto
|
||||
|
||||
### Android
|
||||
```powershell
|
||||
### Android (desarrollo)
|
||||
```bash
|
||||
cd app
|
||||
./gradlew assembleDebug
|
||||
# instalar en dispositivo/emulador conectado:
|
||||
./gradlew installDebug
|
||||
```
|
||||
> Pendiente: confirmar si se usará emulador Android Studio o tablet física para pruebas (no definido aún).
|
||||
|
||||
### Backend
|
||||
```powershell
|
||||
Emulador vs tablet física: definir al arrancar el sprint 1. Recomendado tablet física (Lenovo Tab / Samsung Tab de gama media) porque el monitoreo de batería no se comporta igual en emulador.
|
||||
|
||||
### Backend (desarrollo local)
|
||||
```bash
|
||||
cd backend
|
||||
dotnet restore
|
||||
docker compose -f docker-compose.dev.yml up -d # levanta postgres
|
||||
dotnet ef database update --project src/Rachita.Infrastructure --startup-project src/Rachita.Api
|
||||
dotnet run --project src/Rachita.Api
|
||||
# API en http://localhost:5002
|
||||
```
|
||||
|
||||
### Docker (backend, producción)
|
||||
```powershell
|
||||
docker build -t rachita-api ./backend
|
||||
docker run -d -p 5000:8080 --env-file .env rachita-api
|
||||
### Backend (producción — servidor PapuTec)
|
||||
```bash
|
||||
# En el servidor, primera vez:
|
||||
sudo mkdir -p /opt/rachita && sudo chown johann:johann /opt/rachita
|
||||
scp backend/docker-compose.prod.yml johann@paputec.mx:/opt/rachita/
|
||||
# crear /opt/rachita/.env con perms 600 (DB_PASSWORD, JWT_SECRET, etc.)
|
||||
|
||||
# Deploy (idealmente vía Gitea Actions, ver §4.3):
|
||||
ssh johann@paputec.mx 'cd /opt/rachita && sudo -n docker compose -f docker-compose.prod.yml pull && sudo -n docker compose -f docker-compose.prod.yml up -d'
|
||||
```
|
||||
> Pendiente definir: variables de entorno exactas, configuración de Nginx (dominio/subdominio), certificados TLS y compose file cuando se llegue al sprint de despliegue.
|
||||
|
||||
---
|
||||
|
||||
## 8. Pendientes / decisiones abiertas
|
||||
## 10. Uso del prototipo `Rachita.html`
|
||||
|
||||
Es un bundle exportado de una herramienta de diseño (manifiesto base64 + template comprimido), **no** es código fuente Kotlin/React reutilizable directamente. Úsalo solo para:
|
||||
- Extraer valores exactos de la guía de estilo (colores, radios, sombras, timings de animación).
|
||||
- Confirmar el copy exacto en español de cada pantalla.
|
||||
- Revisar el flujo de estados (`showDash`, `isLow`, `isCharging`, etc.) como referencia de máquina de estados al implementar los ViewModels.
|
||||
|
||||
Si necesitas inspeccionar su contenido interno, desempácalo en `_extracted/` (carpeta local, agregada a `.gitignore`, no versionar).
|
||||
|
||||
---
|
||||
|
||||
## 11. Pendientes / decisiones abiertas
|
||||
|
||||
Estas preguntas del PRD (§16) siguen sin resolver y pueden afectar el modelo de datos o la UX; confirmarlas antes de implementar el módulo correspondiente:
|
||||
1. ¿La racha global se pierde por completo al fallar un día, o hay "congelador" desde v1?
|
||||
2. ¿Los hábitos tienen horario límite individual?
|
||||
3. ¿Quién etiqueta el "medio" del dibujo (crayón/acuarela/…)?
|
||||
4. ¿Reporte/panel para el adulto en v1 o se pospone?
|
||||
5. Dominio/subdominio exacto y configuración de Nginx para el backend en el servidor Ubuntu (pendiente de confirmar con el usuario).
|
||||
6. Estrategia exacta de sincronización offline↔backend para hábitos y museo (más allá de "Rachas en equipo") aún no definida.
|
||||
|
||||
---
|
||||
|
||||
## 9. Uso del prototipo `Rachita.html`
|
||||
|
||||
Es un bundle exportado de una herramienta de diseño (contiene un manifiesto base64 + template comprimido), **no** es código fuente Kotlin/React reutilizable directamente. Úsalo solo para:
|
||||
- Extraer valores exactos de la guía de estilo (colores, radios, sombras, timings de animación).
|
||||
- Confirmar el copy exacto en español de cada pantalla.
|
||||
- Revisar el flujo de estados (`showDash`, `isLow`, `isCharging`, etc.) como referencia de máquina de estados al implementar el ViewModel de Compose.
|
||||
|
||||
Si necesitas inspeccionar su contenido interno, ya fue desempaquetado una vez en `_extracted/` (carpeta temporal, no versionar en git — agregar a `.gitignore` si se conserva).
|
||||
5. **Subdominio de producción** — confirmar `rachita.paputec.mx` (Opción A, §4.2). Bloquea el primer despliegue.
|
||||
6. **Tablet objetivo** — modelo específico para pruebas de batería/kiosk (afecta pruebas de Screen Pinning).
|
||||
7. Estrategia exacta de sincronización offline↔backend para hábitos y museo (más allá de "Rachas en equipo").
|
||||
|
||||
Reference in New Issue
Block a user