fix: cumplir las guias de calidad de Android Auto y localizar el arbol del coche
Google Play devolvio "Approved with Issues" en el codigo 157: "clicking on stop button makes the entire app useless", citado contra las Android for Cars App Quality Guidelines. La causa no era el boton de parar. Maquina de estados del transporte _cambiarFuente publicaba mediaItem y loading ANTES de su primer await y solo comprobaba su revision despues de que _recrearPlayer retornase. Los cambios de fuente se encolan incrementando la revision al encolar, no al ejecutar, asi que tocar una emisora, tocar otra antes de que cargue y pulsar Stop dejaba que las entradas obsoletas reescribieran loading sobre el idle que stop() acababa de publicar. Estado final: loading para siempre sobre una sesion que audio_service ya habia desactivado. Ahora la guarda de revision es la primera sentencia del metodo. pause() no invalidaba una carga en vuelo, asi que la emisora arrancaba igual despues de pulsar pausa; se revalida la intencion antes de llamar a play(). Se anade un suelo de estado que cierra cualquier loading o buffering sin carga viva, exento cuando el reproductor ya entrego audio y solo esta rebufferando, para no convertir un tunel en un error. El presupuesto hasta el primer mensaje baja a menos de diez segundos y los reintentos ya no borran el mensaje visible. Tier gratuito en el coche El arbol devolvia una unica fila no reproducible para cualquier carpeta cuando no habia premium, y un revisor con instalacion limpia siempre es tier gratuito. Ademas skipToNext, skipToPrevious, playFromSearch y playFromMediaId retornaban en silencio. La raiz gratuita pasa a ofrecer una sola carpeta con emisoras reales y reproducibles, compiladas en el binario para que existan en frio, y la puerta de entitlement acota contenido en vez de bloquear acciones. Se elimina la fila "Funcion Premium". Una consulta de voz vacia arranca la ultima emisora, que fallaba tambien a los clientes de pago. Localizacion El locale del handler solo lo fijaba un widget que el motor headless nunca construye, asi que todo error del coche salia en castellano. Se resuelve desde el locale de plataforma. Se traducen las once etiquetas del arbol que estaban a fuego y se retira la convencion que lo justificaba. Un test nuevo falla si vuelve a aparecer texto visible fuera del sistema de traduccion. Suite completa: 1455 pasan, 2 omitidos. Los mecanismos se verificaron por mutacion: borrar cada uno pone la suite en rojo. flutter analyze mantiene los 5 avisos preexistentes.
This commit is contained in:
@@ -2,6 +2,8 @@ import 'package:flutter_test/flutter_test.dart';
|
||||
import 'package:just_audio/just_audio.dart' show PlayerState, ProcessingState;
|
||||
import 'package:pluriwave/servicios/servicio_audio.dart';
|
||||
|
||||
import '../helpers/handlers_audio.dart';
|
||||
|
||||
/// eq-estado-unico — the equalizer's on/off flag gets a SINGLE owner.
|
||||
///
|
||||
/// Reported bug: «alguna emisora parece que esta con la ecualizacion activada
|
||||
@@ -28,6 +30,8 @@ import 'package:pluriwave/servicios/servicio_audio.dart';
|
||||
void main() {
|
||||
TestWidgetsFlutterBinding.ensureInitialized();
|
||||
|
||||
final crearHandler = registrarHandlersLiberables();
|
||||
|
||||
group('estadoEqInicial (A — seed the handler from disk on every engine)', () {
|
||||
test('adopts the persisted value when there is one', () {
|
||||
expect(estadoEqInicial(persistido: false), isFalse);
|
||||
@@ -46,7 +50,7 @@ void main() {
|
||||
group('registrarHandler (A — seeding)', () {
|
||||
test('consults the injected read port exactly once and seeds the handler '
|
||||
'with the persisted value', () async {
|
||||
final handler = PluriWaveAudioHandler();
|
||||
final handler = crearHandler();
|
||||
var lecturas = 0;
|
||||
|
||||
registrarHandler(
|
||||
@@ -68,7 +72,7 @@ void main() {
|
||||
|
||||
test('a read failure leaves the handler on the safe default instead of '
|
||||
'propagating', () async {
|
||||
final handler = PluriWaveAudioHandler();
|
||||
final handler = crearHandler();
|
||||
|
||||
registrarHandler(
|
||||
handler,
|
||||
@@ -81,7 +85,7 @@ void main() {
|
||||
|
||||
test('without a read port the handler is left untouched (widget tests, '
|
||||
'fakes)', () async {
|
||||
final handler = PluriWaveAudioHandler();
|
||||
final handler = crearHandler();
|
||||
await handler.setEcualizadorActivo(false);
|
||||
|
||||
registrarHandler(handler);
|
||||
@@ -106,7 +110,7 @@ void main() {
|
||||
'starts from the persisted value, not from a hardcoded default',
|
||||
() async {
|
||||
// One engine does the read `registrarHandler` performs in main.dart.
|
||||
final primero = PluriWaveAudioHandler();
|
||||
final primero = crearHandler();
|
||||
|
||||
// Pin the module cache to the OPPOSITE value first. Without this the
|
||||
// test passes for the wrong reason: whatever ran before may already
|
||||
@@ -123,7 +127,7 @@ void main() {
|
||||
|
||||
// Now the construction window: a handler built by `AudioService.init`'s
|
||||
// builder, with no port of its own yet.
|
||||
final segundo = PluriWaveAudioHandler();
|
||||
final segundo = crearHandler();
|
||||
|
||||
expect(
|
||||
segundo.ecualizadorActivo,
|
||||
@@ -136,12 +140,12 @@ void main() {
|
||||
|
||||
test('the cache follows what the handler itself writes, in both '
|
||||
'directions', () async {
|
||||
final handler = PluriWaveAudioHandler();
|
||||
final handler = crearHandler();
|
||||
registrarHandler(handler);
|
||||
|
||||
await handler.setEcualizadorActivo(false);
|
||||
expect(
|
||||
PluriWaveAudioHandler().ecualizadorActivo,
|
||||
crearHandler().ecualizadorActivo,
|
||||
isFalse,
|
||||
reason:
|
||||
'the write side of the cache: a toggle must be visible to the '
|
||||
@@ -149,14 +153,14 @@ void main() {
|
||||
);
|
||||
|
||||
await handler.setEcualizadorActivo(true);
|
||||
expect(PluriWaveAudioHandler().ecualizadorActivo, isTrue);
|
||||
expect(crearHandler().ecualizadorActivo, isTrue);
|
||||
});
|
||||
});
|
||||
|
||||
group('B — the handler persists its OWN toggle', () {
|
||||
test('an eq toggle writes through the injected port even with no '
|
||||
'EstadoEcualizador in play', () async {
|
||||
final handler = PluriWaveAudioHandler();
|
||||
final handler = crearHandler();
|
||||
final escrituras = <bool>[];
|
||||
|
||||
registrarHandler(
|
||||
@@ -177,7 +181,7 @@ void main() {
|
||||
});
|
||||
|
||||
test('seeding from disk does NOT write back to disk', () async {
|
||||
final handler = PluriWaveAudioHandler();
|
||||
final handler = crearHandler();
|
||||
final escrituras = <bool>[];
|
||||
|
||||
registrarHandler(
|
||||
@@ -192,7 +196,7 @@ void main() {
|
||||
});
|
||||
|
||||
test('a failing write port never breaks the toggle', () async {
|
||||
final handler = PluriWaveAudioHandler();
|
||||
final handler = crearHandler();
|
||||
|
||||
registrarHandler(
|
||||
handler,
|
||||
@@ -248,7 +252,7 @@ void main() {
|
||||
|
||||
group('customAction dispatch (C — zero coverage before this)', () {
|
||||
test('the accionEqToggle literal routes through decidirToggleEq', () async {
|
||||
final handler = PluriWaveAudioHandler();
|
||||
final handler = crearHandler();
|
||||
registrarHandler(handler);
|
||||
await handler.setEcualizadorActivo(true);
|
||||
|
||||
@@ -261,7 +265,7 @@ void main() {
|
||||
|
||||
test('a car toggle persists through the same write port as a phone '
|
||||
'toggle', () async {
|
||||
final handler = PluriWaveAudioHandler();
|
||||
final handler = crearHandler();
|
||||
final escrituras = <bool>[];
|
||||
registrarHandler(
|
||||
handler,
|
||||
@@ -282,7 +286,7 @@ void main() {
|
||||
});
|
||||
|
||||
test('an unknown custom action is a silent no-op', () async {
|
||||
final handler = PluriWaveAudioHandler();
|
||||
final handler = crearHandler();
|
||||
registrarHandler(handler);
|
||||
final antes = handler.ecualizadorActivo;
|
||||
|
||||
@@ -380,7 +384,7 @@ void main() {
|
||||
|
||||
test('the first non-idle event re-asserts the native effect exactly '
|
||||
'once, and staying active never re-asserts again', () async {
|
||||
final handler = PluriWaveAudioHandler();
|
||||
final handler = crearHandler();
|
||||
registrarHandler(handler);
|
||||
handler.simularEcualizadorDisponible(true);
|
||||
|
||||
@@ -410,7 +414,7 @@ void main() {
|
||||
|
||||
test('going idle re-arms the edge, so stop + play re-asserts again — '
|
||||
'this is the `_reproductorActivo = proc != idle` line', () async {
|
||||
final handler = PluriWaveAudioHandler();
|
||||
final handler = crearHandler();
|
||||
registrarHandler(handler);
|
||||
handler.simularEcualizadorDisponible(true);
|
||||
|
||||
@@ -435,7 +439,7 @@ void main() {
|
||||
});
|
||||
|
||||
test('with no native effect attached nothing is ever re-asserted', () async {
|
||||
final handler = PluriWaveAudioHandler();
|
||||
final handler = crearHandler();
|
||||
registrarHandler(handler);
|
||||
// `_eqDisponible` is false off-device, which is also the real
|
||||
// "device has no Equalizer effect" case.
|
||||
@@ -451,7 +455,7 @@ void main() {
|
||||
group('F — the EQ re-push must not rewind the car progress bar', () {
|
||||
test('the EQ controls re-push refreshes updatePosition from the '
|
||||
'player', () async {
|
||||
final handler = PluriWaveAudioHandler();
|
||||
final handler = crearHandler();
|
||||
registrarHandler(handler);
|
||||
handler.playbackState.add(
|
||||
handler.playbackState.value.copyWith(
|
||||
|
||||
Reference in New Issue
Block a user