Créer des applications multimédias pour voitures

Android Auto et Android Automotive OS vous permettent de proposer le contenu de votre application multimédia aux utilisateurs dans leur voiture. Une application multimédia pour voiture doit fournir un service de navigateur multimédia pour qu'Android Auto et Android Automotive OS (ou une autre application proposant un navigateur multimédia) puissent découvrir et afficher votre contenu.

Dans ce guide, nous partons du principe que vous disposez déjà d'une application multimédia qui lit du contenu audio sur un téléphone et que cette application est conforme à l'architecture des applications multimédias Android.

Ce guide décrit les composants requis d'un MediaBrowserService et d'une MediaSession dont votre application a besoin pour fonctionner avec Android Auto ou Android Automotive OS. Une fois que vous avez terminé l'infrastructure multimédia principale, vous pouvez ajouter la compatibilité avec Android Auto et Android Automotive OS à votre application multimédia.

Avant de commencer

  1. Consultez la documentation de l'API multimédia Android.
  2. Pour obtenir des conseils de conception, consultez la section Créer des applications multimédias.
  3. Passez en revue les termes et concepts clés abordés dans cette section.

Termes et concepts clés

Service de navigateur multimédia
Service Android implémenté par votre application multimédia et conforme à l'API MediaBrowserServiceCompat. Votre application utilise ce service pour présenter son contenu.
Navigateur multimédia
API utilisée par les applications multimédias pour découvrir les services de navigateur multimédia et en afficher le contenu. Android Auto et Android Automotive OS utilisent un navigateur multimédia pour trouver le service de navigateur multimédia de votre application.
Élément multimédia

Le navigateur multimédia organise son contenu dans une arborescence d'objets MediaItem. Un élément multimédia peut avoir l'un des indicateurs suivants, ou les deux :

  • FLAG_PLAYABLE : indique que l'élément est une feuille de l'arborescence de contenu. L'élément représente un seul flux audio, comme une chanson d'un album, un chapitre d'un livre audio ou un épisode d'un podcast.
  • FLAG_BROWSABLE : indique que l'élément est un nœud de l'arborescence de contenu et qu'il possède des éléments enfants. Par exemple, l'élément représente un album, et ses enfants sont les chansons qui le constituent.

Un élément multimédia qui est à la fois consultable et lisible se comporte comme une playlist. Vous pouvez sélectionner l'élément proprement dit pour lire tous ses éléments enfants ou vous pouvez parcourir ses éléments enfants.

Optimisé pour les véhicules

Activité d'une application Android Automotive OS conforme aux consignes de conception des applications Android Automotive OS. L'interface de ces activités n'est pas dessinée par Android Automotive OS. Vous devez donc vous assurer que votre application respecte les consignes de conception. En règle générale, cela inclut des polices et des éléments tactiles de plus grande taille, la compatibilité avec les modes Jour et Nuit, ainsi que des rapports de contraste plus élevés.

Les interfaces utilisateur optimisées pour les véhicules ne peuvent être affichées que lorsque les restrictions liées à l'expérience utilisateur du véhicule (CUXR) ne sont pas en vigueur. En effet, elles peuvent nécessiter une plus grande attention ou interaction de la part de l'utilisateur. Ces restrictions ne s'appliquent pas lorsque la voiture est à l'arrêt, mais elles s'appliquent systématiquement lorsque la voiture est en mouvement.

Il n'est pas nécessaire de concevoir des activités pour Android Auto, car cette fonctionnalité dessine sa propre interface optimisée pour les véhicules à l'aide des informations provenant de votre service de navigateur multimédia.

Configurer les fichiers manifestes de votre application

Avant de pouvoir créer votre service de navigateur multimédia, vous devez configurer les fichiers manifestes de votre application.

Déclarer votre service de navigateur multimédia

Android Auto et Android Automotive OS se connectent à votre application via votre service de navigateur multimédia afin de parcourir les éléments multimédias. Déclarez votre service de navigateur multimédia dans le fichier manifeste pour permettre à Android Auto et à Android Automotive OS de découvrir le service et de se connecter à votre application.

L'extrait de code suivant montre comment déclarer le service de navigateur multimédia dans le fichier manifeste. Ajoutez ce code dans le fichier manifeste de votre module Android Automotive OS ainsi que dans celui de votre application pour téléphone.

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

Spécifier des icônes d'application

Vous devez spécifier les icônes d'applications qu'Android Auto et Android Automotive OS peuvent utiliser pour représenter votre application dans l'UI du système. Deux types d'icônes sont obligatoires :

  • Icône de lanceur
  • Icône d'attribution
.

Icône de lanceur

L'icône de lanceur représente votre application dans l'UI du système, par exemple dans le Lanceur d'applications et la barre d'icônes. Vous pouvez indiquer que vous souhaitez utiliser l'icône de votre application mobile pour représenter votre application multimédia pour voitures à l'aide de la déclaration suivante du fichier manifeste :

<application
    ...
    android:icon="@mipmap/ic_launcher"
    ...
/>

Pour utiliser une icône différente de celle de votre application mobile, définissez la propriété android:icon sur l'élément <service> de votre service de navigateur multimédia dans le fichier manifeste :

<application>
    ...
    <service
        ...
        android:icon="@mipmap/auto_launcher"
        ...
    />
</application>

Icône d'attribution

Figure 1 : Icône d'attribution sur la fiche multimédia

L'icône d'attribution est utilisée là où le contenu multimédia est prioritaire, par exemple sur les fiches multimédias. Nous vous conseillons de réutiliser la petite icône associée aux notifications. Cette icône doit être monochrome. Vous pouvez spécifier une icône représentant votre application à l'aide de la déclaration de fichier manifeste suivante :

<application>
    ...
    <meta-data
        android:name="androidx.car.app.TintableAttributionIcon"
        android:resource="@drawable/ic_status_icon" />
    ...
</application>

Créer votre service de navigateur multimédia

Pour créer un service de navigateur multimédia, développez la classe MediaBrowserServiceCompat. Android Auto et Android Automotive OS peuvent alors utiliser votre service pour effectuer les opérations suivantes :

  • Parcourir la hiérarchie de contenu de votre application afin de présenter un menu à l'utilisateur
  • Obtenir le jeton de l'objet MediaSessionCompat de votre application afin de contrôler la lecture audio

Vous pouvez également utiliser votre service de navigateur multimédia pour permettre à d'autres clients d'accéder au contenu multimédia à partir de votre application. Il peut s'agir d'autres applications sur le téléphone d'un utilisateur ou d'autres clients distants.

Workflow du service de navigateur multimédia

Cette section explique comment Android Automotive OS et Android Auto interagissent avec votre service de navigateur multimédia au cours d'un workflow utilisateur standard.

  1. L'utilisateur lance votre application sur Android Automotive OS ou sur Android Auto.
  2. Android Automotive OS ou Android Auto contacte le service de navigateur multimédia de votre application à l'aide de la méthode onCreate(). Dans l'implémentation de la méthode onCreate(), vous devez créer et enregistrer un objet MediaSessionCompat ainsi que son objet de rappel.
  3. Android Automotive OS ou Android Auto appelle la méthode onGetRoot() de votre service pour obtenir l'élément multimédia racine de votre hiérarchie de contenu. L'élément multimédia racine n'est pas affiché, mais utilisé pour récupérer plus de contenu de votre application.
  4. Android Automotive OS ou Android Auto appelle la méthode onLoadChildren() de votre service pour obtenir les enfants de l'élément multimédia racine. Android Automotive OS et Android Auto affichent ces éléments multimédias en tant que premier niveau d'éléments de contenu. Consultez la section Structurer le menu racine de cette page pour en savoir plus sur les attentes du système à ce niveau.
  5. Si l'utilisateur sélectionne un élément multimédia consultable, la méthode onLoadChildren() de votre service est de nouveau appelée pour récupérer les enfants de l'élément de menu sélectionné.
  6. Si l'utilisateur sélectionne un élément multimédia lisible, Android Automotive OS ou Android Auto appelle la méthode de rappel de la session multimédia appropriée pour effectuer cette action.
  7. Si votre application l'y autorise, l'utilisateur peut également effectuer des recherches dans votre contenu. Dans ce cas, Android Automotive OS ou Android Auto appelle la méthode onSearch() de votre service.

Créer une hiérarchie de contenu

Android Auto et Android Automotive OS appellent le service de navigateur multimédia de votre application pour connaître le contenu disponible. Pour cela, vous devez implémenter deux méthodes dans votre service de navigateur multimédia : onGetRoot() et onLoadChildren().

Implémenter onGetRoot

La méthode onGetRoot() de votre service renvoie des informations sur le nœud racine de votre hiérarchie de contenu. Android Auto et Android Automotive OS utilisent ce nœud racine pour demander le reste du contenu à l'aide de la méthode onLoadChildren().

L'extrait de code suivant illustre une implémentation simple de la méthode 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);
}

Pour un exemple plus détaillé de cette méthode, consultez la méthode onGetRoot() dans l'application exemple Universal Android Music Player sur GitHub.

Ajouter la validation de package pour onGetRoot()

