בקרי המדיה ב-Android נמצאים ליד ההגדרות המהירות. סשנים מתאריך אפליקציות מרובות מסודרות בקרוסלה שניתן להחליק. בקרוסלה רשומים סשנים בסדר הזה:
- סטרימינג שמושמע באופן מקומי בטלפון
- שידורים מרוחקים, כמו אלה שזוהו במכשירים חיצוניים או בסשנים של הפעלת Cast
- סשנים קודמים שניתן להמשיך, לפי הסדר שבו הופעלו לאחרונה
החל מ-Android 13 (רמת API 33), כדי להבטיח שמשתמשים יוכלו לגשת
קבוצה של בקרי מדיה לאפליקציות שמפעילות מדיה, לחצני פעולה בלחצני המדיה
נגזרים מהמצב Player
.
כך ניתן להציג קבוצה עקבית של פקדי מדיה ועיצוב מלוטש יותר בחוויית השליטה במדיה במכשירים שונים.
איור 1 מציג דוגמה לאופן שבו זה נראה בטלפון ובטאבלט, בהתאמה.
המערכת מציגה עד חמישה לחצני פעולה על סמך המצב Player
, בתור
שמתוארים בטבלה הבאה. במצב קומפקטי, רק שלוש הפעולות הראשונות
יוצגו משבצות זמן. זה תואם לאופן שבו לחצני המדיה מעובדים
בפלטפורמות Android כמו Auto , Assistant ו-Wear OS.
משבצת | קריטריונים | פעולה |
---|---|---|
1 |
playWhenReady
שגוי או שההפעלה הנוכחית
הוא STATE_ENDED .
|
הפעלה |
הערך playWhenReady מוגדר כ-True ומצב ההפעלה הנוכחי הוא STATE_BUFFERING .
|
סימן גרפי שמוצג בזמן טעינה | |
הערך playWhenReady מוגדר כ-True ומצב ההפעלה הנוכחי הוא STATE_READY . |
השהיה | |
2 | פקודת הנגן COMMAND_SEEK_TO_PREVIOUS או COMMAND_SEEK_TO_PREVIOUS_MEDIA_ITEM זמינות. |
הקודם |
אי אפשר להשתמש בפקודת נגן COMMAND_SEEK_TO_PREVIOUS או COMMAND_SEEK_TO_PREVIOUS_MEDIA_ITEM , והפריסה המותאמת אישית שעדיין לא הוצבה זמינה למלא את המשבצת. |
בהתאמה אישית | |
(עדיין לא נתמך ב-Media3 ) PlaybackState התוספות כוללות ערך בוליאני true עבור המפתח EXTRAS_KEY_SLOT_RESERVATION_SEEK_TO_PREV . |
ריק | |
3 | פקודת הנגן COMMAND_SEEK_TO_NEXT או COMMAND_SEEK_TO_NEXT_MEDIA_ITEM זמינות. |
הבא |
אי אפשר להשתמש בפקודת נגן COMMAND_SEEK_TO_NEXT או COMMAND_SEEK_TO_NEXT_MEDIA_ITEM , והפריסה המותאמת אישית שעדיין לא הוצבה זמינה למלא את המשבצת. |
בהתאמה אישית | |
(עדיין לא נתמך ב-Media3 ) PlaybackState התוספות כוללות ערך בוליאני true עבור המפתח EXTRAS_KEY_SLOT_RESERVATION_SEEK_TO_NEXT . |
ריק | |
4 | כדי למלא את המשבצת, תוצג לכם פקודה בהתאמה אישית מהפריסה המותאמת אישית שעדיין לא הצבתם. | בהתאמה אישית |
5 | כדי למלא את המשבצת, תוצג לכם פקודה בהתאמה אישית מהפריסה המותאמת אישית שעדיין לא הצבתם. | בהתאמה אישית |
פקודות מותאמות אישית מסודרות לפי הסדר שבו הן נוספו פריסה מותאמת אישית.
התאמה אישית של לחצני הפקודה
כדי להתאים אישית את בקרי המדיה של המערכת באמצעות Jetpack Media3:
אפשר להגדיר את הפריסה המותאמת אישית של הסשן ואת הפקודות הזמינות
בהתאם, כאשר מטמיעים MediaSessionService
:
ב-
onCreate()
, מפתחיםMediaSession
ולהגדיר את הפריסה המותאמת אישית של לחצני פקודה.ב-
MediaSession.Callback.onConnect()
, מתן הרשאה לנאמני מידע על ידי הגדרת הפקודות הזמינות שלהם, כולל פקודות מותאמות אישית, בConnectionResult
.ב-
MediaSession.Callback.onCustomCommand()
, להגיב לפקודה המותאמת אישית שהמשתמש בחר.
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); } } }
למידע נוסף על ההגדרה של MediaSession
כך שלקוחות יאהבו את
שהמערכת יכולה להתחבר לאפליקציית המדיה,
הענקת שליטה ללקוחות אחרים.
עם Jetpack Media3, כשמטמיעים MediaSession
, ה-PlaybackState
מעודכן באופן אוטומטי לפי נגן המדיה. באופן דומה,
תטמיע MediaSessionService
, הספרייה מפרסמת באופן אוטומטי
התראה MediaStyle
בשבילך ולשמור על עדכניות.
לחצני תגובה לפעולה
כשמשתמש מקיש על לחצן פעולה בלחצני המדיה של המערכת,
הפקודה MediaController
שולחת פקודת הפעלה למכשיר MediaSession
.
לאחר מכן, MediaSession
מעביר את הפקודות האלה לנגן. פקודות
מוגדר בPlayer
של Media3
ממשק המדיה מטופל באופן אוטומטי
סשן.
הוספת פקודות בהתאמה אישית כדי לקבל הנחיות איך להגיב לפקודה בהתאמה אישית.
התנהגות לפני מערכת Android 13
לצורך תאימות לאחור, ממשק המשתמש של המערכת ממשיך לספק פריסה חלופית
שמשתמשת בפעולות של התראות לגבי אפליקציות שלא מתעדכנות כדי לטרגט ל-Android 13,
או שלא כוללים מידע על PlaybackState
. לחצני הפעולה
נגזר מהרשימה Notification.Action
שמצורפת אל MediaStyle
התראה. המערכת מציגה עד חמש פעולות לפי הסדר שבו הן
נוספו. במצב קומפקטי מוצגים עד שלושה לחצנים, לפי
שמועברים אל setShowActionsInCompactView()
.
הפעולות המותאמות אישית מופיעות לפי הסדר שבו הן נוספו
PlaybackState
הקוד הבא לדוגמה ממחיש איך להוסיף פעולות ל-MediaStyle. התראה :
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();
תמיכה בהמשך ההפעלה של מדיה
המשך מדיה מאפשר למשתמשים להפעיל מחדש סשנים קודמים מהקרוסלה בלי להפעיל את האפליקציה. כשההפעלה מתחילה, המשתמש יוצר אינטראקציה עם ללחצני המדיה בדרך הרגילה.
ניתן להפעיל ולהשבית את התכונה של המשך ההפעלה באמצעות אפליקציית ההגדרות. מתחת לקטע קול > אפשרויות מדיה. המשתמש יכול לגשת להגדרות גם דרך מקישים על סמל גלגל השיניים שמופיע אחרי שמחליקים על הקרוסלה המורחבת.
Media3 כולל ממשקי API שמאפשרים לתמוך בקלות בהמשך מדיה. לצפייה המשך הפעלה עם Media3 לקבלת הדרכה לגבי יישום התכונה הזו.
שימוש בממשקי ה-API הקודמים למדיה
בקטע הזה מוסבר איך לשלב את פקדי המדיה של המערכת באמצעות את ממשקי ה-API הקודמים של MediaCompat.
המערכת מאחזרת את המידע הבא
MediaMetadata
של MediaSession
, ויוצג כשהוא יהיה זמין:
METADATA_KEY_ALBUM_ART_URI
METADATA_KEY_TITLE
METADATA_KEY_DISPLAY_TITLE
METADATA_KEY_ARTIST
METADATA_KEY_DURATION
(אם משך הזמן לא מוגדר, סרגל הדילוג לא מוגדר הצגת ההתקדמות)
כדי לוודא שתהיה לכם התראה תקינה ומדויקת על בקרת המדיה,
הגדרת הערך של METADATA_KEY_TITLE
או METADATA_KEY_DISPLAY_TITLE
מטא-נתונים לשם המדיה שמופעלת כרגע.
נגן המדיה מציג את משך הזמן שחלף עבור ההפעלה הנוכחית
מדיה, וגם סרגל דילוג שממופה אל MediaSession
PlaybackState
.
נגן המדיה מציג את ההתקדמות של המדיה שמופעלת כרגע, יחד עם
סרגל דילוג שממופה אל MediaSession
PlaybackState
. סרגל הדילוג
מאפשר למשתמשים לשנות את המיקום ומציג את הזמן שחלף למדיה
שימושי. כדי שסרגל הדילוג יופעל, צריך להטמיע
PlaybackState.Builder#setActions
וכוללים ACTION_SEEK_TO
.
משבצת | פעולה | קריטריונים |
---|---|---|
1 | הפעלה |
המדינה הנוכחית של PlaybackState היא אחת מהאפשרויות הבאות:
|
סימן גרפי שמוצג בזמן טעינה |
המדינה הנוכחית של PlaybackState היא אחת מהאפשרויות הבאות:
|
|
השהיה | המדינה הנוכחית של PlaybackState היא אף אחת מהאפשרויות שלמעלה. |
|
2 | הקודם | PlaybackState פעולות כוללות ACTION_SKIP_TO_PREVIOUS . |
בהתאמה אישית | PlaybackState פעולות לא כוללות ACTION_SKIP_TO_PREVIOUS , ו-PlaybackState פעולות מותאמות אישית כוללות פעולה מותאמת אישית שעדיין לא בוצעה. |
|
ריק | PlaybackState התוספות כוללות ערך בוליאני true עבור המפתח SESSION_EXTRAS_KEY_SLOT_RESERVATION_SKIP_TO_PREV . |
|
3 | הבא | PlaybackState פעולות כוללות ACTION_SKIP_TO_NEXT . |
בהתאמה אישית | PlaybackState פעולות לא כוללות ACTION_SKIP_TO_NEXT , ו-PlaybackState פעולות מותאמות אישית כוללות פעולה מותאמת אישית שעדיין לא בוצעה. |
|
ריק | PlaybackState התוספות כוללות ערך בוליאני true עבור המפתח SESSION_EXTRAS_KEY_SLOT_RESERVATION_SKIP_TO_NEXT . |
|
4 | בהתאמה אישית | PlaybackState פעולות מותאמות אישית כוללות פעולה מותאמת אישית שעדיין לא בוצעה. |
5 | בהתאמה אישית | PlaybackState פעולות מותאמות אישית כוללות פעולה מותאמת אישית שעדיין לא בוצעה. |
הוספת פעולות רגילות
דוגמאות הקוד הבאות ממחישות איך להוסיף את הטקסט הרגיל של PlaybackState
פעולות מותאמות אישית.
כדי להפעיל, להשהות, לבצע את הפעולות הקודמות או לבצע את הפעולה הבאה, צריך להגדיר את הפעולות האלה בתור
PlaybackState
של סשן המדיה.
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);
אם אינך רוצה לחצנים במיקומים הקודמים או הבאים, אל תוסיף
ACTION_SKIP_TO_PREVIOUS
או ACTION_SKIP_TO_NEXT
, ובמקום זאת להוסיף תוספות אל
הסשן:
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);
הוספת פעולות מותאמות אישית
כדי לבצע פעולות אחרות שרוצים להציג בלחצני המדיה, אפשר ליצור
PlaybackStateCompat.CustomAction
ולהוסיף אותה לPlaybackState
במקום זאת. הפעולות האלה מוצגות בקטע
סדר ההוספה שלהם.
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);
תגובה לפעולות PlaybackState
כשמשתמש מקיש על לחצן, מערכת SystemUI משתמשת
MediaController.TransportControls
כדי לשלוח פקודה חזרה אל MediaSession
. צריך לרשום שיחה חוזרת
שיוכלו להגיב בצורה תקינה לאירועים האלה.
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); } } };
המשך מדיה
כדי שאפליקציית הנגן תופיע באזור של ההגדרות המהירות:
עליך ליצור התראת MediaStyle
עם אסימון MediaSession
חוקי.
כדי להציג את הכותרת עבור התראת MediaStyle, יש להשתמש ב-
NotificationBuilder.setContentTitle()
כדי להציג את סמל המותג בנגן המדיה, צריך להשתמש ב-
NotificationBuilder.setSmallIcon()
כדי לתמוך בהמשך ההפעלה, המפתחים צריכים להטמיע MediaBrowserService
ו-MediaSession
. חובה להטמיע את הקריאה החוזרת (callback) של onPlay()
באמצעות MediaSession
.
הטמעת MediaBrowserService
לאחר אתחול המכשיר, המערכת מחפשת את חמש פריטי המדיה האחרונים שהיו בשימוש אפליקציות, ומספק פקדים שניתן להשתמש בהם כדי להפעיל מחדש את ההפעלה מכל אפליקציה.
המערכת מנסה ליצור קשר עם MediaBrowserService
באמצעות חיבור דרך
SystemUI. האפליקציה שלך צריכה לאפשר חיבורים כאלה, אחרת היא לא יכולה לתמוך
כדי שניתן יהיה להמשיך את ההפעלה.
ניתן לזהות ולאמת חיבורים מ-SystemUI באמצעות שם החבילה
com.android.systemui
וחתימה. ה-SystemUI חתום באמצעות הפלטפורמה
לחתימה. דוגמה לבדיקה מול חתימת הפלטפורמה יכולה להיות
שנמצא באפליקציית UAMP.
כדי שניתן יהיה להמשיך להפעיל שוב את ההפעלה, MediaBrowserService
צריך
להטמיע התנהגויות אלה:
onGetRoot()
חייב להחזיר במהירות שורש שאינו null. לוגיקה מורכבת אחרת יטופלו בonLoadChildren()
מתי בוצעה קריאה לפונקציה
onLoadChildren()
במזהה המדיה הבסיסית, התוצאה חייבת להכיל FLAG_PLAYABLE לילדים.האפליקציה
MediaBrowserService
צריכה להחזיר את פריט המדיה האחרון שהופעל כאשר הם מקבלים Additional_RECENT שאילתה. הערך המוחזר צריך להיות פריט מדיה בפועל ולא גנרי מותאמת אישית.MediaBrowserService
חייב לספק תנאי מתאים MediaDescription עם שדה לא ריק title וכן כותרת משנה. צריך גם להגדיר URI של סמל או מפת סיביות של סמל.
דוגמאות הקוד הבאות ממחישות איך להטמיע את 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); }