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
- Pelajari Dokumentasi API media Android.
- Pelajari Membuat aplikasi media untuk panduan desain.
- 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
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.
- Pengguna meluncurkan aplikasi Anda di Android Automotive OS atau Android Auto.
- Android Automotive OS atau Android Auto menghubungi layanan browser media aplikasi
Anda menggunakan metode
onCreate()
. Di dalam implementasi metodeonCreate()
, Anda harus membuat dan mendaftarkan objekMediaSessionCompat
beserta objek callback-nya. - 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. - 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. - Jika pengguna memilih item media yang dapat dijelajahi, metode
onLoadChildren()
layanan Anda akan dipanggil lagi untuk mengambil turunan item menu yang dipilih. - 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.
- 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
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:
- Batas jumlah turunan root: pada sebagian besar kasus, Anda dapat memperkirakan angka ini adalah empat. Artinya, lebih dari empat tab tidak dapat ditampilkan.
- Tanda yang didukung pada turunan root:
Anda dapat memperkirakan nilai ini adalah
MediaItem#FLAG_BROWSABLE
. Artinya, hanya item yang dapat dijelajahi, bukan item yang dapat diputar, yang dapat ditampilkan sebagai tab.
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:
MediaMetadataCompat.METADATA_KEY_DISPLAY_ICON_URI
MediaMetadataCompat.METADATA_KEY_ART_URI
MediaMetadataCompat.METADATA_KEY_ALBUM_ART_URI
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.
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(); }
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:
DESCRIPTION_EXTRAS_KEY_CONTENT_STYLE_BROWSABLE
: menunjukkan petunjuk presentasi untuk semua item yang dapat dijelajahi dalam hierarki penjelajahan.DESCRIPTION_EXTRAS_KEY_CONTENT_STYLE_PLAYABLE
: menunjukkan petunjuk presentasi untuk semua item yang dapat diputar dalam hierarki penjelajahan.
Kunci dapat memetakan ke nilai konstanta integer berikut untuk memengaruhi presentasi item tersebut:
DESCRIPTION_EXTRAS_VALUE_CONTENT_STYLE_LIST_ITEM
: item yang sesuai ditampilkan sebagai item daftar.DESCRIPTION_EXTRAS_VALUE_CONTENT_STYLE_GRID_ITEM
: item yang sesuai ditampilkan sebagai item petak.DESCRIPTION_EXTRAS_VALUE_CONTENT_STYLE_CATEGORY_LIST_ITEM
: item yang sesuai disajikan sebagai item daftar "kategori". Item ini sama dengan item daftar biasa, hanya saja margin diterapkan di sekitar ikon item, karena ikon terlihat lebih baik jika berukuran kecil. Ikon harus berupa vektor drawable yang dapat diberi warna. Petunjuk ini hanya tersedia untuk item yang dapat dijelajahi.DESCRIPTION_EXTRAS_VALUE_CONTENT_STYLE_CATEGORY_GRID_ITEM
: item yang sesuai ditampilkan sebagai item petak "kategori". Ini sama dengan item petak biasa, hanya saja margin harus diterapkan di sekitar ikon item, karena ikon terlihat lebih baik bila berukuran kecil. Ikon harus berupa vektor drawable yang dapat diberi warna. Petunjuk ini hanya tersedia untuk item yang dapat dijelajahi.
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:
- Item media A dengan
extras.putString(MediaConstants.DESCRIPTION_EXTRAS_KEY_CONTENT_STYLE_GROUP_TITLE, "Songs")
- Item media B dengan
extras.putString(MediaConstants.DESCRIPTION_EXTRAS_KEY_CONTENT_STYLE_GROUP_TITLE, "Albums")
- Item media C dengan
extras.putString(MediaConstants.DESCRIPTION_EXTRAS_KEY_CONTENT_STYLE_GROUP_TITLE, "Songs")
- Item media D dengan
extras.putString(MediaConstants.DESCRIPTION_EXTRAS_KEY_CONTENT_STYLE_GROUP_TITLE, "Songs")
- 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:
- Item media A dengan
extras.putString(MediaConstants.DESCRIPTION_EXTRAS_KEY_CONTENT_STYLE_GROUP_TITLE, "Songs")
- Item media C dengan
extras.putString(MediaConstants.DESCRIPTION_EXTRAS_KEY_CONTENT_STYLE_GROUP_TITLE, "Songs")
- Item media D dengan
extras.putString(MediaConstants.DESCRIPTION_EXTRAS_KEY_CONTENT_STYLE_GROUP_TITLE, "Songs")
- Item media B dengan
extras.putString(MediaConstants.DESCRIPTION_EXTRAS_KEY_CONTENT_STYLE_GROUP_TITLE, "Albums")
- 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.
Konstanta berikut dapat digunakan dalam kedua tambahan deskripsi MediaItem
dan tambahan MediaMetadata
:
EXTRA_DOWNLOAD_STATUS
: menunjukkan status download item. Gunakan konstanta ini sebagai kunci; konstanta panjang berikut adalah nilai yang memungkinkan:STATUS_DOWNLOADED
: item telah didownload sepenuhnya.STATUS_DOWNLOADING
: item sedang didownload.STATUS_NOT_DOWNLOADED
: item tidak didownload.
METADATA_KEY_IS_EXPLICIT
: menunjukkan apakah item berisi konten vulgar. Untuk menunjukkan item bersifat vulgar, gunakan konstanta ini sebagai kunci danMETADATA_VALUE_ATTRIBUTE_PRESENT
panjang sebagai nilai.
Konstanta berikut hanya dapat digunakan dalam tambahan deskripsi MediaItem
:
DESCRIPTION_EXTRAS_KEY_COMPLETION_STATUS
: menunjukkan status penyelesaian konten berformat panjang, seperti episode podcast atau buku audio. Gunakan konstanta ini sebagai kunci; konstanta bilangan bulat berikut adalah nilai yang memungkinkan:DESCRIPTION_EXTRAS_VALUE_COMPLETION_STATUS_NOT_PLAYED
: item belum diputar sama sekali.DESCRIPTION_EXTRAS_VALUE_COMPLETION_STATUS_PARTIALLY_PLAYED
: item telah diputar sebagian, dan posisi saat ini berada di tengah.DESCRIPTION_EXTRAS_VALUE_COMPLETION_STATUS_FULLY_PLAYED
: item telah selesai.
DESCRIPTION_EXTRAS_KEY_COMPLETION_PERCENTAGE
: menunjukkan jumlah progres penyelesaian pada konten berdurasi panjang sebagai jumlah ganda antara 0,0 dan 1,0. Tambahan ini memberikan lebih banyak informasi tentang statusPARTIALLY_PLAYING
sehingga Android Auto atau Android Automotive OS menampilkan indikator progres yang lebih bermakna, seperti status progres. Jika menggunakan tambahan ini, lihat bagian tentang memperbarui status progres di tampilan jelajah saat konten diputar dalam panduan ini untuk mempelajari cara memperbarui indikator ini setelah tayangan awal.
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:
- Saat dibuat,
MediaItem
harus mengirimDESCRIPTION_EXTRAS_KEY_COMPLETION_PERCENTAGE
pada bagian tambahannya dengan nilai antara 0.0 sampai 1.0. MediaMetadataCompat
harus mengirimMETADATA_KEY_MEDIA_ID
beserta nilai string yang sama dengan ID media yang diteruskan keMediaItem
.PlaybackStateCompat
harus menyertakan tambahan dengan kunciPLAYBACK_STATE_EXTRAS_KEY_MEDIA_ID
yang memetakan ke nilai string yang sama dengan ID media yang diteruskan keMediaItem
.
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());
Menampilkan hasil penelusuran yang dapat dijelajahi
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
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".
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.
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:
- Ganti dua metode dalam
implementasi
MediaBrowserServiceCompat
: - Mengurai batas tindakan saat runtime:
- Di
onGetRoot()
, dapatkan jumlah maksimum tindakan yang diizinkan untuk setiapMediaItem
menggunakan kunciBROWSER_ROOT_HINTS_KEY_CUSTOM_BROWSER_ACTION_LIMIT
dirootHints
Bundle
. Batas 0 menunjukkan bahwa fitur tidak didukung oleh sistem.
- Di
- 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 objekBundle
tindakan ke daftar.
- Untuk setiap tindakan, buat objek
- Tambahkan daftar global ke
BrowseRoot
:- Di
BrowseRoot
tambahanBundle
, tambahkan daftar tindakan sebagaiParcelable
Arraylist
menggunakan kunciBROWSER_SERVICE_EXTRAS_KEY_CUSTOM_BROWSER_ACTION_ROOT_LIST
.
- Di
- Tambahkan tindakan ke objek
MediaItem
:- Anda dapat menambahkan tindakan ke setiap objek
MediaItem
dengan menyertakan daftar ID tindakan di tambahanMediaDescriptionCompat
menggunakan kunciDESCRIPTION_EXTRAS_KEY_CUSTOM_BROWSER_ACTION_ID_LIST
. Daftar ini harus merupakan subkumpulan dari daftar tindakan global yang Anda tentukan diBrowseRoot
.
- Anda dapat menambahkan tindakan ke setiap objek
- Menangani tindakan dan menampilkan progres atau hasil:
- Di
onCustomAction
, tangani tindakan berdasarkan ID tindakan dan data lain yang Anda perlukan. Anda bisa mendapatkan IDMediaItem
yang memicu tindakan dari tambahan menggunakan kunciEXTRAS_KEY_CUSTOM_BROWSER_ACTION_MEDIA_ITEM_ID
.. - Anda dapat memperbarui daftar tindakan untuk
MediaItem
dengan menyertakan kunciEXTRAS_KEY_CUSTOM_BROWSER_ACTION_RESULT_REFRESH_ITEM
dalam progres atau paket hasil.
- Di
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);
- Pesan untuk pengguna
- 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:
- Download: Ini adalah status awal tindakan. Saat pengguna memilih
tindakan ini, Anda dapat menukarnya dengan "Mendownload" dan memanggil
sendProgressUpdate
untuk memperbarui UI. - Mendownload: Status ini menunjukkan bahwa download sedang berlangsung. Anda dapat menggunakan status ini untuk menampilkan status progres atau indikator lain kepada pengguna.
- Didownload: Status ini menunjukkan bahwa download telah selesai. Saat
download selesai, Anda dapat menukar "Downloading" dengan "Downloaded" dan memanggil
sendResult
dengan kunciEXTRAS_KEY_CUSTOM_BROWSER_ACTION_RESULT_REFRESH_ITEM
untuk menunjukkan bahwa item harus dimuat ulang. Selain itu, Anda dapat menggunakan kunciEXTRAS_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:
- 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 kunciEXTRAS_KEY_CUSTOM_BROWSER_ACTION_RESULT_REFRESH_ITEM
untuk memperbarui UI. - 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 kunciEXTRAS_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.
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.
Menambahkan link dari item yang sedang diputar
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".