Lorsqu'un appel est effectué vers la méthode onGetRoot() de votre service, le package à l'origine de l'appel transmet des informations d'identification à votre service. Votre service peut utiliser ces informations pour déterminer si ce package peut accéder à votre contenu. Par exemple, vous pouvez limiter l'accès au contenu de votre application à une liste de packages approuvés en comparant clientPackageName à votre liste d'autorisation et en vérifiant le certificat utilisé pour la signature de l'APK du package. Si le package ne peut pas être vérifié, renvoyez null pour refuser l'accès à votre contenu.

Pour que les applications système (comme Android Auto et Android Automotive OS) puissent accéder à votre contenu, votre service doit toujours renvoyer une valeur BrowserRoot non nulle lorsque ces applications appellent la méthode onGetRoot(). La signature de l'application système Android Automotive OS peut varier selon la marque et le modèle de la voiture. Vous devez donc autoriser les connexions provenant de toutes les applications système pour assurer une bonne compatibilité avec Android Automotive OS.

L'extrait de code suivant montre comment votre service peut vérifier que le package à l'origine de l'appel est bien une application système :

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
}

Cet extrait de code est extrait de la classe PackageValidator dans l'application exemple Universal Android Music Player sur GitHub. Reportez-vous à cette classe pour obtenir un exemple plus détaillé d'implémentation de la validation de package pour la méthode onGetRoot() de votre service.

En plus d'autoriser les applications système, vous devez permettre à l'Assistant Google de se connecter à votre MediaBrowserService. Notez que l'Assistant Google utilise des noms de package distincts pour le téléphone (ce qui inclut Android Auto) et pour Android Automotive OS.

Implémenter onLoadChildren()

Après avoir reçu votre objet de nœud racine, Android Auto et Android Automotive OS créent un menu de niveau supérieur en appelant onLoadChildren() sur cet objet pour obtenir ses enfants. Les applications clientes créent des sous-menus en appelant cette même méthode à l'aide d'objets de nœuds enfants.

Chaque nœud de votre hiérarchie de contenu est représenté par un objet MediaBrowserCompat.MediaItem. Chacun de ces éléments multimédias est identifié par une chaîne d'identifiant unique. Les applications clientes traitent ces chaînes d'identifiant comme des jetons opaques. Lorsqu'une application cliente souhaite accéder à un sous-menu ou lire un élément multimédia, elle transmet le jeton. Votre application est chargée d'associer le jeton à l'élément multimédia approprié.

L'extrait de code suivant illustre une implémentation simple de la méthode 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 whether 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 whether 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);
}

Pour obtenir un exemple complet de cette méthode, consultez la méthode onLoadChildren() dans l'application exemple Universal Android Music Player sur GitHub.

Structurer le menu racine

Figure 2 : Contenu racine affiché sous la forme d'onglets de navigation

Android Auto et Android Automotive OS présentent des contraintes spécifiques concernant la structure du menu racine. Elles sont transmises à MediaBrowserService via des suggestions de racine qui peuvent être lues au moyen de l'argument Bundle transmis à onGetRoot(). En suivant ces suggestions, le système affiche le contenu racine de manière optimale sous forme d'onglets de navigation. Si vous ne suivez pas ces suggestions, une partie du contenu racine peut être supprimée ou rendue moins visible par le système. Deux suggestions sont envoyées :

Utilisez le code suivant pour lire les suggestions de racine pertinentes :

Kotlin

import androidx.media.utils.MediaConstants

// Later, in your MediaBrowserServiceCompat.
override fun onGetRoot(
    clientPackageName: String,
    clientUid: Int,
    rootHints: Bundle
): BrowserRoot {

  val maximumRootChildLimit = rootHints.getInt(
      MediaConstants.BROWSER_ROOT_HINTS_KEY_ROOT_CHILDREN_LIMIT,
      /* defaultValue= */ 4)
  val supportedRootChildFlags = rootHints.getInt(
      MediaConstants.BROWSER_ROOT_HINTS_KEY_ROOT_CHILDREN_SUPPORTED_FLAGS,
      /* defaultValue= */ MediaItem.FLAG_BROWSABLE)

  // Rest of method...
}

Java

import androidx.media.utils.MediaConstants;

// Later, in your MediaBrowserServiceCompat.
@Override
public BrowserRoot onGetRoot(
    String clientPackageName, int clientUid, Bundle rootHints) {

    int maximumRootChildLimit = rootHints.getInt(
        MediaConstants.BROWSER_ROOT_HINTS_KEY_ROOT_CHILDREN_LIMIT,
        /* defaultValue= */ 4);
    int supportedRootChildFlags = rootHints.getInt(
        MediaConstants.BROWSER_ROOT_HINTS_KEY_ROOT_CHILDREN_SUPPORTED_FLAGS,
        /* defaultValue= */ MediaItem.FLAG_BROWSABLE);

    // Rest of method...
}

Vous pouvez brancher la logique de votre hiérarchie de contenu en fonction des valeurs de ces suggestions, en particulier si votre hiérarchie varie entre les différentes intégrations de MediaBrowser en dehors d'Android Auto et d'Android Automotive OS. Par exemple, si vous affichez normalement un élément racine pouvant être lu, vous pouvez, à la place, l'imbriquer dans un élément racine consultable, en raison de la valeur de la suggestion des indicateurs acceptés.

En plus des suggestions de racine, vous devez suivre quelques consignes supplémentaires pour garantir un affichage optimal des onglets :

  • Fournissez des icônes monochromes, de préférence blanches, pour chaque élément de l'onglet.
  • Fournissez des libellés courts, mais explicites, pour chaque élément de l'onglet. En utilisant des libellés courts, il y a moins de risques que les chaînes soient tronquées.

Afficher une illustration de l'élément multimédia

Les illustrations d'éléments multimédias doivent être transmises sous la forme d'un URI local en utilisant ContentResolver.SCHEME_CONTENT ou ContentResolver.SCHEME_ANDROID_RESOURCE. Cet URI local doit correspondre à un bitmap ou un drawable vectoriel dans les ressources de l'application. Pour les objets MediaDescriptionCompat représentant des éléments de la hiérarchie de contenu, transmettez l'URI via setIconUri(). Pour les objets MediaMetadataCompat représentant l'élément en cours de lecture, transmettez l'URI via putString() à l'aide de l'une des clés suivantes :

Les étapes suivantes décrivent comment télécharger une illustration à partir d'un URI Web et comment l'exposer via un URI local. Pour un exemple plus complet, consultez l'implémentation de openFile() et des méthodes environnantes dans l'application exemple Universal Android Music Player.

  1. Créez un URI content:// correspondant à l'URI Web. Le service de navigateur multimédia et la session multimédia doivent transmettre cet URI de contenu à Android Auto et à Android Automotive OS.

    Kotlin

    fun Uri.asAlbumArtContentURI(): Uri {
      return Uri.Builder()
        .scheme(ContentResolver.SCHEME_CONTENT)
        .authority(CONTENT_PROVIDER_AUTHORITY)
        .appendPath(this.getPath()) // Make sure you trust the URI
        .build()
    }
    

    Java

    public static Uri asAlbumArtContentURI(Uri webUri) {
      return new Uri.Builder()
        .scheme(ContentResolver.SCHEME_CONTENT)
        .authority(CONTENT_PROVIDER_AUTHORITY)
        .appendPath(webUri.getPath()) // Make sure you trust the URI!
        .build();
    }
    
  2. Dans votre implémentation de ContentProvider.openFile(), vérifiez s'il existe un fichier pour l'URI correspondant. Si ce n'est pas le cas, téléchargez et mettez en cache le fichier image. L'extrait de code suivant utilise Glide.

    Kotlin

    override fun openFile(uri: Uri, mode: String): ParcelFileDescriptor? {
      val context = this.context ?: return null
      val file = File(context.cacheDir, uri.path)
      if (!file.exists()) {
        val remoteUri = Uri.Builder()
            .scheme("https")
            .authority("my-image-site")
            .appendPath(uri.path)
            .build()
        val cacheFile = Glide.with(context)
            .asFile()
            .load(remoteUri)
            .submit()
            .get(DOWNLOAD_TIMEOUT_SECONDS, TimeUnit.SECONDS)
    
        cacheFile.renameTo(file)
        file = cacheFile
      }
      return ParcelFileDescriptor.open(file, ParcelFileDescriptor.MODE_READ_ONLY)
    }
    

    Java

    @Nullable
    @Override
    public ParcelFileDescriptor openFile(@NonNull Uri uri, @NonNull String mode)
        throws FileNotFoundException {
      Context context = this.getContext();
      File file = new File(context.getCacheDir(), uri.getPath());
      if (!file.exists()) {
        Uri remoteUri = new Uri.Builder()
            .scheme("https")
            .authority("my-image-site")
            .appendPath(uri.getPath())
            .build();
        File cacheFile = Glide.with(context)
            .asFile()
            .load(remoteUri)
            .submit()
            .get(DOWNLOAD_TIMEOUT_SECONDS, TimeUnit.SECONDS);
    
        cacheFile.renameTo(file);
        file = cacheFile;
      }
      return ParcelFileDescriptor.open(file, ParcelFileDescriptor.MODE_READ_ONLY);
    }
    

Pour en savoir plus sur les fournisseurs de contenu, consultez la section Créer un fournisseur de contenu.

