Skip to content

Commit

Permalink
Forward legacy controller onPlay/PrepareFromXY calls to onAddMediaItems
Browse files Browse the repository at this point in the history
These legacy callbacks are currently forwarded to onSetMediaUri which
will be removed in the future.

Also make sure to only call player.prepare/play after the items have
been set.

The calls to onAddQueueItem are also forwarded to onAddMediaItems to
actually allow a session to resolve these items to playable media, which
wasn't possible so far.

PiperOrigin-RevId: 453625204
(cherry picked from commit bd126ec)
  • Loading branch information
tonihei authored and marcbaechinger committed Jun 9, 2022
1 parent 926327e commit f5dc99f
Show file tree
Hide file tree
Showing 5 changed files with 295 additions and 227 deletions.
2 changes: 2 additions & 0 deletions RELEASENOTES.md
Expand Up @@ -143,6 +143,8 @@
* Replace `MediaSession.MediaItemFiler` with
`MediaSession.Callback.onAddMediaItems` to allow asynchronous resolution
of requests.
* Forward legacy `MediaController` calls to play media to
`MediaSession.Callback.onAddMediaItems` instead of `onSetMediaUri`.
* Data sources:
* Rename `DummyDataSource` to `PlaceholderDataSource`.
* Workaround OkHttp interrupt handling.
Expand Down
Expand Up @@ -19,7 +19,6 @@ import android.app.PendingIntent.FLAG_IMMUTABLE
import android.app.PendingIntent.FLAG_UPDATE_CURRENT
import android.app.TaskStackBuilder
import android.content.Intent
import android.net.Uri
import android.os.Build
import android.os.Bundle
import androidx.media3.common.AudioAttributes
Expand Down Expand Up @@ -182,37 +181,21 @@ class PlaybackService : MediaLibraryService() {
return Futures.immediateFuture(LibraryResult.ofItemList(children, params))
}

override fun onSetMediaUri(
session: MediaSession,
controller: ControllerInfo,
uri: Uri,
extras: Bundle
): Int {

if (uri.toString().startsWith(SEARCH_QUERY_PREFIX) ||
uri.toString().startsWith(SEARCH_QUERY_PREFIX_COMPAT)
) {
val searchQuery =
uri.getQueryParameter("query") ?: return SessionResult.RESULT_ERROR_NOT_SUPPORTED
setMediaItemFromSearchQuery(searchQuery)

return SessionResult.RESULT_SUCCESS
} else {
return SessionResult.RESULT_ERROR_NOT_SUPPORTED
}
}

override fun onAddMediaItems(
mediaSession: MediaSession,
controller: MediaSession.ControllerInfo,
mediaItems: List<MediaItem>
): ListenableFuture<List<MediaItem>> {
val updatedMediaItems: List<MediaItem> =
mediaItems.map { mediaItem -> MediaItemTree.getItem(mediaItem.mediaId) ?: mediaItem }
mediaItems.map { mediaItem ->
if (mediaItem.requestMetadata.searchQuery != null)
getMediaItemFromSearchQuery(mediaItem.requestMetadata.searchQuery!!)
else MediaItemTree.getItem(mediaItem.mediaId) ?: mediaItem
}
return Futures.immediateFuture(updatedMediaItems)
}

private fun setMediaItemFromSearchQuery(query: String) {
private fun getMediaItemFromSearchQuery(query: String): MediaItem {
// Only accept query with pattern "play [Title]" or "[Title]"
// Where [Title]: must be exactly matched
// If no media with exact name found, play a random media instead
Expand All @@ -223,8 +206,7 @@ class PlaybackService : MediaLibraryService() {
query
}

val item = MediaItemTree.getItemFromTitle(mediaTitle) ?: MediaItemTree.getRandomItem()
player.setMediaItem(item)
return MediaItemTree.getItemFromTitle(mediaTitle) ?: MediaItemTree.getRandomItem()
}
}

