Medien-Apps für Autos entwickeln

Mit Android Auto und Android Automotive OS können Sie Nutzern Ihre Medien-App-Inhalte in ihrem Auto zur Verfügung stellen.

Es gibt zwei Möglichkeiten, Medien-Apps für Autos zu erstellen:

  • In diesem Leitfaden wird beschrieben, wie Sie mit einem MediaBrowserService und einem MediaSession eine App erstellen, mit der Android Auto und Android Automotive OS eine Verbindung herstellen können, um für die Verwendung im Auto optimierte Ansichten für das Durchsuchen und Abspielen von Medien zu rendern.

  • Medien-Apps können auch mit Car App Library-Vorlagen erstellt werden, die anpassbare Formatierung, Browsing-Funktionen und erweiterte benutzerdefinierte Aktionen bieten. Weitere Informationen zur Implementierung finden Sie unter Medien-App mit Vorlagen erstellen. Vorlagenbasierte Media-Apps werden derzeit nur auf Android Auto unterstützt.

In dieser Anleitung werden die erforderlichen Komponenten eines MediaBrowserService und MediaSession beschrieben, die Ihre App benötigt, um auf Android Auto oder Android Automotive OS zu funktionieren. Nachdem Sie die Medieninfrastruktur abgeschlossen haben, können Sie Ihrer Medien-App Unterstützung für Android Auto und Unterstützung für Android Automotive OS hinzufügen.

In diesem Leitfaden wird davon ausgegangen, dass Sie bereits eine Media-App haben, die Audio auf einem Smartphone abspielt, und dass Ihre Media-App der Architektur für Android-Media-Apps entspricht.

Hinweis

  1. Dokumentation zu Android Media APIs
  2. Designrichtlinien für Media-Apps
  3. Sehen Sie sich die in diesem Abschnitt aufgeführten wichtigen Begriffe und Konzepte an.

Wichtige Begriffe und Konzepte

Medienbrowser-Dienst
Ein Android-Dienst, der von deiner Media-App implementiert wird und der MediaBrowserServiceCompat-API entspricht. Ihre App verwendet diesen Dienst, um ihre Inhalte zu präsentieren.
Media-Browser
Eine API, die von Media-Apps verwendet wird, um Media-Browser-Dienste zu erkennen und deren Inhalte anzuzeigen. Android Auto und Android Automotive OS verwenden einen Media-Browser, um den Media-Browser-Dienst Ihrer App zu finden.
Medienelement

Der Media-Browser organisiert seine Inhalte in einer Baumstruktur von MediaItem-Objekten. Ein Media-Element kann eines oder beide der folgenden Flags haben:

  • FLAG_PLAYABLE: Gibt an, dass der Artikel ein Blatt im Inhaltsbaum ist. Das Element stellt einen einzelnen Soundstream dar, z. B. einen Titel auf einem Album, ein Kapitel in einem Hörbuch oder eine Folge eines Podcasts.
  • FLAG_BROWSABLE: Gibt an, dass das Element ein Knoten im Inhaltsbaum ist und untergeordnete Elemente hat. Das Element stellt beispielsweise ein Album dar und seine untergeordneten Elemente sind die Songs auf dem Album.

Ein Medienelement, das sowohl durchsuchbar als auch abspielbar ist, verhält sich wie eine Playlist. Sie können das Element selbst auswählen, um alle untergeordneten Elemente abzuspielen, oder die untergeordneten Elemente durchsuchen.

Für Fahrzeuge optimiert

Eine Aktivität für eine Android Automotive OS-App, die den Android Automotive OS-Designrichtlinien entspricht. Die Benutzeroberfläche für diese Aktivitäten wird nicht von Android Automotive OS gerendert. Sie müssen also dafür sorgen, dass Ihre App den Designrichtlinien entspricht. Dazu gehören in der Regel größere Tippziele und Schriftgrößen, Unterstützung für den Tag- und Nachtmodus sowie höhere Kontrastverhältnisse.

Fahrzeugoptimierte Benutzeroberflächen dürfen nur angezeigt werden, wenn keine Car User Experience Restrictions (CUXRs) gelten, da diese Oberflächen möglicherweise eine längere Aufmerksamkeit oder Interaktion des Nutzers erfordern. CUXRs sind nicht wirksam, wenn das Auto steht oder geparkt ist, aber immer, wenn es sich bewegt.

Sie müssen keine Aktivitäten für Android Auto entwerfen, da Android Auto eine eigene für Fahrzeuge optimierte Benutzeroberfläche mit den Informationen aus Ihrem Media-Browser-Dienst erstellt.

Manifestdateien der App konfigurieren

Bevor Sie Ihren Media-Browser-Dienst erstellen können, müssen Sie die Manifestdateien Ihrer App konfigurieren.

Medienbrowser-Dienst deklarieren

Sowohl Android Auto als auch Android Automotive OS stellen über Ihren Media-Browser-Dienst eine Verbindung zu Ihrer App her, um Medieninhalte zu durchsuchen. Deklarieren Sie Ihren MediaBrowser-Dienst in Ihrem Manifest, damit Android Auto und Android Automotive OS den Dienst erkennen und eine Verbindung zu Ihrer App herstellen können.

Das folgende Code-Snippet zeigt, wie Sie Ihren Media-Browser-Dienst in Ihrem Manifest deklarieren. Fügen Sie diesen Code in die Manifestdatei für Ihr Android Automotive OS-Modul und in die Manifestdatei für Ihre Smartphone-App ein.

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

App-Symbole angeben

Sie müssen App-Symbole angeben, die von Android Auto und Android Automotive OS verwendet werden können, um Ihre App in der System-UI darzustellen. Es sind zwei Arten von Symbolen erforderlich:

  • Launcher-Symbol
  • Attributionssymbol

Launcher-Symbol

Das Launcher-Symbol repräsentiert Ihre App in der System-UI, z. B. im Launcher und in der Symbolleiste. Sie können angeben, dass das Symbol Ihrer mobilen App für Ihre Car Media-App verwendet werden soll. Verwenden Sie dazu die folgende Manifestdeklaration:

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

Wenn Sie ein anderes Symbol als das Ihrer mobilen App verwenden möchten, legen Sie die Eigenschaft android:icon im <service>-Element Ihres Media-Browser-Dienstes im Manifest fest:

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

Attributionssymbol

Abbildung 1: Symbol für die Quellenangabe auf der Medienkarte.

Das Attributionssymbol wird an Stellen verwendet, an denen Medieninhalte Vorrang haben, z. B. auf Medienkarten. Sie können das kleine Symbol, das für Benachrichtigungen verwendet wird, wiederverwenden. Dieses Symbol muss einfarbig sein. Sie können ein Symbol angeben, das zur Darstellung Ihrer App verwendet wird, indem Sie die folgende Manifestdeklaration verwenden:

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

Medienbrowser-Dienst erstellen

Sie erstellen einen Medienbrowser-Dienst, indem Sie die Klasse MediaBrowserServiceCompat erweitern. Sowohl Android Auto als auch Android Automotive OS können Ihren Dienst dann für Folgendes verwenden:

  • Durchsuchen Sie die Inhaltshierarchie Ihrer App, um dem Nutzer ein Menü zu präsentieren.
  • Rufen Sie das Token für das MediaSessionCompat-Objekt Ihrer App ab, um die Audiowiedergabe zu steuern.

Sie können Ihren Mediabrowserdienst auch verwenden, um anderen Clients den Zugriff auf Medieninhalte aus Ihrer App zu ermöglichen. Diese Media-Clients können andere Apps auf dem Smartphone eines Nutzers oder andere Remote-Clients sein.

Workflow für Medienbrowser-Dienst

