Add SEEK_TO_PREVIOUS command

PiperOrigin-RevId: 384257929
This commit is contained in:
kimvde 2021-07-12 18:01:42 +01:00 committed by Oliver Woodman
parent 1058997e38
commit 8451be1b0b
4 changed files with 297 additions and 166 deletions

View File

@ -31,6 +31,7 @@ import static com.google.android.exoplayer2.Player.COMMAND_SEEK_FORWARD;
import static com.google.android.exoplayer2.Player.COMMAND_SEEK_IN_CURRENT_WINDOW;
import static com.google.android.exoplayer2.Player.COMMAND_SEEK_TO_DEFAULT_POSITION;
import static com.google.android.exoplayer2.Player.COMMAND_SEEK_TO_NEXT_WINDOW;
import static com.google.android.exoplayer2.Player.COMMAND_SEEK_TO_PREVIOUS;
import static com.google.android.exoplayer2.Player.COMMAND_SEEK_TO_PREVIOUS_WINDOW;
import static com.google.android.exoplayer2.Player.COMMAND_SEEK_TO_WINDOW;
import static com.google.android.exoplayer2.Player.COMMAND_SET_DEVICE_VOLUME;
@ -1207,6 +1208,7 @@ public class CastPlayerTest {
assertThat(castPlayer.isCommandAvailable(COMMAND_SEEK_TO_DEFAULT_POSITION)).isTrue();
assertThat(castPlayer.isCommandAvailable(COMMAND_SEEK_IN_CURRENT_WINDOW)).isTrue();
assertThat(castPlayer.isCommandAvailable(COMMAND_SEEK_TO_PREVIOUS_WINDOW)).isFalse();
assertThat(castPlayer.isCommandAvailable(COMMAND_SEEK_TO_PREVIOUS)).isTrue();
assertThat(castPlayer.isCommandAvailable(COMMAND_SEEK_TO_NEXT_WINDOW)).isTrue();
assertThat(castPlayer.isCommandAvailable(COMMAND_SEEK_TO_WINDOW)).isTrue();
assertThat(castPlayer.isCommandAvailable(COMMAND_SEEK_BACK)).isTrue();
@ -1251,35 +1253,74 @@ public class CastPlayerTest {
assertThat(castPlayer.isCommandAvailable(COMMAND_SEEK_FORWARD)).isFalse();
}
@Test
public void isCommandAvailable_duringUnseekableLiveItem_isFalseForSeekToPrevious() {
MediaItem mediaItem = createMediaItem(/* mediaQueueItemId= */ 1);
List<MediaItem> mediaItems = ImmutableList.of(mediaItem);
int[] mediaQueueItemIds = new int[] {1};
int[] streamTypes = new int[] {MediaInfo.STREAM_TYPE_LIVE};
long[] durationsMs = new long[] {C.TIME_UNSET};
castPlayer.addMediaItem(mediaItem);
updateTimeLine(
mediaItems,
mediaQueueItemIds,
/* currentItemId= */ 1,
streamTypes,
durationsMs,
/* positionMs= */ C.TIME_UNSET);
assertThat(castPlayer.isCommandAvailable(COMMAND_SEEK_TO_PREVIOUS)).isFalse();
}
@Test
public void
isCommandAvailable_duringUnseekableLiveItemWithPreviousWindow_isTrueForSeekToPrevious() {
int[] mediaQueueItemIds = new int[] {1, 2};
List<MediaItem> mediaItems = createMediaItems(mediaQueueItemIds);
int[] streamTypes = new int[] {MediaInfo.STREAM_TYPE_BUFFERED, MediaInfo.STREAM_TYPE_LIVE};
long[] durationsMs = new long[] {10_000, C.TIME_UNSET};
castPlayer.addMediaItems(mediaItems);
updateTimeLine(
mediaItems,
mediaQueueItemIds,
/* currentItemId= */ 2,
streamTypes,
durationsMs,
/* positionMs= */ C.TIME_UNSET);
assertThat(castPlayer.isCommandAvailable(COMMAND_SEEK_TO_PREVIOUS)).isTrue();
}
@Test
public void seekTo_nextWindow_notifiesAvailableCommandsChanged() {
when(mockRemoteMediaClient.queueJumpToItem(anyInt(), anyLong(), eq(null)))
.thenReturn(mockPendingResult);
Player.Commands commandsWithSeekInCurrentAndToNext =
createWithPermanentAndSeekInCurrentCommands(COMMAND_SEEK_TO_NEXT_WINDOW);
Player.Commands commandsWithSeekInCurrentAndToPrevious =
createWithPermanentAndSeekInCurrentCommands(COMMAND_SEEK_TO_PREVIOUS_WINDOW);
Player.Commands commandsWithSeekAnywhere =
createWithPermanentAndSeekInCurrentCommands(
COMMAND_SEEK_TO_NEXT_WINDOW, COMMAND_SEEK_TO_PREVIOUS_WINDOW);
Player.Commands commandsWithSeekToPreviousWindow =
createWithDefaultCommands(COMMAND_SEEK_TO_PREVIOUS_WINDOW);
Player.Commands commandsWithSeekToNextWindow =
createWithDefaultCommands(COMMAND_SEEK_TO_NEXT_WINDOW);
Player.Commands commandsWithSeekToPreviousAndNextWindow =
createWithDefaultCommands(COMMAND_SEEK_TO_PREVIOUS_WINDOW, COMMAND_SEEK_TO_NEXT_WINDOW);
int[] mediaQueueItemIds = new int[] {1, 2, 3, 4};
List<MediaItem> mediaItems = createMediaItems(mediaQueueItemIds);
castPlayer.addMediaItems(mediaItems);
updateTimeLine(mediaItems, mediaQueueItemIds, /* currentItemId= */ 1);
verify(mockListener).onAvailableCommandsChanged(commandsWithSeekInCurrentAndToNext);
verify(mockListener).onAvailableCommandsChanged(commandsWithSeekToNextWindow);
// Check that there were no other calls to onAvailableCommandsChanged.
verify(mockListener).onAvailableCommandsChanged(any());
castPlayer.seekTo(/* windowIndex= */ 1, /* positionMs= */ 0);
verify(mockListener).onAvailableCommandsChanged(commandsWithSeekAnywhere);
verify(mockListener).onAvailableCommandsChanged(commandsWithSeekToPreviousAndNextWindow);
verify(mockListener, times(2)).onAvailableCommandsChanged(any());
castPlayer.seekTo(/* windowIndex= */ 2, /* positionMs= */ 0);
verify(mockListener, times(2)).onAvailableCommandsChanged(any());
castPlayer.seekTo(/* windowIndex= */ 3, /* positionMs= */ 0);
verify(mockListener).onAvailableCommandsChanged(commandsWithSeekInCurrentAndToPrevious);
verify(mockListener).onAvailableCommandsChanged(commandsWithSeekToPreviousWindow);
verify(mockListener, times(3)).onAvailableCommandsChanged(any());
}
@ -1287,31 +1328,30 @@ public class CastPlayerTest {
public void seekTo_previousWindow_notifiesAvailableCommandsChanged() {
when(mockRemoteMediaClient.queueJumpToItem(anyInt(), anyLong(), eq(null)))
.thenReturn(mockPendingResult);
Player.Commands commandsWithSeekInCurrentAndToNext =
createWithPermanentAndSeekInCurrentCommands(COMMAND_SEEK_TO_NEXT_WINDOW);
Player.Commands commandsWithSeekInCurrentAndToPrevious =
createWithPermanentAndSeekInCurrentCommands(COMMAND_SEEK_TO_PREVIOUS_WINDOW);
Player.Commands commandsWithSeekAnywhere =
createWithPermanentAndSeekInCurrentCommands(
COMMAND_SEEK_TO_NEXT_WINDOW, COMMAND_SEEK_TO_PREVIOUS_WINDOW);
Player.Commands commandsWithSeekToPreviousWindow =
createWithDefaultCommands(COMMAND_SEEK_TO_PREVIOUS_WINDOW);
Player.Commands commandsWithSeekToNextWindow =
createWithDefaultCommands(COMMAND_SEEK_TO_NEXT_WINDOW);
Player.Commands commandsWithSeekToPreviousAndNextWindow =
createWithDefaultCommands(COMMAND_SEEK_TO_PREVIOUS_WINDOW, COMMAND_SEEK_TO_NEXT_WINDOW);
int[] mediaQueueItemIds = new int[] {1, 2, 3, 4};
List<MediaItem> mediaItems = createMediaItems(mediaQueueItemIds);
castPlayer.addMediaItems(mediaItems);
updateTimeLine(mediaItems, mediaQueueItemIds, /* currentItemId= */ 4);
verify(mockListener).onAvailableCommandsChanged(commandsWithSeekInCurrentAndToPrevious);
verify(mockListener).onAvailableCommandsChanged(commandsWithSeekToPreviousWindow);
// Check that there were no other calls to onAvailableCommandsChanged.
verify(mockListener).onAvailableCommandsChanged(any());
castPlayer.seekTo(/* windowIndex= */ 2, /* positionMs= */ 0);
verify(mockListener).onAvailableCommandsChanged(commandsWithSeekAnywhere);
verify(mockListener).onAvailableCommandsChanged(commandsWithSeekToPreviousAndNextWindow);
verify(mockListener, times(2)).onAvailableCommandsChanged(any());
castPlayer.seekTo(/* windowIndex= */ 1, /* positionMs= */ 0);
verify(mockListener, times(2)).onAvailableCommandsChanged(any());
castPlayer.seekTo(/* windowIndex= */ 0, /* positionMs= */ 0);
verify(mockListener).onAvailableCommandsChanged(commandsWithSeekInCurrentAndToNext);
verify(mockListener).onAvailableCommandsChanged(commandsWithSeekToNextWindow);
verify(mockListener, times(3)).onAvailableCommandsChanged(any());
}
@ -1319,13 +1359,13 @@ 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 = createWithPermanentAndSeekInCurrentCommands();
Player.Commands defaultCommands = createWithDefaultCommands();
int[] mediaQueueItemIds = new int[] {1};
List<MediaItem> mediaItems = createMediaItems(mediaQueueItemIds);
castPlayer.addMediaItems(mediaItems);
updateTimeLine(mediaItems, mediaQueueItemIds, /* currentItemId= */ 1);
verify(mockListener).onAvailableCommandsChanged(commandsWithSeekInCurrent);
verify(mockListener).onAvailableCommandsChanged(defaultCommands);
castPlayer.seekTo(/* windowIndex= */ 0, /* positionMs= */ 200);
castPlayer.seekTo(/* windowIndex= */ 0, /* positionMs= */ 100);
@ -1335,10 +1375,9 @@ public class CastPlayerTest {
@Test
public void addMediaItem_atTheEnd_notifiesAvailableCommandsChanged() {
Player.Commands commandsWithSeekInCurrent = createWithPermanentAndSeekInCurrentCommands();
;
Player.Commands commandsWithSeekInCurrentAndToNext =
createWithPermanentAndSeekInCurrentCommands(COMMAND_SEEK_TO_NEXT_WINDOW);
Player.Commands defaultCommands = createWithDefaultCommands();
Player.Commands commandsWithSeekToNextWindow =
createWithDefaultCommands(COMMAND_SEEK_TO_NEXT_WINDOW);
MediaItem mediaItem1 = createMediaItem(/* mediaQueueItemId= */ 1);
MediaItem mediaItem2 = createMediaItem(/* mediaQueueItemId= */ 2);
MediaItem mediaItem3 = createMediaItem(/* mediaQueueItemId= */ 3);
@ -1348,7 +1387,7 @@ public class CastPlayerTest {
ImmutableList.of(mediaItem1),
/* mediaQueueItemIds= */ new int[] {1},
/* currentItemId= */ 1);
verify(mockListener).onAvailableCommandsChanged(commandsWithSeekInCurrent);
verify(mockListener).onAvailableCommandsChanged(defaultCommands);
// Check that there were no other calls to onAvailableCommandsChanged.
verify(mockListener).onAvailableCommandsChanged(any());
@ -1357,7 +1396,7 @@ public class CastPlayerTest {
ImmutableList.of(mediaItem1, mediaItem2),
/* mediaQueueItemIds= */ new int[] {1, 2},
/* currentItemId= */ 1);
verify(mockListener).onAvailableCommandsChanged(commandsWithSeekInCurrentAndToNext);
verify(mockListener).onAvailableCommandsChanged(commandsWithSeekToNextWindow);
verify(mockListener, times(2)).onAvailableCommandsChanged(any());
castPlayer.addMediaItem(mediaItem3);
@ -1370,10 +1409,9 @@ public class CastPlayerTest {
@Test
public void addMediaItem_atTheStart_notifiesAvailableCommandsChanged() {
Player.Commands commandsWithSeekInCurrent = createWithPermanentAndSeekInCurrentCommands();
;
Player.Commands commandsWithSeekInCurrentAndToPrevious =
createWithPermanentAndSeekInCurrentCommands(COMMAND_SEEK_TO_PREVIOUS_WINDOW);
Player.Commands defaultCommands = createWithDefaultCommands();
Player.Commands commandsWithSeekToPreviousWindow =
createWithDefaultCommands(COMMAND_SEEK_TO_PREVIOUS_WINDOW);
MediaItem mediaItem1 = createMediaItem(/* mediaQueueItemId= */ 1);
MediaItem mediaItem2 = createMediaItem(/* mediaQueueItemId= */ 2);
MediaItem mediaItem3 = createMediaItem(/* mediaQueueItemId= */ 3);
@ -1383,7 +1421,7 @@ public class CastPlayerTest {
ImmutableList.of(mediaItem1),
/* mediaQueueItemIds= */ new int[] {1},
/* currentItemId= */ 1);
verify(mockListener).onAvailableCommandsChanged(commandsWithSeekInCurrent);
verify(mockListener).onAvailableCommandsChanged(defaultCommands);
// Check that there were no other calls to onAvailableCommandsChanged.
verify(mockListener).onAvailableCommandsChanged(any());
@ -1392,7 +1430,7 @@ public class CastPlayerTest {
ImmutableList.of(mediaItem2, mediaItem1),
/* mediaQueueItemIds= */ new int[] {2, 1},
/* currentItemId= */ 1);
verify(mockListener).onAvailableCommandsChanged(commandsWithSeekInCurrentAndToPrevious);
verify(mockListener).onAvailableCommandsChanged(commandsWithSeekToPreviousWindow);
verify(mockListener, times(2)).onAvailableCommandsChanged(any());
castPlayer.addMediaItem(/* index= */ 0, mediaItem3);
@ -1405,11 +1443,10 @@ public class CastPlayerTest {
@Test
public void removeMediaItem_atTheEnd_notifiesAvailableCommandsChanged() {
Player.Commands commandsWithoutSeek = createWithPermanentCommands();
Player.Commands commandsWithSeekInCurrent = createWithPermanentAndSeekInCurrentCommands();
;
Player.Commands commandsWithSeekInCurrentAndToNext =
createWithPermanentAndSeekInCurrentCommands(COMMAND_SEEK_TO_NEXT_WINDOW);
Player.Commands defaultCommands = createWithDefaultCommands();
Player.Commands commandsWithSeekToNextWindow =
createWithDefaultCommands(COMMAND_SEEK_TO_NEXT_WINDOW);
Player.Commands emptyTimelineCommands = createWithDefaultCommands(/* isTimelineEmpty= */ true);
MediaItem mediaItem1 = createMediaItem(/* mediaQueueItemId= */ 1);
MediaItem mediaItem2 = createMediaItem(/* mediaQueueItemId= */ 2);
MediaItem mediaItem3 = createMediaItem(/* mediaQueueItemId= */ 3);
@ -1419,7 +1456,7 @@ public class CastPlayerTest {
ImmutableList.of(mediaItem1, mediaItem2, mediaItem3),
/* mediaQueueItemIds= */ new int[] {1, 2, 3},
/* currentItemId= */ 1);
verify(mockListener).onAvailableCommandsChanged(commandsWithSeekInCurrentAndToNext);
verify(mockListener).onAvailableCommandsChanged(commandsWithSeekToNextWindow);
// Check that there were no other calls to onAvailableCommandsChanged.
verify(mockListener).onAvailableCommandsChanged(any());
@ -1435,7 +1472,7 @@ public class CastPlayerTest {
ImmutableList.of(mediaItem1),
/* mediaQueueItemIds= */ new int[] {1},
/* currentItemId= */ 1);
verify(mockListener).onAvailableCommandsChanged(commandsWithSeekInCurrent);
verify(mockListener).onAvailableCommandsChanged(defaultCommands);
verify(mockListener, times(2)).onAvailableCommandsChanged(any());
castPlayer.removeMediaItem(/* index= */ 0);
@ -1443,7 +1480,7 @@ public class CastPlayerTest {
ImmutableList.of(),
/* mediaQueueItemIds= */ new int[0],
/* currentItemId= */ C.INDEX_UNSET);
verify(mockListener).onAvailableCommandsChanged(commandsWithoutSeek);
verify(mockListener).onAvailableCommandsChanged(emptyTimelineCommands);
verify(mockListener, times(3)).onAvailableCommandsChanged(any());
}
@ -1451,11 +1488,10 @@ public class CastPlayerTest {
public void removeMediaItem_atTheStart_notifiesAvailableCommandsChanged() {
when(mockRemoteMediaClient.queueJumpToItem(anyInt(), anyLong(), eq(null)))
.thenReturn(mockPendingResult);
Player.Commands commandsWithoutSeek = createWithPermanentCommands();
Player.Commands commandsWithSeekInCurrent = createWithPermanentAndSeekInCurrentCommands();
;
Player.Commands commandsWithSeekInCurrentAndToPrevious =
createWithPermanentAndSeekInCurrentCommands(COMMAND_SEEK_TO_PREVIOUS_WINDOW);
Player.Commands defaultCommands = createWithDefaultCommands();
Player.Commands commandsWithSeekToPreviousWindow =
createWithDefaultCommands(COMMAND_SEEK_TO_PREVIOUS_WINDOW);
Player.Commands emptyTimelineCommands = createWithDefaultCommands(/* isTimelineEmpty= */ true);
MediaItem mediaItem1 = createMediaItem(/* mediaQueueItemId= */ 1);
MediaItem mediaItem2 = createMediaItem(/* mediaQueueItemId= */ 2);
MediaItem mediaItem3 = createMediaItem(/* mediaQueueItemId= */ 3);
@ -1465,7 +1501,7 @@ public class CastPlayerTest {
ImmutableList.of(mediaItem1, mediaItem2, mediaItem3),
/* mediaQueueItemIds= */ new int[] {1, 2, 3},
/* currentItemId= */ 3);
verify(mockListener).onAvailableCommandsChanged(commandsWithSeekInCurrentAndToPrevious);
verify(mockListener).onAvailableCommandsChanged(commandsWithSeekToPreviousWindow);
// Check that there were no other calls to onAvailableCommandsChanged.
verify(mockListener).onAvailableCommandsChanged(any());
@ -1481,7 +1517,7 @@ public class CastPlayerTest {
ImmutableList.of(mediaItem3),
/* mediaQueueItemIds= */ new int[] {3},
/* currentItemId= */ 3);
verify(mockListener).onAvailableCommandsChanged(commandsWithSeekInCurrent);
verify(mockListener).onAvailableCommandsChanged(defaultCommands);
verify(mockListener, times(2)).onAvailableCommandsChanged(any());
castPlayer.removeMediaItem(/* index= */ 0);
@ -1489,15 +1525,15 @@ public class CastPlayerTest {
ImmutableList.of(),
/* mediaQueueItemIds= */ new int[0],
/* currentItemId= */ C.INDEX_UNSET);
verify(mockListener).onAvailableCommandsChanged(commandsWithoutSeek);
verify(mockListener).onAvailableCommandsChanged(emptyTimelineCommands);
verify(mockListener, times(3)).onAvailableCommandsChanged(any());
}
@Test
public void removeMediaItem_current_notifiesAvailableCommandsChanged() {
Player.Commands commandsWithSeekInCurrent = createWithPermanentAndSeekInCurrentCommands();
Player.Commands commandsWithSeekInCurrentAndToNext =
createWithPermanentAndSeekInCurrentCommands(COMMAND_SEEK_TO_NEXT_WINDOW);
Player.Commands defaultCommands = createWithDefaultCommands();
Player.Commands commandsWithSeekToNextWindow =
createWithDefaultCommands(COMMAND_SEEK_TO_NEXT_WINDOW);
MediaItem mediaItem1 = createMediaItem(/* mediaQueueItemId= */ 1);
MediaItem mediaItem2 = createMediaItem(/* mediaQueueItemId= */ 2);
@ -1506,7 +1542,7 @@ public class CastPlayerTest {
ImmutableList.of(mediaItem1, mediaItem2),
/* mediaQueueItemIds= */ new int[] {1, 2},
/* currentItemId= */ 1);
verify(mockListener).onAvailableCommandsChanged(commandsWithSeekInCurrentAndToNext);
verify(mockListener).onAvailableCommandsChanged(commandsWithSeekToNextWindow);
// Check that there were no other calls to onAvailableCommandsChanged.
verify(mockListener).onAvailableCommandsChanged(any());
@ -1515,7 +1551,7 @@ public class CastPlayerTest {
ImmutableList.of(mediaItem2),
/* mediaQueueItemIds= */ new int[] {2},
/* currentItemId= */ 2);
verify(mockListener).onAvailableCommandsChanged(commandsWithSeekInCurrent);
verify(mockListener).onAvailableCommandsChanged(defaultCommands);
verify(mockListener, times(2)).onAvailableCommandsChanged(any());
}
@ -1523,22 +1559,20 @@ public class CastPlayerTest {
public void setRepeatMode_all_notifiesAvailableCommandsChanged() {
when(mockRemoteMediaClient.queueSetRepeatMode(anyInt(), eq(null)))
.thenReturn(mockPendingResult);
Player.Commands commandsWithSeekInCurrent = createWithPermanentAndSeekInCurrentCommands();
;
Player.Commands commandsWithSeekAnywhere =
createWithPermanentAndSeekInCurrentCommands(
COMMAND_SEEK_TO_NEXT_WINDOW, COMMAND_SEEK_TO_PREVIOUS_WINDOW);
Player.Commands defaultCommands = createWithDefaultCommands();
Player.Commands commandsWithSeekToPreviousAndNextWindow =
createWithDefaultCommands(COMMAND_SEEK_TO_PREVIOUS_WINDOW, COMMAND_SEEK_TO_NEXT_WINDOW);
int[] mediaQueueItemIds = new int[] {1};
List<MediaItem> mediaItems = createMediaItems(mediaQueueItemIds);
castPlayer.addMediaItems(mediaItems);
updateTimeLine(mediaItems, mediaQueueItemIds, /* currentItemId= */ 1);
verify(mockListener).onAvailableCommandsChanged(commandsWithSeekInCurrent);
verify(mockListener).onAvailableCommandsChanged(defaultCommands);
// Check that there were no other calls to onAvailableCommandsChanged.
verify(mockListener).onAvailableCommandsChanged(any());
castPlayer.setRepeatMode(Player.REPEAT_MODE_ALL);
verify(mockListener).onAvailableCommandsChanged(commandsWithSeekAnywhere);
verify(mockListener).onAvailableCommandsChanged(commandsWithSeekToPreviousAndNextWindow);
verify(mockListener, times(2)).onAvailableCommandsChanged(any());
}
@ -1546,14 +1580,13 @@ public class CastPlayerTest {
public void setRepeatMode_one_doesNotNotifyAvailableCommandsChanged() {
when(mockRemoteMediaClient.queueSetRepeatMode(anyInt(), eq(null)))
.thenReturn(mockPendingResult);
Player.Commands commandsWithSeekInCurrent = createWithPermanentAndSeekInCurrentCommands();
;
Player.Commands defaultCommands = createWithDefaultCommands();
int[] mediaQueueItemIds = new int[] {1};
List<MediaItem> mediaItems = createMediaItems(mediaQueueItemIds);
castPlayer.addMediaItems(mediaItems);
updateTimeLine(mediaItems, mediaQueueItemIds, /* currentItemId= */ 1);
verify(mockListener).onAvailableCommandsChanged(commandsWithSeekInCurrent);
verify(mockListener).onAvailableCommandsChanged(defaultCommands);
// Check that there were no other calls to onAvailableCommandsChanged.
verify(mockListener).onAvailableCommandsChanged(any());
@ -1648,22 +1681,22 @@ public class CastPlayerTest {
remoteMediaClientCallback.onStatusUpdated();
}
private static Player.Commands createWithPermanentCommands(
@Player.Command int... additionalCommands) {
private static Player.Commands createWithDefaultCommands(
boolean isTimelineEmpty, @Player.Command int... additionalCommands) {
Player.Commands.Builder builder = new Player.Commands.Builder();
builder.addAll(CastPlayer.PERMANENT_AVAILABLE_COMMANDS);
if (!isTimelineEmpty) {
builder.add(COMMAND_SEEK_IN_CURRENT_WINDOW);
builder.add(COMMAND_SEEK_TO_PREVIOUS);
builder.add(COMMAND_SEEK_BACK);
builder.add(COMMAND_SEEK_FORWARD);
}
builder.addAll(additionalCommands);
return builder.build();
}
private static Player.Commands createWithPermanentAndSeekInCurrentCommands(
private static Player.Commands createWithDefaultCommands(
@Player.Command int... additionalCommands) {
Player.Commands.Builder builder = new Player.Commands.Builder();
builder.addAll(CastPlayer.PERMANENT_AVAILABLE_COMMANDS);
builder.add(COMMAND_SEEK_IN_CURRENT_WINDOW);
builder.add(COMMAND_SEEK_BACK);
builder.add(COMMAND_SEEK_FORWARD);
builder.addAll(additionalCommands);
return builder.build();
return createWithDefaultCommands(/* isTimelineEmpty= */ false, additionalCommands);
}
}

View File

@ -296,6 +296,11 @@ public abstract class BasePlayer implements Player {
.addIf(COMMAND_SEEK_TO_DEFAULT_POSITION, !isPlayingAd())
.addIf(COMMAND_SEEK_IN_CURRENT_WINDOW, isCurrentWindowSeekable() && !isPlayingAd())
.addIf(COMMAND_SEEK_TO_PREVIOUS_WINDOW, hasPrevious() && !isPlayingAd())
.addIf(
COMMAND_SEEK_TO_PREVIOUS,
!getCurrentTimeline().isEmpty()
&& (hasPrevious() || !isCurrentWindowLive() || isCurrentWindowSeekable())
&& !isPlayingAd())
.addIf(COMMAND_SEEK_TO_NEXT_WINDOW, hasNext() && !isPlayingAd())
.addIf(COMMAND_SEEK_TO_WINDOW, !isPlayingAd())
.addIf(COMMAND_SEEK_BACK, isCurrentWindowSeekable() && !isPlayingAd())

View File

@ -640,6 +640,7 @@ public interface Player {
COMMAND_SEEK_TO_DEFAULT_POSITION,
COMMAND_SEEK_IN_CURRENT_WINDOW,
COMMAND_SEEK_TO_PREVIOUS_WINDOW,
COMMAND_SEEK_TO_PREVIOUS,
COMMAND_SEEK_TO_NEXT_WINDOW,
COMMAND_SEEK_TO_WINDOW,
COMMAND_SEEK_BACK,
@ -1255,15 +1256,16 @@ public interface Player {
* Commands that can be executed on a {@code Player}. One of {@link #COMMAND_PLAY_PAUSE}, {@link
* #COMMAND_PREPARE_STOP}, {@link #COMMAND_SEEK_TO_DEFAULT_POSITION}, {@link
* #COMMAND_SEEK_IN_CURRENT_WINDOW}, {@link #COMMAND_SEEK_TO_PREVIOUS_WINDOW}, {@link
* #COMMAND_SEEK_TO_NEXT_WINDOW}, {@link #COMMAND_SEEK_TO_WINDOW}, {@link #COMMAND_SEEK_BACK},
* {@link #COMMAND_SEEK_FORWARD}, {@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_TIMELINE}, {@link
* #COMMAND_GET_MEDIA_ITEMS_METADATA}, {@link #COMMAND_SET_MEDIA_ITEMS_METADATA}, {@link
* #COMMAND_CHANGE_MEDIA_ITEMS}, {@link #COMMAND_GET_AUDIO_ATTRIBUTES}, {@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}.
* #COMMAND_SEEK_TO_PREVIOUS}, {@link #COMMAND_SEEK_TO_NEXT_WINDOW}, {@link
* #COMMAND_SEEK_TO_WINDOW}, {@link #COMMAND_SEEK_BACK}, {@link #COMMAND_SEEK_FORWARD}, {@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_TIMELINE}, {@link #COMMAND_GET_MEDIA_ITEMS_METADATA}, {@link
* #COMMAND_SET_MEDIA_ITEMS_METADATA}, {@link #COMMAND_CHANGE_MEDIA_ITEMS}, {@link
* #COMMAND_GET_AUDIO_ATTRIBUTES}, {@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)
@ -1274,6 +1276,7 @@ public interface Player {
COMMAND_SEEK_TO_DEFAULT_POSITION,
COMMAND_SEEK_IN_CURRENT_WINDOW,
COMMAND_SEEK_TO_PREVIOUS_WINDOW,
COMMAND_SEEK_TO_PREVIOUS,
COMMAND_SEEK_TO_NEXT_WINDOW,
COMMAND_SEEK_TO_WINDOW,
COMMAND_SEEK_BACK,
@ -1306,46 +1309,48 @@ public interface Player {
int COMMAND_SEEK_IN_CURRENT_WINDOW = 4;
/** Command to seek to the default position of the previous window. */
int COMMAND_SEEK_TO_PREVIOUS_WINDOW = 5;
/** Command to seek to an earlier position in the current or previous window. */
int COMMAND_SEEK_TO_PREVIOUS = 6;
/** Command to seek to the default position of the next window. */
int COMMAND_SEEK_TO_NEXT_WINDOW = 6;
int COMMAND_SEEK_TO_NEXT_WINDOW = 7;
/** Command to seek anywhere in any window. */
int COMMAND_SEEK_TO_WINDOW = 7;
int COMMAND_SEEK_TO_WINDOW = 8;
/** Command to seek back by a fixed increment into the current window. */
int COMMAND_SEEK_BACK = 8;
int COMMAND_SEEK_BACK = 9;
/** Command to seek forward by a fixed increment into the current window. */
int COMMAND_SEEK_FORWARD = 9;
int COMMAND_SEEK_FORWARD = 10;
/** Command to set the playback speed and pitch. */
int COMMAND_SET_SPEED_AND_PITCH = 10;
int COMMAND_SET_SPEED_AND_PITCH = 11;
/** Command to enable shuffling. */
int COMMAND_SET_SHUFFLE_MODE = 11;
int COMMAND_SET_SHUFFLE_MODE = 12;
/** Command to set the repeat mode. */
int COMMAND_SET_REPEAT_MODE = 12;
int COMMAND_SET_REPEAT_MODE = 13;
/** Command to get the {@link MediaItem} of the current window. */
int COMMAND_GET_CURRENT_MEDIA_ITEM = 13;
int COMMAND_GET_CURRENT_MEDIA_ITEM = 14;
/** Command to get the information about the current timeline. */
int COMMAND_GET_TIMELINE = 14;
int COMMAND_GET_TIMELINE = 15;
/** Command to get the {@link MediaItem MediaItems} metadata. */
int COMMAND_GET_MEDIA_ITEMS_METADATA = 15;
int COMMAND_GET_MEDIA_ITEMS_METADATA = 16;
/** Command to set the {@link MediaItem MediaItems} metadata. */
int COMMAND_SET_MEDIA_ITEMS_METADATA = 16;
int COMMAND_SET_MEDIA_ITEMS_METADATA = 17;
/** Command to change the {@link MediaItem MediaItems} in the playlist. */
int COMMAND_CHANGE_MEDIA_ITEMS = 17;
int COMMAND_CHANGE_MEDIA_ITEMS = 18;
/** Command to get the player current {@link AudioAttributes}. */
int COMMAND_GET_AUDIO_ATTRIBUTES = 18;
int COMMAND_GET_AUDIO_ATTRIBUTES = 19;
/** Command to get the player volume. */
int COMMAND_GET_VOLUME = 19;
int COMMAND_GET_VOLUME = 20;
/** Command to get the device volume and whether it is muted. */
int COMMAND_GET_DEVICE_VOLUME = 20;
int COMMAND_GET_DEVICE_VOLUME = 21;
/** Command to set the player volume. */
int COMMAND_SET_VOLUME = 21;
int COMMAND_SET_VOLUME = 22;
/** Command to set the device volume and mute it. */
int COMMAND_SET_DEVICE_VOLUME = 22;
int COMMAND_SET_DEVICE_VOLUME = 23;
/** Command to increase and decrease the device volume and mute it. */
int COMMAND_ADJUST_DEVICE_VOLUME = 23;
int COMMAND_ADJUST_DEVICE_VOLUME = 24;
/** Command to set and clear the surface on which to render the video. */
int COMMAND_SET_VIDEO_SURFACE = 24;
int COMMAND_SET_VIDEO_SURFACE = 25;
/** Command to get the text that should currently be displayed by the player. */
int COMMAND_GET_TEXT = 25;
int COMMAND_GET_TEXT = 26;
/** Represents an invalid {@link Command}. */
int COMMAND_INVALID = -1;

View File

@ -31,6 +31,7 @@ import static com.google.android.exoplayer2.Player.COMMAND_SEEK_FORWARD;
import static com.google.android.exoplayer2.Player.COMMAND_SEEK_IN_CURRENT_WINDOW;
import static com.google.android.exoplayer2.Player.COMMAND_SEEK_TO_DEFAULT_POSITION;
import static com.google.android.exoplayer2.Player.COMMAND_SEEK_TO_NEXT_WINDOW;
import static com.google.android.exoplayer2.Player.COMMAND_SEEK_TO_PREVIOUS;
import static com.google.android.exoplayer2.Player.COMMAND_SEEK_TO_PREVIOUS_WINDOW;
import static com.google.android.exoplayer2.Player.COMMAND_SEEK_TO_WINDOW;
import static com.google.android.exoplayer2.Player.COMMAND_SET_DEVICE_VOLUME;
@ -8165,6 +8166,7 @@ public final class ExoPlayerTest {
assertThat(player.isCommandAvailable(COMMAND_SEEK_TO_DEFAULT_POSITION)).isTrue();
assertThat(player.isCommandAvailable(COMMAND_SEEK_IN_CURRENT_WINDOW)).isFalse();
assertThat(player.isCommandAvailable(COMMAND_SEEK_TO_PREVIOUS_WINDOW)).isFalse();
assertThat(player.isCommandAvailable(COMMAND_SEEK_TO_PREVIOUS)).isTrue();
assertThat(player.isCommandAvailable(COMMAND_SEEK_TO_NEXT_WINDOW)).isTrue();
assertThat(player.isCommandAvailable(COMMAND_SEEK_TO_WINDOW)).isTrue();
assertThat(player.isCommandAvailable(COMMAND_SEEK_BACK)).isFalse();
@ -8214,6 +8216,7 @@ public final class ExoPlayerTest {
assertThat(player.isCommandAvailable(COMMAND_SEEK_IN_CURRENT_WINDOW)).isFalse();
assertThat(player.isCommandAvailable(COMMAND_SEEK_TO_PREVIOUS_WINDOW)).isFalse();
assertThat(player.isCommandAvailable(COMMAND_SEEK_TO_PREVIOUS)).isFalse();
assertThat(player.isCommandAvailable(COMMAND_SEEK_TO_NEXT_WINDOW)).isFalse();
assertThat(player.isCommandAvailable(COMMAND_SEEK_TO_WINDOW)).isFalse();
assertThat(player.isCommandAvailable(COMMAND_SEEK_BACK)).isFalse();
@ -8240,13 +8243,67 @@ public final class ExoPlayerTest {
assertThat(player.isCommandAvailable(COMMAND_SEEK_FORWARD)).isFalse();
}
@Test
public void isCommandAvailable_duringUnseekableLiveItem_isFalseForSeekToPrevious()
throws Exception {
Timeline timelineWithUnseekableLiveWindow =
new FakeTimeline(
new TimelineWindowDefinition(
/* periodCount= */ 1,
/* id= */ 0,
/* isSeekable= */ false,
/* isDynamic= */ true,
/* isLive= */ true,
/* isPlaceholder= */ false,
/* durationUs= */ C.TIME_UNSET,
/* defaultPositionUs= */ 10_000_000,
TimelineWindowDefinition.DEFAULT_WINDOW_OFFSET_IN_FIRST_PERIOD_US,
AdPlaybackState.NONE));
ExoPlayer player = new TestExoPlayerBuilder(context).build();
player.addMediaSource(new FakeMediaSource(timelineWithUnseekableLiveWindow));
player.prepare();
runUntilPlaybackState(player, Player.STATE_READY);
assertThat(player.isCommandAvailable(COMMAND_SEEK_TO_PREVIOUS)).isFalse();
}
@Test
public void
isCommandAvailable_duringUnseekableLiveItemWithPreviousWindow_isTrueForSeekToPrevious()
throws Exception {
Timeline timelineWithUnseekableLiveWindow =
new FakeTimeline(
new TimelineWindowDefinition(/* periodCount= */ 1, /* id= */ 0),
new TimelineWindowDefinition(
/* periodCount= */ 1,
/* id= */ 1,
/* isSeekable= */ false,
/* isDynamic= */ true,
/* isLive= */ true,
/* isPlaceholder= */ false,
/* durationUs= */ C.TIME_UNSET,
/* defaultPositionUs= */ 10_000_000,
TimelineWindowDefinition.DEFAULT_WINDOW_OFFSET_IN_FIRST_PERIOD_US,
AdPlaybackState.NONE));
ExoPlayer player = new TestExoPlayerBuilder(context).build();
player.addMediaSource(new FakeMediaSource(timelineWithUnseekableLiveWindow));
player.seekTo(/* windowIndex= */ 1, /* positionMs= */ 0);
player.prepare();
runUntilPlaybackState(player, Player.STATE_READY);
assertThat(player.isCommandAvailable(COMMAND_SEEK_TO_PREVIOUS)).isTrue();
}
@Test
public void seekTo_nextWindow_notifiesAvailableCommandsChanged() {
Player.Commands commandsWithSeekToNext = createWithDefaultCommands(COMMAND_SEEK_TO_NEXT_WINDOW);
Player.Commands commandsWithSeekToPrevious =
Player.Commands commandsWithSeekToPreviousWindow =
createWithDefaultCommands(COMMAND_SEEK_TO_PREVIOUS_WINDOW);
Player.Commands commandsWithSeekToNextAndPrevious =
createWithDefaultCommands(COMMAND_SEEK_TO_NEXT_WINDOW, COMMAND_SEEK_TO_PREVIOUS_WINDOW);
Player.Commands commandsWithSeekToNextWindow =
createWithDefaultCommands(COMMAND_SEEK_TO_NEXT_WINDOW);
Player.Commands commandsWithSeekToPreviousAndNextWindow =
createWithDefaultCommands(COMMAND_SEEK_TO_PREVIOUS_WINDOW, COMMAND_SEEK_TO_NEXT_WINDOW);
Player.Listener mockListener = mock(Player.Listener.class);
ExoPlayer player = new TestExoPlayerBuilder(context).build();
player.addListener(mockListener);
@ -8257,29 +8314,30 @@ public final class ExoPlayerTest {
new FakeMediaSource(),
new FakeMediaSource(),
new FakeMediaSource()));
verify(mockListener).onAvailableCommandsChanged(commandsWithSeekToNext);
verify(mockListener).onAvailableCommandsChanged(commandsWithSeekToNextWindow);
// Check that there were no other calls to onAvailableCommandsChanged.
verify(mockListener).onAvailableCommandsChanged(any());
player.seekTo(/* windowIndex= */ 1, /* positionMs= */ 0);
verify(mockListener).onAvailableCommandsChanged(commandsWithSeekToNextAndPrevious);
verify(mockListener).onAvailableCommandsChanged(commandsWithSeekToPreviousAndNextWindow);
verify(mockListener, times(2)).onAvailableCommandsChanged(any());
player.seekTo(/* windowIndex= */ 2, /* positionMs= */ 0);
verify(mockListener, times(2)).onAvailableCommandsChanged(any());
player.seekTo(/* windowIndex= */ 3, /* positionMs= */ 0);
verify(mockListener).onAvailableCommandsChanged(commandsWithSeekToPrevious);
verify(mockListener).onAvailableCommandsChanged(commandsWithSeekToPreviousWindow);
verify(mockListener, times(3)).onAvailableCommandsChanged(any());
}
@Test
public void seekTo_previousWindow_notifiesAvailableCommandsChanged() {
Player.Commands commandsWithSeekToNext = createWithDefaultCommands(COMMAND_SEEK_TO_NEXT_WINDOW);
Player.Commands commandsWithSeekToPrevious =
Player.Commands commandsWithSeekToPreviousWindow =
createWithDefaultCommands(COMMAND_SEEK_TO_PREVIOUS_WINDOW);
Player.Commands commandsWithSeekToNextAndPrevious =
createWithDefaultCommands(COMMAND_SEEK_TO_NEXT_WINDOW, COMMAND_SEEK_TO_PREVIOUS_WINDOW);
Player.Commands commandsWithSeekToNextWindow =
createWithDefaultCommands(COMMAND_SEEK_TO_NEXT_WINDOW);
Player.Commands commandsWithSeekToPreviousAndNextWindow =
createWithDefaultCommands(COMMAND_SEEK_TO_PREVIOUS_WINDOW, COMMAND_SEEK_TO_NEXT_WINDOW);
Player.Listener mockListener = mock(Player.Listener.class);
ExoPlayer player = new TestExoPlayerBuilder(context).build();
player.addListener(mockListener);
@ -8291,44 +8349,49 @@ public final class ExoPlayerTest {
new FakeMediaSource(),
new FakeMediaSource(),
new FakeMediaSource()));
verify(mockListener).onAvailableCommandsChanged(commandsWithSeekToPrevious);
verify(mockListener).onAvailableCommandsChanged(commandsWithSeekToPreviousWindow);
// Check that there were no other calls to onAvailableCommandsChanged.
verify(mockListener).onAvailableCommandsChanged(any());
player.seekTo(/* windowIndex= */ 2, /* positionMs= */ 0);
verify(mockListener).onAvailableCommandsChanged(commandsWithSeekToNextAndPrevious);
verify(mockListener).onAvailableCommandsChanged(commandsWithSeekToPreviousAndNextWindow);
verify(mockListener, times(2)).onAvailableCommandsChanged(any());
player.seekTo(/* windowIndex= */ 1, /* positionMs= */ 0);
verify(mockListener, times(2)).onAvailableCommandsChanged(any());
player.seekTo(/* windowIndex= */ 0, /* positionMs= */ 0);
verify(mockListener).onAvailableCommandsChanged(commandsWithSeekToNext);
verify(mockListener).onAvailableCommandsChanged(commandsWithSeekToNextWindow);
verify(mockListener, times(3)).onAvailableCommandsChanged(any());
}
@Test
public void seekTo_sameWindow_doesNotNotifyAvailableCommandsChanged() {
Player.Commands defaultCommands = createWithDefaultCommands();
Player.Listener mockListener = mock(Player.Listener.class);
ExoPlayer player = new TestExoPlayerBuilder(context).build();
player.addListener(mockListener);
player.addMediaSources(ImmutableList.of(new FakeMediaSource()));
verify(mockListener).onAvailableCommandsChanged(defaultCommands);
player.seekTo(/* windowIndex= */ 0, /* positionMs= */ 200);
player.seekTo(/* windowIndex= */ 0, /* positionMs= */ 100);
verify(mockListener, never()).onAvailableCommandsChanged(any());
// Check that there were no other calls to onAvailableCommandsChanged.
verify(mockListener).onAvailableCommandsChanged(any());
}
@Test
public void automaticWindowTransition_notifiesAvailableCommandsChanged() throws Exception {
Player.Commands commandsWithSeekToNext = createWithDefaultCommands(COMMAND_SEEK_TO_NEXT_WINDOW);
Player.Commands commandsWithSeekInCurrentAndToNext =
Player.Commands commandsWithSeekToNextWindow =
createWithDefaultCommands(COMMAND_SEEK_TO_NEXT_WINDOW);
Player.Commands commandsWithSeekInCurrentAndToNextWindow =
createWithDefaultCommands(
COMMAND_SEEK_IN_CURRENT_WINDOW,
COMMAND_SEEK_BACK,
COMMAND_SEEK_FORWARD,
COMMAND_SEEK_TO_NEXT_WINDOW);
Player.Commands commandsWithSeekInCurrentAndToPrevious =
Player.Commands commandsWithSeekInCurrentAndToPreviousWindow =
createWithDefaultCommands(
COMMAND_SEEK_IN_CURRENT_WINDOW,
COMMAND_SEEK_BACK,
@ -8351,13 +8414,13 @@ public final class ExoPlayerTest {
new FakeMediaSource(),
new FakeMediaSource(),
new FakeMediaSource()));
verify(mockListener).onAvailableCommandsChanged(commandsWithSeekToNext);
verify(mockListener).onAvailableCommandsChanged(commandsWithSeekToNextWindow);
// Check that there were no other calls to onAvailableCommandsChanged.
verify(mockListener).onAvailableCommandsChanged(any());
player.prepare();
runUntilPlaybackState(player, Player.STATE_READY);
verify(mockListener).onAvailableCommandsChanged(commandsWithSeekInCurrentAndToNext);
verify(mockListener).onAvailableCommandsChanged(commandsWithSeekInCurrentAndToNextWindow);
verify(mockListener, times(2)).onAvailableCommandsChanged(any());
playUntilStartOfWindow(player, /* windowIndex= */ 1);
@ -8371,60 +8434,67 @@ public final class ExoPlayerTest {
player.play();
runUntilPlaybackState(player, Player.STATE_ENDED);
verify(mockListener).onAvailableCommandsChanged(commandsWithSeekInCurrentAndToPrevious);
verify(mockListener).onAvailableCommandsChanged(commandsWithSeekInCurrentAndToPreviousWindow);
verify(mockListener, times(4)).onAvailableCommandsChanged(any());
}
@Test
public void addMediaSource_atTheEnd_notifiesAvailableCommandsChanged() {
Player.Commands commandsWithSeekToNext = createWithDefaultCommands(COMMAND_SEEK_TO_NEXT_WINDOW);
Player.Commands defaultCommands = createWithDefaultCommands();
Player.Commands commandsWithSeekToNextWindow =
createWithDefaultCommands(COMMAND_SEEK_TO_NEXT_WINDOW);
Player.Listener mockListener = mock(Player.Listener.class);
ExoPlayer player = new TestExoPlayerBuilder(context).build();
player.addListener(mockListener);
player.addMediaSource(new FakeMediaSource());
verify(mockListener, never()).onAvailableCommandsChanged(any());
player.addMediaSource(new FakeMediaSource());
verify(mockListener).onAvailableCommandsChanged(commandsWithSeekToNext);
verify(mockListener).onAvailableCommandsChanged(defaultCommands);
// Check that there were no other calls to onAvailableCommandsChanged.
verify(mockListener).onAvailableCommandsChanged(any());
player.addMediaSource(new FakeMediaSource());
verify(mockListener).onAvailableCommandsChanged(any());
verify(mockListener).onAvailableCommandsChanged(commandsWithSeekToNextWindow);
verify(mockListener, times(2)).onAvailableCommandsChanged(any());
player.addMediaSource(new FakeMediaSource());
verify(mockListener, times(2)).onAvailableCommandsChanged(any());
}
@Test
public void addMediaSource_atTheStart_notifiesAvailableCommandsChanged() {
Player.Commands commandsWithSeekToPrevious =
Player.Commands defaultCommands = createWithDefaultCommands();
Player.Commands commandsWithSeekToPreviousWindow =
createWithDefaultCommands(COMMAND_SEEK_TO_PREVIOUS_WINDOW);
Player.Listener mockListener = mock(Player.Listener.class);
ExoPlayer player = new TestExoPlayerBuilder(context).build();
player.addListener(mockListener);
player.addMediaSource(new FakeMediaSource());
verify(mockListener, never()).onAvailableCommandsChanged(any());
player.addMediaSource(/* index= */ 0, new FakeMediaSource());
verify(mockListener).onAvailableCommandsChanged(commandsWithSeekToPrevious);
verify(mockListener).onAvailableCommandsChanged(defaultCommands);
// Check that there were no other calls to onAvailableCommandsChanged.
verify(mockListener).onAvailableCommandsChanged(any());
player.addMediaSource(/* index= */ 0, new FakeMediaSource());
verify(mockListener).onAvailableCommandsChanged(any());
verify(mockListener).onAvailableCommandsChanged(commandsWithSeekToPreviousWindow);
verify(mockListener, times(2)).onAvailableCommandsChanged(any());
player.addMediaSource(/* index= */ 0, new FakeMediaSource());
verify(mockListener, times(2)).onAvailableCommandsChanged(any());
}
@Test
public void removeMediaItem_atTheEnd_notifiesAvailableCommandsChanged() {
Player.Commands commandsWithSeekToNext = createWithDefaultCommands(COMMAND_SEEK_TO_NEXT_WINDOW);
Player.Commands commandsWithoutSeek = createWithDefaultCommands();
Player.Commands defaultCommands = createWithDefaultCommands();
Player.Commands commandsWithSeekToNextWindow =
createWithDefaultCommands(COMMAND_SEEK_TO_NEXT_WINDOW);
Player.Commands emptyTimelineCommands = createWithDefaultCommands(/* isTimelineEmpty */ true);
Player.Listener mockListener = mock(Player.Listener.class);
ExoPlayer player = new TestExoPlayerBuilder(context).build();
player.addListener(mockListener);
player.addMediaSources(
ImmutableList.of(new FakeMediaSource(), new FakeMediaSource(), new FakeMediaSource()));
verify(mockListener).onAvailableCommandsChanged(commandsWithSeekToNext);
verify(mockListener).onAvailableCommandsChanged(commandsWithSeekToNextWindow);
// Check that there were no other calls to onAvailableCommandsChanged.
verify(mockListener).onAvailableCommandsChanged(any());
@ -8432,18 +8502,20 @@ public final class ExoPlayerTest {
verify(mockListener).onAvailableCommandsChanged(any());
player.removeMediaItem(/* index= */ 1);
verify(mockListener).onAvailableCommandsChanged(commandsWithoutSeek);
verify(mockListener).onAvailableCommandsChanged(defaultCommands);
verify(mockListener, times(2)).onAvailableCommandsChanged(any());
player.removeMediaItem(/* index= */ 0);
verify(mockListener, times(2)).onAvailableCommandsChanged(any());
verify(mockListener).onAvailableCommandsChanged(emptyTimelineCommands);
verify(mockListener, times(3)).onAvailableCommandsChanged(any());
}
@Test
public void removeMediaItem_atTheStart_notifiesAvailableCommandsChanged() {
Player.Commands commandsWithSeekToPrevious =
Player.Commands defaultCommands = createWithDefaultCommands();
Player.Commands commandsWithSeekToPreviousWindow =
createWithDefaultCommands(COMMAND_SEEK_TO_PREVIOUS_WINDOW);
Player.Commands commandsWithoutSeek = createWithDefaultCommands();
Player.Commands emptyTimelineCommands = createWithDefaultCommands(/* isTimelineEmpty */ true);
Player.Listener mockListener = mock(Player.Listener.class);
ExoPlayer player = new TestExoPlayerBuilder(context).build();
player.addListener(mockListener);
@ -8451,7 +8523,7 @@ public final class ExoPlayerTest {
player.seekTo(/* windowIndex= */ 2, /* positionMs= */ 0);
player.addMediaSources(
ImmutableList.of(new FakeMediaSource(), new FakeMediaSource(), new FakeMediaSource()));
verify(mockListener).onAvailableCommandsChanged(commandsWithSeekToPrevious);
verify(mockListener).onAvailableCommandsChanged(commandsWithSeekToPreviousWindow);
// Check that there were no other calls to onAvailableCommandsChanged.
verify(mockListener).onAvailableCommandsChanged(any());
@ -8459,64 +8531,72 @@ public final class ExoPlayerTest {
verify(mockListener).onAvailableCommandsChanged(any());
player.removeMediaItem(/* index= */ 0);
verify(mockListener).onAvailableCommandsChanged(commandsWithoutSeek);
verify(mockListener).onAvailableCommandsChanged(defaultCommands);
verify(mockListener, times(2)).onAvailableCommandsChanged(any());
player.removeMediaItem(/* index= */ 0);
verify(mockListener, times(2)).onAvailableCommandsChanged(any());
verify(mockListener).onAvailableCommandsChanged(emptyTimelineCommands);
verify(mockListener, times(3)).onAvailableCommandsChanged(any());
}
@Test
public void removeMediaItem_current_notifiesAvailableCommandsChanged() {
Player.Commands commandsWithSeekToNext = createWithDefaultCommands(COMMAND_SEEK_TO_NEXT_WINDOW);
Player.Commands commandsWithoutSeek = createWithDefaultCommands();
Player.Commands defaultCommands = createWithDefaultCommands();
Player.Commands commandsWithSeekToNextWindow =
createWithDefaultCommands(COMMAND_SEEK_TO_NEXT_WINDOW);
Player.Listener mockListener = mock(Player.Listener.class);
ExoPlayer player = new TestExoPlayerBuilder(context).build();
player.addListener(mockListener);
player.addMediaSources(ImmutableList.of(new FakeMediaSource(), new FakeMediaSource()));
verify(mockListener).onAvailableCommandsChanged(commandsWithSeekToNext);
verify(mockListener).onAvailableCommandsChanged(commandsWithSeekToNextWindow);
// Check that there were no other calls to onAvailableCommandsChanged.
verify(mockListener).onAvailableCommandsChanged(any());
player.removeMediaItem(/* index= */ 0);
verify(mockListener).onAvailableCommandsChanged(commandsWithoutSeek);
verify(mockListener).onAvailableCommandsChanged(defaultCommands);
verify(mockListener, times(2)).onAvailableCommandsChanged(any());
}
@Test
public void setRepeatMode_all_notifiesAvailableCommandsChanged() {
Player.Commands commandsWithSeekToNextAndPrevious =
createWithDefaultCommands(COMMAND_SEEK_TO_NEXT_WINDOW, COMMAND_SEEK_TO_PREVIOUS_WINDOW);
Player.Commands defaultCommands = createWithDefaultCommands();
Player.Commands commandsWithSeekToPreviousAndNextWindow =
createWithDefaultCommands(COMMAND_SEEK_TO_PREVIOUS_WINDOW, COMMAND_SEEK_TO_NEXT_WINDOW);
Player.Listener mockListener = mock(Player.Listener.class);
ExoPlayer player = new TestExoPlayerBuilder(context).build();
player.addListener(mockListener);
player.addMediaSource(new FakeMediaSource());
verify(mockListener, never()).onAvailableCommandsChanged(any());
player.setRepeatMode(Player.REPEAT_MODE_ALL);
verify(mockListener).onAvailableCommandsChanged(commandsWithSeekToNextAndPrevious);
verify(mockListener).onAvailableCommandsChanged(defaultCommands);
// Check that there were no other calls to onAvailableCommandsChanged.
verify(mockListener).onAvailableCommandsChanged(any());
player.setRepeatMode(Player.REPEAT_MODE_ALL);
verify(mockListener).onAvailableCommandsChanged(commandsWithSeekToPreviousAndNextWindow);
verify(mockListener, times(2)).onAvailableCommandsChanged(any());
}
@Test
public void setRepeatMode_one_doesNotNotifyAvailableCommandsChanged() {
Player.Commands defaultCommands = createWithDefaultCommands();
Player.Listener mockListener = mock(Player.Listener.class);
ExoPlayer player = new TestExoPlayerBuilder(context).build();
player.addListener(mockListener);
player.addMediaSource(new FakeMediaSource());
verify(mockListener).onAvailableCommandsChanged(defaultCommands);
player.setRepeatMode(Player.REPEAT_MODE_ONE);
verify(mockListener, never()).onAvailableCommandsChanged(any());
verify(mockListener).onAvailableCommandsChanged(any());
}
@Test
public void setShuffleModeEnabled_notifiesAvailableCommandsChanged() {
Player.Commands commandsWithSeekToNext = createWithDefaultCommands(COMMAND_SEEK_TO_NEXT_WINDOW);
Player.Commands commandsWithSeekToPrevious =
Player.Commands commandsWithSeekToPreviousWindow =
createWithDefaultCommands(COMMAND_SEEK_TO_PREVIOUS_WINDOW);
Player.Commands commandsWithSeekToNextWindow =
createWithDefaultCommands(COMMAND_SEEK_TO_NEXT_WINDOW);
Player.Listener mockListener = mock(Player.Listener.class);
ExoPlayer player = new TestExoPlayerBuilder(context).build();
player.addListener(mockListener);
@ -8528,12 +8608,12 @@ public final class ExoPlayerTest {
new FakeMediaSource());
player.addMediaSource(mediaSource);
verify(mockListener).onAvailableCommandsChanged(commandsWithSeekToNext);
verify(mockListener).onAvailableCommandsChanged(commandsWithSeekToNextWindow);
// Check that there were no other calls to onAvailableCommandsChanged.
verify(mockListener).onAvailableCommandsChanged(any());
player.setShuffleModeEnabled(true);
verify(mockListener).onAvailableCommandsChanged(commandsWithSeekToPrevious);
verify(mockListener).onAvailableCommandsChanged(commandsWithSeekToPreviousWindow);
verify(mockListener, times(2)).onAvailableCommandsChanged(any());
}
@ -10799,11 +10879,13 @@ public final class ExoPlayerTest {
}
private static Player.Commands createWithDefaultCommands(
@Player.Command int... additionalCommands) {
boolean isTimelineEmpty, @Player.Command int... additionalCommands) {
Player.Commands.Builder builder = new Player.Commands.Builder();
builder.addAll(
COMMAND_PLAY_PAUSE,
COMMAND_PREPARE_STOP,
COMMAND_SEEK_TO_DEFAULT_POSITION,
COMMAND_SEEK_TO_WINDOW,
COMMAND_SET_SPEED_AND_PITCH,
COMMAND_SET_SHUFFLE_MODE,
COMMAND_SET_REPEAT_MODE,
@ -10819,13 +10901,19 @@ public final class ExoPlayerTest {
COMMAND_SET_DEVICE_VOLUME,
COMMAND_ADJUST_DEVICE_VOLUME,
COMMAND_SET_VIDEO_SURFACE,
COMMAND_GET_TEXT,
COMMAND_SEEK_TO_DEFAULT_POSITION,
COMMAND_SEEK_TO_WINDOW);
COMMAND_GET_TEXT);
if (!isTimelineEmpty) {
builder.add(COMMAND_SEEK_TO_PREVIOUS);
}
builder.addAll(additionalCommands);
return builder.build();
}
private static Player.Commands createWithDefaultCommands(
@Player.Command int... additionalCommands) {
return createWithDefaultCommands(/* isTimelineEmpty= */ false, additionalCommands);
}
// Internal classes.
/** {@link FakeRenderer} that can sleep and be woken-up. */