feat(eq): android auto custom equalizer and robust device detection
Build & Deploy PluriWave / Análisis de código (push) Successful in 26s
Build & Deploy PluriWave / Build APK + AAB release (push) Successful in 2m16s

- MainActivity: onListen re-emits the current active device and registers
  the audio device callback idempotently, so recreated activities resync
  instead of freezing the active-device id on a disconnected device.
- servicio_dispositivo_audio: resubscribir() re-opens the event channel;
  estado_ecualizador exposes refrescarDispositivoActual() with an
  in-flight guard, invoked on app resume and when opening advanced EQ
  options, clearing stale green-dot device selections.
- navegacion_auto/servicio_audio: new 'Personalizado' browse tree in
  Android Auto (5 band folders, 13 gain steps each) applied live via
  setBanda; preset and gain taps persist at device level when
  multi-device EQ is active and respect station/matrix overrides,
  with apply-before-persist ordering and children-changed notifications.
- l10n: regenerate stale generated localizations; add rxdart as direct
  dependency for the subscribeToChildren override.
This commit is contained in:
Javier Bautista Fernández
2026-07-22 10:26:02 +02:00
parent 3473034b58
commit 163ff69f7a
29 changed files with 1883 additions and 42 deletions
@@ -146,5 +146,61 @@ void main() {
expect(granted, isFalse);
},
);
// ── Fix: robust active-device detection (stale green dot) ────────────────
test(
'resubscribir sends cancel+listen to the event channel and forwards '
'the device the fresh onListen emits',
() async {
var listens = 0;
var cancels = 0;
TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger
.setMockStreamHandler(
const EventChannel(methodChannelName),
MockStreamHandler.inline(
onListen: (arguments, events) {
listens++;
// Mirrors the native onListen immediate resync: every
// (re)subscription receives the current active device.
events.success({
'id': 'bt_a2dp:AA:BB:CC:DD:EE:FF',
'type': 8,
'name': 'My BT Device',
});
},
onCancel: (arguments) => cancels++,
),
);
addTearDown(() {
TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger
.setMockStreamHandler(
const EventChannel(methodChannelName),
null,
);
});
final servicioLocal = ServicioDispositivoAudioReal();
addTearDown(servicioLocal.dispose);
// The constructor's initial subscription delivers the first resync.
final primero = await servicioLocal.onDispositivoCambiado.first;
expect(primero.id, 'bt_a2dp:AA:BB:CC:DD:EE:FF');
expect(listens, 1);
expect(cancels, 0);
final segundoFuturo = servicioLocal.onDispositivoCambiado.first;
await servicioLocal.resubscribir();
final segundo = await segundoFuturo;
expect(cancels, 1);
expect(listens, 2);
expect(segundo.tipo, TipoDispositivo.bluetoothA2dp);
expect(
servicioLocal.dispositivoActual?.id,
'bt_a2dp:AA:BB:CC:DD:EE:FF',
);
},
);
});
}