Add session extras to the state of the controller

This change adds `MediaController.getSessionExtras()` through
which a controller can access the session extras.

The session extras can be set for the entire session when
building the session. This can be overridden for specific
controllers in `MediaSession.Callback.onConnect`.

PiperOrigin-RevId: 584430419
This commit is contained in:
bachinger 2023-11-21 14:29:56 -08:00 committed by Copybara-Service
parent 1d61c48266
commit a063d137b4
16 changed files with 383 additions and 57 deletions

View File

@ -53,6 +53,8 @@ import java.util.List;
public final Bundle tokenExtras; public final Bundle tokenExtras;
public final Bundle sessionExtras;
public final PlayerInfo playerInfo; public final PlayerInfo playerInfo;
public final ImmutableList<CommandButton> customLayout; public final ImmutableList<CommandButton> customLayout;
@ -67,6 +69,7 @@ import java.util.List;
Player.Commands playerCommandsFromSession, Player.Commands playerCommandsFromSession,
Player.Commands playerCommandsFromPlayer, Player.Commands playerCommandsFromPlayer,
Bundle tokenExtras, Bundle tokenExtras,
Bundle sessionExtras,
PlayerInfo playerInfo) { PlayerInfo playerInfo) {
this.libraryVersion = libraryVersion; this.libraryVersion = libraryVersion;
this.sessionInterfaceVersion = sessionInterfaceVersion; this.sessionInterfaceVersion = sessionInterfaceVersion;
@ -77,6 +80,7 @@ import java.util.List;
this.playerCommandsFromSession = playerCommandsFromSession; this.playerCommandsFromSession = playerCommandsFromSession;
this.playerCommandsFromPlayer = playerCommandsFromPlayer; this.playerCommandsFromPlayer = playerCommandsFromPlayer;
this.tokenExtras = tokenExtras; this.tokenExtras = tokenExtras;
this.sessionExtras = sessionExtras;
this.playerInfo = playerInfo; this.playerInfo = playerInfo;
} }
@ -90,11 +94,12 @@ import java.util.List;
private static final String FIELD_PLAYER_COMMANDS_FROM_SESSION = Util.intToStringMaxRadix(4); private static final String FIELD_PLAYER_COMMANDS_FROM_SESSION = Util.intToStringMaxRadix(4);
private static final String FIELD_PLAYER_COMMANDS_FROM_PLAYER = Util.intToStringMaxRadix(5); private static final String FIELD_PLAYER_COMMANDS_FROM_PLAYER = Util.intToStringMaxRadix(5);
private static final String FIELD_TOKEN_EXTRAS = Util.intToStringMaxRadix(6); private static final String FIELD_TOKEN_EXTRAS = Util.intToStringMaxRadix(6);
private static final String FIELD_SESSION_EXTRAS = Util.intToStringMaxRadix(11);
private static final String FIELD_PLAYER_INFO = Util.intToStringMaxRadix(7); private static final String FIELD_PLAYER_INFO = Util.intToStringMaxRadix(7);
private static final String FIELD_SESSION_INTERFACE_VERSION = Util.intToStringMaxRadix(8); private static final String FIELD_SESSION_INTERFACE_VERSION = Util.intToStringMaxRadix(8);
private static final String FIELD_IN_PROCESS_BINDER = Util.intToStringMaxRadix(10); private static final String FIELD_IN_PROCESS_BINDER = Util.intToStringMaxRadix(10);
// Next field key = 11 // Next field key = 12
@Override @Override
public Bundle toBundle() { public Bundle toBundle() {
@ -115,6 +120,7 @@ import java.util.List;
bundle.putBundle(FIELD_PLAYER_COMMANDS_FROM_SESSION, playerCommandsFromSession.toBundle()); bundle.putBundle(FIELD_PLAYER_COMMANDS_FROM_SESSION, playerCommandsFromSession.toBundle());
bundle.putBundle(FIELD_PLAYER_COMMANDS_FROM_PLAYER, playerCommandsFromPlayer.toBundle()); bundle.putBundle(FIELD_PLAYER_COMMANDS_FROM_PLAYER, playerCommandsFromPlayer.toBundle());
bundle.putBundle(FIELD_TOKEN_EXTRAS, tokenExtras); bundle.putBundle(FIELD_TOKEN_EXTRAS, tokenExtras);
bundle.putBundle(FIELD_SESSION_EXTRAS, sessionExtras);
Player.Commands intersectedCommands = Player.Commands intersectedCommands =
MediaUtils.intersect(playerCommandsFromSession, playerCommandsFromPlayer); MediaUtils.intersect(playerCommandsFromSession, playerCommandsFromPlayer);
bundle.putBundle( bundle.putBundle(
@ -179,6 +185,7 @@ import java.util.List;
? Player.Commands.EMPTY ? Player.Commands.EMPTY
: Player.Commands.fromBundle(playerCommandsFromSessionBundle); : Player.Commands.fromBundle(playerCommandsFromSessionBundle);
@Nullable Bundle tokenExtras = bundle.getBundle(FIELD_TOKEN_EXTRAS); @Nullable Bundle tokenExtras = bundle.getBundle(FIELD_TOKEN_EXTRAS);
@Nullable Bundle sessionExtras = bundle.getBundle(FIELD_SESSION_EXTRAS);
@Nullable Bundle playerInfoBundle = bundle.getBundle(FIELD_PLAYER_INFO); @Nullable Bundle playerInfoBundle = bundle.getBundle(FIELD_PLAYER_INFO);
PlayerInfo playerInfo = PlayerInfo playerInfo =
playerInfoBundle == null ? PlayerInfo.DEFAULT : PlayerInfo.fromBundle(playerInfoBundle); playerInfoBundle == null ? PlayerInfo.DEFAULT : PlayerInfo.fromBundle(playerInfoBundle);
@ -192,6 +199,7 @@ import java.util.List;
playerCommandsFromSession, playerCommandsFromSession,
playerCommandsFromPlayer, playerCommandsFromPlayer,
tokenExtras == null ? Bundle.EMPTY : tokenExtras, tokenExtras == null ? Bundle.EMPTY : tokenExtras,
sessionExtras == null ? Bundle.EMPTY : sessionExtras,
playerInfo); playerInfo);
} }

View File

