Google se compromete a impulsar la igualdad racial para las comunidades afrodescendientes. Obtén información al respecto.

Cómo compilar apps multimedia para vehículos

Android Auto y el SO Android Automotive te permiten ofrecerles a los usuarios el contenido multimedia de tu app en sus vehículos. Una app multimedia para automóviles debe proporcionar un servicio de navegador multimedia para que Android Auto y el SO Android Automotive (u otra aplicación con navegador multimedia) puedan descubrir y mostrar tu contenido.

En esta guía, se supone que ya tienes una app multimedia que reproduce audio en un teléfono y que cumple con la arquitectura de apps multimedia de Android.

En esta guía, se describen los componentes necesarios de los elementos MediaBrowserService y MediaSession que tu app necesita para funcionar en Android Auto o el SO Android Automotive. Después de completar la infraestructura de medios principal, puedes agregar compatibilidad con Android Auto y agregar compatibilidad con el SO Android Automotive a tu app multimedia.

Antes de comenzar

  1. Revisa la documentación de la API de medios de Android.
  2. Revisa los lineamientos de diseño de apps para el SO Android Automotive y los lineamientos de diseño de apps para Android Auto.
  3. Revisa los términos y conceptos clave que se indican en esta sección.

Términos y conceptos clave

Servicio de navegador multimedia
Es un servicio de Android que implementa tu app multimedia y que cumple con la API de MediaBrowserServiceCompat. Tu app usa este servicio para exponer su contenido.
Navegador multimedia
Es una API que usan las apps multimedia para descubrir servicios de navegador multimedia y mostrar su contenido. Android Auto y el SO Android Automotive usan un navegador multimedia para encontrar el servicio de navegador multimedia de tu app.
Elemento multimedia

El navegador multimedia organiza su contenido en un árbol de objetos MediaItem. Un elemento multimedia puede tener una de estas marcas o ambas:

  • Reproducible: Esta marca indica que el elemento es una hoja en el árbol de contenido. El elemento representa una sola transmisión de sonido, como una canción de un álbum, un capítulo de un libro de audio o un episodio de un podcast.
  • Explorable: Esta marca indica que el elemento es un nodo en el árbol de contenido y que tiene elementos secundarios. Por ejemplo, el elemento representa un álbum y sus elementos secundarios son las canciones del álbum.

Un elemento multimedia reproducible y explorable funciona como una lista de reproducción. Puedes seleccionar el elemento para que se reproduzcan todos sus elementos secundarios o puedes navegar por sus elementos secundarios.

Actividades optimizadas para vehículos

Se trata de las actividades de una app con SO Android Automotive que cumplen con los lineamientos de diseño para el SO Android Automotive. La interfaz de estas actividades no está desarrollada por el SO Android Automotive, por lo que debes asegurarte de que tu app cumpla con los lineamientos de diseño. Generalmente, se incluyen objetivos táctiles y tamaños de fuente más grandes, compatibilidad con los modos diurno y nocturno, y relaciones de contraste más altas.

Las interfaces de usuario optimizadas para vehículos solo se muestran si las restricciones de la experiencia del usuario de vehículo (CUXR) no están vigentes, porque estas interfaces requieren atención extendida o interacción del usuario. Las CUXR no se aplican si el vehículo está detenido o estacionado, pero siempre se aplican cuando está en movimiento.

No es necesario diseñar actividades para Android Auto porque este usa su propia interfaz optimizada para vehículos con la información de tu servicio de navegador multimedia.

Cómo configurar los archivos del manifiesto de tu app

Para poder crear tu servicio de navegador multimedia, debes configurar los archivos de manifiesto de tu app.

Declara tu servicio de navegador multimedia

Tanto Android Auto como el SO Android Automotive se conectan a tu app mediante tu servicio de navegador multimedia para explorar elementos multimedia. Debes declarar tu servicio de navegador multimedia en el manifiesto a fin de que Android Auto y el SO Android Automotive descubran el servicio y se conecten a tu app.

En el siguiente fragmento de código, se muestra cómo declarar el servicio de navegador multimedia en el manifiesto. Debes incluir este código en el archivo de manifiesto del módulo del SO Android Automotive y en el archivo de manifiesto de la app de tu teléfono.

<application>
        ...
        <service android:name=".MyMediaBrowserService"
                 android:exported="true">
            <intent-filter>
                <action android:name="android.media.browse.MediaBrowserService"/>
            </intent-filter>
        </service>
        ...
    <application>
    

Especifica un ícono de la app

Debes especificar un ícono de la app que Android Auto y el SO Android Automotive puedan usar para representar tu app en la IU del sistema.

Puedes especificar el ícono que se usa para representar a tu app con la siguiente declaración de manifiesto:

<!--The android:icon attribute is used by Android Automotive OS-->
    <application
        ...
        android:icon="@mipmap/ic_launcher">
        ...
        <!--Used by Android Auto-->
        <meta-data android:name="com.google.android.gms.car.notification.SmallIcon"
                   android:resource="@drawable/ic_auto_icon" />
        ...
    <application>
    

Crea tu servicio de navegador multimedia

Puedes extender la clase MediaBrowserServiceCompat para crear un servicio de navegador multimedia. Android Auto y el SO Android Automotive pueden usar tu servicio para hacer lo siguiente:

  • Explorar la jerarquía del contenido de tu app con el fin de mostrar un menú al usuario
  • Obtener el token para el objeto MediaSessionCompat de tu app con el fin de controlar la reproducción de audio

Flujo de trabajo del servicio de navegador multimedia

En esta sección, se describe cómo el SO Android Automotive y Android Auto interactúan con tu servicio de navegadores multimedia durante un flujo de trabajo típico de los usuarios.

  1. Un usuario inicia tu app en el SO Android Automotive o en Android Auto.
  2. El SO Android Automotive o Android Auto se comunican con el servicio de navegador multimedia de tu app mediante el método onCreate(). En la implementación del método onCreate(), debes crear y registrar un objeto MediaSessionCompat y su objeto de devolución de llamada.
  3. El SO Android Automotive o Android Auto llaman al método onGetRoot() del servicio para obtener el elemento multimedia raíz en la jerarquía de contenido. El elemento multimedia raíz no se muestra, sino que se usa para recuperar más contenido de la app.
  4. El SO Android Automotive o Android Auto llaman al método onLoadChildren() del servicio para obtener los elementos secundarios del elemento multimedia raíz. El SO Android Automotive y Android Auto muestran estos elementos multimedia como el nivel superior de los elementos de contenido. Los elementos de contenido de nivel superior deben poder explorarse.
  5. Si el usuario selecciona un elemento multimedia explorable, se llama de nuevo al método onLoadChildren() del servicio para recuperar los elementos secundarios del elemento de menú seleccionado.
  6. Si el usuario selecciona un elemento multimedia reproducible, el SO Android Automotive o Android Auto llaman al método de devolución de llamada de la sesión multimedia correspondiente para llevar a cabo esa acción.
  7. Si la app lo permite, el usuario también puede hacer búsquedas en el contenido. En este caso, el SO Android Automotive o Android Auto llaman al método onSearch() del servicio.