In diesem Abschnitt wird beschrieben, wie Android Automotive OS und Android Auto während eines typischen Nutzer-Workflows mit Ihrem Media-Browser-Dienst interagieren.

  1. Der Nutzer startet Ihre App unter Android Automotive OS oder Android Auto.
  2. Android Automotive OS oder Android Auto stellen über die Methode onCreate() eine Verbindung zum Media Browser-Dienst Ihrer App her. In Ihrer Implementierung der Methode onCreate() müssen Sie ein MediaSessionCompat-Objekt und das zugehörige Callback-Objekt erstellen und registrieren.
  3. Android Automotive OS oder Android Auto ruft die Methode onGetRoot() Ihres Dienstes auf, um das Stamm-Media-Element in Ihrer Inhaltshierarchie abzurufen. Das Stamm-Media-Element wird nicht angezeigt, sondern zum Abrufen weiterer Inhalte aus Ihrer App verwendet.
  4. Android Automotive OS oder Android Auto ruft die Methode onLoadChildren() Ihres Dienstes auf, um die untergeordneten Elemente des Stamm-Media-Elements abzurufen. In Android Automotive OS und Android Auto werden diese Media-Elemente als oberste Ebene der Inhaltselemente angezeigt. Weitere Informationen dazu, was das System auf dieser Ebene erwartet, finden Sie auf dieser Seite im Abschnitt Struktur des Stammmenüs.
  5. Wenn der Nutzer ein durchsuchbares Media-Element auswählt, wird die Methode onLoadChildren() Ihres Dienstes noch einmal aufgerufen, um die untergeordneten Elemente des ausgewählten Menüelements abzurufen.
  6. Wenn der Nutzer ein abspielbares Media-Element auswählt, ruft Android Automotive OS oder Android Auto die entsprechende Media-Sitzungs-Callback-Methode auf, um diese Aktion auszuführen.
  7. Wenn Ihre App dies unterstützt, kann der Nutzer auch in Ihren Inhalten suchen. In diesem Fall rufen Android Automotive OS oder Android Auto die Methode onSearch() Ihres Dienstes auf.

Contenthierarchie erstellen

Android Auto und Android Automotive OS rufen den MediaBrowser-Dienst Ihrer App auf, um herauszufinden, welche Inhalte verfügbar sind. Dazu müssen Sie in Ihrem Media-Browser-Dienst zwei Methoden implementieren: onGetRoot() und onLoadChildren().

„onGetRoot“ implementieren

Die Methode onGetRoot() Ihres Dienstes gibt Informationen zum Stammknoten Ihrer Inhaltshierarchie zurück. Android Auto und Android Automotive OS verwenden diesen Stammknoten, um den Rest Ihrer Inhalte mit der Methode onLoadChildren() anzufordern.

Das folgende Code-Snippet zeigt eine einfache 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 detaillierteres Beispiel für diese Methode finden Sie in der onGetRoot()-Methode in der Universal Android Music Player-Beispiel-App auf GitHub.

Paketvalidierung für onGetRoot() hinzufügen

Wenn die onGetRoot()-Methode Ihres Dienstes aufgerufen wird, übergibt das aufrufende Paket Identifikationsinformationen an Ihren Dienst. Ihr Dienst kann anhand dieser Informationen entscheiden, ob das Paket auf Ihre Inhalte zugreifen darf. Sie können beispielsweise den Zugriff auf die Inhalte Ihrer App auf eine Liste genehmigter Pakete beschränken, indem Sie den clientPackageName mit Ihrer Zulassungsliste vergleichen und das Zertifikat überprüfen, das zum Signieren des APK des Pakets verwendet wurde. Wenn das Paket nicht überprüft werden kann, gib null zurück, um den Zugriff auf deine Inhalte zu verweigern.

Damit System-Apps wie Android Auto und Android Automotive OS auf Ihre Inhalte zugreifen können, muss Ihr Dienst immer einen Wert ungleich null für BrowserRoot zurückgeben, wenn diese System-Apps die Methode onGetRoot() aufrufen. Die Signatur der Android Automotive OS-System-App kann je nach Marke und Modell des Autos variieren. Daher müssen Sie Verbindungen von allen System-Apps zulassen, um Android Automotive OS zuverlässig zu unterstützen.

Das folgende Code-Snippet zeigt, wie Ihr 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
}

Dieses Code-Snippet ist ein Auszug aus der Klasse PackageValidator in der Universal Android Music Player-Beispiel-App auf GitHub. In dieser Klasse finden Sie ein detaillierteres Beispiel für die Implementierung der Paketvalidierung für die onGetRoot()-Methode Ihres Dienstes.

Sie müssen nicht nur System-Apps zulassen, sondern auch Google Assistant die Verbindung zu Ihrem MediaBrowserService erlauben. Google Assistant hat separate Paketnamen für das Smartphone, das Android Auto enthält, und für Android Automotive OS.

onLoadChildren() implementieren

Nachdem Android Auto und Android Automotive OS das Stammknotenobjekt erhalten haben, wird ein Menü der obersten Ebene erstellt, indem onLoadChildren() für das Stammknotenobjekt aufgerufen wird, um die untergeordneten Elemente abzurufen. Client-Apps erstellen Untermenüs, indem sie dieselbe Methode mit untergeordneten Knotenobjekten aufrufen.

Jeder Knoten in Ihrer Inhaltshierarchie wird durch ein MediaBrowserCompat.MediaItem-Objekt dargestellt. Jedes dieser Media-Elemente wird durch einen eindeutigen ID-String identifiziert. Client-Apps behandeln diese ID-Strings als intransparente Tokens. Wenn eine Clientanwendung ein Untermenü aufrufen oder ein Media-Element abspielen möchte, übergibt sie das Token. Ihre App ist dafür verantwortlich, das Token dem entsprechenden Media-Element zuzuordnen.

Das folgende Code-Snippet zeigt eine einfache Implementierung der Methode 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);
}

Ein vollständiges Beispiel für diese Methode finden Sie in der Beispiel-App „Universal Android Music Player“ auf GitHub in der Methode onLoadChildren().

Strukturieren des Stammmenüs

Abbildung 2: Root-Inhalte, die als Navigationstabs angezeigt werden.

Für Android Auto und Android Automotive OS gelten bestimmte Einschränkungen hinsichtlich der Struktur des Stammmenüs. Diese werden über Root-Hinweise an MediaBrowserService übermittelt, die über das Argument Bundle gelesen werden können, das an onGetRoot() übergeben wird. Wenn Sie diese Hinweise beachten, kann das System die Root-Inhalte optimal als Navigationstabs darstellen. Wenn Sie diese Hinweise nicht beachten, werden einige Root-Inhalte möglicherweise vom System entfernt oder weniger auffindbar gemacht. Es werden zwei Hinweise gesendet:

Verwenden Sie den folgenden Code, um die relevanten Root-Hinweise zu lesen:

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

Sie können die Logik für die Struktur Ihrer Inhaltshierarchie basierend auf den Werten dieser Hinweise verzweigen, insbesondere wenn sich Ihre Hierarchie zwischen MediaBrowser-Integrationen außerhalb von Android Auto und Android Automotive OS unterscheidet. Wenn Sie beispielsweise normalerweise ein abspielbares Stamm-Element präsentieren, möchten Sie es möglicherweise aufgrund des Hinweises zum Wert der unterstützten Flags stattdessen unter einem durchsuchbaren Stamm-Element einbetten.

Neben den Root-Hinweisen gibt es einige zusätzliche Richtlinien, die Sie beachten sollten, damit Tabs optimal gerendert werden:

  • Stellen Sie für jedes Tab-Element einfarbige, vorzugsweise weiße Symbole bereit.
  • Geben Sie für jedes Tab-Element kurze, aber aussagekräftige Labels an. Wenn Sie Labels kurz halten, verringern Sie die Wahrscheinlichkeit, dass die Strings abgeschnitten werden.

Media-Grafiken anzeigen

Das Artwork für Media-Elemente muss als lokaler URI über ContentResolver.SCHEME_CONTENT oder ContentResolver.SCHEME_ANDROID_RESOURCE übergeben werden. Dieser lokale URI muss entweder zu einer Bitmap oder einem Vektordrawable in den Ressourcen der Anwendung aufgelöst werden. Für MediaDescriptionCompat-Objekte, die Elemente in der Inhaltshierarchie darstellen, übergeben Sie den URI über setIconUri(). Für MediaMetadataCompat-Objekte, die das aktuell wiedergegebene Element darstellen, übergeben Sie den URI über putString() mit einem der folgenden Schlüssel:

In den folgenden Schritten wird beschrieben, wie Sie Grafiken von einem Web-URI herunterladen und über einen lokalen URI verfügbar machen. Ein vollständigeres Beispiel finden Sie in der Implementierung von openFile() und den umgebenden Methoden in der Universal Android Music Player-Beispiel-App.

  1. Erstellen Sie einen content://-URI, der dem Web-URI entspricht. Der Media Browser-Dienst und die Media Session übergeben diesen Inhalts-URI an Android Auto und 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. Prüfen Sie in Ihrer Implementierung von ContentProvider.openFile(), ob eine Datei für den entsprechenden URI vorhanden ist. Falls nicht, laden Sie die Bilddatei herunter und speichern Sie sie im Cache. Im folgenden Code-Snippet wird Glide verwendet.

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

Weitere Informationen zu Inhaltsanbietern finden Sie unter Inhaltsanbieter erstellen.

Inhaltsstile anwenden

Nachdem Sie Ihre Inhaltshierarchie mit durchsuchbaren oder abspielbaren Elementen erstellt haben, können Sie Inhaltsstile anwenden, die bestimmen, wie diese Elemente im Auto angezeigt werden.

Sie können die folgenden Inhaltsstile verwenden:

Listenelemente

Bei diesem Content-Stil werden Titel und Metadaten gegenüber Bildern priorisiert.

Rasterelemente

Bei diesem Content-Stil werden Bilder gegenüber Titeln und Metadaten priorisiert.

Standardstile für Inhalte festlegen

Sie können globale Standardeinstellungen für die Darstellung Ihrer Media-Elemente festlegen, indem Sie bestimmte Konstanten in das BrowserRoot-Extras-Bundle der onGetRoot()-Methode Ihres Dienstes einfügen. Android Auto und Android Automotive OS lesen dieses Bundle und suchen nach diesen Konstanten, um den entsprechenden Stil zu bestimmen.

Die folgenden Extras können als Schlüssel im Bundle verwendet werden:

Die Schlüssel können den folgenden ganzzahligen Konstantenwerten zugeordnet werden, um die Darstellung der Elemente zu beeinflussen:

  • DESCRIPTION_EXTRAS_VALUE_CONTENT_STYLE_LIST_ITEM: Die entsprechenden Elemente werden als Listenelemente dargestellt.
  • DESCRIPTION_EXTRAS_VALUE_CONTENT_STYLE_GRID_ITEM: Die entsprechenden Elemente werden als Rasterelemente dargestellt.
  • DESCRIPTION_EXTRAS_VALUE_CONTENT_STYLE_CATEGORY_LIST_ITEM: Die entsprechenden Elemente werden als Listenelemente vom Typ „category“ dargestellt. Diese sind mit normalen Listenelementen identisch, mit der Ausnahme, dass um die Symbole der Elemente herum Ränder eingefügt werden, da die Symbole in klein besser aussehen. Die Symbole müssen färbbare Vektor-Drawables sein. Dieser Hinweis sollte nur für durchsuchbare Elemente angezeigt werden.
  • DESCRIPTION_EXTRAS_VALUE_CONTENT_STYLE_CATEGORY_GRID_ITEM: Die entsprechenden Elemente werden als Rasterelemente der Kategorie dargestellt. Diese sind mit normalen Rasterelementen identisch, mit dem Unterschied, dass um die Symbole der Elemente herum Ränder angewendet werden, da die Symbole in klein besser aussehen. Die Symbole müssen färbbare Vektor-Drawables sein. Dieser Hinweis sollte nur für durchsuchbare Elemente angezeigt werden.

Im folgenden Code-Snippet wird gezeigt, wie Sie das Standardinhaltsformat für durchsuchbare Elemente auf Raster und für abspielbare Elemente auf Listen festlegen:

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

Inhaltsstile für einzelne Elemente festlegen

Mit der Content Style API können Sie den Standardinhaltsstil für untergeordnete Elemente eines durchsuchbaren Media-Elements sowie für Media-Elemente selbst überschreiben.

Wenn Sie die Standardeinstellung für die children eines durchsuchbaren Media-Elements überschreiben möchten, erstellen Sie im MediaDescription des Media-Elements ein Extras-Bundle und fügen Sie die oben genannten Hinweise hinzu. DESCRIPTION_EXTRAS_KEY_CONTENT_STYLE_PLAYABLE gilt für die abspielbaren untergeordneten Elemente dieses Elements, während DESCRIPTION_EXTRAS_KEY_CONTENT_STYLE_BROWSABLE für die durchsuchbaren untergeordneten Elemente dieses Elements gilt.

Wenn Sie den Standardwert für ein bestimmtes Media-Element selbst und nicht für seine untergeordneten Elemente überschreiben möchten, erstellen Sie im MediaDescription des Media-Elements ein Extras-Bundle und fügen Sie einen Hinweis mit dem Schlüssel DESCRIPTION_EXTRAS_KEY_CONTENT_STYLE_SINGLE_ITEM hinzu. Verwenden Sie dieselben Werte wie oben beschrieben, um die Darstellung des Artikels festzulegen.

Das folgende Code-Snippet zeigt, wie Sie ein durchsuchbares MediaItem erstellen, das den Standardinhaltsstil für sich selbst und seine untergeordneten Elemente überschreibt. Es wird als Kategorie-Listenelement formatiert, seine durchsuchbaren untergeordneten Elemente als Listenelemente und seine abspielbaren untergeordneten Elemente als Gridelemente:

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

Elemente anhand von Titelhinweisen gruppieren

Um ähnliche Media-Elemente zu gruppieren, verwenden Sie einen Hinweis pro Element. Für jedes Media-Element in einer Gruppe muss im MediaDescription ein Extras-Bundle deklariert werden, das eine Zuordnung mit dem Schlüssel DESCRIPTION_EXTRAS_KEY_CONTENT_STYLE_GROUP_TITLE und einem identischen Stringwert enthält. Lokalisieren Sie diesen String, der als Titel der Gruppe verwendet wird.

Das folgende Code-Snippet zeigt, wie Sie ein MediaItem-Objekt mit der Untergruppenüberschrift "Songs" erstellen:

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*/);
}

Ihre App muss alle Media-Elemente, die Sie gruppieren möchten, als zusammenhängenden Block übergeben. Angenommen, Sie möchten zwei Gruppen von Media-Elementen, „Songs“ und „Alben“, in dieser Reihenfolge anzeigen und Ihre App übergibt fünf Media-Elemente in der folgenden Reihenfolge:

  1. Medienelement A mit extras.putString(MediaConstants.DESCRIPTION_EXTRAS_KEY_CONTENT_STYLE_GROUP_TITLE, "Songs")
  2. Medienelement B mit extras.putString(MediaConstants.DESCRIPTION_EXTRAS_KEY_CONTENT_STYLE_GROUP_TITLE, "Albums")
  3. Medienelement C mit extras.putString(MediaConstants.DESCRIPTION_EXTRAS_KEY_CONTENT_STYLE_GROUP_TITLE, "Songs")
  4. Mediendatei D mit extras.putString(MediaConstants.DESCRIPTION_EXTRAS_KEY_CONTENT_STYLE_GROUP_TITLE, "Songs")
  5. Medienelement E mit extras.putString(MediaConstants.DESCRIPTION_EXTRAS_KEY_CONTENT_STYLE_GROUP_TITLE, "Albums")

Da die Media-Elemente für die Gruppen „Songs“ und „Alben“ nicht in zusammenhängenden Blöcken gespeichert werden, interpretiert Android Auto und Android Automotive OS dies als die folgenden vier Gruppen:

  • Gruppe 1 mit dem Namen „Songs“ mit Media-Element A
  • Gruppe 2 mit dem Namen „Alben“, die das Media-Element B enthält
  • Gruppe 3 mit dem Namen „Songs“ mit den Media-Elementen C und D
  • Gruppe 4 mit dem Namen „Alben“, die das Media-Element E enthält