@ -408,10 +408,10 @@ public class MediaController implements Player {
} }
/** /**
* Called when the session extras have changed. * Called when the session extras are set on the session side.
* *
* @param controller The controller. * @param controller The controller.
* @param extras The session extras that have changed. * @param extras The session extras that have been set on the session.
*/ */
default void onExtrasChanged(MediaController controller, Bundle extras) {} default void onExtrasChanged(MediaController controller, Bundle extras) {}
@ -967,6 +967,20 @@ public class MediaController implements Player {
return isConnected() ? impl.getCustomLayout() : ImmutableList.of(); return isConnected() ? impl.getCustomLayout() : ImmutableList.of();
} }
/**
* Returns the session extras.
*
* <p>After being connected, {@link Listener#onExtrasChanged(MediaController, Bundle)} is called
* when the extras on the session are set.
*
* @return The session extras.
*/
@UnstableApi
public final Bundle getSessionExtras() {
verifyApplicationThread();
return isConnected() ? impl.getSessionExtras() : Bundle.EMPTY;
}
/** Returns {@code null}. */ /** Returns {@code null}. */
@UnstableApi @UnstableApi
@Override @Override
@ -2029,6 +2043,8 @@ public class MediaController implements Player {
ImmutableList<CommandButton> getCustomLayout(); ImmutableList<CommandButton> getCustomLayout();
Bundle getSessionExtras();
Timeline getCurrentTimeline(); Timeline getCurrentTimeline();
void setMediaItem(MediaItem mediaItem); void setMediaItem(MediaItem mediaItem);

View File

@ -134,6 +134,7 @@ import org.checkerframework.checker.nullness.qual.NonNull;
private long lastSetPlayWhenReadyCalledTimeMs; private long lastSetPlayWhenReadyCalledTimeMs;
@Nullable private PlayerInfo pendingPlayerInfo; @Nullable private PlayerInfo pendingPlayerInfo;
@Nullable private BundlingExclusions pendingBundlingExclusions; @Nullable private BundlingExclusions pendingBundlingExclusions;
private Bundle sessionExtras;
public MediaControllerImplBase( public MediaControllerImplBase(
Context context, Context context,
@ -173,6 +174,7 @@ import org.checkerframework.checker.nullness.qual.NonNull;
.getInstance() .getInstance()
.runOnApplicationLooper(MediaControllerImplBase.this.getInstance()::release); .runOnApplicationLooper(MediaControllerImplBase.this.getInstance()::release);
surfaceCallback = new SurfaceCallback(); surfaceCallback = new SurfaceCallback();
sessionExtras = Bundle.EMPTY;
serviceConnection = serviceConnection =
(this.token.getType() == SessionToken.TYPE_SESSION) (this.token.getType() == SessionToken.TYPE_SESSION)
@ -727,6 +729,11 @@ import org.checkerframework.checker.nullness.qual.NonNull;
return customLayout; return customLayout;
} }
@Override
public Bundle getSessionExtras() {
return sessionExtras;
}
@Override @Override
public Timeline getCurrentTimeline() { public Timeline getCurrentTimeline() {
return playerInfo.timeline; return playerInfo.timeline;
@ -2545,6 +2552,7 @@ import org.checkerframework.checker.nullness.qual.NonNull;
token.getPackageName(), token.getPackageName(),
result.sessionBinder, result.sessionBinder,
result.tokenExtras); result.tokenExtras);
sessionExtras = result.sessionExtras;
getInstance().notifyAccepted(); getInstance().notifyAccepted();
} }
@ -2763,6 +2771,7 @@ import org.checkerframework.checker.nullness.qual.NonNull;
if (!isConnected()) { if (!isConnected()) {
return; return;
} }
sessionExtras = extras;
getInstance() getInstance()
.notifyControllerListener(listener -> listener.onExtrasChanged(getInstance(), extras)); .notifyControllerListener(listener -> listener.onExtrasChanged(getInstance(), extras));
} }

View File

