Cómo compilar una app multimedia con plantillas

Las apps de contenido multimedia basadas en plantillas están en versión beta
En este momento, cualquier persona puede publicar apps de contenido multimedia con plantilla en los segmentos de pruebas internas y cerradas de Google Play Store. Más adelante, se permitirá la publicación en segmentos abiertos y de producción.

Las apps de contenido multimedia que usan las plantillas de la biblioteca de apps para vehículos pueden personalizar su experiencia de navegación y reproducción de contenido multimedia, a la vez que garantizan que la experiencia esté optimizada para las pantallas de los vehículos y minimice las distracciones durante la conducción.

En esta guía, se da por sentado que ya tienes una app de contenido multimedia que reproduce audio en un teléfono y que cumple con la arquitectura de apps de contenido multimedia de Android. La biblioteca de apps para vehículos te permite reemplazar la experiencia en la app por plantillas en lugar de las que se crearon con la estructura de datos MediaBrowser de Cómo compilar apps multimedia para vehículos. Aún debes proporcionar un MediaSession para los controles de reproducción y un MediaBrowserService o MediaLibraryService, que se usa para las recomendaciones y otras experiencias inteligentes.

Las experiencias de estacionamiento y conducción ya están disponibles en versión beta
Las apps de contenido multimedia basadas en plantillas ahora pueden agregar una experiencia de estacionamiento a conducción en el SO Android Automotive. Esto permite que tu app agregue actividades nativas adaptables mientras el vehículo está estacionado y realice una transición fluida a la experiencia de reproducción de contenido multimedia con plantillas cuando el vehículo está en movimiento.

Configura el manifiesto de tu app

Además de los pasos que se describen en Cómo usar la biblioteca de apps de Android para vehículos, se requiere lo siguiente para las apps de contenido multimedia basadas en plantillas:

Declara la compatibilidad de categoría en tu manifiesto

Tu app debe declarar la categoría de app para vehículos androidx.car.app.category.MEDIA en el filtro de intents de su CarAppService.

<application>
    ...
   <service
       ...
        android:name=".MyCarAppService"
        android:exported="true">
      <intent-filter>
        <action android:name="androidx.car.app.CarAppService" />
        <category android:name="androidx.car.app.category.MEDIA"/>
      </intent-filter>
    </service>
    ...
<application>

Para obtener acceso a MediaPlaybackTemplate, tu app también debe declarar el permiso androidx.car.app.MEDIA_TEMPLATES en su archivo de manifiesto:

<manifest ...>
  ...
  <uses-permission android:name="androidx.car.app.MEDIA_TEMPLATES"/>
  ...
</manifest>

Establece el nivel mínimo de API de la app para automóviles

Las apps de contenido multimedia que usan MediaPlaybackTemplate solo se admiten en el nivel 8 de la API de CAL y versiones posteriores. Asegúrate de que el valor mínimo de Car App API level esté establecido en 8.

<application ...>
  ...
  <meta-data
    android:name="androidx.car.app.minCarApiLevel"
    android:value="8"/>
  ...
</application>

Proporciona un ícono de atribución

Asegúrate de agregar un ícono de atribución para las apps multimedia compiladas con la biblioteca de apps para vehículos.

Declara la compatibilidad con Android Auto

Asegúrate de que se incluya lo siguiente en el manifiesto de tu app:

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

Luego, agrega la declaración template a automotive_app_desc.xml en tus recursos XML. Debería tener el siguiente aspecto:

<automotiveApp xmlns:android="http://schemas.android.com/apk/res/android">
 <uses name="media"/>
 <uses name="template"/>
</automotiveApp>

Cómo declarar la compatibilidad con el SO Android Automotive

Existen dos formas diferentes de distribuir una app de contenido multimedia compatible con la biblioteca de apps para vehículos en el SO Android Automotive: como un solo APK o como dos APK independientes. Si distribuyes un solo APK, este admitirá vehículos habilitados para el SO Android Automotive con el host de la biblioteca de apps para vehículos y, de lo contrario, recurrirá a una aplicación MediaBrowserService o MediaLibraryService, incluso para versiones anteriores de Android (Android 10 a Android 13). Si decides distribuir dos APKs independientes, puedes actualizar las nuevas incorporaciones a la versión de la biblioteca de apps para vehículos sin temor a afectar la versión MediaBrowserService o MediaLibraryService de tu app.