Appliquer des styles de contenu

Après avoir créé votre hiérarchie de contenu à l'aide d'éléments consultables ou lisibles, vous pouvez appliquer des styles de contenu pour déterminer la façon dont ils sont affichés sur l'écran de la voiture.

Vous pouvez utiliser les styles de contenu suivants :

Éléments de liste

Ce style de contenu donne la priorité aux titres et aux métadonnées par rapport aux images.

Éléments de grille

Ce style de contenu donne la priorité aux images par rapport aux titres et aux métadonnées.

Définir des styles de contenu par défaut

Vous pouvez définir des valeurs par défaut globales pour l'affichage de vos éléments multimédias en incluant certaines constantes dans le bundle d'extras BrowserRoot de la méthode onGetRoot() de votre service. Android Auto et Android Automotive OS lisent le bundle à la recherche de ces constantes afin de déterminer le style approprié.

Les extras suivants peuvent être utilisés comme clés dans le bundle :

Les clés peuvent correspondre aux valeurs constantes entières suivantes pour influencer la présentation de ces éléments :

  • DESCRIPTION_EXTRAS_VALUE_CONTENT_STYLE_LIST_ITEM : les éléments correspondants sont présentés sous la forme d'éléments de liste.
  • DESCRIPTION_EXTRAS_VALUE_CONTENT_STYLE_GRID_ITEM : les éléments correspondants sont présentés sous la forme d'éléments de grille.
  • DESCRIPTION_EXTRAS_VALUE_CONTENT_STYLE_CATEGORY_LIST_ITEM : les éléments correspondants sont présentés sous la forme d'éléments de liste "catégorie". Ils sont semblables à des éléments de liste ordinaires, si ce n'est que des marges sont appliquées autour de leurs icônes, car une petite taille d'icône garantit un meilleur rendu. Les icônes doivent être des drawables vectoriels teintés. Cette suggestion ne doit être fournie que pour les éléments consultables.
  • DESCRIPTION_EXTRAS_VALUE_CONTENT_STYLE_CATEGORY_GRID_ITEM : les éléments correspondants sont présentés sous la forme d'éléments de grille "catégorie". Ils sont semblables à des éléments de grille ordinaires, si ce n'est que des marges sont appliquées autour de leurs icônes, car une petite taille d'icône garantit un meilleur rendu. Les icônes doivent être des drawables vectoriels teintés. Cette suggestion ne doit être fournie que pour les éléments consultables.

L'extrait de code suivant montre comment définir le style de contenu par défaut des éléments consultables sur des grilles et des éléments lisibles sur des listes :

Kotlin

import androidx.media.utils.MediaConstants

@Nullable
override fun onGetRoot(
    @NonNull clientPackageName: String,
    clientUid: Int,
    @Nullable rootHints: Bundle
): BrowserRoot {
    val extras = Bundle()
    extras.putInt(
        MediaConstants.DESCRIPTION_EXTRAS_KEY_CONTENT_STYLE_BROWSABLE,
        MediaConstants.DESCRIPTION_EXTRAS_VALUE_CONTENT_STYLE_GRID_ITEM)
    extras.putInt(
        MediaConstants.DESCRIPTION_EXTRAS_KEY_CONTENT_STYLE_PLAYABLE,
        MediaConstants.DESCRIPTION_EXTRAS_VALUE_CONTENT_STYLE_LIST_ITEM)
    return BrowserRoot(ROOT_ID, extras)
}

Java

import androidx.media.utils.MediaConstants;

@Nullable
@Override
public BrowserRoot onGetRoot(
    @NonNull String clientPackageName,
    int clientUid,
    @Nullable Bundle rootHints) {
    Bundle extras = new Bundle();
    extras.putInt(
        MediaConstants.DESCRIPTION_EXTRAS_KEY_CONTENT_STYLE_BROWSABLE,
        MediaConstants.DESCRIPTION_EXTRAS_VALUE_CONTENT_STYLE_GRID_ITEM);
    extras.putInt(
        MediaConstants.DESCRIPTION_EXTRAS_KEY_CONTENT_STYLE_PLAYABLE,
        MediaConstants.DESCRIPTION_EXTRAS_VALUE_CONTENT_STYLE_LIST_ITEM);
    return new BrowserRoot(ROOT_ID, extras);
}

Définir des styles de contenu par élément

L'API Content Style vous permet d'ignorer le style de contenu par défaut pour tous les enfants de l'élément multimédia consultable, ainsi que pour tout élément multimédia proprement dit.

Pour remplacer la valeur par défaut des enfants d'un élément multimédia consultable, créez un groupe d'extras dans la MediaDescription de l'élément multimédia et ajoutez les suggestions mentionnées précédemment. DESCRIPTION_EXTRAS_KEY_CONTENT_STYLE_PLAYABLE s'applique aux enfants lisibles de cet élément, tandis que DESCRIPTION_EXTRAS_KEY_CONTENT_STYLE_BROWSABLE concerne les enfants consultables.

Pour ignorer la valeur par défaut d'un élément multimédia proprement dit (et non ses enfants), créez un bundle d'extras dans la MediaDescription de l'élément multimédia et ajoutez une suggestion avec la clé DESCRIPTION_EXTRAS_KEY_CONTENT_STYLE_SINGLE_ITEM. Utilisez les mêmes valeurs que celles décrites précédemment pour spécifier la présentation de cet élément.

L'extrait de code suivant montre comment créer un MediaItem consultable qui ignore le style de contenu par défaut pour lui-même et pour ses enfants. Il se présente comme un élément de liste "catégorie", ses enfants consultables comme des éléments de liste et ses enfants lisibles comme des éléments de grille :

Kotlin

import androidx.media.utils.MediaConstants

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(
        MediaConstants.DESCRIPTION_EXTRAS_KEY_CONTENT_STYLE_SINGLE_ITEM,
        MediaConstants.DESCRIPTION_EXTRAS_VALUE_CONTENT_STYLE_CATEGORY_LIST_ITEM)
    extras.putInt(
        MediaConstants.DESCRIPTION_EXTRAS_KEY_CONTENT_STYLE_BROWSABLE,
        MediaConstants.DESCRIPTION_EXTRAS_VALUE_CONTENT_STYLE_LIST_ITEM)
    extras.putInt(
        MediaConstants.DESCRIPTION_EXTRAS_KEY_CONTENT_STYLE_PLAYABLE,
        MediaConstants.DESCRIPTION_EXTRAS_VALUE_CONTENT_STYLE_GRID_ITEM)
    mediaDescriptionBuilder.setExtras(extras)
    return MediaBrowser.MediaItem(
        mediaDescriptionBuilder.build(), MediaBrowser.MediaItem.FLAG_BROWSABLE)
}

Java

import androidx.media.utils.MediaConstants;

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(
        MediaConstants.DESCRIPTION_EXTRAS_KEY_CONTENT_STYLE_SINGLE_ITEM,
        MediaConstants.DESCRIPTION_EXTRAS_VALUE_CONTENT_STYLE_CATEGORY_LIST_ITEM);
    extras.putInt(
        MediaConstants.DESCRIPTION_EXTRAS_KEY_CONTENT_STYLE_BROWSABLE,
        MediaConstants.DESCRIPTION_EXTRAS_VALUE_CONTENT_STYLE_LIST_ITEM);
    extras.putInt(
        MediaConstants.DESCRIPTION_EXTRAS_KEY_CONTENT_STYLE_PLAYABLE,
        MediaConstants.DESCRIPTION_EXTRAS_VALUE_CONTENT_STYLE_GRID_ITEM);
    mediaDescriptionBuilder.setExtras(extras);
    return new MediaBrowser.MediaItem(
        mediaDescriptionBuilder.build(), MediaBrowser.MediaItem.FLAG_BROWSABLE);
}

Regrouper des éléments à l'aide de suggestions de titre

Pour regrouper des éléments multimédias associés, utilisez une suggestion par élément. Chaque élément multimédia d'un groupe doit déclarer un bundle d'extras dans sa MediaDescription, laquelle inclut une correspondance avec la clé DESCRIPTION_EXTRAS_KEY_CONTENT_STYLE_GROUP_TITLE et une valeur de chaîne identique. Localisez cette chaîne, qui sera utilisée comme titre du groupe.

L'extrait de code suivant montre comment créer un MediaItem avec un en-tête de sous-groupe "Songs" :

Kotlin

import androidx.media.utils.MediaConstants

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(
        MediaConstants.DESCRIPTION_EXTRAS_KEY_CONTENT_STYLE_GROUP_TITLE,
        "Songs")
    mediaDescriptionBuilder.setExtras(extras)
    return MediaBrowser.MediaItem(
        mediaDescriptionBuilder.build(), /* playable or browsable flag*/)
}

Java

import androidx.media.utils.MediaConstants;

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(
       MediaConstants.DESCRIPTION_EXTRAS_KEY_CONTENT_STYLE_GROUP_TITLE,
       "Songs");
   mediaDescriptionBuilder.setExtras(extras);
   return new MediaBrowser.MediaItem(
       mediaDescriptionBuilder.build(), /* playable or browsable flag*/);
}

