Créer une hiérarchie de contenu

Android Auto et Android Automotive OS (AAOS) appellent le service de navigateur multimédia de votre application pour découvrir le contenu disponible. Pour cela, vous devez implémenter ces deux méthodes dans votre service de navigateur multimédia.

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 AAOS utilisent ce nœud racine pour demander le reste du contenu à l'aide de la méthode onLoadChildren. Cet extrait de code montre une implémentation 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 obtenir un exemple détaillé de cette méthode, consultez onGetRoot dans l'application exemple Universal Android Music Player sur GitHub.

Ajouter la validation de package

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 :

  • Comparez le clientPackageName à votre liste d'autorisation.
  • Vérifiez le certificat utilisé pour signer 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 AAOS) puissent accéder à votre contenu, votre service doit renvoyer une valeur BrowserRoot non nulle lorsque ces applications appellent la méthode onGetRoot.

La signature de l'application système AAOS varie en fonction de la marque et du modèle d'une voiture. Veillez à autoriser les connexions de toutes les applications système pour prendre en charge AAOS.

Cet extrait de code montre comment votre service peut valider que le package appelant est 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 provient 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. L'Assistant Google utilise des noms de package distincts pour le téléphone (ce qui inclut Android Auto) et pour Android Automotive OS (AAOS).

Implémenter onLoadChildren

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

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é.

Cet extrait de code montre une implémentation de 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&lt;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 descendants of that menu in the mediaItems list.
    }
    result.sendResult(mediaItems)
}

Java

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

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

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

    // 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 descendants of that menu in the mediaItems list.
    }
    result.sendResult(mediaItems);
}

Pour voir un exemple de cette méthode, consultez onLoadChildren dans l'application exemple Universal Android Music Player sur GitHub.

Structurer le menu racine

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 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.

Contenu racine affiché sous la forme d&#39;onglets de navigation

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

En appliquant ces suggestions, le système affiche le contenu racine sous forme d'onglets de navigation. Si vous ne suivez pas ces suggestions, il se peut qu'une partie du contenu racine soit supprimée ou que le système la rende moins visible. Ces suggestions sont transmises :

Kotlin

import androidx.media.utils.MediaConstants

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'AAOS.

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, suivez ces consignes pour afficher les onglets de manière optimale :

  • Icônes monochromes (de préférence blanches) pour chaque élément de l'onglet

  • Des libellés courts et explicites pour chaque élément d'onglet (les libellés courts réduisent le risque de troncature)