Distribución de un solo APK

Cuando distribuyas un solo APK para la biblioteca de apps para vehículos y las versiones MediaBrowserService o MediaLibraryService de tu app, es fundamental que configures android.software.car.templates_host.media como android:required="false".

<uses-feature android:name="android.software.car.templates_host.media" android:required="false"/>

A continuación, sigue los lineamientos de la biblioteca de apps para vehículos para AAOS y, luego, introduce un CarAppActivity que se pueda iniciar (o una actividad de trampolín). Debes establecer la actividad en android:enabled="false" en el manifiesto. Luego, agrega una etiqueta de metadatos a la declaración MediaBrowserService que indique el componente CarAppActivity como reemplazo. Consulta el siguiente manifiesto de ejemplo:

<service android:name=".media.MyMediaService"
    android:exported="true"
    android:label="@string/app_name">
    <intent-filter>
        <action android:name="androidx.media3.session.MediaLibraryService"/>
    </intent-filter>

    <!-- Link to Car App Library Activity -->
    <meta-data
        android:name="androidx.car.app.media.CalMediaActivityComponent"
        android:value="com.example.mediaapp.LaunchableTrampoline"/>
</service>

<activity
    android:name=".LaunchableTrampoline"
    android:exported="true"
    android:theme="@android:style/Theme.DeviceDefault.NoActionBar"
    android:launchMode="singleTask"
    android:label="@string/app_name_cal"
    android:enabled="false"> <!-- Set to false -->

    <meta-data android:name="distractionOptimized" android:value="true" />

    <intent-filter>
        <action android:name="android.intent.action.MAIN"/>
        <action android:name="androidx.car.app.media.action.SHOW_MEDIA_PLAYBACK"/>
        <category android:name="android.intent.category.LAUNCHER"/>
    </intent-filter>
</activity>

Distribución de Play

Tu APK con la biblioteca de apps para vehículos y MediaBrowserService o MediaLibraryService debe estar habilitado con un código de versión superior y un minSdk orientado a Android 14 (34).

Distribución con dos APK

Para distribuir dos APKs independientes, uno que use la biblioteca de apps para vehículos y otro que use MediaBrowserService o MediaLibraryService, sigue estos pasos para asegurarte de que se segmenten correctamente las capacidades del vehículo.

Cuando crees un APK independiente para la versión de la biblioteca de apps para vehículos de tu app, deberás establecer el android.software.car.templates_host.media en android:required=true. Esto garantiza que la app se distribuya solo en las compilaciones del SO Android Automotive certificadas con compatibilidad con el host de la biblioteca de apps para vehículos.

<uses-feature android:name="android.software.car.templates_host.media" android:required="true"/>

Además de usar android.software.car.templates_host.media y establecerlo en android:required=true, sigue estos pasos para habilitar el SO Android Automotive en tu actividad de la biblioteca de apps para vehículos que se puede iniciar.

Distribución de Play

El APK que usa la biblioteca de apps para vehículos se debe distribuir en el segmento exclusivo de Automotive OS.

Cómo admitir acciones de voz

Habilita la voz en tu app para que los usuarios puedan completar acciones comunes sin usar las manos. Consulta Cómo admitir acciones de voz para contenido multimedia para obtener instrucciones de implementación más detalladas. Con una app de contenido multimedia basada en plantillas, si recibes un comando por voz, no necesitas actualizar tu MediaBrowserService ni MediaLibraryService con los resultados de la búsqueda. En su lugar, considera agregar una acción en tu plantilla de reproducción de contenido multimedia para permitir que el usuario encuentre más contenido en función de esa reproducción o búsqueda. Se requiere la compatibilidad con comandos por voz para cumplir con el lineamiento de calidad VC-1.

Crea tu plantilla de reproducción

La plantilla MediaPlaybackTemplate muestra información de reproducción de contenido multimedia en la app de contenido multimedia de la biblioteca de apps para vehículos. Esta plantilla permite configurar un encabezado con un título y acciones personalizables, mientras que el host completa la información de contenido multimedia y los controles de reproducción según el estado de la MediaSession de tu app.

