mirror of
https://github.com/samsonjs/media.git
synced 2026-03-26 09:35:47 +00:00
Some Player implementations have no playlist capability but can still
set a MediaItem for playback. Examples are a MediaController connected
to a legacy MediaSession, ExoPlayer up to 2.12 or MediaPlayer.
To indicate this capability, we need an allowed command in addition
to COMMAND_CHANGE_MEDIA_ITEMS that just allows to set a single item
that replaces everything that is currently played.
#minor-release
PiperOrigin-RevId: 453879626
(cherry picked from commit 5333c67d08)
1871 lines
83 KiB
Java
1871 lines
83 KiB
Java
/*
|
|
* Copyright (C) 2019 The Android Open Source Project
|
|
*
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
* you may not use this file except in compliance with the License.
|
|
* You may obtain a copy of the License at
|
|
*
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
*
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
* See the License for the specific language governing permissions and
|
|
* limitations under the License.
|
|
*/
|
|
package androidx.media3.cast;
|
|
|
|
import static androidx.media3.common.Player.COMMAND_ADJUST_DEVICE_VOLUME;
|
|
import static androidx.media3.common.Player.COMMAND_CHANGE_MEDIA_ITEMS;
|
|
import static androidx.media3.common.Player.COMMAND_GET_AUDIO_ATTRIBUTES;
|
|
import static androidx.media3.common.Player.COMMAND_GET_CURRENT_MEDIA_ITEM;
|
|
import static androidx.media3.common.Player.COMMAND_GET_DEVICE_VOLUME;
|
|
import static androidx.media3.common.Player.COMMAND_GET_MEDIA_ITEMS_METADATA;
|
|
import static androidx.media3.common.Player.COMMAND_GET_TEXT;
|
|
import static androidx.media3.common.Player.COMMAND_GET_TIMELINE;
|
|
import static androidx.media3.common.Player.COMMAND_GET_VOLUME;
|
|
import static androidx.media3.common.Player.COMMAND_PLAY_PAUSE;
|
|
import static androidx.media3.common.Player.COMMAND_PREPARE;
|
|
import static androidx.media3.common.Player.COMMAND_SEEK_BACK;
|
|
import static androidx.media3.common.Player.COMMAND_SEEK_FORWARD;
|
|
import static androidx.media3.common.Player.COMMAND_SEEK_IN_CURRENT_MEDIA_ITEM;
|
|
import static androidx.media3.common.Player.COMMAND_SEEK_TO_DEFAULT_POSITION;
|
|
import static androidx.media3.common.Player.COMMAND_SEEK_TO_MEDIA_ITEM;
|
|
import static androidx.media3.common.Player.COMMAND_SEEK_TO_NEXT;
|
|
import static androidx.media3.common.Player.COMMAND_SEEK_TO_NEXT_MEDIA_ITEM;
|
|
import static androidx.media3.common.Player.COMMAND_SEEK_TO_PREVIOUS;
|
|
import static androidx.media3.common.Player.COMMAND_SEEK_TO_PREVIOUS_MEDIA_ITEM;
|
|
import static androidx.media3.common.Player.COMMAND_SET_DEVICE_VOLUME;
|
|
import static androidx.media3.common.Player.COMMAND_SET_MEDIA_ITEM;
|
|
import static androidx.media3.common.Player.COMMAND_SET_MEDIA_ITEMS_METADATA;
|
|
import static androidx.media3.common.Player.COMMAND_SET_REPEAT_MODE;
|
|
import static androidx.media3.common.Player.COMMAND_SET_SHUFFLE_MODE;
|
|
import static androidx.media3.common.Player.COMMAND_SET_SPEED_AND_PITCH;
|
|
import static androidx.media3.common.Player.COMMAND_SET_VIDEO_SURFACE;
|
|
import static androidx.media3.common.Player.COMMAND_SET_VOLUME;
|
|
import static androidx.media3.common.Player.COMMAND_STOP;
|
|
import static androidx.media3.common.Player.DISCONTINUITY_REASON_REMOVE;
|
|
import static androidx.media3.common.Player.MEDIA_ITEM_TRANSITION_REASON_PLAYLIST_CHANGED;
|
|
import static com.google.common.truth.Truth.assertThat;
|
|
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.reset;
|
|
import static org.mockito.Mockito.times;
|
|
import static org.mockito.Mockito.verify;
|
|
import static org.mockito.Mockito.verifyNoMoreInteractions;
|
|
import static org.mockito.Mockito.when;
|
|
import static org.mockito.MockitoAnnotations.initMocks;
|
|
|
|
import android.net.Uri;
|
|
import androidx.media3.common.C;
|
|
import androidx.media3.common.MediaItem;
|
|
import androidx.media3.common.MimeTypes;
|
|
import androidx.media3.common.PlaybackParameters;
|
|
import androidx.media3.common.Player;
|
|
import androidx.media3.common.Timeline;
|
|
import androidx.media3.common.util.Assertions;
|
|
import androidx.test.ext.junit.runners.AndroidJUnit4;
|
|
import com.google.android.gms.cast.MediaInfo;
|
|
import com.google.android.gms.cast.MediaQueueItem;
|
|
import com.google.android.gms.cast.MediaStatus;
|
|
import com.google.android.gms.cast.framework.CastContext;
|
|
import com.google.android.gms.cast.framework.CastSession;
|
|
import com.google.android.gms.cast.framework.SessionManager;
|
|
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.common.collect.ImmutableList;
|
|
import java.util.ArrayList;
|
|
import java.util.Arrays;
|
|
import java.util.Collections;
|
|
import java.util.List;
|
|
import org.junit.Before;
|
|
import org.junit.Test;
|
|
import org.junit.runner.RunWith;
|
|
import org.mockito.ArgumentCaptor;
|
|
import org.mockito.Captor;
|
|
import org.mockito.InOrder;
|
|
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 MediaQueue mockMediaQueue;
|
|
@Mock private CastContext mockCastContext;
|
|
@Mock private SessionManager mockSessionManager;
|
|
@Mock private CastSession mockCastSession;
|
|
@Mock private Player.Listener mockListener;
|
|
@Mock private PendingResult<RemoteMediaClient.MediaChannelResult> mockPendingResult;
|
|
|
|
@Captor
|
|
private ArgumentCaptor<ResultCallback<RemoteMediaClient.MediaChannelResult>>
|
|
setResultCallbackArgumentCaptor;
|
|
|
|
@Captor private ArgumentCaptor<RemoteMediaClient.Callback> callbackArgumentCaptor;
|
|
@Captor private ArgumentCaptor<MediaQueueItem[]> queueItemsArgumentCaptor;
|
|
@Captor private ArgumentCaptor<MediaItem> mediaItemCaptor;
|
|
|
|
@SuppressWarnings("deprecation")
|
|
@Before
|
|
public void setUp() {
|
|
initMocks(this);
|
|
when(mockCastContext.getSessionManager()).thenReturn(mockSessionManager);
|
|
when(mockSessionManager.getCurrentCastSession()).thenReturn(mockCastSession);
|
|
when(mockCastSession.getRemoteMediaClient()).thenReturn(mockRemoteMediaClient);
|
|
when(mockRemoteMediaClient.getMediaStatus()).thenReturn(mockMediaStatus);
|
|
when(mockRemoteMediaClient.getMediaQueue()).thenReturn(mockMediaQueue);
|
|
when(mockMediaQueue.getItemIds()).thenReturn(new int[0]);
|
|
// 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);
|
|
when(mockMediaStatus.getPlaybackRate()).thenReturn(1.0d);
|
|
castPlayer = new CastPlayer(mockCastContext);
|
|
castPlayer.addListener(mockListener);
|
|
verify(mockRemoteMediaClient).registerCallback(callbackArgumentCaptor.capture());
|
|
remoteMediaClientCallback = callbackArgumentCaptor.getValue();
|
|
}
|
|
|
|
@SuppressWarnings("deprecation")
|
|
@Test
|
|
public void setPlayWhenReady_masksRemoteState() {
|
|
when(mockRemoteMediaClient.getPlayerState()).thenReturn(MediaStatus.PLAYER_STATE_PLAYING);
|
|
// Trigger initial update to get out of STATE_IDLE to make onIsPlaying() be called.
|
|
remoteMediaClientCallback.onStatusUpdated();
|
|
reset(mockListener);
|
|
when(mockRemoteMediaClient.play()).thenReturn(mockPendingResult);
|
|
assertThat(castPlayer.getPlayWhenReady()).isFalse();
|
|
|
|
castPlayer.play();
|
|
verify(mockPendingResult).setResultCallback(setResultCallbackArgumentCaptor.capture());
|
|
assertThat(castPlayer.getPlayWhenReady()).isTrue();
|
|
verify(mockListener).onPlayerStateChanged(true, Player.STATE_READY);
|
|
verify(mockListener).onIsPlayingChanged(true);
|
|
verify(mockListener)
|
|
.onPlayWhenReadyChanged(true, Player.PLAY_WHEN_READY_CHANGE_REASON_USER_REQUEST);
|
|
|
|
// There is a status update in the middle, which should be hidden by masking.
|
|
remoteMediaClientCallback.onStatusUpdated();
|
|
verifyNoMoreInteractions(mockListener);
|
|
|
|
// Upon result, the remoteMediaClient has updated its state according to the play() call.
|
|
when(mockRemoteMediaClient.isPaused()).thenReturn(false);
|
|
setResultCallbackArgumentCaptor
|
|
.getValue()
|
|
.onResult(mock(RemoteMediaClient.MediaChannelResult.class));
|
|
verifyNoMoreInteractions(mockListener);
|
|
}
|
|
|
|
@SuppressWarnings("deprecation")
|
|
@Test
|
|
public void setPlayWhenReadyMasking_updatesUponResultChange() {
|
|
when(mockRemoteMediaClient.getPlayerState()).thenReturn(MediaStatus.PLAYER_STATE_PLAYING);
|
|
// Trigger initial update to get out of STATE_IDLE to make onIsPlaying() be called.
|
|
remoteMediaClientCallback.onStatusUpdated();
|
|
reset(mockListener);
|
|
when(mockRemoteMediaClient.play()).thenReturn(mockPendingResult);
|
|
assertThat(castPlayer.getPlayWhenReady()).isFalse();
|
|
|
|
castPlayer.play();
|
|
verify(mockPendingResult).setResultCallback(setResultCallbackArgumentCaptor.capture());
|
|
assertThat(castPlayer.getPlayWhenReady()).isTrue();
|
|
verify(mockListener).onIsPlayingChanged(true);
|
|
verify(mockListener).onPlayerStateChanged(true, Player.STATE_READY);
|
|
verify(mockListener)
|
|
.onPlayWhenReadyChanged(true, Player.PLAY_WHEN_READY_CHANGE_REASON_USER_REQUEST);
|
|
|
|
// Upon result, the remote media client is still paused. The state should reflect that.
|
|
setResultCallbackArgumentCaptor
|
|
.getValue()
|
|
.onResult(mock(RemoteMediaClient.MediaChannelResult.class));
|
|
verify(mockListener).onPlayerStateChanged(false, Player.STATE_READY);
|
|
verify(mockListener).onIsPlayingChanged(false);
|
|
verify(mockListener).onPlayWhenReadyChanged(false, Player.PLAY_WHEN_READY_CHANGE_REASON_REMOTE);
|
|
assertThat(castPlayer.getPlayWhenReady()).isFalse();
|
|
verifyNoMoreInteractions(mockListener);
|
|
}
|
|
|
|
@SuppressWarnings("deprecation")
|
|
@Test
|
|
public void setPlayWhenReady_correctChangeReasonOnPause() {
|
|
when(mockRemoteMediaClient.getPlayerState()).thenReturn(MediaStatus.PLAYER_STATE_PLAYING);
|
|
// Trigger initial update to get out of STATE_IDLE to make onIsPlaying() be called.
|
|
remoteMediaClientCallback.onStatusUpdated();
|
|
reset(mockListener);
|
|
when(mockRemoteMediaClient.play()).thenReturn(mockPendingResult);
|
|
when(mockRemoteMediaClient.pause()).thenReturn(mockPendingResult);
|
|
|
|
castPlayer.play();
|
|
assertThat(castPlayer.getPlayWhenReady()).isTrue();
|
|
verify(mockListener).onIsPlayingChanged(true);
|
|
verify(mockListener).onPlayerStateChanged(true, Player.STATE_READY);
|
|
verify(mockListener)
|
|
.onPlayWhenReadyChanged(true, Player.PLAY_WHEN_READY_CHANGE_REASON_USER_REQUEST);
|
|
|
|
castPlayer.pause();
|
|
assertThat(castPlayer.getPlayWhenReady()).isFalse();
|
|
verify(mockListener).onIsPlayingChanged(false);
|
|
verify(mockListener)
|
|
.onPlayWhenReadyChanged(false, Player.PLAY_WHEN_READY_CHANGE_REASON_USER_REQUEST);
|
|
verify(mockListener).onPlayerStateChanged(false, Player.STATE_READY);
|
|
verifyNoMoreInteractions(mockListener);
|
|
}
|
|
|
|
@SuppressWarnings("deprecation")
|
|
@Test
|
|
public void playWhenReady_changesOnStatusUpdates() {
|
|
when(mockRemoteMediaClient.getPlayerState()).thenReturn(MediaStatus.PLAYER_STATE_PLAYING);
|
|
assertThat(castPlayer.getPlayWhenReady()).isFalse();
|
|
when(mockRemoteMediaClient.isPaused()).thenReturn(false);
|
|
remoteMediaClientCallback.onStatusUpdated();
|
|
verify(mockListener).onPlayerStateChanged(true, Player.STATE_READY);
|
|
verify(mockListener).onPlaybackStateChanged(Player.STATE_READY);
|
|
verify(mockListener).onPlayWhenReadyChanged(true, Player.PLAY_WHEN_READY_CHANGE_REASON_REMOTE);
|
|
assertThat(castPlayer.getPlayWhenReady()).isTrue();
|
|
verify(mockListener).onIsPlayingChanged(true);
|
|
verifyNoMoreInteractions(mockListener);
|
|
}
|
|
|
|
@Test
|
|
public void playbackParameters_defaultPlaybackSpeed_isUnitSpeed() {
|
|
assertThat(castPlayer.getPlaybackParameters()).isEqualTo(PlaybackParameters.DEFAULT);
|
|
}
|
|
|
|
@Test
|
|
public void playbackParameters_onStatusUpdated_setsRemotePlaybackSpeed() {
|
|
PlaybackParameters expectedPlaybackParameters = new PlaybackParameters(/* speed= */ 1.234f);
|
|
when(mockMediaStatus.getPlaybackRate()).thenReturn(1.234d);
|
|
|
|
remoteMediaClientCallback.onStatusUpdated();
|
|
|
|
assertThat(castPlayer.getPlaybackParameters()).isEqualTo(expectedPlaybackParameters);
|
|
verify(mockListener).onPlaybackParametersChanged(expectedPlaybackParameters);
|
|
}
|
|
|
|
@Test
|
|
public void playbackParameters_onStatusUpdated_ignoreInPausedState() {
|
|
when(mockMediaStatus.getPlaybackRate()).thenReturn(0.0d);
|
|
|
|
remoteMediaClientCallback.onStatusUpdated();
|
|
|
|
assertThat(castPlayer.getPlaybackParameters()).isEqualTo(PlaybackParameters.DEFAULT);
|
|
verifyNoMoreInteractions(mockListener);
|
|
}
|
|
|
|
@Test
|
|
public void setPlaybackParameters_speedOutOfRange_valueIsConstraintToMinAndMax() {
|
|
when(mockRemoteMediaClient.setPlaybackRate(eq(2d), any())).thenReturn(mockPendingResult);
|
|
when(mockRemoteMediaClient.setPlaybackRate(eq(0.5d), any())).thenReturn(mockPendingResult);
|
|
PlaybackParameters expectedMaxValuePlaybackParameters = new PlaybackParameters(/* speed= */ 2f);
|
|
PlaybackParameters expectedMinValuePlaybackParameters =
|
|
new PlaybackParameters(/* speed= */ 0.5f);
|
|
|
|
castPlayer.setPlaybackParameters(new PlaybackParameters(/* speed= */ 2.001f));
|
|
verify(mockListener).onPlaybackParametersChanged(expectedMaxValuePlaybackParameters);
|
|
castPlayer.setPlaybackParameters(new PlaybackParameters(/* speed= */ 0.499f));
|
|
verify(mockListener).onPlaybackParametersChanged(expectedMinValuePlaybackParameters);
|
|
}
|
|
|
|
@Test
|
|
public void setPlaybackParameters_masksPendingState() {
|
|
PlaybackParameters playbackParameters = new PlaybackParameters(/* speed= */ 1.234f);
|
|
when(mockRemoteMediaClient.setPlaybackRate(eq((double) 1.234f), any()))
|
|
.thenReturn(mockPendingResult);
|
|
|
|
castPlayer.setPlaybackParameters(playbackParameters);
|
|
|
|
verify(mockPendingResult).setResultCallback(setResultCallbackArgumentCaptor.capture());
|
|
assertThat(castPlayer.getPlaybackParameters().speed).isEqualTo(1.234f);
|
|
verify(mockListener).onPlaybackParametersChanged(playbackParameters);
|
|
|
|
// Simulate a status update while the update is pending that must not override the masked speed.
|
|
when(mockMediaStatus.getPlaybackRate()).thenReturn(99.0d);
|
|
remoteMediaClientCallback.onStatusUpdated();
|
|
assertThat(castPlayer.getPlaybackParameters().speed).isEqualTo(1.234f);
|
|
|
|
// Call the captured result callback when the device responds. The listener must not be called.
|
|
when(mockMediaStatus.getPlaybackRate()).thenReturn(1.234d);
|
|
setResultCallbackArgumentCaptor
|
|
.getValue()
|
|
.onResult(mock(RemoteMediaClient.MediaChannelResult.class));
|
|
assertThat(castPlayer.getPlaybackParameters().speed).isEqualTo(1.234f);
|
|
verifyNoMoreInteractions(mockListener);
|
|
}
|
|
|
|
@Test
|
|
public void setPlaybackParameters_speedChangeNotSupported_resetOnResultCallback() {
|
|
when(mockRemoteMediaClient.setPlaybackRate(eq((double) 1.234f), any()))
|
|
.thenReturn(mockPendingResult);
|
|
PlaybackParameters playbackParameters = new PlaybackParameters(/* speed= */ 1.234f);
|
|
|
|
// Change the playback speed and and capture the result callback.
|
|
castPlayer.setPlaybackParameters(playbackParameters);
|
|
verify(mockPendingResult).setResultCallback(setResultCallbackArgumentCaptor.capture());
|
|
verify(mockListener).onPlaybackParametersChanged(new PlaybackParameters(/* speed= */ 1.234f));
|
|
|
|
// The device does not support speed changes and returns unit speed to the result callback.
|
|
when(mockMediaStatus.getPlaybackRate()).thenReturn(1.0d);
|
|
setResultCallbackArgumentCaptor
|
|
.getValue()
|
|
.onResult(mock(RemoteMediaClient.MediaChannelResult.class));
|
|
assertThat(castPlayer.getPlaybackParameters()).isEqualTo(PlaybackParameters.DEFAULT);
|
|
verify(mockListener).onPlaybackParametersChanged(PlaybackParameters.DEFAULT);
|
|
verifyNoMoreInteractions(mockListener);
|
|
}
|
|
|
|
@Test
|
|
public void setRepeatMode_masksRemoteState() {
|
|
when(mockRemoteMediaClient.queueSetRepeatMode(anyInt(), any())).thenReturn(mockPendingResult);
|
|
assertThat(castPlayer.getRepeatMode()).isEqualTo(Player.REPEAT_MODE_OFF);
|
|
|
|
castPlayer.setRepeatMode(Player.REPEAT_MODE_ONE);
|
|
verify(mockPendingResult).setResultCallback(setResultCallbackArgumentCaptor.capture());
|
|
assertThat(castPlayer.getRepeatMode()).isEqualTo(Player.REPEAT_MODE_ONE);
|
|
verify(mockListener).onRepeatModeChanged(Player.REPEAT_MODE_ONE);
|
|
|
|
// There is a status update in the middle, which should be hidden by masking.
|
|
when(mockMediaStatus.getQueueRepeatMode()).thenReturn(MediaStatus.REPEAT_MODE_REPEAT_ALL);
|
|
remoteMediaClientCallback.onStatusUpdated();
|
|
verifyNoMoreInteractions(mockListener);
|
|
|
|
// Upon result, the mediaStatus now exposes the new repeat mode.
|
|
when(mockMediaStatus.getQueueRepeatMode()).thenReturn(MediaStatus.REPEAT_MODE_REPEAT_SINGLE);
|
|
setResultCallbackArgumentCaptor
|
|
.getValue()
|
|
.onResult(mock(RemoteMediaClient.MediaChannelResult.class));
|
|
verifyNoMoreInteractions(mockListener);
|
|
}
|
|
|
|
@Test
|
|
public void setRepeatMode_updatesUponResultChange() {
|
|
when(mockRemoteMediaClient.queueSetRepeatMode(anyInt(), any())).thenReturn(mockPendingResult);
|
|
|
|
castPlayer.setRepeatMode(Player.REPEAT_MODE_ONE);
|
|
verify(mockPendingResult).setResultCallback(setResultCallbackArgumentCaptor.capture());
|
|
assertThat(castPlayer.getRepeatMode()).isEqualTo(Player.REPEAT_MODE_ONE);
|
|
verify(mockListener).onRepeatModeChanged(Player.REPEAT_MODE_ONE);
|
|
|
|
// There is a status update in the middle, which should be hidden by masking.
|
|
when(mockMediaStatus.getQueueRepeatMode()).thenReturn(MediaStatus.REPEAT_MODE_REPEAT_ALL);
|
|
remoteMediaClientCallback.onStatusUpdated();
|
|
verifyNoMoreInteractions(mockListener);
|
|
|
|
// Upon result, the repeat mode is ALL. The state should reflect that.
|
|
setResultCallbackArgumentCaptor
|
|
.getValue()
|
|
.onResult(mock(RemoteMediaClient.MediaChannelResult.class));
|
|
verify(mockListener).onRepeatModeChanged(Player.REPEAT_MODE_ALL);
|
|
assertThat(castPlayer.getRepeatMode()).isEqualTo(Player.REPEAT_MODE_ALL);
|
|
}
|
|
|
|
@Test
|
|
public void repeatMode_changesOnStatusUpdates() {
|
|
assertThat(castPlayer.getRepeatMode()).isEqualTo(Player.REPEAT_MODE_OFF);
|
|
when(mockMediaStatus.getQueueRepeatMode()).thenReturn(MediaStatus.REPEAT_MODE_REPEAT_SINGLE);
|
|
remoteMediaClientCallback.onStatusUpdated();
|
|
verify(mockListener).onRepeatModeChanged(Player.REPEAT_MODE_ONE);
|
|
assertThat(castPlayer.getRepeatMode()).isEqualTo(Player.REPEAT_MODE_ONE);
|
|
}
|
|
|
|
@Test
|
|
public void setMediaItems_callsRemoteMediaClient() {
|
|
List<MediaItem> mediaItems = new ArrayList<>();
|
|
String uri1 = "http://www.google.com/video1";
|
|
String uri2 = "http://www.google.com/video2";
|
|
mediaItems.add(
|
|
new MediaItem.Builder().setUri(uri1).setMimeType(MimeTypes.APPLICATION_MPD).build());
|
|
mediaItems.add(
|
|
new MediaItem.Builder().setUri(uri2).setMimeType(MimeTypes.APPLICATION_MP4).build());
|
|
|
|
castPlayer.setMediaItems(mediaItems, /* startWindowIndex= */ 1, /* startPositionMs= */ 2000L);
|
|
|
|
verify(mockRemoteMediaClient)
|
|
.queueLoad(queueItemsArgumentCaptor.capture(), eq(1), anyInt(), eq(2000L), any());
|
|
MediaQueueItem[] mediaQueueItems = queueItemsArgumentCaptor.getValue();
|
|
assertThat(mediaQueueItems[0].getMedia().getContentId()).isEqualTo(uri1);
|
|
assertThat(mediaQueueItems[1].getMedia().getContentId()).isEqualTo(uri2);
|
|
}
|
|
|
|
@Test
|
|
public void setMediaItems_doNotReset_callsRemoteMediaClient() {
|
|
MediaItem.Builder builder = new MediaItem.Builder();
|
|
List<MediaItem> mediaItems = new ArrayList<>();
|
|
String uri1 = "http://www.google.com/video1";
|
|
String uri2 = "http://www.google.com/video2";
|
|
mediaItems.add(builder.setUri(uri1).setMimeType(MimeTypes.APPLICATION_MPD).build());
|
|
mediaItems.add(builder.setUri(uri2).setMimeType(MimeTypes.APPLICATION_MP4).build());
|
|
int startWindowIndex = C.INDEX_UNSET;
|
|
long startPositionMs = 2000L;
|
|
|
|
castPlayer.setMediaItems(mediaItems, startWindowIndex, startPositionMs);
|
|
|
|
verify(mockRemoteMediaClient)
|
|
.queueLoad(queueItemsArgumentCaptor.capture(), eq(0), anyInt(), eq(0L), any());
|
|
|
|
MediaQueueItem[] mediaQueueItems = queueItemsArgumentCaptor.getValue();
|
|
assertThat(mediaQueueItems[0].getMedia().getContentId()).isEqualTo(uri1);
|
|
assertThat(mediaQueueItems[1].getMedia().getContentId()).isEqualTo(uri2);
|
|
}
|
|
|
|
@Test
|
|
public void setMediaItems_replaceExistingPlaylist_notifiesMediaItemTransition() {
|
|
List<MediaItem> firstPlaylist = new ArrayList<>();
|
|
String uri1 = "http://www.google.com/video1";
|
|
String uri2 = "http://www.google.com/video2";
|
|
firstPlaylist.add(
|
|
new MediaItem.Builder().setUri(uri1).setMimeType(MimeTypes.APPLICATION_MPD).build());
|
|
firstPlaylist.add(
|
|
new MediaItem.Builder().setUri(uri2).setMimeType(MimeTypes.APPLICATION_MP4).build());
|
|
ImmutableList<MediaItem> secondPlaylist =
|
|
ImmutableList.of(
|
|
new MediaItem.Builder()
|
|
.setUri(Uri.EMPTY)
|
|
.setMimeType(MimeTypes.APPLICATION_MPD)
|
|
.build());
|
|
|
|
castPlayer.setMediaItems(
|
|
firstPlaylist, /* startWindowIndex= */ 1, /* startPositionMs= */ 2000L);
|
|
updateTimeLine(
|
|
firstPlaylist, /* mediaQueueItemIds= */ new int[] {1, 2}, /* currentItemId= */ 2);
|
|
// Replacing existing playlist.
|
|
castPlayer.setMediaItems(
|
|
secondPlaylist, /* startWindowIndex= */ 0, /* startPositionMs= */ 1000L);
|
|
updateTimeLine(secondPlaylist, /* mediaQueueItemIds= */ new int[] {3}, /* currentItemId= */ 3);
|
|
|
|
InOrder inOrder = Mockito.inOrder(mockListener);
|
|
inOrder
|
|
.verify(mockListener, times(2))
|
|
.onMediaItemTransition(
|
|
mediaItemCaptor.capture(), eq(MEDIA_ITEM_TRANSITION_REASON_PLAYLIST_CHANGED));
|
|
inOrder.verify(mockListener, never()).onMediaItemTransition(any(), anyInt());
|
|
assertThat(mediaItemCaptor.getAllValues().get(1).localConfiguration.tag).isEqualTo(3);
|
|
}
|
|
|
|
@SuppressWarnings("deprecation") // Verifies deprecated callback being called correctly.
|
|
@Test
|
|
public void setMediaItems_replaceExistingPlaylist_notifiesPositionDiscontinuity() {
|
|
List<MediaItem> firstPlaylist = new ArrayList<>();
|
|
String uri1 = "http://www.google.com/video1";
|
|
String uri2 = "http://www.google.com/video2";
|
|
firstPlaylist.add(
|
|
new MediaItem.Builder().setUri(uri1).setMimeType(MimeTypes.APPLICATION_MPD).build());
|
|
firstPlaylist.add(
|
|
new MediaItem.Builder().setUri(uri2).setMimeType(MimeTypes.APPLICATION_MP4).build());
|
|
ImmutableList<MediaItem> secondPlaylist =
|
|
ImmutableList.of(
|
|
new MediaItem.Builder()
|
|
.setUri(Uri.EMPTY)
|
|
.setMimeType(MimeTypes.APPLICATION_MPD)
|
|
.build());
|
|
|
|
castPlayer.setMediaItems(
|
|
firstPlaylist, /* startWindowIndex= */ 1, /* startPositionMs= */ 2000L);
|
|
updateTimeLine(
|
|
firstPlaylist,
|
|
/* mediaQueueItemIds= */ new int[] {1, 2},
|
|
/* currentItemId= */ 2,
|
|
/* streamTypes= */ new int[] {
|
|
MediaInfo.STREAM_TYPE_BUFFERED, MediaInfo.STREAM_TYPE_BUFFERED
|
|
},
|
|
/* durationsMs= */ new long[] {20_000, 20_000},
|
|
/* positionMs= */ 2000L);
|
|
// Replacing existing playlist.
|
|
castPlayer.setMediaItems(
|
|
secondPlaylist, /* startWindowIndex= */ 0, /* startPositionMs= */ 1000L);
|
|
updateTimeLine(
|
|
secondPlaylist,
|
|
/* mediaQueueItemIds= */ new int[] {3},
|
|
/* currentItemId= */ 3,
|
|
/* streamTypes= */ new int[] {MediaInfo.STREAM_TYPE_BUFFERED},
|
|
/* durationsMs= */ new long[] {20_000},
|
|
/* positionMs= */ 1000L);
|
|
|
|
Player.PositionInfo oldPosition =
|
|
new Player.PositionInfo(
|
|
/* windowUid= */ 2,
|
|
/* windowIndex= */ 1,
|
|
new MediaItem.Builder().setUri(Uri.EMPTY).setTag(2).build(),
|
|
/* periodUid= */ 2,
|
|
/* periodIndex= */ 1,
|
|
/* positionMs= */ 2000,
|
|
/* contentPositionMs= */ 2000,
|
|
/* adGroupIndex= */ C.INDEX_UNSET,
|
|
/* adIndexInAdGroup= */ C.INDEX_UNSET);
|
|
Player.PositionInfo newPosition =
|
|
new Player.PositionInfo(
|
|
/* windowUid= */ 3,
|
|
/* windowIndex= */ 0,
|
|
new MediaItem.Builder().setUri(Uri.EMPTY).setTag(3).build(),
|
|
/* periodUid= */ 3,
|
|
/* periodIndex= */ 0,
|
|
/* positionMs= */ 1000,
|
|
/* contentPositionMs= */ 1000,
|
|
/* adGroupIndex= */ C.INDEX_UNSET,
|
|
/* adIndexInAdGroup= */ C.INDEX_UNSET);
|
|
InOrder inOrder = Mockito.inOrder(mockListener);
|
|
inOrder.verify(mockListener).onPositionDiscontinuity(eq(DISCONTINUITY_REASON_REMOVE));
|
|
inOrder
|
|
.verify(mockListener)
|
|
.onPositionDiscontinuity(eq(oldPosition), eq(newPosition), eq(DISCONTINUITY_REASON_REMOVE));
|
|
inOrder.verify(mockListener, never()).onPositionDiscontinuity(anyInt());
|
|
inOrder.verify(mockListener, never()).onPositionDiscontinuity(any(), any(), anyInt());
|
|
}
|
|
|
|
@Test
|
|
public void addMediaItems_callsRemoteMediaClient() {
|
|
MediaItem.Builder builder = new MediaItem.Builder();
|
|
List<MediaItem> mediaItems = new ArrayList<>();
|
|
String uri1 = "http://www.google.com/video1";
|
|
String uri2 = "http://www.google.com/video2";
|
|
mediaItems.add(builder.setUri(uri1).setMimeType(MimeTypes.APPLICATION_MPD).build());
|
|
mediaItems.add(builder.setUri(uri2).setMimeType(MimeTypes.APPLICATION_MP4).build());
|
|
|
|
castPlayer.addMediaItems(mediaItems);
|
|
|
|
verify(mockRemoteMediaClient)
|
|
.queueInsertItems(
|
|
queueItemsArgumentCaptor.capture(), eq(MediaQueueItem.INVALID_ITEM_ID), any());
|
|
|
|
MediaQueueItem[] mediaQueueItems = queueItemsArgumentCaptor.getValue();
|
|
assertThat(mediaQueueItems[0].getMedia().getContentId()).isEqualTo(uri1);
|
|
assertThat(mediaQueueItems[1].getMedia().getContentId()).isEqualTo(uri2);
|
|
}
|
|
|
|
@SuppressWarnings("ConstantConditions")
|
|
@Test
|
|
public void addMediaItems_insertAtIndex_callsRemoteMediaClient() {
|
|
int[] mediaQueueItemIds = createMediaQueueItemIds(/* numberOfIds= */ 2);
|
|
List<MediaItem> mediaItems = createMediaItems(mediaQueueItemIds);
|
|
addMediaItemsAndUpdateTimeline(mediaItems, mediaQueueItemIds);
|
|
String uri = "http://www.google.com/video3";
|
|
MediaItem anotherMediaItem =
|
|
new MediaItem.Builder().setUri(uri).setMimeType(MimeTypes.APPLICATION_MPD).build();
|
|
|
|
// Add another on position 1
|
|
int index = 1;
|
|
castPlayer.addMediaItems(index, Collections.singletonList(anotherMediaItem));
|
|
|
|
verify(mockRemoteMediaClient)
|
|
.queueInsertItems(
|
|
queueItemsArgumentCaptor.capture(),
|
|
eq((int) mediaItems.get(index).localConfiguration.tag),
|
|
any());
|
|
|
|
MediaQueueItem[] mediaQueueItems = queueItemsArgumentCaptor.getValue();
|
|
assertThat(mediaQueueItems[0].getMedia().getContentId()).isEqualTo(uri);
|
|
}
|
|
|
|
@Test
|
|
public void moveMediaItem_callsRemoteMediaClient() {
|
|
int[] mediaQueueItemIds = createMediaQueueItemIds(/* numberOfIds= */ 5);
|
|
List<MediaItem> mediaItems = createMediaItems(mediaQueueItemIds);
|
|
addMediaItemsAndUpdateTimeline(mediaItems, mediaQueueItemIds);
|
|
|
|
castPlayer.moveMediaItem(/* currentIndex= */ 1, /* newIndex= */ 2);
|
|
|
|
verify(mockRemoteMediaClient)
|
|
.queueReorderItems(new int[] {2}, /* insertBeforeItemId= */ 4, /* customData= */ null);
|
|
}
|
|
|
|
@Test
|
|
public void moveMediaItem_toBegin_callsRemoteMediaClient() {
|
|
int[] mediaQueueItemIds = createMediaQueueItemIds(/* numberOfIds= */ 5);
|
|
List<MediaItem> mediaItems = createMediaItems(mediaQueueItemIds);
|
|
addMediaItemsAndUpdateTimeline(mediaItems, mediaQueueItemIds);
|
|
|
|
castPlayer.moveMediaItem(/* currentIndex= */ 1, /* newIndex= */ 0);
|
|
|
|
verify(mockRemoteMediaClient)
|
|
.queueReorderItems(new int[] {2}, /* insertBeforeItemId= */ 1, /* customData= */ null);
|
|
}
|
|
|
|
@Test
|
|
public void moveMediaItem_toEnd_callsRemoteMediaClient() {
|
|
int[] mediaQueueItemIds = createMediaQueueItemIds(/* numberOfIds= */ 5);
|
|
List<MediaItem> mediaItems = createMediaItems(mediaQueueItemIds);
|
|
addMediaItemsAndUpdateTimeline(mediaItems, mediaQueueItemIds);
|
|
|
|
castPlayer.moveMediaItem(/* currentIndex= */ 1, /* newIndex= */ 4);
|
|
|
|
verify(mockRemoteMediaClient)
|
|
.queueReorderItems(
|
|
new int[] {2},
|
|
/* insertBeforeItemId= */ MediaQueueItem.INVALID_ITEM_ID,
|
|
/* customData= */ null);
|
|
}
|
|
|
|
@Test
|
|
public void moveMediaItems_callsRemoteMediaClient() {
|
|
int[] mediaQueueItemIds = createMediaQueueItemIds(/* numberOfIds= */ 5);
|
|
List<MediaItem> mediaItems = createMediaItems(mediaQueueItemIds);
|
|
addMediaItemsAndUpdateTimeline(mediaItems, mediaQueueItemIds);
|
|
|
|
castPlayer.moveMediaItems(/* fromIndex= */ 0, /* toIndex= */ 3, /* newIndex= */ 1);
|
|
|
|
verify(mockRemoteMediaClient)
|
|
.queueReorderItems(
|
|
new int[] {1, 2, 3}, /* insertBeforeItemId= */ 5, /* customData= */ null);
|
|
}
|
|
|
|
@Test
|
|
public void moveMediaItems_toBeginning_callsRemoteMediaClient() {
|
|
int[] mediaQueueItemIds = createMediaQueueItemIds(/* numberOfIds= */ 5);
|
|
List<MediaItem> mediaItems = createMediaItems(mediaQueueItemIds);
|
|
addMediaItemsAndUpdateTimeline(mediaItems, mediaQueueItemIds);
|
|
|
|
castPlayer.moveMediaItems(/* fromIndex= */ 1, /* toIndex= */ 4, /* newIndex= */ 0);
|
|
|
|
verify(mockRemoteMediaClient)
|
|
.queueReorderItems(
|
|
new int[] {2, 3, 4}, /* insertBeforeItemId= */ 1, /* customData= */ null);
|
|
}
|
|
|
|
@Test
|
|
public void moveMediaItems_toEnd_callsRemoteMediaClient() {
|
|
int[] mediaQueueItemIds = createMediaQueueItemIds(/* numberOfIds= */ 5);
|
|
List<MediaItem> mediaItems = createMediaItems(mediaQueueItemIds);
|
|
addMediaItemsAndUpdateTimeline(mediaItems, mediaQueueItemIds);
|
|
|
|
castPlayer.moveMediaItems(/* fromIndex= */ 0, /* toIndex= */ 2, /* newIndex= */ 3);
|
|
|
|
verify(mockRemoteMediaClient)
|
|
.queueReorderItems(
|
|
new int[] {1, 2},
|
|
/* insertBeforeItemId= */ MediaQueueItem.INVALID_ITEM_ID,
|
|
/* customData= */ null);
|
|
}
|
|
|
|
@Test
|
|
public void moveMediaItems_noItems_doesNotCallRemoteMediaClient() {
|
|
int[] mediaQueueItemIds = createMediaQueueItemIds(/* numberOfIds= */ 5);
|
|
List<MediaItem> mediaItems = createMediaItems(mediaQueueItemIds);
|
|
addMediaItemsAndUpdateTimeline(mediaItems, mediaQueueItemIds);
|
|
|
|
castPlayer.moveMediaItems(/* fromIndex= */ 1, /* toIndex= */ 1, /* newIndex= */ 0);
|
|
|
|
verify(mockRemoteMediaClient, never()).queueReorderItems(any(), anyInt(), any());
|
|
}
|
|
|
|
@Test
|
|
public void moveMediaItems_noMove_doesNotCallRemoteMediaClient() {
|
|
int[] mediaQueueItemIds = createMediaQueueItemIds(/* numberOfIds= */ 5);
|
|
List<MediaItem> mediaItems = createMediaItems(mediaQueueItemIds);
|
|
addMediaItemsAndUpdateTimeline(mediaItems, mediaQueueItemIds);
|
|
|
|
castPlayer.moveMediaItems(/* fromIndex= */ 1, /* toIndex= */ 3, /* newIndex= */ 1);
|
|
|
|
verify(mockRemoteMediaClient, never()).queueReorderItems(any(), anyInt(), any());
|
|
}
|
|
|
|
@Test
|
|
public void removeMediaItems_callsRemoteMediaClient() {
|
|
int[] mediaQueueItemIds = createMediaQueueItemIds(/* numberOfIds= */ 5);
|
|
List<MediaItem> mediaItems = createMediaItems(mediaQueueItemIds);
|
|
addMediaItemsAndUpdateTimeline(mediaItems, mediaQueueItemIds);
|
|
|
|
castPlayer.removeMediaItems(/* fromIndex= */ 1, /* toIndex= */ 4);
|
|
|
|
verify(mockRemoteMediaClient).queueRemoveItems(new int[] {2, 3, 4}, /* customData= */ null);
|
|
}
|
|
|
|
@Test
|
|
public void clearMediaItems_callsRemoteMediaClient() {
|
|
int[] mediaQueueItemIds = createMediaQueueItemIds(/* numberOfIds= */ 5);
|
|
List<MediaItem> mediaItems = createMediaItems(mediaQueueItemIds);
|
|
addMediaItemsAndUpdateTimeline(mediaItems, mediaQueueItemIds);
|
|
|
|
castPlayer.clearMediaItems();
|
|
|
|
verify(mockRemoteMediaClient)
|
|
.queueRemoveItems(new int[] {1, 2, 3, 4, 5}, /* customData= */ null);
|
|
}
|
|
|
|
@SuppressWarnings("ConstantConditions")
|
|
@Test
|
|
public void addMediaItems_fillsTimeline() {
|
|
Timeline.Window window = new Timeline.Window();
|
|
int[] mediaQueueItemIds = createMediaQueueItemIds(/* numberOfIds= */ 5);
|
|
List<MediaItem> mediaItems = createMediaItems(mediaQueueItemIds);
|
|
|
|
addMediaItemsAndUpdateTimeline(mediaItems, mediaQueueItemIds);
|
|
|
|
Timeline currentTimeline = castPlayer.getCurrentTimeline();
|
|
for (int i = 0; i < mediaItems.size(); i++) {
|
|
assertThat(currentTimeline.getWindow(/* windowIndex= */ i, window).uid)
|
|
.isEqualTo(mediaItems.get(i).localConfiguration.tag);
|
|
}
|
|
}
|
|
|
|
@Test
|
|
public void addMediaItems_notifiesMediaItemTransition() {
|
|
MediaItem mediaItem = createMediaItem(/* mediaQueueItemId= */ 1);
|
|
List<MediaItem> mediaItems = ImmutableList.of(mediaItem);
|
|
int[] mediaQueueItemIds = new int[] {1};
|
|
|
|
castPlayer.addMediaItems(mediaItems);
|
|
updateTimeLine(mediaItems, mediaQueueItemIds, /* currentItemId= */ 1);
|
|
|
|
InOrder inOrder = Mockito.inOrder(mockListener);
|
|
inOrder
|
|
.verify(mockListener)
|
|
.onMediaItemTransition(
|
|
mediaItemCaptor.capture(), eq(Player.MEDIA_ITEM_TRANSITION_REASON_PLAYLIST_CHANGED));
|
|
inOrder.verify(mockListener, never()).onMediaItemTransition(any(), anyInt());
|
|
assertThat(mediaItemCaptor.getValue().localConfiguration.tag)
|
|
.isEqualTo(mediaItem.localConfiguration.tag);
|
|
}
|
|
|
|
@Test
|
|
public void clearMediaItems_notifiesMediaItemTransition() {
|
|
int[] mediaQueueItemIds = new int[] {1, 2};
|
|
List<MediaItem> mediaItems = createMediaItems(mediaQueueItemIds);
|
|
|
|
castPlayer.addMediaItems(mediaItems);
|
|
updateTimeLine(mediaItems, mediaQueueItemIds, /* currentItemId= */ 1);
|
|
castPlayer.clearMediaItems();
|
|
updateTimeLine(
|
|
/* mediaItems= */ ImmutableList.of(),
|
|
/* mediaQueueItemIds= */ new int[0],
|
|
/* currentItemId= */ C.INDEX_UNSET);
|
|
|
|
InOrder inOrder = Mockito.inOrder(mockListener);
|
|
inOrder
|
|
.verify(mockListener)
|
|
.onMediaItemTransition(any(), eq(Player.MEDIA_ITEM_TRANSITION_REASON_PLAYLIST_CHANGED));
|
|
inOrder
|
|
.verify(mockListener)
|
|
.onMediaItemTransition(
|
|
/* mediaItem= */ null, Player.MEDIA_ITEM_TRANSITION_REASON_PLAYLIST_CHANGED);
|
|
inOrder.verify(mockListener, never()).onMediaItemTransition(any(), anyInt());
|
|
}
|
|
|
|
@Test
|
|
@SuppressWarnings("deprecation") // Mocks deprecated method used by the CastPlayer.
|
|
public void clearMediaItems_notifiesPositionDiscontinuity() {
|
|
int[] mediaQueueItemIds = new int[] {1, 2};
|
|
List<MediaItem> mediaItems = createMediaItems(mediaQueueItemIds);
|
|
|
|
castPlayer.addMediaItems(mediaItems);
|
|
updateTimeLine(
|
|
mediaItems,
|
|
mediaQueueItemIds,
|
|
/* currentItemId= */ 1,
|
|
new int[] {MediaInfo.STREAM_TYPE_BUFFERED, MediaInfo.STREAM_TYPE_BUFFERED},
|
|
/* durationsMs= */ new long[] {20_000L, 30_000L},
|
|
/* positionMs= */ 1234);
|
|
castPlayer.clearMediaItems();
|
|
updateTimeLine(
|
|
/* mediaItems= */ ImmutableList.of(),
|
|
/* mediaQueueItemIds= */ new int[0],
|
|
/* currentItemId= */ C.INDEX_UNSET,
|
|
new int[] {MediaInfo.STREAM_TYPE_BUFFERED},
|
|
/* durationsMs= */ new long[] {20_000L},
|
|
/* positionMs= */ 0);
|
|
|
|
Player.PositionInfo oldPosition =
|
|
new Player.PositionInfo(
|
|
/* windowUid= */ 1,
|
|
/* windowIndex= */ 0,
|
|
new MediaItem.Builder().setUri(Uri.EMPTY).setTag(1).build(),
|
|
/* periodUid= */ 1,
|
|
/* periodIndex= */ 0,
|
|
/* positionMs= */ 1234,
|
|
/* contentPositionMs= */ 1234,
|
|
/* adGroupIndex= */ C.INDEX_UNSET,
|
|
/* adIndexInAdGroup= */ C.INDEX_UNSET);
|
|
Player.PositionInfo newPosition =
|
|
new Player.PositionInfo(
|
|
/* windowUid= */ null,
|
|
/* windowIndex= */ 0,
|
|
/* mediaItem= */ null,
|
|
/* periodUid= */ null,
|
|
/* periodIndex= */ 0,
|
|
/* positionMs= */ 0,
|
|
/* contentPositionMs= */ 0,
|
|
/* adGroupIndex= */ C.INDEX_UNSET,
|
|
/* adIndexInAdGroup= */ C.INDEX_UNSET);
|
|
InOrder inOrder = Mockito.inOrder(mockListener);
|
|
inOrder.verify(mockListener).onPositionDiscontinuity(eq(Player.DISCONTINUITY_REASON_REMOVE));
|
|
inOrder
|
|
.verify(mockListener)
|
|
.onPositionDiscontinuity(
|
|
eq(oldPosition), eq(newPosition), eq(Player.DISCONTINUITY_REASON_REMOVE));
|
|
inOrder.verify(mockListener, never()).onPositionDiscontinuity(anyInt());
|
|
inOrder.verify(mockListener, never()).onPositionDiscontinuity(any(), any(), anyInt());
|
|
}
|
|
|
|
@Test
|
|
public void removeCurrentMediaItem_notifiesMediaItemTransition() {
|
|
MediaItem mediaItem1 = createMediaItem(/* mediaQueueItemId= */ 1);
|
|
MediaItem mediaItem2 = createMediaItem(/* mediaQueueItemId= */ 2);
|
|
List<MediaItem> mediaItems = ImmutableList.of(mediaItem1, mediaItem2);
|
|
int[] mediaQueueItemIds = new int[] {1, 2};
|
|
|
|
castPlayer.addMediaItems(mediaItems);
|
|
updateTimeLine(mediaItems, mediaQueueItemIds, /* currentItemId= */ 1);
|
|
castPlayer.removeMediaItem(/* index= */ 0);
|
|
// Update with the new timeline after removal.
|
|
updateTimeLine(
|
|
ImmutableList.of(mediaItem2),
|
|
/* mediaQueueItemIds= */ new int[] {2},
|
|
/* currentItemId= */ 2);
|
|
|
|
InOrder inOrder = Mockito.inOrder(mockListener);
|
|
inOrder
|
|
.verify(mockListener, times(2))
|
|
.onMediaItemTransition(
|
|
mediaItemCaptor.capture(), eq(Player.MEDIA_ITEM_TRANSITION_REASON_PLAYLIST_CHANGED));
|
|
inOrder.verify(mockListener, never()).onMediaItemTransition(any(), anyInt());
|
|
assertThat(mediaItemCaptor.getAllValues().get(0).localConfiguration.tag)
|
|
.isEqualTo(mediaItem1.localConfiguration.tag);
|
|
assertThat(mediaItemCaptor.getAllValues().get(1).localConfiguration.tag)
|
|
.isEqualTo(mediaItem2.localConfiguration.tag);
|
|
}
|
|
|
|
@Test
|
|
@SuppressWarnings("deprecation") // Mocks deprecated method used by the CastPlayer.
|
|
public void removeCurrentMediaItem_notifiesPositionDiscontinuity() {
|
|
MediaItem mediaItem1 = createMediaItem(/* mediaQueueItemId= */ 1);
|
|
MediaItem mediaItem2 = createMediaItem(/* mediaQueueItemId= */ 2);
|
|
List<MediaItem> mediaItems = ImmutableList.of(mediaItem1, mediaItem2);
|
|
int[] mediaQueueItemIds = new int[] {1, 2};
|
|
|
|
castPlayer.addMediaItems(mediaItems);
|
|
updateTimeLine(
|
|
mediaItems,
|
|
mediaQueueItemIds,
|
|
/* currentItemId= */ 1,
|
|
new int[] {MediaInfo.STREAM_TYPE_BUFFERED, MediaInfo.STREAM_TYPE_BUFFERED},
|
|
/* durationsMs= */ new long[] {20_000L, 30_000L},
|
|
/* positionMs= */ 1234);
|
|
castPlayer.removeMediaItem(/* index= */ 0);
|
|
// Update with the new timeline after removal.
|
|
updateTimeLine(
|
|
ImmutableList.of(mediaItem2),
|
|
/* mediaQueueItemIds= */ new int[] {2},
|
|
/* currentItemId= */ 2,
|
|
new int[] {MediaInfo.STREAM_TYPE_BUFFERED},
|
|
/* durationsMs= */ new long[] {20_000L},
|
|
/* positionMs= */ 0);
|
|
|
|
Player.PositionInfo oldPosition =
|
|
new Player.PositionInfo(
|
|
/* windowUid= */ 1,
|
|
/* windowIndex= */ 0,
|
|
new MediaItem.Builder().setUri(Uri.EMPTY).setTag(1).build(),
|
|
/* periodUid= */ 1,
|
|
/* periodIndex= */ 0,
|
|
/* positionMs= */ 1234,
|
|
/* contentPositionMs= */ 1234,
|
|
/* adGroupIndex= */ C.INDEX_UNSET,
|
|
/* adIndexInAdGroup= */ C.INDEX_UNSET);
|
|
Player.PositionInfo newPosition =
|
|
new Player.PositionInfo(
|
|
/* windowUid= */ 2,
|
|
/* windowIndex= */ 0,
|
|
new MediaItem.Builder().setUri(Uri.EMPTY).setTag(2).build(),
|
|
/* periodUid= */ 2,
|
|
/* periodIndex= */ 0,
|
|
/* positionMs= */ 0,
|
|
/* contentPositionMs= */ 0,
|
|
/* adGroupIndex= */ C.INDEX_UNSET,
|
|
/* adIndexInAdGroup= */ C.INDEX_UNSET);
|
|
InOrder inOrder = Mockito.inOrder(mockListener);
|
|
inOrder.verify(mockListener).onPositionDiscontinuity(eq(Player.DISCONTINUITY_REASON_REMOVE));
|
|
inOrder
|
|
.verify(mockListener)
|
|
.onPositionDiscontinuity(
|
|
eq(oldPosition), eq(newPosition), eq(Player.DISCONTINUITY_REASON_REMOVE));
|
|
inOrder.verify(mockListener, never()).onPositionDiscontinuity(anyInt());
|
|
inOrder.verify(mockListener, never()).onPositionDiscontinuity(any(), any(), anyInt());
|
|
}
|
|
|
|
@Test
|
|
public void removeCurrentMediaItem_byRemoteClient_notifiesMediaItemTransition() {
|
|
MediaItem mediaItem1 = createMediaItem(/* mediaQueueItemId= */ 1);
|
|
MediaItem mediaItem2 = createMediaItem(/* mediaQueueItemId= */ 2);
|
|
List<MediaItem> mediaItems = ImmutableList.of(mediaItem1, mediaItem2);
|
|
|
|
castPlayer.addMediaItems(mediaItems);
|
|
updateTimeLine(mediaItems, new int[] {1, 2}, /* currentItemId= */ 1);
|
|
// Update with the new timeline after removal on the device.
|
|
updateTimeLine(
|
|
ImmutableList.of(mediaItem2),
|
|
/* mediaQueueItemIds= */ new int[] {2},
|
|
/* currentItemId= */ 2);
|
|
|
|
InOrder inOrder = Mockito.inOrder(mockListener);
|
|
inOrder
|
|
.verify(mockListener, times(2))
|
|
.onMediaItemTransition(
|
|
mediaItemCaptor.capture(), eq(Player.MEDIA_ITEM_TRANSITION_REASON_PLAYLIST_CHANGED));
|
|
inOrder.verify(mockListener, never()).onMediaItemTransition(any(), anyInt());
|
|
List<MediaItem> capturedMediaItems = mediaItemCaptor.getAllValues();
|
|
assertThat(capturedMediaItems.get(0).localConfiguration.tag)
|
|
.isEqualTo(mediaItem1.localConfiguration.tag);
|
|
assertThat(capturedMediaItems.get(1).localConfiguration.tag)
|
|
.isEqualTo(mediaItem2.localConfiguration.tag);
|
|
}
|
|
|
|
@Test
|
|
@SuppressWarnings("deprecation") // Mocks deprecated method used by the CastPlayer.
|
|
public void removeCurrentMediaItem_byRemoteClient_notifiesPositionDiscontinuity() {
|
|
MediaItem mediaItem1 = createMediaItem(/* mediaQueueItemId= */ 1);
|
|
MediaItem mediaItem2 = createMediaItem(/* mediaQueueItemId= */ 2);
|
|
List<MediaItem> mediaItems = ImmutableList.of(mediaItem1, mediaItem2);
|
|
|
|
castPlayer.addMediaItems(mediaItems);
|
|
updateTimeLine(
|
|
mediaItems,
|
|
new int[] {1, 2},
|
|
/* currentItemId= */ 1,
|
|
new int[] {MediaInfo.STREAM_TYPE_BUFFERED, MediaInfo.STREAM_TYPE_BUFFERED},
|
|
/* durationsMs= */ new long[] {20_000L, 30_000L},
|
|
/* positionMs= */ 1234);
|
|
// Update with the new timeline after removal on the device.
|
|
updateTimeLine(
|
|
ImmutableList.of(mediaItem2),
|
|
/* mediaQueueItemIds= */ new int[] {2},
|
|
/* currentItemId= */ 2,
|
|
new int[] {MediaInfo.STREAM_TYPE_BUFFERED},
|
|
/* durationsMs= */ new long[] {30_000L},
|
|
/* positionMs= */ 0);
|
|
|
|
Player.PositionInfo oldPosition =
|
|
new Player.PositionInfo(
|
|
/* windowUid= */ 1,
|
|
/* windowIndex= */ 0,
|
|
new MediaItem.Builder().setUri(Uri.EMPTY).setTag(1).build(),
|
|
/* periodUid= */ 1,
|
|
/* periodIndex= */ 0,
|
|
/* positionMs= */ 0, // position at which we receive the timeline change
|
|
/* contentPositionMs= */ 0, // position at which we receive the timeline change
|
|
/* adGroupIndex= */ C.INDEX_UNSET,
|
|
/* adIndexInAdGroup= */ C.INDEX_UNSET);
|
|
Player.PositionInfo newPosition =
|
|
new Player.PositionInfo(
|
|
/* windowUid= */ 2,
|
|
/* windowIndex= */ 0,
|
|
new MediaItem.Builder().setUri(Uri.EMPTY).setTag(2).build(),
|
|
/* periodUid= */ 2,
|
|
/* periodIndex= */ 0,
|
|
/* positionMs= */ 0,
|
|
/* contentPositionMs= */ 0,
|
|
/* adGroupIndex= */ C.INDEX_UNSET,
|
|
/* adIndexInAdGroup= */ C.INDEX_UNSET);
|
|
InOrder inOrder = Mockito.inOrder(mockListener);
|
|
inOrder.verify(mockListener).onPositionDiscontinuity(eq(Player.DISCONTINUITY_REASON_REMOVE));
|
|
inOrder
|
|
.verify(mockListener)
|
|
.onPositionDiscontinuity(
|
|
eq(oldPosition), eq(newPosition), eq(Player.DISCONTINUITY_REASON_REMOVE));
|
|
inOrder.verify(mockListener, never()).onPositionDiscontinuity(anyInt());
|
|
inOrder.verify(mockListener, never()).onPositionDiscontinuity(any(), any(), anyInt());
|
|
}
|
|
|
|
@Test
|
|
public void removeNonCurrentMediaItem_doesNotNotifyMediaItemTransition() {
|
|
MediaItem mediaItem1 = createMediaItem(/* mediaQueueItemId= */ 1);
|
|
MediaItem mediaItem2 = createMediaItem(/* mediaQueueItemId= */ 2);
|
|
List<MediaItem> mediaItems = ImmutableList.of(mediaItem1, mediaItem2);
|
|
int[] mediaQueueItemIds = new int[] {1, 2};
|
|
|
|
castPlayer.addMediaItems(mediaItems);
|
|
updateTimeLine(mediaItems, mediaQueueItemIds, /* currentItemId= */ 1);
|
|
castPlayer.removeMediaItem(/* index= */ 1);
|
|
updateTimeLine(
|
|
ImmutableList.of(mediaItem1),
|
|
/* mediaQueueItemIds= */ new int[] {1},
|
|
/* currentItemId= */ 1);
|
|
|
|
InOrder inOrder = Mockito.inOrder(mockListener);
|
|
inOrder
|
|
.verify(mockListener)
|
|
.onMediaItemTransition(any(), eq(Player.MEDIA_ITEM_TRANSITION_REASON_PLAYLIST_CHANGED));
|
|
inOrder.verify(mockListener, never()).onMediaItemTransition(any(), anyInt());
|
|
}
|
|
|
|
@Test
|
|
@SuppressWarnings("deprecation") // Mocks deprecated method used by the CastPlayer.
|
|
public void removeNonCurrentMediaItem_doesNotNotifyPositionDiscontinuity() {
|
|
MediaItem mediaItem1 = createMediaItem(/* mediaQueueItemId= */ 1);
|
|
MediaItem mediaItem2 = createMediaItem(/* mediaQueueItemId= */ 2);
|
|
List<MediaItem> mediaItems = ImmutableList.of(mediaItem1, mediaItem2);
|
|
int[] mediaQueueItemIds = new int[] {1, 2};
|
|
|
|
castPlayer.addMediaItems(mediaItems);
|
|
updateTimeLine(mediaItems, mediaQueueItemIds, /* currentItemId= */ 1);
|
|
castPlayer.removeMediaItem(/* index= */ 1);
|
|
updateTimeLine(
|
|
ImmutableList.of(mediaItem1),
|
|
/* mediaQueueItemIds= */ new int[] {1},
|
|
/* currentItemId= */ 1);
|
|
|
|
verify(mockListener, never()).onPositionDiscontinuity(anyInt());
|
|
verify(mockListener, never()).onPositionDiscontinuity(any(), any(), anyInt());
|
|
}
|
|
|
|
@Test
|
|
public void seekTo_otherWindow_notifiesMediaItemTransition() {
|
|
when(mockRemoteMediaClient.queueJumpToItem(anyInt(), anyLong(), eq(null)))
|
|
.thenReturn(mockPendingResult);
|
|
MediaItem mediaItem1 = createMediaItem(/* mediaQueueItemId= */ 1);
|
|
MediaItem mediaItem2 = createMediaItem(/* mediaQueueItemId= */ 2);
|
|
List<MediaItem> mediaItems = ImmutableList.of(mediaItem1, mediaItem2);
|
|
int[] mediaQueueItemIds = new int[] {1, 2};
|
|
|
|
castPlayer.addMediaItems(mediaItems);
|
|
updateTimeLine(mediaItems, mediaQueueItemIds, /* currentItemId= */ 1);
|
|
castPlayer.seekTo(/* windowIndex= */ 1, /* positionMs= */ 1234);
|
|
|
|
InOrder inOrder = Mockito.inOrder(mockListener);
|
|
inOrder
|
|
.verify(mockListener)
|
|
.onMediaItemTransition(any(), eq(Player.MEDIA_ITEM_TRANSITION_REASON_PLAYLIST_CHANGED));
|
|
inOrder
|
|
.verify(mockListener)
|
|
.onMediaItemTransition(
|
|
mediaItemCaptor.capture(), eq(Player.MEDIA_ITEM_TRANSITION_REASON_SEEK));
|
|
inOrder.verify(mockListener, never()).onPositionDiscontinuity(any(), any(), anyInt());
|
|
assertThat(mediaItemCaptor.getValue().localConfiguration.tag)
|
|
.isEqualTo(mediaItem2.localConfiguration.tag);
|
|
}
|
|
|
|
@Test
|
|
@SuppressWarnings("deprecation") // Mocks deprecated method used by the CastPlayer.
|
|
public void seekTo_otherWindow_notifiesPositionDiscontinuity() {
|
|
when(mockRemoteMediaClient.queueJumpToItem(anyInt(), anyLong(), eq(null)))
|
|
.thenReturn(mockPendingResult);
|
|
MediaItem mediaItem1 = createMediaItem(/* mediaQueueItemId= */ 1);
|
|
MediaItem mediaItem2 = createMediaItem(/* mediaQueueItemId= */ 2);
|
|
List<MediaItem> mediaItems = ImmutableList.of(mediaItem1, mediaItem2);
|
|
int[] mediaQueueItemIds = new int[] {1, 2};
|
|
|
|
castPlayer.addMediaItems(mediaItems);
|
|
updateTimeLine(mediaItems, mediaQueueItemIds, /* currentItemId= */ 1);
|
|
castPlayer.seekTo(/* windowIndex= */ 1, /* positionMs= */ 1234);
|
|
|
|
Player.PositionInfo oldPosition =
|
|
new Player.PositionInfo(
|
|
/* windowUid= */ 1,
|
|
/* windowIndex= */ 0,
|
|
new MediaItem.Builder().setUri(Uri.EMPTY).setTag(1).build(),
|
|
/* periodUid= */ 1,
|
|
/* periodIndex= */ 0,
|
|
/* positionMs= */ 0,
|
|
/* contentPositionMs= */ 0,
|
|
/* adGroupIndex= */ C.INDEX_UNSET,
|
|
/* adIndexInAdGroup= */ C.INDEX_UNSET);
|
|
Player.PositionInfo newPosition =
|
|
new Player.PositionInfo(
|
|
/* windowUid= */ 2,
|
|
/* windowIndex= */ 1,
|
|
new MediaItem.Builder().setUri(Uri.EMPTY).setTag(2).build(),
|
|
/* periodUid= */ 2,
|
|
/* periodIndex= */ 1,
|
|
/* positionMs= */ 1234,
|
|
/* contentPositionMs= */ 1234,
|
|
/* adGroupIndex= */ C.INDEX_UNSET,
|
|
/* adIndexInAdGroup= */ C.INDEX_UNSET);
|
|
InOrder inOrder = Mockito.inOrder(mockListener);
|
|
inOrder.verify(mockListener).onPositionDiscontinuity(eq(Player.DISCONTINUITY_REASON_SEEK));
|
|
inOrder
|
|
.verify(mockListener)
|
|
.onPositionDiscontinuity(
|
|
eq(oldPosition), eq(newPosition), eq(Player.DISCONTINUITY_REASON_SEEK));
|
|
inOrder.verify(mockListener, never()).onPositionDiscontinuity(anyInt());
|
|
inOrder.verify(mockListener, never()).onPositionDiscontinuity(any(), any(), anyInt());
|
|
}
|
|
|
|
@Test
|
|
@SuppressWarnings("deprecation") // Mocks deprecated method used by the CastPlayer.
|
|
public void seekTo_sameWindow_doesNotNotifyMediaItemTransition() {
|
|
when(mockRemoteMediaClient.seek(anyLong())).thenReturn(mockPendingResult);
|
|
int[] mediaQueueItemIds = new int[] {1, 2};
|
|
List<MediaItem> mediaItems = createMediaItems(mediaQueueItemIds);
|
|
|
|
castPlayer.addMediaItems(mediaItems);
|
|
updateTimeLine(mediaItems, mediaQueueItemIds, /* currentItemId= */ 1);
|
|
castPlayer.seekTo(/* windowIndex= */ 0, /* positionMs= */ 1234);
|
|
|
|
InOrder inOrder = Mockito.inOrder(mockListener);
|
|
inOrder
|
|
.verify(mockListener)
|
|
.onMediaItemTransition(any(), eq(Player.MEDIA_ITEM_TRANSITION_REASON_PLAYLIST_CHANGED));
|
|
inOrder.verify(mockListener, never()).onMediaItemTransition(any(), anyInt());
|
|
}
|
|
|
|
@Test
|
|
@SuppressWarnings("deprecation") // Mocks deprecated method used by the CastPlayer.
|
|
public void seekTo_sameWindow_notifiesPositionDiscontinuity() {
|
|
when(mockRemoteMediaClient.seek(anyLong())).thenReturn(mockPendingResult);
|
|
int[] mediaQueueItemIds = new int[] {1, 2};
|
|
List<MediaItem> mediaItems = createMediaItems(mediaQueueItemIds);
|
|
|
|
castPlayer.addMediaItems(mediaItems);
|
|
updateTimeLine(mediaItems, mediaQueueItemIds, /* currentItemId= */ 1);
|
|
castPlayer.seekTo(/* windowIndex= */ 0, /* positionMs= */ 1234);
|
|
|
|
MediaItem mediaItem = new MediaItem.Builder().setUri(Uri.EMPTY).setTag(1).build();
|
|
Player.PositionInfo oldPosition =
|
|
new Player.PositionInfo(
|
|
/* windowUid= */ 1,
|
|
/* windowIndex= */ 0,
|
|
mediaItem,
|
|
/* periodUid= */ 1,
|
|
/* periodIndex= */ 0,
|
|
/* positionMs= */ 0,
|
|
/* contentPositionMs= */ 0,
|
|
/* adGroupIndex= */ C.INDEX_UNSET,
|
|
/* adIndexInAdGroup= */ C.INDEX_UNSET);
|
|
Player.PositionInfo newPosition =
|
|
new Player.PositionInfo(
|
|
/* windowUid= */ 1,
|
|
/* windowIndex= */ 0,
|
|
mediaItem,
|
|
/* periodUid= */ 1,
|
|
/* periodIndex= */ 0,
|
|
/* positionMs= */ 1234,
|
|
/* contentPositionMs= */ 1234,
|
|
/* adGroupIndex= */ C.INDEX_UNSET,
|
|
/* adIndexInAdGroup= */ C.INDEX_UNSET);
|
|
InOrder inOrder = Mockito.inOrder(mockListener);
|
|
inOrder.verify(mockListener).onPositionDiscontinuity(eq(Player.DISCONTINUITY_REASON_SEEK));
|
|
inOrder
|
|
.verify(mockListener)
|
|
.onPositionDiscontinuity(
|
|
eq(oldPosition), eq(newPosition), eq(Player.DISCONTINUITY_REASON_SEEK));
|
|
inOrder.verify(mockListener, never()).onPositionDiscontinuity(anyInt());
|
|
inOrder.verify(mockListener, never()).onPositionDiscontinuity(any(), any(), anyInt());
|
|
}
|
|
|
|
@Test
|
|
public void autoTransition_notifiesMediaItemTransition() {
|
|
int[] mediaQueueItemIds = new int[] {1, 2};
|
|
// When the remote Cast player transitions to an item that wasn't played before, the media state
|
|
// delivers the duration for that media item which updates the timeline accordingly.
|
|
List<MediaItem> mediaItems = createMediaItems(mediaQueueItemIds);
|
|
|
|
castPlayer.addMediaItems(mediaItems);
|
|
updateTimeLine(mediaItems, mediaQueueItemIds, /* currentItemId= */ 1);
|
|
updateTimeLine(mediaItems, mediaQueueItemIds, /* currentItemId= */ 2);
|
|
|
|
InOrder inOrder = Mockito.inOrder(mockListener);
|
|
inOrder
|
|
.verify(mockListener)
|
|
.onMediaItemTransition(any(), eq(Player.MEDIA_ITEM_TRANSITION_REASON_PLAYLIST_CHANGED));
|
|
inOrder
|
|
.verify(mockListener)
|
|
.onMediaItemTransition(
|
|
mediaItemCaptor.capture(), eq(Player.MEDIA_ITEM_TRANSITION_REASON_AUTO));
|
|
inOrder.verify(mockListener, never()).onMediaItemTransition(any(), anyInt());
|
|
assertThat(mediaItemCaptor.getValue().localConfiguration.tag).isEqualTo(2);
|
|
}
|
|
|
|
@Test
|
|
@SuppressWarnings("deprecation") // Mocks deprecated method used by the CastPlayer.
|
|
public void autoTransition_notifiesPositionDiscontinuity() {
|
|
int[] mediaQueueItemIds = new int[] {1, 2};
|
|
int[] streamTypes = {MediaInfo.STREAM_TYPE_BUFFERED, MediaInfo.STREAM_TYPE_BUFFERED};
|
|
long[] durationsFirstMs = {12500, C.TIME_UNSET};
|
|
// When the remote Cast player transitions to an item that wasn't played before, the media state
|
|
// delivers the duration for that media item which updates the timeline accordingly.
|
|
long[] durationsSecondMs = {12500, 22000};
|
|
List<MediaItem> mediaItems = createMediaItems(mediaQueueItemIds);
|
|
|
|
castPlayer.addMediaItems(mediaItems);
|
|
updateTimeLine(
|
|
mediaItems,
|
|
mediaQueueItemIds,
|
|
/* currentItemId= */ 1,
|
|
/* streamTypes= */ streamTypes,
|
|
/* durationsMs= */ durationsFirstMs,
|
|
/* positionMs= */ C.TIME_UNSET);
|
|
updateTimeLine(
|
|
mediaItems,
|
|
mediaQueueItemIds,
|
|
/* currentItemId= */ 2,
|
|
/* streamTypes= */ streamTypes,
|
|
/* durationsMs= */ durationsSecondMs,
|
|
/* positionMs= */ C.TIME_UNSET);
|
|
|
|
Player.PositionInfo oldPosition =
|
|
new Player.PositionInfo(
|
|
/* windowUid= */ 1,
|
|
/* windowIndex= */ 0,
|
|
new MediaItem.Builder().setUri(Uri.EMPTY).setTag(1).build(),
|
|
/* periodUid= */ 1,
|
|
/* periodIndex= */ 0,
|
|
/* positionMs= */ 12500,
|
|
/* contentPositionMs= */ 12500,
|
|
/* adGroupIndex= */ C.INDEX_UNSET,
|
|
/* adIndexInAdGroup= */ C.INDEX_UNSET);
|
|
Player.PositionInfo newPosition =
|
|
new Player.PositionInfo(
|
|
/* windowUid= */ 2,
|
|
/* windowIndex= */ 1,
|
|
new MediaItem.Builder().setUri(Uri.EMPTY).setTag(2).build(),
|
|
/* periodUid= */ 2,
|
|
/* periodIndex= */ 1,
|
|
/* positionMs= */ 0,
|
|
/* contentPositionMs= */ 0,
|
|
/* adGroupIndex= */ C.INDEX_UNSET,
|
|
/* adIndexInAdGroup= */ C.INDEX_UNSET);
|
|
InOrder inOrder = Mockito.inOrder(mockListener);
|
|
inOrder
|
|
.verify(mockListener)
|
|
.onPositionDiscontinuity(eq(Player.DISCONTINUITY_REASON_AUTO_TRANSITION));
|
|
inOrder
|
|
.verify(mockListener)
|
|
.onPositionDiscontinuity(
|
|
eq(oldPosition), eq(newPosition), eq(Player.DISCONTINUITY_REASON_AUTO_TRANSITION));
|
|
inOrder.verify(mockListener, never()).onPositionDiscontinuity(anyInt());
|
|
inOrder.verify(mockListener, never()).onPositionDiscontinuity(any(), any(), anyInt());
|
|
}
|
|
|
|
@Test
|
|
@SuppressWarnings("deprecation") // Mocks deprecated method used by the CastPlayer.
|
|
public void seekBack_notifiesPositionDiscontinuity() {
|
|
when(mockRemoteMediaClient.seek(anyLong())).thenReturn(mockPendingResult);
|
|
int[] mediaQueueItemIds = new int[] {1};
|
|
List<MediaItem> mediaItems = createMediaItems(mediaQueueItemIds);
|
|
int currentItemId = 1;
|
|
int[] streamTypes = new int[] {MediaInfo.STREAM_TYPE_BUFFERED};
|
|
long[] durationsMs = new long[] {3 * C.DEFAULT_SEEK_BACK_INCREMENT_MS};
|
|
long positionMs = 2 * C.DEFAULT_SEEK_BACK_INCREMENT_MS;
|
|
|
|
castPlayer.addMediaItems(mediaItems);
|
|
updateTimeLine(
|
|
mediaItems, mediaQueueItemIds, currentItemId, streamTypes, durationsMs, positionMs);
|
|
castPlayer.seekBack();
|
|
|
|
MediaItem mediaItem = new MediaItem.Builder().setUri(Uri.EMPTY).setTag(1).build();
|
|
Player.PositionInfo oldPosition =
|
|
new Player.PositionInfo(
|
|
/* windowUid= */ 1,
|
|
/* windowIndex= */ 0,
|
|
mediaItem,
|
|
/* periodUid= */ 1,
|
|
/* periodIndex= */ 0,
|
|
/* positionMs= */ 2 * C.DEFAULT_SEEK_BACK_INCREMENT_MS,
|
|
/* contentPositionMs= */ 2 * C.DEFAULT_SEEK_BACK_INCREMENT_MS,
|
|
/* adGroupIndex= */ C.INDEX_UNSET,
|
|
/* adIndexInAdGroup= */ C.INDEX_UNSET);
|
|
Player.PositionInfo newPosition =
|
|
new Player.PositionInfo(
|
|
/* windowUid= */ 1,
|
|
/* windowIndex= */ 0,
|
|
mediaItem,
|
|
/* periodUid= */ 1,
|
|
/* periodIndex= */ 0,
|
|
/* positionMs= */ C.DEFAULT_SEEK_BACK_INCREMENT_MS,
|
|
/* contentPositionMs= */ C.DEFAULT_SEEK_BACK_INCREMENT_MS,
|
|
/* adGroupIndex= */ C.INDEX_UNSET,
|
|
/* adIndexInAdGroup= */ C.INDEX_UNSET);
|
|
InOrder inOrder = Mockito.inOrder(mockListener);
|
|
inOrder.verify(mockListener).onPositionDiscontinuity(eq(Player.DISCONTINUITY_REASON_SEEK));
|
|
inOrder
|
|
.verify(mockListener)
|
|
.onPositionDiscontinuity(
|
|
eq(oldPosition), eq(newPosition), eq(Player.DISCONTINUITY_REASON_SEEK));
|
|
inOrder.verify(mockListener, never()).onPositionDiscontinuity(anyInt());
|
|
inOrder.verify(mockListener, never()).onPositionDiscontinuity(any(), any(), anyInt());
|
|
}
|
|
|
|
@Test
|
|
@SuppressWarnings("deprecation") // Mocks deprecated method used by the CastPlayer.
|
|
public void seekForward_notifiesPositionDiscontinuity() {
|
|
when(mockRemoteMediaClient.seek(anyLong())).thenReturn(mockPendingResult);
|
|
int[] mediaQueueItemIds = new int[] {1};
|
|
List<MediaItem> mediaItems = createMediaItems(mediaQueueItemIds);
|
|
int currentItemId = 1;
|
|
int[] streamTypes = new int[] {MediaInfo.STREAM_TYPE_BUFFERED};
|
|
long[] durationsMs = new long[] {2 * C.DEFAULT_SEEK_FORWARD_INCREMENT_MS};
|
|
long positionMs = 0;
|
|
|
|
castPlayer.addMediaItems(mediaItems);
|
|
updateTimeLine(
|
|
mediaItems, mediaQueueItemIds, currentItemId, streamTypes, durationsMs, positionMs);
|
|
castPlayer.seekForward();
|
|
|
|
MediaItem mediaItem = new MediaItem.Builder().setUri(Uri.EMPTY).setTag(1).build();
|
|
Player.PositionInfo oldPosition =
|
|
new Player.PositionInfo(
|
|
/* windowUid= */ 1,
|
|
/* windowIndex= */ 0,
|
|
mediaItem,
|
|
/* periodUid= */ 1,
|
|
/* periodIndex= */ 0,
|
|
/* positionMs= */ 0,
|
|
/* contentPositionMs= */ 0,
|
|
/* adGroupIndex= */ C.INDEX_UNSET,
|
|
/* adIndexInAdGroup= */ C.INDEX_UNSET);
|
|
Player.PositionInfo newPosition =
|
|
new Player.PositionInfo(
|
|
/* windowUid= */ 1,
|
|
/* windowIndex= */ 0,
|
|
mediaItem,
|
|
/* periodUid= */ 1,
|
|
/* periodIndex= */ 0,
|
|
/* positionMs= */ C.DEFAULT_SEEK_FORWARD_INCREMENT_MS,
|
|
/* contentPositionMs= */ C.DEFAULT_SEEK_FORWARD_INCREMENT_MS,
|
|
/* adGroupIndex= */ C.INDEX_UNSET,
|
|
/* adIndexInAdGroup= */ C.INDEX_UNSET);
|
|
InOrder inOrder = Mockito.inOrder(mockListener);
|
|
inOrder.verify(mockListener).onPositionDiscontinuity(eq(Player.DISCONTINUITY_REASON_SEEK));
|
|
inOrder
|
|
.verify(mockListener)
|
|
.onPositionDiscontinuity(
|
|
eq(oldPosition), eq(newPosition), eq(Player.DISCONTINUITY_REASON_SEEK));
|
|
inOrder.verify(mockListener, never()).onPositionDiscontinuity(anyInt());
|
|
inOrder.verify(mockListener, never()).onPositionDiscontinuity(any(), any(), anyInt());
|
|
}
|
|
|
|
@Test
|
|
public void isCommandAvailable_isTrueForAvailableCommands() {
|
|
int[] mediaQueueItemIds = new int[] {1, 2};
|
|
List<MediaItem> mediaItems = createMediaItems(mediaQueueItemIds);
|
|
|
|
castPlayer.addMediaItems(mediaItems);
|
|
updateTimeLine(mediaItems, mediaQueueItemIds, /* currentItemId= */ 1);
|
|
|
|
assertThat(castPlayer.isCommandAvailable(COMMAND_PLAY_PAUSE)).isTrue();
|
|
assertThat(castPlayer.isCommandAvailable(COMMAND_PREPARE)).isTrue();
|
|
assertThat(castPlayer.isCommandAvailable(COMMAND_STOP)).isTrue();
|
|
assertThat(castPlayer.isCommandAvailable(COMMAND_SEEK_TO_DEFAULT_POSITION)).isTrue();
|
|
assertThat(castPlayer.isCommandAvailable(COMMAND_SEEK_IN_CURRENT_MEDIA_ITEM)).isTrue();
|
|
assertThat(castPlayer.isCommandAvailable(COMMAND_SEEK_TO_PREVIOUS_MEDIA_ITEM)).isFalse();
|
|
assertThat(castPlayer.isCommandAvailable(COMMAND_SEEK_TO_PREVIOUS)).isTrue();
|
|
assertThat(castPlayer.isCommandAvailable(COMMAND_SEEK_TO_NEXT_MEDIA_ITEM)).isTrue();
|
|
assertThat(castPlayer.isCommandAvailable(COMMAND_SEEK_TO_NEXT)).isTrue();
|
|
assertThat(castPlayer.isCommandAvailable(COMMAND_SEEK_TO_MEDIA_ITEM)).isTrue();
|
|
assertThat(castPlayer.isCommandAvailable(COMMAND_SEEK_BACK)).isTrue();
|
|
assertThat(castPlayer.isCommandAvailable(COMMAND_SEEK_FORWARD)).isTrue();
|
|
assertThat(castPlayer.isCommandAvailable(COMMAND_SET_SPEED_AND_PITCH)).isTrue();
|
|
assertThat(castPlayer.isCommandAvailable(COMMAND_SET_SHUFFLE_MODE)).isFalse();
|
|
assertThat(castPlayer.isCommandAvailable(COMMAND_SET_REPEAT_MODE)).isTrue();
|
|
assertThat(castPlayer.isCommandAvailable(COMMAND_GET_CURRENT_MEDIA_ITEM)).isTrue();
|
|
assertThat(castPlayer.isCommandAvailable(COMMAND_GET_TIMELINE)).isTrue();
|
|
assertThat(castPlayer.isCommandAvailable(COMMAND_GET_MEDIA_ITEMS_METADATA)).isTrue();
|
|
assertThat(castPlayer.isCommandAvailable(COMMAND_SET_MEDIA_ITEMS_METADATA)).isTrue();
|
|
assertThat(castPlayer.isCommandAvailable(COMMAND_CHANGE_MEDIA_ITEMS)).isTrue();
|
|
assertThat(castPlayer.isCommandAvailable(COMMAND_SET_MEDIA_ITEM)).isTrue();
|
|
assertThat(castPlayer.isCommandAvailable(COMMAND_GET_AUDIO_ATTRIBUTES)).isFalse();
|
|
assertThat(castPlayer.isCommandAvailable(COMMAND_GET_VOLUME)).isFalse();
|
|
assertThat(castPlayer.isCommandAvailable(COMMAND_GET_DEVICE_VOLUME)).isFalse();
|
|
assertThat(castPlayer.isCommandAvailable(COMMAND_SET_VOLUME)).isFalse();
|
|
assertThat(castPlayer.isCommandAvailable(COMMAND_SET_DEVICE_VOLUME)).isFalse();
|
|
assertThat(castPlayer.isCommandAvailable(COMMAND_ADJUST_DEVICE_VOLUME)).isFalse();
|
|
assertThat(castPlayer.isCommandAvailable(COMMAND_SET_VIDEO_SURFACE)).isFalse();
|
|
assertThat(castPlayer.isCommandAvailable(COMMAND_GET_TEXT)).isFalse();
|
|
}
|
|
|
|
@Test
|
|
public void isCommandAvailable_duringUnseekableItem_isFalseForSeekInCurrentCommands() {
|
|
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_IN_CURRENT_MEDIA_ITEM)).isFalse();
|
|
assertThat(castPlayer.isCommandAvailable(COMMAND_SEEK_BACK)).isFalse();
|
|
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 isCommandAvailable_duringLiveItem_isTrueForSeekToNext() {
|
|
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_NEXT)).isTrue();
|
|
}
|
|
|
|
@Test
|
|
public void seekTo_nextWindow_notifiesAvailableCommandsChanged() {
|
|
when(mockRemoteMediaClient.queueJumpToItem(anyInt(), anyLong(), eq(null)))
|
|
.thenReturn(mockPendingResult);
|
|
Player.Commands commandsWithSeekToPreviousWindow =
|
|
createWithDefaultCommands(COMMAND_SEEK_TO_PREVIOUS_MEDIA_ITEM);
|
|
Player.Commands commandsWithSeekToNextWindow =
|
|
createWithDefaultCommands(COMMAND_SEEK_TO_NEXT_MEDIA_ITEM, COMMAND_SEEK_TO_NEXT);
|
|
Player.Commands commandsWithSeekToPreviousAndNextWindow =
|
|
createWithDefaultCommands(
|
|
COMMAND_SEEK_TO_PREVIOUS_MEDIA_ITEM,
|
|
COMMAND_SEEK_TO_NEXT_MEDIA_ITEM,
|
|
COMMAND_SEEK_TO_NEXT);
|
|
int[] mediaQueueItemIds = new int[] {1, 2, 3, 4};
|
|
List<MediaItem> mediaItems = createMediaItems(mediaQueueItemIds);
|
|
|
|
castPlayer.addMediaItems(mediaItems);
|
|
updateTimeLine(mediaItems, mediaQueueItemIds, /* currentItemId= */ 1);
|
|
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(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(commandsWithSeekToPreviousWindow);
|
|
verify(mockListener, times(3)).onAvailableCommandsChanged(any());
|
|
}
|
|
|
|
@Test
|
|
public void seekTo_previousWindow_notifiesAvailableCommandsChanged() {
|
|
when(mockRemoteMediaClient.queueJumpToItem(anyInt(), anyLong(), eq(null)))
|
|
.thenReturn(mockPendingResult);
|
|
Player.Commands commandsWithSeekToPreviousWindow =
|
|
createWithDefaultCommands(COMMAND_SEEK_TO_PREVIOUS_MEDIA_ITEM);
|
|
Player.Commands commandsWithSeekToNextWindow =
|
|
createWithDefaultCommands(COMMAND_SEEK_TO_NEXT_MEDIA_ITEM, COMMAND_SEEK_TO_NEXT);
|
|
Player.Commands commandsWithSeekToPreviousAndNextWindow =
|
|
createWithDefaultCommands(
|
|
COMMAND_SEEK_TO_PREVIOUS_MEDIA_ITEM,
|
|
COMMAND_SEEK_TO_NEXT_MEDIA_ITEM,
|
|
COMMAND_SEEK_TO_NEXT);
|
|
int[] mediaQueueItemIds = new int[] {1, 2, 3, 4};
|
|
List<MediaItem> mediaItems = createMediaItems(mediaQueueItemIds);
|
|
|
|
castPlayer.addMediaItems(mediaItems);
|
|
updateTimeLine(mediaItems, mediaQueueItemIds, /* currentItemId= */ 4);
|
|
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(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(commandsWithSeekToNextWindow);
|
|
verify(mockListener, times(3)).onAvailableCommandsChanged(any());
|
|
}
|
|
|
|
@Test
|
|
@SuppressWarnings("deprecation") // Mocks deprecated method used by the CastPlayer.
|
|
public void seekTo_sameWindow_doesNotNotifyAvailableCommandsChanged() {
|
|
when(mockRemoteMediaClient.seek(anyLong())).thenReturn(mockPendingResult);
|
|
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(defaultCommands);
|
|
|
|
castPlayer.seekTo(/* windowIndex= */ 0, /* positionMs= */ 200);
|
|
castPlayer.seekTo(/* windowIndex= */ 0, /* positionMs= */ 100);
|
|
// Check that there were no other calls to onAvailableCommandsChanged.
|
|
verify(mockListener).onAvailableCommandsChanged(any());
|
|
}
|
|
|
|
@Test
|
|
public void addMediaItem_atTheEnd_notifiesAvailableCommandsChanged() {
|
|
Player.Commands defaultCommands = createWithDefaultCommands();
|
|
Player.Commands commandsWithSeekToNextWindow =
|
|
createWithDefaultCommands(COMMAND_SEEK_TO_NEXT_MEDIA_ITEM, COMMAND_SEEK_TO_NEXT);
|
|
MediaItem mediaItem1 = createMediaItem(/* mediaQueueItemId= */ 1);
|
|
MediaItem mediaItem2 = createMediaItem(/* mediaQueueItemId= */ 2);
|
|
MediaItem mediaItem3 = createMediaItem(/* mediaQueueItemId= */ 3);
|
|
|
|
castPlayer.addMediaItem(mediaItem1);
|
|
updateTimeLine(
|
|
ImmutableList.of(mediaItem1),
|
|
/* mediaQueueItemIds= */ new int[] {1},
|
|
/* currentItemId= */ 1);
|
|
verify(mockListener).onAvailableCommandsChanged(defaultCommands);
|
|
// 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(commandsWithSeekToNextWindow);
|
|
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, times(2)).onAvailableCommandsChanged(any());
|
|
}
|
|
|
|
@Test
|
|
public void addMediaItem_atTheStart_notifiesAvailableCommandsChanged() {
|
|
Player.Commands defaultCommands = createWithDefaultCommands();
|
|
Player.Commands commandsWithSeekToPreviousWindow =
|
|
createWithDefaultCommands(COMMAND_SEEK_TO_PREVIOUS_MEDIA_ITEM);
|
|
MediaItem mediaItem1 = createMediaItem(/* mediaQueueItemId= */ 1);
|
|
MediaItem mediaItem2 = createMediaItem(/* mediaQueueItemId= */ 2);
|
|
MediaItem mediaItem3 = createMediaItem(/* mediaQueueItemId= */ 3);
|
|
|
|
castPlayer.addMediaItem(mediaItem1);
|
|
updateTimeLine(
|
|
ImmutableList.of(mediaItem1),
|
|
/* mediaQueueItemIds= */ new int[] {1},
|
|
/* currentItemId= */ 1);
|
|
verify(mockListener).onAvailableCommandsChanged(defaultCommands);
|
|
// 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(commandsWithSeekToPreviousWindow);
|
|
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, times(2)).onAvailableCommandsChanged(any());
|
|
}
|
|
|
|
@Test
|
|
public void removeMediaItem_atTheEnd_notifiesAvailableCommandsChanged() {
|
|
Player.Commands defaultCommands = createWithDefaultCommands();
|
|
Player.Commands commandsWithSeekToNextWindow =
|
|
createWithDefaultCommands(COMMAND_SEEK_TO_NEXT_MEDIA_ITEM, COMMAND_SEEK_TO_NEXT);
|
|
Player.Commands emptyTimelineCommands = createWithDefaultCommands(/* isTimelineEmpty= */ true);
|
|
MediaItem mediaItem1 = createMediaItem(/* mediaQueueItemId= */ 1);
|
|
MediaItem mediaItem2 = createMediaItem(/* mediaQueueItemId= */ 2);
|
|
MediaItem mediaItem3 = createMediaItem(/* mediaQueueItemId= */ 3);
|
|
|
|
castPlayer.addMediaItems(ImmutableList.of(mediaItem1, mediaItem2, mediaItem3));
|
|
updateTimeLine(
|
|
ImmutableList.of(mediaItem1, mediaItem2, mediaItem3),
|
|
/* mediaQueueItemIds= */ new int[] {1, 2, 3},
|
|
/* currentItemId= */ 1);
|
|
verify(mockListener).onAvailableCommandsChanged(commandsWithSeekToNextWindow);
|
|
// Check that there were no other calls to onAvailableCommandsChanged.
|
|
verify(mockListener).onAvailableCommandsChanged(any());
|
|
|
|
castPlayer.removeMediaItem(/* index= */ 2);
|
|
updateTimeLine(
|
|
ImmutableList.of(mediaItem1, mediaItem2),
|
|
/* mediaQueueItemIds= */ new int[] {1, 2},
|
|
/* currentItemId= */ 1);
|
|
verify(mockListener).onAvailableCommandsChanged(any());
|
|
|
|
castPlayer.removeMediaItem(/* index= */ 1);
|
|
updateTimeLine(
|
|
ImmutableList.of(mediaItem1),
|
|
/* mediaQueueItemIds= */ new int[] {1},
|
|
/* currentItemId= */ 1);
|
|
verify(mockListener).onAvailableCommandsChanged(defaultCommands);
|
|
verify(mockListener, times(2)).onAvailableCommandsChanged(any());
|
|
|
|
castPlayer.removeMediaItem(/* index= */ 0);
|
|
updateTimeLine(
|
|
ImmutableList.of(),
|
|
/* mediaQueueItemIds= */ new int[0],
|
|
/* currentItemId= */ C.INDEX_UNSET);
|
|
verify(mockListener).onAvailableCommandsChanged(emptyTimelineCommands);
|
|
verify(mockListener, times(3)).onAvailableCommandsChanged(any());
|
|
}
|
|
|
|
@Test
|
|
public void removeMediaItem_atTheStart_notifiesAvailableCommandsChanged() {
|
|
when(mockRemoteMediaClient.queueJumpToItem(anyInt(), anyLong(), eq(null)))
|
|
.thenReturn(mockPendingResult);
|
|
Player.Commands defaultCommands = createWithDefaultCommands();
|
|
Player.Commands commandsWithSeekToPreviousWindow =
|
|
createWithDefaultCommands(COMMAND_SEEK_TO_PREVIOUS_MEDIA_ITEM);
|
|
Player.Commands emptyTimelineCommands = createWithDefaultCommands(/* isTimelineEmpty= */ true);
|
|
MediaItem mediaItem1 = createMediaItem(/* mediaQueueItemId= */ 1);
|
|
MediaItem mediaItem2 = createMediaItem(/* mediaQueueItemId= */ 2);
|
|
MediaItem mediaItem3 = createMediaItem(/* mediaQueueItemId= */ 3);
|
|
|
|
castPlayer.addMediaItems(ImmutableList.of(mediaItem1, mediaItem2, mediaItem3));
|
|
updateTimeLine(
|
|
ImmutableList.of(mediaItem1, mediaItem2, mediaItem3),
|
|
/* mediaQueueItemIds= */ new int[] {1, 2, 3},
|
|
/* currentItemId= */ 3);
|
|
verify(mockListener).onAvailableCommandsChanged(commandsWithSeekToPreviousWindow);
|
|
// Check that there were no other calls to onAvailableCommandsChanged.
|
|
verify(mockListener).onAvailableCommandsChanged(any());
|
|
|
|
castPlayer.removeMediaItem(/* index= */ 0);
|
|
updateTimeLine(
|
|
ImmutableList.of(mediaItem2, mediaItem3),
|
|
/* mediaQueueItemIds= */ new int[] {2, 3},
|
|
/* currentItemId= */ 3);
|
|
verify(mockListener).onAvailableCommandsChanged(any());
|
|
|
|
castPlayer.removeMediaItem(/* index= */ 0);
|
|
updateTimeLine(
|
|
ImmutableList.of(mediaItem3),
|
|
/* mediaQueueItemIds= */ new int[] {3},
|
|
/* currentItemId= */ 3);
|
|
verify(mockListener).onAvailableCommandsChanged(defaultCommands);
|
|
verify(mockListener, times(2)).onAvailableCommandsChanged(any());
|
|
|
|
castPlayer.removeMediaItem(/* index= */ 0);
|
|
updateTimeLine(
|
|
ImmutableList.of(),
|
|
/* mediaQueueItemIds= */ new int[0],
|
|
/* currentItemId= */ C.INDEX_UNSET);
|
|
verify(mockListener).onAvailableCommandsChanged(emptyTimelineCommands);
|
|
verify(mockListener, times(3)).onAvailableCommandsChanged(any());
|
|
}
|
|
|
|
@Test
|
|
public void removeMediaItem_current_notifiesAvailableCommandsChanged() {
|
|
Player.Commands defaultCommands = createWithDefaultCommands();
|
|
Player.Commands commandsWithSeekToNextWindow =
|
|
createWithDefaultCommands(COMMAND_SEEK_TO_NEXT_MEDIA_ITEM, COMMAND_SEEK_TO_NEXT);
|
|
MediaItem mediaItem1 = createMediaItem(/* mediaQueueItemId= */ 1);
|
|
MediaItem mediaItem2 = createMediaItem(/* mediaQueueItemId= */ 2);
|
|
|
|
castPlayer.addMediaItems(ImmutableList.of(mediaItem1, mediaItem2));
|
|
updateTimeLine(
|
|
ImmutableList.of(mediaItem1, mediaItem2),
|
|
/* mediaQueueItemIds= */ new int[] {1, 2},
|
|
/* currentItemId= */ 1);
|
|
verify(mockListener).onAvailableCommandsChanged(commandsWithSeekToNextWindow);
|
|
// Check that there were no other calls to onAvailableCommandsChanged.
|
|
verify(mockListener).onAvailableCommandsChanged(any());
|
|
|
|
castPlayer.removeMediaItem(/* index= */ 0);
|
|
updateTimeLine(
|
|
ImmutableList.of(mediaItem2),
|
|
/* mediaQueueItemIds= */ new int[] {2},
|
|
/* currentItemId= */ 2);
|
|
verify(mockListener).onAvailableCommandsChanged(defaultCommands);
|
|
verify(mockListener, times(2)).onAvailableCommandsChanged(any());
|
|
}
|
|
|
|
@Test
|
|
public void setRepeatMode_all_notifiesAvailableCommandsChanged() {
|
|
when(mockRemoteMediaClient.queueSetRepeatMode(anyInt(), eq(null)))
|
|
.thenReturn(mockPendingResult);
|
|
Player.Commands defaultCommands = createWithDefaultCommands();
|
|
Player.Commands commandsWithSeekToPreviousAndNextWindow =
|
|
createWithDefaultCommands(
|
|
COMMAND_SEEK_TO_PREVIOUS_MEDIA_ITEM,
|
|
COMMAND_SEEK_TO_NEXT_MEDIA_ITEM,
|
|
COMMAND_SEEK_TO_NEXT);
|
|
int[] mediaQueueItemIds = new int[] {1};
|
|
List<MediaItem> mediaItems = createMediaItems(mediaQueueItemIds);
|
|
|
|
castPlayer.addMediaItems(mediaItems);
|
|
updateTimeLine(mediaItems, mediaQueueItemIds, /* currentItemId= */ 1);
|
|
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(commandsWithSeekToPreviousAndNextWindow);
|
|
verify(mockListener, times(2)).onAvailableCommandsChanged(any());
|
|
}
|
|
|
|
@Test
|
|
public void setRepeatMode_one_doesNotNotifyAvailableCommandsChanged() {
|
|
when(mockRemoteMediaClient.queueSetRepeatMode(anyInt(), eq(null)))
|
|
.thenReturn(mockPendingResult);
|
|
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(defaultCommands);
|
|
// Check that there were no other calls to onAvailableCommandsChanged.
|
|
verify(mockListener).onAvailableCommandsChanged(any());
|
|
|
|
castPlayer.setRepeatMode(Player.REPEAT_MODE_ONE);
|
|
verify(mockListener).onAvailableCommandsChanged(any());
|
|
}
|
|
|
|
private int[] createMediaQueueItemIds(int numberOfIds) {
|
|
int[] mediaQueueItemIds = new int[numberOfIds];
|
|
for (int i = 0; i < numberOfIds; i++) {
|
|
mediaQueueItemIds[i] = i + 1;
|
|
}
|
|
return mediaQueueItemIds;
|
|
}
|
|
|
|
private List<MediaItem> createMediaItems(int[] mediaQueueItemIds) {
|
|
List<MediaItem> mediaItems = new ArrayList<>();
|
|
for (int mediaQueueItemId : mediaQueueItemIds) {
|
|
mediaItems.add(createMediaItem(mediaQueueItemId));
|
|
}
|
|
return mediaItems;
|
|
}
|
|
|
|
private MediaItem createMediaItem(int mediaQueueItemId) {
|
|
return new MediaItem.Builder()
|
|
.setUri("http://www.google.com/video" + mediaQueueItemId)
|
|
.setMimeType(MimeTypes.APPLICATION_MPD)
|
|
.setTag(mediaQueueItemId)
|
|
.build();
|
|
}
|
|
|
|
private void addMediaItemsAndUpdateTimeline(List<MediaItem> mediaItems, int[] mediaQueueItemIds) {
|
|
Assertions.checkState(mediaItems.size() == mediaQueueItemIds.length);
|
|
castPlayer.addMediaItems(mediaItems);
|
|
updateTimeLine(mediaItems, mediaQueueItemIds, /* currentItemId= */ 1);
|
|
}
|
|
|
|
private void updateTimeLine(
|
|
List<MediaItem> mediaItems, int[] mediaQueueItemIds, int currentItemId) {
|
|
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,
|
|
/* positionMs= */ C.TIME_UNSET);
|
|
}
|
|
|
|
private void updateTimeLine(
|
|
List<MediaItem> mediaItems,
|
|
int[] mediaQueueItemIds,
|
|
int currentItemId,
|
|
int[] streamTypes,
|
|
long[] durationsMs,
|
|
long positionMs) {
|
|
// Set up mocks to allow the player to update the timeline.
|
|
List<MediaQueueItem> 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.localConfiguration.uri.toString())
|
|
.setStreamType(streamType)
|
|
.setContentType(mediaItem.localConfiguration.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);
|
|
}
|
|
}
|
|
if (positionMs != C.TIME_UNSET) {
|
|
when(mockRemoteMediaClient.getApproximateStreamPosition()).thenReturn(positionMs);
|
|
}
|
|
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.onStatusUpdated();
|
|
}
|
|
|
|
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_MEDIA_ITEM);
|
|
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 createWithDefaultCommands(
|
|
@Player.Command int... additionalCommands) {
|
|
return createWithDefaultCommands(/* isTimelineEmpty= */ false, additionalCommands);
|
|
}
|
|
}
|