Files
pluriwave/openspec/changes/eq-device-autoswitch-ux/spec.md
T
FreeTLab bccc5c48b8
Build & Deploy PluriWave / Análisis de código (push) Successful in 38s
Build & Deploy PluriWave / Build APK + AAB release (push) Successful in 2m30s
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.
2026-07-04 12:42:11 +02:00

7.2 KiB

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