From 6edf52b2c554fa9fd0ba60b1b08d53f43094f83b Mon Sep 17 00:00:00 2001 From: sungsoo Date: Fri, 20 Nov 2020 06:47:25 +0000 Subject: [PATCH] Allow to remove all playlist items PiperOrigin-RevId: 343437513 --- RELEASENOTES.md | 1 + .../media2/SessionPlayerConnectorTest.java | 67 ++++++++++++++----- .../exoplayer2/ext/media2/PlayerWrapper.java | 11 ++- .../ext/media2/SessionPlayerConnector.java | 9 +-- 4 files changed, 66 insertions(+), 22 deletions(-) diff --git a/RELEASENOTES.md b/RELEASENOTES.md index bede252392..3fbf251299 100644 --- a/RELEASENOTES.md +++ b/RELEASENOTES.md @@ -51,6 +51,7 @@ * Media2 extension: * Notify onBufferingEnded when the state of origin player becomes STATE_IDLE or STATE_ENDED. + * Allow to remove all playlist items that makes the player reset. ### 2.12.1 (2020-10-23) ### diff --git a/extensions/media2/src/androidTest/java/com/google/android/exoplayer2/ext/media2/SessionPlayerConnectorTest.java b/extensions/media2/src/androidTest/java/com/google/android/exoplayer2/ext/media2/SessionPlayerConnectorTest.java index cd831fcf1f..1747b3aed7 100644 --- a/extensions/media2/src/androidTest/java/com/google/android/exoplayer2/ext/media2/SessionPlayerConnectorTest.java +++ b/extensions/media2/src/androidTest/java/com/google/android/exoplayer2/ext/media2/SessionPlayerConnectorTest.java @@ -15,6 +15,7 @@ */ package com.google.android.exoplayer2.ext.media2; +import static androidx.media2.common.SessionPlayer.PLAYER_STATE_IDLE; import static androidx.media2.common.SessionPlayer.PLAYER_STATE_PAUSED; import static androidx.media2.common.SessionPlayer.PLAYER_STATE_PLAYING; import static androidx.media2.common.SessionPlayer.PlayerResult.RESULT_INFO_SKIPPED; @@ -60,6 +61,7 @@ import java.util.List; import java.util.concurrent.CopyOnWriteArrayList; import java.util.concurrent.CountDownLatch; import java.util.concurrent.Executor; +import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicLong; import java.util.concurrent.atomic.AtomicReference; import org.junit.Before; @@ -762,18 +764,42 @@ public class SessionPlayerConnectorTest { @SdkSuppress(minSdkVersion = Build.VERSION_CODES.KITKAT) public void setPlaylist_setsPlaylistAndCurrentMediaItem() throws Exception { List playlist = TestUtils.createPlaylist(10); - CountDownLatch onCurrentMediaItemChangedLatch = new CountDownLatch(1); - sessionPlayerConnector.registerPlayerCallback( - executor, new PlayerCallbackForPlaylist(playlist, onCurrentMediaItemChangedLatch)); + PlayerCallbackForPlaylist callback = new PlayerCallbackForPlaylist(playlist, 1); + sessionPlayerConnector.registerPlayerCallback(executor, callback); assertPlayerResultSuccess(sessionPlayerConnector.setPlaylist(playlist, null)); - assertThat(onCurrentMediaItemChangedLatch.await(PLAYLIST_CHANGE_WAIT_TIME_MS, MILLISECONDS)) - .isTrue(); + assertThat(callback.await(PLAYLIST_CHANGE_WAIT_TIME_MS, MILLISECONDS)).isTrue(); assertThat(sessionPlayerConnector.getPlaylist()).isEqualTo(playlist); assertThat(sessionPlayerConnector.getCurrentMediaItem()).isEqualTo(playlist.get(0)); } + @Test + @LargeTest + @SdkSuppress(minSdkVersion = Build.VERSION_CODES.KITKAT) + public void setPlaylistAndRemoveAllPlaylistItem_playerStateBecomesIdle() throws Exception { + List playlist = new ArrayList<>(); + playlist.add(TestUtils.createMediaItem(R.raw.video_1)); + PlayerCallbackForPlaylist callback = + new PlayerCallbackForPlaylist(playlist, 2) { + @Override + public void onPlayerStateChanged(@NonNull SessionPlayer player, int playerState) { + countDown(); + } + }; + sessionPlayerConnector.registerPlayerCallback(executor, callback); + + assertPlayerResultSuccess(sessionPlayerConnector.setPlaylist(playlist, null)); + assertPlayerResultSuccess(sessionPlayerConnector.prepare()); + assertThat(callback.await(PLAYLIST_CHANGE_WAIT_TIME_MS, MILLISECONDS)).isTrue(); + assertThat(sessionPlayerConnector.getPlayerState()).isEqualTo(PLAYER_STATE_PAUSED); + + callback.resetLatch(1); + assertPlayerResultSuccess(sessionPlayerConnector.removePlaylistItem(0)); + assertThat(callback.await(PLAYLIST_CHANGE_WAIT_TIME_MS, MILLISECONDS)).isTrue(); + assertThat(sessionPlayerConnector.getPlayerState()).isEqualTo(PLAYER_STATE_IDLE); + } + @Test @LargeTest @SdkSuppress(minSdkVersion = Build.VERSION_CODES.KITKAT) @@ -958,14 +984,12 @@ public class SessionPlayerConnectorTest { int listSize = 2; List playlist = TestUtils.createPlaylist(listSize); - CountDownLatch onCurrentMediaItemChangedLatch = new CountDownLatch(1); - sessionPlayerConnector.registerPlayerCallback( - executor, new PlayerCallbackForPlaylist(playlist, onCurrentMediaItemChangedLatch)); + PlayerCallbackForPlaylist callback = new PlayerCallbackForPlaylist(playlist, 1); + sessionPlayerConnector.registerPlayerCallback(executor, callback); assertPlayerResultSuccess(sessionPlayerConnector.setPlaylist(playlist, null)); assertThat(sessionPlayerConnector.getCurrentMediaItemIndex()).isEqualTo(0); - assertThat(onCurrentMediaItemChangedLatch.await(PLAYLIST_CHANGE_WAIT_TIME_MS, MILLISECONDS)) - .isTrue(); + assertThat(callback.await(PLAYLIST_CHANGE_WAIT_TIME_MS, MILLISECONDS)).isTrue(); } @Test @@ -1193,16 +1217,15 @@ public class SessionPlayerConnectorTest { int listSize = playlist.size(); // Any value more than list size + 1, to see repeat mode with the recorded video. - CountDownLatch onCurrentMediaItemChangedLatch = new CountDownLatch(listSize + 2); CopyOnWriteArrayList currentMediaItemChanges = new CopyOnWriteArrayList<>(); PlayerCallbackForPlaylist callback = - new PlayerCallbackForPlaylist(playlist, onCurrentMediaItemChangedLatch) { + new PlayerCallbackForPlaylist(playlist, listSize + 2) { @Override public void onCurrentMediaItemChanged( @NonNull SessionPlayer player, @NonNull MediaItem item) { super.onCurrentMediaItemChanged(player, item); currentMediaItemChanges.add(item); - onCurrentMediaItemChangedLatch.countDown(); + countDown(); } @Override @@ -1223,7 +1246,7 @@ public class SessionPlayerConnectorTest { assertWithMessage( "Current media item didn't change as expected. Actual changes were %s", currentMediaItemChanges) - .that(onCurrentMediaItemChangedLatch.await(PLAYBACK_COMPLETED_WAIT_TIME_MS, MILLISECONDS)) + .that(callback.await(PLAYBACK_COMPLETED_WAIT_TIME_MS, MILLISECONDS)) .isTrue(); int expectedMediaItemIndex = 0; @@ -1285,9 +1308,9 @@ public class SessionPlayerConnectorTest { private List playlist; private CountDownLatch onCurrentMediaItemChangedLatch; - PlayerCallbackForPlaylist(List playlist, CountDownLatch latch) { + PlayerCallbackForPlaylist(List playlist, int count) { this.playlist = playlist; - onCurrentMediaItemChangedLatch = latch; + onCurrentMediaItemChangedLatch = new CountDownLatch(count); } @Override @@ -1296,5 +1319,17 @@ public class SessionPlayerConnectorTest { assertThat(sessionPlayerConnector.getCurrentMediaItemIndex()).isEqualTo(currentIndex); onCurrentMediaItemChangedLatch.countDown(); } + + public void resetLatch(int count) { + onCurrentMediaItemChangedLatch = new CountDownLatch(count); + } + + public boolean await(long timeout, TimeUnit unit) throws InterruptedException { + return onCurrentMediaItemChangedLatch.await(timeout, unit); + } + + public void countDown() { + onCurrentMediaItemChangedLatch.countDown(); + } } } diff --git a/extensions/media2/src/main/java/com/google/android/exoplayer2/ext/media2/PlayerWrapper.java b/extensions/media2/src/main/java/com/google/android/exoplayer2/ext/media2/PlayerWrapper.java index 6ed27e512a..21659637ab 100644 --- a/extensions/media2/src/main/java/com/google/android/exoplayer2/ext/media2/PlayerWrapper.java +++ b/extensions/media2/src/main/java/com/google/android/exoplayer2/ext/media2/PlayerWrapper.java @@ -202,6 +202,9 @@ import java.util.List; } public boolean removePlaylistItem(@IntRange(from = 0) int index) { + if (player.getMediaItemCount() <= index) { + return false; + } player.removeMediaItem(index); return true; } @@ -367,7 +370,9 @@ import java.util.List; case Player.STATE_IDLE: return SessionPlayer.PLAYER_STATE_IDLE; case Player.STATE_ENDED: - return SessionPlayer.PLAYER_STATE_PAUSED; + return player.getCurrentMediaItem() == null + ? SessionPlayer.PLAYER_STATE_IDLE + : SessionPlayer.PLAYER_STATE_PAUSED; case Player.STATE_BUFFERING: case Player.STATE_READY: return playWhenReady @@ -428,7 +433,9 @@ import java.util.List; postOrRun(handler, pollBufferRunnable); break; case Player.STATE_ENDED: - listener.onPlaybackEnded(); + if (player.getCurrentMediaItem() != null) { + listener.onPlaybackEnded(); + } player.setPlayWhenReady(false); updateBufferingState(/* isBuffering= */ false); break; diff --git a/extensions/media2/src/main/java/com/google/android/exoplayer2/ext/media2/SessionPlayerConnector.java b/extensions/media2/src/main/java/com/google/android/exoplayer2/ext/media2/SessionPlayerConnector.java index 1c6cc151c9..ccb29f8ca6 100644 --- a/extensions/media2/src/main/java/com/google/android/exoplayer2/ext/media2/SessionPlayerConnector.java +++ b/extensions/media2/src/main/java/com/google/android/exoplayer2/ext/media2/SessionPlayerConnector.java @@ -559,12 +559,16 @@ public final class SessionPlayerConnector extends SessionPlayer { } } + // TODO: Remove this suppress warnings and call onCurrentMediaItemChanged with a null item + // once AndroidX media2 1.2.0 is released + @SuppressWarnings("nullness:argument.type.incompatible") private void handlePlaylistChangedOnHandler() { List currentPlaylist = player.getPlaylist(); MediaMetadata playlistMetadata = player.getPlaylistMetadata(); MediaItem currentMediaItem = player.getCurrentMediaItem(); - boolean notifyCurrentMediaItem = !ObjectsCompat.equals(this.currentMediaItem, currentMediaItem); + boolean notifyCurrentMediaItem = + !ObjectsCompat.equals(this.currentMediaItem, currentMediaItem) && currentMediaItem != null; this.currentMediaItem = currentMediaItem; long currentPosition = getCurrentPosition(); @@ -573,9 +577,6 @@ public final class SessionPlayerConnector extends SessionPlayer { callback.onPlaylistChanged( SessionPlayerConnector.this, currentPlaylist, playlistMetadata); if (notifyCurrentMediaItem) { - Assertions.checkNotNull( - currentMediaItem, "PlaylistManager#currentMediaItem() cannot be changed to null"); - callback.onCurrentMediaItemChanged(SessionPlayerConnector.this, currentMediaItem); // Workaround for MediaSession's issue that current media item change isn't propagated