@ -191,7 +191,8 @@ import org.checkerframework.checker.initialization.qual.UnderInitialization;
maskedPlayerInfo, maskedPlayerInfo,
controllerInfo.availableSessionCommands, controllerInfo.availableSessionCommands,
controllerInfo.availablePlayerCommands, controllerInfo.availablePlayerCommands,
controllerInfo.customLayout); controllerInfo.customLayout,
controllerInfo.sessionExtras);
updateStateMaskedControllerInfo( updateStateMaskedControllerInfo(
maskedControllerInfo, maskedControllerInfo,
/* discontinuityReason= */ null, /* discontinuityReason= */ null,
@ -255,7 +256,8 @@ import org.checkerframework.checker.initialization.qual.UnderInitialization;
/* playerError= */ null), /* playerError= */ null),
controllerInfo.availableSessionCommands, controllerInfo.availableSessionCommands,
controllerInfo.availablePlayerCommands, controllerInfo.availablePlayerCommands,
controllerInfo.customLayout); controllerInfo.customLayout,
controllerInfo.sessionExtras);
updateStateMaskedControllerInfo( updateStateMaskedControllerInfo(
maskedControllerInfo, maskedControllerInfo,
/* discontinuityReason= */ null, /* discontinuityReason= */ null,
@ -376,7 +378,8 @@ import org.checkerframework.checker.initialization.qual.UnderInitialization;
maskedPlayerInfo, maskedPlayerInfo,
controllerInfo.availableSessionCommands, controllerInfo.availableSessionCommands,
controllerInfo.availablePlayerCommands, controllerInfo.availablePlayerCommands,
controllerInfo.customLayout); controllerInfo.customLayout,
controllerInfo.sessionExtras);
updateStateMaskedControllerInfo( updateStateMaskedControllerInfo(
maskedControllerInfo, discontinuityReason, mediaItemTransitionReason); maskedControllerInfo, discontinuityReason, mediaItemTransitionReason);
} }
@ -414,6 +417,11 @@ import org.checkerframework.checker.initialization.qual.UnderInitialization;
return controllerInfo.customLayout; return controllerInfo.customLayout;
} }
@Override
public Bundle getSessionExtras() {
return controllerInfo.sessionExtras;
}
@Override @Override
@Nullable @Nullable
public PlaybackException getPlayerError() { public PlaybackException getPlayerError() {
@ -529,7 +537,8 @@ import org.checkerframework.checker.initialization.qual.UnderInitialization;
controllerInfo.playerInfo.copyWithPlaybackParameters(playbackParameters), controllerInfo.playerInfo.copyWithPlaybackParameters(playbackParameters),
controllerInfo.availableSessionCommands, controllerInfo.availableSessionCommands,
controllerInfo.availablePlayerCommands, controllerInfo.availablePlayerCommands,
controllerInfo.customLayout); controllerInfo.customLayout,
controllerInfo.sessionExtras);
updateStateMaskedControllerInfo( updateStateMaskedControllerInfo(
maskedControllerInfo, maskedControllerInfo,
/* discontinuityReason= */ null, /* discontinuityReason= */ null,
@ -548,7 +557,8 @@ import org.checkerframework.checker.initialization.qual.UnderInitialization;
controllerInfo.playerInfo.copyWithPlaybackParameters(new PlaybackParameters(speed)), controllerInfo.playerInfo.copyWithPlaybackParameters(new PlaybackParameters(speed)),
controllerInfo.availableSessionCommands, controllerInfo.availableSessionCommands,
controllerInfo.availablePlayerCommands, controllerInfo.availablePlayerCommands,
controllerInfo.customLayout); controllerInfo.customLayout,
controllerInfo.sessionExtras);
updateStateMaskedControllerInfo( updateStateMaskedControllerInfo(
maskedControllerInfo, maskedControllerInfo,
/* discontinuityReason= */ null, /* discontinuityReason= */ null,
@ -640,7 +650,8 @@ import org.checkerframework.checker.initialization.qual.UnderInitialization;
maskedPlayerInfo, maskedPlayerInfo,
controllerInfo.availableSessionCommands, controllerInfo.availableSessionCommands,
controllerInfo.availablePlayerCommands, controllerInfo.availablePlayerCommands,
controllerInfo.customLayout); controllerInfo.customLayout,
controllerInfo.sessionExtras);
updateStateMaskedControllerInfo( updateStateMaskedControllerInfo(
maskedControllerInfo, maskedControllerInfo,
/* discontinuityReason= */ null, /* discontinuityReason= */ null,
@ -703,7 +714,8 @@ import org.checkerframework.checker.initialization.qual.UnderInitialization;
maskedPlayerInfo, maskedPlayerInfo,
controllerInfo.availableSessionCommands, controllerInfo.availableSessionCommands,
controllerInfo.availablePlayerCommands, controllerInfo.availablePlayerCommands,
controllerInfo.customLayout); controllerInfo.customLayout,
controllerInfo.sessionExtras);
updateStateMaskedControllerInfo( updateStateMaskedControllerInfo(
maskedControllerInfo, maskedControllerInfo,
/* discontinuityReason= */ null, /* discontinuityReason= */ null,
@ -755,7 +767,8 @@ import org.checkerframework.checker.initialization.qual.UnderInitialization;
maskedPlayerInfo, maskedPlayerInfo,
controllerInfo.availableSessionCommands, controllerInfo.availableSessionCommands,
controllerInfo.availablePlayerCommands, controllerInfo.availablePlayerCommands,
controllerInfo.customLayout); controllerInfo.customLayout,
controllerInfo.sessionExtras);
updateStateMaskedControllerInfo( updateStateMaskedControllerInfo(
maskedControllerInfo, maskedControllerInfo,
/* discontinuityReason= */ null, /* discontinuityReason= */ null,
@ -821,7 +834,8 @@ import org.checkerframework.checker.initialization.qual.UnderInitialization;
maskedPlayerInfo, maskedPlayerInfo,
controllerInfo.availableSessionCommands, controllerInfo.availableSessionCommands,
controllerInfo.availablePlayerCommands, controllerInfo.availablePlayerCommands,
controllerInfo.customLayout); controllerInfo.customLayout,
controllerInfo.sessionExtras);
updateStateMaskedControllerInfo( updateStateMaskedControllerInfo(
maskedControllerInfo, maskedControllerInfo,
/* discontinuityReason= */ null, /* discontinuityReason= */ null,
@ -933,7 +947,8 @@ import org.checkerframework.checker.initialization.qual.UnderInitialization;
controllerInfo.playerInfo.copyWithRepeatMode(repeatMode), controllerInfo.playerInfo.copyWithRepeatMode(repeatMode),
controllerInfo.availableSessionCommands, controllerInfo.availableSessionCommands,
controllerInfo.availablePlayerCommands, controllerInfo.availablePlayerCommands,
controllerInfo.customLayout); controllerInfo.customLayout,
controllerInfo.sessionExtras);
updateStateMaskedControllerInfo( updateStateMaskedControllerInfo(
maskedControllerInfo, maskedControllerInfo,
/* discontinuityReason= */ null, /* discontinuityReason= */ null,
@ -959,7 +974,8 @@ import org.checkerframework.checker.initialization.qual.UnderInitialization;
controllerInfo.playerInfo.copyWithShuffleModeEnabled(shuffleModeEnabled), controllerInfo.playerInfo.copyWithShuffleModeEnabled(shuffleModeEnabled),
controllerInfo.availableSessionCommands, controllerInfo.availableSessionCommands,
controllerInfo.availablePlayerCommands, controllerInfo.availablePlayerCommands,
controllerInfo.customLayout); controllerInfo.customLayout,
controllerInfo.sessionExtras);
updateStateMaskedControllerInfo( updateStateMaskedControllerInfo(
maskedControllerInfo, maskedControllerInfo,
/* discontinuityReason= */ null, /* discontinuityReason= */ null,
@ -1081,7 +1097,8 @@ import org.checkerframework.checker.initialization.qual.UnderInitialization;
controllerInfo.playerInfo.copyWithDeviceVolume(volume, isDeviceMuted), controllerInfo.playerInfo.copyWithDeviceVolume(volume, isDeviceMuted),
controllerInfo.availableSessionCommands, controllerInfo.availableSessionCommands,
controllerInfo.availablePlayerCommands, controllerInfo.availablePlayerCommands,
controllerInfo.customLayout); controllerInfo.customLayout,
controllerInfo.sessionExtras);
updateStateMaskedControllerInfo( updateStateMaskedControllerInfo(
maskedControllerInfo, maskedControllerInfo,
/* discontinuityReason= */ null, /* discontinuityReason= */ null,
@ -1112,7 +1129,8 @@ import org.checkerframework.checker.initialization.qual.UnderInitialization;
controllerInfo.playerInfo.copyWithDeviceVolume(volume + 1, isDeviceMuted), controllerInfo.playerInfo.copyWithDeviceVolume(volume + 1, isDeviceMuted),
controllerInfo.availableSessionCommands, controllerInfo.availableSessionCommands,
controllerInfo.availablePlayerCommands, controllerInfo.availablePlayerCommands,
controllerInfo.customLayout); controllerInfo.customLayout,
controllerInfo.sessionExtras);
updateStateMaskedControllerInfo( updateStateMaskedControllerInfo(
maskedControllerInfo, maskedControllerInfo,
/* discontinuityReason= */ null, /* discontinuityReason= */ null,
@ -1142,7 +1160,8 @@ import org.checkerframework.checker.initialization.qual.UnderInitialization;
controllerInfo.playerInfo.copyWithDeviceVolume(volume - 1, isDeviceMuted), controllerInfo.playerInfo.copyWithDeviceVolume(volume - 1, isDeviceMuted),
controllerInfo.availableSessionCommands, controllerInfo.availableSessionCommands,
controllerInfo.availablePlayerCommands, controllerInfo.availablePlayerCommands,
controllerInfo.customLayout); controllerInfo.customLayout,
controllerInfo.sessionExtras);
updateStateMaskedControllerInfo( updateStateMaskedControllerInfo(
maskedControllerInfo, maskedControllerInfo,
/* discontinuityReason= */ null, /* discontinuityReason= */ null,
@ -1175,7 +1194,8 @@ import org.checkerframework.checker.initialization.qual.UnderInitialization;
controllerInfo.playerInfo.copyWithDeviceVolume(volume, muted), controllerInfo.playerInfo.copyWithDeviceVolume(volume, muted),
controllerInfo.availableSessionCommands, controllerInfo.availableSessionCommands,
controllerInfo.availablePlayerCommands, controllerInfo.availablePlayerCommands,
controllerInfo.customLayout); controllerInfo.customLayout,
controllerInfo.sessionExtras);
updateStateMaskedControllerInfo( updateStateMaskedControllerInfo(
maskedControllerInfo, maskedControllerInfo,
/* discontinuityReason= */ null, /* discontinuityReason= */ null,
@ -1212,7 +1232,8 @@ import org.checkerframework.checker.initialization.qual.UnderInitialization;
Player.PLAYBACK_SUPPRESSION_REASON_NONE), Player.PLAYBACK_SUPPRESSION_REASON_NONE),
controllerInfo.availableSessionCommands, controllerInfo.availableSessionCommands,
controllerInfo.availablePlayerCommands, controllerInfo.availablePlayerCommands,
controllerInfo.customLayout); controllerInfo.customLayout,
controllerInfo.sessionExtras);
updateStateMaskedControllerInfo( updateStateMaskedControllerInfo(
maskedControllerInfo, maskedControllerInfo,
/* discontinuityReason= */ null, /* discontinuityReason= */ null,
@ -1308,7 +1329,8 @@ import org.checkerframework.checker.initialization.qual.UnderInitialization;
convertToNonNullQueueItemList(controllerCompat.getQueue()), convertToNonNullQueueItemList(controllerCompat.getQueue()),
controllerCompat.getQueueTitle(), controllerCompat.getQueueTitle(),
controllerCompat.getRepeatMode(), controllerCompat.getRepeatMode(),
controllerCompat.getShuffleMode()); controllerCompat.getShuffleMode(),
controllerCompat.getExtras());
handleNewLegacyParameters(/* notifyConnected= */ true, newLegacyPlayerInfo); handleNewLegacyParameters(/* notifyConnected= */ true, newLegacyPlayerInfo);
} }
@ -1823,6 +1845,13 @@ import org.checkerframework.checker.initialization.qual.UnderInitialization;
@Override @Override
public void onExtrasChanged(Bundle extras) { public void onExtrasChanged(Bundle extras) {
controllerInfo =
new ControllerInfo(
controllerInfo.playerInfo,
controllerInfo.availableSessionCommands,
controllerInfo.availablePlayerCommands,
controllerInfo.customLayout,
extras);
getInstance() getInstance()
.notifyControllerListener(listener -> listener.onExtrasChanged(getInstance(), extras)); .notifyControllerListener(listener -> listener.onExtrasChanged(getInstance(), extras));
} }
@ -2068,6 +2097,7 @@ import org.checkerframework.checker.initialization.qual.UnderInitialization;
availableSessionCommands, availableSessionCommands,
availablePlayerCommands, availablePlayerCommands,
customLayout, customLayout,
newLegacyPlayerInfo.sessionExtras,
playerError, playerError,
durationMs, durationMs,
currentPositionMs, currentPositionMs,
@ -2235,6 +2265,7 @@ import org.checkerframework.checker.initialization.qual.UnderInitialization;
SessionCommands availableSessionCommands, SessionCommands availableSessionCommands,
Commands availablePlayerCommands, Commands availablePlayerCommands,
ImmutableList<CommandButton> customLayout, ImmutableList<CommandButton> customLayout,
Bundle sessionExtras,
@Nullable PlaybackException playerError, @Nullable PlaybackException playerError,
long durationMs, long durationMs,
long currentPositionMs, long currentPositionMs,
@ -2305,7 +2336,7 @@ import org.checkerframework.checker.initialization.qual.UnderInitialization;
/* parameters= */ TrackSelectionParameters.DEFAULT_WITHOUT_CONTEXT); /* parameters= */ TrackSelectionParameters.DEFAULT_WITHOUT_CONTEXT);
return new ControllerInfo( return new ControllerInfo(
playerInfo, availableSessionCommands, availablePlayerCommands, customLayout); playerInfo, availableSessionCommands, availablePlayerCommands, customLayout, sessionExtras);
} }
private static PositionInfo createPositionInfo( private static PositionInfo createPositionInfo(
@ -2355,6 +2386,7 @@ import org.checkerframework.checker.initialization.qual.UnderInitialization;
@Nullable public final CharSequence queueTitle; @Nullable public final CharSequence queueTitle;
@PlaybackStateCompat.RepeatMode public final int repeatMode; @PlaybackStateCompat.RepeatMode public final int repeatMode;
@PlaybackStateCompat.ShuffleMode public final int shuffleMode; @PlaybackStateCompat.ShuffleMode public final int shuffleMode;
public final Bundle sessionExtras;
public LegacyPlayerInfo() { public LegacyPlayerInfo() {
playbackInfoCompat = null; playbackInfoCompat = null;
@ -2364,6 +2396,7 @@ import org.checkerframework.checker.initialization.qual.UnderInitialization;
queueTitle = null; queueTitle = null;
repeatMode = PlaybackStateCompat.REPEAT_MODE_NONE; repeatMode = PlaybackStateCompat.REPEAT_MODE_NONE;
shuffleMode = PlaybackStateCompat.SHUFFLE_MODE_NONE; shuffleMode = PlaybackStateCompat.SHUFFLE_MODE_NONE;
sessionExtras = Bundle.EMPTY;
} }
public LegacyPlayerInfo( public LegacyPlayerInfo(
@ -2373,7 +2406,8 @@ import org.checkerframework.checker.initialization.qual.UnderInitialization;
List<QueueItem> queue, List<QueueItem> queue,
@Nullable CharSequence queueTitle, @Nullable CharSequence queueTitle,
@PlaybackStateCompat.RepeatMode int repeatMode, @PlaybackStateCompat.RepeatMode int repeatMode,
@PlaybackStateCompat.ShuffleMode int shuffleMode) { @PlaybackStateCompat.ShuffleMode int shuffleMode,
Bundle sessionExtras) {
this.playbackInfoCompat = playbackInfoCompat; this.playbackInfoCompat = playbackInfoCompat;
this.playbackStateCompat = playbackStateCompat; this.playbackStateCompat = playbackStateCompat;
this.mediaMetadataCompat = mediaMetadataCompat; this.mediaMetadataCompat = mediaMetadataCompat;
@ -2381,6 +2415,7 @@ import org.checkerframework.checker.initialization.qual.UnderInitialization;
this.queueTitle = queueTitle; this.queueTitle = queueTitle;
this.repeatMode = repeatMode; this.repeatMode = repeatMode;
this.shuffleMode = shuffleMode; this.shuffleMode = shuffleMode;
this.sessionExtras = sessionExtras;
} }
public LegacyPlayerInfo(LegacyPlayerInfo other) { public LegacyPlayerInfo(LegacyPlayerInfo other) {
@ -2391,6 +2426,7 @@ import org.checkerframework.checker.initialization.qual.UnderInitialization;
queueTitle = other.queueTitle; queueTitle = other.queueTitle;
repeatMode = other.repeatMode; repeatMode = other.repeatMode;
shuffleMode = other.shuffleMode; shuffleMode = other.shuffleMode;
sessionExtras = other.sessionExtras;
} }
@CheckResult @CheckResult
@ -2405,7 +2441,8 @@ import org.checkerframework.checker.initialization.qual.UnderInitialization;
queue, queue,
queueTitle, queueTitle,
repeatMode, repeatMode,
shuffleMode); shuffleMode,
sessionExtras);
} }
@CheckResult @CheckResult
@ -2418,7 +2455,8 @@ import org.checkerframework.checker.initialization.qual.UnderInitialization;
queue, queue,
queueTitle, queueTitle,
repeatMode, repeatMode,
shuffleMode); shuffleMode,
sessionExtras);
} }
@CheckResult @CheckResult
@ -2431,7 +2469,8 @@ import org.checkerframework.checker.initialization.qual.UnderInitialization;
queue, queue,
queueTitle, queueTitle,
repeatMode, repeatMode,
shuffleMode); shuffleMode,
sessionExtras);
} }
@CheckResult @CheckResult
@ -2443,7 +2482,8 @@ import org.checkerframework.checker.initialization.qual.UnderInitialization;
queue, queue,
queueTitle, queueTitle,
repeatMode, repeatMode,
shuffleMode); shuffleMode,
sessionExtras);
} }
@CheckResult @CheckResult
@ -2455,7 +2495,8 @@ import org.checkerframework.checker.initialization.qual.UnderInitialization;
queue, queue,
queueTitle, queueTitle,
repeatMode, repeatMode,
shuffleMode); shuffleMode,
sessionExtras);
} }
@CheckResult @CheckResult
@ -2468,7 +2509,8 @@ import org.checkerframework.checker.initialization.qual.UnderInitialization;
queue, queue,
queueTitle, queueTitle,
repeatMode, repeatMode,
shuffleMode); shuffleMode,
sessionExtras);
} }
@CheckResult @CheckResult
@ -2480,7 +2522,8 @@ import org.checkerframework.checker.initialization.qual.UnderInitialization;
queue, queue,
queueTitle, queueTitle,
repeatMode, repeatMode,
shuffleMode); shuffleMode,
sessionExtras);
} }
@CheckResult @CheckResult
@ -2492,7 +2535,8 @@ import org.checkerframework.checker.initialization.qual.UnderInitialization;
queue, queue,
queueTitle, queueTitle,
repeatMode, repeatMode,
shuffleMode); shuffleMode,
sessionExtras);
} }
} }
@ -2502,23 +2546,27 @@ import org.checkerframework.checker.initialization.qual.UnderInitialization;
public final SessionCommands availableSessionCommands; public final SessionCommands availableSessionCommands;
public final Commands availablePlayerCommands; public final Commands availablePlayerCommands;
public final ImmutableList<CommandButton> customLayout; public final ImmutableList<CommandButton> customLayout;
public final Bundle sessionExtras;
public ControllerInfo() { public ControllerInfo() {
playerInfo = PlayerInfo.DEFAULT.copyWithTimeline(QueueTimeline.DEFAULT); playerInfo = PlayerInfo.DEFAULT.copyWithTimeline(QueueTimeline.DEFAULT);
availableSessionCommands = SessionCommands.EMPTY; availableSessionCommands = SessionCommands.EMPTY;
availablePlayerCommands = Commands.EMPTY; availablePlayerCommands = Commands.EMPTY;
customLayout = ImmutableList.of(); customLayout = ImmutableList.of();
sessionExtras = Bundle.EMPTY;
} }
public ControllerInfo( public ControllerInfo(
PlayerInfo playerInfo, PlayerInfo playerInfo,
SessionCommands availableSessionCommands, SessionCommands availableSessionCommands,
Commands availablePlayerCommands, Commands availablePlayerCommands,
ImmutableList<CommandButton> customLayout) { ImmutableList<CommandButton> customLayout,
Bundle sessionExtras) {
this.playerInfo = playerInfo; this.playerInfo = playerInfo;
this.availableSessionCommands = availableSessionCommands; this.availableSessionCommands = availableSessionCommands;
this.availablePlayerCommands = availablePlayerCommands; this.availablePlayerCommands = availablePlayerCommands;
this.customLayout = customLayout; this.customLayout = customLayout;
this.sessionExtras = sessionExtras;
} }
} }
} }

