Migrate BROWSER_ROOT_HINTS_KEY_ROOT_CHILDREN_SUPPORTED_FLAGS to Media3

PiperOrigin-RevId: 490376734
This commit is contained in:
bachinger 2022-11-23 01:42:43 +00:00 committed by Ian Baker
parent 8ce1213ddd
commit 1803d1cdb8
9 changed files with 230 additions and 6 deletions

View File

@ -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.
*
* <p>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,

View File

@ -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);

View File

@ -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;

View File

@ -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);
}
}

View File

@ -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))

View File

@ -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<MediaItem> resultForLibraryRoot =
threadTestRule
.getHandler()
.postAndSync(
() -> {
Bundle extras = new Bundle();
extras.putBoolean(EXTRA_KEY_ROOT_CHILDREN_BROWSABLE_ONLY, true);
return browser.getLibraryRoot(
new LibraryParams.Builder().setExtras(extras).build());
})
.get(TIMEOUT_MS, MILLISECONDS);
assertThat(resultForLibraryRoot.value.mediaId)
.isEqualTo(ROOT_ID_SUPPORTS_BROWSABLE_CHILDREN_ONLY);
}
@Test
public void getLibraryRoot_browsableRootChildrenOnlyFalse_receivesDefaultRoot() throws Exception {
remoteService.setProxyForTest(TEST_GET_LIBRARY_ROOT);
MediaBrowser browser = createBrowser(/* listener= */ null);
LibraryResult<MediaItem> resultForLibraryRoot =
threadTestRule
.getHandler()
.postAndSync(
() -> {
Bundle extras = new Bundle();
extras.putBoolean(EXTRA_KEY_ROOT_CHILDREN_BROWSABLE_ONLY, false);
return browser.getLibraryRoot(
new LibraryParams.Builder().setExtras(extras).build());
})
.get(TIMEOUT_MS, MILLISECONDS);
assertThat(resultForLibraryRoot.value.mediaId).isEqualTo(ROOT_ID);
}
@Test
public void getChildren_correctMetadataExtras() throws Exception {
LibraryParams params = MediaTestUtils.createLibraryParams();

View File

@ -15,8 +15,12 @@
*/
package androidx.media3.session;
import static android.support.v4.media.MediaBrowserCompat.MediaItem.FLAG_BROWSABLE;
import static android.support.v4.media.MediaBrowserCompat.MediaItem.FLAG_PLAYABLE;
import static android.support.v4.media.MediaMetadataCompat.METADATA_KEY_DURATION;
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.session.MediaConstants.EXTRA_KEY_ROOT_CHILDREN_BROWSABLE_ONLY;
import static com.google.common.truth.Truth.assertThat;
import static java.util.concurrent.TimeUnit.SECONDS;
@ -34,6 +38,7 @@ import android.support.v4.media.session.MediaSessionCompat;
import android.support.v4.media.session.PlaybackStateCompat;
import androidx.annotation.Nullable;
import androidx.media.AudioAttributesCompat;
import androidx.media.utils.MediaConstants;
import androidx.media3.common.AudioAttributes;
import androidx.media3.common.C;
import androidx.media3.common.HeartRating;
@ -271,16 +276,54 @@ public final class MediaUtilsTest {
assertThat(MediaUtils.convertToLibraryParams(context, null)).isNull();
Bundle rootHints = new Bundle();
rootHints.putString("key", "value");
rootHints.putInt(
MediaConstants.BROWSER_ROOT_HINTS_KEY_ROOT_CHILDREN_SUPPORTED_FLAGS, FLAG_BROWSABLE);
rootHints.putBoolean(MediaBrowserService.BrowserRoot.EXTRA_OFFLINE, true);
rootHints.putBoolean(MediaBrowserService.BrowserRoot.EXTRA_RECENT, true);
rootHints.putBoolean(MediaBrowserService.BrowserRoot.EXTRA_SUGGESTED, true);
MediaLibraryService.LibraryParams params =
MediaUtils.convertToLibraryParams(context, rootHints);
assertThat(params.extras.getString("key")).isEqualTo("value");
assertThat(params.extras.getBoolean(EXTRA_KEY_ROOT_CHILDREN_BROWSABLE_ONLY)).isTrue();
assertThat(params.extras.containsKey(BROWSER_ROOT_HINTS_KEY_ROOT_CHILDREN_SUPPORTED_FLAGS))
.isFalse();
assertThat(params.isOffline).isTrue();
assertThat(params.isRecent).isTrue();
assertThat(params.isSuggested).isTrue();
assertThat(params.extras.getString("key")).isEqualTo("value");
}
@Test
public void convertToLibraryParams_rootHintsBrowsableNoFlagSet_browsableOnlyFalse() {
Bundle rootHints = new Bundle();
rootHints.putInt(MediaConstants.BROWSER_ROOT_HINTS_KEY_ROOT_CHILDREN_SUPPORTED_FLAGS, 0);
MediaLibraryService.LibraryParams params =
MediaUtils.convertToLibraryParams(context, rootHints);
assertThat(params.extras.getBoolean(EXTRA_KEY_ROOT_CHILDREN_BROWSABLE_ONLY)).isFalse();
}
@Test
public void convertToLibraryParams_rootHintsPlayableFlagSet_browsableOnlyFalse() {
Bundle rootHints = new Bundle();
rootHints.putInt(
MediaConstants.BROWSER_ROOT_HINTS_KEY_ROOT_CHILDREN_SUPPORTED_FLAGS,
FLAG_PLAYABLE | FLAG_BROWSABLE);
MediaLibraryService.LibraryParams params =
MediaUtils.convertToLibraryParams(context, rootHints);
assertThat(params.extras.getBoolean(EXTRA_KEY_ROOT_CHILDREN_BROWSABLE_ONLY)).isFalse();
}
@Test
public void convertToLibraryParams_rootHintsBrowsableAbsentKey_browsableOnlyFalse() {
MediaLibraryService.LibraryParams params =
MediaUtils.convertToLibraryParams(context, /* legacyBundle= */ Bundle.EMPTY);
assertThat(params.extras.getBoolean(EXTRA_KEY_ROOT_CHILDREN_BROWSABLE_ONLY)).isFalse();
}
@Test
@ -288,6 +331,7 @@ public final class MediaUtilsTest {
assertThat(MediaUtils.convertToRootHints(null)).isNull();
Bundle extras = new Bundle();
extras.putString("key", "value");
extras.putBoolean(EXTRA_KEY_ROOT_CHILDREN_BROWSABLE_ONLY, true);
MediaLibraryService.LibraryParams param =
new MediaLibraryService.LibraryParams.Builder()
.setOffline(true)
@ -295,11 +339,44 @@ public final class MediaUtilsTest {
.setSuggested(true)
.setExtras(extras)
.build();
Bundle rootHints = MediaUtils.convertToRootHints(param);
assertThat(
rootHints.getInt(
BROWSER_ROOT_HINTS_KEY_ROOT_CHILDREN_SUPPORTED_FLAGS, /* defaultValue= */ 0))
.isEqualTo(FLAG_BROWSABLE);
assertThat(rootHints.getString("key")).isEqualTo("value");
assertThat(rootHints.get(EXTRA_KEY_ROOT_CHILDREN_BROWSABLE_ONLY)).isNull();
assertThat(rootHints.getBoolean(MediaBrowserService.BrowserRoot.EXTRA_OFFLINE)).isTrue();
assertThat(rootHints.getBoolean(MediaBrowserService.BrowserRoot.EXTRA_RECENT)).isTrue();
assertThat(rootHints.getBoolean(MediaBrowserService.BrowserRoot.EXTRA_SUGGESTED)).isTrue();
assertThat(rootHints.getString("key")).isEqualTo("value");
}
@Test
public void convertToRootHints_browsableOnlyFalse_correctLegacyBrowsableFlags() {
Bundle extras = new Bundle();
extras.putBoolean(EXTRA_KEY_ROOT_CHILDREN_BROWSABLE_ONLY, false);
MediaLibraryService.LibraryParams param =
new MediaLibraryService.LibraryParams.Builder().setExtras(extras).build();
Bundle rootHints = MediaUtils.convertToRootHints(param);
assertThat(
rootHints.getInt(
BROWSER_ROOT_HINTS_KEY_ROOT_CHILDREN_SUPPORTED_FLAGS, /* defaultValue= */ -1))
.isEqualTo(FLAG_BROWSABLE | FLAG_PLAYABLE);
assertThat(rootHints.get(EXTRA_KEY_ROOT_CHILDREN_BROWSABLE_ONLY)).isNull();
}
@Test
public void convertToRootHints_browsableAbsentKey_noLegacyKeyAdded() {
MediaLibraryService.LibraryParams param =
new MediaLibraryService.LibraryParams.Builder().build();
Bundle rootHints = MediaUtils.convertToRootHints(param);
assertThat(rootHints.get(BROWSER_ROOT_HINTS_KEY_ROOT_CHILDREN_SUPPORTED_FLAGS)).isNull();
}
@Test

View File

@ -22,6 +22,7 @@ import static androidx.media3.session.MediaConstants.EXTRAS_VALUE_COMPLETION_STA
import static androidx.media3.test.session.common.CommonConstants.SUPPORT_APP_PACKAGE_NAME;
import static androidx.media3.test.session.common.MediaBrowserConstants.ROOT_EXTRAS;
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;
@ -37,6 +38,7 @@ import android.support.v4.media.MediaDescriptionCompat;
import android.support.v4.media.session.MediaSessionCompat;
import android.support.v4.media.session.MediaSessionCompat.Callback;
import androidx.annotation.GuardedBy;
import androidx.annotation.Nullable;
import androidx.media.MediaBrowserServiceCompat;
import androidx.media3.common.util.UnstableApi;
import androidx.media3.test.session.common.IRemoteMediaBrowserServiceCompat;
@ -303,7 +305,18 @@ public class MockMediaBrowserServiceCompat extends MediaBrowserServiceCompat {
new MockMediaBrowserServiceCompat.Proxy() {
@Override
public BrowserRoot onGetRoot(
String clientPackageName, int clientUid, Bundle rootHints) {
String clientPackageName, int clientUid, @Nullable Bundle rootHints) {
if (rootHints != null) {
// On API levels lower than 21 root hints are null.
int supportedRootChildrenFlags =
rootHints.getInt(
androidx.media.utils.MediaConstants
.BROWSER_ROOT_HINTS_KEY_ROOT_CHILDREN_SUPPORTED_FLAGS,
/* defaultValue= */ 0);
if ((supportedRootChildrenFlags == MediaItem.FLAG_BROWSABLE)) {
return new BrowserRoot(ROOT_ID_SUPPORTS_BROWSABLE_CHILDREN_ONLY, ROOT_EXTRAS);
}
}
return new BrowserRoot(ROOT_ID, ROOT_EXTRAS);
}
});

View File

@ -20,6 +20,7 @@ import static androidx.media3.session.MediaConstants.EXTRAS_KEY_COMPLETION_STATU
import static androidx.media3.session.MediaConstants.EXTRAS_KEY_ERROR_RESOLUTION_ACTION_INTENT_COMPAT;
import static androidx.media3.session.MediaConstants.EXTRAS_KEY_ERROR_RESOLUTION_ACTION_LABEL_COMPAT;
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.session.MediaTestUtils.assertLibraryParamsEquals;
import static androidx.media3.test.session.common.CommonConstants.SUPPORT_APP_PACKAGE_NAME;
import static androidx.media3.test.session.common.MediaBrowserConstants.CUSTOM_ACTION;
@ -39,6 +40,7 @@ import static androidx.media3.test.session.common.MediaBrowserConstants.PARENT_I
import static androidx.media3.test.session.common.MediaBrowserConstants.PARENT_ID_LONG_LIST;
import static androidx.media3.test.session.common.MediaBrowserConstants.ROOT_EXTRAS;
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_LONG_LIST;
@ -232,6 +234,22 @@ public class MockMediaLibraryService extends MediaLibraryService {
.build())
.build();
}
if (params != null) {
boolean browsableRootChildrenOnly =
params.extras.getBoolean(
EXTRA_KEY_ROOT_CHILDREN_BROWSABLE_ONLY, /* defaultValue= */ false);
if (browsableRootChildrenOnly) {
rootItem =
new MediaItem.Builder()
.setMediaId(ROOT_ID_SUPPORTS_BROWSABLE_CHILDREN_ONLY)
.setMediaMetadata(
new MediaMetadata.Builder()
.setFolderType(MediaMetadata.FOLDER_TYPE_MIXED)
.setIsPlayable(false)
.build())
.build();
}
}
return Futures.immediateFuture(LibraryResult.ofItem(rootItem, ROOT_PARAMS));
}