fix(devices): cache platform device names, dedupe placeholder ids, purge collided EQ entries
Dart half of bt-device-identity. EstadoEcualizador now caches each device's platform-reported name in memory so the settings screen shows the device's own Bluetooth name instead of its raw id when no custom rename exists, and skips auto-creating preset entries for the composite-placeholder sentinel. Enabling multi-device EQ triggers the Bluetooth permission request through the new channel contract. A flag-guarded one-time migration purges only entries keyed by the exact literal placeholder id from the three per-device preference maps, since those collided entries cannot be attributed to a device. Work unit 2/2 of bt-device-identity (Dart state + migration).
This commit is contained in:
@@ -26,6 +26,12 @@ import '../servicios/servicio_ecualizador.dart';
|
||||
/// When the toggle is false, resolution falls back to the original 2-level
|
||||
/// hierarchy (station → global) — zero behavioral change vs. prior releases.
|
||||
class EstadoEcualizador extends ChangeNotifier {
|
||||
/// Composite-placeholder id prefix (bt-device-identity ADR-6): marks a
|
||||
/// device whose real Bluetooth MAC is not yet known (BLUETOOTH_CONNECT
|
||||
/// denied or unresolved). Devices with this prefix still update the
|
||||
/// platform-name cache but never auto-create a `presetsDispositivo` entry.
|
||||
static const _prefijoPlaceholderCompuesto = 'bt_a2dp:name:';
|
||||
|
||||
EstadoEcualizador({
|
||||
required this.audio,
|
||||
ServicioEcualizador? servicio,
|
||||
@@ -54,6 +60,13 @@ class EstadoEcualizador extends ChangeNotifier {
|
||||
/// Custom display names for devices: deviceId → custom name.
|
||||
final Map<String, String> _nombresDispositivos = {};
|
||||
|
||||
/// Last-seen platform (Bluetooth/productName) name per deviceId.
|
||||
///
|
||||
/// In-memory only (bt-device-identity ADR-4) — NOT persisted. Devices
|
||||
/// re-report their name on every enumeration, so this cache self-heals
|
||||
/// every session without needing a SharedPreferences key or migration.
|
||||
final Map<String, String> _nombresPlataforma = {};
|
||||
|
||||
PresetEcualizador _presetPrincipal = PresetEcualizador.flat;
|
||||
PresetEcualizador _presetActual = PresetEcualizador.flat;
|
||||
bool _activo = true;
|
||||
@@ -210,15 +223,27 @@ class EstadoEcualizador extends ChangeNotifier {
|
||||
Future<void> _onDispositivoCambiado(DispositivoAudio dispositivo) async {
|
||||
if (!_eqMultiDeviceEnabled) return;
|
||||
|
||||
// Cache updates on every event regardless of whether a preset entry
|
||||
// gets created below (bt-device-identity ADR-4).
|
||||
_nombresPlataforma[dispositivo.id] = dispositivo.nombre;
|
||||
_dispositivoActualId = dispositivo.id;
|
||||
|
||||
// First-seen device: copy the current resolved preset as its starting
|
||||
// point. builtin_speaker is excluded: it must always fall through to
|
||||
// the hierarchy (L4 global) instead of being pinned by a forced L3
|
||||
// device-level copy, otherwise a later global-preset change would be
|
||||
// masked by this stale entry for the base device.
|
||||
// masked by this stale entry for the base device. Composite-placeholder
|
||||
// ids are also excluded (bt-device-identity ADR-6): they are transient
|
||||
// fallback ids for a device whose real MAC is not yet known, so
|
||||
// persisting a preset entry for them would create dead noise that never
|
||||
// resolves to the eventual real-MAC id.
|
||||
final esBase = dispositivo.tipo == TipoDispositivo.altavozInterno;
|
||||
if (!esBase && !_presetsDispositivo.containsKey(dispositivo.id)) {
|
||||
final esPlaceholderCompuesto = dispositivo.id.startsWith(
|
||||
_prefijoPlaceholderCompuesto,
|
||||
);
|
||||
if (!esBase &&
|
||||
!esPlaceholderCompuesto &&
|
||||
!_presetsDispositivo.containsKey(dispositivo.id)) {
|
||||
final presetBase = _resolverPresetActivo();
|
||||
_presetsDispositivo[dispositivo.id] = presetBase;
|
||||
await servicio.guardarPresetDispositivo(dispositivo.id, presetBase);
|
||||
@@ -252,6 +277,21 @@ class EstadoEcualizador extends ChangeNotifier {
|
||||
if (notificar) notifyListeners();
|
||||
}
|
||||
|
||||
/// Requests `BLUETOOTH_CONNECT` (API 31+) at the point the
|
||||
/// device-management UI is opened (bt-device-identity ADR-1).
|
||||
///
|
||||
/// Thin passthrough to the injected device service so callers don't need
|
||||
/// [ServicioDispositivoAudio] wired as its own top-level `Provider` (Task
|
||||
/// 4.8 — `ServicioDispositivoAudio` is only ever constructed inside
|
||||
/// `EstadoRadio` today, not exposed via the provider tree; routing through
|
||||
/// here avoids adding wiring the tree doesn't already have). Returns false
|
||||
/// when no device service is injected.
|
||||
Future<bool> solicitarPermisoBluetooth() async {
|
||||
final svc = _dispositivoAudio;
|
||||
if (svc == null) return false;
|
||||
return svc.solicitarPermisoBluetooth();
|
||||
}
|
||||
|
||||
Future<void> cambiarPresetPrincipal(
|
||||
PresetEcualizador preset, {
|
||||
bool notificar = true,
|
||||
@@ -353,6 +393,11 @@ class EstadoEcualizador extends ChangeNotifier {
|
||||
String obtenerNombreDispositivo(String deviceId) =>
|
||||
_nombresDispositivos[deviceId] ?? '';
|
||||
|
||||
/// Returns the last-seen platform name for [deviceId], or an empty string
|
||||
/// if none has been observed yet (bt-device-identity ADR-4).
|
||||
String nombrePlataforma(String deviceId) =>
|
||||
_nombresPlataforma[deviceId] ?? '';
|
||||
|
||||
/// Resolves the display name for [deviceId] using the fallback chain:
|
||||
/// custom name → [platformName] → raw [deviceId].
|
||||
String nombreVisible(String deviceId, String platformName) {
|
||||
|
||||
Reference in New Issue
Block a user