// Pull-to-refresh (HealthScreen)SyncHistoricalData → syncData()
// Si pulsera conectada → sync real:
syncHeartRate() + syncSteps()
+ syncSleepHistory() + syncSpo2()
// Si desconectada → solo recarga local:
fetchDataForSelectedDate()
// Stream en tiempo real (Dashboard):StartVitalsStream → bleRepository
.startVitalsStream()
.collect { vitals → updateState }
Regla clave: pull-to-refresh comprueba
connectionState.value is Connected de forma
inmediata. Si no hay conexión, recarga desde SQLite sin intentar
reconectar.
Referencia para modificar: editar
TrueAgeWidget.kt. El anillo, el tamaño de número
y los colores están en RevealedView. El estado de
cálculo en CalculatingView.
¿Cuándo se calcula? Tras cada sync y al
cargar el Dashboard. El ViewModel hace un bucle de 0..9 días
atrás, obtiene DailyMetrics por día y llama a
calculateWindowed().
P
HeartRateIntelligenceEngine
shared/domain/engine/ · Salida: PhysiologyInsight
Motor IA100% Local
Métricas calculadas
rhr = percentil_10(hrHistory_duringSleep) rmssd ≈
sqrt(mean(diff(bpm_sucesivos)²))
// estimación
recoveryScore = sleepScore × 0.65 + cardiacEfficiency × 0.35 ×
hrvMultiplier cardiacEfficiency = 100 - (rhr - 40) × 1.6
sedentaryStressAlert = true si hay picos FC sin variación de
movimiento detectados
Para modificar:ResilienceRadarChart.kt. Los 4 ejes están fijos
(N/E/S/O = HRV/SpO2/Sueño/Recovery). Para añadir ejes, cambiar
el loop de 0..3 y los ángulos a
i * 360/n.
06 — Sección de Salud
HealthSectionContent · 7 Tarjetas
Componente compartido:internal fun HealthSectionContent(state: DashboardState, modifier:
Modifier)
— vive en HealthScreen.kt y se usa en
DashboardScreen y HealthScreen sin
duplicación.
1
ActivityRingsWidget
HealthScreen.kt · private · posición: tarjeta 1
CanvasSpring Bounce
Visual
Origen de datos
steps = selectedDaySteps.totalSteps ?: latestVitals.steps
// si es hoy
calories = selectedDaySteps.totalCalories distKm =
distanceMeters / 1000
3 Anillos — objetivos
Anillo
Color
Objetivo
Fuente
Pasos
Cyan
10 000 pasos
StepsRecord
Calorías
Naranja
500 kcal
StepsRecord
Distancia
Violeta
5 km
StepsRecord
Animación
Cada anillo usa animateFloatAsState con
spring(DampingRatioMediumBouncy, StiffnessLow).
El bounce es suave y da sensación de física real al cargar.
Para modificar objetivos: constantes en la
parte superior de HealthScreen.kt:
STEPS_GOAL_DEFAULT = 10_000,
CALS_GOAL = 500, DIST_GOAL_KM = 5f.
2
BioAgeCard + LoadRecoveryCard
HealthScreen.kt · Row de 2 tarjetas · posición: tarjeta 2
StateSpring
BioAgeCard
trueAgeYears — de
state.trueAgeYears (Int, redondeado)
profile — para calcular edad
cronológica
diff = chronologicalAge - trueAgeYears
// diff > 0 → más joven de lo esperado ✓ // diff < 0 → por
encima de su edad ✗
diff
Color
Mensaje
≥ 5
Cyan
Muy joven para tu edad
1-4
Teal
Más joven de lo esperado
0
Teal
Acorde a tu edad
−1 a −4
Amber
Ligeramente por encima
≤ −5
Rojo
Por encima de tu edad
LoadRecoveryCard
load —
state.activityScore (0-100)
recovery —
physiologyInsight.recoveryScore o estimado
local
insight —
PhysiologyInsight?
Subcomponentes internos
ScoreGauge — arco de
progreso para Carga
RecoveryWidget —
semicírculo WHOOP-style para Recuperación (componente
externo RecoveryWidget.kt)
Para modificar:LoadRecoveryCard en HealthScreen.kt.
El RecoveryWidget vive en
ui/component/RecoveryWidget.kt.
visibleCount = (drawProgress × records.size) drawProgress:
0.0 → 1.0 en 1 400ms
// La línea se dibuja de izquierda a derecha // Se anima
cuando cambia records.size
Widgets inferiores — VitalityStatChip × 2
Estado de Forma —
PhysiologyInsight.RecoveryStatus.label +
subtítulo HRV/RHR
Recuperación — score 0-100
+ barra animada spring
Parámetros de HeartRateCard
currentHr —
latestVitals.heartRate o último bpm
hrHistory —
state.hrHistory (lista completa 24h)
sleepRecords —
state.sleepHistory para overlays
insight —
state.physiologyInsight
recoveryScore — estimado
local si no hay insight
Para modificar el mapa HR:fun HeartRateVitalityMap en
ui/component/Charts.kt. Para modificar los chips:HrVitalityInsights y
VitalityStatChip en
HealthScreen.kt. Para cambiar el umbral de actividad
(actualmente 90 bpm): buscar rec.bpm >= 90 en
ambas ubicaciones.
4
SpO2Card + MetabolismCard
HealthScreen.kt · Row de 2 tarjetas · posición: tarjeta 4