12 KiB
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)
- El 60% ya está hecho. PluriWave usa
audio_service, que ya expone unMediaBrowserService+MediaSession(lo que Android Auto exige). No hay que reescribir el motor de audio. - 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.
- Trabajo real de código: ~1 archivo XML nuevo + 1 línea en el manifest +
implementar 3 métodos en
PluriWaveAudioHandler(getChildren,getMediaItem,playFromMediaId). - 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: 1–2 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) | MediaBrowserService → getChildren() |
| "El usuario tocó ESTE ítem, reproducilo" | Arrancás el stream | MediaSession → playFromMediaId() |
| "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 MediaItem ↔ Emisora |
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:
- En Android Studio → SDK Manager → SDK Tools, instalá Android Auto Desktop Head Unit.
- 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".
- 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) - 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
- Cuenta de desarrollador (pago único de ~25 USD, primera vez).
- 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.
- Completá la ficha de la tienda + el cuestionario de contenido/privacidad (obligatorio para cualquier app nueva).
- Subí la screenshot de la experiencia en coche (del DHU).
- 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é demediaPlayback). targetSdksale deflutter.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.xmlcreado (<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()reutilizaplayMediaIteminterno.- 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
- Media apps for cars — overview (Android Developers)
- Add support for Android Auto to your media app
- Car app quality guidelines
- Distribute to cars (Play Console)
- audio_service (pub.dev)
- audio_service — repo y ejemplos (GitHub)
Próximo paso sugerido
Empezar por el Paso 1 + Paso 2 con un árbol mínimo (solo "Favoritos" con 2–3 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.