Files
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

281 lines
12 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 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) | `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 |
| `MediaItem`s 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`:
```xml
<automotiveApp>
<uses name="media"/>
</automotiveApp>
```
Añadir dentro de `<application>` en `AndroidManifest.xml` (junto al resto de `<meta-data>`):
```xml
<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`:
```dart
// 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:
```bash
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
- [Media apps for cars — overview (Android Developers)](https://developer.android.com/training/cars/media)
- [Add support for Android Auto to your media app](https://developer.android.com/training/cars/media/auto)
- [Car app quality guidelines](https://developer.android.com/docs/quality-guidelines/car-app-quality)
- [Distribute to cars (Play Console)](https://developer.android.com/training/cars/distribute)
- [audio_service (pub.dev)](https://pub.dev/packages/audio_service)
- [audio_service — repo y ejemplos (GitHub)](https://github.com/ryanheise/audio_service)
---
## 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.