In-progress artifact sets from the current SDD cycles: exploration, proposal, spec, design, tasks, and verify reports as produced so far. Also drops a leftover working copy of eq-device-disconnect-revert whose contents were already committed under changes/archive/.
72 lines
5.4 KiB
Markdown
72 lines
5.4 KiB
Markdown
# Proposal: Canonical Bluetooth Device Identity
|
|
|
|
## Intent
|
|
|
|
Renaming a Bluetooth audio device then re-pairing creates a duplicate device in the multi-device EQ list, and devices display raw ids instead of their own Bluetooth name. Two compounding bugs cause this: (1) on Android 12+ the app never requests `BLUETOOTH_CONNECT`, so `getAddress()` returns the placeholder `02:00:00:00:00:00` for every BT device and they all collapse onto one id `bt_a2dp:02:00:00:00:00:00`; (2) any unseen `deviceId` spawns a new entry, and the rename overlay is keyed separately so a rename never transfers. Fixing identity now unblocks reliable per-device EQ before more devices accumulate corrupted keys.
|
|
|
|
## Scope
|
|
|
|
### In Scope
|
|
- Declare `BLUETOOTH_CONNECT` in manifest + runtime request flow, mirroring `POST_NOTIFICATIONS`, triggered on opening the device-management UI.
|
|
- Placeholder detection in `deviceToMap()`: treat `02:00:00:00:00:00` as absent → fall back to `type+productName` composite id (id format unchanged).
|
|
- Cache per-device platform names in `EstadoEcualizador`; fix both `nombreVisible()` call sites so real BT names feed the fallback chain.
|
|
- One-time destructive migration dropping placeholder-keyed entries from the three EQ persistence keys; keep stable-MAC entries.
|
|
- New user-visible strings (permission rationale, migration notice) in all 13 locales.
|
|
|
|
### Out of Scope
|
|
- iOS changes — `stableKey()` already implements uid-primary / type+name fallback.
|
|
- Reworking the composite-key format or the 4-level EQ resolution hierarchy.
|
|
- Recovering data lost to the placeholder collision (unrecoverable by design).
|
|
- Auto-switch / autoswitch-UX behavior (separate change).
|
|
|
|
## Capabilities
|
|
|
|
> No standing `openspec/specs/*` exists for audio devices yet (multi-device-eq shipped as an archived change). These are new capability specs.
|
|
|
|
### New Capabilities
|
|
- `bt-device-identity`: canonical MAC identity, placeholder detection + composite fallback, `BLUETOOTH_CONNECT` runtime flow, per-device name caching, and placeholder-key migration.
|
|
|
|
### Modified Capabilities
|
|
- None.
|
|
|
|
## Approach
|
|
|
|
Restore a stable BT MAC by acquiring `BLUETOOTH_CONNECT` at the point of intent (device-management UI), and make the id pipeline resilient when it is still absent. Kotlin `deviceToMap()` gains an explicit placeholder guard so `02:00:00:00:00:00` is treated as blank, producing a deterministic `type:productName` id instead of a colliding one. `EstadoEcualizador` caches each device's reported platform name so `nombreVisible()` prefers the device's own Bluetooth name over the raw id. A guarded one-time migration purges only placeholder-keyed entries from the three SharedPreferences maps and surfaces a rename-again notice to affected users. MAC stays canonical wherever available; the colon-delimited key format is preserved intact.
|
|
|
|
## Affected Areas
|
|
|
|
| Area | Impact | Description |
|
|
|------|--------|-------------|
|
|
| `android/app/src/main/AndroidManifest.xml` | Modified | Add `BLUETOOTH_CONNECT` (maxSdk-scoped as needed). |
|
|
| `android/app/.../MainActivity.kt` `deviceToMap()` | Modified | Placeholder guard → composite fallback; BT runtime-permission flow (mirror `POST_NOTIFICATIONS`). |
|
|
| `lib/estado/estado_ecualizador.dart` | Modified | Cache per-device platform names; feed `nombreVisible()`; stop spawning entries for placeholder ids. |
|
|
| `lib/pantallas/pantalla_ajustes.dart` (L769, L846) | Modified | Pass real platform name to `nombreVisible()` instead of `''`. |
|
|
| `lib/servicios/servicio_ecualizador.dart` (keys L42-44) | Modified | Migration purging placeholder-keyed entries from 3 maps. |
|
|
| `lib/l10n/*.arb` (13 files) | Modified | Permission-rationale + migration-notice keys. |
|
|
|
|
## Risks
|
|
|
|
| Risk | Likelihood | Mitigation |
|
|
|------|------------|------------|
|
|
| Migration deletes legitimate data | Low | Match ONLY exact `bt_a2dp:02:00:00:00:00:00`; keep all other keys. |
|
|
| BT permission denied → unstable ids | Med | Composite `type:productName` fallback keeps app functional; re-prompt on next UI open. |
|
|
| No BT-permission precedent in codebase | Med | Copy proven `POST_NOTIFICATIONS`/`RECORD_AUDIO` flow verbatim. |
|
|
| Composite-key colon-delimiter safety broken | Low | Id format unchanged; single leading `type:` segment preserved (ADR-3, multi-device-eq). |
|
|
|
|
## Rollback Plan
|
|
|
|
Changes are additive/guarded and behind a versioned migration. To revert: restore prior `MainActivity.kt`, `estado_ecualizador.dart`, `pantalla_ajustes.dart`, remove the `BLUETOOTH_CONNECT` declaration and the migration + l10n keys. The migration is one-shot and gated by a run-once flag; already-deleted placeholder-collision entries were unrecoverable regardless, so revert restores behavior, not lost data. No schema/version bump of the persistence keys, so downgrade is clean.
|
|
|
|
## Dependencies
|
|
|
|
- `permission_handler` capability already used for `POST_NOTIFICATIONS`/`RECORD_AUDIO` (reuse existing pattern; no new package expected).
|
|
|
|
## Success Criteria
|
|
|
|
- [ ] With `BLUETOOTH_CONNECT` granted, distinct BT devices produce distinct MAC-based ids (no `02:00:00:00:00:00`).
|
|
- [ ] Renaming a device then re-pairing does NOT create a duplicate entry.
|
|
- [ ] The device list shows each device's own Bluetooth name by default; custom renames still override.
|
|
- [ ] Migration removes only placeholder-keyed entries; stable-MAC EQ/preset/name data is retained.
|
|
- [ ] Permission rationale + migration notice render correctly in all 13 locales.
|
|
- [ ] `flutter analyze` clean; existing `servicio_ecualizador_test.dart` and EQ state tests pass.
|