Inhaltshierarchie erstellen

Android Auto und Android Automotive OS (AAOS) rufen den Media Browser-Dienst deiner App auf, um herauszufinden, welche Inhalte verfügbar sind. Dazu implementierst du diese beiden Methoden in deinem Media Browser-Dienst.

onGetRoot implementieren

Die Methode onGetRoot deines Dienstes gibt Informationen zum Stammknoten deiner Inhaltshierarchie zurück. Android Auto und AAOS verwenden diesen Stamm Knoten, um die restlichen Inhalte mit der onLoadChildren Methode anzufordern. Dieser Code-Snippet zeigt eine Implementierung der Methode 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);
}

Ein detailliertes Beispiel für diese Methode findest du unter onGetRoot in der Beispiel-App Universal Android Music Player auf GitHub.

Paketvalidierung hinzufügen

Wenn ein Aufruf an die Methode onGetRoot deines Dienstes erfolgt, übergibt das aufrufende Paket Identifikationsinformationen an deinen Dienst. Dein Dienst kann anhand dieser Informationen entscheiden, ob das Paket auf deine Inhalte zugreifen darf.

Du kannst beispielsweise den Zugriff auf die Inhalte deiner App auf eine Liste genehmigter Pakete beschränken:

  • Vergleiche clientPackageName mit deiner Zulassungsliste.
  • Prüfe das Zertifikat, mit dem die APK für das Paket signiert wurde.

Wenn das Paket nicht verifiziert werden kann, gib null zurück, um den Zugriff auf deine Inhalte zu verweigern.

Damit System-Apps wie Android Auto und AAOS auf deine Inhalte zugreifen können, muss dein Dienst ein BrowserRoot zurückgeben, das nicht null ist, wenn diese System-Apps die Methode onGetRoot aufrufen.

Die Signatur der AAOS-System-App variiert je nach Marke und Modell eines Autos. Achte darauf, Verbindungen von allen System-Apps zuzulassen, um AAOS zu unterstützen.

Dieser Code-Snippet zeigt, wie dein Dienst prüfen kann, ob das aufrufende Paket eine System-App ist:

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
}

Dieser Code-Snippet ist ein Auszug aus der PackageValidator Klasse in der Beispiel-App Universal Android Music Player auf GitHub. In dieser Klasse findest du ein detaillierteres Beispiel dafür, wie du die Paketvalidierung für die Methode onGetRoot deines Dienstes implementierst.

Neben System-Apps musst du auch Google Assistant erlauben, eine Verbindung zu deinem MediaBrowserService herzustellen. Google Assistant verwendet unterschiedliche Paketnamen für seine mobilen Apps und AAOS-Apps.

onLoadChildren implementieren

Nachdem Android Auto und AAOS dein Stammknotenobjekt erhalten haben, erstellen sie ein Menü der obersten Ebene, indem sie onLoadChildren für das Stammknotenobjekt aufrufen, um seine Nachfolger abzurufen. Client-Apps erstellen Untermenüs, indem sie dieselbe Methode mit Nachfolgerknotenobjekten aufrufen.

Jeder Knoten in deiner Inhaltshierarchie wird durch ein MediaBrowserCompat.MediaItem-Objekt dargestellt. Jedes dieser MediaItems wird durch einen eindeutigen ID-String identifiziert. Client-Apps behandeln diese ID-Strings als undurchsichtige Tokens.

Wenn eine Client-App zu einem Untermenü wechseln oder ein MediaItem abspielen möchte, übergibt sie das Token. Deine App ist dafür verantwortlich, das Token dem entsprechenden MediaItem zuzuordnen.

Dieser Code-Snippet zeigt eine Implementierung von 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);
}

Ein Beispiel für diese Methode findest du unter onLoadChildren in der Beispiel-App Universal Android Music Player auf GitHub.

Struktur des Stammmenüs

Android Auto und Android Automotive OS haben bestimmte Einschränkungen hinsichtlich der Struktur des Stammmenüs. Diese werden dem MediaBrowserService über Stammhinweise mitgeteilt, die über das Bundle-Argument gelesen werden können, das an onGetRoot() übergeben wird. Wenn diese Hinweise befolgt werden, kann das System die Stamm-Inhalte als Navigationstabs anzeigen. Wenn du diese Hinweise nicht befolgst, werden einige Stamm-Inhalte möglicherweise vom System entfernt oder weniger auffindbar gemacht.

Root-Inhalte als Navigationstabs

Abbildung 1 : Stamm-Inhalte, die als Navigationstabs angezeigt werden.

Wenn du diese Hinweise anwendest, zeigt das System die Stamm-Inhalte als Navigationstabs an. Wenn du diese Hinweise nicht anwendest, werden einige Stamm-Inhalte möglicherweise entfernt oder weniger auffindbar gemacht. Diese Hinweise werden übertragen:

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

Du kannst die Logik für die Struktur deiner Inhaltshierarchie basierend auf den Werten dieser Hinweise verzweigen, insbesondere wenn sich deine Hierarchie zwischen MediaBrowser-Integrationen außerhalb von Android Auto und AAOS unterscheidet.

Wenn du beispielsweise normalerweise ein abspielbares Stamm-Element anzeigst, solltest du es aufgrund des Hinweises zu den unterstützten Flags stattdessen unter einem durchsuchbaren Stamm-Element verschachteln.

Neben Stammhinweisen kannst du diese Richtlinien verwenden, um Tabs optimal zu rendern:

  • Monochrome (vorzugsweise weiße) Symbole für jedes Tab-Element

  • Kurze und aussagekräftige Labels für jedes Tab-Element (kurze Labels verringern die Wahrscheinlichkeit, dass Labels abgeschnitten werden)