Skip to content

Commit

Permalink
Only set the queue when COMMAND_GET_TIMELINE is available
Browse files Browse the repository at this point in the history
Android Auto shows a queue button when the queue is not empty.
Apps were able to remove this queue button with the legacy API
by not setting the queue of the session.

After this change, removing `COMMAND_GET_TIMELINE` from the commands
of the media notification controller or the session player sets the
queue in the platform session to null.

#minor-release
Issue: #339
PiperOrigin-RevId: 573813558
(cherry picked from commit f53e1bc)
  • Loading branch information
marcbaechinger authored and rohitjoins committed Oct 23, 2023
1 parent e46ee01 commit 386d223
Show file tree
Hide file tree
Showing 7 changed files with 147 additions and 59 deletions.
6 changes: 6 additions & 0 deletions RELEASENOTES.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,12 @@
* Muxers:
* IMA extension:
* Session:
* Do not set the queue of the framework session when
`COMMAND_GET_TIMELINE` is not available for the media notification
controller. With Android Auto as the client controller reading from the
framework session, this has the effect that the `queue` button in the UI
of Android Auto is not displayed
(([#339](https://github.com/androidx/media/issues/339)).
* UI:
* Downloads:
* OkHttp Extension:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -476,7 +476,7 @@ public ListenableFuture<SessionResult> setCustomLayout(
ControllerInfo controller, ImmutableList<CommandButton> customLayout) {
if (isMediaNotificationController(controller)) {
playerWrapper.setCustomLayout(customLayout);
sessionLegacyStub.updateLegacySessionPlaybackStateCompat();
sessionLegacyStub.updateLegacySessionPlaybackState(playerWrapper);
}
return dispatchRemoteControllerTask(
controller, (controller1, seq) -> controller1.setCustomLayout(seq, customLayout));
Expand Down Expand Up @@ -519,8 +519,7 @@ public void setAvailableCommands(
ControllerInfo controller, SessionCommands sessionCommands, Player.Commands playerCommands) {
if (sessionStub.getConnectedControllersManager().isConnected(controller)) {
if (isMediaNotificationController(controller)) {
playerWrapper.setAvailableCommands(sessionCommands, playerCommands);
sessionLegacyStub.updateLegacySessionPlaybackStateCompat();
setAvailableFrameworkControllerCommands(sessionCommands, playerCommands);
ControllerInfo systemUiControllerInfo = getSystemUiControllerInfo();
if (systemUiControllerInfo != null) {
// Set the available commands of the proxy controller to the ConnectedControllerRecord of
Expand Down Expand Up @@ -618,13 +617,12 @@ public MediaSession.ConnectionResult onConnectOnHandler(ControllerInfo controlle
"Callback.onConnect must return non-null future");
if (isMediaNotificationController(controller)) {
isMediaNotificationControllerConnected = true;
playerWrapper.setAvailableCommands(
connectionResult.availableSessionCommands, connectionResult.availablePlayerCommands);
playerWrapper.setCustomLayout(
connectionResult.customLayout != null
? connectionResult.customLayout
: instance.getCustomLayout());
sessionLegacyStub.updateLegacySessionPlaybackStateCompat();
setAvailableFrameworkControllerCommands(
connectionResult.availableSessionCommands, connectionResult.availablePlayerCommands);
}
return connectionResult;
}
Expand Down Expand Up @@ -896,6 +894,19 @@ public void onFailure(Throwable t) {
executor);
}

private void setAvailableFrameworkControllerCommands(
SessionCommands sessionCommands, Player.Commands playerCommands) {
boolean commandGetTimelineChanged =
playerWrapper.getAvailablePlayerCommands().contains(Player.COMMAND_GET_TIMELINE)
!= playerCommands.contains(Player.COMMAND_GET_TIMELINE);
playerWrapper.setAvailableCommands(sessionCommands, playerCommands);
if (commandGetTimelineChanged) {
sessionLegacyStub.updateLegacySessionPlaybackStateAndQueue(playerWrapper);
} else {
sessionLegacyStub.updateLegacySessionPlaybackState(playerWrapper);
}
}

private void dispatchRemoteControllerTaskToLegacyStub(RemoteControllerTask task) {
try {
task.run(sessionLegacyStub.getControllerLegacyCbForBroadcast(), /* seq= */ 0);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -124,7 +124,7 @@

private final MediaSessionImpl sessionImpl;
private final MediaSessionManager sessionManager;
private final ControllerCb controllerLegacyCbForBroadcast;
private final ControllerLegacyCbForBroadcast controllerLegacyCbForBroadcast;
private final ConnectionTimeoutHandler connectionTimeoutHandler;
private final MediaPlayPauseKeyHandler mediaPlayPauseKeyHandler;
private final MediaSessionCompat sessionCompat;
Expand Down Expand Up @@ -832,13 +832,22 @@ public void setLegacyControllerDisconnectTimeoutMs(long timeoutMs) {
connectionTimeoutMs = timeoutMs;
}

public void updateLegacySessionPlaybackStateCompat() {
public void updateLegacySessionPlaybackState(PlayerWrapper playerWrapper) {
postOrRun(
sessionImpl.getApplicationHandler(),
() ->
sessionImpl
.getSessionCompat()
.setPlaybackState(sessionImpl.getPlayerWrapper().createPlaybackStateCompat()));
() -> sessionCompat.setPlaybackState(playerWrapper.createPlaybackStateCompat()));
}

public void updateLegacySessionPlaybackStateAndQueue(PlayerWrapper playerWrapper) {
postOrRun(
sessionImpl.getApplicationHandler(),
() -> {
sessionCompat.setPlaybackState(playerWrapper.createPlaybackStateCompat());
controllerLegacyCbForBroadcast.updateQueue(
playerWrapper.getAvailableCommands().contains(Player.COMMAND_GET_TIMELINE)
? playerWrapper.getCurrentTimeline()
: Timeline.EMPTY);
});
}

private void handleMediaRequest(MediaItem mediaItem, boolean play) {
Expand Down Expand Up @@ -967,9 +976,14 @@ private static void setQueue(MediaSessionCompat sessionCompat, @Nullable List<Qu
}

@SuppressWarnings("nullness:argument") // MediaSessionCompat didn't annotate @Nullable.
private static void setQueueTitle(
MediaSessionCompat sessionCompat, @Nullable CharSequence title) {
sessionCompat.setQueueTitle(title);
private void setQueueTitle(MediaSessionCompat sessionCompat, @Nullable CharSequence title) {
sessionCompat.setQueueTitle(isQueueEnabled() ? title : null);
}

private boolean isQueueEnabled() {
PlayerWrapper playerWrapper = sessionImpl.getPlayerWrapper();
return playerWrapper.getAvailablePlayerCommands().contains(Player.COMMAND_GET_TIMELINE)
&& playerWrapper.getAvailableCommands().contains(Player.COMMAND_GET_TIMELINE);
}

private static MediaItem createMediaItemForMediaRequest(
Expand Down Expand Up @@ -1037,7 +1051,7 @@ public ControllerLegacyCbForBroadcast() {
public void onAvailableCommandsChangedFromPlayer(int seq, Player.Commands availableCommands) {
PlayerWrapper playerWrapper = sessionImpl.getPlayerWrapper();
maybeUpdateFlags(playerWrapper);
sessionImpl.getSessionCompat().setPlaybackState(playerWrapper.createPlaybackStateCompat());
updateLegacySessionPlaybackState(playerWrapper);
}

@Override
Expand Down Expand Up @@ -1093,63 +1107,55 @@ public void onPlayerChanged(
// If PlaybackStateCompat isn't updated by above if-statement, forcefully update
// PlaybackStateCompat to tell the latest position and its event
// time. This would also update playback speed, buffering state, player state, and error.
sessionCompat.setPlaybackState(newPlayerWrapper.createPlaybackStateCompat());
updateLegacySessionPlaybackState(newPlayerWrapper);
}
}

@Override
public void onPlayerError(int seq, @Nullable PlaybackException playerError) {
updateLegacySessionPlaybackStateCompat();
updateLegacySessionPlaybackState(sessionImpl.getPlayerWrapper());
}

@Override
public void setCustomLayout(int seq, List<CommandButton> layout) {
updateLegacySessionPlaybackStateCompat();
updateLegacySessionPlaybackState(sessionImpl.getPlayerWrapper());
}

@Override
public void onSessionExtrasChanged(int seq, Bundle sessionExtras) {
sessionImpl.getSessionCompat().setExtras(sessionExtras);
sessionCompat.setExtras(sessionExtras);
}

@Override
public void sendCustomCommand(int seq, SessionCommand command, Bundle args) {
sessionImpl.getSessionCompat().sendSessionEvent(command.customAction, args);
sessionCompat.sendSessionEvent(command.customAction, args);
}

@Override
public void onPlayWhenReadyChanged(
int seq, boolean playWhenReady, @Player.PlaybackSuppressionReason int reason)
throws RemoteException {
// Note: This method does not use any of the given arguments.
sessionImpl
.getSessionCompat()
.setPlaybackState(sessionImpl.getPlayerWrapper().createPlaybackStateCompat());
updateLegacySessionPlaybackState(sessionImpl.getPlayerWrapper());
}

@Override
public void onPlaybackSuppressionReasonChanged(
int seq, @Player.PlaybackSuppressionReason int reason) throws RemoteException {
sessionImpl
.getSessionCompat()
.setPlaybackState(sessionImpl.getPlayerWrapper().createPlaybackStateCompat());
updateLegacySessionPlaybackState(sessionImpl.getPlayerWrapper());
}

@Override
public void onPlaybackStateChanged(
int seq, @Player.State int state, @Nullable PlaybackException playerError)
throws RemoteException {
// Note: This method does not use any of the given arguments.
sessionImpl
.getSessionCompat()
.setPlaybackState(sessionImpl.getPlayerWrapper().createPlaybackStateCompat());
updateLegacySessionPlaybackState(sessionImpl.getPlayerWrapper());
}

@Override
public void onIsPlayingChanged(int seq, boolean isPlaying) throws RemoteException {
sessionImpl
.getSessionCompat()
.setPlaybackState(sessionImpl.getPlayerWrapper().createPlaybackStateCompat());
updateLegacySessionPlaybackState(sessionImpl.getPlayerWrapper());
}

@Override
Expand All @@ -1160,18 +1166,14 @@ public void onPositionDiscontinuity(
@DiscontinuityReason int reason)
throws RemoteException {
// Note: This method does not use any of the given arguments.
sessionImpl
.getSessionCompat()
.setPlaybackState(sessionImpl.getPlayerWrapper().createPlaybackStateCompat());
updateLegacySessionPlaybackState(sessionImpl.getPlayerWrapper());
}

@Override
public void onPlaybackParametersChanged(int seq, PlaybackParameters playbackParameters)
throws RemoteException {
// Note: This method does not use any of the given arguments.
sessionImpl
.getSessionCompat()
.setPlaybackState(sessionImpl.getPlayerWrapper().createPlaybackStateCompat());
updateLegacySessionPlaybackState(sessionImpl.getPlayerWrapper());
}

@Override
Expand All @@ -1186,9 +1188,7 @@ public void onMediaItemTransition(
sessionCompat.setRatingType(
MediaUtils.getRatingCompatStyle(mediaItem.mediaMetadata.userRating));
}
sessionImpl
.getSessionCompat()
.setPlaybackState(sessionImpl.getPlayerWrapper().createPlaybackStateCompat());
updateLegacySessionPlaybackState(sessionImpl.getPlayerWrapper());
}

@Override
Expand All @@ -1200,17 +1200,16 @@ public void onMediaMetadataChanged(int seq, MediaMetadata mediaMetadata) {
public void onTimelineChanged(
int seq, Timeline timeline, @Player.TimelineChangeReason int reason)
throws RemoteException {
if (timeline.isEmpty()) {
setQueue(sessionCompat, null);
return;
}

updateQueue(timeline);
// Duration might be unknown at onMediaItemTransition and become available afterward.
updateMetadataIfChanged();
}

private void updateQueue(Timeline timeline) {
if (!isQueueEnabled() || timeline.isEmpty()) {
setQueue(sessionCompat, /* queue= */ null);
return;
}
List<MediaItem> mediaItemList = MediaUtils.convertToMediaItemList(timeline);
List<@NullableType ListenableFuture<Bitmap>> bitmapFutures = new ArrayList<>();
final AtomicInteger resultCount = new AtomicInteger(0);
Expand Down Expand Up @@ -1266,11 +1265,11 @@ private void handleBitmapFuturesAllCompletedAndSetQueue(
TAG,
"Sending " + truncatedList.size() + " items out of " + timeline.getWindowCount());
}
sessionCompat.setQueue(truncatedList);
setQueue(sessionCompat, truncatedList);
} else {
// Framework MediaSession#setQueue() uses ParceledListSlice,
// which means we can safely send long lists.
sessionCompat.setQueue(queueItemList);
setQueue(sessionCompat, queueItemList);
}
}

Expand All @@ -1288,16 +1287,13 @@ public void onPlaylistMetadataChanged(int seq, MediaMetadata playlistMetadata)
@Override
public void onShuffleModeEnabledChanged(int seq, boolean shuffleModeEnabled)
throws RemoteException {
sessionImpl
.getSessionCompat()
.setShuffleMode(MediaUtils.convertToPlaybackStateCompatShuffleMode(shuffleModeEnabled));
sessionCompat.setShuffleMode(
MediaUtils.convertToPlaybackStateCompatShuffleMode(shuffleModeEnabled));
}

@Override
public void onRepeatModeChanged(int seq, @RepeatMode int repeatMode) throws RemoteException {
sessionImpl
.getSessionCompat()
.setRepeatMode(MediaUtils.convertToPlaybackStateCompatRepeatMode(repeatMode));
sessionCompat.setRepeatMode(MediaUtils.convertToPlaybackStateCompatRepeatMode(repeatMode));
}

@Override
Expand Down Expand Up @@ -1337,9 +1333,7 @@ public void onPeriodicSessionPositionInfoChanged(
boolean unusedCanAccessCurrentMediaItem,
boolean unusedCanAccessTimeline)
throws RemoteException {
sessionImpl
.getSessionCompat()
.setPlaybackState(sessionImpl.getPlayerWrapper().createPlaybackStateCompat());
updateLegacySessionPlaybackState(sessionImpl.getPlayerWrapper());
}

private void updateMetadataIfChanged() {
Expand Down Expand Up @@ -1512,7 +1506,7 @@ public void onReceive(Context context, Intent intent) {
if (keyEvent == null) {
return;
}
getSessionCompat().getController().dispatchMediaButtonEvent(keyEvent);
sessionCompat.getController().dispatchMediaButtonEvent(keyEvent);
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ interface IRemoteMediaControllerCompat {
void addQueueItem(String controllerId, in Bundle description);
void addQueueItemWithIndex(String controllerId, in Bundle description, int index);
void removeQueueItem(String controllerId, in Bundle description);
int getQueueSize(String controllerId);
void setVolumeTo(String controllerId, int value, int flags);
void adjustVolume(String controllerId, int direction, int flags);
void sendCommand(String controllerId, String command, in Bundle params, in ResultReceiver cb);
Expand Down

0 comments on commit 386d223

Please sign in to comment.