Add interoperability for media item commands
See https://developer.android.com/training/cars/media#custom_browse_actions - `MediaMetadata.supportedCommands` is converted to an array list of strings into the extras of `MediaDescriptionCompat` with `DESCRIPTION_EXTRAS_KEY_CUSTOM_BROWSER_ACTION_ID_LIST` and vice versa. - The set of media item command buttons of a session is passed in the root hints to a legacy browser that connects. A Media3 browser connected to a legacy service, gets the set of all commands after calling `getLibraryRoot()`. #cherrypick PiperOrigin-RevId: 678807473
This commit is contained in:
parent
686c3fe7f5
commit
b8ec6b836b
@ -43,6 +43,7 @@ import static androidx.media3.common.util.Assertions.checkNotNull;
|
||||
import static androidx.media3.common.util.Util.constrainValue;
|
||||
import static androidx.media3.session.MediaConstants.EXTRA_KEY_ROOT_CHILDREN_BROWSABLE_ONLY;
|
||||
import static androidx.media3.session.legacy.MediaConstants.BROWSER_ROOT_HINTS_KEY_ROOT_CHILDREN_SUPPORTED_FLAGS;
|
||||
import static androidx.media3.session.legacy.MediaConstants.DESCRIPTION_EXTRAS_KEY_CUSTOM_BROWSER_ACTION_ID_LIST;
|
||||
import static androidx.media3.session.legacy.MediaMetadataCompat.PREFERRED_DESCRIPTION_ORDER;
|
||||
import static androidx.media3.session.legacy.MediaSessionCompat.FLAG_HANDLES_QUEUE_COMMANDS;
|
||||
import static java.lang.Math.max;
|
||||
@ -539,6 +540,15 @@ import java.util.concurrent.TimeoutException;
|
||||
extras.remove(MediaConstants.EXTRAS_KEY_MEDIA_TYPE_COMPAT);
|
||||
}
|
||||
|
||||
if (extras != null
|
||||
&& extras.containsKey(DESCRIPTION_EXTRAS_KEY_CUSTOM_BROWSER_ACTION_ID_LIST)) {
|
||||
builder.setSupportedCommands(
|
||||
ImmutableList.copyOf(
|
||||
checkNotNull(
|
||||
extras.getStringArrayList(
|
||||
DESCRIPTION_EXTRAS_KEY_CUSTOM_BROWSER_ACTION_ID_LIST))));
|
||||
}
|
||||
|
||||
if (extras != null
|
||||
&& extras.containsKey(MediaConstants.EXTRAS_KEY_MEDIA_DESCRIPTION_COMPAT_TITLE)) {
|
||||
builder.setTitle(
|
||||
@ -826,6 +836,14 @@ import java.util.concurrent.TimeoutException;
|
||||
MediaConstants.EXTRAS_KEY_MEDIA_TYPE_COMPAT, checkNotNull(metadata.mediaType));
|
||||
}
|
||||
}
|
||||
if (!metadata.supportedCommands.isEmpty()) {
|
||||
if (extras == null) {
|
||||
extras = new Bundle();
|
||||
}
|
||||
extras.putStringArrayList(
|
||||
DESCRIPTION_EXTRAS_KEY_CUSTOM_BROWSER_ACTION_ID_LIST,
|
||||
new ArrayList<>(metadata.supportedCommands));
|
||||
}
|
||||
CharSequence title;
|
||||
CharSequence subtitle;
|
||||
CharSequence description;
|
||||
@ -1629,6 +1647,87 @@ import java.util.concurrent.TimeoutException;
|
||||
return playbackInfoCompat.getCurrentVolume() == 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts a {@linkplain Bundle custom browse action} to a {@link CommandButton}. Returns null if
|
||||
* the bundle doesn't contain sufficient information to build a command button.
|
||||
*
|
||||
* <p>See <a href="https://developer.android.com/training/cars/media#custom_browse_actions">Custom
|
||||
* Browse Actions for Automotive OS</a>.
|
||||
*
|
||||
* @param browseActionBundle The bundle containing the information of a browse action.
|
||||
* @return The resulting {@link CommandButton} or null.
|
||||
*/
|
||||
@Nullable
|
||||
public static CommandButton convertCustomBrowseActionToCommandButton(Bundle browseActionBundle) {
|
||||
String commandAction =
|
||||
browseActionBundle.getString(
|
||||
androidx.media3.session.legacy.MediaConstants.EXTRAS_KEY_CUSTOM_BROWSER_ACTION_ID);
|
||||
if (commandAction == null) {
|
||||
return null;
|
||||
}
|
||||
@Nullable
|
||||
CommandButton.Builder commandButton =
|
||||
new CommandButton.Builder()
|
||||
.setSessionCommand(new SessionCommand(commandAction, Bundle.EMPTY));
|
||||
String label =
|
||||
browseActionBundle.getString(
|
||||
androidx.media3.session.legacy.MediaConstants.EXTRAS_KEY_CUSTOM_BROWSER_ACTION_LABEL);
|
||||
if (label != null) {
|
||||
commandButton.setDisplayName(label);
|
||||
}
|
||||
String iconUri =
|
||||
browseActionBundle.getString(
|
||||
androidx.media3.session.legacy.MediaConstants
|
||||
.EXTRAS_KEY_CUSTOM_BROWSER_ACTION_ICON_URI);
|
||||
if (iconUri != null) {
|
||||
try {
|
||||
commandButton.setIconUri(Uri.parse(iconUri));
|
||||
} catch (Throwable t) {
|
||||
Log.e(TAG, "error parsing icon URI of legacy browser action " + commandAction, t);
|
||||
}
|
||||
}
|
||||
Bundle actionExtras =
|
||||
browseActionBundle.getBundle(
|
||||
androidx.media3.session.legacy.MediaConstants.EXTRAS_KEY_CUSTOM_BROWSER_ACTION_EXTRAS);
|
||||
if (actionExtras != null) {
|
||||
commandButton.setExtras(actionExtras);
|
||||
}
|
||||
return commandButton.build();
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts a {@link CommandButton} to a {@link Bundle} according to the browse action
|
||||
* specification of Automotive OS.
|
||||
*
|
||||
* <p>See <a href="https://developer.android.com/training/cars/media#custom_browse_actions">Custom
|
||||
* Browse Actions for Automotive OS</a>.
|
||||
*
|
||||
* @param commandButton The {@link CommandButton} to convert.
|
||||
* @return The resulting {@link Bundle}.
|
||||
*/
|
||||
public static Bundle convertToBundle(CommandButton commandButton) {
|
||||
Bundle buttonBundle = new Bundle();
|
||||
if (commandButton.sessionCommand != null) {
|
||||
buttonBundle.putString(
|
||||
androidx.media3.session.legacy.MediaConstants.EXTRAS_KEY_CUSTOM_BROWSER_ACTION_ID,
|
||||
commandButton.sessionCommand.customAction);
|
||||
}
|
||||
buttonBundle.putString(
|
||||
androidx.media3.session.legacy.MediaConstants.EXTRAS_KEY_CUSTOM_BROWSER_ACTION_LABEL,
|
||||
commandButton.displayName.toString());
|
||||
if (commandButton.iconUri != null) {
|
||||
buttonBundle.putString(
|
||||
androidx.media3.session.legacy.MediaConstants.EXTRAS_KEY_CUSTOM_BROWSER_ACTION_ICON_URI,
|
||||
commandButton.iconUri.toString());
|
||||
}
|
||||
if (!commandButton.extras.isEmpty()) {
|
||||
buttonBundle.putBundle(
|
||||
androidx.media3.session.legacy.MediaConstants.EXTRAS_KEY_CUSTOM_BROWSER_ACTION_EXTRAS,
|
||||
commandButton.extras);
|
||||
}
|
||||
return buttonBundle;
|
||||
}
|
||||
|
||||
private static byte[] convertToByteArray(Bitmap bitmap) throws IOException {
|
||||
try (ByteArrayOutputStream stream = new ByteArrayOutputStream()) {
|
||||
bitmap.compress(Bitmap.CompressFormat.PNG, /* ignored */ 0, stream);
|
||||
|
@ -15,10 +15,12 @@
|
||||
*/
|
||||
package androidx.media3.session;
|
||||
|
||||
import static androidx.media3.common.util.Assertions.checkNotNull;
|
||||
import static androidx.media3.session.SessionError.ERROR_BAD_VALUE;
|
||||
import static androidx.media3.session.SessionError.ERROR_PERMISSION_DENIED;
|
||||
import static androidx.media3.session.SessionError.ERROR_SESSION_DISCONNECTED;
|
||||
import static androidx.media3.session.SessionError.ERROR_UNKNOWN;
|
||||
import static androidx.media3.session.legacy.MediaConstants.BROWSER_SERVICE_EXTRAS_KEY_CUSTOM_BROWSER_ACTION_ROOT_LIST;
|
||||
|
||||
import android.content.Context;
|
||||
import android.os.Bundle;
|
||||
@ -50,11 +52,11 @@ import org.checkerframework.checker.initialization.qual.UnderInitialization;
|
||||
private static final String TAG = "MB2ImplLegacy";
|
||||
|
||||
private final HashMap<LibraryParams, MediaBrowserCompat> browserCompats = new HashMap<>();
|
||||
|
||||
private final HashMap<String, List<SubscribeCallback>> subscribeCallbacks = new HashMap<>();
|
||||
|
||||
private final MediaBrowser instance;
|
||||
|
||||
private ImmutableMap<String, CommandButton> commandButtonsForMediaItems;
|
||||
|
||||
MediaBrowserImplLegacy(
|
||||
Context context,
|
||||
@UnderInitialization MediaBrowser instance,
|
||||
@ -63,6 +65,7 @@ import org.checkerframework.checker.initialization.qual.UnderInitialization;
|
||||
BitmapLoader bitmapLoader) {
|
||||
super(context, instance, token, applicationLooper, bitmapLoader);
|
||||
this.instance = instance;
|
||||
commandButtonsForMediaItems = ImmutableMap.of();
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -91,7 +94,7 @@ import org.checkerframework.checker.initialization.qual.UnderInitialization;
|
||||
|
||||
@Override
|
||||
public ImmutableMap<String, CommandButton> getCommandButtonsForMediaItemsMap() {
|
||||
return ImmutableMap.of();
|
||||
return commandButtonsForMediaItems;
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -376,10 +379,40 @@ import org.checkerframework.checker.initialization.qual.UnderInitialization;
|
||||
// Shouldn't be happen. Internal error?
|
||||
result.set(LibraryResult.ofError(ERROR_UNKNOWN));
|
||||
} else {
|
||||
Bundle extras = browserCompat.getExtras();
|
||||
if (extras != null) {
|
||||
ArrayList<Bundle> parcelableArrayList =
|
||||
extras.getParcelableArrayList(
|
||||
BROWSER_SERVICE_EXTRAS_KEY_CUSTOM_BROWSER_ACTION_ROOT_LIST);
|
||||
if (parcelableArrayList != null) {
|
||||
@Nullable
|
||||
ImmutableMap.Builder<String, CommandButton> commandButtonsForMediaItemsBuilder = null;
|
||||
// Converting custom browser action bundles to media item command buttons.
|
||||
for (int i = 0; i < parcelableArrayList.size(); i++) {
|
||||
CommandButton commandButton =
|
||||
LegacyConversions.convertCustomBrowseActionToCommandButton(
|
||||
parcelableArrayList.get(i));
|
||||
if (commandButton != null) {
|
||||
if (commandButtonsForMediaItemsBuilder == null) {
|
||||
// Merge all media item command button of different legacy roots into a single
|
||||
// map. Last wins in case of duplicate action names.
|
||||
commandButtonsForMediaItemsBuilder =
|
||||
new ImmutableMap.Builder<String, CommandButton>()
|
||||
.putAll(commandButtonsForMediaItems);
|
||||
}
|
||||
String customAction = checkNotNull(commandButton.sessionCommand).customAction;
|
||||
commandButtonsForMediaItemsBuilder.put(customAction, commandButton);
|
||||
}
|
||||
}
|
||||
if (commandButtonsForMediaItemsBuilder != null) {
|
||||
commandButtonsForMediaItems = commandButtonsForMediaItemsBuilder.buildKeepingLast();
|
||||
}
|
||||
}
|
||||
}
|
||||
result.set(
|
||||
LibraryResult.ofItem(
|
||||
createRootMediaItem(browserCompat),
|
||||
LegacyConversions.convertToLibraryParams(context, browserCompat.getExtras())));
|
||||
LegacyConversions.convertToLibraryParams(context, extras)));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -23,6 +23,7 @@ import static androidx.media3.session.LibraryResult.RESULT_SUCCESS;
|
||||
import static androidx.media3.session.MediaUtils.TRANSACTION_SIZE_LIMIT_IN_BYTES;
|
||||
import static androidx.media3.session.legacy.MediaBrowserCompat.EXTRA_PAGE;
|
||||
import static androidx.media3.session.legacy.MediaBrowserCompat.EXTRA_PAGE_SIZE;
|
||||
import static androidx.media3.session.legacy.MediaConstants.BROWSER_SERVICE_EXTRAS_KEY_CUSTOM_BROWSER_ACTION_ROOT_LIST;
|
||||
import static androidx.media3.session.legacy.MediaConstants.BROWSER_SERVICE_EXTRAS_KEY_SEARCH_SUPPORTED;
|
||||
|
||||
import android.annotation.SuppressLint;
|
||||
@ -126,6 +127,22 @@ import java.util.concurrent.atomic.AtomicReference;
|
||||
.isSessionCommandAvailable(controller, SessionCommand.COMMAND_CODE_LIBRARY_SEARCH);
|
||||
checkNotNull(extras)
|
||||
.putBoolean(BROWSER_SERVICE_EXTRAS_KEY_SEARCH_SUPPORTED, isSearchSessionCommandAvailable);
|
||||
ImmutableList<CommandButton> commandButtonsForMediaItems =
|
||||
librarySessionImpl.getCommandButtonsForMediaItems();
|
||||
if (!commandButtonsForMediaItems.isEmpty()) {
|
||||
ArrayList<Bundle> browserActionBundles = new ArrayList<>();
|
||||
for (int i = 0; i < commandButtonsForMediaItems.size(); i++) {
|
||||
CommandButton commandButton = commandButtonsForMediaItems.get(i);
|
||||
if (commandButton.sessionCommand != null
|
||||
&& commandButton.sessionCommand.commandCode == SessionCommand.COMMAND_CODE_CUSTOM) {
|
||||
browserActionBundles.add(LegacyConversions.convertToBundle(commandButton));
|
||||
}
|
||||
}
|
||||
if (!browserActionBundles.isEmpty()) {
|
||||
extras.putParcelableArrayList(
|
||||
BROWSER_SERVICE_EXTRAS_KEY_CUSTOM_BROWSER_ACTION_ROOT_LIST, browserActionBundles);
|
||||
}
|
||||
}
|
||||
return new BrowserRoot(result.value.mediaId, extras);
|
||||
}
|
||||
// No library root, but keep browser compat connected to allow getting session unless the
|
||||
|
@ -29,10 +29,13 @@ public class MediaBrowserConstants {
|
||||
public static final String ROOT_EXTRAS_KEY = "root_extras_key";
|
||||
public static final int ROOT_EXTRAS_VALUE = 4321;
|
||||
|
||||
public static final String COMMAND_ACTION_PLAYLIST_ADD = "androidx.media3.actions.playlist_add";
|
||||
public static final String COMMAND_PLAYLIST_ADD = "androidx.media3.commands.playlist_add";
|
||||
public static final String COMMAND_RADIO = "androidx.media3.commands.radio";
|
||||
|
||||
public static final String MEDIA_ID_GET_BROWSABLE_ITEM = "media_id_get_browsable_item";
|
||||
public static final String MEDIA_ID_GET_PLAYABLE_ITEM = "media_id_get_playable_item";
|
||||
public static final String MEDIA_ID_GET_ITEM_WITH_BROWSE_ACTIONS =
|
||||
"media_id_item_with_browse_actions";
|
||||
public static final String MEDIA_ID_GET_ITEM_WITH_METADATA = "media_id_get_item_with_metadata";
|
||||
|
||||
public static final String PARENT_ID = "parent_id";
|
||||
|
@ -30,6 +30,8 @@ public class MediaBrowserServiceCompatConstants {
|
||||
public static final String TEST_GET_CHILDREN_NON_FATAL_AUTHENTICATION_ERROR =
|
||||
"getLibraryRoot_nonFatalAuthenticationError_receivesPlaybackException";
|
||||
public static final String TEST_SEND_CUSTOM_COMMAND = "sendCustomCommand";
|
||||
public static final String TEST_MEDIA_ITEMS_WITH_BROWSE_ACTIONS =
|
||||
"getLibraryRoot_withBrowseActions";
|
||||
|
||||
private MediaBrowserServiceCompatConstants() {}
|
||||
}
|
||||
|
@ -22,6 +22,7 @@ import static androidx.media3.session.MediaLibraryService.MediaLibrarySession.LI
|
||||
import static androidx.media3.session.MediaLibraryService.MediaLibrarySession.LIBRARY_ERROR_REPLICATION_MODE_NON_FATAL;
|
||||
import static androidx.media3.session.MockMediaLibraryService.CONNECTION_HINTS_CUSTOM_LIBRARY_ROOT;
|
||||
import static androidx.media3.session.MockMediaLibraryService.createNotifyChildrenChangedBundle;
|
||||
import static androidx.media3.session.legacy.MediaConstants.DESCRIPTION_EXTRAS_KEY_CUSTOM_BROWSER_ACTION_ID_LIST;
|
||||
import static androidx.media3.test.session.common.CommonConstants.METADATA_ALBUM_TITLE;
|
||||
import static androidx.media3.test.session.common.CommonConstants.METADATA_ARTIST;
|
||||
import static androidx.media3.test.session.common.CommonConstants.METADATA_ARTWORK_URI;
|
||||
@ -37,6 +38,7 @@ import static androidx.media3.test.session.common.MediaBrowserConstants.CUSTOM_A
|
||||
import static androidx.media3.test.session.common.MediaBrowserConstants.GET_CHILDREN_RESULT;
|
||||
import static androidx.media3.test.session.common.MediaBrowserConstants.LONG_LIST_COUNT;
|
||||
import static androidx.media3.test.session.common.MediaBrowserConstants.MEDIA_ID_GET_BROWSABLE_ITEM;
|
||||
import static androidx.media3.test.session.common.MediaBrowserConstants.MEDIA_ID_GET_ITEM_WITH_BROWSE_ACTIONS;
|
||||
import static androidx.media3.test.session.common.MediaBrowserConstants.MEDIA_ID_GET_ITEM_WITH_METADATA;
|
||||
import static androidx.media3.test.session.common.MediaBrowserConstants.MEDIA_ID_GET_PLAYABLE_ITEM;
|
||||
import static androidx.media3.test.session.common.MediaBrowserConstants.PARENT_ID;
|
||||
@ -78,6 +80,7 @@ import android.support.v4.media.MediaBrowserCompat.SubscriptionCallback;
|
||||
import android.support.v4.media.MediaDescriptionCompat;
|
||||
import android.support.v4.media.session.PlaybackStateCompat;
|
||||
import android.text.TextUtils;
|
||||
import androidx.media3.test.session.common.MediaBrowserConstants;
|
||||
import androidx.media3.test.session.common.TestUtils;
|
||||
import androidx.test.ext.junit.runners.AndroidJUnit4;
|
||||
import androidx.test.ext.truth.os.BundleSubject;
|
||||
@ -114,6 +117,71 @@ public class MediaBrowserCompatWithMediaLibraryServiceTest
|
||||
.getExtras()
|
||||
.getInt(ROOT_EXTRAS_KEY, /* defaultValue= */ ROOT_EXTRAS_VALUE + 1))
|
||||
.isEqualTo(ROOT_EXTRAS_VALUE);
|
||||
ArrayList<Bundle> mediaItemCommandButtons =
|
||||
browserCompat
|
||||
.getExtras()
|
||||
.getParcelableArrayList(
|
||||
androidx.media3.session.legacy.MediaConstants
|
||||
.BROWSER_SERVICE_EXTRAS_KEY_CUSTOM_BROWSER_ACTION_ROOT_LIST);
|
||||
assertThat(mediaItemCommandButtons).hasSize(2);
|
||||
assertThat(
|
||||
mediaItemCommandButtons
|
||||
.get(0)
|
||||
.getString(
|
||||
androidx.media3.session.legacy.MediaConstants
|
||||
.EXTRAS_KEY_CUSTOM_BROWSER_ACTION_ID))
|
||||
.isEqualTo(MediaBrowserConstants.COMMAND_PLAYLIST_ADD);
|
||||
assertThat(
|
||||
mediaItemCommandButtons
|
||||
.get(0)
|
||||
.getString(
|
||||
androidx.media3.session.legacy.MediaConstants
|
||||
.EXTRAS_KEY_CUSTOM_BROWSER_ACTION_LABEL))
|
||||
.isEqualTo("Add to playlist");
|
||||
assertThat(
|
||||
mediaItemCommandButtons
|
||||
.get(0)
|
||||
.getString(
|
||||
androidx.media3.session.legacy.MediaConstants
|
||||
.EXTRAS_KEY_CUSTOM_BROWSER_ACTION_ICON_URI))
|
||||
.isEqualTo("http://www.example.com/icon/playlist_add");
|
||||
assertThat(
|
||||
mediaItemCommandButtons
|
||||
.get(0)
|
||||
.getBundle(
|
||||
androidx.media3.session.legacy.MediaConstants
|
||||
.EXTRAS_KEY_CUSTOM_BROWSER_ACTION_EXTRAS)
|
||||
.getString("key-1"))
|
||||
.isEqualTo("playlist_add");
|
||||
assertThat(
|
||||
mediaItemCommandButtons
|
||||
.get(1)
|
||||
.getString(
|
||||
androidx.media3.session.legacy.MediaConstants
|
||||
.EXTRAS_KEY_CUSTOM_BROWSER_ACTION_ID))
|
||||
.isEqualTo(MediaBrowserConstants.COMMAND_RADIO);
|
||||
assertThat(
|
||||
mediaItemCommandButtons
|
||||
.get(1)
|
||||
.getString(
|
||||
androidx.media3.session.legacy.MediaConstants
|
||||
.EXTRAS_KEY_CUSTOM_BROWSER_ACTION_LABEL))
|
||||
.isEqualTo("Radio station");
|
||||
assertThat(
|
||||
mediaItemCommandButtons
|
||||
.get(1)
|
||||
.getString(
|
||||
androidx.media3.session.legacy.MediaConstants
|
||||
.EXTRAS_KEY_CUSTOM_BROWSER_ACTION_ICON_URI))
|
||||
.isEqualTo("http://www.example.com/icon/radio");
|
||||
assertThat(
|
||||
mediaItemCommandButtons
|
||||
.get(1)
|
||||
.getBundle(
|
||||
androidx.media3.session.legacy.MediaConstants
|
||||
.EXTRAS_KEY_CUSTOM_BROWSER_ACTION_EXTRAS)
|
||||
.getString("key-1"))
|
||||
.isEqualTo("radio");
|
||||
|
||||
// Note: Cannot use equals() here because browser compat's extra contains server version,
|
||||
// extra binder, and extra messenger.
|
||||
@ -166,6 +234,35 @@ public class MediaBrowserCompatWithMediaLibraryServiceTest
|
||||
assertThat(itemRef.get().getDescription().getIconBitmap()).isNotNull();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getItem_playableWithBrowseActions_browseActionCorrectlyConverted() throws Exception {
|
||||
String mediaId = MEDIA_ID_GET_ITEM_WITH_BROWSE_ACTIONS;
|
||||
connectAndWait(/* rootHints= */ Bundle.EMPTY);
|
||||
CountDownLatch latch = new CountDownLatch(1);
|
||||
AtomicReference<MediaItem> itemRef = new AtomicReference<>();
|
||||
|
||||
browserCompat.getItem(
|
||||
mediaId,
|
||||
new ItemCallback() {
|
||||
@Override
|
||||
public void onItemLoaded(MediaItem item) {
|
||||
itemRef.set(item);
|
||||
latch.countDown();
|
||||
}
|
||||
});
|
||||
|
||||
assertThat(latch.await(TIMEOUT_MS, MILLISECONDS)).isTrue();
|
||||
assertThat(
|
||||
itemRef
|
||||
.get()
|
||||
.getDescription()
|
||||
.getExtras()
|
||||
.getStringArrayList(DESCRIPTION_EXTRAS_KEY_CUSTOM_BROWSER_ACTION_ID_LIST))
|
||||
.containsExactly(
|
||||
MediaBrowserConstants.COMMAND_PLAYLIST_ADD, MediaBrowserConstants.COMMAND_RADIO)
|
||||
.inOrder();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getItem_metadata() throws Exception {
|
||||
String mediaId = MEDIA_ID_GET_ITEM_WITH_METADATA;
|
||||
@ -295,6 +392,14 @@ public class MediaBrowserCompatWithMediaLibraryServiceTest
|
||||
.isEqualTo(EXTRAS_VALUE_COMPLETION_STATUS_PARTIALLY_PLAYED);
|
||||
assertThat(mediaItem.getDescription().getIconBitmap()).isNotNull();
|
||||
assertThat(onChildrenLoadedWithBundleCalled.get()).isFalse();
|
||||
assertThat(
|
||||
mediaItem
|
||||
.getDescription()
|
||||
.getExtras()
|
||||
.getStringArrayList(DESCRIPTION_EXTRAS_KEY_CUSTOM_BROWSER_ACTION_ID_LIST))
|
||||
.containsExactly(
|
||||
MediaBrowserConstants.COMMAND_PLAYLIST_ADD, MediaBrowserConstants.COMMAND_RADIO)
|
||||
.inOrder();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -31,19 +31,23 @@ import static androidx.media3.test.session.common.MediaBrowserServiceCompatConst
|
||||
import static androidx.media3.test.session.common.MediaBrowserServiceCompatConstants.TEST_GET_CHILDREN_NON_FATAL_AUTHENTICATION_ERROR;
|
||||
import static androidx.media3.test.session.common.MediaBrowserServiceCompatConstants.TEST_GET_CHILDREN_WITH_NULL_LIST;
|
||||
import static androidx.media3.test.session.common.MediaBrowserServiceCompatConstants.TEST_GET_LIBRARY_ROOT;
|
||||
import static androidx.media3.test.session.common.MediaBrowserServiceCompatConstants.TEST_MEDIA_ITEMS_WITH_BROWSE_ACTIONS;
|
||||
import static androidx.media3.test.session.common.MediaBrowserServiceCompatConstants.TEST_ON_CHILDREN_CHANGED_SUBSCRIBE_AND_UNSUBSCRIBE;
|
||||
import static androidx.media3.test.session.common.MediaBrowserServiceCompatConstants.TEST_SEND_CUSTOM_COMMAND;
|
||||
import static androidx.media3.test.session.common.TestUtils.TIMEOUT_MS;
|
||||
import static com.google.common.truth.Truth.assertThat;
|
||||
import static com.google.common.util.concurrent.MoreExecutors.directExecutor;
|
||||
import static java.util.Objects.requireNonNull;
|
||||
import static java.util.concurrent.TimeUnit.MILLISECONDS;
|
||||
import static org.junit.Assert.assertThrows;
|
||||
|
||||
import android.content.Context;
|
||||
import android.net.Uri;
|
||||
import android.os.Bundle;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.media.MediaBrowserServiceCompat;
|
||||
import androidx.media3.common.MediaItem;
|
||||
import androidx.media3.common.MediaMetadata;
|
||||
import androidx.media3.common.PlaybackException;
|
||||
import androidx.media3.common.Player;
|
||||
import androidx.media3.session.MediaLibraryService.LibraryParams;
|
||||
@ -118,6 +122,114 @@ public class MediaBrowserListenerWithMediaBrowserServiceCompatTest {
|
||||
assertThat(thrown).hasCauseThat().isInstanceOf(SecurityException.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getLibraryRoot_browseActionsAvailable() throws Exception {
|
||||
remoteService.setProxyForTest(TEST_MEDIA_ITEMS_WITH_BROWSE_ACTIONS);
|
||||
CommandButton playlistAddButton =
|
||||
new CommandButton.Builder()
|
||||
.setDisplayName("Add to playlist")
|
||||
.setIconUri(Uri.parse("https://www.example.com/icon/playlist_add"))
|
||||
.setSessionCommand(
|
||||
new SessionCommand(MediaBrowserConstants.COMMAND_PLAYLIST_ADD, Bundle.EMPTY))
|
||||
.build();
|
||||
CommandButton radioButton =
|
||||
new CommandButton.Builder()
|
||||
.setDisplayName("Radio station")
|
||||
.setIconUri(Uri.parse("https://www.example.com/icon/radio"))
|
||||
.setSessionCommand(
|
||||
new SessionCommand(MediaBrowserConstants.COMMAND_RADIO, Bundle.EMPTY))
|
||||
.build();
|
||||
MediaItem mediaItem =
|
||||
new MediaItem.Builder()
|
||||
.setMediaId("mediaId")
|
||||
.setMediaMetadata(
|
||||
new MediaMetadata.Builder()
|
||||
.setSupportedCommands(
|
||||
ImmutableList.of(
|
||||
MediaBrowserConstants.COMMAND_PLAYLIST_ADD,
|
||||
MediaBrowserConstants.COMMAND_RADIO,
|
||||
"invalid"))
|
||||
.build())
|
||||
.build();
|
||||
MediaBrowser mediaBrowser = createBrowser(/* listener= */ null);
|
||||
// When connected to a legacy browser service, the library root needs to be requested
|
||||
// before media item commands are available.
|
||||
LibraryResult<MediaItem> libraryResult =
|
||||
threadTestRule
|
||||
.getHandler()
|
||||
.postAndSync(() -> mediaBrowser.getLibraryRoot(new LibraryParams.Builder().build()))
|
||||
.get();
|
||||
assertThat(libraryResult.resultCode).isEqualTo(RESULT_SUCCESS);
|
||||
|
||||
ImmutableList<CommandButton> commandButtons =
|
||||
mediaBrowser.getCommandButtonsForMediaItem(mediaItem);
|
||||
|
||||
assertThat(commandButtons).containsExactly(playlistAddButton, radioButton).inOrder();
|
||||
assertThat(commandButtons.get(0).extras.getString("key-1")).isEqualTo("playlist_add");
|
||||
assertThat(commandButtons.get(1).extras.getString("key-1")).isEqualTo("radio");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getItem_supportedCommandActions_convertedCorrectly() throws Exception {
|
||||
remoteService.setProxyForTest(TEST_MEDIA_ITEMS_WITH_BROWSE_ACTIONS);
|
||||
MediaBrowser mediaBrowser = createBrowser(/* listener= */ null);
|
||||
CommandButton playlistAddButton =
|
||||
new CommandButton.Builder()
|
||||
.setDisplayName("Add to playlist")
|
||||
.setIconUri(Uri.parse("https://www.example.com/icon/playlist_add"))
|
||||
.setSessionCommand(
|
||||
new SessionCommand(MediaBrowserConstants.COMMAND_PLAYLIST_ADD, Bundle.EMPTY))
|
||||
.build();
|
||||
CommandButton radioButton =
|
||||
new CommandButton.Builder()
|
||||
.setDisplayName("Radio station")
|
||||
.setIconUri(Uri.parse("https://www.example.com/icon/radio"))
|
||||
.setSessionCommand(
|
||||
new SessionCommand(MediaBrowserConstants.COMMAND_RADIO, Bundle.EMPTY))
|
||||
.build();
|
||||
// When connected to a legacy browser service, the library root needs to be requested
|
||||
// before media item commands are available.
|
||||
LibraryResult<MediaItem> libraryResult =
|
||||
threadTestRule
|
||||
.getHandler()
|
||||
.postAndSync(() -> mediaBrowser.getLibraryRoot(new LibraryParams.Builder().build()))
|
||||
.get();
|
||||
assertThat(libraryResult.resultCode).isEqualTo(RESULT_SUCCESS);
|
||||
MediaItem mediaItem =
|
||||
threadTestRule.getHandler().postAndSync(() -> mediaBrowser.getItem("mediaId")).get().value;
|
||||
|
||||
ImmutableList<CommandButton> commandButtons =
|
||||
threadTestRule
|
||||
.getHandler()
|
||||
.postAndSync(
|
||||
() -> mediaBrowser.getCommandButtonsForMediaItem(requireNonNull(mediaItem)));
|
||||
|
||||
assertThat(commandButtons).containsExactly(playlistAddButton, radioButton).inOrder();
|
||||
assertThat(commandButtons.get(0).extras.getString("key-1")).isEqualTo("playlist_add");
|
||||
assertThat(commandButtons.get(1).extras.getString("key-1")).isEqualTo("radio");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void sendCustomCommandWithMediaItem_mediaItemIdConvertedCorrectly() throws Exception {
|
||||
remoteService.setProxyForTest(TEST_MEDIA_ITEMS_WITH_BROWSE_ACTIONS);
|
||||
MediaBrowser mediaBrowser = createBrowser(/* listener= */ null);
|
||||
MediaItem mediaItem = new MediaItem.Builder().setMediaId("mediaIdFromCommand").build();
|
||||
|
||||
SessionResult sessionResult =
|
||||
threadTestRule
|
||||
.getHandler()
|
||||
.postAndSync(
|
||||
() ->
|
||||
mediaBrowser.sendCustomCommand(
|
||||
new SessionCommand(MediaBrowserConstants.COMMAND_RADIO, Bundle.EMPTY),
|
||||
mediaItem,
|
||||
/* args= */ Bundle.EMPTY))
|
||||
.get();
|
||||
|
||||
assertThat(sessionResult.extras.getString(MediaConstants.EXTRA_KEY_MEDIA_ID))
|
||||
.isEqualTo("mediaIdFromCommand");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void onChildrenChanged_subscribeAndUnsubscribe() throws Exception {
|
||||
String testParentId = "testOnChildrenChanged";
|
||||
@ -358,8 +470,7 @@ public class MediaBrowserListenerWithMediaBrowserServiceCompatTest {
|
||||
() ->
|
||||
browser.sendCustomCommand(
|
||||
new SessionCommand(
|
||||
MediaBrowserConstants.COMMAND_ACTION_PLAYLIST_ADD,
|
||||
/* extras= */ Bundle.EMPTY),
|
||||
MediaBrowserConstants.COMMAND_PLAYLIST_ADD, /* extras= */ Bundle.EMPTY),
|
||||
/* args= */ Bundle.EMPTY));
|
||||
Futures.addCallback(
|
||||
resultFuture,
|
||||
@ -397,8 +508,7 @@ public class MediaBrowserListenerWithMediaBrowserServiceCompatTest {
|
||||
() ->
|
||||
browser.sendCustomCommand(
|
||||
new SessionCommand(
|
||||
MediaBrowserConstants.COMMAND_ACTION_PLAYLIST_ADD,
|
||||
/* extras= */ Bundle.EMPTY),
|
||||
MediaBrowserConstants.COMMAND_PLAYLIST_ADD, /* extras= */ Bundle.EMPTY),
|
||||
args));
|
||||
Futures.addCallback(
|
||||
resultFuture,
|
||||
|
@ -61,6 +61,7 @@ import androidx.media3.common.VideoSize;
|
||||
import androidx.media3.test.session.R;
|
||||
import androidx.media3.test.session.common.HandlerThreadTestRule;
|
||||
import androidx.media3.test.session.common.MainLooperTestRule;
|
||||
import androidx.media3.test.session.common.MediaBrowserConstants;
|
||||
import androidx.media3.test.session.common.PollingCheck;
|
||||
import androidx.media3.test.session.common.TestUtils;
|
||||
import androidx.test.core.app.ApplicationProvider;
|
||||
@ -557,11 +558,12 @@ public class MediaControllerTest {
|
||||
CommandButton playlistAddButton =
|
||||
new CommandButton.Builder(CommandButton.ICON_PLAYLIST_ADD)
|
||||
.setSessionCommand(
|
||||
new SessionCommand("androidx.media3.actions.playlist_add", Bundle.EMPTY))
|
||||
new SessionCommand(MediaBrowserConstants.COMMAND_PLAYLIST_ADD, Bundle.EMPTY))
|
||||
.build();
|
||||
CommandButton radioButton =
|
||||
new CommandButton.Builder(CommandButton.ICON_RADIO)
|
||||
.setSessionCommand(new SessionCommand("androidx.media3.actions.radio", Bundle.EMPTY))
|
||||
.setSessionCommand(
|
||||
new SessionCommand(MediaBrowserConstants.COMMAND_RADIO, Bundle.EMPTY))
|
||||
.build();
|
||||
MediaItem mediaItem =
|
||||
new MediaItem.Builder()
|
||||
@ -570,8 +572,8 @@ public class MediaControllerTest {
|
||||
new MediaMetadata.Builder()
|
||||
.setSupportedCommands(
|
||||
ImmutableList.of(
|
||||
"androidx.media3.actions.playlist_add",
|
||||
"androidx.media3.actions.radio",
|
||||
MediaBrowserConstants.COMMAND_PLAYLIST_ADD,
|
||||
MediaBrowserConstants.COMMAND_RADIO,
|
||||
"invalid"))
|
||||
.build())
|
||||
.build();
|
||||
@ -595,7 +597,8 @@ public class MediaControllerTest {
|
||||
.setMediaId("mediaId-1")
|
||||
.setMediaMetadata(
|
||||
new MediaMetadata.Builder()
|
||||
.setSupportedCommands(ImmutableList.of("androidx.media3.actions.playlist_add"))
|
||||
.setSupportedCommands(
|
||||
ImmutableList.of(MediaBrowserConstants.COMMAND_PLAYLIST_ADD))
|
||||
.build())
|
||||
.build();
|
||||
CountDownLatch latch = new CountDownLatch(/* count= */ 1);
|
||||
|
@ -103,6 +103,7 @@ import androidx.media3.common.util.Log;
|
||||
import androidx.media3.common.util.Util;
|
||||
import androidx.media3.session.MediaSession.ControllerInfo;
|
||||
import androidx.media3.test.session.common.IRemoteMediaSession;
|
||||
import androidx.media3.test.session.common.MediaBrowserConstants;
|
||||
import androidx.media3.test.session.common.MockActivity;
|
||||
import androidx.media3.test.session.common.TestHandler;
|
||||
import androidx.media3.test.session.common.TestHandler.TestRunnable;
|
||||
@ -240,12 +241,13 @@ public class MediaSessionProviderService extends Service {
|
||||
CommandButton playlistAddButton =
|
||||
new CommandButton.Builder(CommandButton.ICON_PLAYLIST_ADD)
|
||||
.setSessionCommand(
|
||||
new SessionCommand("androidx.media3.actions.playlist_add", Bundle.EMPTY))
|
||||
new SessionCommand(
|
||||
MediaBrowserConstants.COMMAND_PLAYLIST_ADD, Bundle.EMPTY))
|
||||
.build();
|
||||
CommandButton radioButton =
|
||||
new CommandButton.Builder(CommandButton.ICON_RADIO)
|
||||
.setSessionCommand(
|
||||
new SessionCommand("androidx.media3.actions.radio", Bundle.EMPTY))
|
||||
new SessionCommand(MediaBrowserConstants.COMMAND_RADIO, Bundle.EMPTY))
|
||||
.build();
|
||||
builder.setCommandButtonsForMediaItems(
|
||||
ImmutableList.of(playlistAddButton, radioButton));
|
||||
|
@ -19,6 +19,9 @@ import static androidx.media3.session.MediaConstants.EXTRAS_KEY_COMPLETION_STATU
|
||||
import static androidx.media3.session.MediaConstants.EXTRAS_VALUE_COMPLETION_STATUS_FULLY_PLAYED;
|
||||
import static androidx.media3.session.MediaConstants.EXTRAS_VALUE_COMPLETION_STATUS_NOT_PLAYED;
|
||||
import static androidx.media3.session.MediaConstants.EXTRAS_VALUE_COMPLETION_STATUS_PARTIALLY_PLAYED;
|
||||
import static androidx.media3.session.legacy.MediaConstants.BROWSER_ROOT_HINTS_KEY_CUSTOM_BROWSER_ACTION_LIMIT;
|
||||
import static androidx.media3.session.legacy.MediaConstants.BROWSER_SERVICE_EXTRAS_KEY_CUSTOM_BROWSER_ACTION_ROOT_LIST;
|
||||
import static androidx.media3.session.legacy.MediaConstants.DESCRIPTION_EXTRAS_KEY_CUSTOM_BROWSER_ACTION_ID_LIST;
|
||||
import static androidx.media3.test.session.common.CommonConstants.SUPPORT_APP_PACKAGE_NAME;
|
||||
import static androidx.media3.test.session.common.MediaBrowserConstants.ROOT_EXTRAS;
|
||||
import static androidx.media3.test.session.common.MediaBrowserConstants.ROOT_ID;
|
||||
@ -29,8 +32,10 @@ import static androidx.media3.test.session.common.MediaBrowserServiceCompatConst
|
||||
import static androidx.media3.test.session.common.MediaBrowserServiceCompatConstants.TEST_GET_CHILDREN_NON_FATAL_AUTHENTICATION_ERROR;
|
||||
import static androidx.media3.test.session.common.MediaBrowserServiceCompatConstants.TEST_GET_CHILDREN_WITH_NULL_LIST;
|
||||
import static androidx.media3.test.session.common.MediaBrowserServiceCompatConstants.TEST_GET_LIBRARY_ROOT;
|
||||
import static androidx.media3.test.session.common.MediaBrowserServiceCompatConstants.TEST_MEDIA_ITEMS_WITH_BROWSE_ACTIONS;
|
||||
import static androidx.media3.test.session.common.MediaBrowserServiceCompatConstants.TEST_ON_CHILDREN_CHANGED_SUBSCRIBE_AND_UNSUBSCRIBE;
|
||||
import static androidx.media3.test.session.common.MediaBrowserServiceCompatConstants.TEST_SEND_CUSTOM_COMMAND;
|
||||
import static java.lang.Math.min;
|
||||
|
||||
import android.content.Intent;
|
||||
import android.net.Uri;
|
||||
@ -51,6 +56,7 @@ import androidx.media3.test.session.common.MediaBrowserConstants;
|
||||
import androidx.media3.test.session.common.MediaBrowserServiceCompatConstants;
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
@ -276,6 +282,9 @@ public class MockMediaBrowserServiceCompat extends MediaBrowserServiceCompat {
|
||||
case TEST_SEND_CUSTOM_COMMAND:
|
||||
setProxyForTestSendCustomCommand();
|
||||
break;
|
||||
case TEST_MEDIA_ITEMS_WITH_BROWSE_ACTIONS:
|
||||
setProxyForMediaItemsWithBrowseActions(session);
|
||||
break;
|
||||
default:
|
||||
throw new IllegalArgumentException("Unknown testName: " + testName);
|
||||
}
|
||||
@ -341,6 +350,120 @@ public class MockMediaBrowserServiceCompat extends MediaBrowserServiceCompat {
|
||||
});
|
||||
}
|
||||
|
||||
private void setProxyForMediaItemsWithBrowseActions(MediaSessionCompat session) {
|
||||
// See https://developer.android.com/training/cars/media#custom_browse_actions
|
||||
|
||||
Bundle playlistAddBrowseAction = new Bundle();
|
||||
Bundle playlistAddExtras = new Bundle();
|
||||
playlistAddExtras.putString("key-1", "playlist_add");
|
||||
playlistAddBrowseAction.putString(
|
||||
androidx.media3.session.legacy.MediaConstants.EXTRAS_KEY_CUSTOM_BROWSER_ACTION_ID,
|
||||
MediaBrowserConstants.COMMAND_PLAYLIST_ADD);
|
||||
playlistAddBrowseAction.putString(
|
||||
androidx.media3.session.legacy.MediaConstants.EXTRAS_KEY_CUSTOM_BROWSER_ACTION_LABEL,
|
||||
"Add to playlist");
|
||||
playlistAddBrowseAction.putString(
|
||||
androidx.media3.session.legacy.MediaConstants.EXTRAS_KEY_CUSTOM_BROWSER_ACTION_ICON_URI,
|
||||
"https://www.example.com/icon/playlist_add");
|
||||
playlistAddBrowseAction.putBundle(
|
||||
androidx.media3.session.legacy.MediaConstants.EXTRAS_KEY_CUSTOM_BROWSER_ACTION_EXTRAS,
|
||||
playlistAddExtras);
|
||||
Bundle radioBrowseAction = new Bundle();
|
||||
Bundle radioExtras = new Bundle();
|
||||
radioExtras.putString("key-1", "radio");
|
||||
radioBrowseAction.putString(
|
||||
androidx.media3.session.legacy.MediaConstants.EXTRAS_KEY_CUSTOM_BROWSER_ACTION_ID,
|
||||
MediaBrowserConstants.COMMAND_RADIO);
|
||||
radioBrowseAction.putString(
|
||||
androidx.media3.session.legacy.MediaConstants.EXTRAS_KEY_CUSTOM_BROWSER_ACTION_LABEL,
|
||||
"Radio station");
|
||||
radioBrowseAction.putString(
|
||||
androidx.media3.session.legacy.MediaConstants.EXTRAS_KEY_CUSTOM_BROWSER_ACTION_ICON_URI,
|
||||
"https://www.example.com/icon/radio");
|
||||
radioBrowseAction.putBundle(
|
||||
androidx.media3.session.legacy.MediaConstants.EXTRAS_KEY_CUSTOM_BROWSER_ACTION_EXTRAS,
|
||||
radioExtras);
|
||||
|
||||
ImmutableList<Bundle> browseActions =
|
||||
ImmutableList.of(playlistAddBrowseAction, radioBrowseAction);
|
||||
setMediaBrowserServiceProxy(
|
||||
new MockMediaBrowserServiceCompat.Proxy() {
|
||||
@Override
|
||||
public BrowserRoot onGetRoot(
|
||||
String clientPackageName, int clientUid, Bundle rootHints) {
|
||||
int actionLimit =
|
||||
rootHints.getInt(
|
||||
BROWSER_ROOT_HINTS_KEY_CUSTOM_BROWSER_ACTION_LIMIT,
|
||||
/* defaultValue= */ browseActions.size());
|
||||
Bundle extras = new Bundle(rootHints);
|
||||
ArrayList<Bundle> browseActionList = new ArrayList<>();
|
||||
for (int i = 0; i < min(actionLimit, browseActions.size()); i++) {
|
||||
browseActionList.add(browseActions.get(i));
|
||||
}
|
||||
extras.putParcelableArrayList(
|
||||
BROWSER_SERVICE_EXTRAS_KEY_CUSTOM_BROWSER_ACTION_ROOT_LIST, browseActionList);
|
||||
|
||||
session.setPlaybackState(
|
||||
new PlaybackStateCompat.Builder()
|
||||
.setState(
|
||||
PlaybackStateCompat.STATE_PLAYING,
|
||||
/* position= */ 123L,
|
||||
/* playbackSpeed= */ 1.0f)
|
||||
.addCustomAction(
|
||||
new PlaybackStateCompat.CustomAction.Builder(
|
||||
MediaBrowserConstants.COMMAND_PLAYLIST_ADD,
|
||||
"Add to playlist",
|
||||
CommandButton.ICON_PLAYLIST_ADD)
|
||||
.build())
|
||||
.addCustomAction(
|
||||
new PlaybackStateCompat.CustomAction.Builder(
|
||||
MediaBrowserConstants.COMMAND_RADIO,
|
||||
"Radio station",
|
||||
CommandButton.ICON_RADIO)
|
||||
.build())
|
||||
.build());
|
||||
|
||||
return new BrowserRoot(ROOT_ID, extras);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onLoadItem(String itemId, Result<MediaItem> result) {
|
||||
Bundle extras = new Bundle();
|
||||
ArrayList<String> supportedActions = new ArrayList<>();
|
||||
supportedActions.add(MediaBrowserConstants.COMMAND_PLAYLIST_ADD);
|
||||
supportedActions.add(MediaBrowserConstants.COMMAND_RADIO);
|
||||
extras.putStringArrayList(
|
||||
DESCRIPTION_EXTRAS_KEY_CUSTOM_BROWSER_ACTION_ID_LIST, supportedActions);
|
||||
MediaDescriptionCompat description =
|
||||
new MediaDescriptionCompat.Builder()
|
||||
.setMediaId(itemId)
|
||||
.setExtras(extras)
|
||||
.setTitle("title of " + itemId)
|
||||
.build();
|
||||
result.sendResult(new MediaItem(description, MediaItem.FLAG_PLAYABLE));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCustomAction(String action, Bundle extras, Result<Bundle> result) {
|
||||
if (action.equals(MediaBrowserConstants.COMMAND_PLAYLIST_ADD)
|
||||
|| action.equals(MediaBrowserConstants.COMMAND_RADIO)) {
|
||||
Bundle resultBundle = new Bundle();
|
||||
if (extras.containsKey(
|
||||
androidx.media3.session.legacy.MediaConstants
|
||||
.EXTRAS_KEY_CUSTOM_BROWSER_ACTION_MEDIA_ITEM_ID)) {
|
||||
resultBundle.putString(
|
||||
MediaConstants.EXTRA_KEY_MEDIA_ID,
|
||||
extras.getString(
|
||||
androidx.media3.session.legacy.MediaConstants
|
||||
.EXTRAS_KEY_CUSTOM_BROWSER_ACTION_MEDIA_ITEM_ID));
|
||||
}
|
||||
session.setExtras(resultBundle);
|
||||
result.sendResult(resultBundle);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void getChildren_authenticationError_receivesPlaybackException(
|
||||
MediaSessionCompat session, boolean isFatal) {
|
||||
setMediaBrowserServiceProxy(
|
||||
@ -393,7 +516,7 @@ public class MockMediaBrowserServiceCompat extends MediaBrowserServiceCompat {
|
||||
/* playbackSpeed= */ 1.0f)
|
||||
.addCustomAction(
|
||||
new PlaybackStateCompat.CustomAction.Builder(
|
||||
MediaBrowserConstants.COMMAND_ACTION_PLAYLIST_ADD,
|
||||
MediaBrowserConstants.COMMAND_PLAYLIST_ADD,
|
||||
"Add to playlist",
|
||||
CommandButton.ICON_PLAYLIST_ADD)
|
||||
.build())
|
||||
@ -405,7 +528,7 @@ public class MockMediaBrowserServiceCompat extends MediaBrowserServiceCompat {
|
||||
@Override
|
||||
public void onCustomAction(String action, Bundle extras, Result<Bundle> result) {
|
||||
Bundle resultBundle = new Bundle();
|
||||
if (action.equals(MediaBrowserConstants.COMMAND_ACTION_PLAYLIST_ADD)) {
|
||||
if (action.equals(MediaBrowserConstants.COMMAND_PLAYLIST_ADD)) {
|
||||
if (extras.getBoolean("request_error", /* defaultValue= */ false)) {
|
||||
resultBundle.putString("key-1", "error-from-service");
|
||||
result.sendError(resultBundle);
|
||||
|
@ -38,6 +38,7 @@ import static androidx.media3.test.session.common.MediaBrowserConstants.EXTRAS_K
|
||||
import static androidx.media3.test.session.common.MediaBrowserConstants.GET_CHILDREN_RESULT;
|
||||
import static androidx.media3.test.session.common.MediaBrowserConstants.LONG_LIST_COUNT;
|
||||
import static androidx.media3.test.session.common.MediaBrowserConstants.MEDIA_ID_GET_BROWSABLE_ITEM;
|
||||
import static androidx.media3.test.session.common.MediaBrowserConstants.MEDIA_ID_GET_ITEM_WITH_BROWSE_ACTIONS;
|
||||
import static androidx.media3.test.session.common.MediaBrowserConstants.MEDIA_ID_GET_ITEM_WITH_METADATA;
|
||||
import static androidx.media3.test.session.common.MediaBrowserConstants.MEDIA_ID_GET_PLAYABLE_ITEM;
|
||||
import static androidx.media3.test.session.common.MediaBrowserConstants.PARENT_ID;
|
||||
@ -67,6 +68,7 @@ import android.app.PendingIntent;
|
||||
import android.app.Service;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.net.Uri;
|
||||
import android.os.Bundle;
|
||||
import android.os.HandlerThread;
|
||||
import android.os.IBinder;
|
||||
@ -79,6 +81,7 @@ import androidx.media3.common.util.Log;
|
||||
import androidx.media3.common.util.Util;
|
||||
import androidx.media3.session.MediaSession.ControllerInfo;
|
||||
import androidx.media3.test.session.common.CommonConstants;
|
||||
import androidx.media3.test.session.common.MediaBrowserConstants;
|
||||
import androidx.media3.test.session.common.TestHandler;
|
||||
import androidx.media3.test.session.common.TestUtils;
|
||||
import com.google.common.collect.ImmutableList;
|
||||
@ -219,6 +222,10 @@ public class MockMediaLibraryService extends MediaLibraryService {
|
||||
.getInt(
|
||||
CONNECTION_HINTS_KEY_LIBRARY_ERROR_REPLICATION_MODE,
|
||||
LIBRARY_ERROR_REPLICATION_MODE_FATAL);
|
||||
Bundle playlistAddExtras = new Bundle();
|
||||
playlistAddExtras.putString("key-1", "playlist_add");
|
||||
Bundle radioExtras = new Bundle();
|
||||
radioExtras.putString("key-1", "radio");
|
||||
session =
|
||||
new MediaLibrarySession.Builder(
|
||||
MockMediaLibraryService.this,
|
||||
@ -226,6 +233,23 @@ public class MockMediaLibraryService extends MediaLibraryService {
|
||||
callback != null ? callback : new TestLibrarySessionCallback())
|
||||
.setId(ID)
|
||||
.setLibraryErrorReplicationMode(libraryErrorReplicationMode)
|
||||
.setCommandButtonsForMediaItems(
|
||||
ImmutableList.of(
|
||||
new CommandButton.Builder(CommandButton.ICON_PLAYLIST_ADD)
|
||||
.setDisplayName("Add to playlist")
|
||||
.setIconUri(Uri.parse("http://www.example.com/icon/playlist_add"))
|
||||
.setSessionCommand(
|
||||
new SessionCommand(
|
||||
MediaBrowserConstants.COMMAND_PLAYLIST_ADD, Bundle.EMPTY))
|
||||
.setExtras(playlistAddExtras)
|
||||
.build(),
|
||||
new CommandButton.Builder(CommandButton.ICON_RADIO)
|
||||
.setDisplayName("Radio station")
|
||||
.setIconUri(Uri.parse("http://www.example.com/icon/radio"))
|
||||
.setSessionCommand(
|
||||
new SessionCommand(MediaBrowserConstants.COMMAND_RADIO, Bundle.EMPTY))
|
||||
.setExtras(radioExtras)
|
||||
.build()))
|
||||
.build();
|
||||
}
|
||||
return session;
|
||||
@ -333,6 +357,10 @@ public class MockMediaLibraryService extends MediaLibraryService {
|
||||
return Futures.immediateFuture(
|
||||
LibraryResult.ofItem(
|
||||
createPlayableMediaItemWithArtworkData(mediaId), /* params= */ null));
|
||||
case MEDIA_ID_GET_ITEM_WITH_BROWSE_ACTIONS:
|
||||
return Futures.immediateFuture(
|
||||
LibraryResult.ofItem(
|
||||
createPlayableMediaItemWithBrowseActions(mediaId), /* params= */ null));
|
||||
case MEDIA_ID_GET_ITEM_WITH_METADATA:
|
||||
return Futures.immediateFuture(
|
||||
LibraryResult.ofItem(createMediaItemWithMetadata(mediaId), /* params= */ null));
|
||||
@ -589,11 +617,29 @@ public class MockMediaLibraryService extends MediaLibraryService {
|
||||
mediaItem
|
||||
.mediaMetadata
|
||||
.buildUpon()
|
||||
.setSupportedCommands(
|
||||
ImmutableList.of(
|
||||
MediaBrowserConstants.COMMAND_PLAYLIST_ADD,
|
||||
MediaBrowserConstants.COMMAND_RADIO))
|
||||
.setArtworkData(getArtworkData(), MediaMetadata.PICTURE_TYPE_FRONT_COVER)
|
||||
.build();
|
||||
return mediaItem.buildUpon().setMediaMetadata(mediaMetadataWithArtwork).build();
|
||||
}
|
||||
|
||||
private MediaItem createPlayableMediaItemWithBrowseActions(String mediaId) {
|
||||
MediaItem mediaItem = createPlayableMediaItem(mediaId);
|
||||
MediaMetadata mediaMetadataWithBrowseActions =
|
||||
mediaItem
|
||||
.mediaMetadata
|
||||
.buildUpon()
|
||||
.setSupportedCommands(
|
||||
ImmutableList.of(
|
||||
MediaBrowserConstants.COMMAND_PLAYLIST_ADD,
|
||||
MediaBrowserConstants.COMMAND_RADIO))
|
||||
.build();
|
||||
return mediaItem.buildUpon().setMediaMetadata(mediaMetadataWithBrowseActions).build();
|
||||
}
|
||||
|
||||
private static MediaItem createPlayableMediaItem(String mediaId) {
|
||||
Bundle extras = new Bundle();
|
||||
extras.putInt(EXTRAS_KEY_COMPLETION_STATUS, EXTRAS_VALUE_COMPLETION_STATUS_PARTIALLY_PLAYED);
|
||||
|
Loading…
x
Reference in New Issue
Block a user