Android Auto i Android Automotive OS pomagają udostępniać treści aplikacji multimedialnych użytkownikom w samochodzie.
Aplikacje multimedialne na samochody można tworzyć na 2 sposoby:
Z tego przewodnika dowiesz się, jak za pomocą
MediaBrowserService
iMediaSession
utworzyć aplikację, z którą mogą się łączyć Android Auto i Android Automotive OS, aby renderować widoki przeglądania i odtwarzania multimediów zoptymalizowane pod kątem użytkowania w samochodzie.Aplikacje multimedialne można też tworzyć za pomocą szablonów biblioteki aplikacji samochodowych, które umożliwiają dostosowywanie formatowania, przeglądanie i rozszerzanie działań niestandardowych. Szczegółowe informacje o wdrażaniu znajdziesz w artykule Tworzenie aplikacji multimedialnej na podstawie szablonu. Aplikacje multimedialne oparte na szablonach są obecnie obsługiwane tylko w Androidzie Auto.
W tym przewodniku opisujemy wymagane komponenty MediaBrowserService
i MediaSession
, których aplikacja potrzebuje, aby działać w Androidzie Auto lub Androidzie Automotive OS. Po ukończeniu podstawowej infrastruktury multimediów możesz dodać obsługę Androida Auto i dodać obsługę systemu operacyjnego Android Automotive do aplikacji multimedialnej.
W tym przewodniku zakładamy, że masz już aplikację do odtwarzania multimediów, która odtwarza dźwięk na telefonie, i że jest ona zgodna z architekturą aplikacji multimedialnych na Androida.
Zanim zaczniesz
- Zapoznaj się z dokumentacją interfejsu Android Media API.
- Wskazówki dotyczące projektowania znajdziesz w artykule Tworzenie aplikacji multimedialnych.
- Zapoznaj się z kluczowymi terminami i pojęciami wymienionymi w tej sekcji.
Kluczowe terminy i pojęcia
- Usługa przeglądarki multimediów
- Usługa Androida zaimplementowana przez aplikację do obsługi multimediów, która jest zgodna z interfejsem API
MediaBrowserServiceCompat
. Twoja aplikacja korzysta z tej usługi, aby udostępniać swoje treści. - Przeglądarka multimediów
- Interfejs API używany przez aplikacje multimedialne do wykrywania usług przeglądarki multimediów i wyświetlania ich treści. Android Auto i Android Automotive OS używają przeglądarki multimediów, aby znaleźć usługę przeglądarki multimediów Twojej aplikacji.
- Element multimedialny
Przeglądarka multimediów porządkuje treści w drzewie obiektów
MediaItem
. Element multimedialny może mieć jedną z tych flag lub obie:FLAG_PLAYABLE
: oznacza, że element jest węzłem końcowym w drzewie treści. Element reprezentuje pojedynczy strumień dźwięku, np. utwór na albumie, rozdział audiobooka lub odcinek podcastu.FLAG_BROWSABLE
: oznacza, że element jest węzłem w drzewie treści i ma elementy podrzędne. Na przykład element reprezentuje album, a jego elementy podrzędne to utwory z tego albumu.
Element multimedialny, który można przeglądać i odtwarzać, działa jak playlista. Możesz wybrać sam element, aby odtworzyć wszystkie jego elementy podrzędne, lub przeglądać jego elementy podrzędne.
- Zoptymalizowane pod kątem pojazdów
Aktywność aplikacji na system operacyjny Android Automotive, która jest zgodna z wytycznymi dotyczącymi projektowania aplikacji na ten system. Interfejs tych działań nie jest rysowany przez system operacyjny Android Automotive, więc musisz zadbać o to, aby Twoja aplikacja była zgodna z wytycznymi dotyczącymi projektowania. Zazwyczaj obejmuje to większe docelowe elementy dotykowe i rozmiary czcionek, obsługę trybu dziennego i nocnego oraz wyższe współczynniki kontrastu.
Zoptymalizowane pod kątem pojazdów interfejsy użytkownika mogą być wyświetlane tylko wtedy, gdy nie obowiązują ograniczenia dotyczące interfejsu użytkownika w samochodzie (CUXR), ponieważ mogą wymagać od użytkownika większej uwagi lub interakcji. CUXR nie działają, gdy samochód jest zatrzymany lub zaparkowany, ale są zawsze aktywne, gdy samochód jest w ruchu.
Nie musisz projektować aktywności dla Androida Auto, ponieważ Android Auto rysuje własny interfejs zoptymalizowany pod kątem pojazdów, korzystając z informacji z usługi przeglądarki multimediów.
Konfigurowanie plików manifestu aplikacji
Zanim utworzysz usługę przeglądarki multimediów, musisz skonfigurować pliki manifestu aplikacji.
Zadeklaruj usługę przeglądarki multimediów
Zarówno Android Auto, jak i Android Automotive OS łączą się z Twoją aplikacją za pomocą usługi przeglądarki multimediów, aby przeglądać elementy multimedialne. Zadeklaruj usługę przeglądarki multimediów w pliku manifestu, aby Android Auto i Android Automotive OS mogły wykryć usługę i połączyć się z Twoją aplikacją.
Poniższy fragment kodu pokazuje, jak zadeklarować usługę przeglądarki multimediów w pliku manifestu. Dodaj ten kod do pliku manifestu modułu systemu operacyjnego Android Automotive i do pliku manifestu aplikacji na telefon.
<application>
...
<service android:name=".MyMediaBrowserService"
android:exported="true">
<intent-filter>
<action android:name="android.media.browse.MediaBrowserService"/>
</intent-filter>
</service>
...
</application>
Określanie ikon aplikacji
Musisz określić ikony aplikacji, których Android Auto i Android Automotive OS mogą używać do reprezentowania aplikacji w interfejsie systemu. Wymagane są 2 typy ikon:
- Ikona programu uruchamiającego
- Ikona atrybucji
Ikona programu uruchamiającego
Ikona programu uruchamiającego reprezentuje aplikację w interfejsie systemu, np. w programie uruchamiającym i na pasku ikon. Możesz określić, że chcesz używać ikony z aplikacji mobilnej do reprezentowania aplikacji multimedialnej w samochodzie, używając tej deklaracji w pliku manifestu:
<application
...
android:icon="@mipmap/ic_launcher"
...
/>
Aby użyć innej ikony niż w aplikacji mobilnej, ustaw właściwość android:icon
w elemencie <service>
usługi przeglądarki multimediów w pliku manifestu:
<application>
...
<service
...
android:icon="@mipmap/auto_launcher"
...
/>
</application>
Ikona atrybucji