Votre application doit transmettre tous les éléments multimédias que vous souhaitez regrouper sous la forme d'un bloc contigu. Supposons, par exemple, que vous souhaitiez afficher deux groupes d'éléments multimédias "Songs" et "Albums" (dans cet ordre), et que votre application transmette cinq éléments multimédias dans l'ordre suivant :

  1. Élément multimédia A avec extras.putString(MediaConstants.DESCRIPTION_EXTRAS_KEY_CONTENT_STYLE_GROUP_TITLE, "Songs")
  2. Élément multimédia B avec extras.putString(MediaConstants.DESCRIPTION_EXTRAS_KEY_CONTENT_STYLE_GROUP_TITLE, "Albums")
  3. Élément multimédia C avec extras.putString(MediaConstants.DESCRIPTION_EXTRAS_KEY_CONTENT_STYLE_GROUP_TITLE, "Songs")
  4. Élément multimédia D avec extras.putString(MediaConstants.DESCRIPTION_EXTRAS_KEY_CONTENT_STYLE_GROUP_TITLE, "Songs")
  5. Élément multimédia E avec extras.putString(MediaConstants.DESCRIPTION_EXTRAS_KEY_CONTENT_STYLE_GROUP_TITLE, "Albums")

Étant donné que les éléments multimédias des groupes "Songs" et "Albums" ne sont pas regroupés dans des blocs contigus, Android Auto et Android Automotive OS interprètent cela comme s'il s'agissait de quatre groupes :

  • Groupe 1 intitulé "Songs" contenant l'élément multimédia A
  • Groupe 2 intitulé "Albums" contenant l'élément multimédia B
  • Groupe 3 intitulé "Songs" contenant les éléments multimédias C et D
  • Groupe 4 intitulé "Albums" contenant l'élément multimédia E

Pour afficher ces éléments dans deux groupes, votre application doit transmettre les éléments multimédias dans l'ordre suivant :

  1. Élément multimédia A avec extras.putString(MediaConstants.DESCRIPTION_EXTRAS_KEY_CONTENT_STYLE_GROUP_TITLE, "Songs")
  2. Élément multimédia C avec extras.putString(MediaConstants.DESCRIPTION_EXTRAS_KEY_CONTENT_STYLE_GROUP_TITLE, "Songs")
  3. Élément multimédia D avec extras.putString(MediaConstants.DESCRIPTION_EXTRAS_KEY_CONTENT_STYLE_GROUP_TITLE, "Songs")
  4. Élément multimédia B avec extras.putString(MediaConstants.DESCRIPTION_EXTRAS_KEY_CONTENT_STYLE_GROUP_TITLE, "Albums")
  5. Élément multimédia E avec extras.putString(MediaConstants.DESCRIPTION_EXTRAS_KEY_CONTENT_STYLE_GROUP_TITLE, "Albums")

Afficher des indicateurs de métadonnées supplémentaires

Vous pouvez inclure des indicateurs de métadonnées supplémentaires pour fournir des informations immédiates sur le contenu dans l'arborescence du navigateur multimédia et pendant la lecture. Dans l'arborescence de navigation, Android Auto et Android Automotive OS lisent les extras associés à un élément et recherchent certaines constantes pour déterminer les indicateurs à afficher. Lors de la lecture de contenu multimédia, Android Auto et Android Automotive OS lisent les métadonnées de la session multimédia et recherchent certaines constantes pour déterminer les indicateurs à afficher.

Figure 3 : Vue de lecture avec les métadonnées identifiant le titre et l'artiste, ainsi qu'une icône indiquant un contenu explicite

Figure 4 : Vue de navigation affichant un point pour le contenu non lu au niveau du premier élément et une barre de progression pour le contenu lu en partie sur le deuxième élément

Les constantes suivantes peuvent être utilisées à la fois dans les extras de description MediaItem et dans les extras MediaMetadata :

Les constantes ci-dessous ne peuvent être utilisées que dans les extras de description MediaItem :

Pour afficher les indicateurs qui apparaissent lorsque l'utilisateur explore l'arborescence de navigation multimédia, créez un bundle d'extras contenant une ou plusieurs de ces constantes, puis transmettez-le à la méthode MediaDescription.Builder.setExtras().

L'extrait de code suivant montre comment afficher des indicateurs pour un élément multimédia explicite dont 70 % du contenu a été lu :

Kotlin

import androidx.media.utils.MediaConstants

val extras = Bundle()
extras.putLong(
    MediaConstants.METADATA_KEY_IS_EXPLICIT,
    MediaConstants.METADATA_VALUE_ATTRIBUTE_PRESENT)
extras.putInt(
    MediaConstants.DESCRIPTION_EXTRAS_KEY_COMPLETION_STATUS,
    MediaConstants.DESCRIPTION_EXTRAS_VALUE_COMPLETION_STATUS_PARTIALLY_PLAYED)
extras.putDouble(
    MediaConstants.DESCRIPTION_EXTRAS_KEY_COMPLETION_PERCENTAGE, 0.7)
val description =
    MediaDescriptionCompat.Builder()
        .setMediaId(/*...*/)
        .setTitle(resources.getString(/*...*/))
        .setExtras(extras)
        .build()
return MediaBrowserCompat.MediaItem(description, /* flags */)

Java

import androidx.media.utils.MediaConstants;

Bundle extras = new Bundle();
extras.putLong(
    MediaConstants.METADATA_KEY_IS_EXPLICIT,
    MediaConstants.METADATA_VALUE_ATTRIBUTE_PRESENT);
extras.putInt(
    MediaConstants.DESCRIPTION_EXTRAS_KEY_COMPLETION_STATUS,
    MediaConstants.DESCRIPTION_EXTRAS_VALUE_COMPLETION_STATUS_PARTIALLY_PLAYED);
extras.putDouble(
    MediaConstants.DESCRIPTION_EXTRAS_KEY_COMPLETION_PERCENTAGE, 0.7);
MediaDescriptionCompat description =
    new MediaDescriptionCompat.Builder()
        .setMediaId(/*...*/)
        .setTitle(resources.getString(/*...*/))
        .setExtras(extras)
        .build();
return new MediaBrowserCompat.MediaItem(description, /* flags */);

Pour afficher les indicateurs d'un élément multimédia en cours de lecture, vous pouvez déclarer des valeurs Long pour METADATA_KEY_IS_EXPLICIT ou EXTRA_DOWNLOAD_STATUS dans le MediaMetadataCompat de votre mediaSession. Vous ne pouvez pas afficher les indicateurs DESCRIPTION_EXTRAS_KEY_COMPLETION_STATUS ni DESCRIPTION_EXTRAS_KEY_COMPLETION_PERCENTAGE dans la vue de lecture.

L'extrait de code suivant montre comment indiquer que le titre qui se trouve actuellement dans la vue de lecture comporte du contenu explicite et a été téléchargé :

Kotlin

import androidx.media.utils.MediaConstants

mediaSession.setMetadata(
    MediaMetadataCompat.Builder()
        .putString(
            MediaMetadataCompat.METADATA_KEY_DISPLAY_TITLE, "Song Name")
        .putString(
            MediaMetadataCompat.METADATA_KEY_DISPLAY_SUBTITLE, "Artist name")
        .putString(
            MediaMetadataCompat.METADATA_KEY_ALBUM_ART_URI,
            albumArtUri.toString())
        .putLong(
            MediaConstants.METADATA_KEY_IS_EXPLICIT,
            MediaConstants.METADATA_VALUE_ATTRIBUTE_PRESENT)
        .putLong(
            MediaDescriptionCompat.EXTRA_DOWNLOAD_STATUS,
            MediaDescriptionCompat.STATUS_DOWNLOADED)
        .build())

Java

import androidx.media.utils.MediaConstants;

mediaSession.setMetadata(
    new MediaMetadataCompat.Builder()
        .putString(
            MediaMetadataCompat.METADATA_KEY_DISPLAY_TITLE, "Song Name")
        .putString(
            MediaMetadataCompat.METADATA_KEY_DISPLAY_SUBTITLE, "Artist name")
        .putString(
            MediaMetadataCompat.METADATA_KEY_ALBUM_ART_URI,
            albumArtUri.toString())
        .putLong(
            MediaConstants.METADATA_KEY_IS_EXPLICIT,
            MediaConstants.METADATA_VALUE_ATTRIBUTE_PRESENT)
        .putLong(
            MediaDescriptionCompat.EXTRA_DOWNLOAD_STATUS,
            MediaDescriptionCompat.STATUS_DOWNLOADED)
        .build());

Mettre à jour la barre de progression dans la vue de navigation pendant la lecture du contenu

Comme mentionné précédemment, vous pouvez utiliser l'extra DESCRIPTION_EXTRAS_KEY_COMPLETION_PERCENTAGE afin d'afficher une barre de progression pour le contenu lu en partie dans la vue de navigation. Cependant, si un utilisateur poursuit la lecture de ce contenu à partir d'Android Auto ou d'Android Automotive OS, la précision de cet indicateur diminuera au fil du temps.

Pour qu'Android Auto et Android Automotive OS assurent la mise à jour de la barre de progression, vous pouvez fournir des informations supplémentaires dans MediaMetadataCompat et PlaybackStateCompat afin d'associer le contenu en cours à des éléments multimédias de la vue de navigation. Pour que la mise à jour de la barre de progression de l'élément multimédia s'effectue automatiquement, les conditions suivantes doivent être remplies :