Damit diese Elemente in zwei Gruppen angezeigt werden, muss Ihre App die Media-Elemente stattdessen in der folgenden Reihenfolge übergeben:

  1. Medienelement A mit extras.putString(MediaConstants.DESCRIPTION_EXTRAS_KEY_CONTENT_STYLE_GROUP_TITLE, "Songs")
  2. Medienelement C mit extras.putString(MediaConstants.DESCRIPTION_EXTRAS_KEY_CONTENT_STYLE_GROUP_TITLE, "Songs")
  3. Mediendatei D mit extras.putString(MediaConstants.DESCRIPTION_EXTRAS_KEY_CONTENT_STYLE_GROUP_TITLE, "Songs")
  4. Medienelement B mit extras.putString(MediaConstants.DESCRIPTION_EXTRAS_KEY_CONTENT_STYLE_GROUP_TITLE, "Albums")
  5. Medienelement E mit extras.putString(MediaConstants.DESCRIPTION_EXTRAS_KEY_CONTENT_STYLE_GROUP_TITLE, "Albums")

Zusätzliche Metadatenindikatoren anzeigen

Sie können zusätzliche Metadatenindikatoren einfügen, um auf einen Blick Informationen zu Inhalten in der Media-Browser-Baumstruktur und während der Wiedergabe zu erhalten. Im Browse-Baum werden in Android Auto und Android Automotive OS die Extras gelesen, die einem Element zugeordnet sind. Es wird nach bestimmten Konstanten gesucht, um zu ermitteln, welche Indikatoren angezeigt werden sollen. Während der Medienwiedergabe lesen Android Auto und Android Automotive OS die Metadaten für die Mediensitzung und suchen nach bestimmten Konstanten, um anzuzeigende Indikatoren zu ermitteln.

Abbildung 3: Wiedergabeansicht mit Metadaten, die den Song und den Künstler identifizieren, sowie einem Symbol für explizite Inhalte.

Abbildung 4: Die Browsing-Ansicht mit einem Punkt für nicht wiedergegebene Inhalte beim ersten Element und einem Fortschrittsbalken für teilweise wiedergegebene Inhalte beim zweiten Element.

Die folgenden Konstanten können sowohl in MediaItem-Beschreibungsextras als auch in MediaMetadata-Extras verwendet werden:

Die folgenden Konstanten können nur in MediaItem-Beschreibungsextras verwendet werden:

Wenn Sie Indikatoren anzeigen möchten, die eingeblendet werden, während der Nutzer den Media-Browser-Baum durchsucht, erstellen Sie ein Extras-Bundle, das eine oder mehrere dieser Konstanten enthält, und übergeben Sie dieses Bundle an die Methode MediaDescription.Builder.setExtras().

Das folgende Code-Snippet zeigt, wie Indikatoren für ein explizites Media-Element angezeigt werden, das zu 70% abgeschlossen ist:

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 */);

Wenn Sie Indikatoren für ein Media-Element anzeigen möchten, das gerade wiedergegeben wird, können Sie Long-Werte für METADATA_KEY_IS_EXPLICIT oder EXTRA_DOWNLOAD_STATUS im MediaMetadataCompat Ihres mediaSession deklarieren. Die Symbole DESCRIPTION_EXTRAS_KEY_COMPLETION_STATUS und DESCRIPTION_EXTRAS_KEY_COMPLETION_PERCENTAGE können in der Wiedergabeansicht nicht angezeigt werden.

Das folgende Code-Snippet zeigt, wie Sie angeben, dass der aktuelle Song in der Wiedergabeansicht explizit und heruntergeladen ist:

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

Aktualisieren der Fortschrittsanzeige in der Ansicht „Entdecken“ während der Wiedergabe von Inhalten

Wie bereits erwähnt, können Sie mit dem Extra DESCRIPTION_EXTRAS_KEY_COMPLETION_PERCENTAGE eine Fortschrittsanzeige für teilweise wiedergegebene Inhalte in der Ansicht „Durchsuchen“ einblenden. Wenn ein Nutzer die teilweise wiedergegebenen Inhalte jedoch über Android Auto oder Android Automotive OS weiter abspielt, wird die Anzeige mit der Zeit ungenau.

Damit die Fortschrittsanzeige in Android Auto und Android Automotive OS auf dem neuesten Stand bleibt, kannst du in MediaMetadataCompat und PlaybackStateCompat zusätzliche Informationen angeben, um laufende Inhalte mit Media-Elementen in der Ansicht „Durchsuchen“ zu verknüpfen. Damit für ein Media-Element automatisch eine Fortschrittsanzeige aktualisiert wird, müssen die folgenden Anforderungen erfüllt sein:

Das folgende Code-Snippet zeigt, wie Sie angeben, dass das aktuell wiedergegebene Element mit einem Element in der Ansicht „Durchsuchen“ verknüpft ist:

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

Abbildung 5: Wiedergabeansicht mit der Option „Suchergebnisse“ zum Aufrufen von Media-Elementen, die mit der Sprachsuche des Nutzers zusammenhängen.

Ihre App kann kontextbezogene Suchergebnisse liefern, die Nutzern angezeigt werden, wenn sie eine Suchanfrage starten. In Android Auto und Android Automotive OS werden diese Ergebnisse über Suchanfrageoberflächen oder über Affordances angezeigt, die auf Anfragen basieren, die zu einem früheren Zeitpunkt in der Sitzung gestellt wurden. Weitere Informationen finden Sie in diesem Leitfaden im Abschnitt Sprachaktionen unterstützen.

Wenn Sie durchsuchbare Suchergebnisse anzeigen möchten, fügen Sie den Konstantenschlüssel BROWSER_SERVICE_EXTRAS_KEY_SEARCH_SUPPORTED in das Extras-Bundle der Methode onGetRoot() Ihres Dienstes ein und ordnen Sie ihn dem booleschen Wert true zu.

Das folgende Code-Snippet zeigt, wie Sie die Unterstützung in der Methode onGetRoot() aktivieren:

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

Überschreiben Sie die Methode onSearch() in Ihrem Media-Browser-Dienst, um Suchergebnisse zu liefern. Android Auto und Android Automotive OS leiten die Suchbegriffe des Nutzers an diese Methode weiter, wenn ein Nutzer eine Suchanfrageoberfläche oder eine Schaltfläche für Suchergebnisse aufruft.

Sie können die Suchergebnisse der onSearch()-Methode Ihres Dienstes mit Titelelementen organisieren, um sie übersichtlicher zu gestalten. Wenn Ihre App beispielsweise Musik abspielt, können Sie die Suchergebnisse nach Album, Künstler und Songs sortieren.

Das folgende Code-Snippet zeigt eine einfache Implementierung der Methode 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.
}

Benutzerdefinierte Browse-Aktionen

Eine einzelne benutzerdefinierte Browseraktion.

Abbildung 6 Einzelne benutzerdefinierte Browseraktion

Mit benutzerdefinierten Browse-Aktionen können Sie den MediaItem-Objekten Ihrer App in der Media-App des Autos benutzerdefinierte Symbole und Labels hinzufügen und Nutzerinteraktionen mit diesen Aktionen verarbeiten. So können Sie die Funktionalität der Media App auf verschiedene Arten erweitern, z. B. durch Hinzufügen der Aktionen „Herunterladen“, „Zur Warteschlange hinzufügen“, „Radiosender abspielen“, „Favorisieren“ oder „Entfernen“.

Ein benutzerdefiniertes Dreipunkt-Menü für Browseraktionen.

Abbildung 7. Dreipunkt-Menü für benutzerdefinierte Browse-Aktion

Wenn mehr benutzerdefinierte Aktionen vorhanden sind, als der OEM anzeigen darf, wird dem Nutzer ein Überlaufmenü angezeigt.

