Tworzenie aplikacji do multimediów do aut

Android Auto i Android Automotive OS pomagają udostępnić treści z aplikacji do multimediów użytkownikom w samochodzie. Aplikacja do multimediów dla samochodów musi obsługiwać przeglądarkę multimediów, aby systemy operacyjne Android Auto i Android Automotive lub inna aplikacja z przeglądarką multimediów mogły wykrywać i wyświetlać Twoje treści.

W tym przewodniku przyjęto założenie, że masz już aplikację do multimediów, która odtwarza dźwięk na telefonie, i jest ona zgodna z architekturą aplikacji do multimediów na Androida.

W tym przewodniku opisujemy wymagane komponenty MediaBrowserService i MediaSession, których aplikacja potrzebuje, by mogła działać na Androidzie Auto i Androidzie Automotive. Gdy ukończysz podstawową infrastrukturę mediów, możesz dodać obsługę Androida Auto i obsługę systemu operacyjnego Android Automotive do swojej aplikacji do multimediów.

Zanim zaczniesz

  1. Zapoznaj się z dokumentacją interfejsu Android Media API.
  2. W artykule Tworzenie aplikacji multimedialnych znajdziesz wskazówki dotyczące projektowania.
  3. Przejrzyj kluczowe terminy i zagadnienia wymienione w tej sekcji.

Kluczowe terminy i koncepcje

Usługa przeglądarki multimediów
Usługa na Androida wdrożona przez aplikację do multimediów zgodną z interfejsem API MediaBrowserServiceCompat. Aplikacja używa tej usługi do ujawniania swoich treści.
Przeglądarka multimediów
Interfejs API używany przez aplikacje do multimediów do wykrywania usług przeglądarek multimedialnych i wyświetlania ich treści. Android Auto i system operacyjny Android Automotive używają przeglądarki do odtwarzania multimediów, aby znaleźć odpowiednią usługę.
Element multimedialny

Przeglądarka multimediów porządkuje zawartość w postaci drzewa obiektów MediaItem. Element multimedialny może mieć jedną lub obie z tych flag:

  • FLAG_PLAYABLE: oznacza, że element jest liściem na drzewie treści. Element reprezentuje pojedynczy strumień dźwięku, np. utwór z albumu, rozdział w audiobooku 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 przejrzeć jego elementy podrzędne.

Zoptymalizowane pod kątem pojazdów

Działanie aplikacji na system operacyjny Android Automotive zgodnej ze wskazówkami dotyczącymi projektowania tego systemu. Interfejs do tych działań nie jest rysowany przez system operacyjny Android Automotive, dlatego musisz zadbać o to, aby aplikacja była zgodna z wytycznymi dotyczącymi projektowania. Zwykle obejmuje to większe elementy dotykowe i rozmiary czcionek, obsługę trybów dziennych i nocnych oraz wyższe proporcje kontrastu.

Interfejsy zoptymalizowane pod kątem pojazdu mogą się wyświetlać tylko wtedy, gdy nie obowiązują ograniczenia związane z wrażeniami użytkownika (CUXR), ponieważ te interfejsy mogą wymagać od użytkownika większej uwagi lub interakcji. Napędy CUXR nie działają, gdy samochód jest zatrzymany lub zaparkowany, ale zawsze działają, gdy samochód jest w ruchu.

Nie musisz projektować działań pod kątem Androida Auto, ponieważ Android Auto tworzy własny interfejs zoptymalizowany pod kątem pojazdu na podstawie informacji z Twojej usługi przeglądarki multimediów.

Skonfiguruj pliki manifestu aplikacji

Zanim utworzysz usługę przeglądarki multimediów, musisz skonfigurować pliki manifestu aplikacji.

Deklarowanie usługi przeglądarki multimediów

Zarówno Android Auto, jak i system operacyjny Android Automotive łączą się z Twoją aplikacją przez usługę przeglądarki multimediów, aby przeglądać elementy multimedialne. Zadeklaruj usługę przeglądarki multimediów w pliku manifestu, aby systemy Android Auto i Android Automotive mogły ją wykryć i połączyć się z aplikacją.

Ten fragment kodu pokazuje, jak zadeklarować usługę przeglądarki multimediów w pliku manifestu. Umieść ten kod w pliku manifestu modułu systemu operacyjnego Android Automotive oraz w 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 mogą używać do reprezentowania Twojej aplikacji w interfejsie systemu. Wymagane są 2 rodzaje ikon:

  • Ikona Menu z aplikacjami
  • Ikona informacji o źródle

Ikona Menu z aplikacjami

Ikona programu uruchamiającego reprezentuje aplikację w interfejsie systemu, na przykład w programie uruchamiającym czy w zasobniku z ikonami. Aby używać ikony z aplikacji mobilnej do reprezentowania aplikacji multimedialnej w samochodzie, użyj tej deklaracji w pliku manifestu:

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

Aby użyć innej ikony niż w aplikacji mobilnej, ustaw w pliku manifestu właściwość android:icon w elemencie <service> usługi przeglądarki multimediów:

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

Ikona informacji o źródle

Rysunek 1. Ikona informacji o źródle treści na karcie multimediów.

Jest ona używana tam, gdzie pierwszeństwo mają treści multimedialne, np. na kartach multimediów. Rozważ ponowne użycie małej ikony używanej do powiadomień. Ta ikona musi być monochromatyczna. Ikonę, która będzie reprezentować Twoją aplikację, możesz określić za pomocą tej deklaracji w pliku manifestu:

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

Utwórz usługę przeglądarki do multimediów

Możesz utworzyć usługę przeglądarki multimediów, rozszerzając klasę MediaBrowserServiceCompat. Dzięki tej usłudze zarówno Android Auto, jak i Android Automotive będzie mógł:

  • Przejrzyj hierarchię treści aplikacji, aby wyświetlić menu użytkownikowi.
  • Aby sterować odtwarzaniem dźwięku, pobierz token obiektu MediaSessionCompat aplikacji.

Możesz też użyć usługi przeglądarki multimediów, by umożliwić innym klientom dostęp do treści multimedialnych z Twojej aplikacji. Mogą to być inne aplikacje na telefonie użytkownika lub inne klienty zdalne.

Przepływ pracy usługi przeglądarki multimediów

