Elementy sterujące multimediami na Androidzie znajdują się w pobliżu Szybkich ustawień. Sesje od układ wielu aplikacji na przesuwanej karuzeli. Karuzela zawiera listę sesji w tej kolejności:
- Strumienie odtwarzane lokalnie na telefonie
- strumienie zdalne, np. wykryte na urządzeniach zewnętrznych lub w sesjach przesyłania;
- Poprzednie wznawialne sesje, w kolejności, w jakiej były ostatnio odtwarzane
Począwszy od Androida 13 (poziom interfejsu API 33), aby zapewnić użytkownikom dostęp
zestaw elementów sterujących multimediami w aplikacjach odtwarzających multimedia, przyciski poleceń do sterowania multimediami
pochodzą ze stanu Player
.
Dzięki temu możesz uzyskać spójny zestaw funkcji sterowania multimediami funkcje sterowania multimediami na różnych urządzeniach.
Ilustracja 1 pokazuje, jak to wygląda na telefonie i tablecie. .
System wyświetla maksymalnie 5 przycisków poleceń w zależności od stanu Player
:
opisane w poniższej tabeli. W trybie kompaktowym tylko pierwsze 3 czynności
przedziały czasu są wyświetlane. Dzięki temu elementy sterujące multimediami są renderowane w innych
Platformy Androida, takie jak Auto, Asystent i Wear OS.
Boks | Kryteria | Działanie |
---|---|---|
1 |
playWhenReady
ma wartość false (fałsz) lub bieżące odtworzenie
stan to STATE_ENDED .
|
Odtwórz |
playWhenReady ma wartość true (prawda), a obecny stan odtwarzania to STATE_BUFFERING .
|
Wskaźnik postępu ładowania | |
playWhenReady ma wartość true (prawda), a obecny stan odtwarzania to STATE_READY . |
Wstrzymaj | |
2 | Dostępne jest polecenie odtwarzacza COMMAND_SEEK_TO_PREVIOUS lub COMMAND_SEEK_TO_PREVIOUS_MEDIA_ITEM . |
Wstecz |
Nie jest dostępne ani polecenie odtwarzacza COMMAND_SEEK_TO_PREVIOUS , ani COMMAND_SEEK_TO_PREVIOUS_MEDIA_ITEM . W boksie dostępne jest też niestandardowe polecenie z układu niestandardowego, które nie zostało jeszcze umieszczone. |
Możliwość | |
(funkcja nie jest jeszcze obsługiwana w przypadku Media3) rozwiązania PlaybackState zawierają wartość logiczną true dla klucza EXTRAS_KEY_SLOT_RESERVATION_SEEK_TO_PREV . |
Puste | |
3 | Dostępne jest polecenie odtwarzacza COMMAND_SEEK_TO_NEXT lub COMMAND_SEEK_TO_NEXT_MEDIA_ITEM . |
Dalej |
Nie jest dostępne ani polecenie odtwarzacza COMMAND_SEEK_TO_NEXT , ani COMMAND_SEEK_TO_NEXT_MEDIA_ITEM . W boksie dostępne jest też niestandardowe polecenie z układu niestandardowego, które nie zostało jeszcze umieszczone. |
Możliwość | |
(funkcja nie jest jeszcze obsługiwana w przypadku Media3) rozwiązania PlaybackState zawierają wartość logiczną true dla klucza EXTRAS_KEY_SLOT_RESERVATION_SEEK_TO_NEXT . |
Puste | |
4 | W boksie jest dostępne polecenie niestandardowe z układu niestandardowego, które nie zostało jeszcze umieszczone. | Możliwość |
5 | W boksie jest dostępne polecenie niestandardowe z układu niestandardowego, które nie zostało jeszcze umieszczone. | Możliwość |
Polecenia niestandardowe są umieszczane w kolejności, w jakiej zostały dodane do układ niestandardowy.
Dostosuj przyciski poleceń
Aby dostosować systemowe elementy sterujące multimediami za pomocą Jetpack Media3:
możesz ustawić niestandardowy układ sesji
i dostępne polecenia
odpowiednio, podczas implementowania MediaSessionService
:
W
onCreate()
utwórzMediaSession
i zdefiniować układ niestandardowy przycisków poleceń.W
MediaSession.Callback.onConnect()
autoryzować kontrolery, definiując ich dostępne polecenia, w tym polecenia niestandardowe, wConnectionResult
.W
MediaSession.Callback.onCustomCommand()
reagować na niestandardowe polecenie wybrane przez użytkownika.
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); } } }
Aby dowiedzieć się więcej o konfigurowaniu MediaSession
, tak aby klienty takie jak
system może połączyć się z Twoją aplikacją do multimediów, zobacz
Przyznaj kontrolę innym klientom.
W przypadku Jetpack Media3 po wdrożeniu rozwiązania MediaSession
PlaybackState
Odtwarzacz multimediów jest automatycznie aktualizowany. Podobnie, gdy
zaimplementuje MediaSessionService
, biblioteka automatycznie publikuje
Powiadomienie MediaStyle
i dba o jej aktualność.
Reagowanie na przyciski poleceń
Gdy użytkownik kliknie przycisk polecenia w systemowych opcjach sterowania multimediami,
MediaController
wysyła polecenie odtwarzania do urządzenia MediaSession
.
MediaSession
przekazuje te polecenia odtwarzaczowi. Polecenia
zdefiniowane w elemencie Player
w Media3
są automatycznie obsługiwane przez multimedia
.
Więcej informacji znajdziesz w artykule Dodawanie poleceń niestandardowych. .
Działanie sprzed Androida 13
Aby zapewnić zgodność wsteczną, interfejs systemu nadal zawiera alternatywny układ
które korzystają z działań związanych z powiadomieniami w przypadku aplikacji, które nie są aktualizowane pod kątem Androida 13,
lub te, które nie zawierają informacji PlaybackState
. Przyciski poleceń to
pochodzą z listy Notification.Action
dołączonej do pliku MediaStyle
powiadomienia. System wyświetla maksymalnie 5 działań w kolejności, w jakiej
dodano. W trybie kompaktowym mogą być wyświetlane maksymalnie 3 przyciski, zależnie od
przekazywane do setShowActionsInCompactView()
.
Działania niestandardowe są umieszczane w kolejności, w jakiej zostały dodane do
PlaybackState
Poniższy przykładowy kod ilustruje, jak dodawać działania do obiektu MediaStyle powiadomienie :
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();
Wspieranie wznowienia multimediów
Wznowienie multimediów umożliwia użytkownikom ponowne uruchamianie poprzednich sesji z karuzeli bez konieczności uruchamiania aplikacji. Po rozpoczęciu odtwarzania użytkownik wchodzi w interakcję z: sterowanie multimediami w zwykły sposób.
Funkcję wznawiania odtwarzania możesz włączyć lub wyłączyć w aplikacji Ustawienia, w sekcji Dźwięk > Opcjemultimediów. Użytkownik może też uzyskać dostęp do Ustawień przez kliknij ikonę koła zębatego, która wyświetli się po przesunięciu palcem po rozwiniętej karuzeli.
Media3 udostępnia interfejsy API, które ułatwiają wznawianie odtwarzania multimediów. Zobacz Wznowienie odtwarzania dzięki Media3 dokumentacji ze wskazówkami dotyczącymi implementacji tej funkcji.
Korzystanie ze starszych interfejsów API multimediów
W tej sekcji objaśniono sposób integracji z systemowymi elementami sterującymi multimediami za pomocą starszych interfejsów API MediaCompat.
System pobiera następujące informacje z
MediaMetadata
urządzenia MediaSession
. Wyświetla je, gdy jest dostępny:
METADATA_KEY_ALBUM_ART_URI
METADATA_KEY_TITLE
METADATA_KEY_DISPLAY_TITLE
METADATA_KEY_ARTIST
METADATA_KEY_DURATION
(jeśli czas trwania nie jest ustawiony, pasek przewijania nie pokaż postęp)
Aby mieć pewność, że masz prawidłowe i dokładne powiadomienia dotyczące sterowania multimediami:
ustaw wartość METADATA_KEY_TITLE
lub METADATA_KEY_DISPLAY_TITLE
metadanych z tytułu odtwarzanych w danej chwili multimediów.
Odtwarzacz wyświetla czas, który upłynął w trakcie odtwarzania danej treści
multimedia, a także pasek przewijania zmapowany na element MediaSession
PlaybackState
Odtwarzacz pokazuje postęp odtwarzania multimediów,
pasek przewijania zmapowany na element MediaSession
PlaybackState
. Pasek przewijania
Umożliwia użytkownikom zmianę pozycji i wyświetla czas odtwarzania multimediów
elementu. Aby włączyć pasek przewijania, musisz zaimplementować
PlaybackState.Builder#setActions
i uwzględnij ACTION_SEEK_TO
.
Boks | Działanie | Kryteria |
---|---|---|
1 | Odtwórz |
Bieżący stan elementu PlaybackState to:
|
Wskaźnik postępu ładowania |
Bieżący stan elementu PlaybackState to:
|
|
Wstrzymaj | Bieżący stan elementu PlaybackState nie stanowi żadnej z powyższych opcji. |
|
2 | Wstecz | PlaybackState działań, w tym ACTION_SKIP_TO_PREVIOUS . |
Możliwość | Działania PlaybackState nie obejmują działań niestandardowych ACTION_SKIP_TO_PREVIOUS i PlaybackState , które obejmują działania niestandardowe, które nie zostały jeszcze wykonane. |
|
Puste | Rozszerzenia PlaybackState zawierają wartość logiczną true dla klucza SESSION_EXTRAS_KEY_SLOT_RESERVATION_SKIP_TO_PREV . |
|
3 | Dalej | PlaybackState działań, w tym ACTION_SKIP_TO_NEXT . |
Możliwość | Działania PlaybackState nie obejmują działań niestandardowych ACTION_SKIP_TO_NEXT i PlaybackState , które obejmują działania niestandardowe, które nie zostały jeszcze wykonane. |
|
Puste | Rozszerzenia PlaybackState zawierają wartość logiczną true dla klucza SESSION_EXTRAS_KEY_SLOT_RESERVATION_SKIP_TO_NEXT . |
|
4 | Możliwość | PlaybackState działania niestandardowe obejmują takie, które nie zostały jeszcze zastosowane. |
5 | Możliwość | PlaybackState działania niestandardowe obejmują takie, które nie zostały jeszcze zastosowane. |
Dodaj działania standardowe
Poniższe przykłady kodu pokazują, jak dodać standardowy i PlaybackState
za pomocą działań niestandardowych.
Do odtwarzania, wstrzymywania, poprzedniej i następnej czynności ustaw te działania w
PlaybackState
na potrzeby sesji multimediów.
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);
Jeśli w poprzednich lub następnych boksach nie chcesz dodawać przycisków, nie dodawaj
ACTION_SKIP_TO_PREVIOUS
lub ACTION_SKIP_TO_NEXT
, a następnie dodaj dodatki do
sesja:
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);
Dodaj działania niestandardowe
Inne działania, które chcesz wyświetlać za pomocą opcji sterowania multimediami, możesz utworzyć
PlaybackStateCompat.CustomAction
i dodaj je do PlaybackState
. Te czynności są widoczne w
w kolejności, w jakiej zostały dodane.
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);
Reagowanie na działania OdtwarzanieState
Gdy użytkownik klika przycisk,
MediaController.TransportControls
aby odesłać polecenie z powrotem do MediaSession
. Musisz zarejestrować oddzwonienie
którzy potrafią prawidłowo reagować na te zdarzenia.
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); } } };
Wznowienie multimediów
Aby aplikacja odtwarzacza była widoczna w obszarze szybkich ustawień:
musisz utworzyć powiadomienie MediaStyle
z prawidłowym tokenem MediaSession
.
Aby wyświetlić tytuł powiadomienia MediaStyle, użyj
NotificationBuilder.setContentTitle()
Aby wyświetlić ikonę marki w odtwarzaczu, użyj
NotificationBuilder.setSmallIcon()
Aby umożliwić wznowienie odtwarzania, aplikacje muszą zaimplementować MediaBrowserService
i MediaSession
. Element MediaSession
musi implementować wywołanie zwrotne onPlay()
.
Implementacja usługi MediaBrowserService
Po uruchomieniu urządzenia system wyszukuje 5 ostatnio używanych multimediów aplikacji i udostępnia elementy sterujące, które umożliwiają ponowne odtwarzanie odtwarzania z każdej z nich.
System próbuje skontaktować się z urządzeniem MediaBrowserService
przy użyciu połączenia z
Interfejs SystemUI. Aplikacja musi zezwalać na takie połączenia. W przeciwnym razie nie będzie mogła obsługiwać takich połączeń.
na wznowienie odtwarzania.
Połączenia z SystemUI można identyfikować i weryfikować za pomocą nazwy pakietu
com.android.systemui
i podpis. Interfejs SystemUI jest podpisany z platformą.
podpis. Przykładem sposobu sprawdzenia pod kątem podpisu platformy może być
w aplikacji UAMP.
Aby można było wznowić odtwarzanie, MediaBrowserService
musi
zastosuj te zachowania:
Funkcja
onGetRoot()
musi szybko zwrócić pierwiastek inny niż zero. Inna złożona logika powinna są obsługiwane wonLoadChildren()
Kiedy Funkcja
onLoadChildren()
jest wywoływana dla głównego identyfikatora mediów, wynik musi zawierać FLAG_PLAYABLE dziecka.Funkcja
MediaBrowserService
powinna zwrócić ostatnio odtwarzany element multimedialny, gdy: otrzymują EXTRA_LAST zapytania. Zwrócona wartość powinna być rzeczywistym elementem multimedialnym, a nie ogólną .MediaBrowserService
musi zawierać odpowiednie Pole MediaDescription nie jest puste title i napisy. Powinien też ustawić identyfikator URI ikony lub mapa bitowa ikony.
Poniższe przykłady kodu pokazują, jak zaimplementować 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); }