So funktioniert es:

Jede benutzerdefinierte Suchaktion wird durch Folgendes definiert:

  • Eine Aktions-ID (eine eindeutige String-Kennung)
  • Ein Aktionslabel (der Text, der dem Nutzer angezeigt wird)
  • Ein URI für ein Aktionssymbol (ein Vektor-Drawable, das eingefärbt werden kann)

Sie definieren eine Liste benutzerdefinierter Browseraktionen global als Teil von BrowseRoot. Anschließend können Sie eine Teilmenge dieser Aktionen an einzelne MediaItem. anhängen.

Wenn ein Nutzer mit einer benutzerdefinierten Browsing-Aktion interagiert, erhält Ihre App einen Callback in onCustomAction(). Anschließend können Sie die Aktion ausführen und die Liste der Aktionen für MediaItem bei Bedarf aktualisieren. Das ist nützlich für zustandsorientierte Aktionen wie „Favorisieren“ und „Herunterladen“. Bei Aktionen, die nicht aktualisiert werden müssen, z. B. „Play Radio“ (Spiele Radio), müssen Sie die Liste der Aktionen nicht aktualisieren.

Benutzerdefinierte Browse-Aktionen in einem Browse-Knoten-Root.

Abbildung 8. Symbolleiste für benutzerdefinierte Browseraktionen

Sie können einem Browse-Knoten-Root auch benutzerdefinierte Browse-Aktionen anhängen. Diese Aktionen werden in einer sekundären Symbolleiste unter der Hauptsymbolleiste angezeigt.

Benutzerdefinierte Browseraktionen implementieren

So fügen Sie Ihrem Projekt benutzerdefinierte Browse-Aktionen hinzu:

  1. Überschreiben Sie zwei Methoden in Ihrer MediaBrowserServiceCompat-Implementierung:
  2. Aktionslimits zur Laufzeit parsen:
    • Rufen Sie in onGetRoot() die maximal zulässige Anzahl von Aktionen für jedes MediaItem mit dem Schlüssel BROWSER_ROOT_HINTS_KEY_CUSTOM_BROWSER_ACTION_LIMIT im rootHints Bundle ab. Ein Limit von 0 gibt an, dass das Feature vom System nicht unterstützt wird.
  3. Globale Liste der benutzerdefinierten Browseraktionen erstellen:
    • Erstelle für jede Aktion ein Bundle-Objekt mit den folgenden Schlüsseln: * EXTRAS_KEY_CUSTOM_BROWSER_ACTION_ID: Die Aktions-ID * EXTRAS_KEY_CUSTOM_BROWSER_ACTION_LABEL: Das Aktionslabel * EXTRAS_KEY_CUSTOM_BROWSER_ACTION_ICON_URI: Der URI des Aktionssymbols * Füge alle Bundle-Objekte der Aktion in eine Liste ein.
  4. Fügen Sie die globale Liste Ihrem BrowseRoot hinzu:
  5. Fügen Sie Ihren MediaItem-Objekten Aktionen hinzu:
    • Sie können einzelnen MediaItem-Objekten Aktionen hinzufügen, indem Sie die Liste der Aktions-IDs in den MediaDescriptionCompat-Extras mit dem Schlüssel DESCRIPTION_EXTRAS_KEY_CUSTOM_BROWSER_ACTION_ID_LIST einfügen. Diese Liste muss eine Teilmenge der globalen Liste der Aktionen sein, die Sie in der BrowseRoot definiert haben.
  6. Aktionen verarbeiten und Fortschritt oder Ergebnisse zurückgeben:

Hier sind einige Änderungen, die Sie in Ihrem BrowserServiceCompat vornehmen können, um mit benutzerdefinierten Browseraktionen zu beginnen.

BrowserServiceCompat überschreiben

Sie müssen die folgenden Methoden in MediaBrowserServiceCompat überschreiben.

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

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

Limit für Parsing-Vorgänge

Prüfen Sie, wie viele benutzerdefinierte Browseraktionen unterstützt werden.

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

Benutzerdefinierte Browse-Aktion erstellen

Jede Aktion muss in einem separaten Bundle enthalten sein.

  • Aktions-ID
    bundle.putString(MediaConstants.EXTRAS_KEY_CUSTOM_BROWSER_ACTION_ID,
                    "<ACTION_ID>")
  • Aktionslabel
    bundle.putString(MediaConstants.EXTRAS_KEY_CUSTOM_BROWSER_ACTION_LABEL,
                    "<ACTION_LABEL>")
  • Aktionssymbol-URI
    bundle.putString(MediaConstants.EXTRAS_KEY_CUSTOM_BROWSER_ACTION_ICON_URI,
                    "<ACTION_ICON_URI>")

Benutzerdefinierte Browseraktionen für Parceable ArrayList hinzufügen

Fügen Sie alle benutzerdefinierten Bundle-Objekte in ein ArrayList ein.

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

Dem Browsing-Stammverzeichnis eine Liste mit benutzerdefinierten Browsing-Aktionen hinzufügen

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

Aktionen zu einem MediaItem hinzufügen

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);
übergeben wird.

Build-Ergebnis für onCustomAction

  • mediaId aus Bundle extras parsen:
    @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);
    }
  • Bei asynchronen Ergebnissen wird das Ergebnis getrennt. result.detach()
  • Build-Ergebnis-Bundle
    • Nachricht an den Nutzer
      mResultBundle.putString(EXTRAS_KEY_CUSTOM_BROWSER_ACTION_RESULT_MESSAGE,
                mContext.getString(stringRes))
    • Element aktualisieren(zum Aktualisieren von Aktionen in einem Element)
      mResultBundle.putString(EXTRAS_KEY_CUSTOM_BROWSER_ACTION_RESULT_REFRESH_ITEM, mediaId);
    • Wiedergabeansicht öffnen
      //Shows user the PBV without changing the playback state
      mResultBundle.putString(EXTRAS_KEY_CUSTOM_BROWSER_ACTION_RESULT_SHOW_PLAYING_ITEM, null);
    • Knoten aktualisieren
      //Change current browse node to mediaId
      mResultBundle.putString(EXTRAS_KEY_CUSTOM_BROWSER_ACTION_RESULT_BROWSE_NODE, mediaId);
  • Bei einem Fehler rufen Sie result.sendError(resultBundle). an.
  • Wenn es ein Fortschritts-Update gibt, rufen Sie result.sendProgressUpdate(resultBundle) auf.
  • Beende den Vorgang, indem du result.sendResult(resultBundle) anrufst.

Aktionsstatus aktualisieren

Mit der Methode result.sendProgressUpdate(resultBundle) und dem Schlüssel EXTRAS_KEY_CUSTOM_BROWSER_ACTION_RESULT_REFRESH_ITEM können Sie MediaItem aktualisieren, um den neuen Status der Aktion widerzuspiegeln. So können Sie dem Nutzer in Echtzeit Feedback zum Fortschritt und Ergebnis seiner Aktion geben.

Beispiel: Download-Aktion

Hier ist ein Beispiel dafür, wie Sie diese Funktion verwenden können, um eine Downloadaktion mit drei Status zu implementieren:

  1. Herunterladen: Dies ist der Ausgangszustand der Aktion. Wenn der Nutzer diese Aktion auswählt, können Sie sie durch „Wird heruntergeladen“ ersetzen und sendProgressUpdate aufrufen, um die Benutzeroberfläche zu aktualisieren.
  2. Wird heruntergeladen: Dieser Status gibt an, dass der Download läuft. Sie können diesen Status verwenden, um dem Nutzer eine Fortschrittsanzeige oder einen anderen Indikator zu präsentieren.
  3. Heruntergeladen: Dieser Status gibt an, dass der Download abgeschlossen ist. Wenn der Download abgeschlossen ist, können Sie „Downloading“ (Wird heruntergeladen) durch „Downloaded“ (Heruntergeladen) ersetzen und sendResult mit dem Schlüssel EXTRAS_KEY_CUSTOM_BROWSER_ACTION_RESULT_REFRESH_ITEM aufrufen, um anzugeben, dass das Element aktualisiert werden soll. Außerdem können Sie mit dem Schlüssel EXTRAS_KEY_CUSTOM_BROWSER_ACTION_RESULT_MESSAGE eine Erfolgsmeldung für den Nutzer anzeigen.