W tej sekcji omawiamy, jak system operacyjny Android Automotive i Android Auto współdziałają z usługą przeglądarki multimediów w trakcie standardowego przepływu pracy użytkownika.

  1. Użytkownik uruchamia Twoją aplikację na urządzeniu z systemem operacyjnym Android Automotive lub Android Auto.
  2. System operacyjny Android Automotive lub Android Auto kontaktuje się z usługą przeglądarki multimediów w Twojej aplikacji, korzystając z metody onCreate(). Podczas implementacji metody onCreate() musisz utworzyć i zarejestrować obiekt MediaSessionCompat oraz jego obiekt wywołania zwrotnego.
  3. System operacyjny Android Automotive 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, lecz służy do pobierania większej ilości treści z aplikacji.
  4. System operacyjny Android Automotive lub Android Auto wywołuje metodę onLoadChildren() usługi, aby pobrać dane podrzędne głównego elementu multimedialnego. System operacyjny Android Automotive i Android Auto wyświetlają te elementy multimedialne jako elementy treści najwyższego poziomu. Więcej informacji o tym, czego system oczekuje na tym poziomie, znajdziesz w sekcji Struktura menu głównego na tej stronie.
  5. Jeśli użytkownik wybierze element multimedialny, który można przeglądać, metoda onLoadChildren() Twojej usługi zostanie wywołana ponownie, aby pobrać elementy podrzędne wybranej pozycji menu.
  6. Gdy użytkownik wybierze element multimedialny, który można odtworzyć, system operacyjny Android Automotive lub Android Auto wywołuje odpowiednią metodę wywołania zwrotnego sesji multimediów, aby wykonać to działanie.
  7. Jeśli aplikacja obsługuje tę funkcję, użytkownik może też przeszukiwać Twoje treści. W takim przypadku system operacyjny Android Automotive lub Android Auto wywołuje metodę onSearch() usługi.

Utwórz hierarchię treści

Android Auto i Android Automotive OS wywołują usługę przeglądarki multimediów w Twojej aplikacji, aby ustalić, jakie treści są dostępne. Aby to umożliwić, w przeglądarce multimediów musisz wdrożyć 2 metody: onGetRoot() i onLoadChildren()

Implementacja onGetRoot

Metoda onGetRoot() Twojej usługi zwraca informacje o węźle głównym hierarchii treści. Android Auto i system operacyjny Android Automotive używają tego węzła głównego, aby wysyłać żądanie o pozostałe treści za pomocą metody onLoadChildren().

Ten fragment kodu zawiera 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 opisie metody onGetRoot() w przykładowej aplikacji Universal Android Music Player na GitHubie.

Dodaj weryfikację pakietu dla onGetRoot()

Gdy następuje wywołanie metody onGetRoot() usługi, pakiet wywołujący przekazuje do usługi informacje identyfikacyjne. Usługa może używać tych informacji do określenia, czy pakiet może uzyskać dostęp do Twoich treści. Możesz na przykład ograniczyć dostęp do zawartości aplikacji do listy zatwierdzonych pakietów, porównując pakiet clientPackageName z listą dozwolonych i potwierdzając certyfikat używany do podpisania pakietu APK pakietu. Jeśli nie można zweryfikować pakietu, zwróć null, aby odmówić dostępu do Twoich treści.

Aby aplikacje systemowe, takie jak Android Auto i system operacyjny Android Automotive, miały dostęp do Twoich treści, gdy wywołują one metodę onGetRoot(), Twoja usługa musi zawsze zwracać wartość BrowserRoot niezerową. Sygnatura aplikacji systemowej Android Automotive może się różnić w zależności od marki i modelu samochodu, dlatego musisz zezwolić wszystkim aplikacjom systemowym na pełną obsługę systemu operacyjnego Android Automotive.

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 jest fragmentem kodu z zajęć PackageValidator w przykładowej aplikacji Universal Android Music Player na GitHubie. W tej klasie znajdziesz bardziej szczegółowy przykład implementacji weryfikacji pakietu w metodzie onGetRoot() Twojej usługi.

Oprócz zezwalania na aplikacje systemowe musisz zezwolić Asystentowi Google na połączenie się z urządzeniem MediaBrowserService. Pamiętaj, że Asystent Google ma osobne nazwy pakietów na telefon, w tym na Androida Auto i system operacyjny Android Automotive.

Implementacja onLoadChildren()

Po otrzymaniu obiektu węzła głównego, systemy operacyjne Android Auto i Android Automotive tworzą menu najwyższego poziomu, wywołując w obiekcie węzła głównego onLoadChildren() w celu pobrania jego elementów podrzędnych. 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 przez unikalny ciąg identyfikatora. Aplikacje klienckie traktują te ciągi identyfikatorów jako nieprzejrzyste tokeny. Gdy aplikacja kliencka chce przejść do menu podrzędnego lub odtworzyć element multimedialny, przekazuje token. Twoja aplikacja odpowiada za powiązanie tokena z odpowiednim elementem multimedialnym.

Ten fragment kodu zawiera 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 opisie metody onLoadChildren() w przykładowej aplikacji Universal Android Music Player na GitHubie.

Utwórz strukturę menu głównego

Rysunek 2. Zawartość główna wyświetlana jako karty nawigacyjne.

Android Auto i system operacyjny Android Automotive mają określone ograniczenia dotyczące struktury menu głównego. Są one przekazywane do funkcji MediaBrowserService za pomocą wskazówek dotyczących katalogu głównego, które można odczytać za pomocą argumentu Bundle przekazywanego do onGetRoot(). Postępując zgodnie z tymi wskazówkami, system będzie optymalnie wyświetlać główną zawartość jako karty nawigacyjne. Jeśli nie zastosujesz się do tych wskazówek, niektóre treści główne mogą zostać usunięte lub mogą stać się trudniejsze do znalezienia przez system. Wysyłane są 2 wskazówki:

Aby odczytać odpowiednie wskazówki dotyczące poziomu głównego, 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 zastosować rozgałęzienie logiki struktury hierarchii treści na podstawie wartości tych wskazówek, zwłaszcza jeśli Twoja hierarchia różni się w przypadku integracji MediaBrowser poza Androidem Auto i systemem operacyjnym Android Automotive. Jeśli na przykład zwykle wyświetlasz element główny z możliwością odtwarzania, możesz umieścić go w elemencie, który można przeglądać na poziomie głównym, ze względu na wartość wskazówek dotyczących obsługiwanych flag.

Oprócz tych wskazówek warto też przestrzegać kilku dodatkowych wskazówek, które pomogą Ci zadbać o optymalne renderowanie kart:

  • Dla każdego elementu karty należy udostępniać monochromatyczne, najlepiej białe ikony.
  • Dla każdego elementu karty podawaj krótkie, ale treściwe etykiety. Krótsze etykiety zmniejszają ryzyko obcięcia ciągów znaków.

