Use connection hints when connecting to MediaBrowserService

Minor improvement to allow an Media3 browser to pass extras
when connecting the initial browser in `MediaControllerImplLegacy`.
Before this change an empty bundle was sent. After this change
the connection hints of the `Media3 browser is used as root hints
of the initial browser that connects when the Media3 browser is
built in `MediaBrowser.buildAsync`.

#cherrypick

PiperOrigin-RevId: 684372552
This commit is contained in:
bachinger 2024-10-10 03:08:49 -07:00 committed by Copybara-Service
parent b6d0540059
commit cbc0ee369f
8 changed files with 75 additions and 5 deletions

View File

@ -112,6 +112,10 @@
* Fix bug where a Media3 controller was sometimes unable to let a session
app start a foreground service after requesting `play()`.
* Restrict `CommandButton.Builder.setIconUri` to only accept content Uris.
* Pass connection hints of a Media3 browser to the initial
`MediaBrowserCompat` when connecting to a legacy `MediaBrowserCompat`.
The service can receive the connection hints passed in as root hints
with the first call to `onGetRoot()`.
* UI:
* Make the stretched/cropped video in
`PlayerView`-in-Compose-`AndroidView` workaround opt-in, due to issues

View File

@ -292,7 +292,7 @@ public final class MediaBrowser extends MediaController {
if (token.isLegacySession()) {
impl =
new MediaBrowserImplLegacy(
context, this, token, applicationLooper, checkNotNull(bitmapLoader));
context, this, token, connectionHints, applicationLooper, checkNotNull(bitmapLoader));
} else {
impl = new MediaBrowserImplBase(context, this, token, connectionHints, applicationLooper);
}

View File

@ -62,9 +62,10 @@ import org.checkerframework.checker.initialization.qual.UnderInitialization;
Context context,
@UnderInitialization MediaBrowser instance,
SessionToken token,
Bundle connectionHints,
Looper applicationLooper,
BitmapLoader bitmapLoader) {
super(context, instance, token, applicationLooper, bitmapLoader);
super(context, instance, token, connectionHints, applicationLooper, bitmapLoader);
this.instance = instance;
commandButtonsForMediaItems = ImmutableMap.of();
}

View File

@ -568,7 +568,7 @@ public class MediaController implements Player {
@Nullable BitmapLoader bitmapLoader) {
if (token.isLegacySession()) {
return new MediaControllerImplLegacy(
context, this, token, applicationLooper, checkNotNull(bitmapLoader));
context, this, token, connectionHints, applicationLooper, checkNotNull(bitmapLoader));
} else {
return new MediaControllerImplBase(context, this, token, connectionHints, applicationLooper);
}
@ -2073,6 +2073,10 @@ public class MediaController implements Player {
return impl.getBinder();
}
/* package */ Bundle getConnectionHints() {
return impl.getConnectionHints();
}
private void verifyApplicationThread() {
checkState(Looper.myLooper() == getApplicationLooper(), WRONG_THREAD_ERROR_MESSAGE);
}
@ -2081,6 +2085,8 @@ public class MediaController implements Player {
void connect(@UnderInitialization MediaControllerImpl this);
Bundle getConnectionHints();
void addListener(Player.Listener listener);
void removeListener(Player.Listener listener);

View File

@ -215,6 +215,11 @@ import org.checkerframework.checker.nullness.qual.NonNull;
}
}
@Override
public Bundle getConnectionHints() {
return connectionHints;
}
@Override
public void addListener(Listener listener) {
listeners.add(listener);

View File

@ -102,6 +102,7 @@ import org.checkerframework.checker.initialization.qual.UnderInitialization;
private final ControllerCompatCallback controllerCompatCallback;
private final BitmapLoader bitmapLoader;
private final ImmutableList<CommandButton> commandButtonsForMediaItems;
private final Bundle connectionHints;
@Nullable private MediaControllerCompat controllerCompat;
@Nullable private MediaBrowserCompat browserCompat;
@ -117,6 +118,7 @@ import org.checkerframework.checker.initialization.qual.UnderInitialization;
Context context,
@UnderInitialization MediaController instance,
SessionToken token,
Bundle connectionHints,
Looper applicationLooper,
BitmapLoader bitmapLoader) {
// Initialize default values.
@ -134,6 +136,7 @@ import org.checkerframework.checker.initialization.qual.UnderInitialization;
this.instance = instance;
controllerCompatCallback = new ControllerCompatCallback(applicationLooper);
this.token = token;
this.connectionHints = connectionHints;
this.bitmapLoader = bitmapLoader;
currentPositionMs = C.TIME_UNSET;
lastSetPlayWhenReadyCalledTimeMs = C.TIME_UNSET;
@ -154,6 +157,11 @@ import org.checkerframework.checker.initialization.qual.UnderInitialization;
}
}
@Override
public Bundle getConnectionHints() {
return connectionHints;
}
@Override
public void addListener(Listener listener) {
listeners.add(listener);
@ -1410,7 +1418,10 @@ import org.checkerframework.checker.initialization.qual.UnderInitialization;
// Create it on the application looper to respect that.
browserCompat =
new MediaBrowserCompat(
context, token.getComponentName(), new ConnectionCallback(), null);
context,
token.getComponentName(),
new ConnectionCallback(),
instance.getConnectionHints());
browserCompat.connect();
});
}

View File

@ -19,6 +19,7 @@ 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.session.MockMediaBrowserServiceCompat.EXTRAS_KEY_SEND_ROOT_HINTS_AS_SESSION_EXTRAS;
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;
@ -136,6 +137,33 @@ public class MediaBrowserListenerWithMediaBrowserServiceCompatTest {
assertThat(thrown).hasCauseThat().isInstanceOf(SecurityException.class);
}
@Test
public void connect_useConnectionHints_connectionHintsPassedToLegacyServerOnGetRootAsRootHints()
throws Exception {
Bundle connectionHints = new Bundle();
connectionHints.putBoolean(EXTRAS_KEY_SEND_ROOT_HINTS_AS_SESSION_EXTRAS, true);
CountDownLatch latch = new CountDownLatch(/* count= */ 1);
AtomicReference<Bundle> extrasRef = new AtomicReference<>();
createBrowser(
connectionHints,
/* maxCommandsForMediaItems= */ 0,
/* listener= */ new MediaBrowser.Listener() {
@Override
public void onExtrasChanged(MediaController controller, Bundle extras) {
extrasRef.set(extras);
latch.countDown();
}
});
assertThat(latch.await(TIMEOUT_MS, MILLISECONDS)).isTrue();
assertThat(
extrasRef
.get()
.getBoolean(
EXTRAS_KEY_SEND_ROOT_HINTS_AS_SESSION_EXTRAS, /* defaultValue= */ false))
.isTrue();
}
@Test
public void getLibraryRoot_browseActionsAvailable() throws Exception {
remoteService.setProxyForTest(TEST_MEDIA_ITEMS_WITH_BROWSE_ACTIONS);

View File

@ -40,7 +40,9 @@ import static java.lang.Math.min;
import android.content.Intent;
import android.net.Uri;
import android.os.Bundle;
import android.os.Handler;
import android.os.IBinder;
import android.os.Looper;
import android.os.RemoteException;
import android.support.v4.media.MediaBrowserCompat;
import android.support.v4.media.MediaBrowserCompat.MediaItem;
@ -68,6 +70,13 @@ public class MockMediaBrowserServiceCompat extends MediaBrowserServiceCompat {
*/
public static final ImmutableList<MediaItem> MEDIA_ITEMS = createMediaItems();
/**
* Key in the browser root hints to request a confirmation of the call to {@link
* #onGetRoot(String, int, Bundle)}.
*/
public static final String EXTRAS_KEY_SEND_ROOT_HINTS_AS_SESSION_EXTRAS =
"confirm_on_get_root_with_custom_action";
private static final String TAG = "MockMBSCompat";
private static final Object lock = new Object();
@ -166,12 +175,18 @@ public class MockMediaBrowserServiceCompat extends MediaBrowserServiceCompat {
// Test only -- reject any other request.
return null;
}
if (rootHints.getBoolean(EXTRAS_KEY_SEND_ROOT_HINTS_AS_SESSION_EXTRAS, false)) {
// Send delayed because the Media3 browser is in the process of connecting at this point and
// won't receive listener callbacks before being connected.
new Handler(Looper.myLooper())
.postDelayed(() -> sessionCompat.setExtras(rootHints), /* delayMillis= */ 100L);
}
synchronized (lock) {
if (isProxyOverridesMethod("onGetRoot")) {
return serviceProxy.onGetRoot(clientPackageName, clientUid, rootHints);
}
}
return new BrowserRoot("stub", null);
return new BrowserRoot("stub", /* extras= */ rootHints);
}
@Override