Android'deki medya denetimleri Hızlı Ayarlar'ın yanında yer alır. Birden fazla uygulamadan gerçekleştirilen oturumlar, kaydırılabilir bir bantta düzenlenir. Bant, oturumları şu sırayla listeler:
- Telefonda yerel olarak oynatılan canlı yayınlar
- Harici cihazlarda veya yayınlama oturumlarında algılananlar gibi uzak akışlar
- Son oynandıkları sıraya göre önceki devam ettirilebilir oturumlar
Android 13'ten (API düzeyi 33) başlayarak, kullanıcıların medya oynatan uygulamalarla ilgili zengin medya denetimlerine erişebilmesi için medya denetimlerindeki işlem düğmeleri Player
durumundan türetilmiştir.
Böylece, tüm cihazlarda tutarlı bir medya denetimi grubu ve daha gelişmiş bir medya kontrol deneyimi sunabilirsiniz.
Şekil 1'de bu özelliğin bir telefonda ve tablet cihazda nasıl görüneceğine dair bir örnek gösterilmiştir.
Sistem, aşağıdaki tabloda açıklandığı gibi Player
durumuna dayalı olarak en fazla beş işlem düğmesi görüntüler. Kompakt modda yalnızca ilk üç işlem slotu gösterilir. Bu aynı zamanda medya denetimlerinin Auto, Asistan ve Wear OS gibi diğer Android platformlarında oluşturulma şekliyle de uyumludur.
Yer | Ölçütler | İşlem |
---|---|---|
1 |
playWhenReady yanlış veya geçerli oynatma durumu STATE_ENDED şeklindedir.
|
Oynat |
playWhenReady doğru ve mevcut oynatma durumu STATE_BUFFERING .
|
Yükleme döner simgesi | |
playWhenReady doğru ve mevcut oynatma durumu STATE_READY . |
Duraklat | |
2 | Oynatıcı komutu COMMAND_SEEK_TO_PREVIOUS veya COMMAND_SEEK_TO_PREVIOUS_MEDIA_ITEM kullanılabilir. |
Önceki |
COMMAND_SEEK_TO_PREVIOUS veya COMMAND_SEEK_TO_PREVIOUS_MEDIA_ITEM oynatıcı komutu mevcut değildir. Ayrıca, alanı doldurmak için henüz yerleştirilmemiş özel düzenden özel bir komut kullanılabilir. |
Özel | |
(henüz Media3 ile desteklenmemektedir) PlaybackState ekstraları EXTRAS_KEY_SLOT_RESERVATION_SEEK_TO_PREV anahtarı için bir 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 |
COMMAND_SEEK_TO_NEXT veya COMMAND_SEEK_TO_NEXT_MEDIA_ITEM oynatıcı komutu mevcut değildir. Ayrıca, alanı doldurmak için henüz yerleştirilmemiş özel düzenden özel bir komut kullanılabilir. |
Özel | |
(henüz Media3 ile desteklenmemektedir) PlaybackState ekstraları EXTRAS_KEY_SLOT_RESERVATION_SEEK_TO_NEXT anahtarı için bir true boole değeri içerir. |
Boş | |
4 | Alanı doldurmak için, henüz yerleştirilmemiş özel düzenden özel bir komut kullanılabilir. | Özel |
5 | Alanı doldurmak için, henüz yerleştirilmemiş özel düzenden özel bir komut kullanılabilir. | Özel |
Özel komutlar, özel düzene eklendikleri sırayla yerleştirilir.
Komut düğmelerini özelleştir
Sistem medya denetimlerini Jetpack Media3 ile özelleştirmek için MediaSessionService
uygularken oturumun özel düzenini ve denetleyicilerin kullanılabilir komutlarını buna göre ayarlayabilirsiniz:
onCreate()
ürünündeMediaSession
oluşturun ve komut düğmelerinin özel düzenini tanımlayın.MediaSession.Callback.onConnect()
'da özel komutlar dahil olmak üzere mevcut komutlarınıConnectionResult
içinde tanımlayarak denetleyicileri yetkilendirin.MediaSession.Callback.onCustomCommand()
'da, 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); } } }
MediaSession
öğenizi sistem gibi istemcilerin medya uygulamanıza bağlanabileceği şekilde yapılandırma hakkında daha fazla bilgi edinmek için Diğer istemcilere kontrol verme bölümüne bakın.
Jetpack Media3 ile bir MediaSession
uyguladığınızda PlaybackState
cihazınız medya oynatıcı ile otomatik olarak güncel tutulur. Benzer şekilde, bir MediaSessionService
uyguladığınızda kitaplık sizin için otomatik olarak MediaStyle
bildirim yayınlar ve güncel tutar.
İşlem düğmelerine yanıt verme
Kullanıcı, sistem medya denetimlerindeki bir işlem düğmesine dokunduğunda, sistemin MediaController
'i MediaSession
cihazınıza bir oynatma komutu gönderir. Ardından MediaSession
, bu komutları oyuncuya devreder. Media3'ün Player
arayüzünde tanımlanan komutlar, medya oturumu tarafından otomatik olarak işlenir.
Özel bir komuta nasıl yanıt verileceği hakkında yardım için Özel komutlar ekleme bölümüne bakın.
Android 13 Öncesinde Davranış
Geriye dönük uyumluluk amacıyla Sistem Kullanıcı Arayüzü, Android 13'ü hedefleyecek şekilde güncellenmeyen veya PlaybackState
bilgilerini içermeyen uygulamalar için bildirim işlemleri kullanan alternatif bir düzen sağlamaya devam etmektedir. İşlem düğmeleri, MediaStyle
bildirimine ekli Notification.Action
listesinden türetilir. Sistem, eklendikleri sırayla en çok beş işlem görüntüler. Kompakt modda, setShowActionsInCompactView()
öğesine geçirilen değerlere göre belirlenen en fazla üç düğme gösterilir.
Özel işlemler, PlaybackState
öğesine eklendikleri sırayla 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 ettirmeyi destekle
Medya devam ettirme, kullanıcıların uygulamayı başlatmak zorunda kalmadan önceki oturumları banttan yeniden başlatmalarına olanak tanır. Oynatma başladığında kullanıcı, medya kontrolleriyle her zamanki gibi etkileşimde bulunur.
Oynatma devam ettirme özelliği, Ayarlar uygulaması kullanılarak Ses > Medya seçenekleri altından açılabilir ve kapatılabilir. Kullanıcı, Ayarlar'a, genişletilmiş atlı karıncada kaydırdıktan sonra görünen dişli simgesine dokunarak da erişebilir.
Media3, medya devam ettirmeyi kolaylaştırmak için API'ler sunar. Bu özelliğin uygulanması hakkında yardım almak için Media3 ile oynatma devam ettirme dokümanlarına bakın.
Eski medya API'larını kullanma
Bu bölümde, eski MediaCompat API'lerini kullanarak sistem medya denetimleriyle nasıl entegrasyon yapılacağı açıklanmaktadır.
Sistem, MediaSession
MediaMetadata
öğesinden aşağıdaki bilgileri alır ve kullanılabilir olduğunda görüntüler:
METADATA_KEY_ALBUM_ART_URI
METADATA_KEY_TITLE
METADATA_KEY_DISPLAY_TITLE
METADATA_KEY_ARTIST
METADATA_KEY_DURATION
(Süre ayarlanmazsa arama çubuğunda ilerleme durumu gösterilmez)
Geçerli ve doğru bir medya denetimi bildiriminiz olduğundan emin olmak için METADATA_KEY_TITLE
veya METADATA_KEY_DISPLAY_TITLE
meta verilerinin değerini, oynatılmakta olan medyanın başlığına ayarlayın.
Medya oynatıcı, şu anda oynatılan medya için geçen süreyi ve MediaSession
PlaybackState
ile eşleştirilmiş arama çubuğunu gösterir.
Medya oynatıcı, şu anda oynatılan medyanın ilerleme durumunu ve MediaSession
PlaybackState
ile eşleştirilmiş bir arama çubuğu gösterir. Arama çubuğu, kullanıcıların konumu değiştirmesine olanak tanır ve medya öğesi için geçen süreyi görüntüler. Arama çubuğunun etkinleştirilmesi için PlaybackState.Builder#setActions
işlemini uygulamanız ve ACTION_SEEK_TO
eklemeniz gerekir.
Yer | İşlem | Ölçütler |
---|---|---|
1 | Oynat |
PlaybackState için mevcut durum aşağıdakilerden biridir:
|
Yükleme döner simgesi |
PlaybackState için mevcut eyalet aşağıdakilerden biridir:
|
|
Duraklat | PlaybackState öğesinin mevcut durumu yukarıdakilerin hiçbiri değil. |
|
2 | Önceki | PlaybackState işlem şunları içeriyor: ACTION_SKIP_TO_PREVIOUS . |
Özel | PlaybackState işlem ACTION_SKIP_TO_PREVIOUS içermiyor. PlaybackState özel işlemi ise henüz yerleştirilmemiş bir özel işlem içeriyor. |
|
Boş | PlaybackState ekstralar, SESSION_EXTRAS_KEY_SLOT_RESERVATION_SKIP_TO_PREV anahtarı için true boole değeri içerir. |
|
3 | Sonraki | PlaybackState işlem şunları içeriyor: ACTION_SKIP_TO_NEXT . |
Özel | PlaybackState işlem ACTION_SKIP_TO_NEXT içermiyor. PlaybackState özel işlemi ise henüz yerleştirilmemiş bir özel işlem içeriyor. |
|
Boş | PlaybackState ekstralar, SESSION_EXTRAS_KEY_SLOT_RESERVATION_SKIP_TO_NEXT anahtarı için true boole değeri içerir. |
|
4 | Özel | PlaybackState özel işlem henüz yerleştirilmemiş bir özel işlem içeriyor. |
5 | Özel | PlaybackState özel işlem henüz yerleştirilmemiş bir özel işlem içeriyor. |
Standart işlemler ekleyin
Aşağıdaki kod örneklerinde PlaybackState
standart ve özel işlemlerinin nasıl ekleneceği gösterilmektedir.
Oynatma, duraklatma, önceki ve sonraki için bu işlemleri medya oturumunun PlaybackState
bölümünde 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 düğme olmasını istemiyorsanız ACTION_SKIP_TO_PREVIOUS
veya ACTION_SKIP_TO_NEXT
öğelerini eklemeyin, bunun yerine oturuma ekstra öğ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şlemler ekleme
Medya denetimlerinde gösterilmesini istediğiniz diğer işlemler için PlaybackStateCompat.CustomAction
oluşturup bunu PlaybackState
öğesine ekleyebilirsiniz. Bu işlemler eklendikleri sırayla 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
cihazına geri bir komut göndermek için MediaController.TransportControls
aracını kullanır. Bu etkinliklere doğru şekilde yanıt verebilecek
bir geri çağırma 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ünmesini sağlamak için geçerli 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ıda marka simgesini görüntülemek için NotificationBuilder.setSmallIcon()
değerini kullanın.
Oynatmayı devam ettirmeyi desteklemek için uygulamaların bir MediaBrowserService
ve MediaSession
uygulaması gerekir. MediaSession
öğeniz, onPlay()
geri çağırma işlevini uygulamalıdır.
MediaBrowserService
uygulama
Cihaz başlatıldıktan sonra, sistem en son kullanılan beş medya uygulamasını arar ve her uygulamadan oynatmayı yeniden başlatmak için kullanılabilecek denetimler sağlar.
Sistem, SystemUI'den bir bağlantı üzerinden MediaBrowserService
cihazınızla iletişim kurmaya çalışır. Uygulamanızın bu tür bağlantılara izin vermesi gerekir, aksi halde oynatma devam ettirmeyi destekleyemez.
SystemUI ile kurulan 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ının nasıl kontrol edileceğini gösteren bir örneği UAMP uygulamasında bulabilirsiniz.
Oynatmayı devam ettirmeyi desteklemek için MediaBrowserService
cihazınızın şu davranışları uygulaması gerekir:
onGetRoot()
, hızlı bir şekilde boş olmayan bir kök döndürmelidir. Diğer karmaşık mantıklaronLoadChildren()
konumunda ele alınmalıdırKök medya kimliğinde
onLoadChildren()
çağrıldığında, sonuç bir FLAG_PLAYABLE alt öğesi içermelidir.MediaBrowserService
, EXTRA_RECENT sorgusu aldığında en son oynatılan medya öğesini döndürecektir. Döndürülen değer, genel bir işlev değil, gerçek bir medya öğesi olmalıdır.MediaBrowserService
, boş olmayan bir başlık ve altyazı ile uygun bir MediaDescription sağlamalıdır. Ayrıca, bir simge URI'si veya bir simge bit eşlemi de ayarlamalıdır.
Aşağıdaki kod örneklerinde onGetRoot()
özelliğinin nasıl uygulanacağı gösterilmektedir.
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); }