Compila tu jerarquía de contenido

El SO Android Automotive o Android Auto llaman al servicio de navegador multimedia de tu app para averiguar qué contenido está disponible. Para ello, debes implementar dos métodos en tu servicio de navegador multimedia: onGetRoot() y onLoadChildren().

Implementa onGetRoot

El método onGetRoot() de tu servicio muestra información sobre el nodo raíz de tu jerarquía de contenido. El SO Android Automotive y Android Auto usan este nodo raíz para solicitar el resto del contenido mediante el método onLoadChildren().

En el siguiente fragmento de código, se muestra una implementación simple del método onGetRoot():

Kotlin

    override fun onGetRoot(
        clientPackageName: String,
        clientUid: Int,
        rootHints: Bundle?
    ): BrowserRoot? =
        // Verify that the specified package is allowed to access your
        // content! You'll need to write your own logic to do this.
        if (!isValid(clientPackageName, clientUid)) {
            // If the request comes from an untrusted package, return null.
            // No further calls will be made to other media browsing methods.

            null
        } else MediaBrowserServiceCompat.BrowserRoot(MY_MEDIA_ROOT_ID, null)
    

Java

    @Override
    public BrowserRoot onGetRoot(String clientPackageName, int clientUid,
        Bundle rootHints) {

        // Verify that the specified package is allowed to access your
        // content! You'll need to write your own logic to do this.
        if (!isValid(clientPackageName, clientUid)) {
            // If the request comes from an untrusted package, return null.
            // No further calls will be made to other media browsing methods.

            return null;
        }

        return new MediaBrowserServiceCompat.BrowserRoot(MY_MEDIA_ROOT_ID, null);
    }
    

Para obtener un ejemplo más detallado de este método, consulta el método onGetRoot() en la app de ejemplo de Universal Android Music Player en GitHub.

Agrega validación de paquetes para onGetRoot()

Cuando se realiza una llamada al método onGetRoot() de tu servicio, el paquete de llamadas pasa información de identificación a tu servicio. El servicio puede usar esta información para decidir si el paquete podrá acceder al contenido. Por ejemplo, puedes restringir el acceso al contenido de tu app a una lista de paquetes aprobados mediante la comparación del elemento clientPackageName con tu lista blanca y la verificación del certificado que se usa para firmar el APK del paquete. Si no se puede verificar el paquete, muestra null para denegar el acceso a tu contenido.

Con el fin de proporcionarles a las apps del sistema (como el SO Android Automotive y Android Auto) acceso al contenido, el servicio debe mostrar siempre un valor de BrowserRoot que no sea nulo cuando estas apps del sistema llamen al método onGetRoot().

En el siguiente fragmento de código, se muestra cómo el servicio puede validar que el paquete de llamada es una app del sistema:

fun isKnownCaller(
        callingPackage: String,
        callingUid: Int
    ): Boolean {
        ...
        val isCallerKnown = when {
           // If the system is making the call, allow it.
           callingUid == Process.SYSTEM_UID -> true
           // If the app was signed by the same certificate as the platform
           // itself, also allow it.
           callerSignature == platformSignature -> true
           // ... more cases
        }
        return isCallerKnown
    }
    

Este fragmento de código es un extracto de la clase PackageValidator en la app de ejemplo de Universal Android Music Player en GitHub. Consulta esa clase para obtener un ejemplo más detallado de cómo implementar la validación de paquetes para el método onGetRoot() de tu servicio.

Implementa onLoadChildren()

Después de recibir el objeto del nodo raíz, el SO Android Automotive y Android Auto compilan un menú de nivel superior llamando a onLoadChildren() en el objeto de nodo raíz para obtener sus elementos secundarios. Las apps cliente compilan submenús llamando al mismo método con los objetos del nodo secundario.

Cada nodo de tu jerarquía de contenido está representado por un objeto MediaBrowserCompat.MediaItem. Cada uno de estos elementos multimedia se identifica mediante una string de ID única. Las apps cliente tratan estas strings de ID como tokens opacos. Si una app de cliente quiere examinar un submenú o reproducir un elemento multimedia, pasa el token. Tu app es responsable de asociar el token con el elemento multimedia correspondiente.

Nota: Android Auto y el SO Android Automotive tienen límites estrictos sobre la cantidad de elementos multimedia que pueden mostrarse en cada nivel del menú. Estos límites minimizan las distracciones de los conductores y ayudan a operar la app con comandos por voz. Para obtener más información, consulta Cómo examinar los detalles de contenido y Panel de apps de Android Auto.

En el siguiente fragmento de código, se muestra una implementación simple del método onLoadChildren():

Kotlin

    override fun onLoadChildren(
        parentMediaId: String,
        result: Result<List<MediaBrowserCompat.MediaItem>>
    ) {
        // Assume for example that the music catalog is already loaded/cached.

        val mediaItems: MutableList<MediaBrowserCompat.MediaItem> = mutableListOf()

        // Check if this is the root menu:
        if (MY_MEDIA_ROOT_ID == parentMediaId) {

            // build the MediaItem objects for the top level,
            // and put them in the mediaItems list
        } else {

            // examine the passed parentMediaId to see which submenu we're at,
            // and put the children of that menu in the mediaItems list
        }
        result.sendResult(mediaItems)
    }
    

Java

    @Override
    public void onLoadChildren(final String parentMediaId,
        final Result<List<MediaBrowserCompat.MediaItem>> result) {

        // Assume for example that the music catalog is already loaded/cached.

        List<MediaBrowserCompat.MediaItem> mediaItems = new ArrayList<>();

        // Check if this is the root menu:
        if (MY_MEDIA_ROOT_ID.equals(parentMediaId)) {

            // build the MediaItem objects for the top level,
            // and put them in the mediaItems list
        } else {

            // examine the passed parentMediaId to see which submenu we're at,
            // and put the children of that menu in the mediaItems list
        }
        result.sendResult(mediaItems);
    }
    

