Trợ lý Google cho phép bạn sử dụng lệnh thoại để điều khiển nhiều thiết bị, chẳng hạn như Google Home, điện thoại của bạn và nhiều thiết bị khác. Chiến dịch này có tích hợp sẵn tính năng hiểu các lệnh phát nội dung đa phương tiện ("phát nội dung nào đó của Beyoncé") và hỗ trợ các nút điều khiển nội dung nghe nhìn (như tạm dừng, bỏ qua, tua đi, thích).
Trợ lý giao tiếp với các ứng dụng đa phương tiện trên Android bằng một nội dung nghe nhìn . Công cụ này có thể sử dụng ý định hoặc dịch vụ để khởi chạy ứng dụng của bạn và bắt đầu phát. Để có kết quả tốt nhất, ứng dụng của bạn nên triển khai tất cả các tính năng được mô tả trên trang này.
Sử dụng phiên nội dung nghe nhìn
Mọi ứng dụng âm thanh và video đều phải triển khai phiên phát nội dung đa phương tiện để Trợ lý có thể hoạt động các nút điều khiển truyền tải sau khi bắt đầu phát.
Xin lưu ý rằng mặc dù Trợ lý chỉ sử dụng những hành động được liệt kê trong phần này,
phương pháp hay nhất là triển khai tất cả các API chuẩn bị và phát lại để đảm bảo
khả năng tương thích với các ứng dụng khác. Đối với mọi thao tác mà bạn không hỗ trợ,
các lệnh gọi lại phiên phát nội dung đa phương tiện chỉ cần trả về một lỗi bằng cách sử dụng
ERROR_CODE_NOT_SUPPORTED
.
Bật các chế độ điều khiển nội dung nghe nhìn và truyền tải bằng cách đặt những cờ này trong phần cài đặt của ứng dụng
Đối tượng MediaSession
:
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);
Phiên đa phương tiện của ứng dụng phải khai báo các thao tác mà ứng dụng hỗ trợ và triển khai các
lệnh gọi lại phiên đa phương tiện tương ứng. Khai báo các thao tác được hỗ trợ trong
setActions()
.
Chiến lược phát hành đĩa đơn Trình phát nhạc Universal Android dự án mẫu là ví dụ hay về cách thiết lập phiên phát nội dung đa phương tiện.
Thao tác phát
Để bắt đầu phát từ một dịch vụ, phiên phát nội dung đa phương tiện phải có các thao tác PLAY
sau đây và lệnh gọi lại chúng:
Hành động | Lệnh gọi lại |
---|---|
ACTION_PLAY |
onPlay() |
ACTION_PLAY_FROM_SEARCH |
onPlayFromSearch() |
ACTION_PLAY_FROM_URI (*) |
onPlayFromUri() |
Phiên của bạn cũng nên triển khai các thao tác PREPARE
sau đây và lệnh gọi lại của các thao tác đó:
Hành động | Lệnh gọi lại |
---|---|
ACTION_PREPARE |
onPrepare() |
ACTION_PREPARE_FROM_SEARCH |
onPrepareFromSearch() |
ACTION_PREPARE_FROM_URI (*) |
onPrepareFromUri() |
Bằng cách triển khai các API chuẩn bị, độ trễ phát sau lệnh thoại có thể giảm được. Các ứng dụng đa phương tiện muốn cải thiện độ trễ khi phát có thể sử dụng để bắt đầu lưu nội dung vào bộ nhớ đệm và chuẩn bị phát nội dung nghe nhìn.
Phân tích cú pháp truy vấn tìm kiếm
Khi người dùng tìm kiếm một mục nội dung đa phương tiện cụ thể, chẳng hạn như “Phát nhạc jazz trên
[tên ứng dụng của bạn]" hoặc "Nghe [tên bài hát]",
onPrepareFromSearch()
hoặc
onPlayFromSearch()
nhận được một tham số truy vấn và một gói dữ liệu bổ sung.
Ứng dụng của bạn nên phân tích cú pháp truy vấn tìm kiếm bằng giọng nói và bắt đầu phát bằng cách làm theo các các bước:
- Sử dụng gói dữ liệu bổ sung và chuỗi cụm từ tìm kiếm được trả về từ tính năng tìm kiếm bằng giọng nói để lọc kết quả.
- Xây dựng hàng đợi phát dựa trên những kết quả này.
- Phát mục nội dung nghe nhìn phù hợp nhất trong phần kết quả.
onPlayFromSearch()
phương thức lấy một thông số bổ sung có thông tin chi tiết hơn từ giọng nói
tìm kiếm. Những thông tin bổ sung này giúp bạn tìm thấy nội dung âm thanh trong ứng dụng để phát.
Nếu kết quả tìm kiếm không thể cung cấp dữ liệu này, bạn có thể triển khai logic
để phân tích cú pháp truy vấn tìm kiếm thô và phát các bản nhạc phù hợp dựa trên
truy vấn.
Các tính năng bổ sung sau đây được hỗ trợ trong Android Automotive OS và Android Auto:
Đoạn mã sau đây cho biết cách ghi đè onPlayFromSearch()
trong MediaSession.Callback
của bạn
để phân tích cú pháp truy vấn tìm kiếm bằng giọng nói và bắt đầu phát lại:
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 } }
Để xem ví dụ chi tiết hơn về cách triển khai tính năng tìm kiếm bằng giọng nói để phát âm thanh nội dung trong ứng dụng của bạn, hãy xem Universal Android Music Player mẫu.
Xử lý các truy vấn trống
Nếu là onPrepare()
, onPlay()
, onPrepareFromSearch()
hoặc onPlayFromSearch()
được gọi mà không có truy vấn tìm kiếm, ứng dụng đa phương tiện của bạn sẽ phát đoạn mã "hiện tại"
nội dung đa phương tiện. Nếu hiện không có nội dung nghe nhìn, ứng dụng sẽ cố gắng phát nội dung nào đó, chẳng hạn như
dưới dạng bài hát từ danh sách phát gần đây nhất hoặc hàng đợi ngẫu nhiên. Trợ lý sử dụng
các API này khi người dùng yêu cầu "Phát nhạc trên [tên ứng dụng của bạn]" mà không
thông tin bổ sung.
Khi người dùng nói "Phát nhạc trên [tên ứng dụng của bạn]", Android Automotive OS hoặc
Android Auto cố gắng chạy ứng dụng của bạn và phát âm thanh bằng cách gọi onPlayFromSearch()
của ứng dụng
. Tuy nhiên, vì người dùng không nói tên của mục nội dung đa phương tiện, nên thẻ onPlayFromSearch()
sẽ nhận được thông số truy vấn trống. Trong những trường hợp này, ứng dụng của bạn nên
phản hồi bằng cách phát ngay âm thanh, chẳng hạn như bài hát trong
danh sách phát hoặc một hàng đợi ngẫu nhiên.
Khai báo tính năng hỗ trợ cũ cho thao tác bằng giọng nói
Trong hầu hết các trường hợp, việc xử lý các thao tác phát được mô tả ở trên sẽ giúp ứng dụng của bạn có tất cả chức năng phát mà ứng dụng cần. Tuy nhiên, một số hệ thống yêu cầu ứng dụng của bạn phải chứa Bộ lọc ý định cho hoạt động tìm kiếm. Bạn nên khai báo tính năng hỗ trợ cho Ý định này trong các tệp kê khai của ứng dụng.
Đưa mã này vào tệp kê khai cho ứng dụng dành cho điện thoại:
<activity>
<intent-filter>
<action android:name=
"android.media.action.MEDIA_PLAY_FROM_SEARCH" />
<category android:name=
"android.intent.category.DEFAULT" />
</intent-filter>
</activity>
Phương thức điều khiển phương tiện giao thông
Sau khi phiên phát nội dung nghe nhìn của ứng dụng ở trạng thái đang hoạt động, Trợ lý có thể đưa ra lệnh thoại để điều khiển chế độ phát và cập nhật siêu dữ liệu của nội dung nghe nhìn. Để làm được điều này, mã sẽ cho phép các hành động sau đây và triển khai các mã tương ứng lệnh gọi lại:
Hành động | Lệnh gọi lại | Mô tả |
---|---|---|
ACTION_SKIP_TO_NEXT |
onSkipToNext() |
Video tiếp theo |
ACTION_SKIP_TO_PREVIOUS |
onSkipToPrevious() |
Bài hát trước |
ACTION_PAUSE, ACTION_PLAY_PAUSE |
onPause() |
Tạm dừng |
ACTION_STOP |
onStop() |
Dừng |
ACTION_PLAY |
onPlay() |
Tiếp tục |
ACTION_SEEK_TO |
onSeekTo() |
Tua lại 30 giây |
ACTION_SET_RATING |
onSetRating(android.support.v4.media.RatingCompat) |
Thích/không thích. |
ACTION_SET_CAPTIONING_ENABLED |
onSetCaptioningEnabled(boolean) |
Bật/tắt phụ đề. |
Xin lưu ý:
- Để các lệnh tìm kiếm hoạt động,
PlaybackState
cần được cập nhật bằngstate, position, playback speed, and update time
. Ứng dụng phải gọisetPlaybackState()
khi trạng thái thay đổi. - Ứng dụng đa phương tiện cũng phải luôn cập nhật siêu dữ liệu của phiên phát nội dung đa phương tiện. Tính năng này hỗ trợ các câu hỏi như "bài hát nào đang phát?" Ứng dụng phải gọi
setMetadata()
khi các trường áp dụng (chẳng hạn như tên bản nhạc, nghệ sĩ và tên) thay đổi. - Bạn phải đặt
MediaSession.setRatingType()
để cho biết loại điểm xếp hạng mà ứng dụng hỗ trợ, đồng thời ứng dụng phải triển khaionSetRating()
. Nếu không hỗ trợ mức phân loại, thì ứng dụng phải đặt loại mức phân loại thànhRATING_NONE
.
Các thao tác bằng giọng nói mà bạn hỗ trợ có thể sẽ thay đổi theo loại nội dung.
Loại nội dung | Việc cần làm |
---|---|
Âm nhạc |
Phải hỗ trợ: Phát, Tạm dừng, Dừng, Chuyển đến Tiếp theo và Chuyển đến Trước Rất nên hỗ trợ các tính năng: Tua đến |
Podcast |
Phải hỗ trợ: Phát, Tạm dừng, Dừng và Tua đến Đề xuất hỗ trợ cho: Bỏ qua đến Tiếp theo và Bỏ qua đến Trước |
Sách nói | Phải hỗ trợ: Phát, Tạm dừng, Dừng và Tua đến |
Đài | Phải hỗ trợ: Phát, Tạm dừng và Dừng |
Tin tức | Phải hỗ trợ: Phát, Tạm dừng, Dừng, Chuyển đến Tiếp theo và Chuyển đến Trước |
Video |
Phải hỗ trợ: Phát, Tạm dừng, Dừng, Tua đến, Tua lại và Tua nhanh Rất nên hỗ trợ các tính năng: Chuyển đến Tiếp theo và Chuyển đến Trước |
Bạn phải hỗ trợ nhiều hành động nêu trên giống như các dịch vụ sản phẩm của mình cho phép nhưng vẫn phản hồi linh hoạt với mọi thao tác khác. Ví dụ: nếu chỉ người dùng cao cấp có thể quay lại mục trước đó, bạn có thể tăng lỗi nếu người dùng cấp miễn phí yêu cầu Trợ lý trả lại mặt hàng trước đó. Hãy xem mục xử lý lỗi để biết thêm hướng dẫn.
Thử truy vấn bằng giọng nói mẫu
Bảng sau đây trình bày một số truy vấn mẫu mà bạn nên sử dụng khi kiểm thử kết quả triển khai:
Lệnh gọi lại MediaSession | Cụm từ “Ok Google” để sử dụng | |
---|---|---|
onPlay() |
“Chơi”. "Tiếp tục." |
|
onPlayFromSearch()
onPlayFromUri() |
Music |
“Phát nhạc hoặc bài hát trên (tên ứng dụng).” Đây là một truy vấn trống. “Phát (bài hát | nghệ sĩ | đĩa nhạc | thể loại | danh sách phát) trên (tên ứng dụng).” |
Đài | “Phát (tần số | đài) trên (tên ứng dụng).” | |
Audiobook |
"Đọc sách nói của tôi trên (tên ứng dụng)." "Đọc (sách nói) trên (tên ứng dụng)." |
|
Podcast | "Phát (podcast) trên (tên ứng dụng)." | |
onPause() |
"Tạm dừng." | |
onStop() |
"Dừng." | |
onSkipToNext() |
"Next (bài hát | tập | bản nhạc)." | |
onSkipToPrevious() |
"Trước (bài hát | tập | bản nhạc)." | |
onSeekTo() |
"Khởi động lại". “Tua đi ## giây.” “Quay lại ## phút.” |
|
Không áp dụng (hãy lưu
MediaMetadata
đã cập nhật) |
“Tôi đang nghe gì thế?” |
Lỗi
Trợ lý xử lý lỗi trong một phiên nội dung nghe nhìn khi phiên đó xảy ra rồi báo cáo
cho người dùng. Hãy đảm bảo rằng phiên phát nội dung đa phương tiện cập nhật trạng thái truyền tải và
mã lỗi trong PlaybackState
của mình một cách chính xác, như được mô tả trong Làm việc với một
phiên phát nội dung nghe nhìn. Trợ lý
nhận ra tất cả mã lỗi được trả về bởi
getErrorCode()
.
Các trường hợp thường bị xử lý sai
Sau đây là một số ví dụ về các trường hợp lỗi mà bạn nên đảm bảo mình đang xử lý chính xác:
- Người dùng cần đăng nhập
- Đặt mã lỗi
PlaybackState
thànhERROR_CODE_AUTHENTICATION_EXPIRED
. - Đặt thông báo lỗi
PlaybackState
. - Nếu cần để phát, hãy đặt trạng thái
PlaybackState
thànhSTATE_ERROR
. nếu không, hãy giữ nguyên phần còn lại củaPlaybackState
.
- Đặt mã lỗi
- Người dùng yêu cầu một thao tác không thực hiện được
- Đặt mã lỗi
PlaybackState
một cách thích hợp. Ví dụ: đặt giá trịPlaybackState
đếnERROR_CODE_NOT_SUPPORTED
nếu thao tác này không được hỗ trợ hoặcERROR_CODE_PREMIUM_ACCOUNT_REQUIRED
nếu hành động này được bảo vệ bằng cách đăng nhập. - Đặt thông báo lỗi
PlaybackState
. - Giữ nguyên phần còn lại của
PlaybackState
.
- Đặt mã lỗi
- Người dùng yêu cầu nội dung không có trong ứng dụng
- Đặt mã lỗi
PlaybackState
một cách thích hợp. Ví dụ: hãy sử dụngERROR_CODE_NOT_AVAILABLE_IN_REGION
. - Đặt thông báo lỗi
PlaybackState
. - Đặt trạng thái
PlaybackSate
thànhSTATE_ERROR
để làm gián đoạn quá trình phát. nếu không, hãy giữ nguyên phần còn lại củaPlaybackState
.
- Đặt mã lỗi
- Người dùng yêu cầu nội dung không có kết quả khớp chính xác. Ví dụ: một
người dùng cấp miễn phí yêu cầu nội dung chỉ dành cho người dùng cấp cao cấp.
- Bạn không nên trả về lỗi mà nên ưu tiên đang tìm nội dung tương tự để chơi. Trợ lý sẽ xử lý việc nói nhiều nhất phản hồi bằng giọng nói phù hợp trước khi bắt đầu phát.
Phát lại có ý định
Trợ lý có thể chạy ứng dụng âm thanh hoặc video và bắt đầu phát bằng cách gửi một ý định bằng một đường liên kết sâu.
Ý định và đường liên kết sâu của ý định có thể đến từ nhiều nguồn:
- Khi Trợ lý khởi động một ứng dụng di động, ứng dụng này có thể sử dụng Google Tìm kiếm để truy xuất nội dung đã đánh dấu cung cấp hành động xem kèm theo một đường liên kết.
- Khi Trợ lý khởi động một ứng dụng TV, ứng dụng đó phải bao gồm
Nhà cung cấp dịch vụ tìm kiếm truyền hình
để hiển thị URI cho nội dung đa phương tiện. Trợ lý gửi truy vấn đến
trình cung cấp nội dung sẽ trả về một ý định chứa URI cho liên kết sâu và
thao tác không bắt buộc.
Nếu truy vấn trả về một thao tác trong ý định,
Trợ lý sẽ gửi hành động đó và URI trở lại ứng dụng của bạn.
Nếu nhà cung cấp không chỉ định
một hành động, Trợ lý sẽ thêm
ACTION_VIEW
vào Intent.
Trợ lý sẽ thêm EXTRA_START_PLAYBACK
bổ sung với giá trị true
đến ý định mà ứng dụng gửi đến ứng dụng của bạn. Ứng dụng của bạn sẽ bắt đầu phát khi
nhận một ý định bằng EXTRA_START_PLAYBACK
.
Xử lý ý định trong khi hoạt động
Người dùng có thể yêu cầu Trợ lý phát nội dung nào đó trong khi ứng dụng của bạn vẫn đang phát nội dung từ yêu cầu trước đó. Tức là ứng dụng của bạn có thể nhận được các ý định mới để bắt đầu phát trong khi hoạt động phát của ứng dụng đã được khởi chạy và đang hoạt động.
Các hoạt động hỗ trợ ý định có đường liên kết sâu phải ghi đè
onNewIntent()
để xử lý các yêu cầu mới.
Khi bắt đầu phát, Trợ lý có thể thêm
cờ
đến ý định mà ứng dụng gửi đến ứng dụng của bạn. Cụ thể, tính năng này có thể thêm
FLAG_ACTIVITY_CLEAR_TOP
hoặc
FLAG_ACTIVITY_NEW_TASK
hoặc cả hai. Mặc dù mã của bạn
không cần xử lý những cờ này mà hệ thống Android sẽ phản hồi chúng.
Điều này có thể ảnh hưởng đến hành vi của ứng dụng khi có yêu cầu phát thứ hai kèm theo URI mới
trong khi URI trước đó vẫn đang phát. Bạn nên kiểm thử cách ứng dụng phản hồi trong trường hợp này. Bạn có thể dùng lệnh adb
công cụ dòng để mô phỏng tình huống (hằng số 0x14000000
là boolean bitwise HOẶC của hai cờ):
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
Phát từ một dịch vụ
Nếu ứng dụng của bạn có
media browser service
cho phép kết nối từ Trợ lý,
Trợ lý có thể khởi động ứng dụng bằng cách giao tiếp với
media session
.
Dịch vụ trình duyệt nội dung đa phương tiện không được chạy Hoạt động.
Trợ lý sẽ chạy Hoạt động của bạn dựa trên PendingIntent
mà bạn xác định
bằng setSessionActivity().
Hãy nhớ đặt MediaSession.Token khi bạn khởi chạy dịch vụ trình duyệt nội dung đa phương tiện. Nhớ đặt các thao tác phát được hỗ trợ mọi lúc, kể cả trong quá trình khởi chạy. Trợ lý mong đợi nội dung nghe nhìn của bạn để thiết lập hành động phát trước khi Trợ lý gửi lần phát đầu tiên .
Để bắt đầu từ một dịch vụ, Trợ lý sẽ triển khai các API ứng dụng trình duyệt nội dung đa phương tiện. Thư viện này thực hiện các lệnh gọi TransportControls để kích hoạt lệnh gọi lại hành động PLAY trên phiên đa phương tiện của ứng dụng.
Biểu đồ dưới đây hiển thị thứ tự các cuộc gọi do Trợ lý tạo ra và lệnh gọi lại phiên đa phương tiện tương ứng. (Chỉ gửi lệnh gọi lại chuẩn bị nếu ứng dụng của bạn hỗ trợ các ngôn ngữ đó.) Tất cả các lệnh gọi đều không đồng bộ. Trợ lý không chờ phản hồi của ứng dụng.
Khi người dùng ra lệnh thoại để phát, Trợ lý sẽ phản hồi bằng một thông báo ngắn. Ngay khi thông báo hoàn tất, Trợ lý sẽ đưa ra hành động PLAY. Nó không chờ bất kỳ trạng thái phát cụ thể nào.
Nếu ứng dụng của bạn hỗ trợ các hành động ACTION_PREPARE_*
, Trợ lý sẽ gọi hành động PREPARE
trước khi bắt đầu thông báo.
Kết nối với MediaBrowserService
Để dùng một dịch vụ nhằm khởi động ứng dụng của bạn, Trợ lý phải có khả năng kết nối với MediaBrowserService và
truy xuất MediaSession.Token của phương thức đó. Yêu cầu kết nối được xử lý trong
onGetRoot()
. Có hai cách để xử lý yêu cầu:
- Chấp nhận tất cả yêu cầu kết nối
- Chỉ chấp nhận các yêu cầu kết nối từ ứng dụng Trợ lý
Chấp nhận tất cả yêu cầu kết nối
Bạn phải trả về BrowserRoot để cho phép Trợ lý gửi lệnh đến phiên phát nội dung đa phương tiện của bạn. Cách dễ nhất là cho phép tất cả ứng dụng MediaBrowser kết nối với MediaBrowserService. Bạn phải trả về một BrowserRoot không rỗng. Dưới đây là mã hiện hành từ 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... }
Chấp nhận gói ứng dụng Trợ lý và chữ ký
Bạn có thể cho phép Trợ lý kết nối với dịch vụ trình duyệt nội dung đa phương tiện của mình một cách rõ ràng bằng cách kiểm tra tên gói và chữ ký của Trợ lý. Ứng dụng của bạn sẽ nhận được tên gói trong phương thức onGetRoot của MediaBrowserService. Bạn phải trả về BrowserRoot để cho phép Trợ lý gửi lệnh đến phiên phát nội dung đa phương tiện của bạn. Chiến lược phát hành đĩa đơn Trình phát nhạc Universal Music mẫu sẽ duy trì danh sách tên và chữ ký gói đã biết. Dưới đây là tên gói và chữ ký mà Trợ lý Google sử dụng.
<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>