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.
89 lines
6.9 KiB
Markdown
89 lines
6.9 KiB
Markdown
# 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` — assert `TipoDispositivo` enum values and `DispositivoAudio` equality
|
||
- [ ] 1.2 GREEN: create `lib/modelos/dispositivo_audio.dart` — `TipoDispositivo` enum + `DispositivoAudio` value 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 with `dispositivoActual`, `onDispositivoCambiado`, `obtenerDispositivoActual()`, `dispose()`
|
||
- [ ] 1.5 GREEN: add `FakeServicioDispositivoAudio` to `test/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, assert `obtenerDispositivoActual()` maps `{id,type,name}` to `DispositivoAudio`
|
||
- [ ] 2.2 GREEN: implement `ServicioDispositivoAudioReal` in `lib/servicios/servicio_dispositivo_audio.dart` — MethodChannel `getActiveDevice` + EventChannel stream; map type int → `TipoDispositivo`
|
||
- [ ] 2.3 GREEN: add `pluriwave/audio_devices` EventChannel + MethodChannel in `android/app/src/main/kotlin/.../MainActivity.kt` — `AudioDeviceCallback` on 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; derive `portType+uid` key; fallback to `portType+portName` per spec `iOS uid fallback`
|
||
- [ ] 3.2 GREEN: register `AudioDevicesPlugin` in `ios/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 keys `eq_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` (in `servicio_ecualizador.dart`) — add `presetsDispositivo`, `presetsMatriz`, `eqMultiDeviceEnabled` fields; update `fromJson`/`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` — inject `ServicioDispositivoAudio`; add `presetsDispositivo`, `presetsMatriz`, `_dispositivoActual`, `eqMultiDeviceEnabled`; implement `_resolverPreset()` with 4-level hierarchy
|
||
- [ ] 5.3 RED: write failing tests — device stream subscription: `FakeServicioDispositivoAudio` emits device → assert `_resolverPreset()` re-runs and `notifyListeners()` fires
|
||
- [ ] 5.4 GREEN: subscribe to `ServicioDispositivoAudio.onDispositivoCambiado` in `EstadoEcualizador.init()` when toggle is enabled; call `aplicarPresetActivo()` 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 `eqMultiDeviceEnabled` check in `EstadoEcualizador`
|
||
- [ ] 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 includes `schemaVersion: 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` — bump `schemaVersion` to 3; serialize/deserialize `presetsDispositivo` and `presetsMatriz`; handle missing fields from v1/v2 imports as empty maps
|
||
- [ ] 6.3 REFACTOR: confirm v4-future guard (unknown `schemaVersion` values do not crash)
|
||
|
||
## Phase 7: Settings UI (PR 3 scope)
|
||
|
||
- [ ] 7.1 RED: write widget test `test/pantallas/pantalla_ajustes_test.dart` — toggle off: `_SeccionEcualizadorAvanzado` absent; toggle on with devices: device list visible
|
||
- [ ] 7.2 GREEN: add `_SeccionEcualizadorAvanzado` widget in `lib/pantallas/pantalla_ajustes.dart` — `SwitchListTile` for `eqMultiDeviceEnabled`; `ListView` of 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
|