Use MediaBrowserCompat.rootHints as connections hints

In Media3 there is the useful concept of connection hints that a
client can set when building the session and that are sent to the
service and passed to the `Callback.onConnect()` method when the
browser connects.

These connection hints are then included in the `ControllerInfo`
object that later will be passed to every callback method and the
implementor can then take decisions specific to these connection
hints.

These connection hints are not available in media1. However, when
an app creates a `MediaBrowserCompat` object, the constructor takes
a rootHint object that is sent to
`MediaBrowserServiceCompat.onGetRoot()`.

This change uses the browser rootHints as the connection hints when
creating the `ControllerInfo` for legacy browsers and makes them
available to the `MediaLibrarySession.Callback` domain methods in
the same way as connection hints of a Media3 browser.

PiperOrigin-RevId: 484220748
This commit is contained in:
bachinger 2022-10-27 11:50:03 +00:00 committed by microkatz
parent 64e9e88823
commit 21022c77be
5 changed files with 86 additions and 56 deletions

View File

@ -317,14 +317,14 @@ import java.util.concurrent.atomic.AtomicReference;
} }
@Override @Override
public ControllerInfo createControllerInfo(RemoteUserInfo remoteUserInfo) { public ControllerInfo createControllerInfo(RemoteUserInfo remoteUserInfo, Bundle rootHints) {
return new ControllerInfo( return new ControllerInfo(
remoteUserInfo, remoteUserInfo,
ControllerInfo.LEGACY_CONTROLLER_VERSION, ControllerInfo.LEGACY_CONTROLLER_VERSION,
ControllerInfo.LEGACY_CONTROLLER_INTERFACE_VERSION, ControllerInfo.LEGACY_CONTROLLER_INTERFACE_VERSION,
getMediaSessionManager().isTrustedForMediaControl(remoteUserInfo), getMediaSessionManager().isTrustedForMediaControl(remoteUserInfo),
new BrowserLegacyCb(remoteUserInfo), new BrowserLegacyCb(remoteUserInfo),
/* connectionHints= */ Bundle.EMPTY); /* connectionHints= */ rootHints);
} }
public ControllerCb getBrowserLegacyCbForBroadcast() { public ControllerCb getBrowserLegacyCbForBroadcast() {

View File

@ -61,7 +61,8 @@ import java.util.concurrent.atomic.AtomicReference;
public BrowserRoot onGetRoot( public BrowserRoot onGetRoot(
String clientPackageName, int clientUid, @Nullable Bundle rootHints) { String clientPackageName, int clientUid, @Nullable Bundle rootHints) {
RemoteUserInfo info = getCurrentBrowserInfo(); RemoteUserInfo info = getCurrentBrowserInfo();
MediaSession.ControllerInfo controller = createControllerInfo(info); MediaSession.ControllerInfo controller =
createControllerInfo(info, rootHints != null ? rootHints : Bundle.EMPTY);
AtomicReference<MediaSession.ConnectionResult> resultReference = new AtomicReference<>(); AtomicReference<MediaSession.ConnectionResult> resultReference = new AtomicReference<>();
ConditionVariable haveResult = new ConditionVariable(); ConditionVariable haveResult = new ConditionVariable();
@ -92,14 +93,14 @@ import java.util.concurrent.atomic.AtomicReference;
result.sendResult(/* result= */ null); result.sendResult(/* result= */ null);
} }
public ControllerInfo createControllerInfo(RemoteUserInfo info) { public ControllerInfo createControllerInfo(RemoteUserInfo info, Bundle rootHints) {
return new ControllerInfo( return new ControllerInfo(
info, info,
ControllerInfo.LEGACY_CONTROLLER_VERSION, ControllerInfo.LEGACY_CONTROLLER_VERSION,
ControllerInfo.LEGACY_CONTROLLER_INTERFACE_VERSION, ControllerInfo.LEGACY_CONTROLLER_INTERFACE_VERSION,
manager.isTrustedForMediaControl(info), manager.isTrustedForMediaControl(info),
/* cb= */ null, /* cb= */ null,
/* connectionHints= */ Bundle.EMPTY); /* connectionHints= */ rootHints);
} }
public final MediaSessionManager getMediaSessionManager() { public final MediaSessionManager getMediaSessionManager() {

View File

@ -17,6 +17,7 @@ package androidx.media3.session;
import static androidx.media3.session.MediaConstants.EXTRAS_KEY_COMPLETION_STATUS; 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.EXTRAS_VALUE_COMPLETION_STATUS_PARTIALLY_PLAYED;
import static androidx.media3.session.MockMediaLibraryService.CONNECTION_HINTS_CUSTOM_LIBRARY_ROOT;
import static androidx.media3.test.session.common.CommonConstants.METADATA_ARTWORK_URI; import static androidx.media3.test.session.common.CommonConstants.METADATA_ARTWORK_URI;
import static androidx.media3.test.session.common.CommonConstants.METADATA_DESCRIPTION; import static androidx.media3.test.session.common.CommonConstants.METADATA_DESCRIPTION;
import static androidx.media3.test.session.common.CommonConstants.METADATA_EXTRA_KEY; import static androidx.media3.test.session.common.CommonConstants.METADATA_EXTRA_KEY;
@ -93,13 +94,8 @@ public class MediaBrowserCompatWithMediaLibraryServiceTest
public void getRoot() throws Exception { public void getRoot() throws Exception {
// The MockMediaLibraryService gives MediaBrowserConstants.ROOT_ID as root ID, and // The MockMediaLibraryService gives MediaBrowserConstants.ROOT_ID as root ID, and
// MediaBrowserConstants.ROOT_EXTRAS as extras. // MediaBrowserConstants.ROOT_EXTRAS as extras.
handler.postAndSync( connectAndWait(/* connectionHints= */ Bundle.EMPTY);
() -> {
browserCompat =
new MediaBrowserCompat(
context, getServiceComponent(), connectionCallback, /* rootHint= */ null);
});
connectAndWait();
assertThat(browserCompat.getRoot()).isEqualTo(ROOT_ID); assertThat(browserCompat.getRoot()).isEqualTo(ROOT_ID);
assertThat( assertThat(
browserCompat browserCompat
@ -113,10 +109,10 @@ public class MediaBrowserCompatWithMediaLibraryServiceTest
} }
@Test @Test
public void getItem_browsable() throws InterruptedException { public void getItem_browsable() throws Exception {
String mediaId = MEDIA_ID_GET_BROWSABLE_ITEM; String mediaId = MEDIA_ID_GET_BROWSABLE_ITEM;
connectAndWait(); connectAndWait(/* connectionHints= */ Bundle.EMPTY);
CountDownLatch latch = new CountDownLatch(1); CountDownLatch latch = new CountDownLatch(1);
AtomicReference<MediaItem> itemRef = new AtomicReference<>(); AtomicReference<MediaItem> itemRef = new AtomicReference<>();
browserCompat.getItem( browserCompat.getItem(
@ -135,10 +131,10 @@ public class MediaBrowserCompatWithMediaLibraryServiceTest
} }
@Test @Test
public void getItem_playable() throws InterruptedException { public void getItem_playable() throws Exception {
String mediaId = MEDIA_ID_GET_PLAYABLE_ITEM; String mediaId = MEDIA_ID_GET_PLAYABLE_ITEM;
connectAndWait(); connectAndWait(/* connectionHints= */ Bundle.EMPTY);
CountDownLatch latch = new CountDownLatch(1); CountDownLatch latch = new CountDownLatch(1);
AtomicReference<MediaItem> itemRef = new AtomicReference<>(); AtomicReference<MediaItem> itemRef = new AtomicReference<>();
browserCompat.getItem( browserCompat.getItem(
@ -157,10 +153,10 @@ public class MediaBrowserCompatWithMediaLibraryServiceTest
} }
@Test @Test
public void getItem_metadata() throws InterruptedException { public void getItem_metadata() throws Exception {
String mediaId = MEDIA_ID_GET_ITEM_WITH_METADATA; String mediaId = MEDIA_ID_GET_ITEM_WITH_METADATA;
connectAndWait(); connectAndWait(/* connectionHints= */ Bundle.EMPTY);
CountDownLatch latch = new CountDownLatch(1); CountDownLatch latch = new CountDownLatch(1);
AtomicReference<MediaItem> itemRef = new AtomicReference<>(); AtomicReference<MediaItem> itemRef = new AtomicReference<>();
browserCompat.getItem( browserCompat.getItem(
@ -187,10 +183,10 @@ public class MediaBrowserCompatWithMediaLibraryServiceTest
} }
@Test @Test
public void getItem_nullResult() throws InterruptedException { public void getItem_nullResult() throws Exception {
String mediaId = "random_media_id"; String mediaId = "random_media_id";
connectAndWait(); connectAndWait(/* connectionHints= */ Bundle.EMPTY);
CountDownLatch latch = new CountDownLatch(1); CountDownLatch latch = new CountDownLatch(1);
browserCompat.getItem( browserCompat.getItem(
mediaId, mediaId,
@ -210,9 +206,9 @@ public class MediaBrowserCompatWithMediaLibraryServiceTest
} }
@Test @Test
public void getChildren() throws InterruptedException { public void getChildren() throws Exception {
String testParentId = PARENT_ID; String testParentId = PARENT_ID;
connectAndWait(); connectAndWait(/* connectionHints= */ Bundle.EMPTY);
CountDownLatch latch = new CountDownLatch(1); CountDownLatch latch = new CountDownLatch(1);
List<MediaItem> receivedChildren = new ArrayList<>(); List<MediaItem> receivedChildren = new ArrayList<>();
final String[] receivedParentId = new String[1]; final String[] receivedParentId = new String[1];
@ -252,10 +248,10 @@ public class MediaBrowserCompatWithMediaLibraryServiceTest
} }
@Test @Test
public void getChildren_withLongList() throws InterruptedException { public void getChildren_withLongList() throws Exception {
String testParentId = PARENT_ID_LONG_LIST; String testParentId = PARENT_ID_LONG_LIST;
connectAndWait(); connectAndWait(/* connectionHints= */ Bundle.EMPTY);
CountDownLatch latch = new CountDownLatch(1); CountDownLatch latch = new CountDownLatch(1);
browserCompat.subscribe( browserCompat.subscribe(
testParentId, testParentId,
@ -283,14 +279,14 @@ public class MediaBrowserCompatWithMediaLibraryServiceTest
} }
@Test @Test
public void getChildren_withPagination() throws InterruptedException { public void getChildren_withPagination() throws Exception {
String testParentId = PARENT_ID; String testParentId = PARENT_ID;
int page = 4; int page = 4;
int pageSize = 10; int pageSize = 10;
Bundle extras = new Bundle(); Bundle extras = new Bundle();
extras.putString(testParentId, testParentId); extras.putString(testParentId, testParentId);
connectAndWait(); connectAndWait(/* connectionHints= */ Bundle.EMPTY);
CountDownLatch latch = new CountDownLatch(1); CountDownLatch latch = new CountDownLatch(1);
Bundle option = new Bundle(); Bundle option = new Bundle();
option.putInt(MediaBrowserCompat.EXTRA_PAGE, page); option.putInt(MediaBrowserCompat.EXTRA_PAGE, page);
@ -327,9 +323,9 @@ public class MediaBrowserCompatWithMediaLibraryServiceTest
} }
@Test @Test
public void getChildren_authErrorResult() throws InterruptedException { public void getChildren_authErrorResult() throws Exception {
String testParentId = PARENT_ID_AUTH_EXPIRED_ERROR; String testParentId = PARENT_ID_AUTH_EXPIRED_ERROR;
connectAndWait(); connectAndWait(/* connectionHints= */ Bundle.EMPTY);
CountDownLatch errorLatch = new CountDownLatch(1); CountDownLatch errorLatch = new CountDownLatch(1);
browserCompat.subscribe( browserCompat.subscribe(
testParentId, testParentId,
@ -368,10 +364,10 @@ public class MediaBrowserCompatWithMediaLibraryServiceTest
} }
@Test @Test
public void getChildren_emptyResult() throws InterruptedException { public void getChildren_emptyResult() throws Exception {
String testParentId = PARENT_ID_NO_CHILDREN; String testParentId = PARENT_ID_NO_CHILDREN;
connectAndWait(); connectAndWait(/* connectionHints= */ Bundle.EMPTY);
CountDownLatch latch = new CountDownLatch(1); CountDownLatch latch = new CountDownLatch(1);
browserCompat.subscribe( browserCompat.subscribe(
testParentId, testParentId,
@ -387,10 +383,10 @@ public class MediaBrowserCompatWithMediaLibraryServiceTest
} }
@Test @Test
public void getChildren_nullResult() throws InterruptedException { public void getChildren_nullResult() throws Exception {
String testParentId = PARENT_ID_ERROR; String testParentId = PARENT_ID_ERROR;
connectAndWait(); connectAndWait(/* connectionHints= */ Bundle.EMPTY);
CountDownLatch latch = new CountDownLatch(1); CountDownLatch latch = new CountDownLatch(1);
browserCompat.subscribe( browserCompat.subscribe(
testParentId, testParentId,
@ -410,7 +406,7 @@ public class MediaBrowserCompatWithMediaLibraryServiceTest
} }
@Test @Test
public void search() throws InterruptedException { public void search() throws Exception {
String testQuery = SEARCH_QUERY; String testQuery = SEARCH_QUERY;
int page = 4; int page = 4;
int pageSize = 10; int pageSize = 10;
@ -419,7 +415,7 @@ public class MediaBrowserCompatWithMediaLibraryServiceTest
testExtras.putInt(MediaBrowserCompat.EXTRA_PAGE, page); testExtras.putInt(MediaBrowserCompat.EXTRA_PAGE, page);
testExtras.putInt(MediaBrowserCompat.EXTRA_PAGE_SIZE, pageSize); testExtras.putInt(MediaBrowserCompat.EXTRA_PAGE_SIZE, pageSize);
connectAndWait(); connectAndWait(/* connectionHints= */ Bundle.EMPTY);
CountDownLatch latch = new CountDownLatch(1); CountDownLatch latch = new CountDownLatch(1);
browserCompat.search( browserCompat.search(
testQuery, testQuery,
@ -449,7 +445,7 @@ public class MediaBrowserCompatWithMediaLibraryServiceTest
} }
@Test @Test
public void search_withLongList() throws InterruptedException { public void search_withLongList() throws Exception {
String testQuery = SEARCH_QUERY_LONG_LIST; String testQuery = SEARCH_QUERY_LONG_LIST;
int page = 0; int page = 0;
int pageSize = Integer.MAX_VALUE; int pageSize = Integer.MAX_VALUE;
@ -458,7 +454,7 @@ public class MediaBrowserCompatWithMediaLibraryServiceTest
testExtras.putInt(MediaBrowserCompat.EXTRA_PAGE, page); testExtras.putInt(MediaBrowserCompat.EXTRA_PAGE, page);
testExtras.putInt(MediaBrowserCompat.EXTRA_PAGE_SIZE, pageSize); testExtras.putInt(MediaBrowserCompat.EXTRA_PAGE_SIZE, pageSize);
connectAndWait(); connectAndWait(/* connectionHints= */ Bundle.EMPTY);
CountDownLatch latch = new CountDownLatch(1); CountDownLatch latch = new CountDownLatch(1);
browserCompat.search( browserCompat.search(
testQuery, testQuery,
@ -482,12 +478,12 @@ public class MediaBrowserCompatWithMediaLibraryServiceTest
} }
@Test @Test
public void search_emptyResult() throws InterruptedException { public void search_emptyResult() throws Exception {
String testQuery = SEARCH_QUERY_EMPTY_RESULT; String testQuery = SEARCH_QUERY_EMPTY_RESULT;
Bundle testExtras = new Bundle(); Bundle testExtras = new Bundle();
testExtras.putString(testQuery, testQuery); testExtras.putString(testQuery, testQuery);
connectAndWait(); connectAndWait(/* connectionHints= */ Bundle.EMPTY);
CountDownLatch latch = new CountDownLatch(1); CountDownLatch latch = new CountDownLatch(1);
browserCompat.search( browserCompat.search(
testQuery, testQuery,
@ -506,12 +502,12 @@ public class MediaBrowserCompatWithMediaLibraryServiceTest
} }
@Test @Test
public void search_error() throws InterruptedException { public void search_error() throws Exception {
String testQuery = SEARCH_QUERY_ERROR; String testQuery = SEARCH_QUERY_ERROR;
Bundle testExtras = new Bundle(); Bundle testExtras = new Bundle();
testExtras.putString(testQuery, testQuery); testExtras.putString(testQuery, testQuery);
connectAndWait(); connectAndWait(/* connectionHints= */ Bundle.EMPTY);
CountDownLatch latch = new CountDownLatch(1); CountDownLatch latch = new CountDownLatch(1);
browserCompat.search( browserCompat.search(
testQuery, testQuery,
@ -534,11 +530,11 @@ public class MediaBrowserCompatWithMediaLibraryServiceTest
// TODO: Add test for onCustomCommand() in MediaLibrarySessionLegacyCallbackTest. // TODO: Add test for onCustomCommand() in MediaLibrarySessionLegacyCallbackTest.
@Test @Test
public void customAction() throws InterruptedException { public void customAction() throws Exception {
Bundle testArgs = new Bundle(); Bundle testArgs = new Bundle();
testArgs.putString("args_key", "args_value"); testArgs.putString("args_key", "args_value");
connectAndWait(); connectAndWait(/* connectionHints= */ Bundle.EMPTY);
CountDownLatch latch = new CountDownLatch(1); CountDownLatch latch = new CountDownLatch(1);
browserCompat.sendCustomAction( browserCompat.sendCustomAction(
CUSTOM_ACTION, CUSTOM_ACTION,
@ -557,11 +553,11 @@ public class MediaBrowserCompatWithMediaLibraryServiceTest
// TODO: Add test for onCustomCommand() in MediaLibrarySessionLegacyCallbackTest. // TODO: Add test for onCustomCommand() in MediaLibrarySessionLegacyCallbackTest.
@Test @Test
public void customAction_rejected() throws InterruptedException { public void customAction_rejected() throws Exception {
// This action will not be allowed by the library session. // This action will not be allowed by the library session.
String testAction = "random_custom_action"; String testAction = "random_custom_action";
connectAndWait(); connectAndWait(/* connectionHints= */ Bundle.EMPTY);
CountDownLatch latch = new CountDownLatch(1); CountDownLatch latch = new CountDownLatch(1);
browserCompat.sendCustomAction( browserCompat.sendCustomAction(
testAction, testAction,
@ -576,4 +572,15 @@ public class MediaBrowserCompatWithMediaLibraryServiceTest
.that(latch.await(NO_RESPONSE_TIMEOUT_MS, MILLISECONDS)) .that(latch.await(NO_RESPONSE_TIMEOUT_MS, MILLISECONDS))
.isFalse(); .isFalse();
} }
@Test
public void rootBrowserHints_usedAsConnectionHints() throws Exception {
Bundle connectionHints = new Bundle();
connectionHints.putString(CONNECTION_HINTS_CUSTOM_LIBRARY_ROOT, "myLibraryRoot");
connectAndWait(connectionHints);
String root = browserCompat.getRoot();
assertThat(root).isEqualTo("myLibraryRoot");
}
} }

View File

@ -23,6 +23,7 @@ import static java.util.concurrent.TimeUnit.MILLISECONDS;
import android.content.ComponentName; import android.content.ComponentName;
import android.content.Context; import android.content.Context;
import android.os.Bundle;
import android.support.v4.media.MediaBrowserCompat; import android.support.v4.media.MediaBrowserCompat;
import android.support.v4.media.session.MediaControllerCompat; import android.support.v4.media.session.MediaControllerCompat;
import android.support.v4.media.session.PlaybackStateCompat; import android.support.v4.media.session.PlaybackStateCompat;
@ -63,16 +64,10 @@ public class MediaBrowserCompatWithMediaSessionServiceTest {
@Nullable PlaybackStateCompat lastReportedPlaybackStateCompat; @Nullable PlaybackStateCompat lastReportedPlaybackStateCompat;
@Before @Before
public void setUp() throws Exception { public void setUp() {
context = ApplicationProvider.getApplicationContext(); context = ApplicationProvider.getApplicationContext();
handler = threadTestRule.getHandler(); handler = threadTestRule.getHandler();
connectionCallback = new TestConnectionCallback(); connectionCallback = new TestConnectionCallback();
handler.postAndSync(
() -> {
// Make browser's internal handler to be initialized with test thread.
browserCompat =
new MediaBrowserCompat(context, getServiceComponent(), connectionCallback, null);
});
} }
@After @After
@ -87,15 +82,22 @@ public class MediaBrowserCompatWithMediaSessionServiceTest {
return MOCK_MEDIA3_SESSION_SERVICE; return MOCK_MEDIA3_SESSION_SERVICE;
} }
void connectAndWait() throws InterruptedException { void connectAndWait(Bundle connectionHints) throws Exception {
handler.postAndSync(
() -> {
// Make browser's internal handler to be initialized with test thread.
browserCompat =
new MediaBrowserCompat(
context, getServiceComponent(), connectionCallback, connectionHints);
});
browserCompat.connect(); browserCompat.connect();
assertThat(connectionCallback.connectedLatch.await(SERVICE_CONNECTION_TIMEOUT_MS, MILLISECONDS)) assertThat(connectionCallback.connectedLatch.await(SERVICE_CONNECTION_TIMEOUT_MS, MILLISECONDS))
.isTrue(); .isTrue();
} }
@Test @Test
public void connect() throws InterruptedException { public void connect() throws Exception {
connectAndWait(); connectAndWait(/* connectionHints= */ Bundle.EMPTY);
assertThat(connectionCallback.failedLatch.getCount()).isNotEqualTo(0); assertThat(connectionCallback.failedLatch.getCount()).isNotEqualTo(0);
} }
@ -109,7 +111,7 @@ public class MediaBrowserCompatWithMediaSessionServiceTest {
@Test @Test
public void getSessionToken() throws Exception { public void getSessionToken() throws Exception {
connectAndWait(); connectAndWait(/* connectionHints= */ Bundle.EMPTY);
MediaControllerCompat controller = MediaControllerCompat controller =
new MediaControllerCompat(context, browserCompat.getSessionToken()); new MediaControllerCompat(context, browserCompat.getSessionToken());
assertThat(controller.getPackageName()) assertThat(controller.getPackageName())

View File

@ -82,6 +82,9 @@ public class MockMediaLibraryService extends MediaLibraryService {
/** ID of the session that this service will create. */ /** ID of the session that this service will create. */
public static final String ID = "TestLibrary"; public static final String ID = "TestLibrary";
public static final String CONNECTION_HINTS_CUSTOM_LIBRARY_ROOT =
"CONNECTION_HINTS_CUSTOM_LIBRARY_ROOT";
public static final MediaItem ROOT_ITEM = public static final MediaItem ROOT_ITEM =
new MediaItem.Builder() new MediaItem.Builder()
.setMediaId(ROOT_ID) .setMediaId(ROOT_ID)
@ -192,7 +195,24 @@ public class MockMediaLibraryService extends MediaLibraryService {
public ListenableFuture<LibraryResult<MediaItem>> onGetLibraryRoot( public ListenableFuture<LibraryResult<MediaItem>> onGetLibraryRoot(
MediaLibrarySession session, ControllerInfo browser, @Nullable LibraryParams params) { MediaLibrarySession session, ControllerInfo browser, @Nullable LibraryParams params) {
assertLibraryParams(params); assertLibraryParams(params);
return Futures.immediateFuture(LibraryResult.ofItem(ROOT_ITEM, ROOT_PARAMS)); MediaItem rootItem = ROOT_ITEM;
// Use connection hints to select the library root.
String customLibraryRoot =
browser
.getConnectionHints()
.getString(CONNECTION_HINTS_CUSTOM_LIBRARY_ROOT, /* defaultValue= */ null);
if (customLibraryRoot != null) {
rootItem =
new MediaItem.Builder()
.setMediaId(customLibraryRoot)
.setMediaMetadata(
new MediaMetadata.Builder()
.setFolderType(MediaMetadata.FOLDER_TYPE_ALBUMS)
.setIsPlayable(false)
.build())
.build();
}
return Futures.immediateFuture(LibraryResult.ofItem(rootItem, ROOT_PARAMS));
} }
@Override @Override