Rysunek 1. Ikona atrybucji na karcie multimediów.
Ikona atrybucji jest używana w miejscach, w których treść multimedialna ma większe znaczenie, np. na kartach multimediów. Rozważ ponowne użycie małej ikony używanej w powiadomieniach. Ikona musi być monochromatyczna. Możesz określić ikonę, która będzie reprezentować Twoją aplikację, używając tej deklaracji w pliku manifestu:
<application>
...
<meta-data
android:name="androidx.car.app.TintableAttributionIcon"
android:resource="@drawable/ic_status_icon" />
...
</application>
Tworzenie usługi przeglądarki multimediów
Usługę przeglądarki multimediów tworzy się przez rozszerzenie klasy MediaBrowserServiceCompat
. Zarówno Android Auto, jak i Android Automotive OS mogą korzystać z Twojej usługi, aby:
- Przeglądaj hierarchię treści aplikacji, aby wyświetlić użytkownikowi menu.
- Pobierz token dla obiektu
MediaSessionCompat
w aplikacji, aby sterować odtwarzaniem dźwięku.
Możesz też użyć usługi przeglądarki multimediów, aby umożliwić innym klientom dostęp do treści multimedialnych z Twojej aplikacji. Tymi klientami multimedialnymi mogą być inne aplikacje na telefonie użytkownika lub inne zdalne klienty.
Przepływ pracy usługi przeglądarki multimediów
W tej sekcji opisano, jak system operacyjny Android Automotive i Android Auto współdziałają z usługą przeglądarki multimediów podczas typowego procesu pracy użytkownika.
- Użytkownik uruchamia Twoją aplikację w systemie operacyjnym Android Automotive lub Androidzie Auto.
- System operacyjny Android Automotive lub Android Auto kontaktuje się z usługą przeglądarki multimediów aplikacji za pomocą metody
onCreate()
. W implementacji metodyonCreate()
musisz utworzyć i zarejestrować obiektMediaSessionCompat
oraz jego obiekt wywołania zwrotnego. - Android Automotive OS lub Android Auto wywołuje metodę
onGetRoot()
usługi, aby uzyskać główny element multimedialny w hierarchii treści. Główny element multimedialny nie jest wyświetlany. Służy on do pobierania większej ilości treści z aplikacji. - Android Automotive OS lub Android Auto wywołuje metodę
onLoadChildren()
usługi, aby pobrać elementy podrzędne głównego elementu multimedialnego. Android Automotive OS i Android Auto wyświetlają te elementy multimedialne jako najwyższy poziom elementów treści. Więcej informacji o tym, czego system oczekuje na tym poziomie, znajdziesz w sekcji Struktura menu głównego na tej stronie. - Jeśli użytkownik wybierze element multimedialny, który można przeglądać, metoda
onLoadChildren()
usługi zostanie ponownie wywołana, aby pobrać elementy podrzędne wybranego elementu menu. - Jeśli użytkownik wybierze element multimedialny, który można odtworzyć, system operacyjny Android Automotive lub Android Auto wywoła odpowiednią metodę wywołania zwrotnego sesji multimedialnej, aby wykonać to działanie.
- Jeśli Twoja aplikacja obsługuje taką funkcję, użytkownik może też wyszukiwać Twoje treści. W tym przypadku system operacyjny Android Automotive lub Android Auto wywołuje metodę
onSearch()
usługi.
Tworzenie hierarchii treści
Android Auto i Android Automotive OS wywołują usługę przeglądarki multimediów aplikacji, aby sprawdzić, jakie treści są dostępne. Aby to zrobić, musisz wdrożyć w usłudze przeglądarki multimediów 2 metody: onGetRoot()
i onLoadChildren()
.
Implementacja metody onGetRoot
Metoda onGetRoot()
usługi zwraca informacje o węźle głównym hierarchii treści.
Android Auto i Android Automotive OS używają tego węzła głównego, aby za pomocą metody onLoadChildren()
zażądać pozostałych treści.
Poniższy fragment kodu przedstawia prostą implementację metody 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); }
Bardziej szczegółowy przykład tej metody znajdziesz w onGetRoot()
w przykładowej aplikacji Universal Android Music Player na GitHubie.
Dodawanie weryfikacji pakietu w przypadku wywołania funkcji onGetRoot()
Gdy połączenie jest nawiązywane z metodą onGetRoot()
usługi, pakiet wywołujący przekazuje do usługi informacje identyfikacyjne. Twoja usługa może wykorzystać te informacje, aby określić, czy dany pakiet może uzyskać dostęp do Twoich treści. Możesz na przykład ograniczyć dostęp do treści aplikacji do listy zatwierdzonych pakietów, porównując clientPackageName
z listą dozwolonych i weryfikując certyfikat użyty do podpisania pliku APK pakietu. Jeśli nie można zweryfikować pakietu, kliknij null
, aby odmówić dostępu do treści.
Aby zapewnić aplikacjom systemowym, takim jak Android Auto i Android Automotive OS, dostęp do Twoich treści, usługa musi zawsze zwracać wartość inną niż null BrowserRoot
, gdy te aplikacje systemowe wywołują metodę onGetRoot()
. Podpis aplikacji systemowej Android Automotive OS może się różnić w zależności od marki i modelu samochodu, dlatego aby zapewnić solidną obsługę tego systemu, musisz zezwolić na połączenia ze wszystkich aplikacji systemowych.
Ten fragment kodu pokazuje, jak usługa może sprawdzić, czy pakiet wywołujący jest aplikacją systemową:
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
}
Ten fragment kodu pochodzi z PackageValidator
klasy w przykładowej aplikacji Universal Android Music Player na GitHubie. Więcej szczegółów o wdrażaniu weryfikacji pakietu w przypadku onGetRoot()
metody usługi znajdziesz w tej klasie.
Oprócz zezwolenia na korzystanie z aplikacji systemowych musisz zezwolić Asystentowi Google na łączenie się z MediaBrowserService
. Pamiętaj, że Asystent Google ma osobne nazwy pakietów na telefon, w tym Androida Auto, i na system operacyjny Android Automotive.
Implementowanie metody onLoadChildren()
Po otrzymaniu obiektu węzła głównego Android Auto i Android Automotive OS tworzą menu najwyższego poziomu, wywołując onLoadChildren()
w obiekcie węzła głównego, aby uzyskać jego elementy podrzędne. Aplikacje klienckie tworzą podmenu, wywołując tę samą metodę za pomocą obiektów węzłów podrzędnych.
Każdy węzeł w hierarchii treści jest reprezentowany przez obiekt MediaBrowserCompat.MediaItem
. Każdy z tych elementów multimedialnych jest identyfikowany za pomocą unikalnego ciągu znaków. Aplikacje klienckie traktują te ciągi znaków identyfikatora jako nieprzejrzyste tokeny. Gdy aplikacja kliencka chce przejść do podmenu lub odtworzyć element multimedialny, przekazuje token. Aplikacja jest odpowiedzialna za powiązanie tokena z odpowiednim elementem multimedialnym.
Poniższy fragment kodu przedstawia prostą implementację metody 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); }
Pełny przykład tej metody znajdziesz w onLoadChildren()
w przykładowej aplikacji Universal Android Music Player na GitHubie.
Struktura menu głównego

