Files
pluriwave/docs/android-auto.md
T
Javier Bautista Fernández 07c6e32af0
Build & Deploy PluriWave / Análisis de código (push) Successful in 26s
Build & Deploy PluriWave / Build APK + AAB release (push) Successful in 2m22s
docs(auto): android auto research guide and sdd artifacts for android-auto-media
2026-07-16 16:28:54 +02:00

12 KiB
Raw Blame History

Llevar PluriWave a Android Auto

Guía para un desarrollador que nunca publicó una app en Play Store y quiere que PluriWave —instalada en el teléfono— se pueda navegar y controlar desde la pantalla del coche vía Android Auto.

Alcance de esta guía. Hablamos de Android Auto proyectado: la app corre en el móvil y se proyecta al coche. NO es Android Automotive OS (donde la app se instala dentro del sistema del vehículo). PluriWave es una app de audio → categoría "media app". Todo lo de abajo es para esa combinación.


TL;DR (lo importante primero)

  1. El 60% ya está hecho. PluriWave usa audio_service, que ya expone un MediaBrowserService + MediaSession (lo que Android Auto exige). No hay que reescribir el motor de audio.
  2. Falta lo que un coche necesita de más que un teléfono: un árbol navegable de emisoras (para que el coche muestre una lista) y una declaración en el manifest para que Android Auto descubra la app.
  3. Trabajo real de código: ~1 archivo XML nuevo + 1 línea en el manifest + implementar 3 métodos en PluriWaveAudioHandler (getChildren, getMediaItem, playFromMediaId).
  4. Publicación: Android Auto añade una revisión extra de Google contra las car app quality guidelines. Es más estricta y más lenta que la de una app normal.

Tiempo realista para un principiante: 12 semanas (código un par de días, el resto es testing con el emulador de coche y la revisión de Google).


Parte 0 — Cómo funciona (modelo mental)

Un coche con Android Auto no ejecuta tu UI de Flutter. En su lugar, le pide a tu app dos cosas a través de un servicio estándar de Android:

El coche pregunta Tu app responde En Android esto es
"¿Qué contenido tenés para mostrar?" Una lista de ítems (carpetas + emisoras) MediaBrowserServicegetChildren()
"El usuario tocó ESTE ítem, reproducilo" Arrancás el stream MediaSessionplayFromMediaId()
"Mostrame play/pausa/título/carátula" El estado actual PlaybackState + MediaItem

audio_service implementa el MediaBrowserService y el MediaSession por vos. Tu único trabajo es rellenar las respuestas (la lista de emisoras y cómo reproducir cada una).

La UI del coche la dibuja Android Auto, no vos. Vos solo aportás datos y audio.


Parte 1 — Qué ya tiene PluriWave (punto de partida)

Verificado en el código actual:

Pieza Dónde Estado
Dependencia audio_service ^0.18.15 pubspec.yaml
MediaBrowserService declarado en el manifest android/app/src/main/AndroidManifest.xml:47-54
MediaButtonReceiver (controles físicos/notificación) AndroidManifest.xml:62-68
Inicialización del handler lib/main.dart:38 (AudioService.init)
Config del servicio lib/main.dart:21 (AudioServiceConfig)
Handler propio lib/servicios/servicio_audio.dart:127 (PluriWaveAudioHandler extends BaseAudioHandler)
Reproducir un ítem servicio_audio.dart:433 (playMediaItem)
Mapear MediaItemEmisora servicio_audio.dart:705 (_emisoraDesdeMediaItem)
foregroundServiceType="mediaPlayback" AndroidManifest.xml:49

Ventaja clave de la versión 0.18: el handler corre en el mismo isolate que la app, así que getChildren() puede leer directamente tu lista de emisoras/favoritos del estado de la app. No hay que sincronizar entre isolates.

Lo que NO está (el hueco a rellenar)

