Add logic to convert custom layout to button preferences in controller

This ensures that the slots are set according to the implicit placement
rules for the legacy custom layouts. This has to be done when receiving
custom actions from a MediaControllerCompat and for Media3 sessions
setting the custom layout field.

PiperOrigin-RevId: 689737951
This commit is contained in:
tonihei 2024-10-25 04:26:24 -07:00 committed by Copybara-Service
parent d051b4b993
commit aeb9d12a16
10 changed files with 993 additions and 108 deletions

View File

@ -27,6 +27,7 @@ import android.text.TextUtils;
import androidx.annotation.DrawableRes;
import androidx.annotation.IntDef;
import androidx.annotation.Nullable;
import androidx.media3.common.C;
import androidx.media3.common.Player;
import androidx.media3.common.util.UnstableApi;
import androidx.media3.common.util.Util;
@ -799,6 +800,24 @@ public final class CommandButton {
slots);
}
/** Returns a copy with the new {@link #slots} value. */
@CheckReturnValue
/* package */ CommandButton copyWithSlots(ImmutableIntArray slots) {
if (this.slots.equals(slots)) {
return this;
}
return new CommandButton(
sessionCommand,
playerCommand,
icon,
iconResId,
iconUri,
displayName,
new Bundle(extras),
isEnabled,
slots);
}
/** Checks the given command button for equality while ignoring {@link #extras}. */
@Override
public boolean equals(@Nullable Object obj) {
@ -1153,4 +1172,62 @@ public final class CommandButton {
return SLOT_OVERFLOW;
}
}
/**
* Converts a list of buttons defined according to the implicit button placement rules for
* {@linkplain MediaSession#getCustomLayout custom layouts} to {@linkplain
* MediaSession#getMediaButtonPreferences media button preferences}.
*
* @param customLayout A list of buttons compatible with the placement rules of custom layouts.
* @param availablePlayerCommands The available {@link Player.Commands}.
* @param reservationExtras A {@link Bundle} with extras that may contain slot reservations via
* {@link MediaConstants#EXTRAS_KEY_SLOT_RESERVATION_SEEK_TO_NEXT} or {@link
* MediaConstants#EXTRAS_KEY_SLOT_RESERVATION_SEEK_TO_PREV}. The bundle contents will not be
* modified.
* @return The list of buttons as media button preferences.
*/
/* package */ static ImmutableList<CommandButton> getMediaButtonPreferencesFromCustomLayout(
List<CommandButton> customLayout,
Player.Commands availablePlayerCommands,
Bundle reservationExtras) {
if (customLayout.isEmpty()) {
return ImmutableList.of();
}
boolean hasDefaultBackCommand =
availablePlayerCommands.containsAny(
Player.COMMAND_SEEK_TO_PREVIOUS, Player.COMMAND_SEEK_TO_PREVIOUS_MEDIA_ITEM);
boolean hasDefaultForwardCommand =
availablePlayerCommands.containsAny(
Player.COMMAND_SEEK_TO_NEXT, Player.COMMAND_SEEK_TO_NEXT_MEDIA_ITEM);
boolean hasBackSlotReservation =
reservationExtras.getBoolean(
MediaConstants.EXTRAS_KEY_SLOT_RESERVATION_SEEK_TO_PREV, /* defaultValue= */ false);
boolean hasForwardSlotReservation =
reservationExtras.getBoolean(
MediaConstants.EXTRAS_KEY_SLOT_RESERVATION_SEEK_TO_NEXT, /* defaultValue= */ false);
int backButtonIndex = (hasDefaultBackCommand || hasBackSlotReservation) ? C.INDEX_UNSET : 0;
int forwardButtonIndex =
(hasDefaultForwardCommand || hasForwardSlotReservation)
? C.INDEX_UNSET
: (backButtonIndex == 0 ? 1 : 0);
ImmutableList.Builder<CommandButton> mediaButtonPreferences = ImmutableList.builder();
for (int i = 0; i < customLayout.size(); i++) {
CommandButton button = customLayout.get(i);
if (i == backButtonIndex) {
if (forwardButtonIndex == C.INDEX_UNSET) {
mediaButtonPreferences.add(
button.copyWithSlots(ImmutableIntArray.of(SLOT_BACK, SLOT_OVERFLOW)));
} else {
mediaButtonPreferences.add(
button.copyWithSlots(ImmutableIntArray.of(SLOT_BACK, SLOT_FORWARD, SLOT_OVERFLOW)));
}
} else if (i == forwardButtonIndex) {
mediaButtonPreferences.add(
button.copyWithSlots(ImmutableIntArray.of(SLOT_FORWARD, SLOT_OVERFLOW)));
} else {
mediaButtonPreferences.add(button.copyWithSlots(ImmutableIntArray.of(SLOT_OVERFLOW)));
}
}
return mediaButtonPreferences.build();
}
}

View File

@ -1497,10 +1497,14 @@ import java.util.concurrent.TimeoutException;
* Converts {@link CustomAction} in the {@link PlaybackStateCompat} to media button preferences.
*
* @param state The {@link PlaybackStateCompat}.
* @param availablePlayerCommands The available {@link Player.Commands}.
* @param sessionExtras The {@linkplain MediaControllerCompat#getExtras session-level extras}.
* @return The media button preferences.
*/
public static ImmutableList<CommandButton> convertToMediaButtonPreferences(
@Nullable PlaybackStateCompat state) {
@Nullable PlaybackStateCompat state,
Player.Commands availablePlayerCommands,
Bundle sessionExtras) {
if (state == null) {
return ImmutableList.of();
}
@ -1508,7 +1512,7 @@ import java.util.concurrent.TimeoutException;
if (customActions == null) {
return ImmutableList.of();
}
ImmutableList.Builder<CommandButton> mediaButtonPreferences = new ImmutableList.Builder<>();
ImmutableList.Builder<CommandButton> customLayout = new ImmutableList.Builder<>();
for (CustomAction customAction : customActions) {
String action = customAction.getAction();
@Nullable Bundle extras = customAction.getExtras();
@ -1519,16 +1523,16 @@ import java.util.concurrent.TimeoutException;
MediaConstants.EXTRAS_KEY_COMMAND_BUTTON_ICON_COMPAT,
/* defaultValue= */ CommandButton.ICON_UNDEFINED)
: CommandButton.ICON_UNDEFINED;
// TODO: b/332877990 - Set appropriate slots based on available player commands.
CommandButton button =
new CommandButton.Builder(icon, customAction.getIcon())
.setSessionCommand(new SessionCommand(action, extras == null ? Bundle.EMPTY : extras))
.setDisplayName(customAction.getName())
.setEnabled(true)
.build();
mediaButtonPreferences.add(button);
customLayout.add(button);
}
return mediaButtonPreferences.build();
return CommandButton.getMediaButtonPreferencesFromCustomLayout(
customLayout.build(), availablePlayerCommands, sessionExtras);
}
/** Converts {@link AudioAttributesCompat} into {@link AudioAttributes}. */

View File

