Die Mediensteuerung befindet sich unter Android in der Nähe der Schnelleinstellungen. Sitzungen aus mehreren Apps werden in einem Karussell angeordnet, das sich wischen lässt. Im Karussell werden Sitzungen in dieser Reihenfolge aufgeführt:
- Streams, die lokal auf dem Smartphone abgespielt werden
- Remote-Streams, z. B. auf externen Geräten oder in Übertragungssitzungen erkannte Streams
- Bisherige Sitzungen, die fortgesetzt werden können, in der Reihenfolge, in der sie zuletzt gespielt wurden
Ab Android 13 (API-Level 33) werden Aktionsschaltflächen für Mediensteuerelemente aus dem Status Player
abgeleitet, damit Nutzer auf eine breite Palette von Mediensteuerelementen für Apps zugreifen können, in denen Medien wiedergegeben werden.
So können Sie geräteübergreifend einheitliche Mediensteuerelemente und eine optimierte Mediensteuerung anbieten.
Abbildung 1 zeigt, wie das auf einem Smartphone und einem Tablet jeweils aussieht.
Das System zeigt je nach Player
-Status bis zu fünf Aktionsschaltflächen an, wie in der folgenden Tabelle beschrieben. Im kompakten Modus werden nur die ersten drei Aktionsslots angezeigt. Das entspricht der Darstellung der Mediensteuerung auf anderen Android-Plattformen wie Auto, Assistant und Wear OS.
Stellen | Kriterien | Aktion |
---|---|---|
1 |
playWhenReady ist falsch oder der aktuelle Wiedergabestatus ist STATE_ENDED .
|
Wiedergeben |
playWhenReady ist wahr und der aktuelle Wiedergabestatus ist STATE_BUFFERING .
|
Rotierendes Ladesymbol | |
playWhenReady ist wahr und der aktuelle Wiedergabestatus ist STATE_READY . |
Pausieren | |
2 | Der Playerbefehl COMMAND_SEEK_TO_PREVIOUS oder COMMAND_SEEK_TO_PREVIOUS_MEDIA_ITEM ist verfügbar. |
Zurück |
Weder der Spielerbefehl COMMAND_SEEK_TO_PREVIOUS noch COMMAND_SEEK_TO_PREVIOUS_MEDIA_ITEM ist verfügbar. Stattdessen kann ein benutzerdefinierter Befehl aus dem benutzerdefinierten Layout, der noch nicht platziert wurde, verwendet werden, um den Slot zu füllen. |
Benutzerdefiniert | |
Sitzungs-Extras enthalten einen booleschen Wert true für den Schlüssel EXTRAS_KEY_SLOT_RESERVATION_SEEK_TO_PREV . |
Leer | |
3 | Der Playerbefehl COMMAND_SEEK_TO_NEXT oder COMMAND_SEEK_TO_NEXT_MEDIA_ITEM ist verfügbar. |
Weiter |
Weder der Spielerbefehl COMMAND_SEEK_TO_NEXT noch COMMAND_SEEK_TO_NEXT_MEDIA_ITEM ist verfügbar. Stattdessen kann ein benutzerdefinierter Befehl aus dem benutzerdefinierten Layout, der noch nicht platziert wurde, verwendet werden, um den Slot zu füllen. |
Benutzerdefiniert | |
Sitzungs-Extras enthalten einen booleschen Wert true für den Schlüssel EXTRAS_KEY_SLOT_RESERVATION_SEEK_TO_NEXT . |
Leer | |
4 | Es ist ein benutzerdefinierter Befehl aus dem benutzerdefinierten Layout verfügbar, der noch nicht platziert wurde, um den Slot zu füllen. | Benutzerdefiniert |
5 | Es ist ein benutzerdefinierter Befehl aus dem benutzerdefinierten Layout verfügbar, der noch nicht platziert wurde, um den Slot zu füllen. | Benutzerdefiniert |
Benutzerdefinierte Befehle werden in der Reihenfolge platziert, in der sie dem benutzerdefinierten Layout hinzugefügt wurden.
Befehlsschaltflächen anpassen
Wenn du die Systemmediensteuerung mit Jetpack Media3 anpassen möchtest, kannst du das benutzerdefinierte Layout der Sitzung und die verfügbaren Befehle der Controller entsprechend festlegen, wenn du eine MediaSessionService
implementierst:
Erstellen Sie in
onCreate()
eineMediaSession
und definieren Sie das benutzerdefinierte Layout der Befehlsschaltflächen.Autorisieren Sie in
MediaSession.Callback.onConnect()
Controller, indem Sie die verfügbaren Befehle, einschließlich benutzerdefinierter Befehle, inConnectionResult
definieren.Mit
MediaSession.Callback.onCustomCommand()
antwortest du auf den benutzerdefinierten Befehl, den der Nutzer ausgewählt hat.
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); } } }
Weitere Informationen zum Konfigurieren von MediaSession
, damit Clients wie das System eine Verbindung zu deiner Medien-App herstellen können, findest du unter Anderen Clients die Steuerung gewähren.
Wenn du mit Jetpack Media3 eine MediaSession
implementierst, wird deine PlaybackState
automatisch über den Mediaplayer auf dem neuesten Stand gehalten. Wenn Sie eine MediaSessionService
implementieren, veröffentlicht die Bibliothek automatisch eine MediaStyle
-Benachrichtigung für Sie und hält sie auf dem neuesten Stand.
Auf Aktionsschaltflächen reagieren
Wenn ein Nutzer auf eine Aktionsschaltfläche in den Mediensteuerungen des Systems tippt, sendet die MediaController
des Systems einen Wiedergabebefehl an deine MediaSession
. Der MediaSession
leitet diese Befehle dann an den Player weiter. Befehle, die in der Player
-Benutzeroberfläche von Media3 definiert sind, werden automatisch von der Mediensitzung verarbeitet.
Eine Anleitung dazu, wie Sie auf einen benutzerdefinierten Befehl reagieren, finden Sie unter Benutzerdefinierte Befehle hinzufügen.
Verhalten vor Android 13
Für die Abwärtskompatibilität bietet die System-UI weiterhin ein alternatives Layout, das Benachrichtigungsaktionen für Apps verwendet, die nicht auf Android 13 aktualisiert werden oder keine PlaybackState
-Informationen enthalten. Die Aktionsschaltflächen werden aus der Notification.Action
-Liste abgeleitet, die an die MediaStyle
-Benachrichtigung angehängt ist. Das System zeigt bis zu fünf Aktionen in der Reihenfolge an, in der sie hinzugefügt wurden. Im kompakten Modus werden bis zu drei Schaltflächen angezeigt, die von den an setShowActionsInCompactView()
übergebenen Werten abhängen.
Benutzerdefinierte Aktionen werden in der Reihenfolge platziert, in der sie der PlaybackState
hinzugefügt wurden.
Im folgenden Codebeispiel wird gezeigt, wie Sie der Benachrichtigung vom Typ „MediaStyle“ Aktionen hinzufügen :
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();
Fortsetzung der Medienwiedergabe unterstützen
Mit der Medienwiederaufnahme können Nutzer vorherige Sitzungen über das Karussell neu starten, ohne die App starten zu müssen. Zu Beginn der Wiedergabe interagiert der Nutzer wie gewohnt mit den Mediensteuerelementen.
Die Funktion zur Wiederaufnahme der Wiedergabe kann in den Einstellungen unter Ton > Medien aktiviert und deaktiviert werden. Nutzer können auch auf die Einstellungen zugreifen, indem sie auf das Zahnradsymbol tippen, das angezeigt wird, nachdem sie im maximierten Karussell wischen.
Media3 bietet APIs, die die Wiederaufnahme von Medien erleichtern. Eine Anleitung zur Implementierung dieser Funktion findest du in der Dokumentation Wiedergabe fortsetzen mit Media3.
Legacy-Media-APIs verwenden
In diesem Abschnitt wird beschrieben, wie Sie die Systemmediensteuerung mithilfe der alten MediaCompat APIs einbinden.
Das System ruft die folgenden Informationen aus dem MediaMetadata
der MediaSession
ab und zeigt sie an, wenn sie verfügbar sind:
METADATA_KEY_ALBUM_ART_URI
METADATA_KEY_TITLE
METADATA_KEY_DISPLAY_TITLE
METADATA_KEY_ARTIST
METADATA_KEY_DURATION
(Wenn die Dauer nicht festgelegt ist, zeigt die Suchleiste keinen Fortschritt an.)
Damit die Benachrichtigung zur Mediensteuerung gültig und korrekt ist, muss der Wert der METADATA_KEY_TITLE
- oder METADATA_KEY_DISPLAY_TITLE
-Metadaten dem Titel der gerade wiedergegebenen Medien entsprechen.
Der Mediaplayer zeigt die vergangene Zeit für die aktuell wiedergegebenen Medien sowie eine Suchleiste an, die der Taste MediaSession
PlaybackState
zugeordnet ist.
Der Mediaplayer zeigt den Fortschritt der gerade abgespielten Medien sowie eine Suchleiste an, die der Taste MediaSession
PlaybackState
zugeordnet ist. Mit der Suchleiste können Nutzer die Position ändern. Außerdem wird die vergangene Zeit für das Medienelement angezeigt. Damit die Suchleiste aktiviert wird, musst du PlaybackState.Builder#setActions
implementieren und ACTION_SEEK_TO
einschließen.
Stellen | Aktion | Kriterien |
---|---|---|
1 | Wiedergeben |
Der aktuelle Status der PlaybackState ist einer der folgenden:
|
Rotierendes Ladesymbol |
Der aktuelle Status der PlaybackState ist einer der folgenden:
|
|
Pausieren | Der aktuelle Status der PlaybackState ist keiner der oben genannten. |
|
2 | Zurück | PlaybackState actions enthält ACTION_SKIP_TO_PREVIOUS . |
Benutzerdefiniert | PlaybackState -Aktionen enthalten keine ACTION_SKIP_TO_PREVIOUS und PlaybackState -benutzerdefinierten Aktionen enthalten eine benutzerdefinierte Aktion, die noch nicht platziert wurde. |
|
Leer | PlaybackState extras enthält einen booleschen Wert true für den Schlüssel SESSION_EXTRAS_KEY_SLOT_RESERVATION_SKIP_TO_PREV . |
|
3 | Weiter | PlaybackState actions enthält ACTION_SKIP_TO_NEXT . |
Benutzerdefiniert | PlaybackState -Aktionen enthalten keine ACTION_SKIP_TO_NEXT und PlaybackState -benutzerdefinierten Aktionen enthalten eine benutzerdefinierte Aktion, die noch nicht platziert wurde. |
|
Leer | PlaybackState extras enthält einen booleschen Wert true für den Schlüssel SESSION_EXTRAS_KEY_SLOT_RESERVATION_SKIP_TO_NEXT . |
|
4 | Benutzerdefiniert | PlaybackState Benutzerdefinierte Aktionen enthalten eine benutzerdefinierte Aktion, die noch nicht platziert wurde. |
5 | Benutzerdefiniert | PlaybackState Benutzerdefinierte Aktionen enthalten eine benutzerdefinierte Aktion, die noch nicht platziert wurde. |
Standardaktionen hinzufügen
In den folgenden Codebeispielen wird gezeigt, wie Sie PlaybackState
-Standard- und benutzerdefinierte Aktionen hinzufügen.
Legen Sie die Aktionen „Wiedergabe“, „Pause“, „Vorheriger Titel“ und „Nächster Titel“ für die Mediensitzung in der PlaybackState
fest.
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);
Wenn Sie in den vorherigen oder nächsten Slots keine Schaltflächen haben möchten, fügen Sie ACTION_SKIP_TO_PREVIOUS
oder ACTION_SKIP_TO_NEXT
nicht hinzu, sondern fügen Sie der Sitzung stattdessen Extras hinzu:
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);
Benutzerdefinierte Aktionen hinzufügen
Wenn Sie andere Aktionen in den Mediensteuerungen anzeigen lassen möchten, können Sie ein PlaybackStateCompat.CustomAction
erstellen und stattdessen dem PlaybackState
hinzufügen. Diese Aktionen werden in der Reihenfolge angezeigt, in der sie hinzugefügt wurden.
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);
Auf PlaybackState-Aktionen reagieren
Wenn ein Nutzer auf eine Schaltfläche tippt, sendet SystemUI über MediaController.TransportControls
einen Befehl an die MediaSession
zurück. Sie müssen einen Rückruf registrieren, der richtig auf diese Ereignisse reagieren kann.
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); } } };
Fortsetzung der Medienwiedergabe
Damit deine Player-App in den Schnelleinstellungen angezeigt wird, musst du eine MediaStyle
-Benachrichtigung mit einem gültigen MediaSession
-Token erstellen.
Verwenden Sie NotificationBuilder.setContentTitle()
, um den Titel der MediaStyle-Benachrichtigung anzuzeigen.
Verwenden Sie NotificationBuilder.setSmallIcon()
, um das Markensymbol für den Mediaplayer anzuzeigen.
Damit die Wiedergabe fortgesetzt werden kann, müssen Apps MediaBrowserService
und MediaSession
implementieren. In deinem MediaSession
muss der onPlay()
-Callback implementiert sein.
MediaBrowserService
-Implementierung
Nach dem Starten des Geräts sucht das System nach den fünf zuletzt verwendeten Medien-Apps und bietet Steuerelemente, mit denen die Wiedergabe in jeder App neu gestartet werden kann.
Das System versucht, über die SystemUI eine Verbindung zu Ihrem MediaBrowserService
herzustellen. Deine App muss solche Verbindungen zulassen, da andernfalls die Wiedergabe nicht fortgesetzt werden kann.
Verbindungen von SystemUI können anhand des Paketnamens com.android.systemui
und der Signatur identifiziert und überprüft werden. Die SystemUI ist mit der Plattformsignatur signiert. Ein Beispiel für die Prüfung anhand der Plattformsignatur findest du in der UAMP App.
Damit die Wiedergabe fortgesetzt werden kann, muss deine MediaBrowserService
Folgendes implementieren:
onGetRoot()
muss schnell einen nicht nullwertigen Wurzelknoten zurückgeben. Andere komplexe Logik sollte inonLoadChildren()
Wenn
onLoadChildren()
für die Stammmedien-ID aufgerufen wird, muss das Ergebnis ein untergeordnetes Element vom Typ FLAG_PLAYABLE enthalten.MediaBrowserService
sollte das zuletzt wiedergegebene Medienelement zurückgeben, wenn eine EXTRA_RECENT-Abfrage empfangen wird. Der zurückgegebene Wert sollte ein tatsächliches Medienelement und keine generische Funktion sein.MediaBrowserService
muss eine geeignete MediaDescription mit einem nicht leeren Titel und einem Untertitel enthalten. Außerdem sollte ein Symbol-URI oder eine Symbol-Bitmap festgelegt werden.
Die folgenden Codebeispiele veranschaulichen die Implementierung von 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); }