Skip to content

Commit c357e67

Browse files
toniheichristosts
authored andcommitted
Filter available commands based on PlaybackStateCompat actions
This allows a MediaController to understand which methods calls are available on a legacy session. PiperOrigin-RevId: 504306806 (cherry picked from commit 067340c)
1 parent 207d67b commit c357e67

File tree

4 files changed

+499
-29
lines changed

4 files changed

+499
-29
lines changed

libraries/session/src/main/java/androidx/media3/session/MediaControllerImplLegacy.java

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@
4646
import android.view.TextureView;
4747
import androidx.annotation.CheckResult;
4848
import androidx.annotation.Nullable;
49+
import androidx.media.VolumeProviderCompat;
4950
import androidx.media3.common.AudioAttributes;
5051
import androidx.media3.common.C;
5152
import androidx.media3.common.DeviceInfo;
@@ -1877,9 +1878,17 @@ private static ControllerInfo buildNewControllerInfo(
18771878
// Note: Sets the available player command here although it can be obtained before session is
18781879
// ready. It's to follow the decision on MediaController to disallow any commands before
18791880
// connection is made.
1881+
int volumeControlType =
1882+
newLegacyPlayerInfo.playbackInfoCompat != null
1883+
? newLegacyPlayerInfo.playbackInfoCompat.getVolumeControl()
1884+
: VolumeProviderCompat.VOLUME_CONTROL_FIXED;
18801885
availablePlayerCommands =
18811886
(oldControllerInfo.availablePlayerCommands == Commands.EMPTY)
1882-
? MediaUtils.convertToPlayerCommands(sessionFlags, isSessionReady)
1887+
? MediaUtils.convertToPlayerCommands(
1888+
newLegacyPlayerInfo.playbackStateCompat,
1889+
volumeControlType,
1890+
sessionFlags,
1891+
isSessionReady)
18831892
: oldControllerInfo.availablePlayerCommands;
18841893

18851894
PlaybackException playerError =

libraries/session/src/main/java/androidx/media3/session/MediaUtils.java

Lines changed: 85 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -19,13 +19,17 @@
1919
import static androidx.media.utils.MediaConstants.BROWSER_ROOT_HINTS_KEY_ROOT_CHILDREN_SUPPORTED_FLAGS;
2020
import static androidx.media3.common.Player.COMMAND_ADJUST_DEVICE_VOLUME;
2121
import static androidx.media3.common.Player.COMMAND_CHANGE_MEDIA_ITEMS;
22+
import static androidx.media3.common.Player.COMMAND_GET_AUDIO_ATTRIBUTES;
2223
import static androidx.media3.common.Player.COMMAND_GET_CURRENT_MEDIA_ITEM;
2324
import static androidx.media3.common.Player.COMMAND_GET_DEVICE_VOLUME;
2425
import static androidx.media3.common.Player.COMMAND_GET_MEDIA_ITEMS_METADATA;
2526
import static androidx.media3.common.Player.COMMAND_GET_TIMELINE;
2627
import static androidx.media3.common.Player.COMMAND_PLAY_PAUSE;
2728
import static androidx.media3.common.Player.COMMAND_PREPARE;
29+
import static androidx.media3.common.Player.COMMAND_SEEK_BACK;
30+
import static androidx.media3.common.Player.COMMAND_SEEK_FORWARD;
2831
import static androidx.media3.common.Player.COMMAND_SEEK_IN_CURRENT_MEDIA_ITEM;
32+
import static androidx.media3.common.Player.COMMAND_SEEK_TO_DEFAULT_POSITION;
2933
import static androidx.media3.common.Player.COMMAND_SEEK_TO_NEXT;
3034
import static androidx.media3.common.Player.COMMAND_SEEK_TO_NEXT_MEDIA_ITEM;
3135
import static androidx.media3.common.Player.COMMAND_SEEK_TO_PREVIOUS;
@@ -65,6 +69,7 @@
6569
import androidx.annotation.Nullable;
6670
import androidx.media.AudioAttributesCompat;
6771
import androidx.media.MediaBrowserServiceCompat.BrowserRoot;
72+
import androidx.media.VolumeProviderCompat;
6873
import androidx.media3.common.AdPlaybackState;
6974
import androidx.media3.common.AudioAttributes;
7075
import androidx.media3.common.C;
@@ -1070,42 +1075,103 @@ public static List removeNullElements(List<@NullableType T> list) {
10701075
}
10711076

10721077
/**
1073-
* Converts {@link MediaControllerCompat#getFlags() session flags} and {@link
1074-
* MediaControllerCompat#isSessionReady whether session is ready} to {@link Player.Commands}.
1078+
* Converts {@link PlaybackStateCompat}, {@link
1079+
* MediaControllerCompat.PlaybackInfo#getVolumeControl() volume control type}, {@link
1080+
* MediaControllerCompat#getFlags() session flags} and {@link MediaControllerCompat#isSessionReady
1081+
* whether the session is ready} to {@link Player.Commands}.
10751082
*
1076-
* @param sessionFlags The session flag.
1083+
* @param playbackStateCompat The {@link PlaybackStateCompat}.
1084+
* @param volumeControlType The {@link MediaControllerCompat.PlaybackInfo#getVolumeControl()
1085+
* volume control type}.
1086+
* @param sessionFlags The session flags.
10771087
* @param isSessionReady Whether the session compat is ready.
10781088
* @return The converted player commands.
10791089
*/
1080-
public static Player.Commands convertToPlayerCommands(long sessionFlags, boolean isSessionReady) {
1090+
public static Player.Commands convertToPlayerCommands(
1091+
@Nullable PlaybackStateCompat playbackStateCompat,
1092+
int volumeControlType,
1093+
long sessionFlags,
1094+
boolean isSessionReady) {
10811095
Commands.Builder playerCommandsBuilder = new Commands.Builder();
1096+
long actions = playbackStateCompat == null ? 0 : playbackStateCompat.getActions();
1097+
if ((hasAction(actions, PlaybackStateCompat.ACTION_PLAY)
1098+
&& hasAction(actions, PlaybackStateCompat.ACTION_PAUSE))
1099+
|| hasAction(actions, PlaybackStateCompat.ACTION_PLAY_PAUSE)) {
1100+
playerCommandsBuilder.add(COMMAND_PLAY_PAUSE);
1101+
}
1102+
if (hasAction(actions, PlaybackStateCompat.ACTION_PREPARE)) {
1103+
playerCommandsBuilder.add(COMMAND_PREPARE);
1104+
}
1105+
if ((hasAction(actions, PlaybackStateCompat.ACTION_PREPARE_FROM_MEDIA_ID)
1106+
&& hasAction(actions, PlaybackStateCompat.ACTION_PLAY_FROM_MEDIA_ID))
1107+
|| (hasAction(actions, PlaybackStateCompat.ACTION_PREPARE_FROM_SEARCH)
1108+
&& hasAction(actions, PlaybackStateCompat.ACTION_PLAY_FROM_SEARCH))
1109+
|| (hasAction(actions, PlaybackStateCompat.ACTION_PREPARE_FROM_URI)
1110+
&& hasAction(actions, PlaybackStateCompat.ACTION_PLAY_FROM_URI))) {
1111+
// Require both PREPARE and PLAY actions as we have no logic to handle having just one action.
1112+
playerCommandsBuilder.addAll(COMMAND_SET_MEDIA_ITEM, COMMAND_PREPARE);
1113+
}
1114+
if (hasAction(actions, PlaybackStateCompat.ACTION_REWIND)) {
1115+
playerCommandsBuilder.add(COMMAND_SEEK_BACK);
1116+
}
1117+
if (hasAction(actions, PlaybackStateCompat.ACTION_FAST_FORWARD)) {
1118+
playerCommandsBuilder.add(COMMAND_SEEK_FORWARD);
1119+
}
1120+
if (hasAction(actions, PlaybackStateCompat.ACTION_SEEK_TO)) {
1121+
playerCommandsBuilder.addAll(
1122+
COMMAND_SEEK_IN_CURRENT_MEDIA_ITEM, COMMAND_SEEK_TO_DEFAULT_POSITION);
1123+
}
1124+
if (hasAction(actions, PlaybackStateCompat.ACTION_SKIP_TO_NEXT)) {
1125+
playerCommandsBuilder.addAll(COMMAND_SEEK_TO_NEXT, COMMAND_SEEK_TO_NEXT_MEDIA_ITEM);
1126+
}
1127+
if (hasAction(actions, PlaybackStateCompat.ACTION_SKIP_TO_PREVIOUS)) {
1128+
playerCommandsBuilder.addAll(COMMAND_SEEK_TO_PREVIOUS, COMMAND_SEEK_TO_PREVIOUS_MEDIA_ITEM);
1129+
}
1130+
if (hasAction(actions, PlaybackStateCompat.ACTION_SET_PLAYBACK_SPEED)) {
1131+
playerCommandsBuilder.add(COMMAND_SET_SPEED_AND_PITCH);
1132+
}
1133+
if (hasAction(actions, PlaybackStateCompat.ACTION_STOP)) {
1134+
playerCommandsBuilder.add(COMMAND_STOP);
1135+
}
1136+
if (volumeControlType == VolumeProviderCompat.VOLUME_CONTROL_RELATIVE) {
1137+
playerCommandsBuilder.add(COMMAND_ADJUST_DEVICE_VOLUME);
1138+
} else if (volumeControlType == VolumeProviderCompat.VOLUME_CONTROL_ABSOLUTE) {
1139+
playerCommandsBuilder.addAll(COMMAND_ADJUST_DEVICE_VOLUME, COMMAND_SET_DEVICE_VOLUME);
1140+
}
10821141
playerCommandsBuilder.addAll(
1083-
COMMAND_PLAY_PAUSE,
1084-
COMMAND_PREPARE,
1085-
COMMAND_STOP,
1086-
COMMAND_SEEK_IN_CURRENT_MEDIA_ITEM,
1087-
COMMAND_SET_SPEED_AND_PITCH,
10881142
COMMAND_GET_DEVICE_VOLUME,
1089-
COMMAND_SET_DEVICE_VOLUME,
1090-
COMMAND_ADJUST_DEVICE_VOLUME,
10911143
COMMAND_GET_TIMELINE,
1092-
COMMAND_SEEK_TO_PREVIOUS,
1093-
COMMAND_SEEK_TO_PREVIOUS_MEDIA_ITEM,
1094-
COMMAND_SEEK_TO_NEXT,
1095-
COMMAND_SEEK_TO_NEXT_MEDIA_ITEM,
10961144
COMMAND_GET_MEDIA_ITEMS_METADATA,
10971145
COMMAND_GET_CURRENT_MEDIA_ITEM,
1098-
COMMAND_SET_MEDIA_ITEM);
1099-
boolean includePlaylistCommands = (sessionFlags & FLAG_HANDLES_QUEUE_COMMANDS) != 0;
1100-
if (includePlaylistCommands) {
1146+
COMMAND_GET_AUDIO_ATTRIBUTES);
1147+
if ((sessionFlags & FLAG_HANDLES_QUEUE_COMMANDS) != 0) {
11011148
playerCommandsBuilder.add(COMMAND_CHANGE_MEDIA_ITEMS);
1149+
if (hasAction(actions, PlaybackStateCompat.ACTION_SKIP_TO_QUEUE_ITEM)) {
1150+
playerCommandsBuilder.add(Player.COMMAND_SEEK_TO_MEDIA_ITEM);
1151+
}
11021152
}
11031153
if (isSessionReady) {
1104-
playerCommandsBuilder.addAll(COMMAND_SET_SHUFFLE_MODE, COMMAND_SET_REPEAT_MODE);
1154+
if (hasAction(actions, PlaybackStateCompat.ACTION_SET_REPEAT_MODE)) {
1155+
playerCommandsBuilder.add(COMMAND_SET_REPEAT_MODE);
1156+
}
1157+
if (hasAction(actions, PlaybackStateCompat.ACTION_SET_SHUFFLE_MODE)) {
1158+
playerCommandsBuilder.add(COMMAND_SET_SHUFFLE_MODE);
1159+
}
11051160
}
11061161
return playerCommandsBuilder.build();
11071162
}
11081163

1164+
/**
1165+
* Checks if the set of actions contains the specified action.
1166+
*
1167+
* @param actions A bit set of actions.
1168+
* @param action The action to check.
1169+
* @return Whether the action is contained in the set.
1170+
*/
1171+
private static boolean hasAction(long actions, @PlaybackStateCompat.Actions long action) {
1172+
return (actions & action) != 0;
1173+
}
1174+
11091175
/**
11101176
* Converts {@link PlaybackStateCompat} to {@link SessionCommands}.
11111177
*

libraries/test_session_common/src/main/java/androidx/media3/test/session/common/TestUtils.java

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -153,6 +153,20 @@ static void setKeepScreenOn(Activity activity) {
153153
return list.build();
154154
}
155155

156+
/**
157+
* Returns an {@link ImmutableList} with the {@linkplain Player.Command Commands} contained in
158+
* {@code commands}. The contents of the list are in matching order with the {@linkplain
159+
* Player.Command Commands} returned by {@link Player.Commands#get(int)}.
160+
*/
161+
// TODO(b/254265256): Move this method off test-session-common.
162+
public static ImmutableList<@Player.Command Integer> getCommandsAsList(Player.Commands commands) {
163+
ImmutableList.Builder<@Player.Command Integer> list = new ImmutableList.Builder<>();
164+
for (int i = 0; i < commands.size(); i++) {
165+
list.add(commands.get(i));
166+
}
167+
return list.build();
168+
}
169+
156170
/** Returns the bytes of a scaled asset file. */
157171
public static byte[] getByteArrayForScaledBitmap(Context context, String fileName)
158172
throws IOException {

0 commit comments

Comments
 (0)