Các nút điều khiển nội dung nghe nhìn trong Android nằm gần trình đơn Cài đặt nhanh. Phiên từ nhiều ứng dụng được sắp xếp trong một băng chuyền có thể vuốt qua. Băng chuyền liệt kê các phiên theo thứ tự sau:
- Các luồng phát trên điện thoại
- Luồng từ xa, chẳng hạn như luồng phát hiện thấy trên thiết bị bên ngoài hoặc phiên truyền
- Các phiên có thể tiếp tục trước đó, theo thứ tự phát lần gần đây nhất
Kể từ Android 13 (API cấp 33), để đảm bảo người dùng có thể truy cập vào
bộ nút điều khiển nội dung nghe nhìn cho các ứng dụng phát nội dung nghe nhìn, nút hành động trên chế độ điều khiển nội dung nghe nhìn
đều bắt nguồn từ trạng thái Player
.
Bằng cách này, bạn có thể trình bày một bộ nút điều khiển nội dung nghe nhìn nhất quán và tinh tế hơn trải nghiệm điều khiển nội dung nghe nhìn trên các thiết bị.
Hình 1 minh hoạ một ví dụ về giao diện trên điện thoại và máy tính bảng. .
Hệ thống sẽ hiển thị tối đa 5 nút hành động dựa trên trạng thái Player
là
được mô tả trong bảng sau. Ở chế độ thu gọn, chỉ 3 hành động đầu tiên
vị trí được hiển thị. Điều này phù hợp với cách hiển thị các chế độ điều khiển nội dung nghe nhìn trong các
Các nền tảng Android như Auto, Trợ lý và Wear OS.
Khe | Tiêu chí | Hành động |
---|---|---|
1 |
playWhenReady
là false hoặc phát lại hiện tại
trạng tháilà STATE_ENDED .
|
Phát |
playWhenReady là đúng và trạng thái phát hiện tại là STATE_BUFFERING .
|
Vòng quay đang tải | |
playWhenReady là đúng và trạng thái phát hiện tại là STATE_READY . |
Tạm dừng | |
2 | Người chơi có thể dùng lệnh COMMAND_SEEK_TO_PREVIOUS hoặc COMMAND_SEEK_TO_PREVIOUS_MEDIA_ITEM . |
Trước |
Hiện không có lệnh người chơi nào COMMAND_SEEK_TO_PREVIOUS và COMMAND_SEEK_TO_PREVIOUS_MEDIA_ITEM . Lệnh tuỳ chỉnh từ bố cục tuỳ chỉnh chưa được đặt sẽ có sẵn để lấp đầy ô. |
Điều chỉnh | |
(chưa hỗ trợ với Media3) PlaybackState thông tin bổ sung bao gồm giá trị boolean true cho khoá EXTRAS_KEY_SLOT_RESERVATION_SEEK_TO_PREV . |
Trống | |
3 | Người chơi có thể dùng lệnh COMMAND_SEEK_TO_NEXT hoặc COMMAND_SEEK_TO_NEXT_MEDIA_ITEM . |
Tiếp theo |
Hiện không có lệnh người chơi nào COMMAND_SEEK_TO_NEXT và COMMAND_SEEK_TO_NEXT_MEDIA_ITEM . Lệnh tuỳ chỉnh từ bố cục tuỳ chỉnh chưa được đặt sẽ có sẵn để lấp đầy ô. |
Điều chỉnh | |
(chưa hỗ trợ với Media3) PlaybackState thông tin bổ sung bao gồm giá trị boolean true cho khoá EXTRAS_KEY_SLOT_RESERVATION_SEEK_TO_NEXT . |
Trống | |
4 | Có sẵn một lệnh tuỳ chỉnh từ bố cục tuỳ chỉnh chưa được đặt để lấp đầy vị trí. | Điều chỉnh |
5 | Có sẵn một lệnh tuỳ chỉnh từ bố cục tuỳ chỉnh chưa được đặt để lấp đầy vị trí. | Điều chỉnh |
Các lệnh tuỳ chỉnh được đặt theo thứ tự mà chúng được thêm vào bố cục tuỳ chỉnh.
Tuỳ chỉnh nút lệnh
Để tuỳ chỉnh các chế độ điều khiển nội dung nghe nhìn của hệ thống bằng Jetpack Media3,
bạn có thể đặt bố cục tùy chỉnh của phiên và các lệnh có sẵn của
cho phù hợp, khi triển khai MediaSessionService
:
Trong
onCreate()
, hãy tạo mộtMediaSession
và xác định bố cục tuỳ chỉnh nút lệnh.Tại
MediaSession.Callback.onConnect()
, cho phép đơn vị kiểm soát bằng cách xác định các lệnh có sẵn, bao gồm lệnh tuỳ chỉnh, trongConnectionResult
.Tại
MediaSession.Callback.onCustomCommand()
, phản hồi lệnh tuỳ chỉnh đang được người dùng chọn.
Kotlin
class PlaybackService : MediaSessionService() { private val customCommandFavorites = SessionCommand(ACTION_FAVORITES, Bundle.EMPTY) private var mediaSession: MediaSession? = null override fun onCreate() { super.onCreate() val favoriteButton = CommandButton.Builder() .setDisplayName("Save to favorites") .setIconResId(R.drawable.favorite_icon) .setSessionCommand(customCommandFavorites) .build() val player = ExoPlayer.Builder(this).build() // Build the session with a custom layout. mediaSession = MediaSession.Builder(this, player) .setCallback(MyCallback()) .setCustomLayout(ImmutableList.of(favoriteButton)) .build() } private inner class MyCallback : MediaSession.Callback { override fun onConnect( session: MediaSession, controller: MediaSession.ControllerInfo ): ConnectionResult { // Set available player and session commands. return AcceptedResultBuilder(session) .setAvailablePlayerCommands( ConnectionResult.DEFAULT_PLAYER_COMMANDS.buildUpon() .remove(COMMAND_SEEK_TO_NEXT) .remove(COMMAND_SEEK_TO_NEXT_MEDIA_ITEM) .remove(COMMAND_SEEK_TO_PREVIOUS) .remove(COMMAND_SEEK_TO_PREVIOUS_MEDIA_ITEM) .build() ) .setAvailableSessionCommands( ConnectionResult.DEFAULT_SESSION_COMMANDS.buildUpon() .add(customCommandFavorites) .build() ) .build() } override fun onCustomCommand( session: MediaSession, controller: MediaSession.ControllerInfo, customCommand: SessionCommand, args: Bundle ): ListenableFuture{ if (customCommand.customAction == ACTION_FAVORITES) { // Do custom logic here saveToFavorites(session.player.currentMediaItem) return Futures.immediateFuture(SessionResult(SessionResult.RESULT_SUCCESS)) } return super.onCustomCommand(session, controller, customCommand, args) } } }
Java
public class PlaybackService extends MediaSessionService { private static final SessionCommand CUSTOM_COMMAND_FAVORITES = new SessionCommand("ACTION_FAVORITES", Bundle.EMPTY); @Nullable private MediaSession mediaSession; public void onCreate() { super.onCreate(); CommandButton favoriteButton = new CommandButton.Builder() .setDisplayName("Save to favorites") .setIconResId(R.drawable.favorite_icon) .setSessionCommand(CUSTOM_COMMAND_FAVORITES) .build(); Player player = new ExoPlayer.Builder(this).build(); // Build the session with a custom layout. mediaSession = new MediaSession.Builder(this, player) .setCallback(new MyCallback()) .setCustomLayout(ImmutableList.of(favoriteButton)) .build(); } private static class MyCallback implements MediaSession.Callback { @Override public ConnectionResult onConnect( MediaSession session, MediaSession.ControllerInfo controller) { // Set available player and session commands. return new AcceptedResultBuilder(session) .setAvailablePlayerCommands( ConnectionResult.DEFAULT_PLAYER_COMMANDS.buildUpon() .remove(COMMAND_SEEK_TO_NEXT) .remove(COMMAND_SEEK_TO_NEXT_MEDIA_ITEM) .remove(COMMAND_SEEK_TO_PREVIOUS) .remove(COMMAND_SEEK_TO_PREVIOUS_MEDIA_ITEM) .build()) .setAvailableSessionCommands( ConnectionResult.DEFAULT_SESSION_COMMANDS.buildUpon() .add(CUSTOM_COMMAND_FAVORITES) .build()) .build(); } public ListenableFutureonCustomCommand( MediaSession session, MediaSession.ControllerInfo controller, SessionCommand customCommand, Bundle args) { if (customCommand.customAction.equals(CUSTOM_COMMAND_FAVORITES.customAction)) { // Do custom logic here saveToFavorites(session.getPlayer().getCurrentMediaItem()); return Futures.immediateFuture(new SessionResult(SessionResult.RESULT_SUCCESS)); } return MediaSession.Callback.super.onCustomCommand( session, controller, customCommand, args); } } }
Để tìm hiểu thêm về cách định cấu hình MediaSession
để các ứng dụng như
hệ thống có thể kết nối với ứng dụng đa phương tiện của bạn, hãy xem
Cấp quyền kiểm soát cho các ứng dụng khách khác.
Với Jetpack Media3, khi bạn triển khai MediaSession
, PlaybackState
của bạn
sẽ được tự động cập nhật với trình phát nội dung đa phương tiện. Tương tự, khi bạn
triển khai MediaSessionService
, thư viện sẽ tự động xuất bản một
Thông báo MediaStyle
cho bạn và luôn cập nhật thông tin đó.
Nút phản hồi hành động
Khi người dùng nhấn vào một nút hành động trên thanh điều khiển nội dung nghe nhìn của hệ thống,
MediaController
gửi lệnh phát đến MediaSession
của bạn. Chiến lược phát hành đĩa đơn
Sau đó, MediaSession
uỷ quyền các lệnh đó cho người chơi. Lệnh
được xác định trong Player
của Media3
do phương tiện truyền thông xử lý tự động
phiên hoạt động.
Hãy tham khảo bài viết Thêm lệnh tuỳ chỉnh để xem hướng dẫn về cách phản hồi một lệnh tuỳ chỉnh.
Hành vi trước Android 13
Để có khả năng tương thích ngược, Giao diện người dùng hệ thống sẽ tiếp tục cung cấp một bố cục thay thế
sử dụng thao tác thông báo đối với những ứng dụng không cập nhật để nhắm đến Android 13,
hoặc không bao gồm thông tin PlaybackState
. Các nút hành động
bắt nguồn từ danh sách Notification.Action
đính kèm với MediaStyle
. Hệ thống sẽ hiển thị tối đa 5 thao tác theo thứ tự mà chúng
đã được thêm. Ở chế độ thu gọn, tối đa 3 nút được hiển thị, được xác định bởi
được truyền vào setShowActionsInCompactView()
.
Các hành động tuỳ chỉnh được đặt theo thứ tự mà chúng được thêm vào
PlaybackState
.
Ví dụ về mã sau minh hoạ cách thêm các thao tác vào MediaStyle thông báo :
Kotlin
import androidx.core.app.NotificationCompat import androidx.media3.session.MediaStyleNotificationHelper var notification = NotificationCompat.Builder(context, CHANNEL_ID) // Show controls on lock screen even when user hides sensitive content. .setVisibility(NotificationCompat.VISIBILITY_PUBLIC) .setSmallIcon(R.drawable.ic_stat_player) // Add media control buttons that invoke intents in your media service .addAction(R.drawable.ic_prev, "Previous", prevPendingIntent) // #0 .addAction(R.drawable.ic_pause, "Pause", pausePendingIntent) // #1 .addAction(R.drawable.ic_next, "Next", nextPendingIntent) // #2 // Apply the media style template .setStyle(MediaStyleNotificationHelper.MediaStyle(mediaSession) .setShowActionsInCompactView(1 /* #1: pause button */)) .setContentTitle("Wonderful music") .setContentText("My Awesome Band") .setLargeIcon(albumArtBitmap) .build()
Java
import androidx.core.app.NotificationCompat; import androidx.media3.session.MediaStyleNotificationHelper; NotificationCompat.Builder notification = new NotificationCompat.Builder(context, CHANNEL_ID) .setVisibility(NotificationCompat.VISIBILITY_PUBLIC) .setSmallIcon(R.drawable.ic_stat_player) .addAction(R.drawable.ic_prev, "Previous", prevPendingIntent) .addAction(R.drawable.ic_pause, "Pause", pausePendingIntent) .addAction(R.drawable.ic_next, "Next", nextPendingIntent) .setStyle(new MediaStyleNotificationHelper.MediaStyle(mediaSession) .setShowActionsInCompactView(1 /* #1: pause button */)) .setContentTitle("Wonderful music") .setContentText("My Awesome Band") .setLargeIcon(albumArtBitmap) .build();
Hỗ trợ tiếp tục phát nội dung nghe nhìn
Việc tiếp tục phát nội dung nghe nhìn cho phép người dùng bắt đầu lại các phiên trước đó qua băng chuyền mà không phải khởi động ứng dụng. Khi quá trình phát bắt đầu, người dùng sẽ tương tác với các nút điều khiển nội dung đa phương tiện theo cách thông thường.
Bạn có thể bật và tắt tính năng tiếp tục phát bằng ứng dụng Cài đặt, trong Âm thanh > Tuỳ chọn nội dung đa phương tiện. Người dùng cũng có thể truy cập vào phần Cài đặt bằng cách nhấn vào biểu tượng bánh răng xuất hiện sau khi vuốt trên băng chuyền mở rộng.
Media3 cung cấp các API để hỗ trợ việc tiếp tục phát nội dung nghe nhìn dễ dàng hơn. Xem Tiếp tục phát bằng Media3 tài liệu để được hướng dẫn triển khai tính năng này.
Sử dụng API đa phương tiện cũ
Phần này giải thích cách tích hợp với các chế độ điều khiển nội dung nghe nhìn của hệ thống bằng cách sử dụng các API MediaCompat cũ.
Hệ thống truy xuất thông tin sau từ
MediaMetadata
của MediaSession
và hiển thị khi có:
METADATA_KEY_ALBUM_ART_URI
METADATA_KEY_TITLE
METADATA_KEY_DISPLAY_TITLE
METADATA_KEY_ARTIST
METADATA_KEY_DURATION
(Nếu bạn không đặt thời lượng, thì thanh tìm kiếm không hiển thị tiến trình)
Để đảm bảo bạn có thông báo hợp lệ và chính xác về chế độ điều khiển nội dung nghe nhìn,
đặt giá trị của METADATA_KEY_TITLE
hoặc METADATA_KEY_DISPLAY_TITLE
siêu dữ liệu vào tiêu đề của nội dung đa phương tiện hiện đang được phát.
Trình phát nội dung đa phương tiện cho biết thời lượng đã trôi qua của quảng cáo đang phát
nội dung đa phương tiện cùng với thanh tua được liên kết với MediaSession
PlaybackState
Trình phát nội dung đa phương tiện hiển thị tiến trình của nội dung nghe nhìn đang phát cùng với
một thanh tua được liên kết với MediaSession
PlaybackState
. Thanh tua
cho phép người dùng thay đổi vị trí và hiển thị thời gian đã trôi qua cho nội dung nghe nhìn
mục. Để bật thanh tua, bạn phải triển khai
PlaybackState.Builder#setActions
và bao gồm ACTION_SEEK_TO
.
Khe | Hành động | Tiêu chí |
---|---|---|
1 | Phát |
Trạng thái hiện tại của PlaybackState là một trong những trạng thái sau:
|
Vòng quay đang tải |
Trạng thái hiện tại của PlaybackState là một trong những trạng thái sau:
|
|
Tạm dừng | Trạng thái hiện tại của PlaybackState không phải trạng thái nào ở trên. |
|
2 | Trước | Các hành động PlaybackState bao gồm ACTION_SKIP_TO_PREVIOUS . |
Điều chỉnh | PlaybackState Thao tác không bao gồm ACTION_SKIP_TO_PREVIOUS và PlaybackState thao tác tuỳ chỉnh bao gồm một thao tác tuỳ chỉnh chưa được đặt. |
|
Trống | Thông tin bổ sung PlaybackState bao gồm giá trị boolean true cho khoá SESSION_EXTRAS_KEY_SLOT_RESERVATION_SKIP_TO_PREV . |
|
3 | Tiếp theo | Các hành động PlaybackState bao gồm ACTION_SKIP_TO_NEXT . |
Điều chỉnh | PlaybackState Thao tác không bao gồm ACTION_SKIP_TO_NEXT và PlaybackState thao tác tuỳ chỉnh bao gồm một thao tác tuỳ chỉnh chưa được đặt. |
|
Trống | Thông tin bổ sung PlaybackState bao gồm giá trị boolean true cho khoá SESSION_EXTRAS_KEY_SLOT_RESERVATION_SKIP_TO_NEXT . |
|
4 | Điều chỉnh | PlaybackState hành động tuỳ chỉnh bao gồm một hành động tuỳ chỉnh chưa được đặt. |
5 | Điều chỉnh | PlaybackState hành động tuỳ chỉnh bao gồm một hành động tuỳ chỉnh chưa được đặt. |
Thêm các thao tác chuẩn
Các mã ví dụ sau đây minh hoạ cách thêm tiêu chuẩn PlaybackState
và
thao tác tuỳ chỉnh.
Để phát, tạm dừng, trước đó và tiếp theo, hãy đặt những hành động này trong
PlaybackState
cho phiên phát nội dung đa phương tiện.
Kotlin
val session = MediaSessionCompat(context, TAG) val playbackStateBuilder = PlaybackStateCompat.Builder() val style = NotificationCompat.MediaStyle() // For this example, the media is currently paused: val state = PlaybackStateCompat.STATE_PAUSED val position = 0L val playbackSpeed = 1f playbackStateBuilder.setState(state, position, playbackSpeed) // And the user can play, skip to next or previous, and seek val stateActions = PlaybackStateCompat.ACTION_PLAY or PlaybackStateCompat.ACTION_PLAY_PAUSE or PlaybackStateCompat.ACTION_SKIP_TO_PREVIOUS or PlaybackStateCompat.ACTION_SKIP_TO_NEXT or PlaybackStateCompat.ACTION_SEEK_TO // adding the seek action enables seeking with the seekbar playbackStateBuilder.setActions(stateActions) // ... do more setup here ... session.setPlaybackState(playbackStateBuilder.build()) style.setMediaSession(session.sessionToken) notificationBuilder.setStyle(style)
Java
MediaSessionCompat session = new MediaSessionCompat(context, TAG); PlaybackStateCompat.Builder playbackStateBuilder = new PlaybackStateCompat.Builder(); NotificationCompat.MediaStyle style = new NotificationCompat.MediaStyle(); // For this example, the media is currently paused: int state = PlaybackStateCompat.STATE_PAUSED; long position = 0L; float playbackSpeed = 1f; playbackStateBuilder.setState(state, position, playbackSpeed); // And the user can play, skip to next or previous, and seek long stateActions = PlaybackStateCompat.ACTION_PLAY | PlaybackStateCompat.ACTION_PLAY_PAUSE | PlaybackStateCompat.ACTION_SKIP_TO_PREVIOUS | PlaybackStateCompat.ACTION_SKIP_TO_NEXT | PlaybackStateCompat.ACTION_SEEK_TO; // adding this enables the seekbar thumb playbackStateBuilder.setActions(stateActions); // ... do more setup here ... session.setPlaybackState(playbackStateBuilder.build()); style.setMediaSession(session.getSessionToken()); notificationBuilder.setStyle(style);
Nếu bạn không muốn thêm nút nào ở vị trí trước đó hoặc vị trí tiếp theo thì đừng thêm nút
ACTION_SKIP_TO_PREVIOUS
hoặc ACTION_SKIP_TO_NEXT
. Thay vào đó, hãy thêm các ứng dụng khác vào
phiên hoạt động:
Kotlin
session.setExtras(Bundle().apply { putBoolean(SESSION_EXTRAS_KEY_SLOT_RESERVATION_SKIP_TO_PREV, true) putBoolean(SESSION_EXTRAS_KEY_SLOT_RESERVATION_SKIP_TO_NEXT, true) })
Java
Bundle extras = new Bundle(); extras.putBoolean(SESSION_EXTRAS_KEY_SLOT_RESERVATION_SKIP_TO_PREV, true); extras.putBoolean(SESSION_EXTRAS_KEY_SLOT_RESERVATION_SKIP_TO_NEXT, true); session.setExtras(extras);
Thêm thao tác tuỳ chỉnh
Đối với các hành động khác mà bạn muốn hiển thị trên các nút điều khiển nội dung nghe nhìn, bạn có thể tạo một
PlaybackStateCompat.CustomAction
rồi thêm vào PlaybackState
. Bạn có thể xem những hành động này trong
thứ tự thêm vào.
Kotlin
val customAction = PlaybackStateCompat.CustomAction.Builder( "com.example.MY_CUSTOM_ACTION", // action ID "Custom Action", // title - used as content description for the button R.drawable.ic_custom_action ).build() playbackStateBuilder.addCustomAction(customAction)
Java
PlaybackStateCompat.CustomAction customAction = new PlaybackStateCompat.CustomAction.Builder( "com.example.MY_CUSTOM_ACTION", // action ID "Custom Action", // title - used as content description for the button R.drawable.ic_custom_action ).build(); playbackStateBuilder.addCustomAction(customAction);
Phản hồi các thao tác PlaybackState
Khi người dùng nhấn vào một nút, SystemUI sẽ sử dụng
MediaController.TransportControls
để gửi lại một lệnh cho MediaSession
. Bạn cần đăng ký một cuộc gọi lại
có thể phản hồi chính xác với những sự kiện này.
Kotlin
val callback = object: MediaSession.Callback() { override fun onPlay() { // start playback } override fun onPause() { // pause playback } override fun onSkipToPrevious() { // skip to previous } override fun onSkipToNext() { // skip to next } override fun onSeekTo(pos: Long) { // jump to position in track } override fun onCustomAction(action: String, extras: Bundle?) { when (action) { CUSTOM_ACTION_1 -> doCustomAction1(extras) CUSTOM_ACTION_2 -> doCustomAction2(extras) else -> { Log.w(TAG, "Unknown custom action $action") } } } } session.setCallback(callback)
Java
MediaSession.Callback callback = new MediaSession.Callback() { @Override public void onPlay() { // start playback } @Override public void onPause() { // pause playback } @Override public void onSkipToPrevious() { // skip to previous } @Override public void onSkipToNext() { // skip to next } @Override public void onSeekTo(long pos) { // jump to position in track } @Override public void onCustomAction(String action, Bundle extras) { if (action.equals(CUSTOM_ACTION_1)) { doCustomAction1(extras); } else if (action.equals(CUSTOM_ACTION_2)) { doCustomAction2(extras); } else { Log.w(TAG, "Unknown custom action " + action); } } };
Tiếp tục phát nội dung nghe nhìn
Để ứng dụng trình phát của bạn xuất hiện trong phần cài đặt nhanh,
bạn phải tạo thông báo MediaStyle
có mã thông báo MediaSession
hợp lệ.
Để hiển thị tiêu đề cho thông báo MediaStyle, hãy sử dụng
NotificationBuilder.setContentTitle()
.
Để hiển thị biểu tượng thương hiệu cho trình phát nội dung đa phương tiện, hãy sử dụng
NotificationBuilder.setSmallIcon()
.
Để cho phép tiếp tục phát, các ứng dụng phải triển khai một MediaBrowserService
và MediaSession
. MediaSession
của bạn phải triển khai lệnh gọi lại onPlay()
.
Cách triển khai MediaBrowserService
Sau khi thiết bị khởi động, hệ thống sẽ tìm 5 nội dung nghe nhìn được dùng gần đây nhất ứng dụng và cung cấp các nút điều khiển có thể dùng để bắt đầu lại quá trình phát từ mỗi ứng dụng.
Hệ thống cố gắng kết nối với MediaBrowserService
của bạn bằng kết nối từ
Giao diện người dùng hệ thống. Ứng dụng của bạn phải cho phép các kết nối như vậy, nếu không thì không thể hỗ trợ
tiếp tục phát.
Bạn có thể xác định và xác minh các kết nối từ SystemUI bằng tên gói
com.android.systemui
và chữ ký. SystemUI được ký bằng nền tảng
của bạn. Ví dụ về cách kiểm tra chữ ký của nền tảng có thể là
trong ứng dụng UAMP.
Để hỗ trợ tiếp tục phát, MediaBrowserService
của bạn phải
triển khai các hành vi này:
onGetRoot()
phải nhanh chóng trả về một gốc khác rỗng. Logic phức tạp khác phải được xử lý trongonLoadChildren()
Thời gian
onLoadChildren()
được gọi trên mã nhận dạng nội dung nghe nhìn gốc, kết quả phải chứa FLAG_PLAYABLE trẻ.MediaBrowserService
sẽ trả về mục nội dung nghe nhìn được phát gần đây nhất khi họ sẽ nhận được một PHẦN GẦN ĐÂY truy vấn. Giá trị được trả về phải là một mục nội dung đa phương tiện thực tế thay vì mục nội dung chung .MediaBrowserService
phải cung cấp MediaDescription có một phần không trống title và phụ đề. Trình quản lý thẻ cũng sẽ đặt URI biểu tượng hoặc Bitmap biểu tượng.
Các mã ví dụ sau đây minh hoạ cách triển khai onGetRoot()
.
Kotlin
override fun onGetRoot( clientPackageName: String, clientUid: Int, rootHints: Bundle? ): BrowserRoot? { ... // Verify that the specified package is SystemUI. You'll need to write your // own logic to do this. if (isSystem(clientPackageName, clientUid)) { rootHints?.let { if (it.getBoolean(BrowserRoot.EXTRA_RECENT)) { // Return a tree with a single playable media item for resumption. val extras = Bundle().apply { putBoolean(BrowserRoot.EXTRA_RECENT, true) } return BrowserRoot(MY_RECENTS_ROOT_ID, extras) } } // You can return your normal tree if the EXTRA_RECENT flag is not present. return BrowserRoot(MY_MEDIA_ROOT_ID, null) } // Return an empty tree to disallow browsing. return BrowserRoot(MY_EMPTY_ROOT_ID, null)
Java
@Override public BrowserRoot onGetRoot(String clientPackageName, int clientUid, Bundle rootHints) { ... // Verify that the specified package is SystemUI. You'll need to write your // own logic to do this. if (isSystem(clientPackageName, clientUid)) { if (rootHints != null) { if (rootHints.getBoolean(BrowserRoot.EXTRA_RECENT)) { // Return a tree with a single playable media item for resumption. Bundle extras = new Bundle(); extras.putBoolean(BrowserRoot.EXTRA_RECENT, true); return new BrowserRoot(MY_RECENTS_ROOT_ID, extras); } } // You can return your normal tree if the EXTRA_RECENT flag is not present. return new BrowserRoot(MY_MEDIA_ROOT_ID, null); } // Return an empty tree to disallow browsing. return new BrowserRoot(MY_EMPTY_ROOT_ID, null); }