So können Sie dem Nutzer klares Feedback zum Downloadprozess und seinem aktuellen Status geben. Mit Symbolen können Sie noch mehr Details hinzufügen, um den Downloadstatus von 25%, 50 % und 75% anzuzeigen.

Beispiel: Lieblingsaktion

Ein weiteres Beispiel ist eine Lieblingsaktion mit zwei Status:

  1. Favorisieren: Diese Aktion wird für Elemente angezeigt, die sich nicht in der Favoritenliste des Nutzers befinden. Wenn der Nutzer diese Aktion auswählt, können Sie sie durch „Favorisiert“ ersetzen und sendResult mit dem Schlüssel EXTRAS_KEY_CUSTOM_BROWSER_ACTION_RESULT_REFRESH_ITEM aufrufen, um die Benutzeroberfläche zu aktualisieren.
  2. Favorisiert: Diese Aktion wird für Elemente angezeigt, die sich in der Favoritenliste des Nutzers befinden. Wenn der Nutzer diese Aktion auswählt, können Sie sie durch „Favorisieren“ ersetzen und sendResult mit dem Schlüssel EXTRAS_KEY_CUSTOM_BROWSER_ACTION_RESULT_REFRESH_ITEM aufrufen, um die Benutzeroberfläche zu aktualisieren.

Dieser Ansatz bietet Nutzern eine klare und konsistente Möglichkeit, ihre Lieblingsartikel zu verwalten.

Diese Beispiele veranschaulichen die Flexibilität benutzerdefinierter Browse-Aktionen und wie Sie damit eine Vielzahl von Funktionen mit Echtzeit-Feedback für eine verbesserte Nutzerfreundlichkeit in der Media-App des Autos implementieren können.

Eine vollständige Beispielimplementierung dieser Funktion finden Sie im Projekt TestMediaApp.

Wiedergabesteuerung aktivieren

Android Auto und Android Automotive OS senden Befehle zur Wiedergabesteuerung über die MediaSessionCompat deines Dienstes. Sie müssen eine Sitzung registrieren und die zugehörigen Callback-Methoden implementieren.

Mediensitzung registrieren

Erstelle in der Methode onCreate() deines Media-Browser-Dienstes ein MediaSessionCompat und registriere dann die Media-Session, indem du setSessionToken() aufrufst.

Das folgende Code-Snippet zeigt, wie eine Mediensitzung erstellt und registriert wird:

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

Wenn Sie das Media-Sitzungsobjekt erstellen, legen Sie ein Callback-Objekt fest, das zum Verarbeiten von Anfragen zur Wiedergabesteuerung verwendet wird. Sie erstellen dieses Callback-Objekt, indem Sie eine Implementierung der Klasse MediaSessionCompat.Callback für Ihre App bereitstellen. Im nächsten Abschnitt wird beschrieben, wie Sie dieses Objekt implementieren.

Wiedergabebefehle implementieren

Wenn ein Nutzer die Wiedergabe eines Media-Elements aus Ihrer App anfordert, verwenden Android Automotive OS und Android Auto die Klasse MediaSessionCompat.Callback aus dem MediaSessionCompat-Objekt Ihrer App, das sie vom Media-Browser-Dienst Ihrer App erhalten haben. Wenn ein Nutzer die Wiedergabe von Inhalten steuern möchte, z. B. die Wiedergabe pausieren oder zum nächsten Titel springen, rufen Android Auto und Android Automotive OS eine der Methoden des Callback-Objekts auf.

Für die Wiedergabe von Inhalten muss Ihre App die abstrakte Klasse MediaSessionCompat.Callback erweitern und die von Ihrer App unterstützten Methoden implementieren.

Implementieren Sie alle folgenden Callback-Methoden, die für die Art von Inhalten, die Ihre App bietet, sinnvoll sind:

onPrepare()
Wird aufgerufen, wenn die Media-Quelle geändert wird. Android Automotive OS ruft diese Methode auch unmittelbar nach dem Booten auf. Ihre Media-App muss diese Methode implementieren.
onPlay()
Wird aufgerufen, wenn der Nutzer die Wiedergabe startet, ohne ein bestimmtes Element auszuwählen. Ihre App muss ihre Standardinhalte abspielen oder, wenn die Wiedergabe mit onPause() pausiert wurde, die Wiedergabe fortsetzen.

Hinweis:Ihre App sollte nicht automatisch mit der Musikwiedergabe beginnen, wenn Android Automotive OS oder Android Auto eine Verbindung zu Ihrem Media Browser-Dienst herstellen. Weitere Informationen finden Sie im Abschnitt zum Festlegen des anfänglichen Wiedergabestatus.

onPlayFromMediaId()
Wird aufgerufen, wenn der Nutzer ein bestimmtes Element auswählt, um es abzuspielen. Der Methode wird die ID übergeben, die Ihr Mediabrowserdienst dem Media-Element in Ihrer Inhaltshierarchie zugewiesen hat.
onPlayFromSearch()
Wird aufgerufen, wenn der Nutzer die Wiedergabe über eine Suchanfrage startet. Die App muss eine geeignete Auswahl basierend auf dem übergebenen Suchstring treffen.
onPause()
Wird aufgerufen, wenn der Nutzer die Wiedergabe pausiert.
onSkipToNext()
Wird aufgerufen, wenn der Nutzer zum nächsten Element springen möchte.
onSkipToPrevious()
Wird aufgerufen, wenn der Nutzer zum vorherigen Element springen möchte.
onStop()
Wird aufgerufen, wenn der Nutzer die Wiedergabe beendet.

Überschreiben Sie diese Methoden in Ihrer App, um die gewünschten Funktionen bereitzustellen. Sie müssen keine Methode implementieren, wenn ihre Funktion von Ihrer App nicht unterstützt wird. Wenn Ihre App beispielsweise einen Livestream wie eine Sportübertragung wiedergibt, müssen Sie die Methode onSkipToNext() nicht implementieren. Sie können stattdessen die Standardimplementierung von onSkipToNext() verwenden.

Ihre App benötigt keine spezielle Logik, um Inhalte über die Lautsprecher des Autos abzuspielen. Wenn Ihre App eine Anfrage zum Abspielen von Inhalten erhält, kann sie Audioinhalte auf dieselbe Weise wiedergeben wie über die Lautsprecher oder Kopfhörer eines Smartphones. Android Auto und Android Automotive OS senden die Audioinhalte automatisch an das System des Autos, damit sie über die Lautsprecher des Autos wiedergegeben werden.

Weitere Informationen zur Wiedergabe von Audioinhalten finden Sie in der MediaPlayer-Übersicht, der Übersicht über Audio-Apps und der ExoPlayer-Übersicht.

Standard-Wiedergabeaktionen festlegen

Android Auto und Android Automotive OS zeigen die Wiedergabesteuerung basierend auf den Aktionen an, die im PlaybackStateCompat-Objekt aktiviert sind.

Standardmäßig muss Ihre App die folgenden Aktionen unterstützen:

Ihre App kann zusätzlich die folgenden Aktionen unterstützen, sofern sie für die Inhalte der App relevant sind:

Außerdem haben Sie die Möglichkeit, eine Wiedergabeliste zu erstellen, die dem Nutzer angezeigt werden kann. Dies ist jedoch nicht erforderlich. Rufen Sie dazu die Methoden setQueue() und setQueueTitle() auf, aktivieren Sie die Aktion ACTION_SKIP_TO_QUEUE_ITEM und definieren Sie den Callback onSkipToQueueItem().

