docs(openspec): add SDD artifact trail for recent alarm and EQ changes
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:
@@ -0,0 +1,174 @@
|
||||
# Spec: EQ Device Auto-Switch UX
|
||||
|
||||
## Delta for multi-device-eq
|
||||
|
||||
### MODIFIED Requirements
|
||||
|
||||
#### Requirement: Startup device initialization
|
||||
|
||||
When `eqMultiDeviceEnabled` is `true`, the system MUST query the current audio device via `obtenerDispositivoActual()` immediately after `_configurarSuscripcionDispositivo()` runs inside `cargarPersistido()`. The result MUST seed `_dispositivoActualId`, bootstrap a device preset if this is the first time the device is seen, and trigger `_resolverPresetActivo()` so the correct preset is applied before the first playback event.
|
||||
|
||||
(Previously: `_dispositivoActualId` was null at startup; preset resolution required a reconnect event.)
|
||||
|
||||
##### Scenario: correct preset applied at startup — device known
|
||||
|
||||
- GIVEN `eqMultiDeviceEnabled` is `true`
|
||||
- AND a preset for the current device ID already exists in `presetsDispositivo`
|
||||
- WHEN the app starts and `cargarPersistido()` completes
|
||||
- THEN `_dispositivoActualId` MUST be set to the current device ID
|
||||
- AND the device-resolved preset MUST be applied without any user action
|
||||
|
||||
##### Scenario: first-seen device bootstrapped at startup
|
||||
|
||||
- GIVEN `eqMultiDeviceEnabled` is `true`
|
||||
- AND the current device has no existing entry in `presetsDispositivo`
|
||||
- WHEN `cargarPersistido()` runs
|
||||
- THEN the device MUST be bootstrapped with a copy of the currently resolved preset
|
||||
- AND `_dispositivoActualId` MUST be set to that device ID
|
||||
|
||||
##### Scenario: `obtenerDispositivoActual()` fails gracefully
|
||||
|
||||
- GIVEN `eqMultiDeviceEnabled` is `true`
|
||||
- AND the platform channel call throws or returns null
|
||||
- WHEN `cargarPersistido()` runs
|
||||
- THEN `_dispositivoActualId` MUST fall back to `"builtin_speaker"`
|
||||
- AND no exception MUST propagate to callers
|
||||
|
||||
##### Scenario: toggle off — no device query at startup
|
||||
|
||||
- GIVEN `eqMultiDeviceEnabled` is `false`
|
||||
- WHEN the app starts
|
||||
- THEN `obtenerDispositivoActual()` MUST NOT be called
|
||||
- AND EQ behavior MUST be identical to the pre-feature baseline
|
||||
|
||||
---
|
||||
|
||||
## New Capability: eq-device-rename
|
||||
|
||||
### Purpose
|
||||
|
||||
Persistent custom names for audio devices, a connection status indicator per device row, and an edit modal combining rename input with embedded EQ editing. All gated by `eqMultiDeviceEnabled`.
|
||||
|
||||
### Requirements
|
||||
|
||||
#### Requirement: Device name persistence
|
||||
|
||||
The system MUST store a `Map<String, String>` of `deviceId → customName` under the SharedPreferences key `eq_nombres_dispositivos_v1`. `EstadoEcualizador` MUST expose `renombrarDispositivo(id, nombre)` to write an entry and `obtenerNombreDispositivo(id)` to read one. Both operations MUST be reflected in `ServicioEcualizador`.
|
||||
|
||||
##### Scenario: rename persists across restarts
|
||||
|
||||
- GIVEN the user renames device `"bt_a2dp:AA:BB"` to `"Living Room BT"`
|
||||
- WHEN the app is killed and relaunched
|
||||
- THEN `obtenerNombreDispositivo("bt_a2dp:AA:BB")` MUST return `"Living Room BT"`
|
||||
|
||||
##### Scenario: rename is immediately readable
|
||||
|
||||
- GIVEN no custom name exists for a device
|
||||
- WHEN `renombrarDispositivo("bt_a2dp:AA:BB", "Office Speaker")` is called
|
||||
- THEN `obtenerNombreDispositivo("bt_a2dp:AA:BB")` MUST immediately return `"Office Speaker"`
|
||||
|
||||
##### Scenario: empty name is not persisted
|
||||
|
||||
- GIVEN a device already has the custom name `"My Headset"`
|
||||
- WHEN `renombrarDispositivo(id, "")` is called
|
||||
- THEN the existing name MUST be preserved (empty names MUST NOT overwrite)
|
||||
|
||||
##### Scenario: feature toggle off — no name storage
|
||||
|
||||
- GIVEN `eqMultiDeviceEnabled` is `false`
|
||||
- WHEN any rename API is called
|
||||
- THEN the operation MUST be a no-op and MUST NOT write to SharedPreferences
|
||||
|
||||
---
|
||||
|
||||
#### Requirement: Display name fallback chain
|
||||
|
||||
When displaying a device name anywhere in the UI, the system MUST resolve it using the following priority order, stopping at the first non-null, non-empty value:
|
||||
|
||||
1. Custom name from `eq_nombres_dispositivos_v1`
|
||||
2. Platform name (`DispositivoAudio.nombre`)
|
||||
3. Raw device ID
|
||||
|
||||
##### Scenario: custom name wins over platform name
|
||||
|
||||
- GIVEN device has custom name `"Studio Monitors"` and platform name `"USB Audio"`
|
||||
- WHEN the device row is rendered
|
||||
- THEN `"Studio Monitors"` MUST be displayed
|
||||
|
||||
##### Scenario: platform name used when no custom name
|
||||
|
||||
- GIVEN no custom name exists for device with platform name `"Sony WH-1000XM5"`
|
||||
- WHEN the device row is rendered
|
||||
- THEN `"Sony WH-1000XM5"` MUST be displayed
|
||||
|
||||
##### Scenario: device ID used as last resort
|
||||
|
||||
- GIVEN no custom name and platform name is null or empty
|
||||
- WHEN the device row is rendered
|
||||
- THEN the raw device ID MUST be displayed (never an empty or null label)
|
||||
|
||||
---
|
||||
|
||||
#### Requirement: Connection status indicator
|
||||
|
||||
The Settings device list MUST display a visible connection indicator on each device row. The indicator MUST be green when the device ID matches `EstadoEcualizador.dispositivoActualId`, and absent (or neutral) otherwise.
|
||||
|
||||
##### Scenario: active device shows green indicator
|
||||
|
||||
- GIVEN device `"bt_a2dp:AA:BB"` is the current active device
|
||||
- WHEN the device list is rendered in Settings
|
||||
- THEN that device row MUST show a green indicator
|
||||
- AND no other row MUST show a green indicator simultaneously
|
||||
|
||||
##### Scenario: indicator updates after device change
|
||||
|
||||
- GIVEN device `"builtin_speaker"` was active and showing green
|
||||
- WHEN a BT device connects and becomes active
|
||||
- THEN the green indicator MUST move to the BT device row
|
||||
- AND the built-in speaker row MUST no longer show it
|
||||
|
||||
##### Scenario: no indicator when toggle is off
|
||||
|
||||
- GIVEN `eqMultiDeviceEnabled` is `false`
|
||||
- WHEN the Settings screen is viewed
|
||||
- THEN no connection indicator MUST appear (device list section is hidden)
|
||||
|
||||
---
|
||||
|
||||
#### Requirement: Device edit modal
|
||||
|
||||
Tapping a device row in Settings MUST open a bottom sheet containing a rename TextField pre-filled with the device's resolved display name, and an embedded `EcualizadorWidget` scoped to that device's preset. The modal MUST use `showDragHandle: true` and `isScrollControlled: true`.
|
||||
|
||||
##### Scenario: modal opens on device row tap
|
||||
|
||||
- GIVEN the device list is visible in Settings
|
||||
- WHEN the user taps a device row
|
||||
- THEN a bottom sheet MUST appear
|
||||
- AND it MUST contain a TextField pre-filled with the current display name (resolved via fallback chain)
|
||||
- AND it MUST contain an `EcualizadorWidget` showing that device's preset
|
||||
|
||||
##### Scenario: rename confirmed in modal persists
|
||||
|
||||
- GIVEN the edit modal is open for device `"bt_a2dp:AA:BB"`
|
||||
- WHEN the user edits the name field to `"Bedroom Speaker"` and confirms
|
||||
- THEN `renombrarDispositivo("bt_a2dp:AA:BB", "Bedroom Speaker")` MUST be called
|
||||
- AND the device row MUST reflect the new name after the modal closes
|
||||
|
||||
##### Scenario: EQ changes in modal apply to device preset
|
||||
|
||||
- GIVEN the edit modal is open for device `"bt_a2dp:AA:BB"`
|
||||
- WHEN the user adjusts a band in the embedded `EcualizadorWidget`
|
||||
- THEN the change MUST be saved to `presetsDispositivo["bt_a2dp:AA:BB"]`
|
||||
- AND the active EQ MUST update immediately if that device is currently active
|
||||
|
||||
##### Scenario: modal dismiss without save leaves state unchanged
|
||||
|
||||
- GIVEN the edit modal is open and no changes have been confirmed
|
||||
- WHEN the user dismisses the sheet by dragging down
|
||||
- THEN the device name and preset MUST remain unchanged
|
||||
|
||||
##### Scenario: modal is scrollable to prevent EQ widget clipping
|
||||
|
||||
- GIVEN the bottom sheet is open on a small screen
|
||||
- WHEN the user scrolls within the sheet
|
||||
- THEN the full `EcualizadorWidget` MUST be reachable without clipping
|
||||
Reference in New Issue
Block a user