Propagate media3 session extras to media1 PlaybackStateCompat

PiperOrigin-RevId: 642299178
This commit is contained in:
ibaker 2024-06-11 09:41:33 -07:00 committed by Copybara-Service
parent b145a96c79
commit 2456669398
6 changed files with 160 additions and 27 deletions

View File

@ -26,6 +26,9 @@
and how to resolve the error if possible.
* Publish the code for the media3 controller test app that can be used to
test interactions with apps publishing a media session.
* Propagate extras passed to media3's
`MediaSession[Builder].setSessionExtras()` to a media1 controller's
`PlaybackStateCompat.getExtras()`.
* UI:
* Add customisation of various icons in `PlayerControlView` through xml
attributes to allow different drawables per `PlayerView` instance,

View File

@ -240,7 +240,8 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
playIfSuppressed,
customLayout,
connectionResult.availableSessionCommands,
connectionResult.availablePlayerCommands);
connectionResult.availablePlayerCommands,
sessionExtras);
this.playerWrapper = playerWrapper;
postOrRun(
applicationHandler,
@ -265,7 +266,8 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
playIfSuppressed,
playerWrapper.getCustomLayout(),
playerWrapper.getAvailableSessionCommands(),
playerWrapper.getAvailablePlayerCommands()));
playerWrapper.getAvailablePlayerCommands(),
playerWrapper.getLegacyExtras()));
}
private void setPlayerInternal(

View File

@ -1113,6 +1113,8 @@ import org.checkerframework.checker.initialization.qual.Initialized;
@Override
public void onSessionExtrasChanged(int seq, Bundle sessionExtras) {
sessionCompat.setExtras(sessionExtras);
sessionImpl.getPlayerWrapper().setLegacyExtras(sessionExtras);
sessionCompat.setPlaybackState(sessionImpl.getPlayerWrapper().createPlaybackStateCompat());
}
@Override

View File

@ -15,6 +15,7 @@
*/
package androidx.media3.session;
import static androidx.media3.common.util.Assertions.checkArgument;
import static androidx.media3.common.util.Assertions.checkNotNull;
import static androidx.media3.common.util.Assertions.checkState;
import static androidx.media3.common.util.Util.msToUs;
@ -70,6 +71,7 @@ import java.util.List;
private int legacyStatusCode;
@Nullable private String legacyErrorMessage;
@Nullable private Bundle legacyErrorExtras;
@Nullable private Bundle legacyExtras;
private ImmutableList<CommandButton> customLayout;
private SessionCommands availableSessionCommands;
private Commands availablePlayerCommands;
@ -79,12 +81,14 @@ import java.util.List;
boolean playIfSuppressed,
ImmutableList<CommandButton> customLayout,
SessionCommands availableSessionCommands,
Commands availablePlayerCommands) {
Commands availablePlayerCommands,
@Nullable Bundle legacyExtras) {
super(player);
this.playIfSuppressed = playIfSuppressed;
this.customLayout = customLayout;
this.availableSessionCommands = availableSessionCommands;
this.availablePlayerCommands = availablePlayerCommands;
this.legacyExtras = legacyExtras;
legacyStatusCode = STATUS_CODE_SUCCESS_COMPAT;
}
@ -110,6 +114,19 @@ import java.util.List;
return customLayout;
}
public void setLegacyExtras(@Nullable Bundle extras) {
if (extras != null) {
checkArgument(!extras.containsKey(EXTRAS_KEY_PLAYBACK_SPEED_COMPAT));
checkArgument(!extras.containsKey(EXTRAS_KEY_MEDIA_ID_COMPAT));
}
this.legacyExtras = extras;
}
@Nullable
public Bundle getLegacyExtras() {
return legacyExtras;
}
/**
* Sets the legacy error code.
*
@ -1000,6 +1017,13 @@ import java.util.List;
public PlaybackStateCompat createPlaybackStateCompat() {
if (legacyStatusCode != STATUS_CODE_SUCCESS_COMPAT) {
Bundle extras;
if (legacyExtras != null) {
extras = new Bundle(checkNotNull(legacyErrorExtras));
extras.putAll(legacyExtras);
} else {
extras = checkNotNull(legacyErrorExtras);
}
return new PlaybackStateCompat.Builder()
.setState(
PlaybackStateCompat.STATE_ERROR,
@ -1009,7 +1033,7 @@ import java.util.List;
.setActions(0)
.setBufferedPosition(0)
.setErrorMessage(legacyStatusCode, checkNotNull(legacyErrorMessage))
.setExtras(checkNotNull(legacyErrorExtras))
.setExtras(extras)
.build();
}
@Nullable PlaybackException playerError = getPlayerError();
@ -1027,7 +1051,7 @@ import java.util.List;
: MediaSessionCompat.QueueItem.UNKNOWN_ID;
float playbackSpeed = getPlaybackParameters().speed;
float sessionPlaybackSpeed = isPlaying() ? playbackSpeed : 0f;
Bundle extras = new Bundle();
Bundle extras = legacyExtras != null ? new Bundle(legacyExtras) : new Bundle();
extras.putFloat(EXTRAS_KEY_PLAYBACK_SPEED_COMPAT, playbackSpeed);
@Nullable MediaItem currentMediaItem = getCurrentMediaItemWithCommandCheck();
if (currentMediaItem != null && !MediaItem.DEFAULT_MEDIA_ID.equals(currentMediaItem.mediaId)) {

View File

@ -49,7 +49,8 @@ public class PlayerWrapperTest {
/* playIfSuppressed= */ true,
ImmutableList.of(),
SessionCommands.EMPTY,
Player.Commands.EMPTY);
Player.Commands.EMPTY,
/* legacyExtras= */ null);
when(player.isCommandAvailable(anyInt())).thenReturn(true);
when(player.getApplicationLooper()).thenReturn(Looper.myLooper());
}