L'extrait de code suivant montre comment indiquer que l'élément en cours de lecture est associé à un élément de la vue de navigation :

Kotlin

import androidx.media.utils.MediaConstants

// When the MediaItem is constructed to show in the browse view.
// Suppose the item was 25% complete when the user launched the browse view.
val mediaItemExtras = Bundle()
mediaItemExtras.putDouble(
    MediaConstants.DESCRIPTION_EXTRAS_KEY_COMPLETION_PERCENTAGE, 0.25)
val description =
    MediaDescriptionCompat.Builder()
        .setMediaId("my-media-id")
        .setExtras(mediaItemExtras)
        // ...and any other setters.
        .build()
return MediaBrowserCompat.MediaItem(description, /* flags */)

// Elsewhere, when the user has selected MediaItem for playback.
mediaSession.setMetadata(
    MediaMetadataCompat.Builder()
        .putString(MediaMetadata.METADATA_KEY_MEDIA_ID, "my-media-id")
        // ...and any other setters.
        .build())

val playbackStateExtras = Bundle()
playbackStateExtras.putString(
    MediaConstants.PLAYBACK_STATE_EXTRAS_KEY_MEDIA_ID, "my-media-id")
mediaSession.setPlaybackState(
    PlaybackStateCompat.Builder()
        .setExtras(playbackStateExtras)
        // ...and any other setters.
        .build())

Java

import androidx.media.utils.MediaConstants;

// When the MediaItem is constructed to show in the browse view.
// Suppose the item was 25% complete when the user launched the browse view.
Bundle mediaItemExtras = new Bundle();
mediaItemExtras.putDouble(
    MediaConstants.DESCRIPTION_EXTRAS_KEY_COMPLETION_PERCENTAGE, 0.25);
MediaDescriptionCompat description =
    new MediaDescriptionCompat.Builder()
        .setMediaId("my-media-id")
        .setExtras(mediaItemExtras)
        // ...and any other setters.
        .build();
return MediaBrowserCompat.MediaItem(description, /* flags */);

// Elsewhere, when the user has selected MediaItem for playback.
mediaSession.setMetadata(
    new MediaMetadataCompat.Builder()
        .putString(MediaMetadata.METADATA_KEY_MEDIA_ID, "my-media-id")
        // ...and any other setters.
        .build());

Bundle playbackStateExtras = new Bundle();
playbackStateExtras.putString(
    MediaConstants.PLAYBACK_STATE_EXTRAS_KEY_MEDIA_ID, "my-media-id");
mediaSession.setPlaybackState(
    new PlaybackStateCompat.Builder()
        .setExtras(playbackStateExtras)
        // ...and any other setters.
        .build());

Figure 5 : Vue de lecture avec l'option "Search results" (Résultats de recherche) pour l'affichage des éléments multimédias liés à la recherche vocale de l'utilisateur

Votre application peut fournir des résultats de recherche contextuels qui s'affichent lorsque les utilisateurs lancent une requête de recherche. Android Auto et Android Automotive OS affichent ces résultats via des interfaces de requête de recherche ou via des affordances axées sur les requêtes effectuées plus tôt dans la session. Pour en savoir plus, consultez la section Accepter les commandes vocales de ce guide.

Pour afficher des résultats de recherche consultables, ajoutez la clé de constante BROWSER_SERVICE_EXTRAS_KEY_SEARCH_SUPPORTED dans le bundle d'extras de la méthode onGetRoot() de votre service, en la mappant sur la valeur booléenne true.

L'extrait de code suivant montre comment prendre en charge cette fonctionnalité dans la méthode onGetRoot() :

Kotlin

import androidx.media.utils.MediaConstants

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

Java

import androidx.media.utils.MediaConstants;

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

Pour commencer à fournir des résultats de recherche, remplacez la méthode onSearch() dans votre service de navigateur multimédia. Android Auto et Android Automotive OS envoient les termes de recherche de l'utilisateur à cette méthode chaque fois qu'il appelle une interface de requête de recherche ou l'affordance "Résultats de recherche".

Vous pouvez organiser les résultats de recherche de la méthode onSearch() de votre service à l'aide d'éléments de titre pour les rendre plus faciles à parcourir. Par exemple, si votre application lit de la musique, vous pouvez organiser les résultats de recherche par album, artiste et chansons.

L'extrait de code suivant illustre une implémentation simple de la méthode 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<List<MediaItem>> result) {

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

  new AsyncTask<Void, Void, Void>() {
    List<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);
      }
    }
  }.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.
}

Actions de navigation personnalisées

Action de navigation personnalisée unique.

Figure 6 : Action de navigation personnalisée unique

Les actions de navigation personnalisées vous permettent d'ajouter des icônes et des libellés personnalisés aux objets MediaItem de votre appli dans l'application multimédia de la voiture, et de gérer les interactions des utilisateurs avec ces actions. Cela vous permet d'étendre les fonctionnalités de l'application multimédia de différentes manières, par exemple en ajoutant des actions "Download" (Télécharger), "Add to Queue" (Ajouter à la liste d'attente), "Play Radio" (Écouter la radio), "Favorite" (Ajouter aux favoris) ou "Delete" (Supprimer).

Menu à développer d&#39;actions de navigation personnalisées.

Figure 7 : Menu à développer d'actions de navigation personnalisées

Si le nombre d'actions personnalisées affichées dépasse la limite autorisée par l'OEM, un menu à développer est présenté à l'utilisateur.

Fonctionnement

Chaque action de navigation personnalisée est définie avec :

  • Un ID d'action (un identifiant de chaîne unique)
  • Un libellé d'action (texte présenté à l'utilisateur)
  • Un URI d'icône d'action (un drawable vectoriel pouvant être teinté)

Vous définissez une liste d'actions de navigation personnalisées de manière globale dans votre BrowseRoot. Vous pouvez ensuite associer un sous-ensemble de ces actions à des MediaItem. individuels.

Lorsqu'un utilisateur interagit avec une action de navigation personnalisée, votre application reçoit un rappel dans onCustomAction(). Vous pouvez ensuite gérer l'action et mettre à jour la liste des actions pour MediaItem si nécessaire. Cela est utile pour les actions avec état telles que "Favorite" (Ajouter aux favoris) et "Download" (Télécharger). Pour les actions qui n'ont pas besoin d'être mises à jour, comme "Play Radio" (Écouter la radio), vous n'avez pas besoin de mettre à jour la liste des actions.

Actions de navigation personnalisées dans la racine d&#39;un nœud de navigation

Figure 8 : Barre d'outils d'action de navigation personnalisée

Vous pouvez également associer des actions de navigation personnalisées à la racine d'un nœud de navigation. Ces actions s'afficheront dans une barre d'outils secondaire située sous la barre d'outils principale.

Comment implémenter des actions de navigation personnalisées ?

Pour ajouter des actions de navigation personnalisées à votre projet, procédez comme suit :

  1. Remplacez deux méthodes dans votre implémentation MediaBrowserServiceCompat :
  2. Analysez les limites de l'action au moment de l'exécution :
    • Dans onGetRoot(), obtenez le nombre maximal d'actions autorisées pour chaque MediaItem à l'aide de la clé BROWSER_ROOT_HINTS_KEY_CUSTOM_BROWSER_ACTION_LIMIT dans le Bundle rootHints. Une limite de 0 indique que la fonctionnalité n'est pas compatible avec le système.
  3. Créez la liste globale des actions de navigation personnalisées :
    • Pour chaque action, créez un objet Bundle avec les clés suivantes : * EXTRAS_KEY_CUSTOM_BROWSER_ACTION_ID : l'ID d'action * EXTRAS_KEY_CUSTOM_BROWSER_ACTION_LABEL : le libellé d'action * EXTRAS_KEY_CUSTOM_BROWSER_ACTION_ICON_URI : l'URI d'icône d'action * Ajoutez tous les objets Bundle d'action à une liste.
  4. Ajoutez la liste globale à votre BrowseRoot :
  5. Ajoutez des actions à vos objets MediaItem :
       
    • Vous pouvez ajouter des actions à des objets MediaItem individuels en incluant la liste des ID d'action dans les extras MediaDescriptionCompat à l'aide de la clé DESCRIPTION_EXTRAS_KEY_CUSTOM_BROWSER_ACTION_ID_LIST. Cette liste doit être un sous-ensemble de la liste globale des actions que vous avez définies dans BrowseRoot.
  6. Gérez les actions et renvoyez la progression ou les résultats :

Voici quelques modifications que vous pouvez apporter à votre BrowserServiceCompat pour commencer à utiliser les actions de navigation personnalisées.

Ignorer BrowserServiceCompat

Vous devez remplacer les méthodes suivantes dans MediaBrowserServiceCompat.

public void onLoadItem(String itemId, @NonNull Result<MediaBrowserCompat.MediaItem> result)

public void onCustomAction(@NonNull String action, Bundle extras, @NonNull Result<Bundle> result)

Limiter les actions d'analyse

Vérifiez le nombre d'actions de navigation personnalisées acceptées.

