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:
@@ -1133,6 +1133,194 @@ void main() {
|
||||
// WARNING-3: FakeServicioEcualizador preserves nombresDispositivos
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// bt-device-identity Phase 3: platform-name cache + duplicate-entry guard
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
group('EstadoEcualizador — platform-name cache (bt-device-identity Phase 3)', () {
|
||||
const stationUuid = 'station-uuid-bt-identity';
|
||||
|
||||
test('3.1 platform name is cached from a device-change event', () async {
|
||||
final fakeDispositivo = FakeServicioDispositivoAudio();
|
||||
final eq = EstadoEcualizador(
|
||||
audio: FakeServicioAudio(),
|
||||
servicio: FakeServicioEcualizador(eqMultiDeviceEnabled: true),
|
||||
dispositivoAudio: fakeDispositivo,
|
||||
emisoraActualUuid: () => stationUuid,
|
||||
);
|
||||
await eq.cargarPersistido();
|
||||
|
||||
fakeDispositivo.emitirDispositivo(
|
||||
const DispositivoAudio(
|
||||
id: 'bt_a2dp:AA:BB:CC:DD:EE:FF',
|
||||
tipo: TipoDispositivo.bluetoothA2dp,
|
||||
nombre: 'AirPods Pro',
|
||||
),
|
||||
);
|
||||
await Future<void>.delayed(const Duration(milliseconds: 50));
|
||||
|
||||
expect(
|
||||
eq.nombrePlataforma('bt_a2dp:AA:BB:CC:DD:EE:FF'),
|
||||
equals('AirPods Pro'),
|
||||
);
|
||||
eq.dispose();
|
||||
});
|
||||
|
||||
test(
|
||||
'3.4 composite-placeholder sentinel does not create device-list entry',
|
||||
() async {
|
||||
final fakeDispositivo = FakeServicioDispositivoAudio();
|
||||
final eq = EstadoEcualizador(
|
||||
audio: FakeServicioAudio(),
|
||||
servicio: FakeServicioEcualizador(eqMultiDeviceEnabled: true),
|
||||
dispositivoAudio: fakeDispositivo,
|
||||
emisoraActualUuid: () => stationUuid,
|
||||
);
|
||||
await eq.cargarPersistido();
|
||||
|
||||
fakeDispositivo.emitirDispositivo(
|
||||
const DispositivoAudio(
|
||||
id: 'bt_a2dp:name:AirPods-Pro',
|
||||
tipo: TipoDispositivo.bluetoothA2dp,
|
||||
nombre: 'AirPods Pro',
|
||||
),
|
||||
);
|
||||
await Future<void>.delayed(const Duration(milliseconds: 50));
|
||||
|
||||
expect(
|
||||
eq.presetsDispositivo.containsKey('bt_a2dp:name:AirPods-Pro'),
|
||||
isFalse,
|
||||
);
|
||||
expect(
|
||||
eq.nombrePlataforma('bt_a2dp:name:AirPods-Pro'),
|
||||
equals('AirPods Pro'),
|
||||
);
|
||||
eq.dispose();
|
||||
},
|
||||
);
|
||||
|
||||
test('3.6 multiple denied-permission devices do not collide', () async {
|
||||
final fakeDispositivo = FakeServicioDispositivoAudio();
|
||||
final eq = EstadoEcualizador(
|
||||
audio: FakeServicioAudio(),
|
||||
servicio: FakeServicioEcualizador(eqMultiDeviceEnabled: true),
|
||||
dispositivoAudio: fakeDispositivo,
|
||||
emisoraActualUuid: () => stationUuid,
|
||||
);
|
||||
await eq.cargarPersistido();
|
||||
final baseline = eq.presetsDispositivo.length;
|
||||
|
||||
fakeDispositivo.emitirDispositivo(
|
||||
const DispositivoAudio(
|
||||
id: 'bt_a2dp:name:AirPods-Pro',
|
||||
tipo: TipoDispositivo.bluetoothA2dp,
|
||||
nombre: 'AirPods Pro',
|
||||
),
|
||||
);
|
||||
await Future<void>.delayed(const Duration(milliseconds: 50));
|
||||
|
||||
fakeDispositivo.emitirDispositivo(
|
||||
const DispositivoAudio(
|
||||
id: 'bt_a2dp:name:Sony-WH-1000XM5',
|
||||
tipo: TipoDispositivo.bluetoothA2dp,
|
||||
nombre: 'Sony WH-1000XM5',
|
||||
),
|
||||
);
|
||||
await Future<void>.delayed(const Duration(milliseconds: 50));
|
||||
|
||||
expect(
|
||||
eq.nombrePlataforma('bt_a2dp:name:AirPods-Pro'),
|
||||
equals('AirPods Pro'),
|
||||
);
|
||||
expect(
|
||||
eq.nombrePlataforma('bt_a2dp:name:Sony-WH-1000XM5'),
|
||||
equals('Sony WH-1000XM5'),
|
||||
);
|
||||
expect(eq.presetsDispositivo.length, equals(baseline));
|
||||
eq.dispose();
|
||||
});
|
||||
|
||||
test(
|
||||
'3.7 repeated event for known id is a no-op on preset creation '
|
||||
'(regression-lock)',
|
||||
() async {
|
||||
final fakeDispositivo = FakeServicioDispositivoAudio();
|
||||
const device = DispositivoAudio(
|
||||
id: 'bt_a2dp:AA:BB:CC:DD:EE:FF',
|
||||
tipo: TipoDispositivo.bluetoothA2dp,
|
||||
nombre: 'AirPods Pro',
|
||||
);
|
||||
final eq = EstadoEcualizador(
|
||||
audio: FakeServicioAudio(),
|
||||
servicio: FakeServicioEcualizador(eqMultiDeviceEnabled: true),
|
||||
dispositivoAudio: fakeDispositivo,
|
||||
emisoraActualUuid: () => stationUuid,
|
||||
);
|
||||
await eq.cargarPersistido();
|
||||
|
||||
fakeDispositivo.emitirDispositivo(device);
|
||||
await Future<void>.delayed(const Duration(milliseconds: 50));
|
||||
final presetTrasPrimerEvento = eq.presetsDispositivo[device.id];
|
||||
final cantidadTrasPrimerEvento = eq.presetsDispositivo.length;
|
||||
expect(presetTrasPrimerEvento, isNotNull);
|
||||
|
||||
fakeDispositivo.emitirDispositivo(device);
|
||||
await Future<void>.delayed(const Duration(milliseconds: 50));
|
||||
|
||||
expect(
|
||||
eq.presetsDispositivo[device.id],
|
||||
equals(presetTrasPrimerEvento),
|
||||
);
|
||||
expect(eq.presetsDispositivo.length, equals(cantidadTrasPrimerEvento));
|
||||
eq.dispose();
|
||||
},
|
||||
);
|
||||
|
||||
test(
|
||||
'3.8 transient non-BT id during pairing handshake does not corrupt '
|
||||
'the BT entry (regression-lock)',
|
||||
() async {
|
||||
final fakeDispositivo = FakeServicioDispositivoAudio();
|
||||
const btDevice = DispositivoAudio(
|
||||
id: 'bt_a2dp:AA:BB:CC:DD:EE:FF',
|
||||
tipo: TipoDispositivo.bluetoothA2dp,
|
||||
nombre: 'AirPods Pro',
|
||||
);
|
||||
const builtinSpeaker = DispositivoAudio(
|
||||
id: 'builtin_speaker',
|
||||
tipo: TipoDispositivo.altavozInterno,
|
||||
nombre: 'Speaker',
|
||||
);
|
||||
final eq = EstadoEcualizador(
|
||||
audio: FakeServicioAudio(),
|
||||
servicio: FakeServicioEcualizador(
|
||||
principal: PresetEcualizador.rock,
|
||||
eqMultiDeviceEnabled: true,
|
||||
),
|
||||
dispositivoAudio: fakeDispositivo,
|
||||
emisoraActualUuid: () => stationUuid,
|
||||
);
|
||||
await eq.cargarPersistido();
|
||||
|
||||
// Transient builtin_speaker report mid-handshake.
|
||||
fakeDispositivo.emitirDispositivo(builtinSpeaker);
|
||||
await Future<void>.delayed(const Duration(milliseconds: 50));
|
||||
|
||||
// Final BT event once the A2DP profile attaches.
|
||||
fakeDispositivo.emitirDispositivo(btDevice);
|
||||
await Future<void>.delayed(const Duration(milliseconds: 50));
|
||||
|
||||
expect(eq.presetsDispositivo.containsKey(btDevice.id), isTrue);
|
||||
expect(eq.presetsDispositivo.containsKey('builtin_speaker'), isFalse);
|
||||
expect(
|
||||
eq.presetsDispositivo[btDevice.id],
|
||||
equals(PresetEcualizador.rock),
|
||||
);
|
||||
eq.dispose();
|
||||
},
|
||||
);
|
||||
});
|
||||
|
||||
group('FakeServicioEcualizador — nombresDispositivos preservation (WARNING-3)', () {
|
||||
test(
|
||||
'guardarPrincipal preserves nombresDispositivos',
|
||||
|
||||
Reference in New Issue
Block a user