mirror of
https://github.com/androidx/media.git
synced 2025-04-29 22:36:54 +08:00
Add Builder.setMaxCommandsForMediaItems
for browser and controller
The max number of commands for media items of a browser or controller can be configured with `setMaxCommandsForMediaItems(int)` on `MediaController.Builder` and `MediaBrowser.Builder`. An app that has only limited space for displaying commands can hint this limit to the session. A session can receive the value of a connected browser or controller through `getMaxCommandsForMediaItems()` of a `ControllerInfo` that is passed into every callback method. The session can then pick the most sensible commands instead of making the browser app truncating the commands rather randomly. When a `MediaBrowser` is connected against a legacy `MediaBrowserServiceCompat`, the max number of commands is automatically added to the root hints. Conversely, the value passed in with the root hints to `MediaLibraryService` by a legacy `MediaBrowserCompat`, is read into `ControllerInfo` like for a Media3 browser. Issue: androidx/media#1474 #cherrypick PiperOrigin-RevId: 679076506
This commit is contained in:
parent
020ce7765c
commit
65962dcb37
@ -89,8 +89,9 @@
|
||||
manufacturers when setting the broadcast receiver for media button
|
||||
intents ([#1730](https://github.com/androidx/media/issues/1730)).
|
||||
* Add command buttons for media items. This adds the Media3 API for what
|
||||
was known as `Custom browse actions` in the legacy world. No
|
||||
interoperability with the legacy API is provided with this change. See
|
||||
was known as `Custom browse actions` with the legacy library with
|
||||
`MediaBrowserCompat`. Note that with Media3 command buttons for media
|
||||
items are available for both, `MediaBrowser` and `MediaController`. See
|
||||
<a href="https://developer.android.com/training/cars/media#custom_browse_actions">Custom
|
||||
Browse actions of AAOS</a>.
|
||||
* UI:
|
||||
|
@ -39,13 +39,17 @@ import androidx.media3.common.util.Util;
|
||||
|
||||
public final Bundle connectionHints;
|
||||
|
||||
public ConnectionRequest(String packageName, int pid, Bundle connectionHints) {
|
||||
public final int maxCommandsForMediaItems;
|
||||
|
||||
public ConnectionRequest(
|
||||
String packageName, int pid, Bundle connectionHints, int maxCommandsForMediaItems) {
|
||||
this(
|
||||
MediaLibraryInfo.VERSION_INT,
|
||||
MediaControllerStub.VERSION_INT,
|
||||
packageName,
|
||||
pid,
|
||||
new Bundle(connectionHints));
|
||||
new Bundle(connectionHints),
|
||||
maxCommandsForMediaItems);
|
||||
}
|
||||
|
||||
private ConnectionRequest(
|
||||
@ -53,12 +57,14 @@ import androidx.media3.common.util.Util;
|
||||
int controllerInterfaceVersion,
|
||||
String packageName,
|
||||
int pid,
|
||||
Bundle connectionHints) {
|
||||
Bundle connectionHints,
|
||||
int maxCommandsForMediaItems) {
|
||||
this.libraryVersion = libraryVersion;
|
||||
this.controllerInterfaceVersion = controllerInterfaceVersion;
|
||||
this.packageName = packageName;
|
||||
this.pid = pid;
|
||||
this.connectionHints = connectionHints;
|
||||
this.maxCommandsForMediaItems = maxCommandsForMediaItems;
|
||||
}
|
||||
|
||||
private static final String FIELD_LIBRARY_VERSION = Util.intToStringMaxRadix(0);
|
||||
@ -66,8 +72,9 @@ import androidx.media3.common.util.Util;
|
||||
private static final String FIELD_PID = Util.intToStringMaxRadix(2);
|
||||
private static final String FIELD_CONNECTION_HINTS = Util.intToStringMaxRadix(3);
|
||||
private static final String FIELD_CONTROLLER_INTERFACE_VERSION = Util.intToStringMaxRadix(4);
|
||||
private static final String FIELD_MAX_COMMANDS_FOR_MEDIA_ITEM = Util.intToStringMaxRadix(5);
|
||||
|
||||
// Next id: 5
|
||||
// Next id: 6
|
||||
|
||||
public Bundle toBundle() {
|
||||
Bundle bundle = new Bundle();
|
||||
@ -76,6 +83,7 @@ import androidx.media3.common.util.Util;
|
||||
bundle.putInt(FIELD_PID, pid);
|
||||
bundle.putBundle(FIELD_CONNECTION_HINTS, connectionHints);
|
||||
bundle.putInt(FIELD_CONTROLLER_INTERFACE_VERSION, controllerInterfaceVersion);
|
||||
bundle.putInt(FIELD_MAX_COMMANDS_FOR_MEDIA_ITEM, maxCommandsForMediaItems);
|
||||
return bundle;
|
||||
}
|
||||
|
||||
@ -88,12 +96,14 @@ import androidx.media3.common.util.Util;
|
||||
checkArgument(bundle.containsKey(FIELD_PID));
|
||||
int pid = bundle.getInt(FIELD_PID);
|
||||
@Nullable Bundle connectionHints = bundle.getBundle(FIELD_CONNECTION_HINTS);
|
||||
int maxCommandsForMediaItems =
|
||||
bundle.getInt(FIELD_MAX_COMMANDS_FOR_MEDIA_ITEM, /* defaultValue= */ 0);
|
||||
return new ConnectionRequest(
|
||||
libraryVersion,
|
||||
controllerInterfaceVersion,
|
||||
packageName,
|
||||
pid,
|
||||
connectionHints == null ? Bundle.EMPTY : connectionHints);
|
||||
connectionHints == null ? Bundle.EMPTY : connectionHints,
|
||||
maxCommandsForMediaItems);
|
||||
}
|
||||
;
|
||||
}
|
||||
|
@ -1728,6 +1728,23 @@ import java.util.concurrent.TimeoutException;
|
||||
return buttonBundle;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the max number of commands for media items from the {@linkplain
|
||||
* androidx.media3.session.legacy.MediaBrowserServiceCompat#onGetRoot(String, int, Bundle) root
|
||||
* hints} of a legacy {@link MediaBrowserCompat} that connects.
|
||||
*
|
||||
* @param rootHints The root hints passed by the legacy browser when connecting.
|
||||
* @return The specified max number of commands per media items, or 0 if not specified.
|
||||
*/
|
||||
public static int extractMaxCommandsForMediaItemFromRootHints(Bundle rootHints) {
|
||||
return max(
|
||||
0,
|
||||
rootHints.getInt(
|
||||
androidx.media3.session.legacy.MediaConstants
|
||||
.BROWSER_ROOT_HINTS_KEY_CUSTOM_BROWSER_ACTION_LIMIT,
|
||||
/* defaultValue= */ 0));
|
||||
}
|
||||
|
||||
private static byte[] convertToByteArray(Bitmap bitmap) throws IOException {
|
||||
try (ByteArrayOutputStream stream = new ByteArrayOutputStream()) {
|
||||
bitmap.compress(Bitmap.CompressFormat.PNG, /* ignored */ 0, stream);
|
||||
|
@ -60,6 +60,7 @@ public final class MediaBrowser extends MediaController {
|
||||
private Listener listener;
|
||||
private Looper applicationLooper;
|
||||
private @MonotonicNonNull BitmapLoader bitmapLoader;
|
||||
private int maxCommandsForMediaItems;
|
||||
|
||||
/**
|
||||
* Creates a builder for {@link MediaBrowser}.
|
||||
@ -139,6 +140,22 @@ public final class MediaBrowser extends MediaController {
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the max number of commands the controller supports per media item.
|
||||
*
|
||||
* <p>Must be greater or equal to 0. The default is 0.
|
||||
*
|
||||
* @param maxCommandsForMediaItems The max number of commands per media item.
|
||||
* @return The builder to allow chaining.
|
||||
*/
|
||||
@UnstableApi
|
||||
@CanIgnoreReturnValue
|
||||
public Builder setMaxCommandsForMediaItems(int maxCommandsForMediaItems) {
|
||||
checkArgument(maxCommandsForMediaItems >= 0);
|
||||
this.maxCommandsForMediaItems = maxCommandsForMediaItems;
|
||||
return this;
|
||||
}
|
||||
|
||||
// LINT.IfChange(build_async)
|
||||
/**
|
||||
* Builds a {@link MediaBrowser} asynchronously.
|
||||
@ -173,7 +190,14 @@ public final class MediaBrowser extends MediaController {
|
||||
}
|
||||
MediaBrowser browser =
|
||||
new MediaBrowser(
|
||||
context, token, connectionHints, listener, applicationLooper, holder, bitmapLoader);
|
||||
context,
|
||||
token,
|
||||
connectionHints,
|
||||
listener,
|
||||
applicationLooper,
|
||||
holder,
|
||||
bitmapLoader,
|
||||
maxCommandsForMediaItems);
|
||||
postOrRun(new Handler(applicationLooper), () -> holder.setController(browser));
|
||||
return holder;
|
||||
}
|
||||
@ -242,7 +266,8 @@ public final class MediaBrowser extends MediaController {
|
||||
Listener listener,
|
||||
Looper applicationLooper,
|
||||
ConnectionCallback connectionCallback,
|
||||
@Nullable BitmapLoader bitmapLoader) {
|
||||
@Nullable BitmapLoader bitmapLoader,
|
||||
int maxCommandsForMediaItems) {
|
||||
super(
|
||||
context,
|
||||
token,
|
||||
@ -250,7 +275,8 @@ public final class MediaBrowser extends MediaController {
|
||||
listener,
|
||||
applicationLooper,
|
||||
connectionCallback,
|
||||
bitmapLoader);
|
||||
bitmapLoader,
|
||||
maxCommandsForMediaItems);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -110,6 +110,10 @@ import org.checkerframework.checker.initialization.qual.UnderInitialization;
|
||||
result.set(LibraryResult.ofItem(createRootMediaItem(browserCompat), null));
|
||||
} else {
|
||||
Bundle rootHints = LegacyConversions.convertToRootHints(params);
|
||||
rootHints.putInt(
|
||||
androidx.media3.session.legacy.MediaConstants
|
||||
.BROWSER_ROOT_HINTS_KEY_CUSTOM_BROWSER_ACTION_LIMIT,
|
||||
getInstance().getMaxCommandsForMediaItems());
|
||||
MediaBrowserCompat newBrowser =
|
||||
new MediaBrowserCompat(
|
||||
getContext(),
|
||||
|
@ -211,6 +211,7 @@ public class MediaController implements Player {
|
||||
private Listener listener;
|
||||
private Looper applicationLooper;
|
||||
private @MonotonicNonNull BitmapLoader bitmapLoader;
|
||||
private int maxCommandsForMediaItems;
|
||||
|
||||
/**
|
||||
* Creates a builder for {@link MediaController}.
|
||||
@ -304,6 +305,22 @@ public class MediaController implements Player {
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the max number of commands the controller supports per media item.
|
||||
*
|
||||
* <p>Must be greater or equal to 0. The default is 0.
|
||||
*
|
||||
* @param maxCommandsForMediaItems The max number of commands per media item.
|
||||
* @return The builder to allow chaining.
|
||||
*/
|
||||
@UnstableApi
|
||||
@CanIgnoreReturnValue
|
||||
public Builder setMaxCommandsForMediaItems(int maxCommandsForMediaItems) {
|
||||
checkArgument(maxCommandsForMediaItems >= 0);
|
||||
this.maxCommandsForMediaItems = maxCommandsForMediaItems;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds a {@link MediaController} asynchronously.
|
||||
*
|
||||
@ -338,7 +355,14 @@ public class MediaController implements Player {
|
||||
}
|
||||
MediaController controller =
|
||||
new MediaController(
|
||||
context, token, connectionHints, listener, applicationLooper, holder, bitmapLoader);
|
||||
context,
|
||||
token,
|
||||
connectionHints,
|
||||
listener,
|
||||
applicationLooper,
|
||||
holder,
|
||||
bitmapLoader,
|
||||
maxCommandsForMediaItems);
|
||||
postOrRun(new Handler(applicationLooper), () -> holder.setController(controller));
|
||||
return holder;
|
||||
}
|
||||
@ -493,6 +517,8 @@ public class MediaController implements Player {
|
||||
|
||||
private boolean connectionNotified;
|
||||
|
||||
private final int maxCommandsForMediaItems;
|
||||
|
||||
/* package */ final ConnectionCallback connectionCallback;
|
||||
|
||||
/** Creates a {@link MediaController} from the {@link SessionToken}. */
|
||||
@ -505,7 +531,8 @@ public class MediaController implements Player {
|
||||
Listener listener,
|
||||
Looper applicationLooper,
|
||||
ConnectionCallback connectionCallback,
|
||||
@Nullable BitmapLoader bitmapLoader) {
|
||||
@Nullable BitmapLoader bitmapLoader,
|
||||
int maxCommandsForMediaItems) {
|
||||
checkNotNull(context, "context must not be null");
|
||||
checkNotNull(token, "token must not be null");
|
||||
Log.i(
|
||||
@ -526,6 +553,7 @@ public class MediaController implements Player {
|
||||
this.listener = listener;
|
||||
applicationHandler = new Handler(applicationLooper);
|
||||
this.connectionCallback = connectionCallback;
|
||||
this.maxCommandsForMediaItems = maxCommandsForMediaItems;
|
||||
|
||||
impl = createImpl(context, token, connectionHints, applicationLooper, bitmapLoader);
|
||||
impl.connect();
|
||||
@ -1947,6 +1975,10 @@ public class MediaController implements Player {
|
||||
return applicationHandler.getLooper();
|
||||
}
|
||||
|
||||
/* package */ int getMaxCommandsForMediaItems() {
|
||||
return maxCommandsForMediaItems;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the optional time diff (in milliseconds) used for calculating the current position, or
|
||||
* {@link C#TIME_UNSET} if no diff should be applied.
|
||||
|
@ -2534,7 +2534,11 @@ import org.checkerframework.checker.nullness.qual.NonNull;
|
||||
IMediaSession.Stub.asInterface((IBinder) checkStateNotNull(token.getBinder()));
|
||||
int seq = sequencedFutureManager.obtainNextSequenceNumber();
|
||||
ConnectionRequest request =
|
||||
new ConnectionRequest(context.getPackageName(), Process.myPid(), connectionHints);
|
||||
new ConnectionRequest(
|
||||
context.getPackageName(),
|
||||
Process.myPid(),
|
||||
connectionHints,
|
||||
instance.getMaxCommandsForMediaItems());
|
||||
try {
|
||||
iSession.connect(controllerStub, seq, request.toBundle());
|
||||
} catch (RemoteException e) {
|
||||
@ -3285,7 +3289,11 @@ import org.checkerframework.checker.nullness.qual.NonNull;
|
||||
return;
|
||||
}
|
||||
ConnectionRequest request =
|
||||
new ConnectionRequest(getContext().getPackageName(), Process.myPid(), connectionHints);
|
||||
new ConnectionRequest(
|
||||
getContext().getPackageName(),
|
||||
Process.myPid(),
|
||||
connectionHints,
|
||||
instance.getMaxCommandsForMediaItems());
|
||||
iService.connect(controllerStub, request.toBundle());
|
||||
connectionRequested = true;
|
||||
} catch (RemoteException e) {
|
||||
|
@ -19,6 +19,7 @@ import static androidx.media3.common.util.Assertions.checkNotNull;
|
||||
import static androidx.media3.common.util.Assertions.checkStateNotNull;
|
||||
import static androidx.media3.common.util.Util.castNonNull;
|
||||
import static androidx.media3.common.util.Util.postOrRun;
|
||||
import static androidx.media3.session.LegacyConversions.extractMaxCommandsForMediaItemFromRootHints;
|
||||
import static androidx.media3.session.LibraryResult.RESULT_SUCCESS;
|
||||
import static androidx.media3.session.MediaUtils.TRANSACTION_SIZE_LIMIT_IN_BYTES;
|
||||
import static androidx.media3.session.legacy.MediaBrowserCompat.EXTRA_PAGE;
|
||||
@ -372,7 +373,8 @@ import java.util.concurrent.atomic.AtomicReference;
|
||||
ControllerInfo.LEGACY_CONTROLLER_INTERFACE_VERSION,
|
||||
getMediaSessionManager().isTrustedForMediaControl(remoteUserInfo),
|
||||
new BrowserLegacyCb(remoteUserInfo),
|
||||
/* connectionHints= */ rootHints);
|
||||
/* connectionHints= */ rootHints,
|
||||
extractMaxCommandsForMediaItemFromRootHints(rootHints));
|
||||
}
|
||||
|
||||
public ControllerCb getBrowserLegacyCbForBroadcast() {
|
||||
|
@ -487,6 +487,7 @@ public class MediaSession {
|
||||
private final boolean isTrusted;
|
||||
@Nullable private final ControllerCb controllerCb;
|
||||
private final Bundle connectionHints;
|
||||
private final int maxCommandsForMediaItems;
|
||||
|
||||
/**
|
||||
* Creates an instance.
|
||||
@ -499,6 +500,7 @@ public class MediaSession {
|
||||
* ControllerInfo)}.
|
||||
* @param connectionHints A session-specific argument sent from the controller for the
|
||||
* connection. The contents of this bundle may affect the connection result.
|
||||
* @param maxCommandsForMediaItems The max commands the controller supports for media items.
|
||||
*/
|
||||
/* package */ ControllerInfo(
|
||||
RemoteUserInfo remoteUserInfo,
|
||||
@ -506,13 +508,15 @@ public class MediaSession {
|
||||
int interfaceVersion,
|
||||
boolean trusted,
|
||||
@Nullable ControllerCb cb,
|
||||
Bundle connectionHints) {
|
||||
Bundle connectionHints,
|
||||
int maxCommandsForMediaItems) {
|
||||
this.remoteUserInfo = remoteUserInfo;
|
||||
this.libraryVersion = libraryVersion;
|
||||
this.interfaceVersion = interfaceVersion;
|
||||
isTrusted = trusted;
|
||||
controllerCb = cb;
|
||||
this.connectionHints = connectionHints;
|
||||
this.maxCommandsForMediaItems = maxCommandsForMediaItems;
|
||||
}
|
||||
|
||||
/* package */ RemoteUserInfo getRemoteUserInfo() {
|
||||
@ -555,6 +559,15 @@ public class MediaSession {
|
||||
return new Bundle(connectionHints);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the max number of commands for a media item. A positive number or 0 (zero) to
|
||||
* indicate that the feature is not supported by the controller.
|
||||
*/
|
||||
@UnstableApi
|
||||
public int getMaxCommandsForMediaItems() {
|
||||
return maxCommandsForMediaItems;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns if the controller has been granted {@code android.permission.MEDIA_CONTENT_CONTROL}
|
||||
* or has an enabled notification listener so it can be trusted to accept connection and
|
||||
@ -611,7 +624,8 @@ public class MediaSession {
|
||||
ControllerInfo.LEGACY_CONTROLLER_INTERFACE_VERSION,
|
||||
/* trusted= */ false,
|
||||
/* cb= */ null,
|
||||
/* connectionHints= */ Bundle.EMPTY);
|
||||
/* connectionHints= */ Bundle.EMPTY,
|
||||
/* maxCommandsForMediaItems= */ 0);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -653,7 +667,8 @@ public class MediaSession {
|
||||
interfaceVersion,
|
||||
trusted,
|
||||
/* cb= */ null,
|
||||
connectionHints);
|
||||
connectionHints,
|
||||
/* maxCommandsForMediaItems= */ 0);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -320,7 +320,8 @@ import org.checkerframework.checker.initialization.qual.Initialized;
|
||||
ControllerInfo.LEGACY_CONTROLLER_INTERFACE_VERSION,
|
||||
/* trusted= */ false,
|
||||
/* cb= */ null,
|
||||
/* connectionHints= */ Bundle.EMPTY),
|
||||
/* connectionHints= */ Bundle.EMPTY,
|
||||
/* maxCommandsForMediaItems= */ 0),
|
||||
intent);
|
||||
}
|
||||
|
||||
@ -782,7 +783,8 @@ import org.checkerframework.checker.initialization.qual.Initialized;
|
||||
ControllerInfo.LEGACY_CONTROLLER_INTERFACE_VERSION,
|
||||
sessionManager.isTrustedForMediaControl(remoteUserInfo),
|
||||
controllerCb,
|
||||
/* connectionHints= */ Bundle.EMPTY);
|
||||
/* connectionHints= */ Bundle.EMPTY,
|
||||
/* maxCommandsForMediaItems= */ 0);
|
||||
MediaSession.ConnectionResult connectionResult = sessionImpl.onConnectOnHandler(controller);
|
||||
if (!connectionResult.isAccepted) {
|
||||
try {
|
||||
|
@ -466,7 +466,8 @@ public abstract class MediaSessionService extends Service {
|
||||
MediaControllerStub.VERSION_INT,
|
||||
/* trusted= */ false,
|
||||
/* cb= */ null,
|
||||
/* connectionHints= */ Bundle.EMPTY);
|
||||
/* connectionHints= */ Bundle.EMPTY,
|
||||
/* maxCommandsForMediaItems= */ 0);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -761,7 +762,8 @@ public abstract class MediaSessionService extends Service {
|
||||
request.controllerInterfaceVersion,
|
||||
isTrusted,
|
||||
new MediaSessionStub.Controller2Cb(caller),
|
||||
request.connectionHints);
|
||||
request.connectionHints,
|
||||
request.maxCommandsForMediaItems);
|
||||
|
||||
@Nullable MediaSession session;
|
||||
try {
|
||||
|
@ -16,6 +16,7 @@
|
||||
package androidx.media3.session;
|
||||
|
||||
import static androidx.media3.common.util.Util.postOrRun;
|
||||
import static androidx.media3.session.LegacyConversions.extractMaxCommandsForMediaItemFromRootHints;
|
||||
|
||||
import android.os.Bundle;
|
||||
import androidx.annotation.Nullable;
|
||||
@ -100,7 +101,8 @@ import java.util.concurrent.atomic.AtomicReference;
|
||||
ControllerInfo.LEGACY_CONTROLLER_INTERFACE_VERSION,
|
||||
manager.isTrustedForMediaControl(info),
|
||||
/* cb= */ null,
|
||||
/* connectionHints= */ rootHints);
|
||||
/* connectionHints= */ rootHints,
|
||||
extractMaxCommandsForMediaItemFromRootHints(rootHints));
|
||||
}
|
||||
|
||||
public final MediaSessionManager getMediaSessionManager() {
|
||||
|
@ -637,7 +637,8 @@ import java.util.concurrent.ExecutionException;
|
||||
request.controllerInterfaceVersion,
|
||||
sessionManager.isTrustedForMediaControl(remoteUserInfo),
|
||||
new MediaSessionStub.Controller2Cb(caller),
|
||||
request.connectionHints);
|
||||
request.connectionHints,
|
||||
request.maxCommandsForMediaItems);
|
||||
connect(caller, controllerInfo);
|
||||
} finally {
|
||||
Binder.restoreCallingIdentity(token);
|
||||
|
@ -108,7 +108,10 @@ public class MediaSessionStubTest {
|
||||
/* caller= */ null,
|
||||
/* seq= */ 0,
|
||||
/* connectionRequest= */ new ConnectionRequest(
|
||||
"pkg", /* pid= */ 0, /* connectionHints= */ new Bundle())
|
||||
"pkg",
|
||||
/* pid= */ 0,
|
||||
/* connectionHints= */ new Bundle(),
|
||||
/* maxCommandsForMediaItems= */ 0)
|
||||
.toBundle());
|
||||
binder.onCustomCommand(
|
||||
/* caller= */ null,
|
||||
@ -497,7 +500,10 @@ public class MediaSessionStubTest {
|
||||
caller,
|
||||
/* seq= */ 0,
|
||||
/* connectionRequest= */ new ConnectionRequest(
|
||||
/* packageName= */ "invalid", /* pid= */ 9999, /* connectionHints= */ new Bundle())
|
||||
/* packageName= */ "invalid",
|
||||
/* pid= */ 9999,
|
||||
/* connectionHints= */ new Bundle(),
|
||||
/* maxCommandsForMediaItems= */ 0)
|
||||
.toBundle());
|
||||
}
|
||||
}
|
||||
|
@ -85,7 +85,8 @@ public class MediaSessionUnitTest { // Avoid naming collision with session_curre
|
||||
MediaControllerStub.VERSION_INT,
|
||||
/* trusted= */ false,
|
||||
/* cb= */ null,
|
||||
/* connectionHints= */ Bundle.EMPTY);
|
||||
/* connectionHints= */ Bundle.EMPTY,
|
||||
/* maxCommandsForMediaItems= */ 0);
|
||||
|
||||
assertThat(session.isAutomotiveController(controllerInfo)).isFalse();
|
||||
}
|
||||
@ -140,7 +141,8 @@ public class MediaSessionUnitTest { // Avoid naming collision with session_curre
|
||||
MediaControllerStub.VERSION_INT,
|
||||
/* trusted= */ false,
|
||||
/* cb= */ null,
|
||||
/* connectionHints= */ Bundle.EMPTY);
|
||||
/* connectionHints= */ Bundle.EMPTY,
|
||||
/* maxCommandsForMediaItems= */ 0);
|
||||
|
||||
assertThat(session.isAutoCompanionController(controllerInfo)).isFalse();
|
||||
}
|
||||
@ -161,7 +163,8 @@ public class MediaSessionUnitTest { // Avoid naming collision with session_curre
|
||||
MediaControllerStub.VERSION_INT,
|
||||
/* trusted= */ false,
|
||||
/* cb= */ null,
|
||||
connectionHints);
|
||||
connectionHints,
|
||||
/* maxCommandsForMediaItems= */ 0);
|
||||
|
||||
assertThat(session.isMediaNotificationController(controllerInfo)).isTrue();
|
||||
}
|
||||
@ -182,7 +185,8 @@ public class MediaSessionUnitTest { // Avoid naming collision with session_curre
|
||||
MediaControllerStub.VERSION_INT,
|
||||
/* trusted= */ false,
|
||||
/* cb= */ null,
|
||||
connectionHints);
|
||||
connectionHints,
|
||||
/* maxCommandsForMediaItems= */ 0);
|
||||
|
||||
assertThat(session.isMediaNotificationController(controllerInfo)).isFalse();
|
||||
}
|
||||
@ -201,7 +205,8 @@ public class MediaSessionUnitTest { // Avoid naming collision with session_curre
|
||||
MediaControllerStub.VERSION_INT,
|
||||
/* trusted= */ false,
|
||||
/* cb= */ null,
|
||||
/* connectionHints= */ Bundle.EMPTY);
|
||||
/* connectionHints= */ Bundle.EMPTY,
|
||||
/* maxCommandsForMediaItems= */ 0);
|
||||
|
||||
assertThat(session.isMediaNotificationController(controllerInfo)).isFalse();
|
||||
}
|
||||
@ -222,7 +227,8 @@ public class MediaSessionUnitTest { // Avoid naming collision with session_curre
|
||||
MediaSession.ControllerInfo.LEGACY_CONTROLLER_INTERFACE_VERSION,
|
||||
/* trusted= */ false,
|
||||
/* cb= */ null,
|
||||
connectionHints);
|
||||
connectionHints,
|
||||
/* maxCommandsForMediaItems= */ 0);
|
||||
|
||||
assertThat(session.isMediaNotificationController(controllerInfo)).isFalse();
|
||||
}
|
||||
@ -235,6 +241,7 @@ public class MediaSessionUnitTest { // Avoid naming collision with session_curre
|
||||
MediaSession.ControllerInfo.LEGACY_CONTROLLER_INTERFACE_VERSION,
|
||||
/* trusted= */ false,
|
||||
/* cb= */ null,
|
||||
/* connectionHints= */ Bundle.EMPTY);
|
||||
/* connectionHints= */ Bundle.EMPTY,
|
||||
/* maxCommandsForMediaItems= */ 0);
|
||||
}
|
||||
}
|
||||
|
@ -22,6 +22,7 @@ import static androidx.media3.session.MediaLibraryService.MediaLibrarySession.LI
|
||||
import static androidx.media3.session.MediaLibraryService.MediaLibrarySession.LIBRARY_ERROR_REPLICATION_MODE_NON_FATAL;
|
||||
import static androidx.media3.session.MockMediaLibraryService.CONNECTION_HINTS_CUSTOM_LIBRARY_ROOT;
|
||||
import static androidx.media3.session.MockMediaLibraryService.createNotifyChildrenChangedBundle;
|
||||
import static androidx.media3.session.legacy.MediaConstants.BROWSER_ROOT_HINTS_KEY_CUSTOM_BROWSER_ACTION_LIMIT;
|
||||
import static androidx.media3.session.legacy.MediaConstants.DESCRIPTION_EXTRAS_KEY_CUSTOM_BROWSER_ACTION_ID_LIST;
|
||||
import static androidx.media3.test.session.common.CommonConstants.METADATA_ALBUM_TITLE;
|
||||
import static androidx.media3.test.session.common.CommonConstants.METADATA_ARTIST;
|
||||
@ -237,7 +238,9 @@ public class MediaBrowserCompatWithMediaLibraryServiceTest
|
||||
@Test
|
||||
public void getItem_playableWithBrowseActions_browseActionCorrectlyConverted() throws Exception {
|
||||
String mediaId = MEDIA_ID_GET_ITEM_WITH_BROWSE_ACTIONS;
|
||||
connectAndWait(/* rootHints= */ Bundle.EMPTY);
|
||||
Bundle rootHints = new Bundle();
|
||||
rootHints.putInt(BROWSER_ROOT_HINTS_KEY_CUSTOM_BROWSER_ACTION_LIMIT, 10);
|
||||
connectAndWait(rootHints);
|
||||
CountDownLatch latch = new CountDownLatch(1);
|
||||
AtomicReference<MediaItem> itemRef = new AtomicReference<>();
|
||||
|
||||
@ -263,6 +266,36 @@ public class MediaBrowserCompatWithMediaLibraryServiceTest
|
||||
.inOrder();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getItem_maxCommandsForMediaItemSetToBelowMaxAvailableCommands_maxCommandsHonoured()
|
||||
throws Exception {
|
||||
String mediaId = MEDIA_ID_GET_ITEM_WITH_BROWSE_ACTIONS;
|
||||
Bundle rootHints = new Bundle();
|
||||
rootHints.putInt(BROWSER_ROOT_HINTS_KEY_CUSTOM_BROWSER_ACTION_LIMIT, 1);
|
||||
connectAndWait(rootHints);
|
||||
CountDownLatch latch = new CountDownLatch(1);
|
||||
AtomicReference<MediaItem> itemRef = new AtomicReference<>();
|
||||
|
||||
browserCompat.getItem(
|
||||
mediaId,
|
||||
new ItemCallback() {
|
||||
@Override
|
||||
public void onItemLoaded(MediaItem item) {
|
||||
itemRef.set(item);
|
||||
latch.countDown();
|
||||
}
|
||||
});
|
||||
|
||||
assertThat(latch.await(TIMEOUT_MS, MILLISECONDS)).isTrue();
|
||||
assertThat(
|
||||
itemRef
|
||||
.get()
|
||||
.getDescription()
|
||||
.getExtras()
|
||||
.getStringArrayList(DESCRIPTION_EXTRAS_KEY_CUSTOM_BROWSER_ACTION_ID_LIST))
|
||||
.containsExactly(MediaBrowserConstants.COMMAND_PLAYLIST_ADD);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getItem_metadata() throws Exception {
|
||||
String mediaId = MEDIA_ID_GET_ITEM_WITH_METADATA;
|
||||
|
@ -90,9 +90,23 @@ public class MediaBrowserListenerWithMediaBrowserServiceCompatTest {
|
||||
private RemoteMediaBrowserServiceCompat remoteService;
|
||||
|
||||
private MediaBrowser createBrowser(@Nullable MediaBrowser.Listener listener) throws Exception {
|
||||
return createBrowser(
|
||||
/* connectionHints= */ Bundle.EMPTY, /* maxCommandsForMediaItems= */ 0, listener);
|
||||
}
|
||||
|
||||
private MediaBrowser createBrowser(
|
||||
Bundle connectionHints,
|
||||
int maxCommandsForMediaItems,
|
||||
@Nullable MediaBrowser.Listener listener)
|
||||
throws Exception {
|
||||
SessionToken token = new SessionToken(context, MOCK_MEDIA_BROWSER_SERVICE_COMPAT);
|
||||
return (MediaBrowser)
|
||||
controllerTestRule.createController(token, /* connectionHints= */ null, listener);
|
||||
controllerTestRule.createController(
|
||||
token,
|
||||
connectionHints,
|
||||
listener,
|
||||
/* controllerCreationListener= */ null,
|
||||
maxCommandsForMediaItems);
|
||||
}
|
||||
|
||||
@Before
|
||||
@ -151,7 +165,8 @@ public class MediaBrowserListenerWithMediaBrowserServiceCompatTest {
|
||||
"invalid"))
|
||||
.build())
|
||||
.build();
|
||||
MediaBrowser mediaBrowser = createBrowser(/* listener= */ null);
|
||||
MediaBrowser mediaBrowser =
|
||||
createBrowser(Bundle.EMPTY, /* maxCommandsForMediaItems= */ 2, /* listener= */ null);
|
||||
// When connected to a legacy browser service, the library root needs to be requested
|
||||
// before media item commands are available.
|
||||
LibraryResult<MediaItem> libraryResult =
|
||||
@ -172,7 +187,8 @@ public class MediaBrowserListenerWithMediaBrowserServiceCompatTest {
|
||||
@Test
|
||||
public void getItem_supportedCommandActions_convertedCorrectly() throws Exception {
|
||||
remoteService.setProxyForTest(TEST_MEDIA_ITEMS_WITH_BROWSE_ACTIONS);
|
||||
MediaBrowser mediaBrowser = createBrowser(/* listener= */ null);
|
||||
MediaBrowser mediaBrowser =
|
||||
createBrowser(Bundle.EMPTY, /* maxCommandsForMediaItems= */ 1, /* listener= */ null);
|
||||
CommandButton playlistAddButton =
|
||||
new CommandButton.Builder()
|
||||
.setDisplayName("Add to playlist")
|
||||
@ -180,13 +196,6 @@ public class MediaBrowserListenerWithMediaBrowserServiceCompatTest {
|
||||
.setSessionCommand(
|
||||
new SessionCommand(MediaBrowserConstants.COMMAND_PLAYLIST_ADD, Bundle.EMPTY))
|
||||
.build();
|
||||
CommandButton radioButton =
|
||||
new CommandButton.Builder()
|
||||
.setDisplayName("Radio station")
|
||||
.setIconUri(Uri.parse("https://www.example.com/icon/radio"))
|
||||
.setSessionCommand(
|
||||
new SessionCommand(MediaBrowserConstants.COMMAND_RADIO, Bundle.EMPTY))
|
||||
.build();
|
||||
// When connected to a legacy browser service, the library root needs to be requested
|
||||
// before media item commands are available.
|
||||
LibraryResult<MediaItem> libraryResult =
|
||||
@ -204,9 +213,8 @@ public class MediaBrowserListenerWithMediaBrowserServiceCompatTest {
|
||||
.postAndSync(
|
||||
() -> mediaBrowser.getCommandButtonsForMediaItem(requireNonNull(mediaItem)));
|
||||
|
||||
assertThat(commandButtons).containsExactly(playlistAddButton, radioButton).inOrder();
|
||||
assertThat(commandButtons).containsExactly(playlistAddButton);
|
||||
assertThat(commandButtons.get(0).extras.getString("key-1")).isEqualTo("playlist_add");
|
||||
assertThat(commandButtons.get(1).extras.getString("key-1")).isEqualTo("radio");
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -97,7 +97,7 @@ public class MediaBrowserServiceCompatCallbackWithMediaBrowserTest {
|
||||
}
|
||||
});
|
||||
|
||||
RemoteMediaBrowser browser = new RemoteMediaBrowser(context, token, true, null);
|
||||
RemoteMediaBrowser browser = new RemoteMediaBrowser(context, token, true, Bundle.EMPTY);
|
||||
LibraryResult<MediaItem> result = browser.getLibraryRoot(testParams);
|
||||
assertThat(latch.await(TIMEOUT_MS, MILLISECONDS)).isTrue();
|
||||
|
||||
@ -136,7 +136,7 @@ public class MediaBrowserServiceCompatCallbackWithMediaBrowserTest {
|
||||
|
||||
RemoteMediaBrowser browser =
|
||||
new RemoteMediaBrowser(
|
||||
context, token, /* waitForConnection= */ true, /* connectionHints= */ null);
|
||||
context, token, /* waitForConnection= */ true, /* connectionHints= */ Bundle.EMPTY);
|
||||
LibraryResult<MediaItem> result = browser.getItem(testMediaId);
|
||||
|
||||
assertThat(latch.await(TIMEOUT_MS, MILLISECONDS)).isTrue();
|
||||
@ -164,7 +164,7 @@ public class MediaBrowserServiceCompatCallbackWithMediaBrowserTest {
|
||||
|
||||
RemoteMediaBrowser browser =
|
||||
new RemoteMediaBrowser(
|
||||
context, token, /* waitForConnection= */ true, /* connectionHints= */ null);
|
||||
context, token, /* waitForConnection= */ true, /* connectionHints= */ Bundle.EMPTY);
|
||||
LibraryResult<MediaItem> result = browser.getItem(testMediaId);
|
||||
|
||||
assertThat(latch.await(TIMEOUT_MS, MILLISECONDS)).isTrue();
|
||||
@ -187,7 +187,7 @@ public class MediaBrowserServiceCompatCallbackWithMediaBrowserTest {
|
||||
}
|
||||
});
|
||||
|
||||
RemoteMediaBrowser browser = new RemoteMediaBrowser(context, token, true, null);
|
||||
RemoteMediaBrowser browser = new RemoteMediaBrowser(context, token, true, Bundle.EMPTY);
|
||||
LibraryResult<MediaItem> result = browser.getItem(testMediaId);
|
||||
assertThat(latch.await(TIMEOUT_MS, MILLISECONDS)).isTrue();
|
||||
assertThat(result.resultCode).isNotEqualTo(LibraryResult.RESULT_SUCCESS);
|
||||
@ -215,7 +215,7 @@ public class MediaBrowserServiceCompatCallbackWithMediaBrowserTest {
|
||||
latch.countDown();
|
||||
}
|
||||
});
|
||||
RemoteMediaBrowser browser = new RemoteMediaBrowser(context, token, true, null);
|
||||
RemoteMediaBrowser browser = new RemoteMediaBrowser(context, token, true, Bundle.EMPTY);
|
||||
LibraryResult<ImmutableList<MediaItem>> result =
|
||||
browser.getChildren(testParentId, testPage, testPageSize, null);
|
||||
assertThat(latch.await(TIMEOUT_MS, MILLISECONDS)).isTrue();
|
||||
@ -249,7 +249,7 @@ public class MediaBrowserServiceCompatCallbackWithMediaBrowserTest {
|
||||
latch.countDown();
|
||||
}
|
||||
});
|
||||
RemoteMediaBrowser browser = new RemoteMediaBrowser(context, token, true, null);
|
||||
RemoteMediaBrowser browser = new RemoteMediaBrowser(context, token, true, Bundle.EMPTY);
|
||||
LibraryResult<ImmutableList<MediaItem>> result =
|
||||
browser.getChildren(testParentId, testPage, testPageSize, null);
|
||||
assertThat(latch.await(TIMEOUT_MS, MILLISECONDS)).isTrue();
|
||||
@ -277,7 +277,7 @@ public class MediaBrowserServiceCompatCallbackWithMediaBrowserTest {
|
||||
latch.countDown();
|
||||
}
|
||||
});
|
||||
RemoteMediaBrowser browser = new RemoteMediaBrowser(context, token, true, null);
|
||||
RemoteMediaBrowser browser = new RemoteMediaBrowser(context, token, true, Bundle.EMPTY);
|
||||
LibraryResult<ImmutableList<MediaItem>> result =
|
||||
browser.getChildren(testParentId, testPage, testPageSize, null);
|
||||
assertThat(latch.await(TIMEOUT_MS, MILLISECONDS)).isTrue();
|
||||
@ -307,7 +307,7 @@ public class MediaBrowserServiceCompatCallbackWithMediaBrowserTest {
|
||||
latch.countDown();
|
||||
}
|
||||
});
|
||||
RemoteMediaBrowser browser = new RemoteMediaBrowser(context, token, true, null);
|
||||
RemoteMediaBrowser browser = new RemoteMediaBrowser(context, token, true, Bundle.EMPTY);
|
||||
LibraryResult<ImmutableList<MediaItem>> result =
|
||||
browser.getChildren(testParentId, testPage, testPageSize, testParams);
|
||||
assertThat(latch.await(TIMEOUT_MS, MILLISECONDS)).isTrue();
|
||||
@ -332,7 +332,7 @@ public class MediaBrowserServiceCompatCallbackWithMediaBrowserTest {
|
||||
subscribeLatch.countDown();
|
||||
}
|
||||
});
|
||||
RemoteMediaBrowser browser = new RemoteMediaBrowser(context, token, true, null);
|
||||
RemoteMediaBrowser browser = new RemoteMediaBrowser(context, token, true, Bundle.EMPTY);
|
||||
LibraryResult<Void> result = browser.subscribe(testParentId, testParams);
|
||||
assertThat(subscribeLatch.await(TIMEOUT_MS, MILLISECONDS)).isTrue();
|
||||
assertThat(result.resultCode).isEqualTo(LibraryResult.RESULT_SUCCESS);
|
||||
@ -354,7 +354,7 @@ public class MediaBrowserServiceCompatCallbackWithMediaBrowserTest {
|
||||
subscribeLatch.countDown();
|
||||
}
|
||||
});
|
||||
RemoteMediaBrowser browser = new RemoteMediaBrowser(context, token, true, null);
|
||||
RemoteMediaBrowser browser = new RemoteMediaBrowser(context, token, true, Bundle.EMPTY);
|
||||
LibraryResult<Void> result = browser.subscribe(testParentId, null);
|
||||
assertThat(subscribeLatch.await(TIMEOUT_MS, MILLISECONDS)).isTrue();
|
||||
assertThat(result.resultCode).isNotEqualTo(LibraryResult.RESULT_SUCCESS);
|
||||
@ -382,7 +382,7 @@ public class MediaBrowserServiceCompatCallbackWithMediaBrowserTest {
|
||||
}
|
||||
});
|
||||
|
||||
RemoteMediaBrowser browser = new RemoteMediaBrowser(context, token, true, null);
|
||||
RemoteMediaBrowser browser = new RemoteMediaBrowser(context, token, true, Bundle.EMPTY);
|
||||
LibraryResult<Void> result = browser.search(testQuery, testParams);
|
||||
assertThat(latch.await(TIMEOUT_MS, MILLISECONDS)).isTrue();
|
||||
assertThat(result.resultCode).isEqualTo(LibraryResult.RESULT_SUCCESS);
|
||||
@ -403,7 +403,7 @@ public class MediaBrowserServiceCompatCallbackWithMediaBrowserTest {
|
||||
}
|
||||
});
|
||||
|
||||
RemoteMediaBrowser browser = new RemoteMediaBrowser(context, token, true, null);
|
||||
RemoteMediaBrowser browser = new RemoteMediaBrowser(context, token, true, Bundle.EMPTY);
|
||||
LibraryResult<Void> result = browser.search(testQuery, null);
|
||||
assertThat(latch.await(TIMEOUT_MS, MILLISECONDS)).isTrue();
|
||||
assertThat(result.resultCode).isEqualTo(LibraryResult.RESULT_SUCCESS);
|
||||
@ -431,7 +431,7 @@ public class MediaBrowserServiceCompatCallbackWithMediaBrowserTest {
|
||||
}
|
||||
});
|
||||
|
||||
RemoteMediaBrowser browser = new RemoteMediaBrowser(context, token, true, null);
|
||||
RemoteMediaBrowser browser = new RemoteMediaBrowser(context, token, true, Bundle.EMPTY);
|
||||
LibraryResult<ImmutableList<MediaItem>> result =
|
||||
browser.getSearchResult(testQuery, testPage, testPageSize, testParams);
|
||||
assertThat(latch.await(TIMEOUT_MS, MILLISECONDS)).isTrue();
|
||||
@ -456,7 +456,7 @@ public class MediaBrowserServiceCompatCallbackWithMediaBrowserTest {
|
||||
}
|
||||
});
|
||||
|
||||
RemoteMediaBrowser browser = new RemoteMediaBrowser(context, token, true, null);
|
||||
RemoteMediaBrowser browser = new RemoteMediaBrowser(context, token, true, Bundle.EMPTY);
|
||||
LibraryResult<ImmutableList<MediaItem>> result =
|
||||
browser.getSearchResult(testQuery, testPage, testPageSize, testParams);
|
||||
assertThat(latch.await(TIMEOUT_MS, MILLISECONDS)).isTrue();
|
||||
|
@ -763,7 +763,8 @@ public class MediaControllerTest {
|
||||
latch.countDown();
|
||||
}
|
||||
},
|
||||
/* controllerCreationListener= */ MediaController::release);
|
||||
/* controllerCreationListener= */ MediaController::release,
|
||||
/* maxCommandsForMediaItems= */ 0);
|
||||
assertThat(latch.await(TIMEOUT_MS, MILLISECONDS)).isTrue();
|
||||
assertThat(controller.isConnected()).isFalse();
|
||||
}
|
||||
|
@ -105,7 +105,7 @@ public final class MediaControllerTestRule extends ExternalResource {
|
||||
public MediaController createController(
|
||||
MediaSessionCompat.Token token, @Nullable MediaController.Listener listener)
|
||||
throws Exception {
|
||||
return createController(token, listener, /* controllerCreateListener= */ null);
|
||||
return createController(token, listener, /* controllerCreationListener= */ null);
|
||||
}
|
||||
|
||||
/** Creates {@link MediaController} from {@link MediaSessionCompat.Token}. */
|
||||
@ -121,17 +121,6 @@ public final class MediaControllerTestRule extends ExternalResource {
|
||||
return controller;
|
||||
}
|
||||
|
||||
private MediaController createControllerOnHandler(
|
||||
MediaSessionCompat.Token token,
|
||||
TestMediaBrowserListener listener,
|
||||
@Nullable MediaControllerCreationListener controllerCreationListener)
|
||||
throws Exception {
|
||||
SessionToken sessionToken =
|
||||
SessionToken.createSessionToken(context, token).get(TIMEOUT_MS, MILLISECONDS);
|
||||
return createControllerOnHandler(
|
||||
sessionToken, /* connectionHints= */ null, listener, controllerCreationListener);
|
||||
}
|
||||
|
||||
/** Creates {@link MediaController} from {@link SessionToken} with default options. */
|
||||
public MediaController createController(SessionToken token) throws Exception {
|
||||
return createController(token, /* connectionHints= */ null, /* listener= */ null);
|
||||
@ -144,7 +133,11 @@ public final class MediaControllerTestRule extends ExternalResource {
|
||||
@Nullable MediaController.Listener listener)
|
||||
throws Exception {
|
||||
return createController(
|
||||
token, connectionHints, listener, /* controllerCreationListener= */ null);
|
||||
token,
|
||||
connectionHints,
|
||||
listener,
|
||||
/* controllerCreationListener= */ null,
|
||||
/* maxCommandsForMediaItems= */ 0);
|
||||
}
|
||||
|
||||
/** Creates {@link MediaController} from {@link SessionToken}. */
|
||||
@ -152,20 +145,42 @@ public final class MediaControllerTestRule extends ExternalResource {
|
||||
SessionToken token,
|
||||
@Nullable Bundle connectionHints,
|
||||
@Nullable MediaController.Listener listener,
|
||||
@Nullable MediaControllerCreationListener controllerCreationListener)
|
||||
@Nullable MediaControllerCreationListener controllerCreationListener,
|
||||
int maxCommandsForMediaItems)
|
||||
throws Exception {
|
||||
TestMediaBrowserListener testListener = new TestMediaBrowserListener(listener);
|
||||
MediaController controller =
|
||||
createControllerOnHandler(token, connectionHints, testListener, controllerCreationListener);
|
||||
createControllerOnHandler(
|
||||
token,
|
||||
connectionHints,
|
||||
testListener,
|
||||
controllerCreationListener,
|
||||
maxCommandsForMediaItems);
|
||||
controllers.put(controller, testListener);
|
||||
return controller;
|
||||
}
|
||||
|
||||
private MediaController createControllerOnHandler(
|
||||
MediaSessionCompat.Token token,
|
||||
TestMediaBrowserListener listener,
|
||||
@Nullable MediaControllerCreationListener controllerCreationListener)
|
||||
throws Exception {
|
||||
SessionToken sessionToken =
|
||||
SessionToken.createSessionToken(context, token).get(TIMEOUT_MS, MILLISECONDS);
|
||||
return createControllerOnHandler(
|
||||
sessionToken,
|
||||
/* connectionHints= */ null,
|
||||
listener,
|
||||
controllerCreationListener,
|
||||
/* maxCommandsForMediaItems= */ 0);
|
||||
}
|
||||
|
||||
private MediaController createControllerOnHandler(
|
||||
SessionToken token,
|
||||
@Nullable Bundle connectionHints,
|
||||
TestMediaBrowserListener listener,
|
||||
@Nullable MediaControllerCreationListener controllerCreationListener)
|
||||
@Nullable MediaControllerCreationListener controllerCreationListener,
|
||||
int maxCommandsForMediaItems)
|
||||
throws Exception {
|
||||
// Create controller on the test handler, for changing MediaBrowserCompat's Handler
|
||||
// Looper. Otherwise, MediaBrowserCompat will post all the commands to the handler
|
||||
@ -181,6 +196,7 @@ public final class MediaControllerTestRule extends ExternalResource {
|
||||
if (connectionHints != null) {
|
||||
builder.setConnectionHints(connectionHints);
|
||||
}
|
||||
builder.setMaxCommandsForMediaItems(maxCommandsForMediaItems);
|
||||
return builder.buildAsync();
|
||||
} else {
|
||||
MediaController.Builder builder =
|
||||
@ -188,6 +204,7 @@ public final class MediaControllerTestRule extends ExternalResource {
|
||||
if (connectionHints != null) {
|
||||
builder.setConnectionHints(connectionHints);
|
||||
}
|
||||
builder.setMaxCommandsForMediaItems(maxCommandsForMediaItems);
|
||||
return builder.buildAsync();
|
||||
}
|
||||
});
|
||||
|
@ -44,6 +44,7 @@ import com.google.common.util.concurrent.ListenableFuture;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.CountDownLatch;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
import org.junit.Before;
|
||||
import org.junit.ClassRule;
|
||||
@ -83,6 +84,38 @@ public class MediaLibrarySessionCallbackTest {
|
||||
.build();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void onConnect_withMaxCommandsForMediaItems_correctMaxLimitInControllerInfo()
|
||||
throws Exception {
|
||||
CountDownLatch latch = new CountDownLatch(/* count= */ 1);
|
||||
AtomicInteger maxCommandsForMediaItems = new AtomicInteger();
|
||||
MediaLibrarySession.Callback sessionCallback =
|
||||
new MediaLibrarySession.Callback() {
|
||||
@Override
|
||||
public MediaSession.ConnectionResult onConnect(
|
||||
MediaSession session, ControllerInfo browser) {
|
||||
maxCommandsForMediaItems.set(browser.getMaxCommandsForMediaItems());
|
||||
latch.countDown();
|
||||
return MediaLibrarySession.Callback.super.onConnect(session, browser);
|
||||
}
|
||||
};
|
||||
MockMediaLibraryService service = new MockMediaLibraryService();
|
||||
service.attachBaseContext(context);
|
||||
MediaLibrarySession session =
|
||||
sessionTestRule.ensureReleaseAfterTest(
|
||||
new MediaLibrarySession.Builder(service, player, sessionCallback)
|
||||
.setId("onConnect_withMaxCommandForMediaItems_correctMaxLimitInControllerInfo")
|
||||
.build());
|
||||
Bundle connectionHints = new Bundle();
|
||||
connectionHints.putInt(
|
||||
MediaControllerProviderService.CONNECTION_HINT_KEY_MAX_COMMANDS_FOR_MEDIA_ITEMS, 14);
|
||||
|
||||
controllerTestRule.createRemoteBrowser(session.getToken(), connectionHints);
|
||||
|
||||
assertThat(latch.await(TIMEOUT_MS, MILLISECONDS)).isTrue();
|
||||
assertThat(maxCommandsForMediaItems.get()).isEqualTo(14);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void onSubscribeUnsubscribe() throws Exception {
|
||||
String testParentId = "testSubscribeUnsubscribeId";
|
||||
|
@ -353,6 +353,37 @@ public class MediaSessionCallbackTest {
|
||||
assertThat(remoteController.getSessionExtras().size()).isEqualTo(0);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void onConnect_withMaxCommandsForMediaItems_correctMaxLimitInControllerInfo()
|
||||
throws Exception {
|
||||
CountDownLatch latch = new CountDownLatch(/* count= */ 1);
|
||||
AtomicInteger maxCommandsForMediaItems = new AtomicInteger();
|
||||
MediaSession session =
|
||||
sessionTestRule.ensureReleaseAfterTest(
|
||||
new MediaSession.Builder(context, player)
|
||||
.setCallback(
|
||||
new MediaSession.Callback() {
|
||||
@Override
|
||||
public MediaSession.ConnectionResult onConnect(
|
||||
MediaSession session, ControllerInfo controller) {
|
||||
maxCommandsForMediaItems.set(controller.getMaxCommandsForMediaItems());
|
||||
latch.countDown();
|
||||
return MediaSession.Callback.super.onConnect(session, controller);
|
||||
}
|
||||
})
|
||||
.setId("onConnect_withMaxCommandForMediaItems_correctMaxLimitInControllerInfo")
|
||||
.build());
|
||||
Bundle connectionHints = new Bundle();
|
||||
connectionHints.putInt(
|
||||
MediaControllerProviderService.CONNECTION_HINT_KEY_MAX_COMMANDS_FOR_MEDIA_ITEMS, 2);
|
||||
|
||||
remoteControllerTestRule.createRemoteController(
|
||||
session.getToken(), /* waitForConnection= */ true, connectionHints);
|
||||
|
||||
assertThat(latch.await(TIMEOUT_MS, MILLISECONDS)).isTrue();
|
||||
assertThat(maxCommandsForMediaItems.get()).isEqualTo(2);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void onPostConnect_afterConnected() throws Exception {
|
||||
CountDownLatch latch = new CountDownLatch(1);
|
||||
|
@ -145,7 +145,7 @@ public class MediaSessionServiceTest {
|
||||
// Create the remote controller to start the service.
|
||||
RemoteMediaController controller =
|
||||
controllerTestRule.createRemoteController(
|
||||
token, /* waitForConnection= */ true, /* connectionHints= */ null);
|
||||
token, /* waitForConnection= */ true, /* connectionHints= */ Bundle.EMPTY);
|
||||
// Get the started service instance after creation.
|
||||
MockMediaSessionService service =
|
||||
(MockMediaSessionService) testServiceRegistry.getServiceInstance();
|
||||
@ -221,7 +221,7 @@ public class MediaSessionServiceTest {
|
||||
});
|
||||
RemoteMediaController controller =
|
||||
controllerTestRule.createRemoteController(
|
||||
token, /* waitForConnection= */ true, /* connectionHints= */ null);
|
||||
token, /* waitForConnection= */ true, /* connectionHints= */ Bundle.EMPTY);
|
||||
MockMediaSessionService service =
|
||||
(MockMediaSessionService) testServiceRegistry.getServiceInstance();
|
||||
|
||||
@ -459,9 +459,9 @@ public class MediaSessionServiceTest {
|
||||
});
|
||||
|
||||
RemoteMediaController controller1 =
|
||||
controllerTestRule.createRemoteController(token, true, null);
|
||||
controllerTestRule.createRemoteController(token, true, /* connectionHints= */ Bundle.EMPTY);
|
||||
RemoteMediaController controller2 =
|
||||
controllerTestRule.createRemoteController(token, true, null);
|
||||
controllerTestRule.createRemoteController(token, true, /* connectionHints= */ Bundle.EMPTY);
|
||||
|
||||
assertThat(controller2.getConnectedSessionToken())
|
||||
.isNotEqualTo(controller1.getConnectedSessionToken());
|
||||
@ -504,9 +504,9 @@ public class MediaSessionServiceTest {
|
||||
});
|
||||
|
||||
RemoteMediaController controller1 =
|
||||
controllerTestRule.createRemoteController(token, true, null);
|
||||
controllerTestRule.createRemoteController(token, true, /* connectionHints= */ Bundle.EMPTY);
|
||||
RemoteMediaController controller2 =
|
||||
controllerTestRule.createRemoteController(token, true, null);
|
||||
controllerTestRule.createRemoteController(token, true, /* connectionHints= */ Bundle.EMPTY);
|
||||
controller1.release();
|
||||
controller2.release();
|
||||
|
||||
@ -540,9 +540,9 @@ public class MediaSessionServiceTest {
|
||||
});
|
||||
|
||||
RemoteMediaController controller1 =
|
||||
controllerTestRule.createRemoteController(token, true, null);
|
||||
controllerTestRule.createRemoteController(token, true, /* connectionHints= */ Bundle.EMPTY);
|
||||
RemoteMediaController controller2 =
|
||||
controllerTestRule.createRemoteController(token, true, null);
|
||||
controllerTestRule.createRemoteController(token, true, /* connectionHints= */ Bundle.EMPTY);
|
||||
|
||||
controller1.release();
|
||||
assertThat(latch.await(NO_RESPONSE_TIMEOUT_MS, MILLISECONDS)).isFalse();
|
||||
@ -555,7 +555,7 @@ public class MediaSessionServiceTest {
|
||||
@Test
|
||||
public void getSessions() throws Exception {
|
||||
controllerTestRule.createRemoteController(
|
||||
token, /* waitForConnection= */ true, /* connectionHints= */ null);
|
||||
token, /* waitForConnection= */ true, /* connectionHints= */ Bundle.EMPTY);
|
||||
MediaSessionService service = TestServiceRegistry.getInstance().getServiceInstance();
|
||||
MediaSession session = createMediaSession("testGetSessions");
|
||||
service.addSession(session);
|
||||
@ -572,7 +572,7 @@ public class MediaSessionServiceTest {
|
||||
@Test
|
||||
public void addSessions_removedWhenReleased() throws Exception {
|
||||
controllerTestRule.createRemoteController(
|
||||
token, /* waitForConnection= */ true, /* connectionHints= */ null);
|
||||
token, /* waitForConnection= */ true, /* connectionHints= */ Bundle.EMPTY);
|
||||
MediaSessionService service = TestServiceRegistry.getInstance().getServiceInstance();
|
||||
MediaSession session = createMediaSession("testAddSessions_removedWhenReleased");
|
||||
service.addSession(session);
|
||||
|
@ -76,7 +76,7 @@ public final class RemoteControllerTestRule extends ExternalResource {
|
||||
*/
|
||||
public RemoteMediaController createRemoteController(SessionToken token) throws RemoteException {
|
||||
return createRemoteController(
|
||||
token, /* waitForConnection= */ true, /* connectionHints= */ null);
|
||||
token, /* waitForConnection= */ true, /* connectionHints= */ Bundle.EMPTY);
|
||||
}
|
||||
|
||||
/** Creates {@link RemoteMediaController} from {@link SessionToken}. */
|
||||
|
@ -59,6 +59,10 @@ import java.util.concurrent.TimeoutException;
|
||||
* app's requests.
|
||||
*/
|
||||
public class MediaControllerProviderService extends Service {
|
||||
|
||||
public static final String CONNECTION_HINT_KEY_MAX_COMMANDS_FOR_MEDIA_ITEMS =
|
||||
"CONNECTION_HINT_KEY_MAX_COMMANDS_FOR_MEDIA_ITEMS";
|
||||
|
||||
private static final String TAG = "MCProviderService";
|
||||
|
||||
Map<String, MediaController> mediaControllerMap = new HashMap<>();
|
||||
@ -123,6 +127,13 @@ public class MediaControllerProviderService extends Service {
|
||||
boolean waitForConnection)
|
||||
throws RemoteException {
|
||||
SessionToken token = SessionToken.fromBundle(tokenBundle);
|
||||
// Allow a test to define with what max number of commands per item to connect.
|
||||
int maxCommandsForMediaItems =
|
||||
connectionHints.containsKey(CONNECTION_HINT_KEY_MAX_COMMANDS_FOR_MEDIA_ITEMS)
|
||||
? connectionHints.getInt(
|
||||
CONNECTION_HINT_KEY_MAX_COMMANDS_FOR_MEDIA_ITEMS, /* defaultValue= */ -1)
|
||||
: 0;
|
||||
connectionHints.remove(CONNECTION_HINT_KEY_MAX_COMMANDS_FOR_MEDIA_ITEMS);
|
||||
ListenableFuture<? extends MediaController> controllerFuture =
|
||||
runOnHandler(
|
||||
() -> {
|
||||
@ -132,12 +143,18 @@ public class MediaControllerProviderService extends Service {
|
||||
if (connectionHints != null) {
|
||||
builder.setConnectionHints(connectionHints);
|
||||
}
|
||||
if (maxCommandsForMediaItems >= 0) {
|
||||
builder.setMaxCommandsForMediaItems(maxCommandsForMediaItems);
|
||||
}
|
||||
return builder.buildAsync();
|
||||
} else {
|
||||
MediaController.Builder builder = new MediaController.Builder(context, token);
|
||||
if (connectionHints != null) {
|
||||
builder.setConnectionHints(connectionHints);
|
||||
}
|
||||
if (maxCommandsForMediaItems >= 0) {
|
||||
builder.setMaxCommandsForMediaItems(maxCommandsForMediaItems);
|
||||
}
|
||||
return builder.buildAsync();
|
||||
}
|
||||
});
|
||||
|
@ -393,8 +393,7 @@ public class MockMediaBrowserServiceCompat extends MediaBrowserServiceCompat {
|
||||
String clientPackageName, int clientUid, Bundle rootHints) {
|
||||
int actionLimit =
|
||||
rootHints.getInt(
|
||||
BROWSER_ROOT_HINTS_KEY_CUSTOM_BROWSER_ACTION_LIMIT,
|
||||
/* defaultValue= */ browseActions.size());
|
||||
BROWSER_ROOT_HINTS_KEY_CUSTOM_BROWSER_ACTION_LIMIT, /* defaultValue= */ 0);
|
||||
Bundle extras = new Bundle(rootHints);
|
||||
ArrayList<Bundle> browseActionList = new ArrayList<>();
|
||||
for (int i = 0; i < min(actionLimit, browseActions.size()); i++) {
|
||||
|
@ -62,6 +62,7 @@ import static androidx.media3.test.session.common.MediaBrowserConstants.SEARCH_R
|
||||
import static androidx.media3.test.session.common.MediaBrowserConstants.SEARCH_TIME_IN_MS;
|
||||
import static androidx.media3.test.session.common.MediaBrowserConstants.SUBSCRIBE_PARENT_ID_1;
|
||||
import static androidx.media3.test.session.common.MediaBrowserConstants.SUBSCRIBE_PARENT_ID_2;
|
||||
import static java.lang.Math.min;
|
||||
import static java.util.concurrent.TimeUnit.MILLISECONDS;
|
||||
|
||||
import android.app.PendingIntent;
|
||||
@ -360,7 +361,9 @@ public class MockMediaLibraryService extends MediaLibraryService {
|
||||
case MEDIA_ID_GET_ITEM_WITH_BROWSE_ACTIONS:
|
||||
return Futures.immediateFuture(
|
||||
LibraryResult.ofItem(
|
||||
createPlayableMediaItemWithBrowseActions(mediaId), /* params= */ null));
|
||||
createPlayableMediaItemWithCommands(
|
||||
mediaId, browser.getMaxCommandsForMediaItems()),
|
||||
/* params= */ null));
|
||||
case MEDIA_ID_GET_ITEM_WITH_METADATA:
|
||||
return Futures.immediateFuture(
|
||||
LibraryResult.ofItem(createMediaItemWithMetadata(mediaId), /* params= */ null));
|
||||
@ -575,7 +578,7 @@ public class MockMediaLibraryService extends MediaLibraryService {
|
||||
|
||||
int totalItemCount = items.size();
|
||||
int fromIndex = page * pageSize;
|
||||
int toIndex = Math.min((page + 1) * pageSize, totalItemCount);
|
||||
int toIndex = min((page + 1) * pageSize, totalItemCount);
|
||||
|
||||
List<String> paginatedMediaIdList = new ArrayList<>();
|
||||
try {
|
||||
@ -626,17 +629,18 @@ public class MockMediaLibraryService extends MediaLibraryService {
|
||||
return mediaItem.buildUpon().setMediaMetadata(mediaMetadataWithArtwork).build();
|
||||
}
|
||||
|
||||
private MediaItem createPlayableMediaItemWithBrowseActions(String mediaId) {
|
||||
private MediaItem createPlayableMediaItemWithCommands(
|
||||
String mediaId, int maxCommandsForMediaItems) {
|
||||
MediaItem mediaItem = createPlayableMediaItem(mediaId);
|
||||
ImmutableList<String> allCommands =
|
||||
ImmutableList.of(
|
||||
MediaBrowserConstants.COMMAND_PLAYLIST_ADD, MediaBrowserConstants.COMMAND_RADIO);
|
||||
ImmutableList.Builder<String> supportedCommands = new ImmutableList.Builder<>();
|
||||
for (int i = 0; i < min(maxCommandsForMediaItems, allCommands.size()); i++) {
|
||||
supportedCommands.add(allCommands.get(i));
|
||||
}
|
||||
MediaMetadata mediaMetadataWithBrowseActions =
|
||||
mediaItem
|
||||
.mediaMetadata
|
||||
.buildUpon()
|
||||
.setSupportedCommands(
|
||||
ImmutableList.of(
|
||||
MediaBrowserConstants.COMMAND_PLAYLIST_ADD,
|
||||
MediaBrowserConstants.COMMAND_RADIO))
|
||||
.build();
|
||||
mediaItem.mediaMetadata.buildUpon().setSupportedCommands(supportedCommands.build()).build();
|
||||
return mediaItem.buildUpon().setMediaMetadata(mediaMetadataWithBrowseActions).build();
|
||||
}
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user