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 51763b6dc2..065e72a1cd 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 @@ -21,6 +21,7 @@ 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_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_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_REPEAT_MODE; @@ -31,6 +32,7 @@ import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.ArgumentMatchers.anyLong; import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.mock; import static org.mockito.Mockito.never; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; @@ -55,9 +57,9 @@ import com.google.android.gms.cast.framework.media.MediaQueue; import com.google.android.gms.cast.framework.media.RemoteMediaClient; import com.google.android.gms.common.api.PendingResult; import com.google.android.gms.common.api.ResultCallback; -import com.google.android.gms.common.api.Status; import com.google.common.collect.ImmutableList; import java.util.ArrayList; +import java.util.Arrays; import java.util.Collections; import java.util.List; import org.junit.Before; @@ -66,32 +68,26 @@ import org.junit.runner.RunWith; import org.mockito.ArgumentCaptor; import org.mockito.Captor; import org.mockito.Mock; -import org.mockito.Mockito; /** Tests for {@link CastPlayer}. */ @RunWith(AndroidJUnit4.class) public class CastPlayerTest { private CastPlayer castPlayer; - private RemoteMediaClient.Callback remoteMediaClientCallback; @Mock private RemoteMediaClient mockRemoteMediaClient; @Mock private MediaStatus mockMediaStatus; - @Mock private MediaInfo mockMediaInfo; @Mock private MediaQueue mockMediaQueue; - @Mock private MediaQueueItem mockMediaQueueItem; @Mock private CastContext mockCastContext; @Mock private SessionManager mockSessionManager; @Mock private CastSession mockCastSession; @Mock private Player.EventListener mockListener; @Mock private PendingResult mockPendingResult; - @Mock private RemoteMediaClient.MediaChannelResult mediaChannelResultMock; @Captor private ArgumentCaptor> setResultCallbackArgumentCaptor; - @Captor private ArgumentCaptor callbackArgumentCaptor; @Captor private ArgumentCaptor queueItemsArgumentCaptor; @Captor private ArgumentCaptor mediaItemCaptor; @@ -106,8 +102,6 @@ public class CastPlayerTest { when(mockRemoteMediaClient.getMediaStatus()).thenReturn(mockMediaStatus); when(mockRemoteMediaClient.getMediaQueue()).thenReturn(mockMediaQueue); when(mockMediaQueue.getItemIds()).thenReturn(new int[0]); - when(mockRemoteMediaClient.getCurrentItem()).thenReturn(mockMediaQueueItem); - when(mediaChannelResultMock.getStatus()).thenReturn(Status.RESULT_SUCCESS); // Make the remote media client present the same default values as ExoPlayer: when(mockRemoteMediaClient.isPaused()).thenReturn(true); when(mockMediaStatus.getQueueRepeatMode()).thenReturn(MediaStatus.REPEAT_MODE_REPEAT_OFF); @@ -138,7 +132,7 @@ public class CastPlayerTest { when(mockRemoteMediaClient.isPaused()).thenReturn(false); setResultCallbackArgumentCaptor .getValue() - .onResult(Mockito.mock(RemoteMediaClient.MediaChannelResult.class)); + .onResult(mock(RemoteMediaClient.MediaChannelResult.class)); verifyNoMoreInteractions(mockListener); } @@ -158,7 +152,7 @@ public class CastPlayerTest { // Upon result, the remote media client is still paused. The state should reflect that. setResultCallbackArgumentCaptor .getValue() - .onResult(Mockito.mock(RemoteMediaClient.MediaChannelResult.class)); + .onResult(mock(RemoteMediaClient.MediaChannelResult.class)); verify(mockListener).onPlayerStateChanged(false, Player.STATE_IDLE); verify(mockListener).onPlayWhenReadyChanged(false, Player.PLAY_WHEN_READY_CHANGE_REASON_REMOTE); assertThat(castPlayer.getPlayWhenReady()).isFalse(); @@ -212,7 +206,7 @@ public class CastPlayerTest { when(mockMediaStatus.getQueueRepeatMode()).thenReturn(MediaStatus.REPEAT_MODE_REPEAT_SINGLE); setResultCallbackArgumentCaptor .getValue() - .onResult(Mockito.mock(RemoteMediaClient.MediaChannelResult.class)); + .onResult(mock(RemoteMediaClient.MediaChannelResult.class)); verifyNoMoreInteractions(mockListener); } @@ -233,7 +227,7 @@ public class CastPlayerTest { // Upon result, the repeat mode is ALL. The state should reflect that. setResultCallbackArgumentCaptor .getValue() - .onResult(Mockito.mock(RemoteMediaClient.MediaChannelResult.class)); + .onResult(mock(RemoteMediaClient.MediaChannelResult.class)); verify(mockListener).onRepeatModeChanged(Player.REPEAT_MODE_ALL); assertThat(castPlayer.getRepeatMode()).isEqualTo(Player.REPEAT_MODE_ALL); } @@ -599,6 +593,7 @@ public class CastPlayerTest { assertThat(castPlayer.isCommandAvailable(COMMAND_PLAY_PAUSE)).isTrue(); assertThat(castPlayer.isCommandAvailable(COMMAND_PREPARE_STOP_RELEASE)).isTrue(); + assertThat(castPlayer.isCommandAvailable(COMMAND_SEEK_IN_CURRENT_MEDIA_ITEM)).isTrue(); assertThat(castPlayer.isCommandAvailable(COMMAND_SEEK_TO_NEXT_MEDIA_ITEM)).isTrue(); assertThat(castPlayer.isCommandAvailable(COMMAND_SEEK_TO_PREVIOUS_MEDIA_ITEM)).isFalse(); assertThat(castPlayer.isCommandAvailable(COMMAND_SET_SPEED_AND_PITCH)).isFalse(); @@ -610,32 +605,51 @@ public class CastPlayerTest { assertThat(castPlayer.isCommandAvailable(COMMAND_CHANGE_MEDIA_ITEMS)).isTrue(); } + @Test + public void isCommandAvailable_duringUnseekableItem_isFalseForSeekInCurrent() { + MediaItem mediaItem = createMediaItem(/* mediaQueueItemId= */ 1); + List 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); + + assertThat(castPlayer.isCommandAvailable(COMMAND_SEEK_IN_CURRENT_MEDIA_ITEM)).isFalse(); + } + @Test public void seekTo_nextWindow_notifiesAvailableCommandsChanged() { when(mockRemoteMediaClient.queueJumpToItem(anyInt(), anyLong(), eq(null))) .thenReturn(mockPendingResult); - Player.Commands commandsWithSeekToNext = createCommands(COMMAND_SEEK_TO_NEXT_MEDIA_ITEM); - Player.Commands commandsWithSeekToPrevious = - createCommands(COMMAND_SEEK_TO_PREVIOUS_MEDIA_ITEM); - Player.Commands commandsWithSeekToNextAndPrevious = - createCommands(COMMAND_SEEK_TO_NEXT_MEDIA_ITEM, COMMAND_SEEK_TO_PREVIOUS_MEDIA_ITEM); + Player.Commands commandsWithSeekInCurrentAndToNext = + createCommands(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); + Player.Commands commandsWithSeekAnywhere = + createCommands( + COMMAND_SEEK_IN_CURRENT_MEDIA_ITEM, + COMMAND_SEEK_TO_NEXT_MEDIA_ITEM, + COMMAND_SEEK_TO_PREVIOUS_MEDIA_ITEM); int[] mediaQueueItemIds = new int[] {1, 2, 3, 4}; List mediaItems = createMediaItems(mediaQueueItemIds); castPlayer.addMediaItems(mediaItems); updateTimeLine(mediaItems, mediaQueueItemIds, /* currentItemId= */ 1); - verify(mockListener).onAvailableCommandsChanged(commandsWithSeekToNext); + verify(mockListener).onAvailableCommandsChanged(commandsWithSeekInCurrentAndToNext); + // Check that there were no other calls to onAvailableCommandsChanged. verify(mockListener).onAvailableCommandsChanged(any()); castPlayer.seekTo(/* windowIndex= */ 1, /* positionMs= */ 0); - verify(mockListener).onAvailableCommandsChanged(commandsWithSeekToNextAndPrevious); + verify(mockListener).onAvailableCommandsChanged(commandsWithSeekAnywhere); 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(commandsWithSeekToPrevious); + verify(mockListener).onAvailableCommandsChanged(commandsWithSeekInCurrentAndToPrevious); verify(mockListener, times(3)).onAvailableCommandsChanged(any()); } @@ -643,28 +657,33 @@ public class CastPlayerTest { public void seekTo_previousWindow_notifiesAvailableCommandsChanged() { when(mockRemoteMediaClient.queueJumpToItem(anyInt(), anyLong(), eq(null))) .thenReturn(mockPendingResult); - Player.Commands commandsWithSeekToNext = createCommands(COMMAND_SEEK_TO_NEXT_MEDIA_ITEM); - Player.Commands commandsWithSeekToPrevious = - createCommands(COMMAND_SEEK_TO_PREVIOUS_MEDIA_ITEM); - Player.Commands commandsWithSeekToNextAndPrevious = - createCommands(COMMAND_SEEK_TO_NEXT_MEDIA_ITEM, COMMAND_SEEK_TO_PREVIOUS_MEDIA_ITEM); + Player.Commands commandsWithSeekInCurrentAndToNext = + createCommands(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); + Player.Commands commandsWithSeekAnywhere = + createCommands( + COMMAND_SEEK_IN_CURRENT_MEDIA_ITEM, + COMMAND_SEEK_TO_NEXT_MEDIA_ITEM, + COMMAND_SEEK_TO_PREVIOUS_MEDIA_ITEM); int[] mediaQueueItemIds = new int[] {1, 2, 3, 4}; List mediaItems = createMediaItems(mediaQueueItemIds); castPlayer.addMediaItems(mediaItems); updateTimeLine(mediaItems, mediaQueueItemIds, /* currentItemId= */ 4); - verify(mockListener).onAvailableCommandsChanged(commandsWithSeekToPrevious); + verify(mockListener).onAvailableCommandsChanged(commandsWithSeekInCurrentAndToPrevious); + // Check that there were no other calls to onAvailableCommandsChanged. verify(mockListener).onAvailableCommandsChanged(any()); castPlayer.seekTo(/* windowIndex= */ 2, /* positionMs= */ 0); - verify(mockListener).onAvailableCommandsChanged(commandsWithSeekToNextAndPrevious); + verify(mockListener).onAvailableCommandsChanged(commandsWithSeekAnywhere); 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(commandsWithSeekToNext); + verify(mockListener).onAvailableCommandsChanged(commandsWithSeekInCurrentAndToNext); verify(mockListener, times(3)).onAvailableCommandsChanged(any()); } @@ -672,19 +691,25 @@ 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); int[] mediaQueueItemIds = new int[] {1}; List mediaItems = createMediaItems(mediaQueueItemIds); castPlayer.addMediaItems(mediaItems); updateTimeLine(mediaItems, mediaQueueItemIds, /* currentItemId= */ 1); + verify(mockListener).onAvailableCommandsChanged(commandsWithSeekInCurrent); + castPlayer.seekTo(/* windowIndex= */ 0, /* positionMs= */ 200); castPlayer.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 addMediaItem_atTheEnd_notifiesAvailableCommandsChanged() { - Player.Commands commandsWithSeekToNext = createCommands(COMMAND_SEEK_TO_NEXT_MEDIA_ITEM); + Player.Commands commandsWithSeekInCurrent = createCommands(COMMAND_SEEK_IN_CURRENT_MEDIA_ITEM); + Player.Commands commandsWithSeekInCurrentAndToNext = + createCommands(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); @@ -694,28 +719,31 @@ public class CastPlayerTest { ImmutableList.of(mediaItem1), /* mediaQueueItemIds= */ new int[] {1}, /* currentItemId= */ 1); - verify(mockListener, never()).onAvailableCommandsChanged(any()); + verify(mockListener).onAvailableCommandsChanged(commandsWithSeekInCurrent); + // Check that there were no other calls to onAvailableCommandsChanged. + verify(mockListener).onAvailableCommandsChanged(any()); castPlayer.addMediaItem(mediaItem2); updateTimeLine( ImmutableList.of(mediaItem1, mediaItem2), /* mediaQueueItemIds= */ new int[] {1, 2}, /* currentItemId= */ 1); - verify(mockListener).onAvailableCommandsChanged(commandsWithSeekToNext); - verify(mockListener).onAvailableCommandsChanged(any()); + verify(mockListener).onAvailableCommandsChanged(commandsWithSeekInCurrentAndToNext); + verify(mockListener, times(2)).onAvailableCommandsChanged(any()); castPlayer.addMediaItem(mediaItem3); updateTimeLine( ImmutableList.of(mediaItem1, mediaItem2, mediaItem3), /* mediaQueueItemIds= */ new int[] {1, 2, 3}, /* currentItemId= */ 1); - verify(mockListener).onAvailableCommandsChanged(any()); + verify(mockListener, times(2)).onAvailableCommandsChanged(any()); } @Test public void addMediaItem_atTheStart_notifiesAvailableCommandsChanged() { - Player.Commands commandsWithSeekToPrevious = - createCommands(COMMAND_SEEK_TO_PREVIOUS_MEDIA_ITEM); + Player.Commands commandsWithSeekInCurrent = createCommands(COMMAND_SEEK_IN_CURRENT_MEDIA_ITEM); + Player.Commands commandsWithSeekInCurrentAndToPrevious = + createCommands(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); @@ -725,28 +753,32 @@ public class CastPlayerTest { ImmutableList.of(mediaItem1), /* mediaQueueItemIds= */ new int[] {1}, /* currentItemId= */ 1); - verify(mockListener, never()).onAvailableCommandsChanged(any()); + verify(mockListener).onAvailableCommandsChanged(commandsWithSeekInCurrent); + // Check that there were no other calls to onAvailableCommandsChanged. + verify(mockListener).onAvailableCommandsChanged(any()); castPlayer.addMediaItem(/* index= */ 0, mediaItem2); updateTimeLine( ImmutableList.of(mediaItem2, mediaItem1), /* mediaQueueItemIds= */ new int[] {2, 1}, /* currentItemId= */ 1); - verify(mockListener).onAvailableCommandsChanged(commandsWithSeekToPrevious); - verify(mockListener).onAvailableCommandsChanged(any()); + verify(mockListener).onAvailableCommandsChanged(commandsWithSeekInCurrentAndToPrevious); + verify(mockListener, times(2)).onAvailableCommandsChanged(any()); castPlayer.addMediaItem(/* index= */ 0, mediaItem3); updateTimeLine( ImmutableList.of(mediaItem3, mediaItem2, mediaItem1), /* mediaQueueItemIds= */ new int[] {3, 2, 1}, /* currentItemId= */ 1); - verify(mockListener).onAvailableCommandsChanged(any()); + verify(mockListener, times(2)).onAvailableCommandsChanged(any()); } @Test public void removeMediaItem_atTheEnd_notifiesAvailableCommandsChanged() { - Player.Commands commandsWithSeekToNext = createCommands(COMMAND_SEEK_TO_NEXT_MEDIA_ITEM); Player.Commands commandsWithoutSeek = createCommands(); + Player.Commands commandsWithSeekInCurrent = createCommands(COMMAND_SEEK_IN_CURRENT_MEDIA_ITEM); + Player.Commands commandsWithSeekInCurrentAndToNext = + createCommands(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); @@ -756,7 +788,8 @@ public class CastPlayerTest { ImmutableList.of(mediaItem1, mediaItem2, mediaItem3), /* mediaQueueItemIds= */ new int[] {1, 2, 3}, /* currentItemId= */ 1); - verify(mockListener).onAvailableCommandsChanged(commandsWithSeekToNext); + verify(mockListener).onAvailableCommandsChanged(commandsWithSeekInCurrentAndToNext); + // Check that there were no other calls to onAvailableCommandsChanged. verify(mockListener).onAvailableCommandsChanged(any()); castPlayer.removeMediaItem(/* index= */ 2); @@ -771,7 +804,7 @@ public class CastPlayerTest { ImmutableList.of(mediaItem1), /* mediaQueueItemIds= */ new int[] {1}, /* currentItemId= */ 1); - verify(mockListener).onAvailableCommandsChanged(commandsWithoutSeek); + verify(mockListener).onAvailableCommandsChanged(commandsWithSeekInCurrent); verify(mockListener, times(2)).onAvailableCommandsChanged(any()); castPlayer.removeMediaItem(/* index= */ 0); @@ -779,16 +812,18 @@ public class CastPlayerTest { ImmutableList.of(), /* mediaQueueItemIds= */ new int[0], /* currentItemId= */ C.INDEX_UNSET); - verify(mockListener, times(2)).onAvailableCommandsChanged(any()); + verify(mockListener).onAvailableCommandsChanged(commandsWithoutSeek); + verify(mockListener, times(3)).onAvailableCommandsChanged(any()); } @Test public void removeMediaItem_atTheStart_notifiesAvailableCommandsChanged() { when(mockRemoteMediaClient.queueJumpToItem(anyInt(), anyLong(), eq(null))) .thenReturn(mockPendingResult); - Player.Commands commandsWithSeekToPrevious = - createCommands(COMMAND_SEEK_TO_PREVIOUS_MEDIA_ITEM); Player.Commands commandsWithoutSeek = createCommands(); + Player.Commands commandsWithSeekInCurrent = createCommands(COMMAND_SEEK_IN_CURRENT_MEDIA_ITEM); + Player.Commands commandsWithSeekInCurrentAndToPrevious = + createCommands(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); @@ -798,7 +833,8 @@ public class CastPlayerTest { ImmutableList.of(mediaItem1, mediaItem2, mediaItem3), /* mediaQueueItemIds= */ new int[] {1, 2, 3}, /* currentItemId= */ 3); - verify(mockListener).onAvailableCommandsChanged(commandsWithSeekToPrevious); + verify(mockListener).onAvailableCommandsChanged(commandsWithSeekInCurrentAndToPrevious); + // Check that there were no other calls to onAvailableCommandsChanged. verify(mockListener).onAvailableCommandsChanged(any()); castPlayer.removeMediaItem(/* index= */ 0); @@ -813,7 +849,7 @@ public class CastPlayerTest { ImmutableList.of(mediaItem3), /* mediaQueueItemIds= */ new int[] {3}, /* currentItemId= */ 3); - verify(mockListener).onAvailableCommandsChanged(commandsWithoutSeek); + verify(mockListener).onAvailableCommandsChanged(commandsWithSeekInCurrent); verify(mockListener, times(2)).onAvailableCommandsChanged(any()); castPlayer.removeMediaItem(/* index= */ 0); @@ -821,13 +857,15 @@ public class CastPlayerTest { ImmutableList.of(), /* mediaQueueItemIds= */ new int[0], /* currentItemId= */ C.INDEX_UNSET); - verify(mockListener, times(2)).onAvailableCommandsChanged(any()); + verify(mockListener).onAvailableCommandsChanged(commandsWithoutSeek); + verify(mockListener, times(3)).onAvailableCommandsChanged(any()); } @Test public void removeMediaItem_current_notifiesAvailableCommandsChanged() { - Player.Commands commandsWithSeekToNext = createCommands(COMMAND_SEEK_TO_NEXT_MEDIA_ITEM); - Player.Commands commandsWithoutSeek = createCommands(); + Player.Commands commandsWithSeekInCurrent = createCommands(COMMAND_SEEK_IN_CURRENT_MEDIA_ITEM); + Player.Commands commandsWithSeekInCurrentAndToNext = + createCommands(COMMAND_SEEK_IN_CURRENT_MEDIA_ITEM, COMMAND_SEEK_TO_NEXT_MEDIA_ITEM); MediaItem mediaItem1 = createMediaItem(/* mediaQueueItemId= */ 1); MediaItem mediaItem2 = createMediaItem(/* mediaQueueItemId= */ 2); @@ -836,7 +874,8 @@ public class CastPlayerTest { ImmutableList.of(mediaItem1, mediaItem2), /* mediaQueueItemIds= */ new int[] {1, 2}, /* currentItemId= */ 1); - verify(mockListener).onAvailableCommandsChanged(commandsWithSeekToNext); + verify(mockListener).onAvailableCommandsChanged(commandsWithSeekInCurrentAndToNext); + // Check that there were no other calls to onAvailableCommandsChanged. verify(mockListener).onAvailableCommandsChanged(any()); castPlayer.removeMediaItem(/* index= */ 0); @@ -844,7 +883,7 @@ public class CastPlayerTest { ImmutableList.of(mediaItem2), /* mediaQueueItemIds= */ new int[] {2}, /* currentItemId= */ 2); - verify(mockListener).onAvailableCommandsChanged(commandsWithoutSeek); + verify(mockListener).onAvailableCommandsChanged(commandsWithSeekInCurrent); verify(mockListener, times(2)).onAvailableCommandsChanged(any()); } @@ -852,31 +891,42 @@ public class CastPlayerTest { public void setRepeatMode_all_notifiesAvailableCommandsChanged() { when(mockRemoteMediaClient.queueSetRepeatMode(anyInt(), eq(null))) .thenReturn(mockPendingResult); - Player.Commands commandsWithSeekToNextAndPrevious = - createCommands(COMMAND_SEEK_TO_NEXT_MEDIA_ITEM, COMMAND_SEEK_TO_PREVIOUS_MEDIA_ITEM); + Player.Commands commandsWithSeekInCurrent = createCommands(COMMAND_SEEK_IN_CURRENT_MEDIA_ITEM); + Player.Commands commandsWithSeekAnywhere = + createCommands( + COMMAND_SEEK_IN_CURRENT_MEDIA_ITEM, + COMMAND_SEEK_TO_NEXT_MEDIA_ITEM, + COMMAND_SEEK_TO_PREVIOUS_MEDIA_ITEM); int[] mediaQueueItemIds = new int[] {1}; List mediaItems = createMediaItems(mediaQueueItemIds); castPlayer.addMediaItems(mediaItems); updateTimeLine(mediaItems, mediaQueueItemIds, /* currentItemId= */ 1); - verify(mockListener, never()).onAvailableCommandsChanged(any()); + verify(mockListener).onAvailableCommandsChanged(commandsWithSeekInCurrent); + // Check that there were no other calls to onAvailableCommandsChanged. + verify(mockListener).onAvailableCommandsChanged(any()); castPlayer.setRepeatMode(Player.REPEAT_MODE_ALL); - verify(mockListener).onAvailableCommandsChanged(commandsWithSeekToNextAndPrevious); - verify(mockListener).onAvailableCommandsChanged(any()); + verify(mockListener).onAvailableCommandsChanged(commandsWithSeekAnywhere); + verify(mockListener, times(2)).onAvailableCommandsChanged(any()); } @Test public void setRepeatMode_one_doesNotNotifyAvailableCommandsChanged() { when(mockRemoteMediaClient.queueSetRepeatMode(anyInt(), eq(null))) .thenReturn(mockPendingResult); + Player.Commands commandsWithSeekInCurrent = createCommands(COMMAND_SEEK_IN_CURRENT_MEDIA_ITEM); int[] mediaQueueItemIds = new int[] {1}; List mediaItems = createMediaItems(mediaQueueItemIds); castPlayer.addMediaItems(mediaItems); updateTimeLine(mediaItems, mediaQueueItemIds, /* currentItemId= */ 1); + verify(mockListener).onAvailableCommandsChanged(commandsWithSeekInCurrent); + // Check that there were no other calls to onAvailableCommandsChanged. + verify(mockListener).onAvailableCommandsChanged(any()); + castPlayer.setRepeatMode(Player.REPEAT_MODE_ONE); - verify(mockListener, never()).onAvailableCommandsChanged(any()); + verify(mockListener).onAvailableCommandsChanged(any()); } private int[] createMediaQueueItemIds(int numberOfIds) { @@ -911,21 +961,46 @@ public class CastPlayerTest { private void updateTimeLine( List mediaItems, int[] mediaQueueItemIds, int currentItemId) { - List queueItems = new ArrayList<>(); - DefaultMediaItemConverter converter = new DefaultMediaItemConverter(); - for (MediaItem mediaItem : mediaItems) { - queueItems.add(converter.toMediaQueueItem(mediaItem)); - } + int[] streamTypes = new int[mediaItems.size()]; + Arrays.fill(streamTypes, MediaInfo.STREAM_TYPE_BUFFERED); + long[] durationsMs = new long[mediaItems.size()]; + updateTimeLine(mediaItems, mediaQueueItemIds, currentItemId, streamTypes, durationsMs); + } + private void updateTimeLine( + List mediaItems, + int[] mediaQueueItemIds, + int currentItemId, + int[] streamTypes, + long[] durationsMs) { // Set up mocks to allow the player to update the timeline. - when(mockMediaQueue.getItemIds()).thenReturn(mediaQueueItemIds); - if (currentItemId != C.INDEX_UNSET) { - when(mockMediaQueueItem.getItemId()).thenReturn(currentItemId); - when(mockMediaStatus.getCurrentItemId()).thenReturn(currentItemId); + List queueItems = new ArrayList<>(); + for (int i = 0; i < mediaQueueItemIds.length; i++) { + MediaItem mediaItem = mediaItems.get(i); + int mediaQueueItemId = mediaQueueItemIds[i]; + int streamType = streamTypes[i]; + long durationMs = durationsMs[i]; + MediaInfo.Builder mediaInfoBuilder = + new MediaInfo.Builder(mediaItem.playbackProperties.uri.toString()) + .setStreamType(streamType) + .setContentType(mediaItem.playbackProperties.mimeType); + if (durationMs != C.TIME_UNSET) { + mediaInfoBuilder.setStreamDuration(durationMs); + } + MediaInfo mediaInfo = mediaInfoBuilder.build(); + MediaQueueItem mediaQueueItem = mock(MediaQueueItem.class); + when(mediaQueueItem.getItemId()).thenReturn(mediaQueueItemId); + when(mediaQueueItem.getMedia()).thenReturn(mediaInfo); + queueItems.add(mediaQueueItem); + if (mediaQueueItemId == currentItemId) { + when(mockRemoteMediaClient.getCurrentItem()).thenReturn(mediaQueueItem); + when(mockMediaStatus.getMediaInfo()).thenReturn(mediaInfo); + } } - when(mockMediaStatus.getMediaInfo()).thenReturn(mockMediaInfo); - when(mockMediaInfo.getStreamType()).thenReturn(MediaInfo.STREAM_TYPE_NONE); + when(mockMediaQueue.getItemIds()).thenReturn(mediaQueueItemIds); when(mockMediaStatus.getQueueItems()).thenReturn(queueItems); + when(mockMediaStatus.getCurrentItemId()) + .thenReturn(currentItemId == C.INDEX_UNSET ? 0 : currentItemId); // Call listener to update the timeline of the player. remoteMediaClientCallback.onQueueStatusUpdated(); diff --git a/extensions/mediasession/src/main/java/com/google/android/exoplayer2/ext/mediasession/TimelineQueueNavigator.java b/extensions/mediasession/src/main/java/com/google/android/exoplayer2/ext/mediasession/TimelineQueueNavigator.java index 58c4e1e9cc..4a27ca9d93 100644 --- a/extensions/mediasession/src/main/java/com/google/android/exoplayer2/ext/mediasession/TimelineQueueNavigator.java +++ b/extensions/mediasession/src/main/java/com/google/android/exoplayer2/ext/mediasession/TimelineQueueNavigator.java @@ -15,6 +15,9 @@ */ package com.google.android.exoplayer2.ext.mediasession; +import static com.google.android.exoplayer2.Player.COMMAND_SEEK_IN_CURRENT_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 java.lang.Math.min; import android.os.Bundle; @@ -99,12 +102,12 @@ public abstract class TimelineQueueNavigator implements MediaSessionConnector.Qu timeline.getWindow(player.getCurrentWindowIndex(), window); enableSkipTo = timeline.getWindowCount() > 1; enablePrevious = - window.isSeekable + player.isCommandAvailable(COMMAND_SEEK_IN_CURRENT_MEDIA_ITEM) || !window.isLive() - || player.isCommandAvailable(Player.COMMAND_SEEK_TO_PREVIOUS_MEDIA_ITEM); + || player.isCommandAvailable(COMMAND_SEEK_TO_PREVIOUS_MEDIA_ITEM); enableNext = (window.isLive() && window.isDynamic) - || player.isCommandAvailable(Player.COMMAND_SEEK_TO_NEXT_MEDIA_ITEM); + || player.isCommandAvailable(COMMAND_SEEK_TO_NEXT_MEDIA_ITEM); } long actions = 0; 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 b9b6795de4..e43cebfe34 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 @@ -323,6 +323,7 @@ public abstract class BasePlayer implements Player { protected Commands getAvailableCommands(@Command int[] permanentAvailableCommands) { return new Commands.Builder() .addAll(permanentAvailableCommands) + .addIf(COMMAND_SEEK_IN_CURRENT_MEDIA_ITEM, isCurrentWindowSeekable() && !isPlayingAd()) .addIf(COMMAND_SEEK_TO_NEXT_MEDIA_ITEM, hasNext() && !isPlayingAd()) .addIf(COMMAND_SEEK_TO_PREVIOUS_MEDIA_ITEM, hasPrevious() && !isPlayingAd()) .build(); 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 1d6df32184..7d420e3305 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 @@ -1029,43 +1029,54 @@ public interface Player { /** * 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}, {@link - * #COMMAND_SEEK_TO_PREVIOUS_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_PREPARE_STOP_RELEASE}, {@link #COMMAND_SEEK_IN_CURRENT_MEDIA_ITEM}, {@link + * #COMMAND_SEEK_TO_NEXT_MEDIA_ITEM}, {@link #COMMAND_SEEK_TO_PREVIOUS_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}. */ @Documented @Retention(RetentionPolicy.SOURCE) @IntDef({ COMMAND_PLAY_PAUSE, COMMAND_PREPARE_STOP_RELEASE, + COMMAND_SEEK_IN_CURRENT_MEDIA_ITEM, COMMAND_SEEK_TO_NEXT_MEDIA_ITEM, - COMMAND_SEEK_TO_PREVIOUS_MEDIA_ITEM + COMMAND_SEEK_TO_PREVIOUS_MEDIA_ITEM, + 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 }) @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 into the current {@link MediaItem}. */ + int COMMAND_SEEK_IN_CURRENT_MEDIA_ITEM = 3; /** Command to seek to the next {@link MediaItem} in the playlist. */ - int COMMAND_SEEK_TO_NEXT_MEDIA_ITEM = 3; + int COMMAND_SEEK_TO_NEXT_MEDIA_ITEM = 4; /** Command to seek to the previous {@link MediaItem} in the playlist. */ - int COMMAND_SEEK_TO_PREVIOUS_MEDIA_ITEM = 4; + int COMMAND_SEEK_TO_PREVIOUS_MEDIA_ITEM = 5; /** Command to set the playback speed and pitch. */ - int COMMAND_SET_SPEED_AND_PITCH = 5; + int COMMAND_SET_SPEED_AND_PITCH = 6; /** Command to enable shuffling. */ - int COMMAND_SET_SHUFFLE_MODE = 6; + int COMMAND_SET_SHUFFLE_MODE = 7; /** Command to set the repeat mode. */ - int COMMAND_SET_REPEAT_MODE = 7; + int COMMAND_SET_REPEAT_MODE = 8; /** Command to get the current {@link MediaItem}. */ - int COMMAND_GET_CURRENT_MEDIA_ITEM = 8; + int COMMAND_GET_CURRENT_MEDIA_ITEM = 9; /** Command to get the {@link MediaItem MediaItems} in the playlist. */ - int COMMAND_GET_MEDIA_ITEMS = 9; + int COMMAND_GET_MEDIA_ITEMS = 10; /** Command to get the {@link MediaItem MediaItems} metadata. */ - int COMMAND_GET_MEDIA_ITEMS_METADATA = 10; + int COMMAND_GET_MEDIA_ITEMS_METADATA = 11; /** Command to change the {@link MediaItem MediaItems} in the playlist. */ - int COMMAND_CHANGE_MEDIA_ITEMS = 11; + int COMMAND_CHANGE_MEDIA_ITEMS = 12; /** Returns the component of this player for audio output, or null if audio is not supported. */ @Nullable 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 732db4c3e9..962e17b45d 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 @@ -21,6 +21,7 @@ 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_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_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_REPEAT_MODE; @@ -8087,6 +8088,7 @@ public final class ExoPlayerTest { assertThat(player.isCommandAvailable(COMMAND_PLAY_PAUSE)).isTrue(); assertThat(player.isCommandAvailable(COMMAND_PREPARE_STOP_RELEASE)).isTrue(); + assertThat(player.isCommandAvailable(COMMAND_SEEK_IN_CURRENT_MEDIA_ITEM)).isFalse(); assertThat(player.isCommandAvailable(COMMAND_SEEK_TO_NEXT_MEDIA_ITEM)).isTrue(); assertThat(player.isCommandAvailable(COMMAND_SEEK_TO_PREVIOUS_MEDIA_ITEM)).isFalse(); assertThat(player.isCommandAvailable(COMMAND_SET_SPEED_AND_PITCH)).isTrue(); @@ -8099,7 +8101,7 @@ public final class ExoPlayerTest { } @Test - public void isCommandAvailable_whenPlayingAd_isFalseForSeekCommands() throws Exception { + public void isCommandAvailable_duringAd_isFalseForSeekCommands() throws Exception { AdPlaybackState adPlaybackState = new AdPlaybackState(/* adsId= */ new Object(), /* adGroupTimesUs...= */ 0) .withAdCount(/* adGroupIndex= */ 0, /* adCount= */ 1) @@ -8123,10 +8125,28 @@ public final class ExoPlayerTest { player.prepare(); runUntilPlaybackState(player, Player.STATE_READY); + assertThat(player.isCommandAvailable(COMMAND_SEEK_IN_CURRENT_MEDIA_ITEM)).isFalse(); assertThat(player.isCommandAvailable(COMMAND_SEEK_TO_NEXT_MEDIA_ITEM)).isFalse(); assertThat(player.isCommandAvailable(COMMAND_SEEK_TO_PREVIOUS_MEDIA_ITEM)).isFalse(); } + @Test + public void isCommandAvailable_duringUnseekableItem_isFalseForSeekInCurrent() throws Exception { + Timeline timelineWithUnseekableWindow = + new FakeTimeline( + new TimelineWindowDefinition( + /* isSeekable= */ false, + /* isDynamic= */ false, + /* durationUs= */ C.msToUs(10_000))); + ExoPlayer player = new TestExoPlayerBuilder(context).build(); + + player.addMediaSource(new FakeMediaSource(timelineWithUnseekableWindow)); + player.prepare(); + runUntilPlaybackState(player, Player.STATE_READY); + + assertThat(player.isCommandAvailable(COMMAND_SEEK_IN_CURRENT_MEDIA_ITEM)).isFalse(); + } + @Test public void seekTo_nextWindow_notifiesAvailableCommandsChanged() { Player.Commands commandsWithSeekToNext = createCommands(COMMAND_SEEK_TO_NEXT_MEDIA_ITEM); @@ -8145,6 +8165,7 @@ public final class ExoPlayerTest { new FakeMediaSource(), new FakeMediaSource())); verify(mockListener).onAvailableCommandsChanged(commandsWithSeekToNext); + // Check that there were no other calls to onAvailableCommandsChanged. verify(mockListener).onAvailableCommandsChanged(any()); player.seekTo(/* windowIndex= */ 1, /* positionMs= */ 0); @@ -8178,6 +8199,7 @@ public final class ExoPlayerTest { new FakeMediaSource(), new FakeMediaSource())); verify(mockListener).onAvailableCommandsChanged(commandsWithSeekToPrevious); + // Check that there were no other calls to onAvailableCommandsChanged. verify(mockListener).onAvailableCommandsChanged(any()); player.seekTo(/* windowIndex= */ 2, /* positionMs= */ 0); @@ -8207,10 +8229,15 @@ public final class ExoPlayerTest { @Test public void automaticWindowTransition_notifiesAvailableCommandsChanged() throws Exception { Player.Commands commandsWithSeekToNext = createCommands(COMMAND_SEEK_TO_NEXT_MEDIA_ITEM); - Player.Commands commandsWithSeekToPrevious = - createCommands(COMMAND_SEEK_TO_PREVIOUS_MEDIA_ITEM); - Player.Commands commandsWithSeekToNextAndPrevious = - createCommands(COMMAND_SEEK_TO_NEXT_MEDIA_ITEM, COMMAND_SEEK_TO_PREVIOUS_MEDIA_ITEM); + Player.Commands commandsWithSeekInCurrentAndToNext = + createCommands(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); + Player.Commands commandsWithSeekAnywhere = + createCommands( + COMMAND_SEEK_IN_CURRENT_MEDIA_ITEM, + 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); @@ -8222,22 +8249,27 @@ public final class ExoPlayerTest { new FakeMediaSource(), new FakeMediaSource())); verify(mockListener).onAvailableCommandsChanged(commandsWithSeekToNext); + // 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, times(2)).onAvailableCommandsChanged(any()); + playUntilStartOfWindow(player, /* windowIndex= */ 1); runUntilPendingCommandsAreFullyHandled(player); - verify(mockListener).onAvailableCommandsChanged(commandsWithSeekToNextAndPrevious); - verify(mockListener, times(2)).onAvailableCommandsChanged(any()); + verify(mockListener).onAvailableCommandsChanged(commandsWithSeekAnywhere); + verify(mockListener, times(3)).onAvailableCommandsChanged(any()); playUntilStartOfWindow(player, /* windowIndex= */ 2); runUntilPendingCommandsAreFullyHandled(player); - verify(mockListener, times(2)).onAvailableCommandsChanged(any()); + verify(mockListener, times(3)).onAvailableCommandsChanged(any()); player.play(); runUntilPlaybackState(player, Player.STATE_ENDED); - verify(mockListener).onAvailableCommandsChanged(commandsWithSeekToPrevious); - verify(mockListener, times(3)).onAvailableCommandsChanged(any()); + verify(mockListener).onAvailableCommandsChanged(commandsWithSeekInCurrentAndToPrevious); + verify(mockListener, times(4)).onAvailableCommandsChanged(any()); } @Test @@ -8252,6 +8284,7 @@ public final class ExoPlayerTest { player.addMediaSource(new FakeMediaSource()); verify(mockListener).onAvailableCommandsChanged(commandsWithSeekToNext); + // Check that there were no other calls to onAvailableCommandsChanged. verify(mockListener).onAvailableCommandsChanged(any()); player.addMediaSource(new FakeMediaSource()); @@ -8271,6 +8304,7 @@ public final class ExoPlayerTest { player.addMediaSource(/* index= */ 0, new FakeMediaSource()); verify(mockListener).onAvailableCommandsChanged(commandsWithSeekToPrevious); + // Check that there were no other calls to onAvailableCommandsChanged. verify(mockListener).onAvailableCommandsChanged(any()); player.addMediaSource(/* index= */ 0, new FakeMediaSource()); @@ -8288,6 +8322,7 @@ public final class ExoPlayerTest { player.addMediaSources( ImmutableList.of(new FakeMediaSource(), new FakeMediaSource(), new FakeMediaSource())); verify(mockListener).onAvailableCommandsChanged(commandsWithSeekToNext); + // Check that there were no other calls to onAvailableCommandsChanged. verify(mockListener).onAvailableCommandsChanged(any()); player.removeMediaItem(/* index= */ 2); @@ -8314,6 +8349,7 @@ public final class ExoPlayerTest { player.addMediaSources( ImmutableList.of(new FakeMediaSource(), new FakeMediaSource(), new FakeMediaSource())); verify(mockListener).onAvailableCommandsChanged(commandsWithSeekToPrevious); + // Check that there were no other calls to onAvailableCommandsChanged. verify(mockListener).onAvailableCommandsChanged(any()); player.removeMediaItem(/* index= */ 0); @@ -8337,6 +8373,7 @@ public final class ExoPlayerTest { player.addMediaSources(ImmutableList.of(new FakeMediaSource(), new FakeMediaSource())); verify(mockListener).onAvailableCommandsChanged(commandsWithSeekToNext); + // Check that there were no other calls to onAvailableCommandsChanged. verify(mockListener).onAvailableCommandsChanged(any()); player.removeMediaItem(/* index= */ 0); @@ -8357,6 +8394,7 @@ public final class ExoPlayerTest { player.setRepeatMode(Player.REPEAT_MODE_ALL); verify(mockListener).onAvailableCommandsChanged(commandsWithSeekToNextAndPrevious); + // Check that there were no other calls to onAvailableCommandsChanged. verify(mockListener).onAvailableCommandsChanged(any()); } @@ -8388,9 +8426,12 @@ public final class ExoPlayerTest { player.addMediaSource(mediaSource); verify(mockListener).onAvailableCommandsChanged(commandsWithSeekToNext); + // Check that there were no other calls to onAvailableCommandsChanged. + verify(mockListener).onAvailableCommandsChanged(any()); player.setShuffleModeEnabled(true); verify(mockListener).onAvailableCommandsChanged(commandsWithSeekToPrevious); + verify(mockListener, times(2)).onAvailableCommandsChanged(any()); } @Test diff --git a/library/ui/src/main/java/com/google/android/exoplayer2/ui/PlayerControlView.java b/library/ui/src/main/java/com/google/android/exoplayer2/ui/PlayerControlView.java index cef275a037..1499aa556a 100644 --- a/library/ui/src/main/java/com/google/android/exoplayer2/ui/PlayerControlView.java +++ b/library/ui/src/main/java/com/google/android/exoplayer2/ui/PlayerControlView.java @@ -15,6 +15,9 @@ */ package com.google.android.exoplayer2.ui; +import static com.google.android.exoplayer2.Player.COMMAND_SEEK_IN_CURRENT_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.EVENT_IS_PLAYING_CHANGED; import static com.google.android.exoplayer2.Player.EVENT_PLAYBACK_STATE_CHANGED; import static com.google.android.exoplayer2.Player.EVENT_PLAY_WHEN_READY_CHANGED; @@ -910,18 +913,18 @@ public class PlayerControlView extends FrameLayout { if (player != null) { Timeline timeline = player.getCurrentTimeline(); if (!timeline.isEmpty() && !player.isPlayingAd()) { + boolean isSeekable = player.isCommandAvailable(COMMAND_SEEK_IN_CURRENT_MEDIA_ITEM); timeline.getWindow(player.getCurrentWindowIndex(), window); - boolean isSeekable = window.isSeekable; enableSeeking = isSeekable; enablePrevious = isSeekable || !window.isLive() - || player.isCommandAvailable(Player.COMMAND_SEEK_TO_PREVIOUS_MEDIA_ITEM); + || player.isCommandAvailable(COMMAND_SEEK_TO_PREVIOUS_MEDIA_ITEM); enableRewind = isSeekable && controlDispatcher.isRewindEnabled(); enableFastForward = isSeekable && controlDispatcher.isFastForwardEnabled(); enableNext = (window.isLive() && window.isDynamic) - || player.isCommandAvailable(Player.COMMAND_SEEK_TO_NEXT_MEDIA_ITEM); + || player.isCommandAvailable(COMMAND_SEEK_TO_NEXT_MEDIA_ITEM); } } diff --git a/library/ui/src/main/java/com/google/android/exoplayer2/ui/PlayerNotificationManager.java b/library/ui/src/main/java/com/google/android/exoplayer2/ui/PlayerNotificationManager.java index 458b015e92..3e108ceb5c 100644 --- a/library/ui/src/main/java/com/google/android/exoplayer2/ui/PlayerNotificationManager.java +++ b/library/ui/src/main/java/com/google/android/exoplayer2/ui/PlayerNotificationManager.java @@ -15,6 +15,9 @@ */ package com.google.android.exoplayer2.ui; +import static com.google.android.exoplayer2.Player.COMMAND_SEEK_IN_CURRENT_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.EVENT_IS_PLAYING_CHANGED; import static com.google.android.exoplayer2.Player.EVENT_PLAYBACK_PARAMETERS_CHANGED; import static com.google.android.exoplayer2.Player.EVENT_PLAYBACK_STATE_CHANGED; @@ -1458,17 +1461,17 @@ public class PlayerNotificationManager { boolean enableNext = false; Timeline timeline = player.getCurrentTimeline(); if (!timeline.isEmpty() && !player.isPlayingAd()) { + boolean isSeekable = player.isCommandAvailable(COMMAND_SEEK_IN_CURRENT_MEDIA_ITEM); timeline.getWindow(player.getCurrentWindowIndex(), window); - boolean isSeekable = window.isSeekable; enablePrevious = isSeekable || !window.isLive() - || player.isCommandAvailable(Player.COMMAND_SEEK_TO_PREVIOUS_MEDIA_ITEM); + || player.isCommandAvailable(COMMAND_SEEK_TO_PREVIOUS_MEDIA_ITEM); enableRewind = isSeekable && controlDispatcher.isRewindEnabled(); enableFastForward = isSeekable && controlDispatcher.isFastForwardEnabled(); enableNext = (window.isLive() && window.isDynamic) - || player.isCommandAvailable(Player.COMMAND_SEEK_TO_NEXT_MEDIA_ITEM); + || player.isCommandAvailable(COMMAND_SEEK_TO_NEXT_MEDIA_ITEM); } List stringActions = new ArrayList<>(); diff --git a/library/ui/src/main/java/com/google/android/exoplayer2/ui/StyledPlayerControlView.java b/library/ui/src/main/java/com/google/android/exoplayer2/ui/StyledPlayerControlView.java index e0b9800304..f1eb8d0757 100644 --- a/library/ui/src/main/java/com/google/android/exoplayer2/ui/StyledPlayerControlView.java +++ b/library/ui/src/main/java/com/google/android/exoplayer2/ui/StyledPlayerControlView.java @@ -15,6 +15,9 @@ */ package com.google.android.exoplayer2.ui; +import static com.google.android.exoplayer2.Player.COMMAND_SEEK_IN_CURRENT_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.EVENT_IS_PLAYING_CHANGED; import static com.google.android.exoplayer2.Player.EVENT_PLAYBACK_PARAMETERS_CHANGED; import static com.google.android.exoplayer2.Player.EVENT_PLAYBACK_STATE_CHANGED; @@ -1145,18 +1148,18 @@ public class StyledPlayerControlView extends FrameLayout { if (player != null) { Timeline timeline = player.getCurrentTimeline(); if (!timeline.isEmpty() && !player.isPlayingAd()) { + boolean isSeekable = player.isCommandAvailable(COMMAND_SEEK_IN_CURRENT_MEDIA_ITEM); timeline.getWindow(player.getCurrentWindowIndex(), window); - boolean isSeekable = window.isSeekable; enableSeeking = isSeekable; enablePrevious = isSeekable || !window.isLive() - || player.isCommandAvailable(Player.COMMAND_SEEK_TO_PREVIOUS_MEDIA_ITEM); + || player.isCommandAvailable(COMMAND_SEEK_TO_PREVIOUS_MEDIA_ITEM); enableRewind = isSeekable && controlDispatcher.isRewindEnabled(); enableFastForward = isSeekable && controlDispatcher.isFastForwardEnabled(); enableNext = (window.isLive() && window.isDynamic) - || player.isCommandAvailable(Player.COMMAND_SEEK_TO_NEXT_MEDIA_ITEM); + || player.isCommandAvailable(COMMAND_SEEK_TO_NEXT_MEDIA_ITEM); } }