Add available commands to prepare/play/pause/stop/release.

PiperOrigin-RevId: 362292208
This commit is contained in:
kimvde 2021-03-11 15:57:17 +00:00 committed by Ian Baker
parent c7bcacdeba
commit 9d2d334091
7 changed files with 79 additions and 17 deletions

View File

@ -140,14 +140,13 @@ public final class CastPlayer extends BasePlayer {
Looper.getMainLooper(),
Clock.DEFAULT,
(listener, flags) -> listener.onEvents(/* player= */ this, new Events(flags)));
playWhenReady = new StateHolder<>(false);
repeatMode = new StateHolder<>(REPEAT_MODE_OFF);
playbackState = STATE_IDLE;
currentTimeline = CastTimeline.EMPTY_CAST_TIMELINE;
currentTrackGroups = TrackGroupArray.EMPTY;
currentTrackSelection = EMPTY_TRACK_SELECTION_ARRAY;
availableCommands = Commands.EMPTY;
availableCommands = new Commands.Builder().addAll(PERMANENT_AVAILABLE_COMMANDS).build();
pendingSeekWindowIndex = C.INDEX_UNSET;
pendingSeekPositionMs = C.TIME_UNSET;

View File

@ -15,6 +15,8 @@
*/
package com.google.android.exoplayer2.ext.cast;
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_TO_NEXT_MEDIA_ITEM;
import static com.google.android.exoplayer2.Player.COMMAND_SEEK_TO_PREVIOUS_MEDIA_ITEM;
import static com.google.common.truth.Truth.assertThat;
@ -592,6 +594,12 @@ public class CastPlayerTest {
assertThat(castPlayer.isCommandAvailable(COMMAND_SEEK_TO_PREVIOUS_MEDIA_ITEM)).isFalse();
}
@Test
public void isCommandAvailable_containsPermanentCommands() {
assertThat(castPlayer.isCommandAvailable(COMMAND_PLAY_PAUSE)).isTrue();
assertThat(castPlayer.isCommandAvailable(COMMAND_PREPARE_STOP_RELEASE)).isTrue();
}
@Test
public void seekTo_nextWindow_notifiesAvailableCommandsChanged() {
when(mockRemoteMediaClient.queueJumpToItem(anyInt(), anyLong(), eq(null)))
@ -728,6 +736,7 @@ public class CastPlayerTest {
@Test
public void removeMediaItem_atTheEnd_notifiesAvailableCommandsChanged() {
Player.Commands commandsWithSeekToNext = createCommands(COMMAND_SEEK_TO_NEXT_MEDIA_ITEM);
Player.Commands commandsWithoutSeek = createCommands();
MediaItem mediaItem1 = createMediaItem(/* mediaQueueItemId= */ 1);
MediaItem mediaItem2 = createMediaItem(/* mediaQueueItemId= */ 2);
MediaItem mediaItem3 = createMediaItem(/* mediaQueueItemId= */ 3);
@ -752,7 +761,7 @@ public class CastPlayerTest {
ImmutableList.of(mediaItem1),
/* mediaQueueItemIds= */ new int[] {1},
/* currentItemId= */ 1);
verify(mockListener).onAvailableCommandsChanged(Player.Commands.EMPTY);
verify(mockListener).onAvailableCommandsChanged(commandsWithoutSeek);
verify(mockListener, times(2)).onAvailableCommandsChanged(any());
castPlayer.removeMediaItem(/* index= */ 0);
@ -769,6 +778,7 @@ public class CastPlayerTest {
.thenReturn(mockPendingResult);
Player.Commands commandsWithSeekToPrevious =
createCommands(COMMAND_SEEK_TO_PREVIOUS_MEDIA_ITEM);
Player.Commands commandsWithoutSeek = createCommands();
MediaItem mediaItem1 = createMediaItem(/* mediaQueueItemId= */ 1);
MediaItem mediaItem2 = createMediaItem(/* mediaQueueItemId= */ 2);
MediaItem mediaItem3 = createMediaItem(/* mediaQueueItemId= */ 3);
@ -793,7 +803,7 @@ public class CastPlayerTest {
ImmutableList.of(mediaItem3),
/* mediaQueueItemIds= */ new int[] {3},
/* currentItemId= */ 3);
verify(mockListener).onAvailableCommandsChanged(Player.Commands.EMPTY);
verify(mockListener).onAvailableCommandsChanged(commandsWithoutSeek);
verify(mockListener, times(2)).onAvailableCommandsChanged(any());
castPlayer.removeMediaItem(/* index= */ 0);
@ -807,6 +817,7 @@ public class CastPlayerTest {
@Test
public void removeMediaItem_current_notifiesAvailableCommandsChanged() {
Player.Commands commandsWithSeekToNext = createCommands(COMMAND_SEEK_TO_NEXT_MEDIA_ITEM);
Player.Commands commandsWithoutSeek = createCommands();
MediaItem mediaItem1 = createMediaItem(/* mediaQueueItemId= */ 1);
MediaItem mediaItem2 = createMediaItem(/* mediaQueueItemId= */ 2);
@ -823,7 +834,7 @@ public class CastPlayerTest {
ImmutableList.of(mediaItem2),
/* mediaQueueItemIds= */ new int[] {2},
/* currentItemId= */ 2);
verify(mockListener).onAvailableCommandsChanged(Player.Commands.EMPTY);
verify(mockListener).onAvailableCommandsChanged(commandsWithoutSeek);
verify(mockListener, times(2)).onAvailableCommandsChanged(any());
}
@ -912,6 +923,7 @@ public class CastPlayerTest {
private static Player.Commands createCommands(@Player.Command int... commands) {
Player.Commands.Builder builder = new Player.Commands.Builder();
builder.addAll(COMMAND_PLAY_PAUSE, COMMAND_PREPARE_STOP_RELEASE);
for (int command : commands) {
builder.add(command);
}

View File

@ -24,6 +24,9 @@ import java.util.List;
/** Abstract base {@link Player} which implements common implementation independent methods. */
public abstract class BasePlayer implements Player {
protected static final int[] PERMANENT_AVAILABLE_COMMANDS =
new int[] {COMMAND_PLAY_PAUSE, COMMAND_PREPARE_STOP_RELEASE};
protected final Timeline.Window window;
public BasePlayer() {
@ -322,6 +325,7 @@ public abstract class BasePlayer implements Player {
protected Commands getAvailableCommands() {
return new Commands.Builder()
.addAll(PERMANENT_AVAILABLE_COMMANDS)
.addIf(COMMAND_SEEK_TO_NEXT_MEDIA_ITEM, hasNext() && !isPlayingAd())
.addIf(COMMAND_SEEK_TO_PREVIOUS_MEDIA_ITEM, hasPrevious() && !isPlayingAd())
.build();

View File

@ -757,6 +757,18 @@ public interface Player {
return this;
}
/**
* Adds {@link Command commands}.
*
* @param commands The {@link Command commands} to add.
* @return This builder.
* @throws IllegalStateException If {@link #build()} has already been called.
*/
public Builder addAll(@Command int... commands) {
flagsBuilder.addAll(commands);
return this;
}
/**
* Builds a {@link Commands} instance.
*
@ -767,9 +779,6 @@ public interface Player {
}
}
/** An empty set of commands. */
public static final Commands EMPTY = new Builder().build();
private final ExoFlags flags;
private Commands(ExoFlags flags) {
@ -1037,17 +1046,27 @@ public interface Player {
int EVENT_AVAILABLE_COMMANDS_CHANGED = 14;
/**
* Commands that can be executed on a {@code Player}. One of {@link
* #COMMAND_SEEK_TO_NEXT_MEDIA_ITEM} or {@link #COMMAND_SEEK_TO_PREVIOUS_MEDIA_ITEM}.
* Commands that can be executed on a {@code Player}. One of {@link #COMMAND_PLAY_PAUSE}, {@link
* #COMMAND_PREPARE_STOP_RELEASE}, {@link #COMMAND_SEEK_TO_NEXT_MEDIA_ITEM} or {@link
* #COMMAND_SEEK_TO_PREVIOUS_MEDIA_ITEM}.
*/
@Documented
@Retention(RetentionPolicy.SOURCE)
@IntDef({COMMAND_SEEK_TO_NEXT_MEDIA_ITEM, COMMAND_SEEK_TO_PREVIOUS_MEDIA_ITEM})
@IntDef({
COMMAND_PLAY_PAUSE,
COMMAND_PREPARE_STOP_RELEASE,
COMMAND_SEEK_TO_NEXT_MEDIA_ITEM,
COMMAND_SEEK_TO_PREVIOUS_MEDIA_ITEM
})
@interface Command {}
/** Command to start, pause or resume playback. */
int COMMAND_PLAY_PAUSE = 1;
/** Command to prepare the player, stop playback or release the player. */
int COMMAND_PREPARE_STOP_RELEASE = 2;
/** Command to seek to the next {@link MediaItem} in the playlist. */
int COMMAND_SEEK_TO_NEXT_MEDIA_ITEM = 0;
int COMMAND_SEEK_TO_NEXT_MEDIA_ITEM = 3;
/** Command to seek to the previous {@link MediaItem} in the playlist. */
int COMMAND_SEEK_TO_PREVIOUS_MEDIA_ITEM = 1;
int COMMAND_SEEK_TO_PREVIOUS_MEDIA_ITEM = 4;
/** Returns the component of this player for audio output, or null if audio is not supported. */
@Nullable

View File

@ -70,6 +70,20 @@ public final class ExoFlags {
return this;
}
/**
* Adds flags.
*
* @param flags The flags to add.
* @return This builder.
* @throws IllegalStateException If {@link #build()} has already been called.
*/
public Builder addAll(int... flags) {
for (int flag : flags) {
add(flag);
}
return this;
}
/**
* Builds an {@link ExoFlags} instance.
*

View File

@ -174,7 +174,7 @@ import java.util.List;
new ExoTrackSelection[renderers.length],
/* info= */ null);
period = new Timeline.Period();
availableCommands = Commands.EMPTY;
availableCommands = new Commands.Builder().addAll(PERMANENT_AVAILABLE_COMMANDS).build();
maskingWindowIndex = C.INDEX_UNSET;
playbackInfoUpdateHandler = clock.createHandler(applicationLooper, /* callback= */ null);
playbackInfoUpdateListener =

View File

@ -15,6 +15,8 @@
*/
package com.google.android.exoplayer2;
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_TO_NEXT_MEDIA_ITEM;
import static com.google.android.exoplayer2.Player.COMMAND_SEEK_TO_PREVIOUS_MEDIA_ITEM;
import static com.google.android.exoplayer2.robolectric.RobolectricUtil.runMainLooperUntil;
@ -8080,6 +8082,14 @@ public final class ExoPlayerTest {
assertThat(player.isCommandAvailable(COMMAND_SEEK_TO_PREVIOUS_MEDIA_ITEM)).isFalse();
}
@Test
public void isCommandAvailable_containsPermanentCommands() {
ExoPlayer player = new TestExoPlayerBuilder(context).build();
assertThat(player.isCommandAvailable(COMMAND_PLAY_PAUSE)).isTrue();
assertThat(player.isCommandAvailable(COMMAND_PREPARE_STOP_RELEASE)).isTrue();
}
@Test
public void isCommandAvailable_whenPlayingAd_isFalseForSeekCommands() throws Exception {
AdPlaybackState adPlaybackState =
@ -8262,6 +8272,7 @@ 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.EventListener mockListener = mock(Player.EventListener.class);
ExoPlayer player = new TestExoPlayerBuilder(context).build();
player.addListener(mockListener);
@ -8275,7 +8286,7 @@ public final class ExoPlayerTest {
verify(mockListener).onAvailableCommandsChanged(any());
player.removeMediaItem(/* index= */ 1);
verify(mockListener).onAvailableCommandsChanged(Player.Commands.EMPTY);
verify(mockListener).onAvailableCommandsChanged(commandsWithoutSeek);
verify(mockListener, times(2)).onAvailableCommandsChanged(any());
player.removeMediaItem(/* index= */ 0);
@ -8286,6 +8297,7 @@ public final class ExoPlayerTest {
public void removeMediaItem_atTheStart_notifiesAvailableCommandsChanged() {
Player.Commands commandsWithSeekToPrevious =
createCommands(COMMAND_SEEK_TO_PREVIOUS_MEDIA_ITEM);
Player.Commands commandsWithoutSeek = createCommands();
Player.EventListener mockListener = mock(Player.EventListener.class);
ExoPlayer player = new TestExoPlayerBuilder(context).build();
player.addListener(mockListener);
@ -8300,7 +8312,7 @@ public final class ExoPlayerTest {
verify(mockListener).onAvailableCommandsChanged(any());
player.removeMediaItem(/* index= */ 0);
verify(mockListener).onAvailableCommandsChanged(Player.Commands.EMPTY);
verify(mockListener).onAvailableCommandsChanged(commandsWithoutSeek);
verify(mockListener, times(2)).onAvailableCommandsChanged(any());
player.removeMediaItem(/* index= */ 0);
@ -8310,6 +8322,7 @@ 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.EventListener mockListener = mock(Player.EventListener.class);
ExoPlayer player = new TestExoPlayerBuilder(context).build();
player.addListener(mockListener);
@ -8319,7 +8332,7 @@ public final class ExoPlayerTest {
verify(mockListener).onAvailableCommandsChanged(any());
player.removeMediaItem(/* index= */ 0);
verify(mockListener).onAvailableCommandsChanged(Player.Commands.EMPTY);
verify(mockListener).onAvailableCommandsChanged(commandsWithoutSeek);
verify(mockListener, times(2)).onAvailableCommandsChanged(any());
}
@ -9313,6 +9326,7 @@ public final class ExoPlayerTest {
private static Player.Commands createCommands(@Player.Command int... commands) {
Player.Commands.Builder builder = new Player.Commands.Builder();
builder.addAll(COMMAND_PLAY_PAUSE, COMMAND_PREPARE_STOP_RELEASE);
for (int command : commands) {
builder.add(command);
}