Füge außerdem Unterstützung für das Symbol Now Playing (Wird gerade wiedergegeben) hinzu, das angibt, was gerade wiedergegeben wird. Rufen Sie dazu die Methode setActiveQueueItemId() auf und übergeben Sie die ID des aktuell wiedergegebenen Elements in der Warteschlange. Sie müssen setActiveQueueItemId() immer dann aktualisieren, wenn sich die Warteschlange ändert.

In Android Auto und Android Automotive OS werden Schaltflächen für jede aktivierte Aktion sowie die Wiedergabeliste angezeigt. Wenn auf die Schaltflächen geklickt wird, ruft das System den entsprechenden Callback von MediaSessionCompat.Callback auf.

Nicht genutzten Speicherplatz reservieren

In Android Auto und Android Automotive OS wird in der Benutzeroberfläche Platz für die Aktionen ACTION_SKIP_TO_PREVIOUS und ACTION_SKIP_TO_NEXT reserviert. Wenn Ihre App eine dieser Funktionen nicht unterstützt, wird der Bereich in Android Auto und Android Automotive OS verwendet, um benutzerdefinierte Aktionen anzuzeigen, die Sie erstellen.

Wenn Sie diese Bereiche nicht mit benutzerdefinierten Aktionen füllen möchten, können Sie sie reservieren, damit Android Auto und Android Automotive OS den Bereich leer lassen, wenn Ihre App die entsprechende Funktion nicht unterstützt. Rufen Sie dazu die Methode setExtras() mit einem Extras-Bundle auf, das Konstanten enthält, die den reservierten Funktionen entsprechen. SESSION_EXTRAS_KEY_SLOT_RESERVATION_SKIP_TO_NEXT entspricht ACTION_SKIP_TO_NEXT und SESSION_EXTRAS_KEY_SLOT_RESERVATION_SKIP_TO_PREV entspricht ACTION_SKIP_TO_PREVIOUS. Verwenden Sie diese Konstanten als Schlüssel im Bundle und den booleschen Wert true für ihre Werte.

Anfänglichen PlaybackState festlegen

Da Android Auto und Android Automotive OS mit Ihrem Media-Browser-Dienst kommunizieren, wird der Status der Wiedergabe von Inhalten über die PlaybackStateCompat an Ihre Media-Session übermittelt. Ihre App sollte nicht automatisch mit der Musikwiedergabe beginnen, wenn Android Automotive OS oder Android Auto eine Verbindung zu Ihrem MediaBrowserService herstellen. Verlassen Sie sich stattdessen auf Android Auto und Android Automotive OS, um die Wiedergabe basierend auf dem Status des Autos oder Nutzeraktionen fortzusetzen oder zu starten.

Setzen Sie dazu den anfänglichen PlaybackStateCompat Ihrer Mediensitzung auf STATE_STOPPED, STATE_PAUSED, STATE_NONE oder STATE_ERROR.

Mediensitzungen in Android Auto und Android Automotive OS dauern nur so lange wie die Fahrt. Nutzer starten und beenden diese Sitzungen also häufig. Um einen nahtlosen Übergang zwischen Fahrten zu ermöglichen, sollten Sie den vorherigen Sitzungsstatus des Nutzers im Blick behalten. Wenn die Media-App eine Anfrage zum Fortsetzen erhält, kann der Nutzer automatisch dort weitermachen, wo er aufgehört hat, z. B. mit dem zuletzt wiedergegebenen Media-Element, der PlaybackStateCompat und der Warteschlange.

Benutzerdefinierte Wiedergabeaktionen hinzufügen

Du kannst benutzerdefinierte Wiedergabeaktionen hinzufügen, um zusätzliche Aktionen anzuzeigen, die deine Media-App unterstützt. Wenn der Platz ausreicht (und nicht reserviert ist), fügt Android die benutzerdefinierten Aktionen den Transportsteuerungen hinzu. Andernfalls werden die benutzerdefinierten Aktionen im Dreipunkt-Menü angezeigt. Benutzerdefinierte Aktionen werden in der Reihenfolge angezeigt, in der sie der PlaybackStateCompat hinzugefügt werden.

Mit benutzerdefinierten Aktionen können Sie ein Verhalten festlegen, das sich von Standardaktionen unterscheidet. Sie dürfen nicht verwendet werden, um Standardaktionen zu ersetzen oder zu duplizieren.

Mit der Methode addCustomAction() in der Klasse PlaybackStateCompat.Builder können Sie benutzerdefinierte Aktionen hinzufügen.

Das folgende Code-Snippet zeigt, wie Sie die benutzerdefinierte Aktion „Radiosender starten“ hinzufügen:

Kotlin

val customActionExtras = Bundle()
customActionExtras.putInt(
  androidx.media3.session.MediaConstants.EXTRAS_KEY_COMMAND_BUTTON_ICON_COMPAT,
  androidx.media3.session.CommandButton.ICON_RADIO)

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

Java

Bundle customActionExtras = new Bundle();
customActionExtras.putInt(
  androidx.media3.session.MediaConstants.EXTRAS_KEY_COMMAND_BUTTON_ICON_COMPAT,
  androidx.media3.session.CommandButton.ICON_RADIO);

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

Ein detaillierteres Beispiel für diese Methode finden Sie in der setCustomAction()-Methode in der Universal Android Music Player-Beispiel-App auf GitHub.

Nachdem Sie die benutzerdefinierte Aktion erstellt haben, kann Ihre Mediensitzung auf die Aktion reagieren, indem Sie die Methode onCustomAction() überschreiben.

Das folgende Code-Snippet zeigt, wie Ihre App auf die Aktion „Starte einen Radiosender“ reagieren könnte:

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

Ein detaillierteres Beispiel für diese Methode finden Sie in der onCustomAction-Methode in der Universal Android Music Player-Beispiel-App auf GitHub.

Symbole für benutzerdefinierte Aktionen

Für jede benutzerdefinierte Aktion, die Sie erstellen, ist ein Symbol erforderlich.

Wenn die Beschreibung dieses Symbols mit einer der CommandButton.ICON_-Konstanten übereinstimmt, sollten Sie diesen Ganzzahlwert für den EXTRAS_KEY_COMMAND_BUTTON_ICON_COMPAT-Schlüssel der Extras der benutzerdefinierten Aktion festlegen. Auf unterstützten Systemen wird dadurch die an CustomAction.Builder übergebene Symbolressource überschrieben, sodass Systemkomponenten Ihre Aktion und andere Wiedergabeaktionen in einem einheitlichen Stil rendern können.

Sie müssen auch eine Symbolressource angeben. Apps für Autos können auf vielen verschiedenen Bildschirmgrößen und ‑dichten ausgeführt werden. Daher müssen die von Ihnen bereitgestellten Symbole Vektordrawables sein. Mit einem Vektordrawable können Sie Assets skalieren, ohne dass Details verloren gehen. Mit einer Vektorgrafik lassen sich außerdem bei geringeren Auflösungen die Kanten und Ecken an die Pixelgrenzen anpassen.

Wenn eine benutzerdefinierte Aktion statusabhängig ist, z. B. eine Wiedergabeeinstellung aktiviert oder deaktiviert, stellen Sie verschiedene Symbole für die verschiedenen Status bereit, damit Nutzer eine Änderung sehen können, wenn sie die Aktion auswählen.

Alternative Symbolstile für deaktivierte Aktionen bereitstellen

Wenn eine benutzerdefinierte Aktion für den aktuellen Kontext nicht verfügbar ist, ersetzen Sie das Symbol für die benutzerdefinierte Aktion durch ein alternatives Symbol, das angibt, dass die Aktion deaktiviert ist.

Abbildung 6 Beispiele für benutzerdefinierte Aktionssymbole, die nicht dem Stil entsprechen.

Audioformat angeben

Wenn gerade wiedergegebene Medien ein spezielles Audioformat verwenden, können Sie Symbole angeben, die in Autos gerendert werden, die diese Funktion unterstützen. Sie können KEY_CONTENT_FORMAT_TINTABLE_LARGE_ICON_URI und KEY_CONTENT_FORMAT_TINTABLE_SMALL_ICON_URI im Extras-Bundle des aktuell wiedergegebenen Media-Elements festlegen, das an MediaSession.setMetadata() übergeben wird. Achten Sie darauf, beide Extras festzulegen, um verschiedenen Layouts gerecht zu werden.