Rysunek 2. Treści główne wyświetlane jako karty nawigacyjne.
Android Auto i Android Automotive OS mają określone ograniczenia dotyczące struktury menu głównego. Są one przekazywane do MediaBrowserService
za pomocą wskazówek dotyczących katalogu głównego, które można odczytać za pomocą argumentu Bundle
przekazanego do onGetRoot()
.
Dzięki tym wskazówkom system może optymalnie wyświetlać treści główne jako karty nawigacyjne. Jeśli nie będziesz przestrzegać tych wskazówek, system może pominąć niektóre treści główne lub sprawić, że będą one trudniej dostępne. Wysyłane są 2 wskazówki:
- Limit liczby podrzędnych elementów głównych: w większości przypadków ta liczba wynosi 4. Oznacza to, że nie można wyświetlić więcej niż 4 kart.
- Obsługiwane flagi w przypadku elementów podrzędnych w katalogu głównym: możesz oczekiwać, że ta wartość będzie wynosić
MediaItem#FLAG_BROWSABLE
. Oznacza to, że jako karty mogą być wyświetlane tylko elementy, które można przeglądać, a nie te, które można odtwarzać.
Aby odczytać odpowiednie wskazówki dotyczące serwerów głównych, użyj tego kodu:
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... }
Możesz rozgałęzić logikę struktury hierarchii treści na podstawie wartości tych wskazówek, zwłaszcza jeśli hierarchia różni się w przypadku MediaBrowser
integracji poza Androidem Auto i Androidem Automotive OS.
Jeśli na przykład zwykle wyświetlasz element gry głównej, możesz zagnieździć go pod elementem głównym, który można przeglądać, ze względu na wartość wskazówki dotyczącej obsługiwanych flag.
Oprócz wskazówek dotyczących serwerów głównych należy przestrzegać kilku dodatkowych wytycznych, aby karty renderowały się optymalnie:
- Dla każdego elementu karty podaj monochromatyczne ikony, najlepiej białe.
- Podaj krótkie, ale znaczące etykiety dla każdego elementu karty. Krótkie etykiety zmniejszają ryzyko obcięcia ciągów tekstowych.
Wyświetlanie grafiki multimedialnej
Grafika elementów multimedialnych musi być przekazywana jako lokalny identyfikator URI za pomocą elementu ContentResolver.SCHEME_CONTENT
lub ContentResolver.SCHEME_ANDROID_RESOURCE
.
Ten lokalny identyfikator URI musi prowadzić do mapy bitowej lub rysunku wektorowego w zasobach aplikacji. W przypadku obiektów MediaDescriptionCompat
reprezentujących elementy w hierarchii treści przekaż identyfikator URI za pomocą parametru setIconUri()
.
W przypadku obiektów MediaMetadataCompat
reprezentujących aktualnie odtwarzany element przekaż URI za pomocą putString()
, używając dowolnego z tych kluczy:
MediaMetadataCompat.METADATA_KEY_DISPLAY_ICON_URI
MediaMetadataCompat.METADATA_KEY_ART_URI
MediaMetadataCompat.METADATA_KEY_ALBUM_ART_URI
Poniższe kroki opisują, jak pobrać grafikę z identyfikatora URI w internecie i udostępnić ją za pomocą lokalnego identyfikatora URI. Bardziej kompletny przykład znajdziesz w implementacji openFile()
i powiązanych metod w przykładowej aplikacji Universal Android Music Player.
Utwórz
content://
identyfikator URI odpowiadający identyfikatorowi URI w internecie. Usługa przeglądarki multimediów i sesja multimedialna przekazują ten identyfikator URI treści do Androida Auto i Androida 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(); }
W implementacji funkcji
ContentProvider.openFile()
sprawdź, czy istnieje plik dla odpowiedniego identyfikatora URI. Jeśli nie, pobierz plik obrazu i zapisz go w pamięci podręcznej. Poniższy fragment kodu korzysta z biblioteki Glide.Kotlin
override fun openFile(uri: Uri, mode: String): ParcelFileDescriptor? { val context = this.context ?: return null val file = File(context.cacheDir, uri.path) if (!file.exists()) { val remoteUri = Uri.Builder() .scheme("https") .authority("my-image-site") .appendPath(uri.path) .build() val cacheFile = Glide.with(context) .asFile() .load(remoteUri) .submit() .get(DOWNLOAD_TIMEOUT_SECONDS, TimeUnit.SECONDS) cacheFile.renameTo(file) file = cacheFile } return ParcelFileDescriptor.open(file, ParcelFileDescriptor.MODE_READ_ONLY) }
Java
@Nullable @Override public ParcelFileDescriptor openFile(@NonNull Uri uri, @NonNull String mode) throws FileNotFoundException { Context context = this.getContext(); File file = new File(context.getCacheDir(), uri.getPath()); if (!file.exists()) { Uri remoteUri = new Uri.Builder() .scheme("https") .authority("my-image-site") .appendPath(uri.getPath()) .build(); File cacheFile = Glide.with(context) .asFile() .load(remoteUri) .submit() .get(DOWNLOAD_TIMEOUT_SECONDS, TimeUnit.SECONDS); cacheFile.renameTo(file); file = cacheFile; } return ParcelFileDescriptor.open(file, ParcelFileDescriptor.MODE_READ_ONLY); }
Więcej informacji o dostawcach treści znajdziesz w artykule Tworzenie dostawcy treści.
rozważ wstępne ładowanie i buforowanie obrazów.Stosowanie stylów treści
Po utworzeniu hierarchii treści za pomocą elementów, które można przeglądać lub odtwarzać, możesz zastosować style treści określające sposób wyświetlania tych elementów w samochodzie.
Możesz używać tych stylów treści:
- Elementy listy
-
W tym stylu treści priorytetem są tytuły i metadane, a nie obrazy.
- Elementy siatki
-
Ten styl treści stawia obrazy przed tytułami i metadanymi.
Ustawianie domyślnych stylów treści
Możesz ustawić globalne wartości domyślne sposobu wyświetlania elementów multimedialnych, uwzględniając określone stałe w BrowserRoot
pakiecie dodatków metody onGetRoot()
usługi. Android Auto i Android Automotive OS odczytują ten pakiet i szukają tych stałych, aby określić odpowiedni styl.
Jako klucze w pakiecie można używać tych dodatków:
DESCRIPTION_EXTRAS_KEY_CONTENT_STYLE_BROWSABLE
: wskazuje wskazówkę dotyczącą prezentacji wszystkich elementów, które można przeglądać w drzewie przeglądania.DESCRIPTION_EXTRAS_KEY_CONTENT_STYLE_PLAYABLE
: wskazuje wskazówkę dotyczącą prezentacji wszystkich elementów, które można odtworzyć w drzewie przeglądania.
Klucze mogą być powiązane z tymi stałymi wartościami całkowitymi, aby wpływać na prezentację tych elementów:
DESCRIPTION_EXTRAS_VALUE_CONTENT_STYLE_LIST_ITEM
: odpowiadające im produkty są wyświetlane jako elementy listy.DESCRIPTION_EXTRAS_VALUE_CONTENT_STYLE_GRID_ITEM
: odpowiadające im produkty są wyświetlane jako elementy siatki.DESCRIPTION_EXTRAS_VALUE_CONTENT_STYLE_CATEGORY_LIST_ITEM
: odpowiednie elementy są prezentowane jako elementy listy „kategoria”. Są one takie same jak zwykłe elementy listy, z tym że wokół ikon elementów są stosowane marginesy, ponieważ ikony lepiej wyglądają, gdy są małe. Ikony muszą być obiektami rysowalnymi wektorowo z możliwością zabarwienia. Ta wskazówka powinna być podawana tylko w przypadku elementów, które można przeglądać.DESCRIPTION_EXTRAS_VALUE_CONTENT_STYLE_CATEGORY_GRID_ITEM
: odpowiadające im produkty są prezentowane jako elementy siatki „kategoria”. Są one takie same jak zwykłe elementy siatki, z tym że wokół ikon elementów są stosowane marginesy, ponieważ ikony lepiej wyglądają, gdy są małe. Ikony muszą być obiektami rysowalnymi wektorowo z możliwością zabarwienia. Ta wskazówka powinna być podawana tylko w przypadku elementów, które można przeglądać.
Ten fragment kodu pokazuje, jak ustawić domyślny styl treści dla elementów, które można przeglądać, na siatki, a dla elementów, które można odtwarzać, na listy:
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); }
Ustawianie stylów treści dla poszczególnych elementów
Interfejs Content Style API umożliwia zastąpienie domyślnego stylu treści w przypadku elementów podrzędnych dowolnego elementu multimedialnego, który można przeglądać, a także w przypadku samego elementu multimedialnego.
Aby zastąpić domyślne ustawienie elementu multimedialnego z możliwością przeglądania w przypadku elementów podrzędnych, utwórz pakiet dodatków w MediaDescription
elementu multimedialnego i dodaj te same wskazówki, o których wspomnieliśmy wcześniej. DESCRIPTION_EXTRAS_KEY_CONTENT_STYLE_PLAYABLE
ma zastosowanie do elementów podrzędnych, które można odtworzyć, a DESCRIPTION_EXTRAS_KEY_CONTENT_STYLE_BROWSABLE
do elementów podrzędnych, które można przeglądać.
Aby zastąpić wartość domyślną dla konkretnego elementu multimedialnego samego w sobie, a nie jego elementów podrzędnych, utwórz pakiet dodatków w MediaDescription
elementu multimedialnego i dodaj wskazówkę z kluczem DESCRIPTION_EXTRAS_KEY_CONTENT_STYLE_SINGLE_ITEM
.
Aby określić sposób prezentacji produktu, użyj tych samych wartości, które zostały opisane wcześniej.
Poniższy fragment kodu pokazuje, jak utworzyć element MediaItem
, który zastępuje domyślny styl treści zarówno dla siebie, jak i dla elementów podrzędnych. Jest on stylizowany jako element listy kategorii, jego elementy podrzędne, które można przeglądać, jako elementy listy, a elementy podrzędne, które można odtwarzać, jako elementy siatki:
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); }
Grupowanie elementów za pomocą wskazówek dotyczących tytułu
Aby pogrupować powiązane elementy multimedialne, użyj wskazówki dotyczącej poszczególnych elementów. Każdy element multimedialny w grupie musi zadeklarować pakiet dodatków w parametrze MediaDescription
, który zawiera mapowanie z kluczem DESCRIPTION_EXTRAS_KEY_CONTENT_STYLE_GROUP_TITLE
i identyczną wartością ciągu znaków. Zlokalizuj ten ciąg znaków, który jest używany jako tytuł grupy.
Ten fragment kodu pokazuje, jak utworzyć MediaItem
z nagłówkiem podgrupy "Songs"
:
Kotlin
import androidx.media.utils.MediaConstants private fun createMediaItem( mediaId: String, folderName: String, iconUri: Uri ): MediaBrowser.MediaItem { val mediaDescriptionBuilder = MediaDescription.Builder() mediaDescriptionBuilder.setMediaId(mediaId) mediaDescriptionBuilder.setTitle(folderName) mediaDescriptionBuilder.setIconUri(iconUri) val extras = Bundle() extras.putString( MediaConstants.DESCRIPTION_EXTRAS_KEY_CONTENT_STYLE_GROUP_TITLE, "Songs") mediaDescriptionBuilder.setExtras(extras) return MediaBrowser.MediaItem( mediaDescriptionBuilder.build(), /* playable or browsable flag*/) }
Java
import androidx.media.utils.MediaConstants; private MediaBrowser.MediaItem createMediaItem(String mediaId, String folderName, Uri iconUri) { MediaDescription.Builder mediaDescriptionBuilder = new MediaDescription.Builder(); mediaDescriptionBuilder.setMediaId(mediaId); mediaDescriptionBuilder.setTitle(folderName); mediaDescriptionBuilder.setIconUri(iconUri); Bundle extras = new Bundle(); extras.putString( MediaConstants.DESCRIPTION_EXTRAS_KEY_CONTENT_STYLE_GROUP_TITLE, "Songs"); mediaDescriptionBuilder.setExtras(extras); return new MediaBrowser.MediaItem( mediaDescriptionBuilder.build(), /* playable or browsable flag*/); }
Aplikacja musi przekazywać wszystkie elementy multimedialne, które chcesz zgrupować, jako ciągły blok. Załóżmy na przykład, że chcesz wyświetlić 2 grupy elementów multimedialnych: „Utwory” i „Albumy” w tej kolejności, a aplikacja przekazuje 5 elementów multimedialnych w tej kolejności:
- Element multimedialny A z
extras.putString(MediaConstants.DESCRIPTION_EXTRAS_KEY_CONTENT_STYLE_GROUP_TITLE, "Songs")
- Element multimedialny B z
extras.putString(MediaConstants.DESCRIPTION_EXTRAS_KEY_CONTENT_STYLE_GROUP_TITLE, "Albums")
- Element multimedialny C z
extras.putString(MediaConstants.DESCRIPTION_EXTRAS_KEY_CONTENT_STYLE_GROUP_TITLE, "Songs")
- Element multimedialny D z
extras.putString(MediaConstants.DESCRIPTION_EXTRAS_KEY_CONTENT_STYLE_GROUP_TITLE, "Songs")
- Element multimedialny E z
extras.putString(MediaConstants.DESCRIPTION_EXTRAS_KEY_CONTENT_STYLE_GROUP_TITLE, "Albums")
Ponieważ elementy multimedialne w grupach „Utwory” i „Albumy” nie są przechowywane w sąsiadujących blokach, Android Auto i Android Automotive OS interpretują to jako 4 grupy:
- Grupa 1 o nazwie „Utwory” zawierająca element multimedialny A
- Grupa 2 o nazwie „Albumy” zawierająca element multimedialny B
- Grupa 3 o nazwie „Utwory” zawierająca elementy multimedialne C i D
- Grupa 4 o nazwie „Albumy” zawierająca element multimedialny E
Aby wyświetlić te elementy w 2 grupach, aplikacja musi przekazywać elementy multimedialne w tej kolejności:
- Element multimedialny A z
extras.putString(MediaConstants.DESCRIPTION_EXTRAS_KEY_CONTENT_STYLE_GROUP_TITLE, "Songs")
- Element multimedialny C z
extras.putString(MediaConstants.DESCRIPTION_EXTRAS_KEY_CONTENT_STYLE_GROUP_TITLE, "Songs")
- Element multimedialny D z
extras.putString(MediaConstants.DESCRIPTION_EXTRAS_KEY_CONTENT_STYLE_GROUP_TITLE, "Songs")
- Element multimedialny B z
extras.putString(MediaConstants.DESCRIPTION_EXTRAS_KEY_CONTENT_STYLE_GROUP_TITLE, "Albums")
- Element multimedialny E z
extras.putString(MediaConstants.DESCRIPTION_EXTRAS_KEY_CONTENT_STYLE_GROUP_TITLE, "Albums")
Wyświetlanie dodatkowych wskaźników metadanych
Możesz dołączyć dodatkowe wskaźniki metadanych, aby zapewnić szybki dostęp do informacji o treści w drzewie przeglądarki multimediów i podczas odtwarzania. W drzewie przeglądania Android Auto i Android Automotive OS odczytują dodatkowe informacje powiązane z elementem i wyszukują określone stałe, aby określić, które wskaźniki mają być wyświetlane. Podczas odtwarzania multimediów Android Auto i Android Automotive OS odczytują metadane sesji multimedialnej i wyszukują określone stałe, aby określić wskaźniki do wyświetlenia.

