Android'deki medya kontrolleri, Hızlı Ayarlar'ın yakınında bulunur. Birden fazla uygulamadan gelen oturumlar, kaydırılabilir bir bantta düzenlenir. Bant, oturumları şu sırayla listeler:
- Telefonda yerel olarak oynatılan akışlar
- Harici cihazlarda veya yayınlama oturumlarında algılananlar gibi uzaktan yayınlar
- Son oynatıldıkları sırayla, devam ettirilebilir önceki oturumlar
Android 13'ten (API düzeyi 33) itibaren, kullanıcıların medya oynatan uygulamalar için zengin bir medya kontrolleri grubuna erişebilmesini sağlamak amacıyla medya kontrollerindeki işlem düğmeleri Player
durumundan türetilir.
Böylece, cihazlar arasında tutarlı bir medya kumandası grubu ve daha iyi bir medya kumandası deneyimi sunabilirsiniz.
Şekil 1'de, bunun sırasıyla telefon ve tablet cihazlarda nasıl göründüğüne dair bir örnek gösterilmektedir.
Sistem, aşağıdaki tabloda açıklandığı gibi Player
durumuna göre en fazla beş işlem düğmesi gösterir. Kompakt modda yalnızca ilk üç işlem yuvası gösterilir. Bu, Auto, Asistan ve Wear OS gibi diğer Android platformlarında medya kontrollerinin nasıl oluşturulduğuyla uyumludur.
Alan | Ölçütler | İşlem |
---|---|---|
1 |
playWhenReady yanlışsa veya mevcut oynatma durumu STATE_ENDED ise.
|
Oynat |
playWhenReady doğruysa ve mevcut oynatma durumu STATE_BUFFERING ise.
|
Yükleme döner simgesi | |
playWhenReady doğruysa ve mevcut oynatma durumu STATE_READY ise. |
Duraklat | |
2 | Oynatıcı komutu COMMAND_SEEK_TO_PREVIOUS veya COMMAND_SEEK_TO_PREVIOUS_MEDIA_ITEM kullanılabilir. |
Önceki |
Oynatıcı komutu COMMAND_SEEK_TO_PREVIOUS veya COMMAND_SEEK_TO_PREVIOUS_MEDIA_ITEM kullanılamaz. Ayrıca, özel düzendeki ve henüz yerleştirilmemiş bir özel komut, alanı doldurmak için kullanılabilir. |
Özel | |
Oturum ekstraları, EXTRAS_KEY_SLOT_RESERVATION_SEEK_TO_PREV anahtarı için true boole değeri içerir. |
Boş | |
3 | Oynatıcı komutu COMMAND_SEEK_TO_NEXT veya COMMAND_SEEK_TO_NEXT_MEDIA_ITEM kullanılabilir. |
Sonraki |
Oynatıcı komutu COMMAND_SEEK_TO_NEXT veya COMMAND_SEEK_TO_NEXT_MEDIA_ITEM kullanılamaz. Ayrıca, özel düzendeki ve henüz yerleştirilmemiş bir özel komut, alanı doldurmak için kullanılabilir. |
Özel | |
Oturum ekstraları, EXTRAS_KEY_SLOT_RESERVATION_SEEK_TO_NEXT anahtarı için true boole değeri içerir. |
Boş | |
4 | Özel düzendeki henüz yerleştirilmemiş bir özel komut, alanı doldurmak için kullanılabilir. | Özel |
5 | Özel düzendeki henüz yerleştirilmemiş bir özel komut, alanı doldurmak için kullanılabilir. | Özel |
Özel komutlar, özel düzene eklendikleri sıraya göre yerleştirilir.
Komut düğmelerini özelleştirme
Jetpack Media3 ile sistem medya kontrollerini özelleştirmek için MediaSessionService
uygularken oturumun özel düzenini ve denetleyicilerin mevcut komutlarını uygun şekilde ayarlayabilirsiniz:
onCreate()
içinde birMediaSession
oluşturun ve komut düğmelerinin özel düzenini tanımlayın.MediaSession.Callback.onConnect()
'te,ConnectionResult
'te özel komutlar da dahil olmak üzere kullanılabilir komutlarını tanımlayarak denetleyicilere yetki verin.MediaSession.Callback.onCustomCommand()
bölümünde, kullanıcı tarafından seçilen özel komuta yanıt verin.
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); } } }
Sistem gibi istemcilerin medya uygulamanıza bağlanabilmesi için MediaSession
'ünüzü yapılandırma hakkında daha fazla bilgi edinmek istiyorsanız Diğer istemcilere kontrol verme başlıklı makaleyi inceleyin.
Jetpack Media3 ile bir MediaSession
uyguladığınızda PlaybackState
, medya oynatıcıyla otomatik olarak güncel tutulur. Benzer şekilde, bir MediaSessionService
uyguladığınızda kitaplık sizin için otomatik olarak bir MediaStyle
bildirimi yayınlar ve bu bildirimi güncel tutar.
İşlem düğmelerine yanıt verme
Kullanıcı, sistem medya kontrollerindeki bir işlem düğmesine dokunduğunda sistem MediaController
, MediaSession
cihazınıza oynatma komutu gönderir. MediaSession
daha sonra bu komutları oynatıcıya iletir. Media3'ün Player
arayüzünde tanımlanan komutlar, medya oturumu tarafından otomatik olarak işlenir.
Özel komutlara nasıl yanıt vereceğinizle ilgili bilgi için Özel komut ekleme başlıklı makaleyi inceleyin.
Android 13'ten önceki davranış
Sistem kullanıcı arayüzü, geriye dönük uyumluluk için Android 13'ü hedeflemek üzere güncellenmeyen veya PlaybackState
bilgilerini içermeyen uygulamalar için bildirim işlemlerini kullanan alternatif bir düzen sunmaya devam eder. İşlem düğmeleri, MediaStyle
bildirimine eklenmiş Notification.Action
listesinden türetilir. Sistem, en fazla beş işlemi eklendikleri sırayla gösterir. Kompakt modda, setShowActionsInCompactView()
parametresine iletilen değerlere göre üç adede kadar düğme gösterilir.
Özel işlemler, PlaybackState
'e eklendikleri sıraya göre yerleştirilir.
Aşağıdaki kod örneğinde, MediaStyle bildirimine nasıl işlem ekleneceği gösterilmektedir :
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();
Medyayı devam ettirme desteği
Medya devam ettirme özelliği, kullanıcıların uygulamayı başlatmak zorunda kalmadan önceki oturumları banttan yeniden başlatmasına olanak tanır. Oynatma başladığında kullanıcı, medya kontrolleriyle normal şekilde etkileşim kurar.
Oynatma işlemini devam ettirme özelliğini Ayarlar uygulamasındaki Ses > Medya seçenekleri altından etkinleştirebilir veya devre dışı bırakabilirsiniz. Kullanıcı, genişletilmiş bantta kaydırdıktan sonra görünen dişli simgesine dokunarak da Ayarlar'a erişebilir.
Media3, medya oynatmanın devam etmesini desteklemeyi kolaylaştırmak için API'ler sunar. Bu özelliği uygulamayla ilgili yardım için Media3 ile oynatmayı devam ettirme dokümanlarını inceleyin.
Eski medya API'lerini kullanma
Bu bölümde, eski MediaCompat API'leri kullanılarak sistem medya denetimleriyle nasıl entegrasyon yapılacağı açıklanmaktadır.
Sistem, MediaSession
'nin MediaMetadata
'sinden aşağıdaki bilgileri alır ve kullanılabilir olduğunda gösterir:
METADATA_KEY_ALBUM_ART_URI
METADATA_KEY_TITLE
METADATA_KEY_DISPLAY_TITLE
METADATA_KEY_ARTIST
METADATA_KEY_DURATION
(Süre ayarlanmamışsa ilerleme durumu kaydırma çubuğunda gösterilmez)
Geçerli ve doğru bir medya kontrolü bildirimi alabilmeniz için METADATA_KEY_TITLE
veya METADATA_KEY_DISPLAY_TITLE
meta verilerinin değerini, şu anda oynatılan medyanın başlığına ayarlayın.
Medya oynatıcı, şu anda oynatılan medyanın geçtiği süreyi ve MediaSession
PlaybackState
ile eşlenen bir arama çubuğunu gösterir.
Medya oynatıcı, MediaSession
PlaybackState
ile eşlenen bir arama çubuğuyla birlikte, oynatılmakta olan medyanın ilerleme durumunu gösterir. Arama çubuğu, kullanıcıların konumu değiştirmesine olanak tanır ve medya öğesinin geçen süresini gösterir. Arama çubuğunun etkinleştirilebilmesi için PlaybackState.Builder#setActions
'ü uygulamanız ve ACTION_SEEK_TO
'u eklemeniz gerekir.
Alan | İşlem | Ölçütler |
---|---|---|
1 | Oynat |
PlaybackState 'nin mevcut durumu aşağıdakilerden biri olmalıdır:
|
Yükleme döner simgesi |
PlaybackState 'nin mevcut durumu aşağıdakilerden biridir:
|
|
Duraklat | PlaybackState öğesinin mevcut durumu yukarıdakilerden hiçbiri değil. |
|
2 | Önceki | PlaybackState işlemleri ACTION_SKIP_TO_PREVIOUS içerir. |
Özel | PlaybackState işlemler ACTION_SKIP_TO_PREVIOUS içermez ve PlaybackState özel işlemler henüz yerleştirilmemiş bir özel işlem içerir. |
|
Boş | PlaybackState extras, SESSION_EXTRAS_KEY_SLOT_RESERVATION_SKIP_TO_PREV anahtarı için true boole değeri içerir. |
|
3 | Sonraki | PlaybackState işlemleri ACTION_SKIP_TO_NEXT içerir. |
Özel | PlaybackState işlemler ACTION_SKIP_TO_NEXT içermez ve PlaybackState özel işlemler henüz yerleştirilmemiş bir özel işlem içerir. |
|
Boş | PlaybackState extras, SESSION_EXTRAS_KEY_SLOT_RESERVATION_SKIP_TO_NEXT anahtarı için true boole değeri içerir. |
|
4 | Özel | PlaybackState özel işlemler henüz yerleştirilmemiş bir özel işlem içeriyor. |
5 | Özel | PlaybackState özel işlemler henüz yerleştirilmemiş bir özel işlem içeriyor. |
Standart işlemler ekleme
Aşağıdaki kod örnekleri, PlaybackState
standart ve özel işlemlerin nasıl ekleneceğini göstermektedir.
Oynatma, duraklatma, önceki ve sonraki işlemleri medya oturumu için PlaybackState
içinde ayarlayı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);
Önceki veya sonraki alanlarda herhangi bir düğme istemiyorsanız ACTION_SKIP_TO_PREVIOUS
veya ACTION_SKIP_TO_NEXT
eklemeyin. Bunun yerine oturuma ek öğeler ekleyin:
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);
Özel işlem ekleme
Medya kontrollerinde göstermek istediğiniz diğer işlemler için bir PlaybackStateCompat.CustomAction
oluşturabilir ve bunu PlaybackState
'a ekleyebilirsiniz. Bu işlemler, eklendikleri sıraya göre gösterilir.
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 işlemlerine yanıt verme
Kullanıcı bir düğmeye dokunduğunda SystemUI, MediaSession
'a geri komut göndermek için MediaController.TransportControls
'u kullanır. Bu etkinliklere uygun şekilde yanıt verebilecek bir geri çağırma işlevi kaydetmeniz gerekir.
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); } } };
Medyayı devam ettirme
Oynatıcı uygulamanızın hızlı ayar ayarları alanında görünmesi için geçerli bir MediaSession
jetonu içeren bir MediaStyle
bildirimi oluşturmanız gerekir.
MediaStyle bildiriminin başlığını görüntülemek için NotificationBuilder.setContentTitle()
öğesini kullanın.
Medya oynatıcının marka simgesini görüntülemek için NotificationBuilder.setSmallIcon()
simgesini kullanın.
Uygulamaların oynatmayı devam ettirme özelliğini desteklemesi için MediaBrowserService
ve MediaSession
uygulamalarını kullanması gerekir. MediaSession
, onPlay()
geri arama işlevini uygulamalıdır.
MediaBrowserService
uygulaması
Cihaz açıldıktan sonra sistem, en son kullanılan beş medya uygulamasını arar ve her uygulamadan oynatmayı yeniden başlatmak için kullanılabilecek kontroller sağlar.
Sistem, SystemUI üzerinden MediaBrowserService
cihazınızla iletişim kurmaya çalışır. Uygulamanız bu tür bağlantılara izin vermelidir. Aksi takdirde oynatmanın devam ettirilmesini destekleyemez.
SystemUI'den gelen bağlantılar, paket adı com.android.systemui
ve imza kullanılarak tanımlanıp doğrulanabilir. SystemUI, platform imzasıyla imzalanır. Platform imzasıyla karşılaştırma yapmayı gösteren bir örneği UAMP uygulamasında bulabilirsiniz.
Oynatma işleminin devam ettirilmesini desteklemek için MediaBrowserService
'ün aşağıdaki davranışları uygulaması gerekir:
onGetRoot()
, kısa sürede null olmayan bir kök döndürmelidir. Diğer karmaşık mantıklaronLoadChildren()
Kök medya kimliğinde
onLoadChildren()
çağrıldığında sonuçta bir FLAG_PLAYABLE alt öğesi bulunmalıdır.MediaBrowserService
, EXTRA_RECENT sorgusu aldığında en son oynatılan medya öğesini döndürmelidir. Döndürülen değer, genel işlev yerine gerçek bir medya öğesi olmalıdır.MediaBrowserService
, boş olmayan bir başlık ve altyazı içeren uygun bir MediaDescription sağlamalıdır. Ayrıca bir simge URI'si veya simge bitmap'i de ayarlanmalıdır.
Aşağıdaki kod örnekleri, onGetRoot()
değerinin nasıl uygulanacağını göstermektedir.
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); }