Un reproductor de música muestra Sounds of Spring de Summer Fielding con un
     retrato cuadrado de una mujer tocando la guitarra.

Figura 1: MediaPlaybackTemplate con una acción de encabezado para abrir la fila en la parte superior.

En este ejemplo de código, se muestra cómo compilar una plantilla de reproducción de ejemplo que establece una acción de encabezado que permite al usuario navegar a una pantalla con la lista de canciones.

val playbackTemplate = MediaPlaybackTemplate.Builder()
      .setHeader(
        Header.Builder()
          .setStartHeaderAction(Action.BACK)
          .addEndHeaderAction(
                Action.Builder()
                  .setTitle(model.context.getString(R.string.queue_button_title))
                  .setIcon(
                    CarIcon.Builder(
                        IconCompat.createWithResource(
                          model.context,
                          R.drawable.gs_queue_music_vd_theme_24,
                        ))
                      .build())
                  .setOnClickListener(showQueueScreen())
                  .build())
          .setTitle(model.context.getString(R.string.media_playback_view_title))
          .build())
      .build()

Cuando uses MediaPlaybackTemplate, registra un token de MediaSession con MediaPlaybackManager en tu CarAppService. De lo contrario, se mostrará un error cuando se envíe un MediaPlaybackTemplate al host.

import androidx.car.app.media.MediaPlaybackManager
…

override fun onCreateSession(sessionInfo: SessionInfo): Session {
    return object : Session() {
        …

        init {
          lifecycle.addObserver(
            LifecycleEventObserver { _, event ->
              if (event == ON_CREATE) {
                val token = ... // MediaSessionCompat.Token
                (carContext.getCarService(CarContext.MEDIA_PLAYBACK_SERVICE) as MediaPlaybackManager)
                  .registerMediaPlaybackToken(token)
              }
              ...
            }
          )
        }
    }
}

.registerMediaPlaybackToken es necesario para exponer la información y los controles de reproducción de contenido multimedia en Android Auto. Esto también es importante para que el host cree notificaciones específicas de contenido multimedia.

En el caso de las apps que usan la biblioteca de Media3, que utilizan un PlatformToken en lugar de un MediaSessionCompat.Token estándar, deberás implementar un SessionCommand personalizado en tu MediaLibrarySession.Callback que devuelva el token de la plataforma subyacente de la sesión: session.platformToken. En tu CarAppService, envía este comando personalizado a la sesión. Una vez que recibas el token de la plataforma, conviértelo con MediaSessionCompat.Token.fromToken(platformToken) y pásalo a la biblioteca de apps para vehículos en .registerMediaPlaybackToken().

Cómo organizar el contenido multimedia con plantillas

Para organizar el contenido multimedia para la navegación, como canciones o álbumes, te recomendamos que uses SectionedItemTemplate, que te permite usar GridSection y RowSection juntos para crear diseños que mezclen listas de imágenes y elementos de texto.

La interfaz de una app de música muestra las canciones y los álbumes reproducidos recientemente,
     incluidas dos filas verticales y tres imágenes de álbumes horizontales.

Figura 2: Un SectionedItemTemplate que contiene un RowSection seguido de un GridSection

Usa SectionedItemTemplate dentro de un TabTemplate

Una forma conveniente de categorizar el contenido multimedia en tu app es usar SectionedItemTemplate dentro de un TabTemplate.

val template =
      SectionedItemTemplate.Builder()...build();
val tabTemplate =
      TabTemplate.Builder(tabCallback)
          .setTabContents(TabContents.Builder(template).build)
          .setHeaderAction(Action.APP_ICON)
          …
          .build();

Componentes y funciones de la biblioteca de apps para vehículos 1.9

La versión 1.9 de la API de la biblioteca de apps para vehículos presenta componentes personalizados para funciones de navegación únicas, como chips, barras de progreso, elementos condensados, encabezados interactivos y expandidos, secciones destacadas y banners.

La interfaz de una app de música muestra las canciones y los álbumes reproducidos recientemente,
     incluidas dos filas verticales y tres imágenes de álbumes horizontales.

Figura 3: Un SectionedItemTemplate que contiene Chips, Condensed Items, un Interactive Header, Grid Items y un Minimized Control Panel