Para obtener un ejemplo completo de este método, consulta el método onLoadChildren() en la aplicación de Universal Android Music Player en GitHub.

Aplica los estilos de contenido

Después de crear la jerarquía de contenido con elementos que se pueden reproducir o en los que se puede navegar, puedes aplicar estilos de contenido que determinen cómo se muestran esos elementos en el automóvil.

Puedes usar los siguientes estilos de contenido:

Elementos de listas

Este estilo de contenido prioriza los títulos y los metadatos por sobre las imágenes.

Elementos de cuadrícula

Este estilo de contenido prioriza las imágenes por sobre los títulos y los metadatos.

Establece estilos de contenido predeterminados

Puedes establecer valores globales predeterminados para la manera en la que se muestran los elementos multimedia si incluyes ciertas constantes en el paquete de extras BrowserRoot del método onGetRoot() de tu servicio. Android Auto y el SO Android Automotive leen el paquete de extras asociado con cada elemento en el árbol de navegación y buscan esas constantes para determinar el estilo apropiado.

Usa el siguiente código para declarar estas constantes en tu app:

Kotlin

    /** Declares that ContentStyle is supported */
    val CONTENT_STYLE_SUPPORTED = "android.media.browse.CONTENT_STYLE_SUPPORTED"

    /**
    * Bundle extra indicating the presentation hint for playable media items.
    */
    val CONTENT_STYLE_PLAYABLE_HINT = "android.media.browse.CONTENT_STYLE_PLAYABLE_HINT"

    /**
    * Bundle extra indicating the presentation hint for browsable media items.
    */
    val CONTENT_STYLE_BROWSABLE_HINT = "android.media.browse.CONTENT_STYLE_BROWSABLE_HINT"

    /**
    * Specifies the corresponding items should be presented as lists.
    */
    val CONTENT_STYLE_LIST_ITEM_HINT_VALUE = 1

    /**
    * Specifies that the corresponding items should be presented as grids.
    */
    val CONTENT_STYLE_GRID_ITEM_HINT_VALUE = 2
    

Java

    /** Declares that ContentStyle is supported */
    public static final String CONTENT_STYLE_SUPPORTED =;
       "android.media.browse.CONTENT_STYLE_SUPPORTED";

    /**
    * Bundle extra indicating the presentation hint for playable media items.
    */
    public static final String CONTENT_STYLE_PLAYABLE_HINT =
       "android.media.browse.CONTENT_STYLE_PLAYABLE_HINT";

    /**
    * Bundle extra indicating the presentation hint for browsable media items.
    */
    public static final String CONTENT_STYLE_BROWSABLE_HINT =
       "android.media.browse.CONTENT_STYLE_BROWSABLE_HINT";

    /**
    * Specifies the corresponding items should be presented as lists.
    */
    public static final int CONTENT_STYLE_LIST_ITEM_HINT_VALUE = 1;

    /**
    * Specifies that the corresponding items should be presented as grids.
    */
    public static final int CONTENT_STYLE_GRID_ITEM_HINT_VALUE = 2;
    

Una vez que hayas declarado estas constantes, inclúyelas en el paquete de extras del método onGetRoot() de tu servicio para establecer el estilo predeterminado del contenido. En el siguiente fragmento de código, se muestra cómo establecer el estilo de contenido predeterminado de los elementos explorables en cuadrículas y el correspondiente a los elementos reproducibles en listas:

Kotlin

    @Nullable
    override fun onGetRoot(
        @NonNull clientPackageName: String,
        clientUid: Int,
        @Nullable rootHints: Bundle
    ): BrowserRoot {
        val extras = Bundle()
        extras.putBoolean(CONTENT_STYLE_SUPPORTED, true)
        extras.putInt(CONTENT_STYLE_BROWSABLE_HINT, CONTENT_STYLE_GRID_ITEM_HINT_VALUE)
        extras.putInt(CONTENT_STYLE_PLAYABLE_HINT, CONTENT_STYLE_LIST_ITEM_HINT_VALUE)
        return BrowserRoot(ROOT_ID, extras)
    }
    

Java

    @Nullable
    @Override
    public BrowserRoot onGetRoot(@NonNull String clientPackageName, int clientUid,
       @Nullable Bundle rootHints) {
       Bundle extras = new Bundle();
       extras.putBoolean(CONTENT_STYLE_SUPPORTED, true);
       extras.putInt(CONTENT_STYLE_BROWSABLE_HINT, CONTENT_STYLE_GRID_ITEM_HINT_VALUE);
       extras.putInt(CONTENT_STYLE_PLAYABLE_HINT, CONTENT_STYLE_LIST_ITEM_HINT_VALUE);
       return new BrowserRoot(ROOT_ID, extras);
    }
    

Define estilos de contenido por elemento

La API de estilo de contenido te permite anular el estilo de contenido predeterminado de cualquier elemento secundario de un elemento multimedia explorable. Para anular el valor predeterminado, crea un paquete de extras en el objeto MediaDescription de un elemento multimedia.

En el siguiente fragmento de código, se muestra cómo crear un objeto MediaItem explorable que anule el estilo de contenido predeterminado:

Kotlin

    private fun createBrowsableMediaItem(
        mediaId: String,
        folderName: String,
        iconUri: Uri
    ): MediaBrowser.MediaItem {
        val mediaDescriptionBuilder = MediaDescription.Builder()
        mediaDescriptionBuilder.setMediaId(mediaId)
        mediaDescriptionBuilder.setTitle(folderName)
        mediaDescriptionBuilder.setIconUri(iconUri)
        val extras = Bundle()
        extras.putInt(CONTENT_STYLE_BROWSABLE_HINT, CONTENT_STYLE_LIST_ITEM_HINT_VALUE)
        extras.putInt(CONTENT_STYLE_PLAYABLE_HINT, CONTENT_STYLE_GRID_ITEM_HINT_VALUE)
        mediaDescriptionBuilder.setExtras(extras)
        return MediaBrowser.MediaItem(
            mediaDescriptionBuilder.build(), MediaBrowser.MediaItem.FLAG_BROWSABLE)
    }
    

