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);