Membuat aplikasi media untuk mobil

Android Auto dan Android Automotive OS membantu Anda menghadirkan konten aplikasi media kepada pengguna di mobil mereka. Aplikasi media untuk mobil harus menyediakan layanan browser media sehingga Android Auto dan Android Automotive OS, atau aplikasi lain dengan browser media, dapat menemukan dan menampilkan konten Anda.

Panduan ini mengasumsikan bahwa Anda sudah memiliki aplikasi media yang memutar audio di ponsel, dan aplikasi media Anda sesuai dengan arsitektur aplikasi media Android.

Panduan ini menjelaskan komponen wajib MediaBrowserService dan MediaSession yang diperlukan aplikasi Anda agar berfungsi di Android Auto atau Android Automotive OS. Setelah menyelesaikan infrastruktur media inti, Anda dapat menambahkan dukungan untuk Android Auto dan menambahkan dukungan untuk Android Automotive OS ke aplikasi media Anda.

Sebelum memulai

  1. Pelajari Dokumentasi API media Android.
  2. Pelajari Membuat aplikasi media untuk panduan desain.
  3. Pelajari istilah dan konsep utama yang tercantum dalam bagian ini.

Istilah dan konsep utama

MediaBrowserService
Sebuah layanan Android yang diimplementasikan oleh aplikasi media Anda yang mematuhi MediaBrowserServiceCompat API. Aplikasi Anda menggunakan layanan ini untuk mengekspos kontennya.
Browser media
Sebuah API yang digunakan oleh aplikasi media untuk menemukan layanan browser media dan menampilkan kontennya. Android Auto dan Android Automotive OS menggunakan browser media untuk menemukan layanan browser media aplikasi Anda.
Item media

Browser media menyusun kontennya dalam hierarki objek MediaItem. Item media dapat memiliki salah satu atau kedua tanda berikut:

  • FLAG_PLAYABLE: menunjukkan bahwa item adalah sebuah daun pada hierarki konten Item tersebut merepresentasikan satu streaming suara, seperti lagu pada album, bab pada buku audio, atau episode pada podcast.
  • FLAG_BROWSABLE: menunjukkan bahwa item adalah node pada hierarki konten dan memiliki turunan. Misalnya, item tersebut merepresentasikan suatu album dan turunannya adalah lagu pada album tersebut.

Suatu item media yang dapat dijelajahi dan diputar akan berperilaku seperti playlist. Anda dapat memilih item itu sendiri untuk memutar semua turunannya, atau menjelajahi turunannya.

Dioptimalkan untuk kendaraan

Suatu aktivitas untuk aplikasi Android Automotive OS yang mematuhi Pedoman desain Android Automotive OS. Antarmuka untuk aktivitas ini tidak dibuat oleh Android Automotive OS, sehingga Anda harus memastikan bahwa aplikasi Anda mematuhi pedoman desain tersebut. Biasanya, antarmuka ini memiliki target ketuk dan ukuran font yang lebih besar, dukungan untuk mode siang dan malam, dan rasio kontras yang lebih tinggi.

Antarmuka pengguna yang dioptimalkan untuk kendaraan hanya boleh ditampilkan jika Car User Experience Restrictions (CUXR) tidak diberlakukan, karena antarmuka ini bisa saja mengharuskan perhatian atau interaksi yang lama dari pengguna. CUXR tidak diberlakukan saat mobil berhenti atau diparkir, tetapi selalu diberlakukan saat mobil bergerak.

Anda tidak perlu mendesain aktivitas untuk Android Auto, karena Android Auto menggambar sendiri antarmuka yang dioptimalkan untuk kendaraan menggunakan informasi dari layanan browser media.

Mengonfigurasi file manifes aplikasi Anda

Sebelum membuat layanan browser media, Anda harus mengonfigurasi file manifes aplikasi Anda.

Mendeklarasikan layanan browser media Anda

Android Auto dan Android Automotive OS terhubung ke aplikasi Anda melalui layanan browser media agar dapat menjelajahi item media. Deklarasikan layanan browser media Anda dalam manifes untuk memungkinkan Android Auto dan Android Automotive OS menemukan layanan dan terhubung ke aplikasi Anda.

Cuplikan kode berikut menunjukkan cara mendeklarasikan layanan browser media dalam manifes Anda. Anda harus memasukkan kode ini ke dalam file manifes untuk modul Android Automotive OS dan di dalam file manifes untuk aplikasi ponsel Anda.

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

Menentukan ikon aplikasi

Anda perlu menentukan ikon aplikasi yang dapat digunakan Android Auto dan Android Automotive OS untuk merepresentasikan aplikasi Anda di UI sistem. Dua jenis ikon diperlukan:

  • Ikon peluncur
  • Ikon atribusi

Ikon peluncur

Ikon peluncur merepresentasikan aplikasi Anda di UI sistem, seperti pada peluncur dan baki ikon. Anda dapat menentukan apakah akan menggunakan ikon dari aplikasi seluler untuk merepresentasikan aplikasi media mobil menggunakan deklarasi manifes berikut:

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

Untuk menggunakan ikon yang berbeda dengan aplikasi seluler, tetapkan properti android:icon pada elemen <service> layanan browser media dalam manifes:

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

Ikon atribusi

Gambar 1. Ikon atribusi di kartu media.

Ikon atribusi digunakan di tempat konten media diprioritaskan, seperti pada kartu media. Pertimbangkan untuk menggunakan kembali ikon kecil yang digunakan untuk notifikasi. Ikon ini harus monokrom. Anda dapat menentukan ikon yang digunakan untuk merepresentasikan aplikasi Anda menggunakan deklarasi manifes berikut:

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

Membuat layanan browser media

Buatlah layanan browser media dengan memperluas class MediaBrowserServiceCompat. Selanjutnya, Android Auto dan Android Automotive OS dapat menggunakan layanan Anda untuk melakukan hal berikut:

  • Menjelajahi hierarki konten aplikasi Anda untuk menampilkan menu kepada pengguna.
  • Mendapatkan token untuk objek MediaSessionCompat aplikasi Anda guna mengontrol pemutaran audio.

Anda juga dapat menggunakan layanan browser media untuk mengizinkan klien lain mengakses konten media dari aplikasi Anda. Klien media ini mungkin merupakan aplikasi lain di ponsel pengguna, atau dapat berupa klien jarak jauh lainnya.

Alur kerja layanan browser media

Bagian ini menjelaskan cara Android Automotive OS dan Android Auto berinteraksi dengan layanan browser media Anda selama alur kerja pengguna standar.

  1. Pengguna meluncurkan aplikasi Anda di Android Automotive OS atau Android Auto.
  2. Android Automotive OS atau Android Auto menghubungi layanan browser media aplikasi Anda menggunakan metode onCreate(). Di dalam implementasi metode onCreate(), Anda harus membuat dan mendaftarkan objek MediaSessionCompat beserta objek callback-nya.
  3. Android Automotive OS atau Android Auto memanggil metode onGetRoot() layanan Anda untuk mendapatkan item media root dalam hierarki konten Anda. Item media root ini tidak ditampilkan; sebagai gantinya, item ini digunakan untuk mengambil lebih banyak konten dari aplikasi Anda.
  4. Android Automotive OS atau Android Auto memanggil metode onLoadChildren() layanan Anda untuk mendapatkan turunan item media root. Android Automotive OS dan Android Auto menampilkan item media ini sebagai item konten level atas. Lihat Membuat struktur menu root di halaman ini untuk informasi selengkapnya tentang apa yang diharapkan sistem di tingkat ini.
  5. Jika pengguna memilih item media yang dapat dijelajahi, metode onLoadChildren() layanan Anda akan dipanggil lagi untuk mengambil turunan item menu yang dipilih.
  6. Jika pengguna memilih item media yang dapat diputar, Android Automotive OS atau Android Auto akan memanggil metode callback sesi media yang sesuai untuk menjalankan tindakan tersebut.
  7. Jika didukung oleh aplikasi Anda, pengguna juga dapat menelusuri konten Anda. Dalam hal ini, Android Automotive OS atau Android Auto akan memanggil metode onSearch() layanan Anda.

Membuat hierarki konten

Android Auto dan Android Automotive OS memanggil layanan browser media aplikasi Anda untuk mencari tahu konten apa saja yang tersedia. Anda harus mengimplementasikan dua metode di layanan browser media Anda untuk mendukung tindakan ini: onGetRoot() dan onLoadChildren().

Mengimplementasikan onGetRoot

Metode onGetRoot() layanan Anda akan menampilkan informasi tentang node root hierarki konten Anda. Android Auto dan Android Automotive OS menggunakan node root ini untuk meminta konten Anda lainnya menggunakan metode onLoadChildren().

Cuplikan kode berikut menunjukkan implementasi sederhana metode 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);
}

Untuk contoh selengkapnya dari metode ini, lihat metode onGetRoot() pada aplikasi contoh Universal Android Music Player di GitHub.

Menambahkan validasi paket untuk onGetRoot()

Saat panggilan dilakukan ke metode onGetRoot() layanan Anda, paket panggilan ini meneruskan informasi pengidentifikasi ke layanan Anda. Layanan Anda dapat menggunakan informasi ini untuk menentukan apakah paket itu dapat mengakses konten Anda atau tidak. Misalnya, Anda dapat membatasi akses ke konten aplikasi ke daftar paket yang disetujui dengan membandingkan clientPackageName dengan daftar yang diizinkan dan memverifikasi sertifikat yang digunakan untuk menandatangani APK paket. Jika paket tidak dapat diverifikasi, tampilkan null untuk menolak akses ke konten Anda.

Agar aplikasi sistem, seperti Android Auto dan Android Automotive OS, dapat mengakses konten Anda, layanan Anda harus selalu menampilkan BrowserRoot non-null saat aplikasi sistem tersebut memanggil metode onGetRoot(). Tanda tangan aplikasi sistem Android Automotive OS dapat bervariasi bergantung pada merek dan model mobil, jadi Anda perlu mengizinkan koneksi dari semua aplikasi sistem untuk mendukung Android Automotive OS dengan kuat.

Cuplikan kode berikut menunjukkan bagaimana layanan Anda dapat memvalidasi bahwa paket panggilan adalah aplikasi sistem:

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
}

Cuplikan kode ini adalah nukilan dari class PackageValidator di aplikasi contoh Universal Android Music Player di GitHub. Pelajari class tersebut untuk melihat contoh selengkapnya tentang cara mengimplementasikan validasi paket untuk metode onGetRoot() layanan Anda.

Selain mengizinkan aplikasi sistem, Anda harus mengizinkan Asisten Google terhubung ke MediaBrowserService. Perhatikan bahwa Asisten Google memiliki nama paket terpisah untuk ponsel, yang mencakup Android Auto, dan untuk Android Automotive OS.

Mengimplementasikan onLoadChildren()

Setelah menerima objek node root Anda, Android Auto dan Android Automotive OS akan membuat menu level atas dengan memanggil onLoadChildren() pada objek node root untuk memperoleh turunannya. Aplikasi klien membuat submenu dengan memanggil metode yang sama ini menggunakan objek node turunan.

Setiap node dalam hierarki konten Anda direpresentasikan oleh objek MediaBrowserCompat.MediaItem. Masing-masing item media ini diidentifikasi oleh string ID unik. Aplikasi klien memperlakukan string ID ini sebagai token buram. Saat ingin menjelajah ke sebuah submenu, atau memutar item media, aplikasi klien akan meneruskan token ini. Aplikasi Anda harus mengaitkan token tersebut dengan item media yang sesuai.

Cuplikan kode berikut menunjukkan implementasi sederhana metode 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);
}

Untuk contoh lengkap metode ini, lihat metode onLoadChildren() pada aplikasi contoh Universal Android Music Player di GitHub.

Membuat struktur menu root

Gambar 2. Konten root yang ditampilkan sebagai tab navigasi.

Android Auto dan Android Automotive OS memiliki batasan khusus terkait struktur menu root. Hal ini dikomunikasikan ke MediaBrowserService melalui petunjuk root, yang dapat dibaca melalui argumen Bundle yang diteruskan ke onGetRoot(). Dengan mengikuti petunjuk ini, sistem dapat menampilkan konten root secara optimal sebagai tab navigasi. Jika Anda tidak mengikuti petunjuk ini, beberapa konten root mungkin dihapus atau dibuat kurang mudah ditemukan oleh sistem. Dua petunjuk dikirim:

Gunakan kode berikut untuk membaca petunjuk root yang relevan:

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

Anda dapat memilih untuk mencabangkan logika struktur hierarki konten berdasarkan nilai petunjuk ini, terutama jika hierarki Anda bervariasi di antara integrasi MediaBrowser di luar Android Auto dan Android Automotive OS. Misalnya, jika Anda biasanya menampilkan item root yang dapat diputar, sebaiknya Anda menyarangkannya dalam item root yang dapat dijelajahi, karena nilai dari petunjuk tanda yang didukung.

Selain petunjuk root, ada beberapa panduan tambahan yang harus diikuti untuk memastikan bahwa tab dirender secara optimal:

  • Berikan ikon monokrom, sebaiknya putih, untuk setiap item tab.
  • Sediakan label yang singkat namun bermakna untuk setiap item tab. Menjaga label tetap pendek akan mengurangi peluang string dipangkas.

Menampilkan karya seni media

Karya seni untuk item media harus diteruskan sebagai URI lokal menggunakan ContentResolver.SCHEME_CONTENT atau ContentResolver.SCHEME_ANDROID_RESOURCE. URI lokal ini harus me-resolve bitmap atau vektor drawable di resource aplikasi. Untuk objek MediaDescriptionCompat yang mewakili item dalam hierarki konten, teruskan URI melalui setIconUri(). Untuk objek MediaMetadataCompat yang merepresentasikan item yang sedang diputar, teruskan URI melalui putString(), menggunakan salah satu kunci berikut:

Langkah-langkah berikut menjelaskan cara mendownload art dari URI web dan menampilkannya melalui URI lokal. Untuk contoh yang lebih lengkap, lihat implementasi openFile() dan metode di sekitarnya di aplikasi contoh Universal Android Music Player.

  1. Buat URI content:// yang sesuai dengan URI web. Layanan browser media dan sesi media harus meneruskan URI konten ini ke Android Auto dan Android Automotive OS.

    Kotlin

    fun Uri.asAlbumArtContentURI(): Uri {
      return Uri.Builder()
        .scheme(ContentResolver.SCHEME_CONTENT)
        .authority(CONTENT_PROVIDER_AUTHORITY)
        .appendPath(this.getPath()) // Make sure you trust the URI
        .build()
    }

    Java

    public static Uri asAlbumArtContentURI(Uri webUri) {
      return new Uri.Builder()
        .scheme(ContentResolver.SCHEME_CONTENT)
        .authority(CONTENT_PROVIDER_AUTHORITY)
        .appendPath(webUri.getPath()) // Make sure you trust the URI!
        .build();
    }
  2. Dalam implementasi ContentProvider.openFile(), periksa apakah file untuk URI yang sesuai telah tersedia. Jika tidak, download dan cache file gambar. Cuplikan kode berikut menggunakan 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);
    }

Untuk detail selengkapnya tentang penyedia konten, lihat Membuat penyedia konten.

Menerapkan gaya konten

Setelah mem-build hierarki konten menggunakan item yang dapat dijelajahi atau diputar, Anda dapat menerapkan gaya konten yang menentukan cara item tersebut ditampilkan di mobil.

Anda dapat menggunakan gaya konten berikut:

Item daftar

Gaya konten ini lebih memprioritaskan judul dan metadata daripada gambar.

Item petak

Gaya konten ini lebih memprioritaskan gambar daripada judul dan metadata.

Menetapkan gaya konten default

Anda dapat menetapkan gaya default global untuk mengatur cara menampilkan item media dengan menyertakan konstanta tertentu dalam paket tambahan BrowserRoot dari metode onGetRoot() layanan Anda. Android Auto dan Android Automotive OS membaca paket ini dan mencari konstanta tersebut untuk menentukan gaya yang sesuai.

Tambahan berikut dapat digunakan sebagai kunci dalam paket:

Kunci dapat memetakan ke nilai konstanta integer berikut untuk memengaruhi presentasi item tersebut:

Cuplikan kode berikut menunjukkan cara menetapkan gaya konten default untuk item yang dapat dijelajahi ke petak, dan item yang dapat diputar ke daftar:

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

Menetapkan gaya konten per item

Dengan Content Style API, Anda dapat mengganti gaya konten default untuk turunan item media yang dapat dijelajahi, serta item media itu sendiri.

Untuk mengganti default turunan item media yang dapat dijelajahi, buat paket tambahan di MediaDescription item media dan tambahkan petunjuk yang sama seperti yang disebutkan sebelumnya. DESCRIPTION_EXTRAS_KEY_CONTENT_STYLE_PLAYABLE berlaku untuk turunan item yang dapat diputar, sedangkan DESCRIPTION_EXTRAS_KEY_CONTENT_STYLE_BROWSABLE berlaku untuk turunan item yang dapat dijelajahi.

Untuk mengganti gaya default item media tertentu itu sendiri (bukan turunannya), buat paket tambahan di MediaDescription item media dan tambahkan petunjuk dengan kunci DESCRIPTION_EXTRAS_KEY_CONTENT_STYLE_SINGLE_ITEM. Gunakan nilai yang sama seperti yang dijelaskan di atas untuk menentukan presentasi item tersebut.

Cuplikan kode berikut menunjukkan cara membuat MediaItem yang dapat dijelajahi dan akan mengganti gaya konten default untuk diri sendiri dan turunannya. Item media ini menata gayanya sendiri sebagai item daftar kategori, turunannya yang dapat dijelajahi sebagai item daftar, dan turunannya yang dapat diputar sebagai item petak:

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

Mengelompokkan item menggunakan petunjuk judul

Untuk mengelompokkan item media terkait, gunakan petunjuk per item. Setiap item media dalam grup harus mendeklarasikan paket tambahan dalam MediaDescription yang menyertakan pemetaan dengan DESCRIPTION_EXTRAS_KEY_CONTENT_STYLE_GROUP_TITLE kunci dan nilai string yang identik. Lokalkan string ini, yang digunakan sebagai judul grup.

Cuplikan kode berikut menunjukkan cara membuat MediaItem dengan judul subgrup "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*/);
}

Aplikasi Anda harus meneruskan semua item media yang ingin dikelompokkan bersama sebagai blok yang berdekatan. Misalnya, anggaplah Anda ingin menampilkan dua grup item media, "Songs" dan "Albums", dalam urutan tersebut, dan aplikasi Anda meneruskan lima item media dengan urutan sebagai berikut:

  1. Item media A dengan extras.putString(MediaConstants.DESCRIPTION_EXTRAS_KEY_CONTENT_STYLE_GROUP_TITLE, "Songs")
  2. Item media B dengan extras.putString(MediaConstants.DESCRIPTION_EXTRAS_KEY_CONTENT_STYLE_GROUP_TITLE, "Albums")
  3. Item media C dengan extras.putString(MediaConstants.DESCRIPTION_EXTRAS_KEY_CONTENT_STYLE_GROUP_TITLE, "Songs")
  4. Item media D dengan extras.putString(MediaConstants.DESCRIPTION_EXTRAS_KEY_CONTENT_STYLE_GROUP_TITLE, "Songs")
  5. Item media E dengan extras.putString(MediaConstants.DESCRIPTION_EXTRAS_KEY_CONTENT_STYLE_GROUP_TITLE, "Albums")

Karena item media untuk grup "Songs" dan grup "Albums" tidak disimpan bersamaan dalam blok yang berdekatan, Android Auto dan Android Automotive OS akan menafsirkan ini sebagai empat grup berikut:

  • Grup 1 disebut "Songs" yang berisi item media A
  • Grup 2 disebut "Albums" yang berisi item media B
  • Grup 3 disebut "Songs" yang berisi item media C dan D
  • Grup 4 disebut "Albums" yang berisi item media E

Untuk menampilkan item ini dalam dua grup, aplikasi Anda harus meneruskan item media dalam urutan berikut:

  1. Item media A dengan extras.putString(MediaConstants.DESCRIPTION_EXTRAS_KEY_CONTENT_STYLE_GROUP_TITLE, "Songs")
  2. Item media C dengan extras.putString(MediaConstants.DESCRIPTION_EXTRAS_KEY_CONTENT_STYLE_GROUP_TITLE, "Songs")
  3. Item media D dengan extras.putString(MediaConstants.DESCRIPTION_EXTRAS_KEY_CONTENT_STYLE_GROUP_TITLE, "Songs")
  4. Item media B dengan extras.putString(MediaConstants.DESCRIPTION_EXTRAS_KEY_CONTENT_STYLE_GROUP_TITLE, "Albums")
  5. Item media E dengan extras.putString(MediaConstants.DESCRIPTION_EXTRAS_KEY_CONTENT_STYLE_GROUP_TITLE, "Albums")

Menampilkan indikator metadata tambahan

Anda dapat menyertakan indikator metadata tambahan untuk menambahkan informasi sekilas bagi konten dalam hierarki browser media dan selama pemutaran. Di dalam hierarki penjelajahan, Android Auto dan Android Automotive OS membaca tambahan yang terkait dengan item dan mencari konstanta tertentu untuk menentukan indikator mana yang akan ditampilkan. Selama media diputar, Android Auto dan Android Automotive OS membaca metadata untuk sesi media tersebut dan mencari konstanta tertentu untuk menentukan indikator yang akan ditampilkan.

Gambar 3. Tampilan pemutaran dengan metadata yang mengidentifikasi lagu dan artis, serta ikon yang menunjukkan konten eksplisit.

Gambar 4. Tampilan penjelajahan dengan titik untuk konten yang belum diputar pada item pertama, dan status progres untuk konten yang diputar sebagian pada item kedua.

Konstanta berikut dapat digunakan dalam kedua tambahan deskripsi MediaItem dan tambahan MediaMetadata:

Konstanta berikut hanya dapat digunakan dalam tambahan deskripsi MediaItem:

Agar indikator ditampilkan saat pengguna menjelajahi hierarki penjelajahan media, buat paket tambahan yang menyertakan satu atau beberapa konstanta tersebut dan teruskan paket itu ke metode MediaDescription.Builder.setExtras().

Cuplikan kode berikut menunjukkan cara menampilkan indikator untuk item media eksplisit yang sudah 70% selesai:

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

Jika ingin menampilkan indikator untuk item media yang sedang diputar, Anda dapat menyatakan nilai Long untuk METADATA_KEY_IS_EXPLICIT atau EXTRA_DOWNLOAD_STATUS dalam metode MediaMetadataCompat dari mediaSession Anda. Anda tidak dapat menampilkan indikator DESCRIPTION_EXTRAS_KEY_COMPLETION_STATUS atau DESCRIPTION_EXTRAS_KEY_COMPLETION_PERCENTAGE di tampilan pemutaran.

Cuplikan kode berikut mengilustrasikan cara menunjukkan bahwa lagu saat ini di tampilan pemutaran bersifat eksplisit dan telah didownload:

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

Memperbarui status progres di tampilan jelajah saat konten sedang diputar

Seperti yang telah dijelaskan sebelumnya, Anda dapat menggunakan tambahan DESCRIPTION_EXTRAS_KEY_COMPLETION_PERCENTAGE untuk menampilkan status progres dari konten yang diputar sebagian di tampilan jelajah. Namun, jika pengguna terus memutar konten yang diputar sebagian dari Android Auto atau Android Automotive OS, indikator tersebut menjadi tidak akurat seiring berjalannya waktu.

Agar Android Auto dan Android Automotive OS terus memperbarui status progres, Anda dapat memberikan informasi tambahan di MediaMetadataCompat dan PlaybackStateCompat untuk menautkan konten yang sedang berlangsung ke item media di tampilan penjelajahan. Persyaratan berikut harus dipenuhi agar item media memiliki status progres yang otomatis diperbarui:

Cuplikan kode berikut memperlihatkan cara menunjukkan bahwa item yang sedang diputar ditautkan ke item dalam tampilan jelajah:

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

Gambar 5. Tampilan pemutaran dengan opsi "Hasil penelusuran" untuk melihat item media yang terkait dengan penelusuran suara pengguna.

Aplikasi Anda dapat memberikan hasil penelusuran kontekstual yang ditampilkan kepada pengguna saat mereka memulai kueri penelusuran. Android Auto dan Android Automotive OS akan menampilkan hasil ini melalui antarmuka kueri penelusuran atau melalui kemampuan yang melakukan pivot pada kueri yang dibuat sebelumnya di sesi tersebut. Untuk mempelajari lebih lanjut, lihat bagian Mendukung voice action dalam panduan ini.

Untuk menampilkan hasil penelusuran yang dapat dijelajahi, Anda harus menyertakan kunci konstanta BROWSER_SERVICE_EXTRAS_KEY_SEARCH_SUPPORTED dalam paket tambahan metode onGetRoot() layanan, dan memetakan ke boolean true.

Cuplikan kode berikut menunjukkan cara mengaktifkan dukungan dalam metode 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);
}

Untuk mulai menyajikan hasil penelusuran, ganti metode onSearch() di layanan browser media Anda. Android Auto dan Android Automotive OS meneruskan istilah penelusuran pengguna ke metode ini setiap kali pengguna memanggil antarmuka kueri penelusuran atau kemampuan “Hasil penelusuran”.

Anda dapat mengatur hasil penelusuran dari metode onSearch() layanan Anda menggunakan item judul agar lebih mudah ditemukan. Misalnya, jika aplikasi Anda memutar musik, Anda dapat mengatur hasil penelusuran menurut album, artis, dan lagu.

Cuplikan kode berikut menunjukkan implementasi sederhana metode 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.
}

Tindakan Jelajah Kustom

Satu tindakan jelajah kustom.

Gambar 6. Satu Tindakan Jelajah Kustom

Tindakan Jelajah Kustom memungkinkan Anda menambahkan ikon dan label kustom ke objek MediaItem aplikasi di aplikasi media mobil, dan menangani interaksi pengguna dengan tindakan ini. Hal ini memungkinkan Anda memperluas fungsi Aplikasi Media dengan berbagai cara, seperti menambahkan tindakan "Download", "Tambahkan ke Antrean", "Putar Radio", "Favorit", atau "Hapus".

Menu tambahan tindakan jelajah kustom.

Gambar 7. Overflow Tindakan Jelajah Kustom

Jika ada lebih banyak tindakan kustom daripada yang diizinkan OEM untuk ditampilkan, menu tambahan akan ditampilkan kepada pengguna.

Bagaimana cara kerjanya?

Setiap Tindakan Jelajah Kustom ditentukan dengan:

  • ID Action (ID string unik)
  • Label Tindakan (teks yang ditampilkan kepada pengguna)
  • URI Ikon Tindakan (drawable vektor yang dapat diberi tint)

Anda menentukan daftar Tindakan Penjelajahan Kustom secara global sebagai bagian dari BrowseRoot. Kemudian, Anda dapat melampirkan subkumpulan tindakan ini ke setiap MediaItem.

Saat pengguna berinteraksi dengan Action Jelajah Kustom, aplikasi Anda akan menerima callback di onCustomAction(). Kemudian, Anda dapat menangani tindakan dan memperbarui daftar tindakan untuk MediaItem jika perlu. Hal ini berguna untuk tindakan stateful seperti "Favorit" dan "Download". Untuk tindakan yang tidak perlu diperbarui, seperti "Putar Radio", Anda tidak perlu memperbarui daftar tindakan.

Tindakan penjelajahan kustom di root node penjelajahan.

Gambar 8. Toolbar Tindakan Jelajah Kustom

Anda juga dapat melampirkan Tindakan Penjelajahan Kustom ke root node penjelajahan. Tindakan ini akan ditampilkan di toolbar sekunder di bawah toolbar utama.

Cara menerapkan Tindakan Penjelajahan Kustom

Berikut adalah langkah-langkah untuk menambahkan Tindakan Penjelajahan Kustom ke project Anda:

  1. Ganti dua metode dalam implementasi MediaBrowserServiceCompat:
  2. Mengurai batas tindakan saat runtime:
  3. Buat daftar global Tindakan Penjelajahan Kustom:
    • Untuk setiap tindakan, buat objek Bundle dengan kunci berikut: * EXTRAS_KEY_CUSTOM_BROWSER_ACTION_ID: ID tindakan * EXTRAS_KEY_CUSTOM_BROWSER_ACTION_LABEL: Label tindakan * EXTRAS_KEY_CUSTOM_BROWSER_ACTION_ICON_URI: URI ikon tindakan * Tambahkan semua objek Bundle tindakan ke daftar.
  4. Tambahkan daftar global ke BrowseRoot:
  5. Tambahkan tindakan ke objek MediaItem:
    • Anda dapat menambahkan tindakan ke setiap objek MediaItem dengan menyertakan daftar ID tindakan di tambahan MediaDescriptionCompat menggunakan kunci DESCRIPTION_EXTRAS_KEY_CUSTOM_BROWSER_ACTION_ID_LIST. Daftar ini harus merupakan subkumpulan dari daftar tindakan global yang Anda tentukan di BrowseRoot.
  6. Menangani tindakan dan menampilkan progres atau hasil:

Berikut beberapa perubahan yang dapat Anda lakukan di BrowserServiceCompat untuk memulai Tindakan Jelajah Kustom.

Mengganti BrowserServiceCompat

Anda perlu mengganti metode berikut di MediaBrowserServiceCompat.

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

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

Batas tindakan penguraian

Anda harus memeriksa jumlah Tindakan Penjelajahan Kustom yang didukung.

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

Membuat Tindakan Jelajah Kustom

Setiap tindakan harus dikemas ke dalam Bundle terpisah.

  • ID tindakan
    bundle.putString(MediaConstants.EXTRAS_KEY_CUSTOM_BROWSER_ACTION_ID,
                    "<ACTION_ID>")
  • Label Tindakan
    bundle.putString(MediaConstants.EXTRAS_KEY_CUSTOM_BROWSER_ACTION_LABEL,
                    "<ACTION_LABEL>")
  • URI Ikon Tindakan
    bundle.putString(MediaConstants.EXTRAS_KEY_CUSTOM_BROWSER_ACTION_ICON_URI,
                    "<ACTION_ICON_URI>")

Menambahkan Tindakan Jelajah Kustom ke Parceable ArrayList

Tambahkan semua objek Bundle Tindakan Penjelajahan Kustom ke 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;
}

Menambahkan daftar Tindakan Penjelajahan Kustom ke root penjelajahan

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

Menambahkan tindakan ke 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);

Hasil build onCustomAction

  • Mengurai mediaId dari 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);
    }
  • Untuk hasil asinkron, lepaskan hasil. result.detach()
  • Paket hasil build
    • Pesan untuk pengguna
      mResultBundle.putString(EXTRAS_KEY_CUSTOM_BROWSER_ACTION_RESULT_MESSAGE,
                mContext.getString(stringRes))
    • Memperbarui item(gunakan untuk memperbarui tindakan dalam item)
      mResultBundle.putString(EXTRAS_KEY_CUSTOM_BROWSER_ACTION_RESULT_REFRESH_ITEM, mediaId);
    • Membuka tampilan Pemutaran
      //Shows user the PBV without changing the playback state
      mResultBundle.putString(EXTRAS_KEY_CUSTOM_BROWSER_ACTION_RESULT_SHOW_PLAYING_ITEM, null);
    • Mengupdate Node Jelajah
      //Change current browse node to mediaId
      mResultBundle.putString(EXTRAS_KEY_CUSTOM_BROWSER_ACTION_RESULT_BROWSE_NODE, mediaId);
  • Jika terjadi error, panggil result.sendError(resultBundle).
  • Jika progres diperbarui, panggil result.sendProgressUpdate(resultBundle).
  • Selesaikan dengan memanggil result.sendResult(resultBundle).

Memperbarui Status Tindakan

Dengan menggunakan metode result.sendProgressUpdate(resultBundle) dengan kunci EXTRAS_KEY_CUSTOM_BROWSER_ACTION_RESULT_REFRESH_ITEM, Anda dapat memperbarui MediaItem untuk mencerminkan status tindakan yang baru. Hal ini memungkinkan Anda memberikan masukan secara real time kepada pengguna tentang progres dan hasil tindakan mereka.

Contoh: Tindakan Download

Berikut adalah contoh cara menggunakan fitur ini untuk menerapkan tindakan download dengan tiga status:

  1. Download: Ini adalah status awal tindakan. Saat pengguna memilih tindakan ini, Anda dapat menukarnya dengan "Mendownload" dan memanggil sendProgressUpdate untuk memperbarui UI.
  2. Mendownload: Status ini menunjukkan bahwa download sedang berlangsung. Anda dapat menggunakan status ini untuk menampilkan status progres atau indikator lain kepada pengguna.
  3. Didownload: Status ini menunjukkan bahwa download telah selesai. Saat download selesai, Anda dapat menukar "Downloading" dengan "Downloaded" dan memanggil sendResult dengan kunci EXTRAS_KEY_CUSTOM_BROWSER_ACTION_RESULT_REFRESH_ITEM untuk menunjukkan bahwa item harus dimuat ulang. Selain itu, Anda dapat menggunakan kunci EXTRAS_KEY_CUSTOM_BROWSER_ACTION_RESULT_MESSAGE untuk menampilkan pesan berhasil kepada pengguna.

Pendekatan ini memungkinkan Anda memberikan masukan yang jelas kepada pengguna tentang proses download dan statusnya saat ini. Anda dapat menambahkan detail lebih lanjut dengan ikon untuk menampilkan status download 25%, 50%, 75%.

Contoh: Tindakan Favorit

Contoh lainnya adalah tindakan favorit dengan dua status:

  1. Favorit: Tindakan ini ditampilkan untuk item yang tidak ada dalam daftar favorit pengguna. Saat pengguna memilih tindakan ini, Anda dapat menukarnya dengan "Difavoritkan" dan memanggil sendResult dengan kunci EXTRAS_KEY_CUSTOM_BROWSER_ACTION_RESULT_REFRESH_ITEM untuk memperbarui UI.
  2. Favorit: Tindakan ini ditampilkan untuk item yang ada dalam daftar favorit pengguna. Saat pengguna memilih tindakan ini, Anda dapat menukarnya dengan "Favorit" dan memanggil sendResult dengan kunci EXTRAS_KEY_CUSTOM_BROWSER_ACTION_RESULT_REFRESH_ITEM untuk memperbarui UI.

Pendekatan ini memberikan cara yang jelas dan konsisten bagi pengguna untuk mengelola item favorit mereka.

Contoh ini menunjukkan fleksibilitas Tindakan Jelajah Kustom dan cara Anda dapat menggunakannya untuk menerapkan berbagai fungsi dengan masukan real-time untuk pengalaman pengguna yang ditingkatkan di aplikasi media mobil.

Untuk contoh implementasi lengkap fitur ini, Anda dapat melihat project TestMediaApp.

Mengaktifkan kontrol pemutaran

Android Auto dan Android Automotive OS mengirimkan perintah kontrol pemutaran melalui MediaSessionCompat layanan Anda. Anda harus mendaftarkan sesi dan menerapkan metode callback yang terkait.

Mendaftarkan sesi media

Dalam metode onCreate() layanan browser media Anda, buat MediaSessionCompat, lalu daftarkan sesi media tersebut dengan memanggil setSessionToken().

Cuplikan kode berikut menunjukkan cara membuat dan mendaftarkan sesi media:

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

Saat Anda membuat objek sesi media, tetapkan objek callback yang digunakan untuk menangani permintaan kontrol pemutaran. Objek ini dibuat dengan memberikan implementasi class MediaSessionCompat.Callback untuk aplikasi Anda. Bagian selanjutnya membahas cara mengimplementasikan objek ini.

Mengimplementasikan perintah putar

Saat pengguna meminta pemutaran untuk item media dari aplikasi Anda, Android Automotive OS dan Android Auto menggunakan class MediaSessionCompat.Callback dari objek MediaSessionCompat aplikasi Anda yang diperoleh dari layanan browser media aplikasi Anda. Saat pengguna ingin mengontrol pemutaran konten, seperti menjeda pemutaran atau melewati ke lagu berikutnya, Android Auto dan Android Automotive OS akan memanggil salah satu metode objek callback.

Untuk menangani pemutaran konten, aplikasi Anda harus memperluas class MediaSessionCompat.Callback abstrak dan mengimplementasikan metode yang didukung aplikasi Anda.

Implementasikan semua metode callback berikut yang sesuai untuk jenis konten yang ditawarkan aplikasi Anda:

onPrepare()
Dipanggil saat sumber media berubah. Android Automotive OS juga memanggil metode ini segera setelah booting. Aplikasi media Anda harus mengimplementasikan metode ini.
onPlay()
Dipanggil jika pengguna memilih untuk memutar item media tanpa memilih item tertentu. Aplikasi Anda harus memutar konten defaultnya atau, jika pemutaran dijeda dengan onPause(), aplikasi Anda akan melanjutkan pemutaran.

Catatan: Aplikasi Anda tidak boleh otomatis mulai memutar musik saat Android Automotive OS atau Android Auto terhubung ke layanan browser media Anda. Untuk mengetahui informasi selengkapnya, lihat bagian lengkap tentang menyetel status playback awal.

onPlayFromMediaId()
Dipanggil saat pengguna memilih untuk memutar item tertentu. Metode ini diteruskan ID yang ditetapkan layanan browser media Anda ke item media tersebut dalam hierarki konten Anda.
onPlayFromSearch()
Dipanggil saat pengguna memilih untuk memutar item media dari kueri penelusuran. Aplikasi harus membuat pilihan yang tepat berdasarkan string penelusuran yang diteruskan.
onPause()
Dipanggil saat pengguna memilih untuk menjeda pemutaran.
onSkipToNext()
Dipanggil saat pengguna memilih untuk melewati ke item berikutnya.
onSkipToPrevious()
Dipanggil saat pengguna memilih untuk melewati ke item sebelumnya.
onStop()
Dipanggil saat pengguna memilih untuk menghentikan pemutaran.

Ganti metode ini di aplikasi Anda untuk memberikan fungsi yang diinginkan. Anda tidak perlu mengimplementasikan metode jika fungsinya tidak didukung oleh aplikasi Anda. Misalnya, jika aplikasi Anda memutar live stream, seperti siaran olahraga, Anda tidak perlu mengimplementasikan metode onSkipToNext(). Anda dapat menggunakan implementasi default onSkipToNext().

Aplikasi Anda tidak memerlukan logika khusus untuk memutar konten melalui speaker mobil. Saat menerima permintaan untuk memutar konten, aplikasi Anda dapat memutar audio dengan cara yang sama seperti memutar konten melalui speaker ponsel atau headphone pengguna. Android Auto dan Android Automotive OS akan otomatis mengirim konten audio ke sistem mobil untuk diputar melalui speaker mobil.

Untuk mengetahui informasi selengkapnya tentang memutar konten audio, lihat Ringkasan MediaPlayer, Ringkasan aplikasi audio, dan ringkasan ExoPlayer Google.

Menetapkan tindakan pemutaran standar

Android Auto dan Android Automotive OS menampilkan kontrol pemutaran berdasarkan tindakan yang diaktifkan dalam objek PlaybackStateCompat.

Secara default, aplikasi Anda harus mendukung tindakan berikut:

Aplikasi Anda mungkin juga mendukung tindakan berikut jika relevan dengan konten aplikasi:

Selain itu, Anda memiliki opsi untuk membuat antrean pemutaran yang dapat ditampilkan kepada pengguna, tetapi tidak wajib. Untuk melakukannya, panggil metode setQueue() dan setQueueTitle(), aktifkan ACTION_SKIP_TO_QUEUE_ITEM, dan tentukan onSkipToQueueItem() callback.

Selain itu, tambahkan dukungan untuk ikon Now Playing, yang merupakan indikator untuk hal yang sedang diputar. Untuk melakukannya, panggil metode setActiveQueueItemId() dan teruskan ID item yang sedang diputar dalam antrean. Anda harus memperbarui setActiveQueueItemId() setiap kali ada perubahan antrean.

Android Auto dan Android Automotive OS menampilkan tombol untuk setiap tindakan yang diaktifkan serta antrean pemutaran. Jika tombol diklik, sistem akan memanggil callback yang sesuai dari MediaSessionCompat.Callback.

Mencadangkan ruang yang tidak terpakai

Android Auto dan Android Automotive OS mencadangkan ruang pada UI untuk tindakan ACTION_SKIP_TO_PREVIOUS dan ACTION_SKIP_TO_NEXT. Jika aplikasi Anda tidak mendukung salah satu fungsi tersebut, Android Auto dan Android Automotive OS akan menggunakan ruang itu untuk menampilkan tindakan kustom apa pun yang Anda buat.

Jika tidak ingin mengisi ruang tersebut dengan tindakan kustom, Anda dapat mencadangkannya sehingga Android Auto dan Android Automotive OS mengosongkan ruang itu setiap kali aplikasi Anda tidak mendukung fungsi yang sesuai. Untuk melakukannya, panggil metode setExtras() beserta paket tambahan yang berisi konstanta yang terkait dengan fungsi yang dicadangkan. SESSION_EXTRAS_KEY_SLOT_RESERVATION_SKIP_TO_NEXT sesuai dengan ACTION_SKIP_TO_NEXT, dan SESSION_EXTRAS_KEY_SLOT_RESERVATION_SKIP_TO_PREV sesuai dengan ACTION_SKIP_TO_PREVIOUS. Gunakan konstanta ini sebagai kunci dalam paket, dan gunakan boolean true untuk nilainya.

Menetapkan PlaybackState awal

Saat Android Auto dan Android Automotive OS berkomunikasi dengan layanan browser media Anda, sesi media Anda mengomunikasikan status pemutaran konten menggunakan PlaybackStateCompat. Aplikasi Anda tidak boleh otomatis mulai memutar musik saat Android Automotive OS atau Android Auto terhubung ke layanan browser media. Sebagai gantinya, andalkan Android Auto dan Android Automotive OS untuk melanjutkan atau memulai pemutaran berdasarkan keadaan mobil atau tindakan pengguna.

Untuk melakukannya, tetapkan PlaybackStateCompat awal sesi media ke STATE_STOPPED, STATE_PAUSED, STATE_NONE, atau STATE_ERROR.

Sesi media dalam Android Auto dan Android Automotive OS hanya berlangsung selama durasi perjalanan, sehingga pengguna sering memulai dan menghentikan sesi ini. Untuk mempromosikan pengalaman yang lancar di antara drive, lacak status sesi pengguna sebelumnya, sehingga saat aplikasi media menerima permintaan melanjutkan, pengguna dapat secara otomatis melanjutkan dari posisi terakhir yang mereka tinggalkan. Misalnya, item media yang terakhir diputar, PlaybackStateCompat, dan antrean.

Menambahkan tindakan pemutaran kustom

Anda dapat menambahkan tindakan pemutaran kustom untuk menampilkan tindakan tambahan yang didukung aplikasi media Anda. Jika ruangnya memungkinkan (dan tidak dicadangkan), Android akan menambahkan tindakan kustom ke kontrol transport. Jika tidak, tindakan kustom akan ditampilkan di menu tambahan. Tindakan kustom ditampilkan sesuai urutan ditambahkan ke PlaybackStateCompat.

Gunakan tindakan kustom untuk menyediakan perilaku yang berbeda dengan tindakan standar. Jangan menggunakannya untuk mengganti atau menduplikasi tindakan standar.

Anda dapat menambahkan tindakan ini menggunakan metode addCustomAction() di class PlaybackStateCompat.Builder.

Cuplikan kode berikut menunjukkan cara menambahkan tindakan kustom "Mulai saluran radio":

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

Untuk contoh selengkapnya dari metode ini, lihat metode setCustomAction() pada aplikasi contoh Universal Android Music Player di GitHub.

Setelah membuat tindakan kustom, sesi media Anda dapat merespons tindakan tersebut dengan mengganti metode onCustomAction().

Cuplikan kode berikut menunjukkan cara aplikasi Anda merespons tindakan "Mulai saluran radio":

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

Untuk contoh selengkapnya dari metode ini, lihat metode onCustomAction pada aplikasi contoh Universal Android Music Player di GitHub.

Ikon untuk tindakan kustom

Setiap tindakan kustom yang Anda buat memerlukan ikon.

Jika deskripsi ikon tersebut cocok dengan salah satu konstanta CommandButton.ICON_, Anda harus menetapkan nilai bilangan bulat tersebut untuk kunci EXTRAS_KEY_COMMAND_BUTTON_ICON_COMPAT dari tambahan tindakan kustom. Pada sistem yang didukung, tindakan ini akan mengganti resource ikon yang diteruskan ke CustomAction.Builder, sehingga komponen sistem dapat merender tindakan dan tindakan pemutaran lainnya dalam gaya yang konsisten.

Anda juga harus menentukan resource ikon. Aplikasi untuk mobil dapat berjalan di berbagai ukuran dan kepadatan layar, sehingga ikon yang Anda sediakan haruslah berupa vector drawable. Vector drawable memungkinkan Anda mengubah skala aset tanpa kehilangan detailnya. Vector drawable juga memudahkan penyelarasan tepi dan sudut dengan batas piksel pada resolusi yang lebih kecil.

Jika tindakan kustom berstatus stateful (misalnya, tindakan ini mengaktifkan atau menonaktifkan setelan pemutaran), berikan ikon yang berbeda untuk status yang berbeda, sehingga pengguna dapat melihat perubahan secara visual saat mereka memilih tindakan.

Menyediakan gaya ikon alternatif untuk tindakan yang dinonaktifkan

Apabila tindakan kustom tidak tersedia untuk konteks yang sedang aktif, tukar ikon tindakan kustom dengan ikon alternatif yang menunjukkan bahwa tindakan dinonaktifkan.

Gambar 6. Contoh ikon tindakan kustom gaya tidak aktif.

Menunjukkan format audio

Untuk menunjukkan bahwa media yang sedang diputar menggunakan format audio khusus, Anda dapat menentukan ikon yang dirender di mobil yang mendukung fitur ini. Anda dapat menyetel KEY_CONTENT_FORMAT_TINTABLE_LARGE_ICON_URI dan KEY_CONTENT_FORMAT_TINTABLE_SMALL_ICON_URI dalam paket tambahan dari item media yang saat ini diputar (diteruskan ke MediaSession.setMetadata()). Pastikan untuk menetapkan kedua tambahan tersebut untuk mengakomodasi tata letak yang berbeda.

Selain itu, Anda dapat menyetel tambahan KEY_IMMERSIVE_AUDIO untuk memberi tahu OEM mobil bahwa ini adalah audio imersif, dan OEM harus sangat berhati-hati saat memutuskan apakah akan menerapkan efek audio yang mungkin mengganggu konten yang imersif.

Anda dapat mengonfigurasi item media yang sedang diputar agar subtitel, deskripsi, atau keduanya merupakan link ke item media lain. Hal tersebut memungkinkan pengguna dengan cepat menuju item terkait; misalnya, pengguna dapat melompat ke lagu lain dari artis yang sama, episode lain dari podcast tersebut, dll. Jika mobil mendukung fitur ini, pengguna dapat mengetuk link untuk menjelajahi konten tersebut.

Untuk menambahkan link, konfigurasi metadata KEY_SUBTITLE_LINK_MEDIA_ID (untuk menautkan dari subtitel) atau KEY_DESCRIPTION_LINK_MEDIA_ID (untuk menautkan dari deskripsi). Untuk mengetahui detailnya, baca dokumentasi referensi untuk kolom metadata tersebut.

Mendukung voice action

Aplikasi media Anda harus mendukung voice action untuk memberi pengemudi pengalaman yang aman dan nyaman serta minim gangguan. Misalnya, saat aplikasi Anda sedang memutar satu item media, pengguna dapat mengucapkan "Putar [judul lagu]" untuk meminta aplikasi tersebut memutar lagu lain tanpa melihat atau menyentuh layar mobil. Pengguna dapat memulai kueri dengan mengklik tombol yang sesuai pada setir atau mengucapkan frasa pengaktif "Ok Google".

Saat Android Auto atau Android Automotive OS mendeteksi dan menafsirkan voice action, voice action akan dikirimkan ke aplikasi melalui onPlayFromSearch(). Saat menerima callback ini, aplikasi akan menemukan konten yang cocok dengan string query dan memulai pemutaran.

Pengguna dapat menentukan kategori istilah yang berbeda dalam kuerinya, di antaranya genre, artis, album, nama lagu, stasiun radio, atau playlist. Saat membuat dukungan untuk penelusuran, pertimbangkan semua kategori yang sesuai untuk aplikasi Anda. Jika Android Auto atau Android Automotive OS mendeteksi bahwa kueri tertentu cocok dengan kategori tertentu, tindakan ini menambahkan tambahan di parameter extras. Tambahan berikut dapat dikirim:

Akun untuk string query kosong, yang dapat dikirim oleh Android Auto atau Android Automotive OS jika pengguna tidak menentukan istilah penelusuran. Misalnya, jika pengguna mengucapkan "Putar musik". Dalam hal ini, aplikasi Anda dapat memilih untuk memulai trek yang baru diputar atau baru disarankan.

Jika penelusuran tidak dapat diproses dengan cepat, jangan lakukan pemblokiran di onPlayFromSearch(). Sebagai gantinya, tetapkan status pemutaran ke STATE_CONNECTING dan lakukan penelusuran pada thread asinkron.

Setelah pemutaran dimulai, pertimbangkan untuk mengisi antrean sesi media dengan konten terkait. Misalnya, jika pengguna meminta agar album diputar, aplikasi Anda dapat mengisi antrean dengan daftar lagu album. Pertimbangkan juga untuk menerapkan dukungan untuk hasil penelusuran yang dapat dijelajahi agar pengguna dapat memilih jalur lain yang cocok dengan kueri mereka.

Selain kueri "putar", Android Auto dan Android Automotive OS mengenali kueri suara untuk mengontrol pemutaran seperti "jeda musik" dan "lagu berikutnya", dan mencocokkan perintah ini dengan callback sesi media yang sesuai, seperti onPause() dan onSkipToNext().

Untuk contoh selengkapnya cara mengimplementasikan tindakan pemutaran yang diaktifkan dengan suara di aplikasi Anda, lihat Asisten Google dan aplikasi media.

Mengimplementasikan pengamanan dari gangguan

Karena ponsel pengguna terhubung ke speaker mobil saat menggunakan Android Auto, Anda harus mengambil tindakan pencegahan tambahan untuk meminimalkan gangguan bagi pengemudi.

Menahan alarm di mobil

Aplikasi media Android Auto tidak boleh memulai pemutaran audio melalui speaker mobil kecuali jika pengguna memulai pemutaran dengan menekan tombol putar. Bahkan alarm yang dijadwalkan pengguna dari aplikasi media pun tidak boleh mulai memutar musik melalui speaker mobil.

Untuk memenuhi persyaratan ini, aplikasi Anda dapat menggunakan CarConnection sebagai sinyal sebelum memutar audio apa pun. Aplikasi Anda dapat memeriksa apakah ponsel memproyeksikan ke layar mobil dengan mengamati LiveData untuk jenis koneksi mobil dan memeriksa apakah sama dengan CONNECTION_TYPE_PROJECTION atau tidak.

Jika ponsel pengguna sedang memproyeksikan, aplikasi media yang mendukung alarm harus melakukan salah satu tindakan berikut:

  • Menonaktifkan alarm.
  • Memutar alarm melalui STREAM_ALARM, dan menyediakan UI pada layar ponsel untuk menonaktifkan alarm.

Menangani iklan media

Secara default, Android Auto menampilkan notifikasi saat metadata media berubah selama sesi pemutaran audio. Saat aplikasi media beralih dari memutar musik ke menjalankan iklan, akan sangat mengganggu (dan tidak perlu) jika notifikasi ditampilkan kepada pengguna. Untuk mencegah Android Auto menampilkan notifikasi dalam kasus ini, Anda harus menetapkan kunci metadata media METADATA_KEY_IS_ADVERTISEMENT ke METADATA_VALUE_ATTRIBUTE_PRESENT, seperti yang ditunjukkan dalam cuplikan kode berikut:

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

Menangani error umum

Saat aplikasi mengalami error, setel status pemutarannya ke STATE_ERROR dan tampilkan pesan error menggunakan metode setErrorMessage(). Lihat PlaybackStateCompat untuk mengetahui daftar kode error yang dapat Anda gunakan saat menyetel pesan error. Pesan error harus terlihat oleh pengguna dan dilokalkan sesuai bahasa pengguna saat ini. Selanjutnya Android Auto dan Android Automotive OS dapat menampilkan pesan error kepada pengguna.

Misalnya, jika konten tidak tersedia di wilayah pengguna saat ini, Anda dapat menggunakan kode error ERROR_CODE_NOT_AVAILABLE_IN_REGION saat menyetel pesan error.

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

Untuk mengetahui informasi selengkapnya tentang status error, lihat Menggunakan sesi media: Status dan error.

Jika pengguna Android Auto perlu membuka aplikasi ponsel Anda untuk menyelesaikan error, berikan informasi tersebut kepada pengguna dalam pesan Anda. Misalnya, pesan error Anda seharusnya terlihat "Login ke [nama aplikasi Anda]", bukan "Harap login".

Referensi lainnya