Asisten Google memungkinkan Anda menggunakan perintah suara untuk mengontrol banyak perangkat, seperti Google Home, ponsel, dan sebagainya. Asisten Google memiliki kemampuan bawaan untuk memahami perintah media ("putar lagu Beyoncé") dan mendukung kontrol media (seperti jeda, lewati, maju cepat, sukai).
Asisten berkomunikasi dengan aplikasi media Android menggunakan sesi media. Asisten dapat menggunakan intent atau layanan untuk meluncurkan aplikasi Anda dan memulai pemutaran. Untuk hasil terbaik, aplikasi Anda harus menerapkan semua fitur yang dijelaskan di halaman ini.
Menggunakan sesi media
Setiap aplikasi audio dan video harus menerapkan sesi media agar Asisten dapat mengoperasikan kontrol transport setelah pemutaran dimulai.
Perhatikan bahwa meskipun Asisten hanya menggunakan tindakan yang tercantum di bagian ini, praktik terbaiknya adalah menerapkan semua API persiapan dan pemutaran untuk memastikan kompatibilitas dengan aplikasi lain. Untuk tindakan apa pun yang tidak Anda dukung, callback sesi media dapat menampilkan error menggunakan ERROR_CODE_NOT_SUPPORTED.
Aktifkan kontrol media dan transport dengan menetapkan tanda ini di objek
MediaSession aplikasi Anda:
Kotlin
session.setFlags( MediaSessionCompat.FLAG_HANDLES_MEDIA_BUTTONS or MediaSessionCompat.FLAG_HANDLES_TRANSPORT_CONTROLS )
Java
session.setFlags(MediaSessionCompat.FLAG_HANDLES_MEDIA_BUTTONS | MediaSessionCompat.FLAG_HANDLES_TRANSPORT_CONTROLS);
Sesi media aplikasi Anda harus mendeklarasikan tindakan yang didukungnya, dan menerapkan callback sesi media yang terkait. Deklarasikan tindakan yang didukung di
setActions().
Project contoh Universal Android Music Player adalah contoh yang baik tentang cara menyiapkan sesi media.
Tindakan pemutaran
Agar dapat memulai pemutaran dari layanan, sesi media harus menerapkan tindakan PLAY ini dan callback-nya:
| Tindakan | Callback |
|---|---|
ACTION_PLAY |
onPlay() |
ACTION_PLAY_FROM_SEARCH |
onPlayFromSearch() |
ACTION_PLAY_FROM_URI (*) |
onPlayFromUri() |
Sesi Anda juga harus menerapkan tindakan PREPARE ini dan callback-nya:
| Tindakan | Callback |
|---|---|
ACTION_PREPARE |
onPrepare() |
ACTION_PREPARE_FROM_SEARCH |
onPrepareFromSearch() |
ACTION_PREPARE_FROM_URI (*) |
onPrepareFromUri() |
(*) Tindakan berbasis URI Asisten Google hanya berfungsi untuk perusahaan yang menyediakan URI ke Google. Untuk mempelajari lebih lanjut cara mendeskripsikan konten media Anda ke Google, lihat Media Actions.
Dengan menerapkan API persiapan, latensi pemutaran setelah perintah suara dapat dikurangi. Aplikasi media yang ingin meningkatkan latensi pemutaran dapat menggunakan waktu tambahan untuk mulai meng-cache konten dan menyiapkan pemutaran media.
Mengurai kueri penelusuran
Saat pengguna menelusuri item media tertentu, seperti "Putar jazz di [nama aplikasi Anda]" atau "Dengarkan [judul lagu]", metode callback onPrepareFromSearch() atau onPlayFromSearch() akan menerima hasil penelusuran suara dalam parameter kueri dan paket tambahan.
Aplikasi Anda harus mengurai kueri penelusuran suara dan memulai pemutaran dengan mengikuti langkah-langkah berikut:
- Gunakan paket tambahan dan string kueri penelusuran yang ditampilkan dari penelusuran suara untuk memfilter hasil.
- Buat antrean pemutaran berdasarkan hasil ini.
- Putar item media yang paling relevan dari hasil.
Metode onPlayFromSearch() mengambil parameter tambahan yang memuat informasi yang lebih terperinci dari penelusuran suara. Parameter tambahan ini membantu Anda menemukan konten audio di aplikasi Anda untuk diputar.
Jika hasil penelusuran tidak dapat menyediakan data ini, Anda dapat menerapkan logika untuk mengurai kueri penelusuran mentah dan memutar trek yang sesuai berdasarkan kueri.
Parameter tambahan berikut didukung di Android Automotive OS dan Android Auto:
Cuplikan kode berikut menunjukkan cara mengganti metode onPlayFromSearch() dalam implementasi MediaSession.Callback Anda untuk mengurai kueri penelusuran suara dan memulai pemutaran:
Kotlin
override fun onPlayFromSearch(query: String?, extras: Bundle?) { if (query.isNullOrEmpty()) { // The user provided generic string e.g. 'Play music' // Build appropriate playlist queue } else { // Build a queue based on songs that match "query" or "extras" param val mediaFocus: String? = extras?.getString(MediaStore.EXTRA_MEDIA_FOCUS) if (mediaFocus == MediaStore.Audio.Artists.ENTRY_CONTENT_TYPE) { isArtistFocus = true artist = extras.getString(MediaStore.EXTRA_MEDIA_ARTIST) } else if (mediaFocus == MediaStore.Audio.Albums.ENTRY_CONTENT_TYPE) { isAlbumFocus = true album = extras.getString(MediaStore.EXTRA_MEDIA_ALBUM) } // Implement additional "extras" param filtering } // Implement your logic to retrieve the queue var result: String? = when { isArtistFocus -> artist?.also { searchMusicByArtist(it) } isAlbumFocus -> album?.also { searchMusicByAlbum(it) } else -> null } result = result ?: run { // No focus found, search by query for song title query?.also { searchMusicBySongTitle(it) } } if (result?.isNotEmpty() == true) { // Immediately start playing from the beginning of the search results // Implement your logic to start playing music playMusic(result) } else { // Handle no queue found. Stop playing if the app // is currently playing a song } }
Java
@Override public void onPlayFromSearch(String query, Bundle extras) { if (TextUtils.isEmpty(query)) { // The user provided generic string e.g. 'Play music' // Build appropriate playlist queue } else { // Build a queue based on songs that match "query" or "extras" param String mediaFocus = extras.getString(MediaStore.EXTRA_MEDIA_FOCUS); if (TextUtils.equals(mediaFocus, MediaStore.Audio.Artists.ENTRY_CONTENT_TYPE)) { isArtistFocus = true; artist = extras.getString(MediaStore.EXTRA_MEDIA_ARTIST); } else if (TextUtils.equals(mediaFocus, MediaStore.Audio.Albums.ENTRY_CONTENT_TYPE)) { isAlbumFocus = true; album = extras.getString(MediaStore.EXTRA_MEDIA_ALBUM); } // Implement additional "extras" param filtering } // Implement your logic to retrieve the queue if (isArtistFocus) { result = searchMusicByArtist(artist); } else if (isAlbumFocus) { result = searchMusicByAlbum(album); } if (result == null) { // No focus found, search by query for song title result = searchMusicBySongTitle(query); } if (result != null && !result.isEmpty()) { // Immediately start playing from the beginning of the search results // Implement your logic to start playing music playMusic(result); } else { // Handle no queue found. Stop playing if the app // is currently playing a song } }
Untuk contoh yang lebih terperinci tentang cara menerapkan penelusuran suara untuk memutar konten audio di aplikasi Anda, lihat contoh Universal Android Music Player.
Menangani kueri kosong
Jika onPrepare(), onPlay(), onPrepareFromSearch(), atau onPlayFromSearch() dipanggil tanpa kueri penelusuran, aplikasi media Anda akan memutar media "saat ini". Jika tidak ada media saat ini, aplikasi akan mencoba memutar sesuatu, seperti lagu dari playlist terbaru atau antrean acak. Asisten menggunakan API ini saat pengguna meminta “Putar musik di [nama aplikasi Anda]” tanpa informasi tambahan.
Saat pengguna mengucapkan “Putar musik di [nama aplikasi Anda]”, Android Automotive OS atau Android Auto akan mencoba meluncurkan aplikasi Anda dan memutar audio dengan memanggil metode onPlayFromSearch() aplikasi Anda. Namun, karena pengguna tidak menyebutkan nama item medianya, metode onPlayFromSearch() akan menerima parameter kueri kosong. Dalam kasus semacam ini, aplikasi Anda harus
merespons dengan segera memutar audio, seperti lagu dari playlist terbaru
atau antrean acak.
Mendeklarasikan dukungan lama untuk voice action
Biasanya, menangani tindakan pemutaran yang dijelaskan di atas memberikan semua fungsionalitas pemutaran yang diperlukan aplikasi Anda. Namun, beberapa sistem mengharuskan aplikasi Anda memuat filter Intent untuk penelusuran. Anda harus mendeklarasikan dukungan untuk filter Intent ini dalam file manifes aplikasi.
Sertakan kode ini dalam file manifes untuk aplikasi ponsel:
<activity>
<intent-filter>
<action android:name=
"android.media.action.MEDIA_PLAY_FROM_SEARCH" />
<category android:name=
"android.intent.category.DEFAULT" />
</intent-filter>
</activity>
Kontrol transport
Setelah sesi media aplikasi Anda aktif, Asisten dapat mengeluarkan perintah suara untuk mengontrol pemutaran dan memperbarui metadata media. Agar perilaku ini berfungsi, kode Anda harus mengaktifkan tindakan berikut dan menerapkan callback yang terkait:
| Tindakan | Callback | Deskripsi |
|---|---|---|
ACTION_SKIP_TO_NEXT |
onSkipToNext() |
Video berikutnya |
ACTION_SKIP_TO_PREVIOUS |
onSkipToPrevious() |
Lagu sebelumnya |
ACTION_PAUSE, ACTION_PLAY_PAUSE |
onPause() |
Jeda |
ACTION_STOP |
onStop() |
Hentikan |
ACTION_PLAY |
onPlay() |
Lanjutkan |
ACTION_SEEK_TO |
onSeekTo() |
Mundur 30 detik |
ACTION_SET_RATING |
onSetRating(android.support.v4.media.RatingCompat) |
Suka/Tidak suka. |
ACTION_SET_CAPTIONING_ENABLED |
onSetCaptioningEnabled(boolean) |
Mengaktifkan atau menonaktifkan teks. |
Perhatikan:
- Agar perintah mencari berfungsi,
PlaybackStateharus diperbarui denganstate, position, playback speed, and update time. Aplikasi harus memanggilsetPlaybackState()saat status berubah. - Aplikasi media juga harus menjaga agar metadata sesi media selalu terbaru. Ini mendukung pertanyaan seperti "lagu apa yang sedang diputar?" Aplikasi harus memanggil
setMetadata()jika kolom yang berlaku (seperti judul lagu, artis, dan nama) berubah. MediaSession.setRatingType()harus ditetapkan untuk menunjukkan jenis rating yang didukung aplikasi, dan aplikasi harus menerapkanonSetRating(). Jika tidak mendukung rating, aplikasi harus menetapkan jenis rating keRATING_NONE.
Tindakan suara yang Anda dukung kemungkinan akan bervariasi menurut jenis konten.
| Jenis Konten | Tindakan yang Diperlukan |
|---|---|
| Musik |
Harus mendukung: Putar, Jeda, Hentikan, Lewati ke Berikutnya, dan Lewati ke Sebelumnya Sangat merekomendasikan dukungan untuk: Cari Ke |
| Podcast |
Harus mendukung: Putar, Jeda, Hentikan, dan Cari Ke Rekomendasikan dukungan untuk: Lewati ke Berikutnya dan Lewati ke Sebelumnya |
| Buku audio | Harus mendukung: Putar, Jeda, Hentikan, dan Cari Ke |
| Radio | Harus mendukung: Putar, Jeda, dan Hentikan |
| Berita | Harus mendukung: Putar, Jeda, Hentikan, Lewati ke Berikutnya, dan Lewati ke Sebelumnya |
| Video |
Harus mendukung: Putar, Jeda, Hentikan, Cari Ke, Putar Ulang, dan Maju Cepat Sangat merekomendasikan dukungan untuk: Lewati ke Berikutnya dan Lewati ke Sebelumnya |
Anda harus mendukung sebanyak mungkin tindakan yang tercantum di atas sesuai dengan penawaran produk Anda, tetapi tetap merespons dengan baik tindakan lainnya. Misalnya, jika hanya pengguna premium yang dapat kembali ke item sebelumnya, Anda dapat memunculkan error jika pengguna tingkat gratis meminta Asisten untuk kembali ke item sebelumnya. Lihat bagian penanganan error untuk mendapatkan panduan selengkapnya.
Contoh kueri suara untuk dicoba
Tabel berikut menguraikan beberapa contoh kueri yang harus Anda gunakan saat menguji penerapan:
| Callback MediaSession | Frasa “Ok Google” yang akan digunakan | |
|---|---|---|
onPlay() |
“Putar”. “Lanjutkan.” |
|
onPlayFromSearch()
onPlayFromUri() |
Musik |
“Putar musik atau lagu di (nama aplikasi)”. Ini adalah kueri kosong. “Putar (lagu | artis | album | genre | playlist) di (nama aplikasi).” |
| Radio | “Putar (frekuensi | stasiun) di (nama aplikasi).” | |
| Audiobook |
“Baca audiobook saya di (nama aplikasi)”. “Baca (audiobook) di (nama aplikasi).” |
|
| Podcast | “Putar (podcast) di (nama aplikasi).” | |
onPause() |
“Jeda”. | |
onStop() |
“Berhenti.” | |
onSkipToNext() |
“(Lagu | episode | trek) berikutnya”. | |
onSkipToPrevious() |
“(Lagu | episode | trek) sebelumnya”. | |
onSeekTo() |
“Mulai ulang.” “Maju ## detik.” “Kembali ## menit.” |
|
T/A (terus perbarui
MediaMetadata
Anda) |
"Apa yang sedang diputar?" | |
Error
Asisten menangani error yang terjadi dari sesi media dan melaporkannya kepada pengguna. Pastikan sesi media Anda memperbarui status transport dan kode error dalam PlaybackState-nya dengan benar, seperti dijelaskan dalam Bekerja dengan sesi media. Asisten mengenali semua kode error yang ditampilkan oleh getErrorCode().
Kasus yang sering salah ditangani
Berikut beberapa contoh kasus error yang harus Anda pastikan ditangani dengan benar:
- Pengguna harus login
- Tetapkan kode error
PlaybackStatekeERROR_CODE_AUTHENTICATION_EXPIRED. - Tetapkan pesan error
PlaybackState. - Jika diperlukan untuk pemutaran, tetapkan status
PlaybackStatekeSTATE_ERROR, jika tidak, pertahankanPlaybackStatelainnya apa adanya.
- Tetapkan kode error
- Pengguna meminta tindakan yang tidak tersedia
- Tetapkan kode error
PlaybackStatedengan tepat. Misalnya, tetapkanPlaybackStatekeERROR_CODE_NOT_SUPPORTEDjika tindakan tidak didukung atauERROR_CODE_PREMIUM_ACCOUNT_REQUIREDjika tindakan dilindungi login. - Tetapkan pesan error
PlaybackState. - Pertahankan
PlaybackStatelainnya apa adanya.
- Tetapkan kode error
- Pengguna meminta konten yang tidak tersedia di aplikasi
- Tetapkan kode error
PlaybackStatedengan tepat. Misalnya, gunakanERROR_CODE_NOT_AVAILABLE_IN_REGION. - Tetapkan pesan error
PlaybackState. - Tetapkan status
PlaybackSatekeSTATE_ERRORuntuk menghentikan pemutaran, jika tidak, pertahankanPlaybackStatelainnya apa adanya.
- Tetapkan kode error
- Pengguna meminta konten yang tidak memiliki kecocokan persis. Misalnya, pengguna paket gratis meminta konten yang hanya tersedia untuk pengguna paket premium.
- Sebaiknya Anda tidak menampilkan error, dan harus memprioritaskan menemukan sesuatu yang serupa untuk dimainkan. Asisten akan menangani pengucapan respons suara yang paling relevan sebelum pemutaran dimulai.
Memulai pemutaran dengan intent
Asisten dapat meluncurkan aplikasi audio atau video dan memulai pemutaran dengan mengirimkan intent yang berisi deep link.
Intent dan deep link-nya dapat berasal dari sumber berbeda:
- Saat memulai aplikasi seluler, Asisten dapat menggunakan penelusuran Google untuk mengambil konten yang telah di-markup yang menyediakan tindakan tonton melalui sebuah link.
- Saat Asisten memulai aplikasi TV, aplikasi Anda harus menyertakan
Penyedia Penelusuran TV
untuk menampakkan URI untuk konten media. Asisten mengirimkan kueri ke penyedia konten yang perlu menampilkan intent yang berisi URI untuk deep link dan tindakan opsional.
Jika kueri ini menampilkan tindakan dalam intent, Asisten akan mengirim tindakan tersebut dan URI-nya kembali ke aplikasi Anda. Jika penyedia tidak menentukan tindakan, Asisten akan menambahkan
ACTION_VIEWke Intent.
Asisten menambahkan EXTRA_START_PLAYBACK ekstra dengan nilai true
ke intent yang dikirimkannya ke aplikasi Anda. Aplikasi Anda akan memulai pemutaran saat menerima intent yang berisi EXTRA_START_PLAYBACK.
Menangani intent selagi aktif
Pengguna dapat meminta Asisten untuk memutar sesuatu selagi aplikasi Anda masih memutar konten dari permintaan sebelumnya. Artinya, aplikasi Anda dapat menerima intent baru untuk memulai pemutaran, sementara aktivitas pemutarannya sudah diluncurkan dan aktif.
Aktivitas yang mendukung intent dengan deep link harus mengganti
onNewIntent()
untuk menangani permintaan baru.
Saat memulai pemutaran, Asisten mungkin menambahkan tanda tambahan ke intent yang dikirimkannya ke aplikasi Anda. Secara khusus, Asisten dapat menambahkan FLAG_ACTIVITY_CLEAR_TOP atau FLAG_ACTIVITY_NEW_TASK, atau keduanya. Meskipun kode Anda tidak perlu menangani tanda ini, sistem Android akan meresponsnya.
Perilaku aplikasi Anda dapat terpengaruh jika permintaan pemutaran kedua yang memuat URI baru tiba sementara URI sebelumnya masih sedang diputar. Dalam kasus semacam ini, ada baiknya untuk menguji respons aplikasi Anda. Anda dapat menggunakan alat command line adb untuk menyimulasikan situasi ini (konstanta 0x14000000 adalah bitwise boolean OR dari kedua tanda):
adb shell 'am start -a android.intent.action.VIEW --ez android.intent.extra.START_PLAYBACK true -d "<first_uri>"' -f 0x14000000
adb shell 'am start -a android.intent.action.VIEW --ez android.intent.extra.START_PLAYBACK true -d "<second_uri>"' -f 0x14000000
Pemutaran dari layanan
Jika aplikasi Anda memiliki
media browser service
yang mengizinkan koneksi dari Asisten,
Asisten dapat memulai aplikasi dengan berkomunikasi dengan
media session layanan.
Layanan browser media tidak boleh meluncurkan Aktivitas.
Asisten akan meluncurkan Aktivitas Anda berdasarkan PendingIntent yang Anda tetapkan dengan setSessionActivity().
Pastikan Anda menetapkan MediaSession.Token saat menginisialisasi layanan browser media. Ingatlah untuk menetapkan tindakan pemutaran yang didukung sepanjang waktu, termasuk selama inisialisasi. Asisten mengharapkan aplikasi media Anda menetapkan tindakan pemutaran sebelum Asisten mengirimkan perintah pemutaran pertamanya.
Untuk memulai dari layanan, Asisten akan menerapkan API klien browser media. Asisten melakukan panggilan TransportControls yang memicu callback tindakan PLAY pada sesi media aplikasi Anda.
Diagram berikut menunjukkan urutan panggilan yang dihasilkan oleh Asisten dan callback sesi media yang terkait. (Callback prepare dikirim hanya jika aplikasi Anda mendukungnya.) Semua panggilan bersifat asinkron. Asisten tidak menunggu respons apa pun dari aplikasi Anda.

Saat pengguna mengeluarkan perintah suara untuk memutar media, Asisten merespons dengan pengumuman singkat. Segera setelah pengumuman itu selesai, Asisten akan mengeluarkan tindakan PLAY. Asisten tidak menunggu status pemutaran tertentu.
Jika aplikasi Anda mendukung tindakan ACTION_PREPARE_*, Asisten akan memanggil tindakan PREPARE sebelum memulai pengumuman.
Membuat koneksi ke MediaBrowserService
Agar dapat menggunakan layanan untuk memulai aplikasi Anda, Asisten harus dapat tersambung ke MediaBrowserService dan mengambil MediaSession.Token-nya. Permintaan koneksi ditangani dalam metode
onGetRoot()
layanan. Ada dua cara untuk menangani permintaan:
- Menerima semua permintaan koneksi
- Menerima permintaan koneksi dari aplikasi Asisten saja
Menerima semua permintaan koneksi
Anda harus menampilkan BrowserRoot agar dapat mengizinkan Asisten untuk mengirimkan perintah ke sesi media Anda. Cara termudahnya adalah dengan mengizinkan semua aplikasi MediaBrowser untuk tersambung ke MediaBrowserService Anda. Anda harus menampilkan BrowserRoot bukan null. Berikut adalah kode yang berlaku dari Universal Music Player:
Kotlin
override fun onGetRoot( clientPackageName: String, clientUid: Int, rootHints: Bundle? ): BrowserRoot? { // To ensure you are not allowing any arbitrary app to browse your app's contents, you // need to check the origin: if (!packageValidator.isCallerAllowed(this, clientPackageName, clientUid)) { // If the request comes from an untrusted package, return an empty browser root. // If you return null, then the media browser will not be able to connect and // no further calls will be made to other media browsing methods. Log.i(TAG, "OnGetRoot: Browsing NOT ALLOWED for unknown caller. Returning empty " + "browser root so all apps can use MediaController. $clientPackageName") return MediaBrowserServiceCompat.BrowserRoot(MEDIA_ID_EMPTY_ROOT, null) } // Return browser roots for browsing... }
Java
@Override public BrowserRoot onGetRoot(@NonNull String clientPackageName, int clientUid, Bundle rootHints) { // To ensure you are not allowing any arbitrary app to browse your app's contents, you // need to check the origin: if (!packageValidator.isCallerAllowed(this, clientPackageName, clientUid)) { // If the request comes from an untrusted package, return an empty browser root. // If you return null, then the media browser will not be able to connect and // no further calls will be made to other media browsing methods. LogHelper.i(TAG, "OnGetRoot: Browsing NOT ALLOWED for unknown caller. " + "Returning empty browser root so all apps can use MediaController." + clientPackageName); return new MediaBrowserServiceCompat.BrowserRoot(MEDIA_ID_EMPTY_ROOT, null); } // Return browser roots for browsing... }
Menerima paket dan tanda tangan aplikasi Asisten
Anda dapat secara eksplisit mengizinkan Asisten untuk tersambung ke layanan browser media dengan memeriksa nama dan tanda tangan paketnya. Aplikasi Anda akan menerima nama paket dalam metode onGetRoot MediaBrowserService. Anda harus menampilkan BrowserRoot agar dapat mengizinkan Asisten untuk mengirimkan perintah ke sesi media Anda. Contoh Universal Music Player ini mengelola daftar nama dan tanda tangan paket yang dikenal. Di bawah ini adalah nama dan tanda tangan paket yang digunakan oleh Asisten Google.
<signature name="Google" package="com.google.android.googlequicksearchbox">
<key release="false">19:75:b2:f1:71:77:bc:89:a5:df:f3:1f:9e:64:a6:ca:e2:81:a5:3d:c1:d1:d5:9b:1d:14:7f:e1:c8:2a:fa:00</key>
<key release="true">f0:fd:6c:5b:41:0f:25:cb:25:c3:b5:33:46:c8:97:2f:ae:30:f8:ee:74:11:df:91:04:80:ad:6b:2d:60:db:83</key>
</signature>
<signature name="Google Assistant on Android Automotive OS" package="com.google.android.carassistant">
<key release="false">17:E2:81:11:06:2F:97:A8:60:79:7A:83:70:5B:F8:2C:7C:C0:29:35:56:6D:46:22:BC:4E:CF:EE:1B:EB:F8:15</key>
<key release="true">74:B6:FB:F7:10:E8:D9:0D:44:D3:40:12:58:89:B4:23:06:A6:2C:43:79:D0:E5:A6:62:20:E3:A6:8A:BF:90:E2</key>
</signature>