Compat logic for MediaController.getCustomLayout

When a new media session sets media button preferences, we need to
"translate" them back to a custom layout to ensure the user preferences
are represented as closely as possible when the controller uses the
old button placement rules.

PiperOrigin-RevId: 693306153
This commit is contained in:
tonihei 2024-11-05 05:03:29 -08:00 committed by Copybara-Service
parent 9fb4ed91b6
commit 12cb803486
5 changed files with 1102 additions and 64 deletions

View File

@ -410,6 +410,8 @@ public class MediaController implements Player {
* Called when the {@linkplain #getCustomLayout() custom layout} changed.
*
* <p>This method will be deprecated, prefer to use {@link #onMediaButtonPreferencesChanged}.
* Note that the media button preferences use {@link CommandButton#slots} to define the allowed
* button placement.
*
* <p>The custom layout can change when either the session {@linkplain
* MediaSession#setCustomLayout changes the custom layout}, or when the session {@linkplain
@ -1116,6 +1118,8 @@ public class MediaController implements Player {
* Returns the custom layout.
*
* <p>This method will be deprecated, prefer to use {@link #getMediaButtonPreferences()} instead.
* Note that the media button preferences use {@link CommandButton#slots} to define the allowed
* button placement.
*
* <p>After being connected, a change of the custom layout is reported with {@link
* Listener#onCustomLayoutChanged(MediaController, List)}.
@ -1127,7 +1131,8 @@ public class MediaController implements Player {
*/
@UnstableApi
public final ImmutableList<CommandButton> getCustomLayout() {
return getMediaButtonPreferences();
verifyApplicationThread();
return isConnected() ? impl.getCustomLayout() : ImmutableList.of();
}
/**
@ -2208,6 +2213,8 @@ public class MediaController implements Player {
ImmutableList<CommandButton> getMediaButtonPreferences();
ImmutableList<CommandButton> getCustomLayout();
ImmutableList<CommandButton> getCommandButtonsForMediaItem(MediaItem mediaItem);
Bundle getSessionExtras();

View File

@ -93,7 +93,6 @@ import com.google.common.util.concurrent.MoreExecutors;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Objects;
import java.util.concurrent.CancellationException;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeoutException;
@ -128,6 +127,7 @@ import org.checkerframework.checker.nullness.qual.NonNull;
private ImmutableList<CommandButton> customLayoutOriginal;
private ImmutableList<CommandButton> mediaButtonPreferencesOriginal;
private ImmutableList<CommandButton> resolvedMediaButtonPreferences;
private ImmutableList<CommandButton> resolvedCustomLayout;
private ImmutableMap<String, CommandButton> commandButtonsForMediaItemsMap;
private SessionCommands sessionCommands;
private Commands playerCommandsFromSession;
@ -158,6 +158,7 @@ import org.checkerframework.checker.nullness.qual.NonNull;
customLayoutOriginal = ImmutableList.of();
mediaButtonPreferencesOriginal = ImmutableList.of();
resolvedMediaButtonPreferences = ImmutableList.of();
resolvedCustomLayout = ImmutableList.of();
commandButtonsForMediaItemsMap = ImmutableMap.of();
playerCommandsFromSession = Commands.EMPTY;
playerCommandsFromPlayer = Commands.EMPTY;
@ -751,6 +752,11 @@ import org.checkerframework.checker.nullness.qual.NonNull;
return resolvedMediaButtonPreferences;
}
@Override
public ImmutableList<CommandButton> getCustomLayout() {
return resolvedCustomLayout;
}
@Override
public ImmutableList<CommandButton> getCommandButtonsForMediaItem(MediaItem mediaItem) {
ImmutableList<String> supportedActions = mediaItem.mediaMetadata.supportedCommands;
@ -2662,6 +2668,9 @@ import org.checkerframework.checker.nullness.qual.NonNull;
sessionCommands,
intersectedPlayerCommands,
result.sessionExtras);
resolvedCustomLayout =
resolveCustomLayout(
resolvedMediaButtonPreferences, result.sessionExtras, intersectedPlayerCommands);
ImmutableMap.Builder<String, CommandButton> commandButtonsForMediaItems =
new ImmutableMap.Builder<>();
for (int i = 0; i < result.commandButtonsForMediaItems.size(); i++) {
@ -2842,8 +2851,10 @@ import org.checkerframework.checker.nullness.qual.NonNull;
!Util.areEqual(intersectedPlayerCommands, prevIntersectedPlayerCommands);
}
boolean mediaButtonPreferencesChanged = false;
boolean customLayoutChanged = false;
if (sessionCommandsChanged || intersectedPlayerCommandsChanged) {
ImmutableList<CommandButton> oldMediaButtonPreferences = resolvedMediaButtonPreferences;
ImmutableList<CommandButton> oldCustomLayout = resolvedCustomLayout;
resolvedMediaButtonPreferences =
resolveMediaButtonPreferences(
mediaButtonPreferencesOriginal,
@ -2851,8 +2862,12 @@ import org.checkerframework.checker.nullness.qual.NonNull;
sessionCommands,
intersectedPlayerCommands,
sessionExtras);
resolvedCustomLayout =
resolveCustomLayout(
resolvedMediaButtonPreferences, sessionExtras, intersectedPlayerCommands);
mediaButtonPreferencesChanged =
!resolvedMediaButtonPreferences.equals(oldMediaButtonPreferences);
customLayoutChanged = !resolvedCustomLayout.equals(oldCustomLayout);
}
if (intersectedPlayerCommandsChanged) {
listeners.sendEvent(
@ -2865,14 +2880,17 @@ import org.checkerframework.checker.nullness.qual.NonNull;
listener ->
listener.onAvailableSessionCommandsChanged(getInstance(), sessionCommands));
}
if (customLayoutChanged) {
getInstance()
.notifyControllerListener(
listener -> listener.onCustomLayoutChanged(getInstance(), resolvedCustomLayout));
}
if (mediaButtonPreferencesChanged) {
getInstance()
.notifyControllerListener(
listener -> {
listener.onCustomLayoutChanged(getInstance(), resolvedMediaButtonPreferences);
listener.onMediaButtonPreferencesChanged(
getInstance(), resolvedMediaButtonPreferences);
});
listener ->
listener.onMediaButtonPreferencesChanged(
getInstance(), resolvedMediaButtonPreferences));
}
}
@ -2891,8 +2909,10 @@ import org.checkerframework.checker.nullness.qual.NonNull;
boolean intersectedPlayerCommandsChanged =
!Util.areEqual(intersectedPlayerCommands, prevIntersectedPlayerCommands);
boolean mediaButtonPreferencesChanged = false;
boolean customLayoutChanged = false;
if (intersectedPlayerCommandsChanged) {
ImmutableList<CommandButton> oldMediaButtonPreferences = resolvedMediaButtonPreferences;
ImmutableList<CommandButton> oldCustomLayout = resolvedCustomLayout;
resolvedMediaButtonPreferences =
resolveMediaButtonPreferences(
mediaButtonPreferencesOriginal,
@ -2900,20 +2920,27 @@ import org.checkerframework.checker.nullness.qual.NonNull;
sessionCommands,
intersectedPlayerCommands,
sessionExtras);
resolvedCustomLayout =
resolveCustomLayout(
resolvedMediaButtonPreferences, sessionExtras, intersectedPlayerCommands);
mediaButtonPreferencesChanged =
!resolvedMediaButtonPreferences.equals(oldMediaButtonPreferences);
customLayoutChanged = !resolvedCustomLayout.equals(oldCustomLayout);
listeners.sendEvent(
/* eventFlag= */ Player.EVENT_AVAILABLE_COMMANDS_CHANGED,
listener -> listener.onAvailableCommandsChanged(intersectedPlayerCommands));
}
if (customLayoutChanged) {
getInstance()
.notifyControllerListener(
listener -> listener.onCustomLayoutChanged(getInstance(), resolvedCustomLayout));
}
if (mediaButtonPreferencesChanged) {
getInstance()
.notifyControllerListener(
listener -> {
listener.onCustomLayoutChanged(getInstance(), resolvedMediaButtonPreferences);
listener.onMediaButtonPreferencesChanged(
getInstance(), resolvedMediaButtonPreferences);
});
listener ->
listener.onMediaButtonPreferencesChanged(
getInstance(), resolvedMediaButtonPreferences));
}
}
@ -2922,6 +2949,7 @@ import org.checkerframework.checker.nullness.qual.NonNull;
return;
}
ImmutableList<CommandButton> oldMediaButtonPreferences = resolvedMediaButtonPreferences;
ImmutableList<CommandButton> oldCustomLayout = resolvedCustomLayout;
customLayoutOriginal = ImmutableList.copyOf(layout);
resolvedMediaButtonPreferences =
resolveMediaButtonPreferences(
@ -2930,17 +2958,23 @@ import org.checkerframework.checker.nullness.qual.NonNull;
sessionCommands,
intersectedPlayerCommands,
sessionExtras);
resolvedCustomLayout =
resolveCustomLayout(
resolvedMediaButtonPreferences, sessionExtras, intersectedPlayerCommands);
boolean mediaButtonPreferencesChanged =
!Objects.equals(resolvedMediaButtonPreferences, oldMediaButtonPreferences);
!resolvedMediaButtonPreferences.equals(oldMediaButtonPreferences);
boolean customLayoutChanged = !resolvedCustomLayout.equals(oldCustomLayout);
getInstance()
.notifyControllerListener(
listener -> {
ListenableFuture<SessionResult> future =
checkNotNull(
listener.onSetCustomLayout(getInstance(), resolvedMediaButtonPreferences),
listener.onSetCustomLayout(getInstance(), resolvedCustomLayout),
"MediaController.Listener#onSetCustomLayout() must not return null");
if (customLayoutChanged) {
listener.onCustomLayoutChanged(getInstance(), resolvedCustomLayout);
}
if (mediaButtonPreferencesChanged) {
listener.onCustomLayoutChanged(getInstance(), resolvedMediaButtonPreferences);
listener.onMediaButtonPreferencesChanged(
getInstance(), resolvedMediaButtonPreferences);
}
@ -2953,6 +2987,7 @@ import org.checkerframework.checker.nullness.qual.NonNull;
return;
}
ImmutableList<CommandButton> oldMediaButtonPreferences = resolvedMediaButtonPreferences;
ImmutableList<CommandButton> oldCustomLayout = resolvedCustomLayout;
mediaButtonPreferencesOriginal = ImmutableList.copyOf(mediaButtonPreferences);
resolvedMediaButtonPreferences =
resolveMediaButtonPreferences(
@ -2961,17 +2996,23 @@ import org.checkerframework.checker.nullness.qual.NonNull;
sessionCommands,
intersectedPlayerCommands,
sessionExtras);
resolvedCustomLayout =
resolveCustomLayout(
resolvedMediaButtonPreferences, sessionExtras, intersectedPlayerCommands);
boolean mediaButtonPreferencesChanged =
!Objects.equals(resolvedMediaButtonPreferences, oldMediaButtonPreferences);
!resolvedMediaButtonPreferences.equals(oldMediaButtonPreferences);
boolean customLayoutChanged = !resolvedCustomLayout.equals(oldCustomLayout);
getInstance()
.notifyControllerListener(
listener -> {
ListenableFuture<SessionResult> future =
checkNotNull(
listener.onSetCustomLayout(getInstance(), resolvedMediaButtonPreferences),
listener.onSetCustomLayout(getInstance(), resolvedCustomLayout),
"MediaController.Listener#onSetCustomLayout() must not return null");
if (customLayoutChanged) {
listener.onCustomLayoutChanged(getInstance(), resolvedCustomLayout);
}
if (mediaButtonPreferencesChanged) {
listener.onCustomLayoutChanged(getInstance(), resolvedMediaButtonPreferences);
listener.onMediaButtonPreferencesChanged(
getInstance(), resolvedMediaButtonPreferences);
}
@ -2984,6 +3025,7 @@ import org.checkerframework.checker.nullness.qual.NonNull;
return;
}
ImmutableList<CommandButton> oldMediaButtonPreferences = resolvedMediaButtonPreferences;
ImmutableList<CommandButton> oldCustomLayout = resolvedCustomLayout;
sessionExtras = extras;
resolvedMediaButtonPreferences =
resolveMediaButtonPreferences(
@ -2992,14 +3034,20 @@ import org.checkerframework.checker.nullness.qual.NonNull;
sessionCommands,
intersectedPlayerCommands,
sessionExtras);
resolvedCustomLayout =
resolveCustomLayout(
resolvedMediaButtonPreferences, sessionExtras, intersectedPlayerCommands);
boolean mediaButtonPreferencesChanged =
!Objects.equals(resolvedMediaButtonPreferences, oldMediaButtonPreferences);
!resolvedMediaButtonPreferences.equals(oldMediaButtonPreferences);
boolean customLayoutChanged = !resolvedCustomLayout.equals(oldCustomLayout);
getInstance()
.notifyControllerListener(
listener -> {
listener.onExtrasChanged(getInstance(), extras);
if (customLayoutChanged) {
listener.onCustomLayoutChanged(getInstance(), resolvedCustomLayout);
}
if (mediaButtonPreferencesChanged) {
listener.onCustomLayoutChanged(getInstance(), resolvedMediaButtonPreferences);
listener.onMediaButtonPreferencesChanged(
getInstance(), resolvedMediaButtonPreferences);
}
@ -3365,6 +3413,22 @@ import org.checkerframework.checker.nullness.qual.NonNull;
resolvedButtons, sessionCommands, playerCommands);
}
private static ImmutableList<CommandButton> resolveCustomLayout(
List<CommandButton> mediaButtonPreferences,
Bundle sessionExtras,
Player.Commands availableCommands) {
boolean backSlotAllowed =
!sessionExtras.getBoolean(MediaConstants.EXTRAS_KEY_SLOT_RESERVATION_SEEK_TO_PREV)
&& !availableCommands.containsAny(
Player.COMMAND_SEEK_TO_PREVIOUS_MEDIA_ITEM, Player.COMMAND_SEEK_TO_PREVIOUS);
boolean forwardSlotAllowed =
!sessionExtras.getBoolean(MediaConstants.EXTRAS_KEY_SLOT_RESERVATION_SEEK_TO_NEXT)
&& !availableCommands.containsAny(
Player.COMMAND_SEEK_TO_NEXT_MEDIA_ITEM, Player.COMMAND_SEEK_TO_NEXT);
return CommandButton.getCustomLayoutFromMediaButtonPreferences(
mediaButtonPreferences, backSlotAllowed, forwardSlotAllowed);
}
private static Commands createIntersectedCommandsEnsuringCommandReleaseAvailable(
Commands commandFromSession, Commands commandsFromPlayer) {
Commands intersectedCommands = MediaUtils.intersect(commandFromSession, commandsFromPlayer);

View File

@ -437,6 +437,11 @@ import org.checkerframework.checker.initialization.qual.UnderInitialization;
return controllerInfo.mediaButtonPreferences;
}
@Override
public ImmutableList<CommandButton> getCustomLayout() {
return controllerInfo.mediaButtonPreferences;
}
@Override
public Bundle getSessionExtras() {
return controllerInfo.sessionExtras;

View File

@ -335,8 +335,7 @@ public class MediaSessionServiceTest {
.containsExactly(
button1
.copyWithIsEnabled(false)
.copyWithSlots(
ImmutableIntArray.of(CommandButton.SLOT_FORWARD, CommandButton.SLOT_OVERFLOW)),
.copyWithSlots(ImmutableIntArray.of(CommandButton.SLOT_FORWARD)),
button2
.copyWithIsEnabled(false)
.copyWithSlots(ImmutableIntArray.of(CommandButton.SLOT_OVERFLOW)))