diff --git a/libraries/session/src/main/java/androidx/media3/session/MediaConstants.java b/libraries/session/src/main/java/androidx/media3/session/MediaConstants.java index 2db4a53fec..0ebcd49075 100644 --- a/libraries/session/src/main/java/androidx/media3/session/MediaConstants.java +++ b/libraries/session/src/main/java/androidx/media3/session/MediaConstants.java @@ -32,7 +32,7 @@ import androidx.media3.session.MediaLibraryService.MediaLibrarySession; public final class MediaConstants { /** - * The legacy error code for expired authentication. + * {@link Bundle} key used for the error code for expired authentication. * *

Use this error code to indicate an expired authentication when {@linkplain * LibraryResult#ofError(int, LibraryParams) creating a library result} for an unsuccessful @@ -43,7 +43,23 @@ public final class MediaConstants { public static final int ERROR_CODE_AUTHENTICATION_EXPIRED_COMPAT = 3; /** - * The extras key for the localized error resolution string. + * {@link Bundle} key used for the value of {@code Player.getPlaybackParameters().speed}. + * + *

Use this key in the extras bundle of the legacy {@link PlaybackStateCompat}. + */ + @UnstableApi public static final String EXTRAS_KEY_PLAYBACK_SPEED_COMPAT = "EXO_SPEED"; + + /** + * {@link Bundle} key used for the media id of the media being played. + * + *

Use this key in the extras bundle of the legacy {@link PlaybackStateCompat}. + */ + @UnstableApi + public static final String EXTRAS_KEY_MEDIA_ID_COMPAT = + androidx.media.utils.MediaConstants.PLAYBACK_STATE_EXTRAS_KEY_MEDIA_ID; + + /** + * {@link Bundle} key used for a localized error resolution string. * *

Use this key to populate the extras bundle of the {@link LibraryParams} when {@linkplain * LibraryResult#ofError(int, LibraryParams) creating a library result} for an unsuccessful @@ -53,7 +69,7 @@ public final class MediaConstants { androidx.media.utils.MediaConstants.PLAYBACK_STATE_EXTRAS_KEY_ERROR_RESOLUTION_ACTION_LABEL; /** - * The extras key for the error resolution intent. + * {@link Bundle} key used for an error resolution intent. * *

Use this key to populate the extras bundle of the {@link LibraryParams} when {@linkplain * LibraryResult#ofError(int, LibraryParams) creating a library result} for an unsuccessful diff --git a/libraries/session/src/main/java/androidx/media3/session/MediaUtils.java b/libraries/session/src/main/java/androidx/media3/session/MediaUtils.java index f73ca3cfcb..ae17cc834f 100644 --- a/libraries/session/src/main/java/androidx/media3/session/MediaUtils.java +++ b/libraries/session/src/main/java/androidx/media3/session/MediaUtils.java @@ -728,20 +728,17 @@ import org.checkerframework.checker.nullness.compatqual.NullableType; public static int convertToPlaybackStateCompatState( @Nullable PlaybackException playerError, @Player.State int playbackState, - boolean playWhenReady, - boolean isPlaying) { + boolean playWhenReady) { if (playerError != null) { return PlaybackStateCompat.STATE_ERROR; } - if (isPlaying) { - return PlaybackStateCompat.STATE_PLAYING; - } switch (playbackState) { case Player.STATE_IDLE: return PlaybackStateCompat.STATE_NONE; case Player.STATE_READY: + return playWhenReady ? PlaybackStateCompat.STATE_PLAYING : PlaybackStateCompat.STATE_PAUSED; case Player.STATE_ENDED: - return PlaybackStateCompat.STATE_PAUSED; + return PlaybackStateCompat.STATE_STOPPED; case Player.STATE_BUFFERING: return playWhenReady ? PlaybackStateCompat.STATE_BUFFERING diff --git a/libraries/session/src/main/java/androidx/media3/session/PlayerWrapper.java b/libraries/session/src/main/java/androidx/media3/session/PlayerWrapper.java index 793fdd1136..a9eab3f1a3 100644 --- a/libraries/session/src/main/java/androidx/media3/session/PlayerWrapper.java +++ b/libraries/session/src/main/java/androidx/media3/session/PlayerWrapper.java @@ -18,6 +18,8 @@ package androidx.media3.session; import static androidx.media3.common.util.Assertions.checkNotNull; import static androidx.media3.common.util.Assertions.checkState; import static androidx.media3.common.util.Util.postOrRun; +import static androidx.media3.session.MediaConstants.EXTRAS_KEY_MEDIA_ID_COMPAT; +import static androidx.media3.session.MediaConstants.EXTRAS_KEY_PLAYBACK_SPEED_COMPAT; import android.media.AudioManager; import android.os.Bundle; @@ -768,7 +770,7 @@ import java.util.List; @Nullable PlaybackException playerError = getPlayerError(); int state = MediaUtils.convertToPlaybackStateCompatState( - playerError, getPlaybackState(), getPlayWhenReady(), isPlaying()); + playerError, getPlaybackState(), getPlayWhenReady()); long allActions = PlaybackStateCompat.ACTION_STOP | PlaybackStateCompat.ACTION_PAUSE @@ -798,16 +800,22 @@ import java.util.List; allActions |= PlaybackStateCompat.ACTION_SKIP_TO_NEXT; } long queueItemId = MediaUtils.convertToQueueItemId(getCurrentMediaItemIndex()); + float playbackSpeed = getPlaybackParameters().speed; + float sessionPlaybackSpeed = isPlaying() ? playbackSpeed : 0f; + Bundle extras = new Bundle(); + extras.putFloat(EXTRAS_KEY_PLAYBACK_SPEED_COMPAT, playbackSpeed); + @Nullable MediaItem currentMediaItem = getCurrentMediaItem(); + if (currentMediaItem != null && !MediaItem.DEFAULT_MEDIA_ID.equals(currentMediaItem.mediaId)) { + extras.putString(EXTRAS_KEY_MEDIA_ID_COMPAT, currentMediaItem.mediaId); + } PlaybackStateCompat.Builder builder = new PlaybackStateCompat.Builder() .setState( - state, - getCurrentPosition(), - getPlaybackParameters().speed, - SystemClock.elapsedRealtime()) + state, getCurrentPosition(), sessionPlaybackSpeed, SystemClock.elapsedRealtime()) .setActions(allActions) .setActiveQueueItemId(queueItemId) - .setBufferedPosition(getBufferedPosition()); + .setBufferedPosition(getBufferedPosition()) + .setExtras(extras); for (int i = 0; i < customLayout.size(); i++) { CommandButton commandButton = customLayout.get(i); diff --git a/libraries/test_session_common/src/main/aidl/androidx/media3/test/session/common/IRemoteMediaSession.aidl b/libraries/test_session_common/src/main/aidl/androidx/media3/test/session/common/IRemoteMediaSession.aidl index 4c119fdf6a..5220083112 100644 --- a/libraries/test_session_common/src/main/aidl/androidx/media3/test/session/common/IRemoteMediaSession.aidl +++ b/libraries/test_session_common/src/main/aidl/androidx/media3/test/session/common/IRemoteMediaSession.aidl @@ -55,7 +55,6 @@ interface IRemoteMediaSession { void notifyPlayerError(String sessionId, in Bundle playerErrorBundle); void notifyPlayWhenReadyChanged(String sessionId, boolean playWhenReady, int reason); void notifyPlaybackStateChanged(String sessionId, int state); - void notifyIsPlayingChanged(String sessionId, boolean isPlaying); void notifyIsLoadingChanged(String sessionId, boolean isLoading); void notifyPositionDiscontinuity(String sessionId, in Bundle oldPositionBundle, in Bundle newPositionBundle, int reason); diff --git a/libraries/test_session_common/src/main/java/androidx/media3/test/session/common/CommonConstants.java b/libraries/test_session_common/src/main/java/androidx/media3/test/session/common/CommonConstants.java index 64a92bd2a8..b7c70c4825 100644 --- a/libraries/test_session_common/src/main/java/androidx/media3/test/session/common/CommonConstants.java +++ b/libraries/test_session_common/src/main/java/androidx/media3/test/session/common/CommonConstants.java @@ -90,7 +90,6 @@ public class CommonConstants { public static final String KEY_PLAY_WHEN_READY = "playWhenReady"; public static final String KEY_PLAYBACK_SUPPRESSION_REASON = "playbackSuppressionReason"; public static final String KEY_PLAYBACK_STATE = "playbackState"; - public static final String KEY_IS_PLAYING = "isPlaying"; public static final String KEY_IS_LOADING = "isLoading"; public static final String KEY_REPEAT_MODE = "repeatMode"; public static final String KEY_SHUFFLE_MODE_ENABLED = "shuffleModeEnabled"; diff --git a/libraries/test_session_current/src/androidTest/java/androidx/media3/session/MediaBrowserCompatWithMediaLibraryServiceTest.java b/libraries/test_session_current/src/androidTest/java/androidx/media3/session/MediaBrowserCompatWithMediaLibraryServiceTest.java index d365a80edf..e9ec255abc 100644 --- a/libraries/test_session_current/src/androidTest/java/androidx/media3/session/MediaBrowserCompatWithMediaLibraryServiceTest.java +++ b/libraries/test_session_current/src/androidTest/java/androidx/media3/session/MediaBrowserCompatWithMediaLibraryServiceTest.java @@ -338,7 +338,6 @@ public class MediaBrowserCompatWithMediaLibraryServiceTest } }); assertThat(errorLatch.await(TIMEOUT_MS, MILLISECONDS)).isTrue(); - assertThat(lastReportedPlaybackStateCompat).isNotNull(); assertThat(lastReportedPlaybackStateCompat.getState()) .isEqualTo(PlaybackStateCompat.STATE_ERROR); assertThat( @@ -361,7 +360,11 @@ public class MediaBrowserCompatWithMediaLibraryServiceTest // Any successful calls remove the error state, assertThat(lastReportedPlaybackStateCompat.getState()) .isNotEqualTo(PlaybackStateCompat.STATE_ERROR); - assertThat(lastReportedPlaybackStateCompat.getExtras()).isNull(); + assertThat( + lastReportedPlaybackStateCompat + .getExtras() + .getString(MediaConstants.EXTRAS_KEY_ERROR_RESOLUTION_ACTION_LABEL_COMPAT)) + .isNull(); } @Test diff --git a/libraries/test_session_current/src/androidTest/java/androidx/media3/session/MediaControllerCompatCallbackWithMediaSessionTest.java b/libraries/test_session_current/src/androidTest/java/androidx/media3/session/MediaControllerCompatCallbackWithMediaSessionTest.java index ac53cdf47f..f44d842839 100644 --- a/libraries/test_session_current/src/androidTest/java/androidx/media3/session/MediaControllerCompatCallbackWithMediaSessionTest.java +++ b/libraries/test_session_current/src/androidTest/java/androidx/media3/session/MediaControllerCompatCallbackWithMediaSessionTest.java @@ -123,6 +123,7 @@ public class MediaControllerCompatCallbackWithMediaSessionTest { Bundle playerConfig = new RemoteMediaSession.MockPlayerConfigBuilder() .setPlaybackState(testState) + .setPlayWhenReady(true) .setBufferedPosition(testBufferingPosition) .setPlaybackParameters(new PlaybackParameters(testSpeed)) .setTimeline(testTimeline) @@ -153,7 +154,7 @@ public class MediaControllerCompatCallbackWithMediaSessionTest { .isEqualTo(testState); assertThat(controller.getPlaybackState().getBufferedPosition()) .isEqualTo(testBufferingPosition); - assertThat(controller.getPlaybackState().getPlaybackSpeed()).isWithin(EPSILON).of(testSpeed); + assertThat(controller.getPlaybackState().getPlaybackSpeed()).isEqualTo(testSpeed); assertThat(controller.getMetadata().getString(METADATA_KEY_MEDIA_ID)) .isEqualTo(testMediaItems.get(testItemIndex).mediaId); @@ -380,9 +381,7 @@ public class MediaControllerCompatCallbackWithMediaSessionTest { assertThat(latchForPlaybackState.await(TIMEOUT_MS, MILLISECONDS)).isTrue(); assertThat(playbackStateRef.get().getBufferedPosition()).isEqualTo(testBufferedPositionMs); assertThat(playbackStateRef.get().getPosition()).isEqualTo(testCurrentPositionMs); - assertThat(playbackStateRef.get().getPlaybackSpeed()) - .isWithin(EPSILON) - .of(playbackParameters.speed); + assertThat(playbackStateRef.get().getPlaybackSpeed()).isEqualTo(playbackParameters.speed); assertThat(latchForMetadata.await(TIMEOUT_MS, MILLISECONDS)).isTrue(); assertThat(metadataRef.get().getString(METADATA_KEY_MEDIA_ID)) @@ -604,7 +603,10 @@ public class MediaControllerCompatCallbackWithMediaSessionTest { @Test public void onPlaybackParametersChanged_notifiesPlaybackStateCompatChanges() throws Exception { PlaybackParameters playbackParameters = new PlaybackParameters(/* speed= */ 1.5f); - + session.getMockPlayer().setPlaybackState(Player.STATE_READY); + session + .getMockPlayer() + .setPlayWhenReady(/* playWhenReady= */ true, Player.PLAYBACK_SUPPRESSION_REASON_NONE); AtomicReference playbackStateRef = new AtomicReference<>(); CountDownLatch latch = new CountDownLatch(1); MediaControllerCompat.Callback callback = @@ -619,12 +621,21 @@ public class MediaControllerCompatCallbackWithMediaSessionTest { session.getMockPlayer().notifyPlaybackParametersChanged(playbackParameters); assertThat(latch.await(TIMEOUT_MS, MILLISECONDS)).isTrue(); - assertThat(playbackStateRef.get().getPlaybackSpeed()) - .isWithin(EPSILON) - .of(playbackParameters.speed); + assertThat(playbackStateRef.get().getPlaybackSpeed()).isEqualTo(playbackParameters.speed); + assertThat( + playbackStateRef + .get() + .getExtras() + .getFloat(MediaConstants.EXTRAS_KEY_PLAYBACK_SPEED_COMPAT)) + .isEqualTo(playbackParameters.speed); assertThat(controllerCompat.getPlaybackState().getPlaybackSpeed()) - .isWithin(EPSILON) - .of(playbackParameters.speed); + .isEqualTo(playbackParameters.speed); + assertThat( + controllerCompat + .getPlaybackState() + .getExtras() + .getFloat(MediaConstants.EXTRAS_KEY_PLAYBACK_SPEED_COMPAT)) + .isEqualTo(playbackParameters.speed); } @Test @@ -632,11 +643,8 @@ public class MediaControllerCompatCallbackWithMediaSessionTest { throws Exception { session .getMockPlayer() - .setPlayWhenReady( - /* playWhenReady= */ true, - Player.PLAYBACK_SUPPRESSION_REASON_TRANSIENT_AUDIO_FOCUS_LOSS); + .setPlayWhenReady(/* playWhenReady= */ true, Player.PLAYBACK_SUPPRESSION_REASON_NONE); session.getMockPlayer().notifyPlaybackStateChanged(STATE_READY); - session.getMockPlayer().notifyIsPlayingChanged(false); AtomicReference playbackStateCompatRef = new AtomicReference<>(); CountDownLatch latch = new CountDownLatch(1); @@ -657,6 +665,28 @@ public class MediaControllerCompatCallbackWithMediaSessionTest { assertThat(latch.await(TIMEOUT_MS, MILLISECONDS)).isTrue(); assertThat(playbackStateCompatRef.get().getState()).isEqualTo(PlaybackStateCompat.STATE_PAUSED); + assertThat(playbackStateCompatRef.get().getPlaybackSpeed()).isEqualTo(0f); + assertThat( + playbackStateCompatRef + .get() + .getExtras() + .getFloat(MediaConstants.EXTRAS_KEY_PLAYBACK_SPEED_COMPAT)) + .isEqualTo(1f); + assertThat( + playbackStateCompatRef + .get() + .getExtras() + .getFloat(MediaConstants.EXTRAS_KEY_PLAYBACK_SPEED_COMPAT)) + .isEqualTo(1f); + assertThat(controllerCompat.getPlaybackState().getState()) + .isEqualTo(PlaybackStateCompat.STATE_PAUSED); + assertThat(controllerCompat.getPlaybackState().getPlaybackSpeed()).isEqualTo(0f); + assertThat( + controllerCompat + .getPlaybackState() + .getExtras() + .getFloat(MediaConstants.EXTRAS_KEY_PLAYBACK_SPEED_COMPAT)) + .isEqualTo(1f); } @Test @@ -666,7 +696,6 @@ public class MediaControllerCompatCallbackWithMediaSessionTest { .getMockPlayer() .setPlayWhenReady(/* playWhenReady= */ false, Player.PLAYBACK_SUPPRESSION_REASON_NONE); session.getMockPlayer().notifyPlaybackStateChanged(Player.STATE_BUFFERING); - session.getMockPlayer().notifyIsPlayingChanged(false); AtomicReference playbackStateCompatRef = new AtomicReference<>(); CountDownLatch latch = new CountDownLatch(1); @@ -687,17 +716,36 @@ public class MediaControllerCompatCallbackWithMediaSessionTest { assertThat(playbackStateCompatRef.get().getState()) .isEqualTo(PlaybackStateCompat.STATE_BUFFERING); + assertThat(playbackStateCompatRef.get().getPlaybackSpeed()).isEqualTo(0f); + assertThat( + playbackStateCompatRef + .get() + .getExtras() + .getFloat(MediaConstants.EXTRAS_KEY_PLAYBACK_SPEED_COMPAT)) + .isEqualTo(1f); + assertThat( + playbackStateCompatRef + .get() + .getExtras() + .getFloat(MediaConstants.EXTRAS_KEY_PLAYBACK_SPEED_COMPAT)) + .isEqualTo(1f); + assertThat(controllerCompat.getPlaybackState().getState()) + .isEqualTo(PlaybackStateCompat.STATE_BUFFERING); + assertThat(controllerCompat.getPlaybackState().getPlaybackSpeed()).isEqualTo(0f); + assertThat( + controllerCompat + .getPlaybackState() + .getExtras() + .getFloat(MediaConstants.EXTRAS_KEY_PLAYBACK_SPEED_COMPAT)) + .isEqualTo(1f); } @Test - public void playbackStateChange_playbackStateBecomesEnded_notifiesPaused() throws Exception { + public void playbackStateChange_playbackStateBecomesEnded_notifiesStopped() throws Exception { session .getMockPlayer() - .setPlayWhenReady( - /* playWhenReady= */ true, - Player.PLAYBACK_SUPPRESSION_REASON_TRANSIENT_AUDIO_FOCUS_LOSS); + .setPlayWhenReady(/* playWhenReady= */ true, Player.PLAYBACK_SUPPRESSION_REASON_NONE); session.getMockPlayer().notifyPlaybackStateChanged(STATE_READY); - session.getMockPlayer().notifyIsPlayingChanged(false); AtomicReference playbackStateCompatRef = new AtomicReference<>(); CountDownLatch latch = new CountDownLatch(1); @@ -714,12 +762,39 @@ public class MediaControllerCompatCallbackWithMediaSessionTest { session.getMockPlayer().notifyPlaybackStateChanged(STATE_ENDED); assertThat(latch.await(TIMEOUT_MS, MILLISECONDS)).isTrue(); - assertThat(playbackStateCompatRef.get().getState()).isEqualTo(PlaybackStateCompat.STATE_PAUSED); + assertThat(playbackStateCompatRef.get().getState()) + .isEqualTo(PlaybackStateCompat.STATE_STOPPED); + assertThat(playbackStateCompatRef.get().getPlaybackSpeed()).isEqualTo(0f); + assertThat( + playbackStateCompatRef + .get() + .getExtras() + .getFloat(MediaConstants.EXTRAS_KEY_PLAYBACK_SPEED_COMPAT)) + .isEqualTo(1f); + assertThat( + playbackStateCompatRef + .get() + .getExtras() + .getFloat(MediaConstants.EXTRAS_KEY_PLAYBACK_SPEED_COMPAT)) + .isEqualTo(1f); + assertThat(controllerCompat.getPlaybackState().getState()) + .isEqualTo(PlaybackStateCompat.STATE_STOPPED); + assertThat(controllerCompat.getPlaybackState().getPlaybackSpeed()).isEqualTo(0f); + assertThat( + controllerCompat + .getPlaybackState() + .getExtras() + .getFloat(MediaConstants.EXTRAS_KEY_PLAYBACK_SPEED_COMPAT)) + .isEqualTo(1f); } @Test - public void playbackStateChange_isPlayingBecomesTrue_notifiesPlaying() throws Exception { - session.getMockPlayer().notifyIsPlayingChanged(false); + public void playbackStateChange_withPlaybackSuppression_notifiesPlayingWithSpeedZero() + throws Exception { + session.getMockPlayer().setPlaybackState(Player.STATE_READY); + session + .getMockPlayer() + .setPlayWhenReady(/* playWhenReady= */ true, Player.PLAYBACK_SUPPRESSION_REASON_NONE); AtomicReference playbackStateCompatRef = new AtomicReference<>(); CountDownLatch latch = new CountDownLatch(1); @@ -733,11 +808,83 @@ public class MediaControllerCompatCallbackWithMediaSessionTest { }; controllerCompat.registerCallback(callback, handler); - session.getMockPlayer().notifyIsPlayingChanged(true); + session + .getMockPlayer() + .notifyPlayWhenReadyChanged( + /* playWhenReady= */ true, + Player.PLAYBACK_SUPPRESSION_REASON_TRANSIENT_AUDIO_FOCUS_LOSS); assertThat(latch.await(TIMEOUT_MS, MILLISECONDS)).isTrue(); assertThat(playbackStateCompatRef.get().getState()) .isEqualTo(PlaybackStateCompat.STATE_PLAYING); + assertThat(playbackStateCompatRef.get().getPlaybackSpeed()).isEqualTo(0f); + assertThat( + playbackStateCompatRef + .get() + .getExtras() + .getFloat(MediaConstants.EXTRAS_KEY_PLAYBACK_SPEED_COMPAT)) + .isEqualTo(1f); + assertThat( + playbackStateCompatRef + .get() + .getExtras() + .getFloat(MediaConstants.EXTRAS_KEY_PLAYBACK_SPEED_COMPAT)) + .isEqualTo(1f); + assertThat(controllerCompat.getPlaybackState().getState()) + .isEqualTo(PlaybackStateCompat.STATE_PLAYING); + assertThat(controllerCompat.getPlaybackState().getPlaybackSpeed()).isEqualTo(0f); + assertThat( + controllerCompat + .getPlaybackState() + .getExtras() + .getFloat(MediaConstants.EXTRAS_KEY_PLAYBACK_SPEED_COMPAT)) + .isEqualTo(1f); + } + + @Test + public void playbackStateChange_playWhenReadyBecomesTrueWhenReady_notifiesPlaying() + throws Exception { + session.getMockPlayer().setPlaybackState(Player.STATE_READY); + session + .getMockPlayer() + .setPlayWhenReady(/* playWhenReady= */ false, Player.PLAYBACK_SUPPRESSION_REASON_NONE); + + AtomicReference playbackStateCompatRef = new AtomicReference<>(); + CountDownLatch latch = new CountDownLatch(1); + MediaControllerCompat.Callback callback = + new MediaControllerCompat.Callback() { + @Override + public void onPlaybackStateChanged(PlaybackStateCompat playbackStateCompat) { + playbackStateCompatRef.set(playbackStateCompat); + latch.countDown(); + } + }; + controllerCompat.registerCallback(callback, handler); + + session + .getMockPlayer() + .notifyPlayWhenReadyChanged( + /* playWhenReady= */ true, Player.PLAYBACK_SUPPRESSION_REASON_NONE); + assertThat(latch.await(TIMEOUT_MS, MILLISECONDS)).isTrue(); + + assertThat(playbackStateCompatRef.get().getState()) + .isEqualTo(PlaybackStateCompat.STATE_PLAYING); + assertThat(playbackStateCompatRef.get().getPlaybackSpeed()).isEqualTo(1f); + assertThat( + playbackStateCompatRef + .get() + .getExtras() + .getFloat(MediaConstants.EXTRAS_KEY_PLAYBACK_SPEED_COMPAT)) + .isEqualTo(1f); + assertThat(controllerCompat.getPlaybackState().getState()) + .isEqualTo(PlaybackStateCompat.STATE_PLAYING); + assertThat(controllerCompat.getPlaybackState().getPlaybackSpeed()).isEqualTo(1f); + assertThat( + controllerCompat + .getPlaybackState() + .getExtras() + .getFloat(MediaConstants.EXTRAS_KEY_PLAYBACK_SPEED_COMPAT)) + .isEqualTo(1f); } @Test diff --git a/libraries/test_session_current/src/androidTest/java/androidx/media3/session/MediaControllerListenerTest.java b/libraries/test_session_current/src/androidTest/java/androidx/media3/session/MediaControllerListenerTest.java index 36a598f988..3a69aeb043 100644 --- a/libraries/test_session_current/src/androidTest/java/androidx/media3/session/MediaControllerListenerTest.java +++ b/libraries/test_session_current/src/androidTest/java/androidx/media3/session/MediaControllerListenerTest.java @@ -1818,8 +1818,9 @@ public class MediaControllerListenerTest { @Test public void onIsPlayingChanged_isNotified() throws Exception { - boolean testIsPlaying = true; - remoteSession.getMockPlayer().notifyIsPlayingChanged(false); + remoteSession + .getMockPlayer() + .setPlayWhenReady(/* playWhenReady= */ true, Player.PLAYBACK_SUPPRESSION_REASON_NONE); MediaController controller = controllerTestRule.createController(remoteSession.getToken()); CountDownLatch latch = new CountDownLatch(2); AtomicBoolean isPlayingGetterRef = new AtomicBoolean(); @@ -1844,18 +1845,17 @@ public class MediaControllerListenerTest { }; threadTestRule.getHandler().postAndSync(() -> controller.addListener(listener)); - remoteSession.getMockPlayer().notifyIsPlayingChanged(testIsPlaying); + remoteSession.getMockPlayer().notifyPlaybackStateChanged(Player.STATE_READY); assertThat(latch.await(TIMEOUT_MS, MILLISECONDS)).isTrue(); - assertThat(isPlayingParamRef.get()).isEqualTo(testIsPlaying); - assertThat(isPlayingGetterRef.get()).isEqualTo(testIsPlaying); - assertThat(isPlayingOnEventsRef.get()).isEqualTo(testIsPlaying); - assertThat(getEventsAsList(eventsRef.get())).containsExactly(Player.EVENT_IS_PLAYING_CHANGED); + assertThat(isPlayingParamRef.get()).isTrue(); + assertThat(isPlayingGetterRef.get()).isTrue(); + assertThat(isPlayingOnEventsRef.get()).isTrue(); + assertThat(getEventsAsList(eventsRef.get())).contains(Player.EVENT_IS_PLAYING_CHANGED); } @Test public void onIsPlayingChanged_updatesGetters() throws Exception { - boolean testIsPlaying = true; long testCurrentPositionMs = 11; long testContentPositionMs = testCurrentPositionMs; // Not playing an ad long testBufferedPositionMs = 100; @@ -1863,7 +1863,9 @@ public class MediaControllerListenerTest { long testTotalBufferedDurationMs = 120; long testCurrentLiveOffsetMs = 10; long testContentBufferedPositionMs = 240; - remoteSession.getMockPlayer().notifyIsPlayingChanged(false); + remoteSession + .getMockPlayer() + .setPlayWhenReady(/* playWhenReady= */ true, Player.PLAYBACK_SUPPRESSION_REASON_NONE); MediaController controller = controllerTestRule.createController(remoteSession.getToken()); threadTestRule.getHandler().postAndSync(() -> controller.setTimeDiffMs(/* timeDiff= */ 0L)); CountDownLatch latch = new CountDownLatch(2); @@ -1906,10 +1908,10 @@ public class MediaControllerListenerTest { remoteSession.getMockPlayer().setTotalBufferedDuration(testTotalBufferedDurationMs); remoteSession.getMockPlayer().setCurrentLiveOffset(testCurrentLiveOffsetMs); remoteSession.getMockPlayer().setContentBufferedPosition(testContentBufferedPositionMs); - remoteSession.getMockPlayer().notifyIsPlayingChanged(testIsPlaying); + remoteSession.getMockPlayer().notifyPlaybackStateChanged(Player.STATE_READY); assertThat(latch.await(TIMEOUT_MS, MILLISECONDS)).isTrue(); - assertThat(isPlayingRef.get()).isEqualTo(testIsPlaying); + assertThat(isPlayingRef.get()).isEqualTo(true); assertThat(currentPositionMsRef.get()).isEqualTo(testCurrentPositionMs); assertThat(contentPositionMsRef.get()).isEqualTo(testContentPositionMs); assertThat(bufferedPositionMsRef.get()).isEqualTo(testBufferedPositionMs); @@ -1917,7 +1919,7 @@ public class MediaControllerListenerTest { assertThat(totalBufferedDurationMsRef.get()).isEqualTo(testTotalBufferedDurationMs); assertThat(currentLiveOffsetMsRef.get()).isEqualTo(testCurrentLiveOffsetMs); assertThat(contentBufferedPositionMsRef.get()).isEqualTo(testContentBufferedPositionMs); - assertThat(getEventsAsList(eventsRef.get())).containsExactly(Player.EVENT_IS_PLAYING_CHANGED); + assertThat(getEventsAsList(eventsRef.get())).contains(Player.EVENT_IS_PLAYING_CHANGED); } @Test diff --git a/libraries/test_session_current/src/androidTest/java/androidx/media3/session/MediaControllerTest.java b/libraries/test_session_current/src/androidTest/java/androidx/media3/session/MediaControllerTest.java index 046eede07a..3c13e756ce 100644 --- a/libraries/test_session_current/src/androidTest/java/androidx/media3/session/MediaControllerTest.java +++ b/libraries/test_session_current/src/androidTest/java/androidx/media3/session/MediaControllerTest.java @@ -279,7 +279,6 @@ public class MediaControllerTest { @Player.PlaybackSuppressionReason int playbackSuppressionReason = Player.PLAYBACK_SUPPRESSION_REASON_TRANSIENT_AUDIO_FOCUS_LOSS; @Player.State int playbackState = Player.STATE_READY; - boolean isPlaying = true; boolean isLoading = true; boolean isShuffleModeEnabled = true; @RepeatMode int repeatMode = Player.REPEAT_MODE_ONE; @@ -327,7 +326,6 @@ public class MediaControllerTest { .setPlayWhenReady(playWhenReady) .setPlaybackSuppressionReason(playbackSuppressionReason) .setPlaybackState(playbackState) - .setIsPlaying(isPlaying) .setIsLoading(isLoading) .setShuffleModeEnabled(isShuffleModeEnabled) .setRepeatMode(repeatMode) @@ -360,7 +358,6 @@ public class MediaControllerTest { AtomicBoolean playWhenReadyRef = new AtomicBoolean(); AtomicInteger playbackSuppressionReasonRef = new AtomicInteger(); AtomicInteger playbackStateRef = new AtomicInteger(); - AtomicBoolean isPlayingRef = new AtomicBoolean(); AtomicBoolean isLoadingRef = new AtomicBoolean(); AtomicBoolean isShuffleModeEnabledRef = new AtomicBoolean(); AtomicInteger repeatModeRef = new AtomicInteger(); @@ -393,7 +390,6 @@ public class MediaControllerTest { playWhenReadyRef.set(controller.getPlayWhenReady()); playbackSuppressionReasonRef.set(controller.getPlaybackSuppressionReason()); playbackStateRef.set(controller.getPlaybackState()); - isPlayingRef.set(controller.isPlaying()); isLoadingRef.set(controller.isLoading()); isShuffleModeEnabledRef.set(controller.getShuffleModeEnabled()); repeatModeRef.set(controller.getRepeatMode()); @@ -423,7 +419,6 @@ public class MediaControllerTest { assertThat(playWhenReadyRef.get()).isEqualTo(playWhenReady); assertThat(playbackSuppressionReasonRef.get()).isEqualTo(playbackSuppressionReason); assertThat(playbackStateRef.get()).isEqualTo(playbackState); - assertThat(isPlayingRef.get()).isEqualTo(isPlaying); assertThat(isLoadingRef.get()).isEqualTo(isLoading); assertThat(isShuffleModeEnabledRef.get()).isEqualTo(isShuffleModeEnabled); assertThat(repeatModeRef.get()).isEqualTo(repeatMode); @@ -842,7 +837,7 @@ public class MediaControllerTest { long testCurrentPositionMs = 100L; Bundle playerConfig = new RemoteMediaSession.MockPlayerConfigBuilder() - .setIsPlaying(false) + .setPlaybackState(Player.STATE_BUFFERING) .setCurrentPosition(testCurrentPositionMs) .setDuration(10_000L) .build(); @@ -868,7 +863,8 @@ public class MediaControllerTest { long testTimeDiff = 50L; Bundle playerConfig = new RemoteMediaSession.MockPlayerConfigBuilder() - .setIsPlaying(true) + .setPlaybackState(Player.STATE_READY) + .setPlayWhenReady(true) .setCurrentPosition(testCurrentPosition) .setDuration(10_000L) .setPlaybackParameters(testPlaybackParameters) @@ -897,7 +893,8 @@ public class MediaControllerTest { new RemoteMediaSession.MockPlayerConfigBuilder() .setContentPosition(testContentPosition) .setDuration(10_000L) - .setIsPlaying(true) + .setPlaybackState(Player.STATE_READY) + .setPlayWhenReady(true) .setIsPlayingAd(true) .setCurrentAdGroupIndex(0) .setCurrentAdIndexInAdGroup(0) @@ -925,7 +922,8 @@ public class MediaControllerTest { .setCurrentPosition(100L) .setContentPosition(100L) // Same as current position b/c not playing an ad .setDuration(10_000L) - .setIsPlaying(true) + .setPlayWhenReady(true) + .setPlaybackState(Player.STATE_READY) .setIsPlayingAd(false) .setPlaybackParameters(new PlaybackParameters(/* speed= */ 2.0f)) .build(); @@ -956,7 +954,8 @@ public class MediaControllerTest { .setCurrentPosition(10L) .setContentPosition(50L) .setDuration(10_000L) - .setIsPlaying(true) + .setPlayWhenReady(true) + .setPlaybackState(Player.STATE_READY) .setIsPlayingAd(true) .setCurrentAdGroupIndex(0) .setCurrentAdIndexInAdGroup(0) diff --git a/libraries/test_session_current/src/androidTest/java/androidx/media3/session/MediaSessionKeyEventTest.java b/libraries/test_session_current/src/androidTest/java/androidx/media3/session/MediaSessionKeyEventTest.java index 1ef9e15947..a6a1f2327f 100644 --- a/libraries/test_session_current/src/androidTest/java/androidx/media3/session/MediaSessionKeyEventTest.java +++ b/libraries/test_session_current/src/androidTest/java/androidx/media3/session/MediaSessionKeyEventTest.java @@ -21,6 +21,7 @@ import static androidx.media3.test.session.common.TestUtils.LONG_TIMEOUT_MS; import static androidx.media3.test.session.common.TestUtils.TIMEOUT_MS; import static com.google.common.truth.Truth.assertThat; import static java.util.concurrent.TimeUnit.MILLISECONDS; +import static org.junit.Assume.assumeTrue; import android.content.Context; import android.media.AudioManager; @@ -98,8 +99,14 @@ public class MediaSessionKeyEventTest { // Here's the requirement for an app to receive media key events via MediaSession. // - SDK < 26: Player should be playing for receiving key events // - SDK >= 26: Play a media item in the same process of the session for receiving key events. - handler.postAndSync(() -> player.notifyIsPlayingChanged(/* isPlaying= */ true)); - if (Util.SDK_INT >= 26) { + if (Util.SDK_INT < 26) { + handler.postAndSync( + () -> { + player.notifyPlayWhenReadyChanged( + /* playWhenReady= */ true, Player.PLAYBACK_SUPPRESSION_REASON_NONE); + player.notifyPlaybackStateChanged(Player.STATE_READY); + }); + } else { CountDownLatch latch = new CountDownLatch(1); handler.postAndSync( () -> { @@ -168,6 +175,10 @@ public class MediaSessionKeyEventTest { @Test public void playPauseKeyEvent_paused_play() throws Exception { + // We don't receive media key events when we are not playing on API < 26, so we can't test this + // case as it's not supported. + assumeTrue(Util.SDK_INT >= 26); + handler.postAndSync( () -> { player.playbackState = Player.STATE_READY; @@ -180,6 +191,10 @@ public class MediaSessionKeyEventTest { @Test public void playPauseKeyEvent_fromIdle_prepareAndPlay() throws Exception { + // We don't receive media key events when we are not playing on API < 26, so we can't test this + // case as it's not supported. + assumeTrue(Util.SDK_INT >= 26); + handler.postAndSync( () -> { player.playbackState = Player.STATE_IDLE; @@ -193,6 +208,10 @@ public class MediaSessionKeyEventTest { @Test public void playPauseKeyEvent_playWhenReadyAndEnded_seekAndPlay() throws Exception { + // We don't receive media key events when we are not playing on API < 26, so we can't test this + // case as it's not supported. + assumeTrue(Util.SDK_INT >= 26); + handler.postAndSync( () -> { player.playWhenReady = true; diff --git a/libraries/test_session_current/src/main/java/androidx/media3/session/MediaSessionProviderService.java b/libraries/test_session_current/src/main/java/androidx/media3/session/MediaSessionProviderService.java index fa312abd31..ce0c68eb09 100644 --- a/libraries/test_session_current/src/main/java/androidx/media3/session/MediaSessionProviderService.java +++ b/libraries/test_session_current/src/main/java/androidx/media3/session/MediaSessionProviderService.java @@ -36,7 +36,6 @@ import static androidx.media3.test.session.common.CommonConstants.KEY_DEVICE_MUT import static androidx.media3.test.session.common.CommonConstants.KEY_DEVICE_VOLUME; import static androidx.media3.test.session.common.CommonConstants.KEY_DURATION; import static androidx.media3.test.session.common.CommonConstants.KEY_IS_LOADING; -import static androidx.media3.test.session.common.CommonConstants.KEY_IS_PLAYING; import static androidx.media3.test.session.common.CommonConstants.KEY_IS_PLAYING_AD; import static androidx.media3.test.session.common.CommonConstants.KEY_MAX_SEEK_TO_PREVIOUS_POSITION_MS; import static androidx.media3.test.session.common.CommonConstants.KEY_MEDIA_METADATA; @@ -374,7 +373,6 @@ public class MediaSessionProviderService extends Service { player.playbackSuppressionReason = config.getInt(KEY_PLAYBACK_SUPPRESSION_REASON, player.playbackSuppressionReason); player.playbackState = config.getInt(KEY_PLAYBACK_STATE, player.playbackState); - player.isPlaying = config.getBoolean(KEY_IS_PLAYING, player.isPlaying); player.isLoading = config.getBoolean(KEY_IS_LOADING, player.isLoading); player.repeatMode = config.getInt(KEY_REPEAT_MODE, player.repeatMode); player.shuffleModeEnabled = @@ -695,16 +693,6 @@ public class MediaSessionProviderService extends Service { }); } - @Override - public void notifyIsPlayingChanged(String sessionId, boolean isPlaying) throws RemoteException { - runOnHandler( - () -> { - MediaSession session = sessionMap.get(sessionId); - MockPlayer player = (MockPlayer) session.getPlayer(); - player.notifyIsPlayingChanged(isPlaying); - }); - } - @Override public void notifyIsLoadingChanged(String sessionId, boolean isLoading) throws RemoteException { runOnHandler( diff --git a/libraries/test_session_current/src/main/java/androidx/media3/session/MockPlayer.java b/libraries/test_session_current/src/main/java/androidx/media3/session/MockPlayer.java index 282c88ef3f..2f1460035e 100644 --- a/libraries/test_session_current/src/main/java/androidx/media3/session/MockPlayer.java +++ b/libraries/test_session_current/src/main/java/androidx/media3/session/MockPlayer.java @@ -245,7 +245,6 @@ public class MockPlayer implements Player { public boolean playWhenReady; public @PlaybackSuppressionReason int playbackSuppressionReason; public @State int playbackState; - public boolean isPlaying; public boolean isLoading; public MediaMetadata mediaMetadata; public Commands commands; @@ -521,6 +520,12 @@ public class MockPlayer implements Player { } } + /** + * Changes the values returned from {@link #getPlayWhenReady()} and {@link + * #getPlaybackSuppressionReason()}, and triggers {@link Player.Listener#onPlayWhenReadyChanged}, + * {@link Player.Listener#onPlaybackSuppressionReasonChanged} or {@link + * Player.Listener#onIsPlayingChanged} as appropriate. + */ public void notifyPlayWhenReadyChanged( boolean playWhenReady, @PlayWhenReadyChangeReason int reason) { boolean playWhenReadyChanged = (this.playWhenReady != playWhenReady); @@ -529,8 +534,10 @@ public class MockPlayer implements Player { return; } + boolean wasPlaying = isPlaying(); this.playWhenReady = playWhenReady; this.playbackSuppressionReason = reason; + boolean isPlaying = isPlaying(); for (Listener listener : listeners) { if (playWhenReadyChanged) { listener.onPlayWhenReadyChanged( @@ -539,26 +546,29 @@ public class MockPlayer implements Player { if (playbackSuppressionReasonChanged) { listener.onPlaybackSuppressionReasonChanged(reason); } + if (isPlaying != wasPlaying) { + listener.onIsPlayingChanged(isPlaying); + } } } + /** + * Changes the value returned from {@link #getPlaybackState()} and triggers {@link + * Player.Listener#onPlaybackStateChanged} and/or {@link Player.Listener#onIsPlayingChanged} as + * appropriate. + */ public void notifyPlaybackStateChanged(@State int playbackState) { if (this.playbackState == playbackState) { return; } + boolean wasPlaying = isPlaying(); this.playbackState = playbackState; + boolean isPlaying = isPlaying(); for (Listener listener : listeners) { listener.onPlaybackStateChanged(playbackState); - } - } - - public void notifyIsPlayingChanged(boolean isPlaying) { - if (this.isPlaying == isPlaying) { - return; - } - this.isPlaying = isPlaying; - for (Listener listener : listeners) { - listener.onIsPlayingChanged(isPlaying); + if (isPlaying != wasPlaying) { + listener.onIsPlayingChanged(isPlaying); + } } } @@ -698,7 +708,9 @@ public class MockPlayer implements Player { @Override public boolean isPlaying() { - return isPlaying; + return playWhenReady + && playbackState == Player.STATE_READY + && playbackSuppressionReason == Player.PLAYBACK_SUPPRESSION_REASON_NONE; } @Override diff --git a/libraries/test_session_current/src/main/java/androidx/media3/session/RemoteMediaSession.java b/libraries/test_session_current/src/main/java/androidx/media3/session/RemoteMediaSession.java index e6647a6981..7d5cadfa8d 100644 --- a/libraries/test_session_current/src/main/java/androidx/media3/session/RemoteMediaSession.java +++ b/libraries/test_session_current/src/main/java/androidx/media3/session/RemoteMediaSession.java @@ -35,7 +35,6 @@ import static androidx.media3.test.session.common.CommonConstants.KEY_DEVICE_MUT import static androidx.media3.test.session.common.CommonConstants.KEY_DEVICE_VOLUME; import static androidx.media3.test.session.common.CommonConstants.KEY_DURATION; import static androidx.media3.test.session.common.CommonConstants.KEY_IS_LOADING; -import static androidx.media3.test.session.common.CommonConstants.KEY_IS_PLAYING; import static androidx.media3.test.session.common.CommonConstants.KEY_IS_PLAYING_AD; import static androidx.media3.test.session.common.CommonConstants.KEY_MAX_SEEK_TO_PREVIOUS_POSITION_MS; import static androidx.media3.test.session.common.CommonConstants.KEY_MEDIA_METADATA; @@ -296,10 +295,6 @@ public class RemoteMediaSession { binder.notifyPlaybackStateChanged(sessionId, state); } - public void notifyIsPlayingChanged(boolean isPlaying) throws RemoteException { - binder.notifyIsPlayingChanged(sessionId, isPlaying); - } - public void notifyIsLoadingChanged(boolean isLoading) throws RemoteException { binder.notifyIsLoadingChanged(sessionId, isLoading); } @@ -687,12 +682,6 @@ public class RemoteMediaSession { return this; } - @CanIgnoreReturnValue - public MockPlayerConfigBuilder setIsPlaying(boolean isPlaying) { - bundle.putBoolean(KEY_IS_PLAYING, isPlaying); - return this; - } - @CanIgnoreReturnValue public MockPlayerConfigBuilder setIsLoading(boolean isLoading) { bundle.putBoolean(KEY_IS_LOADING, isLoading);