diff --git a/libraries/session/src/main/java/androidx/media3/session/MediaConstants.java b/libraries/session/src/main/java/androidx/media3/session/MediaConstants.java index 0ebcd49075..d79385f5e4 100644 --- a/libraries/session/src/main/java/androidx/media3/session/MediaConstants.java +++ b/libraries/session/src/main/java/androidx/media3/session/MediaConstants.java @@ -389,12 +389,31 @@ public final class MediaConstants { * * @see MediaLibrarySession.Callback#onGetLibraryRoot(MediaLibrarySession, * MediaSession.ControllerInfo, LibraryParams) + * @see MediaBrowser#getLibraryRoot(LibraryParams) * @see LibraryParams#extras */ @UnstableApi public static final String EXTRAS_KEY_ROOT_CHILDREN_LIMIT = androidx.media.utils.MediaConstants.BROWSER_ROOT_HINTS_KEY_ROOT_CHILDREN_LIMIT; + /** + * {@link Bundle} key used in {@link LibraryParams#extras} passed to {@link + * MediaLibrarySession.Callback#onGetLibraryRoot(MediaLibrarySession, MediaSession.ControllerInfo, + * LibraryParams)} to indicate whether only browsable media items are supported as children of the + * root node by the {@link MediaBrowser}. If true, root children that are not browsable may be + * omitted or made less discoverable. + * + *
TYPE: boolean.
+ *
+ * @see MediaLibrarySession.Callback#onGetLibraryRoot(MediaLibrarySession,
+ * MediaSession.ControllerInfo, LibraryParams)
+ * @see MediaBrowser#getLibraryRoot(LibraryParams)
+ * @see LibraryParams#extras
+ */
+ @UnstableApi
+ public static final String EXTRA_KEY_ROOT_CHILDREN_BROWSABLE_ONLY =
+ "androidx.media3.session.LibraryParams.Extras.KEY_ROOT_CHILDREN_BROWSABLE_ONLY";
+
/**
* {@link Bundle} key used in {@link LibraryParams#extras} passed by the {@link MediaBrowser} as
* root hints to {@link MediaLibrarySession.Callback#onGetLibraryRoot(MediaLibrarySession,
diff --git a/libraries/session/src/main/java/androidx/media3/session/MediaUtils.java b/libraries/session/src/main/java/androidx/media3/session/MediaUtils.java
index 97d240032c..64a25e0eb9 100644
--- a/libraries/session/src/main/java/androidx/media3/session/MediaUtils.java
+++ b/libraries/session/src/main/java/androidx/media3/session/MediaUtils.java
@@ -16,6 +16,7 @@
package androidx.media3.session;
import static android.support.v4.media.session.MediaSessionCompat.FLAG_HANDLES_QUEUE_COMMANDS;
+import static androidx.media.utils.MediaConstants.BROWSER_ROOT_HINTS_KEY_ROOT_CHILDREN_SUPPORTED_FLAGS;
import static androidx.media3.common.Player.COMMAND_ADJUST_DEVICE_VOLUME;
import static androidx.media3.common.Player.COMMAND_CHANGE_MEDIA_ITEMS;
import static androidx.media3.common.Player.COMMAND_GET_CURRENT_MEDIA_ITEM;
@@ -38,6 +39,7 @@ import static androidx.media3.common.Player.COMMAND_STOP;
import static androidx.media3.common.util.Assertions.checkNotNull;
import static androidx.media3.common.util.Util.castNonNull;
import static androidx.media3.common.util.Util.constrainValue;
+import static androidx.media3.session.MediaConstants.EXTRA_KEY_ROOT_CHILDREN_BROWSABLE_ONLY;
import static java.lang.Math.max;
import static java.util.concurrent.TimeUnit.MILLISECONDS;
@@ -996,6 +998,15 @@ import org.checkerframework.checker.nullness.compatqual.NullableType;
}
try {
legacyBundle.setClassLoader(context.getClassLoader());
+ int supportedChildrenFlags =
+ legacyBundle.getInt(
+ BROWSER_ROOT_HINTS_KEY_ROOT_CHILDREN_SUPPORTED_FLAGS, /* defaultValue= */ -1);
+ if (supportedChildrenFlags >= 0) {
+ legacyBundle.remove(BROWSER_ROOT_HINTS_KEY_ROOT_CHILDREN_SUPPORTED_FLAGS);
+ legacyBundle.putBoolean(
+ EXTRA_KEY_ROOT_CHILDREN_BROWSABLE_ONLY,
+ supportedChildrenFlags == MediaBrowserCompat.MediaItem.FLAG_BROWSABLE);
+ }
return new LibraryParams.Builder()
.setExtras(legacyBundle)
.setRecent(legacyBundle.getBoolean(BrowserRoot.EXTRA_RECENT))
@@ -1015,6 +1026,18 @@ import org.checkerframework.checker.nullness.compatqual.NullableType;
return null;
}
Bundle rootHints = new Bundle(params.extras);
+ if (params.extras.containsKey(EXTRA_KEY_ROOT_CHILDREN_BROWSABLE_ONLY)) {
+ boolean browsableChildrenSupported =
+ params.extras.getBoolean(
+ EXTRA_KEY_ROOT_CHILDREN_BROWSABLE_ONLY, /* defaultValue= */ false);
+ rootHints.remove(EXTRA_KEY_ROOT_CHILDREN_BROWSABLE_ONLY);
+ rootHints.putInt(
+ BROWSER_ROOT_HINTS_KEY_ROOT_CHILDREN_SUPPORTED_FLAGS,
+ browsableChildrenSupported
+ ? MediaBrowserCompat.MediaItem.FLAG_BROWSABLE
+ : MediaBrowserCompat.MediaItem.FLAG_BROWSABLE
+ | MediaBrowserCompat.MediaItem.FLAG_PLAYABLE);
+ }
rootHints.putBoolean(BrowserRoot.EXTRA_RECENT, params.isRecent);
rootHints.putBoolean(BrowserRoot.EXTRA_OFFLINE, params.isOffline);
rootHints.putBoolean(BrowserRoot.EXTRA_SUGGESTED, params.isSuggested);
diff --git a/libraries/test_session_common/src/main/java/androidx/media3/test/session/common/MediaBrowserConstants.java b/libraries/test_session_common/src/main/java/androidx/media3/test/session/common/MediaBrowserConstants.java
index 65e7847199..6cf1ce19e1 100644
--- a/libraries/test_session_common/src/main/java/androidx/media3/test/session/common/MediaBrowserConstants.java
+++ b/libraries/test_session_common/src/main/java/androidx/media3/test/session/common/MediaBrowserConstants.java
@@ -25,6 +25,8 @@ import java.util.List;
public class MediaBrowserConstants {
public static final String ROOT_ID = "rootId";
+ public static final String ROOT_ID_SUPPORTS_BROWSABLE_CHILDREN_ONLY =
+ "root_id_supports_browsable_children_only";
public static final Bundle ROOT_EXTRAS = new Bundle();
public static final String ROOT_EXTRAS_KEY = "root_extras_key";
public static final int ROOT_EXTRAS_VALUE = 4321;
diff --git a/libraries/test_session_current/src/androidTest/java/androidx/media3/session/MediaBrowserCompatWithMediaLibraryServiceTest.java b/libraries/test_session_current/src/androidTest/java/androidx/media3/session/MediaBrowserCompatWithMediaLibraryServiceTest.java
index 4e36a19ece..4e3ae96507 100644
--- a/libraries/test_session_current/src/androidTest/java/androidx/media3/session/MediaBrowserCompatWithMediaLibraryServiceTest.java
+++ b/libraries/test_session_current/src/androidTest/java/androidx/media3/session/MediaBrowserCompatWithMediaLibraryServiceTest.java
@@ -45,6 +45,7 @@ import static androidx.media3.test.session.common.MediaBrowserConstants.ROOT_EXT
import static androidx.media3.test.session.common.MediaBrowserConstants.ROOT_EXTRAS_KEY;
import static androidx.media3.test.session.common.MediaBrowserConstants.ROOT_EXTRAS_VALUE;
import static androidx.media3.test.session.common.MediaBrowserConstants.ROOT_ID;
+import static androidx.media3.test.session.common.MediaBrowserConstants.ROOT_ID_SUPPORTS_BROWSABLE_CHILDREN_ONLY;
import static androidx.media3.test.session.common.MediaBrowserConstants.SEARCH_QUERY;
import static androidx.media3.test.session.common.MediaBrowserConstants.SEARCH_QUERY_EMPTY_RESULT;
import static androidx.media3.test.session.common.MediaBrowserConstants.SEARCH_QUERY_ERROR;
@@ -615,4 +616,31 @@ public class MediaBrowserCompatWithMediaLibraryServiceTest
assertThat(isSearchSupported).isFalse();
}
+
+ @Test
+ public void rootBrowserHints_legacyBrowsableFlagSet_receivesRootWithBrowsableChildrenOnly()
+ throws Exception {
+ Bundle rootHints = new Bundle();
+ rootHints.putInt(
+ androidx.media.utils.MediaConstants.BROWSER_ROOT_HINTS_KEY_ROOT_CHILDREN_SUPPORTED_FLAGS,
+ MediaItem.FLAG_BROWSABLE);
+ connectAndWait(rootHints);
+
+ String root = browserCompat.getRoot();
+
+ assertThat(root).isEqualTo(ROOT_ID_SUPPORTS_BROWSABLE_CHILDREN_ONLY);
+ }
+
+ @Test
+ public void rootBrowserHints_legacyPlayableFlagSet_receivesDefaultRoot() throws Exception {
+ Bundle connectionHints = new Bundle();
+ connectionHints.putInt(
+ androidx.media.utils.MediaConstants.BROWSER_ROOT_HINTS_KEY_ROOT_CHILDREN_SUPPORTED_FLAGS,
+ MediaItem.FLAG_BROWSABLE | MediaItem.FLAG_PLAYABLE);
+ connectAndWait(connectionHints);
+
+ String root = browserCompat.getRoot();
+
+ assertThat(root).isEqualTo(ROOT_ID);
+ }
}
diff --git a/libraries/test_session_current/src/androidTest/java/androidx/media3/session/MediaBrowserCompatWithMediaSessionServiceTest.java b/libraries/test_session_current/src/androidTest/java/androidx/media3/session/MediaBrowserCompatWithMediaSessionServiceTest.java
index 12caed0f8e..4199b8f610 100644
--- a/libraries/test_session_current/src/androidTest/java/androidx/media3/session/MediaBrowserCompatWithMediaSessionServiceTest.java
+++ b/libraries/test_session_current/src/androidTest/java/androidx/media3/session/MediaBrowserCompatWithMediaSessionServiceTest.java
@@ -82,13 +82,12 @@ public class MediaBrowserCompatWithMediaSessionServiceTest {
return MOCK_MEDIA3_SESSION_SERVICE;
}
- void connectAndWait(Bundle connectionHints) throws Exception {
+ void connectAndWait(Bundle rootHints) throws Exception {
handler.postAndSync(
() -> {
// Make browser's internal handler to be initialized with test thread.
browserCompat =
- new MediaBrowserCompat(
- context, getServiceComponent(), connectionCallback, connectionHints);
+ new MediaBrowserCompat(context, getServiceComponent(), connectionCallback, rootHints);
});
browserCompat.connect();
assertThat(connectionCallback.connectedLatch.await(SERVICE_CONNECTION_TIMEOUT_MS, MILLISECONDS))
diff --git a/libraries/test_session_current/src/androidTest/java/androidx/media3/session/MediaBrowserListenerWithMediaBrowserServiceCompatTest.java b/libraries/test_session_current/src/androidTest/java/androidx/media3/session/MediaBrowserListenerWithMediaBrowserServiceCompatTest.java
index d9f8a0afd0..30bd1c83a2 100644
--- a/libraries/test_session_current/src/androidTest/java/androidx/media3/session/MediaBrowserListenerWithMediaBrowserServiceCompatTest.java
+++ b/libraries/test_session_current/src/androidTest/java/androidx/media3/session/MediaBrowserListenerWithMediaBrowserServiceCompatTest.java
@@ -18,10 +18,13 @@ package androidx.media3.session;
import static androidx.media3.session.LibraryResult.RESULT_SUCCESS;
import static androidx.media3.session.MediaConstants.EXTRAS_KEY_COMPLETION_STATUS;
import static androidx.media3.session.MediaConstants.EXTRAS_VALUE_COMPLETION_STATUS_PARTIALLY_PLAYED;
+import static androidx.media3.session.MediaConstants.EXTRA_KEY_ROOT_CHILDREN_BROWSABLE_ONLY;
import static androidx.media3.test.session.common.CommonConstants.MOCK_MEDIA_BROWSER_SERVICE_COMPAT;
import static androidx.media3.test.session.common.MediaBrowserConstants.PARENT_ID;
import static androidx.media3.test.session.common.MediaBrowserConstants.ROOT_EXTRAS_KEY;
import static androidx.media3.test.session.common.MediaBrowserConstants.ROOT_EXTRAS_VALUE;
+import static androidx.media3.test.session.common.MediaBrowserConstants.ROOT_ID;
+import static androidx.media3.test.session.common.MediaBrowserConstants.ROOT_ID_SUPPORTS_BROWSABLE_CHILDREN_ONLY;
import static androidx.media3.test.session.common.MediaBrowserServiceCompatConstants.TEST_CONNECT_REJECTED;
import static androidx.media3.test.session.common.MediaBrowserServiceCompatConstants.TEST_GET_CHILDREN;
import static androidx.media3.test.session.common.MediaBrowserServiceCompatConstants.TEST_GET_LIBRARY_ROOT;
@@ -163,6 +166,48 @@ public class MediaBrowserListenerWithMediaBrowserServiceCompatTest {
assertThat(extras.getInt(ROOT_EXTRAS_KEY, ROOT_EXTRAS_VALUE + 1)).isEqualTo(ROOT_EXTRAS_VALUE);
}
+ @Test
+ public void getLibraryRoot_browsableRootChildrenOnly_receivesRootWithBrowsableChildrenOnly()
+ throws Exception {
+ remoteService.setProxyForTest(TEST_GET_LIBRARY_ROOT);
+ MediaBrowser browser = createBrowser(/* listener= */ null);
+
+ LibraryResult