Elementy sterujące multimediami na Androidzie znajdują się w pobliżu Szybkich ustawień. Sesje z wielu aplikacji są wyświetlane na przesuwanej karuzeli. Karuzela wyświetla sesje w takiej kolejności:
- Strumieniowo odtwarzane lokalnie na telefonie
- Strumienie zdalne (np. wykryte na urządzeniach zewnętrznych) lub sesje przesyłania
- Poprzednie sesje wznawiane (kolejność, w jakiej były ostatnio odtwarzane)
Aby zapewnić użytkownikom dostęp do bogatego zestawu elementów sterujących multimediami w aplikacjach, które odtwarzają multimedia, od Androida 13 (poziom interfejsu API 33) przyciski poleceń są pobierane ze stanu Player
.
Dzięki temu możesz zapewnić spójny zestaw opcji sterowania multimediami i zapewnić bardziej dopracowane sterowanie multimediami na różnych urządzeniach.
Rysunek 1 pokazuje przykładową prezentację na telefonie i tablecie.
System wyświetla maksymalnie 5 przycisków poleceń na podstawie stanu Player
, zgodnie z opisem w tabeli poniżej. W trybie kompaktowym wyświetlają się tylko 3 pierwsze przedziały działań. Ta zasada jest podobna do tego, jak elementy sterujące multimediami są renderowane na innych platformach z Androidem, takich jak Auto, Asystent czy Wear OS.
Boks | Kryteria | Działanie |
---|---|---|
1 |
playWhenReady ma wartość Fałsz lub obecny stan odtwarzania to STATE_ENDED .
|
Odtwórz |
playWhenReady to prawda, a bieżący stan odtwarzania to STATE_BUFFERING .
|
Wskaźnik wczytywania | |
playWhenReady to prawda, a bieżący 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 polecenie odtwarzacza COMMAND_SEEK_TO_PREVIOUS ani COMMAND_SEEK_TO_PREVIOUS_MEDIA_ITEM , a do wypełnienia boksu dostępne jest polecenie niestandardowe z układu niestandardowego, które nie zostało jeszcze umieszczone. |
Możliwość | |
(nieobsługiwane jeszcze w przypadku Media3) PlaybackState extras zawiera wartość logiczną true dla klucza EXTRAS_KEY_SLOT_RESERVATION_SEEK_TO_PREV . |
Brak | |
3 | Dostępne jest polecenie odtwarzacza COMMAND_SEEK_TO_NEXT lub COMMAND_SEEK_TO_NEXT_MEDIA_ITEM . |
Dalej |
Nie jest dostępne polecenie odtwarzacza COMMAND_SEEK_TO_NEXT ani COMMAND_SEEK_TO_NEXT_MEDIA_ITEM , a do wypełnienia boksu dostępne jest polecenie niestandardowe z układu niestandardowego, które nie zostało jeszcze umieszczone. |
Możliwość | |
(nieobsługiwane jeszcze w przypadku Media3) PlaybackState extras zawiera wartość logiczną true dla klucza EXTRAS_KEY_SLOT_RESERVATION_SEEK_TO_NEXT . |
Brak | |
4 | Aby wypełnić boks, dostępne jest polecenie niestandardowe z układu niestandardowego, który nie został jeszcze umieszczony. | Możliwość |
5 | Aby wypełnić boks, dostępne jest polecenie niestandardowe z układu niestandardowego, który nie został jeszcze umieszczony. | Możliwość |
Polecenia niestandardowe są umieszczane w kolejności, w jakiej zostały dodane do układu niestandardowego.
Dostosuj przyciski poleceń
Aby dostosować opcje sterowania multimediami za pomocą Jetpack Media3, podczas implementowania MediaSessionService
możesz ustawić niestandardowy układ sesji i dostępne polecenia kontrolerów:
W
onCreate()
utwórzMediaSession
i zdefiniuj niestandardowy układ przycisków poleceń.W narzędziu
MediaSession.Callback.onConnect()
autoryzuj kontrolery, definiując ich dostępne polecenia, w tym polecenia niestandardowe, w elemencieConnectionResult
.W
MediaSession.Callback.onCustomCommand()
odpowiedz na polecenie niestandardowe 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); } } }
Więcej informacji o konfigurowaniu urządzenia MediaSession
tak, aby klienty takie jak system mogły łączyć się z Twoją aplikacją do multimediów, znajdziesz w sekcji Przyznawanie kontroli innym klientom.
Dzięki Jetpack Media3, gdy wdrożysz MediaSession
, PlaybackState
automatycznie aktualizuje się za pomocą odtwarzacza multimediów. Podobnie gdy zaimplementujesz element MediaSessionService
, biblioteka automatycznie opublikuje powiadomienie MediaStyle
i będzie je aktualizować.
Reakcja na przyciski poleceń
Gdy użytkownik kliknie przycisk polecenia w systemowych elementach sterujących multimediami, MediaController
systemu wyśle polecenie odtwarzania na urządzenie MediaSession
. Następnie MediaSession
przekazuje te polecenia graczowi. Polecenia zdefiniowane w interfejsie Player
Media3 są obsługiwane automatycznie przez sesję multimediów.
Wskazówki dotyczące odpowiadania na polecenia niestandardowe znajdziesz w sekcji Dodawanie poleceń niestandardowych.
Działanie sprzed Androida 13
Aby zapewnić zgodność wsteczną, interfejs systemu nadal udostępnia alternatywny układ, który wykorzystuje działania związane z powiadomieniami w przypadku aplikacji nieaktualizowanych na Androida 13 lub niezawierających informacji PlaybackState
. Przyciski poleceń pochodzą z listy Notification.Action
dołączonej do powiadomienia MediaStyle
. System wyświetla maksymalnie 5 działań w kolejności, w jakiej zostały dodane. W trybie kompaktowym wyświetlane są maksymalnie 3 przyciski określone na podstawie wartości przekazywanych do setShowActionsInCompactView()
.
Działania niestandardowe są umieszczane w kolejności, w jakiej zostały dodane do zakresu PlaybackState
.
W poniższym przykładzie kodu widać, jak dodać działania do powiadomienia 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();
Obsługa wznowienia multimediów
Wznawianie multimediów umożliwia użytkownikom ponowne uruchamianie poprzednich sesji z karuzeli bez konieczności uruchamiania aplikacji. Po rozpoczęciu odtwarzania użytkownik w zwykły sposób wchodzi w interakcję z elementami sterującymi multimediami.
Funkcję wznawiania odtwarzania możesz włączyć lub wyłączyć w sekcji Dźwięk > Multimedia w aplikacji Ustawienia. Użytkownik może też otworzyć Ustawienia, klikając ikonę koła zębatego, która pojawi się po przesunięciu palcem po rozwiniętej karuzeli.
Media3 udostępnia interfejsy API, które ułatwiają wznawianie odtwarzania multimediów. Wskazówki dotyczące wdrażania tej funkcji znajdziesz w dokumentacji wznawiania odtwarzania za pomocą Media3.
Korzystanie ze starszych interfejsów API multimediów
W tej sekcji wyjaśniamy, jak przeprowadzić integrację z systemowymi elementami sterującymi multimediami za pomocą starszych interfejsów API MediaCompat.
System pobiera te informacje z komponentu MediaMetadata
MediaSession
i wyświetla je, gdy są dostępne:
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 pokazuje postępu)
Aby otrzymać prawidłowe i dokładne powiadomienie dotyczące sterowania multimediami, jako wartość metadanych METADATA_KEY_TITLE
lub METADATA_KEY_DISPLAY_TITLE
podaj tytuł aktualnie odtwarzanych multimediów.
Odtwarzacz pokazuje czas odtwarzania aktualnie odtwarzanych multimediów i pasek przewijania, który jest zmapowany na element MediaSession
PlaybackState
.
Odtwarzacz pokazuje postęp obecnie odtwarzanych multimediów oraz pasek przewijania przypisany do elementu MediaSession
PlaybackState
. Pasek przewijania umożliwia zmianę pozycji elementu multimedialnego i pokazuje czas, który upłynął od danego elementu multimedialnego. Aby włączyć pasek przewijania, musisz zaimplementować funkcję PlaybackState.Builder#setActions
i dodać ACTION_SEEK_TO
.
Boks | Działanie | Kryteria |
---|---|---|
1 | Odtwórz |
Obecny stan elementu PlaybackState to:
|
Wskaźnik wczytywania |
Obecny stan elementu PlaybackState to:
|
|
Wstrzymaj | Obecny stan (PlaybackState ) nie jest jednym z powyższych. |
|
2 | Wstecz | PlaybackState działania obejmują ACTION_SKIP_TO_PREVIOUS . |
Możliwość | PlaybackState działania nie obejmują ACTION_SKIP_TO_PREVIOUS ani PlaybackState działań niestandardowych, które nie obejmują działania niestandardowego, które nie zostało jeszcze wykonane. |
|
Brak | PlaybackState extras zawiera wartość logiczną true dla klucza SESSION_EXTRAS_KEY_SLOT_RESERVATION_SKIP_TO_PREV . |
|
3 | Dalej | PlaybackState działania obejmują ACTION_SKIP_TO_NEXT . |
Możliwość | PlaybackState działania nie obejmują ACTION_SKIP_TO_NEXT ani PlaybackState działań niestandardowych, które nie obejmują działania niestandardowego, które nie zostało jeszcze wykonane. |
|
Brak | PlaybackState extras zawiera wartość logiczną true dla klucza SESSION_EXTRAS_KEY_SLOT_RESERVATION_SKIP_TO_NEXT . |
|
4 | Możliwość | PlaybackState działania niestandardowe obejmują działanie niestandardowe, które nie zostało jeszcze wykonane. |
5 | Możliwość | PlaybackState działania niestandardowe obejmują działanie niestandardowe, które nie zostało jeszcze wykonane. |
Dodaj działania standardowe
Poniższe przykłady kodu pokazują, jak dodać działania standardowe i niestandardowe PlaybackState
.
Odtwarzanie, wstrzymywanie, przejście do poprzedniej i następnej trasy ustaw w polu PlaybackState
sesji multimedialnej.
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 nie chcesz dodawać przycisków w poprzednim lub następnym przedziale, nie dodawaj elementów ACTION_SKIP_TO_PREVIOUS
ani ACTION_SKIP_TO_NEXT
, tylko dodatkowe elementy w sesji:
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
Aby wyświetlić inne działania w panelu sterowania multimediami, możesz utworzyć obiekt PlaybackStateCompat.CustomAction
i dodać go do elementu PlaybackState
. Te działania są wyświetlane
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 PlaybackState
Gdy użytkownik kliknie przycisk, SystemUI użyje MediaController.TransportControls
, aby przesłać polecenie z powrotem do MediaSession
. Musisz zarejestrować wywołanie zwrotne,
które może 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 Twoja aplikacja gracza wyświetlała się w obszarze szybkich ustawień, musisz utworzyć powiadomienie MediaStyle
z ważnym 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ć wznawianie odtwarzania, aplikacje muszą implementować MediaBrowserService
i MediaSession
. MediaSession
musi implementować wywołanie zwrotne onPlay()
.
Implementacja usługi MediaBrowserService
Po uruchomieniu urządzenia system wyszukuje 5 ostatnio używanych aplikacji do multimediów i udostępnia elementy sterujące, które można wykorzystać do ponownego uruchomienia odtwarzania z każdej aplikacji.
System próbuje połączyć się z urządzeniem MediaBrowserService
przez połączenie z interfejsu SystemUI. Twoja aplikacja musi zezwalać na takie połączenia. W przeciwnym razie nie będzie obsługiwać wznawiania odtwarzania.
Połączenia z SystemUI można identyfikować i weryfikować za pomocą nazwy pakietu com.android.systemui
i podpisu. Element SystemUI jest podpisany podpisem platformy. Przykład sprawdzania zgodności z podpisem platformy znajdziesz w aplikacji UAMP.
Aby umożliwić wznawianie odtwarzania, MediaBrowserService
musi stosować te zachowania:
onGetRoot()
musi szybko zwracać pierwiastek o wartości niezerowej. Inne złożone logiki należy obsługiwać w elemencieonLoadChildren()
Wywołanie funkcji
onLoadChildren()
z głównym identyfikatorem mediów w wyniku musi zawierać element podrzędny FLAG_PLAYABLE.Funkcja
MediaBrowserService
powinna zwracać ostatnio odtwarzany element multimedialny po otrzymaniu zapytania EXTRA_RECENT. Zwrócona wartość powinna być rzeczywistym elementem multimedialnym, a nie funkcją ogólną.MediaBrowserService
musi podać odpowiedni MediaDescription, a tytuł i podtytuł nie mogą być puste. Powinien też ustawiać identyfikator URI ikony lub mapę bitową.
Poniższe przykłady kodu pokazują, jak wdrożyć 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); }