public BrowserRoot onGetRoot(@NonNull String clientPackageName, int clientUid, Bundle rootHints) {
    rootHints.getInt(
            MediaConstants.BROWSER_ROOT_HINTS_KEY_CUSTOM_BROWSER_ACTION_LIMIT, 0)
}

Créer une action de navigation personnalisée

Chaque action doit être empaquetée dans un Bundle distinct.

  • ID de l'action
    bundle.putString(MediaConstants.EXTRAS_KEY_CUSTOM_BROWSER_ACTION_ID,
                    "<ACTION_ID>")
    
  • Libellé d'action :
    bundle.putString(MediaConstants.EXTRAS_KEY_CUSTOM_BROWSER_ACTION_LABEL,
                    "<ACTION_LABEL>")
    
  • URI de l'icône d'action
    bundle.putString(MediaConstants.EXTRAS_KEY_CUSTOM_BROWSER_ACTION_ICON_URI,
                    "<ACTION_ICON_URI>")
    

Ajouter des actions de navigation personnalisées à ArrayList Parceable

Ajoutez tous les objets Bundle d'action de navigation personnalisée dans une ArrayList.

private ArrayList<Bundle> createCustomActionsList(
                                        CustomBrowseAction browseActions) {
    ArrayList<Bundle> browseActionsBundle = new ArrayList<>();
    for (CustomBrowseAction browseAction : browseActions) {
        Bundle action = new Bundle();
        action.putString(EXTRAS_KEY_CUSTOM_BROWSER_ACTION_ID,
                browseAction.mId);
        action.putString(EXTRAS_KEY_CUSTOM_BROWSER_ACTION_LABEL,
                getString(browseAction.mLabelResId));
        action.putString(EXTRAS_KEY_CUSTOM_BROWSER_ACTION_ICON_URI,
                browseAction.mIcon);
        browseActionsBundle.add(action);
    }
    return browseActionsBundle;
}

Ajouter une liste d'actions de navigation personnalisées à la racine de navigation

public BrowserRoot onGetRoot(@NonNull String clientPackageName, int clientUid,
                             Bundle rootHints) {
    Bundle browserRootExtras = new Bundle();
    browserRootExtras.putParcelableArrayList(
            BROWSER_SERVICE_EXTRAS_KEY_CUSTOM_BROWSER_ACTION_ROOT_LIST,
            createCustomActionsList()));
    mRoot = new BrowserRoot(ROOT_ID, browserRootExtras);
    return mRoot;
}

Ajouter des actions à un MediaItem

MediaDescriptionCompat buildDescription (long id, String title, String subtitle,
                String description, Uri iconUri, Uri mediaUri,
                ArrayList<String> browseActionIds) {

    MediaDescriptionCompat.Builder bob = new MediaDescriptionCompat.Builder();
    bob.setMediaId(id);
    bob.setTitle(title);
    bob.setSubtitle(subtitle);
    bob.setDescription(description);
    bob.setIconUri(iconUri);
    bob.setMediaUri(mediaUri);

    Bundle extras = new Bundle();
    extras.putStringArrayList(
          DESCRIPTION_EXTRAS_KEY_CUSTOM_BROWSER_ACTION_ID_LIST,
          browseActionIds);

    bob.setExtras(extras);
    return bob.build();
}
MediaItem mediaItem = new MediaItem(buildDescription(...), flags);