Rysunek 3. Widok odtwarzania z metadanymi identyfikującymi utwór i wykonawcę oraz ikoną wskazującą treści dla dorosłych.

Rysunek 4. Widok przeglądania z kropką przy nieodtwarzanych treściach na pierwszym elemencie i paskiem postępu przy częściowo odtwarzanych treściach na drugim elemencie.
W obu typach rozszerzeń – MediaItem
rozszerzeniach z opisem i MediaMetadata
rozszerzeniach – można używać tych stałych:
EXTRA_DOWNLOAD_STATUS
: wskazuje stan pobierania elementu. Użyj tej stałej jako klucza. Możliwe wartości to te długie stałe:STATUS_DOWNLOADED
: produkt został w pełni pobrany.STATUS_DOWNLOADING
: produkt jest pobierany.STATUS_NOT_DOWNLOADED
: produkt nie jest pobrany.
METADATA_KEY_IS_EXPLICIT
: wskazuje, czy produkt zawiera treści dla pełnoletnich. Aby wskazać, że produkt jest przeznaczony dla dorosłych, użyj tej stałej jako klucza i długiegoMETADATA_VALUE_ATTRIBUTE_PRESENT
jako wartości.
W rozszerzeniach z tekstem reklamy MediaItem
można używać tylko tych stałych:
DESCRIPTION_EXTRAS_KEY_COMPLETION_STATUS
: oznacza stan ukończenia treści długich, takich jak odcinki podcastów lub audiobooki. Użyj tej stałej jako klucza. Możliwe wartości to te stałe całkowite:DESCRIPTION_EXTRAS_VALUE_COMPLETION_STATUS_NOT_PLAYED
: produkt nie został w ogóle odtworzony.DESCRIPTION_EXTRAS_VALUE_COMPLETION_STATUS_PARTIALLY_PLAYED
: element został częściowo odtworzony, a bieżąca pozycja znajduje się gdzieś pośrodku.DESCRIPTION_EXTRAS_VALUE_COMPLETION_STATUS_FULLY_PLAYED
: produkt został ukończony.
DESCRIPTION_EXTRAS_KEY_COMPLETION_PERCENTAGE
: wskazuje stopień ukończenia długich treści jako liczbę zmiennoprzecinkową z zakresu od 0,0 do 1,0 włącznie. Ten dodatkowy element zawiera więcej informacji oPARTIALLY_PLAYING
stanie, dzięki czemu Android Auto lub system operacyjny Android Automotive może wyświetlać bardziej przydatny wskaźnik postępu, np. pasek postępu. Jeśli używasz tego dodatku, zapoznaj się z sekcją aktualizowania paska postępu w widoku przeglądania podczas odtwarzania treści w tym przewodniku, aby dowiedzieć się, jak aktualizować ten wskaźnik po jego pierwszym wyświetleniu.
Aby wyświetlać wskaźniki, które pojawiają się podczas przeglądania drzewa multimediów, utwórz pakiet dodatków zawierający co najmniej jedną z tych stałych i przekaż go do metody MediaDescription.Builder.setExtras()
.
Poniższy fragment kodu pokazuje, jak wyświetlić wskaźniki dla elementu multimedialnego z treściami dla dorosłych, który został odtworzony w 70%:
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 */);
Aby wyświetlać wskaźniki dla odtwarzanego obecnie elementu multimedialnego, możesz zadeklarować wartości Long
dla METADATA_KEY_IS_EXPLICIT
lub EXTRA_DOWNLOAD_STATUS
w MediaMetadataCompat
mediaSession
. W widoku odtwarzania nie można wyświetlać wskaźników DESCRIPTION_EXTRAS_KEY_COMPLETION_STATUS
ani DESCRIPTION_EXTRAS_KEY_COMPLETION_PERCENTAGE
.
Fragment kodu poniżej pokazuje, jak wskazać, że bieżąca piosenka w widoku odtwarzania zawiera wulgaryzmy i została pobrana:
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());
Aktualizowanie paska postępu w widoku przeglądania podczas odtwarzania treści
Jak wspomnieliśmy wcześniej, możesz użyć dodatku DESCRIPTION_EXTRAS_KEY_COMPLETION_PERCENTAGE
, aby wyświetlić pasek postępu dla częściowo odtworzonych treści w widoku przeglądania. Jeśli jednak użytkownik będzie nadal odtwarzać częściowo odtworzone treści w Androidzie Auto lub Androidzie Automotive OS, wskaźnik ten z czasem stanie się niedokładny.
Aby pasek postępu w Androidzie Auto i Androidzie Automotive OS był aktualny, możesz podać dodatkowe informacje w elementach MediaMetadataCompat
i PlaybackStateCompat
, aby połączyć bieżące treści z elementami multimedialnymi w widoku przeglądania. Aby element multimedialny miał automatycznie aktualizowany pasek postępu, musi spełniać te wymagania:
- Podczas tworzenia
MediaItem
musi wysłaćDESCRIPTION_EXTRAS_KEY_COMPLETION_PERCENTAGE
w dodatkach z wartością z zakresu od 0, 0 do 1, 0 (włącznie). - Element
MediaMetadataCompat
musi wysyłaćMETADATA_KEY_MEDIA_ID
z wartością ciągu równą identyfikatorowi multimediów przekazywanemu do elementuMediaItem
. PlaybackStateCompat
musi zawierać dodatkowy element z kluczemPLAYBACK_STATE_EXTRAS_KEY_MEDIA_ID
odpowiadającym wartości ciągu tekstowego równej identyfikatorowi multimediów przekazanemu doMediaItem
.
Ten fragment kodu pokazuje, jak wskazać, że aktualnie odtwarzany element jest połączony z elementem w widoku przeglądania:
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());
Wyświetlanie wyników wyszukiwania, które można przeglądać