Java

    private MediaBrowser.MediaItem createBrowsableMediaItem(String mediaId,
       String folderName, Uri iconUri) {
       MediaDescription.Builder mediaDescriptionBuilder = new MediaDescription.Builder();
       mediaDescriptionBuilder.setMediaId(mediaId);
       mediaDescriptionBuilder.setTitle(folderName);
       mediaDescriptionBuilder.setIconUri(iconUri);
       Bundle extras = new Bundle();
       extras.putInt(CONTENT_STYLE_BROWSABLE_HINT, CONTENT_STYLE_LIST_ITEM_HINT_VALUE);
       extras.putInt(CONTENT_STYLE_PLAYABLE_HINT, CONTENT_STYLE_GRID_ITEM_HINT_VALUE);
       mediaDescriptionBuilder.setExtras(extras);
       return new MediaBrowser.MediaItem(
           mediaDescriptionBuilder.build(), MediaBrowser.MediaItem.FLAG_BROWSABLE);
    }
    

Agrupa elementos con sugerencias de títulos

Para agrupar elementos multimedia relacionados, usa una sugerencia que se relacione con ese elemento. Cada elemento multimedia de un grupo debe declarar un paquete de extras en su MediaDescription que use una string idéntica. Esta string se usa como título del grupo y se puede localizar.

En el siguiente fragmento de código, se muestra cómo crear un objeto MediaItem con un encabezado de subgrupo de "Songs":

Kotlin

    val EXTRA_CONTENT_STYLE_GROUP_TITLE_HINT = "android.media.browse.CONTENT_STYLE_GROUP_TITLE_HINT"

    private fun createMediaItem(
        mediaId: String,
        folderName: String,
        iconUri: Uri
    ): MediaBrowser.MediaItem {
        val mediaDescriptionBuilder = MediaDescription.Builder()
        mediaDescriptionBuilder.setMediaId(mediaId)
        mediaDescriptionBuilder.setTitle(folderName)
        mediaDescriptionBuilder.setIconUri(iconUri)
        val extras = Bundle()
        extras.putString(EXTRA_CONTENT_STYLE_GROUP_TITLE_HINT, "Songs")
        mediaDescriptionBuilder.setExtras(extras)
        return MediaBrowser.MediaItem(
            mediaDescriptionBuilder.build(), /* playable or browsable flag*/)
    }
    

Java

    public static final String EXTRA_CONTENT_STYLE_GROUP_TITLE_HINT =
      "android.media.browse.CONTENT_STYLE_GROUP_TITLE_HINT";

    private MediaBrowser.MediaItem createMediaItem(String mediaId, String folderName, Uri iconUri) {
       MediaDescription.Builder mediaDescriptionBuilder = new MediaDescription.Builder();
       mediaDescriptionBuilder.setMediaId(mediaId);
       mediaDescriptionBuilder.setTitle(folderName);
       mediaDescriptionBuilder.setIconUri(iconUri);
       Bundle extras = new Bundle();
       extras.putString(EXTRA_CONTENT_STYLE_GROUP_TITLE_HINT, "Songs");
       mediaDescriptionBuilder.setExtras(extras);
       return new MediaBrowser.MediaItem(
           mediaDescriptionBuilder.build(), /* playable or browsable flag*/);
    }
    

Tu app debe pasar todos los elementos multimedia que deseas agrupar como un bloque contiguo. Por ejemplo, supongamos que quieres mostrar dos grupos de elementos multimedia, "Canciones" y "Álbumes" (en ese orden), y que tu aplicación transmite cinco elementos multimedia en el siguiente orden:

  1. Elemento multimedia A con extras.putString(EXTRA_CONTENT_STYLE_GROUP_TITLE_HINT, "Songs")
  2. Elemento multimedia B con extras.putString(EXTRA_CONTENT_STYLE_GROUP_TITLE_HINT, "Albums")
  3. Elemento multimedia C con extras.putString(EXTRA_CONTENT_STYLE_GROUP_TITLE_HINT, "Songs")
  4. Elemento multimedia D con extras.putString(EXTRA_CONTENT_STYLE_GROUP_TITLE_HINT, "Songs")
  5. Elemento multimedia E con extras.putString(EXTRA_CONTENT_STYLE_GROUP_TITLE_HINT, "Albums")

Debido a que los elementos multimedia de los grupos "Canciones" y "Álbumes" no se mantienen juntos en bloques contiguos, Android Auto y el SO Android Automotive interpretarían esto como los siguientes cuatro grupos:

  • Grupo 1 llamado "Canciones" que contiene el elemento multimedia A
  • Grupo 2 llamado "Álbumes" que contiene el elemento multimedia B
  • Grupo 3 llamado "Canciones" que contiene elementos multimedia C y D
  • Grupo 4 llamado "Álbumes" que contiene el elemento multimedia E

Para mostrar estos elementos en dos grupos, la app pasaría las aplicaciones en el siguiente orden:

  1. Elemento multimedia A con extras.putString(EXTRA_CONTENT_STYLE_GROUP_TITLE_HINT, "Songs")
  2. Elemento multimedia C con extras.putString(EXTRA_CONTENT_STYLE_GROUP_TITLE_HINT, "Songs")
  3. Elemento multimedia D con extras.putString(EXTRA_CONTENT_STYLE_GROUP_TITLE_HINT, "Songs")
  4. Elemento multimedia B con extras.putString(EXTRA_CONTENT_STYLE_GROUP_TITLE_HINT, "Albums")
  5. Elemento multimedia E con extras.putString(EXTRA_CONTENT_STYLE_GROUP_TITLE_HINT, "Albums")

Muestra indicadores de metadatos adicionales

Figura 3: Vista de reproducción con metadatos que identifican la canción y el artista

Puedes incluir indicadores de metadatos adicionales a fin de proporcionar información de un vistazo sobre el contenido en el árbol del navegador multimedia y durante la reproducción. Dentro del árbol de navegación, Android Auto y el SO Android Automotive leen los extras asociados con un elemento y buscan ciertas constantes para determinar qué indicadores mostrar. Durante la reproducción de contenido multimedia, Android Auto y el SO Android Automotive leen los metadatos de la sesión multimedia y buscan determinadas constantes para determinar los indicadores que se mostrarán.

Usa el siguiente código para declarar las constantes del indicador de metadatos en tu app:

Kotlin

    // Bundle extra indicating that a song contains explicit content.
    var EXTRA_IS_EXPLICIT = "android.media.IS_EXPLICIT"

    /**
    * Bundle extra indicating that a media item is available offline.
    * Same as MediaDescriptionCompat.EXTRA_DOWNLOAD_STATUS.
    */
    var EXTRA_IS_DOWNLOADED = "android.media.extra.DOWNLOAD_STATUS"

    /**
    * Bundle extra value indicating that an item should show the corresponding
    * metadata.
    */
    var EXTRA_METADATA_ENABLED_VALUE:Long = 1

    /**
    * Bundle extra indicating the played state of long-form content (such as podcast
    * episodes or audiobooks).
    */
    var EXTRA_PLAY_COMPLETION_STATE = "android.media.extra.PLAYBACK_STATUS"

    /**
    * Value for EXTRA_PLAY_COMPLETION_STATE that indicates the media item has
    * not been played at all.
    */
    var STATUS_NOT_PLAYED = 0

    /**
    * Value for EXTRA_PLAY_COMPLETION_STATE that indicates the media item has
    * been partially played (i.e. the current position is somewhere in the middle).
    */
    var STATUS_PARTIALLY_PLAYED = 1

    /**
    * Value for EXTRA_PLAY_COMPLETION_STATE that indicates the media item has
    * been completed.
    */
    var STATUS_FULLY_PLAYED = 2
    

Java

    // Bundle extra indicating that a song contains explicit content.
    String EXTRA_IS_EXPLICIT = "android.media.IS_EXPLICIT";

    /**
     * Bundle extra indicating that a media item is available offline.
     * Same as MediaDescriptionCompat.EXTRA_DOWNLOAD_STATUS.
     */
    String EXTRA_IS_DOWNLOADED = "android.media.extra.DOWNLOAD_STATUS";

    /**
     * Bundle extra value indicating that an item should show the corresponding
     * metadata.
     */
    long EXTRA_METADATA_ENABLED_VALUE = 1;

    /**
     * Bundle extra indicating the played state of long-form content (such as podcast
     * episodes or audiobooks).
     */
    String EXTRA_PLAY_COMPLETION_STATE = "android.media.extra.PLAYBACK_STATUS";

    /**
     * Value for EXTRA_PLAY_COMPLETION_STATE that indicates the media item has
     * not been played at all.
     */
    int STATUS_NOT_PLAYED = 0;

    /**
     * Value for EXTRA_PLAY_COMPLETION_STATE that indicates the media item has
     * been partially played (i.e. the current position is somewhere in the middle).
     */
    int STATUS_PARTIALLY_PLAYED = 1;

    /**
     * Value for EXTRA_PLAY_COMPLETION_STATE that indicates the media item has
     * been completed.
     */
    int STATUS_FULLY_PLAYED = 2;
    

Una vez que hayas declarado estas constantes, puedes usarlas para mostrar los indicadores de metadatos. A fin de mostrar los indicadores que aparecen mientras el usuario explora el árbol del navegador multimedia, crea un paquete de extras que incluya una o más de estas constantes y pasa ese paquete al método MediaDescription.Builder.setExtras().

En el siguiente fragmento de código, se describe cómo mostrar los indicadores para un elemento multimedia explícito con reproducción parcial:

Kotlin

    val extras = Bundle()
    extras.putLong(EXTRA_IS_EXPLICIT, 1)
    extras.putInt(EXTRA_PLAY_COMPLETION_STATE, STATUS_PARTIALLY_PLAYED)
    val description = MediaDescriptionCompat.Builder()
    .setMediaId(/*...*/)
    .setTitle(resources.getString(/*...*/))
    .setExtras(extras)
    .build()
    return MediaBrowserCompat.MediaItem(description, /* flags */)
    

Java

    Bundle extras = new Bundle();
    extras.putLong(EXTRA_IS_EXPLICIT, 1);
    extras.putInt(EXTRA_PLAY_COMPLETION_STATE, STATUS_PARTIALLY_PLAYED);

    MediaDescriptionCompat description = new MediaDescriptionCompat.Builder()
      .setMediaId(/*...*/)
      .setTitle(resources.getString(/*...*/))
      .setExtras(extras)
      .build();
    return new MediaBrowserCompat.MediaItem(description, /* flags */);
    

Si quieres mostrar los indicadores de un elemento multimedia que se está reproduciendo, puedes declarar los valores de Long EXTRA_IS_EXPLICIT o EXTRA_IS_DOWNLOADED en el método MediaMetadata.Builder() de tu mediaSession. No puedes mostrar el indicador EXTRA_PLAY_COMPLETION_STATE en la vista de reproducción.

En el siguiente fragmento de código, se muestra cómo indicar que la canción actual de la vista de reproducción es explícita y que se descargó:

Kotlin

    mediaSession.setMetadata(
      MediaMetadata.Builder()
      .putString(
        MediaMetadata.METADATA_KEY_DISPLAY_TITLE, "Song Name")
      .putString(
        MediaMetadata.METADATA_KEY_DISPLAY_SUBTITLE, "Artist name")
      .putString(MediaMetadata.METADATA_KEY_ALBUM_ART_URI, albumArtUri.toString())
      .putLong(
        EXTRA_IS_EXPLICIT, EXTRA_METADATA_ENABLED_VALUE)
      .putLong(
        EXTRA_IS_DOWNLOADED, EXTRA_METADATA_ENABLED_VALUE)
      .build())
    

Java

    mediaSession.setMetadata(
        new MediaMetadata.Builder()
            .putString(
                MediaMetadata.METADATA_KEY_DISPLAY_TITLE, "Song Name")
            .putString(
                MediaMetadata.METADATA_KEY_DISPLAY_SUBTITLE, "Artist name")
            .putString(MediaMetadata.METADATA_KEY_ALBUM_ART_URI, albumArtUri.toString())
            .putLong(
                EXTRA_IS_EXPLICIT, EXTRA_METADATA_ENABLED_VALUE)
            .putLong(
                EXTRA_IS_DOWNLOADED, EXTRA_METADATA_ENABLED_VALUE)
            .build());
    

Con el fin de ayudar a un usuario a explorar el contenido, tu app puede permitirle examinar un grupo de resultados relacionados cada vez que realiza una búsqueda por voz. Android Auto y el SO Android Automotive muestran estos resultados como una barra "Mostrar más resultados" en la interfaz.

Figura 4: Opción "Más resultados" para ver resultados de la búsqueda relacionados

Para mostrar resultados de la búsqueda explorables, debes crear una constante y luego incluirla en el paquete de extras del método onGetRoot() de tu servicio.

En el siguiente fragmento de código, se muestra cómo habilitar la compatibilidad en el método onGetRoot():

Kotlin

    // Bundle extra indicating that onSearch() is supported
    val EXTRA_MEDIA_SEARCH_SUPPORTED = "android.media.browse.SEARCH_SUPPORTED"

    @Nullable
    fun onGetRoot(
        @NonNull clientPackageName: String,
        clientUid: Int,
        @Nullable rootHints: Bundle
    ): BrowserRoot {
        val extras = Bundle()
        extras.putBoolean(EXTRA_MEDIA_SEARCH_SUPPORTED, true)
        return BrowserRoot(ROOT_ID, extras)
    }
    

