docs(openspec): add SDD artifact trail for recent alarm and EQ changes
Build & Deploy PluriWave / Análisis de código (push) Successful in 38s
Build & Deploy PluriWave / Build APK + AAB release (push) Successful in 2m30s

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.
This commit is contained in:
2026-07-04 12:42:11 +02:00
parent e5b6d8acb3
commit bccc5c48b8
64 changed files with 6020 additions and 0 deletions
@@ -0,0 +1,58 @@
# Exploration: Multi-Device Equalizer
## Current State
- Per-station EQ exists: `Map<String, PresetEcualizador>` in SharedPreferences keyed by station UUID
- No device detection or per-device EQ
- EQ is Android-only via `just_audio`'s `AndroidEqualizer`
- 3 existing platform channels in `MainActivity.kt` (visualizer, alarm, file_actions)
- `ServicioAudioSession` handles interruptions/becoming-noisy only — no device identity
## Key Findings
1. **EQ must be swapped in software** on device change — no native per-device EQ hook exists
2. **BT MAC available** via `AudioManager.getDevices()``AudioDeviceInfo.getAddress()` (API 23+). Does NOT require `BLUETOOTH_CONNECT` permission
3. **`_recrearPlayer()` resets AndroidEqualizer** on every source change — re-application must use device+station resolved preset
4. **iOS EQ is a no-op** but device tracking via `AVAudioSession.currentRoute` is possible
5. **No Flutter package covers this**`flutter_audio_output` is unmaintained, `audio_session` lacks device identity
## Recommended Approach
Custom platform channel `pluriwave/audio_devices` (Approach A):
- Android: `AudioManager.getDevices()` + `AudioDeviceCallback`
- iOS: `AVAudioSession.currentRoute` + `routeChangeNotification`
- Dart bridge: `ServicioDispositivoAudio` with `Stream<DispositivoAudio>` and `Future<List<DispositivoAudio>>`
## Resolution Hierarchy
```
1. presetsMatriz["stationUuid:deviceId"] ← station × device (most specific)
2. presetsEmisoraMap[stationUuid] ← station-only (existing)
3. presetsDispositivo[deviceId] ← device-only
4. presetPrincipal ← global default (existing)
```
## Persistence
New SharedPreferences keys (additive):
- `eq_multi_device_enabled_v1` → bool
- `eq_preset_por_dispositivo_v1` → JSON Map<deviceId, preset>
- `eq_presets_matriz_v1` → JSON Map<"stationUuid:deviceId", preset>
## Affected Files
| Area | Files |
|------|-------|
| Model | NEW `dispositivo_audio.dart` |
| Service | NEW `servicio_dispositivo_audio.dart`, extend `servicio_ecualizador.dart`, extend `servicio_audio.dart`, extend `servicio_export_import.dart` |
| State | Extend `estado_ecualizador.dart` |
| Native | Extend `MainActivity.kt`, NEW `AudioDevicesPlugin.swift` |
| UI | Extend `pantalla_ajustes.dart` |
| Tests | Extend existing + new test files |
## Risks
- Android minSdk must be ≥ 23 (likely already gated by AndroidEqualizer)
- iOS uid instability: use `portType+portName` as fallback key
- Backup v3 import must degrade gracefully in v2 builds
- Feature toggle OFF = zero regression invariant