@ -2660,7 +2660,8 @@ import org.checkerframework.checker.nullness.qual.NonNull;
mediaButtonPreferencesOriginal,
customLayoutOriginal,
sessionCommands,
intersectedPlayerCommands);
intersectedPlayerCommands,
result.sessionExtras);
ImmutableMap.Builder<String, CommandButton> commandButtonsForMediaItems =
new ImmutableMap.Builder<>();
for (int i = 0; i < result.commandButtonsForMediaItems.size(); i++) {
@ -2848,7 +2849,8 @@ import org.checkerframework.checker.nullness.qual.NonNull;
mediaButtonPreferencesOriginal,
customLayoutOriginal,
sessionCommands,
intersectedPlayerCommands);
intersectedPlayerCommands,
sessionExtras);
mediaButtonPreferencesChanged =
!resolvedMediaButtonPreferences.equals(oldMediaButtonPreferences);
}
@ -2896,7 +2898,8 @@ import org.checkerframework.checker.nullness.qual.NonNull;
mediaButtonPreferencesOriginal,
customLayoutOriginal,
sessionCommands,
intersectedPlayerCommands);
intersectedPlayerCommands,
sessionExtras);
mediaButtonPreferencesChanged =
!resolvedMediaButtonPreferences.equals(oldMediaButtonPreferences);
listeners.sendEvent(
@ -2922,7 +2925,11 @@ import org.checkerframework.checker.nullness.qual.NonNull;
customLayoutOriginal = ImmutableList.copyOf(layout);
resolvedMediaButtonPreferences =
resolveMediaButtonPreferences(
mediaButtonPreferencesOriginal, layout, sessionCommands, intersectedPlayerCommands);
mediaButtonPreferencesOriginal,
layout,
sessionCommands,
intersectedPlayerCommands,
sessionExtras);
boolean mediaButtonPreferencesChanged =
!Objects.equals(resolvedMediaButtonPreferences, oldMediaButtonPreferences);
getInstance()
@ -2952,7 +2959,8 @@ import org.checkerframework.checker.nullness.qual.NonNull;
mediaButtonPreferences,
customLayoutOriginal,
sessionCommands,
intersectedPlayerCommands);
intersectedPlayerCommands,
sessionExtras);
boolean mediaButtonPreferencesChanged =
!Objects.equals(resolvedMediaButtonPreferences, oldMediaButtonPreferences);
getInstance()
@ -2975,9 +2983,27 @@ import org.checkerframework.checker.nullness.qual.NonNull;
if (!isConnected()) {
return;
}
ImmutableList<CommandButton> oldMediaButtonPreferences = resolvedMediaButtonPreferences;
sessionExtras = extras;
resolvedMediaButtonPreferences =
resolveMediaButtonPreferences(
mediaButtonPreferencesOriginal,
customLayoutOriginal,
sessionCommands,
intersectedPlayerCommands,
sessionExtras);
boolean mediaButtonPreferencesChanged =
!Objects.equals(resolvedMediaButtonPreferences, oldMediaButtonPreferences);
getInstance()
.notifyControllerListener(listener -> listener.onExtrasChanged(getInstance(), extras));
.notifyControllerListener(
listener -> {
listener.onExtrasChanged(getInstance(), extras);
if (mediaButtonPreferencesChanged) {
listener.onCustomLayoutChanged(getInstance(), resolvedMediaButtonPreferences);
listener.onMediaButtonPreferencesChanged(
getInstance(), resolvedMediaButtonPreferences);
}
});
}
public void onSetSessionActivity(int seq, PendingIntent sessionActivity) {
@ -3327,12 +3353,16 @@ import org.checkerframework.checker.nullness.qual.NonNull;
List<CommandButton> mediaButtonPreferences,
List<CommandButton> customLayout,
SessionCommands sessionCommands,
Player.Commands playerCommands) {
// TODO: b/332877990 - When using custom layout, set correct slots based on available commands.
Player.Commands playerCommands,
Bundle sessionExtras) {
List<CommandButton> resolvedButtons = mediaButtonPreferences;
if (resolvedButtons.isEmpty()) {
resolvedButtons =
CommandButton.getMediaButtonPreferencesFromCustomLayout(
customLayout, playerCommands, sessionExtras);
}
return CommandButton.copyWithUnavailableButtonsDisabled(
mediaButtonPreferences.isEmpty() ? customLayout : mediaButtonPreferences,
sessionCommands,
playerCommands);
resolvedButtons, sessionCommands, playerCommands);
}
private static Commands createIntersectedCommandsEnsuringCommandReleaseAvailable(

View File

@ -110,6 +110,7 @@ import org.checkerframework.checker.initialization.qual.UnderInitialization;
private boolean connected;
private LegacyPlayerInfo legacyPlayerInfo;
private LegacyPlayerInfo pendingLegacyPlayerInfo;
private boolean hasPendingExtrasChange;
private ControllerInfo controllerInfo;
private long currentPositionMs;
private long lastSetPlayWhenReadyCalledTimeMs;
@ -1575,6 +1576,7 @@ import org.checkerframework.checker.initialization.qual.UnderInitialization;
controllerCompat.getRatingType(),
getInstance().getTimeDiffMs(),
getRoutingControllerId(controllerCompat),
hasPendingExtrasChange,
context);
Pair<@NullableType Integer, @NullableType Integer> reasons =
calculateDiscontinuityAndTransitionReason(
@ -1589,6 +1591,13 @@ import org.checkerframework.checker.initialization.qual.UnderInitialization;
newControllerInfo,
/* discontinuityReason= */ reasons.first,
/* mediaItemTransitionReason= */ reasons.second);
if (hasPendingExtrasChange) {
hasPendingExtrasChange = false;
getInstance()
.notifyControllerListener(
listener ->
listener.onExtrasChanged(getInstance(), newLegacyPlayerInfo.sessionExtras));
}
}
private void updateStateMaskedControllerInfo(
@ -1918,16 +1927,10 @@ import org.checkerframework.checker.initialization.qual.UnderInitialization;
@Override
public void onExtrasChanged(@Nullable Bundle extras) {
controllerInfo =
new ControllerInfo(
controllerInfo.playerInfo,
controllerInfo.availableSessionCommands,
controllerInfo.availablePlayerCommands,
controllerInfo.mediaButtonPreferences,
extras,
/* sessionError= */ null);
getInstance()
.notifyControllerListener(listener -> listener.onExtrasChanged(getInstance(), extras));
Bundle nonNullExtras = extras == null ? new Bundle() : extras;
pendingLegacyPlayerInfo = pendingLegacyPlayerInfo.copyWithSessionExtras(nonNullExtras);
hasPendingExtrasChange = true;
startWaitingForPendingChanges();
}
@Override
@ -1989,6 +1992,7 @@ import org.checkerframework.checker.initialization.qual.UnderInitialization;
@RatingCompat.Style int ratingType,
long timeDiffMs,
@Nullable String routingControllerId,
boolean hasPendingExtrasChange,
Context context) {
QueueTimeline currentTimeline;
MediaMetadata mediaMetadata;
@ -2073,24 +2077,6 @@ import org.checkerframework.checker.initialization.qual.UnderInitialization;
mediaMetadata = oldControllerInfo.playerInfo.mediaMetadata;
}
playlistMetadata =
oldLegacyPlayerInfo.queueTitle == newLegacyPlayerInfo.queueTitle
? oldControllerInfo.playerInfo.playlistMetadata
: LegacyConversions.convertToMediaMetadata(newLegacyPlayerInfo.queueTitle);
repeatMode = LegacyConversions.convertToRepeatMode(newLegacyPlayerInfo.repeatMode);
shuffleModeEnabled =
LegacyConversions.convertToShuffleModeEnabled(newLegacyPlayerInfo.shuffleMode);
if (oldLegacyPlayerInfo.playbackStateCompat != newLegacyPlayerInfo.playbackStateCompat) {
availableSessionCommands =
LegacyConversions.convertToSessionCommands(
newLegacyPlayerInfo.playbackStateCompat, isSessionReady);
mediaButtonPreferences =
LegacyConversions.convertToMediaButtonPreferences(
newLegacyPlayerInfo.playbackStateCompat);
} else {
availableSessionCommands = oldControllerInfo.availableSessionCommands;
mediaButtonPreferences = oldControllerInfo.mediaButtonPreferences;
}
// Note: Sets the available player command here although it can be obtained before session is
// ready. It's to follow the decision on MediaController to disallow any commands before
// connection is made.
@ -2105,6 +2091,28 @@ import org.checkerframework.checker.initialization.qual.UnderInitialization;
sessionFlags,
isSessionReady);
playlistMetadata =
oldLegacyPlayerInfo.queueTitle == newLegacyPlayerInfo.queueTitle
? oldControllerInfo.playerInfo.playlistMetadata
: LegacyConversions.convertToMediaMetadata(newLegacyPlayerInfo.queueTitle);
repeatMode = LegacyConversions.convertToRepeatMode(newLegacyPlayerInfo.repeatMode);
shuffleModeEnabled =
LegacyConversions.convertToShuffleModeEnabled(newLegacyPlayerInfo.shuffleMode);
if (oldLegacyPlayerInfo.playbackStateCompat != newLegacyPlayerInfo.playbackStateCompat
|| hasPendingExtrasChange) {
availableSessionCommands =
LegacyConversions.convertToSessionCommands(
newLegacyPlayerInfo.playbackStateCompat, isSessionReady);
mediaButtonPreferences =
LegacyConversions.convertToMediaButtonPreferences(
newLegacyPlayerInfo.playbackStateCompat,
availablePlayerCommands,
newLegacyPlayerInfo.sessionExtras);
} else {
availableSessionCommands = oldControllerInfo.availableSessionCommands;
mediaButtonPreferences = oldControllerInfo.mediaButtonPreferences;
}
PlaybackException playerError =
LegacyConversions.convertToPlaybackException(newLegacyPlayerInfo.playbackStateCompat);
SessionError sessionError =
@ -2626,6 +2634,19 @@ import org.checkerframework.checker.initialization.qual.UnderInitialization;
shuffleMode,
sessionExtras);
}
@CheckResult
public LegacyPlayerInfo copyWithSessionExtras(Bundle sessionExtras) {
return new LegacyPlayerInfo(
playbackInfoCompat,
playbackStateCompat,
mediaMetadataCompat,
queue,
queueTitle,
repeatMode,
shuffleMode,
sessionExtras);
}
}
private static class ControllerInfo {

View File

@ -543,4 +543,374 @@ public class CommandButtonTest {
assertThat(restoredButtonAssumingOldSessionInterface.isEnabled).isTrue();
}
@Test
public void
getMediaButtonPreferencesFromCustomLayout_withPrevAndNextCommands_returnsCorrectSlots() {
ImmutableList<CommandButton> customLayout =
ImmutableList.of(
new CommandButton.Builder(CommandButton.ICON_ALBUM)
.setPlayerCommand(Player.COMMAND_PREPARE)
.build(),
new CommandButton.Builder(CommandButton.ICON_SHUFFLE_ON)
.setPlayerCommand(Player.COMMAND_SEEK_TO_NEXT)
.build(),
new CommandButton.Builder(CommandButton.ICON_ARTIST)
.setPlayerCommand(Player.COMMAND_SEEK_TO_PREVIOUS)
.build());
Bundle reservationBundle = new Bundle();
reservationBundle.putBoolean(MediaConstants.EXTRAS_KEY_SLOT_RESERVATION_SEEK_TO_PREV, false);
reservationBundle.putBoolean(MediaConstants.EXTRAS_KEY_SLOT_RESERVATION_SEEK_TO_NEXT, false);
Player.Commands playerCommands =
new Player.Commands.Builder()
.addAll(Player.COMMAND_SEEK_TO_NEXT, Player.COMMAND_SEEK_TO_PREVIOUS)
.build();
ImmutableList<CommandButton> mediaButtonPreferences =
CommandButton.getMediaButtonPreferencesFromCustomLayout(
customLayout, playerCommands, reservationBundle);
assertThat(mediaButtonPreferences)
.containsExactly(
new CommandButton.Builder(CommandButton.ICON_ALBUM)
.setPlayerCommand(Player.COMMAND_PREPARE)
.setSlots(CommandButton.SLOT_OVERFLOW)
.build(),
new CommandButton.Builder(CommandButton.ICON_SHUFFLE_ON)
.setPlayerCommand(Player.COMMAND_SEEK_TO_NEXT)
.setSlots(CommandButton.SLOT_OVERFLOW)
.build(),
new CommandButton.Builder(CommandButton.ICON_ARTIST)
.setPlayerCommand(Player.COMMAND_SEEK_TO_PREVIOUS)
.setSlots(CommandButton.SLOT_OVERFLOW)
.build())
.inOrder();
}
@Test
public void
getMediaButtonPreferencesFromCustomLayout_withPrevCommandNoNextReservation_returnsCorrectSlots() {
ImmutableList<CommandButton> customLayout =
ImmutableList.of(
new CommandButton.Builder(CommandButton.ICON_ALBUM)
.setPlayerCommand(Player.COMMAND_PREPARE)
.build(),
new CommandButton.Builder(CommandButton.ICON_SHUFFLE_ON)
.setPlayerCommand(Player.COMMAND_SEEK_TO_NEXT)
.build(),
new CommandButton.Builder(CommandButton.ICON_ARTIST)
.setPlayerCommand(Player.COMMAND_SEEK_TO_PREVIOUS)
.build());
Bundle reservationBundle = new Bundle();
reservationBundle.putBoolean(MediaConstants.EXTRAS_KEY_SLOT_RESERVATION_SEEK_TO_PREV, false);
reservationBundle.putBoolean(MediaConstants.EXTRAS_KEY_SLOT_RESERVATION_SEEK_TO_NEXT, false);
Player.Commands playerCommands =
new Player.Commands.Builder().addAll(Player.COMMAND_SEEK_TO_PREVIOUS_MEDIA_ITEM).build();
ImmutableList<CommandButton> mediaButtonPreferences =
CommandButton.getMediaButtonPreferencesFromCustomLayout(
customLayout, playerCommands, reservationBundle);
assertThat(mediaButtonPreferences)
.containsExactly(
new CommandButton.Builder(CommandButton.ICON_ALBUM)
.setPlayerCommand(Player.COMMAND_PREPARE)
.setSlots(CommandButton.SLOT_FORWARD, CommandButton.SLOT_OVERFLOW)
.build(),
new CommandButton.Builder(CommandButton.ICON_SHUFFLE_ON)
.setPlayerCommand(Player.COMMAND_SEEK_TO_NEXT)
.setSlots(CommandButton.SLOT_OVERFLOW)
.build(),
new CommandButton.Builder(CommandButton.ICON_ARTIST)
.setPlayerCommand(Player.COMMAND_SEEK_TO_PREVIOUS)
.setSlots(CommandButton.SLOT_OVERFLOW)
.build())
.inOrder();
}
@Test
public void
getMediaButtonPreferencesFromCustomLayout_withPrevCommandAndNextReservation_returnsCorrectSlots() {
ImmutableList<CommandButton> customLayout =
ImmutableList.of(
new CommandButton.Builder(CommandButton.ICON_ALBUM)
.setPlayerCommand(Player.COMMAND_PREPARE)
.build(),
new CommandButton.Builder(CommandButton.ICON_SHUFFLE_ON)
.setPlayerCommand(Player.COMMAND_SEEK_TO_NEXT)
.build(),
new CommandButton.Builder(CommandButton.ICON_ARTIST)
.setPlayerCommand(Player.COMMAND_SEEK_TO_PREVIOUS)
.build());
Bundle reservationBundle = new Bundle();
reservationBundle.putBoolean(MediaConstants.EXTRAS_KEY_SLOT_RESERVATION_SEEK_TO_PREV, false);
reservationBundle.putBoolean(MediaConstants.EXTRAS_KEY_SLOT_RESERVATION_SEEK_TO_NEXT, true);
Player.Commands playerCommands =
new Player.Commands.Builder().addAll(Player.COMMAND_SEEK_TO_PREVIOUS_MEDIA_ITEM).build();
ImmutableList<CommandButton> mediaButtonPreferences =
CommandButton.getMediaButtonPreferencesFromCustomLayout(
customLayout, playerCommands, reservationBundle);
assertThat(mediaButtonPreferences)
.containsExactly(
new CommandButton.Builder(CommandButton.ICON_ALBUM)
.setPlayerCommand(Player.COMMAND_PREPARE)
.setSlots(CommandButton.SLOT_OVERFLOW)
.build(),
new CommandButton.Builder(CommandButton.ICON_SHUFFLE_ON)
.setPlayerCommand(Player.COMMAND_SEEK_TO_NEXT)
.setSlots(CommandButton.SLOT_OVERFLOW)
.build(),
new CommandButton.Builder(CommandButton.ICON_ARTIST)
.setPlayerCommand(Player.COMMAND_SEEK_TO_PREVIOUS)
.setSlots(CommandButton.SLOT_OVERFLOW)
.build())
.inOrder();
}
@Test
public void
getMediaButtonPreferencesFromCustomLayout_withNextCommandNoPrevReservation_returnsCorrectSlots() {
ImmutableList<CommandButton> customLayout =
ImmutableList.of(
new CommandButton.Builder(CommandButton.ICON_ALBUM)
.setPlayerCommand(Player.COMMAND_PREPARE)
.build(),
new CommandButton.Builder(CommandButton.ICON_SHUFFLE_ON)
.setPlayerCommand(Player.COMMAND_SEEK_TO_NEXT)
.build(),
new CommandButton.Builder(CommandButton.ICON_ARTIST)
.setPlayerCommand(Player.COMMAND_SEEK_TO_PREVIOUS)
.build());
Bundle reservationBundle = new Bundle();
reservationBundle.putBoolean(MediaConstants.EXTRAS_KEY_SLOT_RESERVATION_SEEK_TO_PREV, false);
reservationBundle.putBoolean(MediaConstants.EXTRAS_KEY_SLOT_RESERVATION_SEEK_TO_NEXT, false);
Player.Commands playerCommands =
new Player.Commands.Builder().addAll(Player.COMMAND_SEEK_TO_NEXT_MEDIA_ITEM).build();
ImmutableList<CommandButton> mediaButtonPreferences =
CommandButton.getMediaButtonPreferencesFromCustomLayout(
customLayout, playerCommands, reservationBundle);
assertThat(mediaButtonPreferences)
.containsExactly(
new CommandButton.Builder(CommandButton.ICON_ALBUM)
.setPlayerCommand(Player.COMMAND_PREPARE)
.setSlots(CommandButton.SLOT_BACK, CommandButton.SLOT_OVERFLOW)
.build(),
new CommandButton.Builder(CommandButton.ICON_SHUFFLE_ON)
.setPlayerCommand(Player.COMMAND_SEEK_TO_NEXT)
.setSlots(CommandButton.SLOT_OVERFLOW)
.build(),
new CommandButton.Builder(CommandButton.ICON_ARTIST)
.setPlayerCommand(Player.COMMAND_SEEK_TO_PREVIOUS)
.setSlots(CommandButton.SLOT_OVERFLOW)
.build())
.inOrder();
}
@Test
public void
getMediaButtonPreferencesFromCustomLayout_withNextCommandAndPrevReservation_returnsCorrectSlots() {
ImmutableList<CommandButton> customLayout =
ImmutableList.of(
new CommandButton.Builder(CommandButton.ICON_ALBUM)
.setPlayerCommand(Player.COMMAND_PREPARE)
.build(),
new CommandButton.Builder(CommandButton.ICON_SHUFFLE_ON)
.setPlayerCommand(Player.COMMAND_SEEK_TO_NEXT)
.build(),
new CommandButton.Builder(CommandButton.ICON_ARTIST)
.setPlayerCommand(Player.COMMAND_SEEK_TO_PREVIOUS)
.build());
Bundle reservationBundle = new Bundle();
reservationBundle.putBoolean(MediaConstants.EXTRAS_KEY_SLOT_RESERVATION_SEEK_TO_PREV, true);
reservationBundle.putBoolean(MediaConstants.EXTRAS_KEY_SLOT_RESERVATION_SEEK_TO_NEXT, false);
Player.Commands playerCommands =
new Player.Commands.Builder().addAll(Player.COMMAND_SEEK_TO_NEXT_MEDIA_ITEM).build();
ImmutableList<CommandButton> mediaButtonPreferences =
CommandButton.getMediaButtonPreferencesFromCustomLayout(
customLayout, playerCommands, reservationBundle);
assertThat(mediaButtonPreferences)
.containsExactly(
new CommandButton.Builder(CommandButton.ICON_ALBUM)
.setPlayerCommand(Player.COMMAND_PREPARE)
.setSlots(CommandButton.SLOT_OVERFLOW)
.build(),
new CommandButton.Builder(CommandButton.ICON_SHUFFLE_ON)
.setPlayerCommand(Player.COMMAND_SEEK_TO_NEXT)
.setSlots(CommandButton.SLOT_OVERFLOW)
.build(),
new CommandButton.Builder(CommandButton.ICON_ARTIST)
.setPlayerCommand(Player.COMMAND_SEEK_TO_PREVIOUS)
.setSlots(CommandButton.SLOT_OVERFLOW)
.build())
.inOrder();
}
@Test
public void
getMediaButtonPreferencesFromCustomLayout_withoutPrevNextCommandsNoReservations_returnsCorrectSlots() {
ImmutableList<CommandButton> customLayout =
ImmutableList.of(
new CommandButton.Builder(CommandButton.ICON_ALBUM)
.setPlayerCommand(Player.COMMAND_PREPARE)
.build(),
new CommandButton.Builder(CommandButton.ICON_SHUFFLE_ON)
.setPlayerCommand(Player.COMMAND_SEEK_TO_NEXT)
.build(),
new CommandButton.Builder(CommandButton.ICON_ARTIST)
.setPlayerCommand(Player.COMMAND_SEEK_TO_PREVIOUS)
.build());
Bundle reservationBundle = new Bundle();
reservationBundle.putBoolean(MediaConstants.EXTRAS_KEY_SLOT_RESERVATION_SEEK_TO_PREV, false);
reservationBundle.putBoolean(MediaConstants.EXTRAS_KEY_SLOT_RESERVATION_SEEK_TO_NEXT, false);
Player.Commands playerCommands = Player.Commands.EMPTY;
ImmutableList<CommandButton> mediaButtonPreferences =
CommandButton.getMediaButtonPreferencesFromCustomLayout(
customLayout, playerCommands, reservationBundle);
assertThat(mediaButtonPreferences)
.containsExactly(
new CommandButton.Builder(CommandButton.ICON_ALBUM)
.setPlayerCommand(Player.COMMAND_PREPARE)
.setSlots(
CommandButton.SLOT_BACK,
CommandButton.SLOT_FORWARD,
CommandButton.SLOT_OVERFLOW)
.build(),
new CommandButton.Builder(CommandButton.ICON_SHUFFLE_ON)
.setPlayerCommand(Player.COMMAND_SEEK_TO_NEXT)
.setSlots(CommandButton.SLOT_FORWARD, CommandButton.SLOT_OVERFLOW)
.build(),
new CommandButton.Builder(CommandButton.ICON_ARTIST)
.setPlayerCommand(Player.COMMAND_SEEK_TO_PREVIOUS)
.setSlots(CommandButton.SLOT_OVERFLOW)
.build())
.inOrder();
}
@Test
public void
getMediaButtonPreferencesFromCustomLayout_withoutPrevNextCommandsAndPrevReservation_returnsCorrectSlots() {
ImmutableList<CommandButton> customLayout =
ImmutableList.of(
new CommandButton.Builder(CommandButton.ICON_ALBUM)
.setPlayerCommand(Player.COMMAND_PREPARE)
.build(),
new CommandButton.Builder(CommandButton.ICON_SHUFFLE_ON)
.setPlayerCommand(Player.COMMAND_SEEK_TO_NEXT)
.build(),
new CommandButton.Builder(CommandButton.ICON_ARTIST)
.setPlayerCommand(Player.COMMAND_SEEK_TO_PREVIOUS)
.build());
Bundle reservationBundle = new Bundle();
reservationBundle.putBoolean(MediaConstants.EXTRAS_KEY_SLOT_RESERVATION_SEEK_TO_PREV, true);
reservationBundle.putBoolean(MediaConstants.EXTRAS_KEY_SLOT_RESERVATION_SEEK_TO_NEXT, false);
Player.Commands playerCommands = Player.Commands.EMPTY;
ImmutableList<CommandButton> mediaButtonPreferences =
CommandButton.getMediaButtonPreferencesFromCustomLayout(
customLayout, playerCommands, reservationBundle);
assertThat(mediaButtonPreferences)
.containsExactly(
new CommandButton.Builder(CommandButton.ICON_ALBUM)
.setPlayerCommand(Player.COMMAND_PREPARE)
.setSlots(CommandButton.SLOT_FORWARD, CommandButton.SLOT_OVERFLOW)
.build(),
new CommandButton.Builder(CommandButton.ICON_SHUFFLE_ON)
.setPlayerCommand(Player.COMMAND_SEEK_TO_NEXT)
.setSlots(CommandButton.SLOT_OVERFLOW)
.build(),
new CommandButton.Builder(CommandButton.ICON_ARTIST)
.setPlayerCommand(Player.COMMAND_SEEK_TO_PREVIOUS)
.setSlots(CommandButton.SLOT_OVERFLOW)
.build())
.inOrder();
}
@Test
public void
getMediaButtonPreferencesFromCustomLayout_withoutPrevNextCommandsAndNextReservation_returnsCorrectSlots() {
ImmutableList<CommandButton> customLayout =
ImmutableList.of(
new CommandButton.Builder(CommandButton.ICON_ALBUM)
.setPlayerCommand(Player.COMMAND_PREPARE)
.build(),
new CommandButton.Builder(CommandButton.ICON_SHUFFLE_ON)
.setPlayerCommand(Player.COMMAND_SEEK_TO_NEXT)
.build(),
new CommandButton.Builder(CommandButton.ICON_ARTIST)
.setPlayerCommand(Player.COMMAND_SEEK_TO_PREVIOUS)
.build());
Bundle reservationBundle = new Bundle();
reservationBundle.putBoolean(MediaConstants.EXTRAS_KEY_SLOT_RESERVATION_SEEK_TO_PREV, false);
reservationBundle.putBoolean(MediaConstants.EXTRAS_KEY_SLOT_RESERVATION_SEEK_TO_NEXT, true);
Player.Commands playerCommands = Player.Commands.EMPTY;
ImmutableList<CommandButton> mediaButtonPreferences =
CommandButton.getMediaButtonPreferencesFromCustomLayout(
customLayout, playerCommands, reservationBundle);
assertThat(mediaButtonPreferences)
.containsExactly(
new CommandButton.Builder(CommandButton.ICON_ALBUM)
.setPlayerCommand(Player.COMMAND_PREPARE)
.setSlots(CommandButton.SLOT_BACK, CommandButton.SLOT_OVERFLOW)
.build(),
new CommandButton.Builder(CommandButton.ICON_SHUFFLE_ON)
.setPlayerCommand(Player.COMMAND_SEEK_TO_NEXT)
.setSlots(CommandButton.SLOT_OVERFLOW)
.build(),
new CommandButton.Builder(CommandButton.ICON_ARTIST)
.setPlayerCommand(Player.COMMAND_SEEK_TO_PREVIOUS)
.setSlots(CommandButton.SLOT_OVERFLOW)
.build())
.inOrder();
}
@Test
public void
getMediaButtonPreferencesFromCustomLayout_withoutPrevNextCommandsAndPrevNextReservations_returnsCorrectSlots() {
ImmutableList<CommandButton> customLayout =
ImmutableList.of(
new CommandButton.Builder(CommandButton.ICON_ALBUM)
.setPlayerCommand(Player.COMMAND_PREPARE)
.build(),
new CommandButton.Builder(CommandButton.ICON_SHUFFLE_ON)
.setPlayerCommand(Player.COMMAND_SEEK_TO_NEXT)
.build(),
new CommandButton.Builder(CommandButton.ICON_ARTIST)
.setPlayerCommand(Player.COMMAND_SEEK_TO_PREVIOUS)
.build());
Bundle reservationBundle = new Bundle();
reservationBundle.putBoolean(MediaConstants.EXTRAS_KEY_SLOT_RESERVATION_SEEK_TO_PREV, true);
reservationBundle.putBoolean(MediaConstants.EXTRAS_KEY_SLOT_RESERVATION_SEEK_TO_NEXT, true);
Player.Commands playerCommands = Player.Commands.EMPTY;
ImmutableList<CommandButton> mediaButtonPreferences =
CommandButton.getMediaButtonPreferencesFromCustomLayout(
customLayout, playerCommands, reservationBundle);
assertThat(mediaButtonPreferences)
.containsExactly(
new CommandButton.Builder(CommandButton.ICON_ALBUM)
.setPlayerCommand(Player.COMMAND_PREPARE)
.setSlots(CommandButton.SLOT_OVERFLOW)
.build(),
new CommandButton.Builder(CommandButton.ICON_SHUFFLE_ON)
.setPlayerCommand(Player.COMMAND_SEEK_TO_NEXT)
.setSlots(CommandButton.SLOT_OVERFLOW)
.build(),
new CommandButton.Builder(CommandButton.ICON_ARTIST)
.setPlayerCommand(Player.COMMAND_SEEK_TO_PREVIOUS)
.setSlots(CommandButton.SLOT_OVERFLOW)
.build())
.inOrder();
}
}

