diff --git a/libraries/session/src/main/java/androidx/media3/session/MediaControllerImplLegacy.java b/libraries/session/src/main/java/androidx/media3/session/MediaControllerImplLegacy.java index 489997beb8..fc58fbcbc5 100644 --- a/libraries/session/src/main/java/androidx/media3/session/MediaControllerImplLegacy.java +++ b/libraries/session/src/main/java/androidx/media3/session/MediaControllerImplLegacy.java @@ -46,6 +46,7 @@ import android.view.SurfaceView; import android.view.TextureView; import androidx.annotation.CheckResult; import androidx.annotation.Nullable; +import androidx.media.VolumeProviderCompat; import androidx.media3.common.AudioAttributes; import androidx.media3.common.C; import androidx.media3.common.DeviceInfo; @@ -1877,9 +1878,17 @@ import org.checkerframework.checker.nullness.compatqual.NullableType; // Note: Sets the available player command here although it can be obtained before session is // ready. It's to follow the decision on MediaController to disallow any commands before // connection is made. + int volumeControlType = + newLegacyPlayerInfo.playbackInfoCompat != null + ? newLegacyPlayerInfo.playbackInfoCompat.getVolumeControl() + : VolumeProviderCompat.VOLUME_CONTROL_FIXED; availablePlayerCommands = (oldControllerInfo.availablePlayerCommands == Commands.EMPTY) - ? MediaUtils.convertToPlayerCommands(sessionFlags, isSessionReady) + ? MediaUtils.convertToPlayerCommands( + newLegacyPlayerInfo.playbackStateCompat, + volumeControlType, + sessionFlags, + isSessionReady) : oldControllerInfo.availablePlayerCommands; PlaybackException playerError = diff --git a/libraries/session/src/main/java/androidx/media3/session/MediaUtils.java b/libraries/session/src/main/java/androidx/media3/session/MediaUtils.java index fc983aa537..95f3e3bbf5 100644 --- a/libraries/session/src/main/java/androidx/media3/session/MediaUtils.java +++ b/libraries/session/src/main/java/androidx/media3/session/MediaUtils.java @@ -19,13 +19,17 @@ import static android.support.v4.media.session.MediaSessionCompat.FLAG_HANDLES_Q import static androidx.media.utils.MediaConstants.BROWSER_ROOT_HINTS_KEY_ROOT_CHILDREN_SUPPORTED_FLAGS; import static androidx.media3.common.Player.COMMAND_ADJUST_DEVICE_VOLUME; import static androidx.media3.common.Player.COMMAND_CHANGE_MEDIA_ITEMS; +import static androidx.media3.common.Player.COMMAND_GET_AUDIO_ATTRIBUTES; import static androidx.media3.common.Player.COMMAND_GET_CURRENT_MEDIA_ITEM; import static androidx.media3.common.Player.COMMAND_GET_DEVICE_VOLUME; import static androidx.media3.common.Player.COMMAND_GET_MEDIA_ITEMS_METADATA; import static androidx.media3.common.Player.COMMAND_GET_TIMELINE; import static androidx.media3.common.Player.COMMAND_PLAY_PAUSE; import static androidx.media3.common.Player.COMMAND_PREPARE; +import static androidx.media3.common.Player.COMMAND_SEEK_BACK; +import static androidx.media3.common.Player.COMMAND_SEEK_FORWARD; import static androidx.media3.common.Player.COMMAND_SEEK_IN_CURRENT_MEDIA_ITEM; +import static androidx.media3.common.Player.COMMAND_SEEK_TO_DEFAULT_POSITION; import static androidx.media3.common.Player.COMMAND_SEEK_TO_NEXT; import static androidx.media3.common.Player.COMMAND_SEEK_TO_NEXT_MEDIA_ITEM; import static androidx.media3.common.Player.COMMAND_SEEK_TO_PREVIOUS; @@ -65,6 +69,7 @@ import android.util.Pair; import androidx.annotation.Nullable; import androidx.media.AudioAttributesCompat; import androidx.media.MediaBrowserServiceCompat.BrowserRoot; +import androidx.media.VolumeProviderCompat; import androidx.media3.common.AdPlaybackState; import androidx.media3.common.AudioAttributes; import androidx.media3.common.C; @@ -1070,42 +1075,103 @@ import org.checkerframework.checker.nullness.compatqual.NullableType; } /** - * Converts {@link MediaControllerCompat#getFlags() session flags} and {@link - * MediaControllerCompat#isSessionReady whether session is ready} to {@link Player.Commands}. + * Converts {@link PlaybackStateCompat}, {@link + * MediaControllerCompat.PlaybackInfo#getVolumeControl() volume control type}, {@link + * MediaControllerCompat#getFlags() session flags} and {@link MediaControllerCompat#isSessionReady + * whether the session is ready} to {@link Player.Commands}. * - * @param sessionFlags The session flag. + * @param playbackStateCompat The {@link PlaybackStateCompat}. + * @param volumeControlType The {@link MediaControllerCompat.PlaybackInfo#getVolumeControl() + * volume control type}. + * @param sessionFlags The session flags. * @param isSessionReady Whether the session compat is ready. * @return The converted player commands. */ - public static Player.Commands convertToPlayerCommands(long sessionFlags, boolean isSessionReady) { + public static Player.Commands convertToPlayerCommands( + @Nullable PlaybackStateCompat playbackStateCompat, + int volumeControlType, + long sessionFlags, + boolean isSessionReady) { Commands.Builder playerCommandsBuilder = new Commands.Builder(); + long actions = playbackStateCompat == null ? 0 : playbackStateCompat.getActions(); + if ((hasAction(actions, PlaybackStateCompat.ACTION_PLAY) + && hasAction(actions, PlaybackStateCompat.ACTION_PAUSE)) + || hasAction(actions, PlaybackStateCompat.ACTION_PLAY_PAUSE)) { + playerCommandsBuilder.add(COMMAND_PLAY_PAUSE); + } + if (hasAction(actions, PlaybackStateCompat.ACTION_PREPARE)) { + playerCommandsBuilder.add(COMMAND_PREPARE); + } + if ((hasAction(actions, PlaybackStateCompat.ACTION_PREPARE_FROM_MEDIA_ID) + && hasAction(actions, PlaybackStateCompat.ACTION_PLAY_FROM_MEDIA_ID)) + || (hasAction(actions, PlaybackStateCompat.ACTION_PREPARE_FROM_SEARCH) + && hasAction(actions, PlaybackStateCompat.ACTION_PLAY_FROM_SEARCH)) + || (hasAction(actions, PlaybackStateCompat.ACTION_PREPARE_FROM_URI) + && hasAction(actions, PlaybackStateCompat.ACTION_PLAY_FROM_URI))) { + // Require both PREPARE and PLAY actions as we have no logic to handle having just one action. + playerCommandsBuilder.addAll(COMMAND_SET_MEDIA_ITEM, COMMAND_PREPARE); + } + if (hasAction(actions, PlaybackStateCompat.ACTION_REWIND)) { + playerCommandsBuilder.add(COMMAND_SEEK_BACK); + } + if (hasAction(actions, PlaybackStateCompat.ACTION_FAST_FORWARD)) { + playerCommandsBuilder.add(COMMAND_SEEK_FORWARD); + } + if (hasAction(actions, PlaybackStateCompat.ACTION_SEEK_TO)) { + playerCommandsBuilder.addAll( + COMMAND_SEEK_IN_CURRENT_MEDIA_ITEM, COMMAND_SEEK_TO_DEFAULT_POSITION); + } + if (hasAction(actions, PlaybackStateCompat.ACTION_SKIP_TO_NEXT)) { + playerCommandsBuilder.addAll(COMMAND_SEEK_TO_NEXT, COMMAND_SEEK_TO_NEXT_MEDIA_ITEM); + } + if (hasAction(actions, PlaybackStateCompat.ACTION_SKIP_TO_PREVIOUS)) { + playerCommandsBuilder.addAll(COMMAND_SEEK_TO_PREVIOUS, COMMAND_SEEK_TO_PREVIOUS_MEDIA_ITEM); + } + if (hasAction(actions, PlaybackStateCompat.ACTION_SET_PLAYBACK_SPEED)) { + playerCommandsBuilder.add(COMMAND_SET_SPEED_AND_PITCH); + } + if (hasAction(actions, PlaybackStateCompat.ACTION_STOP)) { + playerCommandsBuilder.add(COMMAND_STOP); + } + if (volumeControlType == VolumeProviderCompat.VOLUME_CONTROL_RELATIVE) { + playerCommandsBuilder.add(COMMAND_ADJUST_DEVICE_VOLUME); + } else if (volumeControlType == VolumeProviderCompat.VOLUME_CONTROL_ABSOLUTE) { + playerCommandsBuilder.addAll(COMMAND_ADJUST_DEVICE_VOLUME, COMMAND_SET_DEVICE_VOLUME); + } playerCommandsBuilder.addAll( - COMMAND_PLAY_PAUSE, - COMMAND_PREPARE, - COMMAND_STOP, - COMMAND_SEEK_IN_CURRENT_MEDIA_ITEM, - COMMAND_SET_SPEED_AND_PITCH, COMMAND_GET_DEVICE_VOLUME, - COMMAND_SET_DEVICE_VOLUME, - COMMAND_ADJUST_DEVICE_VOLUME, COMMAND_GET_TIMELINE, - COMMAND_SEEK_TO_PREVIOUS, - COMMAND_SEEK_TO_PREVIOUS_MEDIA_ITEM, - COMMAND_SEEK_TO_NEXT, - COMMAND_SEEK_TO_NEXT_MEDIA_ITEM, COMMAND_GET_MEDIA_ITEMS_METADATA, COMMAND_GET_CURRENT_MEDIA_ITEM, - COMMAND_SET_MEDIA_ITEM); - boolean includePlaylistCommands = (sessionFlags & FLAG_HANDLES_QUEUE_COMMANDS) != 0; - if (includePlaylistCommands) { + COMMAND_GET_AUDIO_ATTRIBUTES); + if ((sessionFlags & FLAG_HANDLES_QUEUE_COMMANDS) != 0) { playerCommandsBuilder.add(COMMAND_CHANGE_MEDIA_ITEMS); + if (hasAction(actions, PlaybackStateCompat.ACTION_SKIP_TO_QUEUE_ITEM)) { + playerCommandsBuilder.add(Player.COMMAND_SEEK_TO_MEDIA_ITEM); + } } if (isSessionReady) { - playerCommandsBuilder.addAll(COMMAND_SET_SHUFFLE_MODE, COMMAND_SET_REPEAT_MODE); + if (hasAction(actions, PlaybackStateCompat.ACTION_SET_REPEAT_MODE)) { + playerCommandsBuilder.add(COMMAND_SET_REPEAT_MODE); + } + if (hasAction(actions, PlaybackStateCompat.ACTION_SET_SHUFFLE_MODE)) { + playerCommandsBuilder.add(COMMAND_SET_SHUFFLE_MODE); + } } return playerCommandsBuilder.build(); } + /** + * Checks if the set of actions contains the specified action. + * + * @param actions A bit set of actions. + * @param action The action to check. + * @return Whether the action is contained in the set. + */ + private static boolean hasAction(long actions, @PlaybackStateCompat.Actions long action) { + return (actions & action) != 0; + } + /** * Converts {@link PlaybackStateCompat} to {@link SessionCommands}. * diff --git a/libraries/test_session_common/src/main/java/androidx/media3/test/session/common/TestUtils.java b/libraries/test_session_common/src/main/java/androidx/media3/test/session/common/TestUtils.java index fe90b5bd04..5a6e05df29 100644 --- a/libraries/test_session_common/src/main/java/androidx/media3/test/session/common/TestUtils.java +++ b/libraries/test_session_common/src/main/java/androidx/media3/test/session/common/TestUtils.java @@ -153,6 +153,20 @@ public class TestUtils { return list.build(); } + /** + * Returns an {@link ImmutableList} with the {@linkplain Player.Command Commands} contained in + * {@code commands}. The contents of the list are in matching order with the {@linkplain + * Player.Command Commands} returned by {@link Player.Commands#get(int)}. + */ + // TODO(b/254265256): Move this method off test-session-common. + public static ImmutableList<@Player.Command Integer> getCommandsAsList(Player.Commands commands) { + ImmutableList.Builder<@Player.Command Integer> list = new ImmutableList.Builder<>(); + for (int i = 0; i < commands.size(); i++) { + list.add(commands.get(i)); + } + return list.build(); + } + /** Returns the bytes of a scaled asset file. */ public static byte[] getByteArrayForScaledBitmap(Context context, String fileName) throws IOException { diff --git a/libraries/test_session_current/src/androidTest/java/androidx/media3/session/MediaUtilsTest.java b/libraries/test_session_current/src/androidTest/java/androidx/media3/session/MediaUtilsTest.java index 9511124a5b..9b0864857a 100644 --- a/libraries/test_session_current/src/androidTest/java/androidx/media3/session/MediaUtilsTest.java +++ b/libraries/test_session_current/src/androidTest/java/androidx/media3/session/MediaUtilsTest.java @@ -18,12 +18,12 @@ package androidx.media3.session; import static android.support.v4.media.MediaBrowserCompat.MediaItem.FLAG_BROWSABLE; import static android.support.v4.media.MediaBrowserCompat.MediaItem.FLAG_PLAYABLE; import static android.support.v4.media.MediaMetadataCompat.METADATA_KEY_DURATION; -import static android.support.v4.media.session.MediaSessionCompat.FLAG_HANDLES_QUEUE_COMMANDS; import static androidx.media.utils.MediaConstants.BROWSER_ROOT_HINTS_KEY_ROOT_CHILDREN_SUPPORTED_FLAGS; import static androidx.media3.common.MimeTypes.AUDIO_AAC; import static androidx.media3.common.MimeTypes.VIDEO_H264; import static androidx.media3.common.MimeTypes.VIDEO_H265; import static androidx.media3.session.MediaConstants.EXTRA_KEY_ROOT_CHILDREN_BROWSABLE_ONLY; +import static androidx.media3.test.session.common.TestUtils.getCommandsAsList; import static com.google.common.truth.Truth.assertThat; import static java.util.concurrent.TimeUnit.SECONDS; @@ -43,6 +43,7 @@ import android.support.v4.media.session.PlaybackStateCompat; import android.util.Pair; import androidx.annotation.Nullable; import androidx.media.AudioAttributesCompat; +import androidx.media.VolumeProviderCompat; import androidx.media.utils.MediaConstants; import androidx.media3.common.AudioAttributes; import androidx.media3.common.C; @@ -479,19 +480,399 @@ public final class MediaUtilsTest { } @Test - public void convertToPlayerCommands() { - long sessionFlags = FLAG_HANDLES_QUEUE_COMMANDS; + public void convertToPlayerCommands_withNoActions_onlyDefaultCommandsAvailable() { + PlaybackStateCompat playbackStateCompat = + new PlaybackStateCompat.Builder().setActions(/* capabilities= */ 0).build(); + Player.Commands playerCommands = - MediaUtils.convertToPlayerCommands(sessionFlags, /* isSessionReady= */ true); - assertThat(playerCommands.contains(Player.COMMAND_GET_TIMELINE)).isTrue(); + MediaUtils.convertToPlayerCommands( + playbackStateCompat, + /* volumeControlType= */ VolumeProviderCompat.VOLUME_CONTROL_FIXED, + /* sessionFlags= */ 0, + /* isSessionReady= */ true); + + assertThat(getCommandsAsList(playerCommands)) + .containsExactly( + Player.COMMAND_GET_TIMELINE, + Player.COMMAND_GET_CURRENT_MEDIA_ITEM, + Player.COMMAND_GET_DEVICE_VOLUME, + Player.COMMAND_GET_MEDIA_ITEMS_METADATA, + Player.COMMAND_GET_AUDIO_ATTRIBUTES); } @Test - public void convertToPlayerCommands_whenSessionIsNotReady_disallowsShuffle() { - long sessionFlags = FLAG_HANDLES_QUEUE_COMMANDS; + public void convertToPlayerCommands_withJustPlayAction_playPauseCommandNotAvailable() { + PlaybackStateCompat playbackStateCompat = + new PlaybackStateCompat.Builder().setActions(PlaybackStateCompat.ACTION_PLAY).build(); + Player.Commands playerCommands = - MediaUtils.convertToPlayerCommands(sessionFlags, /* isSessionReady= */ false); - assertThat(playerCommands.contains(Player.COMMAND_SET_SHUFFLE_MODE)).isFalse(); + MediaUtils.convertToPlayerCommands( + playbackStateCompat, + /* volumeControlType= */ VolumeProviderCompat.VOLUME_CONTROL_FIXED, + /* sessionFlags= */ 0, + /* isSessionReady= */ true); + + assertThat(getCommandsAsList(playerCommands)).doesNotContain(Player.COMMAND_PLAY_PAUSE); + } + + @Test + public void convertToPlayerCommands_withJustPauseAction_playPauseCommandNotAvailable() { + PlaybackStateCompat playbackStateCompat = + new PlaybackStateCompat.Builder().setActions(PlaybackStateCompat.ACTION_PAUSE).build(); + + Player.Commands playerCommands = + MediaUtils.convertToPlayerCommands( + playbackStateCompat, + /* volumeControlType= */ VolumeProviderCompat.VOLUME_CONTROL_FIXED, + /* sessionFlags= */ 0, + /* isSessionReady= */ true); + + assertThat(getCommandsAsList(playerCommands)).doesNotContain(Player.COMMAND_PLAY_PAUSE); + } + + @Test + public void convertToPlayerCommands_withPlayAndPauseAction_playPauseCommandAvailable() { + PlaybackStateCompat playbackStateCompat = + new PlaybackStateCompat.Builder() + .setActions(PlaybackStateCompat.ACTION_PLAY | PlaybackStateCompat.ACTION_PAUSE) + .build(); + + Player.Commands playerCommands = + MediaUtils.convertToPlayerCommands( + playbackStateCompat, + /* volumeControlType= */ VolumeProviderCompat.VOLUME_CONTROL_FIXED, + /* sessionFlags= */ 0, + /* isSessionReady= */ true); + + assertThat(getCommandsAsList(playerCommands)).contains(Player.COMMAND_PLAY_PAUSE); + } + + @Test + public void convertToPlayerCommands_withPlayPauseAction_playPauseCommandAvailable() { + PlaybackStateCompat playbackStateCompat = + new PlaybackStateCompat.Builder().setActions(PlaybackStateCompat.ACTION_PLAY_PAUSE).build(); + + Player.Commands playerCommands = + MediaUtils.convertToPlayerCommands( + playbackStateCompat, + /* volumeControlType= */ VolumeProviderCompat.VOLUME_CONTROL_FIXED, + /* sessionFlags= */ 0, + /* isSessionReady= */ true); + + assertThat(getCommandsAsList(playerCommands)).contains(Player.COMMAND_PLAY_PAUSE); + } + + @Test + public void convertToPlayerCommands_withPrepareAction_prepareCommandAvailable() { + PlaybackStateCompat playbackStateCompat = + new PlaybackStateCompat.Builder().setActions(PlaybackStateCompat.ACTION_PREPARE).build(); + + Player.Commands playerCommands = + MediaUtils.convertToPlayerCommands( + playbackStateCompat, + /* volumeControlType= */ VolumeProviderCompat.VOLUME_CONTROL_FIXED, + /* sessionFlags= */ 0, + /* isSessionReady= */ true); + + assertThat(getCommandsAsList(playerCommands)).contains(Player.COMMAND_PREPARE); + } + + @Test + public void convertToPlayerCommands_withRewindAction_seekBackCommandAvailable() { + PlaybackStateCompat playbackStateCompat = + new PlaybackStateCompat.Builder().setActions(PlaybackStateCompat.ACTION_REWIND).build(); + + Player.Commands playerCommands = + MediaUtils.convertToPlayerCommands( + playbackStateCompat, + /* volumeControlType= */ VolumeProviderCompat.VOLUME_CONTROL_FIXED, + /* sessionFlags= */ 0, + /* isSessionReady= */ true); + + assertThat(getCommandsAsList(playerCommands)).contains(Player.COMMAND_SEEK_BACK); + } + + @Test + public void convertToPlayerCommands_withFastForwardAction_seekForwardCommandAvailable() { + PlaybackStateCompat playbackStateCompat = + new PlaybackStateCompat.Builder() + .setActions(PlaybackStateCompat.ACTION_FAST_FORWARD) + .build(); + + Player.Commands playerCommands = + MediaUtils.convertToPlayerCommands( + playbackStateCompat, + /* volumeControlType= */ VolumeProviderCompat.VOLUME_CONTROL_FIXED, + /* sessionFlags= */ 0, + /* isSessionReady= */ true); + + assertThat(getCommandsAsList(playerCommands)).contains(Player.COMMAND_SEEK_FORWARD); + } + + @Test + public void convertToPlayerCommands_withSeekToAction_seekInCurrentMediaItemCommandAvailable() { + PlaybackStateCompat playbackStateCompat = + new PlaybackStateCompat.Builder().setActions(PlaybackStateCompat.ACTION_SEEK_TO).build(); + + Player.Commands playerCommands = + MediaUtils.convertToPlayerCommands( + playbackStateCompat, + /* volumeControlType= */ VolumeProviderCompat.VOLUME_CONTROL_FIXED, + /* sessionFlags= */ 0, + /* isSessionReady= */ true); + + assertThat(getCommandsAsList(playerCommands)) + .contains(Player.COMMAND_SEEK_IN_CURRENT_MEDIA_ITEM); + } + + @Test + public void convertToPlayerCommands_withSkipToNextAction_seekToNextCommandsAvailable() { + PlaybackStateCompat playbackStateCompat = + new PlaybackStateCompat.Builder() + .setActions(PlaybackStateCompat.ACTION_SKIP_TO_NEXT) + .build(); + + Player.Commands playerCommands = + MediaUtils.convertToPlayerCommands( + playbackStateCompat, + /* volumeControlType= */ VolumeProviderCompat.VOLUME_CONTROL_FIXED, + /* sessionFlags= */ 0, + /* isSessionReady= */ true); + + assertThat(getCommandsAsList(playerCommands)) + .containsAtLeast(Player.COMMAND_SEEK_TO_NEXT, Player.COMMAND_SEEK_TO_NEXT_MEDIA_ITEM); + } + + @Test + public void convertToPlayerCommands_withSkipToPreviousAction_seekToPreviousCommandsAvailable() { + PlaybackStateCompat playbackStateCompat = + new PlaybackStateCompat.Builder() + .setActions(PlaybackStateCompat.ACTION_SKIP_TO_PREVIOUS) + .build(); + + Player.Commands playerCommands = + MediaUtils.convertToPlayerCommands( + playbackStateCompat, + /* volumeControlType= */ VolumeProviderCompat.VOLUME_CONTROL_FIXED, + /* sessionFlags= */ 0, + /* isSessionReady= */ true); + + assertThat(getCommandsAsList(playerCommands)) + .containsAtLeast( + Player.COMMAND_SEEK_TO_PREVIOUS, Player.COMMAND_SEEK_TO_PREVIOUS_MEDIA_ITEM); + } + + @Test + public void + convertToPlayerCommands_withPlayFromActionsWithoutPrepareFromAction_setMediaItemCommandNotAvailable() { + PlaybackStateCompat playbackStateCompat = + new PlaybackStateCompat.Builder() + .setActions( + PlaybackStateCompat.ACTION_PLAY_FROM_MEDIA_ID + | PlaybackStateCompat.ACTION_PLAY_FROM_SEARCH + | PlaybackStateCompat.ACTION_PLAY_FROM_URI) + .build(); + + Player.Commands playerCommands = + MediaUtils.convertToPlayerCommands( + playbackStateCompat, + /* volumeControlType= */ VolumeProviderCompat.VOLUME_CONTROL_FIXED, + /* sessionFlags= */ 0, + /* isSessionReady= */ true); + + assertThat(getCommandsAsList(playerCommands)) + .containsNoneOf(Player.COMMAND_SET_MEDIA_ITEM, Player.COMMAND_PREPARE); + } + + @Test + public void + convertToPlayerCommands_withPrepareFromActionsWithoutPlayFromAction_setMediaItemCommandNotAvailable() { + PlaybackStateCompat playbackStateCompat = + new PlaybackStateCompat.Builder() + .setActions( + PlaybackStateCompat.ACTION_PREPARE_FROM_MEDIA_ID + | PlaybackStateCompat.ACTION_PREPARE_FROM_SEARCH + | PlaybackStateCompat.ACTION_PREPARE_FROM_URI) + .build(); + + Player.Commands playerCommands = + MediaUtils.convertToPlayerCommands( + playbackStateCompat, + /* volumeControlType= */ VolumeProviderCompat.VOLUME_CONTROL_FIXED, + /* sessionFlags= */ 0, + /* isSessionReady= */ true); + + assertThat(getCommandsAsList(playerCommands)) + .containsNoneOf(Player.COMMAND_SET_MEDIA_ITEM, Player.COMMAND_PREPARE); + } + + @Test + public void + convertToPlayerCommands_withPlayFromAndPrepareFromMediaId_setMediaItemPrepareAndPlayAvailable() { + PlaybackStateCompat playbackStateCompat = + new PlaybackStateCompat.Builder() + .setActions( + PlaybackStateCompat.ACTION_PLAY_FROM_MEDIA_ID + | PlaybackStateCompat.ACTION_PREPARE_FROM_MEDIA_ID) + .build(); + + Player.Commands playerCommands = + MediaUtils.convertToPlayerCommands( + playbackStateCompat, + /* volumeControlType= */ VolumeProviderCompat.VOLUME_CONTROL_FIXED, + /* sessionFlags= */ 0, + /* isSessionReady= */ true); + + assertThat(getCommandsAsList(playerCommands)) + .containsAtLeast(Player.COMMAND_SET_MEDIA_ITEM, Player.COMMAND_PREPARE); + } + + @Test + public void + convertToPlayerCommands_withPlayFromAndPrepareFromSearch_setMediaItemPrepareAndPlayAvailable() { + PlaybackStateCompat playbackStateCompat = + new PlaybackStateCompat.Builder() + .setActions( + PlaybackStateCompat.ACTION_PLAY_FROM_SEARCH + | PlaybackStateCompat.ACTION_PREPARE_FROM_SEARCH) + .build(); + + Player.Commands playerCommands = + MediaUtils.convertToPlayerCommands( + playbackStateCompat, + /* volumeControlType= */ VolumeProviderCompat.VOLUME_CONTROL_FIXED, + /* sessionFlags= */ 0, + /* isSessionReady= */ true); + + assertThat(getCommandsAsList(playerCommands)) + .containsAtLeast(Player.COMMAND_SET_MEDIA_ITEM, Player.COMMAND_PREPARE); + } + + @Test + public void + convertToPlayerCommands_withPlayFromAndPrepareFromUri_setMediaItemPrepareAndPlayAvailable() { + PlaybackStateCompat playbackStateCompat = + new PlaybackStateCompat.Builder() + .setActions( + PlaybackStateCompat.ACTION_PLAY_FROM_URI + | PlaybackStateCompat.ACTION_PREPARE_FROM_URI) + .build(); + + Player.Commands playerCommands = + MediaUtils.convertToPlayerCommands( + playbackStateCompat, + /* volumeControlType= */ VolumeProviderCompat.VOLUME_CONTROL_FIXED, + /* sessionFlags= */ 0, + /* isSessionReady= */ true); + + assertThat(getCommandsAsList(playerCommands)) + .containsAtLeast(Player.COMMAND_SET_MEDIA_ITEM, Player.COMMAND_PREPARE); + } + + @Test + public void convertToPlayerCommands_withSetPlaybackSpeedAction_setSpeedCommandAvailable() { + PlaybackStateCompat playbackStateCompat = + new PlaybackStateCompat.Builder() + .setActions(PlaybackStateCompat.ACTION_SET_PLAYBACK_SPEED) + .build(); + + Player.Commands playerCommands = + MediaUtils.convertToPlayerCommands( + playbackStateCompat, + /* volumeControlType= */ VolumeProviderCompat.VOLUME_CONTROL_FIXED, + /* sessionFlags= */ 0, + /* isSessionReady= */ true); + + assertThat(getCommandsAsList(playerCommands)).contains(Player.COMMAND_SET_SPEED_AND_PITCH); + } + + @Test + public void convertToPlayerCommands_withStopAction_stopCommandAvailable() { + PlaybackStateCompat playbackStateCompat = + new PlaybackStateCompat.Builder().setActions(PlaybackStateCompat.ACTION_STOP).build(); + + Player.Commands playerCommands = + MediaUtils.convertToPlayerCommands( + playbackStateCompat, + /* volumeControlType= */ VolumeProviderCompat.VOLUME_CONTROL_FIXED, + /* sessionFlags= */ 0, + /* isSessionReady= */ true); + + assertThat(getCommandsAsList(playerCommands)).contains(Player.COMMAND_STOP); + } + + @Test + public void convertToPlayerCommands_withRelativeVolumeControl_adjustVolumeCommandAvailable() { + PlaybackStateCompat playbackStateCompat = + new PlaybackStateCompat.Builder().setActions(/* capabilities= */ 0).build(); + + Player.Commands playerCommands = + MediaUtils.convertToPlayerCommands( + playbackStateCompat, + /* volumeControlType= */ VolumeProviderCompat.VOLUME_CONTROL_RELATIVE, + /* sessionFlags= */ 0, + /* isSessionReady= */ true); + + assertThat(getCommandsAsList(playerCommands)).contains(Player.COMMAND_ADJUST_DEVICE_VOLUME); + assertThat(getCommandsAsList(playerCommands)).doesNotContain(Player.COMMAND_SET_DEVICE_VOLUME); + } + + @Test + public void convertToPlayerCommands_withAbsoluteVolumeControl_adjustVolumeCommandAvailable() { + PlaybackStateCompat playbackStateCompat = + new PlaybackStateCompat.Builder().setActions(/* capabilities= */ 0).build(); + + Player.Commands playerCommands = + MediaUtils.convertToPlayerCommands( + playbackStateCompat, + /* volumeControlType= */ VolumeProviderCompat.VOLUME_CONTROL_ABSOLUTE, + /* sessionFlags= */ 0, + /* isSessionReady= */ true); + + assertThat(getCommandsAsList(playerCommands)) + .containsAtLeast(Player.COMMAND_ADJUST_DEVICE_VOLUME, Player.COMMAND_SET_DEVICE_VOLUME); + } + + @Test + public void + convertToPlayerCommands_withShuffleRepeatActionsAndSessionReady_shuffleAndRepeatCommandsAvailable() { + PlaybackStateCompat playbackStateCompat = + new PlaybackStateCompat.Builder() + .setActions( + PlaybackStateCompat.ACTION_SET_REPEAT_MODE + | PlaybackStateCompat.ACTION_SET_SHUFFLE_MODE) + .build(); + + Player.Commands playerCommands = + MediaUtils.convertToPlayerCommands( + playbackStateCompat, + /* volumeControlType= */ VolumeProviderCompat.VOLUME_CONTROL_FIXED, + /* sessionFlags= */ 0, + /* isSessionReady= */ true); + + assertThat(getCommandsAsList(playerCommands)) + .containsAtLeast(Player.COMMAND_SET_REPEAT_MODE, Player.COMMAND_SET_SHUFFLE_MODE); + } + + @Test + public void + convertToPlayerCommands_withShuffleRepeatActionsAndSessionNotReady_shuffleAndRepeatCommandsNotAvailable() { + PlaybackStateCompat playbackStateCompat = + new PlaybackStateCompat.Builder() + .setActions( + PlaybackStateCompat.ACTION_SET_REPEAT_MODE + | PlaybackStateCompat.ACTION_SET_SHUFFLE_MODE) + .build(); + + Player.Commands playerCommands = + MediaUtils.convertToPlayerCommands( + playbackStateCompat, + /* volumeControlType= */ VolumeProviderCompat.VOLUME_CONTROL_FIXED, + /* sessionFlags= */ 0, + /* isSessionReady= */ false); + + assertThat(getCommandsAsList(playerCommands)) + .containsNoneOf(Player.COMMAND_SET_REPEAT_MODE, Player.COMMAND_SET_SHUFFLE_MODE); } @Test