Java

    public static final String EXTRA_MEDIA_SEARCH_SUPPORTED =
       "android.media.browse.SEARCH_SUPPORTED";

    @Nullable
    @Override
    public BrowserRoot onGetRoot(@NonNull String clientPackageName, int clientUid,
       @Nullable Bundle rootHints) {
       Bundle extras = new Bundle();
       extras.putBoolean(EXTRA_MEDIA_SEARCH_SUPPORTED, true);
       return new BrowserRoot(ROOT_ID, extras);
    }
    

Para comenzar a proporcionar resultados de la búsqueda, anula el método onSearch() en el servicio de navegador multimedia. Android Auto y el SO Android Automotive reenvían los términos de búsqueda de un usuario a este método cada vez que el usuario invoca la opción "Mostrar más resultados". Puedes organizar los resultados de la búsqueda del método onSearch() de tu servicio mediante elementos de título para que sean más explorables. Por ejemplo, si la app reproduce música, podrás organizar los resultados por "Álbum", "Artista" y "Canciones".

En el siguiente fragmento de código, se muestra una implementación simple del método onSearch():

Kotlin

    fun onSearch(query: String, extras: Bundle) {
      // Detach from results to unblock the caller (if a search is expensive)
      result.detach()
      object:AsyncTask() {
        internal var searchResponse:ArrayList
        internal var succeeded = false
        protected fun doInBackground(vararg params:Void):Void {
          searchResponse = ArrayList()
          if (doSearch(query, extras, searchResponse))
          {
            succeeded = true
          }
          return null
        }
        protected fun onPostExecute(param:Void) {
          if (succeeded)
          {
            // Sending an empty List informs the caller that there were no results.
            result.sendResult(searchResponse)
          }
          else
          {
            // This invokes onError() on the search callback
            result.sendResult(null)
          }
          return null
        }
      }.execute()
    }
    // Populates resultsToFill with search results. Returns true on success or false on error
    private fun doSearch(
        query: String,
        extras: Bundle,
        resultsToFill: ArrayList
    ): Boolean {
      // Implement this method
    }
    

Java

    @Override
    public void onSearch(final String query, final Bundle extras,
                            Result<ArrayList<MediaItem>> result) {

      // Detach from results to unblock the caller (if a search is expensive)
      result.detach();

      new AsyncTask<Void, Void, Void>() {
        ArrayList<MediaItem> searchResponse;
        boolean succeeded = false;
        @Override
        protected Void doInBackground(Void... params) {
          searchResponse = new ArrayList<MediaItem>();
          if (doSearch(query, extras, searchResponse)) {
            succeeded = true;
          }
          return null;
        }

        @Override
        protected void onPostExecute(Void param) {
          if (succeeded) {
           // Sending an empty List informs the caller that there were no results.
           result.sendResult(searchResponse);
          } else {
            // This invokes onError() on the search callback
            result.sendResult(null);
          }
          return null;
        }
      }.execute()
    }

    /** Populates resultsToFill with search results. Returns true on success or false on error */
    private boolean doSearch(String query, Bundle extras, ArrayList<MediaItem> resultsToFill) {
        // Implement this method
    }
    

Habilita el control de reproducción

Android Auto y el SO Android Automotive envían comandos de control de reproducción mediante el elemento MediaSessionCompat del servicio. Debes registrar una sesión y, luego, implementar los métodos de devolución de llamada asociados.

Registra una sesión multimedia

En el método onCreate() de tu navegador multimedia, crea un objeto MediaSessionCompat y, luego, llama a setSessionToken() a fin de registrar la sesión multimedia.

En el siguiente fragmento de código, se muestra cómo crear y registrar una sesión multimedia:

Kotlin

    override fun onCreate() {
        super.onCreate()

        ...
        // Start a new MediaSession
        val session = MediaSessionCompat(this, "session tag").apply {
            // Set a callback object to handle play control requests, which
            // implements MediaSession.Callback
            setCallback(MyMediaSessionCallback())
        }
        sessionToken = session.sessionToken

        ...
    }
    

Java

    public void onCreate() {
        super.onCreate();

        ...
        // Start a new MediaSession
        MediaSessionCompat session = new MediaSessionCompat(this, "session tag");
        setSessionToken(session.getSessionToken());

        // Set a callback object to handle play control requests, which
        // implements MediaSession.Callback
        session.setCallback(new MyMediaSessionCallback());

        ...
    }
    

Cuando creas el objeto de la sesión multimedia, estableces un objeto de devolución de llamada que se usa para manejar las solicitudes de control de reproducción. Para crear este objeto de devolución de llamada, debes proporcionar una implementación de la clase MediaSessionCompat.Callback para tu app. En la siguiente sección, se explica cómo implementar este objeto.

Implementa comandos de reproducción

Cuando un usuario solicita la reproducción de un elemento multimedia de tu app, el SO Android Automotive y Android Auto usan la clase MediaSessionCompat.Callback del objeto MediaSessionCompat de tu app que se obtuvo del servicio de navegador multimedia de tu app. Cuando un usuario desea controlar la reproducción de contenido, como pausar la reproducción u omitir la siguiente pista, Android Auto y el SO Android Automotive invocan a uno de los métodos del objeto de devolución de llamada.

Para controlar la reproducción de contenido, tu app debe extender la clase abstracta MediaSessionCompat.Callback e implementar los métodos compatibles con tu app.

Debes implementar todos los siguientes métodos de devolución de llamada que correspondan al tipo de contenido que ofrece tu app:

onPrepare()
Se invoca cuando cambia la fuente multimedia. El SO Android Automotive también invoca este método de inmediato después del inicio. Debes implementar este método en tu app multimedia.
onPlay()
Se invoca si el usuario opta por reproducir contenido sin elegir un elemento específico. Tu app debe reproducir este contenido predeterminado. Si la reproducción se detuvo con onPause(), tu app debería reanudar la reproducción.

Nota: Tu app no debería comenzar a reproducir música automáticamente cuando el SO Android Automotive o Android Auto se conecten a tu servicio de navegador multimedia. Para obtener más información, consulta Configura el estado de reproducción inicial.