View File

@ -1081,7 +1081,10 @@ public final class LegacyConversionsTest {
@Test
public void convertToMediaButtonPreferences_withNull_returnsEmptyList() {
assertThat(LegacyConversions.convertToMediaButtonPreferences(null)).isEmpty();
assertThat(
LegacyConversions.convertToMediaButtonPreferences(
null, Player.Commands.EMPTY, Bundle.EMPTY))
.isEmpty();
}
@Test
@ -1107,7 +1110,9 @@ public final class LegacyConversionsTest {
.addCustomAction(action)
.build();
ImmutableList<CommandButton> buttons = LegacyConversions.convertToMediaButtonPreferences(state);
ImmutableList<CommandButton> buttons =
LegacyConversions.convertToMediaButtonPreferences(
state, Player.Commands.EMPTY, Bundle.EMPTY);
assertThat(buttons).hasSize(1);
CommandButton button = buttons.get(0);
@ -1140,7 +1145,9 @@ public final class LegacyConversionsTest {
.addCustomAction(action)
.build();
ImmutableList<CommandButton> buttons = LegacyConversions.convertToMediaButtonPreferences(state);
ImmutableList<CommandButton> buttons =
LegacyConversions.convertToMediaButtonPreferences(
state, Player.Commands.EMPTY, Bundle.EMPTY);
assertThat(buttons).hasSize(1);
CommandButton button = buttons.get(0);

View File

@ -47,6 +47,7 @@ import androidx.test.core.app.ApplicationProvider;
import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.LargeTest;
import com.google.common.collect.ImmutableList;
import com.google.common.primitives.ImmutableIntArray;
import com.google.common.util.concurrent.Futures;
import com.google.common.util.concurrent.ListenableFuture;
import java.util.ArrayList;
@ -475,11 +476,16 @@ public class MediaControllerListenerWithMediaSessionCompatTest {
.setDisplayName("button1")
.setIconResId(R.drawable.media3_notification_small_icon)
.setSessionCommand(new SessionCommand("command1", Bundle.EMPTY))
.setEnabled(true)
.setSlots(
CommandButton.SLOT_BACK, CommandButton.SLOT_FORWARD, CommandButton.SLOT_OVERFLOW)
.build();
CommandButton button2 =
new CommandButton.Builder(CommandButton.ICON_FAST_FORWARD)
.setDisplayName("button2")
.setSessionCommand(new SessionCommand("command2", Bundle.EMPTY))
.setEnabled(true)
.setSlots(CommandButton.SLOT_FORWARD, CommandButton.SLOT_OVERFLOW)
.build();
ConditionVariable onSetCustomLayoutCalled = new ConditionVariable();
ConditionVariable onCustomLayoutChangedCalled = new ConditionVariable();
@ -537,10 +543,8 @@ public class MediaControllerListenerWithMediaSessionCompatTest {
assertThat(onSetCustomLayoutCalled.block(TIMEOUT_MS)).isTrue();
assertThat(onCustomLayoutChangedCalled.block(TIMEOUT_MS)).isTrue();
ImmutableList<CommandButton> expectedFirstCustomLayout =
ImmutableList.of(button1.copyWithIsEnabled(true), button2.copyWithIsEnabled(true));
ImmutableList<CommandButton> expectedSecondCustomLayout =
ImmutableList.of(button1.copyWithIsEnabled(true));
ImmutableList<CommandButton> expectedFirstCustomLayout = ImmutableList.of(button1, button2);
ImmutableList<CommandButton> expectedSecondCustomLayout = ImmutableList.of(button1);
assertThat(setCustomLayoutArguments)
.containsExactly(expectedFirstCustomLayout, expectedSecondCustomLayout)
.inOrder();
@ -559,11 +563,16 @@ public class MediaControllerListenerWithMediaSessionCompatTest {
.setDisplayName("button1")
.setIconResId(R.drawable.media3_notification_small_icon)
.setSessionCommand(new SessionCommand("command1", Bundle.EMPTY))
.setEnabled(true)
.setSlots(
CommandButton.SLOT_BACK, CommandButton.SLOT_FORWARD, CommandButton.SLOT_OVERFLOW)
.build();
CommandButton button2 =
new CommandButton.Builder(CommandButton.ICON_FAST_FORWARD)
.setDisplayName("button2")
.setSessionCommand(new SessionCommand("command2", Bundle.EMPTY))
.setEnabled(true)
.setSlots(CommandButton.SLOT_FORWARD, CommandButton.SLOT_OVERFLOW)
.build();
ConditionVariable onMediaButtonPreferencesChangedCalled = new ConditionVariable();
List<List<CommandButton>> onMediaButtonPreferencesChangedArguments = new ArrayList<>();
@ -609,9 +618,8 @@ public class MediaControllerListenerWithMediaSessionCompatTest {
assertThat(onMediaButtonPreferencesChangedCalled.block(TIMEOUT_MS)).isTrue();
ImmutableList<CommandButton> expectedFirstMediaButtonPreferences =
ImmutableList.of(button1.copyWithIsEnabled(true), button2.copyWithIsEnabled(true));
ImmutableList<CommandButton> expectedSecondMediaButtonPreferences =
ImmutableList.of(button1.copyWithIsEnabled(true));
ImmutableList.of(button1, button2);
ImmutableList<CommandButton> expectedSecondMediaButtonPreferences = ImmutableList.of(button1);
assertThat(onMediaButtonPreferencesChangedArguments)
.containsExactly(expectedFirstMediaButtonPreferences, expectedSecondMediaButtonPreferences)
.inOrder();
@ -620,6 +628,177 @@ public class MediaControllerListenerWithMediaSessionCompatTest {
.inOrder();
}
@Test
public void getMediaButtonPreferences_withPrevNextActions() throws Exception {
CommandButton button1 =
new CommandButton.Builder(CommandButton.ICON_UNDEFINED)
.setDisplayName("button1")
.setIconResId(R.drawable.media3_notification_small_icon)
.setSessionCommand(new SessionCommand("command1", Bundle.EMPTY))
.build();
CommandButton button2 =
new CommandButton.Builder(CommandButton.ICON_FAST_FORWARD)
.setDisplayName("button2")
.setSessionCommand(new SessionCommand("command2", Bundle.EMPTY))
.build();
ConditionVariable onMediaButtonPreferencesChangedCalled = new ConditionVariable();
List<List<CommandButton>> reportedMediaButtonPreferences = new ArrayList<>();
controllerTestRule.createController(
session.getSessionToken(),
new MediaController.Listener() {
@Override
public void onMediaButtonPreferencesChanged(
MediaController controller, List<CommandButton> mediaButtonPreferences) {
reportedMediaButtonPreferences.add(mediaButtonPreferences);
onMediaButtonPreferencesChangedCalled.open();
}
});
Bundle extras1 = new Bundle();
extras1.putString("key", "value-1");
PlaybackStateCompat.CustomAction customAction1 =
new PlaybackStateCompat.CustomAction.Builder(
"command1", "button1", /* icon= */ R.drawable.media3_notification_small_icon)
.setExtras(extras1)
.build();
Bundle extras2 = new Bundle();
extras2.putString("key", "value-2");
extras2.putInt(
MediaConstants.EXTRAS_KEY_COMMAND_BUTTON_ICON_COMPAT, CommandButton.ICON_FAST_FORWARD);
PlaybackStateCompat.CustomAction customAction2 =
new PlaybackStateCompat.CustomAction.Builder(
"command2", "button2", /* icon= */ R.drawable.media3_icon_fast_forward)
.setExtras(extras2)
.build();
PlaybackStateCompat playbackStatePrev =
new PlaybackStateCompat.Builder()
.addCustomAction(customAction1)
.addCustomAction(customAction2)
.setActions(PlaybackStateCompat.ACTION_SKIP_TO_PREVIOUS)
.build();
PlaybackStateCompat playbackStateNext =
new PlaybackStateCompat.Builder()
.addCustomAction(customAction1)
.addCustomAction(customAction2)
.setActions(PlaybackStateCompat.ACTION_SKIP_TO_NEXT)
.build();
PlaybackStateCompat playbackStatePrevNext =
new PlaybackStateCompat.Builder()
.addCustomAction(customAction1)
.addCustomAction(customAction2)
.setActions(
PlaybackStateCompat.ACTION_SKIP_TO_PREVIOUS
| PlaybackStateCompat.ACTION_SKIP_TO_NEXT)
.build();
session.setPlaybackState(playbackStatePrev);
assertThat(onMediaButtonPreferencesChangedCalled.block(TIMEOUT_MS)).isTrue();
onMediaButtonPreferencesChangedCalled.close();
session.setPlaybackState(playbackStateNext);
assertThat(onMediaButtonPreferencesChangedCalled.block(TIMEOUT_MS)).isTrue();
onMediaButtonPreferencesChangedCalled.close();
session.setPlaybackState(playbackStatePrevNext);
assertThat(onMediaButtonPreferencesChangedCalled.block(TIMEOUT_MS)).isTrue();
assertThat(reportedMediaButtonPreferences)
.containsExactly(
ImmutableList.of(
button1.copyWithSlots(
ImmutableIntArray.of(CommandButton.SLOT_FORWARD, CommandButton.SLOT_OVERFLOW)),
button2.copyWithSlots(ImmutableIntArray.of(CommandButton.SLOT_OVERFLOW))),
ImmutableList.of(
button1.copyWithSlots(
ImmutableIntArray.of(CommandButton.SLOT_BACK, CommandButton.SLOT_OVERFLOW)),
button2.copyWithSlots(ImmutableIntArray.of(CommandButton.SLOT_OVERFLOW))),
ImmutableList.of(
button1.copyWithSlots(ImmutableIntArray.of(CommandButton.SLOT_OVERFLOW)),
button2.copyWithSlots(ImmutableIntArray.of(CommandButton.SLOT_OVERFLOW))))
.inOrder();
}
@Test
public void getMediaButtonPreferences_withSlotReservations() throws Exception {
CommandButton button1 =
new CommandButton.Builder(CommandButton.ICON_UNDEFINED)
.setDisplayName("button1")
.setIconResId(R.drawable.media3_notification_small_icon)
.setSessionCommand(new SessionCommand("command1", Bundle.EMPTY))
.build();
CommandButton button2 =
new CommandButton.Builder(CommandButton.ICON_FAST_FORWARD)
.setDisplayName("button2")
.setSessionCommand(new SessionCommand("command2", Bundle.EMPTY))
.build();
ConditionVariable onMediaButtonPreferencesChangedCalled = new ConditionVariable();
List<List<CommandButton>> reportedMediaButtonPreferences = new ArrayList<>();
controllerTestRule.createController(
session.getSessionToken(),
new MediaController.Listener() {
@Override
public void onMediaButtonPreferencesChanged(
MediaController controller, List<CommandButton> mediaButtonPreferences) {
reportedMediaButtonPreferences.add(mediaButtonPreferences);
onMediaButtonPreferencesChangedCalled.open();
}
});
Bundle extras1 = new Bundle();
extras1.putString("key", "value-1");
PlaybackStateCompat.CustomAction customAction1 =
new PlaybackStateCompat.CustomAction.Builder(
"command1", "button1", /* icon= */ R.drawable.media3_notification_small_icon)
.setExtras(extras1)
.build();
Bundle extras2 = new Bundle();
extras2.putString("key", "value-2");
extras2.putInt(
MediaConstants.EXTRAS_KEY_COMMAND_BUTTON_ICON_COMPAT, CommandButton.ICON_FAST_FORWARD);
PlaybackStateCompat.CustomAction customAction2 =
new PlaybackStateCompat.CustomAction.Builder(
"command2", "button2", /* icon= */ R.drawable.media3_icon_fast_forward)
.setExtras(extras2)
.build();
PlaybackStateCompat playbackState =
new PlaybackStateCompat.Builder()
.addCustomAction(customAction1)
.addCustomAction(customAction2)
.build();
Bundle extrasPrevSlotReservation = new Bundle();
extrasPrevSlotReservation.putBoolean(
androidx.media.utils.MediaConstants.SESSION_EXTRAS_KEY_SLOT_RESERVATION_SKIP_TO_PREV, true);
Bundle extrasNextSlotReservation = new Bundle();
extrasNextSlotReservation.putBoolean(
androidx.media.utils.MediaConstants.SESSION_EXTRAS_KEY_SLOT_RESERVATION_SKIP_TO_NEXT, true);
Bundle extrasPrevNextSlotReservation = new Bundle();
extrasPrevNextSlotReservation.putBoolean(
androidx.media.utils.MediaConstants.SESSION_EXTRAS_KEY_SLOT_RESERVATION_SKIP_TO_PREV, true);
extrasPrevNextSlotReservation.putBoolean(
androidx.media.utils.MediaConstants.SESSION_EXTRAS_KEY_SLOT_RESERVATION_SKIP_TO_NEXT, true);
session.setExtras(extrasPrevSlotReservation);
session.setPlaybackState(playbackState);
assertThat(onMediaButtonPreferencesChangedCalled.block(TIMEOUT_MS)).isTrue();
onMediaButtonPreferencesChangedCalled.close();
session.setExtras(extrasNextSlotReservation);
assertThat(onMediaButtonPreferencesChangedCalled.block(TIMEOUT_MS)).isTrue();
onMediaButtonPreferencesChangedCalled.close();
session.setExtras(extrasPrevNextSlotReservation);
assertThat(onMediaButtonPreferencesChangedCalled.block(TIMEOUT_MS)).isTrue();
assertThat(reportedMediaButtonPreferences)
.containsExactly(
ImmutableList.of(
button1.copyWithSlots(
ImmutableIntArray.of(CommandButton.SLOT_FORWARD, CommandButton.SLOT_OVERFLOW)),
button2.copyWithSlots(ImmutableIntArray.of(CommandButton.SLOT_OVERFLOW))),
ImmutableList.of(
button1.copyWithSlots(
ImmutableIntArray.of(CommandButton.SLOT_BACK, CommandButton.SLOT_OVERFLOW)),
button2.copyWithSlots(ImmutableIntArray.of(CommandButton.SLOT_OVERFLOW))),
ImmutableList.of(
button1.copyWithSlots(ImmutableIntArray.of(CommandButton.SLOT_OVERFLOW)),
button2.copyWithSlots(ImmutableIntArray.of(CommandButton.SLOT_OVERFLOW))))
.inOrder();
}
@Test
public void getCurrentPosition_unknownPlaybackPosition_convertedToZero() throws Exception {
session.setPlaybackState(

View File

@ -70,6 +70,7 @@ import androidx.test.core.app.ApplicationProvider;
import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.LargeTest;
import com.google.common.collect.ImmutableList;
import com.google.common.primitives.ImmutableIntArray;
import com.google.common.util.concurrent.Futures;
import com.google.common.util.concurrent.ListenableFuture;
import java.util.ArrayList;
@ -215,11 +216,11 @@ public class MediaControllerTest {
assertThat(threadTestRule.getHandler().postAndSync(controller::getCustomLayout))
.containsExactly(
button1.copyWithIsEnabled(true),
button2.copyWithIsEnabled(false),
button3.copyWithIsEnabled(false),
button4.copyWithIsEnabled(true),
button5.copyWithIsEnabled(false))
withBackForwardOverflowSlot(button1.copyWithIsEnabled(true)),
withForwardOverflowSlot(button2.copyWithIsEnabled(false)),
withOverflowSlot(button3.copyWithIsEnabled(false)),
withOverflowSlot(button4.copyWithIsEnabled(true)),
withOverflowSlot(button5.copyWithIsEnabled(false)))
.inOrder();
session.cleanUp();
@ -299,15 +300,17 @@ public class MediaControllerTest {
threadTestRule.getHandler().postAndSync(controller::getCustomLayout);
assertThat(initialCustomLayoutFromGetter)
.containsExactly(button1.copyWithIsEnabled(true), button3.copyWithIsEnabled(false))
.containsExactly(
withBackForwardOverflowSlot(button1.copyWithIsEnabled(true)),
withForwardOverflowSlot(button3.copyWithIsEnabled(false)))
.inOrder();
ImmutableList<CommandButton> expectedNewButtons =
ImmutableList.of(
button1.copyWithIsEnabled(true),
button2.copyWithIsEnabled(false),
button4.copyWithIsEnabled(false),
button5.copyWithIsEnabled(true),
button6.copyWithIsEnabled(false));
withBackForwardOverflowSlot(button1.copyWithIsEnabled(true)),
withForwardOverflowSlot(button2.copyWithIsEnabled(false)),
withOverflowSlot(button4.copyWithIsEnabled(false)),
withOverflowSlot(button5.copyWithIsEnabled(true)),
withOverflowSlot(button6.copyWithIsEnabled(false)));
assertThat(newCustomLayoutFromGetter).containsExactlyElementsIn(expectedNewButtons).inOrder();
assertThat(reportedCustomLayout.get()).containsExactlyElementsIn(expectedNewButtons).inOrder();
assertThat(reportedCustomLayoutChanged.get())
@ -375,39 +378,39 @@ public class MediaControllerTest {
assertThat(latch.await(TIMEOUT_MS, MILLISECONDS)).isTrue();
assertThat(initialCustomLayout)
.containsExactly(
button1.copyWithIsEnabled(true),
button2.copyWithIsEnabled(false),
button3.copyWithIsEnabled(true),
button4.copyWithIsEnabled(false));
withBackForwardOverflowSlot(button1.copyWithIsEnabled(true)),
withForwardOverflowSlot(button2.copyWithIsEnabled(false)),
withOverflowSlot(button3.copyWithIsEnabled(true)),
withOverflowSlot(button4.copyWithIsEnabled(false)));
assertThat(reportedCustomLayoutChanged).hasSize(2);
assertThat(reportedCustomLayoutChanged.get(0))
.containsExactly(
button1.copyWithIsEnabled(false),
button2.copyWithIsEnabled(false),
button3.copyWithIsEnabled(false),
button4.copyWithIsEnabled(false))
withBackForwardOverflowSlot(button1.copyWithIsEnabled(false)),
withForwardOverflowSlot(button2.copyWithIsEnabled(false)),
withOverflowSlot(button3.copyWithIsEnabled(false)),
withOverflowSlot(button4.copyWithIsEnabled(false)))
.inOrder();
assertThat(reportedCustomLayoutChanged.get(1))
.containsExactly(
button1.copyWithIsEnabled(false),
button2.copyWithIsEnabled(false),
button3.copyWithIsEnabled(false),
button4.copyWithIsEnabled(true))
withBackForwardOverflowSlot(button1.copyWithIsEnabled(false)),
withForwardOverflowSlot(button2.copyWithIsEnabled(false)),
withOverflowSlot(button3.copyWithIsEnabled(false)),
withOverflowSlot(button4.copyWithIsEnabled(true)))
.inOrder();
assertThat(getterCustomLayoutChanged).hasSize(2);
assertThat(getterCustomLayoutChanged.get(0))
.containsExactly(
button1.copyWithIsEnabled(false),
button2.copyWithIsEnabled(false),
button3.copyWithIsEnabled(false),
button4.copyWithIsEnabled(false))
withBackForwardOverflowSlot(button1.copyWithIsEnabled(false)),
withForwardOverflowSlot(button2.copyWithIsEnabled(false)),
withOverflowSlot(button3.copyWithIsEnabled(false)),
withOverflowSlot(button4.copyWithIsEnabled(false)))
.inOrder();
assertThat(getterCustomLayoutChanged.get(1))
.containsExactly(
button1.copyWithIsEnabled(false),
button2.copyWithIsEnabled(false),
button3.copyWithIsEnabled(false),
button4.copyWithIsEnabled(true))
withBackForwardOverflowSlot(button1.copyWithIsEnabled(false)),
withForwardOverflowSlot(button2.copyWithIsEnabled(false)),
withOverflowSlot(button3.copyWithIsEnabled(false)),
withOverflowSlot(button4.copyWithIsEnabled(true)))
.inOrder();
session.cleanUp();
}
@ -450,13 +453,18 @@ public class MediaControllerTest {
new Player.Commands.Builder().add(Player.COMMAND_PLAY_PAUSE).build());
assertThat(latch.await(TIMEOUT_MS, MILLISECONDS)).isTrue();
assertThat(initialCustomLayout).containsExactly(button.copyWithIsEnabled(true));
assertThat(initialCustomLayout)
.containsExactly(withBackForwardOverflowSlot(button.copyWithIsEnabled(true)));
assertThat(reportedCustomLayouts).hasSize(2);
assertThat(reportedCustomLayouts.get(0)).containsExactly(button.copyWithIsEnabled(false));
assertThat(reportedCustomLayouts.get(1)).containsExactly(button.copyWithIsEnabled(true));
assertThat(reportedCustomLayouts.get(0))
.containsExactly(withBackForwardOverflowSlot(button.copyWithIsEnabled(false)));
assertThat(reportedCustomLayouts.get(1))
.containsExactly(withBackForwardOverflowSlot(button.copyWithIsEnabled(true)));
assertThat(getterCustomLayouts).hasSize(2);
assertThat(getterCustomLayouts.get(0)).containsExactly(button.copyWithIsEnabled(false));
assertThat(getterCustomLayouts.get(1)).containsExactly(button.copyWithIsEnabled(true));
assertThat(getterCustomLayouts.get(0))
.containsExactly(withBackForwardOverflowSlot(button.copyWithIsEnabled(false)));
assertThat(getterCustomLayouts.get(1))
.containsExactly(withBackForwardOverflowSlot(button.copyWithIsEnabled(true)));
session.cleanUp();
}
@ -526,36 +534,190 @@ public class MediaControllerTest {
session.setCustomLayout(ImmutableList.of(button1, button2));
assertThat(latch.await(TIMEOUT_MS, MILLISECONDS)).isTrue();
CommandButton button1Enabled = button1.copyWithIsEnabled(true);
CommandButton button2Disabled = button2.copyWithIsEnabled(false);
CommandButton button3Disabled = button3.copyWithIsEnabled(false);
CommandButton button4Disabled = button4.copyWithIsEnabled(false);
assertThat(initialCustomLayout).containsExactly(button1Enabled, button2Disabled).inOrder();
CommandButton button1EnabledBackSlot =
withBackForwardOverflowSlot(button1.copyWithIsEnabled(true));
CommandButton button2DisabledForwardSlot =
withForwardOverflowSlot(button2.copyWithIsEnabled(false));
CommandButton button3DisabledBackSlot =
withBackForwardOverflowSlot(button3.copyWithIsEnabled(false));
CommandButton button4DisabledForwardSlot =
withForwardOverflowSlot(button4.copyWithIsEnabled(false));
assertThat(initialCustomLayout)
.containsExactly(button1EnabledBackSlot, button2DisabledForwardSlot)
.inOrder();
assertThat(reportedCustomLayout)
.containsExactly(
ImmutableList.of(button1Enabled, button2Disabled),
ImmutableList.of(button3Disabled, button4Disabled),
ImmutableList.of(button1Enabled, button2Disabled))
ImmutableList.of(button1EnabledBackSlot, button2DisabledForwardSlot),
ImmutableList.of(button3DisabledBackSlot, button4DisabledForwardSlot),
ImmutableList.of(button1EnabledBackSlot, button2DisabledForwardSlot))
.inOrder();
assertThat(getterCustomLayout)
.containsExactly(
ImmutableList.of(button1Enabled, button2Disabled),
ImmutableList.of(button3Disabled, button4Disabled),
ImmutableList.of(button1Enabled, button2Disabled))
ImmutableList.of(button1EnabledBackSlot, button2DisabledForwardSlot),
ImmutableList.of(button3DisabledBackSlot, button4DisabledForwardSlot),
ImmutableList.of(button1EnabledBackSlot, button2DisabledForwardSlot))
.inOrder();
assertThat(reportedCustomLayoutChanged)
.containsExactly(
ImmutableList.of(button3Disabled, button4Disabled),
ImmutableList.of(button1Enabled, button2Disabled))
ImmutableList.of(button3DisabledBackSlot, button4DisabledForwardSlot),
ImmutableList.of(button1EnabledBackSlot, button2DisabledForwardSlot))
.inOrder();
assertThat(getterCustomLayoutChanged)
.containsExactly(
ImmutableList.of(button3Disabled, button4Disabled),
ImmutableList.of(button1Enabled, button2Disabled))
ImmutableList.of(button3DisabledBackSlot, button4DisabledForwardSlot),
ImmutableList.of(button1EnabledBackSlot, button2DisabledForwardSlot))
.inOrder();
session.cleanUp();
}
@Test
public void getCustomLayout_setAvailablePrevNextCommand_reportsCustomLayoutChanged()
throws Exception {
RemoteMediaSession session = createRemoteMediaSession(TEST_GET_CUSTOM_LAYOUT, null);
CommandButton button1 =
new CommandButton.Builder(CommandButton.ICON_ALBUM)
.setDisplayName("button1")
.setSessionCommand(new SessionCommand("command1", Bundle.EMPTY))
.build();
CommandButton button2 =
new CommandButton.Builder(CommandButton.ICON_REWIND)
.setDisplayName("button2")
.setSessionCommand(new SessionCommand("command2", Bundle.EMPTY))
.build();
CommandButton button3 =
new CommandButton.Builder(CommandButton.ICON_SHUFFLE_ON)
.setDisplayName("button3")
.setSessionCommand(new SessionCommand("command3", Bundle.EMPTY))
.build();
SessionCommands allSessionCommands =
new SessionCommands.Builder()
.add(button1.sessionCommand)
.add(button2.sessionCommand)
.add(button3.sessionCommand)
.build();
setupCustomLayout(session, ImmutableList.of(button1, button2, button3));
CountDownLatch latch = new CountDownLatch(4);
List<List<CommandButton>> reportedCustomLayouts = new ArrayList<>();
MediaController.Listener listener =
new MediaController.Listener() {
@Override
public void onCustomLayoutChanged(
MediaController controller, List<CommandButton> layout) {
reportedCustomLayouts.add(layout);
latch.countDown();
}
};
controllerTestRule.createController(
session.getToken(), /* connectionHints= */ Bundle.EMPTY, listener);
session.setAvailableCommands(allSessionCommands, Player.Commands.EMPTY);
session.setAvailableCommands(
allSessionCommands, new Player.Commands.Builder().add(Player.COMMAND_SEEK_TO_NEXT).build());
session.setAvailableCommands(
allSessionCommands,
new Player.Commands.Builder().add(Player.COMMAND_SEEK_TO_PREVIOUS).build());
session.setAvailableCommands(
allSessionCommands,
new Player.Commands.Builder()
.addAll(Player.COMMAND_SEEK_TO_NEXT, Player.COMMAND_SEEK_TO_PREVIOUS)
.build());
assertThat(latch.await(TIMEOUT_MS, MILLISECONDS)).isTrue();
assertThat(reportedCustomLayouts)
.containsExactly(
ImmutableList.of(
withBackForwardOverflowSlot(button1),
withForwardOverflowSlot(button2),
withOverflowSlot(button3)),
ImmutableList.of(
withBackOverflowSlot(button1),
withOverflowSlot(button2),
withOverflowSlot(button3)),
ImmutableList.of(
withForwardOverflowSlot(button1),
withOverflowSlot(button2),
withOverflowSlot(button3)),
ImmutableList.of(
withOverflowSlot(button1), withOverflowSlot(button2), withOverflowSlot(button3)));
session.cleanUp();
}
@Test
public void getCustomLayout_setSessionExtrasForPrevNextReservations_reportsCustomLayoutChanged()
throws Exception {
RemoteMediaSession session = createRemoteMediaSession(TEST_GET_CUSTOM_LAYOUT, null);
CommandButton button1 =
new CommandButton.Builder(CommandButton.ICON_ALBUM)
.setDisplayName("button1")
.setSessionCommand(new SessionCommand("command1", Bundle.EMPTY))
.build();
CommandButton button2 =
new CommandButton.Builder(CommandButton.ICON_REWIND)
.setDisplayName("button2")
.setSessionCommand(new SessionCommand("command2", Bundle.EMPTY))
.build();
CommandButton button3 =
new CommandButton.Builder(CommandButton.ICON_SHUFFLE_ON)
.setDisplayName("button3")
.setSessionCommand(new SessionCommand("command3", Bundle.EMPTY))
.build();
SessionCommands allSessionCommands =
new SessionCommands.Builder()
.add(button1.sessionCommand)
.add(button2.sessionCommand)
.add(button3.sessionCommand)
.build();
setupCustomLayout(session, ImmutableList.of(button1, button2, button3));
CountDownLatch latch = new CountDownLatch(4);
List<List<CommandButton>> reportedCustomLayouts = new ArrayList<>();
MediaController.Listener listener =
new MediaController.Listener() {
@Override
public void onCustomLayoutChanged(
MediaController controller, List<CommandButton> layout) {
reportedCustomLayouts.add(layout);
latch.countDown();
}
};
Bundle extrasNextSlotReservation = new Bundle();
extrasNextSlotReservation.putBoolean(
MediaConstants.EXTRAS_KEY_SLOT_RESERVATION_SEEK_TO_NEXT, true);
Bundle extrasPrevSlotReservation = new Bundle();
extrasPrevSlotReservation.putBoolean(
MediaConstants.EXTRAS_KEY_SLOT_RESERVATION_SEEK_TO_PREV, true);
Bundle extrasPrevNextSlotReservation = new Bundle();
extrasPrevNextSlotReservation.putBoolean(
MediaConstants.EXTRAS_KEY_SLOT_RESERVATION_SEEK_TO_PREV, true);
extrasPrevNextSlotReservation.putBoolean(
MediaConstants.EXTRAS_KEY_SLOT_RESERVATION_SEEK_TO_NEXT, true);
controllerTestRule.createController(
session.getToken(), /* connectionHints= */ Bundle.EMPTY, listener);
session.setAvailableCommands(allSessionCommands, Player.Commands.EMPTY);
session.setSessionExtras(extrasNextSlotReservation);
session.setSessionExtras(extrasPrevSlotReservation);
session.setSessionExtras(extrasPrevNextSlotReservation);
assertThat(latch.await(TIMEOUT_MS, MILLISECONDS)).isTrue();
assertThat(reportedCustomLayouts)
.containsExactly(
ImmutableList.of(
withBackForwardOverflowSlot(button1),
withForwardOverflowSlot(button2),
withOverflowSlot(button3)),
ImmutableList.of(
withBackOverflowSlot(button1),
withOverflowSlot(button2),
withOverflowSlot(button3)),
ImmutableList.of(
withForwardOverflowSlot(button1),
withOverflowSlot(button2),
withOverflowSlot(button3)),
ImmutableList.of(
withOverflowSlot(button1), withOverflowSlot(button2), withOverflowSlot(button3)));
session.cleanUp();
}
@Test
public void getMediaButtonPreferences_mediaButtonPreferencesBuiltWithSession_includedOnConnect()
throws Exception {
@ -2422,4 +2584,24 @@ public class MediaControllerTest {
session.setMediaButtonPreferences(ImmutableList.copyOf(mediaButtonPreferences));
assertThat(latch.await(TIMEOUT_MS, MILLISECONDS)).isTrue();
}
private static CommandButton withBackForwardOverflowSlot(CommandButton button) {
return button.copyWithSlots(
ImmutableIntArray.of(
CommandButton.SLOT_BACK, CommandButton.SLOT_FORWARD, CommandButton.SLOT_OVERFLOW));
}
private static CommandButton withBackOverflowSlot(CommandButton button) {
return button.copyWithSlots(
ImmutableIntArray.of(CommandButton.SLOT_BACK, CommandButton.SLOT_OVERFLOW));
}
private static CommandButton withForwardOverflowSlot(CommandButton button) {
return button.copyWithSlots(
ImmutableIntArray.of(CommandButton.SLOT_FORWARD, CommandButton.SLOT_OVERFLOW));
}
private static CommandButton withOverflowSlot(CommandButton button) {
return button.copyWithSlots(ImmutableIntArray.of(CommandButton.SLOT_OVERFLOW));
}
}

