Skip to content

Fix a bug and add a feature for media resumption #2458

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 2 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -166,10 +166,10 @@ public ListenableFuture<LibraryResult<ImmutableList<MediaItem>>> onGetChildrenOn
if (!canResumePlaybackOnStart()) {
return Futures.immediateFuture(LibraryResult.ofError(ERROR_NOT_SUPPORTED));
}
// Advertise support for playback resumption. If STATE_IDLE, the request arrives at boot time
// to get the full item data to build a notification. If not STATE_IDLE we don't need to
// deliver the full media item, so we do the minimal viable effort.
return getPlayerWrapper().getPlaybackState() == Player.STATE_IDLE
// Advertise support for playback resumption. If we're not playing, the request probably
// arrived at boot time to get the full item data to build a notification. If we're playing,
// we don't need to deliver the full media item, so we do the minimal viable effort.
return !getPlayerWrapper().getPlayWhenReady()
? getRecentMediaItemAtDeviceBootTime(browser, params)
: Futures.immediateFuture(
LibraryResult.ofItemList(
Expand Down Expand Up @@ -470,7 +470,7 @@ private void postOrRunOnApplicationHandler(Runnable runnable) {
? checkNotNull(getMediaNotificationControllerInfo())
: controller;
ListenableFuture<MediaSession.MediaItemsWithStartPosition> future =
callback.onPlaybackResumption(instance, controller);
callback.onPlaybackResumption(instance, controller, false);
Futures.addCallback(
future,
new FutureCallback<MediaSession.MediaItemsWithStartPosition>() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1729,6 +1729,17 @@ default ListenableFuture<MediaItemsWithStartPosition> onSetMediaItems(
new MediaItemsWithStartPosition(mediaItemList, startIndex, startPositionMs)));
}

/**
* @deprecated Override {@link MediaSession.Callback#onPlaybackResumption(MediaSession,
* ControllerInfo, boolean)} instead.
*/
@Deprecated
@UnstableApi
default ListenableFuture<MediaItemsWithStartPosition> onPlaybackResumption(
MediaSession mediaSession, ControllerInfo controller) {
return Futures.immediateFailedFuture(new UnsupportedOperationException());
}

/**
* Returns the playlist with which the player should be prepared when a controller requests to
* play without a current {@link MediaItem}.
Expand All @@ -1750,16 +1761,24 @@ default ListenableFuture<MediaItemsWithStartPosition> onSetMediaItems(
* Player#COMMAND_GET_CURRENT_MEDIA_ITEM} and either {@link Player#COMMAND_SET_MEDIA_ITEM} or
* {@link Player#COMMAND_CHANGE_MEDIA_ITEMS} available.
*
* <p>If {@code isForPlayback} is true, {@link ManuallyHandlePlaybackResumption} may be set on
* the future to manually handle playback resumption. Refer to the {@link
* ManuallyHandlePlaybackResumption} javadoc for more details.
*
* @param mediaSession The media session for which playback resumption is requested.
* @param controller The {@linkplain ControllerInfo controller} that requests the playback
* resumption. This may be a short living controller created only for issuing a play command
* for resuming playback.
* @param isForPlayback Whether playback is going to be started as a result of the future being
* completed. If false, SystemUI is querying for media item data in order to build and
* display the resumption notification at boot time.
* @return The {@linkplain MediaItemsWithStartPosition playlist} to resume playback with.
*/
@UnstableApi
@SuppressWarnings("deprecation") // calling deprecated API for backwards compatibility
default ListenableFuture<MediaItemsWithStartPosition> onPlaybackResumption(
MediaSession mediaSession, ControllerInfo controller) {
return Futures.immediateFailedFuture(new UnsupportedOperationException());
MediaSession mediaSession, ControllerInfo controller, boolean isForPlayback) {
return onPlaybackResumption(mediaSession, controller);
}

/**
Expand Down Expand Up @@ -1875,6 +1894,25 @@ public int hashCode() {
}
}

/**
* Exception that may be set to the future in {@link
* MediaSession.Callback#onPlaybackResumption(MediaSession, ControllerInfo, boolean)} if the
* parameter {@code isForPlayback} is true, in order to signal that the app wishes to handle this
* resumption itself. This means the app is responsible for restoring a playlist, setting it to
* the player, and starting playback.
*
* <p>Do note this has various pitfalls and needs to be used carefully:<br>
* - {@link MediaSession.Callback#onPlayerInteractionFinished(MediaSession, ControllerInfo,
* Player.Commands)} will NOT be called for the resumption command.<br>
* - If playback is not actually resumed in this method, the resumption notification will end up
* non-functional, but will keep being displayed. This is not a suitable way to disable playback
* resumption, do not attempt to disable it this way.<br>
* - If the app takes too long to go into foreground, the grant to go into foreground may have
* expired.
*/
@UnstableApi
public static class ManuallyHandlePlaybackResumption extends Exception {}

/**
* A result for {@link Callback#onConnect(MediaSession, ControllerInfo)} to denote the set of
* available commands and the media button preferences for a {@link ControllerInfo controller}.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1121,7 +1121,7 @@ protected MediaSessionServiceLegacyStub getLegacyBrowserService() {
@Nullable
ListenableFuture<MediaItemsWithStartPosition> future =
checkNotNull(
callback.onPlaybackResumption(instance, controllerForRequest),
callback.onPlaybackResumption(instance, controllerForRequest, true),
"Callback.onPlaybackResumption must return a non-null future");
Futures.addCallback(
future,
Expand Down Expand Up @@ -1151,6 +1151,9 @@ public void onFailure(Throwable t) {
+ " media button receiver to your manifest or if you implement the recent"
+ " media item contract with your MediaLibraryService.",
t);
} else if (t instanceof MediaSession.ManuallyHandlePlaybackResumption) {
// See ManuallyHandlePlaybackResumption javadoc for details.
return;
} else {
Log.e(
TAG,
Expand Down