onPlayFromMediaId()
Se invoca cuando el usuario elige reproducir un elemento específico. El método recibe el ID que tu servicio de navegador multimedia asignó al elemento multimedia en tu jerarquía de contenido.
onPlayFromSearch()
Se invoca cuando el usuario elige reproducir un elemento desde una búsqueda. La app debe hacer la elección apropiada según la string de búsqueda que se pasa.
onPause()
Se invoca cuando el usuario elige pausar la reproducción.
onSkipToNext()
Se invoca cuando el usuario elige omitir el elemento siguiente.
onSkipToPrevious()
Se invoca cuando el usuario elige omitir el elemento anterior.
onStop()
Se invoca cuando el usuario elige detener la reproducción.

La app debe anular estos métodos para proporcionar la funcionalidad deseada. No es necesario que implementes un método si tu app no lo admite. Por ejemplo, si tu app reprodujera una transmisión en vivo (como una transmisión deportiva), no tendría sentido implementar el método onSkipToNext(); podrías usar la implementación predeterminada de onSkipToNext() en su lugar.

Tu app no necesita una lógica especial para reproducir contenido en las bocinas del vehículo. Si la app recibe una solicitud para reproducir contenido, deberá reproducir el audio de la misma manera que lo hace normalmente (por ejemplo, por el altavoz o los auriculares del teléfono). Android Auto y el SO Android Automotive envían automáticamente el contenido de audio al sistema del automóvil para reproducirlo en las bocinas del automóvil.

Para obtener más información acerca de cómo reproducir contenido de audio, consulta Cómo reproducir contenido multimedia, Cómo administrar la reproducción de audio y ExoPlayer.

Establece acciones de reproducción estándar

Android Auto y el SO Android Automotive muestran los controles de reproducción según las acciones habilitadas en el objeto PlaybackStateCompat.

De forma predeterminada, la app debe admitir las siguientes acciones:

Además, te recomendamos crear una cola de reproducción que se pueda mostrar al usuario. Para ello, debes llamar a los métodos setQueue() y setQueueTitle(), habilitar la acción ACTION_SKIP_TO_QUEUE_ITEM y definir la devolución de llamada onSkipToQueueItem()

Android Auto y el SO Android Automotive muestran los botones para cada acción habilitada, así como la cola de reproducción si eliges crear una.

Reserva un espacio sin usar

Android Auto y el SO Android Automotive reservan espacio en la IU para las acciones ACTION_SKIP_TO_PREVIOUS y ACTION_SKIP_TO_NEXT. Además, Android Auto reserva espacio para la cola de reproducción. Si tu app no admite una de estas funciones, Android Auto y el SO Android Automotive usan el espacio para mostrar cualquier acción personalizada que crees.

Si no quieres llenar esos espacios con acciones personalizadas, puedes reservarlos para que Android Auto y el SO Android Automotive dejen el espacio en blanco cuando tu app no admita la función correspondiente. Para ello, llama al método setExtras() con un paquete de extras que contenga constantes que correspondan a cada una de las funciones reservadas. Configura en true cada constante para la que desees reservar espacio.

En el siguiente fragmento de código, se muestran las constantes que puedes usar para reservar el espacio sin usar:

Kotlin

    // Use these extras to show the transport control buttons for the corresponding actions,
    // even when they are not enabled in the PlaybackState.
    private const val PLAYBACK_SLOT_RESERVATION_SKIP_TO_NEXT =
            "android.media.playback.ALWAYS_RESERVE_SPACE_FOR.ACTION_SKIP_TO_NEXT"
    private const val PLAYBACK_SLOT_RESERVATION_SKIP_TO_PREV =
            "android.media.playback.ALWAYS_RESERVE_SPACE_FOR.ACTION_SKIP_TO_PREVIOUS"
    private const val PLAYBACK_SLOT_RESERVATION_QUEUE =
            "android.media.playback.ALWAYS_RESERVE_SPACE_FOR.ACTION_QUEUE"
    

Java

    // Use these extras to show the transport control buttons for the corresponding actions,
    // even when they are not enabled in the PlaybackState.
    private static final String PLAYBACK_SLOT_RESERVATION_SKIP_TO_NEXT =
        "android.media.playback.ALWAYS_RESERVE_SPACE_FOR.ACTION_SKIP_TO_NEXT";
    private static final String PLAYBACK_SLOT_RESERVATION_SKIP_TO_PREV =
        "android.media.playback.ALWAYS_RESERVE_SPACE_FOR.ACTION_SKIP_TO_PREVIOUS";
    private static final String PLAYBACK_SLOT_RESERVATION_QUEUE =
        "android.media.playback.ALWAYS_RESERVE_SPACE_FOR.ACTION_QUEUE";
    

Establece el PlaybackState inicial

A medida que Android Auto y el SO Android Automotive se comunican con tu servicio de navegador multimedia, tu sesión multimedia comunica el estado de la reproducción de contenido mediante PlaybackState. Tu aplicación no debería iniciar automáticamente la reproducción de música cuando el SO Android Automotive o Android Auto se conecten a tu servicio de navegador multimedia. En su lugar, confía en Android Auto y el SO Android Automotive para reanudar o iniciar la reproducción según el estado del automóvil o las acciones del usuario.

Para ello, configura el elemento PlaybackState inicial de tu sesión multimedia en STATE_STOPPED, STATE_PAUSED, STATE_NONE o STATE_ERROR.

Agrega acciones de reproducción personalizadas

Puedes agregar acciones de reproducción personalizadas para mostrar las acciones adicionales que admite tu app multimedia. Si el espacio lo permite (y no está reservado), Android agrega las acciones personalizadas a los controles de transporte. De lo contrario, las acciones personalizadas se muestran en el menú ampliado. Las acciones personalizadas se muestran en el orden en que se agregan a PlaybackState.

Puedes agregar estas acciones con el método addCustomAction() en la clase PlaybackStateCompat.Builder.

En el siguiente fragmento de código, se muestra cómo agregar una acción "Iniciar un canal de radio":

Kotlin

    stateBuilder.addCustomAction(
            PlaybackStateCompat.CustomAction.Builder(
                    CUSTOM_ACTION_START_RADIO_FROM_MEDIA,
                    resources.getString(R.string.start_radio_from_media),
                    startRadioFromMediaIcon
            ).run {
                setExtras(customActionExtras)
                build()
            }
    )
    

Java

    stateBuilder.addCustomAction(new PlaybackStateCompat.CustomAction.Builder(
        CUSTOM_ACTION_START_RADIO_FROM_MEDIA,
        resources.getString(R.string.start_radio_from_media), startRadioFromMediaIcon)
        .setExtras(customActionExtras)
        .build());
    

Para obtener un ejemplo detallado de este método, consulta el método setCustomAction() en la app de ejemplo de Universal Android Music Player en GitHub.