Wyświetlanie elementów graficznych

Elementy graficzne przeznaczone do elementów multimedialnych muszą być przekazywane jako lokalny identyfikator URI przy użyciu ContentResolver.SCHEME_CONTENT lub ContentResolver.SCHEME_ANDROID_RESOURCE. Ten lokalny identyfikator URI musi przyjmować postać bitmapy lub obiekt rysowalny wektorowo w zasobach aplikacji. W przypadku obiektów MediaDescriptionCompat reprezentujących elementy w hierarchii treści przekaż identyfikator URI za pomocą funkcji setIconUri(). W przypadku obiektów MediaMetadataCompat reprezentujących aktualnie odtwarzany element przekaż identyfikator URI za pomocą putString(), korzystając z dowolnego z tych kluczy:

Poniżej opisujemy, jak pobrać sztukę z identyfikatora URI w sieci i udostępnić ją za pomocą lokalnego identyfikatora URI. Pełniejszy przykład znajdziesz w opisie implementacji metody openFile() i otaczających ją metod w przykładowej aplikacji odtwarzacza muzyki Universal Android.

  1. Utwórz identyfikator URI content:// odpowiadający identyfikatorowi URI sieciowej. Usługa przeglądarki multimediów i sesja multimediów przekazują ten identyfikator URI treści do Androida Auto i Androida Automotive.

    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. W swojej implementacji ContentProvider.openFile() sprawdź, czy dla odpowiedniego identyfikatora URI istnieje plik. Jeśli nie, pobierz plik obrazu i zapisz go w pamięci podręcznej. Ten fragment kodu używa metody 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.

Zastosuj style treści

Po zbudowaniu hierarchii treści za pomocą elementów, które można przeglądać lub odtwarzać, możesz zastosować style treści, które określają, jak elementy te będą wyświetlane w samochodzie.

Możesz używać tych stylów treści:

Elementy listy

Ten styl treści traktuje priorytetowo tytuły i metadane.

Elementy siatki

Ten styl treści traktuje obrazy priorytetowo przed tytułami i metadanymi.

Ustaw domyślne style treści

Możesz ustawić globalne domyślne ustawienia wyświetlania elementów multimedialnych, dodając określone stałe do pakietu dodatków BrowserRoot w metodzie onGetRoot(). Android Auto i system operacyjny Android Automotive odczytują ten pakiet i szukają tych stałych, aby określić właściwy styl.

Te dodatki mogą zostać użyte jako klucze w pakiecie:

Klucze mogą być mapowane na te stałe wartości całkowite, co wpływa na wyświetlanie tych elementów:

  • DESCRIPTION_EXTRAS_VALUE_CONTENT_STYLE_LIST_ITEM: odpowiednie elementy są wyświetlane jako elementy listy.
  • DESCRIPTION_EXTRAS_VALUE_CONTENT_STYLE_GRID_ITEM: odpowiednie elementy są wyświetlane jako elementy siatki.
  • DESCRIPTION_EXTRAS_VALUE_CONTENT_STYLE_CATEGORY_LIST_ITEM: odpowiednie elementy są wyświetlane jako elementy listy „kategoria”. Są one takie same jak zwykłe elementy list, z tym że wokół ikon elementów są stosowane marginesy, ponieważ ikony wyglądają lepiej, gdy są małe. Ikony muszą być wektorowymi elementami rysowalnymi. Ta wskazówka powinna być podana tylko w przypadku elementów, które można przeglądać.
  • DESCRIPTION_EXTRAS_VALUE_CONTENT_STYLE_CATEGORY_GRID_ITEM: odpowiadające im elementy są wyświetlane jako elementy siatki „Kategoria”. Są takie same jak zwykłe elementy siatki z tą różnicą, że wokół ikon elementów są stosowane marginesy, ponieważ ikony wyglądają lepiej, gdy są małe. Ikony muszą być wektorowymi elementami rysowalnymi. Ta wskazówka powinna być podana tylko w przypadku elementów, które można przeglądać.

Poniższy fragment kodu pokazuje, jak ustawić domyślny styl treści dla elementów możliwych do przeglądania jako siatki, a elementy gry 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 poszczególnych elementów

Interfejs Content Style API pozwala zastąpić domyślny styl treści w elementach podrzędnych każdego elementu multimedialnego, który można przeglądać, a także samych elementów multimedialnych.

Aby zastąpić ustawienie domyślne w przypadku elementów podrzędnych elementu multimedialnego, który można przeglądać, utwórz pakiet rozszerzeń w elemencie MediaDescription elementu multimedialnego i dodaj podane wyżej wskazówki. DESCRIPTION_EXTRAS_KEY_CONTENT_STYLE_PLAYABLE obowiązuje w przypadku dzieci, które można zagrać w ten element, a DESCRIPTION_EXTRAS_KEY_CONTENT_STYLE_BROWSABLE – do dzieci, które można przeglądać.

Aby zastąpić ustawienie domyślne w przypadku samego elementu multimedialnego, a nie jego elementów podrzędnych, utwórz pakiet dodatków w elemencie MediaDescription elementu multimedialnego i dodaj wskazówkę z kluczem DESCRIPTION_EXTRAS_KEY_CONTENT_STYLE_SINGLE_ITEM. Użyj wartości opisanych wcześniej, aby określić prezentację tego elementu.

Poniższy fragment kodu pokazuje, jak utworzyć możliwy do przeglądania element MediaItem, który zastąpi domyślny styl treści zarówno w przypadku siebie, jak i jego elementów podrzędnych. Stylizuje się jako element listy kategorii, 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łów

Do grupowania powiązanych elementów multimedialnych możesz używać wskazówek dotyczących poszczególnych elementów. Każdy element multimedialny w grupie musi zadeklarować w elemencie MediaDescription pakiet dodatków, który obejmuje 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 będzie używany jako tytuł grupy.

Poniższy fragment kodu pokazuje, jak utworzyć obiekt 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ć, tworząc obok siebie blok. Załóżmy na przykład, że chcesz wyświetlić w tej kolejności 2 grupy elementów multimedialnych – „Utwory” i „Albumy”, a aplikacja przesyła 5 elementów multimedialnych w tej kolejności:

  1. Element multimedialny A z: extras.putString(MediaConstants.DESCRIPTION_EXTRAS_KEY_CONTENT_STYLE_GROUP_TITLE, "Songs")
  2. Element multimedialny B z: extras.putString(MediaConstants.DESCRIPTION_EXTRAS_KEY_CONTENT_STYLE_GROUP_TITLE, "Albums")
  3. Element multimedialny C z: extras.putString(MediaConstants.DESCRIPTION_EXTRAS_KEY_CONTENT_STYLE_GROUP_TITLE, "Songs")
  4. Element multimedialny D z: extras.putString(MediaConstants.DESCRIPTION_EXTRAS_KEY_CONTENT_STYLE_GROUP_TITLE, "Songs")
  5. Element multimedialny E z wartością extras.putString(MediaConstants.DESCRIPTION_EXTRAS_KEY_CONTENT_STYLE_GROUP_TITLE, "Albums")

Elementy multimedialne z grup „Utwory” i „Albumy” nie są umieszczone w sąsiadujących ze sobą blokach, dlatego Android Auto i Android Automotive 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 przekazać je w takiej kolejności:

  1. Element multimedialny A z: extras.putString(MediaConstants.DESCRIPTION_EXTRAS_KEY_CONTENT_STYLE_GROUP_TITLE, "Songs")
  2. Element multimedialny C z: extras.putString(MediaConstants.DESCRIPTION_EXTRAS_KEY_CONTENT_STYLE_GROUP_TITLE, "Songs")
  3. Element multimedialny D z: extras.putString(MediaConstants.DESCRIPTION_EXTRAS_KEY_CONTENT_STYLE_GROUP_TITLE, "Songs")
  4. Element multimedialny B z: extras.putString(MediaConstants.DESCRIPTION_EXTRAS_KEY_CONTENT_STYLE_GROUP_TITLE, "Albums")
  5. Element multimedialny E z wartością extras.putString(MediaConstants.DESCRIPTION_EXTRAS_KEY_CONTENT_STYLE_GROUP_TITLE, "Albums")

Wyświetl dodatkowe wskaźniki metadanych

Możesz dołączyć dodatkowe wskaźniki metadanych, aby szybko wyświetlać informacje o treściach w drzewie przeglądarki multimediów i podczas odtwarzania. W drzewie przeglądania systemy operacyjne Android Auto i Android Automotive odczytują dodatki związane z produktem i szukają określonych stałych, aby określić, które wskaźniki mają być wyświetlane. Podczas odtwarzania multimediów Android Auto i system operacyjny Android Automotive odczytują metadane sesji multimedialnej i poszukują określonych stałych, 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 pierwszym elemencie i paskiem postępu dla częściowo odtworzonych treści w przypadku drugiego elementu.

Te stałe mogą być używane zarówno w opisie MediaItem, jak i w dodatkowych (MediaMetadata):

Tych stałych można używać tylko w dodatkowych opisach (MediaItem):

Aby wyświetlać wskaźniki, które pojawiają się, gdy użytkownik przegląda drzewo przeglądania multimediów, utwórz pakiet dodatków zawierający co najmniej 1 z tych stałych i przekaż go do metody MediaDescription.Builder.setExtras().

Ten fragment kodu pokazuje, jak wyświetlać wskaźniki, które pokazują, że element multimedialny jest gotowy 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 odtwarzanego elementu multimedialnego, możesz zadeklarować wartości Long dla elementu METADATA_KEY_IS_EXPLICIT lub EXTRA_DOWNLOAD_STATUS w MediaMetadataCompat elementu mediaSession. W widoku odtwarzania nie można wyświetlić wskaźników DESCRIPTION_EXTRAS_KEY_COMPLETION_STATUS ani DESCRIPTION_EXTRAS_KEY_COMPLETION_PERCENTAGE.

Ten fragment kodu pokazuje, jak wskazać, że bieżący utwór w widoku odtwarzania jest przeznaczony dla pełnoletnich i został pobrany:

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

Aktualizuj pasek postępu w widoku przeglądania w trakcie odtwarzania treści.

Jak już wspomnieliśmy, możesz użyć dodatku DESCRIPTION_EXTRAS_KEY_COMPLETION_PERCENTAGE, aby wyświetlić pasek postępu w przypadku częściowo odtworzonych treści w widoku przeglądania. Jeśli jednak użytkownik będzie nadal odtwarzać częściowo odtworzoną treść z systemu Android Auto lub Android Automotive, z upływem czasu wskaźnik ten stanie się niedokładny.

Aby zapewnić aktualność paska postępu w systemach Android Auto i Android Automotive, możesz podać dodatkowe informacje w usługach 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:

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

Rysunek 5. Widok odtwarzania z opcją „Wyniki wyszukiwania” służący do wyświetlania elementów multimedialnych związanych z wyszukiwaniem głosowym użytkownika.

Aplikacja może oferować kontekstowe wyniki wyszukiwania, które wyświetlają się użytkownikom po rozpoczęciu wyszukiwania. Android Auto i Android Automotive OS wyświetlają te wyniki w interfejsach zapytań lub za pomocą asortymentów, które łączą się z zapytaniami wprowadzonymi wcześniej w sesji. Więcej informacji znajdziesz w sekcji Obsługa komend głosowych w tym przewodniku.

Aby wyświetlić możliwe do przeglądania wyniki wyszukiwania, umieść klucz stały BROWSER_SERVICE_EXTRAS_KEY_SEARCH_SUPPORTED w pakiecie dodatków metod onGetRoot() usługi, mapując go na wartość logiczną true.

Ten fragment kodu pokazuje, jak włączyć obsługę w metodzie 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ąć podawać wyniki wyszukiwania, zastąp metodę onSearch() w usłudze przeglądarki multimediów. Android Auto i system operacyjny Android Automotive przekazują hasła wyszukiwane przez użytkownika za pomocą tej metody za każdym razem, gdy użytkownik wywołuje interfejs zapytania wyszukiwania lub akredytację „Wyniki wyszukiwania”.

Możesz uporządkować wyniki wyszukiwania za pomocą metody onSearch() w Twojej usłudze za pomocą elementów tytułów, aby łatwiej je przeglądać. Jeśli na przykład aplikacja odtwarza muzykę, możesz uporządkować wyniki wyszukiwania według albumu, wykonawcy i utworów.

Ten fragment kodu zawiera 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

Jedno niestandardowe działanie przeglądania.

Rysunek 6. Jedno niestandardowe działanie przeglądania

Niestandardowe działania przeglądania umożliwiają dodawanie niestandardowych ikon i etykiet do obiektów MediaItem w aplikacji multimedialnej w samochodzie oraz obsługę interakcji użytkowników z tymi działaniami. Dzięki temu możesz rozszerzyć funkcjonalność aplikacji do multimediów, np. dodać działania „Pobierz”, „Dodaj do kolejki”, „Włącz radio”, „Ulubione” lub „Usuń”.

Niestandardowe rozszerzone menu działań przeglądania.

Rysunek 7. Rozszerzone menu niestandardowego działania przeglądania

Jeśli dostępnych jest więcej działań niestandardowych niż zezwala producent OEM, użytkownik zobaczy rozszerzone menu.

Jak to działa?

Każde niestandardowe działanie przeglądania jest zdefiniowane za pomocą:

  • Identyfikator działania (unikalny identyfikator ciągu znaków).
  • Etykieta działania (tekst wyświetlany użytkownikowi).
  • Identyfikator URI ikony działania (element rysowalny wektorowo, z możliwością zabarwienia)

W BrowseRoot następuje globalnie lista niestandardowych działań przeglądania. Następnie możesz dołączyć podzbiór tych działań do poszczególnych MediaItem.

Gdy użytkownik wejdzie w interakcję z niestandardowym działaniem przeglądania, aplikacja otrzyma wywołanie zwrotne w komponencie onCustomAction(). Możesz wykonać działanie i w razie potrzeby zaktualizować listę działań dla elementu MediaItem. Jest to przydatne w przypadku działań stanowych, np. „Ulubione” i „Pobierz”. W przypadku działań, które nie wymagają aktualizacji, np. „Włącz radio”, nie musisz aktualizować listy działań.

Niestandardowe działania przeglądania w katalogu głównym węzła przeglądania.

Rysunek 8. Niestandardowy pasek narzędzi działań związanych z przeglądaniem

Niestandardowe działania przeglądania możesz też dołączyć do katalogu głównego węzła przeglądania. Te czynności będą wyświetlane na dodatkowym pasku narzędzi pod głównym paskiem narzędzi.

Jak zaimplementować niestandardowe działania przeglądania

Oto kroki, które pozwalają dodać do projektu niestandardowe działania przeglądania:

  1. Zastąp 2 metody w implementacji MediaBrowserServiceCompat:
  2. Analizuj limity działań w czasie działania:
    • W onGetRoot() sprawdź maksymalną dozwoloną liczbę działań dla każdego MediaItem za pomocą klucza BROWSER_ROOT_HINTS_KEY_CUSTOM_BROWSER_ACTION_LIMIT w: Bundle rootHints. Wartość 0 oznacza, że dana funkcja nie jest obsługiwana przez system.
  3. Utwórz globalną listę niestandardowych działań przeglądania:
    • Dla każdej czynności 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: identyfikator URI ikony działania * Dodaj wszystkie obiekty działania Bundle do listy.
  4. Dodaj listę globalną do elementu BrowseRoot:
  5. Dodaj działania do obiektów MediaItem:
    • Możesz dodawać działania do poszczególnych obiektów MediaItem, dodając listę identyfikatorów działań w dodatkach MediaDescriptionCompat za pomocą klucza DESCRIPTION_EXTRAS_KEY_CUSTOM_BROWSER_ACTION_ID_LIST. Ta lista musi być podzbiorem globalnej listy działań zdefiniowanych w BrowseRoot.
  6. Obsługa działań oraz zwracanie postępów lub wyników:

Oto kilka zmian, które możesz wprowadzić w BrowserServiceCompat, aby zacząć korzystać z niestandardowych działań przeglądania.

Zastąp zasadę BrowserServiceCompat

Musisz zastąpić poniższe metody w zadaniu 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ń analizy

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 należy spakować w osobnym pliku 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>")
    

Dodaj niestandardowe działania przeglądania do kampanii Parceable ArrayList

Dodaj wszystkie obiekty niestandardowego działania przeglądania Bundle do elementu 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;
}

Dodaj listę 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;
}

Dodaj działania 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);

Utwórz onCustomAction wynik

  • Przeanalizuj element 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);
    }
    
  • Aby uzyskać wyniki asynchroniczne, odłącz wynik. result.detach()
  • Utwórz pakiet wyników
    • Wiadomość do użytkownika
      mResultBundle.putString(EXTRAS_KEY_CUSTOM_BROWSER_ACTION_RESULT_MESSAGE,
                mContext.getString(stringRes))
      
    • Aktualizowanie elementu(służy 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);
      
    • Aktualizacja węzła przeglądania
      //Change current browse node to mediaId
      mResultBundle.putString(EXTRAS_KEY_CUSTOM_BROWSER_ACTION_RESULT_BROWSE_NODE, mediaId);
      
  • Jeśli wystąpi błąd, zadzwoń pod numer result.sendError(resultBundle).
  • Jeśli postęp jest zaktualizowany, zadzwoń pod numer result.sendProgressUpdate(resultBundle).
  • Zakończ, dzwoniąc pod numer result.sendResult(resultBundle).

Aktualizowanie stanu działania

Używając metody result.sendProgressUpdate(resultBundle) z kluczem EXTRAS_KEY_CUSTOM_BROWSER_ACTION_RESULT_REFRESH_ITEM, możesz zaktualizować MediaItem, aby odzwierciedlał nowy stan działania. Dzięki temu możesz informować użytkownika w czasie rzeczywistym o postępach i wynikach podejmowanych działań.

Przykład: działanie polegające na pobraniu

Oto przykład użycia tej funkcji do zaimplementowania działania pobierania z 3 stanami:

  1. Pobieranie: to początkowy stan działania. Gdy użytkownik wybierze to działanie, możesz zamienić je na „Pobieranie” i wywołać metodę sendProgressUpdate, aby zaktualizować interfejs.
  2. Pobieranie: ten stan oznacza, że trwa pobieranie. Możesz użyć tego stanu, by wyświetlić użytkownikowi pasek postępu lub inny wskaźnik.
  3. Pobrano: ten stan oznacza, że pobieranie zostało zakończone. Po zakończeniu pobierania możesz zamienić „Pobieranie” na „Pobrane” i wywołać metodę sendResult za pomocą klawisza EXTRAS_KEY_CUSTOM_BROWSER_ACTION_RESULT_REFRESH_ITEM, aby wskazać, że element powinien zostać odświeżony. Możesz też użyć klucza EXTRAS_KEY_CUSTOM_BROWSER_ACTION_RESULT_MESSAGE, aby wyświetlić użytkownikowi komunikat o powodzeniu.

Dzięki temu możesz wyraźnie poinformować użytkownika o procesie pobierania i jego obecnym stanie. Możesz dodać jeszcze więcej szczegółów za pomocą ikon, które pokazują stany pobierania: 25%, 50% i 75%.

Przykład: ulubiona akcja

Kolejny przykład to ulubione działanie z 2 stanami:

  1. Ulubione: działanie jest wyświetlane w przypadku elementów, których nie ma na liście ulubionych użytkownika. Gdy użytkownik wybierze to działanie, możesz zamienić je na „Dodano do ulubionych” i wywołać sendResult za pomocą klucza EXTRAS_KEY_CUSTOM_BROWSER_ACTION_RESULT_REFRESH_ITEM, aby zaktualizować interfejs.
  2. Dodane do ulubionych: to działanie jest wyświetlane w przypadku elementów znajdujących się na liście ulubionych użytkownika. Gdy użytkownik wybierze to działanie, możesz zamienić je na „Ulubione” i wywołać metodę sendResult przy użyciu klucza EXTRAS_KEY_CUSTOM_BROWSER_ACTION_RESULT_REFRESH_ITEM, aby zaktualizować interfejs.

Dzięki temu użytkownicy mogą w jasny i spójny sposób zarządzać ulubionymi elementami.

Te przykłady pokazują elastyczność niestandardowych działań przeglądania i sposoby ich wykorzystania do zaimplementowania różnych funkcji z opiniami w czasie rzeczywistym, aby zwiększyć wygodę użytkowników korzystających z aplikacji do multimediów w samochodzie.

Pełną przykładową implementację tej funkcji znajdziesz w projekcie TestMediaApp.

Włącz sterowanie odtwarzaniem

Android Auto i system operacyjny Android Automotive wysyłają polecenia sterujące odtwarzaniem przez MediaSessionCompat usługi. Musisz zarejestrować sesję i zaimplementować powiązane z nią metody wywołania zwrotnego.

Zarejestruj sesję 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().

Ten fragment kodu pokazuje, jak utworzyć i zarejestrować sesję multimediów:

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 multimediów ustawiasz obiekt wywołania zwrotnego służący do obsługi żądań sterowania odtwarzaniem. Obiekt wywołania zwrotnego możesz utworzyć, dostarczając implementację klasy MediaSessionCompat.Callback dla swojej aplikacji. W następnej sekcji dowiesz się, jak wdrożyć ten obiekt.

Wdrażanie poleceń Play

Gdy użytkownik prosi o odtworzenie elementu multimedialnego z Twojej aplikacji, system operacyjny Android Automotive i Android Auto korzystają z klasy MediaSessionCompat.Callback z obiektu MediaSessionCompat aplikacji uzyskanego z usługi przeglądarki multimediów w aplikacji. Gdy użytkownik chce sterować odtwarzaniem treści, np. wstrzymać odtwarzanie lub przejść do następnej ścieżki, Android Auto i Android Automotive OS wywołają jedną z metod obiektu wywołania zwrotnego.

Aby umożliwić odtwarzanie treści, Twoja aplikacja musi rozszerzyć abstrakcyjną klasę MediaSessionCompat.Callback i zaimplementować metody obsługiwane przez Twoją aplikację.

Zaimplementuj wszystkie poniższe metody wywołania zwrotnego, które są odpowiednie dla typu treści oferowanych przez Twoją aplikację:

onPrepare()
Wywoływane po zmianie źródła multimediów. System operacyjny Android Automotive też wywołuje tę metodę natychmiast po uruchomieniu. Twoja aplikacja do multimediów musi implementować tę metodę.
onPlay()
Wywoływane, jeśli użytkownik zdecyduje się zagrać w grze bez wybierania konkretnego elementu. Aplikacja musi odtwarzać swoje domyślne treści. Jeśli odtwarzanie zostało wstrzymane za pomocą onPause(), aplikacja wznowi odtwarzanie.

Uwaga: aplikacja nie powinna automatycznie odtwarzać muzyki, gdy system operacyjny Android Automotive lub Android Auto połączy się z przeglądarką multimediów. Więcej informacji znajdziesz w sekcji na temat ustawiania początkowego stanu odtwarzania.

onPlayFromMediaId()
Wywoływane, gdy użytkownik chce odtworzyć konkretny element. Metoda jest przekazywana identyfikator, który usługa przeglądarki multimediów przypisała elementowi multimedialnemu w hierarchii treści.
onPlayFromSearch()
Wywoływane, gdy użytkownik chce odtworzyć film, wpisując zapytanie. 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 umożliwić korzystanie z odpowiednich funkcji. Nie musisz implementować metody, jeśli jej funkcja 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().

Twoja aplikacja nie wymaga żadnej specjalnej logiki do odtwarzania treści przez głośniki w samochodzie. Gdy aplikacja otrzyma prośbę o odtworzenie treści, może odtworzyć dźwięk w taki sam sposób jak dźwięk przez głośniki lub słuchawki telefonu użytkownika. Android Auto i system operacyjny Android Automotive automatycznie przesyłają treści audio do systemu samochodu, aby odtwarzać je przez głośniki samochodu.

Więcej informacji o odtwarzaniu treści audio znajdziesz w omówieniu odtwarzacza MediaPlayer, omówieniu aplikacji Audio i omówieniu odtwarzacza ExoPlayer.

Ustaw standardowe działania związane z odtwarzaniem

Android Auto i system operacyjny Android Automotive wyświetlają elementy sterujące odtwarzaniem na podstawie działań, które włączono w obiekcie PlaybackStateCompat.

Domyślnie aplikacja musi obsługiwać te działania:

Aplikacja może dodatkowo obsługiwać te działania, jeśli są powiązane z jej zawartością:

Dodatkowo możesz utworzyć kolejkę odtwarzania, która może się wyświetlać użytkownikowi, ale nie jest wymagana. Aby to zrobić, wywołaj metody setQueue() i setQueueTitle(), włącz działanie ACTION_SKIP_TO_QUEUE_ITEM i zdefiniuj wywołanie zwrotne onSkipToQueueItem().

Dodano też obsługę ikony Teraz odtwarzane, która wskazuje, co jest odtwarzane. Aby to zrobić, wywołaj metodę setActiveQueueItemId() i przekaż identyfikator aktualnie odtwarzanego elementu w kolejce. Musisz aktualizować setActiveQueueItemId() przy każdej zmianie w kolejce.

Systemy Android Auto i Android Automotive wyświetlają przyciski dla każdego włączonego działania oraz kolejki odtwarzania. Po kliknięciu przycisków system wywoła odpowiednie wywołanie zwrotne z MediaSessionCompat.Callback.

Zarezerwuj niewykorzystane miejsce

Android Auto i system operacyjny Android Automotive zarezerwują w interfejsie miejsce na działania ACTION_SKIP_TO_PREVIOUS i ACTION_SKIP_TO_NEXT. Jeśli Twoja aplikacja nie obsługuje żadnej z tych funkcji, Android Auto i system operacyjny Android Automotive będą wyświetlać w tym miejscu wszystkie utworzone przez Ciebie działania niestandardowe.

Jeśli nie chcesz wypełniać tych miejsc działaniami niestandardowymi, możesz je zarezerwować. Dzięki temu Android Auto i Android Automotive OS będą pozostawiać puste miejsce, gdy aplikacja nie obsługuje danej funkcji. W tym celu wywołaj metodę setExtras() z pakietem dodatkowych, który zawiera stałe odpowiadające funkcjom zarezerwowanym. SESSION_EXTRAS_KEY_SLOT_RESERVATION_SKIP_TO_NEXT odpowiada wartości ACTION_SKIP_TO_NEXT, a SESSION_EXTRAS_KEY_SLOT_RESERVATION_SKIP_TO_PREV odpowiada wartości ACTION_SKIP_TO_PREVIOUS. Użyj tych stałych jako kluczy w pakiecie, a ich wartości – wartości logicznej true.

Ustawienie początkowego stanu Odtwarzanie

Gdy Android Auto i Android Automotive OS komunikują się z przeglądarką multimediów, sesja multimediów informuje o stanie odtwarzania treści za pomocą interfejsu PlaybackStateCompat. Aplikacja nie powinna automatycznie rozpoczynać odtwarzania muzyki, gdy system operacyjny Android Automotive lub Android Auto połączy się z przeglądarką multimediów. Zamiast tego w zależności od stanu samochodu lub działań użytkownika korzystaj z Androida Auto i systemu operacyjnego Android Automotive.

Aby to zrobić, ustaw początkowy PlaybackStateCompat sesji multimedialnej na STATE_STOPPED, STATE_PAUSED, STATE_NONE lub STATE_ERROR.

Sesje multimediów w Androidzie Auto i systemie operacyjnym Android Automotive trwają tylko do końca jazdy, więc użytkownicy często uruchamiają i zatrzymują te sesje. Aby zapewnić płynną obsługę różnych dysków, śledź poprzedni stan sesji użytkownika. Dzięki temu, gdy aplikacja do multimediów otrzyma żądanie wznowienia, będzie mógł automatycznie wznowić odtwarzanie w miejscu, w którym przerwano odtwarzanie – na przykład ostatnio odtwarzany element multimedialny, element PlaybackStateCompat czy kolejkę.

Dodawanie niestandardowych działań związanych z odtwarzaniem

Możesz dodać niestandardowe działania związane z odtwarzaniem, aby wyświetlać dodatkowe działania obsługiwane przez Twoją aplikację do multimediów. Jeśli miejsce na dane pozwala(i nie jest zarezerwowane), Android dodaje działania niestandardowe do ustawień transportu. W przeciwnym razie działania niestandardowe wyświetlą się w rozszerzonym menu. Działania niestandardowe są wyświetlane w kolejności, w której zostały dodane do: PlaybackStateCompat.

Korzystaj z działań niestandardowych, by zapewniać zachowanie różniące się od działań standardowych. Nie używaj ich do zastępowania ani powielania działań standardowych.

Działania niestandardowe możesz dodawać za pomocą metody addCustomAction() w klasie PlaybackStateCompat.Builder.

Ten fragment kodu pokazuje, jak dodać niestandardowe działanie „Uruchom kanał radiowy”:

Kotlin

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

Java

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

Bardziej szczegółowy przykład tej metody znajdziesz w opisie metody setCustomAction() w przykładowej aplikacji Universal Android Music Player na GitHubie.

Po utworzeniu działania niestandardowego sesja multimediów może zareagować na to działanie, zastępując metodę onCustomAction().

Ten fragment kodu pokazuje, jak aplikacja może zareagować 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 opisie metody onCustomAction w przykładowej aplikacji Universal Android Music Player na GitHubie.

Ikony działań niestandardowych

Każde utworzone działanie niestandardowe wymaga zasobu ikony. Aplikacje w samochodach mogą działać na ekranach o różnej rozdzielczości i o różnej rozdzielczości, dlatego przesyłane ikony muszą być elementami rysowalnymi w formacie wektorowym. Obiekt rysowalny w formacie wektorowym pozwala skalować zasoby bez utraty szczegółów. Funkcja rysowania wektorowego ułatwia też wyrównywanie krawędzi i rogów do granic pikseli przy mniejszych rozdzielczościach.

Jeśli działanie niestandardowe ma stan stanowy – np. włącza lub wyłącza ustawienie odtwarzania – dostarczaj różne ikony dla poszczególnych stanów. Dzięki temu użytkownicy mogą zobaczyć zmianę po wybraniu tego działania.

Podawanie alternatywnych stylów ikon dla wyłączonych działań

Gdy działanie niestandardowe jest niedostępne w bieżącym kontekście, zamień ikonę działania niestandardowego na inną ikonę wskazującą, że to działanie jest wyłączone.

Rysunek 6. Przykłady ikon niestandardowych działań w nietypowym stylu.

Wskaż format dźwięku

Aby wskazać, że aktualnie odtwarzane multimedia używają specjalnego formatu dźwięku, możesz określić ikony renderowane w samochodach obsługujących tę funkcję. Możesz ustawić właściwości KEY_CONTENT_FORMAT_TINTABLE_LARGE_ICON_URI i KEY_CONTENT_FORMAT_TINTABLE_SMALL_ICON_URI w pakiecie z dodatkowymi elementami aktualnie odtwarzanego elementu multimedialnego (przekazanego do MediaSession.setMetadata()). Pamiętaj, aby ustawić oba te elementy dodatkowe, aby dostosować je do różnych układów.

Dodatkowo możesz dodać KEY_IMMERSIVE_AUDIO, aby poinformować producentów OEM, że jest to dźwięk wciągający. Dlatego powinni oni być bardzo ostrożni przy podejmowaniu decyzji o zastosowaniu efektów dźwiękowych, które mogą zakłócać treści multimedialne.

Możesz skonfigurować aktualnie odtwarzany element multimedialny w taki sposób, aby jego podtytuł i opis lub oba te elementy były linkami do innych elementów multimedialnych. Dzięki temu użytkownik może szybko przejść do powiązanych elementów, np. przejść do innych utworów tego samego wykonawcy, do innych odcinków danego podcastu itd. Jeśli samochód obsługuje tę funkcję, użytkownik może kliknąć link, aby przejrzeć te treści.

Aby dodawać linki, skonfiguruj metadane KEY_SUBTITLE_LINK_MEDIA_ID (linki z podtytułu) lub KEY_DESCRIPTION_LINK_MEDIA_ID (linki z opisu). Szczegółowe informacje na ten temat można znaleźć w dokumentacji tych pól metadanych.

Obsługa komend głosowych

Aplikacja do multimediów musi obsługiwać komendy głosowe, aby kierowcy mogli bezpiecznie i wygodnie korzystać z urządzenia, a jednocześnie ograniczyć rozpraszanie uwagi. Jeśli na przykład aplikacja odtwarza jeden element multimedialny, użytkownik może powiedzieć „Włącz [tytuł utworu]”, aby aplikacja włączyła inny utwór bez patrzenia na wyświetlacz samochodu ani dotykania go. Użytkownicy mogą inicjować zapytania, klikając odpowiednie przyciski na kierownicy lub wypowiadając słowa-klucze „OK Google”.

Gdy Android Auto lub Android Automotive wykryje i zinterpretuje komendę głosową, jest ona przekazywana do aplikacji przez onPlayFromSearch(). Po otrzymaniu wywołania zwrotnego aplikacja znajduje treści pasujące do ciągu query i rozpoczyna odtwarzanie.

Użytkownicy mogą określać w zapytaniach różne kategorie haseł, np. gatunek, wykonawcę, album, tytuł utworu, stację radiową lub playlistę. Tworząc obsługę wyszukiwania, uwzględnij wszystkie kategorie odpowiednie dla Twojej aplikacji. Jeśli Android Auto lub Android Automotive wykryje, że dane zapytanie pasuje do określonych kategorii, doda dodatkowe informacje w parametrze extras. Można wysłać te dodatki:

Konto zawiera pusty ciąg znaków query, który może być wysyłany przez system operacyjny Android Auto lub Android Automotive, jeśli użytkownik nie określi wyszukiwanego hasła. na przykład „Włącz jakąś muzykę”. W takim przypadku aplikacja może włączyć ostatnio odtwarzany lub proponowany utwór.

Jeśli wyszukiwanie nie może być szybko przetwarzane, nie blokuj wyszukiwania w onPlayFromSearch(). Zamiast tego ustaw stan odtwarzania na STATE_CONNECTING i przeprowadź wyszukiwanie w wątku asynchronicznym.

Gdy rozpocznie się odtwarzanie, spróbuj uzupełnić kolejkę sesji multimedialnej odpowiednimi treściami. Jeśli na przykład użytkownik poprosi o odtworzenie albumu, aplikacja może wypełnić kolejkę listą utworów z tego albumu. Rozważ też wdrożenie możliwości przeglądania wyników wyszukiwania, aby użytkownik mógł wybrać inną ścieżkę pasującą do jego zapytania.

Oprócz zapytań „odtwórz” Android Auto i systemy operacyjne Android Automotive rozpoznają zapytania głosowe, aby sterować odtwarzaniem, np. „wstrzymaj muzykę” i „następny utwór”, i dopasowują te polecenia do odpowiednich wywołań zwrotnych sesji multimediów, takich jak onPause() i onSkipToNext().

Szczegółowy przykład implementowania w aplikacji działań związanych z odtwarzaniem z obsługą głosu znajdziesz w artykule o Asystencie Google i aplikacjach do multimediów.

Stosuj środki ochrony rozpraszające uwagę

Telefon użytkownika jest podłączony do głośników samochodu podczas korzystania z Androida Auto, dlatego musisz podjąć dodatkowe środki ostrożności, aby nie rozpraszać kierowcy.

Wyciszanie alarmów w samochodzie

Aplikacje do multimediów na Androidzie Auto nie mogą rozpoczynać odtwarzania dźwięku przez głośniki samochodu, chyba że użytkownik rozpocznie odtwarzanie np. przez naciśnięcie przycisku odtwarzania. Nawet alarm zaplanowany przez użytkownika z aplikacji do multimediów nie może rozpoczynać odtwarzania muzyki przez głośniki samochodu.

Aby spełnić to wymaganie, przed odtworzeniem dźwięku aplikacja może użyć CarConnection jako sygnału. Aplikacja może sprawdzić, czy telefon wyświetla obraz na ekranie samochodu, obserwując zdarzenie LiveData jako typ połączenia i sprawdzając, czy ma on wartość CONNECTION_TYPE_PROJECTION.

Jeśli telefon użytkownika przesyła obraz, aplikacje do multimediów obsługujące alarmy muszą wykonywać jedną z tych czynności:

  • Wyłącz alarm.
  • Włącz alarm na telefonie STREAM_ALARM i udostępniaj interfejs na ekranie telefonu, który umożliwia jego wyłączenie.

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 do multimediów przełącza się z muzyki na wyświetlanie reklam, wyświetlanie powiadomienia jest rozpraszające. Aby w takim przypadku Android Auto nie mógł wyświetlić powiadomienia, ustaw 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, korzystając z metody setErrorMessage(). Listę kodów błędów, których możesz użyć podczas ustawiania komunikatu o błędzie, znajdziesz w sekcji PlaybackStateCompat. Komunikaty o błędach muszą być widoczne dla użytkownika i muszą być zlokalizowane w jego bieżącym języku. Android Auto i system operacyjny Android Automotive mogą wyświetlić użytkownikowi komunikat o błędzie.

Jeśli na przykład treść nie jest dostępna w regionie użytkownika, możesz podczas ustawiania tego komunikatu użyć kodu błędu ERROR_CODE_NOT_AVAILABLE_IN_REGION.

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łędu znajdziesz w artykule Korzystanie z sesji multimediów: stany i błędy.

Jeśli użytkownik Androida Auto musi otworzyć aplikację na telefon, aby naprawić błąd, podaj mu te informacje w wiadomości. Komunikat o błędzie może np. brzmieć „Zaloguj się w aplikacji [nazwa aplikacji]”, a nie „Zaloguj się”.

Inne materiały