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. * Called when the {@linkplain #getCustomLayout() custom layout} changed.
* *
* <p>This method will be deprecated, prefer to use {@link #onMediaButtonPreferencesChanged}. * <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 * <p>The custom layout can change when either the session {@linkplain
* MediaSession#setCustomLayout changes the custom layout}, or when 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. * Returns the custom layout.
* *
* <p>This method will be deprecated, prefer to use {@link #getMediaButtonPreferences()} instead. * <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 * <p>After being connected, a change of the custom layout is reported with {@link
* Listener#onCustomLayoutChanged(MediaController, List)}. * Listener#onCustomLayoutChanged(MediaController, List)}.
@ -1127,7 +1131,8 @@ public class MediaController implements Player {
*/ */
@UnstableApi @UnstableApi
public final ImmutableList<CommandButton> getCustomLayout() { 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> getMediaButtonPreferences();
ImmutableList<CommandButton> getCustomLayout();
ImmutableList<CommandButton> getCommandButtonsForMediaItem(MediaItem mediaItem); ImmutableList<CommandButton> getCommandButtonsForMediaItem(MediaItem mediaItem);
Bundle getSessionExtras(); Bundle getSessionExtras();

View file

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

View file

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

View file

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