Forward legacy controller onPlay/PrepareFromXY calls to onAddMediaItems
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
This commit is contained in:
parent
4a6f431f01
commit
bd126ec5c5
@ -143,6 +143,8 @@
|
|||||||
* Replace `MediaSession.MediaItemFiler` with
|
* Replace `MediaSession.MediaItemFiler` with
|
||||||
`MediaSession.Callback.onAddMediaItems` to allow asynchronous resolution
|
`MediaSession.Callback.onAddMediaItems` to allow asynchronous resolution
|
||||||
of requests.
|
of requests.
|
||||||
|
* Forward legacy `MediaController` calls to play media to
|
||||||
|
`MediaSession.Callback.onAddMediaItems` instead of `onSetMediaUri`.
|
||||||
* Data sources:
|
* Data sources:
|
||||||
* Rename `DummyDataSource` to `PlaceholderDataSource`.
|
* Rename `DummyDataSource` to `PlaceholderDataSource`.
|
||||||
* Workaround OkHttp interrupt handling.
|
* Workaround OkHttp interrupt handling.
|
||||||
|
@ -19,7 +19,6 @@ import android.app.PendingIntent.FLAG_IMMUTABLE
|
|||||||
import android.app.PendingIntent.FLAG_UPDATE_CURRENT
|
import android.app.PendingIntent.FLAG_UPDATE_CURRENT
|
||||||
import android.app.TaskStackBuilder
|
import android.app.TaskStackBuilder
|
||||||
import android.content.Intent
|
import android.content.Intent
|
||||||
import android.net.Uri
|
|
||||||
import android.os.Build
|
import android.os.Build
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import androidx.media3.common.AudioAttributes
|
import androidx.media3.common.AudioAttributes
|
||||||
@ -182,37 +181,21 @@ class PlaybackService : MediaLibraryService() {
|
|||||||
return Futures.immediateFuture(LibraryResult.ofItemList(children, params))
|
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(
|
override fun onAddMediaItems(
|
||||||
mediaSession: MediaSession,
|
mediaSession: MediaSession,
|
||||||
controller: MediaSession.ControllerInfo,
|
controller: MediaSession.ControllerInfo,
|
||||||
mediaItems: List<MediaItem>
|
mediaItems: List<MediaItem>
|
||||||
): ListenableFuture<List<MediaItem>> {
|
): ListenableFuture<List<MediaItem>> {
|
||||||
val updatedMediaItems: 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)
|
return Futures.immediateFuture(updatedMediaItems)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun setMediaItemFromSearchQuery(query: String) {
|
private fun getMediaItemFromSearchQuery(query: String): MediaItem {
|
||||||
// Only accept query with pattern "play [Title]" or "[Title]"
|
// Only accept query with pattern "play [Title]" or "[Title]"
|
||||||
// Where [Title]: must be exactly matched
|
// Where [Title]: must be exactly matched
|
||||||
// If no media with exact name found, play a random media instead
|
// If no media with exact name found, play a random media instead
|
||||||
@ -223,8 +206,7 @@ class PlaybackService : MediaLibraryService() {
|
|||||||
query
|
query
|
||||||
}
|
}
|
||||||
|
|
||||||
val item = MediaItemTree.getItemFromTitle(mediaTitle) ?: MediaItemTree.getRandomItem()
|
return MediaItemTree.getItemFromTitle(mediaTitle) ?: MediaItemTree.getRandomItem()
|
||||||
player.setMediaItem(item)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -973,48 +973,6 @@ public class MediaSession {
|
|||||||
* <p>The implementation should create proper {@link MediaItem media item(s)} for the given
|
* <p>The implementation should create proper {@link MediaItem media item(s)} for the given
|
||||||
* {@code uri} and call {@link Player#setMediaItems}.
|
* {@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 session The session for this event.
|
||||||
* @param controller The controller information.
|
* @param controller The controller information.
|
||||||
* @param uri The uri.
|
* @param uri The uri.
|
||||||
@ -1057,7 +1015,8 @@ public class MediaSession {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Called when a controller requested to add new {@linkplain MediaItem media items} to the
|
* 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
|
* <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
|
* MediaItem.LocalConfiguration} (for example, a URI) and need to be updated to make them
|
||||||
@ -1066,7 +1025,28 @@ public class MediaSession {
|
|||||||
* MediaItem#requestMetadata}.
|
* MediaItem#requestMetadata}.
|
||||||
*
|
*
|
||||||
* <p>Return a {@link ListenableFuture} with the resolved {@link MediaItem media items}. You can
|
* <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 mediaSession The session for this event.
|
||||||
* @param controller The controller information.
|
* @param controller The controller information.
|
||||||
|
@ -82,6 +82,9 @@ import androidx.media3.common.util.Util;
|
|||||||
import androidx.media3.session.MediaSession.ControllerCb;
|
import androidx.media3.session.MediaSession.ControllerCb;
|
||||||
import androidx.media3.session.MediaSession.ControllerInfo;
|
import androidx.media3.session.MediaSession.ControllerInfo;
|
||||||
import androidx.media3.session.SessionCommand.CommandCode;
|
import androidx.media3.session.SessionCommand.CommandCode;
|
||||||
|
import com.google.common.collect.ImmutableList;
|
||||||
|
import com.google.common.util.concurrent.FutureCallback;
|
||||||
|
import com.google.common.util.concurrent.Futures;
|
||||||
import com.google.common.util.concurrent.ListenableFuture;
|
import com.google.common.util.concurrent.ListenableFuture;
|
||||||
import com.google.common.util.concurrent.MoreExecutors;
|
import com.google.common.util.concurrent.MoreExecutors;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
@ -270,39 +273,25 @@ import org.checkerframework.checker.initialization.qual.Initialized;
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onPrepareFromMediaId(String mediaId, @Nullable Bundle extras) {
|
public void onPrepareFromMediaId(String mediaId, @Nullable Bundle extras) {
|
||||||
Uri mediaUri =
|
handleMediaRequest(
|
||||||
new Uri.Builder()
|
createMediaItemForMediaRequest(
|
||||||
.scheme(MediaConstants.MEDIA_URI_SCHEME)
|
mediaId, /* mediaUri= */ null, /* searchQuery= */ null, extras),
|
||||||
.authority(MediaConstants.MEDIA_URI_AUTHORITY)
|
/* play= */ false);
|
||||||
.path(MediaConstants.MEDIA_URI_PATH_PREPARE_FROM_MEDIA_ID)
|
|
||||||
.appendQueryParameter(MediaConstants.MEDIA_URI_QUERY_ID, mediaId)
|
|
||||||
.build();
|
|
||||||
onPrepareFromUri(mediaUri, extras);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onPrepareFromSearch(String query, @Nullable Bundle extras) {
|
public void onPrepareFromSearch(String query, @Nullable Bundle extras) {
|
||||||
Uri mediaUri =
|
handleMediaRequest(
|
||||||
new Uri.Builder()
|
createMediaItemForMediaRequest(/* mediaId= */ null, /* mediaUri= */ null, query, extras),
|
||||||
.scheme(MediaConstants.MEDIA_URI_SCHEME)
|
/* play= */ false);
|
||||||
.authority(MediaConstants.MEDIA_URI_AUTHORITY)
|
|
||||||
.path(MediaConstants.MEDIA_URI_PATH_PREPARE_FROM_SEARCH)
|
|
||||||
.appendQueryParameter(MediaConstants.MEDIA_URI_QUERY_QUERY, query)
|
|
||||||
.build();
|
|
||||||
onPrepareFromUri(mediaUri, extras);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onPrepareFromUri(Uri mediaUri, @Nullable Bundle extras) {
|
public void onPrepareFromUri(Uri mediaUri, @Nullable Bundle extras) {
|
||||||
dispatchSessionTaskWithSessionCommand(
|
handleMediaRequest(
|
||||||
SessionCommand.COMMAND_CODE_SESSION_SET_MEDIA_URI,
|
createMediaItemForMediaRequest(
|
||||||
controller -> {
|
/* mediaId= */ null, mediaUri, /* searchQuery= */ null, extras),
|
||||||
if (sessionImpl.onSetMediaUriOnHandler(
|
/* play= */ false);
|
||||||
controller, mediaUri, extras == null ? Bundle.EMPTY : extras)
|
|
||||||
== RESULT_SUCCESS) {
|
|
||||||
sessionImpl.getPlayerWrapper().prepare();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -325,47 +314,25 @@ import org.checkerframework.checker.initialization.qual.Initialized;
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onPlayFromMediaId(String mediaId, @Nullable Bundle extras) {
|
public void onPlayFromMediaId(String mediaId, @Nullable Bundle extras) {
|
||||||
Uri mediaUri =
|
handleMediaRequest(
|
||||||
new Uri.Builder()
|
createMediaItemForMediaRequest(
|
||||||
.scheme(MediaConstants.MEDIA_URI_SCHEME)
|
mediaId, /* mediaUri= */ null, /* searchQuery= */ null, extras),
|
||||||
.authority(MediaConstants.MEDIA_URI_AUTHORITY)
|
/* play= */ true);
|
||||||
.path(MediaConstants.MEDIA_URI_PATH_PLAY_FROM_MEDIA_ID)
|
|
||||||
.appendQueryParameter(MediaConstants.MEDIA_URI_QUERY_ID, mediaId)
|
|
||||||
.build();
|
|
||||||
onPlayFromUri(mediaUri, extras);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onPlayFromSearch(String query, @Nullable Bundle extras) {
|
public void onPlayFromSearch(String query, @Nullable Bundle extras) {
|
||||||
Uri mediaUri =
|
handleMediaRequest(
|
||||||
new Uri.Builder()
|
createMediaItemForMediaRequest(/* mediaId= */ null, /* mediaUri= */ null, query, extras),
|
||||||
.scheme(MediaConstants.MEDIA_URI_SCHEME)
|
/* play= */ true);
|
||||||
.authority(MediaConstants.MEDIA_URI_AUTHORITY)
|
|
||||||
.path(MediaConstants.MEDIA_URI_PATH_PLAY_FROM_SEARCH)
|
|
||||||
.appendQueryParameter(MediaConstants.MEDIA_URI_QUERY_QUERY, query)
|
|
||||||
.build();
|
|
||||||
onPlayFromUri(mediaUri, extras);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onPlayFromUri(Uri mediaUri, @Nullable Bundle extras) {
|
public void onPlayFromUri(Uri mediaUri, @Nullable Bundle extras) {
|
||||||
dispatchSessionTaskWithSessionCommand(
|
handleMediaRequest(
|
||||||
SessionCommand.COMMAND_CODE_SESSION_SET_MEDIA_URI,
|
createMediaItemForMediaRequest(
|
||||||
controller -> {
|
/* mediaId= */ null, mediaUri, /* searchQuery= */ null, extras),
|
||||||
if (sessionImpl.onSetMediaUriOnHandler(
|
/* play= */ true);
|
||||||
controller, mediaUri, extras == null ? Bundle.EMPTY : extras)
|
|
||||||
== RESULT_SUCCESS) {
|
|
||||||
PlayerWrapper playerWrapper = sessionImpl.getPlayerWrapper();
|
|
||||||
@Player.State int playbackState = playerWrapper.getPlaybackState();
|
|
||||||
if (playbackState == Player.STATE_IDLE) {
|
|
||||||
playerWrapper.prepare();
|
|
||||||
} else if (playbackState == STATE_ENDED) {
|
|
||||||
playerWrapper.seekTo(
|
|
||||||
playerWrapper.getCurrentMediaItemIndex(), /* positionMs= */ C.TIME_UNSET);
|
|
||||||
}
|
|
||||||
playerWrapper.play();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -498,40 +465,12 @@ import org.checkerframework.checker.initialization.qual.Initialized;
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onAddQueueItem(@Nullable MediaDescriptionCompat description) {
|
public void onAddQueueItem(@Nullable MediaDescriptionCompat description) {
|
||||||
if (description == null) {
|
handleOnAddQueueItem(description, /* index= */ C.INDEX_UNSET);
|
||||||
return;
|
|
||||||
}
|
|
||||||
dispatchSessionTaskWithPlayerCommand(
|
|
||||||
COMMAND_CHANGE_MEDIA_ITEMS,
|
|
||||||
controller -> {
|
|
||||||
@Nullable String mediaId = description.getMediaId();
|
|
||||||
if (TextUtils.isEmpty(mediaId)) {
|
|
||||||
Log.w(TAG, "onAddQueueItem(): Media ID shouldn't be empty");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
MediaItem mediaItem = MediaUtils.convertToMediaItem(description);
|
|
||||||
sessionImpl.getPlayerWrapper().addMediaItem(mediaItem);
|
|
||||||
},
|
|
||||||
sessionCompat.getCurrentControllerInfo());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onAddQueueItem(@Nullable MediaDescriptionCompat description, int index) {
|
public void onAddQueueItem(@Nullable MediaDescriptionCompat description, int index) {
|
||||||
if (description == null) {
|
handleOnAddQueueItem(description, index);
|
||||||
return;
|
|
||||||
}
|
|
||||||
dispatchSessionTaskWithPlayerCommand(
|
|
||||||
COMMAND_CHANGE_MEDIA_ITEMS,
|
|
||||||
controller -> {
|
|
||||||
@Nullable String mediaId = description.getMediaId();
|
|
||||||
if (TextUtils.isEmpty(mediaId)) {
|
|
||||||
Log.w(TAG, "onAddQueueItem(): Media ID shouldn't be empty");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
MediaItem mediaItem = MediaUtils.convertToMediaItem(description);
|
|
||||||
sessionImpl.getPlayerWrapper().addMediaItem(index, mediaItem);
|
|
||||||
},
|
|
||||||
sessionCompat.getCurrentControllerInfo());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -738,6 +677,85 @@ import org.checkerframework.checker.initialization.qual.Initialized;
|
|||||||
connectionTimeoutMs = timeoutMs;
|
connectionTimeoutMs = timeoutMs;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void handleMediaRequest(MediaItem mediaItem, boolean play) {
|
||||||
|
dispatchSessionTaskWithPlayerCommand(
|
||||||
|
COMMAND_CHANGE_MEDIA_ITEMS,
|
||||||
|
controller -> {
|
||||||
|
ListenableFuture<List<MediaItem>> mediaItemsFuture =
|
||||||
|
sessionImpl.onAddMediaItemsOnHandler(controller, ImmutableList.of(mediaItem));
|
||||||
|
Futures.addCallback(
|
||||||
|
mediaItemsFuture,
|
||||||
|
new FutureCallback<List<MediaItem>>() {
|
||||||
|
@Override
|
||||||
|
public void onSuccess(List<MediaItem> mediaItems) {
|
||||||
|
postOrRun(
|
||||||
|
sessionImpl.getApplicationHandler(),
|
||||||
|
() -> {
|
||||||
|
Player player = sessionImpl.getPlayerWrapper();
|
||||||
|
player.setMediaItems(mediaItems);
|
||||||
|
@Player.State int playbackState = player.getPlaybackState();
|
||||||
|
if (playbackState == Player.STATE_IDLE) {
|
||||||
|
player.prepare();
|
||||||
|
} else if (playbackState == Player.STATE_ENDED) {
|
||||||
|
player.seekTo(/* positionMs= */ C.TIME_UNSET);
|
||||||
|
}
|
||||||
|
if (play) {
|
||||||
|
player.play();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onFailure(Throwable t) {
|
||||||
|
// Do nothing, the session is free to ignore these requests.
|
||||||
|
}
|
||||||
|
},
|
||||||
|
MoreExecutors.directExecutor());
|
||||||
|
},
|
||||||
|
sessionCompat.getCurrentControllerInfo());
|
||||||
|
}
|
||||||
|
|
||||||
|
private void handleOnAddQueueItem(@Nullable MediaDescriptionCompat description, int index) {
|
||||||
|
if (description == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
dispatchSessionTaskWithPlayerCommand(
|
||||||
|
COMMAND_CHANGE_MEDIA_ITEMS,
|
||||||
|
controller -> {
|
||||||
|
@Nullable String mediaId = description.getMediaId();
|
||||||
|
if (TextUtils.isEmpty(mediaId)) {
|
||||||
|
Log.w(TAG, "onAddQueueItem(): Media ID shouldn't be empty");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
MediaItem mediaItem = MediaUtils.convertToMediaItem(description);
|
||||||
|
ListenableFuture<List<MediaItem>> mediaItemsFuture =
|
||||||
|
sessionImpl.onAddMediaItemsOnHandler(controller, ImmutableList.of(mediaItem));
|
||||||
|
Futures.addCallback(
|
||||||
|
mediaItemsFuture,
|
||||||
|
new FutureCallback<List<MediaItem>>() {
|
||||||
|
@Override
|
||||||
|
public void onSuccess(List<MediaItem> mediaItems) {
|
||||||
|
postOrRun(
|
||||||
|
sessionImpl.getApplicationHandler(),
|
||||||
|
() -> {
|
||||||
|
if (index == C.INDEX_UNSET) {
|
||||||
|
sessionImpl.getPlayerWrapper().addMediaItems(mediaItems);
|
||||||
|
} else {
|
||||||
|
sessionImpl.getPlayerWrapper().addMediaItems(index, mediaItems);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onFailure(Throwable t) {
|
||||||
|
// Do nothing, the session is free to ignore these requests.
|
||||||
|
}
|
||||||
|
},
|
||||||
|
MoreExecutors.directExecutor());
|
||||||
|
},
|
||||||
|
sessionCompat.getCurrentControllerInfo());
|
||||||
|
}
|
||||||
|
|
||||||
private static void sendCustomCommandResultWhenReady(
|
private static void sendCustomCommandResultWhenReady(
|
||||||
ResultReceiver receiver, ListenableFuture<SessionResult> future) {
|
ResultReceiver receiver, ListenableFuture<SessionResult> future) {
|
||||||
future.addListener(
|
future.addListener(
|
||||||
@ -776,6 +794,22 @@ import org.checkerframework.checker.initialization.qual.Initialized;
|
|||||||
sessionCompat.setQueueTitle(title);
|
sessionCompat.setQueueTitle(title);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static MediaItem createMediaItemForMediaRequest(
|
||||||
|
@Nullable String mediaId,
|
||||||
|
@Nullable Uri mediaUri,
|
||||||
|
@Nullable String searchQuery,
|
||||||
|
@Nullable Bundle extras) {
|
||||||
|
return new MediaItem.Builder()
|
||||||
|
.setMediaId(mediaId == null ? MediaItem.DEFAULT_MEDIA_ID : mediaId)
|
||||||
|
.setRequestMetadata(
|
||||||
|
new MediaItem.RequestMetadata.Builder()
|
||||||
|
.setMediaUri(mediaUri)
|
||||||
|
.setSearchQuery(searchQuery)
|
||||||
|
.setExtras(extras)
|
||||||
|
.build())
|
||||||
|
.build();
|
||||||
|
}
|
||||||
|
|
||||||
/* @FunctionalInterface */
|
/* @FunctionalInterface */
|
||||||
private interface SessionTask {
|
private interface SessionTask {
|
||||||
|
|
||||||
|
@ -56,11 +56,16 @@ import androidx.media3.test.session.common.TestUtils;
|
|||||||
import androidx.test.core.app.ApplicationProvider;
|
import androidx.test.core.app.ApplicationProvider;
|
||||||
import androidx.test.ext.junit.runners.AndroidJUnit4;
|
import androidx.test.ext.junit.runners.AndroidJUnit4;
|
||||||
import androidx.test.filters.LargeTest;
|
import androidx.test.filters.LargeTest;
|
||||||
|
import com.google.common.collect.ImmutableList;
|
||||||
import com.google.common.util.concurrent.Futures;
|
import com.google.common.util.concurrent.Futures;
|
||||||
import com.google.common.util.concurrent.ListenableFuture;
|
import com.google.common.util.concurrent.ListenableFuture;
|
||||||
|
import com.google.common.util.concurrent.ListeningExecutorService;
|
||||||
|
import com.google.common.util.concurrent.MoreExecutors;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.concurrent.CountDownLatch;
|
import java.util.concurrent.CountDownLatch;
|
||||||
|
import java.util.concurrent.Executors;
|
||||||
|
import java.util.concurrent.atomic.AtomicReference;
|
||||||
import org.junit.After;
|
import org.junit.After;
|
||||||
import org.junit.Before;
|
import org.junit.Before;
|
||||||
import org.junit.ClassRule;
|
import org.junit.ClassRule;
|
||||||
@ -75,6 +80,7 @@ public class MediaSessionCallbackWithMediaControllerCompatTest {
|
|||||||
|
|
||||||
private static final String TAG = "MSCallbackWithMCCTest";
|
private static final String TAG = "MSCallbackWithMCCTest";
|
||||||
|
|
||||||
|
private static final String TEST_URI = "http://test.test";
|
||||||
private static final String EXPECTED_CONTROLLER_PACKAGE_NAME =
|
private static final String EXPECTED_CONTROLLER_PACKAGE_NAME =
|
||||||
(Util.SDK_INT < 21 || Util.SDK_INT >= 24) ? SUPPORT_APP_PACKAGE_NAME : LEGACY_CONTROLLER;
|
(Util.SDK_INT < 21 || Util.SDK_INT >= 24) ? SUPPORT_APP_PACKAGE_NAME : LEGACY_CONTROLLER;
|
||||||
|
|
||||||
@ -88,6 +94,7 @@ public class MediaSessionCallbackWithMediaControllerCompatTest {
|
|||||||
private RemoteMediaControllerCompat controller;
|
private RemoteMediaControllerCompat controller;
|
||||||
private MockPlayer player;
|
private MockPlayer player;
|
||||||
private AudioManager audioManager;
|
private AudioManager audioManager;
|
||||||
|
private ListeningExecutorService executorService;
|
||||||
|
|
||||||
@Before
|
@Before
|
||||||
public void setUp() {
|
public void setUp() {
|
||||||
@ -95,6 +102,9 @@ public class MediaSessionCallbackWithMediaControllerCompatTest {
|
|||||||
handler = threadTestRule.getHandler();
|
handler = threadTestRule.getHandler();
|
||||||
player = new MockPlayer.Builder().setApplicationLooper(handler.getLooper()).build();
|
player = new MockPlayer.Builder().setApplicationLooper(handler.getLooper()).build();
|
||||||
audioManager = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE);
|
audioManager = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE);
|
||||||
|
// Intentionally use an Executor with another thread to test asynchronous workflows involving
|
||||||
|
// background tasks.
|
||||||
|
executorService = MoreExecutors.listeningDecorator(Executors.newSingleThreadExecutor());
|
||||||
}
|
}
|
||||||
|
|
||||||
@After
|
@After
|
||||||
@ -107,6 +117,7 @@ public class MediaSessionCallbackWithMediaControllerCompatTest {
|
|||||||
controller.cleanUp();
|
controller.cleanUp();
|
||||||
controller = null;
|
controller = null;
|
||||||
}
|
}
|
||||||
|
executorService.shutdownNow();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@ -294,10 +305,22 @@ public class MediaSessionCallbackWithMediaControllerCompatTest {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void addQueueItem() throws Exception {
|
public void addQueueItem() throws Exception {
|
||||||
|
AtomicReference<List<MediaItem>> requestedMediaItems = new AtomicReference<>();
|
||||||
|
MediaItem resolvedMediaItem = MediaItem.fromUri(TEST_URI);
|
||||||
|
MediaSession.Callback callback =
|
||||||
|
new MediaSession.Callback() {
|
||||||
|
@Override
|
||||||
|
public ListenableFuture<List<MediaItem>> onAddMediaItems(
|
||||||
|
MediaSession mediaSession, ControllerInfo controller, List<MediaItem> mediaItems) {
|
||||||
|
requestedMediaItems.set(mediaItems);
|
||||||
|
// Resolve MediaItem asynchronously to test correct threading logic.
|
||||||
|
return executorService.submit(() -> ImmutableList.of(resolvedMediaItem));
|
||||||
|
}
|
||||||
|
};
|
||||||
session =
|
session =
|
||||||
new MediaSession.Builder(context, player)
|
new MediaSession.Builder(context, player)
|
||||||
.setId("addQueueItem")
|
.setId("addQueueItem")
|
||||||
.setCallback(new TestSessionCallback())
|
.setCallback(callback)
|
||||||
.build();
|
.build();
|
||||||
controller =
|
controller =
|
||||||
new RemoteMediaControllerCompat(
|
new RemoteMediaControllerCompat(
|
||||||
@ -305,49 +328,73 @@ public class MediaSessionCallbackWithMediaControllerCompatTest {
|
|||||||
|
|
||||||
handler.postAndSync(
|
handler.postAndSync(
|
||||||
() -> {
|
() -> {
|
||||||
player.timeline = MediaTestUtils.createTimeline(/* windowCount= */ 10);
|
List<MediaItem> mediaItems = MediaTestUtils.createMediaItems(/* size= */ 10);
|
||||||
|
player.setMediaItems(mediaItems);
|
||||||
|
player.timeline = MediaTestUtils.createTimeline(mediaItems);
|
||||||
player.notifyTimelineChanged(Player.TIMELINE_CHANGE_REASON_PLAYLIST_CHANGED);
|
player.notifyTimelineChanged(Player.TIMELINE_CHANGE_REASON_PLAYLIST_CHANGED);
|
||||||
});
|
});
|
||||||
|
|
||||||
// Prepare an item to add.
|
// Prepare an item to add.
|
||||||
String mediaId = "newMediaItemId";
|
String mediaId = "newMediaItemId";
|
||||||
MediaDescriptionCompat desc = new MediaDescriptionCompat.Builder().setMediaId(mediaId).build();
|
Uri mediaUri = Uri.parse("https://test.test");
|
||||||
|
MediaDescriptionCompat desc =
|
||||||
|
new MediaDescriptionCompat.Builder().setMediaId(mediaId).setMediaUri(mediaUri).build();
|
||||||
controller.addQueueItem(desc);
|
controller.addQueueItem(desc);
|
||||||
|
|
||||||
player.awaitMethodCalled(MockPlayer.METHOD_ADD_MEDIA_ITEM, TIMEOUT_MS);
|
player.awaitMethodCalled(MockPlayer.METHOD_ADD_MEDIA_ITEMS, TIMEOUT_MS);
|
||||||
assertThat(player.mediaItems).hasSize(1);
|
assertThat(requestedMediaItems.get()).hasSize(1);
|
||||||
assertThat(player.mediaItems.get(0).mediaId).isEqualTo(mediaId);
|
assertThat(requestedMediaItems.get().get(0).mediaId).isEqualTo(mediaId);
|
||||||
|
assertThat(requestedMediaItems.get().get(0).requestMetadata.mediaUri).isEqualTo(mediaUri);
|
||||||
|
assertThat(player.mediaItems).hasSize(11);
|
||||||
|
assertThat(player.mediaItems.get(10)).isEqualTo(resolvedMediaItem);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void addQueueItemWithIndex() throws Exception {
|
public void addQueueItemWithIndex() throws Exception {
|
||||||
|
AtomicReference<List<MediaItem>> requestedMediaItems = new AtomicReference<>();
|
||||||
|
MediaItem resolvedMediaItem = MediaItem.fromUri(TEST_URI);
|
||||||
|
MediaSession.Callback callback =
|
||||||
|
new MediaSession.Callback() {
|
||||||
|
@Override
|
||||||
|
public ListenableFuture<List<MediaItem>> onAddMediaItems(
|
||||||
|
MediaSession mediaSession, ControllerInfo controller, List<MediaItem> mediaItems) {
|
||||||
|
requestedMediaItems.set(mediaItems);
|
||||||
|
// Resolve MediaItem asynchronously to test correct threading logic.
|
||||||
|
return executorService.submit(() -> ImmutableList.of(resolvedMediaItem));
|
||||||
|
}
|
||||||
|
};
|
||||||
session =
|
session =
|
||||||
new MediaSession.Builder(context, player)
|
new MediaSession.Builder(context, player)
|
||||||
.setId("addQueueItemWithIndex")
|
.setId("addQueueItemWithIndex")
|
||||||
.setCallback(new TestSessionCallback())
|
.setCallback(callback)
|
||||||
.build();
|
.build();
|
||||||
controller =
|
controller =
|
||||||
new RemoteMediaControllerCompat(
|
new RemoteMediaControllerCompat(
|
||||||
context, session.getSessionCompat().getSessionToken(), /* waitForConnection= */ true);
|
context, session.getSessionCompat().getSessionToken(), /* waitForConnection= */ true);
|
||||||
|
|
||||||
List<MediaItem> mediaItems = MediaTestUtils.createMediaItems(/* size= */ 10);
|
|
||||||
handler.postAndSync(
|
handler.postAndSync(
|
||||||
() -> {
|
() -> {
|
||||||
|
List<MediaItem> mediaItems = MediaTestUtils.createMediaItems(/* size= */ 10);
|
||||||
player.setMediaItems(mediaItems);
|
player.setMediaItems(mediaItems);
|
||||||
player.timeline = new PlaylistTimeline(mediaItems);
|
player.timeline = MediaTestUtils.createTimeline(mediaItems);
|
||||||
player.notifyTimelineChanged(Player.TIMELINE_CHANGE_REASON_PLAYLIST_CHANGED);
|
player.notifyTimelineChanged(Player.TIMELINE_CHANGE_REASON_PLAYLIST_CHANGED);
|
||||||
});
|
});
|
||||||
|
|
||||||
// Prepare an item to add.
|
// Prepare an item to add.
|
||||||
int testIndex = 1;
|
int testIndex = 1;
|
||||||
String mediaId = "media_id";
|
String mediaId = "media_id";
|
||||||
MediaDescriptionCompat desc = new MediaDescriptionCompat.Builder().setMediaId(mediaId).build();
|
Uri mediaUri = Uri.parse("https://test.test");
|
||||||
|
MediaDescriptionCompat desc =
|
||||||
|
new MediaDescriptionCompat.Builder().setMediaId(mediaId).setMediaUri(mediaUri).build();
|
||||||
controller.addQueueItem(desc, testIndex);
|
controller.addQueueItem(desc, testIndex);
|
||||||
|
|
||||||
player.awaitMethodCalled(MockPlayer.METHOD_ADD_MEDIA_ITEM_WITH_INDEX, TIMEOUT_MS);
|
player.awaitMethodCalled(MockPlayer.METHOD_ADD_MEDIA_ITEMS_WITH_INDEX, TIMEOUT_MS);
|
||||||
|
assertThat(requestedMediaItems.get()).hasSize(1);
|
||||||
|
assertThat(requestedMediaItems.get().get(0).mediaId).isEqualTo(mediaId);
|
||||||
|
assertThat(requestedMediaItems.get().get(0).requestMetadata.mediaUri).isEqualTo(mediaUri);
|
||||||
assertThat(player.index).isEqualTo(testIndex);
|
assertThat(player.index).isEqualTo(testIndex);
|
||||||
assertThat(player.mediaItems).hasSize(11);
|
assertThat(player.mediaItems).hasSize(11);
|
||||||
assertThat(player.mediaItems.get(1).mediaId).isEqualTo(mediaId);
|
assertThat(player.mediaItems.get(1)).isEqualTo(resolvedMediaItem);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@ -788,16 +835,16 @@ public class MediaSessionCallbackWithMediaControllerCompatTest {
|
|||||||
Uri mediaUri = Uri.parse("foo://bar");
|
Uri mediaUri = Uri.parse("foo://bar");
|
||||||
Bundle bundle = new Bundle();
|
Bundle bundle = new Bundle();
|
||||||
bundle.putString("key", "value");
|
bundle.putString("key", "value");
|
||||||
CountDownLatch latch = new CountDownLatch(1);
|
AtomicReference<List<MediaItem>> requestedMediaItems = new AtomicReference<>();
|
||||||
|
MediaItem resolvedMediaItem = MediaItem.fromUri(TEST_URI);
|
||||||
MediaSession.Callback callback =
|
MediaSession.Callback callback =
|
||||||
new TestSessionCallback() {
|
new MediaSession.Callback() {
|
||||||
@Override
|
@Override
|
||||||
public int onSetMediaUri(
|
public ListenableFuture<List<MediaItem>> onAddMediaItems(
|
||||||
MediaSession session, ControllerInfo controller, Uri uri, Bundle extras) {
|
MediaSession mediaSession, ControllerInfo controller, List<MediaItem> mediaItems) {
|
||||||
assertThat(uri).isEqualTo(mediaUri);
|
requestedMediaItems.set(mediaItems);
|
||||||
assertThat(TestUtils.equals(bundle, extras)).isTrue();
|
// Resolve MediaItem asynchronously to test correct threading logic.
|
||||||
latch.countDown();
|
return executorService.submit(() -> ImmutableList.of(resolvedMediaItem));
|
||||||
return RESULT_SUCCESS;
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
session =
|
session =
|
||||||
@ -811,8 +858,12 @@ public class MediaSessionCallbackWithMediaControllerCompatTest {
|
|||||||
|
|
||||||
controller.getTransportControls().prepareFromUri(mediaUri, bundle);
|
controller.getTransportControls().prepareFromUri(mediaUri, bundle);
|
||||||
|
|
||||||
assertThat(latch.await(TIMEOUT_MS, MILLISECONDS)).isTrue();
|
player.awaitMethodCalled(MockPlayer.METHOD_SET_MEDIA_ITEMS, TIMEOUT_MS);
|
||||||
player.awaitMethodCalled(MockPlayer.METHOD_PREPARE, TIMEOUT_MS);
|
player.awaitMethodCalled(MockPlayer.METHOD_PREPARE, TIMEOUT_MS);
|
||||||
|
assertThat(requestedMediaItems.get()).hasSize(1);
|
||||||
|
assertThat(requestedMediaItems.get().get(0).requestMetadata.mediaUri).isEqualTo(mediaUri);
|
||||||
|
TestUtils.equals(requestedMediaItems.get().get(0).requestMetadata.extras, bundle);
|
||||||
|
assertThat(player.mediaItems).containsExactly(resolvedMediaItem);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@ -820,16 +871,16 @@ public class MediaSessionCallbackWithMediaControllerCompatTest {
|
|||||||
Uri request = Uri.parse("foo://bar");
|
Uri request = Uri.parse("foo://bar");
|
||||||
Bundle bundle = new Bundle();
|
Bundle bundle = new Bundle();
|
||||||
bundle.putString("key", "value");
|
bundle.putString("key", "value");
|
||||||
CountDownLatch latch = new CountDownLatch(1);
|
AtomicReference<List<MediaItem>> requestedMediaItems = new AtomicReference<>();
|
||||||
|
MediaItem resolvedMediaItem = MediaItem.fromUri(TEST_URI);
|
||||||
MediaSession.Callback callback =
|
MediaSession.Callback callback =
|
||||||
new TestSessionCallback() {
|
new MediaSession.Callback() {
|
||||||
@Override
|
@Override
|
||||||
public int onSetMediaUri(
|
public ListenableFuture<List<MediaItem>> onAddMediaItems(
|
||||||
MediaSession session, ControllerInfo controller, Uri uri, Bundle extras) {
|
MediaSession mediaSession, ControllerInfo controller, List<MediaItem> mediaItems) {
|
||||||
assertThat(uri).isEqualTo(request);
|
requestedMediaItems.set(mediaItems);
|
||||||
assertThat(TestUtils.equals(bundle, extras)).isTrue();
|
// Resolve MediaItem asynchronously to test correct threading logic.
|
||||||
latch.countDown();
|
return executorService.submit(() -> ImmutableList.of(resolvedMediaItem));
|
||||||
return RESULT_SUCCESS;
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
session =
|
session =
|
||||||
@ -843,8 +894,13 @@ public class MediaSessionCallbackWithMediaControllerCompatTest {
|
|||||||
|
|
||||||
controller.getTransportControls().playFromUri(request, bundle);
|
controller.getTransportControls().playFromUri(request, bundle);
|
||||||
|
|
||||||
assertThat(latch.await(TIMEOUT_MS, MILLISECONDS)).isTrue();
|
player.awaitMethodCalled(MockPlayer.METHOD_SET_MEDIA_ITEMS, TIMEOUT_MS);
|
||||||
|
player.awaitMethodCalled(MockPlayer.METHOD_PREPARE, TIMEOUT_MS);
|
||||||
player.awaitMethodCalled(MockPlayer.METHOD_PLAY, TIMEOUT_MS);
|
player.awaitMethodCalled(MockPlayer.METHOD_PLAY, TIMEOUT_MS);
|
||||||
|
assertThat(requestedMediaItems.get()).hasSize(1);
|
||||||
|
assertThat(requestedMediaItems.get().get(0).requestMetadata.mediaUri).isEqualTo(request);
|
||||||
|
TestUtils.equals(requestedMediaItems.get().get(0).requestMetadata.extras, bundle);
|
||||||
|
assertThat(player.mediaItems).containsExactly(resolvedMediaItem);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@ -852,17 +908,16 @@ public class MediaSessionCallbackWithMediaControllerCompatTest {
|
|||||||
String request = "media_id";
|
String request = "media_id";
|
||||||
Bundle bundle = new Bundle();
|
Bundle bundle = new Bundle();
|
||||||
bundle.putString("key", "value");
|
bundle.putString("key", "value");
|
||||||
CountDownLatch latch = new CountDownLatch(1);
|
AtomicReference<List<MediaItem>> requestedMediaItems = new AtomicReference<>();
|
||||||
|
MediaItem resolvedMediaItem = MediaItem.fromUri(TEST_URI);
|
||||||
MediaSession.Callback callback =
|
MediaSession.Callback callback =
|
||||||
new TestSessionCallback() {
|
new MediaSession.Callback() {
|
||||||
@Override
|
@Override
|
||||||
public int onSetMediaUri(
|
public ListenableFuture<List<MediaItem>> onAddMediaItems(
|
||||||
MediaSession session, ControllerInfo controller, Uri uri, Bundle extras) {
|
MediaSession mediaSession, ControllerInfo controller, List<MediaItem> mediaItems) {
|
||||||
assertThat(uri.toString())
|
requestedMediaItems.set(mediaItems);
|
||||||
.isEqualTo("androidx://media3-session/prepareFromMediaId?id=" + request);
|
// Resolve MediaItem asynchronously to test correct threading logic.
|
||||||
assertThat(TestUtils.equals(bundle, extras)).isTrue();
|
return executorService.submit(() -> ImmutableList.of(resolvedMediaItem));
|
||||||
latch.countDown();
|
|
||||||
return RESULT_SUCCESS;
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
session =
|
session =
|
||||||
@ -876,8 +931,12 @@ public class MediaSessionCallbackWithMediaControllerCompatTest {
|
|||||||
|
|
||||||
controller.getTransportControls().prepareFromMediaId(request, bundle);
|
controller.getTransportControls().prepareFromMediaId(request, bundle);
|
||||||
|
|
||||||
assertThat(latch.await(TIMEOUT_MS, MILLISECONDS)).isTrue();
|
player.awaitMethodCalled(MockPlayer.METHOD_SET_MEDIA_ITEMS, TIMEOUT_MS);
|
||||||
player.awaitMethodCalled(MockPlayer.METHOD_PREPARE, TIMEOUT_MS);
|
player.awaitMethodCalled(MockPlayer.METHOD_PREPARE, TIMEOUT_MS);
|
||||||
|
assertThat(requestedMediaItems.get()).hasSize(1);
|
||||||
|
assertThat(requestedMediaItems.get().get(0).mediaId).isEqualTo(request);
|
||||||
|
TestUtils.equals(requestedMediaItems.get().get(0).requestMetadata.extras, bundle);
|
||||||
|
assertThat(player.mediaItems).containsExactly(resolvedMediaItem);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@ -885,17 +944,16 @@ public class MediaSessionCallbackWithMediaControllerCompatTest {
|
|||||||
String mediaId = "media_id";
|
String mediaId = "media_id";
|
||||||
Bundle bundle = new Bundle();
|
Bundle bundle = new Bundle();
|
||||||
bundle.putString("key", "value");
|
bundle.putString("key", "value");
|
||||||
CountDownLatch latch = new CountDownLatch(1);
|
AtomicReference<List<MediaItem>> requestedMediaItems = new AtomicReference<>();
|
||||||
|
MediaItem resolvedMediaItem = MediaItem.fromUri(TEST_URI);
|
||||||
MediaSession.Callback callback =
|
MediaSession.Callback callback =
|
||||||
new TestSessionCallback() {
|
new MediaSession.Callback() {
|
||||||
@Override
|
@Override
|
||||||
public int onSetMediaUri(
|
public ListenableFuture<List<MediaItem>> onAddMediaItems(
|
||||||
MediaSession session, ControllerInfo controller, Uri uri, Bundle extras) {
|
MediaSession mediaSession, ControllerInfo controller, List<MediaItem> mediaItems) {
|
||||||
assertThat(uri.toString())
|
requestedMediaItems.set(mediaItems);
|
||||||
.isEqualTo("androidx://media3-session/playFromMediaId?id=" + mediaId);
|
// Resolve MediaItem asynchronously to test correct threading logic.
|
||||||
assertThat(TestUtils.equals(bundle, extras)).isTrue();
|
return executorService.submit(() -> ImmutableList.of(resolvedMediaItem));
|
||||||
latch.countDown();
|
|
||||||
return RESULT_SUCCESS;
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
session =
|
session =
|
||||||
@ -909,8 +967,13 @@ public class MediaSessionCallbackWithMediaControllerCompatTest {
|
|||||||
|
|
||||||
controller.getTransportControls().playFromMediaId(mediaId, bundle);
|
controller.getTransportControls().playFromMediaId(mediaId, bundle);
|
||||||
|
|
||||||
assertThat(latch.await(TIMEOUT_MS, MILLISECONDS)).isTrue();
|
player.awaitMethodCalled(MockPlayer.METHOD_SET_MEDIA_ITEMS, TIMEOUT_MS);
|
||||||
|
player.awaitMethodCalled(MockPlayer.METHOD_PREPARE, TIMEOUT_MS);
|
||||||
player.awaitMethodCalled(MockPlayer.METHOD_PLAY, TIMEOUT_MS);
|
player.awaitMethodCalled(MockPlayer.METHOD_PLAY, TIMEOUT_MS);
|
||||||
|
assertThat(requestedMediaItems.get()).hasSize(1);
|
||||||
|
assertThat(requestedMediaItems.get().get(0).mediaId).isEqualTo(mediaId);
|
||||||
|
TestUtils.equals(requestedMediaItems.get().get(0).requestMetadata.extras, bundle);
|
||||||
|
assertThat(player.mediaItems).containsExactly(resolvedMediaItem);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@ -918,17 +981,16 @@ public class MediaSessionCallbackWithMediaControllerCompatTest {
|
|||||||
String query = "test_query";
|
String query = "test_query";
|
||||||
Bundle bundle = new Bundle();
|
Bundle bundle = new Bundle();
|
||||||
bundle.putString("key", "value");
|
bundle.putString("key", "value");
|
||||||
CountDownLatch latch = new CountDownLatch(1);
|
AtomicReference<List<MediaItem>> requestedMediaItems = new AtomicReference<>();
|
||||||
|
MediaItem resolvedMediaItem = MediaItem.fromUri(TEST_URI);
|
||||||
MediaSession.Callback callback =
|
MediaSession.Callback callback =
|
||||||
new TestSessionCallback() {
|
new MediaSession.Callback() {
|
||||||
@Override
|
@Override
|
||||||
public int onSetMediaUri(
|
public ListenableFuture<List<MediaItem>> onAddMediaItems(
|
||||||
MediaSession session, ControllerInfo controller, Uri uri, Bundle extras) {
|
MediaSession mediaSession, ControllerInfo controller, List<MediaItem> mediaItems) {
|
||||||
assertThat(uri.toString())
|
requestedMediaItems.set(mediaItems);
|
||||||
.isEqualTo("androidx://media3-session/prepareFromSearch?query=" + query);
|
// Resolve MediaItem asynchronously to test correct threading logic.
|
||||||
assertThat(TestUtils.equals(bundle, extras)).isTrue();
|
return executorService.submit(() -> ImmutableList.of(resolvedMediaItem));
|
||||||
latch.countDown();
|
|
||||||
return RESULT_SUCCESS;
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
session =
|
session =
|
||||||
@ -942,8 +1004,12 @@ public class MediaSessionCallbackWithMediaControllerCompatTest {
|
|||||||
|
|
||||||
controller.getTransportControls().prepareFromSearch(query, bundle);
|
controller.getTransportControls().prepareFromSearch(query, bundle);
|
||||||
|
|
||||||
assertThat(latch.await(TIMEOUT_MS, MILLISECONDS)).isTrue();
|
player.awaitMethodCalled(MockPlayer.METHOD_SET_MEDIA_ITEMS, TIMEOUT_MS);
|
||||||
player.awaitMethodCalled(MockPlayer.METHOD_PREPARE, TIMEOUT_MS);
|
player.awaitMethodCalled(MockPlayer.METHOD_PREPARE, TIMEOUT_MS);
|
||||||
|
assertThat(requestedMediaItems.get()).hasSize(1);
|
||||||
|
assertThat(requestedMediaItems.get().get(0).requestMetadata.searchQuery).isEqualTo(query);
|
||||||
|
TestUtils.equals(requestedMediaItems.get().get(0).requestMetadata.extras, bundle);
|
||||||
|
assertThat(player.mediaItems).containsExactly(resolvedMediaItem);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@ -951,17 +1017,16 @@ public class MediaSessionCallbackWithMediaControllerCompatTest {
|
|||||||
String query = "test_query";
|
String query = "test_query";
|
||||||
Bundle bundle = new Bundle();
|
Bundle bundle = new Bundle();
|
||||||
bundle.putString("key", "value");
|
bundle.putString("key", "value");
|
||||||
CountDownLatch latch = new CountDownLatch(1);
|
AtomicReference<List<MediaItem>> requestedMediaItems = new AtomicReference<>();
|
||||||
|
MediaItem resolvedMediaItem = MediaItem.fromUri(TEST_URI);
|
||||||
MediaSession.Callback callback =
|
MediaSession.Callback callback =
|
||||||
new TestSessionCallback() {
|
new MediaSession.Callback() {
|
||||||
@Override
|
@Override
|
||||||
public int onSetMediaUri(
|
public ListenableFuture<List<MediaItem>> onAddMediaItems(
|
||||||
MediaSession session, ControllerInfo controller, Uri uri, Bundle extras) {
|
MediaSession mediaSession, ControllerInfo controller, List<MediaItem> mediaItems) {
|
||||||
assertThat(uri.toString())
|
requestedMediaItems.set(mediaItems);
|
||||||
.isEqualTo("androidx://media3-session/playFromSearch?query=" + query);
|
// Resolve MediaItem asynchronously to test correct threading logic.
|
||||||
assertThat(TestUtils.equals(bundle, extras)).isTrue();
|
return executorService.submit(() -> ImmutableList.of(resolvedMediaItem));
|
||||||
latch.countDown();
|
|
||||||
return RESULT_SUCCESS;
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
session =
|
session =
|
||||||
@ -975,8 +1040,13 @@ public class MediaSessionCallbackWithMediaControllerCompatTest {
|
|||||||
|
|
||||||
controller.getTransportControls().playFromSearch(query, bundle);
|
controller.getTransportControls().playFromSearch(query, bundle);
|
||||||
|
|
||||||
assertThat(latch.await(TIMEOUT_MS, MILLISECONDS)).isTrue();
|
player.awaitMethodCalled(MockPlayer.METHOD_SET_MEDIA_ITEMS, TIMEOUT_MS);
|
||||||
|
player.awaitMethodCalled(MockPlayer.METHOD_PREPARE, TIMEOUT_MS);
|
||||||
player.awaitMethodCalled(MockPlayer.METHOD_PLAY, TIMEOUT_MS);
|
player.awaitMethodCalled(MockPlayer.METHOD_PLAY, TIMEOUT_MS);
|
||||||
|
assertThat(requestedMediaItems.get()).hasSize(1);
|
||||||
|
assertThat(requestedMediaItems.get().get(0).requestMetadata.searchQuery).isEqualTo(query);
|
||||||
|
TestUtils.equals(requestedMediaItems.get().get(0).requestMetadata.extras, bundle);
|
||||||
|
assertThat(player.mediaItems).containsExactly(resolvedMediaItem);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
Loading…
x
Reference in New Issue
Block a user