diff --git a/library/core/src/main/java/com/google/android/exoplayer2/ExoPlayerImpl.java b/library/core/src/main/java/com/google/android/exoplayer2/ExoPlayerImpl.java index 745801131c..015c606070 100644 --- a/library/core/src/main/java/com/google/android/exoplayer2/ExoPlayerImpl.java +++ b/library/core/src/main/java/com/google/android/exoplayer2/ExoPlayerImpl.java @@ -432,11 +432,16 @@ import java.util.concurrent.TimeoutException; Timeline oldTimeline = getCurrentTimeline(); pendingOperationAcks++; List holders = addMediaSourceHolders(index, mediaSources); - Timeline timeline = + PlaybackInfo playbackInfo = maskTimelineAndWindowIndex(currentWindowIndex, currentPositionMs, oldTimeline); internalPlayer.addMediaSources(index, holders, shuffleOrder); - notifyListeners( - listener -> listener.onTimelineChanged(timeline, TIMELINE_CHANGE_REASON_PLAYLIST_CHANGED)); + updatePlaybackInfo( + playbackInfo, + /* positionDiscontinuity= */ false, + /* ignored */ DISCONTINUITY_REASON_INTERNAL, + /* timelineChangeReason= */ TIMELINE_CHANGE_REASON_PLAYLIST_CHANGED, + /* ignored */ PLAY_WHEN_READY_CHANGE_REASON_USER_REQUEST, + /* seekProcessed= */ false); } @Override @@ -469,11 +474,16 @@ import java.util.concurrent.TimeoutException; pendingOperationAcks++; newFromIndex = Math.min(newFromIndex, mediaSourceHolders.size() - (toIndex - fromIndex)); Playlist.moveMediaSourceHolders(mediaSourceHolders, fromIndex, toIndex, newFromIndex); - Timeline timeline = + PlaybackInfo playbackInfo = maskTimelineAndWindowIndex(currentWindowIndex, currentPositionMs, oldTimeline); internalPlayer.moveMediaSources(fromIndex, toIndex, newFromIndex, shuffleOrder); - notifyListeners( - listener -> listener.onTimelineChanged(timeline, TIMELINE_CHANGE_REASON_PLAYLIST_CHANGED)); + updatePlaybackInfo( + playbackInfo, + /* positionDiscontinuity= */ false, + /* ignored */ DISCONTINUITY_REASON_INTERNAL, + /* timelineChangeReason= */ TIMELINE_CHANGE_REASON_PLAYLIST_CHANGED, + /* ignored */ PLAY_WHEN_READY_CHANGE_REASON_USER_REQUEST, + /* seekProcessed= */ false); } @Override @@ -486,13 +496,18 @@ import java.util.concurrent.TimeoutException; @Override public void setShuffleOrder(ShuffleOrder shuffleOrder) { - Timeline timeline = maskTimeline(); + PlaybackInfo playbackInfo = maskTimeline(); maskWithCurrentPosition(); pendingOperationAcks++; this.shuffleOrder = shuffleOrder; internalPlayer.setShuffleOrder(shuffleOrder); - notifyListeners( - listener -> listener.onTimelineChanged(timeline, TIMELINE_CHANGE_REASON_PLAYLIST_CHANGED)); + updatePlaybackInfo( + playbackInfo, + /* positionDiscontinuity= */ false, + /* ignored */ DISCONTINUITY_REASON_INTERNAL, + /* timelineChangeReason= */ TIMELINE_CHANGE_REASON_PLAYLIST_CHANGED, + /* ignored */ PLAY_WHEN_READY_CHANGE_REASON_USER_REQUEST, + /* seekProcessed= */ false); } @Override @@ -517,38 +532,26 @@ import java.util.concurrent.TimeoutException; return pauseAtEndOfMediaItems; } - @SuppressWarnings("deprecation") public void setPlayWhenReady( boolean playWhenReady, @PlaybackSuppressionReason int playbackSuppressionReason, @PlayWhenReadyChangeReason int playWhenReadyChangeReason) { - boolean oldIsPlaying = isPlaying(); - boolean playWhenReadyChanged = playbackInfo.playWhenReady != playWhenReady; - boolean suppressionReasonChanged = - playbackInfo.playbackSuppressionReason != playbackSuppressionReason; - if (!playWhenReadyChanged && !suppressionReasonChanged) { + if (playbackInfo.playWhenReady == playWhenReady + && playbackInfo.playbackSuppressionReason == playbackSuppressionReason) { return; } maskWithCurrentPosition(); pendingOperationAcks++; - playbackInfo = playbackInfo.copyWithPlayWhenReady(playWhenReady, playbackSuppressionReason); + PlaybackInfo playbackInfo = + this.playbackInfo.copyWithPlayWhenReady(playWhenReady, playbackSuppressionReason); internalPlayer.setPlayWhenReady(playWhenReady, playbackSuppressionReason); - boolean isPlaying = isPlaying(); - boolean isPlayingChanged = oldIsPlaying != isPlaying; - int playbackState = playbackInfo.playbackState; - notifyListeners( - listener -> { - if (playWhenReadyChanged) { - listener.onPlayerStateChanged(playWhenReady, playbackState); - listener.onPlayWhenReadyChanged(playWhenReady, playWhenReadyChangeReason); - } - if (suppressionReasonChanged) { - listener.onPlaybackSuppressionReasonChanged(playbackSuppressionReason); - } - if (isPlayingChanged) { - listener.onIsPlayingChanged(isPlaying); - } - }); + updatePlaybackInfo( + playbackInfo, + /* positionDiscontinuity= */ false, + /* ignored */ DISCONTINUITY_REASON_INTERNAL, + /* ignored */ TIMELINE_CHANGE_REASON_PLAYLIST_CHANGED, + playWhenReadyChangeReason, + /* seekProcessed= */ false); } @Override @@ -589,7 +592,6 @@ import java.util.concurrent.TimeoutException; return playbackInfo.isLoading; } - @SuppressWarnings("deprecation") @Override public void seekTo(int windowIndex, long positionMs) { Timeline timeline = playbackInfo.timeline; @@ -597,8 +599,6 @@ import java.util.concurrent.TimeoutException; throw new IllegalSeekPositionException(timeline, windowIndex, positionMs); } hasPendingSeek = true; - boolean playWhenReady = getPlayWhenReady(); - @Player.State int playbackState = getPlaybackState(); pendingOperationAcks++; if (isPlayingAd()) { // TODO: Investigate adding support for seeking during ads. This is complicated to do in @@ -617,18 +617,16 @@ import java.util.concurrent.TimeoutException; maskWindowIndexAndPositionForSeek(timeline, windowIndex, positionMs); @Player.State int newPlaybackState = - playbackState == Player.STATE_IDLE ? Player.STATE_IDLE : Player.STATE_BUFFERING; - boolean playbackStateChanged = playbackState != newPlaybackState; - playbackInfo = playbackInfo.copyWithPlaybackState(newPlaybackState); + getPlaybackState() == Player.STATE_IDLE ? Player.STATE_IDLE : Player.STATE_BUFFERING; + PlaybackInfo playbackInfo = this.playbackInfo.copyWithPlaybackState(newPlaybackState); internalPlayer.seekTo(timeline, windowIndex, C.msToUs(positionMs)); - notifyListeners( - listener -> { - listener.onPositionDiscontinuity(DISCONTINUITY_REASON_SEEK); - if (playbackStateChanged) { - listener.onPlayerStateChanged(playWhenReady, newPlaybackState); - listener.onPlaybackStateChanged(newPlaybackState); - } - }); + updatePlaybackInfo( + playbackInfo, + /* positionDiscontinuity= */ true, + /* positionDiscontinuityReason= */ DISCONTINUITY_REASON_SEEK, + /* ignored */ TIMELINE_CHANGE_REASON_PLAYLIST_CHANGED, + /* ignored */ PLAY_WHEN_READY_CHANGE_REASON_USER_REQUEST, + /* seekProcessed= */ false); } /** @deprecated Use {@link #setPlaybackSpeed(float)} instead. */ @@ -1011,7 +1009,6 @@ import java.util.concurrent.TimeoutException; seekProcessed)); } - @SuppressWarnings("deprecation") private void setMediaSourcesInternal( List mediaItems, int startWindowIndex, @@ -1019,14 +1016,14 @@ import java.util.concurrent.TimeoutException; boolean resetToDefaultPosition) { int currentWindowIndex = getCurrentWindowIndexInternal(); long currentPositionMs = getCurrentPosition(); - boolean currentPlayWhenReady = getPlayWhenReady(); pendingOperationAcks++; if (!mediaSourceHolders.isEmpty()) { removeMediaSourceHolders( /* fromIndex= */ 0, /* toIndexExclusive= */ mediaSourceHolders.size()); } List holders = addMediaSourceHolders(/* index= */ 0, mediaItems); - Timeline timeline = maskTimeline(); + PlaybackInfo playbackInfo = maskTimeline(); + Timeline timeline = playbackInfo.timeline; if (!timeline.isEmpty() && startWindowIndex >= timeline.getWindowCount()) { throw new IllegalSeekPositionException(timeline, startWindowIndex, startPositionMs); } @@ -1040,9 +1037,9 @@ import java.util.concurrent.TimeoutException; } maskWindowIndexAndPositionForSeek( timeline, startWindowIndex == C.INDEX_UNSET ? 0 : startWindowIndex, startPositionMs); - // mask the playback state + // Mask the playback state. int maskingPlaybackState = playbackInfo.playbackState; - if (startWindowIndex != C.INDEX_UNSET) { + if (startWindowIndex != C.INDEX_UNSET && playbackInfo.playbackState != STATE_IDLE) { // Position reset to startWindowIndex (results in pending initial seek). if (timeline.isEmpty() || startWindowIndex >= timeline.getWindowCount()) { // Setting an empty timeline or invalid seek transitions to ended. @@ -1051,23 +1048,16 @@ import java.util.concurrent.TimeoutException; maskingPlaybackState = STATE_BUFFERING; } } - boolean playbackStateChanged = - playbackInfo.playbackState != STATE_IDLE - && playbackInfo.playbackState != maskingPlaybackState; - int finalMaskingPlaybackState = maskingPlaybackState; - if (playbackStateChanged) { - playbackInfo = playbackInfo.copyWithPlaybackState(finalMaskingPlaybackState); - } + playbackInfo = playbackInfo.copyWithPlaybackState(maskingPlaybackState); internalPlayer.setMediaSources( holders, startWindowIndex, C.msToUs(startPositionMs), shuffleOrder); - notifyListeners( - listener -> { - listener.onTimelineChanged(timeline, TIMELINE_CHANGE_REASON_PLAYLIST_CHANGED); - if (playbackStateChanged) { - listener.onPlayerStateChanged(currentPlayWhenReady, finalMaskingPlaybackState); - listener.onPlaybackStateChanged(finalMaskingPlaybackState); - } - }); + updatePlaybackInfo( + playbackInfo, + /* positionDiscontinuity= */ false, + /* ignored */ Player.DISCONTINUITY_REASON_INTERNAL, + /* timelineChangeReason= */ TIMELINE_CHANGE_REASON_PLAYLIST_CHANGED, + /* ignored */ PLAY_WHEN_READY_CHANGE_REASON_USER_REQUEST, + /* seekProcessed= */ false); } private List addMediaSourceHolders( @@ -1085,18 +1075,16 @@ import java.util.concurrent.TimeoutException; return holders; } - @SuppressWarnings("deprecation") private void removeMediaItemsInternal(int fromIndex, int toIndex) { Assertions.checkArgument( fromIndex >= 0 && toIndex >= fromIndex && toIndex <= mediaSourceHolders.size()); int currentWindowIndex = getCurrentWindowIndex(); long currentPositionMs = getCurrentPosition(); - boolean currentPlayWhenReady = getPlayWhenReady(); Timeline oldTimeline = getCurrentTimeline(); int currentMediaSourceCount = mediaSourceHolders.size(); pendingOperationAcks++; removeMediaSourceHolders(fromIndex, /* toIndexExclusive= */ toIndex); - Timeline timeline = + PlaybackInfo playbackInfo = maskTimelineAndWindowIndex(currentWindowIndex, currentPositionMs, oldTimeline); // Player transitions to STATE_ENDED if the current index is part of the removed tail. final boolean transitionsToEnded = @@ -1104,19 +1092,18 @@ import java.util.concurrent.TimeoutException; && playbackInfo.playbackState != STATE_ENDED && fromIndex < toIndex && toIndex == currentMediaSourceCount - && currentWindowIndex >= timeline.getWindowCount(); + && currentWindowIndex >= playbackInfo.timeline.getWindowCount(); if (transitionsToEnded) { playbackInfo = playbackInfo.copyWithPlaybackState(STATE_ENDED); } internalPlayer.removeMediaSources(fromIndex, toIndex, shuffleOrder); - notifyListeners( - listener -> { - listener.onTimelineChanged(timeline, TIMELINE_CHANGE_REASON_PLAYLIST_CHANGED); - if (transitionsToEnded) { - listener.onPlayerStateChanged(currentPlayWhenReady, STATE_ENDED); - listener.onPlaybackStateChanged(STATE_ENDED); - } - }); + updatePlaybackInfo( + playbackInfo, + /* positionDiscontinuity= */ false, + /* ignored */ Player.DISCONTINUITY_REASON_INTERNAL, + /* timelineChangeReason= */ TIMELINE_CHANGE_REASON_PLAYLIST_CHANGED, + /* ignored */ PLAY_WHEN_READY_CHANGE_REASON_USER_REQUEST, + /* seekProcessed= */ false); } private List removeMediaSourceHolders( @@ -1129,18 +1116,17 @@ import java.util.concurrent.TimeoutException; return removed; } - private Timeline maskTimeline() { - playbackInfo = - playbackInfo.copyWithTimeline( - mediaSourceHolders.isEmpty() - ? Timeline.EMPTY - : new Playlist.PlaylistTimeline(mediaSourceHolders, shuffleOrder)); - return playbackInfo.timeline; + private PlaybackInfo maskTimeline() { + return playbackInfo.copyWithTimeline( + mediaSourceHolders.isEmpty() + ? Timeline.EMPTY + : new Playlist.PlaylistTimeline(mediaSourceHolders, shuffleOrder)); } - private Timeline maskTimelineAndWindowIndex( + private PlaybackInfo maskTimelineAndWindowIndex( int currentWindowIndex, long currentPositionMs, Timeline oldTimeline) { - Timeline maskingTimeline = maskTimeline(); + PlaybackInfo playbackInfo = maskTimeline(); + Timeline maskingTimeline = playbackInfo.timeline; if (oldTimeline.isEmpty()) { // The index is the default index or was set by a seek in the empty old timeline. maskingWindowIndex = currentWindowIndex; @@ -1148,7 +1134,7 @@ import java.util.concurrent.TimeoutException; // The seek is not valid in the new timeline. maskWithDefaultPosition(maskingTimeline); } - return maskingTimeline; + return playbackInfo; } @Nullable Pair periodPosition = @@ -1186,7 +1172,7 @@ import java.util.concurrent.TimeoutException; maskWithDefaultPosition(maskingTimeline); } } - return maskingTimeline; + return playbackInfo; } private void maskWindowIndexAndPositionForSeek( diff --git a/library/core/src/test/java/com/google/android/exoplayer2/ExoPlayerTest.java b/library/core/src/test/java/com/google/android/exoplayer2/ExoPlayerTest.java index cf92c77840..035cf542b1 100644 --- a/library/core/src/test/java/com/google/android/exoplayer2/ExoPlayerTest.java +++ b/library/core/src/test/java/com/google/android/exoplayer2/ExoPlayerTest.java @@ -3391,7 +3391,6 @@ public final class ExoPlayerTest { .blockUntilActionScheduleFinished(TIMEOUT_MS) .blockUntilEnded(TIMEOUT_MS); exoPlayerTestRunner.assertTimelineChangeReasonsEqual( - Player.TIMELINE_CHANGE_REASON_PLAYLIST_CHANGED, Player.TIMELINE_CHANGE_REASON_SOURCE_UPDATE); assertArrayEquals(new long[] {2}, windowCounts); assertArrayEquals(new int[] {seekToWindowIndex}, currentWindowIndices); @@ -4157,7 +4156,7 @@ public final class ExoPlayerTest { int seekToWindowIndex = 1; ActionSchedule actionSchedule = new ActionSchedule.Builder(TAG) - .waitForTimelineChanged() + .waitForPlaybackState(Player.STATE_ENDED) .executeRunnable( new PlayerRunnable() { @Override @@ -4168,12 +4167,11 @@ public final class ExoPlayerTest { } }) .executeRunnable( - () -> { - concatenatingMediaSource.addMediaSource( - new FakeMediaSource(fakeTimeline, Builder.VIDEO_FORMAT)); - concatenatingMediaSource.addMediaSource( - new FakeMediaSource(fakeTimeline, Builder.VIDEO_FORMAT)); - }) + () -> + concatenatingMediaSource.addMediaSources( + Arrays.asList( + new FakeMediaSource(fakeTimeline, Builder.VIDEO_FORMAT), + new FakeMediaSource(fakeTimeline, Builder.VIDEO_FORMAT)))) .waitForTimelineChanged() .executeRunnable( new PlayerRunnable() { @@ -4203,7 +4201,7 @@ public final class ExoPlayerTest { ConcatenatingMediaSource concatenatingMediaSource = new ConcatenatingMediaSource(/* isAtomic= */ false); ActionSchedule actionSchedule = - new ActionSchedule.Builder(TAG).waitForTimelineChanged().build(); + new ActionSchedule.Builder(TAG).waitForPlaybackState(Player.STATE_ENDED).build(); ExoPlayerTestRunner exoPlayerTestRunner = new Builder() .setMediaSources(concatenatingMediaSource) @@ -4229,7 +4227,7 @@ public final class ExoPlayerTest { int seekToWindowIndex = 1; ActionSchedule actionSchedule = new ActionSchedule.Builder(TAG) - .waitForTimelineChanged() + .waitForPlaybackState(Player.STATE_ENDED) .executeRunnable( new PlayerRunnable() { @Override @@ -4240,12 +4238,11 @@ public final class ExoPlayerTest { } }) .executeRunnable( - () -> { - concatenatingMediaSource.addMediaSource( - new FakeMediaSource(fakeTimeline, Builder.VIDEO_FORMAT)); - concatenatingMediaSource.addMediaSource( - new FakeMediaSource(fakeTimeline, Builder.VIDEO_FORMAT)); - }) + () -> + concatenatingMediaSource.addMediaSources( + Arrays.asList( + new FakeMediaSource(fakeTimeline, Builder.VIDEO_FORMAT), + new FakeMediaSource(fakeTimeline, Builder.VIDEO_FORMAT)))) .waitForTimelineChanged() .executeRunnable( new PlayerRunnable() { @@ -4727,7 +4724,6 @@ public final class ExoPlayerTest { new int[] {Player.STATE_IDLE, Player.STATE_IDLE, Player.STATE_IDLE, Player.STATE_IDLE}, maskingPlaybackStates); exoPlayerTestRunner.assertTimelineChangeReasonsEqual( - Player.TIMELINE_CHANGE_REASON_PLAYLIST_CHANGED, Player.TIMELINE_CHANGE_REASON_PLAYLIST_CHANGED, Player.TIMELINE_CHANGE_REASON_PLAYLIST_CHANGED, Player.TIMELINE_CHANGE_REASON_PLAYLIST_CHANGED); @@ -4768,7 +4764,6 @@ public final class ExoPlayerTest { Player.STATE_BUFFERING, Player.STATE_READY, Player.STATE_ENDED); assertArrayEquals(new int[] {Player.STATE_IDLE}, maskingPlaybackStates); exoPlayerTestRunner.assertTimelineChangeReasonsEqual( - Player.TIMELINE_CHANGE_REASON_PLAYLIST_CHANGED, Player.TIMELINE_CHANGE_REASON_PLAYLIST_CHANGED, Player.TIMELINE_CHANGE_REASON_SOURCE_UPDATE); } @@ -4841,7 +4836,6 @@ public final class ExoPlayerTest { Player.STATE_BUFFERING, Player.STATE_READY, Player.STATE_ENDED); assertArrayEquals(new int[] {Player.STATE_IDLE}, maskingPlaybackStates); exoPlayerTestRunner.assertTimelineChangeReasonsEqual( - Player.TIMELINE_CHANGE_REASON_PLAYLIST_CHANGED, Player.TIMELINE_CHANGE_REASON_PLAYLIST_CHANGED, Player.TIMELINE_CHANGE_REASON_SOURCE_UPDATE); } @@ -4924,9 +4918,6 @@ public final class ExoPlayerTest { }, maskingPlaybackStates); exoPlayerTestRunner.assertTimelineChangeReasonsEqual( - Player.TIMELINE_CHANGE_REASON_PLAYLIST_CHANGED, - Player.TIMELINE_CHANGE_REASON_PLAYLIST_CHANGED, - Player.TIMELINE_CHANGE_REASON_PLAYLIST_CHANGED, Player.TIMELINE_CHANGE_REASON_PLAYLIST_CHANGED, Player.TIMELINE_CHANGE_REASON_SOURCE_UPDATE, Player.TIMELINE_CHANGE_REASON_PLAYLIST_CHANGED, @@ -4969,7 +4960,6 @@ public final class ExoPlayerTest { exoPlayerTestRunner.assertPlaybackStatesEqual(Player.STATE_ENDED); assertArrayEquals(new int[] {Player.STATE_ENDED}, maskingPlaybackStates); exoPlayerTestRunner.assertTimelineChangeReasonsEqual( - Player.TIMELINE_CHANGE_REASON_PLAYLIST_CHANGED, Player.TIMELINE_CHANGE_REASON_PLAYLIST_CHANGED, Player.TIMELINE_CHANGE_REASON_SOURCE_UPDATE); } @@ -5050,7 +5040,6 @@ public final class ExoPlayerTest { exoPlayerTestRunner.assertTimelineChangeReasonsEqual( Player.TIMELINE_CHANGE_REASON_PLAYLIST_CHANGED, Player.TIMELINE_CHANGE_REASON_SOURCE_UPDATE, - Player.TIMELINE_CHANGE_REASON_PLAYLIST_CHANGED, Player.TIMELINE_CHANGE_REASON_PLAYLIST_CHANGED); } @@ -5213,7 +5202,6 @@ public final class ExoPlayerTest { Player.STATE_ENDED); assertArrayEquals(new int[] {Player.STATE_ENDED}, maskingPlaybackStates); exoPlayerTestRunner.assertTimelineChangeReasonsEqual( - Player.TIMELINE_CHANGE_REASON_PLAYLIST_CHANGED, Player.TIMELINE_CHANGE_REASON_PLAYLIST_CHANGED, Player.TIMELINE_CHANGE_REASON_SOURCE_UPDATE, Player.TIMELINE_CHANGE_REASON_PLAYLIST_CHANGED, @@ -6161,6 +6149,7 @@ public final class ExoPlayerTest { public void loading_withLargeAllocationCausingOom_playsRemainingMediaAndThenThrows() { Loader.Loadable loadable = new Loader.Loadable() { + @SuppressWarnings("UnusedVariable") @Override public void load() throws IOException { @SuppressWarnings("unused") // This test needs the allocation to cause an OOM. @@ -6229,6 +6218,97 @@ public final class ExoPlayerTest { assertThat(renderer.sampleBufferReadCount).isEqualTo(3); } + @Test + public void seekTo_whileReady_callsOnIsPlayingChanged() throws Exception { + ActionSchedule actionSchedule = + new ActionSchedule.Builder(TAG) + .waitForPlaybackState(Player.STATE_READY) + .seek(/* positionMs= */ 0) + .waitForPlaybackState(Player.STATE_ENDED) + .build(); + List onIsPlayingChanges = new ArrayList<>(); + Player.EventListener eventListener = + new Player.EventListener() { + @Override + public void onIsPlayingChanged(boolean isPlaying) { + onIsPlayingChanges.add(isPlaying); + } + }; + new ExoPlayerTestRunner.Builder() + .setEventListener(eventListener) + .setActionSchedule(actionSchedule) + .build(context) + .start() + .blockUntilActionScheduleFinished(TIMEOUT_MS) + .blockUntilEnded(TIMEOUT_MS); + + assertThat(onIsPlayingChanges).containsExactly(true, false, true, false).inOrder(); + } + + @Test + public void multipleListenersAndMultipleCallbacks_callbacksAreOrderedByType() throws Exception { + String playWhenReadyChange1 = "playWhenReadyChange1"; + String playWhenReadyChange2 = "playWhenReadyChange2"; + String isPlayingChange1 = "isPlayingChange1"; + String isPlayingChange2 = "isPlayingChange2"; + ArrayList events = new ArrayList<>(); + Player.EventListener eventListener1 = + new Player.EventListener() { + @Override + public void onPlayWhenReadyChanged(boolean playWhenReady, int reason) { + events.add(playWhenReadyChange1); + } + + @Override + public void onIsPlayingChanged(boolean isPlaying) { + events.add(isPlayingChange1); + } + }; + Player.EventListener eventListener2 = + new Player.EventListener() { + @Override + public void onPlayWhenReadyChanged(boolean playWhenReady, int reason) { + events.add(playWhenReadyChange2); + } + + @Override + public void onIsPlayingChanged(boolean isPlaying) { + events.add(isPlayingChange2); + } + }; + ActionSchedule actionSchedule = + new ActionSchedule.Builder(TAG) + .pause() + .executeRunnable( + new PlayerRunnable() { + @Override + public void run(SimpleExoPlayer player) { + player.addListener(eventListener1); + player.addListener(eventListener2); + } + }) + .waitForPlaybackState(Player.STATE_READY) + .play() + .waitForPlaybackState(Player.STATE_ENDED) + .build(); + new ExoPlayerTestRunner.Builder() + .setActionSchedule(actionSchedule) + .build(context) + .start() + .blockUntilActionScheduleFinished(TIMEOUT_MS) + .blockUntilEnded(TIMEOUT_MS); + + assertThat(events) + .containsExactly( + playWhenReadyChange1, + playWhenReadyChange2, + isPlayingChange1, + isPlayingChange2, + isPlayingChange1, + isPlayingChange2) + .inOrder(); + } + // Internal methods. private static ActionSchedule.Builder addSurfaceSwitch(ActionSchedule.Builder builder) {