Rysunek 5. Widok odtwarzania z opcją „Wyniki wyszukiwania” umożliwiającą wyświetlanie multimediów związanych z wyszukiwaniem głosowym użytkownika.
Aplikacja może wyświetlać użytkownikom kontekstowe wyniki wyszukiwania, gdy wpisują oni wyszukiwane hasło. Android Auto i Android Automotive OS wyświetlają te wyniki za pomocą interfejsów zapytań lub funkcji, które opierają się na zapytaniach wpisanych wcześniej w sesji. Więcej informacji znajdziesz w sekcji Obsługa komend głosowych w tym przewodniku.
Aby wyświetlać wyniki wyszukiwania, które można przeglądać, umieść stały klucz
BROWSER_SERVICE_EXTRAS_KEY_SEARCH_SUPPORTED
w pakiecie dodatków metody onGetRoot()
usługi, mapując go na wartość logiczną true
.
Poniższy fragment kodu pokazuje, jak włączyć obsługę w onGetRoot()
:
Kotlin
import androidx.media.utils.MediaConstants @Nullable fun onGetRoot( @NonNull clientPackageName: String, clientUid: Int, @Nullable rootHints: Bundle ): BrowserRoot { val extras = Bundle() extras.putBoolean( MediaConstants.BROWSER_SERVICE_EXTRAS_KEY_SEARCH_SUPPORTED, true) return BrowserRoot(ROOT_ID, extras) }
Java
import androidx.media.utils.MediaConstants; @Nullable @Override public BrowserRoot onGetRoot( @NonNull String clientPackageName, int clientUid, @Nullable Bundle rootHints) { Bundle extras = new Bundle(); extras.putBoolean( MediaConstants.BROWSER_SERVICE_EXTRAS_KEY_SEARCH_SUPPORTED, true); return new BrowserRoot(ROOT_ID, extras); }
Aby zacząć wyświetlać wyniki wyszukiwania, zastąp metodę onSearch()
w usłudze przeglądarki multimediów. Android Auto i Android Automotive OS przekazują do tej metody hasła wyszukiwane przez użytkownika za każdym razem, gdy użytkownik wywoła interfejs zapytania wyszukiwania lub element „Wyniki wyszukiwania”.
Możesz uporządkować wyniki wyszukiwania z onSearch()
usługi za pomocą elementów tytułowych, aby ułatwić ich przeglądanie. Jeśli na przykład Twoja aplikacja odtwarza muzykę, możesz uporządkować wyniki wyszukiwania według albumów, wykonawców i utworów.
Poniższy fragment kodu przedstawia prostą implementację metody 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. }
Niestandardowe działania przeglądania

Rysunek 6. Pojedyncze niestandardowe działanie przeglądania
Niestandardowe działania przeglądania umożliwiają dodawanie niestandardowych ikon i etykiet do obiektówMediaItem
aplikacji w samochodowej aplikacji multimedialnej oraz obsługę interakcji użytkowników z tymi działaniami. Dzięki temu możesz rozszerzyć funkcjonalność aplikacji do obsługi multimediów na różne sposoby, np. dodając działania „Pobierz”, „Dodaj do kolejki”, „Odtwórz radio”, „Ulubione” lub „Usuń”.

Rysunek 7. Rozszerzenie niestandardowego działania przeglądania
Jeśli liczba działań niestandardowych przekracza liczbę działań, które producent OEM zezwala na wyświetlanie, użytkownikowi zostanie wyświetlone menu przepełnienia.
Jak to działa?
Każde niestandardowe działanie przeglądania jest definiowane przez:
- Identyfikator działania (unikalny ciąg znaków)
- etykietę działania (tekst wyświetlany użytkownikowi);
- Identyfikator URI ikony działania (obiekt rysowalny wektorowo, który można zabarwić)
Listę niestandardowych działań przeglądania możesz zdefiniować globalnie w ramach BrowseRoot
. Następnie możesz przypisać podzbiór tych działań do poszczególnychMediaItem.
Gdy użytkownik wejdzie w interakcję z niestandardowym działaniem przeglądania, Twoja aplikacja otrzyma wywołanie zwrotne w onCustomAction()
. Następnie możesz wykonać działanie i w razie potrzeby zaktualizować listę działań dla MediaItem
. Jest to przydatne w przypadku działań stanowych, takich jak „Ulubione” i „Pobierz”. W przypadku działań, które nie wymagają aktualizacji, np. „Włącz radio”, nie musisz aktualizować listy działań.