Außerdem kannst du das KEY_IMMERSIVE_AUDIO-Extra festlegen, um Autohersteller darauf hinzuweisen, dass es sich um immersiven Audioinhalt handelt. Sie sollten daher sehr vorsichtig sein, wenn sie entscheiden, ob sie Audioeffekte anwenden, die die immersiven Inhalte beeinträchtigen könnten.

Sie können das aktuell wiedergegebene Media-Element so konfigurieren, dass der Untertitel, die Beschreibung oder beides Links zu anderen Media-Elementen sind. So kann der Nutzer schnell zu ähnlichen Inhalten springen, z. B. zu anderen Songs desselben Künstlers oder anderen Folgen dieses Podcasts. Wenn das Auto diese Funktion unterstützt, können Nutzer auf den Link tippen, um diese Inhalte aufzurufen.

Wenn Sie Links hinzufügen möchten, konfigurieren Sie die Metadaten KEY_SUBTITLE_LINK_MEDIA_ID (um über den Untertitel zu verlinken) oder KEY_DESCRIPTION_LINK_MEDIA_ID (um über die Beschreibung zu verlinken). Weitere Informationen finden Sie in der Referenzdokumentation zu diesen Metadatenfeldern.

Sprachbefehle unterstützen

Ihre Media-App muss Sprachaktionen unterstützen, damit Fahrer eine sichere und komfortable Nutzung ohne Ablenkung haben. Wenn Ihre App beispielsweise ein Media-Element wiedergibt, kann der Nutzer „Play [song title]“ sagen, um Ihre App anzuweisen, einen anderen Song abzuspielen, ohne auf das Display des Autos zu schauen oder es zu berühren. Nutzer können Anfragen starten, indem sie auf die entsprechenden Tasten auf dem Lenkrad klicken oder die Hotwords Ok Google sagen.

Wenn Android Auto oder Android Automotive OS eine Sprachaktion erkennt und interpretiert, wird diese Sprachaktion über onPlayFromSearch() an die App gesendet. Nach Erhalt dieses Rückrufs sucht die App nach Inhalten, die dem String query entsprechen, und startet die Wiedergabe.

Nutzer können in ihrer Anfrage verschiedene Kategorien von Begriffen angeben, z. B. Genre, Künstler, Album, Titelname, Radiosender oder Playlist. Berücksichtigen Sie beim Entwickeln der Unterstützung für die Suche alle Kategorien, die für Ihre App sinnvoll sind. Wenn Android Auto oder Android Automotive OS erkennt, dass eine bestimmte Anfrage in bestimmte Kategorien passt, werden Extras an den Parameter extras angehängt. Folgende Extras können gesendet werden:

Berücksichtigen Sie einen leeren query-String, der von Android Auto oder Android Automotive OS gesendet werden kann, wenn der Nutzer keine Suchbegriffe angibt. Wenn der Nutzer beispielsweise sagt: Spiel Musik ab. In diesem Fall wird in Ihrer App möglicherweise ein kürzlich wiedergegebener oder neu vorgeschlagener Titel gestartet.

Wenn eine Suche nicht schnell verarbeitet werden kann, blockieren Sie sie nicht in onPlayFromSearch(). Stelle stattdessen den Wiedergabestatus auf STATE_CONNECTING ein und führe die Suche in einem asynchronen Thread aus.

Sobald die Wiedergabe beginnt, kannst du die Warteschlange der Mediensitzung mit ähnlichen Inhalten füllen. Wenn der Nutzer beispielsweise die Wiedergabe eines Albums anfordert, kann deine App die Warteschlange mit der Titelliste des Albums füllen. Erwäge auch, durchsuchbare Suchergebnisse zu implementieren, damit Nutzer einen anderen Titel auswählen können, der ihrer Anfrage entspricht.

Neben „play“-Anfragen erkennen Android Auto und Android Automotive OS auch Sprachbefehle zur Steuerung der Wiedergabe wie „pause music“ (Musik pausieren) und „next song“ (nächster Song) und ordnen diese Befehle den entsprechenden MediaSession-Callbacks wie onPause() und onSkipToNext() zu.

Ein ausführliches Beispiel für die Implementierung von sprachgesteuerten Wiedergabeaktionen in Ihrer App finden Sie unter Google Assistant und Media-Apps.

Ablenkungsschutz implementieren

Da das Smartphone eines Nutzers bei der Verwendung von Android Auto mit den Lautsprechern des Autos verbunden ist, müssen Sie zusätzliche Vorsichtsmaßnahmen treffen, um Ablenkungen des Fahrers zu vermeiden.

Alarme im Auto unterdrücken

Android Auto-Media-Apps dürfen Audioinhalte nicht über die Autolautsprecher wiedergeben, es sei denn, der Nutzer startet die Wiedergabe, indem er beispielsweise eine Wiedergabetaste drückt. Auch ein vom Nutzer geplanter Wecker aus Ihrer Media-App darf nicht über die Autolautsprecher Musik abspielen.

Um diese Anforderung zu erfüllen, kann Ihre App CarConnection als Signal verwenden, bevor Audio wiedergegeben wird. Ihre App kann prüfen, ob das Smartphone auf ein Autodisplay projiziert wird. Dazu muss sie LiveData für den Verbindungstyp für das Auto beobachten und prüfen, ob er gleich CONNECTION_TYPE_PROJECTION ist.

Wenn das Smartphone des Nutzers projiziert wird, müssen Media-Apps, die Alarme unterstützen, eine der folgenden Aktionen ausführen:

  • Deaktivieren Sie den Alarm.
  • Spiele den Wecker über STREAM_ALARM ab und stelle auf dem Smartphone-Display eine Benutzeroberfläche zum Deaktivieren des Weckers bereit.

Umgang mit Media-Werbung

Standardmäßig zeigt Android Auto eine Benachrichtigung an, wenn sich die Metadaten von Medien während der Audiowiedergabe ändern. Wenn eine Media-App von der Musikwiedergabe zur Ausführung einer Anzeige wechselt, ist es störend, dem Nutzer eine Benachrichtigung zu präsentieren. Damit Android Auto in diesem Fall keine Benachrichtigung anzeigt, müssen Sie den Media-Metadatenschlüssel METADATA_KEY_IS_ADVERTISEMENT auf METADATA_VALUE_ATTRIBUTE_PRESENT setzen, wie im folgenden Code-Snippet gezeigt:

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

Allgemeine Fehler beheben

Wenn in der App ein Fehler auftritt, legen Sie den Wiedergabestatus auf STATE_ERROR fest und geben Sie mit der Methode setErrorMessage() eine Fehlermeldung an. Eine Liste der Fehlercodes, die Sie beim Festlegen der Fehlermeldung verwenden können, finden Sie unter PlaybackStateCompat. Fehlermeldungen müssen für den Nutzer sichtbar und in der aktuellen Sprache des Nutzers lokalisiert sein. Android Auto und Android Automotive OS können dem Nutzer dann die Fehlermeldung anzeigen.

Wenn Inhalte beispielsweise in der aktuellen Region des Nutzers nicht verfügbar sind, können Sie den Fehlercode ERROR_CODE_NOT_AVAILABLE_IN_REGION verwenden, wenn Sie die Fehlermeldung festlegen.

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

Weitere Informationen zu Fehlerstatus finden Sie unter Mediensitzung verwenden: Status und Fehler.

Wenn ein Android Auto-Nutzer Ihre Smartphone-App öffnen muss, um einen Fehler zu beheben, geben Sie diese Information in Ihrer Nachricht an. Ihre Fehlermeldung könnte beispielsweise „Melden Sie sich in [Name Ihrer App] an“ anstelle von „Bitte melden Sie sich an“ lauten.

Weitere Informationen