Expand Down
Expand Up @@ -973,48 +973,6 @@ default ListenableFuture<SessionResult> onSetRating(
* <p>The implementation should create proper {@link MediaItem media item(s)} for the given
* {@code uri} and call {@link Player#setMediaItems}.
*
* <p>When {@link MediaControllerCompat} is connected and sends commands with following methods,
* the {@code uri} will have the following patterns:
*
* <table>
* <caption>Uri patterns corresponding to MediaControllerCompat command methods</caption>
* <tr>
* <th>Method</th>
* <th>Uri pattern</th>
* </tr>
* <tr>
* <td>{@link MediaControllerCompat.TransportControls#prepareFromUri prepareFromUri}</td>
* <td>The {@code uri} passed as argument</td>
* </tr>
* <tr>
* <td>
* {@link MediaControllerCompat.TransportControls#prepareFromMediaId prepareFromMediaId}
* </td>
* <td>{@code androidx://media3-session/prepareFromMediaId?id=[mediaId]}</td>
* </tr>
* <tr>
* <td>
* {@link MediaControllerCompat.TransportControls#prepareFromSearch prepareFromSearch}
* </td>
* <td>{@code androidx://media3-session/prepareFromSearch?query=[query]}</td>
* </tr>
* <tr>
* <td>{@link MediaControllerCompat.TransportControls#playFromUri playFromUri}</td>
* <td>The {@code uri} passed as argument</td>
* </tr>
* <tr>
* <td>{@link MediaControllerCompat.TransportControls#playFromMediaId playFromMediaId}</td>
* <td>{@code androidx://media3-session/playFromMediaId?id=[mediaId]}</td>
* </tr>
* <tr>
* <td>{@link MediaControllerCompat.TransportControls#playFromSearch playFromSearch}</td>
* <td>{@code androidx://media3-session/playFromSearch?query=[query]}</td>
* </tr>
* </table>
*
* <p>{@link Player#prepare()} or {@link Player#play()} should follow if this is called by above
* methods.
*
* @param session The session for this event.
* @param controller The controller information.
* @param uri The uri.
Expand Down Expand Up @@ -1057,7 +1015,8 @@ default ListenableFuture<SessionResult> onCustomCommand(

/**
* Called when a controller requested to add new {@linkplain MediaItem media items} to the
* playlist.
* playlist via one of the {@code Player.addMediaItem(s)} or {@code Player.setMediaItem(s)}
* methods.
*
* <p>Note that the requested {@linkplain MediaItem media items} don't have a {@link
* MediaItem.LocalConfiguration} (for example, a URI) and need to be updated to make them
Expand All @@ -1066,7 +1025,28 @@ default ListenableFuture<SessionResult> onCustomCommand(
* MediaItem#requestMetadata}.
*
* <p>Return a {@link ListenableFuture} with the resolved {@link MediaItem media items}. You can
* also return the items directly by using Guava's {@link Futures#immediateFuture(Object)}.
* also return the items directly by using Guava's {@link Futures#immediateFuture(Object)}. Once
* the {@link MediaItem media items} have been resolved, the session will call {@link
* Player#setMediaItems} or {@link Player#addMediaItems} as requested.
*
* <p>Interoperability: This method will be called in response to the following {@link
* MediaControllerCompat} methods:
*
* <ul>
* <li>{@link MediaControllerCompat.TransportControls#prepareFromUri prepareFromUri}
* <li>{@link MediaControllerCompat.TransportControls#playFromUri playFromUri}
* <li>{@link MediaControllerCompat.TransportControls#prepareFromMediaId prepareFromMediaId}
* <li>{@link MediaControllerCompat.TransportControls#playFromMediaId playFromMediaId}
* <li>{@link MediaControllerCompat.TransportControls#prepareFromSearch prepareFromSearch}
* <li>{@link MediaControllerCompat.TransportControls#playFromSearch playFromSearch}
* <li>{@link MediaControllerCompat.TransportControls#addQueueItem addQueueItem}
* </ul>
*
* The values of {@link MediaItem#mediaId}, {@link MediaItem.RequestMetadata#mediaUri}, {@link
* MediaItem.RequestMetadata#searchQuery} and {@link MediaItem.RequestMetadata#extras} will be
* set to match the legacy method call. The session will call {@link Player#setMediaItems} or
* {@link Player#addMediaItems}, followed by {@link Player#prepare()} and {@link Player#play()}
* as appropriate once the {@link MediaItem} has been resolved.
*
* @param mediaSession The session for this event.
* @param controller The controller information.
Expand Down

0 comments on commit f5dc99f

Please sign in to comment.