main
2
Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
241f81e535 |
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. |
||
|
|
3449e2cb79 |
fix: corregir ecualizador desincronizado, musica local en Android Auto y bloqueo del paywall
Tres fallos reportados en uso real, con sus causas raiz verificadas en codigo.
1. Ecualizador: el estado no tenia dueño unico
El handler arrancaba con `_ecualizadorActivo = true` a fuego. El valor
persistido solo llegaba por EstadoEcualizador.cargarPersistido(), alcanzable
unicamente desde el arbol de widgets, que un arranque headless de Android Auto
nunca construye. Resultado: el coche reproducia con el EQ forzado a ON mientras
disco e interfaz decian OFF.
Ahora registrarHandler siembra el flag desde disco en todos los motores y
setEcualizadorActivo persiste por su cuenta, asi que un toggle desde el coche o
la notificacion sobrevive sin EstadoEcualizador. _resincronizarConHandler pasa
a ser adopcion pura de interfaz.
Ademas mapearGananciaNativa enviaba 0 dB al punto MEDIO del rango nativo. Con
un getBandLevelRange() asimetrico, un preset plano metia varios dB de boost
real: la causa del "suena muy alto con el boton apagado". Reescrito para
escalar cada lado contra su propio extremo, de modo que 0 dB es siempre 0.
El dispatch de customAction no tenia ningun test. Se extrae decidirToggleEq y
se cubre contra el handler real. El efecto nativo se re-asierta al reactivarse
el reproductor, porque AudioEffect.setEnabled de just_audio es un no-op
mientras la plataforma esta desacoplada.
2. Musica Local no aparecia en el arbol de Android Auto
hayCarpetaConfigurada() consultaba MethodChannel('pluriwave/file_actions'),
registrado solo en MainActivity.configureFlutterEngine. Sin Activity no hay
handler, invokeMethod lanza MissingPluginException y el catch la confundia con
"permiso revocado", omitiendo el nodo. No dependia del entitlement.
La logica SAF sale a packages/pluriwave_file_actions, un paquete plugin local.
El motor headless que crea audio_service ejecuta GeneratedPluginRegistrant en
su constructor, asi que el canal queda registrado en ambos motores. Repuntar el
manifest a una subclase de AudioService no era viable: AudioServicePlugin
enlaza por ComponentName explicito y la app perderia el audio.
EstadoCarpetaLocal de tres valores separa "sin carpeta" de "canal no
disponible"; la raiz decide por la URI persistida y el subarbol muestra un item
explicativo en vez de una carpeta vacia. La invalidacion del arbol cacheado se
dispara al reanudar con el coche ya suscrito; el guardia anterior miraba
View.maybeOf, que bajo runApp siempre existe, por lo que se gastaba en el
arranque headless y no volvia a dispararse.
3. El paywall bloqueaba las compras
restorePurchases() de in_app_purchase_android emite siempre, y con lista vacia
si no hay nada que restaurar. El `if (compras.isEmpty) return;` se la tragaba,
noEncontrada nunca se emitia y la rama que limpia _compraEnCurso estaba muerta
en produccion. Como comprar y restaurar comparten ese flag, un usuario sin
compras que pulsaba restaurar se quedaba sin poder comprar.
Suite completa: 1366 pasan, 2 omitidos. 17 tests nuevos, todos nacidos rojos y
verificados por mutacion. flutter analyze mantiene los 5 avisos preexistentes.
|