Résultat de la compilation onCustomAction

  • Analyser mediaId à partir de Bundle extras :
    @Override
    public void onCustomAction(
              @NonNull String action, Bundle extras, @NonNull Result<Bundle> result){
      String mediaId = extras.getString(MediaConstans.EXTRAS_KEY_CUSTOM_BROWSER_ACTION_MEDIA_ITEM_ID);
    }
    
  • Pour les résultats asynchrones, dissociez "result." result.detach()
  • Compilation du bundle result
    • Message à l'utilisateur
      mResultBundle.putString(EXTRAS_KEY_CUSTOM_BROWSER_ACTION_RESULT_MESSAGE,
                mContext.getString(stringRes))
      
    • Mettre à jour un élément(permet de mettre à jour les actions d'un élément)
      mResultBundle.putString(EXTRAS_KEY_CUSTOM_BROWSER_ACTION_RESULT_REFRESH_ITEM, mediaId);
      
    • Ouvrir la vue de lecture
      //Shows user the PBV without changing the playback state
      mResultBundle.putString(EXTRAS_KEY_CUSTOM_BROWSER_ACTION_RESULT_SHOW_PLAYING_ITEM, null);
      
    • Mettre à jour le nœud de navigation
      //Change current browse node to mediaId
      mResultBundle.putString(EXTRAS_KEY_CUSTOM_BROWSER_ACTION_RESULT_BROWSE_NODE, mediaId);
      
  • En cas d'erreur, appelez result.sendError(resultBundle)..
  • Si la progression est mise à jour, appelez result.sendProgressUpdate(resultBundle).
  • Pour terminer, appelez result.sendResult(resultBundle).

Mettre à jour l'état de l'action

En utilisant la méthode result.sendProgressUpdate(resultBundle) avec la clé EXTRAS_KEY_CUSTOM_BROWSER_ACTION_RESULT_REFRESH_ITEM, vous pouvez mettre à jour le MediaItem pour refléter le nouvel état de l'action. Cela vous permet de fournir à l'utilisateur des commentaires en temps réel sur la progression et le résultat de son action.

Exemple : action "Download" (Télécharger)

Voici un exemple d'utilisation de cette fonctionnalité pour implémenter une action de téléchargement avec trois états :

  1. Download (Télécharger) : il s'agit de l'état initial de l'action. Lorsque l'utilisateur sélectionne cette action, vous pouvez la remplacer par "Downloading" (Téléchargement) et appeler sendProgressUpdate pour mettre à jour l'UI.
  2. Downloading (Téléchargement) : cet état indique que le téléchargement est en cours. Vous pouvez utiliser cet état pour présenter une barre de progression ou un autre indicateur à l'utilisateur.
  3. Downloaded (Téléchargé) : cet état indique que le téléchargement est terminé. Une fois le téléchargement terminé, vous pouvez remplacer "Downloading" (Téléchargement) par "Downloaded" (Téléchargé) et appeler sendResult avec la clé EXTRAS_KEY_CUSTOM_BROWSER_ACTION_RESULT_REFRESH_ITEM pour indiquer que l'élément doit être actualisé. Vous pouvez également utiliser la clé EXTRAS_KEY_CUSTOM_BROWSER_ACTION_RESULT_MESSAGE pour afficher un message de réussite à l'utilisateur.

Cette approche vous permet de fournir des informations claires à l'utilisateur sur le processus de téléchargement et son état actuel. Vous pouvez ajouter encore plus de détails avec des icônes qui indiquent les états de téléchargement à 25 %, 50 % ou 75 %.

Exemple : action "Favorite" (Ajouter aux favoris)

Un autre exemple est une action d'ajout aux favoris avec deux états :

  1. Favorite (Ajouter aux favoris) : cette action s'affiche pour les éléments qui ne figurent pas dans la liste des favoris de l'utilisateur. Lorsque l'utilisateur sélectionne cette action, vous pouvez la remplacer par "Favorited" (Ajouté aux favoris) et appeler sendResult avec la clé EXTRAS_KEY_CUSTOM_BROWSER_ACTION_RESULT_REFRESH_ITEM pour mettre à jour l'UI.
  2. Favorited (Ajouté aux favoris) : cette action s'affiche pour les éléments figurant dans la liste des favoris de l'utilisateur. Lorsque l'utilisateur sélectionne cette action, vous pouvez la remplacer par "Favorite" (Ajouter aux favoris) et appeler sendResult avec la clé EXTRAS_KEY_CUSTOM_BROWSER_ACTION_RESULT_REFRESH_ITEM pour mettre à jour l'UI.

Cette approche permet aux utilisateurs de gérer de manière claire et cohérente leurs favoris.

Ces exemples montrent la flexibilité des actions de navigation personnalisées et comment les utiliser pour implémenter diverses fonctionnalités avec des commentaires en temps réel afin d'améliorer l'expérience utilisateur dans l'application multimédia de la voiture.

Pour obtenir un exemple complet d'implémentation de cette fonctionnalité, vous pouvez consulter le projet TestMediaApp.

Activer la commande de lecture

Android Auto et Android Automotive OS envoient des commandes de lecture via la classe MediaSessionCompat de votre service. Vous devez enregistrer une session et implémenter les méthodes de rappel associées.

Enregistrer une session multimédia

Dans la méthode onCreate() de votre service de navigateur multimédia, créez une classe MediaSessionCompat, puis enregistrez la session multimédia en appelant setSessionToken().

L'extrait de code suivant montre comment créer et enregistrer une session multimédia :

Kotlin

override fun onCreate() {
    super.onCreate()
    ...
    // Start a new MediaSession.
    val session = MediaSessionCompat(this, "session tag").apply {
        // Set a callback object that implements MediaSession.Callback
        // to handle play control requests.
        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 that implements MediaSession.Callback
    // to handle play control requests.
    session.setCallback(new MyMediaSessionCallback());
    ...
}

Lorsque vous créez l'objet de session multimédia, vous définissez un objet de rappel utilisé pour gérer les requêtes de commande de lecture. Pour créer cet objet de rappel, vous devez fournir une implémentation de la classe MediaSessionCompat.Callback pour votre application. La section suivante vous explique comment implémenter cet objet.

Implémenter des commandes de lecture

Lorsqu'un utilisateur demande de lire un élément multimédia de votre application, Android Automotive OS et Android Auto utilisent la classe MediaSessionCompat.Callback de l'objet MediaSessionCompat qu'ils ont reçu du service de navigateur multimédia de l'application. Lorsqu'un utilisateur souhaite contrôler la lecture du contenu (par exemple, en mettant en pause la lecture ou en passant au titre suivant), Android Auto et Android Automotive OS appellent l'une des méthodes de l'objet de rappel.

Pour gérer la lecture de contenu, votre application doit étendre la classe abstraite MediaSessionCompat.Callback et implémenter les méthodes compatibles avec votre application.

Implémentez toutes les méthodes de rappel suivantes qui sont adaptées au type de contenu proposé par votre application :

onPrepare()
Appelée lorsque la source multimédia est modifiée. Android Automotive OS appelle également cette méthode juste après le démarrage. Votre application multimédia doit implémenter cette méthode.
onPlay()
Appelée si l'utilisateur lance la lecture sans choisir d'élément spécifique. Votre application doit lire son contenu par défaut ou, si la lecture a été interrompue avec onPause(), l'application doit la reprendre.

Remarque : L'application ne doit pas lancer automatiquement la lecture de musique quand Android Automotive OS ou Android Auto se connecte à votre service de navigateur multimédia. Pour en savoir plus, consultez la section sur la définition de l'état de lecture initial.

onPlayFromMediaId()
Appelée lorsque l'utilisateur lance la lecture d'un élément spécifique. Cette méthode transmet l'ID que votre service de navigateur multimédia a affecté à l'élément multimédia dans votre hiérarchie de contenu.
onPlayFromSearch()
Appelée lorsque l'utilisateur lance la lecture à partir d'une requête de recherche. L'application doit faire le bon choix en fonction de la chaîne de recherche qui a été transmise.
onPause()
Appelée lorsque l'utilisateur décide de suspendre la lecture.
onSkipToNext()
Appelée lorsque l'utilisateur décide de passer à l'élément suivant.
onSkipToPrevious()
Appelée lorsque l'utilisateur décide de revenir à l'élément précédent.
onStop()
Appelée lorsque l'utilisateur décide d'arrêter la lecture.

Remplacez ces méthodes dans votre application pour fournir toutes les fonctionnalités souhaitées. Vous n'avez pas à implémenter une méthode si sa fonctionnalité n'est pas compatible avec votre application. Par exemple, si votre application lit une diffusion en direct, telle qu'une émission sportive, vous n'avez pas besoin d'implémenter la méthode onSkipToNext(). Vous pouvez utiliser l'implémentation par défaut de onSkipToNext() à la place.

Votre application n'a pas besoin d'une logique particulière pour lire du contenu via les haut-parleurs de la voiture. Lorsque votre application reçoit une requête de lecture de contenu, elle peut lire le contenu audio de la même manière que via le casque ou le haut-parleur du téléphone. Android Auto et Android Automotive OS envoient automatiquement le contenu audio au système de la voiture pour qu'il le diffuse sur ses haut-parleurs.

Pour en savoir plus sur la lecture de contenu audio, consultez les pages Présentation de MediaPlayer, Présentation de l'application audio et Présentation d'ExoPlayer.

Définir des actions de lecture standards

Android Auto et Android Automotive OS affichent les commandes de lecture en fonction des actions activées dans l'objet PlaybackStateCompat.

Par défaut, votre application doit accepter les actions suivantes :

Votre application peut, en outre, accepter les actions suivantes si elles sont pertinentes par rapport à son contenu :

En outre, vous avez la possibilité de créer une file d'attente de lecture que l'utilisateur peut voir, mais ce n'est pas obligatoire. Pour ce faire, vous devez appeler les méthodes setQueue() et setQueueTitle(), activer l'action ACTION_SKIP_TO_QUEUE_ITEM et définir le rappel onSkipToQueueItem().

Vous devez également utiliser l'icône En écoute, qui indique l'élément en cours de lecture. Pour ce faire, appelez la méthode setActiveQueueItemId() et transmettez l'ID de l'élément en cours de lecture dans la file d'attente. Vous devez mettre à jour setActiveQueueItemId() chaque fois qu'une file d'attente est modifiée.

Android Auto et Android Automotive OS affichent des boutons pour chaque action activée, ainsi que la file d'attente de lecture. Lorsque l'utilisateur clique sur les boutons, le système appelle le rappel correspondant à partir de MediaSessionCompat.Callback.

Réserver de l'espace inutilisé

Android Auto et Android Automotive OS réservent de l'espace dans l'UI pour les actions ACTION_SKIP_TO_PREVIOUS et ACTION_SKIP_TO_NEXT. Si votre application n'est pas compatible avec l'une de ces fonctions, Android Auto et Android Automotive OS utilisent cet espace pour afficher les actions personnalisées que vous créez, le cas échéant.

Si vous ne souhaitez pas renseigner des actions personnalisées dans ces espaces, vous pouvez les réserver afin qu'Android Auto et Android Automotive OS les laissent vides chaque fois que votre application n'accepte pas la fonction correspondante. Pour ce faire, appelez la méthode setExtras() avec un bundle d'extras contenant des constantes correspondant aux fonctions réservées. SESSION_EXTRAS_KEY_SLOT_RESERVATION_SKIP_TO_NEXT correspond à ACTION_SKIP_TO_NEXT et SESSION_EXTRAS_KEY_SLOT_RESERVATION_SKIP_TO_PREV à ACTION_SKIP_TO_PREVIOUS. Utilisez ces constantes comme clés dans le bundle et utilisez la valeur booléenne true pour leurs valeurs.

Définir la classe PlaybackState initiale

Quand Android Auto et Android Automotive OS communiquent avec votre service de navigateur multimédia, votre session multimédia transmet l'état de lecture du contenu à l'aide de PlaybackStateCompat. L'application ne doit pas lancer automatiquement la lecture de musique quand Android Automotive OS ou Android Auto se connecte à votre service de navigateur multimédia. Au lieu de cela, Android Auto et Android Automotive OS doivent reprendre ou lancer la lecture en fonction de l'état de la voiture ou des actions de l'utilisateur.

Pour ce faire, définissez la classe PlaybackStateCompat initiale de votre session multimédia sur STATE_STOPPED, STATE_PAUSED, STATE_NONE ou STATE_ERROR.

La durée des sessions multimédias dans Android Auto et Android Automotive OS correspond à celle du trajet. Par conséquent, les utilisateurs lancent et arrêtent fréquemment ces sessions. Pour assurer une expérience optimale entre les lecteurs, effectuez le suivi de l'état de la session précédente. Ainsi, lorsque l'application multimédia reçoit une demande de reprise, l'utilisateur peut reprendre automatiquement là où il s'était arrêté (par exemple, le dernier élément multimédia lu, le PlaybackStateCompat et la file d'attente).

Ajouter des actions de lecture personnalisées

Vous pouvez ajouter des actions de lecture personnalisées pour afficher plus d'actions compatibles avec votre application multimédia. Si l'espace le permet (et s'il n'est pas réservé), Android ajoute les actions personnalisées aux commandes de transport. Sinon, les actions personnalisées sont affichées dans le menu à développer. Ces actions s'affichent dans l'ordre dans lequel elles ont été ajoutées à PlaybackStateCompat.

Les actions personnalisées permettent de fournir un comportement différent des actions standards. Ne les utilisez pas pour remplacer ou dupliquer des actions standards.

Vous pouvez ajouter des actions personnalisées à l'aide de la méthode addCustomAction() dans la classe PlaybackStateCompat.Builder.

L'extrait de code suivant montre comment ajouter une action "Lancer une chaîne de radio" personnalisée :

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());

Pour un exemple plus détaillé de cette méthode, consultez la méthode setCustomAction() dans l'application exemple Universal Android Music Player sur GitHub.

Une fois que vous avez créé votre action personnalisée, votre session multimédia peut y répondre en remplaçant la méthode onCustomAction().

L'extrait de code suivant montre comment votre application peut répondre à une action "Lancer une chaîne 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)) {
        ...
    }
}

Pour un exemple plus détaillé de cette méthode, consultez la méthode onCustomAction dans l'application exemple Universal Android Music Player sur GitHub.

Icônes des actions personnalisées

Chaque action personnalisée que vous créez a besoin d'une ressource d'icône. Les applications utilisées dans les voitures peuvent être exécutées sur un large éventail de tailles et de densités d'écran. Les icônes que vous fournissez doivent donc être des drawables vectoriels. Un drawable vectoriel permet de dimensionner des éléments en conservant le même niveau de détail. Ce type de graphique facilite également l'alignement des contours et des coins sur les limites de pixels à des résolutions inférieures.

Dans le cas d'une action personnalisée avec état (par exemple, qui active ou désactive un paramètre de lecture), fournissez différentes icônes pour les différents états, de sorte que les utilisateurs perçoivent un changement visuel lorsqu'ils sélectionnent l'action.

Fournir d'autres styles d'icônes pour les actions désactivées