View File

@ -56,6 +56,7 @@ import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.LargeTest;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.Iterables;
import com.google.common.primitives.ImmutableIntArray;
import com.google.common.util.concurrent.Futures;
import com.google.common.util.concurrent.ListenableFuture;
import com.google.common.util.concurrent.ListeningExecutorService;
@ -162,7 +163,6 @@ public class MediaSessionCallbackTest {
.setSessionCommand(new SessionCommand("command1", Bundle.EMPTY))
.setEnabled(true)
.build();
CommandButton button1Disabled = button1.copyWithIsEnabled(false);
CommandButton button2 =
new CommandButton.Builder(CommandButton.ICON_PAUSE)
.setDisplayName("button2")
@ -177,6 +177,7 @@ public class MediaSessionCallbackTest {
return new AcceptedResultBuilder(session)
.setAvailableSessionCommands(
new SessionCommands.Builder().add(button2.sessionCommand).build())
.setAvailablePlayerCommands(new Player.Commands.Builder().addAllCommands().build())
.setCustomLayout(ImmutableList.of(button1, button2))
.build();
}
@ -198,7 +199,13 @@ public class MediaSessionCallbackTest {
ImmutableList<CommandButton> layout = remoteController.getCustomLayout();
assertThat(layout).containsExactly(button1Disabled, button2).inOrder();
assertThat(layout)
.containsExactly(
button1
.copyWithIsEnabled(false)
.copyWithSlots(ImmutableIntArray.of(CommandButton.SLOT_OVERFLOW)),
button2.copyWithSlots(ImmutableIntArray.of(CommandButton.SLOT_OVERFLOW)))
.inOrder();
assertThat(remoteController.sendCustomCommand(button1.sessionCommand, Bundle.EMPTY).resultCode)
.isEqualTo(ERROR_PERMISSION_DENIED);
assertThat(remoteController.sendCustomCommand(button2.sessionCommand, Bundle.EMPTY).resultCode)

View File

@ -47,6 +47,7 @@ import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.MediumTest;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.Lists;
import com.google.common.primitives.ImmutableIntArray;
import com.google.common.util.concurrent.ListenableFuture;
import com.google.common.util.concurrent.SettableFuture;
import java.util.ArrayList;
@ -331,7 +332,14 @@ public class MediaSessionServiceTest {
assertThat(mediaControllerCompat.getPlaybackState().getActions())
.isEqualTo(PlaybackStateCompat.ACTION_SET_RATING);
assertThat(remoteController.getCustomLayout())
.containsExactly(button1.copyWithIsEnabled(false), button2.copyWithIsEnabled(false))
.containsExactly(
button1
.copyWithIsEnabled(false)
.copyWithSlots(
ImmutableIntArray.of(CommandButton.SLOT_FORWARD, CommandButton.SLOT_OVERFLOW)),
button2
.copyWithIsEnabled(false)
.copyWithSlots(ImmutableIntArray.of(CommandButton.SLOT_OVERFLOW)))
.inOrder();
assertThat(initialCustomActionsInControllerCompat).isEmpty();
assertThat(mediaControllerCompat.getPlaybackState().getCustomActions()).hasSize(2);