La interfaz de una app de música muestra las canciones y los álbumes reproducidos recientemente,
     incluidas dos filas verticales y tres imágenes de álbumes horizontales.

Figura 4: Dos pantallas de navegación de contenido multimedia con Expanded Header, Spotlight Sections y Progress Bars

Si quieres obtener más información para diseñar la interfaz de usuario de tu app multimedia con estas plantillas, consulta el documento sobre apps multimedia.

Cuando se explora contenido multimedia, es importante que el usuario pueda navegar rápidamente a la MediaPlaybackTemplate con una distracción mínima.Para cumplir con el requisito de calidad MFT-1, tu app debe tener una forma de acceder a la MediaPlaybackTemplate desde todas las pantallas de exploración de contenido multimedia.

Si usas SectionedItemTemplate, puedes lograrlo agregando un botón de acción que te lleve a la pantalla de reproducción de contenido multimedia. Usa la acción estándar Action.MEDIA_PLAYBACK de la biblioteca de apps para vehículos. Una app multimedia mostrará esta acción como un panel de control minimizado, que es necesario para cumplir con el requisito de calidad MFT-1 si usas la API de la biblioteca de apps para vehículos 1.9 o una versión posterior. Para otras plantillas, una acción de encabezado es otra forma de lograrlo.

Controla los intents de reproducción de contenido multimedia del sistema

Es obligatorio dirigir al usuario a la MediaPlaybackTemplate cuando se inicia una aplicación desde una plataforma de reproducción de contenido multimedia del sistema, como una tarjeta multimedia. Requerimos que las aplicaciones de música controlen este Intent Action para brindar una experiencia fluida a los usuarios.

Agrega la acción androidx.car.app.media.action.SHOW_MEDIA_PLAYBACK al filtro de intents de tu componente de la biblioteca de apps para vehículos (ya sea CarAppActivity o tu trampolín Activity).

Asegúrate de que tu actividad use un launchMode de singleTask o singleTop para que se invoque onNewIntent().

<activity
    android:name=".LaunchableTrampoline"
    android:exported="true"
    android:theme="@android:style/Theme.DeviceDefault.NoActionBar"
    android:launchMode="singleTask"
    android:label="@string/app_name_cal"
    android:enabled="false">

    <meta-data android:name="distractionOptimized" android:value="true" />

    <intent-filter>
        <action android:name="android.intent.action.MAIN"/>
        <action android:name="androidx.car.app.media.action.SHOW_MEDIA_PLAYBACK"/>
        <category android:name="android.intent.category.LAUNCHER"/>
    </intent-filter>
</activity>

En tu clase Session, anula onNewIntent() para analizar el intent entrante. Si la acción del intent entrante coincide con SHOW_MEDIA_PLAYBACK, dirige al usuario a la pantalla Está sonando.

@Override
public void onNewIntent(@NonNull Intent intent) {
    super.onNewIntent(intent);
    if (SHOW_MEDIA_PLAYBACK.equals(intent.getAction())) {
        ScreenManager screenManager = getCarContext().getCarService(ScreenManager.class);
        // Avoid redundant navigation if already on the playing screen
        if (screenManager.getTop() instanceof MyMediaPlayScreen) {
            return;
        }
        screenManager.push(MyMediaPlayScreen.createScreenFromPlaying(
                getCarContext(), mMediaSessionController));
    }
}

Si usas una actividad de trampolín, verifica la acción de intent en onCreate(). Pasa esta acción al intent de creación de CarAppActivity antes de llamar a finish().

public class LaunchableTrampoline extends AppCompatActivity {
    private static final String SHOW_MEDIA_PLAYBACK = "androidx.car.app.media.action.SHOW_MEDIA_PLAYBACK";

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        Intent receivedIntent = getIntent();
        String action;

        if (SHOW_MEDIA_PLAYBACK.equals(receivedIntent.getAction())) {
            action = SHOW_MEDIA_PLAYBACK;
        } else {
            action = Intent.ACTION_MAIN;
        }

        Intent intent = new Intent(action);
        intent.setClassName(getPackageName(), "androidx.car.app.activity.CarAppActivity");
        startActivity(intent);
        finish();
    }
}