View File

@ -440,16 +440,34 @@ public abstract class MediaLibraryService extends MediaSessionService {
} }
/** /**
* Sets an extra {@link Bundle} for the {@link MediaLibrarySession}. The {@link * Sets an extras {@link Bundle} for the {@linkplain MediaLibrarySession#getToken() session
* MediaLibrarySession#getToken()} session token} will have the {@link * token}. If not set, {@link Bundle#EMPTY} is used.
* SessionToken#getExtras() extras}. If not set, an empty {@link Bundle} will be used.
* *
* @param extras The extra {@link Bundle}. * <p>A controller has access to these extras through the {@linkplain
* MediaController#getConnectedToken() connected token}.
*
* @param tokenExtras The extras {@link Bundle}.
* @return The builder to allow chaining. * @return The builder to allow chaining.
*/ */
@Override @Override
public Builder setExtras(Bundle extras) { public Builder setExtras(Bundle tokenExtras) {
return super.setExtras(extras); return super.setExtras(tokenExtras);
}
/**
* Sets the {@linkplain MediaLibrarySession#getSessionExtras() session extras}. If not set,
* {@link Bundle#EMPTY} is used.
*
* <p>A controller has access to session extras through {@link
* MediaController#getSessionExtras()}.
*
* @param sessionExtras The session extras {@link Bundle}.
* @return The builder to allow chaining.
*/
@UnstableApi
@Override
public Builder setSessionExtras(Bundle sessionExtras) {
return super.setSessionExtras(sessionExtras);
} }
/** /**
@ -551,7 +569,8 @@ public abstract class MediaLibraryService extends MediaSessionService {
sessionActivity, sessionActivity,
customLayout, customLayout,
callback, callback,
extras, tokenExtras,
sessionExtras,
checkNotNull(bitmapLoader), checkNotNull(bitmapLoader),
playIfSuppressed, playIfSuppressed,
isPeriodicPositionUpdateEnabled); isPeriodicPositionUpdateEnabled);
@ -566,6 +585,7 @@ public abstract class MediaLibraryService extends MediaSessionService {
ImmutableList<CommandButton> customLayout, ImmutableList<CommandButton> customLayout,
MediaSession.Callback callback, MediaSession.Callback callback,
Bundle tokenExtras, Bundle tokenExtras,
Bundle sessionExtras,
BitmapLoader bitmapLoader, BitmapLoader bitmapLoader,
boolean playIfSuppressed, boolean playIfSuppressed,
boolean isPeriodicPositionUpdateEnabled) { boolean isPeriodicPositionUpdateEnabled) {
@ -577,6 +597,7 @@ public abstract class MediaLibraryService extends MediaSessionService {
customLayout, customLayout,
callback, callback,
tokenExtras, tokenExtras,
sessionExtras,
bitmapLoader, bitmapLoader,
playIfSuppressed, playIfSuppressed,
isPeriodicPositionUpdateEnabled); isPeriodicPositionUpdateEnabled);
@ -591,6 +612,7 @@ public abstract class MediaLibraryService extends MediaSessionService {
ImmutableList<CommandButton> customLayout, ImmutableList<CommandButton> customLayout,
MediaSession.Callback callback, MediaSession.Callback callback,
Bundle tokenExtras, Bundle tokenExtras,
Bundle sessionExtras,
BitmapLoader bitmapLoader, BitmapLoader bitmapLoader,
boolean playIfSuppressed, boolean playIfSuppressed,
boolean isPeriodicPositionUpdateEnabled) { boolean isPeriodicPositionUpdateEnabled) {
@ -603,6 +625,7 @@ public abstract class MediaLibraryService extends MediaSessionService {
customLayout, customLayout,
(Callback) callback, (Callback) callback,
tokenExtras, tokenExtras,
sessionExtras,
bitmapLoader, bitmapLoader,
playIfSuppressed, playIfSuppressed,
isPeriodicPositionUpdateEnabled); isPeriodicPositionUpdateEnabled);

View File

@ -74,6 +74,7 @@ import java.util.concurrent.Future;
ImmutableList<CommandButton> customLayout, ImmutableList<CommandButton> customLayout,
MediaLibrarySession.Callback callback, MediaLibrarySession.Callback callback,
Bundle tokenExtras, Bundle tokenExtras,
Bundle sessionExtras,
BitmapLoader bitmapLoader, BitmapLoader bitmapLoader,
boolean playIfSuppressed, boolean playIfSuppressed,
boolean isPeriodicPositionUpdateEnabled) { boolean isPeriodicPositionUpdateEnabled) {
@ -86,6 +87,7 @@ import java.util.concurrent.Future;
customLayout, customLayout,
callback, callback,
tokenExtras, tokenExtras,
sessionExtras,
bitmapLoader, bitmapLoader,
playIfSuppressed, playIfSuppressed,
isPeriodicPositionUpdateEnabled); isPeriodicPositionUpdateEnabled);

View File

@ -318,16 +318,34 @@ public class MediaSession {
} }
/** /**
* Sets an extra {@link Bundle} for the {@link MediaSession}. The {@link * Sets an extras {@link Bundle} for the {@linkplain MediaSession#getToken() session token}. If
* MediaSession#getToken()} session token} will have the {@link SessionToken#getExtras() * not set, {@link Bundle#EMPTY} is used.
* extras}. If not set, an empty {@link Bundle} will be used.
* *
* @param extras The extra {@link Bundle}. * <p>A controller has access to these extras through the {@linkplain
* MediaController#getConnectedToken() connected token}.
*
* @param tokenExtras The extras {@link Bundle}.
* @return The builder to allow chaining. * @return The builder to allow chaining.
*/ */
@Override @Override
public Builder setExtras(Bundle extras) { public Builder setExtras(Bundle tokenExtras) {
return super.setExtras(extras); return super.setExtras(tokenExtras);
}
/**
* Sets the {@linkplain MediaSession#getSessionExtras() session extras}. If not set, {@link
* Bundle#EMPTY} is used.
*
* <p>A controller has access to session extras through {@linkplain
* MediaController#getSessionExtras()}.
*
* @param sessionExtras The session extras {@link Bundle}.
* @return The builder to allow chaining.
*/
@UnstableApi
@Override
public Builder setSessionExtras(Bundle sessionExtras) {
return super.setSessionExtras(sessionExtras);
} }
/** /**
@ -426,7 +444,8 @@ public class MediaSession {
sessionActivity, sessionActivity,
customLayout, customLayout,
callback, callback,
extras, tokenExtras,
sessionExtras,
checkNotNull(bitmapLoader), checkNotNull(bitmapLoader),
playIfSuppressed, playIfSuppressed,
isPeriodicPositionUpdateEnabled); isPeriodicPositionUpdateEnabled);
@ -621,6 +640,7 @@ public class MediaSession {
ImmutableList<CommandButton> customLayout, ImmutableList<CommandButton> customLayout,
Callback callback, Callback callback,
Bundle tokenExtras, Bundle tokenExtras,
Bundle sessionExtras,
BitmapLoader bitmapLoader, BitmapLoader bitmapLoader,
boolean playIfSuppressed, boolean playIfSuppressed,
boolean isPeriodicPositionUpdateEnabled) { boolean isPeriodicPositionUpdateEnabled) {
@ -639,6 +659,7 @@ public class MediaSession {
customLayout, customLayout,
callback, callback,
tokenExtras, tokenExtras,
sessionExtras,
bitmapLoader, bitmapLoader,
playIfSuppressed, playIfSuppressed,
isPeriodicPositionUpdateEnabled); isPeriodicPositionUpdateEnabled);
@ -652,6 +673,7 @@ public class MediaSession {
ImmutableList<CommandButton> customLayout, ImmutableList<CommandButton> customLayout,
Callback callback, Callback callback,
Bundle tokenExtras, Bundle tokenExtras,
Bundle sessionExtras,
BitmapLoader bitmapLoader, BitmapLoader bitmapLoader,
boolean playIfSuppressed, boolean playIfSuppressed,
boolean isPeriodicPositionUpdateEnabled) { boolean isPeriodicPositionUpdateEnabled) {
@ -664,6 +686,7 @@ public class MediaSession {
customLayout, customLayout,
callback, callback,
tokenExtras, tokenExtras,
sessionExtras,
bitmapLoader, bitmapLoader,
playIfSuppressed, playIfSuppressed,
isPeriodicPositionUpdateEnabled); isPeriodicPositionUpdateEnabled);
@ -982,7 +1005,23 @@ public class MediaSession {
} }
/** /**
* Sends the session extras to connected controllers. * Returns the session extras.
*
* <p>For informational purpose only. Mutations on the {@link Bundle} do not have immediate
* effect. To change the session extras use {@link #setSessionExtras(Bundle)} or {@link
* #setSessionExtras(ControllerInfo, Bundle)}.
*/
@UnstableApi
public Bundle getSessionExtras() {
return impl.getSessionExtras();
}
/**
* Sets the {@linkplain #getSessionExtras() session extras} and sends them to connected
* controllers.
*
* <p>The initial extras can be set {@linkplain Builder#setSessionExtras(Bundle) when building the
* session}.
* *
* <p>This is a synchronous call and doesn't wait for results from the controllers. * <p>This is a synchronous call and doesn't wait for results from the controllers.
* *
@ -996,6 +1035,11 @@ public class MediaSession {
/** /**
* Sends the session extras to the connected controller. * Sends the session extras to the connected controller.
* *
* <p>The initial extras for a specific controller can be set in {@link
* Callback#onConnect(MediaSession, ControllerInfo)} when {@link
* ConnectionResult.AcceptedResultBuilder#setSessionExtras(Bundle) building the connection
* result}.
*
* <p>This is a synchronous call and doesn't wait for results from the controller. * <p>This is a synchronous call and doesn't wait for results from the controller.
* *
* <p>Interoperability: This call has no effect when called for a {@linkplain * <p>Interoperability: This call has no effect when called for a {@linkplain
@ -1549,6 +1593,7 @@ public class MediaSession {
private SessionCommands availableSessionCommands; private SessionCommands availableSessionCommands;
private Player.Commands availablePlayerCommands = DEFAULT_PLAYER_COMMANDS; private Player.Commands availablePlayerCommands = DEFAULT_PLAYER_COMMANDS;
@Nullable private ImmutableList<CommandButton> customLayout; @Nullable private ImmutableList<CommandButton> customLayout;
@Nullable private Bundle sessionExtras;
/** /**
* Creates an instance. * Creates an instance.
@ -1611,10 +1656,26 @@ public class MediaSession {
return this; return this;
} }
/**
* Sets the session extras, overriding the {@linkplain MediaSession#getSessionExtras() extras
* of the session}.
*
* <p>The default is null to indicate that the extras of the session should be used.
*/
@CanIgnoreReturnValue
public AcceptedResultBuilder setSessionExtras(Bundle sessionExtras) {
this.sessionExtras = sessionExtras;
return this;
}
/** Returns a new {@link ConnectionResult} instance for accepting a connection. */ /** Returns a new {@link ConnectionResult} instance for accepting a connection. */
public ConnectionResult build() { public ConnectionResult build() {
return new ConnectionResult( return new ConnectionResult(
/* accepted= */ true, availableSessionCommands, availablePlayerCommands, customLayout); /* accepted= */ true,
availableSessionCommands,
availablePlayerCommands,
customLayout,
sessionExtras);
} }
} }
@ -1642,16 +1703,21 @@ public class MediaSession {
/** The custom layout or null if the custom layout of the session should be used. */ /** The custom layout or null if the custom layout of the session should be used. */
@UnstableApi @Nullable public final ImmutableList<CommandButton> customLayout; @UnstableApi @Nullable public final ImmutableList<CommandButton> customLayout;
/** The session extras. */
@UnstableApi @Nullable public final Bundle sessionExtras;
/** Creates a new instance with the given available session and player commands. */ /** Creates a new instance with the given available session and player commands. */
private ConnectionResult( private ConnectionResult(
boolean accepted, boolean accepted,
SessionCommands availableSessionCommands, SessionCommands availableSessionCommands,
Player.Commands availablePlayerCommands, Player.Commands availablePlayerCommands,
@Nullable ImmutableList<CommandButton> customLayout) { @Nullable ImmutableList<CommandButton> customLayout,
@Nullable Bundle sessionExtras) {
isAccepted = accepted; isAccepted = accepted;
this.availableSessionCommands = availableSessionCommands; this.availableSessionCommands = availableSessionCommands;
this.availablePlayerCommands = availablePlayerCommands; this.availablePlayerCommands = availablePlayerCommands;
this.customLayout = customLayout; this.customLayout = customLayout;
this.sessionExtras = sessionExtras;
} }
/** /**
@ -1670,13 +1736,18 @@ public class MediaSession {
/* accepted= */ true, /* accepted= */ true,
availableSessionCommands, availableSessionCommands,
availablePlayerCommands, availablePlayerCommands,
/* customLayout= */ null); /* customLayout= */ null,
/* sessionExtras= */ null);
} }
/** Creates a {@link ConnectionResult} to reject a connection. */ /** Creates a {@link ConnectionResult} to reject a connection. */
public static ConnectionResult reject() { public static ConnectionResult reject() {
return new ConnectionResult( return new ConnectionResult(
/* accepted= */ false, SessionCommands.EMPTY, Player.Commands.EMPTY, ImmutableList.of()); /* accepted= */ false,
SessionCommands.EMPTY,
Player.Commands.EMPTY,
/* customLayout= */ ImmutableList.of(),
/* sessionExtras= */ Bundle.EMPTY);
} }
} }
@ -1850,7 +1921,8 @@ public class MediaSession {
/* package */ String id; /* package */ String id;
/* package */ CallbackT callback; /* package */ CallbackT callback;
/* package */ @Nullable PendingIntent sessionActivity; /* package */ @Nullable PendingIntent sessionActivity;
/* package */ Bundle extras; /* package */ Bundle tokenExtras;
/* package */ Bundle sessionExtras;
/* package */ @MonotonicNonNull BitmapLoader bitmapLoader; /* package */ @MonotonicNonNull BitmapLoader bitmapLoader;
/* package */ boolean playIfSuppressed; /* package */ boolean playIfSuppressed;
/* package */ ImmutableList<CommandButton> customLayout; /* package */ ImmutableList<CommandButton> customLayout;
@ -1862,7 +1934,8 @@ public class MediaSession {
checkArgument(player.canAdvertiseSession()); checkArgument(player.canAdvertiseSession());
id = ""; id = "";
this.callback = callback; this.callback = callback;
extras = Bundle.EMPTY; tokenExtras = Bundle.EMPTY;
sessionExtras = Bundle.EMPTY;
customLayout = ImmutableList.of(); customLayout = ImmutableList.of();
playIfSuppressed = true; playIfSuppressed = true;
isPeriodicPositionUpdateEnabled = true; isPeriodicPositionUpdateEnabled = true;
@ -1887,8 +1960,14 @@ public class MediaSession {
} }
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
public BuilderT setExtras(Bundle extras) { public BuilderT setExtras(Bundle tokenExtras) {
this.extras = new Bundle(checkNotNull(extras)); this.tokenExtras = new Bundle(checkNotNull(tokenExtras));
return (BuilderT) this;
}
@SuppressWarnings("unchecked")
public BuilderT setSessionExtras(Bundle sessionExtras) {
this.sessionExtras = new Bundle(checkNotNull(sessionExtras));
return (BuilderT) this; return (BuilderT) this;
} }

View File

@ -149,6 +149,7 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
private long sessionPositionUpdateDelayMs; private long sessionPositionUpdateDelayMs;
private boolean isMediaNotificationControllerConnected; private boolean isMediaNotificationControllerConnected;
private ImmutableList<CommandButton> customLayout; private ImmutableList<CommandButton> customLayout;
private Bundle sessionExtras;
public MediaSessionImpl( public MediaSessionImpl(
MediaSession instance, MediaSession instance,
@ -159,6 +160,7 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
ImmutableList<CommandButton> customLayout, ImmutableList<CommandButton> customLayout,
MediaSession.Callback callback, MediaSession.Callback callback,
Bundle tokenExtras, Bundle tokenExtras,
Bundle sessionExtras,
BitmapLoader bitmapLoader, BitmapLoader bitmapLoader,
boolean playIfSuppressed, boolean playIfSuppressed,
boolean isPeriodicPositionUpdateEnabled) { boolean isPeriodicPositionUpdateEnabled) {
@ -168,6 +170,7 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
this.sessionActivity = sessionActivity; this.sessionActivity = sessionActivity;
this.customLayout = customLayout; this.customLayout = customLayout;
this.callback = callback; this.callback = callback;
this.sessionExtras = sessionExtras;
this.bitmapLoader = bitmapLoader; this.bitmapLoader = bitmapLoader;
this.playIfSuppressed = playIfSuppressed; this.playIfSuppressed = playIfSuppressed;
this.isPeriodicPositionUpdateEnabled = isPeriodicPositionUpdateEnabled; this.isPeriodicPositionUpdateEnabled = isPeriodicPositionUpdateEnabled;
@ -481,6 +484,7 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
} }
public void setSessionExtras(Bundle sessionExtras) { public void setSessionExtras(Bundle sessionExtras) {
this.sessionExtras = sessionExtras;
dispatchRemoteControllerTaskWithoutReturn( dispatchRemoteControllerTaskWithoutReturn(
(controller, seq) -> controller.onSessionExtrasChanged(seq, sessionExtras)); (controller, seq) -> controller.onSessionExtrasChanged(seq, sessionExtras));
} }
@ -489,8 +493,16 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
if (sessionStub.getConnectedControllersManager().isConnected(controller)) { if (sessionStub.getConnectedControllersManager().isConnected(controller)) {
dispatchRemoteControllerTaskWithoutReturn( dispatchRemoteControllerTaskWithoutReturn(
controller, (callback, seq) -> callback.onSessionExtrasChanged(seq, sessionExtras)); controller, (callback, seq) -> callback.onSessionExtrasChanged(seq, sessionExtras));
if (isMediaNotificationController(controller)) {
dispatchRemoteControllerTaskToLegacyStub(
(callback, seq) -> callback.onSessionExtrasChanged(seq, sessionExtras));
} }
} }
}
public Bundle getSessionExtras() {
return sessionExtras;
}
public BitmapLoader getBitmapLoader() { public BitmapLoader getBitmapLoader() {
return bitmapLoader; return bitmapLoader;

View File

@ -528,6 +528,9 @@ import java.util.concurrent.ExecutionException;
connectionResult.availablePlayerCommands, connectionResult.availablePlayerCommands,
playerWrapper.getAvailableCommands(), playerWrapper.getAvailableCommands(),
sessionImpl.getToken().getExtras(), sessionImpl.getToken().getExtras(),
connectionResult.sessionExtras != null
? connectionResult.sessionExtras
: sessionImpl.getSessionExtras(),
playerInfo); playerInfo);
// Double check if session is still there, because release() can be called in // Double check if session is still there, because release() can be called in