Falta Consecuencia hoy
res/xml/automotive_app_desc.xml Android Auto no descubre la app
<meta-data com.google.android.gms.car.application> en el manifest idem
Override de getChildren() / getMediaItem() El coche no tiene ninguna lista que mostrar
Override de playFromMediaId() Tocar una emisora en el coche no reproduce nada
MediaItems con carátula (artUri) por emisora Google rechaza apps de media sin título+thumbnail por ítem

Hoy PluriWave solo sabe reproducir un MediaItem que le pasa su propia UI de Flutter (playMediaItem). El coche necesita el camino inverso: pedir la lista y arrancar por id.


Parte 2 — Quick path (los pasos, en orden)

Paso 1 · Declarar la app ante Android Auto

Crear android/app/src/main/res/xml/automotive_app_desc.xml:

<automotiveApp>
    <uses name="media"/>
</automotiveApp>

Añadir dentro de <application> en AndroidManifest.xml (junto al resto de <meta-data>):

<meta-data
    android:name="com.google.android.gms.car.application"
    android:resource="@xml/automotive_app_desc"/>

Con esto Android Auto ya "ve" la app, pero seguirá vacía hasta el Paso 2.

Paso 2 · Construir el árbol navegable (el trabajo de fondo)

En PluriWaveAudioHandler (lib/servicios/servicio_audio.dart) implementar estos métodos. Firmas verificadas contra el código de audio_service:

// Devuelve los hijos de una "carpeta". El root usa AudioService.browsableRootId.
Future<List<MediaItem>> getChildren(String parentMediaId,
    [Map<String, dynamic>? options]);

// Metadatos de un ítem concreto (por si el coche los pide sueltos).
Future<MediaItem?> getMediaItem(String mediaId);

// El usuario tocó un ítem en la pantalla del coche → reproducirlo.
Future<void> playFromMediaId(String mediaId, [Map<String, dynamic>? extras]);

// (Opcional) búsqueda por voz "pon Radio X".
Future<List<MediaItem>> playFromSearch(String query, [Map<String, dynamic>? extras]);

Diseño de árbol sugerido para PluriWave:

root (AudioService.browsableRootId)
├── Favoritos        (playable: false  → carpeta)
│   ├── Emisora A    (playable: true)
│   └── Emisora B    (playable: true)
├── Todas las emisoras (playable: false)
│   └── ...
└── Mis emisoras     (playable: false)  // las custom del usuario
    └── ...

Reglas de un MediaItem:

Campo Carpeta Emisora reproducible
id id estable de la categoría id estable de la emisora
title nombre visible nombre de la emisora (obligatorio)
playable false true
artUri opcional carátula/logo (obligatorio para pasar la revisión)

Lógica a reutilizar: ya tenés _emisoraDesdeMediaItem (servicio_audio.dart:705) y playMediaItem (servicio_audio.dart:433). En playFromMediaId(id) resolvés el id → Emisora → construís el MediaItem real → llamás al mismo playMediaItem interno. No dupliques la lógica de reproducción, enchufala.

Paso 3 · Carátulas accesibles

