fix: el coche recuerda la ultima emisora y deja de publicar una sesion fantasma
Tres defectos preexistentes alrededor de la reanudacion en Android Auto. Ninguno es una regresion: el consumidor (la raiz `recent`) se añadio en septiembre y es lo que dejo el hueco a la vista. La ultima emisora solo la escribia el telefono La clave `ultima_emisora_v1` tenia como unico escritor a `EstadoRadio._persistirUltimaEmisora`, y `EstadoRadio` solo existe si hay arbol de widgets. El motor que arranca Android Auto es headless de verdad, asi que una sesion que ocurriera solo en el coche jamas actualizaba la clave y al reconectar se ofrecia la emisora de la ultima vez que se uso el movil. El handler recibe ahora sus puertos de lectura y escritura, con la misma forma que los del ecualizador y el contexto de salto, y escribe desde `_cambiarFuente`: el cuello de botella por el que pasan todas las rutas -- telefono, toque en el coche, voz, saltos, avance de cola y la propia reanudacion. Se ELIMINA el escritor del telefono en vez de sumar un segundo. Dos escritores independientes de la misma clave acaban divergiendo siempre; es exactamente lo que ya costo varias rondas con el flag del ecualizador. Las pistas locales quedan excluidas: un `content://` guardado como ultima emisora seria una fila de reanudacion que no resuelve a nada. play() sin fuente levantaba un servicio en primer plano vacio just_audio publica `playing:true` antes de comprobar si hay fuente, asi que un `play()` en frio no tocaba la plataforma pero si emitia ese estado sobre `processingState: idle`. audio_service entraba en estado de reproduccion mientras el estado nativo seguia en NONE: notificacion con boton de pausa, cero audio, sin titulo ni caratula, y un Future que no se completaba nunca. El coche enruta su tecla de play directamente ahi. Ahora `play()` sin fuente abierta restaura la ultima emisora por la ruta normal, y si no hay nada que restaurar no toca el reproductor ni publica nada. En frio no habia metadatos que enseñar El unico `mediaItem.add` util vivia dentro de `_cambiarFuente`, asi que en un motor recien arrancado el lado nativo nunca recibia metadatos. Se siembra el `mediaItem` de la emisora persistida sin cargar ni reproducir nada, con guarda antes y despues de la lectura de disco para no pisar una emisora ya sonando. `getMediaItem` resolvia solo contra el universo completo -- vacio en el motor del coche -- mientras `porUuid` si caia en las destacadas. El coche podia navegar una emisora destacada y luego no resolver su ficha. Ambos usan ahora la misma ruta. Suite completa: 1529 pasan, 2 omitidos. flutter analyze mantiene los 5 avisos preexistentes.
This commit is contained in:
@@ -7,6 +7,7 @@ import 'package:pluriwave/estado/estado_radio.dart';
|
||||
import 'package:pluriwave/modelos/emisora.dart';
|
||||
import 'package:pluriwave/modelos/grupo_favoritos.dart';
|
||||
import 'package:pluriwave/modelos/preset_ecualizador.dart';
|
||||
import 'package:pluriwave/servicios/emisoras_destacadas.dart';
|
||||
import 'package:pluriwave/servicios/navegacion_auto.dart';
|
||||
import 'package:pluriwave/servicios/servicio_audio.dart';
|
||||
import 'package:shared_preferences/shared_preferences.dart';
|
||||
@@ -792,21 +793,16 @@ void main() {
|
||||
'(reinicio) como emisoraActual DETENIDA -- no arranca audio, no '
|
||||
'reproduce, sólo queda seleccionada', () async {
|
||||
final emisora = emisoraDemo(uuid: 'last-1', nombre: 'Ultima FM');
|
||||
final estadoUno = EstadoRadio(
|
||||
esPremium: () => true,
|
||||
audio: FakeServicioAudio(),
|
||||
favoritos: FakeServicioFavoritos(),
|
||||
radio: FakeServicioRadio(),
|
||||
servicioEcualizador: FakeServicioEcualizador(),
|
||||
resolverArchivoCustom: _archivoCustomVacio,
|
||||
iniciarAutomaticamente: false,
|
||||
);
|
||||
await estadoUno.inicializar();
|
||||
await estadoUno.reproducir(emisora);
|
||||
await estadoUno.detenerReproduccion();
|
||||
// Lets the fire-and-forget persistence write settle before
|
||||
// spinning up the "restart" instance.
|
||||
await Future<void>.delayed(Duration.zero);
|
||||
// The record is now written by the audio handler's `_cambiarFuente`
|
||||
// (`GuardarUltimaEmisoraPersistida`), which is the SINGLE writer of
|
||||
// `ultima_emisora_v1` and the only one that also exists on the headless
|
||||
// Android Auto engine — `EstadoRadio` used to write it too and no
|
||||
// longer does. Seeded through that same production function here, so
|
||||
// this test covers what `EstadoRadio` actually owns (the RESTORE) with
|
||||
// a real payload instead of one a fake invented. The write itself is
|
||||
// covered end to end in
|
||||
// `test/servicios/servicio_audio_ultima_emisora_test.dart`.
|
||||
await guardarUltimaEmisoraPersistida(emisora);
|
||||
|
||||
final audioDos = FakeServicioAudio();
|
||||
final estadoDos = EstadoRadio(
|
||||
@@ -848,9 +844,18 @@ void main() {
|
||||
});
|
||||
|
||||
test('una emisora seleccionada desde el auto (fuera de reproducir()) '
|
||||
'también se recuerda para la próxima instancia', () async {
|
||||
'deja de estar ensombrecida por la seleccion previa del telefono',
|
||||
() async {
|
||||
// The PERSISTENCE half of this scenario moved to the handler, which is
|
||||
// the only writer that exists on a car-only session — it is covered by
|
||||
// «playFromMediaId desde el coche persiste ESA emisora» in
|
||||
// `test/servicios/servicio_audio_ultima_emisora_test.dart`. What
|
||||
// `EstadoRadio` still owns here, and what this test now pins, is the
|
||||
// shadowing fix: a car selection bypasses `reproducir()`, so without
|
||||
// the `estadoStream` listener `_emisoraSeleccionada` would keep
|
||||
// shadowing the car's station on the `emisoraActual` getter.
|
||||
final audio = _AudioControlado();
|
||||
final estadoUno = EstadoRadio(
|
||||
final estado = EstadoRadio(
|
||||
esPremium: () => true,
|
||||
audio: audio,
|
||||
favoritos: FakeServicioFavoritos(),
|
||||
@@ -859,7 +864,16 @@ void main() {
|
||||
resolverArchivoCustom: _archivoCustomVacio,
|
||||
iniciarAutomaticamente: false,
|
||||
);
|
||||
await estadoUno.inicializar();
|
||||
await estado.inicializar();
|
||||
final desdeElTelefono = emisoraDemo(
|
||||
uuid: 'phone-picked',
|
||||
nombre: 'Elegida en el telefono',
|
||||
);
|
||||
unawaited(estado.reproducir(desdeElTelefono));
|
||||
audio.completar(desdeElTelefono.uuid);
|
||||
await Future<void>.delayed(Duration.zero);
|
||||
expect(estado.emisoraActual?.uuid, desdeElTelefono.uuid);
|
||||
|
||||
final desdeCoche = emisoraDemo(
|
||||
uuid: 'auto-remembered',
|
||||
nombre: 'Recordada desde el auto',
|
||||
@@ -867,18 +881,14 @@ void main() {
|
||||
audio.seleccionarDesdeAuto(desdeCoche);
|
||||
await Future<void>.delayed(Duration.zero);
|
||||
|
||||
final estadoDos = EstadoRadio(
|
||||
esPremium: () => true,
|
||||
audio: FakeServicioAudio(),
|
||||
favoritos: FakeServicioFavoritos(),
|
||||
radio: FakeServicioRadio(),
|
||||
servicioEcualizador: FakeServicioEcualizador(),
|
||||
resolverArchivoCustom: _archivoCustomVacio,
|
||||
iniciarAutomaticamente: false,
|
||||
expect(
|
||||
estado.emisoraActual?.uuid,
|
||||
desdeCoche.uuid,
|
||||
reason:
|
||||
'the car changed the station without going through reproducir(); '
|
||||
'the phone UI must follow it instead of keeping the previous '
|
||||
'selection on screen',
|
||||
);
|
||||
await estadoDos.inicializar();
|
||||
|
||||
expect(estadoDos.emisoraActual?.uuid, desdeCoche.uuid);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user