Después de crear tu acción personalizada, tu sesión multimedia puede responder a la acción mediante la anulación del método onCustomAction().

En el siguiente fragmento de código, se muestra cómo tu app puede responder a una acción "Iniciar un canal de radio":

Kotlin

    override fun onCustomAction(action: String, extras: Bundle?) {
        when(action) {
            CUSTOM_ACTION_START_RADIO_FROM_MEDIA -> {
                ...
            }
        }
    }
    

Java

    @Override
    public void onCustomAction(@NonNull String action, Bundle extras) {
        if (CUSTOM_ACTION_START_RADIO_FROM_MEDIA.equals(action)) {
            ...
        }
    }
    

Para obtener un ejemplo detallado de este método, consulta el método onCustomAction en la app de ejemplo de Universal Android Music Player en GitHub.

Íconos para acciones personalizadas

Cada acción personalizada que creas requiere un recurso de ícono. Las apps de vehículos se pueden ejecutar en diferentes tamaños y densidades de pantallas, de modo que los íconos que proporcionas deben ser elementos de diseño de vectores. Un elemento de diseño de vector te permite escalar elementos sin perder los detalles y también facilita la alineación de los bordes y las esquinas según los límites de píxeles en resoluciones más pequeñas.

Proporciona un estilo de ícono alternativo para las acciones inhabilitadas

Para los casos en los que una acción personalizada no está disponible para el contexto actual, reemplaza el ícono de acción personalizada por un ícono alternativo que muestre que la acción está inhabilitada.

Figura 5: Ejemplo de íconos de acción personalizada inhabilitada

Cómo admitir acciones de voz

Tu app multimedia debe admitir acciones de voz para ayudar a proporcionar a los conductores una experiencia más segura y conveniente, que minimice las distracciones. Por ejemplo, si tu app ya está reproduciendo un elemento multimedia, el usuario puede decir "Reproducir Rapsodia bohemia" para indicarle a tu app que reproduzca una canción diferente sin mirar ni tocar la pantalla del automóvil.

Para obtener un ejemplo detallado sobre cómo implementar acciones de reproducción habilitadas para voz en tu app, consulta El Asistente de Google y las apps multimedia.

Cómo implementar protecciones contra distracción

Debido a que el teléfono del usuario está conectado a las bocinas del vehículo mientras usa Android Auto, debes tomar precauciones adicionales para ayudar a evitar la distracción del conductor.

Detecta el modo de vehículo

Las apps multimedia de Android Auto no deben iniciar la reproducción de audio en las bocinas del vehículo a menos que el usuario inicie la reproducción de forma consciente (por ejemplo, presionando el botón de reproducción en tu app). Incluso una alarma programada por el usuario desde la app multimedia no debería iniciar la reproducción de música en las bocinas del vehículo. Para cumplir con este requisito, la app deberá determinar si el teléfono está en modo de vehículo antes de reproducir audio. Tu app puede comprobar si el teléfono está en modo automóvil. Para ello, debe llamar al método getCurrentModeType().

Si el teléfono del usuario está en modo de vehículo, las apps multimedia que admiten alarmas deben hacer una de las siguientes acciones:

  • Desactivar la alarma
  • Reproducir la alarma mediante STREAM_ALARM y proporcionar una IU en la pantalla del teléfono para desactivarla

En el siguiente fragmento de código, se muestra cómo comprobar si una app se está ejecutando en modo de vehículo:

Kotlin

    fun isCarUiMode(c: Context): Boolean {
        val uiModeManager = c.getSystemService(Context.UI_MODE_SERVICE) as UiModeManager
        return if (uiModeManager.currentModeType == Configuration.UI_MODE_TYPE_CAR) {
            LogHelper.d(TAG, "Running in Car mode")
            true
        } else {
            LogHelper.d(TAG, "Running in a non-Car mode")
            false
        }
    }
    

Java

     public static boolean isCarUiMode(Context c) {
          UiModeManager uiModeManager = (UiModeManager) c.getSystemService(Context.UI_MODE_SERVICE);
          if (uiModeManager.getCurrentModeType() == Configuration.UI_MODE_TYPE_CAR) {
                LogHelper.d(TAG, "Running in Car mode");
                return true;
          } else {
              LogHelper.d(TAG, "Running in a non-Car mode");
              return false;
            }
      }
    

Controla los anuncios de medios

De manera predeterminada, Android Auto muestra una notificación cuando los metadatos de los elementos multimedia cambian durante una sesión de reproducción de audio. Cuando una app multimedia cambia de reproducir música a ejecutar un anuncio, mostrar una notificación al usuario es una distracción (y es innecesario). Para evitar que Android Auto muestre una notificación en este caso, debes configurar la clave de metadatos de medios android.media.metadata.ADVERTISEMENT en 1, como se muestra en el siguiente fragmento de código:

Kotlin

    const val EXTRA_METADATA_ADVERTISEMENT = "android.media.metadata.ADVERTISEMENT"
    ...
    override fun onPlayFromMediaId(mediaId: String, extras: Bundle?) {
        MediaMetadataCompat.Builder().apply {
            // ...
            if (isAd(mediaId)) {
                putLong(EXTRA_METADATA_ADVERTISEMENT, 1)
            }
            // ...
            mediaSession.setMetadata(build())
        }
    }
    

Java

    public static final String EXTRA_METADATA_ADVERTISEMENT =
        "android.media.metadata.ADVERTISEMENT";

    @Override
    public void onPlayFromMediaId(String mediaId, Bundle extras) {
        MediaMetadataCompat.Builder builder = new MediaMetadataCompat.Builder();
        // ...
        if (isAd(mediaId)) {
            builder.putLong(EXTRA_METADATA_ADVERTISEMENT, 1);
        }
        // ...
        mediaSession.setMetadata(builder.build());
    }
    

Cómo controlar los errores generales

Cuando la app experimenta un error, debes establecer el estado de reproducción en STATE_ERROR y proporcionar un mensaje de error con el método setErrorMessage(). Los mensajes de error se deben mostrar al usuario y se deben localizar según la configuración regional actual del usuario. Android Auto y el SO Android Automotive pueden mostrar el mensaje de error al usuario.

Para obtener más información sobre los estados de error, consulta Cómo trabajar con una sesión multimedia: estados y errores.

Si un usuario de Android Auto necesita abrir la app del teléfono para resolver un error, el mensaje deberá proporcionar esa información al usuario. Por ejemplo, el mensaje de error deberá decir "Acceder a [nombre de tu app]" en lugar de "Acceder".

Otros recursos