View File

@ -29,6 +29,7 @@ interface IRemoteMediaController {
// MediaController Methods // MediaController Methods
Bundle getConnectedSessionToken(String controllerId); Bundle getConnectedSessionToken(String controllerId);
Bundle getSessionExtras(String controllerId);
void play(String controllerId); void play(String controllerId);
void pause(String controllerId); void pause(String controllerId);
void setPlayWhenReady(String controllerId, boolean playWhenReady); void setPlayWhenReady(String controllerId, boolean playWhenReady);

View File

@ -1895,7 +1895,7 @@ public class MediaControllerListenerTest {
.getMockPlayer() .getMockPlayer()
.setPlayWhenReady(/* playWhenReady= */ true, Player.PLAYBACK_SUPPRESSION_REASON_NONE); .setPlayWhenReady(/* playWhenReady= */ true, Player.PLAYBACK_SUPPRESSION_REASON_NONE);
MediaController controller = controllerTestRule.createController(remoteSession.getToken()); MediaController controller = controllerTestRule.createController(remoteSession.getToken());
threadTestRule.getHandler().postAndSync(() -> controller.setTimeDiffMs(/* timeDiff= */ 0L)); threadTestRule.getHandler().postAndSync(() -> controller.setTimeDiffMs(/* timeDiffMs= */ 0L));
CountDownLatch latch = new CountDownLatch(2); CountDownLatch latch = new CountDownLatch(2);
AtomicBoolean isPlayingRef = new AtomicBoolean(); AtomicBoolean isPlayingRef = new AtomicBoolean();
AtomicLong currentPositionMsRef = new AtomicLong(); AtomicLong currentPositionMsRef = new AtomicLong();
@ -2136,7 +2136,7 @@ public class MediaControllerListenerTest {
remoteMockPlayer.setCurrentAdGroupIndex(testCurrentAdGroupIndex); remoteMockPlayer.setCurrentAdGroupIndex(testCurrentAdGroupIndex);
remoteMockPlayer.setCurrentAdIndexInAdGroup(testCurrentAdIndexInAdGroup); remoteMockPlayer.setCurrentAdIndexInAdGroup(testCurrentAdIndexInAdGroup);
remoteMockPlayer.notifyPositionDiscontinuity( remoteMockPlayer.notifyPositionDiscontinuity(
/* oldPositionInfo= */ SessionPositionInfo.DEFAULT_POSITION_INFO, /* oldPosition= */ SessionPositionInfo.DEFAULT_POSITION_INFO,
newPositionInfo, newPositionInfo,
Player.DISCONTINUITY_REASON_INTERNAL); Player.DISCONTINUITY_REASON_INTERNAL);
@ -2470,11 +2470,13 @@ public class MediaControllerListenerTest {
sessionExtras.putString("key-0", "value-0"); sessionExtras.putString("key-0", "value-0");
CountDownLatch latch = new CountDownLatch(1); CountDownLatch latch = new CountDownLatch(1);
List<Bundle> receivedSessionExtras = new ArrayList<>(); List<Bundle> receivedSessionExtras = new ArrayList<>();
List<Bundle> getterSessionExtras = new ArrayList<>();
MediaController.Listener listener = MediaController.Listener listener =
new MediaController.Listener() { new MediaController.Listener() {
@Override @Override
public void onExtrasChanged(MediaController controller, Bundle extras) { public void onExtrasChanged(MediaController controller, Bundle extras) {
receivedSessionExtras.add(extras); receivedSessionExtras.add(extras);
getterSessionExtras.add(controller.getSessionExtras());
latch.countDown(); latch.countDown();
} }
}; };
@ -2487,6 +2489,8 @@ public class MediaControllerListenerTest {
assertThat(latch.await(TIMEOUT_MS, MILLISECONDS)).isTrue(); assertThat(latch.await(TIMEOUT_MS, MILLISECONDS)).isTrue();
assertThat(receivedSessionExtras).hasSize(1); assertThat(receivedSessionExtras).hasSize(1);
assertThat(TestUtils.equals(receivedSessionExtras.get(0), sessionExtras)).isTrue(); assertThat(TestUtils.equals(receivedSessionExtras.get(0), sessionExtras)).isTrue();
assertThat(getterSessionExtras).hasSize(1);
assertThat(TestUtils.equals(getterSessionExtras.get(0), sessionExtras)).isTrue();
} }
@Test @Test
@ -2495,11 +2499,13 @@ public class MediaControllerListenerTest {
sessionExtras.putString("key-0", "value-0"); sessionExtras.putString("key-0", "value-0");
CountDownLatch latch = new CountDownLatch(1); CountDownLatch latch = new CountDownLatch(1);
List<Bundle> receivedSessionExtras = new ArrayList<>(); List<Bundle> receivedSessionExtras = new ArrayList<>();
List<Bundle> getterSessionExtras = new ArrayList<>();
MediaController.Listener listener = MediaController.Listener listener =
new MediaController.Listener() { new MediaController.Listener() {
@Override @Override
public void onExtrasChanged(MediaController controller, Bundle extras) { public void onExtrasChanged(MediaController controller, Bundle extras) {
receivedSessionExtras.add(extras); receivedSessionExtras.add(extras);
getterSessionExtras.add(controller.getSessionExtras());
latch.countDown(); latch.countDown();
} }
}; };
@ -2513,6 +2519,7 @@ public class MediaControllerListenerTest {
assertThat(latch.await(TIMEOUT_MS, MILLISECONDS)).isTrue(); assertThat(latch.await(TIMEOUT_MS, MILLISECONDS)).isTrue();
assertThat(receivedSessionExtras).hasSize(1); assertThat(receivedSessionExtras).hasSize(1);
assertThat(TestUtils.equals(receivedSessionExtras.get(0), sessionExtras)).isTrue(); assertThat(TestUtils.equals(receivedSessionExtras.get(0), sessionExtras)).isTrue();
assertThat(TestUtils.equals(getterSessionExtras.get(0), sessionExtras)).isTrue();
} }
@Test @Test

View File

@ -188,12 +188,14 @@ public class MediaControllerListenerWithMediaSessionCompatTest {
sessionExtras.putString("key-1", "value-1"); sessionExtras.putString("key-1", "value-1");
CountDownLatch countDownLatch = new CountDownLatch(1); CountDownLatch countDownLatch = new CountDownLatch(1);
List<Bundle> receivedSessionExtras = new ArrayList<>(); List<Bundle> receivedSessionExtras = new ArrayList<>();
List<Bundle> getterSessionExtras = new ArrayList<>();
controllerTestRule.createController( controllerTestRule.createController(
session.getSessionToken(), session.getSessionToken(),
new MediaController.Listener() { new MediaController.Listener() {
@Override @Override
public void onExtrasChanged(MediaController controller, Bundle extras) { public void onExtrasChanged(MediaController controller, Bundle extras) {
receivedSessionExtras.add(extras); receivedSessionExtras.add(extras);
getterSessionExtras.add(controller.getSessionExtras());
countDownLatch.countDown(); countDownLatch.countDown();
} }
}); });
@ -202,6 +204,22 @@ public class MediaControllerListenerWithMediaSessionCompatTest {
assertThat(countDownLatch.await(1_000, MILLISECONDS)).isTrue(); assertThat(countDownLatch.await(1_000, MILLISECONDS)).isTrue();
assertThat(TestUtils.equals(receivedSessionExtras.get(0), sessionExtras)).isTrue(); assertThat(TestUtils.equals(receivedSessionExtras.get(0), sessionExtras)).isTrue();
assertThat(TestUtils.equals(getterSessionExtras.get(0), sessionExtras)).isTrue();
}
@Test
public void setSessionExtras_includedWhenConnecting() throws Exception {
Bundle sessionExtras = new Bundle();
sessionExtras.putString("key-1", "value-1");
session.setExtras(sessionExtras);
MediaController controller = controllerTestRule.createController(session.getSessionToken());
assertThat(
TestUtils.equals(
threadTestRule.getHandler().postAndSync(controller::getSessionExtras),
sessionExtras))
.isTrue();
} }
@Test @Test

View File

@ -422,6 +422,23 @@ public class MediaControllerTest {
session.cleanUp(); session.cleanUp();
} }
@Test
public void getSessionExtras_includedInConnectionStateWhenConnecting() throws Exception {
RemoteMediaSession session =
createRemoteMediaSession(TEST_GET_CUSTOM_LAYOUT, /* tokenExtras= */ null);
Bundle sessionExtras = new Bundle();
sessionExtras.putString("key1", "value1");
session.setSessionExtras(sessionExtras);
MediaController controller = controllerTestRule.createController(session.getToken());
assertThat(
threadTestRule.getHandler().postAndSync(controller::getSessionExtras).getString("key1"))
.isEqualTo("value1");
session.cleanUp();
}
@Test @Test
public void getAvailableCommands_emptyPlayerCommands_commandReleaseStillAvailable() public void getAvailableCommands_emptyPlayerCommands_commandReleaseStillAvailable()
throws Exception { throws Exception {
@ -772,7 +789,7 @@ public class MediaControllerTest {
session.setAvailableCommands(builder.build(), Player.Commands.EMPTY); session.setAvailableCommands(builder.build(), Player.Commands.EMPTY);
String testMediaId = "testMediaId"; String testMediaId = "testMediaId";
Rating testRating = new HeartRating(/* hasHeart= */ true); Rating testRating = new HeartRating(/* isHeart= */ true);
threadTestRule threadTestRule
.getHandler() .getHandler()
.postAndSync( .postAndSync(

View File

@ -234,6 +234,80 @@ public class MediaSessionCallbackTest {
assertThat(remoteController.getAvailableCommands().contains(Player.COMMAND_RELEASE)).isTrue(); assertThat(remoteController.getAvailableCommands().contains(Player.COMMAND_RELEASE)).isTrue();
} }
@Test
public void onConnect_connectionResultExtrasAreNull_usesSessionExtras() throws Exception {
MediaSession.Callback callback =
new MediaSession.Callback() {
@Override
public MediaSession.ConnectionResult onConnect(
MediaSession session, ControllerInfo controller) {
return new AcceptedResultBuilder(session)
.setAvailablePlayerCommands(Player.Commands.EMPTY)
.build();
}
};
Bundle sessionExtras = new Bundle();
sessionExtras.putString("origin", "session");
MediaSession session =
sessionTestRule.ensureReleaseAfterTest(
new MediaSession.Builder(context, player)
.setSessionExtras(sessionExtras)
.setCallback(callback)
.setId("onConnect_connectionResultExtrasAreNull_usesGlobalSessionExtras")
.build());
RemoteMediaController remoteController =
remoteControllerTestRule.createRemoteController(session.getToken());
assertThat(remoteController.getSessionExtras().getString("origin")).isEqualTo("session");
}
@Test
public void onConnect_connectionResultExtrasAreSet_usesControllerSpecificSessionExtras()
throws Exception {
MediaSession.Callback callback =
new MediaSession.Callback() {
@Override
public MediaSession.ConnectionResult onConnect(
MediaSession session, ControllerInfo controller) {
Bundle sessionExtras = new Bundle();
sessionExtras.putString("origin", "controller");
return new AcceptedResultBuilder(session)
.setSessionExtras(sessionExtras)
.setAvailablePlayerCommands(Player.Commands.EMPTY)
.build();
}
};
Bundle sessionExtras = new Bundle();
sessionExtras.putString("origin", "session");
MediaSession session =
sessionTestRule.ensureReleaseAfterTest(
new MediaSession.Builder(context, player)
.setSessionExtras(sessionExtras)
.setCallback(callback)
.setId("onConnect_connectionResultExtrasAreSet_usesControllerSpecificSessionExtras")
.build());
RemoteMediaController remoteController =
remoteControllerTestRule.createRemoteController(session.getToken());
assertThat(remoteController.getSessionExtras().getString("origin")).isEqualTo("controller");
}
@Test
public void onConnect_connectionResultDefault_emptySessionExtras() throws Exception {
MediaSession session =
sessionTestRule.ensureReleaseAfterTest(
new MediaSession.Builder(context, player)
.setId("onConnect_connectionResultDefault_emptySessionExtras")
.build());
RemoteMediaController remoteController =
remoteControllerTestRule.createRemoteController(session.getToken());
assertThat(remoteController.getSessionExtras().size()).isEqualTo(0);
}
@Test @Test
public void onPostConnect_afterConnected() throws Exception { public void onPostConnect_afterConnected() throws Exception {
CountDownLatch latch = new CountDownLatch(1); CountDownLatch latch = new CountDownLatch(1);

View File

@ -175,6 +175,11 @@ public class MediaControllerProviderService extends Service {
}); });
} }
@Override
public Bundle getSessionExtras(String controllerId) throws RemoteException {
return runOnHandler(mediaControllerMap.get(controllerId)::getSessionExtras);
}
@Override @Override
public void play(String controllerId) throws RemoteException { public void play(String controllerId) throws RemoteException {
runOnHandler( runOnHandler(

View File

@ -97,6 +97,10 @@ public class RemoteMediaController {
return sessionTokenBundle == null ? null : SessionToken.fromBundle(sessionTokenBundle); return sessionTokenBundle == null ? null : SessionToken.fromBundle(sessionTokenBundle);
} }
public Bundle getSessionExtras() throws RemoteException {
return binder.getSessionExtras(controllerId);
}
public void play() throws RemoteException { public void play() throws RemoteException {
binder.play(controllerId); binder.play(controllerId);
} }