View File

@ -25,6 +25,7 @@ import static androidx.media3.test.session.common.MediaSessionConstants.TEST_MED
import static androidx.media3.test.session.common.MediaSessionConstants.TEST_SET_SHOW_PLAY_BUTTON_IF_SUPPRESSED_TO_FALSE;
import static androidx.media3.test.session.common.TestUtils.LONG_TIMEOUT_MS;
import static androidx.media3.test.session.common.TestUtils.TIMEOUT_MS;
import static androidx.test.ext.truth.os.BundleSubject.assertThat;
import static com.google.common.truth.Truth.assertThat;
import static java.util.concurrent.TimeUnit.MILLISECONDS;
@ -999,33 +1000,106 @@ public class MediaControllerCompatCallbackWithMediaSessionTest {
}
@Test
public void setSessionExtras_cnExtrasChangedCalled() throws Exception {
public void setSessionExtras_toAllControllers_extrasAndStateCallbacks() throws Exception {
Bundle sessionExtras = new Bundle();
sessionExtras.putString("key-0", "value-0");
CountDownLatch latch = new CountDownLatch(1);
List<Bundle> receivedSessionExtras = new ArrayList<>();
CountDownLatch onExtrasChangedCalled = new CountDownLatch(1);
CountDownLatch onPlaybackStateChangedCalled = new CountDownLatch(1);
AtomicReference<Bundle> sessionExtrasFromCallback = new AtomicReference<>();
AtomicReference<Bundle> sessionExtrasFromController = new AtomicReference<>();
AtomicReference<Bundle> playbackStateExtrasFromCallback = new AtomicReference<>();
AtomicReference<Bundle> playbackStateExtrasFromController = new AtomicReference<>();
MediaControllerCompat.Callback callback =
new MediaControllerCompat.Callback() {
@Override
public void onExtrasChanged(Bundle extras) {
receivedSessionExtras.add(extras);
receivedSessionExtras.add(controllerCompat.getExtras());
latch.countDown();
sessionExtrasFromCallback.set(extras);
sessionExtrasFromController.set(controllerCompat.getExtras());
onExtrasChangedCalled.countDown();
}
@Override
public void onPlaybackStateChanged(PlaybackStateCompat state) {
playbackStateExtrasFromCallback.set(state.getExtras());
playbackStateExtrasFromController.set(controllerCompat.getPlaybackState().getExtras());
onPlaybackStateChangedCalled.countDown();
}
};
controllerCompat.registerCallback(callback, handler);
session.setSessionExtras(sessionExtras);
assertThat(latch.await(TIMEOUT_MS, MILLISECONDS)).isTrue();
assertThat(TestUtils.equals(receivedSessionExtras.get(0), sessionExtras)).isTrue();
assertThat(TestUtils.equals(receivedSessionExtras.get(1), sessionExtras)).isTrue();
assertThat(onExtrasChangedCalled.await(TIMEOUT_MS, MILLISECONDS)).isTrue();
assertThat(onPlaybackStateChangedCalled.await(TIMEOUT_MS, MILLISECONDS)).isTrue();
assertThat(sessionExtrasFromCallback.get()).hasSize(1);
assertThat(sessionExtrasFromCallback.get()).string("key-0").isEqualTo("value-0");
assertThat(sessionExtrasFromController.get()).hasSize(1);
assertThat(sessionExtrasFromController.get()).string("key-0").isEqualTo("value-0");
assertThat(playbackStateExtrasFromCallback.get()).hasSize(2);
assertThat(playbackStateExtrasFromCallback.get())
.doubleFloat(MediaConstants.EXTRAS_KEY_PLAYBACK_SPEED_COMPAT)
.isEqualTo(0.0);
assertThat(playbackStateExtrasFromCallback.get()).string("key-0").isEqualTo("value-0");
assertThat(playbackStateExtrasFromController.get()).hasSize(2);
assertThat(playbackStateExtrasFromController.get())
.doubleFloat(MediaConstants.EXTRAS_KEY_PLAYBACK_SPEED_COMPAT)
.isEqualTo(0.0);
assertThat(playbackStateExtrasFromController.get()).string("key-0").isEqualTo("value-0");
}
@Test
public void setSessionExtras_toMediaNotificationControllers_extrasAndStateCallbacks()
throws Exception {
Bundle sessionExtras = new Bundle();
sessionExtras.putString("key-0", "value-0");
CountDownLatch onExtrasChangedCalled = new CountDownLatch(1);
CountDownLatch onPlaybackStateChangedCalled = new CountDownLatch(1);
AtomicReference<Bundle> sessionExtrasFromCallback = new AtomicReference<>();
AtomicReference<Bundle> sessionExtrasFromController = new AtomicReference<>();
AtomicReference<Bundle> playbackStateExtrasFromCallback = new AtomicReference<>();
AtomicReference<Bundle> playbackStateExtrasFromController = new AtomicReference<>();
MediaControllerCompat.Callback callback =
new MediaControllerCompat.Callback() {
@Override
public void onExtrasChanged(Bundle extras) {
sessionExtrasFromCallback.set(extras);
sessionExtrasFromController.set(controllerCompat.getExtras());
onExtrasChangedCalled.countDown();
}
@Override
public void onPlaybackStateChanged(PlaybackStateCompat state) {
playbackStateExtrasFromCallback.set(state.getExtras());
playbackStateExtrasFromController.set(controllerCompat.getPlaybackState().getExtras());
onPlaybackStateChangedCalled.countDown();
}
};
controllerCompat.registerCallback(callback, handler);
session.setSessionExtras(/* controllerKey= */ NOTIFICATION_CONTROLLER_KEY, sessionExtras);
assertThat(onExtrasChangedCalled.await(TIMEOUT_MS, MILLISECONDS)).isTrue();
assertThat(onPlaybackStateChangedCalled.await(TIMEOUT_MS, MILLISECONDS)).isTrue();
assertThat(sessionExtrasFromCallback.get()).hasSize(1);
assertThat(sessionExtrasFromCallback.get()).string("key-0").isEqualTo("value-0");
assertThat(sessionExtrasFromController.get()).hasSize(1);
assertThat(sessionExtrasFromController.get()).string("key-0").isEqualTo("value-0");
assertThat(playbackStateExtrasFromCallback.get()).hasSize(2);
assertThat(playbackStateExtrasFromCallback.get())
.doubleFloat(MediaConstants.EXTRAS_KEY_PLAYBACK_SPEED_COMPAT)
.isEqualTo(0.0);
assertThat(playbackStateExtrasFromCallback.get()).string("key-0").isEqualTo("value-0");
assertThat(playbackStateExtrasFromController.get()).hasSize(2);
assertThat(playbackStateExtrasFromController.get())
.doubleFloat(MediaConstants.EXTRAS_KEY_PLAYBACK_SPEED_COMPAT)
.isEqualTo(0.0);
assertThat(playbackStateExtrasFromController.get()).string("key-0").isEqualTo("value-0");
}
@Test
public void sendError_toAllControllers_onPlaybackStateChangedToErrorStateAndWithCorrectErrorData()
throws Exception {
CountDownLatch latch = new CountDownLatch(2);
CountDownLatch latch = new CountDownLatch(3);
List<PlaybackStateCompat> playbackStates = new ArrayList<>();
MediaControllerCompat.Callback callback =
new MediaControllerCompat.Callback() {
@ -1036,9 +1110,12 @@ public class MediaControllerCompatCallbackWithMediaSessionTest {
}
};
controllerCompat.registerCallback(callback, handler);
Bundle sessionExtras = new Bundle();
sessionExtras.putString("initialKey", "initialValue");
session.setSessionExtras(sessionExtras);
PlaybackStateCompat initialPlaybackStateCompat = controllerCompat.getPlaybackState();
Bundle errorBundle = new Bundle();
errorBundle.putInt("intKey", 99);
errorBundle.putInt("errorKey", 99);
session.sendError(
/* controllerKey= */ null,
@ -1047,13 +1124,19 @@ public class MediaControllerCompatCallbackWithMediaSessionTest {
errorBundle);
assertThat(latch.await(TIMEOUT_MS, MILLISECONDS)).isTrue();
assertThat(playbackStates).hasSize(2);
PlaybackStateCompat errorPlaybackStateCompat = playbackStates.get(0);
assertThat(playbackStates).hasSize(3);
// Skip the playback state from the first setSessionExtras() call.
PlaybackStateCompat errorPlaybackStateCompat = playbackStates.get(1);
assertThat(errorPlaybackStateCompat.getState()).isEqualTo(PlaybackStateCompat.STATE_ERROR);
assertThat(errorPlaybackStateCompat.getErrorCode()).isEqualTo(1);
assertThat(errorPlaybackStateCompat.getErrorMessage().toString())
.isEqualTo(context.getString(R.string.authentication_required));
PlaybackStateCompat resolvedPlaybackStateCompat = playbackStates.get(1);
assertThat(errorPlaybackStateCompat.getExtras()).hasSize(2);
assertThat(errorPlaybackStateCompat.getExtras()).integer("errorKey").isEqualTo(99);
assertThat(errorPlaybackStateCompat.getExtras()).string("initialKey").isEqualTo("initialValue");
PlaybackStateCompat resolvedPlaybackStateCompat = playbackStates.get(2);
assertThat(resolvedPlaybackStateCompat.getState())
.isEqualTo(initialPlaybackStateCompat.getState());
assertThat(resolvedPlaybackStateCompat.getErrorCode())
@ -1061,13 +1144,18 @@ public class MediaControllerCompatCallbackWithMediaSessionTest {
assertThat(resolvedPlaybackStateCompat.getErrorMessage()).isNull();
assertThat(resolvedPlaybackStateCompat.getActions())
.isEqualTo(initialPlaybackStateCompat.getActions());
assertThat(resolvedPlaybackStateCompat.getExtras())
.hasSize(initialPlaybackStateCompat.getExtras().size());
assertThat(resolvedPlaybackStateCompat.getExtras())
.string("initialKey")
.isEqualTo(initialPlaybackStateCompat.getExtras().getString("initialKey"));
}
@Test
public void
sendError_toMediaNotificationControllers_onPlaybackStateChangedToErrorStateAndWithCorrectErrorData()
throws Exception {
CountDownLatch latch = new CountDownLatch(2);
CountDownLatch latch = new CountDownLatch(3);
List<PlaybackStateCompat> playbackStates = new ArrayList<>();
MediaControllerCompat.Callback callback =
new MediaControllerCompat.Callback() {
@ -1078,10 +1166,13 @@ public class MediaControllerCompatCallbackWithMediaSessionTest {
}
};
controllerCompat.registerCallback(callback, handler);
Bundle sessionExtras = new Bundle();
sessionExtras.putString("initialKey", "initialValue");
session.setSessionExtras(/* controllerKey= */ NOTIFICATION_CONTROLLER_KEY, sessionExtras);
PlaybackStateCompat initialPlaybackStateCompat = controllerCompat.getPlaybackState();
Bundle errorBundle = new Bundle();
errorBundle.putInt("intKey", 99);
Bundle errorBundle = new Bundle();
errorBundle.putInt("errorKey", 99);
session.sendError(
/* controllerKey= */ NOTIFICATION_CONTROLLER_KEY,
/* errorCode= */ 1,
@ -1089,15 +1180,20 @@ public class MediaControllerCompatCallbackWithMediaSessionTest {
errorBundle);
assertThat(latch.await(TIMEOUT_MS, MILLISECONDS)).isTrue();
assertThat(playbackStates).hasSize(2);
PlaybackStateCompat errorPlaybackStateCompat = playbackStates.get(0);
assertThat(playbackStates).hasSize(3);
// Skip the playback state from the first setSessionExtras() call.
PlaybackStateCompat errorPlaybackStateCompat = playbackStates.get(1);
assertThat(errorPlaybackStateCompat.getState()).isEqualTo(PlaybackStateCompat.STATE_ERROR);
assertThat(errorPlaybackStateCompat.getErrorCode()).isEqualTo(1);
assertThat(errorPlaybackStateCompat.getErrorMessage().toString())
.isEqualTo(context.getString(R.string.authentication_required));
assertThat(errorPlaybackStateCompat.getActions()).isEqualTo(0);
assertThat(TestUtils.equals(errorPlaybackStateCompat.getExtras(), errorBundle)).isTrue();
PlaybackStateCompat resolvedPlaybackStateCompat = playbackStates.get(1);
assertThat(errorPlaybackStateCompat.getExtras()).hasSize(2);
assertThat(errorPlaybackStateCompat.getExtras()).string("initialKey").isEqualTo("initialValue");
assertThat(errorPlaybackStateCompat.getExtras()).integer("errorKey").isEqualTo(99);
PlaybackStateCompat resolvedPlaybackStateCompat = playbackStates.get(2);
assertThat(resolvedPlaybackStateCompat.getState())
.isEqualTo(initialPlaybackStateCompat.getState());
assertThat(resolvedPlaybackStateCompat.getErrorCode())
@ -1105,6 +1201,11 @@ public class MediaControllerCompatCallbackWithMediaSessionTest {
assertThat(resolvedPlaybackStateCompat.getErrorMessage()).isNull();
assertThat(resolvedPlaybackStateCompat.getActions())
.isEqualTo(initialPlaybackStateCompat.getActions());
assertThat(resolvedPlaybackStateCompat.getExtras())
.hasSize(initialPlaybackStateCompat.getExtras().size());
assertThat(resolvedPlaybackStateCompat.getExtras())
.string("initialKey")
.isEqualTo(initialPlaybackStateCompat.getExtras().getString("initialKey"));
}
@Test