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:
2026-09-04 13:26:00 +02:00
parent 575ba793ae
commit 8e155cc0ac
43 changed files with 4446 additions and 310 deletions
+78
View File
@@ -3415,6 +3415,84 @@ abstract class AppLocalizations {
/// In es, this message translates to:
/// **'Ahora no'**
String get premiumAhoraNo;
/// No description provided for @autoErrorEmisoraPremium.
///
/// In es, this message translates to:
/// **'Esta emisora es Premium. Abre PluriWave en el móvil para desbloquearla.'**
String get autoErrorEmisoraPremium;
/// No description provided for @autoErrorBusquedaSinResultados.
///
/// In es, this message translates to:
/// **'No hemos encontrado esa emisora. Prueba con otro nombre.'**
String get autoErrorBusquedaSinResultados;
/// No description provided for @autoCarpetaEscuchar.
///
/// In es, this message translates to:
/// **'Escuchar'**
String get autoCarpetaEscuchar;
/// No description provided for @autoCarpetaFavoritos.
///
/// In es, this message translates to:
/// **'Favoritos'**
String get autoCarpetaFavoritos;
/// No description provided for @autoCarpetaTodas.
///
/// In es, this message translates to:
/// **'Todas las emisoras'**
String get autoCarpetaTodas;
/// No description provided for @autoCarpetaMisEmisoras.
///
/// In es, this message translates to:
/// **'Mis emisoras'**
String get autoCarpetaMisEmisoras;
/// No description provided for @autoCarpetaMusicaLocal.
///
/// In es, this message translates to:
/// **'Música Local'**
String get autoCarpetaMusicaLocal;
/// No description provided for @autoMusicaLocalNoDisponible.
///
/// In es, this message translates to:
/// **'Abre PluriWave en el móvil para leer tu música'**
String get autoMusicaLocalNoDisponible;
/// No description provided for @autoCargarMas.
///
/// In es, this message translates to:
/// **'Más…'**
String get autoCargarMas;
/// No description provided for @autoOrdenarPorCalidad.
///
/// In es, this message translates to:
/// **'Ordenar por calidad'**
String get autoOrdenarPorCalidad;
/// No description provided for @autoReproducirCarpeta.
///
/// In es, this message translates to:
/// **'Reproducir carpeta'**
String get autoReproducirCarpeta;
/// No description provided for @autoReproducirAleatorio.
///
/// In es, this message translates to:
/// **'Reproducir aleatorio'**
String get autoReproducirAleatorio;
/// No description provided for @autoPistaSinNombre.
///
/// In es, this message translates to:
/// **'Pista sin nombre'**
String get autoPistaSinNombre;
}
class _AppLocalizationsDelegate
+42
View File
@@ -1888,4 +1888,46 @@ class AppLocalizationsAr extends AppLocalizations {
@override
String get premiumAhoraNo => 'ليس الآن';
@override
String get autoErrorEmisoraPremium =>
'هذه المحطة ضمن Premium. افتح PluriWave على هاتفك لفتحها.';
@override
String get autoErrorBusquedaSinResultados =>
'لم نعثر على تلك المحطة. جرّب اسمًا آخر.';
@override
String get autoCarpetaEscuchar => 'الاستماع';
@override
String get autoCarpetaFavoritos => 'المفضلة';
@override
String get autoCarpetaTodas => 'كل المحطات';
@override
String get autoCarpetaMisEmisoras => 'محطاتي';
@override
String get autoCarpetaMusicaLocal => 'الموسيقى المحلية';
@override
String get autoMusicaLocalNoDisponible =>
'افتح PluriWave على هاتفك لقراءة موسيقاك';
@override
String get autoCargarMas => 'المزيد…';
@override
String get autoOrdenarPorCalidad => 'الترتيب حسب الجودة';
@override
String get autoReproducirCarpeta => 'تشغيل المجلد';
@override
String get autoReproducirAleatorio => 'تشغيل عشوائي';
@override
String get autoPistaSinNombre => 'مقطع بلا اسم';
}
+42
View File
@@ -1900,4 +1900,46 @@ class AppLocalizationsBn extends AppLocalizations {
@override
String get premiumAhoraNo => 'এখন নয়';
@override
String get autoErrorEmisoraPremium =>
'এই স্টেশনটি Premium। আনলক করতে ফোনে PluriWave খুলুন।';
@override
String get autoErrorBusquedaSinResultados =>
'সেই স্টেশনটি খুঁজে পাওয়া যায়নি। অন্য নাম চেষ্টা করুন।';
@override
String get autoCarpetaEscuchar => 'শুনুন';
@override
String get autoCarpetaFavoritos => 'প্রিয়';
@override
String get autoCarpetaTodas => 'সব স্টেশন';
@override
String get autoCarpetaMisEmisoras => 'আমার স্টেশন';
@override
String get autoCarpetaMusicaLocal => 'স্থানীয় সঙ্গীত';
@override
String get autoMusicaLocalNoDisponible =>
'আপনার গান পড়তে ফোনে PluriWave খুলুন';
@override
String get autoCargarMas => 'আরও…';
@override
String get autoOrdenarPorCalidad => 'মান অনুসারে সাজান';
@override
String get autoReproducirCarpeta => 'ফোল্ডার চালান';
@override
String get autoReproducirAleatorio => 'এলোমেলোভাবে চালান';
@override
String get autoPistaSinNombre => 'নামহীন ট্র্যাক';
}
+42
View File
@@ -1913,4 +1913,46 @@ class AppLocalizationsDe extends AppLocalizations {
@override
String get premiumAhoraNo => 'Nicht jetzt';
@override
String get autoErrorEmisoraPremium =>
'Dieser Sender ist Premium. Öffne PluriWave auf dem Handy, um ihn freizuschalten.';
@override
String get autoErrorBusquedaSinResultados =>
'Wir haben diesen Sender nicht gefunden. Versuch es mit einem anderen Namen.';
@override
String get autoCarpetaEscuchar => 'Hören';
@override
String get autoCarpetaFavoritos => 'Favoriten';
@override
String get autoCarpetaTodas => 'Alle Sender';
@override
String get autoCarpetaMisEmisoras => 'Meine Sender';
@override
String get autoCarpetaMusicaLocal => 'Lokale Musik';
@override
String get autoMusicaLocalNoDisponible =>
'Öffne PluriWave auf dem Handy, um deine Musik zu lesen';
@override
String get autoCargarMas => 'Mehr…';
@override
String get autoOrdenarPorCalidad => 'Nach Qualität sortieren';
@override
String get autoReproducirCarpeta => 'Ordner abspielen';
@override
String get autoReproducirAleatorio => 'Zufallswiedergabe';
@override
String get autoPistaSinNombre => 'Unbenannter Titel';
}
+42
View File
@@ -1893,4 +1893,46 @@ class AppLocalizationsEn extends AppLocalizations {
@override
String get premiumAhoraNo => 'Not now';
@override
String get autoErrorEmisoraPremium =>
'This station is Premium. Open PluriWave on your phone to unlock it.';
@override
String get autoErrorBusquedaSinResultados =>
'We couldn\'t find that station. Try another name.';
@override
String get autoCarpetaEscuchar => 'Listen';
@override
String get autoCarpetaFavoritos => 'Favorites';
@override
String get autoCarpetaTodas => 'All stations';
@override
String get autoCarpetaMisEmisoras => 'My stations';
@override
String get autoCarpetaMusicaLocal => 'Local music';
@override
String get autoMusicaLocalNoDisponible =>
'Open PluriWave on your phone to read your music';
@override
String get autoCargarMas => 'More…';
@override
String get autoOrdenarPorCalidad => 'Sort by quality';
@override
String get autoReproducirCarpeta => 'Play folder';
@override
String get autoReproducirAleatorio => 'Shuffle play';
@override
String get autoPistaSinNombre => 'Untitled track';
}
+42
View File
@@ -1908,4 +1908,46 @@ class AppLocalizationsEs extends AppLocalizations {
@override
String get premiumAhoraNo => 'Ahora no';
@override
String get autoErrorEmisoraPremium =>
'Esta emisora es Premium. Abre PluriWave en el móvil para desbloquearla.';
@override
String get autoErrorBusquedaSinResultados =>
'No hemos encontrado esa emisora. Prueba con otro nombre.';
@override
String get autoCarpetaEscuchar => 'Escuchar';
@override
String get autoCarpetaFavoritos => 'Favoritos';
@override
String get autoCarpetaTodas => 'Todas las emisoras';
@override
String get autoCarpetaMisEmisoras => 'Mis emisoras';
@override
String get autoCarpetaMusicaLocal => 'Música Local';
@override
String get autoMusicaLocalNoDisponible =>
'Abre PluriWave en el móvil para leer tu música';
@override
String get autoCargarMas => 'Más…';
@override
String get autoOrdenarPorCalidad => 'Ordenar por calidad';
@override
String get autoReproducirCarpeta => 'Reproducir carpeta';
@override
String get autoReproducirAleatorio => 'Reproducir aleatorio';
@override
String get autoPistaSinNombre => 'Pista sin nombre';
}
+42
View File
@@ -1922,4 +1922,46 @@ class AppLocalizationsFr extends AppLocalizations {
@override
String get premiumAhoraNo => 'Plus tard';
@override
String get autoErrorEmisoraPremium =>
'Cette station est Premium. Ouvre PluriWave sur ton téléphone pour la débloquer.';
@override
String get autoErrorBusquedaSinResultados =>
'Nous n\'avons pas trouvé cette station. Essaie un autre nom.';
@override
String get autoCarpetaEscuchar => 'Écouter';
@override
String get autoCarpetaFavoritos => 'Favoris';
@override
String get autoCarpetaTodas => 'Toutes les stations';
@override
String get autoCarpetaMisEmisoras => 'Mes stations';
@override
String get autoCarpetaMusicaLocal => 'Musique locale';
@override
String get autoMusicaLocalNoDisponible =>
'Ouvrez PluriWave sur votre téléphone pour lire votre musique';
@override
String get autoCargarMas => 'Plus…';
@override
String get autoOrdenarPorCalidad => 'Trier par qualité';
@override
String get autoReproducirCarpeta => 'Lire le dossier';
@override
String get autoReproducirAleatorio => 'Lecture aléatoire';
@override
String get autoPistaSinNombre => 'Piste sans nom';
}
+42
View File
@@ -1893,4 +1893,46 @@ class AppLocalizationsHi extends AppLocalizations {
@override
String get premiumAhoraNo => 'अभी नहीं';
@override
String get autoErrorEmisoraPremium =>
'यह स्टेशन Premium है। इसे अनलॉक करने के लिए फ़ोन पर PluriWave खोलें।';
@override
String get autoErrorBusquedaSinResultados =>
'वह स्टेशन नहीं मिला। कोई दूसरा नाम आज़माएँ।';
@override
String get autoCarpetaEscuchar => 'सुनें';
@override
String get autoCarpetaFavoritos => 'पसंदीदा';
@override
String get autoCarpetaTodas => 'सभी स्टेशन';
@override
String get autoCarpetaMisEmisoras => 'मेरे स्टेशन';
@override
String get autoCarpetaMusicaLocal => 'लोकल संगीत';
@override
String get autoMusicaLocalNoDisponible =>
'अपना संगीत पढ़ने के लिए फ़ोन पर PluriWave खोलें';
@override
String get autoCargarMas => 'और…';
@override
String get autoOrdenarPorCalidad => 'गुणवत्ता के अनुसार क्रमबद्ध करें';
@override
String get autoReproducirCarpeta => 'फ़ोल्डर चलाएँ';
@override
String get autoReproducirAleatorio => 'शफ़ल चलाएँ';
@override
String get autoPistaSinNombre => 'बिना नाम का ट्रैक';
}
+42
View File
@@ -1904,4 +1904,46 @@ class AppLocalizationsId extends AppLocalizations {
@override
String get premiumAhoraNo => 'Nanti saja';
@override
String get autoErrorEmisoraPremium =>
'Stasiun ini Premium. Buka PluriWave di ponsel untuk membukanya.';
@override
String get autoErrorBusquedaSinResultados =>
'Kami tidak menemukan stasiun itu. Coba nama lain.';
@override
String get autoCarpetaEscuchar => 'Dengarkan';
@override
String get autoCarpetaFavoritos => 'Favorit';
@override
String get autoCarpetaTodas => 'Semua stasiun';
@override
String get autoCarpetaMisEmisoras => 'Stasiun saya';
@override
String get autoCarpetaMusicaLocal => 'Musik lokal';
@override
String get autoMusicaLocalNoDisponible =>
'Buka PluriWave di ponsel untuk membaca musik Anda';
@override
String get autoCargarMas => 'Lainnya…';
@override
String get autoOrdenarPorCalidad => 'Urutkan menurut kualitas';
@override
String get autoReproducirCarpeta => 'Putar folder';
@override
String get autoReproducirAleatorio => 'Putar acak';
@override
String get autoPistaSinNombre => 'Trek tanpa nama';
}
+42
View File
@@ -1919,4 +1919,46 @@ class AppLocalizationsIt extends AppLocalizations {
@override
String get premiumAhoraNo => 'Non ora';
@override
String get autoErrorEmisoraPremium =>
'Questa stazione è Premium. Apri PluriWave sul telefono per sbloccarla.';
@override
String get autoErrorBusquedaSinResultados =>
'Non abbiamo trovato quella stazione. Prova con un altro nome.';
@override
String get autoCarpetaEscuchar => 'Ascolta';
@override
String get autoCarpetaFavoritos => 'Preferiti';
@override
String get autoCarpetaTodas => 'Tutte le emittenti';
@override
String get autoCarpetaMisEmisoras => 'Le mie emittenti';
@override
String get autoCarpetaMusicaLocal => 'Musica locale';
@override
String get autoMusicaLocalNoDisponible =>
'Apri PluriWave sul telefono per leggere la tua musica';
@override
String get autoCargarMas => 'Altro…';
@override
String get autoOrdenarPorCalidad => 'Ordina per qualità';
@override
String get autoReproducirCarpeta => 'Riproduci cartella';
@override
String get autoReproducirAleatorio => 'Riproduzione casuale';
@override
String get autoPistaSinNombre => 'Traccia senza nome';
}
+41
View File
@@ -1836,4 +1836,45 @@ class AppLocalizationsJa extends AppLocalizations {
@override
String get premiumAhoraNo => '後で';
@override
String get autoErrorEmisoraPremium =>
'この放送局は Premium です。スマートフォンで PluriWave を開いてロックを解除してください。';
@override
String get autoErrorBusquedaSinResultados => 'その放送局は見つかりませんでした。別の名前をお試しください。';
@override
String get autoCarpetaEscuchar => '聴く';
@override
String get autoCarpetaFavoritos => 'お気に入り';
@override
String get autoCarpetaTodas => 'すべての局';
@override
String get autoCarpetaMisEmisoras => 'マイ局';
@override
String get autoCarpetaMusicaLocal => 'ローカルの音楽';
@override
String get autoMusicaLocalNoDisponible =>
'音楽を読み込むにはスマートフォンで PluriWave を開いてください';
@override
String get autoCargarMas => 'もっと見る…';
@override
String get autoOrdenarPorCalidad => '音質順に並べ替え';
@override
String get autoReproducirCarpeta => 'フォルダを再生';
@override
String get autoReproducirAleatorio => 'シャッフル再生';
@override
String get autoPistaSinNombre => '名称未設定のトラック';
}
+42
View File
@@ -1904,4 +1904,46 @@ class AppLocalizationsPt extends AppLocalizations {
@override
String get premiumAhoraNo => 'Agora não';
@override
String get autoErrorEmisoraPremium =>
'Esta estação é Premium. Abra o PluriWave no telemóvel para a desbloquear.';
@override
String get autoErrorBusquedaSinResultados =>
'Não encontrámos essa estação. Tente outro nome.';
@override
String get autoCarpetaEscuchar => 'Ouvir';
@override
String get autoCarpetaFavoritos => 'Favoritos';
@override
String get autoCarpetaTodas => 'Todas as estações';
@override
String get autoCarpetaMisEmisoras => 'As minhas estações';
@override
String get autoCarpetaMusicaLocal => 'Música local';
@override
String get autoMusicaLocalNoDisponible =>
'Abra o PluriWave no telemóvel para ler a sua música';
@override
String get autoCargarMas => 'Mais…';
@override
String get autoOrdenarPorCalidad => 'Ordenar por qualidade';
@override
String get autoReproducirCarpeta => 'Reproduzir pasta';
@override
String get autoReproducirAleatorio => 'Reprodução aleatória';
@override
String get autoPistaSinNombre => 'Faixa sem nome';
}
+42
View File
@@ -1911,4 +1911,46 @@ class AppLocalizationsRu extends AppLocalizations {
@override
String get premiumAhoraNo => 'Не сейчас';
@override
String get autoErrorEmisoraPremium =>
'Эта станция доступна в Premium. Откройте PluriWave на телефоне, чтобы разблокировать её.';
@override
String get autoErrorBusquedaSinResultados =>
'Мы не нашли такую станцию. Попробуйте другое название.';
@override
String get autoCarpetaEscuchar => 'Слушать';
@override
String get autoCarpetaFavoritos => 'Избранное';
@override
String get autoCarpetaTodas => 'Все станции';
@override
String get autoCarpetaMisEmisoras => 'Мои станции';
@override
String get autoCarpetaMusicaLocal => 'Локальная музыка';
@override
String get autoMusicaLocalNoDisponible =>
'Откройте PluriWave на телефоне, чтобы прочитать вашу музыку';
@override
String get autoCargarMas => 'Ещё…';
@override
String get autoOrdenarPorCalidad => 'Сортировать по качеству';
@override
String get autoReproducirCarpeta => 'Воспроизвести папку';
@override
String get autoReproducirAleatorio => 'Случайное воспроизведение';
@override
String get autoPistaSinNombre => 'Трек без названия';
}
+40
View File
@@ -1821,4 +1821,44 @@ class AppLocalizationsZh extends AppLocalizations {
@override
String get premiumAhoraNo => '以后再说';
@override
String get autoErrorEmisoraPremium =>
'该电台属于 Premium 内容。请在手机上打开 PluriWave 解锁。';
@override
String get autoErrorBusquedaSinResultados => '没有找到该电台。请换个名称再试。';
@override
String get autoCarpetaEscuchar => '收听';
@override
String get autoCarpetaFavoritos => '收藏';
@override
String get autoCarpetaTodas => '全部电台';
@override
String get autoCarpetaMisEmisoras => '我的电台';
@override
String get autoCarpetaMusicaLocal => '本地音乐';
@override
String get autoMusicaLocalNoDisponible => '请在手机上打开 PluriWave 以读取您的音乐';
@override
String get autoCargarMas => '更多…';
@override
String get autoOrdenarPorCalidad => '按音质排序';
@override
String get autoReproducirCarpeta => '播放文件夹';
@override
String get autoReproducirAleatorio => '随机播放';
@override
String get autoPistaSinNombre => '未命名曲目';
}