Rysunek 8. Pasek narzędzi niestandardowego działania przeglądania
Możesz też dołączyć niestandardowe działania przeglądania do węzła głównego przeglądania. Te działania będą wyświetlane na dodatkowym pasku narzędzi pod głównym paskiem narzędzi.
Jak wdrażać niestandardowe działania przeglądania
Aby dodać do projektu niestandardowe działania przeglądania:
- Zastąp 2 metody w implementacji
MediaBrowserServiceCompat
: - Analizuj limity działań w czasie działania:
- W
onGetRoot()
uzyskaj maksymalną liczbę działań dozwolonych dla każdegoMediaItem
za pomocą kluczaBROWSER_ROOT_HINTS_KEY_CUSTOM_BROWSER_ACTION_LIMIT
wrootHints
Bundle
. Limit 0 oznacza, że funkcja nie jest obsługiwana przez system.
- W
- Utwórz globalną listę niestandardowych działań przeglądania:
- Dla każdego działania utwórz obiekt
Bundle
z tymi kluczami: *EXTRAS_KEY_CUSTOM_BROWSER_ACTION_ID
: identyfikator działania *EXTRAS_KEY_CUSTOM_BROWSER_ACTION_LABEL
: etykieta działania *EXTRAS_KEY_CUSTOM_BROWSER_ACTION_ICON_URI
: URI ikony działania * Dodaj wszystkie obiektyBundle
działania do listy.
- Dla każdego działania utwórz obiekt
- Dodaj listę globalną do
BrowseRoot
:- W sekcji
BrowseRoot
extrasBundle
dodaj listę działań jakoParcelable
Arraylist
, używając kluczaBROWSER_SERVICE_EXTRAS_KEY_CUSTOM_BROWSER_ACTION_ROOT_LIST
.
- W sekcji
- Dodaj działania do obiektów
MediaItem
:- Możesz dodawać działania do poszczególnych obiektów
MediaItem
, umieszczając listę identyfikatorów działań w dodatkowych informacjachMediaDescriptionCompat
za pomocą kluczaDESCRIPTION_EXTRAS_KEY_CUSTOM_BROWSER_ACTION_ID_LIST
. Ta lista musi być podzbiorem globalnej listy działań zdefiniowanych wBrowseRoot
.
- Możesz dodawać działania do poszczególnych obiektów
- Obsługuj działania i zwracaj postęp lub wyniki:
- W
onCustomAction
wykonaj czynność na podstawie identyfikatora czynności i innych potrzebnych danych. IdentyfikatorMediaItem
, które wywołało działanie, możesz uzyskać z dodatków za pomocą kluczaEXTRAS_KEY_CUSTOM_BROWSER_ACTION_MEDIA_ITEM_ID
. - Możesz zaktualizować listę działań dla
MediaItem
, uwzględniając kluczEXTRAS_KEY_CUSTOM_BROWSER_ACTION_RESULT_REFRESH_ITEM
w pakiecie postępu lub wyniku.
- W
Oto kilka zmian, które możesz wprowadzić w BrowserServiceCompat
, aby rozpocząć korzystanie z niestandardowych działań przeglądania.
Zastąpienie BrowserServiceCompat
Musisz zastąpić te metody w klasie MediaBrowserServiceCompat
.
public void onLoadItem(String itemId, @NonNull Result<MediaBrowserCompat.MediaItem> result)
public void onCustomAction(@NonNull String action, Bundle extras, @NonNull Result<Bundle> result)
Limit działań analizowania
Sprawdź, ile niestandardowych działań przeglądania jest obsługiwanych.
public BrowserRoot onGetRoot(@NonNull String clientPackageName, int clientUid, Bundle rootHints) { rootHints.getInt( MediaConstants.BROWSER_ROOT_HINTS_KEY_CUSTOM_BROWSER_ACTION_LIMIT, 0) }
Tworzenie niestandardowego działania przeglądania
Każde działanie musi być spakowane w osobny Bundle
.
- Identyfikator działania
bundle.putString(MediaConstants.EXTRAS_KEY_CUSTOM_BROWSER_ACTION_ID, "<ACTION_ID>")
- Etykieta działania
bundle.putString(MediaConstants.EXTRAS_KEY_CUSTOM_BROWSER_ACTION_LABEL, "<ACTION_LABEL>")
- Identyfikator URI ikony działania
bundle.putString(MediaConstants.EXTRAS_KEY_CUSTOM_BROWSER_ACTION_ICON_URI, "<ACTION_ICON_URI>")
Dodawanie niestandardowych działań przeglądania do Parceable
ArrayList
Dodaj wszystkie obiekty Custom Browse Action Bundle
do obiektu ArrayList
.
private ArrayList<Bundle> createCustomActionsList( CustomBrowseAction browseActions) { ArrayList<Bundle> browseActionsBundle = new ArrayList<>(); for (CustomBrowseAction browseAction : browseActions) { Bundle action = new Bundle(); action.putString(EXTRAS_KEY_CUSTOM_BROWSER_ACTION_ID, browseAction.mId); action.putString(EXTRAS_KEY_CUSTOM_BROWSER_ACTION_LABEL, getString(browseAction.mLabelResId)); action.putString(EXTRAS_KEY_CUSTOM_BROWSER_ACTION_ICON_URI, browseAction.mIcon); browseActionsBundle.add(action); } return browseActionsBundle; }
Dodawanie listy niestandardowych działań przeglądania do katalogu głównego przeglądania
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; }
Dodawanie działań do MediaItem
MediaDescriptionCompat buildDescription (long id, String title, String subtitle, String description, Uri iconUri, Uri mediaUri, ArrayList<String> browseActionIds) { MediaDescriptionCompat.Builder bob = new MediaDescriptionCompat.Builder(); bob.setMediaId(id); bob.setTitle(title); bob.setSubtitle(subtitle); bob.setDescription(description); bob.setIconUri(iconUri); bob.setMediaUri(mediaUri); Bundle extras = new Bundle(); extras.putStringArrayList( DESCRIPTION_EXTRAS_KEY_CUSTOM_BROWSER_ACTION_ID_LIST, browseActionIds); bob.setExtras(extras); return bob.build(); } MediaItem mediaItem = new MediaItem(buildDescription(...), flags);
Wynik kompilacji onCustomAction
- Wyodrębnij identyfikator mediaId z
Bundle extras
:@Override public void onCustomAction( @NonNull String action, Bundle extras, @NonNull Result<Bundle> result){ String mediaId = extras.getString(MediaConstans.EXTRAS_KEY_CUSTOM_BROWSER_ACTION_MEDIA_ITEM_ID); }
- W przypadku wyników asynchronicznych odłącz wynik.
result.detach()
- Pakiet wyników kompilacji
- Wiadomość do użytkownika
mResultBundle.putString(EXTRAS_KEY_CUSTOM_BROWSER_ACTION_RESULT_MESSAGE, mContext.getString(stringRes))
- Aktualizacja elementu(używana do aktualizowania działań w elemencie)
mResultBundle.putString(EXTRAS_KEY_CUSTOM_BROWSER_ACTION_RESULT_REFRESH_ITEM, mediaId);
- Otwórz widok odtwarzania
//Shows user the PBV without changing the playback state mResultBundle.putString(EXTRAS_KEY_CUSTOM_BROWSER_ACTION_RESULT_SHOW_PLAYING_ITEM, null);
- Aktualizowanie węzła przeglądania
//Change current browse node to mediaId mResultBundle.putString(EXTRAS_KEY_CUSTOM_BROWSER_ACTION_RESULT_BROWSE_NODE, mediaId);
- Wiadomość do użytkownika
- Jeśli wystąpił błąd, zadzwoń pod numer
result.sendError(resultBundle).
- Jeśli chcesz uzyskać informacje o postępach, zadzwoń pod numer
result.sendProgressUpdate(resultBundle)
. - Zakończ, dzwoniąc pod numer
result.sendResult(resultBundle)
.
Aktualizowanie stanu działania
Za pomocą metody result.sendProgressUpdate(resultBundle)
z kluczem EXTRAS_KEY_CUSTOM_BROWSER_ACTION_RESULT_REFRESH_ITEM
możesz zaktualizować wartość MediaItem
, aby odzwierciedlała nowy stan działania. Dzięki temu możesz przekazywać użytkownikowi informacje w czasie rzeczywistym o postępach i wynikach jego działania.
Przykład: działanie polegające na pobraniu
Oto przykład użycia tej funkcji do wdrożenia działania pobierania z 3 stanami:
- Pobieranie: to początkowy stan działania. Gdy użytkownik wybierze to działanie, możesz zamienić je na „Pobieranie” i wywołać
sendProgressUpdate
, aby zaktualizować interfejs. - Pobieranie: ten stan oznacza, że pobieranie jest w toku. Za pomocą tego stanu możesz wyświetlać użytkownikowi pasek postępu lub inny wskaźnik.
- Pobrane: ten stan oznacza, że pobieranie zostało zakończone. Po zakończeniu pobierania możesz zamienić „Pobieranie” na „Pobrano” i wywołać
sendResult
za pomocą kluczaEXTRAS_KEY_CUSTOM_BROWSER_ACTION_RESULT_REFRESH_ITEM
, aby wskazać, że element powinien zostać odświeżony. Dodatkowo możesz użyć klawiszaEXTRAS_KEY_CUSTOM_BROWSER_ACTION_RESULT_MESSAGE
, aby wyświetlić użytkownikowi komunikat o sukcesie.
Dzięki temu możesz przekazywać użytkownikowi jasne informacje o procesie pobierania i jego aktualnym stanie. Możesz dodać jeszcze więcej szczegółów za pomocą ikon, które będą wskazywać stan pobierania na poziomie 25%, 50% i 75%.
Przykład: ulubione działanie
Innym przykładem jest ulubiona akcja z 2 stanami:
- Ulubione: to działanie jest wyświetlane w przypadku elementów, które nie znajdują się na liście ulubionych użytkownika. Gdy użytkownik wybierze to działanie, możesz zamienić je na „Ulubione” i wywołać
sendResult
za pomocą kluczaEXTRAS_KEY_CUSTOM_BROWSER_ACTION_RESULT_REFRESH_ITEM
, aby zaktualizować interfejs. - Ulubione: to działanie jest wyświetlane w przypadku elementów, które znajdują się na liście ulubionych użytkownika. Gdy użytkownik wybierze to działanie, możesz zamienić je na „Ulubione” i wywołać
sendResult
z kluczemEXTRAS_KEY_CUSTOM_BROWSER_ACTION_RESULT_REFRESH_ITEM
, aby zaktualizować interfejs.
Dzięki temu użytkownicy mogą w prosty i spójny sposób zarządzać ulubionymi elementami.
Te przykłady pokazują elastyczność niestandardowych działań przeglądania i sposoby ich wykorzystania do wdrażania różnych funkcji z informacjami zwrotnymi w czasie rzeczywistym, co zwiększa komfort korzystania z aplikacji multimedialnej w samochodzie.
Pełny przykład wdrożenia tej funkcji znajdziesz w TestMediaApp
projekcie.
Włączanie sterowania odtwarzaniem
Android Auto i Android Automotive OS wysyłają polecenia sterowania odtwarzaniem za pomocą MediaSessionCompat
usługi.
Musisz zarejestrować sesję i wdrożyć powiązane z nią metody wywołania zwrotnego.
Rejestrowanie sesji multimediów
W metodzie onCreate()
usługi przeglądarki multimediów utwórz MediaSessionCompat
, a następnie zarejestruj sesję multimediów, wywołując setSessionToken()
.
Poniższy fragment kodu pokazuje, jak utworzyć i zarejestrować sesję multimedialną:
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()); ... }
Podczas tworzenia obiektu sesji multimedialnej ustawiasz obiekt wywołania zwrotnego, który jest używany do obsługi żądań sterowania odtwarzaniem. Ten obiekt wywołania zwrotnego tworzysz, podając implementację klasy MediaSessionCompat.Callback
w swojej aplikacji. W następnej sekcji dowiesz się, jak zaimplementować ten obiekt.
Implementowanie poleceń odtwarzania
Gdy użytkownik poprosi o odtwarzanie elementu multimedialnego z Twojej aplikacji, Android Automotive OS i Android Auto użyją klasy MediaSessionCompat.Callback
z obiektu MediaSessionCompat
Twojej aplikacji, który został uzyskany z usługi przeglądarki multimediów aplikacji. Gdy użytkownik chce sterować odtwarzaniem treści, np. wstrzymać odtwarzanie lub przejść do następnego utworu, Android Auto i Android Automotive OS wywołują jedną z metod obiektu wywołania zwrotnego.
Aby obsługiwać odtwarzanie treści, aplikacja musi rozszerzać klasę abstrakcyjną MediaSessionCompat.Callback
i wdrażać metody, które obsługuje.
Zaimplementuj wszystkie te metody wywołania zwrotnego, które są odpowiednie dla rodzaju treści oferowanych przez Twoją aplikację:
onPrepare()
- Wywoływane, gdy zmieni się źródło multimediów. System operacyjny Android Automotive również wywołuje tę metodę natychmiast po uruchomieniu. Aplikacja multimedialna musi implementować tę metodę.
onPlay()
- Wywoływane, gdy użytkownik wybierze odtwarzanie bez wybierania konkretnego elementu. Aplikacja musi odtwarzać domyślne treści lub, jeśli odtwarzanie zostało wstrzymane za pomocą
onPause()
, wznowić odtwarzanie.Uwaga: aplikacja nie powinna automatycznie rozpoczynać odtwarzania muzyki, gdy system operacyjny Android Automotive lub Android Auto połączy się z usługą przeglądarki multimediów. Więcej informacji znajdziesz w sekcji Ustawianie początkowego stanu odtwarzania.
onPlayFromMediaId()
- Wywoływane, gdy użytkownik wybierze odtworzenie określonego elementu. Metoda przekazuje identyfikator, który usługa przeglądarki multimediów przypisała do elementu multimedialnego w hierarchii treści.
onPlayFromSearch()
- Wywoływane, gdy użytkownik wybierze odtwarzanie na podstawie zapytania. Aplikacja musi dokonać odpowiedniego wyboru na podstawie przekazanego ciągu wyszukiwania.
onPause()
- Wywoływane, gdy użytkownik zdecyduje się wstrzymać odtwarzanie.
onSkipToNext()
- Wywoływane, gdy użytkownik zdecyduje się przejść do następnego elementu.
onSkipToPrevious()
- Wywoływane, gdy użytkownik zdecyduje się przejść do poprzedniego elementu.
onStop()
- Wywoływane, gdy użytkownik zdecyduje się zatrzymać odtwarzanie.
Zastąp te metody w aplikacji, aby zapewnić dowolną funkcjonalność. Nie musisz implementować metody, jeśli jej funkcjonalność nie jest obsługiwana przez Twoją aplikację. Jeśli na przykład aplikacja odtwarza transmisję na żywo, np. transmisję sportową, nie musisz implementować metody onSkipToNext()
. Zamiast tego możesz użyć domyślnej implementacji onSkipToNext()
.
Aplikacja nie wymaga specjalnej logiki, aby odtwarzać treści przez głośniki w samochodzie. Gdy aplikacja otrzyma prośbę o odtwarzanie treści, może odtwarzać dźwięk w taki sam sposób, jak odtwarza treści przez głośniki lub słuchawki telefonu użytkownika. Android Auto i Android Automotive OS automatycznie wysyłają treści audio do systemu samochodu, aby odtwarzać je przez głośniki samochodowe.
Więcej informacji o odtwarzaniu treści audio znajdziesz w artykułach Omówienie MediaPlayer, Omówienie aplikacji audio i omówienie ExoPlayera.
Ustawianie standardowych działań odtwarzania
Android Auto i Android Automotive OS wyświetlają elementy sterujące odtwarzaniem na podstawie działań włączonych w obiekcie PlaybackStateCompat
.
Domyślnie aplikacja musi obsługiwać te działania:
Jeśli jest to istotne dla treści aplikacji, może ona dodatkowo obsługiwać te działania:
Możesz też utworzyć kolejkę odtwarzania, która będzie wyświetlana użytkownikowi, ale nie jest to wymagane. Aby to zrobić, wywołaj metody setQueue()
i setQueueTitle()
, włącz działanie ACTION_SKIP_TO_QUEUE_ITEM
i zdefiniuj wywołanie zwrotne onSkipToQueueItem()
.
Dodaliśmy też obsługę ikony Teraz odtwarzane, która wskazuje, co jest obecnie odtwarzane. Aby to zrobić, wywołaj metodę setActiveQueueItemId()
i przekaż identyfikator aktualnie odtwarzanego elementu w kolejce. Musisz aktualizować setActiveQueueItemId()
za każdym razem, gdy nastąpi zmiana w kolejce.
Android Auto i Android Automotive OS wyświetlają przyciski dla każdego włączonego działania, a także kolejkę odtwarzania. Gdy użytkownik kliknie przyciski, system wywoła odpowiednie wywołanie zwrotne z MediaSessionCompat.Callback
.
Rezerwowanie nieużywanego miejsca
Android Auto i Android Automotive OS rezerwują w interfejsie miejsce na działaniaACTION_SKIP_TO_PREVIOUS
i ACTION_SKIP_TO_NEXT
. Jeśli Twoja aplikacja nie obsługuje jednej z tych funkcji, Android Auto i Android Automotive OS wykorzystują to miejsce do wyświetlania utworzonych przez Ciebie działań niestandardowych.
Jeśli nie chcesz wypełniać tych miejsc działaniami niestandardowymi, możesz je zarezerwować, aby Android Auto i Android Automotive OS pozostawiały je puste, gdy aplikacja nie obsługuje odpowiedniej funkcji. Aby to zrobić, wywołaj metodę setExtras()
z pakietem dodatków zawierającym stałe odpowiadające zarezerwowanym funkcjom.
SESSION_EXTRAS_KEY_SLOT_RESERVATION_SKIP_TO_NEXT
odpowiada ACTION_SKIP_TO_NEXT
, a SESSION_EXTRAS_KEY_SLOT_RESERVATION_SKIP_TO_PREV
odpowiada ACTION_SKIP_TO_PREVIOUS
. Użyj tych stałych jako kluczy w pakiecie i wartości logicznej true
jako ich wartości.
Ustawianie początkowego stanu odtwarzania
Gdy Android Auto i Android Automotive OS komunikują się z usługą przeglądarki multimediów, sesja multimediów przekazuje stan odtwarzania treści za pomocą PlaybackStateCompat
.
Aplikacja nie powinna automatycznie rozpoczynać odtwarzania muzyki, gdy system operacyjny Android Automotive lub Android Auto połączy się z usługą przeglądarki multimediów. Zamiast tego polegaj na Androidzie Auto i Androidzie Automotive OS, które wznawiają lub rozpoczynają odtwarzanie na podstawie stanu samochodu lub działań użytkownika.
Aby to zrobić, ustaw początkowy stan PlaybackStateCompat
sesji multimedialnej na STATE_STOPPED
, STATE_PAUSED
, STATE_NONE
lub STATE_ERROR
.
Sesje multimedialne w Androidzie Auto i Androidzie Automotive OS trwają tylko podczas jazdy, więc użytkownicy często je rozpoczynają i kończą. Aby zapewnić płynne przejście między przejazdami, śledź stan poprzedniej sesji użytkownika. Dzięki temu, gdy aplikacja multimedialna otrzyma prośbę o wznowienie, użytkownik będzie mógł automatycznie kontynuować od miejsca, w którym skończył, np. od ostatnio odtwarzanego elementu multimedialnego, PlaybackStateCompat
i kolejki.
Dodawanie niestandardowych działań odtwarzania
Możesz dodać niestandardowe działania odtwarzania, aby wyświetlać dodatkowe działania obsługiwane przez aplikację multimedialną. Jeśli jest wystarczająco dużo miejsca (i nie jest ono zarezerwowane), Android dodaje niestandardowe działania do elementów sterujących transportem. W przeciwnym razie działania niestandardowe będą wyświetlane w menu dodatkowym. Działania niestandardowe są wyświetlane w kolejności, w jakiej zostały dodane do PlaybackStateCompat
.
Używaj działań niestandardowych, aby zapewnić zachowanie inne niż w przypadku standardowych działań. Nie używaj ich do zastępowania ani duplikowania standardowych działań.
Możesz dodawać działania niestandardowe za pomocą metody addCustomAction()
w klasie PlaybackStateCompat.Builder
.
Poniższy fragment kodu pokazuje, jak dodać niestandardowe działanie „Uruchom kanał radiowy”:
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());
Bardziej szczegółowy przykład tej metody znajdziesz w setCustomAction()
w przykładowej aplikacji Universal Android Music Player na GitHubie.
Po utworzeniu działania niestandardowego sesja multimedialna może na nie odpowiadać, zastępując metodę onCustomAction()
.
Ten fragment kodu pokazuje, jak aplikacja może reagować na działanie „Uruchom kanał radiowy”:
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)) { ... } }
Bardziej szczegółowy przykład tej metody znajdziesz w onCustomAction
w przykładowej aplikacji Universal Android Music Player na GitHubie.
Ikony działań niestandardowych
Każde utworzone przez Ciebie działanie niestandardowe wymaga ikony.
Jeśli opis tej ikony pasuje do jednej ze stałych CommandButton.ICON_
, ustaw tę wartość całkowitą dla klucza EXTRAS_KEY_COMMAND_BUTTON_ICON_COMPAT
dodatków działania niestandardowego. W obsługiwanych systemach zastąpi to ikonę
zasobu przekazaną do CustomAction.Builder
, co umożliwi komponentom systemu renderowanie działania
i innych działań związanych z odtwarzaniem w spójnym stylu.
Musisz też określić zasób ikony. Aplikacje w samochodach mogą działać na ekranach o różnych rozmiarach i gęstościach pikseli, dlatego ikony muszą być rysunkami wektorowymi. Obiekt rysowalny wektorowy umożliwia skalowanie zasobów bez utraty szczegółów. Obiekt rysowalny wektorowy ułatwia też wyrównywanie krawędzi i narożników do granic pikseli przy mniejszych rozdzielczościach.
Jeśli działanie niestandardowe ma stan, np. włącza lub wyłącza ustawienie odtwarzania, podaj różne ikony dla różnych stanów, aby użytkownicy mogli zobaczyć zmianę po wybraniu działania.
Podaj alternatywne style ikon dla wyłączonych działań
Gdy działanie niestandardowe jest niedostępne w bieżącym kontekście, zamień ikonę działania niestandardowego na ikonę alternatywną, która wskazuje, że działanie jest wyłączone.
Określanie formatu audio
Aby wskazać, że odtwarzane multimedia korzystają ze specjalnego formatu audio, możesz określić ikony, które będą renderowane w samochodach obsługujących tę funkcję. Możesz ustawić KEY_CONTENT_FORMAT_TINTABLE_LARGE_ICON_URI
i KEY_CONTENT_FORMAT_TINTABLE_SMALL_ICON_URI
w pakiecie dodatków aktualnie odtwarzanego elementu multimedialnego (przekazywanego do MediaSession.setMetadata()
). Pamiętaj, aby ustawić oba te dodatki, aby uwzględnić różne układy.
Możesz też ustawić wartość KEY_IMMERSIVE_AUDIO
extra, aby poinformować producentów samochodów, że jest to dźwięk przestrzenny, i poprosić ich o zachowanie szczególnej ostrożności przy podejmowaniu decyzji o zastosowaniu efektów dźwiękowych, które mogą zakłócać odbiór treści przestrzennych.
Dodawanie linków z aktualnie odtwarzanego elementu
Możesz skonfigurować aktualnie odtwarzany element multimedialny tak, aby jego napisy, opis lub oba te elementy były linkami do innych elementów multimedialnych. Umożliwia to użytkownikowi szybkie przejście do powiązanych elementów, np. innych utworów tego samego wykonawcy, innych odcinków tego podcastu itp. Jeśli samochód obsługuje tę funkcję, użytkownicy mogą kliknąć link, aby przejść do tych treści.
Aby dodać linki, skonfiguruj metadane KEY_SUBTITLE_LINK_MEDIA_ID
(aby utworzyć link z napisów) lub KEY_DESCRIPTION_LINK_MEDIA_ID
(aby utworzyć link z opisu). Szczegółowe informacje znajdziesz w dokumentacji referencyjnej tych pól metadanych.
Obsługa komend głosowych
Aplikacja multimedialna musi obsługiwać komendy głosowe, aby zapewnić kierowcom bezpieczne i wygodne korzystanie z niej, które minimalizuje rozproszenie uwagi. Jeśli na przykład aplikacja odtwarza jeden element multimedialny, użytkownik może powiedzieć „Odtwórz [tytuł utworu]”, aby polecić aplikacji odtworzenie innego utworu bez patrzenia na wyświetlacz samochodu i bez dotykania go. Użytkownicy mogą wysyłać zapytania, klikając odpowiednie przyciski na kierownicy lub wypowiadając słowa aktywujące „OK Google”.
Gdy Android Auto lub Android Automotive OS wykryje i zinterpretuje komendę głosową, jest ona przekazywana do aplikacji za pomocą onPlayFromSearch()
.
Po otrzymaniu tego wywołania zwrotnego aplikacja wyszukuje treści pasujące do query
ciągu znaków i rozpoczyna odtwarzanie.
Użytkownicy mogą określać w zapytaniu różne kategorie terminów, np. gatunek, wykonawca, album, nazwa utworu, stacja radiowa lub playlista. Podczas tworzenia obsługi wyszukiwania uwzględnij wszystkie kategorie, które pasują do Twojej aplikacji. Jeśli Android Auto lub Android Automotive OS wykryje, że dane zapytanie pasuje do określonych kategorii, dołączy dodatkowe informacje w parametrze extras
. Możesz wysłać te dodatki:
Uwzględnij pusty ciąg znaków query
, który może być wysyłany przez Androida Auto lub system operacyjny Android Automotive, jeśli użytkownik nie określi wyszukiwanych haseł.
Na przykład, jeśli użytkownik powie „Włącz jakąś muzykę”. W takim przypadku aplikacja może rozpocząć odtwarzanie ostatnio odtwarzanego lub nowo sugerowanego utworu.
Jeśli wyszukiwanie nie może zostać szybko przetworzone, nie blokuj go w onPlayFromSearch()
.
Zamiast tego ustaw stan odtwarzania na STATE_CONNECTING
i przeprowadź wyszukiwanie na wątku asynchronicznym.
Po rozpoczęciu odtwarzania możesz wypełnić kolejkę sesji multimedialnej powiązanymi treściami. Jeśli na przykład użytkownik poprosi o odtwarzanie albumu, aplikacja może wypełnić kolejkę listą utworów z tego albumu. Warto też wdrożyć obsługę wyników wyszukiwania, które można przeglądać, aby użytkownik mógł wybrać inny utwór pasujący do jego zapytania.
Oprócz zapytań typu „odtwarzaj” Android Auto i Android Automotive OS rozpoznają zapytania głosowe dotyczące sterowania odtwarzaniem, takie jak „wstrzymaj muzykę” i „następny utwór”, i dopasowują te polecenia do odpowiednich wywołań zwrotnych sesji multimedialnej, takich jak onPause()
i onSkipToNext()
.
Szczegółowy przykład implementacji w aplikacji działań związanych z odtwarzaniem za pomocą głosu znajdziesz w artykule Asystent Google i aplikacje multimedialne.
Wdrażanie zabezpieczeń przed rozproszeniem uwagi
Podczas korzystania z Androida Auto telefon użytkownika jest połączony z głośnikami samochodu, dlatego musisz podjąć dodatkowe środki ostrożności, aby zapobiec rozproszeniu uwagi kierowcy.
Blokowanie alarmów w samochodzie
Aplikacje multimedialne na Androida Auto nie mogą rozpoczynać odtwarzania dźwięku przez głośniki samochodowe, chyba że użytkownik rozpocznie odtwarzanie, np. naciskając przycisk odtwarzania. Nawet alarm zaplanowany przez użytkownika w aplikacji multimedialnej nie może odtwarzać muzyki przez głośniki samochodowe.
Aby spełnić to wymaganie, aplikacja może używać sygnału CarConnection
przed odtworzeniem dźwięku. Aplikacja może sprawdzić, czy telefon wyświetla obraz na ekranie samochodu, obserwując LiveData
dla typu połączenia z samochodem i sprawdzając, czy jest on równy CONNECTION_TYPE_PROJECTION
.
Jeśli telefon użytkownika wyświetla obraz, aplikacje multimedialne obsługujące alarmy muszą wykonać jedną z tych czynności:
- Wyłącz alarm.
- Odtwórz alarm na
STREAM_ALARM
i wyświetl interfejs na ekranie telefonu, aby wyłączyć alarm.
Obsługa reklam medialnych
Domyślnie Android Auto wyświetla powiadomienie, gdy metadane multimediów zmienią się podczas sesji odtwarzania dźwięku. Gdy aplikacja multimedialna przełącza się z odtwarzania muzyki na wyświetlanie reklamy, wyświetlanie użytkownikowi powiadomienia może rozpraszać jego uwagę. Aby zapobiec wyświetlaniu powiadomienia przez Androida Auto w tym przypadku, musisz ustawić klucz metadanych multimediów METADATA_KEY_IS_ADVERTISEMENT
na METADATA_VALUE_ATTRIBUTE_PRESENT
, jak pokazano w tym fragmencie kodu:
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()); }
Obsługa błędów ogólnych
Gdy w aplikacji wystąpi błąd, ustaw stan odtwarzania na STATE_ERROR
i podaj komunikat o błędzie za pomocą metody setErrorMessage()
. Listę kodów błędów, których możesz użyć podczas ustawiania komunikatu o błędzie, znajdziesz w artykule PlaybackStateCompat
.
Komunikaty o błędach muszą być wyświetlane użytkownikowi i dostosowane do jego bieżącego ustawienia regionalnego. Android Auto i Android Automotive OS mogą wtedy wyświetlić użytkownikowi komunikat o błędzie.
Jeśli na przykład treść nie jest dostępna w bieżącym regionie użytkownika, możesz użyć kodu błędu ERROR_CODE_NOT_AVAILABLE_IN_REGION
podczas ustawiania komunikatu o błędzie.
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());
Więcej informacji o stanach błędów znajdziesz w artykule Korzystanie z sesji multimedialnej: stany i błędy.
Jeśli użytkownik Androida Auto musi otworzyć aplikację na telefonie, aby rozwiązać problem, podaj mu tę informację w wiadomości. Na przykład komunikat o błędzie może brzmieć „Zaloguj się w aplikacji [nazwa aplikacji]” zamiast „Zaloguj się”.