Skip to content

feat(wear): performance toggles for offline watch playback, set from the phone - #24

Open
PonceGL wants to merge 1 commit into
feature/wear-playlist-transferfrom
feature/wear-performance-settings
Open

feat(wear): performance toggles for offline watch playback, set from the phone#24
PonceGL wants to merge 1 commit into
feature/wear-playlist-transferfrom
feature/wear-performance-settings

Conversation

@PonceGL

@PonceGL PonceGL commented Aug 11, 2026

Copy link
Copy Markdown
Owner

Qué es esto

Sección nueva "Reloj" en Ajustes del teléfono con 3 switches que solo afectan la reproducción standalone local del reloj (sin teléfono conectado) — el escenario de hacer ejercicio, donde una app de fitness compite por CPU/RAM/batería y la pantalla está encendida todo el tiempo, así que el gating automático de isInteractive (ambient/segundo plano) de la sesión pasada no ayuda aquí.

Los 3 toggles (evaluación completa en el plan)

Toggle Costo real que evita
Mostrar carátula Bitmap sin comprimir hasta ~16MB por canción, sin caché, sin .recycle(). El de mayor impacto con diferencia.
Color dinámico Re-muestreo del bitmap completo en el hilo principal para theming — de paso corregí el desperdicio real: WearTheme.kt ahora prefiere el seed color ya calculado en hilo IO en vez de recalcularlo (esto aplica siempre, no solo con el toggle apagado).
Animación del botón de Play Reconstrucción de un path de 320 puntos por frame, continua mientras suena música — incluso viendo Letras o Biblioteca (el pager mantiene la página compuesta).

Evalué toda la pantalla del reproductor y descarté el resto (barra de progreso, PlayingEqIcon, letras) por ya estar gateados o ya no correr durante reproducción local — no hay relleno, los 3 son costos medibles reales.

Arquitectura: teléfono configura, reloj cachea

Sync vía DataItem, no MessageClient — misma razón que el fix de sync de playlist de este stack: MessageClient solo confirma entrega local, no que el reloj lo recibió. Un DataItem sí llega de forma durable cuando el reloj reconecta, sin necesitar ack propio.

El reloj guarda lo último sincronizado en un WearPerformanceSettingsRepository local (mismo DataStore que WearPlaybackStatePersistence) — así aplica aunque el teléfono no esté conectado en el momento de reproducir, que es todo el punto.

Los toggles solo afectan el decode local (WearLocalPlayerRepository) — en modo controlador remoto la carátula/tema ya llegan resueltos del teléfono, no hay nada que ahorrar ahí, así que se ignoran por completo en ese modo.

Tests

6 tests nuevos para WearPerformanceSettingsRepository (default true, round-trip por flag vía Turbine, semántica de sobrescritura). WearPerformanceSettingsPublisher sin test unitario — misma limitación que WearStatePublisher (llamada estática a Wearable.getDataClient(), sin agente de mockeo inline en este sandbox). Arreglé 2 archivos de test existentes rotos por el nuevo parámetro del constructor de WearLocalPlayerRepository.

Verifiqué :wear:assembleRelease y :app:assembleRelease (R8 + shrinkResources) limpios — con lo del shrinker de hace unas horas, no iba a dar nada por sentado esta vez.

Pendiente de verificación en hardware: efecto inmediato de cada toggle sin reiniciar la app, comportamiento correcto en modo remoto, y que un reloj recién reconectado reciba el valor vigente.

…the phone

Adds a "Watch" category to the phone's Settings hub with three switches
that only affect the watch's standalone local playback (no phone
connected) — the running/exercise scenario where a fitness app is
competing for the same CPU/RAM/battery and the screen is on the whole
time, so the existing isInteractive-based lifecycle gating (ambient/
background) can't help.

Evaluated the whole player screen for what's actually worth toggling and
landed on three real, measurable costs (ruled out the rest — progress
bar, PlayingEqIcon, lyrics — as already lifecycle-gated or already inert
during local playback):

- Album art: up to a ~16MB uncompressed bitmap per song, no cache, no
  recycle. By far the biggest win.
- Dynamic color theming: a redundant main-thread bitmap resample in
  WearTheme when a cheap IO-thread seed color was already available
  (fixed unconditionally regardless of the toggle, since it's a pure
  inefficiency either way).
- Play button ring animation: continuous per-frame 320-point path
  rebuild, kept alive by the pager even off-page.

Sync is phone -> watch via a DataItem (WearDataPaths.WEAR_PERFORMANCE_
SETTINGS), not MessageClient — same reasoning as the playlist-sync ack
fix earlier this stack: MessageClient only confirms local hand-off, and
a DataItem instead durably reaches the watch once it reconnects, no ack
needed. The watch caches the last-synced values locally
(WearPerformanceSettingsRepository, same DataStore singleton as
WearPlaybackStatePersistence) so they still apply when the phone isn't
reachable at the moment local playback starts — the whole point.

Toggles only gate WearLocalPlayerRepository's local decode path, never
remote-controller mode: that path already gets its art/theme pre-resolved
from the phone and never decodes anything heavy locally, so there's
nothing to save there.

New/changed test coverage: 6 tests for WearPerformanceSettingsRepository
(default-true, per-flag round-trip via Turbine, overwrite semantics).
WearPerformanceSettingsPublisher isn't unit tested — same constraint as
the existing WearStatePublisher it mirrors (Wearable.getDataClient() is
a static GMS call, no test double without the inline-mocking agent that
hangs in this sandbox). Fixed two pre-existing test files broken by
WearLocalPlayerRepository's new constructor parameter.

Verified :wear and :app both still build clean in release (R8 +
shrinkResources) with the new resources/classes, given the resource-
shrinker incident earlier this stack.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant