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 Pedoman desain aplikasi Android Automotive OS dan Pedoman desain aplikasi Android Auto.
  3. Pelajari istilah dan konsep utama yang tercantum dalam bagian ini.

Istilah dan konsep utama

Layanan Browser Media
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:

  • Dapat diputar: Tanda ini 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.
  • Dapat dijelajahi: Tanda ini menunjukkan bahwa item adalah suatu 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 membuat sendiri antarmuka yang dioptimalkan untuk kendaraan menggunakan informasi dari layanan browser media Anda.

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 dalam manifes Anda agar Android Auto dan Android Automotive OS dapat menemukan layanan tersebut 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.

Anda dapat menentukan ikon yang digunakan untuk merepresentasikan aplikasi Anda menggunakan deklarasi manifes berikut:

<!--The android:icon attribute is used by Android Automotive OS-->
<application>
    ...
    android:icon="@mipmap/ic_launcher">
    ...
    <!--Used by Android Auto-->
    <meta-data android:name="com.google.android.gms.car.notification.SmallIcon"
               android:resource="@drawable/ic_auto_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.

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 untuk mengetahui 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 harus mengizinkan sambungan 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 untuk 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.

Catatan: Android Auto dan Android Automotive OS memiliki batasan ketat terkait jumlah item media yang dapat ditampilkan di setiap level menu. Batasan ini meminimalkan gangguan bagi pengemudi dan membantu mereka mengoperasikan aplikasi Anda dengan perintah suara. Untuk mengetahui informasi selengkapnya, lihat Menjelajahi detail konten dan Tampilan penjelajahan.

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 if 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 if 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 1. Konten root 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 dapat dihapus atau dibuat kurang mudah ditemukan oleh sistem. Dua petunjuk dikirim:

  1. Batas jumlah turunan root: pada sebagian besar kasus, Anda dapat memperkirakan angka ini adalah empat. Artinya, lebih dari empat tab tidak dapat ditampilkan.
  2. Tanda yang didukung pada turunan root: Anda dapat memperkirakan nilai ini adalah MediaItem#FLAG_BROWSABLE. Artinya, hanya item yang dapat dijelajahi yang dapat ditampilkan sebagai tab, dan item yang dapat diputar tidak dapat ditampilkan.

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:

  1. Berikan ikon monokrom (sebaiknya putih) untuk setiap item tab.
  2. 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 MediaDescription yang mewakili item dalam hierarki konten, teruskan URI melalui setIconUri(). Untuk objek MediaMetadata yang merepresentasikan item yang sedang diputar, teruskan URI melalui putString(), menggunakan salah satu kunci berikut:

Berikut adalah contoh cara mendownload art dari URI web dan menampilkannya melalui URI lokal:

  1. Download file gambar Anda (cuplikan kode berikut menggunakan Glide).

    Kotlin

    val artFile = Glide.with(context)
      .downloadOnly()
      .load(imageUri)
      .submit()
      .get()
    

    Java

    File artFile = Glide.with(context)
      .downloadOnly()
      .load(imageUri)
      .submit()
      .get();
    
  2. Membuat URI content:// untuk file. Layanan browser media dan sesi media harus meneruskan URI ini ke Android Auto dan Android Automotive OS.

    Kotlin

    fun File.asAlbumArtContentURI(): Uri {
      return Uri.Builder()
        .scheme(ContentResolver.SCHEME_CONTENT)
        .authority(AUTHORITY)
        .appendPath(this.path)
        .build()
    }
    

    Java

    public static Uri asAlbumArtContentURI(File file) {
      return Uri.Builder()
        .scheme(ContentResolver.SCHEME_CONTENT)
        .authority(AUTHORITY)
        .appendPath(file.getPath())
        .build();
    }
    
  3. Buat file dapat diakses di metode ContentProvider.openFile().

    Kotlin

    override fun openFile(uri: Uri, mode: String): ParcelFileDescriptor? {
      val context = this.context ?: return null
      val file = File(uri.path)
      if (!file.exists()) {
        throw FileNotFoundException(uri.path)
      }
      // Only allow access to files under cache path
      val cachePath = context.cacheDir.path
      if (!file.path.startsWith(cachePath)) {
        throw FileNotFoundException()
      }
      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(uri.getPath());
      if (!file.exists()) {
        throw new FileNotFoundException(uri.getPath());
      }
      // Only allow access to files under cache path
      String cachePath = context.getCacheDir().getPath();
      if (!file.getPath().startsWith(cachePath)) {
        throw new FileNotFoundException();
      }
      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

Content Style API memungkinkan Anda mengganti gaya konten default untuk turunan mana pun dari item media yang dapat dijelajahi. Untuk mengganti gaya default, buat paket tambahan di MediaDescription item media, lalu tambahkan petunjuk yang sama seperti yang dijelaskan di atas.

Cuplikan kode berikut menunjukkan cara membuat MediaItem yang dapat dijelajahi yang akan mengganti gaya konten default:

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_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_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 mereka yang menyertakan pemetaan dengan kunci DESCRIPTION_EXTRAS_KEY_CONTENT_STYLE_GROUP_TITLE dan kunci nilai string yang identik. String ini digunakan sebagai judul grup dan harus dilokalkan.

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 tersebut dalam dua grup, aplikasi Anda seharusnya meneruskan aplikasi 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

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

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:

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 vulgar yang diputar sebagian:

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)
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);
MediaDescriptionCompat description =
    new MediaDescriptionCompat.Builder()
        .setMediaId(/*...*/)
        .setTitle(resources.getString(/*...*/))
        .setExtras(extras)
        .build();
return new MediaBrowserCompat.MediaItem(description, /* flags */);

Untuk menampilkan indikator item media yang sedang diputar, Anda dapat mendeklarasikan nilai Long untuk METADATA_KEY_IS_EXPLICIT atau EXTRA_DOWNLOAD_STATUS dalam metode MediaMetadata.Builder() dari mediaSession Anda. Anda tidak dapat menampilkan indikator DESCRIPTION_EXTRAS_KEY_COMPLETION_STATUS pada tampilan pemutaran.

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

Kotlin

import androidx.media.utils.MediaConstants

mediaSession.setMetadata(
    MediaMetadata.Builder()
        .putString(
            MediaMetadata.METADATA_KEY_DISPLAY_TITLE, "Song Name")
        .putString(
            MediaMetadata.METADATA_KEY_DISPLAY_SUBTITLE, "Artist name")
        .putString(
            MediaMetadata.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 MediaMetadata.Builder()
        .putString(
            MediaMetadata.METADATA_KEY_DISPLAY_TITLE, "Song Name")
        .putString(
            MediaMetadata.METADATA_KEY_DISPLAY_SUBTITLE, "Artist name")
        .putString(
            MediaMetadata.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());

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

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 to handle play control requests, which
        // implements MediaSession.Callback
        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 to handle play control requests, which
    // implements MediaSession.Callback
    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.

Anda harus mengimplementasikan 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 akan memutar konten default-nya. 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, baca Menetapkan status pemutaran 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.

Aplikasi Anda harus mengganti metode ini untuk menyediakan fungsionalitas yang diinginkan. Anda tidak perlu mengimplementasikan metode yang tidak didukung oleh aplikasi Anda. Misalnya, jika aplikasi Anda memutar live stream (seperti siaran olahraga), metode onSkipToNext() tidak akan sesuai, dan sebagai gantinya 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 akan memutar audio seperti biasanya (misalnya, memutar konten melalui speaker atau headphone ponsel 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 Pemutaran media, Mengelola pemutaran audio, dan ExoPlayer.

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, sebaiknya Anda membuat antrean pemutaran untuk ditampilkan kepada pengguna. Untuk melakukannya, Anda perlu memanggil metode setQueue() dan setQueueTitle(), mengaktifkan tindakan ACTION_SKIP_TO_QUEUE_ITEM, serta menentukan callback onSkipToQueueItem().

Android Auto dan Android Automotive OS menampilkan tombol untuk setiap tindakan yang diaktifkan, serta antrean pemutaran jika Anda memilih untuk membuatnya.

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 akan 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 PlaybackState. 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 PlaybackState awal dari sesi media Anda ke STATE_STOPPED, STATE_PAUSED, STATE_NONE, atau STATE_ERROR.

Sesi media dalam Android Auto dan Android Otomotif 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 (misalnya, item media yang terakhir diputar, PlaybackState, dan antrean), sehingga saat aplikasi media menerima permintaan melanjutkan, pengguna dapat secara otomatis melanjutkan dari posisi terakhir yang mereka tinggalkan.

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 penambahannya ke PlaybackState.

Tindakan kustom harus memberikan perilaku yang berbeda dari tindakan standar, dan tidak boleh digunakan untuk menggantikan 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

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

Java

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

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 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 4. Contoh ikon tindakan kustom gaya tidak aktif.

Mendukung tindakan suara

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 Bohemian Rhapsody" untuk meminta aplikasi tersebut memutar lagu lain tanpa melihat atau menyentuh layar mobil.

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

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

Mendeteksi mode mobil

Aplikasi media Android Auto tidak boleh memulai pemutaran audio melalui speaker mobil kecuali jika pengguna memulai pemutaran dengan sengaja (misalnya dengan mengetuk tombol putar di aplikasi Anda). Bahkan alarm yang dijadwalkan pengguna dari aplikasi media pun tidak boleh mulai memutar musik melalui speaker mobil. Untuk memenuhi persyaratan ini, aplikasi Anda harus menentukan apakah ponsel sedang dalam mode mobil sebelum memutar audio apa pun. Untuk memeriksa apakah ponsel dalam mode mobil atau tidak, aplikasi Anda dapat memanggil metode getCurrentModeType().

Jika ponsel pengguna sedang dalam mode mobil, 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.

Cuplikan kode berikut menunjukkan cara memeriksa apakah aplikasi berjalan dalam mode mobil:

Kotlin

fun isCarUiMode(c: Context): Boolean {
    val uiModeManager = c.getSystemService(Context.UI_MODE_SERVICE) as UiModeManager
    return if (uiModeManager.currentModeType == Configuration.UI_MODE_TYPE_CAR) {
        LogHelper.d(TAG, "Running in Car mode")
        true
    } else {
        LogHelper.d(TAG, "Running in a non-Car mode")
        false
    }
}

Java

 public static boolean isCarUiMode(Context c) {
      UiModeManager uiModeManager = (UiModeManager) c.getSystemService(Context.UI_MODE_SERVICE);
      if (uiModeManager.getCurrentModeType() == Configuration.UI_MODE_TYPE_CAR) {
            LogHelper.d(TAG, "Running in Car mode");
            return true;
      } else {
          LogHelper.d(TAG, "Running in a non-Car mode");
          return false;
        }
  }

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 as 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 as you normally would
    mediaSession.setMetadata(builder.build());
}

Menangani error umum

Saat aplikasi mengalami error, Anda harus menetapkan status pemutarannya ke STATE_ERROR dan menampilkan pesan error menggunakan metode setErrorMessage(). 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.

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

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

Referensi lainnya