Refine auto-update logic of CommandButton.isEnabled
We currently update this value for controllers to match the availability of the associated command. This however makes it impossible to mark a button as unavailable if the command is available. This can be refined by only setting the 'enabled' field to false if the command is not available, not the other way round. And we should also enable the button by default as disabling is the unusual case not many apps will use. In addition, this change fixes missing update logic when the player commands changed and it adds some additional test coverage for all these cases. PiperOrigin-RevId: 612881016
This commit is contained in:
parent
8b59888766
commit
9abe9e2a97
@ -58,6 +58,9 @@
|
||||
* Fix issue where the current position jumps back when the controller
|
||||
replaces the current item
|
||||
([#951](https://github.com/androidx/media/issues/951)).
|
||||
* Change default of `CommandButton.enabled` to `true` and ensure the value
|
||||
can stay false for controllers even if the associated command is
|
||||
available.
|
||||
* UI:
|
||||
* Fallback to include audio track language name if `Locale` cannot
|
||||
identify a display name
|
||||
|
@ -412,6 +412,7 @@ public final class CommandButton implements Bundleable {
|
||||
extras = Bundle.EMPTY;
|
||||
playerCommand = Player.COMMAND_INVALID;
|
||||
icon = ICON_UNDEFINED;
|
||||
enabled = true;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -507,6 +508,12 @@ public final class CommandButton implements Bundleable {
|
||||
/**
|
||||
* Sets whether the button is enabled.
|
||||
*
|
||||
* <p>Note that this value will be set to {@code false} for {@link MediaController} instances if
|
||||
* the corresponding command is not available to this controller (see {@link #setPlayerCommand}
|
||||
* and {@link #setSessionCommand}).
|
||||
*
|
||||
* <p>The default value is {@code true}.
|
||||
*
|
||||
* @param enabled Whether the button is enabled.
|
||||
* @return This builder for chaining.
|
||||
*/
|
||||
@ -571,7 +578,13 @@ public final class CommandButton implements Bundleable {
|
||||
*/
|
||||
@UnstableApi public final Bundle extras;
|
||||
|
||||
/** Whether it's enabled. */
|
||||
/**
|
||||
* Whether the button is enabled.
|
||||
*
|
||||
* <p>Note that this value will be set to {@code false} for {@link MediaController} instances if
|
||||
* the corresponding command is not available to this controller (see {@link #playerCommand} and
|
||||
* {@link #sessionCommand}).
|
||||
*/
|
||||
public final boolean isEnabled;
|
||||
|
||||
private CommandButton(
|
||||
@ -639,31 +652,35 @@ public final class CommandButton implements Bundleable {
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a list of command buttons with the {@link CommandButton#isEnabled} flag set according
|
||||
* to the available commands passed in.
|
||||
* Returns a list of command buttons with the {@link CommandButton#isEnabled} flag set to false if
|
||||
* the corresponding command is not available.
|
||||
*/
|
||||
/* package */ static ImmutableList<CommandButton> getEnabledCommandButtons(
|
||||
/* package */ static ImmutableList<CommandButton> copyWithUnavailableButtonsDisabled(
|
||||
List<CommandButton> commandButtons,
|
||||
SessionCommands sessionCommands,
|
||||
Player.Commands playerCommands) {
|
||||
ImmutableList.Builder<CommandButton> enabledButtons = new ImmutableList.Builder<>();
|
||||
ImmutableList.Builder<CommandButton> updatedButtons = new ImmutableList.Builder<>();
|
||||
for (int i = 0; i < commandButtons.size(); i++) {
|
||||
CommandButton button = commandButtons.get(i);
|
||||
enabledButtons.add(
|
||||
button.copyWithIsEnabled(isEnabled(button, sessionCommands, playerCommands)));
|
||||
if (isButtonCommandAvailable(button, sessionCommands, playerCommands)) {
|
||||
updatedButtons.add(button);
|
||||
} else {
|
||||
updatedButtons.add(button.copyWithIsEnabled(false));
|
||||
}
|
||||
}
|
||||
return enabledButtons.build();
|
||||
return updatedButtons.build();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether the {@link CommandButton} is enabled given the available commands passed in.
|
||||
* Returns whether the required command ({@link #playerCommand} or {@link #sessionCommand}) for
|
||||
* the button is available.
|
||||
*
|
||||
* @param button The command button.
|
||||
* @param sessionCommands The available session commands.
|
||||
* @param playerCommands The available player commands.
|
||||
* @return Whether the button is enabled given the available commands.
|
||||
* @return Whether the command required for this button is available.
|
||||
*/
|
||||
/* package */ static boolean isEnabled(
|
||||
/* package */ static boolean isButtonCommandAvailable(
|
||||
CommandButton button, SessionCommands sessionCommands, Player.Commands playerCommands) {
|
||||
return (button.sessionCommand != null && sessionCommands.contains(button.sessionCommand))
|
||||
|| (button.playerCommand != Player.COMMAND_INVALID
|
||||
@ -706,7 +723,7 @@ public final class CommandButton implements Bundleable {
|
||||
if (iconUri != null) {
|
||||
bundle.putParcelable(FIELD_ICON_URI, iconUri);
|
||||
}
|
||||
if (isEnabled) {
|
||||
if (!isEnabled) {
|
||||
bundle.putBoolean(FIELD_ENABLED, isEnabled);
|
||||
}
|
||||
return bundle;
|
||||
@ -722,9 +739,18 @@ public final class CommandButton implements Bundleable {
|
||||
@SuppressWarnings("deprecation") // Deprecated instance of deprecated class
|
||||
public static final Creator<CommandButton> CREATOR = CommandButton::fromBundle;
|
||||
|
||||
/** Restores a {@code CommandButton} from a {@link Bundle}. */
|
||||
/**
|
||||
* @deprecated Use {@link #fromBundle(Bundle, int)} instead.
|
||||
*/
|
||||
@Deprecated
|
||||
@UnstableApi
|
||||
public static CommandButton fromBundle(Bundle bundle) {
|
||||
return fromBundle(bundle, MediaSessionStub.VERSION_INT);
|
||||
}
|
||||
|
||||
/** Restores a {@code CommandButton} from a {@link Bundle}. */
|
||||
@UnstableApi
|
||||
public static CommandButton fromBundle(Bundle bundle, int sessionInterfaceVersion) {
|
||||
@Nullable Bundle sessionCommandBundle = bundle.getBundle(FIELD_SESSION_COMMAND);
|
||||
@Nullable
|
||||
SessionCommand sessionCommand =
|
||||
@ -735,7 +761,10 @@ public final class CommandButton implements Bundleable {
|
||||
int iconResId = bundle.getInt(FIELD_ICON_RES_ID, /* defaultValue= */ 0);
|
||||
CharSequence displayName = bundle.getCharSequence(FIELD_DISPLAY_NAME, /* defaultValue= */ "");
|
||||
@Nullable Bundle extras = bundle.getBundle(FIELD_EXTRAS);
|
||||
boolean enabled = bundle.getBoolean(FIELD_ENABLED, /* defaultValue= */ false);
|
||||
// Before sessionInterfaceVersion == 3, the session expected this value to be meaningless and we
|
||||
// can only assume it was meant to be true.
|
||||
boolean enabled =
|
||||
sessionInterfaceVersion < 3 || bundle.getBoolean(FIELD_ENABLED, /* defaultValue= */ true);
|
||||
@Nullable Uri iconUri = bundle.getParcelable(FIELD_ICON_URI);
|
||||
@Icon int icon = bundle.getInt(FIELD_ICON, /* defaultValue= */ ICON_UNDEFINED);
|
||||
Builder builder = new Builder();
|
||||
|
@ -164,7 +164,8 @@ import java.util.List;
|
||||
List<Bundle> commandButtonArrayList = bundle.getParcelableArrayList(FIELD_CUSTOM_LAYOUT);
|
||||
ImmutableList<CommandButton> customLayout =
|
||||
commandButtonArrayList != null
|
||||
? BundleCollectionUtil.fromBundleList(CommandButton::fromBundle, commandButtonArrayList)
|
||||
? BundleCollectionUtil.fromBundleList(
|
||||
b -> CommandButton.fromBundle(b, sessionInterfaceVersion), commandButtonArrayList)
|
||||
: ImmutableList.of();
|
||||
@Nullable Bundle sessionCommandsBundle = bundle.getBundle(FIELD_SESSION_COMMANDS);
|
||||
SessionCommands sessionCommands =
|
||||
|
@ -388,6 +388,9 @@ public class MediaController implements Player {
|
||||
* changes the available commands} for a controller that affect whether buttons of the custom
|
||||
* layout are enabled or disabled.
|
||||
*
|
||||
* <p>Note that the {@linkplain CommandButton#isEnabled enabled} flag is set to {@code false} if
|
||||
* the available commands do not allow to use a button.
|
||||
*
|
||||
* @param controller The controller.
|
||||
* @param layout The ordered list of {@linkplain CommandButton command buttons}.
|
||||
*/
|
||||
@ -976,6 +979,9 @@ public class MediaController implements Player {
|
||||
* <p>After being connected, a change of the custom layout is reported with {@link
|
||||
* Listener#onCustomLayoutChanged(MediaController, List)}.
|
||||
*
|
||||
* <p>Note that the {@linkplain CommandButton#isEnabled enabled} flag is set to {@code false} if
|
||||
* the available commands do not allow to use a button.
|
||||
*
|
||||
* @return The custom layout.
|
||||
*/
|
||||
@UnstableApi
|
||||
|
@ -120,7 +120,8 @@ import org.checkerframework.checker.nullness.qual.NonNull;
|
||||
private boolean released;
|
||||
private PlayerInfo playerInfo;
|
||||
@Nullable private PendingIntent sessionActivity;
|
||||
private ImmutableList<CommandButton> customLayout;
|
||||
private ImmutableList<CommandButton> customLayoutOriginal;
|
||||
private ImmutableList<CommandButton> customLayoutWithUnavailableButtonsDisabled;
|
||||
private SessionCommands sessionCommands;
|
||||
private Commands playerCommandsFromSession;
|
||||
private Commands playerCommandsFromPlayer;
|
||||
@ -146,7 +147,8 @@ import org.checkerframework.checker.nullness.qual.NonNull;
|
||||
playerInfo = PlayerInfo.DEFAULT;
|
||||
surfaceSize = Size.UNKNOWN;
|
||||
sessionCommands = SessionCommands.EMPTY;
|
||||
customLayout = ImmutableList.of();
|
||||
customLayoutOriginal = ImmutableList.of();
|
||||
customLayoutWithUnavailableButtonsDisabled = ImmutableList.of();
|
||||
playerCommandsFromSession = Commands.EMPTY;
|
||||
playerCommandsFromPlayer = Commands.EMPTY;
|
||||
intersectedPlayerCommands =
|
||||
@ -726,7 +728,7 @@ import org.checkerframework.checker.nullness.qual.NonNull;
|
||||
|
||||
@Override
|
||||
public ImmutableList<CommandButton> getCustomLayout() {
|
||||
return customLayout;
|
||||
return customLayoutWithUnavailableButtonsDisabled;
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -2611,8 +2613,9 @@ import org.checkerframework.checker.nullness.qual.NonNull;
|
||||
intersectedPlayerCommands =
|
||||
createIntersectedCommandsEnsuringCommandReleaseAvailable(
|
||||
playerCommandsFromSession, playerCommandsFromPlayer);
|
||||
customLayout =
|
||||
CommandButton.getEnabledCommandButtons(
|
||||
customLayoutOriginal = result.customLayout;
|
||||
customLayoutWithUnavailableButtonsDisabled =
|
||||
CommandButton.copyWithUnavailableButtonsDisabled(
|
||||
result.customLayout, sessionCommands, intersectedPlayerCommands);
|
||||
playerInfo = result.playerInfo;
|
||||
try {
|
||||
@ -2765,6 +2768,7 @@ import org.checkerframework.checker.nullness.qual.NonNull;
|
||||
if (!playerCommandsChanged && !sessionCommandsChanged) {
|
||||
return;
|
||||
}
|
||||
this.sessionCommands = sessionCommands;
|
||||
boolean intersectedPlayerCommandsChanged = false;
|
||||
if (playerCommandsChanged) {
|
||||
playerCommandsFromSession = playerCommands;
|
||||
@ -2776,13 +2780,12 @@ import org.checkerframework.checker.nullness.qual.NonNull;
|
||||
!Util.areEqual(intersectedPlayerCommands, prevIntersectedPlayerCommands);
|
||||
}
|
||||
boolean customLayoutChanged = false;
|
||||
if (sessionCommandsChanged) {
|
||||
this.sessionCommands = sessionCommands;
|
||||
ImmutableList<CommandButton> oldCustomLayout = customLayout;
|
||||
customLayout =
|
||||
CommandButton.getEnabledCommandButtons(
|
||||
customLayout, sessionCommands, intersectedPlayerCommands);
|
||||
customLayoutChanged = !customLayout.equals(oldCustomLayout);
|
||||
if (sessionCommandsChanged || intersectedPlayerCommandsChanged) {
|
||||
ImmutableList<CommandButton> oldCustomLayout = customLayoutWithUnavailableButtonsDisabled;
|
||||
customLayoutWithUnavailableButtonsDisabled =
|
||||
CommandButton.copyWithUnavailableButtonsDisabled(
|
||||
customLayoutOriginal, sessionCommands, intersectedPlayerCommands);
|
||||
customLayoutChanged = !customLayoutWithUnavailableButtonsDisabled.equals(oldCustomLayout);
|
||||
}
|
||||
if (intersectedPlayerCommandsChanged) {
|
||||
listeners.sendEvent(
|
||||
@ -2798,7 +2801,9 @@ import org.checkerframework.checker.nullness.qual.NonNull;
|
||||
if (customLayoutChanged) {
|
||||
getInstance()
|
||||
.notifyControllerListener(
|
||||
listener -> listener.onCustomLayoutChanged(getInstance(), customLayout));
|
||||
listener ->
|
||||
listener.onCustomLayoutChanged(
|
||||
getInstance(), customLayoutWithUnavailableButtonsDisabled));
|
||||
}
|
||||
}
|
||||
|
||||
@ -2816,11 +2821,24 @@ import org.checkerframework.checker.nullness.qual.NonNull;
|
||||
playerCommandsFromSession, playerCommandsFromPlayer);
|
||||
boolean intersectedPlayerCommandsChanged =
|
||||
!Util.areEqual(intersectedPlayerCommands, prevIntersectedPlayerCommands);
|
||||
boolean customLayoutChanged = false;
|
||||
if (intersectedPlayerCommandsChanged) {
|
||||
ImmutableList<CommandButton> oldCustomLayout = customLayoutWithUnavailableButtonsDisabled;
|
||||
customLayoutWithUnavailableButtonsDisabled =
|
||||
CommandButton.copyWithUnavailableButtonsDisabled(
|
||||
customLayoutOriginal, sessionCommands, intersectedPlayerCommands);
|
||||
customLayoutChanged = !customLayoutWithUnavailableButtonsDisabled.equals(oldCustomLayout);
|
||||
listeners.sendEvent(
|
||||
/* eventFlag= */ Player.EVENT_AVAILABLE_COMMANDS_CHANGED,
|
||||
listener -> listener.onAvailableCommandsChanged(intersectedPlayerCommands));
|
||||
}
|
||||
if (customLayoutChanged) {
|
||||
getInstance()
|
||||
.notifyControllerListener(
|
||||
listener ->
|
||||
listener.onCustomLayoutChanged(
|
||||
getInstance(), customLayoutWithUnavailableButtonsDisabled));
|
||||
}
|
||||
}
|
||||
|
||||
// Calling deprecated listener callback method for backwards compatibility.
|
||||
@ -2829,19 +2847,24 @@ import org.checkerframework.checker.nullness.qual.NonNull;
|
||||
if (!isConnected()) {
|
||||
return;
|
||||
}
|
||||
ImmutableList<CommandButton> oldCustomLayout = customLayout;
|
||||
customLayout =
|
||||
CommandButton.getEnabledCommandButtons(layout, sessionCommands, intersectedPlayerCommands);
|
||||
boolean hasCustomLayoutChanged = !Objects.equals(customLayout, oldCustomLayout);
|
||||
ImmutableList<CommandButton> oldCustomLayout = customLayoutWithUnavailableButtonsDisabled;
|
||||
customLayoutOriginal = ImmutableList.copyOf(layout);
|
||||
customLayoutWithUnavailableButtonsDisabled =
|
||||
CommandButton.copyWithUnavailableButtonsDisabled(
|
||||
layout, sessionCommands, intersectedPlayerCommands);
|
||||
boolean hasCustomLayoutChanged =
|
||||
!Objects.equals(customLayoutWithUnavailableButtonsDisabled, oldCustomLayout);
|
||||
getInstance()
|
||||
.notifyControllerListener(
|
||||
listener -> {
|
||||
ListenableFuture<SessionResult> future =
|
||||
checkNotNull(
|
||||
listener.onSetCustomLayout(getInstance(), customLayout),
|
||||
listener.onSetCustomLayout(
|
||||
getInstance(), customLayoutWithUnavailableButtonsDisabled),
|
||||
"MediaController.Listener#onSetCustomLayout() must not return null");
|
||||
if (hasCustomLayoutChanged) {
|
||||
listener.onCustomLayoutChanged(getInstance(), customLayout);
|
||||
listener.onCustomLayoutChanged(
|
||||
getInstance(), customLayoutWithUnavailableButtonsDisabled);
|
||||
}
|
||||
sendControllerResultWhenReady(seq, future);
|
||||
});
|
||||
|
@ -112,8 +112,18 @@ import org.checkerframework.checker.nullness.qual.NonNull;
|
||||
}
|
||||
List<CommandButton> layout;
|
||||
try {
|
||||
@Nullable MediaControllerImplBase controller = this.controller.get();
|
||||
@Nullable
|
||||
SessionToken connectedToken = controller == null ? null : controller.getConnectedToken();
|
||||
if (connectedToken == null) {
|
||||
// Stale event.
|
||||
return;
|
||||
}
|
||||
int sessionInterfaceVersion = connectedToken.getInterfaceVersion();
|
||||
layout =
|
||||
BundleCollectionUtil.fromBundleList(CommandButton::fromBundle, commandButtonBundleList);
|
||||
BundleCollectionUtil.fromBundleList(
|
||||
bundle -> CommandButton.fromBundle(bundle, sessionInterfaceVersion),
|
||||
commandButtonBundleList);
|
||||
} catch (RuntimeException e) {
|
||||
Log.w(TAG, "Ignoring malformed Bundle for CommandButton", e);
|
||||
return;
|
||||
|
@ -388,6 +388,9 @@ public class MediaSession {
|
||||
* <p>Use {@code MediaSession.setCustomLayout(..)} to update the custom layout during the life
|
||||
* time of the session.
|
||||
*
|
||||
* <p>On the controller side, the {@linkplain CommandButton#isEnabled enabled} flag is set to
|
||||
* {@code false} if the available commands of a controller do not allow to use a button.
|
||||
*
|
||||
* @param customLayout The ordered list of {@link CommandButton command buttons}.
|
||||
* @return The builder to allow chaining.
|
||||
*/
|
||||
@ -911,7 +914,8 @@ public class MediaSession {
|
||||
* MediaController#getCustomLayout() controller already has available}. Note that this comparison
|
||||
* uses {@link CommandButton#equals} and therefore ignores {@link CommandButton#extras}.
|
||||
*
|
||||
* <p>It's up to controller's decision how to represent the layout in its own UI.
|
||||
* <p>On the controller side, the {@linkplain CommandButton#isEnabled enabled} flag is set to
|
||||
* {@code false} if the available commands of the controller do not allow to use a button.
|
||||
*
|
||||
* <p>Interoperability: This call has no effect when called for a {@linkplain
|
||||
* ControllerInfo#LEGACY_CONTROLLER_VERSION legacy controller}.
|
||||
@ -933,9 +937,8 @@ public class MediaSession {
|
||||
* <p>Calling this method broadcasts the custom layout to all connected Media3 controllers,
|
||||
* including the {@linkplain #getMediaNotificationControllerInfo() media notification controller}.
|
||||
*
|
||||
* <p>On the controller side, the {@linkplain CommandButton#isEnabled enabled} flag is set
|
||||
* according to the available commands of the controller which overrides a value that has been set
|
||||
* by the session.
|
||||
* <p>On the controller side, the {@linkplain CommandButton#isEnabled enabled} flag is set to
|
||||
* {@code false} if the available commands of a controller do not allow to use a button.
|
||||
*
|
||||
* <p>{@link MediaController.Listener#onCustomLayoutChanged(MediaController, List)} is only called
|
||||
* if the new custom layout is different to the custom layout the {@linkplain
|
||||
@ -1653,7 +1656,9 @@ public class MediaSession {
|
||||
*
|
||||
* <p>Make sure to have the session commands of all command buttons of the custom layout
|
||||
* included in the {@linkplain #setAvailableSessionCommands(SessionCommands)} available
|
||||
* session commands}.
|
||||
* session commands} On the controller side, the {@linkplain CommandButton#isEnabled enabled}
|
||||
* flag is set to {@code false} if the available commands of the controller do not allow to
|
||||
* use a button.
|
||||
*/
|
||||
@CanIgnoreReturnValue
|
||||
public AcceptedResultBuilder setCustomLayout(@Nullable List<CommandButton> customLayout) {
|
||||
|
@ -113,7 +113,7 @@ import java.util.concurrent.ExecutionException;
|
||||
private static final String TAG = "MediaSessionStub";
|
||||
|
||||
/** The version of the IMediaSession interface. */
|
||||
public static final int VERSION_INT = 2;
|
||||
public static final int VERSION_INT = 3;
|
||||
|
||||
/**
|
||||
* Sequence number used when a controller method is triggered on the sesison side that wasn't
|
||||
|
@ -1043,25 +1043,23 @@ import java.util.List;
|
||||
|
||||
for (int i = 0; i < customLayout.size(); i++) {
|
||||
CommandButton commandButton = customLayout.get(i);
|
||||
if (commandButton.sessionCommand != null) {
|
||||
SessionCommand sessionCommand = commandButton.sessionCommand;
|
||||
if (sessionCommand.commandCode == SessionCommand.COMMAND_CODE_CUSTOM
|
||||
&& CommandButton.isEnabled(
|
||||
commandButton, availableSessionCommands, availablePlayerCommands)) {
|
||||
Bundle actionExtras = sessionCommand.customExtras;
|
||||
if (commandButton.icon != CommandButton.ICON_UNDEFINED) {
|
||||
actionExtras = new Bundle(sessionCommand.customExtras);
|
||||
actionExtras.putInt(
|
||||
MediaConstants.EXTRAS_KEY_COMMAND_BUTTON_ICON_COMPAT, commandButton.icon);
|
||||
}
|
||||
builder.addCustomAction(
|
||||
new PlaybackStateCompat.CustomAction.Builder(
|
||||
sessionCommand.customAction,
|
||||
commandButton.displayName,
|
||||
commandButton.iconResId)
|
||||
.setExtras(actionExtras)
|
||||
.build());
|
||||
SessionCommand sessionCommand = commandButton.sessionCommand;
|
||||
if (sessionCommand != null
|
||||
&& commandButton.isEnabled
|
||||
&& sessionCommand.commandCode == SessionCommand.COMMAND_CODE_CUSTOM
|
||||
&& CommandButton.isButtonCommandAvailable(
|
||||
commandButton, availableSessionCommands, availablePlayerCommands)) {
|
||||
Bundle actionExtras = sessionCommand.customExtras;
|
||||
if (commandButton.icon != CommandButton.ICON_UNDEFINED) {
|
||||
actionExtras = new Bundle(sessionCommand.customExtras);
|
||||
actionExtras.putInt(
|
||||
MediaConstants.EXTRAS_KEY_COMMAND_BUTTON_ICON_COMPAT, commandButton.icon);
|
||||
}
|
||||
builder.addCustomAction(
|
||||
new PlaybackStateCompat.CustomAction.Builder(
|
||||
sessionCommand.customAction, commandButton.displayName, commandButton.iconResId)
|
||||
.setExtras(actionExtras)
|
||||
.build());
|
||||
}
|
||||
}
|
||||
if (playerError != null) {
|
||||
|
@ -31,7 +31,8 @@ import org.junit.runner.RunWith;
|
||||
public class CommandButtonTest {
|
||||
|
||||
@Test
|
||||
public void isEnabled_playerCommandAvailableOrUnavailableInPlayerCommands_isEnabledCorrectly() {
|
||||
public void
|
||||
isButtonCommandAvailable_playerCommandAvailableOrUnavailableInPlayerCommands_isEnabledCorrectly() {
|
||||
CommandButton button =
|
||||
new CommandButton.Builder()
|
||||
.setDisplayName("button")
|
||||
@ -41,14 +42,18 @@ public class CommandButtonTest {
|
||||
Player.Commands availablePlayerCommands =
|
||||
Player.Commands.EMPTY.buildUpon().add(Player.COMMAND_SEEK_TO_NEXT).build();
|
||||
|
||||
assertThat(CommandButton.isEnabled(button, SessionCommands.EMPTY, Player.Commands.EMPTY))
|
||||
assertThat(
|
||||
CommandButton.isButtonCommandAvailable(
|
||||
button, SessionCommands.EMPTY, Player.Commands.EMPTY))
|
||||
.isFalse();
|
||||
assertThat(CommandButton.isEnabled(button, SessionCommands.EMPTY, availablePlayerCommands))
|
||||
assertThat(
|
||||
CommandButton.isButtonCommandAvailable(
|
||||
button, SessionCommands.EMPTY, availablePlayerCommands))
|
||||
.isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void isEnabled_sessionCommandAvailableOrUnavailable_isEnabledCorrectly() {
|
||||
public void isButtonCommandAvailable_sessionCommandAvailableOrUnavailable_isEnabledCorrectly() {
|
||||
SessionCommand command1 = new SessionCommand("command1", Bundle.EMPTY);
|
||||
CommandButton button =
|
||||
new CommandButton.Builder()
|
||||
@ -59,14 +64,18 @@ public class CommandButtonTest {
|
||||
SessionCommands availableSessionCommands =
|
||||
SessionCommands.EMPTY.buildUpon().add(command1).build();
|
||||
|
||||
assertThat(CommandButton.isEnabled(button, SessionCommands.EMPTY, Player.Commands.EMPTY))
|
||||
assertThat(
|
||||
CommandButton.isButtonCommandAvailable(
|
||||
button, SessionCommands.EMPTY, Player.Commands.EMPTY))
|
||||
.isFalse();
|
||||
assertThat(CommandButton.isEnabled(button, availableSessionCommands, Player.Commands.EMPTY))
|
||||
assertThat(
|
||||
CommandButton.isButtonCommandAvailable(
|
||||
button, availableSessionCommands, Player.Commands.EMPTY))
|
||||
.isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getEnabledCommandButtons() {
|
||||
public void copyWithUnavailableButtonsDisabled() {
|
||||
CommandButton button1 =
|
||||
new CommandButton.Builder()
|
||||
.setDisplayName("button1")
|
||||
@ -86,11 +95,11 @@ public class CommandButtonTest {
|
||||
Player.Commands.EMPTY.buildUpon().add(Player.COMMAND_SEEK_TO_PREVIOUS).build();
|
||||
|
||||
assertThat(
|
||||
CommandButton.getEnabledCommandButtons(
|
||||
CommandButton.copyWithUnavailableButtonsDisabled(
|
||||
ImmutableList.of(button1, button2), SessionCommands.EMPTY, Player.Commands.EMPTY))
|
||||
.containsExactly(button1, button2);
|
||||
.containsExactly(button1.copyWithIsEnabled(false), button2.copyWithIsEnabled(false));
|
||||
assertThat(
|
||||
CommandButton.getEnabledCommandButtons(
|
||||
CommandButton.copyWithUnavailableButtonsDisabled(
|
||||
ImmutableList.of(button1, button2),
|
||||
availableSessionCommands,
|
||||
availablePlayerCommands))
|
||||
@ -134,7 +143,8 @@ public class CommandButtonTest {
|
||||
.setPlayerCommand(Player.COMMAND_SEEK_TO_PREVIOUS)
|
||||
.build();
|
||||
|
||||
CommandButton serialisedButton = CommandButton.fromBundle(button.toBundle());
|
||||
CommandButton serialisedButton =
|
||||
CommandButton.fromBundle(button.toBundle(), MediaSessionStub.VERSION_INT);
|
||||
|
||||
assertThat(serialisedButton.iconUri).isEqualTo(uri);
|
||||
}
|
||||
@ -148,7 +158,8 @@ public class CommandButtonTest {
|
||||
.setPlayerCommand(Player.COMMAND_SEEK_TO_PREVIOUS)
|
||||
.build();
|
||||
|
||||
CommandButton serialisedButton = CommandButton.fromBundle(button.toBundle());
|
||||
CommandButton serialisedButton =
|
||||
CommandButton.fromBundle(button.toBundle(), MediaSessionStub.VERSION_INT);
|
||||
|
||||
assertThat(serialisedButton.iconUri).isNull();
|
||||
}
|
||||
@ -180,7 +191,8 @@ public class CommandButtonTest {
|
||||
.setPlayerCommand(Player.COMMAND_SEEK_TO_NEXT)
|
||||
.build();
|
||||
|
||||
assertThat(button).isEqualTo(CommandButton.fromBundle(button.toBundle()));
|
||||
assertThat(button)
|
||||
.isEqualTo(CommandButton.fromBundle(button.toBundle(), MediaSessionStub.VERSION_INT));
|
||||
assertThat(button)
|
||||
.isNotEqualTo(
|
||||
new CommandButton.Builder()
|
||||
@ -205,7 +217,7 @@ public class CommandButtonTest {
|
||||
assertThat(button)
|
||||
.isNotEqualTo(
|
||||
new CommandButton.Builder()
|
||||
.setEnabled(true)
|
||||
.setEnabled(false)
|
||||
.setDisplayName("button")
|
||||
.setIconResId(R.drawable.media3_notification_small_icon)
|
||||
.setPlayerCommand(Player.COMMAND_SEEK_TO_NEXT)
|
||||
@ -380,11 +392,11 @@ public class CommandButtonTest {
|
||||
new CommandButton.Builder().setPlayerCommand(Player.COMMAND_PLAY_PAUSE).build();
|
||||
|
||||
CommandButton restoredButtonWithSessionCommand =
|
||||
CommandButton.fromBundle(buttonWithSessionCommand.toBundle());
|
||||
CommandButton.fromBundle(buttonWithSessionCommand.toBundle(), MediaSessionStub.VERSION_INT);
|
||||
CommandButton restoredButtonWithPlayerCommand =
|
||||
CommandButton.fromBundle(buttonWithPlayerCommand.toBundle());
|
||||
CommandButton.fromBundle(buttonWithPlayerCommand.toBundle(), MediaSessionStub.VERSION_INT);
|
||||
CommandButton restoredButtonWithDefaultValues =
|
||||
CommandButton.fromBundle(buttonWithDefaultValues.toBundle());
|
||||
CommandButton.fromBundle(buttonWithDefaultValues.toBundle(), MediaSessionStub.VERSION_INT);
|
||||
|
||||
assertThat(restoredButtonWithSessionCommand).isEqualTo(buttonWithSessionCommand);
|
||||
assertThat(restoredButtonWithSessionCommand.extras.get("key")).isEqualTo("value");
|
||||
@ -392,4 +404,19 @@ public class CommandButtonTest {
|
||||
assertThat(restoredButtonWithPlayerCommand.extras.get("key")).isEqualTo("value");
|
||||
assertThat(restoredButtonWithDefaultValues).isEqualTo(buttonWithDefaultValues);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void fromBundle_withSessionInterfaceVersionLessThan3_setsEnabledToTrue() {
|
||||
CommandButton buttonWithEnabledFalse =
|
||||
new CommandButton.Builder()
|
||||
.setEnabled(false)
|
||||
.setSessionCommand(new SessionCommand(SessionCommand.COMMAND_CODE_SESSION_SET_RATING))
|
||||
.build();
|
||||
|
||||
CommandButton restoredButtonAssumingOldSessionInterface =
|
||||
CommandButton.fromBundle(
|
||||
buttonWithEnabledFalse.toBundle(), /* sessionInterfaceVersion= */ 2);
|
||||
|
||||
assertThat(restoredButtonAssumingOldSessionInterface.isEnabled).isTrue();
|
||||
}
|
||||
}
|
||||
|
@ -158,6 +158,8 @@ public class MediaSessionServiceTest {
|
||||
throws TimeoutException {
|
||||
SessionCommand command1 = new SessionCommand("command1", Bundle.EMPTY);
|
||||
SessionCommand command2 = new SessionCommand("command2", Bundle.EMPTY);
|
||||
SessionCommand command3 = new SessionCommand("command3", Bundle.EMPTY);
|
||||
SessionCommand command4 = new SessionCommand("command4", Bundle.EMPTY);
|
||||
CommandButton button1 =
|
||||
new CommandButton.Builder()
|
||||
.setDisplayName("customAction1")
|
||||
@ -170,10 +172,23 @@ public class MediaSessionServiceTest {
|
||||
.setIconResId(R.drawable.media3_notification_small_icon)
|
||||
.setSessionCommand(command2)
|
||||
.build();
|
||||
CommandButton button3 =
|
||||
new CommandButton.Builder()
|
||||
.setDisplayName("customAction3")
|
||||
.setEnabled(false)
|
||||
.setIconResId(R.drawable.media3_notification_small_icon)
|
||||
.setSessionCommand(command3)
|
||||
.build();
|
||||
CommandButton button4 =
|
||||
new CommandButton.Builder()
|
||||
.setDisplayName("customAction4")
|
||||
.setIconResId(R.drawable.media3_notification_small_icon)
|
||||
.setSessionCommand(command4)
|
||||
.build();
|
||||
ExoPlayer player = new TestExoPlayerBuilder(context).build();
|
||||
MediaSession session =
|
||||
new MediaSession.Builder(context, player)
|
||||
.setCustomLayout(ImmutableList.of(button1, button2))
|
||||
.setCustomLayout(ImmutableList.of(button1, button2, button3, button4))
|
||||
.setCallback(
|
||||
new MediaSession.Callback() {
|
||||
@Override
|
||||
@ -186,6 +201,7 @@ public class MediaSessionServiceTest {
|
||||
.buildUpon()
|
||||
.add(command1)
|
||||
.add(command2)
|
||||
.add(command3)
|
||||
.build())
|
||||
.build();
|
||||
}
|
||||
|
@ -1446,10 +1446,9 @@ public class MediaControllerCompatPlaybackStateCompatActionsWithMediaSessionTest
|
||||
Player player = createDefaultPlayer();
|
||||
Bundle extras1 = new Bundle();
|
||||
extras1.putString("key1", "value1");
|
||||
Bundle extras2 = new Bundle();
|
||||
extras1.putString("key2", "value2");
|
||||
SessionCommand command1 = new SessionCommand("command1", extras1);
|
||||
SessionCommand command2 = new SessionCommand("command2", extras2);
|
||||
SessionCommand command2 = new SessionCommand("command2", Bundle.EMPTY);
|
||||
SessionCommand command3 = new SessionCommand("command3", Bundle.EMPTY);
|
||||
ImmutableList<CommandButton> customLayout =
|
||||
ImmutableList.of(
|
||||
new CommandButton.Builder()
|
||||
@ -1461,6 +1460,12 @@ public class MediaControllerCompatPlaybackStateCompatActionsWithMediaSessionTest
|
||||
.setDisplayName("button2")
|
||||
.setIconResId(R.drawable.media3_notification_pause)
|
||||
.setSessionCommand(command2)
|
||||
.build(),
|
||||
new CommandButton.Builder()
|
||||
.setDisplayName("button3")
|
||||
.setEnabled(false)
|
||||
.setIconResId(R.drawable.media3_notification_pause)
|
||||
.setSessionCommand(command3)
|
||||
.build());
|
||||
MediaSession.Callback callback =
|
||||
new MediaSession.Callback() {
|
||||
@ -1469,7 +1474,11 @@ public class MediaControllerCompatPlaybackStateCompatActionsWithMediaSessionTest
|
||||
MediaSession session, MediaSession.ControllerInfo controller) {
|
||||
return new AcceptedResultBuilder(session)
|
||||
.setAvailableSessionCommands(
|
||||
ConnectionResult.DEFAULT_SESSION_COMMANDS.buildUpon().add(command1).build())
|
||||
ConnectionResult.DEFAULT_SESSION_COMMANDS
|
||||
.buildUpon()
|
||||
.add(command1)
|
||||
.add(command3)
|
||||
.build())
|
||||
.build();
|
||||
}
|
||||
};
|
||||
|
@ -123,32 +123,31 @@ public class MediaControllerTest {
|
||||
|
||||
@Test
|
||||
public void builder() throws Exception {
|
||||
MediaController.Builder builder;
|
||||
SessionToken token = remoteSession.getToken();
|
||||
|
||||
try {
|
||||
builder = new MediaController.Builder(null, token);
|
||||
new MediaController.Builder(null, token);
|
||||
assertWithMessage("null context shouldn't be allowed").fail();
|
||||
} catch (NullPointerException e) {
|
||||
// expected. pass-through
|
||||
}
|
||||
|
||||
try {
|
||||
builder = new MediaController.Builder(context, null);
|
||||
new MediaController.Builder(context, null);
|
||||
assertWithMessage("null token shouldn't be allowed").fail();
|
||||
} catch (NullPointerException e) {
|
||||
// expected. pass-through
|
||||
}
|
||||
|
||||
try {
|
||||
builder = new MediaController.Builder(context, token).setListener(null);
|
||||
new MediaController.Builder(context, token).setListener(null);
|
||||
assertWithMessage("null listener shouldn't be allowed").fail();
|
||||
} catch (NullPointerException e) {
|
||||
// expected. pass-through
|
||||
}
|
||||
|
||||
try {
|
||||
builder = new MediaController.Builder(context, token).setApplicationLooper(null);
|
||||
new MediaController.Builder(context, token).setApplicationLooper(null);
|
||||
assertWithMessage("null looper shouldn't be allowed").fail();
|
||||
} catch (NullPointerException e) {
|
||||
// expected. pass-through
|
||||
@ -182,6 +181,7 @@ public class MediaControllerTest {
|
||||
CommandButton button2 =
|
||||
new CommandButton.Builder()
|
||||
.setDisplayName("button2")
|
||||
.setEnabled(false)
|
||||
.setIconResId(R.drawable.media3_notification_small_icon)
|
||||
.setSessionCommand(new SessionCommand("command2", Bundle.EMPTY))
|
||||
.build();
|
||||
@ -191,11 +191,28 @@ public class MediaControllerTest {
|
||||
.setIconResId(R.drawable.media3_notification_small_icon)
|
||||
.setSessionCommand(new SessionCommand("command3", Bundle.EMPTY))
|
||||
.build();
|
||||
setupCustomLayout(session, ImmutableList.of(button1, button2, button3));
|
||||
CommandButton button4 =
|
||||
new CommandButton.Builder()
|
||||
.setDisplayName("button4")
|
||||
.setIconResId(R.drawable.media3_notification_small_icon)
|
||||
.setPlayerCommand(Player.COMMAND_PLAY_PAUSE)
|
||||
.build();
|
||||
CommandButton button5 =
|
||||
new CommandButton.Builder()
|
||||
.setDisplayName("button5")
|
||||
.setIconResId(R.drawable.media3_notification_small_icon)
|
||||
.setPlayerCommand(Player.COMMAND_GET_TRACKS)
|
||||
.build();
|
||||
setupCustomLayout(session, ImmutableList.of(button1, button2, button3, button4, button5));
|
||||
MediaController controller = controllerTestRule.createController(session.getToken());
|
||||
|
||||
assertThat(threadTestRule.getHandler().postAndSync(controller::getCustomLayout))
|
||||
.containsExactly(button1.copyWithIsEnabled(true), button2.copyWithIsEnabled(true), button3)
|
||||
.containsExactly(
|
||||
button1.copyWithIsEnabled(true),
|
||||
button2.copyWithIsEnabled(false),
|
||||
button3.copyWithIsEnabled(false),
|
||||
button4.copyWithIsEnabled(true),
|
||||
button5.copyWithIsEnabled(false))
|
||||
.inOrder();
|
||||
|
||||
session.cleanUp();
|
||||
@ -214,6 +231,7 @@ public class MediaControllerTest {
|
||||
CommandButton button2 =
|
||||
new CommandButton.Builder()
|
||||
.setDisplayName("button2")
|
||||
.setEnabled(false)
|
||||
.setIconResId(R.drawable.media3_notification_small_icon)
|
||||
.setSessionCommand(new SessionCommand("command2", Bundle.EMPTY))
|
||||
.build();
|
||||
@ -229,7 +247,19 @@ public class MediaControllerTest {
|
||||
.setIconResId(R.drawable.media3_notification_small_icon)
|
||||
.setSessionCommand(new SessionCommand("command4", Bundle.EMPTY))
|
||||
.build();
|
||||
setupCustomLayout(session, ImmutableList.of(button1, button2));
|
||||
CommandButton button5 =
|
||||
new CommandButton.Builder()
|
||||
.setDisplayName("button5")
|
||||
.setIconResId(R.drawable.media3_notification_small_icon)
|
||||
.setPlayerCommand(Player.COMMAND_PLAY_PAUSE)
|
||||
.build();
|
||||
CommandButton button6 =
|
||||
new CommandButton.Builder()
|
||||
.setDisplayName("button6")
|
||||
.setIconResId(R.drawable.media3_notification_small_icon)
|
||||
.setPlayerCommand(Player.COMMAND_GET_TRACKS)
|
||||
.build();
|
||||
setupCustomLayout(session, ImmutableList.of(button1, button3));
|
||||
CountDownLatch latch = new CountDownLatch(2);
|
||||
AtomicReference<List<CommandButton>> reportedCustomLayout = new AtomicReference<>();
|
||||
AtomicReference<List<CommandButton>> reportedCustomLayoutChanged = new AtomicReference<>();
|
||||
@ -255,23 +285,32 @@ public class MediaControllerTest {
|
||||
});
|
||||
ImmutableList<CommandButton> initialCustomLayoutFromGetter =
|
||||
threadTestRule.getHandler().postAndSync(controller::getCustomLayout);
|
||||
session.setCustomLayout(ImmutableList.of(button3, button4));
|
||||
session.setCustomLayout(ImmutableList.of(button1, button2, button4, button5, button6));
|
||||
assertThat(latch.await(TIMEOUT_MS, MILLISECONDS)).isTrue();
|
||||
|
||||
ImmutableList<CommandButton> newCustomLayoutFromGetter =
|
||||
threadTestRule.getHandler().postAndSync(controller::getCustomLayout);
|
||||
|
||||
assertThat(initialCustomLayoutFromGetter)
|
||||
.containsExactly(button1.copyWithIsEnabled(true), button2.copyWithIsEnabled(true))
|
||||
.containsExactly(button1.copyWithIsEnabled(true), button3.copyWithIsEnabled(false))
|
||||
.inOrder();
|
||||
ImmutableList<CommandButton> expectedNewButtons =
|
||||
ImmutableList.of(
|
||||
button1.copyWithIsEnabled(true),
|
||||
button2.copyWithIsEnabled(false),
|
||||
button4.copyWithIsEnabled(false),
|
||||
button5.copyWithIsEnabled(true),
|
||||
button6.copyWithIsEnabled(false));
|
||||
assertThat(newCustomLayoutFromGetter).containsExactlyElementsIn(expectedNewButtons).inOrder();
|
||||
assertThat(reportedCustomLayout.get()).containsExactlyElementsIn(expectedNewButtons).inOrder();
|
||||
assertThat(reportedCustomLayoutChanged.get())
|
||||
.containsExactlyElementsIn(expectedNewButtons)
|
||||
.inOrder();
|
||||
assertThat(newCustomLayoutFromGetter).containsExactly(button3, button4).inOrder();
|
||||
assertThat(reportedCustomLayout.get()).containsExactly(button3, button4).inOrder();
|
||||
assertThat(reportedCustomLayoutChanged.get()).containsExactly(button3, button4).inOrder();
|
||||
session.cleanUp();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getCustomLayout_setAvailableCommandsAddOrRemoveCommands_reportsCustomLayoutChanged()
|
||||
public void getCustomLayout_setAvailableCommandsOnSession_reportsCustomLayoutChanged()
|
||||
throws Exception {
|
||||
RemoteMediaSession session = createRemoteMediaSession(TEST_GET_CUSTOM_LAYOUT, null);
|
||||
CommandButton button1 =
|
||||
@ -283,10 +322,23 @@ public class MediaControllerTest {
|
||||
CommandButton button2 =
|
||||
new CommandButton.Builder()
|
||||
.setDisplayName("button2")
|
||||
.setEnabled(false)
|
||||
.setIconResId(R.drawable.media3_notification_small_icon)
|
||||
.setSessionCommand(new SessionCommand("command2", Bundle.EMPTY))
|
||||
.build();
|
||||
setupCustomLayout(session, ImmutableList.of(button1, button2));
|
||||
CommandButton button3 =
|
||||
new CommandButton.Builder()
|
||||
.setDisplayName("button3")
|
||||
.setIconResId(R.drawable.media3_notification_small_icon)
|
||||
.setPlayerCommand(Player.COMMAND_PLAY_PAUSE)
|
||||
.build();
|
||||
CommandButton button4 =
|
||||
new CommandButton.Builder()
|
||||
.setDisplayName("button4")
|
||||
.setIconResId(R.drawable.media3_notification_small_icon)
|
||||
.setPlayerCommand(Player.COMMAND_GET_TRACKS)
|
||||
.build();
|
||||
setupCustomLayout(session, ImmutableList.of(button1, button2, button3, button4));
|
||||
CountDownLatch latch = new CountDownLatch(2);
|
||||
List<List<CommandButton>> reportedCustomLayoutChanged = new ArrayList<>();
|
||||
List<List<CommandButton>> getterCustomLayoutChanged = new ArrayList<>();
|
||||
@ -308,23 +360,95 @@ public class MediaControllerTest {
|
||||
|
||||
// Remove commands in custom layout from available commands.
|
||||
session.setAvailableCommands(SessionCommands.EMPTY, Player.Commands.EMPTY);
|
||||
// Add one command back.
|
||||
// Add one sesion and player command back.
|
||||
session.setAvailableCommands(
|
||||
new SessionCommands.Builder().add(button2.sessionCommand).build(), Player.Commands.EMPTY);
|
||||
new SessionCommands.Builder().add(button2.sessionCommand).build(),
|
||||
new Player.Commands.Builder().add(Player.COMMAND_GET_TRACKS).build());
|
||||
|
||||
assertThat(latch.await(TIMEOUT_MS, MILLISECONDS)).isTrue();
|
||||
assertThat(initialCustomLayout)
|
||||
.containsExactly(button1.copyWithIsEnabled(true), button2.copyWithIsEnabled(true));
|
||||
.containsExactly(
|
||||
button1.copyWithIsEnabled(true),
|
||||
button2.copyWithIsEnabled(false),
|
||||
button3.copyWithIsEnabled(true),
|
||||
button4.copyWithIsEnabled(false));
|
||||
assertThat(reportedCustomLayoutChanged).hasSize(2);
|
||||
assertThat(reportedCustomLayoutChanged.get(0)).containsExactly(button1, button2).inOrder();
|
||||
assertThat(reportedCustomLayoutChanged.get(0))
|
||||
.containsExactly(
|
||||
button1.copyWithIsEnabled(false),
|
||||
button2.copyWithIsEnabled(false),
|
||||
button3.copyWithIsEnabled(false),
|
||||
button4.copyWithIsEnabled(false))
|
||||
.inOrder();
|
||||
assertThat(reportedCustomLayoutChanged.get(1))
|
||||
.containsExactly(button1, button2.copyWithIsEnabled(true))
|
||||
.containsExactly(
|
||||
button1.copyWithIsEnabled(false),
|
||||
button2.copyWithIsEnabled(false),
|
||||
button3.copyWithIsEnabled(false),
|
||||
button4.copyWithIsEnabled(true))
|
||||
.inOrder();
|
||||
assertThat(getterCustomLayoutChanged).hasSize(2);
|
||||
assertThat(getterCustomLayoutChanged.get(0)).containsExactly(button1, button2).inOrder();
|
||||
assertThat(getterCustomLayoutChanged.get(1))
|
||||
.containsExactly(button1, button2.copyWithIsEnabled(true))
|
||||
assertThat(getterCustomLayoutChanged.get(0))
|
||||
.containsExactly(
|
||||
button1.copyWithIsEnabled(false),
|
||||
button2.copyWithIsEnabled(false),
|
||||
button3.copyWithIsEnabled(false),
|
||||
button4.copyWithIsEnabled(false))
|
||||
.inOrder();
|
||||
assertThat(getterCustomLayoutChanged.get(1))
|
||||
.containsExactly(
|
||||
button1.copyWithIsEnabled(false),
|
||||
button2.copyWithIsEnabled(false),
|
||||
button3.copyWithIsEnabled(false),
|
||||
button4.copyWithIsEnabled(true))
|
||||
.inOrder();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getCustomLayout_setAvailableCommandsOnPlayer_reportsCustomLayoutChanged()
|
||||
throws Exception {
|
||||
RemoteMediaSession session = createRemoteMediaSession(TEST_GET_CUSTOM_LAYOUT, null);
|
||||
CommandButton button =
|
||||
new CommandButton.Builder()
|
||||
.setDisplayName("button")
|
||||
.setIconResId(R.drawable.media3_notification_small_icon)
|
||||
.setPlayerCommand(Player.COMMAND_PLAY_PAUSE)
|
||||
.build();
|
||||
setupCustomLayout(session, ImmutableList.of(button));
|
||||
CountDownLatch latch = new CountDownLatch(2);
|
||||
List<List<CommandButton>> reportedCustomLayouts = new ArrayList<>();
|
||||
List<List<CommandButton>> getterCustomLayouts = new ArrayList<>();
|
||||
MediaController.Listener listener =
|
||||
new MediaController.Listener() {
|
||||
@Override
|
||||
public void onCustomLayoutChanged(
|
||||
MediaController controller, List<CommandButton> layout) {
|
||||
reportedCustomLayouts.add(layout);
|
||||
getterCustomLayouts.add(controller.getCustomLayout());
|
||||
latch.countDown();
|
||||
}
|
||||
};
|
||||
MediaController controller =
|
||||
controllerTestRule.createController(
|
||||
session.getToken(), /* connectionHints= */ Bundle.EMPTY, listener);
|
||||
ImmutableList<CommandButton> initialCustomLayout =
|
||||
threadTestRule.getHandler().postAndSync(controller::getCustomLayout);
|
||||
|
||||
// Disable player command and then add it back.
|
||||
session.getMockPlayer().notifyAvailableCommandsChanged(Player.Commands.EMPTY);
|
||||
session
|
||||
.getMockPlayer()
|
||||
.notifyAvailableCommandsChanged(
|
||||
new Player.Commands.Builder().add(Player.COMMAND_PLAY_PAUSE).build());
|
||||
|
||||
assertThat(latch.await(TIMEOUT_MS, MILLISECONDS)).isTrue();
|
||||
assertThat(initialCustomLayout).containsExactly(button.copyWithIsEnabled(true));
|
||||
assertThat(reportedCustomLayouts).hasSize(2);
|
||||
assertThat(reportedCustomLayouts.get(0)).containsExactly(button.copyWithIsEnabled(false));
|
||||
assertThat(reportedCustomLayouts.get(1)).containsExactly(button.copyWithIsEnabled(true));
|
||||
assertThat(getterCustomLayouts).hasSize(2);
|
||||
assertThat(getterCustomLayouts.get(0)).containsExactly(button.copyWithIsEnabled(false));
|
||||
assertThat(getterCustomLayouts.get(1)).containsExactly(button.copyWithIsEnabled(true));
|
||||
}
|
||||
|
||||
@Test
|
||||
@ -341,6 +465,7 @@ public class MediaControllerTest {
|
||||
CommandButton button2 =
|
||||
new CommandButton.Builder()
|
||||
.setDisplayName("button2")
|
||||
.setEnabled(false)
|
||||
.setIconResId(R.drawable.media3_notification_small_icon)
|
||||
.setSessionCommand(new SessionCommand("command2", Bundle.EMPTY))
|
||||
.build();
|
||||
@ -393,27 +518,31 @@ public class MediaControllerTest {
|
||||
|
||||
assertThat(latch.await(TIMEOUT_MS, MILLISECONDS)).isTrue();
|
||||
CommandButton button1Enabled = button1.copyWithIsEnabled(true);
|
||||
CommandButton button2Enabled = button2.copyWithIsEnabled(true);
|
||||
assertThat(initialCustomLayout).containsExactly(button1Enabled, button2Enabled).inOrder();
|
||||
CommandButton button2Disabled = button2.copyWithIsEnabled(false);
|
||||
CommandButton button3Disabled = button3.copyWithIsEnabled(false);
|
||||
CommandButton button4Disabled = button4.copyWithIsEnabled(false);
|
||||
assertThat(initialCustomLayout).containsExactly(button1Enabled, button2Disabled).inOrder();
|
||||
assertThat(reportedCustomLayout)
|
||||
.containsExactly(
|
||||
ImmutableList.of(button1Enabled, button2Enabled),
|
||||
ImmutableList.of(button3, button4),
|
||||
ImmutableList.of(button1Enabled, button2Enabled))
|
||||
ImmutableList.of(button1Enabled, button2Disabled),
|
||||
ImmutableList.of(button3Disabled, button4Disabled),
|
||||
ImmutableList.of(button1Enabled, button2Disabled))
|
||||
.inOrder();
|
||||
assertThat(getterCustomLayout)
|
||||
.containsExactly(
|
||||
ImmutableList.of(button1Enabled, button2Enabled),
|
||||
ImmutableList.of(button3, button4),
|
||||
ImmutableList.of(button1Enabled, button2Enabled))
|
||||
ImmutableList.of(button1Enabled, button2Disabled),
|
||||
ImmutableList.of(button3Disabled, button4Disabled),
|
||||
ImmutableList.of(button1Enabled, button2Disabled))
|
||||
.inOrder();
|
||||
assertThat(reportedCustomLayoutChanged)
|
||||
.containsExactly(
|
||||
ImmutableList.of(button3, button4), ImmutableList.of(button1Enabled, button2Enabled))
|
||||
ImmutableList.of(button3Disabled, button4Disabled),
|
||||
ImmutableList.of(button1Enabled, button2Disabled))
|
||||
.inOrder();
|
||||
assertThat(getterCustomLayoutChanged)
|
||||
.containsExactly(
|
||||
ImmutableList.of(button3, button4), ImmutableList.of(button1Enabled, button2Enabled))
|
||||
ImmutableList.of(button3Disabled, button4Disabled),
|
||||
ImmutableList.of(button1Enabled, button2Disabled))
|
||||
.inOrder();
|
||||
session.cleanUp();
|
||||
}
|
||||
|
@ -319,7 +319,9 @@ public class MediaSessionServiceTest {
|
||||
.isTrue();
|
||||
assertThat(mediaControllerCompat.getPlaybackState().getActions())
|
||||
.isEqualTo(PlaybackStateCompat.ACTION_SET_RATING);
|
||||
assertThat(remoteController.getCustomLayout()).containsExactly(button1, button2).inOrder();
|
||||
assertThat(remoteController.getCustomLayout())
|
||||
.containsExactly(button1.copyWithIsEnabled(false), button2.copyWithIsEnabled(false))
|
||||
.inOrder();
|
||||
assertThat(initialCustomLayoutInControllerCompat).isEmpty();
|
||||
assertThat(LegacyConversions.convertToCustomLayout(mediaControllerCompat.getPlaybackState()))
|
||||
.containsExactly(button1.copyWithIsEnabled(true), button3.copyWithIsEnabled(true))
|
||||
|
@ -207,7 +207,7 @@ public class MediaSessionProviderService extends Service {
|
||||
.add(new SessionCommand("command1", Bundle.EMPTY))
|
||||
.add(new SessionCommand("command2", Bundle.EMPTY))
|
||||
.build(),
|
||||
Player.Commands.EMPTY);
|
||||
new Player.Commands.Builder().add(Player.COMMAND_PLAY_PAUSE).build());
|
||||
}
|
||||
});
|
||||
break;
|
||||
@ -549,7 +549,7 @@ public class MediaSessionProviderService extends Service {
|
||||
() -> {
|
||||
ImmutableList.Builder<CommandButton> builder = new ImmutableList.Builder<>();
|
||||
for (Bundle bundle : layout) {
|
||||
builder.add(CommandButton.fromBundle(bundle));
|
||||
builder.add(CommandButton.fromBundle(bundle, MediaSessionStub.VERSION_INT));
|
||||
}
|
||||
MediaSession session = sessionMap.get(sessionId);
|
||||
session.setCustomLayout(builder.build());
|
||||
|
@ -381,7 +381,7 @@ public class RemoteMediaController {
|
||||
ArrayList<Bundle> list = customLayoutBundle.getParcelableArrayList(KEY_COMMAND_BUTTON_LIST);
|
||||
ImmutableList.Builder<CommandButton> customLayout = new ImmutableList.Builder<>();
|
||||
for (Bundle bundle : list) {
|
||||
customLayout.add(CommandButton.fromBundle(bundle));
|
||||
customLayout.add(CommandButton.fromBundle(bundle, MediaSessionStub.VERSION_INT));
|
||||
}
|
||||
return customLayout.build();
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user