artUri tiene que ser una URL/҇URI que el sistema pueda cargar (http(s) o content://). Las emisoras que ya tienen logo remoto sirven directo. Para emisoras sin logo, definí una carátula por defecto (asset empaquetado servido vía content:// o un placeholder remoto).

Paso 4 · (Opcional pero recomendado) Content style

Android Auto puede pintar los ítems como lista o grid. Se controla con hints en el extras/config del root (constantes CONTENT_STYLE_* de la spec de MediaBrowser). Para una app de radio, grid para emisoras (se ven los logos) queda mejor. Es pulido, no bloquea la publicación.


Parte 3 · Probar sin coche (DHU — Desktop Head Unit)

No necesitás un coche para testear. Google da un emulador de la pantalla del coche.

Quick path del testeo:

  1. En Android Studio → SDK Manager → SDK Tools, instalá Android Auto Desktop Head Unit.
  2. En el teléfono: instalá la app Android Auto, entrá en sus ajustes y tocá 10 veces la versión para activar modo desarrollador; ahí activá "Head unit server".
  3. Conectá el teléfono por USB y lanzá el DHU:
    cd "$ANDROID_HOME/extras/google/auto"
    ./desktop-head-unit          # (desktop-head-unit.exe en Windows)
    
  4. En la ventana del DHU deberías ver PluriWave en la sección de media. Navegá el árbol y reproducí una emisora.

Checklist de humo en el DHU:

  • La app aparece en la lista de apps de media del coche.
  • Se ve el árbol (Favoritos / Todas / Mis emisoras).
  • Cada emisora muestra título + carátula.
  • Tocar una emisora arranca el audio.
  • Play / pausa / stop responden desde la pantalla del coche.
  • Al pausar en el coche, la app del teléfono refleja el mismo estado (y viceversa).

Parte 4 · Publicar en Play Store (lo específico de un primer publicador)

Publicar una app con Android Auto no es igual que una app normal: dispara una revisión adicional de Google contra las car app quality guidelines.

Requisitos de calidad que Google verifica (media apps)

Requisito Qué significa para PluriWave
Integración con MediaSession Ya lo da audio_service
Soportar play/pausa o stop Ya lo tenés
Título + thumbnail por cada ítem ← esto es lo que hay que asegurar (Paso 3)
Poder llegar a la vista de reproducción desde el browsing Se cumple con el árbol bien armado
Al menos 1 screenshot real, sin editar, de la experiencia en coche Sacala del DHU

Pasos en Play Console

  1. Cuenta de desarrollador (pago único de ~25 USD, primera vez).
  2. Subí primero a un track de pruebas cerrado, NO directo a producción.

    Importante: si el build va en un track de testing y no cumple, Google te avisa pero igual lo aprueba para ese track. Si el mismo build va a producción y no cumple, lo rechaza. Por eso: cerrado → arreglás → producción.

  3. Completá la ficha de la tienda + el cuestionario de contenido/privacidad (obligatorio para cualquier app nueva).
  4. Subí la screenshot de la experiencia en coche (del DHU).
  5. Enviá a revisión. La revisión de coche puede tardar de unas horas hasta 7 días (a veces más), bastante más que una app solo-móvil.

Gotchas para PluriWave concretamente

  • Muchos permisos sensibles. El manifest pide localización, RECORD_AUDIO, SCHEDULE_EXACT_ALARM, SYSTEM_EXEMPTED, etc. La revisión de coche mira con lupa; tené a mano la justificación de cada permiso (la sección de foreground service de Play Console te va a pedir el porqué de mediaPlayback).
  • targetSdk sale de flutter.targetSdkVersion (android/app/build.gradle). Play exige un target reciente para apps nuevas; verificá que cumpla el mínimo del año antes de subir.
  • Streams que fallan. Google prueba reproducir. Si una emisora del árbol está caída, da mala impresión. Exponé en el árbol emisoras fiables (favoritos del usuario, o un set curado) y manejá el error de stream con gracia (ya tenés controlador_reconexion.dart).

Checklist maestro

Código

  • res/xml/automotive_app_desc.xml creado (<uses name="media"/>).
  • <meta-data com.google.android.gms.car.application> en el manifest.
  • getChildren() devuelve el árbol (carpetas + emisoras).
  • getMediaItem() resuelve un id suelto.
  • playFromMediaId() reutiliza playMediaItem interno.
  • Cada emisora expone title + artUri.

Testing

  • Funciona en el DHU (navegar + reproducir + play/pausa).
  • Estado sincronizado coche ↔ teléfono.

Publicación

  • Cuenta de desarrollador creada.
  • Screenshot de la experiencia en coche subida.
  • Subido primero a track cerrado.
  • Justificación de permisos preparada.
  • Enviado a revisión.

Referencias


Próximo paso sugerido

Empezar por el Paso 1 + Paso 2 con un árbol mínimo (solo "Favoritos" con 23 emisoras hardcodeadas) y verlo en el DHU. Cuando eso reproduzca en el emulador de coche, recién ahí ampliar el árbol y pulir carátulas. Es el bucle de feedback más corto para no programar a ciegas.