Persist the exploration, proposal, spec, design, tasks, and verify/archive reports produced during the multi-device EQ, alarm-countdown, and notification-visual-polish SDD cycles.
6.9 KiB
6.9 KiB
Tasks: Multi-Device Equalizer
Review Workload Forecast
| Field | Value |
|---|---|
| Estimated changed lines | 700–950 |
| 400-line budget risk | High |
| Chained PRs recommended | Yes |
| Suggested split | PR 1: Model + Service + Platform channel → PR 2: State layer + persistence → PR 3: UI + export-import |
| Delivery strategy | ask-on-risk |
| Chain strategy | pending |
Decision needed before apply: Yes Chained PRs recommended: Yes Chain strategy: pending 400-line budget risk: High
Suggested Work Units
| Unit | Goal | Likely PR | Notes |
|---|---|---|---|
| 1 | Platform bridge + Dart service + model + fakes | PR 1 | Base: feature/multi-device-eq; no behavioral changes yet |
| 2 | State layer 4-level resolution + persistence + toggle | PR 2 | Base: PR 1 branch; depends on Unit 1 |
| 3 | Settings UI + export/import v3 | PR 3 | Base: PR 2 branch; depends on Unit 2 |
Phase 1: Foundation — Model and Service Interface (PR 1 scope)
- 1.1 RED: write
test/modelos/dispositivo_audio_test.dart— assertTipoDispositivoenum values andDispositivoAudioequality - 1.2 GREEN: create
lib/modelos/dispositivo_audio.dart—TipoDispositivoenum +DispositivoAudiovalue class with==/hashCode - 1.3 RED: write
test/servicios/servicio_dispositivo_audio_test.dart— assert abstract contract + fake stream behavior - 1.4 GREEN: create
lib/servicios/servicio_dispositivo_audio.dart— abstract class withdispositivoActual,onDispositivoCambiado,obtenerDispositivoActual(),dispose() - 1.5 GREEN: add
FakeServicioDispositivoAudiototest/helpers/fakes.dart—StreamController-backed fake;emitDispositivo(DispositivoAudio)helper - 1.6 REFACTOR: ensure fake satisfies all scenarios from spec
audio-device-detection / Fakeable service interface
Phase 2: Platform Channel — Android (PR 1 scope)
- 2.1 RED: write
test/servicios/servicio_dispositivo_audio_real_test.dart— stub MethodChannel, assertobtenerDispositivoActual()maps{id,type,name}toDispositivoAudio - 2.2 GREEN: implement
ServicioDispositivoAudioRealinlib/servicios/servicio_dispositivo_audio.dart— MethodChannelgetActiveDevice+ EventChannel stream; map type int →TipoDispositivo - 2.3 GREEN: add
pluriwave/audio_devicesEventChannel + MethodChannel inandroid/app/src/main/kotlin/.../MainActivity.kt—AudioDeviceCallbackon API ≥ 23; stable key derivation (bt_a2dp:<MAC>,wired_headset,builtin_speaker,usb_headset:<address>) - 2.4 REFACTOR: verify BT MAC key matches spec scenario
BT MAC key is stable across restarts
Phase 3: Platform Channel — iOS (PR 1 scope)
- 3.1 GREEN: create
ios/Runner/AudioDevicesPlugin.swift—AVAudioSession.routeChangeNotification→ EventChannel; deriveportType+uidkey; fallback toportType+portNameper speciOS uid fallback - 3.2 GREEN: register
AudioDevicesPlugininios/Runner/AppDelegate.swift - 3.3 RED: write unit test asserting toggle-disabled path returns empty stream (uses fake, not real channel)
Phase 4: EQ Service — Persistence Layer (PR 2 scope)
- 4.1 RED: extend
test/servicios/servicio_ecualizador_test.dart— assert new SP key CRUD:guardarPresetDispositivo,obtenerPresetDispositivo,guardarPresetMatriz,obtenerPresetMatriz,obtenerToggleMultiDispositivo - 4.2 GREEN: modify
lib/servicios/servicio_ecualizador.dart— add SP keyseq_multi_device_enabled_v1,eq_preset_por_dispositivo_v1,eq_presets_matriz_v1; add typed read/write methods for device and matrix maps - 4.3 GREEN: extend
ConfiguracionEcualizador(inservicio_ecualizador.dart) — addpresetsDispositivo,presetsMatriz,eqMultiDeviceEnabledfields; updatefromJson/toJson - 4.4 REFACTOR: validate storage size stays under 50 KB for 250 matrix entries per spec scenario
Phase 5: State Layer — 4-Level Resolution (PR 2 scope)
- 5.1 RED: write failing tests in
test/estado/estado_ecualizador_test.dart— 4-level resolution scenarios: matrix wins, falls back to station, falls back to device, falls back to global - 5.2 GREEN: modify
lib/estado/estado_ecualizador.dart— injectServicioDispositivoAudio; addpresetsDispositivo,presetsMatriz,_dispositivoActual,eqMultiDeviceEnabled; implement_resolverPreset()with 4-level hierarchy - 5.3 RED: write failing tests — device stream subscription:
FakeServicioDispositivoAudioemits device → assert_resolverPreset()re-runs andnotifyListeners()fires - 5.4 GREEN: subscribe to
ServicioDispositivoAudio.onDispositivoCambiadoinEstadoEcualizador.init()when toggle is enabled; callaplicarPresetActivo()on event - 5.5 RED: write failing tests — first-seen device copies current resolved preset; subsequent reconnect does not overwrite
- 5.6 GREEN: implement first-device initialization: if
presetsDispositivo[deviceId]is null, set it to current resolved preset and persist - 5.7 RED: write failing tests — toggle-off path uses only 2-level resolution (station → global); no device subscription established
- 5.8 GREEN: guard all device logic behind
eqMultiDeviceEnabledcheck inEstadoEcualizador - 5.9 REFACTOR: ensure
_recrearPlayer()/aplicarPresetActivo()path applies device-resolved preset, not stale_presetActual, per ADR-5
Phase 6: Export/Import v3 (PR 3 scope)
- 6.1 RED: extend
test/servicios/servicio_export_import_test.dart— v3 export includesschemaVersion: 3,presetsPorDispositivo,presetsMatriz; v3 round-trip preserves device presets; v2 import succeeds with empty device maps - 6.2 GREEN: modify
lib/servicios/servicio_export_import.dart— bumpschemaVersionto 3; serialize/deserializepresetsDispositivoandpresetsMatriz; handle missing fields from v1/v2 imports as empty maps - 6.3 REFACTOR: confirm v4-future guard (unknown
schemaVersionvalues do not crash)
Phase 7: Settings UI (PR 3 scope)
- 7.1 RED: write widget test
test/pantallas/pantalla_ajustes_test.dart— toggle off:_SeccionEcualizadorAvanzadoabsent; toggle on with devices: device list visible - 7.2 GREEN: add
_SeccionEcualizadorAvanzadowidget inlib/pantallas/pantalla_ajustes.dart—SwitchListTileforeqMultiDeviceEnabled;ListViewof known devices and their preset names, shown only when toggle is on and devices detected - 7.3 REFACTOR: confirm widget is hidden (not just invisible) when toggle is off per spec scenario
Phase 8: Integration Verification
- 8.1 Run
flutter test— all new and modified tests must pass - 8.2 Run
flutter analyze— zero new warnings or errors - 8.3 Run
dart format .— no unformatted files - 8.4 Manual smoke: toggle off → verify zero EQ behavior change vs. current release
- 8.5 Manual smoke: toggle on, connect BT device → verify EQ swap within 500ms