Trigger media item transition event in CastPlayer

#minor-release

PiperOrigin-RevId: 361803897
This commit is contained in:
kimvde 2021-03-09 15:22:26 +00:00 committed by Ian Baker
parent ff8db9a4fe
commit b74bfa1e9a
3 changed files with 140 additions and 6 deletions

View File

@ -63,6 +63,8 @@
* Fix `onPositionDiscontinuity` event so that it is not triggered with
reason `DISCONTINUITY_REASON_PERIOD_TRANSITION` after a seek to another
media item and so that it is not triggered after a timeline change.
* Trigger `onMediaItemTransition` event for all reasons except
`MEDIA_ITEM_TRANSITION_REASON_REPEAT`.
### 2.13.2 (2021-02-25)

View File

@ -440,6 +440,13 @@ public final class CastPlayer extends BasePlayer {
if (getCurrentWindowIndex() != windowIndex) {
remoteMediaClient.queueJumpToItem((int) currentTimeline.getPeriod(windowIndex, period).uid,
positionMs, null).setResultCallback(seekResultCallback);
// TODO(internal b/182261884): queue `onMediaItemTransition` event when the media item is
// repeated.
MediaItem mediaItem = currentTimeline.getWindow(windowIndex, window).mediaItem;
listeners.queueEvent(
Player.EVENT_MEDIA_ITEM_TRANSITION,
listener ->
listener.onMediaItemTransition(mediaItem, MEDIA_ITEM_TRANSITION_REASON_SEEK));
} else {
remoteMediaClient.seek(positionMs).setResultCallback(seekResultCallback);
}
@ -637,6 +644,11 @@ public final class CastPlayer extends BasePlayer {
listeners.queueEvent(
Player.EVENT_POSITION_DISCONTINUITY,
listener -> listener.onPositionDiscontinuity(DISCONTINUITY_REASON_PERIOD_TRANSITION));
listeners.queueEvent(
Player.EVENT_MEDIA_ITEM_TRANSITION,
listener ->
listener.onMediaItemTransition(
getCurrentMediaItem(), MEDIA_ITEM_TRANSITION_REASON_AUTO));
}
if (updateTracksAndSelectionsAndNotifyIfChanged()) {
listeners.queueEvent(
@ -681,6 +693,8 @@ public final class CastPlayer extends BasePlayer {
@SuppressWarnings("deprecation") // Calling deprecated listener method.
private void updateTimelineAndNotifyIfChanged() {
Timeline previousTimeline = currentTimeline;
int previousWindowIndex = currentWindowIndex;
if (updateTimeline()) {
// TODO: Differentiate TIMELINE_CHANGE_REASON_PLAYLIST_CHANGED and
// TIMELINE_CHANGE_REASON_SOURCE_UPDATE [see internal: b/65152553].
@ -692,7 +706,26 @@ public final class CastPlayer extends BasePlayer {
timeline, /* manifest= */ null, Player.TIMELINE_CHANGE_REASON_SOURCE_UPDATE);
listener.onTimelineChanged(timeline, Player.TIMELINE_CHANGE_REASON_SOURCE_UPDATE);
});
updateAvailableCommandsAndNotifyIfChanged();
boolean mediaItemTransitioned;
if (currentTimeline.isEmpty() && previousTimeline.isEmpty()) {
mediaItemTransitioned = false;
} else if (currentTimeline.isEmpty() != previousTimeline.isEmpty()) {
mediaItemTransitioned = true;
} else {
Object previousWindowUid = previousTimeline.getWindow(previousWindowIndex, window).uid;
Object currentWindowUid = currentTimeline.getWindow(currentWindowIndex, window).uid;
mediaItemTransitioned = !currentWindowUid.equals(previousWindowUid);
}
if (mediaItemTransitioned) {
listeners.queueEvent(
Player.EVENT_MEDIA_ITEM_TRANSITION,
listener ->
listener.onMediaItemTransition(
getCurrentMediaItem(), MEDIA_ITEM_TRANSITION_REASON_PLAYLIST_CHANGED));
}
}
}

View File

@ -80,8 +80,8 @@ public class CastPlayerTest {
setResultCallbackArgumentCaptor;
@Captor private ArgumentCaptor<RemoteMediaClient.Callback> callbackArgumentCaptor;
@Captor private ArgumentCaptor<MediaQueueItem[]> queueItemsArgumentCaptor;
@Captor private ArgumentCaptor<MediaItem> mediaItemCaptor;
@SuppressWarnings("deprecation")
@Before
@ -457,9 +457,107 @@ public class CastPlayerTest {
}
}
@Test
public void addMediaItems_notifiesMediaItemTransition() {
MediaItem mediaItem = createMediaItem(/* mediaQueueItemId= */ 1);
List<MediaItem> mediaItems = ImmutableList.of(mediaItem);
int[] mediaQueueItemIds = new int[] {1};
addMediaItemsAndUpdateTimeline(mediaItems, mediaQueueItemIds);
verify(mockListener)
.onMediaItemTransition(
mediaItemCaptor.capture(), eq(Player.MEDIA_ITEM_TRANSITION_REASON_PLAYLIST_CHANGED));
assertThat(mediaItemCaptor.getValue().playbackProperties.tag)
.isEqualTo(mediaItem.playbackProperties.tag);
verify(mockListener).onMediaItemTransition(any(), anyInt());
}
@Test
public void clearMediaItems_notifiesMediaItemTransition() {
int[] mediaQueueItemIds = new int[] {1, 2};
List<MediaItem> mediaItems = createMediaItems(mediaQueueItemIds);
addMediaItemsAndUpdateTimeline(mediaItems, mediaQueueItemIds);
verify(mockListener).onMediaItemTransition(any(), anyInt());
clearMediaItemsAndUpdateTimeline();
verify(mockListener)
.onMediaItemTransition(
/* mediaItem= */ null, Player.MEDIA_ITEM_TRANSITION_REASON_PLAYLIST_CHANGED);
verify(mockListener, times(2)).onMediaItemTransition(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};
addMediaItemsAndUpdateTimeline(mediaItems, mediaQueueItemIds);
verify(mockListener).onMediaItemTransition(any(), anyInt());
removeMediaItemsAndUpdateTimeline(
mediaItems, mediaQueueItemIds, /* fromIndex= */ 0, /* toIndex= */ 1);
verify(mockListener, times(2))
.onMediaItemTransition(
mediaItemCaptor.capture(), eq(Player.MEDIA_ITEM_TRANSITION_REASON_PLAYLIST_CHANGED));
assertThat(mediaItemCaptor.getValue().playbackProperties.tag)
.isEqualTo(mediaItem2.playbackProperties.tag);
verify(mockListener, times(2)).onMediaItemTransition(any(), anyInt());
}
@Test
public void removeNonCurrentMediaItem_doesNotNotifyMediaItemTransition() {
int[] mediaQueueItemIds = new int[] {1, 2};
List<MediaItem> mediaItems = createMediaItems(mediaQueueItemIds);
addMediaItemsAndUpdateTimeline(mediaItems, mediaQueueItemIds);
verify(mockListener).onMediaItemTransition(any(), anyInt());
removeMediaItemsAndUpdateTimeline(
mediaItems, mediaQueueItemIds, /* fromIndex= */ 1, /* toIndex= */ 2);
verify(mockListener).onMediaItemTransition(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};
addMediaItemsAndUpdateTimeline(mediaItems, mediaQueueItemIds);
verify(mockListener).onMediaItemTransition(any(), anyInt());
castPlayer.seekTo(/* windowIndex= */ 1, /* positionMs= */ 0);
verify(mockListener)
.onMediaItemTransition(
mediaItemCaptor.capture(), eq(Player.MEDIA_ITEM_TRANSITION_REASON_SEEK));
assertThat(mediaItemCaptor.getValue().playbackProperties.tag)
.isEqualTo(mediaItem2.playbackProperties.tag);
verify(mockListener, times(2)).onMediaItemTransition(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);
addMediaItemsAndUpdateTimeline(mediaItems, mediaQueueItemIds);
verify(mockListener).onMediaItemTransition(any(), anyInt());
castPlayer.seekTo(/* windowIndex= */ 0, /* positionMs= */ 0);
verify(mockListener).onMediaItemTransition(any(), anyInt());
}
@Test
public void seekTo_otherWindow_notifiesAvailableCommandsChanged() {
when(mockRemoteMediaClient.play()).thenReturn(mockPendingResult);
when(mockRemoteMediaClient.queueJumpToItem(anyInt(), anyLong(), eq(null)))
.thenReturn(mockPendingResult);
Player.Commands commandsWithHasNext =
@ -488,7 +586,6 @@ public class CastPlayerTest {
@Test
public void addMediaItems_whenLastPlaying_notifiesAvailableCommandsChanged() {
when(mockRemoteMediaClient.play()).thenReturn(mockPendingResult);
Player.Commands commandsWithHasNext =
new Player.Commands.Builder().add(COMMAND_SEEK_TO_NEXT_MEDIA_ITEM).build();
int[] mediaQueueItemIds = new int[] {1};
@ -519,7 +616,6 @@ public class CastPlayerTest {
@Test
public void removeMediaItems_followingCurrent_notifiesAvailableCommandsChanged() {
when(mockRemoteMediaClient.play()).thenReturn(mockPendingResult);
Player.Commands commandsWithHasNext =
new Player.Commands.Builder().add(COMMAND_SEEK_TO_NEXT_MEDIA_ITEM).build();
MediaItem mediaItem1 = createMediaItem(/* mediaQueueItemId= */ 1);
@ -550,7 +646,6 @@ public class CastPlayerTest {
@Test
public void setRepeatMode_all_notifiesAvailableCommandsChanged() {
when(mockRemoteMediaClient.play()).thenReturn(mockPendingResult);
when(mockRemoteMediaClient.queueSetRepeatMode(anyInt(), eq(null)))
.thenReturn(mockPendingResult);
Player.Commands commandsWithHasNext =
@ -568,7 +663,6 @@ public class CastPlayerTest {
@Test
public void setRepeatMode_one_doesNotNotifyAvailableCommandsChanged() {
when(mockRemoteMediaClient.play()).thenReturn(mockPendingResult);
when(mockRemoteMediaClient.queueSetRepeatMode(anyInt(), eq(null)))
.thenReturn(mockPendingResult);
int[] mediaQueueItemIds = new int[] {1};
@ -658,6 +752,11 @@ public class CastPlayerTest {
updateTimeLine(mediaItems, mediaQueueItemIds);
}
private void clearMediaItemsAndUpdateTimeline() {
castPlayer.clearMediaItems();
updateTimeLine(ImmutableList.of(), new int[0]);
}
private void updateTimeLine(List<MediaItem> mediaItems, int[] mediaQueueItemIds) {
List<MediaQueueItem> queueItems = new ArrayList<>();
DefaultMediaItemConverter converter = new DefaultMediaItemConverter();