mirror of
https://github.com/androidx/media.git
synced 2025-04-30 06:46:50 +08:00
Add logic to convert button preferences to custom layout in sessions
This ensures that media button preferences with slots for BACK, FORWARD and OVERFLOW are converted to the legacy custom layout according to the implicit placement rules. This has to happen when populating the MediaSessionCompat and when generating the notification for older APIs. Sending these preferences to older Media3 controllers can filter them down to the custom layout the session would have used previously, but there is no need to adjust the reservation extras because the older Media3 custom layout has no concept of these extras. PiperOrigin-RevId: 689761637
This commit is contained in:
parent
8811b454bb
commit
a15571c8ee
@ -1173,6 +1173,63 @@ public final class CommandButton {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Converts a list of buttons defined as {@linkplain MediaSession#getMediaButtonPreferences media
|
||||||
|
* button preferences} to the list of buttons for a {@linkplain MediaSession#getCustomLayout
|
||||||
|
* custom layout} according to the implicit button placement rules applied for custom layouts.
|
||||||
|
*
|
||||||
|
* @param mediaButtonPreferences The list of buttons as media button preferences.
|
||||||
|
* @param reservationExtras A writable {@link Bundle} that receives the extras for slot
|
||||||
|
* reservations via {@link MediaConstants#EXTRAS_KEY_SLOT_RESERVATION_SEEK_TO_NEXT} or {@link
|
||||||
|
* MediaConstants#EXTRAS_KEY_SLOT_RESERVATION_SEEK_TO_PREV} to match the returned custom
|
||||||
|
* layout.
|
||||||
|
* @return A list of buttons compatible with the placement rules of custom layouts.
|
||||||
|
*/
|
||||||
|
/* package */ static ImmutableList<CommandButton> getCustomLayoutFromMediaButtonPreferences(
|
||||||
|
List<CommandButton> mediaButtonPreferences, Bundle reservationExtras) {
|
||||||
|
if (mediaButtonPreferences.isEmpty()) {
|
||||||
|
reservationExtras.putBoolean(MediaConstants.EXTRAS_KEY_SLOT_RESERVATION_SEEK_TO_PREV, true);
|
||||||
|
reservationExtras.putBoolean(MediaConstants.EXTRAS_KEY_SLOT_RESERVATION_SEEK_TO_NEXT, true);
|
||||||
|
return ImmutableList.of();
|
||||||
|
}
|
||||||
|
int backButtonIndex = C.INDEX_UNSET;
|
||||||
|
int forwardButtonIndex = C.INDEX_UNSET;
|
||||||
|
for (int i = 0; i < mediaButtonPreferences.size(); i++) {
|
||||||
|
CommandButton button = mediaButtonPreferences.get(i);
|
||||||
|
for (int s = 0; s < button.slots.length(); s++) {
|
||||||
|
@Slot int slot = button.slots.get(s);
|
||||||
|
if (slot == SLOT_OVERFLOW) {
|
||||||
|
// Will go into overflow.
|
||||||
|
break;
|
||||||
|
} else if (backButtonIndex == C.INDEX_UNSET && slot == SLOT_BACK) {
|
||||||
|
backButtonIndex = i;
|
||||||
|
} else if (forwardButtonIndex == C.INDEX_UNSET && slot == SLOT_FORWARD) {
|
||||||
|
forwardButtonIndex = i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
boolean hasBackButton = backButtonIndex != C.INDEX_UNSET;
|
||||||
|
boolean hasForwardButton = forwardButtonIndex != C.INDEX_UNSET;
|
||||||
|
reservationExtras.putBoolean(
|
||||||
|
MediaConstants.EXTRAS_KEY_SLOT_RESERVATION_SEEK_TO_PREV, !hasBackButton);
|
||||||
|
reservationExtras.putBoolean(
|
||||||
|
MediaConstants.EXTRAS_KEY_SLOT_RESERVATION_SEEK_TO_NEXT, !hasForwardButton);
|
||||||
|
ImmutableList.Builder<CommandButton> customLayout = ImmutableList.builder();
|
||||||
|
if (hasBackButton) {
|
||||||
|
customLayout.add(mediaButtonPreferences.get(backButtonIndex));
|
||||||
|
}
|
||||||
|
if (hasForwardButton) {
|
||||||
|
customLayout.add(mediaButtonPreferences.get(forwardButtonIndex));
|
||||||
|
}
|
||||||
|
for (int i = 0; i < mediaButtonPreferences.size(); i++) {
|
||||||
|
CommandButton button = mediaButtonPreferences.get(i);
|
||||||
|
if (i != backButtonIndex && i != forwardButtonIndex && button.slots.contains(SLOT_OVERFLOW)) {
|
||||||
|
customLayout.add(button);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return customLayout.build();
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Converts a list of buttons defined according to the implicit button placement rules for
|
* Converts a list of buttons defined according to the implicit button placement rules for
|
||||||
* {@linkplain MediaSession#getCustomLayout custom layouts} to {@linkplain
|
* {@linkplain MediaSession#getCustomLayout custom layouts} to {@linkplain
|
||||||
|
@ -131,11 +131,13 @@ import java.util.List;
|
|||||||
mediaButtonPreferences, CommandButton::toBundle));
|
mediaButtonPreferences, CommandButton::toBundle));
|
||||||
} else {
|
} else {
|
||||||
// Controller doesn't support media button preferences, send the list as a custom layout.
|
// Controller doesn't support media button preferences, send the list as a custom layout.
|
||||||
// TODO: b/332877990 - More accurately reflect media button preferences as custom layout.
|
// Ignore reservation extras as they were not directly supported in older controllers.
|
||||||
|
ImmutableList<CommandButton> customLayout =
|
||||||
|
CommandButton.getCustomLayoutFromMediaButtonPreferences(
|
||||||
|
mediaButtonPreferences, /* reservationExtras= */ new Bundle());
|
||||||
bundle.putParcelableArrayList(
|
bundle.putParcelableArrayList(
|
||||||
FIELD_CUSTOM_LAYOUT,
|
FIELD_CUSTOM_LAYOUT,
|
||||||
BundleCollectionUtil.toBundleArrayList(
|
BundleCollectionUtil.toBundleArrayList(customLayout, CommandButton::toBundle));
|
||||||
mediaButtonPreferences, CommandButton::toBundle));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (!commandButtonsForMediaItems.isEmpty()) {
|
if (!commandButtonsForMediaItems.isEmpty()) {
|
||||||
|
@ -46,6 +46,7 @@ import androidx.media3.common.util.UnstableApi;
|
|||||||
import androidx.media3.common.util.Util;
|
import androidx.media3.common.util.Util;
|
||||||
import androidx.media3.session.MediaStyleNotificationHelper.MediaStyle;
|
import androidx.media3.session.MediaStyleNotificationHelper.MediaStyle;
|
||||||
import com.google.common.collect.ImmutableList;
|
import com.google.common.collect.ImmutableList;
|
||||||
|
import com.google.common.primitives.ImmutableIntArray;
|
||||||
import com.google.common.util.concurrent.FutureCallback;
|
import com.google.common.util.concurrent.FutureCallback;
|
||||||
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;
|
||||||
@ -303,7 +304,6 @@ public class DefaultMediaNotificationProvider implements MediaNotification.Provi
|
|||||||
Callback onNotificationChangedCallback) {
|
Callback onNotificationChangedCallback) {
|
||||||
ensureNotificationChannel();
|
ensureNotificationChannel();
|
||||||
|
|
||||||
// TODO: b/332877990 - More accurately reflect media button preferences in the notification.
|
|
||||||
ImmutableList.Builder<CommandButton> mediaButtonPreferencesWithEnabledCommandButtonsOnly =
|
ImmutableList.Builder<CommandButton> mediaButtonPreferencesWithEnabledCommandButtonsOnly =
|
||||||
new ImmutableList.Builder<>();
|
new ImmutableList.Builder<>();
|
||||||
for (int i = 0; i < mediaButtonPreferences.size(); i++) {
|
for (int i = 0; i < mediaButtonPreferences.size(); i++) {
|
||||||
@ -445,9 +445,24 @@ public class DefaultMediaNotificationProvider implements MediaNotification.Provi
|
|||||||
Player.Commands playerCommands,
|
Player.Commands playerCommands,
|
||||||
ImmutableList<CommandButton> mediaButtonPreferences,
|
ImmutableList<CommandButton> mediaButtonPreferences,
|
||||||
boolean showPauseButton) {
|
boolean showPauseButton) {
|
||||||
// Skip to previous action.
|
Bundle reservations = new Bundle();
|
||||||
|
ImmutableList<CommandButton> customLayout =
|
||||||
|
CommandButton.getCustomLayoutFromMediaButtonPreferences(
|
||||||
|
mediaButtonPreferences, reservations);
|
||||||
|
boolean hasCustomBackButton =
|
||||||
|
!reservations.getBoolean(MediaConstants.EXTRAS_KEY_SLOT_RESERVATION_SEEK_TO_PREV);
|
||||||
|
boolean hasCustomForwardButton =
|
||||||
|
!reservations.getBoolean(MediaConstants.EXTRAS_KEY_SLOT_RESERVATION_SEEK_TO_NEXT);
|
||||||
|
int nextCustomLayoutIndex = 0;
|
||||||
|
|
||||||
ImmutableList.Builder<CommandButton> commandButtons = new ImmutableList.Builder<>();
|
ImmutableList.Builder<CommandButton> commandButtons = new ImmutableList.Builder<>();
|
||||||
if (playerCommands.containsAny(COMMAND_SEEK_TO_PREVIOUS, COMMAND_SEEK_TO_PREVIOUS_MEDIA_ITEM)) {
|
if (hasCustomBackButton) {
|
||||||
|
commandButtons.add(
|
||||||
|
customLayout
|
||||||
|
.get(nextCustomLayoutIndex++)
|
||||||
|
.copyWithSlots(ImmutableIntArray.of(CommandButton.SLOT_BACK)));
|
||||||
|
} else if (playerCommands.containsAny(
|
||||||
|
COMMAND_SEEK_TO_PREVIOUS, COMMAND_SEEK_TO_PREVIOUS_MEDIA_ITEM)) {
|
||||||
commandButtons.add(
|
commandButtons.add(
|
||||||
new CommandButton.Builder(CommandButton.ICON_PREVIOUS)
|
new CommandButton.Builder(CommandButton.ICON_PREVIOUS)
|
||||||
.setPlayerCommand(COMMAND_SEEK_TO_PREVIOUS_MEDIA_ITEM)
|
.setPlayerCommand(COMMAND_SEEK_TO_PREVIOUS_MEDIA_ITEM)
|
||||||
@ -470,20 +485,21 @@ public class DefaultMediaNotificationProvider implements MediaNotification.Provi
|
|||||||
.build());
|
.build());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Skip to next action.
|
if (hasCustomForwardButton) {
|
||||||
if (playerCommands.containsAny(COMMAND_SEEK_TO_NEXT, COMMAND_SEEK_TO_NEXT_MEDIA_ITEM)) {
|
commandButtons.add(
|
||||||
|
customLayout
|
||||||
|
.get(nextCustomLayoutIndex++)
|
||||||
|
.copyWithSlots(ImmutableIntArray.of(CommandButton.SLOT_FORWARD)));
|
||||||
|
} else if (playerCommands.containsAny(COMMAND_SEEK_TO_NEXT, COMMAND_SEEK_TO_NEXT_MEDIA_ITEM)) {
|
||||||
commandButtons.add(
|
commandButtons.add(
|
||||||
new CommandButton.Builder(CommandButton.ICON_NEXT)
|
new CommandButton.Builder(CommandButton.ICON_NEXT)
|
||||||
.setPlayerCommand(COMMAND_SEEK_TO_NEXT_MEDIA_ITEM)
|
.setPlayerCommand(COMMAND_SEEK_TO_NEXT_MEDIA_ITEM)
|
||||||
.setDisplayName(context.getString(R.string.media3_controls_seek_to_next_description))
|
.setDisplayName(context.getString(R.string.media3_controls_seek_to_next_description))
|
||||||
.build());
|
.build());
|
||||||
}
|
}
|
||||||
for (int i = 0; i < mediaButtonPreferences.size(); i++) {
|
for (int i = nextCustomLayoutIndex; i < customLayout.size(); i++) {
|
||||||
CommandButton button = mediaButtonPreferences.get(i);
|
commandButtons.add(
|
||||||
if (button.sessionCommand != null
|
customLayout.get(i).copyWithSlots(ImmutableIntArray.of(CommandButton.SLOT_OVERFLOW)));
|
||||||
&& button.sessionCommand.commandCode == SessionCommand.COMMAND_CODE_CUSTOM) {
|
|
||||||
commandButtons.add(button);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return commandButtons.build();
|
return commandButtons.build();
|
||||||
}
|
}
|
||||||
|
@ -526,7 +526,7 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
|
|||||||
public ListenableFuture<SessionResult> setMediaButtonPreferences(
|
public ListenableFuture<SessionResult> setMediaButtonPreferences(
|
||||||
ControllerInfo controller, ImmutableList<CommandButton> mediaButtonPreferences) {
|
ControllerInfo controller, ImmutableList<CommandButton> mediaButtonPreferences) {
|
||||||
if (isMediaNotificationController(controller)) {
|
if (isMediaNotificationController(controller)) {
|
||||||
playerWrapper.setMediaButtonPreferences(mediaButtonPreferences);
|
setLegacyMediaButtonPreferences(mediaButtonPreferences);
|
||||||
sessionLegacyStub.updateLegacySessionPlaybackState(playerWrapper);
|
sessionLegacyStub.updateLegacySessionPlaybackState(playerWrapper);
|
||||||
}
|
}
|
||||||
return dispatchRemoteControllerTask(
|
return dispatchRemoteControllerTask(
|
||||||
@ -540,7 +540,7 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
|
|||||||
*/
|
*/
|
||||||
public void setMediaButtonPreferences(ImmutableList<CommandButton> mediaButtonPreferences) {
|
public void setMediaButtonPreferences(ImmutableList<CommandButton> mediaButtonPreferences) {
|
||||||
this.mediaButtonPreferences = mediaButtonPreferences;
|
this.mediaButtonPreferences = mediaButtonPreferences;
|
||||||
playerWrapper.setMediaButtonPreferences(mediaButtonPreferences);
|
setLegacyMediaButtonPreferences(mediaButtonPreferences);
|
||||||
dispatchRemoteControllerTaskWithoutReturn(
|
dispatchRemoteControllerTaskWithoutReturn(
|
||||||
(controller, seq) -> controller.setMediaButtonPreferences(seq, mediaButtonPreferences));
|
(controller, seq) -> controller.setMediaButtonPreferences(seq, mediaButtonPreferences));
|
||||||
}
|
}
|
||||||
@ -722,14 +722,18 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
|
|||||||
"Callback.onConnect must return non-null future");
|
"Callback.onConnect must return non-null future");
|
||||||
if (isMediaNotificationController(controller) && connectionResult.isAccepted) {
|
if (isMediaNotificationController(controller) && connectionResult.isAccepted) {
|
||||||
isMediaNotificationControllerConnected = true;
|
isMediaNotificationControllerConnected = true;
|
||||||
|
ImmutableList<CommandButton> mediaButtonPreferences =
|
||||||
|
connectionResult.mediaButtonPreferences != null
|
||||||
|
? connectionResult.mediaButtonPreferences
|
||||||
|
: instance.getMediaButtonPreferences();
|
||||||
|
if (mediaButtonPreferences.isEmpty()) {
|
||||||
playerWrapper.setCustomLayout(
|
playerWrapper.setCustomLayout(
|
||||||
connectionResult.customLayout != null
|
connectionResult.customLayout != null
|
||||||
? connectionResult.customLayout
|
? connectionResult.customLayout
|
||||||
: instance.getCustomLayout());
|
: instance.getCustomLayout());
|
||||||
playerWrapper.setMediaButtonPreferences(
|
} else {
|
||||||
connectionResult.mediaButtonPreferences != null
|
setLegacyMediaButtonPreferences(mediaButtonPreferences);
|
||||||
? connectionResult.mediaButtonPreferences
|
}
|
||||||
: instance.getMediaButtonPreferences());
|
|
||||||
setAvailableFrameworkControllerCommands(
|
setAvailableFrameworkControllerCommands(
|
||||||
connectionResult.availableSessionCommands, connectionResult.availablePlayerCommands);
|
connectionResult.availableSessionCommands, connectionResult.availablePlayerCommands);
|
||||||
}
|
}
|
||||||
@ -1037,6 +1041,14 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void setLegacyMediaButtonPreferences(
|
||||||
|
ImmutableList<CommandButton> mediaButtonPreferences) {
|
||||||
|
boolean extrasChanged = playerWrapper.setMediaButtonPreferences(mediaButtonPreferences);
|
||||||
|
if (extrasChanged) {
|
||||||
|
sessionLegacyStub.getSessionCompat().setExtras(playerWrapper.getLegacyExtras());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private void setAvailableFrameworkControllerCommands(
|
private void setAvailableFrameworkControllerCommands(
|
||||||
SessionCommands sessionCommands, Player.Commands playerCommands) {
|
SessionCommands sessionCommands, Player.Commands playerCommands) {
|
||||||
boolean commandGetTimelineChanged =
|
boolean commandGetTimelineChanged =
|
||||||
|
@ -1085,10 +1085,13 @@ import org.checkerframework.checker.initialization.qual.Initialized;
|
|||||||
onRepeatModeChanged(seq, newPlayerWrapper.getRepeatMode());
|
onRepeatModeChanged(seq, newPlayerWrapper.getRepeatMode());
|
||||||
}
|
}
|
||||||
|
|
||||||
// Forcefully update playback info to update VolumeProviderCompat attached to the
|
// Forcefully update device info to update VolumeProviderCompat attached to the old player.
|
||||||
// old player.
|
|
||||||
onDeviceInfoChanged(seq, newPlayerWrapper.getDeviceInfo());
|
onDeviceInfoChanged(seq, newPlayerWrapper.getDeviceInfo());
|
||||||
|
|
||||||
|
if (hasChangedSlotReservationExtras(oldPlayerWrapper, newPlayerWrapper)) {
|
||||||
|
sessionCompat.setExtras(newPlayerWrapper.getLegacyExtras());
|
||||||
|
}
|
||||||
|
|
||||||
// Rest of changes are all notified via PlaybackStateCompat.
|
// Rest of changes are all notified via PlaybackStateCompat.
|
||||||
maybeUpdateFlags(newPlayerWrapper);
|
maybeUpdateFlags(newPlayerWrapper);
|
||||||
@Nullable MediaItem newMediaItem = newPlayerWrapper.getCurrentMediaItemWithCommandCheck();
|
@Nullable MediaItem newMediaItem = newPlayerWrapper.getCurrentMediaItemWithCommandCheck();
|
||||||
@ -1122,8 +1125,9 @@ import org.checkerframework.checker.initialization.qual.Initialized;
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onSessionExtrasChanged(int seq, Bundle sessionExtras) {
|
public void onSessionExtrasChanged(int seq, Bundle sessionExtras) {
|
||||||
sessionCompat.setExtras(sessionExtras);
|
PlayerWrapper playerWrapper = sessionImpl.getPlayerWrapper();
|
||||||
sessionImpl.getPlayerWrapper().setLegacyExtras(sessionExtras);
|
playerWrapper.setLegacyExtras(sessionExtras);
|
||||||
|
sessionCompat.setExtras(playerWrapper.getLegacyExtras());
|
||||||
sessionCompat.setPlaybackState(sessionImpl.getPlayerWrapper().createPlaybackStateCompat());
|
sessionCompat.setPlaybackState(sessionImpl.getPlayerWrapper().createPlaybackStateCompat());
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1421,6 +1425,28 @@ import org.checkerframework.checker.initialization.qual.Initialized;
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static boolean hasChangedSlotReservationExtras(
|
||||||
|
@Nullable PlayerWrapper oldPlayerWrapper, PlayerWrapper newPlayerWrapper) {
|
||||||
|
if (oldPlayerWrapper == null) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
Bundle oldExtras = oldPlayerWrapper.getLegacyExtras();
|
||||||
|
boolean oldPrevReservation =
|
||||||
|
oldExtras.getBoolean(
|
||||||
|
MediaConstants.EXTRAS_KEY_SLOT_RESERVATION_SEEK_TO_PREV, /* defaultVale= */ false);
|
||||||
|
boolean oldNextReservation =
|
||||||
|
oldExtras.getBoolean(
|
||||||
|
MediaConstants.EXTRAS_KEY_SLOT_RESERVATION_SEEK_TO_NEXT, /* defaultVale= */ false);
|
||||||
|
Bundle newExtras = newPlayerWrapper.getLegacyExtras();
|
||||||
|
boolean newPrevReservation =
|
||||||
|
newExtras.getBoolean(
|
||||||
|
MediaConstants.EXTRAS_KEY_SLOT_RESERVATION_SEEK_TO_PREV, /* defaultVale= */ false);
|
||||||
|
boolean newNextReservation =
|
||||||
|
newExtras.getBoolean(
|
||||||
|
MediaConstants.EXTRAS_KEY_SLOT_RESERVATION_SEEK_TO_NEXT, /* defaultVale= */ false);
|
||||||
|
return (oldPrevReservation != newPrevReservation) || (oldNextReservation != newNextReservation);
|
||||||
|
}
|
||||||
|
|
||||||
private static class ConnectionTimeoutHandler extends Handler {
|
private static class ConnectionTimeoutHandler extends Handler {
|
||||||
|
|
||||||
private static final int MSG_CONNECTION_TIMED_OUT = 1001;
|
private static final int MSG_CONNECTION_TIMED_OUT = 1001;
|
||||||
|
@ -2085,10 +2085,13 @@ import java.util.concurrent.ExecutionException;
|
|||||||
BundleCollectionUtil.toBundleList(mediaButtonPreferences, CommandButton::toBundle));
|
BundleCollectionUtil.toBundleList(mediaButtonPreferences, CommandButton::toBundle));
|
||||||
} else {
|
} else {
|
||||||
// Controller doesn't support media button preferences, send the list as a custom layout.
|
// Controller doesn't support media button preferences, send the list as a custom layout.
|
||||||
// TODO: b/332877990 - More accurately reflect media button preferences as custom layout.
|
// Ignore reservation extras as they were not directly supported in older controllers.
|
||||||
|
ImmutableList<CommandButton> customLayout =
|
||||||
|
CommandButton.getCustomLayoutFromMediaButtonPreferences(
|
||||||
|
mediaButtonPreferences, /* reservationExtras= */ new Bundle());
|
||||||
iController.onSetCustomLayout(
|
iController.onSetCustomLayout(
|
||||||
sequenceNumber,
|
sequenceNumber,
|
||||||
BundleCollectionUtil.toBundleList(mediaButtonPreferences, CommandButton::toBundle));
|
BundleCollectionUtil.toBundleList(customLayout, CommandButton::toBundle));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -106,6 +106,11 @@ import java.util.List;
|
|||||||
this.availableSessionCommands = availableSessionCommands;
|
this.availableSessionCommands = availableSessionCommands;
|
||||||
this.availablePlayerCommands = availablePlayerCommands;
|
this.availablePlayerCommands = availablePlayerCommands;
|
||||||
this.legacyExtras = legacyExtras;
|
this.legacyExtras = legacyExtras;
|
||||||
|
if (!mediaButtonPreferences.isEmpty()) {
|
||||||
|
this.customLayout =
|
||||||
|
CommandButton.getCustomLayoutFromMediaButtonPreferences(
|
||||||
|
mediaButtonPreferences, this.legacyExtras);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setAvailableCommands(
|
public void setAvailableCommands(
|
||||||
@ -126,8 +131,31 @@ import java.util.List;
|
|||||||
this.customLayout = customLayout;
|
this.customLayout = customLayout;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setMediaButtonPreferences(ImmutableList<CommandButton> mediaButtonPreferences) {
|
/**
|
||||||
|
* Sets new media button preferences.
|
||||||
|
*
|
||||||
|
* @param mediaButtonPreferences The list of {@link CommandButton} defining the media button
|
||||||
|
* preferences.
|
||||||
|
* @return Whether the {@linkplain #getLegacyExtras platform session extras} were updated as a
|
||||||
|
* result of this change.
|
||||||
|
*/
|
||||||
|
public boolean setMediaButtonPreferences(ImmutableList<CommandButton> mediaButtonPreferences) {
|
||||||
this.mediaButtonPreferences = mediaButtonPreferences;
|
this.mediaButtonPreferences = mediaButtonPreferences;
|
||||||
|
boolean hadPrevReservation =
|
||||||
|
legacyExtras.getBoolean(
|
||||||
|
MediaConstants.EXTRAS_KEY_SLOT_RESERVATION_SEEK_TO_PREV, /* defaultVale= */ false);
|
||||||
|
boolean hadNextReservation =
|
||||||
|
legacyExtras.getBoolean(
|
||||||
|
MediaConstants.EXTRAS_KEY_SLOT_RESERVATION_SEEK_TO_NEXT, /* defaultVale= */ false);
|
||||||
|
this.customLayout =
|
||||||
|
CommandButton.getCustomLayoutFromMediaButtonPreferences(
|
||||||
|
mediaButtonPreferences, legacyExtras);
|
||||||
|
return (legacyExtras.getBoolean(
|
||||||
|
MediaConstants.EXTRAS_KEY_SLOT_RESERVATION_SEEK_TO_PREV, /* defaultVale= */ false)
|
||||||
|
!= hadPrevReservation)
|
||||||
|
|| (legacyExtras.getBoolean(
|
||||||
|
MediaConstants.EXTRAS_KEY_SLOT_RESERVATION_SEEK_TO_NEXT, /* defaultVale= */ false)
|
||||||
|
!= hadNextReservation);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* package */ ImmutableList<CommandButton> getCustomLayout() {
|
/* package */ ImmutableList<CommandButton> getCustomLayout() {
|
||||||
@ -141,6 +169,11 @@ import java.util.List;
|
|||||||
public void setLegacyExtras(Bundle extras) {
|
public void setLegacyExtras(Bundle extras) {
|
||||||
checkArgument(!extras.containsKey(EXTRAS_KEY_PLAYBACK_SPEED_COMPAT));
|
checkArgument(!extras.containsKey(EXTRAS_KEY_PLAYBACK_SPEED_COMPAT));
|
||||||
checkArgument(!extras.containsKey(EXTRAS_KEY_MEDIA_ID_COMPAT));
|
checkArgument(!extras.containsKey(EXTRAS_KEY_MEDIA_ID_COMPAT));
|
||||||
|
if (!mediaButtonPreferences.isEmpty()) {
|
||||||
|
// Re-calculate custom layout in case we have to set any additional extras.
|
||||||
|
this.customLayout =
|
||||||
|
CommandButton.getCustomLayoutFromMediaButtonPreferences(mediaButtonPreferences, extras);
|
||||||
|
}
|
||||||
this.legacyExtras = extras;
|
this.legacyExtras = extras;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1040,6 +1073,14 @@ import java.util.List;
|
|||||||
for (int i = 0; i < availableCommands.size(); i++) {
|
for (int i = 0; i < availableCommands.size(); i++) {
|
||||||
actions |= convertCommandToPlaybackStateActions(availableCommands.get(i));
|
actions |= convertCommandToPlaybackStateActions(availableCommands.get(i));
|
||||||
}
|
}
|
||||||
|
if (!mediaButtonPreferences.isEmpty()
|
||||||
|
&& !legacyExtras.getBoolean(MediaConstants.EXTRAS_KEY_SLOT_RESERVATION_SEEK_TO_PREV)) {
|
||||||
|
actions &= ~PlaybackStateCompat.ACTION_SKIP_TO_PREVIOUS;
|
||||||
|
}
|
||||||
|
if (!mediaButtonPreferences.isEmpty()
|
||||||
|
&& !legacyExtras.getBoolean(MediaConstants.EXTRAS_KEY_SLOT_RESERVATION_SEEK_TO_NEXT)) {
|
||||||
|
actions &= ~PlaybackStateCompat.ACTION_SKIP_TO_NEXT;
|
||||||
|
}
|
||||||
long queueItemId =
|
long queueItemId =
|
||||||
isCommandAvailable(COMMAND_GET_TIMELINE)
|
isCommandAvailable(COMMAND_GET_TIMELINE)
|
||||||
? LegacyConversions.convertToQueueItemId(getCurrentMediaItemIndex())
|
? LegacyConversions.convertToQueueItemId(getCurrentMediaItemIndex())
|
||||||
@ -1064,18 +1105,14 @@ import java.util.List;
|
|||||||
.setActiveQueueItemId(queueItemId)
|
.setActiveQueueItemId(queueItemId)
|
||||||
.setBufferedPosition(compatBufferedPosition)
|
.setBufferedPosition(compatBufferedPosition)
|
||||||
.setExtras(extras);
|
.setExtras(extras);
|
||||||
|
for (int i = 0; i < customLayout.size(); i++) {
|
||||||
// TODO: b/332877990 - More accurately reflect media button preferences as custom actions.
|
CommandButton commandButton = customLayout.get(i);
|
||||||
List<CommandButton> buttonsForCustomActions =
|
|
||||||
mediaButtonPreferences.isEmpty() ? customLayout : mediaButtonPreferences;
|
|
||||||
for (int i = 0; i < buttonsForCustomActions.size(); i++) {
|
|
||||||
CommandButton commandButton = buttonsForCustomActions.get(i);
|
|
||||||
SessionCommand sessionCommand = commandButton.sessionCommand;
|
SessionCommand sessionCommand = commandButton.sessionCommand;
|
||||||
if (sessionCommand != null
|
if (sessionCommand != null
|
||||||
&& commandButton.isEnabled
|
&& commandButton.isEnabled
|
||||||
&& sessionCommand.commandCode == SessionCommand.COMMAND_CODE_CUSTOM
|
&& sessionCommand.commandCode == SessionCommand.COMMAND_CODE_CUSTOM
|
||||||
&& CommandButton.isButtonCommandAvailable(
|
&& CommandButton.isButtonCommandAvailable(
|
||||||
commandButton, availableSessionCommands, availablePlayerCommands)) {
|
commandButton, availableSessionCommands, availableCommands)) {
|
||||||
Bundle actionExtras = sessionCommand.customExtras;
|
Bundle actionExtras = sessionCommand.customExtras;
|
||||||
if (commandButton.icon != CommandButton.ICON_UNDEFINED) {
|
if (commandButton.icon != CommandButton.ICON_UNDEFINED) {
|
||||||
actionExtras = new Bundle(sessionCommand.customExtras);
|
actionExtras = new Bundle(sessionCommand.customExtras);
|
||||||
|
@ -544,6 +544,203 @@ public class CommandButtonTest {
|
|||||||
assertThat(restoredButtonAssumingOldSessionInterface.isEnabled).isTrue();
|
assertThat(restoredButtonAssumingOldSessionInterface.isEnabled).isTrue();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void getCustomLayoutFromMediaButtonPreferences_noBackForwardSlots_returnsCorrectButtons() {
|
||||||
|
ImmutableList<CommandButton> mediaButtonPreferences =
|
||||||
|
ImmutableList.of(
|
||||||
|
new CommandButton.Builder(CommandButton.ICON_ALBUM)
|
||||||
|
.setPlayerCommand(Player.COMMAND_PREPARE)
|
||||||
|
.setSlots(CommandButton.SLOT_OVERFLOW, CommandButton.SLOT_BACK)
|
||||||
|
.build(),
|
||||||
|
new CommandButton.Builder(CommandButton.ICON_NEXT)
|
||||||
|
.setPlayerCommand(Player.COMMAND_SEEK_TO_NEXT)
|
||||||
|
.setSlots(CommandButton.SLOT_FORWARD_SECONDARY)
|
||||||
|
.build(),
|
||||||
|
new CommandButton.Builder(CommandButton.ICON_REWIND)
|
||||||
|
.setPlayerCommand(Player.COMMAND_SEEK_TO_PREVIOUS)
|
||||||
|
.setSlots(CommandButton.SLOT_BACK_SECONDARY, CommandButton.SLOT_OVERFLOW)
|
||||||
|
.build());
|
||||||
|
Bundle reservationBundle = new Bundle();
|
||||||
|
|
||||||
|
ImmutableList<CommandButton> customLayout =
|
||||||
|
CommandButton.getCustomLayoutFromMediaButtonPreferences(
|
||||||
|
mediaButtonPreferences, reservationBundle);
|
||||||
|
|
||||||
|
assertThat(customLayout)
|
||||||
|
.containsExactly(
|
||||||
|
new CommandButton.Builder(CommandButton.ICON_ALBUM)
|
||||||
|
.setPlayerCommand(Player.COMMAND_PREPARE)
|
||||||
|
.setSlots(CommandButton.SLOT_OVERFLOW, CommandButton.SLOT_BACK)
|
||||||
|
.build(),
|
||||||
|
new CommandButton.Builder(CommandButton.ICON_REWIND)
|
||||||
|
.setPlayerCommand(Player.COMMAND_SEEK_TO_PREVIOUS)
|
||||||
|
.setSlots(CommandButton.SLOT_BACK_SECONDARY, CommandButton.SLOT_OVERFLOW)
|
||||||
|
.build())
|
||||||
|
.inOrder();
|
||||||
|
assertThat(
|
||||||
|
reservationBundle.getBoolean(MediaConstants.EXTRAS_KEY_SLOT_RESERVATION_SEEK_TO_PREV))
|
||||||
|
.isTrue();
|
||||||
|
assertThat(
|
||||||
|
reservationBundle.getBoolean(MediaConstants.EXTRAS_KEY_SLOT_RESERVATION_SEEK_TO_NEXT))
|
||||||
|
.isTrue();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void getCustomLayoutFromMediaButtonPreferences_withBackSlot_returnsCorrectButtons() {
|
||||||
|
ImmutableList<CommandButton> mediaButtonPreferences =
|
||||||
|
ImmutableList.of(
|
||||||
|
new CommandButton.Builder(CommandButton.ICON_ALBUM)
|
||||||
|
.setPlayerCommand(Player.COMMAND_PREPARE)
|
||||||
|
.setSlots(CommandButton.SLOT_OVERFLOW, CommandButton.SLOT_BACK)
|
||||||
|
.build(),
|
||||||
|
new CommandButton.Builder(CommandButton.ICON_PREVIOUS)
|
||||||
|
.setPlayerCommand(Player.COMMAND_SEEK_TO_PREVIOUS)
|
||||||
|
.setSlots(CommandButton.SLOT_BACK, CommandButton.SLOT_OVERFLOW)
|
||||||
|
.build(),
|
||||||
|
new CommandButton.Builder(CommandButton.ICON_NEXT)
|
||||||
|
.setPlayerCommand(Player.COMMAND_SEEK_TO_NEXT)
|
||||||
|
.setSlots(CommandButton.SLOT_FORWARD_SECONDARY)
|
||||||
|
.build(),
|
||||||
|
new CommandButton.Builder(CommandButton.ICON_REWIND)
|
||||||
|
.setPlayerCommand(Player.COMMAND_SEEK_TO_PREVIOUS)
|
||||||
|
.setSlots(CommandButton.SLOT_BACK_SECONDARY, CommandButton.SLOT_OVERFLOW)
|
||||||
|
.build());
|
||||||
|
Bundle reservationBundle = new Bundle();
|
||||||
|
|
||||||
|
ImmutableList<CommandButton> customLayout =
|
||||||
|
CommandButton.getCustomLayoutFromMediaButtonPreferences(
|
||||||
|
mediaButtonPreferences, reservationBundle);
|
||||||
|
|
||||||
|
assertThat(customLayout)
|
||||||
|
.containsExactly(
|
||||||
|
new CommandButton.Builder(CommandButton.ICON_PREVIOUS)
|
||||||
|
.setPlayerCommand(Player.COMMAND_SEEK_TO_PREVIOUS)
|
||||||
|
.setSlots(CommandButton.SLOT_BACK, CommandButton.SLOT_OVERFLOW)
|
||||||
|
.build(),
|
||||||
|
new CommandButton.Builder(CommandButton.ICON_ALBUM)
|
||||||
|
.setPlayerCommand(Player.COMMAND_PREPARE)
|
||||||
|
.setSlots(CommandButton.SLOT_OVERFLOW, CommandButton.SLOT_BACK)
|
||||||
|
.build(),
|
||||||
|
new CommandButton.Builder(CommandButton.ICON_REWIND)
|
||||||
|
.setPlayerCommand(Player.COMMAND_SEEK_TO_PREVIOUS)
|
||||||
|
.setSlots(CommandButton.SLOT_BACK_SECONDARY, CommandButton.SLOT_OVERFLOW)
|
||||||
|
.build())
|
||||||
|
.inOrder();
|
||||||
|
assertThat(
|
||||||
|
reservationBundle.getBoolean(MediaConstants.EXTRAS_KEY_SLOT_RESERVATION_SEEK_TO_PREV))
|
||||||
|
.isFalse();
|
||||||
|
assertThat(
|
||||||
|
reservationBundle.getBoolean(MediaConstants.EXTRAS_KEY_SLOT_RESERVATION_SEEK_TO_NEXT))
|
||||||
|
.isTrue();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void getCustomLayoutFromMediaButtonPreferences_withForwardSlot_returnsCorrectButtons() {
|
||||||
|
ImmutableList<CommandButton> mediaButtonPreferences =
|
||||||
|
ImmutableList.of(
|
||||||
|
new CommandButton.Builder(CommandButton.ICON_ALBUM)
|
||||||
|
.setPlayerCommand(Player.COMMAND_PREPARE)
|
||||||
|
.setSlots(CommandButton.SLOT_OVERFLOW, CommandButton.SLOT_FORWARD)
|
||||||
|
.build(),
|
||||||
|
new CommandButton.Builder(CommandButton.ICON_NEXT)
|
||||||
|
.setPlayerCommand(Player.COMMAND_SEEK_TO_NEXT)
|
||||||
|
.setSlots(CommandButton.SLOT_FORWARD, CommandButton.SLOT_OVERFLOW)
|
||||||
|
.build(),
|
||||||
|
new CommandButton.Builder(CommandButton.ICON_NEXT)
|
||||||
|
.setPlayerCommand(Player.COMMAND_SEEK_TO_NEXT)
|
||||||
|
.setSlots(CommandButton.SLOT_FORWARD_SECONDARY)
|
||||||
|
.build(),
|
||||||
|
new CommandButton.Builder(CommandButton.ICON_REWIND)
|
||||||
|
.setPlayerCommand(Player.COMMAND_SEEK_TO_PREVIOUS)
|
||||||
|
.setSlots(CommandButton.SLOT_BACK_SECONDARY, CommandButton.SLOT_OVERFLOW)
|
||||||
|
.build());
|
||||||
|
Bundle reservationBundle = new Bundle();
|
||||||
|
|
||||||
|
ImmutableList<CommandButton> customLayout =
|
||||||
|
CommandButton.getCustomLayoutFromMediaButtonPreferences(
|
||||||
|
mediaButtonPreferences, reservationBundle);
|
||||||
|
|
||||||
|
assertThat(customLayout)
|
||||||
|
.containsExactly(
|
||||||
|
new CommandButton.Builder(CommandButton.ICON_NEXT)
|
||||||
|
.setPlayerCommand(Player.COMMAND_SEEK_TO_NEXT)
|
||||||
|
.setSlots(CommandButton.SLOT_FORWARD, CommandButton.SLOT_OVERFLOW)
|
||||||
|
.build(),
|
||||||
|
new CommandButton.Builder(CommandButton.ICON_ALBUM)
|
||||||
|
.setPlayerCommand(Player.COMMAND_PREPARE)
|
||||||
|
.setSlots(CommandButton.SLOT_OVERFLOW, CommandButton.SLOT_FORWARD)
|
||||||
|
.build(),
|
||||||
|
new CommandButton.Builder(CommandButton.ICON_REWIND)
|
||||||
|
.setPlayerCommand(Player.COMMAND_SEEK_TO_PREVIOUS)
|
||||||
|
.setSlots(CommandButton.SLOT_BACK_SECONDARY, CommandButton.SLOT_OVERFLOW)
|
||||||
|
.build())
|
||||||
|
.inOrder();
|
||||||
|
assertThat(
|
||||||
|
reservationBundle.getBoolean(MediaConstants.EXTRAS_KEY_SLOT_RESERVATION_SEEK_TO_PREV))
|
||||||
|
.isTrue();
|
||||||
|
assertThat(
|
||||||
|
reservationBundle.getBoolean(MediaConstants.EXTRAS_KEY_SLOT_RESERVATION_SEEK_TO_NEXT))
|
||||||
|
.isFalse();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void
|
||||||
|
getCustomLayoutFromMediaButtonPreferences_withForwardAndBackSlot_returnsCorrectButtons() {
|
||||||
|
ImmutableList<CommandButton> mediaButtonPreferences =
|
||||||
|
ImmutableList.of(
|
||||||
|
new CommandButton.Builder(CommandButton.ICON_ALBUM)
|
||||||
|
.setPlayerCommand(Player.COMMAND_PREPARE)
|
||||||
|
.setSlots(CommandButton.SLOT_OVERFLOW, CommandButton.SLOT_FORWARD)
|
||||||
|
.build(),
|
||||||
|
new CommandButton.Builder(CommandButton.ICON_NEXT)
|
||||||
|
.setPlayerCommand(Player.COMMAND_SEEK_TO_NEXT)
|
||||||
|
.setSlots(CommandButton.SLOT_FORWARD, CommandButton.SLOT_OVERFLOW)
|
||||||
|
.build(),
|
||||||
|
new CommandButton.Builder(CommandButton.ICON_NEXT)
|
||||||
|
.setPlayerCommand(Player.COMMAND_SEEK_TO_NEXT)
|
||||||
|
.setSlots(CommandButton.SLOT_FORWARD_SECONDARY)
|
||||||
|
.build(),
|
||||||
|
new CommandButton.Builder(CommandButton.ICON_REWIND)
|
||||||
|
.setPlayerCommand(Player.COMMAND_SEEK_TO_PREVIOUS)
|
||||||
|
.setSlots(CommandButton.SLOT_BACK_SECONDARY, CommandButton.SLOT_OVERFLOW)
|
||||||
|
.build(),
|
||||||
|
new CommandButton.Builder(CommandButton.ICON_PREVIOUS)
|
||||||
|
.setPlayerCommand(Player.COMMAND_SEEK_TO_PREVIOUS)
|
||||||
|
.setSlots(CommandButton.SLOT_CENTRAL, CommandButton.SLOT_BACK)
|
||||||
|
.build());
|
||||||
|
Bundle reservationBundle = new Bundle();
|
||||||
|
|
||||||
|
ImmutableList<CommandButton> customLayout =
|
||||||
|
CommandButton.getCustomLayoutFromMediaButtonPreferences(
|
||||||
|
mediaButtonPreferences, reservationBundle);
|
||||||
|
|
||||||
|
assertThat(customLayout)
|
||||||
|
.containsExactly(
|
||||||
|
new CommandButton.Builder(CommandButton.ICON_PREVIOUS)
|
||||||
|
.setPlayerCommand(Player.COMMAND_SEEK_TO_PREVIOUS)
|
||||||
|
.setSlots(CommandButton.SLOT_CENTRAL, CommandButton.SLOT_BACK)
|
||||||
|
.build(),
|
||||||
|
new CommandButton.Builder(CommandButton.ICON_NEXT)
|
||||||
|
.setPlayerCommand(Player.COMMAND_SEEK_TO_NEXT)
|
||||||
|
.setSlots(CommandButton.SLOT_FORWARD, CommandButton.SLOT_OVERFLOW)
|
||||||
|
.build(),
|
||||||
|
new CommandButton.Builder(CommandButton.ICON_ALBUM)
|
||||||
|
.setPlayerCommand(Player.COMMAND_PREPARE)
|
||||||
|
.setSlots(CommandButton.SLOT_OVERFLOW, CommandButton.SLOT_FORWARD)
|
||||||
|
.build(),
|
||||||
|
new CommandButton.Builder(CommandButton.ICON_REWIND)
|
||||||
|
.setPlayerCommand(Player.COMMAND_SEEK_TO_PREVIOUS)
|
||||||
|
.setSlots(CommandButton.SLOT_BACK_SECONDARY, CommandButton.SLOT_OVERFLOW)
|
||||||
|
.build())
|
||||||
|
.inOrder();
|
||||||
|
assertThat(
|
||||||
|
reservationBundle.getBoolean(MediaConstants.EXTRAS_KEY_SLOT_RESERVATION_SEEK_TO_PREV))
|
||||||
|
.isFalse();
|
||||||
|
assertThat(
|
||||||
|
reservationBundle.getBoolean(MediaConstants.EXTRAS_KEY_SLOT_RESERVATION_SEEK_TO_NEXT))
|
||||||
|
.isFalse();
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void
|
public void
|
||||||
getMediaButtonPreferencesFromCustomLayout_withPrevAndNextCommands_returnsCorrectSlots() {
|
getMediaButtonPreferencesFromCustomLayout_withPrevAndNextCommands_returnsCorrectSlots() {
|
||||||
|
@ -196,6 +196,60 @@ public class DefaultMediaNotificationProviderTest {
|
|||||||
assertThat(mediaButtons).containsExactly(customCommandButton);
|
assertThat(mediaButtons).containsExactly(customCommandButton);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void getMediaButtons_customButtonsForPrevNextSlots_overridesDefaultPrevNextButtons() {
|
||||||
|
DefaultMediaNotificationProvider defaultMediaNotificationProvider =
|
||||||
|
new DefaultMediaNotificationProvider.Builder(ApplicationProvider.getApplicationContext())
|
||||||
|
.build();
|
||||||
|
Commands commands = new Commands.Builder().addAllCommands().build();
|
||||||
|
SessionCommand customSessionCommand = new SessionCommand("", Bundle.EMPTY);
|
||||||
|
CommandButton customCommandButton =
|
||||||
|
new CommandButton.Builder(CommandButton.ICON_UNDEFINED)
|
||||||
|
.setDisplayName("displayName")
|
||||||
|
.setIconResId(R.drawable.media3_icon_circular_play)
|
||||||
|
.setSessionCommand(customSessionCommand)
|
||||||
|
.build();
|
||||||
|
CommandButton customBackButton =
|
||||||
|
new CommandButton.Builder(CommandButton.ICON_UNDEFINED)
|
||||||
|
.setSlots(CommandButton.SLOT_BACK)
|
||||||
|
.setDisplayName("displayName")
|
||||||
|
.setIconResId(R.drawable.media3_icon_circular_play)
|
||||||
|
.setSessionCommand(customSessionCommand)
|
||||||
|
.build();
|
||||||
|
CommandButton customForwardButton =
|
||||||
|
new CommandButton.Builder(CommandButton.ICON_UNDEFINED)
|
||||||
|
.setSlots(CommandButton.SLOT_FORWARD)
|
||||||
|
.setDisplayName("displayName")
|
||||||
|
.setIconResId(R.drawable.media3_icon_circular_play)
|
||||||
|
.setSessionCommand(customSessionCommand)
|
||||||
|
.build();
|
||||||
|
CommandButton customCentralButton =
|
||||||
|
new CommandButton.Builder(CommandButton.ICON_UNDEFINED)
|
||||||
|
.setSlots(CommandButton.SLOT_CENTRAL)
|
||||||
|
.setDisplayName("displayName")
|
||||||
|
.setIconResId(R.drawable.media3_icon_circular_play)
|
||||||
|
.setSessionCommand(customSessionCommand)
|
||||||
|
.build();
|
||||||
|
Player player = new TestExoPlayerBuilder(context).build();
|
||||||
|
MediaSession mediaSession = new MediaSession.Builder(context, player).build();
|
||||||
|
|
||||||
|
ImmutableList<CommandButton> mediaButtons =
|
||||||
|
defaultMediaNotificationProvider.getMediaButtons(
|
||||||
|
mediaSession,
|
||||||
|
commands,
|
||||||
|
ImmutableList.of(
|
||||||
|
customCommandButton, customForwardButton, customBackButton, customCentralButton),
|
||||||
|
/* showPauseButton= */ true);
|
||||||
|
mediaSession.release();
|
||||||
|
player.release();
|
||||||
|
|
||||||
|
assertThat(mediaButtons).hasSize(4);
|
||||||
|
assertThat(mediaButtons.get(0)).isEqualTo(customBackButton);
|
||||||
|
assertThat(mediaButtons.get(1).playerCommand).isEqualTo(Player.COMMAND_PLAY_PAUSE);
|
||||||
|
assertThat(mediaButtons.get(2)).isEqualTo(customForwardButton);
|
||||||
|
assertThat(mediaButtons.get(3)).isEqualTo(customCommandButton);
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void addNotificationActions_customCompactViewDeclarations_correctCompactViewIndices() {
|
public void addNotificationActions_customCompactViewDeclarations_correctCompactViewIndices() {
|
||||||
DefaultMediaNotificationProvider defaultMediaNotificationProvider =
|
DefaultMediaNotificationProvider defaultMediaNotificationProvider =
|
||||||
|
@ -1640,15 +1640,18 @@ public class MediaControllerCompatPlaybackStateCompatActionsWithMediaSessionTest
|
|||||||
new CommandButton.Builder(CommandButton.ICON_PLAY)
|
new CommandButton.Builder(CommandButton.ICON_PLAY)
|
||||||
.setDisplayName("button1")
|
.setDisplayName("button1")
|
||||||
.setSessionCommand(command1)
|
.setSessionCommand(command1)
|
||||||
|
.setSlots(CommandButton.SLOT_OVERFLOW)
|
||||||
.build(),
|
.build(),
|
||||||
new CommandButton.Builder(CommandButton.ICON_PAUSE)
|
new CommandButton.Builder(CommandButton.ICON_PAUSE)
|
||||||
.setDisplayName("button2")
|
.setDisplayName("button2")
|
||||||
.setSessionCommand(command2)
|
.setSessionCommand(command2)
|
||||||
|
.setSlots(CommandButton.SLOT_OVERFLOW)
|
||||||
.build(),
|
.build(),
|
||||||
new CommandButton.Builder(CommandButton.ICON_PAUSE)
|
new CommandButton.Builder(CommandButton.ICON_PAUSE)
|
||||||
.setDisplayName("button3")
|
.setDisplayName("button3")
|
||||||
.setEnabled(false)
|
.setEnabled(false)
|
||||||
.setSessionCommand(command3)
|
.setSessionCommand(command3)
|
||||||
|
.setSlots(CommandButton.SLOT_OVERFLOW)
|
||||||
.build());
|
.build());
|
||||||
MediaSession.Callback callback =
|
MediaSession.Callback callback =
|
||||||
new MediaSession.Callback() {
|
new MediaSession.Callback() {
|
||||||
@ -1702,10 +1705,12 @@ public class MediaControllerCompatPlaybackStateCompatActionsWithMediaSessionTest
|
|||||||
new CommandButton.Builder(CommandButton.ICON_PLAY)
|
new CommandButton.Builder(CommandButton.ICON_PLAY)
|
||||||
.setDisplayName("button1")
|
.setDisplayName("button1")
|
||||||
.setSessionCommand(command1)
|
.setSessionCommand(command1)
|
||||||
|
.setSlots(CommandButton.SLOT_OVERFLOW)
|
||||||
.build(),
|
.build(),
|
||||||
new CommandButton.Builder(CommandButton.ICON_PAUSE)
|
new CommandButton.Builder(CommandButton.ICON_PAUSE)
|
||||||
.setDisplayName("button2")
|
.setDisplayName("button2")
|
||||||
.setSessionCommand(command2)
|
.setSessionCommand(command2)
|
||||||
|
.setSlots(CommandButton.SLOT_OVERFLOW)
|
||||||
.build());
|
.build());
|
||||||
MediaSession.Callback callback =
|
MediaSession.Callback callback =
|
||||||
new MediaSession.Callback() {
|
new MediaSession.Callback() {
|
||||||
@ -1773,10 +1778,12 @@ public class MediaControllerCompatPlaybackStateCompatActionsWithMediaSessionTest
|
|||||||
new CommandButton.Builder(CommandButton.ICON_PLAY)
|
new CommandButton.Builder(CommandButton.ICON_PLAY)
|
||||||
.setDisplayName("button1")
|
.setDisplayName("button1")
|
||||||
.setSessionCommand(command1)
|
.setSessionCommand(command1)
|
||||||
|
.setSlots(CommandButton.SLOT_OVERFLOW)
|
||||||
.build(),
|
.build(),
|
||||||
new CommandButton.Builder(CommandButton.ICON_PAUSE)
|
new CommandButton.Builder(CommandButton.ICON_PAUSE)
|
||||||
.setDisplayName("button2")
|
.setDisplayName("button2")
|
||||||
.setSessionCommand(command2)
|
.setSessionCommand(command2)
|
||||||
|
.setSlots(CommandButton.SLOT_OVERFLOW)
|
||||||
.build());
|
.build());
|
||||||
MediaSession.Callback callback =
|
MediaSession.Callback callback =
|
||||||
new MediaSession.Callback() {
|
new MediaSession.Callback() {
|
||||||
@ -1831,6 +1838,148 @@ public class MediaControllerCompatPlaybackStateCompatActionsWithMediaSessionTest
|
|||||||
releasePlayer(player);
|
releasePlayer(player);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void
|
||||||
|
playerWithMediaButtonPreferences_withBackForwardSlots_overridesPrevNextActionsWhenNeeded()
|
||||||
|
throws Exception {
|
||||||
|
Player player =
|
||||||
|
createPlayer(
|
||||||
|
/* onPostCreationTask= */ createdPlayer -> {
|
||||||
|
createdPlayer.setMediaItems(
|
||||||
|
ImmutableList.of(
|
||||||
|
MediaItem.fromUri("asset://media/wav/sample.wav"),
|
||||||
|
MediaItem.fromUri("asset://media/wav/sample.wav"),
|
||||||
|
MediaItem.fromUri("asset://media/wav/sample.wav")));
|
||||||
|
createdPlayer.seekToDefaultPosition(/* mediaItemIndex= */ 1);
|
||||||
|
});
|
||||||
|
SessionCommand command1 = new SessionCommand("command1", Bundle.EMPTY);
|
||||||
|
SessionCommand command2 = new SessionCommand("command2", Bundle.EMPTY);
|
||||||
|
SessionCommand command3 = new SessionCommand("command3", Bundle.EMPTY);
|
||||||
|
SessionCommand commandIgnored = new SessionCommand("shouldBeIgnored", Bundle.EMPTY);
|
||||||
|
ImmutableList<CommandButton> mediaButtonPreferencesWithBackForward =
|
||||||
|
ImmutableList.of(
|
||||||
|
new CommandButton.Builder(CommandButton.ICON_PLAY)
|
||||||
|
.setDisplayName("button1")
|
||||||
|
.setSessionCommand(command1)
|
||||||
|
.setSlots(CommandButton.SLOT_OVERFLOW)
|
||||||
|
.build(),
|
||||||
|
new CommandButton.Builder(CommandButton.ICON_PLAY)
|
||||||
|
.setDisplayName("shouldBeIgnored")
|
||||||
|
.setSessionCommand(commandIgnored)
|
||||||
|
.setSlots(CommandButton.SLOT_BACK_SECONDARY)
|
||||||
|
.build(),
|
||||||
|
new CommandButton.Builder(CommandButton.ICON_PAUSE)
|
||||||
|
.setDisplayName("button2")
|
||||||
|
.setSessionCommand(command2)
|
||||||
|
.setSlots(CommandButton.SLOT_FORWARD)
|
||||||
|
.build(),
|
||||||
|
new CommandButton.Builder(CommandButton.ICON_PAUSE)
|
||||||
|
.setDisplayName("button3")
|
||||||
|
.setSessionCommand(command3)
|
||||||
|
.setSlots(CommandButton.SLOT_BACK)
|
||||||
|
.build(),
|
||||||
|
new CommandButton.Builder(CommandButton.ICON_PLAY)
|
||||||
|
.setDisplayName("shouldBeIgnored")
|
||||||
|
.setSessionCommand(commandIgnored)
|
||||||
|
.setSlots(CommandButton.SLOT_BACK)
|
||||||
|
.build());
|
||||||
|
ImmutableList<CommandButton> mediaButtonPreferencesWithoutBackForward =
|
||||||
|
ImmutableList.of(
|
||||||
|
new CommandButton.Builder(CommandButton.ICON_PLAY)
|
||||||
|
.setDisplayName("button1")
|
||||||
|
.setSessionCommand(command1)
|
||||||
|
.setSlots(CommandButton.SLOT_OVERFLOW)
|
||||||
|
.build(),
|
||||||
|
new CommandButton.Builder(CommandButton.ICON_PLAY)
|
||||||
|
.setDisplayName("shouldBeIgnored")
|
||||||
|
.setSessionCommand(commandIgnored)
|
||||||
|
.setSlots(CommandButton.SLOT_BACK_SECONDARY)
|
||||||
|
.build(),
|
||||||
|
new CommandButton.Builder(CommandButton.ICON_PAUSE)
|
||||||
|
.setDisplayName("button2")
|
||||||
|
.setSessionCommand(command2)
|
||||||
|
.setSlots(CommandButton.SLOT_OVERFLOW)
|
||||||
|
.build());
|
||||||
|
MediaSession.Callback callback =
|
||||||
|
new MediaSession.Callback() {
|
||||||
|
@Override
|
||||||
|
public ConnectionResult onConnect(
|
||||||
|
MediaSession session, MediaSession.ControllerInfo controller) {
|
||||||
|
return new AcceptedResultBuilder(session)
|
||||||
|
.setAvailableSessionCommands(
|
||||||
|
ConnectionResult.DEFAULT_SESSION_COMMANDS
|
||||||
|
.buildUpon()
|
||||||
|
.add(command1)
|
||||||
|
.add(command2)
|
||||||
|
.add(command3)
|
||||||
|
.add(commandIgnored)
|
||||||
|
.build())
|
||||||
|
.build();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
MediaSession mediaSession =
|
||||||
|
new MediaSession.Builder(ApplicationProvider.getApplicationContext(), player)
|
||||||
|
.setCallback(callback)
|
||||||
|
.setMediaButtonPreferences(mediaButtonPreferencesWithBackForward)
|
||||||
|
.build();
|
||||||
|
connectMediaNotificationController(mediaSession);
|
||||||
|
MediaControllerCompat controllerCompat = createMediaControllerCompat(mediaSession);
|
||||||
|
CountDownLatch controllerUpdatedLatch = new CountDownLatch(1);
|
||||||
|
MediaControllerCompat.Callback controllerCallback =
|
||||||
|
new MediaControllerCompat.Callback() {
|
||||||
|
@Override
|
||||||
|
public void onPlaybackStateChanged(PlaybackStateCompat state) {
|
||||||
|
controllerUpdatedLatch.countDown();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
List<PlaybackStateCompat.CustomAction> customActions1 =
|
||||||
|
controllerCompat.getPlaybackState().getCustomActions();
|
||||||
|
Bundle extras1 = controllerCompat.getExtras();
|
||||||
|
long actions1 = controllerCompat.getPlaybackState().getActions();
|
||||||
|
controllerCompat.registerCallback(controllerCallback, threadTestRule.getHandler());
|
||||||
|
mediaSession.setMediaButtonPreferences(mediaButtonPreferencesWithoutBackForward);
|
||||||
|
controllerUpdatedLatch.await(TIMEOUT_MS, MILLISECONDS);
|
||||||
|
List<PlaybackStateCompat.CustomAction> customActions2 =
|
||||||
|
controllerCompat.getPlaybackState().getCustomActions();
|
||||||
|
Bundle extras2 = controllerCompat.getExtras();
|
||||||
|
long actions2 = controllerCompat.getPlaybackState().getActions();
|
||||||
|
mediaSession.release();
|
||||||
|
releasePlayer(player);
|
||||||
|
|
||||||
|
assertThat(customActions1).hasSize(3);
|
||||||
|
assertThat(customActions1.get(0).getAction()).isEqualTo("command3");
|
||||||
|
assertThat(customActions1.get(1).getAction()).isEqualTo("command2");
|
||||||
|
assertThat(customActions1.get(2).getAction()).isEqualTo("command1");
|
||||||
|
assertThat(
|
||||||
|
extras1.getBoolean(
|
||||||
|
androidx.media.utils.MediaConstants
|
||||||
|
.SESSION_EXTRAS_KEY_SLOT_RESERVATION_SKIP_TO_PREV))
|
||||||
|
.isFalse();
|
||||||
|
assertThat(
|
||||||
|
extras1.getBoolean(
|
||||||
|
androidx.media.utils.MediaConstants
|
||||||
|
.SESSION_EXTRAS_KEY_SLOT_RESERVATION_SKIP_TO_NEXT))
|
||||||
|
.isFalse();
|
||||||
|
assertThat(actions1 & PlaybackStateCompat.ACTION_SKIP_TO_NEXT).isEqualTo(0);
|
||||||
|
assertThat(actions1 & PlaybackStateCompat.ACTION_SKIP_TO_PREVIOUS).isEqualTo(0);
|
||||||
|
assertThat(customActions2).hasSize(2);
|
||||||
|
assertThat(customActions2.get(0).getAction()).isEqualTo("command1");
|
||||||
|
assertThat(customActions2.get(1).getAction()).isEqualTo("command2");
|
||||||
|
assertThat(
|
||||||
|
extras2.getBoolean(
|
||||||
|
androidx.media.utils.MediaConstants
|
||||||
|
.SESSION_EXTRAS_KEY_SLOT_RESERVATION_SKIP_TO_PREV))
|
||||||
|
.isTrue();
|
||||||
|
assertThat(
|
||||||
|
extras2.getBoolean(
|
||||||
|
androidx.media.utils.MediaConstants
|
||||||
|
.SESSION_EXTRAS_KEY_SLOT_RESERVATION_SKIP_TO_NEXT))
|
||||||
|
.isTrue();
|
||||||
|
assertThat(actions2 & PlaybackStateCompat.ACTION_SKIP_TO_NEXT).isNotEqualTo(0);
|
||||||
|
assertThat(actions2 & PlaybackStateCompat.ACTION_SKIP_TO_PREVIOUS).isNotEqualTo(0);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Connect a controller that mimics the media notification controller that is connected by {@link
|
* Connect a controller that mimics the media notification controller that is connected by {@link
|
||||||
* MediaNotificationManager} when the session is running in the service.
|
* MediaNotificationManager} when the session is running in the service.
|
||||||
|
Loading…
x
Reference in New Issue
Block a user