diff --git a/extensions/cast/src/main/java/com/google/android/exoplayer2/ext/cast/CastPlayer.java b/extensions/cast/src/main/java/com/google/android/exoplayer2/ext/cast/CastPlayer.java index 4462e4cfa1..836e0b06ca 100644 --- a/extensions/cast/src/main/java/com/google/android/exoplayer2/ext/cast/CastPlayer.java +++ b/extensions/cast/src/main/java/com/google/android/exoplayer2/ext/cast/CastPlayer.java @@ -75,17 +75,18 @@ public final class CastPlayer extends BasePlayer { } @VisibleForTesting - /* package */ static final int[] PERMANENT_AVAILABLE_COMMANDS = - new int[] { - COMMAND_PLAY_PAUSE, - COMMAND_PREPARE_STOP_RELEASE, - COMMAND_SEEK_TO_MEDIA_ITEM, - COMMAND_SET_REPEAT_MODE, - COMMAND_GET_CURRENT_MEDIA_ITEM, - COMMAND_GET_MEDIA_ITEMS, - COMMAND_GET_MEDIA_ITEMS_METADATA, - COMMAND_CHANGE_MEDIA_ITEMS - }; + /* package */ static final Commands PERMANENT_AVAILABLE_COMMANDS = + new Commands.Builder() + .addAll( + COMMAND_PLAY_PAUSE, + COMMAND_PREPARE_STOP_RELEASE, + COMMAND_SEEK_TO_MEDIA_ITEM, + COMMAND_SET_REPEAT_MODE, + COMMAND_GET_CURRENT_MEDIA_ITEM, + COMMAND_GET_MEDIA_ITEMS, + COMMAND_GET_MEDIA_ITEMS_METADATA, + COMMAND_CHANGE_MEDIA_ITEMS) + .build(); private static final String TAG = "CastPlayer"; diff --git a/extensions/cast/src/test/java/com/google/android/exoplayer2/ext/cast/CastPlayerTest.java b/extensions/cast/src/test/java/com/google/android/exoplayer2/ext/cast/CastPlayerTest.java index 11b217d4cf..746d5bf77b 100644 --- a/extensions/cast/src/test/java/com/google/android/exoplayer2/ext/cast/CastPlayerTest.java +++ b/extensions/cast/src/test/java/com/google/android/exoplayer2/ext/cast/CastPlayerTest.java @@ -15,19 +15,26 @@ */ package com.google.android.exoplayer2.ext.cast; +import static com.google.android.exoplayer2.Player.COMMAND_ADJUST_DEVICE_VOLUME; import static com.google.android.exoplayer2.Player.COMMAND_CHANGE_MEDIA_ITEMS; import static com.google.android.exoplayer2.Player.COMMAND_GET_CURRENT_MEDIA_ITEM; +import static com.google.android.exoplayer2.Player.COMMAND_GET_DEVICE_VOLUME; import static com.google.android.exoplayer2.Player.COMMAND_GET_MEDIA_ITEMS; import static com.google.android.exoplayer2.Player.COMMAND_GET_MEDIA_ITEMS_METADATA; +import static com.google.android.exoplayer2.Player.COMMAND_GET_TEXT; +import static com.google.android.exoplayer2.Player.COMMAND_GET_VOLUME; import static com.google.android.exoplayer2.Player.COMMAND_PLAY_PAUSE; import static com.google.android.exoplayer2.Player.COMMAND_PREPARE_STOP_RELEASE; import static com.google.android.exoplayer2.Player.COMMAND_SEEK_IN_CURRENT_MEDIA_ITEM; import static com.google.android.exoplayer2.Player.COMMAND_SEEK_TO_MEDIA_ITEM; import static com.google.android.exoplayer2.Player.COMMAND_SEEK_TO_NEXT_MEDIA_ITEM; import static com.google.android.exoplayer2.Player.COMMAND_SEEK_TO_PREVIOUS_MEDIA_ITEM; +import static com.google.android.exoplayer2.Player.COMMAND_SET_DEVICE_VOLUME; import static com.google.android.exoplayer2.Player.COMMAND_SET_REPEAT_MODE; import static com.google.android.exoplayer2.Player.COMMAND_SET_SHUFFLE_MODE; import static com.google.android.exoplayer2.Player.COMMAND_SET_SPEED_AND_PITCH; +import static com.google.android.exoplayer2.Player.COMMAND_SET_VIDEO_SURFACE; +import static com.google.android.exoplayer2.Player.COMMAND_SET_VOLUME; import static com.google.common.truth.Truth.assertThat; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyInt; @@ -605,6 +612,13 @@ public class CastPlayerTest { assertThat(castPlayer.isCommandAvailable(COMMAND_GET_MEDIA_ITEMS)).isTrue(); assertThat(castPlayer.isCommandAvailable(COMMAND_GET_MEDIA_ITEMS_METADATA)).isTrue(); assertThat(castPlayer.isCommandAvailable(COMMAND_CHANGE_MEDIA_ITEMS)).isTrue(); + assertThat(castPlayer.isCommandAvailable(COMMAND_GET_VOLUME)).isFalse(); + assertThat(castPlayer.isCommandAvailable(COMMAND_GET_DEVICE_VOLUME)).isFalse(); + assertThat(castPlayer.isCommandAvailable(COMMAND_SET_VOLUME)).isFalse(); + assertThat(castPlayer.isCommandAvailable(COMMAND_SET_DEVICE_VOLUME)).isFalse(); + assertThat(castPlayer.isCommandAvailable(COMMAND_ADJUST_DEVICE_VOLUME)).isFalse(); + assertThat(castPlayer.isCommandAvailable(COMMAND_SET_VIDEO_SURFACE)).isFalse(); + assertThat(castPlayer.isCommandAvailable(COMMAND_GET_TEXT)).isFalse(); } @Test @@ -626,11 +640,13 @@ public class CastPlayerTest { when(mockRemoteMediaClient.queueJumpToItem(anyInt(), anyLong(), eq(null))) .thenReturn(mockPendingResult); Player.Commands commandsWithSeekInCurrentAndToNext = - createCommands(COMMAND_SEEK_IN_CURRENT_MEDIA_ITEM, COMMAND_SEEK_TO_NEXT_MEDIA_ITEM); + createWithPermanentCommands( + COMMAND_SEEK_IN_CURRENT_MEDIA_ITEM, COMMAND_SEEK_TO_NEXT_MEDIA_ITEM); Player.Commands commandsWithSeekInCurrentAndToPrevious = - createCommands(COMMAND_SEEK_IN_CURRENT_MEDIA_ITEM, COMMAND_SEEK_TO_PREVIOUS_MEDIA_ITEM); + createWithPermanentCommands( + COMMAND_SEEK_IN_CURRENT_MEDIA_ITEM, COMMAND_SEEK_TO_PREVIOUS_MEDIA_ITEM); Player.Commands commandsWithSeekAnywhere = - createCommands( + createWithPermanentCommands( COMMAND_SEEK_IN_CURRENT_MEDIA_ITEM, COMMAND_SEEK_TO_NEXT_MEDIA_ITEM, COMMAND_SEEK_TO_PREVIOUS_MEDIA_ITEM); @@ -660,11 +676,13 @@ public class CastPlayerTest { when(mockRemoteMediaClient.queueJumpToItem(anyInt(), anyLong(), eq(null))) .thenReturn(mockPendingResult); Player.Commands commandsWithSeekInCurrentAndToNext = - createCommands(COMMAND_SEEK_IN_CURRENT_MEDIA_ITEM, COMMAND_SEEK_TO_NEXT_MEDIA_ITEM); + createWithPermanentCommands( + COMMAND_SEEK_IN_CURRENT_MEDIA_ITEM, COMMAND_SEEK_TO_NEXT_MEDIA_ITEM); Player.Commands commandsWithSeekInCurrentAndToPrevious = - createCommands(COMMAND_SEEK_IN_CURRENT_MEDIA_ITEM, COMMAND_SEEK_TO_PREVIOUS_MEDIA_ITEM); + createWithPermanentCommands( + COMMAND_SEEK_IN_CURRENT_MEDIA_ITEM, COMMAND_SEEK_TO_PREVIOUS_MEDIA_ITEM); Player.Commands commandsWithSeekAnywhere = - createCommands( + createWithPermanentCommands( COMMAND_SEEK_IN_CURRENT_MEDIA_ITEM, COMMAND_SEEK_TO_NEXT_MEDIA_ITEM, COMMAND_SEEK_TO_PREVIOUS_MEDIA_ITEM); @@ -693,7 +711,8 @@ public class CastPlayerTest { @SuppressWarnings("deprecation") // Mocks deprecated method used by the CastPlayer. public void seekTo_sameWindow_doesNotNotifyAvailableCommandsChanged() { when(mockRemoteMediaClient.seek(anyLong())).thenReturn(mockPendingResult); - Player.Commands commandsWithSeekInCurrent = createCommands(COMMAND_SEEK_IN_CURRENT_MEDIA_ITEM); + Player.Commands commandsWithSeekInCurrent = + createWithPermanentCommands(COMMAND_SEEK_IN_CURRENT_MEDIA_ITEM); int[] mediaQueueItemIds = new int[] {1}; List mediaItems = createMediaItems(mediaQueueItemIds); @@ -709,9 +728,11 @@ public class CastPlayerTest { @Test public void addMediaItem_atTheEnd_notifiesAvailableCommandsChanged() { - Player.Commands commandsWithSeekInCurrent = createCommands(COMMAND_SEEK_IN_CURRENT_MEDIA_ITEM); + Player.Commands commandsWithSeekInCurrent = + createWithPermanentCommands(COMMAND_SEEK_IN_CURRENT_MEDIA_ITEM); Player.Commands commandsWithSeekInCurrentAndToNext = - createCommands(COMMAND_SEEK_IN_CURRENT_MEDIA_ITEM, COMMAND_SEEK_TO_NEXT_MEDIA_ITEM); + createWithPermanentCommands( + COMMAND_SEEK_IN_CURRENT_MEDIA_ITEM, COMMAND_SEEK_TO_NEXT_MEDIA_ITEM); MediaItem mediaItem1 = createMediaItem(/* mediaQueueItemId= */ 1); MediaItem mediaItem2 = createMediaItem(/* mediaQueueItemId= */ 2); MediaItem mediaItem3 = createMediaItem(/* mediaQueueItemId= */ 3); @@ -743,9 +764,11 @@ public class CastPlayerTest { @Test public void addMediaItem_atTheStart_notifiesAvailableCommandsChanged() { - Player.Commands commandsWithSeekInCurrent = createCommands(COMMAND_SEEK_IN_CURRENT_MEDIA_ITEM); + Player.Commands commandsWithSeekInCurrent = + createWithPermanentCommands(COMMAND_SEEK_IN_CURRENT_MEDIA_ITEM); Player.Commands commandsWithSeekInCurrentAndToPrevious = - createCommands(COMMAND_SEEK_IN_CURRENT_MEDIA_ITEM, COMMAND_SEEK_TO_PREVIOUS_MEDIA_ITEM); + createWithPermanentCommands( + COMMAND_SEEK_IN_CURRENT_MEDIA_ITEM, COMMAND_SEEK_TO_PREVIOUS_MEDIA_ITEM); MediaItem mediaItem1 = createMediaItem(/* mediaQueueItemId= */ 1); MediaItem mediaItem2 = createMediaItem(/* mediaQueueItemId= */ 2); MediaItem mediaItem3 = createMediaItem(/* mediaQueueItemId= */ 3); @@ -777,10 +800,12 @@ public class CastPlayerTest { @Test public void removeMediaItem_atTheEnd_notifiesAvailableCommandsChanged() { - Player.Commands commandsWithoutSeek = createCommands(); - Player.Commands commandsWithSeekInCurrent = createCommands(COMMAND_SEEK_IN_CURRENT_MEDIA_ITEM); + Player.Commands commandsWithoutSeek = createWithPermanentCommands(); + Player.Commands commandsWithSeekInCurrent = + createWithPermanentCommands(COMMAND_SEEK_IN_CURRENT_MEDIA_ITEM); Player.Commands commandsWithSeekInCurrentAndToNext = - createCommands(COMMAND_SEEK_IN_CURRENT_MEDIA_ITEM, COMMAND_SEEK_TO_NEXT_MEDIA_ITEM); + createWithPermanentCommands( + COMMAND_SEEK_IN_CURRENT_MEDIA_ITEM, COMMAND_SEEK_TO_NEXT_MEDIA_ITEM); MediaItem mediaItem1 = createMediaItem(/* mediaQueueItemId= */ 1); MediaItem mediaItem2 = createMediaItem(/* mediaQueueItemId= */ 2); MediaItem mediaItem3 = createMediaItem(/* mediaQueueItemId= */ 3); @@ -822,10 +847,12 @@ public class CastPlayerTest { public void removeMediaItem_atTheStart_notifiesAvailableCommandsChanged() { when(mockRemoteMediaClient.queueJumpToItem(anyInt(), anyLong(), eq(null))) .thenReturn(mockPendingResult); - Player.Commands commandsWithoutSeek = createCommands(); - Player.Commands commandsWithSeekInCurrent = createCommands(COMMAND_SEEK_IN_CURRENT_MEDIA_ITEM); + Player.Commands commandsWithoutSeek = createWithPermanentCommands(); + Player.Commands commandsWithSeekInCurrent = + createWithPermanentCommands(COMMAND_SEEK_IN_CURRENT_MEDIA_ITEM); Player.Commands commandsWithSeekInCurrentAndToPrevious = - createCommands(COMMAND_SEEK_IN_CURRENT_MEDIA_ITEM, COMMAND_SEEK_TO_PREVIOUS_MEDIA_ITEM); + createWithPermanentCommands( + COMMAND_SEEK_IN_CURRENT_MEDIA_ITEM, COMMAND_SEEK_TO_PREVIOUS_MEDIA_ITEM); MediaItem mediaItem1 = createMediaItem(/* mediaQueueItemId= */ 1); MediaItem mediaItem2 = createMediaItem(/* mediaQueueItemId= */ 2); MediaItem mediaItem3 = createMediaItem(/* mediaQueueItemId= */ 3); @@ -865,9 +892,11 @@ public class CastPlayerTest { @Test public void removeMediaItem_current_notifiesAvailableCommandsChanged() { - Player.Commands commandsWithSeekInCurrent = createCommands(COMMAND_SEEK_IN_CURRENT_MEDIA_ITEM); + Player.Commands commandsWithSeekInCurrent = + createWithPermanentCommands(COMMAND_SEEK_IN_CURRENT_MEDIA_ITEM); Player.Commands commandsWithSeekInCurrentAndToNext = - createCommands(COMMAND_SEEK_IN_CURRENT_MEDIA_ITEM, COMMAND_SEEK_TO_NEXT_MEDIA_ITEM); + createWithPermanentCommands( + COMMAND_SEEK_IN_CURRENT_MEDIA_ITEM, COMMAND_SEEK_TO_NEXT_MEDIA_ITEM); MediaItem mediaItem1 = createMediaItem(/* mediaQueueItemId= */ 1); MediaItem mediaItem2 = createMediaItem(/* mediaQueueItemId= */ 2); @@ -893,9 +922,10 @@ public class CastPlayerTest { public void setRepeatMode_all_notifiesAvailableCommandsChanged() { when(mockRemoteMediaClient.queueSetRepeatMode(anyInt(), eq(null))) .thenReturn(mockPendingResult); - Player.Commands commandsWithSeekInCurrent = createCommands(COMMAND_SEEK_IN_CURRENT_MEDIA_ITEM); + Player.Commands commandsWithSeekInCurrent = + createWithPermanentCommands(COMMAND_SEEK_IN_CURRENT_MEDIA_ITEM); Player.Commands commandsWithSeekAnywhere = - createCommands( + createWithPermanentCommands( COMMAND_SEEK_IN_CURRENT_MEDIA_ITEM, COMMAND_SEEK_TO_NEXT_MEDIA_ITEM, COMMAND_SEEK_TO_PREVIOUS_MEDIA_ITEM); @@ -917,7 +947,8 @@ public class CastPlayerTest { public void setRepeatMode_one_doesNotNotifyAvailableCommandsChanged() { when(mockRemoteMediaClient.queueSetRepeatMode(anyInt(), eq(null))) .thenReturn(mockPendingResult); - Player.Commands commandsWithSeekInCurrent = createCommands(COMMAND_SEEK_IN_CURRENT_MEDIA_ITEM); + Player.Commands commandsWithSeekInCurrent = + createWithPermanentCommands(COMMAND_SEEK_IN_CURRENT_MEDIA_ITEM); int[] mediaQueueItemIds = new int[] {1}; List mediaItems = createMediaItems(mediaQueueItemIds); @@ -1008,12 +1039,11 @@ public class CastPlayerTest { remoteMediaClientCallback.onQueueStatusUpdated(); } - private static Player.Commands createCommands(@Player.Command int... commands) { + private static Player.Commands createWithPermanentCommands( + @Player.Command int... additionalCommands) { Player.Commands.Builder builder = new Player.Commands.Builder(); builder.addAll(CastPlayer.PERMANENT_AVAILABLE_COMMANDS); - for (int command : commands) { - builder.add(command); - } + builder.addAll(additionalCommands); return builder.build(); } } diff --git a/library/common/src/main/java/com/google/android/exoplayer2/BasePlayer.java b/library/common/src/main/java/com/google/android/exoplayer2/BasePlayer.java index c14138c064..ac99cc96ab 100644 --- a/library/common/src/main/java/com/google/android/exoplayer2/BasePlayer.java +++ b/library/common/src/main/java/com/google/android/exoplayer2/BasePlayer.java @@ -328,7 +328,7 @@ public abstract class BasePlayer implements Player { return repeatMode == REPEAT_MODE_ONE ? REPEAT_MODE_OFF : repeatMode; } - protected Commands getAvailableCommands(@Command int[] permanentAvailableCommands) { + protected Commands getAvailableCommands(Commands permanentAvailableCommands) { return new Commands.Builder() .addAll(permanentAvailableCommands) .addIf(COMMAND_SEEK_IN_CURRENT_MEDIA_ITEM, isCurrentWindowSeekable() && !isPlayingAd()) diff --git a/library/common/src/main/java/com/google/android/exoplayer2/Player.java b/library/common/src/main/java/com/google/android/exoplayer2/Player.java index f668e4df76..d23814cfb0 100644 --- a/library/common/src/main/java/com/google/android/exoplayer2/Player.java +++ b/library/common/src/main/java/com/google/android/exoplayer2/Player.java @@ -751,6 +751,18 @@ public interface Player { return this; } + /** + * Adds {@link Commands}. + * + * @param commands The set of {@link Command commands} to add. + * @return This builder. + * @throws IllegalStateException If {@link #build()} has already been called. + */ + public Builder addAll(Commands commands) { + flagsBuilder.addAll(commands.flags); + return this; + } + /** * Builds a {@link Commands} instance. * @@ -761,6 +773,9 @@ public interface Player { } } + /** An empty set of commands. */ + public static final Commands EMPTY = new Builder().build(); + private final ExoFlags flags; private Commands(ExoFlags flags) { @@ -1034,7 +1049,10 @@ public interface Player { * #COMMAND_SEEK_TO_MEDIA_ITEM}, {@link #COMMAND_SET_SPEED_AND_PITCH}, {@link * #COMMAND_SET_SHUFFLE_MODE}, {@link #COMMAND_SET_REPEAT_MODE}, {@link * #COMMAND_GET_CURRENT_MEDIA_ITEM}, {@link #COMMAND_GET_MEDIA_ITEMS}, {@link - * #COMMAND_GET_MEDIA_ITEMS_METADATA} or {@link #COMMAND_CHANGE_MEDIA_ITEMS}. + * #COMMAND_GET_MEDIA_ITEMS_METADATA}, {@link #COMMAND_CHANGE_MEDIA_ITEMS}, {@link + * #COMMAND_GET_VOLUME}, {@link #COMMAND_GET_DEVICE_VOLUME}, {@link #COMMAND_SET_VOLUME}, {@link + * #COMMAND_SET_DEVICE_VOLUME}, {@link #COMMAND_ADJUST_DEVICE_VOLUME}, {@link + * #COMMAND_SET_VIDEO_SURFACE} or {@link #COMMAND_GET_TEXT}. */ @Documented @Retention(RetentionPolicy.SOURCE) @@ -1051,7 +1069,14 @@ public interface Player { COMMAND_GET_CURRENT_MEDIA_ITEM, COMMAND_GET_MEDIA_ITEMS, COMMAND_GET_MEDIA_ITEMS_METADATA, - COMMAND_CHANGE_MEDIA_ITEMS + COMMAND_CHANGE_MEDIA_ITEMS, + COMMAND_GET_VOLUME, + COMMAND_GET_DEVICE_VOLUME, + COMMAND_SET_VOLUME, + COMMAND_SET_DEVICE_VOLUME, + COMMAND_ADJUST_DEVICE_VOLUME, + COMMAND_SET_VIDEO_SURFACE, + COMMAND_GET_TEXT }) @interface Command {} /** Command to start, pause or resume playback. */ @@ -1080,6 +1105,20 @@ public interface Player { int COMMAND_GET_MEDIA_ITEMS_METADATA = 12; /** Command to change the {@link MediaItem MediaItems} in the playlist. */ int COMMAND_CHANGE_MEDIA_ITEMS = 13; + /** Command to get the player volume. */ + int COMMAND_GET_VOLUME = 14; + /** Command to get the device volume. */ + int COMMAND_GET_DEVICE_VOLUME = 15; + /** Command to set the player volume. */ + int COMMAND_SET_VOLUME = 16; + /** Command to set the device volume. */ + int COMMAND_SET_DEVICE_VOLUME = 17; + /** Command to increment or decrement the device volume. */ + int COMMAND_ADJUST_DEVICE_VOLUME = 18; + /** Command to set the surface on which to render the video. */ + int COMMAND_SET_VIDEO_SURFACE = 19; + /** Command to get the text that should currently be displayed by the player. */ + int COMMAND_GET_TEXT = 20; /** Returns the component of this player for audio output, or null if audio is not supported. */ @Nullable diff --git a/library/common/src/main/java/com/google/android/exoplayer2/util/ExoFlags.java b/library/common/src/main/java/com/google/android/exoplayer2/util/ExoFlags.java index 34faa856aa..3829515bc4 100644 --- a/library/common/src/main/java/com/google/android/exoplayer2/util/ExoFlags.java +++ b/library/common/src/main/java/com/google/android/exoplayer2/util/ExoFlags.java @@ -84,6 +84,20 @@ public final class ExoFlags { return this; } + /** + * Adds {@link ExoFlags flags}. + * + * @param flags The set of flags to add. + * @return This builder. + * @throws IllegalStateException If {@link #build()} has already been called. + */ + public Builder addAll(ExoFlags flags) { + for (int i = 0; i < flags.size(); i++) { + add(flags.get(i)); + } + return this; + } + /** * Builds an {@link ExoFlags} instance. * diff --git a/library/core/src/main/java/com/google/android/exoplayer2/ExoPlayer.java b/library/core/src/main/java/com/google/android/exoplayer2/ExoPlayer.java index f02ba4d6ff..40d0645e2f 100644 --- a/library/core/src/main/java/com/google/android/exoplayer2/ExoPlayer.java +++ b/library/core/src/main/java/com/google/android/exoplayer2/ExoPlayer.java @@ -463,7 +463,8 @@ public interface ExoPlayer extends Player { pauseAtEndOfMediaItems, clock, looper, - /* wrappingPlayer= */ null); + /* wrappingPlayer= */ null, + /* additionalPermanentAvailableCommands= */ Commands.EMPTY); if (setForegroundModeTimeoutMs > 0) { player.experimentalSetForegroundModeTimeoutMs(setForegroundModeTimeoutMs); diff --git a/library/core/src/main/java/com/google/android/exoplayer2/ExoPlayerFactory.java b/library/core/src/main/java/com/google/android/exoplayer2/ExoPlayerFactory.java index 40c3473abd..49de1eb290 100644 --- a/library/core/src/main/java/com/google/android/exoplayer2/ExoPlayerFactory.java +++ b/library/core/src/main/java/com/google/android/exoplayer2/ExoPlayerFactory.java @@ -261,6 +261,7 @@ public final class ExoPlayerFactory { /* pauseAtEndOfMediaItems= */ false, Clock.DEFAULT, applicationLooper, - /* wrappingPlayer= */ null); + /* wrappingPlayer= */ null, + /* additionalPermanentAvailableCommands= */ Player.Commands.EMPTY); } } diff --git a/library/core/src/main/java/com/google/android/exoplayer2/ExoPlayerImpl.java b/library/core/src/main/java/com/google/android/exoplayer2/ExoPlayerImpl.java index c81c2c71f4..568fa1c400 100644 --- a/library/core/src/main/java/com/google/android/exoplayer2/ExoPlayerImpl.java +++ b/library/core/src/main/java/com/google/android/exoplayer2/ExoPlayerImpl.java @@ -26,7 +26,6 @@ import android.os.Handler; import android.os.Looper; import android.util.Pair; import androidx.annotation.Nullable; -import androidx.annotation.VisibleForTesting; import com.google.android.exoplayer2.PlayerMessage.Target; import com.google.android.exoplayer2.analytics.AnalyticsCollector; import com.google.android.exoplayer2.metadata.Metadata; @@ -57,20 +56,6 @@ import java.util.concurrent.CopyOnWriteArraySet; */ /* package */ final class ExoPlayerImpl extends BasePlayer implements ExoPlayer { - @VisibleForTesting - /* package */ static final int[] PERMANENT_AVAILABLE_COMMANDS = - new int[] { - COMMAND_PLAY_PAUSE, - COMMAND_PREPARE_STOP_RELEASE, - COMMAND_SET_SPEED_AND_PITCH, - COMMAND_SET_SHUFFLE_MODE, - COMMAND_SET_REPEAT_MODE, - COMMAND_GET_CURRENT_MEDIA_ITEM, - COMMAND_GET_MEDIA_ITEMS, - COMMAND_GET_MEDIA_ITEMS_METADATA, - COMMAND_CHANGE_MEDIA_ITEMS - }; - private static final String TAG = "ExoPlayerImpl"; /** @@ -81,6 +66,7 @@ import java.util.concurrent.CopyOnWriteArraySet; * operation. */ /* package */ final TrackSelectorResult emptyTrackSelectorResult; + /* package */ final Commands permanentAvailableCommands; private final Renderer[] renderers; private final TrackSelector trackSelector; @@ -139,6 +125,8 @@ import java.util.concurrent.CopyOnWriteArraySet; * which is used to call listeners on. * @param wrappingPlayer The {@link Player} wrapping this one if applicable. This player instance * should be used for all externally visible callbacks. + * @param additionalPermanentAvailableCommands The {@link Commands} that are permanently available + * in the wrapping player but that are not in this player. */ @SuppressLint("HandlerLeak") public ExoPlayerImpl( @@ -155,7 +143,8 @@ import java.util.concurrent.CopyOnWriteArraySet; boolean pauseAtEndOfMediaItems, Clock clock, Looper applicationLooper, - @Nullable Player wrappingPlayer) { + @Nullable Player wrappingPlayer, + Commands additionalPermanentAvailableCommands) { Log.i( TAG, "Init " @@ -192,9 +181,23 @@ import java.util.concurrent.CopyOnWriteArraySet; new ExoTrackSelection[renderers.length], /* info= */ null); period = new Timeline.Period(); + permanentAvailableCommands = + new Commands.Builder() + .addAll( + COMMAND_PLAY_PAUSE, + COMMAND_PREPARE_STOP_RELEASE, + COMMAND_SET_SPEED_AND_PITCH, + COMMAND_SET_SHUFFLE_MODE, + COMMAND_SET_REPEAT_MODE, + COMMAND_GET_CURRENT_MEDIA_ITEM, + COMMAND_GET_MEDIA_ITEMS, + COMMAND_GET_MEDIA_ITEMS_METADATA, + COMMAND_CHANGE_MEDIA_ITEMS) + .addAll(additionalPermanentAvailableCommands) + .build(); availableCommands = new Commands.Builder() - .addAll(PERMANENT_AVAILABLE_COMMANDS) + .addAll(permanentAvailableCommands) .add(COMMAND_SEEK_TO_MEDIA_ITEM) .build(); maskingWindowIndex = C.INDEX_UNSET; @@ -1188,7 +1191,7 @@ import java.util.concurrent.CopyOnWriteArraySet; private void updateAvailableCommands() { Commands previousAvailableCommands = availableCommands; - availableCommands = getAvailableCommands(PERMANENT_AVAILABLE_COMMANDS); + availableCommands = getAvailableCommands(permanentAvailableCommands); if (!availableCommands.equals(previousAvailableCommands)) { listeners.queueEvent( Player.EVENT_AVAILABLE_COMMANDS_CHANGED, diff --git a/library/core/src/main/java/com/google/android/exoplayer2/SimpleExoPlayer.java b/library/core/src/main/java/com/google/android/exoplayer2/SimpleExoPlayer.java index 9a55918922..316c6d573e 100644 --- a/library/core/src/main/java/com/google/android/exoplayer2/SimpleExoPlayer.java +++ b/library/core/src/main/java/com/google/android/exoplayer2/SimpleExoPlayer.java @@ -668,6 +668,17 @@ public class SimpleExoPlayer extends BasePlayer throwsWhenUsingWrongThread = true; // Build the player and associated objects. + Commands additionalPermanentAvailableCommands = + new Commands.Builder() + .addAll( + COMMAND_GET_VOLUME, + COMMAND_GET_DEVICE_VOLUME, + COMMAND_SET_VOLUME, + COMMAND_SET_DEVICE_VOLUME, + COMMAND_ADJUST_DEVICE_VOLUME, + COMMAND_SET_VIDEO_SURFACE, + COMMAND_GET_TEXT) + .build(); player = new ExoPlayerImpl( renderers, @@ -683,7 +694,8 @@ public class SimpleExoPlayer extends BasePlayer builder.pauseAtEndOfMediaItems, builder.clock, builder.looper, - /* wrappingPlayer= */ this); + /* wrappingPlayer= */ this, + additionalPermanentAvailableCommands); player.addListener(componentListener); player.addAudioOffloadListener(componentListener); diff --git a/library/core/src/test/java/com/google/android/exoplayer2/ExoPlayerTest.java b/library/core/src/test/java/com/google/android/exoplayer2/ExoPlayerTest.java index 23c83b817d..0f849ec25c 100644 --- a/library/core/src/test/java/com/google/android/exoplayer2/ExoPlayerTest.java +++ b/library/core/src/test/java/com/google/android/exoplayer2/ExoPlayerTest.java @@ -15,19 +15,26 @@ */ package com.google.android.exoplayer2; +import static com.google.android.exoplayer2.Player.COMMAND_ADJUST_DEVICE_VOLUME; import static com.google.android.exoplayer2.Player.COMMAND_CHANGE_MEDIA_ITEMS; import static com.google.android.exoplayer2.Player.COMMAND_GET_CURRENT_MEDIA_ITEM; +import static com.google.android.exoplayer2.Player.COMMAND_GET_DEVICE_VOLUME; import static com.google.android.exoplayer2.Player.COMMAND_GET_MEDIA_ITEMS; import static com.google.android.exoplayer2.Player.COMMAND_GET_MEDIA_ITEMS_METADATA; +import static com.google.android.exoplayer2.Player.COMMAND_GET_TEXT; +import static com.google.android.exoplayer2.Player.COMMAND_GET_VOLUME; import static com.google.android.exoplayer2.Player.COMMAND_PLAY_PAUSE; import static com.google.android.exoplayer2.Player.COMMAND_PREPARE_STOP_RELEASE; import static com.google.android.exoplayer2.Player.COMMAND_SEEK_IN_CURRENT_MEDIA_ITEM; import static com.google.android.exoplayer2.Player.COMMAND_SEEK_TO_MEDIA_ITEM; import static com.google.android.exoplayer2.Player.COMMAND_SEEK_TO_NEXT_MEDIA_ITEM; import static com.google.android.exoplayer2.Player.COMMAND_SEEK_TO_PREVIOUS_MEDIA_ITEM; +import static com.google.android.exoplayer2.Player.COMMAND_SET_DEVICE_VOLUME; import static com.google.android.exoplayer2.Player.COMMAND_SET_REPEAT_MODE; import static com.google.android.exoplayer2.Player.COMMAND_SET_SHUFFLE_MODE; import static com.google.android.exoplayer2.Player.COMMAND_SET_SPEED_AND_PITCH; +import static com.google.android.exoplayer2.Player.COMMAND_SET_VIDEO_SURFACE; +import static com.google.android.exoplayer2.Player.COMMAND_SET_VOLUME; import static com.google.android.exoplayer2.robolectric.RobolectricUtil.runMainLooperUntil; import static com.google.android.exoplayer2.robolectric.TestPlayerRunHelper.playUntilStartOfWindow; import static com.google.android.exoplayer2.robolectric.TestPlayerRunHelper.runUntilPendingCommandsAreFullyHandled; @@ -8065,6 +8072,13 @@ public final class ExoPlayerTest { assertThat(player.isCommandAvailable(COMMAND_GET_MEDIA_ITEMS)).isTrue(); assertThat(player.isCommandAvailable(COMMAND_GET_MEDIA_ITEMS_METADATA)).isTrue(); assertThat(player.isCommandAvailable(COMMAND_CHANGE_MEDIA_ITEMS)).isTrue(); + assertThat(player.isCommandAvailable(COMMAND_GET_VOLUME)).isTrue(); + assertThat(player.isCommandAvailable(COMMAND_GET_DEVICE_VOLUME)).isTrue(); + assertThat(player.isCommandAvailable(COMMAND_SET_VOLUME)).isTrue(); + assertThat(player.isCommandAvailable(COMMAND_SET_DEVICE_VOLUME)).isTrue(); + assertThat(player.isCommandAvailable(COMMAND_ADJUST_DEVICE_VOLUME)).isTrue(); + assertThat(player.isCommandAvailable(COMMAND_SET_VIDEO_SURFACE)).isTrue(); + assertThat(player.isCommandAvailable(COMMAND_GET_TEXT)).isTrue(); } @Test @@ -8117,11 +8131,13 @@ public final class ExoPlayerTest { @Test public void seekTo_nextWindow_notifiesAvailableCommandsChanged() { - Player.Commands commandsWithSeekToNext = createCommands(COMMAND_SEEK_TO_NEXT_MEDIA_ITEM); + Player.Commands commandsWithSeekToNext = + createWithPermanentCommands(COMMAND_SEEK_TO_NEXT_MEDIA_ITEM); Player.Commands commandsWithSeekToPrevious = - createCommands(COMMAND_SEEK_TO_PREVIOUS_MEDIA_ITEM); + createWithPermanentCommands(COMMAND_SEEK_TO_PREVIOUS_MEDIA_ITEM); Player.Commands commandsWithSeekToNextAndPrevious = - createCommands(COMMAND_SEEK_TO_NEXT_MEDIA_ITEM, COMMAND_SEEK_TO_PREVIOUS_MEDIA_ITEM); + createWithPermanentCommands( + COMMAND_SEEK_TO_NEXT_MEDIA_ITEM, COMMAND_SEEK_TO_PREVIOUS_MEDIA_ITEM); Player.EventListener mockListener = mock(Player.EventListener.class); ExoPlayer player = new TestExoPlayerBuilder(context).build(); player.addListener(mockListener); @@ -8150,11 +8166,13 @@ public final class ExoPlayerTest { @Test public void seekTo_previousWindow_notifiesAvailableCommandsChanged() { - Player.Commands commandsWithSeekToNext = createCommands(COMMAND_SEEK_TO_NEXT_MEDIA_ITEM); + Player.Commands commandsWithSeekToNext = + createWithPermanentCommands(COMMAND_SEEK_TO_NEXT_MEDIA_ITEM); Player.Commands commandsWithSeekToPrevious = - createCommands(COMMAND_SEEK_TO_PREVIOUS_MEDIA_ITEM); + createWithPermanentCommands(COMMAND_SEEK_TO_PREVIOUS_MEDIA_ITEM); Player.Commands commandsWithSeekToNextAndPrevious = - createCommands(COMMAND_SEEK_TO_NEXT_MEDIA_ITEM, COMMAND_SEEK_TO_PREVIOUS_MEDIA_ITEM); + createWithPermanentCommands( + COMMAND_SEEK_TO_NEXT_MEDIA_ITEM, COMMAND_SEEK_TO_PREVIOUS_MEDIA_ITEM); Player.EventListener mockListener = mock(Player.EventListener.class); ExoPlayer player = new TestExoPlayerBuilder(context).build(); player.addListener(mockListener); @@ -8196,13 +8214,16 @@ public final class ExoPlayerTest { @Test public void automaticWindowTransition_notifiesAvailableCommandsChanged() throws Exception { - Player.Commands commandsWithSeekToNext = createCommands(COMMAND_SEEK_TO_NEXT_MEDIA_ITEM); + Player.Commands commandsWithSeekToNext = + createWithPermanentCommands(COMMAND_SEEK_TO_NEXT_MEDIA_ITEM); Player.Commands commandsWithSeekInCurrentAndToNext = - createCommands(COMMAND_SEEK_IN_CURRENT_MEDIA_ITEM, COMMAND_SEEK_TO_NEXT_MEDIA_ITEM); + createWithPermanentCommands( + COMMAND_SEEK_IN_CURRENT_MEDIA_ITEM, COMMAND_SEEK_TO_NEXT_MEDIA_ITEM); Player.Commands commandsWithSeekInCurrentAndToPrevious = - createCommands(COMMAND_SEEK_IN_CURRENT_MEDIA_ITEM, COMMAND_SEEK_TO_PREVIOUS_MEDIA_ITEM); + createWithPermanentCommands( + COMMAND_SEEK_IN_CURRENT_MEDIA_ITEM, COMMAND_SEEK_TO_PREVIOUS_MEDIA_ITEM); Player.Commands commandsWithSeekAnywhere = - createCommands( + createWithPermanentCommands( COMMAND_SEEK_IN_CURRENT_MEDIA_ITEM, COMMAND_SEEK_TO_NEXT_MEDIA_ITEM, COMMAND_SEEK_TO_PREVIOUS_MEDIA_ITEM); @@ -8242,7 +8263,8 @@ public final class ExoPlayerTest { @Test public void addMediaSource_atTheEnd_notifiesAvailableCommandsChanged() { - Player.Commands commandsWithSeekToNext = createCommands(COMMAND_SEEK_TO_NEXT_MEDIA_ITEM); + Player.Commands commandsWithSeekToNext = + createWithPermanentCommands(COMMAND_SEEK_TO_NEXT_MEDIA_ITEM); Player.EventListener mockListener = mock(Player.EventListener.class); ExoPlayer player = new TestExoPlayerBuilder(context).build(); player.addListener(mockListener); @@ -8262,7 +8284,7 @@ public final class ExoPlayerTest { @Test public void addMediaSource_atTheStart_notifiesAvailableCommandsChanged() { Player.Commands commandsWithSeekToPrevious = - createCommands(COMMAND_SEEK_TO_PREVIOUS_MEDIA_ITEM); + createWithPermanentCommands(COMMAND_SEEK_TO_PREVIOUS_MEDIA_ITEM); Player.EventListener mockListener = mock(Player.EventListener.class); ExoPlayer player = new TestExoPlayerBuilder(context).build(); player.addListener(mockListener); @@ -8281,8 +8303,9 @@ public final class ExoPlayerTest { @Test public void removeMediaItem_atTheEnd_notifiesAvailableCommandsChanged() { - Player.Commands commandsWithSeekToNext = createCommands(COMMAND_SEEK_TO_NEXT_MEDIA_ITEM); - Player.Commands commandsWithoutSeek = createCommands(); + Player.Commands commandsWithSeekToNext = + createWithPermanentCommands(COMMAND_SEEK_TO_NEXT_MEDIA_ITEM); + Player.Commands commandsWithoutSeek = createWithPermanentCommands(); Player.EventListener mockListener = mock(Player.EventListener.class); ExoPlayer player = new TestExoPlayerBuilder(context).build(); player.addListener(mockListener); @@ -8307,8 +8330,8 @@ public final class ExoPlayerTest { @Test public void removeMediaItem_atTheStart_notifiesAvailableCommandsChanged() { Player.Commands commandsWithSeekToPrevious = - createCommands(COMMAND_SEEK_TO_PREVIOUS_MEDIA_ITEM); - Player.Commands commandsWithoutSeek = createCommands(); + createWithPermanentCommands(COMMAND_SEEK_TO_PREVIOUS_MEDIA_ITEM); + Player.Commands commandsWithoutSeek = createWithPermanentCommands(); Player.EventListener mockListener = mock(Player.EventListener.class); ExoPlayer player = new TestExoPlayerBuilder(context).build(); player.addListener(mockListener); @@ -8333,8 +8356,9 @@ public final class ExoPlayerTest { @Test public void removeMediaItem_current_notifiesAvailableCommandsChanged() { - Player.Commands commandsWithSeekToNext = createCommands(COMMAND_SEEK_TO_NEXT_MEDIA_ITEM); - Player.Commands commandsWithoutSeek = createCommands(); + Player.Commands commandsWithSeekToNext = + createWithPermanentCommands(COMMAND_SEEK_TO_NEXT_MEDIA_ITEM); + Player.Commands commandsWithoutSeek = createWithPermanentCommands(); Player.EventListener mockListener = mock(Player.EventListener.class); ExoPlayer player = new TestExoPlayerBuilder(context).build(); player.addListener(mockListener); @@ -8352,7 +8376,8 @@ public final class ExoPlayerTest { @Test public void setRepeatMode_all_notifiesAvailableCommandsChanged() { Player.Commands commandsWithSeekToNextAndPrevious = - createCommands(COMMAND_SEEK_TO_NEXT_MEDIA_ITEM, COMMAND_SEEK_TO_PREVIOUS_MEDIA_ITEM); + createWithPermanentCommands( + COMMAND_SEEK_TO_NEXT_MEDIA_ITEM, COMMAND_SEEK_TO_PREVIOUS_MEDIA_ITEM); Player.EventListener mockListener = mock(Player.EventListener.class); ExoPlayer player = new TestExoPlayerBuilder(context).build(); player.addListener(mockListener); @@ -8379,9 +8404,10 @@ public final class ExoPlayerTest { @Test public void setShuffleModeEnabled_notifiesAvailableCommandsChanged() { - Player.Commands commandsWithSeekToNext = createCommands(COMMAND_SEEK_TO_NEXT_MEDIA_ITEM); + Player.Commands commandsWithSeekToNext = + createWithPermanentCommands(COMMAND_SEEK_TO_NEXT_MEDIA_ITEM); Player.Commands commandsWithSeekToPrevious = - createCommands(COMMAND_SEEK_TO_PREVIOUS_MEDIA_ITEM); + createWithPermanentCommands(COMMAND_SEEK_TO_PREVIOUS_MEDIA_ITEM); Player.EventListener mockListener = mock(Player.EventListener.class); ExoPlayer player = new TestExoPlayerBuilder(context).build(); player.addListener(mockListener); @@ -9351,13 +9377,28 @@ public final class ExoPlayerTest { return false; } - private static Player.Commands createCommands(@Player.Command int... commands) { + private static Player.Commands createWithPermanentCommands( + @Player.Command int... additionalCommands) { Player.Commands.Builder builder = new Player.Commands.Builder(); - builder.addAll(ExoPlayerImpl.PERMANENT_AVAILABLE_COMMANDS); - builder.add(COMMAND_SEEK_TO_MEDIA_ITEM); - for (int command : commands) { - builder.add(command); - } + builder.addAll( + COMMAND_PLAY_PAUSE, + COMMAND_PREPARE_STOP_RELEASE, + COMMAND_SET_SPEED_AND_PITCH, + COMMAND_SET_SHUFFLE_MODE, + COMMAND_SET_REPEAT_MODE, + COMMAND_GET_CURRENT_MEDIA_ITEM, + COMMAND_GET_MEDIA_ITEMS, + COMMAND_GET_MEDIA_ITEMS_METADATA, + COMMAND_CHANGE_MEDIA_ITEMS, + COMMAND_GET_VOLUME, + COMMAND_GET_DEVICE_VOLUME, + COMMAND_SET_VOLUME, + COMMAND_SET_DEVICE_VOLUME, + COMMAND_ADJUST_DEVICE_VOLUME, + COMMAND_SET_VIDEO_SURFACE, + COMMAND_GET_TEXT, + COMMAND_SEEK_TO_MEDIA_ITEM); + builder.addAll(additionalCommands); return builder.build(); }