Android'deki medya denetimleri Hızlı Ayarlar'ın yakınındadır. Şu kaynaktan gelen oturumlar: birden fazla uygulama kaydırılabilir bir bantta düzenlenir. Bant, oturumları listeler şu sırayla:
- Telefonda yerel olarak oynatılan canlı yayınlar
- Harici cihazlarda veya yayın oturumlarında algılananlar gibi uzak yayınlar
- Son çalındıkları sırayla önceki devam ettirilebilir oturumlar
Kullanıcıların zengin bir içeriğe erişebilmesini sağlamak için Android 13'ten (API düzeyi 33) itibaren
Medya oynatan uygulamalar için medya denetimleri grubu, medya denetimlerindeki işlem düğmeleri
Player
durumundan türetilir.
Böylece tutarlı medya denetimleri sunabilir ve daha gösterişli tüm cihazlarda medya kontrolü deneyimi.
Şekil 1'de bunun bir telefon ve tablet cihazında nasıl görüneceği gösterilmektedir. tıklayın.
Sistem, Player
durumuna göre en fazla beş işlem düğmesi gösterir.
aşağıdaki tabloda açıklanmıştır. Kompakt modda, yalnızca ilk üç işlem
yuva gösterilir. Bu, medya denetimlerinin diğer
Auto, Asistan ve Wear OS gibi Android platformları.
Alan | Ölçütler | İşlem |
---|---|---|
1 |
playWhenReady
yanlış veya geçerli oynatma
eyalet STATE_ENDED .
|
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 kullanılamaz. Ayrıca henüz yerleştirilmeyen özel düzenden özel bir komut, alanı doldurmak için kullanılabilir. |
Özel | |
(henüz Media3 ile desteklenmemektedir) PlaybackState 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 |
COMMAND_SEEK_TO_NEXT veya COMMAND_SEEK_TO_NEXT_MEDIA_ITEM oynatıcı komutu kullanılamaz. Ayrıca henüz yerleştirilmeyen özel düzenden özel bir komut, alanı doldurmak için kullanılabilir. |
Özel | |
(henüz Media3 ile desteklenmemektedir) PlaybackState ekstraları, EXTRAS_KEY_SLOT_RESERVATION_SEEK_TO_NEXT anahtarı için true boole değeri içerir. |
Boş | |
4 | Alanı doldurmak için henüz yerleştirilmeyen özel düzenden özel bir komut kullanılabilir. | Özel |
5 | Alanı doldurmak için henüz yerleştirilmeyen özel düzenden özel bir komut kullanılabilir. | Özel |
Özel komutlar özel düzen.
Komut düğmelerini özelleştir
Sistem medya denetimlerini Jetpack Media3 ile özelleştirmek için
oturumun özel düzenini ve
uygun şekilde kontrol edin: MediaSessionService
uygularken:
onCreate()
bölgesindeMediaSession
oluşturun ve özel düzeni tanımlamanızı sağlar ekleyebilirsiniz.MediaSession.Callback.onConnect()
bölgesinde, Mevcut komutlarını tanımlayarak denetleyicileri yetkilendirin. Örneğin, özel komutlarConnectionResult
içinde.MediaSession.Callback.onCustomCommand()
bölgesinde, kullanıcı tarafından seçilen özel komuta yanıt verme.
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
yapılandırması hakkında daha fazla bilgi edinmek için
uygulamanıza bağlanabildiğini görmek için
Diğer istemcilere denetim izni verin.
Jetpack Media3 ile bir MediaSession
uyguladığınızda, PlaybackState
medya oynatıcıyla otomatik olarak güncel tutulur. Aynı şekilde
MediaSessionService
uygulanırsa kitaplık otomatik olarak bir
MediaStyle
bildirim
güncel tutar.
İşlem düğmelerine yanıt verme
Bir kullanıcı, sistem medya denetimlerindeki bir işlem düğmesine dokunduğunda sistem
MediaController
, MediaSession
cihazınıza bir oynatma komutu gönderir. İlgili içeriği oluşturmak için kullanılan
MediaSession
, ardından bu komutları oyuncuya delege eder. Komutlar
Media3'ün Player
bölümünde tanımlanmıştır
arayüz, medya öğeleri tarafından otomatik olarak işlenir.
kabul edilir.
Özel komut ekleme bölümünü inceleyin. inceleyebilirsiniz.
Android 13 Öncesi Davranış
Sistem kullanıcı arayüzü, geriye dönük uyumluluk için alternatif bir düzen sağlamaya devam eder.
Android 13'ü hedefleyecek şekilde güncellenmeyen uygulamalar için bildirim işlemleri kullanan
veya PlaybackState
bilgilerini içermez. İşlem düğmeleri
MediaStyle
öğesine ekli Notification.Action
listesinden türetilmiştir
bildirimi görürsünüz. Sistem en çok beş işlemi gerçekleştirildikleri sırada görüntüler
eklendi. Kompakt modundayken
setShowActionsInCompactView()
değerine geçirilen değerlerdir.
Özel işlemler
PlaybackState
Aşağıdaki kod örneğinde, MediaStyle'a işlem eklemek için bildirim :
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
Medyayı devam ettirme, kullanıcıların önceki oturumları banttan yeniden başlatmasına olanak tanır ve uygulamayı başlatmak zorunda kalmadan. Oynatma başladığında kullanıcı normal şekilde değiştirebilirsiniz.
Oynatmayı devam ettirme özelliği, Ayarlar uygulaması kullanılarak açılıp kapatılabilir. Ses > Medya seçenekleri. Kullanıcı Ayarlar'a şu şekilde de erişebilir: Genişletilmiş bantta hızlıca kaydırdıktan sonra görünen dişli simgesine dokunun.
Media3, medyaya devam ettirmeyi desteklemeyi kolaylaştıran API'ler sunar. Bkz. Media3 ile oynatmayı devam ettirme dokümanlarına göz atabilirsiniz.
Eski medya API'lerini kullanma
Bu bölümde, eski MediaCompat API'leri.
Sistem, Google Etiket Yöneticisi'nden aşağıdaki bilgileri
MediaSession
adlı çocuğun MediaMetadata
parametresidir ve mümkün 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 arama çubuğu durmaz ilerleme durumunu göster)
Geçerli ve doğru bir medya kontrolü bildiriminiz olduğundan emin olmak için
METADATA_KEY_TITLE
veya METADATA_KEY_DISPLAY_TITLE
değerini ayarlayın
oynatılmakta olan medyanın başlığına
eşlemesini sağlar.
Medya oynatıcı, oynatılan oynatma için geçen süreyi gösterir
MediaSession
ile eşlenen arama çubuğuyla birlikte medya
PlaybackState
.
Medya oynatıcı, oynatılan medyanın ilerleme durumunu
MediaSession
PlaybackState
ile eşlenen bir arama çubuğu. Arama çubuğu
Kullanıcıların konumu değiştirmesine olanak tanır ve medya içeriği için geçen süreyi görüntüler
öğe. Arama çubuğunun etkinleştirilmesi için
PlaybackState.Builder#setActions
ve ACTION_SEEK_TO
dahil edin.
Alan | İşlem | Ölçütler |
---|---|---|
1 | Oynat |
PlaybackState bilgisinin mevcut eyaleti aşağıdakilerden biridir:
|
Yükleme döner simgesi |
PlaybackState bilgisinin mevcut eyaleti aşağıdakilerden biridir:
|
|
Duraklat | PlaybackState bölgesinin mevcut eyaleti yukarıdakilerin hiçbirinden farklıdır. |
|
2 | Önceki | PlaybackState işlem ACTION_SKIP_TO_PREVIOUS içeriyor. |
Özel | PlaybackState işlemleri ACTION_SKIP_TO_PREVIOUS içermiyor ve PlaybackState özel işlemi henüz uygulanmamış 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 ACTION_SKIP_TO_NEXT içeriyor. |
Özel | PlaybackState işlemleri ACTION_SKIP_TO_NEXT içermiyor ve PlaybackState özel işlemi henüz uygulanmamış 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şlemi henüz yerleştirilmemiş bir özel işlem içeriyor. |
5 | Özel | PlaybackState özel işlemi henüz yerleştirilmemiş bir özel işlem içeriyor. |
Standart işlemler ekle
Aşağıdaki kod örneklerinde PlaybackState
standardının ve reklam öğelerinin
özel işlemler.
Oynatma, duraklatma, önceki ve sonraki için bu işlemleri
medya oturumu için PlaybackState
.
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 eklemek için
ACTION_SKIP_TO_PREVIOUS
veya ACTION_SKIP_TO_NEXT
olup yerine ekstralar ekleyebilirsiniz:
oturum:
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 ekleyin
Medya denetimlerinde gösterilmesini istediğiniz diğer işlemler için
PlaybackStateCompat.CustomAction
bunun yerine bunu PlaybackState
öğesine ekleyin. Bu işlemler,
bir sıralamayı görürsünüz.
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);
Oynatma Durumu işlemlerine yanıt verme
Kullanıcı bir düğmeye dokunduğunda SystemUI
MediaController.TransportControls
MediaSession
öğesine bir komut gönderin. Geri çağırma kaydetmeniz gerekiyor
yanıt verebilen bir grup insandır.
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ı Ayarlar alanında görünmesini sağlamak için
geçerli bir MediaSession
jetonuyla MediaStyle
bildirimi oluşturmanız gerekir.
MediaStyle bildiriminin başlığını görüntülemek için
NotificationBuilder.setContentTitle()
Medya oynatıcıda marka simgesini görüntülemek için şunu kullanın:
NotificationBuilder.setSmallIcon()
Oynatmayı devam ettirmeyi desteklemek için uygulamaların bir MediaBrowserService
uygulaması gerekir
ve MediaSession
. MediaSession
uygulamanız, onPlay()
geri çağırmasını uygulamalıdır.
MediaBrowserService
uygulama
Cihaz başlatıldıktan sonra, sistem en son kullanılan beş medyayı arar ve her uygulamadan oynatmayı yeniden başlatmak için kullanılabilecek kontroller sağlar.
Sistem, şu kaynaktan bir bağlantıyla MediaBrowserService
ile iletişim kurmaya çalışır:
Sistem Arayüzü. Uygulamanız bu tür bağlantılara izin vermelidir, aksi takdirde desteklenmiyor
oynatmaya devam etmesini sağlar.
SystemUI'den gelen bağlantılar, paket adı kullanılarak tanımlanıp doğrulanabilir
com.android.systemui
ve imza. SystemUI, platformla imzalanır
imzası var. Platform imzasını karşılaştırarak kontrol etme
UAMP uygulamasında bulabilirsiniz.
Oynatmayı devam ettirmeyi desteklemek için MediaBrowserService
cihazınız
şu davranışları uygulayabilirsiniz:
onGetRoot()
, hızlı bir şekilde null olmayan bir kök döndürmelidir. Diğer karmaşık mantıkonLoadChildren()
içinde ele alınacakZaman
onLoadChildren()
, kök medya kimliğinde çağrılıyor. Sonuç, bir FLAG_PLAYABLE alt öğesi olarak görünür.MediaBrowserService
, şu durumda en son oynatılan medya öğesini döndürmelidir: CANNOT TRANSLATE EKSTRA_SON emin olun. Döndürülen değer, genel değil, gerçek bir medya öğesi olmalıdır işlevini kullanın.MediaBrowserService
, uygun bir Boş olmayan bir MediaDescription title ve altyazı. Ayrıca, simge URI'si veya simge bit eşlemi kullanır.
Aşağıdaki kod örnekleri, onGetRoot()
uygulamasının nasıl uygulanacağını gösterir.
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); }