Lorsqu'une action personnalisée n'est pas disponible pour le contexte actuel, remplacez l'icône d'action personnalisée par une autre indiquant que l'action est désactivée.

Figure 6 : Exemples d'icônes personnalisées pour des actions désactivées

Indiquer le format audio

Pour indiquer que le contenu multimédia en cours de lecture utilise un format audio spécial, vous pouvez spécifier des icônes affichées dans les voitures compatibles avec cette fonctionnalité. Vous pouvez définir KEY_CONTENT_FORMAT_TINTABLE_LARGE_ICON_URI et KEY_CONTENT_FORMAT_TINTABLE_SMALL_ICON_URI dans le bundle d'extras de l'élément multimédia en cours de lecture (transmis à MediaSession.setMetadata()). Assurez-vous de définir ces deux extras pour vous adapter aux différentes mises en page.

De plus, vous pouvez définir l'extra KEY_IMMERSIVE_AUDIO pour indiquer aux OEM automobiles qu'il s'agit d'un son immersif. Ils doivent donc être très prudents lorsqu'ils décident d'appliquer des effets audio qui peuvent interférer avec le contenu immersif.

Vous pouvez configurer l'élément multimédia en cours de lecture de sorte que son sous-titre, sa description ou les deux correspondent à des liens vers d'autres éléments multimédias. Cela permet à l'utilisateur de passer rapidement aux éléments connexes ; par exemple, ils peuvent accéder à d'autres titres du même artiste, à d'autres épisodes du podcast, etc. Si la voiture est compatible avec cette fonctionnalité, les utilisateurs peuvent appuyer sur le lien pour accéder au contenu.

Pour ajouter des liens, configurez les métadonnées KEY_SUBTITLE_LINK_MEDIA_ID (pour créer un lien à partir du sous-titre) ou KEY_DESCRIPTION_LINK_MEDIA_ID (pour créer un lien à partir de la description). Pour en savoir plus, consultez le document de référence sur ces champs de métadonnées.

Accepter les commandes vocales

Votre application multimédia doit accepter les commandes vocales pour offrir aux conducteurs un environnement sûr et pratique qui minimise les distractions. Par exemple, si votre application lit un élément multimédia, l'utilisateur peut dire Mets [titre de la chanson] pour lui demander de lire un autre titre sans qu'il n'ait à regarder ni à toucher l'écran de la voiture. Les utilisateurs peuvent lancer des requêtes en cliquant sur les boutons appropriés au volant ou en prononçant les mots clés Ok Google.

Quand Android Auto ou Android Automotive OS détecte et interprète une commande vocale, elle est envoyée à l'application via onPlayFromSearch(). À la réception de ce rappel, l'application recherche le contenu correspondant à la chaîne query et lance la lecture.

Les utilisateurs peuvent spécifier différentes catégories de termes dans leur requête : genre, artiste, album, titre de la chanson, station de radio, playlist, etc. Lorsque vous développez la compatibilité de la recherche, tenez compte de toutes les catégories pertinentes pour votre application. Si Android Auto ou Android Automotive OS détecte qu'une requête donnée correspond à certaines catégories, des extras sont ajoutés dans le paramètre extras. Vous pouvez envoyer les extras supplémentaires suivants :

Prenez en compte une chaîne query vide, qui peut être envoyée par Android Auto ou Android Automotive OS si l'utilisateur ne spécifie pas de termes de recherche (par exemple, si l'utilisateur dit Mets de la musique). Dans ce cas, l'application peut décider de lancer un titre écouté récemment ou une nouvelle suggestion.

Si une recherche ne peut pas être traitée rapidement, ne restez pas bloqué à l'état onPlayFromSearch(). Définissez plutôt l'état de lecture sur STATE_CONNECTING et effectuez la recherche sur un thread asynchrone.

Une fois que la lecture a commencé, nous vous conseillons d'ajouter le contenu associé dans la file d'attente de la session. Par exemple, si l'utilisateur a demandé la lecture d'un album, l'application peut renseigner la liste des titres de cet album dans la file d'attente. Pensez également à accepter les résultats de recherche consultables, de sorte qu'un utilisateur puisse choisir un autre titre correspondant à sa requête.

Outre les requêtes lire, Android Auto et Android Automotive OS reconnaissent les requêtes vocales permettant de contrôler la lecture, comme mettre la musique en pause et titre suivant. Ils associent également ces commandes aux rappels de session multimédia appropriés, comme onPause() et onSkipToNext().

Pour obtenir un exemple détaillé de la méthode d'implémentation des actions de lecture par commande vocale dans votre application, consultez Assistant Google et les applications multimédias.

Implémenter des mesures de protection contre la distraction

Lorsque le conducteur utilise Android Auto, son smartphone est connecté aux haut-parleurs de la voiture. Vous devez donc prendre des précautions supplémentaires pour éviter toute distraction.

Supprimer les alarmes dans le véhicule

Les applications multimédias Android Auto ne doivent pas démarrer la lecture audio via les haut-parleurs de la voiture, sauf si l'utilisateur lance la lecture en appuyant, par exemple, sur un bouton de lecture. Cela vaut également pour une alarme programmée par l'utilisateur depuis l'application multimédia.

Pour satisfaire à cette exigence, votre application peut utiliser CarConnection comme signal avant de lire du contenu audio. Pour déterminer si le téléphone projette actuellement du contenu sur l'écran d'une voiture, l'application peut vérifier si la classe LiveData du type de connexion de la voiture est égale à CONNECTION_TYPE_PROJECTION.

Si une projection de contenu est en cours, l'application multimédia compatible avec les alarmes doit effectuer l'une des opérations suivantes :

  • Désactiver l'alarme
  • Lire l'alarme via STREAM_ALARM et afficher une UI sur l'écran du téléphone pour la désactiver

Gérer les publicités multimédias

Par défaut, Android Auto affiche une notification lorsque les métadonnées multimédias changent au cours d'une session de lecture audio. Lorsqu'une application multimédia passe de la lecture de musique à la diffusion d'une publicité, l'affichage d'une notification risquerait de distraire l'utilisateur. Pour empêcher Android Auto d'afficher une notification, vous devez définir la clé des métadonnées multimédias METADATA_KEY_IS_ADVERTISEMENT sur METADATA_VALUE_ATTRIBUTE_PRESENT., comme dans l'extrait de code ci-dessous :

Kotlin

import androidx.media.utils.MediaConstants

override fun onPlayFromMediaId(mediaId: String, extras: Bundle?) {
    MediaMetadataCompat.Builder().apply {
        if (isAd(mediaId)) {
            putLong(
                MediaConstants.METADATA_KEY_IS_ADVERTISEMENT,
                MediaConstants.METADATA_VALUE_ATTRIBUTE_PRESENT)
        }
        // ...add any other properties you normally would.
        mediaSession.setMetadata(build())
    }
}

Java

import androidx.media.utils.MediaConstants;

@Override
public void onPlayFromMediaId(String mediaId, Bundle extras) {
    MediaMetadataCompat.Builder builder = new MediaMetadataCompat.Builder();
    if (isAd(mediaId)) {
        builder.putLong(
            MediaConstants.METADATA_KEY_IS_ADVERTISEMENT,
            MediaConstants.METADATA_VALUE_ATTRIBUTE_PRESENT);
    }
    // ...add any other properties you normally would.
    mediaSession.setMetadata(builder.build());
}

Gérer les erreurs générales

Lorsque l'application rencontre une erreur, définissez l'état de lecture sur STATE_ERROR et fournissez un message d'erreur à l'aide de la méthode setErrorMessage(). Consultez la section PlaybackStateCompat pour obtenir la liste des codes d'erreur que vous pouvez utiliser lorsque vous définissez le message d'erreur. Les messages d'erreur doivent être visibles par l'utilisateur et localisés en fonction de ses paramètres régionaux actuels. Android Auto et Android Automotive OS peuvent alors afficher le message d'erreur.

Par exemple, si le contenu n'est pas disponible dans la région actuelle de l'utilisateur, vous pouvez utiliser le code d'erreur ERROR_CODE_NOT_AVAILABLE_IN_REGION lorsque vous définissez le message.

Kotlin

mediaSession.setPlaybackState(
    PlaybackStateCompat.Builder()
        .setState(PlaybackStateCompat.STATE_ERROR)
        .setErrorMessage(PlaybackStateCompat.ERROR_CODE_NOT_AVAILABLE_IN_REGION, getString(R.string.error_unsupported_region))
        // ...and any other setters.
        .build())

Java

mediaSession.setPlaybackState(
    new PlaybackStateCompat.Builder()
        .setState(PlaybackStateCompat.STATE_ERROR)
        .setErrorMessage(PlaybackStateCompat.ERROR_CODE_NOT_AVAILABLE_IN_REGION, getString(R.string.error_unsupported_region))
        // ...and any other setters.
        .build());

Pour en savoir plus sur les états d'erreur, consultez Utiliser une session multimédia : états et erreurs.

Si un utilisateur Android Auto doit ouvrir votre application pour téléphone afin de résoudre une erreur, fournissez-lui cette information dans votre message. Par exemple, votre message d'erreur doit indiquer "Connectez-vous à [nom de votre application]" plutôt que "Veuillez vous connecter".

Autres ressources