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.DrawableRes;
import androidx.annotation.IntDef; import androidx.annotation.IntDef;
import androidx.annotation.Nullable; import androidx.annotation.Nullable;
import androidx.media3.common.C;
import androidx.media3.common.Player; import androidx.media3.common.Player;
import androidx.media3.common.util.UnstableApi; import androidx.media3.common.util.UnstableApi;
import androidx.media3.common.util.Util; import androidx.media3.common.util.Util;
@ -799,6 +800,24 @@ public final class CommandButton {
slots); 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}. */ /** Checks the given command button for equality while ignoring {@link #extras}. */
@Override @Override
public boolean equals(@Nullable Object obj) { public boolean equals(@Nullable Object obj) {
@ -1153,4 +1172,62 @@ public final class CommandButton {
return SLOT_OVERFLOW; 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. * Converts {@link CustomAction} in the {@link PlaybackStateCompat} to media button preferences.
* *
* @param state The {@link PlaybackStateCompat}. * @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. * @return The media button preferences.
*/ */
public static ImmutableList<CommandButton> convertToMediaButtonPreferences( public static ImmutableList<CommandButton> convertToMediaButtonPreferences(
@Nullable PlaybackStateCompat state) { @Nullable PlaybackStateCompat state,
Player.Commands availablePlayerCommands,
Bundle sessionExtras) {
if (state == null) { if (state == null) {
return ImmutableList.of(); return ImmutableList.of();
} }
@ -1508,7 +1512,7 @@ import java.util.concurrent.TimeoutException;
if (customActions == null) { if (customActions == null) {
return ImmutableList.of(); return ImmutableList.of();
} }
ImmutableList.Builder<CommandButton> mediaButtonPreferences = new ImmutableList.Builder<>(); ImmutableList.Builder<CommandButton> customLayout = new ImmutableList.Builder<>();
for (CustomAction customAction : customActions) { for (CustomAction customAction : customActions) {
String action = customAction.getAction(); String action = customAction.getAction();
@Nullable Bundle extras = customAction.getExtras(); @Nullable Bundle extras = customAction.getExtras();
@ -1519,16 +1523,16 @@ import java.util.concurrent.TimeoutException;
MediaConstants.EXTRAS_KEY_COMMAND_BUTTON_ICON_COMPAT, MediaConstants.EXTRAS_KEY_COMMAND_BUTTON_ICON_COMPAT,
/* defaultValue= */ CommandButton.ICON_UNDEFINED) /* defaultValue= */ CommandButton.ICON_UNDEFINED)
: CommandButton.ICON_UNDEFINED; : CommandButton.ICON_UNDEFINED;
// TODO: b/332877990 - Set appropriate slots based on available player commands.
CommandButton button = CommandButton button =
new CommandButton.Builder(icon, customAction.getIcon()) new CommandButton.Builder(icon, customAction.getIcon())
.setSessionCommand(new SessionCommand(action, extras == null ? Bundle.EMPTY : extras)) .setSessionCommand(new SessionCommand(action, extras == null ? Bundle.EMPTY : extras))
.setDisplayName(customAction.getName()) .setDisplayName(customAction.getName())
.setEnabled(true) .setEnabled(true)
.build(); .build();
mediaButtonPreferences.add(button); customLayout.add(button);
} }
return mediaButtonPreferences.build(); return CommandButton.getMediaButtonPreferencesFromCustomLayout(
customLayout.build(), availablePlayerCommands, sessionExtras);
} }
/** Converts {@link AudioAttributesCompat} into {@link AudioAttributes}. */ /** Converts {@link AudioAttributesCompat} into {@link AudioAttributes}. */

View file

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

View file

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

View file

@ -543,4 +543,374 @@ public class CommandButtonTest {
assertThat(restoredButtonAssumingOldSessionInterface.isEnabled).isTrue(); 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 @Test
public void convertToMediaButtonPreferences_withNull_returnsEmptyList() { public void convertToMediaButtonPreferences_withNull_returnsEmptyList() {
assertThat(LegacyConversions.convertToMediaButtonPreferences(null)).isEmpty(); assertThat(
LegacyConversions.convertToMediaButtonPreferences(
null, Player.Commands.EMPTY, Bundle.EMPTY))
.isEmpty();
} }
@Test @Test
@ -1107,7 +1110,9 @@ public final class LegacyConversionsTest {
.addCustomAction(action) .addCustomAction(action)
.build(); .build();
ImmutableList<CommandButton> buttons = LegacyConversions.convertToMediaButtonPreferences(state); ImmutableList<CommandButton> buttons =
LegacyConversions.convertToMediaButtonPreferences(
state, Player.Commands.EMPTY, Bundle.EMPTY);
assertThat(buttons).hasSize(1); assertThat(buttons).hasSize(1);
CommandButton button = buttons.get(0); CommandButton button = buttons.get(0);
@ -1140,7 +1145,9 @@ public final class LegacyConversionsTest {
.addCustomAction(action) .addCustomAction(action)
.build(); .build();
ImmutableList<CommandButton> buttons = LegacyConversions.convertToMediaButtonPreferences(state); ImmutableList<CommandButton> buttons =
LegacyConversions.convertToMediaButtonPreferences(
state, Player.Commands.EMPTY, Bundle.EMPTY);
assertThat(buttons).hasSize(1); assertThat(buttons).hasSize(1);
CommandButton button = buttons.get(0); 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.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.LargeTest; import androidx.test.filters.LargeTest;
import com.google.common.collect.ImmutableList; 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.Futures;
import com.google.common.util.concurrent.ListenableFuture; import com.google.common.util.concurrent.ListenableFuture;
import java.util.ArrayList; import java.util.ArrayList;
@ -475,11 +476,16 @@ public class MediaControllerListenerWithMediaSessionCompatTest {
.setDisplayName("button1") .setDisplayName("button1")
.setIconResId(R.drawable.media3_notification_small_icon) .setIconResId(R.drawable.media3_notification_small_icon)
.setSessionCommand(new SessionCommand("command1", Bundle.EMPTY)) .setSessionCommand(new SessionCommand("command1", Bundle.EMPTY))
.setEnabled(true)
.setSlots(
CommandButton.SLOT_BACK, CommandButton.SLOT_FORWARD, CommandButton.SLOT_OVERFLOW)
.build(); .build();
CommandButton button2 = CommandButton button2 =
new CommandButton.Builder(CommandButton.ICON_FAST_FORWARD) new CommandButton.Builder(CommandButton.ICON_FAST_FORWARD)
.setDisplayName("button2") .setDisplayName("button2")
.setSessionCommand(new SessionCommand("command2", Bundle.EMPTY)) .setSessionCommand(new SessionCommand("command2", Bundle.EMPTY))
.setEnabled(true)
.setSlots(CommandButton.SLOT_FORWARD, CommandButton.SLOT_OVERFLOW)
.build(); .build();
ConditionVariable onSetCustomLayoutCalled = new ConditionVariable(); ConditionVariable onSetCustomLayoutCalled = new ConditionVariable();
ConditionVariable onCustomLayoutChangedCalled = new ConditionVariable(); ConditionVariable onCustomLayoutChangedCalled = new ConditionVariable();
@ -537,10 +543,8 @@ public class MediaControllerListenerWithMediaSessionCompatTest {
assertThat(onSetCustomLayoutCalled.block(TIMEOUT_MS)).isTrue(); assertThat(onSetCustomLayoutCalled.block(TIMEOUT_MS)).isTrue();
assertThat(onCustomLayoutChangedCalled.block(TIMEOUT_MS)).isTrue(); assertThat(onCustomLayoutChangedCalled.block(TIMEOUT_MS)).isTrue();
ImmutableList<CommandButton> expectedFirstCustomLayout = ImmutableList<CommandButton> expectedFirstCustomLayout = ImmutableList.of(button1, button2);
ImmutableList.of(button1.copyWithIsEnabled(true), button2.copyWithIsEnabled(true)); ImmutableList<CommandButton> expectedSecondCustomLayout = ImmutableList.of(button1);
ImmutableList<CommandButton> expectedSecondCustomLayout =
ImmutableList.of(button1.copyWithIsEnabled(true));
assertThat(setCustomLayoutArguments) assertThat(setCustomLayoutArguments)
.containsExactly(expectedFirstCustomLayout, expectedSecondCustomLayout) .containsExactly(expectedFirstCustomLayout, expectedSecondCustomLayout)
.inOrder(); .inOrder();
@ -559,11 +563,16 @@ public class MediaControllerListenerWithMediaSessionCompatTest {
.setDisplayName("button1") .setDisplayName("button1")
.setIconResId(R.drawable.media3_notification_small_icon) .setIconResId(R.drawable.media3_notification_small_icon)
.setSessionCommand(new SessionCommand("command1", Bundle.EMPTY)) .setSessionCommand(new SessionCommand("command1", Bundle.EMPTY))
.setEnabled(true)
.setSlots(
CommandButton.SLOT_BACK, CommandButton.SLOT_FORWARD, CommandButton.SLOT_OVERFLOW)
.build(); .build();
CommandButton button2 = CommandButton button2 =
new CommandButton.Builder(CommandButton.ICON_FAST_FORWARD) new CommandButton.Builder(CommandButton.ICON_FAST_FORWARD)
.setDisplayName("button2") .setDisplayName("button2")
.setSessionCommand(new SessionCommand("command2", Bundle.EMPTY)) .setSessionCommand(new SessionCommand("command2", Bundle.EMPTY))
.setEnabled(true)
.setSlots(CommandButton.SLOT_FORWARD, CommandButton.SLOT_OVERFLOW)
.build(); .build();
ConditionVariable onMediaButtonPreferencesChangedCalled = new ConditionVariable(); ConditionVariable onMediaButtonPreferencesChangedCalled = new ConditionVariable();
List<List<CommandButton>> onMediaButtonPreferencesChangedArguments = new ArrayList<>(); List<List<CommandButton>> onMediaButtonPreferencesChangedArguments = new ArrayList<>();
@ -609,9 +618,8 @@ public class MediaControllerListenerWithMediaSessionCompatTest {
assertThat(onMediaButtonPreferencesChangedCalled.block(TIMEOUT_MS)).isTrue(); assertThat(onMediaButtonPreferencesChangedCalled.block(TIMEOUT_MS)).isTrue();
ImmutableList<CommandButton> expectedFirstMediaButtonPreferences = ImmutableList<CommandButton> expectedFirstMediaButtonPreferences =
ImmutableList.of(button1.copyWithIsEnabled(true), button2.copyWithIsEnabled(true)); ImmutableList.of(button1, button2);
ImmutableList<CommandButton> expectedSecondMediaButtonPreferences = ImmutableList<CommandButton> expectedSecondMediaButtonPreferences = ImmutableList.of(button1);
ImmutableList.of(button1.copyWithIsEnabled(true));
assertThat(onMediaButtonPreferencesChangedArguments) assertThat(onMediaButtonPreferencesChangedArguments)
.containsExactly(expectedFirstMediaButtonPreferences, expectedSecondMediaButtonPreferences) .containsExactly(expectedFirstMediaButtonPreferences, expectedSecondMediaButtonPreferences)
.inOrder(); .inOrder();
@ -620,6 +628,177 @@ public class MediaControllerListenerWithMediaSessionCompatTest {
.inOrder(); .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 @Test
public void getCurrentPosition_unknownPlaybackPosition_convertedToZero() throws Exception { public void getCurrentPosition_unknownPlaybackPosition_convertedToZero() throws Exception {
session.setPlaybackState( session.setPlaybackState(

View file

@ -70,6 +70,7 @@ import androidx.test.core.app.ApplicationProvider;
import androidx.test.ext.junit.runners.AndroidJUnit4; import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.LargeTest; import androidx.test.filters.LargeTest;
import com.google.common.collect.ImmutableList; 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.Futures;
import com.google.common.util.concurrent.ListenableFuture; import com.google.common.util.concurrent.ListenableFuture;
import java.util.ArrayList; import java.util.ArrayList;
@ -215,11 +216,11 @@ public class MediaControllerTest {
assertThat(threadTestRule.getHandler().postAndSync(controller::getCustomLayout)) assertThat(threadTestRule.getHandler().postAndSync(controller::getCustomLayout))
.containsExactly( .containsExactly(
button1.copyWithIsEnabled(true), withBackForwardOverflowSlot(button1.copyWithIsEnabled(true)),
button2.copyWithIsEnabled(false), withForwardOverflowSlot(button2.copyWithIsEnabled(false)),
button3.copyWithIsEnabled(false), withOverflowSlot(button3.copyWithIsEnabled(false)),
button4.copyWithIsEnabled(true), withOverflowSlot(button4.copyWithIsEnabled(true)),
button5.copyWithIsEnabled(false)) withOverflowSlot(button5.copyWithIsEnabled(false)))
.inOrder(); .inOrder();
session.cleanUp(); session.cleanUp();
@ -299,15 +300,17 @@ public class MediaControllerTest {
threadTestRule.getHandler().postAndSync(controller::getCustomLayout); threadTestRule.getHandler().postAndSync(controller::getCustomLayout);
assertThat(initialCustomLayoutFromGetter) assertThat(initialCustomLayoutFromGetter)
.containsExactly(button1.copyWithIsEnabled(true), button3.copyWithIsEnabled(false)) .containsExactly(
withBackForwardOverflowSlot(button1.copyWithIsEnabled(true)),
withForwardOverflowSlot(button3.copyWithIsEnabled(false)))
.inOrder(); .inOrder();
ImmutableList<CommandButton> expectedNewButtons = ImmutableList<CommandButton> expectedNewButtons =
ImmutableList.of( ImmutableList.of(
button1.copyWithIsEnabled(true), withBackForwardOverflowSlot(button1.copyWithIsEnabled(true)),
button2.copyWithIsEnabled(false), withForwardOverflowSlot(button2.copyWithIsEnabled(false)),
button4.copyWithIsEnabled(false), withOverflowSlot(button4.copyWithIsEnabled(false)),
button5.copyWithIsEnabled(true), withOverflowSlot(button5.copyWithIsEnabled(true)),
button6.copyWithIsEnabled(false)); withOverflowSlot(button6.copyWithIsEnabled(false)));
assertThat(newCustomLayoutFromGetter).containsExactlyElementsIn(expectedNewButtons).inOrder(); assertThat(newCustomLayoutFromGetter).containsExactlyElementsIn(expectedNewButtons).inOrder();
assertThat(reportedCustomLayout.get()).containsExactlyElementsIn(expectedNewButtons).inOrder(); assertThat(reportedCustomLayout.get()).containsExactlyElementsIn(expectedNewButtons).inOrder();
assertThat(reportedCustomLayoutChanged.get()) assertThat(reportedCustomLayoutChanged.get())
@ -375,39 +378,39 @@ public class MediaControllerTest {
assertThat(latch.await(TIMEOUT_MS, MILLISECONDS)).isTrue(); assertThat(latch.await(TIMEOUT_MS, MILLISECONDS)).isTrue();
assertThat(initialCustomLayout) assertThat(initialCustomLayout)
.containsExactly( .containsExactly(
button1.copyWithIsEnabled(true), withBackForwardOverflowSlot(button1.copyWithIsEnabled(true)),
button2.copyWithIsEnabled(false), withForwardOverflowSlot(button2.copyWithIsEnabled(false)),
button3.copyWithIsEnabled(true), withOverflowSlot(button3.copyWithIsEnabled(true)),
button4.copyWithIsEnabled(false)); withOverflowSlot(button4.copyWithIsEnabled(false)));
assertThat(reportedCustomLayoutChanged).hasSize(2); assertThat(reportedCustomLayoutChanged).hasSize(2);
assertThat(reportedCustomLayoutChanged.get(0)) assertThat(reportedCustomLayoutChanged.get(0))
.containsExactly( .containsExactly(
button1.copyWithIsEnabled(false), withBackForwardOverflowSlot(button1.copyWithIsEnabled(false)),
button2.copyWithIsEnabled(false), withForwardOverflowSlot(button2.copyWithIsEnabled(false)),
button3.copyWithIsEnabled(false), withOverflowSlot(button3.copyWithIsEnabled(false)),
button4.copyWithIsEnabled(false)) withOverflowSlot(button4.copyWithIsEnabled(false)))
.inOrder(); .inOrder();
assertThat(reportedCustomLayoutChanged.get(1)) assertThat(reportedCustomLayoutChanged.get(1))
.containsExactly( .containsExactly(
button1.copyWithIsEnabled(false), withBackForwardOverflowSlot(button1.copyWithIsEnabled(false)),
button2.copyWithIsEnabled(false), withForwardOverflowSlot(button2.copyWithIsEnabled(false)),
button3.copyWithIsEnabled(false), withOverflowSlot(button3.copyWithIsEnabled(false)),
button4.copyWithIsEnabled(true)) withOverflowSlot(button4.copyWithIsEnabled(true)))
.inOrder(); .inOrder();
assertThat(getterCustomLayoutChanged).hasSize(2); assertThat(getterCustomLayoutChanged).hasSize(2);
assertThat(getterCustomLayoutChanged.get(0)) assertThat(getterCustomLayoutChanged.get(0))
.containsExactly( .containsExactly(
button1.copyWithIsEnabled(false), withBackForwardOverflowSlot(button1.copyWithIsEnabled(false)),
button2.copyWithIsEnabled(false), withForwardOverflowSlot(button2.copyWithIsEnabled(false)),
button3.copyWithIsEnabled(false), withOverflowSlot(button3.copyWithIsEnabled(false)),
button4.copyWithIsEnabled(false)) withOverflowSlot(button4.copyWithIsEnabled(false)))
.inOrder(); .inOrder();
assertThat(getterCustomLayoutChanged.get(1)) assertThat(getterCustomLayoutChanged.get(1))
.containsExactly( .containsExactly(
button1.copyWithIsEnabled(false), withBackForwardOverflowSlot(button1.copyWithIsEnabled(false)),
button2.copyWithIsEnabled(false), withForwardOverflowSlot(button2.copyWithIsEnabled(false)),
button3.copyWithIsEnabled(false), withOverflowSlot(button3.copyWithIsEnabled(false)),
button4.copyWithIsEnabled(true)) withOverflowSlot(button4.copyWithIsEnabled(true)))
.inOrder(); .inOrder();
session.cleanUp(); session.cleanUp();
} }
@ -450,13 +453,18 @@ public class MediaControllerTest {
new Player.Commands.Builder().add(Player.COMMAND_PLAY_PAUSE).build()); new Player.Commands.Builder().add(Player.COMMAND_PLAY_PAUSE).build());
assertThat(latch.await(TIMEOUT_MS, MILLISECONDS)).isTrue(); 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).hasSize(2);
assertThat(reportedCustomLayouts.get(0)).containsExactly(button.copyWithIsEnabled(false)); assertThat(reportedCustomLayouts.get(0))
assertThat(reportedCustomLayouts.get(1)).containsExactly(button.copyWithIsEnabled(true)); .containsExactly(withBackForwardOverflowSlot(button.copyWithIsEnabled(false)));
assertThat(reportedCustomLayouts.get(1))
.containsExactly(withBackForwardOverflowSlot(button.copyWithIsEnabled(true)));
assertThat(getterCustomLayouts).hasSize(2); assertThat(getterCustomLayouts).hasSize(2);
assertThat(getterCustomLayouts.get(0)).containsExactly(button.copyWithIsEnabled(false)); assertThat(getterCustomLayouts.get(0))
assertThat(getterCustomLayouts.get(1)).containsExactly(button.copyWithIsEnabled(true)); .containsExactly(withBackForwardOverflowSlot(button.copyWithIsEnabled(false)));
assertThat(getterCustomLayouts.get(1))
.containsExactly(withBackForwardOverflowSlot(button.copyWithIsEnabled(true)));
session.cleanUp(); session.cleanUp();
} }
@ -526,36 +534,190 @@ public class MediaControllerTest {
session.setCustomLayout(ImmutableList.of(button1, button2)); session.setCustomLayout(ImmutableList.of(button1, button2));
assertThat(latch.await(TIMEOUT_MS, MILLISECONDS)).isTrue(); assertThat(latch.await(TIMEOUT_MS, MILLISECONDS)).isTrue();
CommandButton button1Enabled = button1.copyWithIsEnabled(true); CommandButton button1EnabledBackSlot =
CommandButton button2Disabled = button2.copyWithIsEnabled(false); withBackForwardOverflowSlot(button1.copyWithIsEnabled(true));
CommandButton button3Disabled = button3.copyWithIsEnabled(false); CommandButton button2DisabledForwardSlot =
CommandButton button4Disabled = button4.copyWithIsEnabled(false); withForwardOverflowSlot(button2.copyWithIsEnabled(false));
assertThat(initialCustomLayout).containsExactly(button1Enabled, button2Disabled).inOrder(); CommandButton button3DisabledBackSlot =
withBackForwardOverflowSlot(button3.copyWithIsEnabled(false));
CommandButton button4DisabledForwardSlot =
withForwardOverflowSlot(button4.copyWithIsEnabled(false));
assertThat(initialCustomLayout)
.containsExactly(button1EnabledBackSlot, button2DisabledForwardSlot)
.inOrder();
assertThat(reportedCustomLayout) assertThat(reportedCustomLayout)
.containsExactly( .containsExactly(
ImmutableList.of(button1Enabled, button2Disabled), ImmutableList.of(button1EnabledBackSlot, button2DisabledForwardSlot),
ImmutableList.of(button3Disabled, button4Disabled), ImmutableList.of(button3DisabledBackSlot, button4DisabledForwardSlot),
ImmutableList.of(button1Enabled, button2Disabled)) ImmutableList.of(button1EnabledBackSlot, button2DisabledForwardSlot))
.inOrder(); .inOrder();
assertThat(getterCustomLayout) assertThat(getterCustomLayout)
.containsExactly( .containsExactly(
ImmutableList.of(button1Enabled, button2Disabled), ImmutableList.of(button1EnabledBackSlot, button2DisabledForwardSlot),
ImmutableList.of(button3Disabled, button4Disabled), ImmutableList.of(button3DisabledBackSlot, button4DisabledForwardSlot),
ImmutableList.of(button1Enabled, button2Disabled)) ImmutableList.of(button1EnabledBackSlot, button2DisabledForwardSlot))
.inOrder(); .inOrder();
assertThat(reportedCustomLayoutChanged) assertThat(reportedCustomLayoutChanged)
.containsExactly( .containsExactly(
ImmutableList.of(button3Disabled, button4Disabled), ImmutableList.of(button3DisabledBackSlot, button4DisabledForwardSlot),
ImmutableList.of(button1Enabled, button2Disabled)) ImmutableList.of(button1EnabledBackSlot, button2DisabledForwardSlot))
.inOrder(); .inOrder();
assertThat(getterCustomLayoutChanged) assertThat(getterCustomLayoutChanged)
.containsExactly( .containsExactly(
ImmutableList.of(button3Disabled, button4Disabled), ImmutableList.of(button3DisabledBackSlot, button4DisabledForwardSlot),
ImmutableList.of(button1Enabled, button2Disabled)) ImmutableList.of(button1EnabledBackSlot, button2DisabledForwardSlot))
.inOrder(); .inOrder();
session.cleanUp(); 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 @Test
public void getMediaButtonPreferences_mediaButtonPreferencesBuiltWithSession_includedOnConnect() public void getMediaButtonPreferences_mediaButtonPreferencesBuiltWithSession_includedOnConnect()
throws Exception { throws Exception {
@ -2422,4 +2584,24 @@ public class MediaControllerTest {
session.setMediaButtonPreferences(ImmutableList.copyOf(mediaButtonPreferences)); session.setMediaButtonPreferences(ImmutableList.copyOf(mediaButtonPreferences));
assertThat(latch.await(TIMEOUT_MS, MILLISECONDS)).isTrue(); 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 androidx.test.filters.LargeTest;
import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableList;
import com.google.common.collect.Iterables; 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.Futures;
import com.google.common.util.concurrent.ListenableFuture; import com.google.common.util.concurrent.ListenableFuture;
import com.google.common.util.concurrent.ListeningExecutorService; import com.google.common.util.concurrent.ListeningExecutorService;
@ -162,7 +163,6 @@ public class MediaSessionCallbackTest {
.setSessionCommand(new SessionCommand("command1", Bundle.EMPTY)) .setSessionCommand(new SessionCommand("command1", Bundle.EMPTY))
.setEnabled(true) .setEnabled(true)
.build(); .build();
CommandButton button1Disabled = button1.copyWithIsEnabled(false);
CommandButton button2 = CommandButton button2 =
new CommandButton.Builder(CommandButton.ICON_PAUSE) new CommandButton.Builder(CommandButton.ICON_PAUSE)
.setDisplayName("button2") .setDisplayName("button2")
@ -177,6 +177,7 @@ public class MediaSessionCallbackTest {
return new AcceptedResultBuilder(session) return new AcceptedResultBuilder(session)
.setAvailableSessionCommands( .setAvailableSessionCommands(
new SessionCommands.Builder().add(button2.sessionCommand).build()) new SessionCommands.Builder().add(button2.sessionCommand).build())
.setAvailablePlayerCommands(new Player.Commands.Builder().addAllCommands().build())
.setCustomLayout(ImmutableList.of(button1, button2)) .setCustomLayout(ImmutableList.of(button1, button2))
.build(); .build();
} }
@ -198,7 +199,13 @@ public class MediaSessionCallbackTest {
ImmutableList<CommandButton> layout = remoteController.getCustomLayout(); 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) assertThat(remoteController.sendCustomCommand(button1.sessionCommand, Bundle.EMPTY).resultCode)
.isEqualTo(ERROR_PERMISSION_DENIED); .isEqualTo(ERROR_PERMISSION_DENIED);
assertThat(remoteController.sendCustomCommand(button2.sessionCommand, Bundle.EMPTY).resultCode) 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 androidx.test.filters.MediumTest;
import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableList;
import com.google.common.collect.Lists; 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.ListenableFuture;
import com.google.common.util.concurrent.SettableFuture; import com.google.common.util.concurrent.SettableFuture;
import java.util.ArrayList; import java.util.ArrayList;
@ -331,7 +332,14 @@ public class MediaSessionServiceTest {
assertThat(mediaControllerCompat.getPlaybackState().getActions()) assertThat(mediaControllerCompat.getPlaybackState().getActions())
.isEqualTo(PlaybackStateCompat.ACTION_SET_RATING); .isEqualTo(PlaybackStateCompat.ACTION_SET_RATING);
assertThat(remoteController.getCustomLayout()) 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(); .inOrder();
assertThat(initialCustomActionsInControllerCompat).isEmpty(); assertThat(initialCustomActionsInControllerCompat).isEmpty();
assertThat(mediaControllerCompat.getPlaybackState().getCustomActions()).hasSize(2); assertThat(mediaControllerCompat.getPlaybackState().getCustomActions()).hasSize(2);