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.common.util.Util.constrainValue;
|
||||||
import static androidx.media3.session.MediaConstants.EXTRA_KEY_ROOT_CHILDREN_BROWSABLE_ONLY;
|
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.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.MediaMetadataCompat.PREFERRED_DESCRIPTION_ORDER;
|
||||||
import static androidx.media3.session.legacy.MediaSessionCompat.FLAG_HANDLES_QUEUE_COMMANDS;
|
import static androidx.media3.session.legacy.MediaSessionCompat.FLAG_HANDLES_QUEUE_COMMANDS;
|
||||||
import static java.lang.Math.max;
|
import static java.lang.Math.max;
|
||||||
@ -539,6 +540,15 @@ import java.util.concurrent.TimeoutException;
|
|||||||
extras.remove(MediaConstants.EXTRAS_KEY_MEDIA_TYPE_COMPAT);
|
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
|
if (extras != null
|
||||||
&& extras.containsKey(MediaConstants.EXTRAS_KEY_MEDIA_DESCRIPTION_COMPAT_TITLE)) {
|
&& extras.containsKey(MediaConstants.EXTRAS_KEY_MEDIA_DESCRIPTION_COMPAT_TITLE)) {
|
||||||
builder.setTitle(
|
builder.setTitle(
|
||||||
@ -826,6 +836,14 @@ import java.util.concurrent.TimeoutException;
|
|||||||
MediaConstants.EXTRAS_KEY_MEDIA_TYPE_COMPAT, checkNotNull(metadata.mediaType));
|
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 title;
|
||||||
CharSequence subtitle;
|
CharSequence subtitle;
|
||||||
CharSequence description;
|
CharSequence description;
|
||||||
@ -1629,6 +1647,87 @@ import java.util.concurrent.TimeoutException;
|
|||||||
return playbackInfoCompat.getCurrentVolume() == 0;
|
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 {
|
private static byte[] convertToByteArray(Bitmap bitmap) throws IOException {
|
||||||
try (ByteArrayOutputStream stream = new ByteArrayOutputStream()) {
|
try (ByteArrayOutputStream stream = new ByteArrayOutputStream()) {
|
||||||
bitmap.compress(Bitmap.CompressFormat.PNG, /* ignored */ 0, stream);
|
bitmap.compress(Bitmap.CompressFormat.PNG, /* ignored */ 0, stream);
|
||||||
|
@ -15,10 +15,12 @@
|
|||||||
*/
|
*/
|
||||||
package androidx.media3.session;
|
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_BAD_VALUE;
|
||||||
import static androidx.media3.session.SessionError.ERROR_PERMISSION_DENIED;
|
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_SESSION_DISCONNECTED;
|
||||||
import static androidx.media3.session.SessionError.ERROR_UNKNOWN;
|
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.content.Context;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
@ -50,11 +52,11 @@ import org.checkerframework.checker.initialization.qual.UnderInitialization;
|
|||||||
private static final String TAG = "MB2ImplLegacy";
|
private static final String TAG = "MB2ImplLegacy";
|
||||||
|
|
||||||
private final HashMap<LibraryParams, MediaBrowserCompat> browserCompats = new HashMap<>();
|
private final HashMap<LibraryParams, MediaBrowserCompat> browserCompats = new HashMap<>();
|
||||||
|
|
||||||
private final HashMap<String, List<SubscribeCallback>> subscribeCallbacks = new HashMap<>();
|
private final HashMap<String, List<SubscribeCallback>> subscribeCallbacks = new HashMap<>();
|
||||||
|
|
||||||
private final MediaBrowser instance;
|
private final MediaBrowser instance;
|
||||||
|
|
||||||
|
private ImmutableMap<String, CommandButton> commandButtonsForMediaItems;
|
||||||
|
|
||||||
MediaBrowserImplLegacy(
|
MediaBrowserImplLegacy(
|
||||||
Context context,
|
Context context,
|
||||||
@UnderInitialization MediaBrowser instance,
|
@UnderInitialization MediaBrowser instance,
|
||||||
@ -63,6 +65,7 @@ import org.checkerframework.checker.initialization.qual.UnderInitialization;
|
|||||||
BitmapLoader bitmapLoader) {
|
BitmapLoader bitmapLoader) {
|
||||||
super(context, instance, token, applicationLooper, bitmapLoader);
|
super(context, instance, token, applicationLooper, bitmapLoader);
|
||||||
this.instance = instance;
|
this.instance = instance;
|
||||||
|
commandButtonsForMediaItems = ImmutableMap.of();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -91,7 +94,7 @@ import org.checkerframework.checker.initialization.qual.UnderInitialization;
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public ImmutableMap<String, CommandButton> getCommandButtonsForMediaItemsMap() {
|
public ImmutableMap<String, CommandButton> getCommandButtonsForMediaItemsMap() {
|
||||||
return ImmutableMap.of();
|
return commandButtonsForMediaItems;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -376,10 +379,40 @@ import org.checkerframework.checker.initialization.qual.UnderInitialization;
|
|||||||
// Shouldn't be happen. Internal error?
|
// Shouldn't be happen. Internal error?
|
||||||
result.set(LibraryResult.ofError(ERROR_UNKNOWN));
|
result.set(LibraryResult.ofError(ERROR_UNKNOWN));
|
||||||
} else {
|
} 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(
|
result.set(
|
||||||
LibraryResult.ofItem(
|
LibraryResult.ofItem(
|
||||||
createRootMediaItem(browserCompat),
|
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.MediaUtils.TRANSACTION_SIZE_LIMIT_IN_BYTES;
|
||||||
import static androidx.media3.session.legacy.MediaBrowserCompat.EXTRA_PAGE;
|
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.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 static androidx.media3.session.legacy.MediaConstants.BROWSER_SERVICE_EXTRAS_KEY_SEARCH_SUPPORTED;
|
||||||
|
|
||||||
import android.annotation.SuppressLint;
|
import android.annotation.SuppressLint;
|
||||||
@ -126,6 +127,22 @@ import java.util.concurrent.atomic.AtomicReference;
|
|||||||
.isSessionCommandAvailable(controller, SessionCommand.COMMAND_CODE_LIBRARY_SEARCH);
|
.isSessionCommandAvailable(controller, SessionCommand.COMMAND_CODE_LIBRARY_SEARCH);
|
||||||
checkNotNull(extras)
|
checkNotNull(extras)
|
||||||
.putBoolean(BROWSER_SERVICE_EXTRAS_KEY_SEARCH_SUPPORTED, isSearchSessionCommandAvailable);
|
.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);
|
return new BrowserRoot(result.value.mediaId, extras);
|
||||||
}
|
}
|
||||||
// No library root, but keep browser compat connected to allow getting session unless the
|
// 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 String ROOT_EXTRAS_KEY = "root_extras_key";
|
||||||
public static final int ROOT_EXTRAS_VALUE = 4321;
|
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_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_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 MEDIA_ID_GET_ITEM_WITH_METADATA = "media_id_get_item_with_metadata";
|
||||||
|
|
||||||
public static final String PARENT_ID = "parent_id";
|
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 =
|
public static final String TEST_GET_CHILDREN_NON_FATAL_AUTHENTICATION_ERROR =
|
||||||
"getLibraryRoot_nonFatalAuthenticationError_receivesPlaybackException";
|
"getLibraryRoot_nonFatalAuthenticationError_receivesPlaybackException";
|
||||||
public static final String TEST_SEND_CUSTOM_COMMAND = "sendCustomCommand";
|
public static final String TEST_SEND_CUSTOM_COMMAND = "sendCustomCommand";
|
||||||
|
public static final String TEST_MEDIA_ITEMS_WITH_BROWSE_ACTIONS =
|
||||||
|
"getLibraryRoot_withBrowseActions";
|
||||||
|
|
||||||
private MediaBrowserServiceCompatConstants() {}
|
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.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.CONNECTION_HINTS_CUSTOM_LIBRARY_ROOT;
|
||||||
import static androidx.media3.session.MockMediaLibraryService.createNotifyChildrenChangedBundle;
|
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_ALBUM_TITLE;
|
||||||
import static androidx.media3.test.session.common.CommonConstants.METADATA_ARTIST;
|
import static androidx.media3.test.session.common.CommonConstants.METADATA_ARTIST;
|
||||||
import static androidx.media3.test.session.common.CommonConstants.METADATA_ARTWORK_URI;
|
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.GET_CHILDREN_RESULT;
|
||||||
import static androidx.media3.test.session.common.MediaBrowserConstants.LONG_LIST_COUNT;
|
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_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_ITEM_WITH_METADATA;
|
||||||
import static androidx.media3.test.session.common.MediaBrowserConstants.MEDIA_ID_GET_PLAYABLE_ITEM;
|
import static androidx.media3.test.session.common.MediaBrowserConstants.MEDIA_ID_GET_PLAYABLE_ITEM;
|
||||||
import static androidx.media3.test.session.common.MediaBrowserConstants.PARENT_ID;
|
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.MediaDescriptionCompat;
|
||||||
import android.support.v4.media.session.PlaybackStateCompat;
|
import android.support.v4.media.session.PlaybackStateCompat;
|
||||||
import android.text.TextUtils;
|
import android.text.TextUtils;
|
||||||
|
import androidx.media3.test.session.common.MediaBrowserConstants;
|
||||||
import androidx.media3.test.session.common.TestUtils;
|
import androidx.media3.test.session.common.TestUtils;
|
||||||
import androidx.test.ext.junit.runners.AndroidJUnit4;
|
import androidx.test.ext.junit.runners.AndroidJUnit4;
|
||||||
import androidx.test.ext.truth.os.BundleSubject;
|
import androidx.test.ext.truth.os.BundleSubject;
|
||||||
@ -114,6 +117,71 @@ public class MediaBrowserCompatWithMediaLibraryServiceTest
|
|||||||
.getExtras()
|
.getExtras()
|
||||||
.getInt(ROOT_EXTRAS_KEY, /* defaultValue= */ ROOT_EXTRAS_VALUE + 1))
|
.getInt(ROOT_EXTRAS_KEY, /* defaultValue= */ ROOT_EXTRAS_VALUE + 1))
|
||||||
.isEqualTo(ROOT_EXTRAS_VALUE);
|
.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,
|
// Note: Cannot use equals() here because browser compat's extra contains server version,
|
||||||
// extra binder, and extra messenger.
|
// extra binder, and extra messenger.
|
||||||
@ -166,6 +234,35 @@ public class MediaBrowserCompatWithMediaLibraryServiceTest
|
|||||||
assertThat(itemRef.get().getDescription().getIconBitmap()).isNotNull();
|
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
|
@Test
|
||||||
public void getItem_metadata() throws Exception {
|
public void getItem_metadata() throws Exception {
|
||||||
String mediaId = MEDIA_ID_GET_ITEM_WITH_METADATA;
|
String mediaId = MEDIA_ID_GET_ITEM_WITH_METADATA;
|
||||||
@ -295,6 +392,14 @@ public class MediaBrowserCompatWithMediaLibraryServiceTest
|
|||||||
.isEqualTo(EXTRAS_VALUE_COMPLETION_STATUS_PARTIALLY_PLAYED);
|
.isEqualTo(EXTRAS_VALUE_COMPLETION_STATUS_PARTIALLY_PLAYED);
|
||||||
assertThat(mediaItem.getDescription().getIconBitmap()).isNotNull();
|
assertThat(mediaItem.getDescription().getIconBitmap()).isNotNull();
|
||||||
assertThat(onChildrenLoadedWithBundleCalled.get()).isFalse();
|
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_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_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_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_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.MediaBrowserServiceCompatConstants.TEST_SEND_CUSTOM_COMMAND;
|
||||||
import static androidx.media3.test.session.common.TestUtils.TIMEOUT_MS;
|
import static androidx.media3.test.session.common.TestUtils.TIMEOUT_MS;
|
||||||
import static com.google.common.truth.Truth.assertThat;
|
import static com.google.common.truth.Truth.assertThat;
|
||||||
import static com.google.common.util.concurrent.MoreExecutors.directExecutor;
|
import static com.google.common.util.concurrent.MoreExecutors.directExecutor;
|
||||||
|
import static java.util.Objects.requireNonNull;
|
||||||
import static java.util.concurrent.TimeUnit.MILLISECONDS;
|
import static java.util.concurrent.TimeUnit.MILLISECONDS;
|
||||||
import static org.junit.Assert.assertThrows;
|
import static org.junit.Assert.assertThrows;
|
||||||
|
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
|
import android.net.Uri;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import androidx.annotation.Nullable;
|
import androidx.annotation.Nullable;
|
||||||
import androidx.media.MediaBrowserServiceCompat;
|
import androidx.media.MediaBrowserServiceCompat;
|
||||||
import androidx.media3.common.MediaItem;
|
import androidx.media3.common.MediaItem;
|
||||||
|
import androidx.media3.common.MediaMetadata;
|
||||||
import androidx.media3.common.PlaybackException;
|
import androidx.media3.common.PlaybackException;
|
||||||
import androidx.media3.common.Player;
|
import androidx.media3.common.Player;
|
||||||
import androidx.media3.session.MediaLibraryService.LibraryParams;
|
import androidx.media3.session.MediaLibraryService.LibraryParams;
|
||||||
@ -118,6 +122,114 @@ public class MediaBrowserListenerWithMediaBrowserServiceCompatTest {
|
|||||||
assertThat(thrown).hasCauseThat().isInstanceOf(SecurityException.class);
|
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
|
@Test
|
||||||
public void onChildrenChanged_subscribeAndUnsubscribe() throws Exception {
|
public void onChildrenChanged_subscribeAndUnsubscribe() throws Exception {
|
||||||
String testParentId = "testOnChildrenChanged";
|
String testParentId = "testOnChildrenChanged";
|
||||||
@ -358,8 +470,7 @@ public class MediaBrowserListenerWithMediaBrowserServiceCompatTest {
|
|||||||
() ->
|
() ->
|
||||||
browser.sendCustomCommand(
|
browser.sendCustomCommand(
|
||||||
new SessionCommand(
|
new SessionCommand(
|
||||||
MediaBrowserConstants.COMMAND_ACTION_PLAYLIST_ADD,
|
MediaBrowserConstants.COMMAND_PLAYLIST_ADD, /* extras= */ Bundle.EMPTY),
|
||||||
/* extras= */ Bundle.EMPTY),
|
|
||||||
/* args= */ Bundle.EMPTY));
|
/* args= */ Bundle.EMPTY));
|
||||||
Futures.addCallback(
|
Futures.addCallback(
|
||||||
resultFuture,
|
resultFuture,
|
||||||
@ -397,8 +508,7 @@ public class MediaBrowserListenerWithMediaBrowserServiceCompatTest {
|
|||||||
() ->
|
() ->
|
||||||
browser.sendCustomCommand(
|
browser.sendCustomCommand(
|
||||||
new SessionCommand(
|
new SessionCommand(
|
||||||
MediaBrowserConstants.COMMAND_ACTION_PLAYLIST_ADD,
|
MediaBrowserConstants.COMMAND_PLAYLIST_ADD, /* extras= */ Bundle.EMPTY),
|
||||||
/* extras= */ Bundle.EMPTY),
|
|
||||||
args));
|
args));
|
||||||
Futures.addCallback(
|
Futures.addCallback(
|
||||||
resultFuture,
|
resultFuture,
|
||||||
|
@ -61,6 +61,7 @@ import androidx.media3.common.VideoSize;
|
|||||||
import androidx.media3.test.session.R;
|
import androidx.media3.test.session.R;
|
||||||
import androidx.media3.test.session.common.HandlerThreadTestRule;
|
import androidx.media3.test.session.common.HandlerThreadTestRule;
|
||||||
import androidx.media3.test.session.common.MainLooperTestRule;
|
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.PollingCheck;
|
||||||
import androidx.media3.test.session.common.TestUtils;
|
import androidx.media3.test.session.common.TestUtils;
|
||||||
import androidx.test.core.app.ApplicationProvider;
|
import androidx.test.core.app.ApplicationProvider;
|
||||||
@ -557,11 +558,12 @@ public class MediaControllerTest {
|
|||||||
CommandButton playlistAddButton =
|
CommandButton playlistAddButton =
|
||||||
new CommandButton.Builder(CommandButton.ICON_PLAYLIST_ADD)
|
new CommandButton.Builder(CommandButton.ICON_PLAYLIST_ADD)
|
||||||
.setSessionCommand(
|
.setSessionCommand(
|
||||||
new SessionCommand("androidx.media3.actions.playlist_add", Bundle.EMPTY))
|
new SessionCommand(MediaBrowserConstants.COMMAND_PLAYLIST_ADD, Bundle.EMPTY))
|
||||||
.build();
|
.build();
|
||||||
CommandButton radioButton =
|
CommandButton radioButton =
|
||||||
new CommandButton.Builder(CommandButton.ICON_RADIO)
|
new CommandButton.Builder(CommandButton.ICON_RADIO)
|
||||||
.setSessionCommand(new SessionCommand("androidx.media3.actions.radio", Bundle.EMPTY))
|
.setSessionCommand(
|
||||||
|
new SessionCommand(MediaBrowserConstants.COMMAND_RADIO, Bundle.EMPTY))
|
||||||
.build();
|
.build();
|
||||||
MediaItem mediaItem =
|
MediaItem mediaItem =
|
||||||
new MediaItem.Builder()
|
new MediaItem.Builder()
|
||||||
@ -570,8 +572,8 @@ public class MediaControllerTest {
|
|||||||
new MediaMetadata.Builder()
|
new MediaMetadata.Builder()
|
||||||
.setSupportedCommands(
|
.setSupportedCommands(
|
||||||
ImmutableList.of(
|
ImmutableList.of(
|
||||||
"androidx.media3.actions.playlist_add",
|
MediaBrowserConstants.COMMAND_PLAYLIST_ADD,
|
||||||
"androidx.media3.actions.radio",
|
MediaBrowserConstants.COMMAND_RADIO,
|
||||||
"invalid"))
|
"invalid"))
|
||||||
.build())
|
.build())
|
||||||
.build();
|
.build();
|
||||||
@ -595,7 +597,8 @@ public class MediaControllerTest {
|
|||||||
.setMediaId("mediaId-1")
|
.setMediaId("mediaId-1")
|
||||||
.setMediaMetadata(
|
.setMediaMetadata(
|
||||||
new MediaMetadata.Builder()
|
new MediaMetadata.Builder()
|
||||||
.setSupportedCommands(ImmutableList.of("androidx.media3.actions.playlist_add"))
|
.setSupportedCommands(
|
||||||
|
ImmutableList.of(MediaBrowserConstants.COMMAND_PLAYLIST_ADD))
|
||||||
.build())
|
.build())
|
||||||
.build();
|
.build();
|
||||||
CountDownLatch latch = new CountDownLatch(/* count= */ 1);
|
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.common.util.Util;
|
||||||
import androidx.media3.session.MediaSession.ControllerInfo;
|
import androidx.media3.session.MediaSession.ControllerInfo;
|
||||||
import androidx.media3.test.session.common.IRemoteMediaSession;
|
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.MockActivity;
|
||||||
import androidx.media3.test.session.common.TestHandler;
|
import androidx.media3.test.session.common.TestHandler;
|
||||||
import androidx.media3.test.session.common.TestHandler.TestRunnable;
|
import androidx.media3.test.session.common.TestHandler.TestRunnable;
|
||||||
@ -240,12 +241,13 @@ public class MediaSessionProviderService extends Service {
|
|||||||
CommandButton playlistAddButton =
|
CommandButton playlistAddButton =
|
||||||
new CommandButton.Builder(CommandButton.ICON_PLAYLIST_ADD)
|
new CommandButton.Builder(CommandButton.ICON_PLAYLIST_ADD)
|
||||||
.setSessionCommand(
|
.setSessionCommand(
|
||||||
new SessionCommand("androidx.media3.actions.playlist_add", Bundle.EMPTY))
|
new SessionCommand(
|
||||||
|
MediaBrowserConstants.COMMAND_PLAYLIST_ADD, Bundle.EMPTY))
|
||||||
.build();
|
.build();
|
||||||
CommandButton radioButton =
|
CommandButton radioButton =
|
||||||
new CommandButton.Builder(CommandButton.ICON_RADIO)
|
new CommandButton.Builder(CommandButton.ICON_RADIO)
|
||||||
.setSessionCommand(
|
.setSessionCommand(
|
||||||
new SessionCommand("androidx.media3.actions.radio", Bundle.EMPTY))
|
new SessionCommand(MediaBrowserConstants.COMMAND_RADIO, Bundle.EMPTY))
|
||||||
.build();
|
.build();
|
||||||
builder.setCommandButtonsForMediaItems(
|
builder.setCommandButtonsForMediaItems(
|
||||||
ImmutableList.of(playlistAddButton, radioButton));
|
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_FULLY_PLAYED;
|
||||||
import static androidx.media3.session.MediaConstants.EXTRAS_VALUE_COMPLETION_STATUS_NOT_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.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.CommonConstants.SUPPORT_APP_PACKAGE_NAME;
|
||||||
import static androidx.media3.test.session.common.MediaBrowserConstants.ROOT_EXTRAS;
|
import static androidx.media3.test.session.common.MediaBrowserConstants.ROOT_EXTRAS;
|
||||||
import static androidx.media3.test.session.common.MediaBrowserConstants.ROOT_ID;
|
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_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_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_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_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.MediaBrowserServiceCompatConstants.TEST_SEND_CUSTOM_COMMAND;
|
||||||
|
import static java.lang.Math.min;
|
||||||
|
|
||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
import android.net.Uri;
|
import android.net.Uri;
|
||||||
@ -51,6 +56,7 @@ import androidx.media3.test.session.common.MediaBrowserConstants;
|
|||||||
import androidx.media3.test.session.common.MediaBrowserServiceCompatConstants;
|
import androidx.media3.test.session.common.MediaBrowserServiceCompatConstants;
|
||||||
import com.google.common.collect.ImmutableList;
|
import com.google.common.collect.ImmutableList;
|
||||||
import java.lang.reflect.Method;
|
import java.lang.reflect.Method;
|
||||||
|
import java.util.ArrayList;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
@ -276,6 +282,9 @@ public class MockMediaBrowserServiceCompat extends MediaBrowserServiceCompat {
|
|||||||
case TEST_SEND_CUSTOM_COMMAND:
|
case TEST_SEND_CUSTOM_COMMAND:
|
||||||
setProxyForTestSendCustomCommand();
|
setProxyForTestSendCustomCommand();
|
||||||
break;
|
break;
|
||||||
|
case TEST_MEDIA_ITEMS_WITH_BROWSE_ACTIONS:
|
||||||
|
setProxyForMediaItemsWithBrowseActions(session);
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
throw new IllegalArgumentException("Unknown testName: " + testName);
|
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(
|
private void getChildren_authenticationError_receivesPlaybackException(
|
||||||
MediaSessionCompat session, boolean isFatal) {
|
MediaSessionCompat session, boolean isFatal) {
|
||||||
setMediaBrowserServiceProxy(
|
setMediaBrowserServiceProxy(
|
||||||
@ -393,7 +516,7 @@ public class MockMediaBrowserServiceCompat extends MediaBrowserServiceCompat {
|
|||||||
/* playbackSpeed= */ 1.0f)
|
/* playbackSpeed= */ 1.0f)
|
||||||
.addCustomAction(
|
.addCustomAction(
|
||||||
new PlaybackStateCompat.CustomAction.Builder(
|
new PlaybackStateCompat.CustomAction.Builder(
|
||||||
MediaBrowserConstants.COMMAND_ACTION_PLAYLIST_ADD,
|
MediaBrowserConstants.COMMAND_PLAYLIST_ADD,
|
||||||
"Add to playlist",
|
"Add to playlist",
|
||||||
CommandButton.ICON_PLAYLIST_ADD)
|
CommandButton.ICON_PLAYLIST_ADD)
|
||||||
.build())
|
.build())
|
||||||
@ -405,7 +528,7 @@ public class MockMediaBrowserServiceCompat extends MediaBrowserServiceCompat {
|
|||||||
@Override
|
@Override
|
||||||
public void onCustomAction(String action, Bundle extras, Result<Bundle> result) {
|
public void onCustomAction(String action, Bundle extras, Result<Bundle> result) {
|
||||||
Bundle resultBundle = new Bundle();
|
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)) {
|
if (extras.getBoolean("request_error", /* defaultValue= */ false)) {
|
||||||
resultBundle.putString("key-1", "error-from-service");
|
resultBundle.putString("key-1", "error-from-service");
|
||||||
result.sendError(resultBundle);
|
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.GET_CHILDREN_RESULT;
|
||||||
import static androidx.media3.test.session.common.MediaBrowserConstants.LONG_LIST_COUNT;
|
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_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_ITEM_WITH_METADATA;
|
||||||
import static androidx.media3.test.session.common.MediaBrowserConstants.MEDIA_ID_GET_PLAYABLE_ITEM;
|
import static androidx.media3.test.session.common.MediaBrowserConstants.MEDIA_ID_GET_PLAYABLE_ITEM;
|
||||||
import static androidx.media3.test.session.common.MediaBrowserConstants.PARENT_ID;
|
import static androidx.media3.test.session.common.MediaBrowserConstants.PARENT_ID;
|
||||||
@ -67,6 +68,7 @@ import android.app.PendingIntent;
|
|||||||
import android.app.Service;
|
import android.app.Service;
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
|
import android.net.Uri;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.os.HandlerThread;
|
import android.os.HandlerThread;
|
||||||
import android.os.IBinder;
|
import android.os.IBinder;
|
||||||
@ -79,6 +81,7 @@ import androidx.media3.common.util.Log;
|
|||||||
import androidx.media3.common.util.Util;
|
import androidx.media3.common.util.Util;
|
||||||
import androidx.media3.session.MediaSession.ControllerInfo;
|
import androidx.media3.session.MediaSession.ControllerInfo;
|
||||||
import androidx.media3.test.session.common.CommonConstants;
|
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.TestHandler;
|
||||||
import androidx.media3.test.session.common.TestUtils;
|
import androidx.media3.test.session.common.TestUtils;
|
||||||
import com.google.common.collect.ImmutableList;
|
import com.google.common.collect.ImmutableList;
|
||||||
@ -219,6 +222,10 @@ public class MockMediaLibraryService extends MediaLibraryService {
|
|||||||
.getInt(
|
.getInt(
|
||||||
CONNECTION_HINTS_KEY_LIBRARY_ERROR_REPLICATION_MODE,
|
CONNECTION_HINTS_KEY_LIBRARY_ERROR_REPLICATION_MODE,
|
||||||
LIBRARY_ERROR_REPLICATION_MODE_FATAL);
|
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 =
|
session =
|
||||||
new MediaLibrarySession.Builder(
|
new MediaLibrarySession.Builder(
|
||||||
MockMediaLibraryService.this,
|
MockMediaLibraryService.this,
|
||||||
@ -226,6 +233,23 @@ public class MockMediaLibraryService extends MediaLibraryService {
|
|||||||
callback != null ? callback : new TestLibrarySessionCallback())
|
callback != null ? callback : new TestLibrarySessionCallback())
|
||||||
.setId(ID)
|
.setId(ID)
|
||||||
.setLibraryErrorReplicationMode(libraryErrorReplicationMode)
|
.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();
|
.build();
|
||||||
}
|
}
|
||||||
return session;
|
return session;
|
||||||
@ -333,6 +357,10 @@ public class MockMediaLibraryService extends MediaLibraryService {
|
|||||||
return Futures.immediateFuture(
|
return Futures.immediateFuture(
|
||||||
LibraryResult.ofItem(
|
LibraryResult.ofItem(
|
||||||
createPlayableMediaItemWithArtworkData(mediaId), /* params= */ null));
|
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:
|
case MEDIA_ID_GET_ITEM_WITH_METADATA:
|
||||||
return Futures.immediateFuture(
|
return Futures.immediateFuture(
|
||||||
LibraryResult.ofItem(createMediaItemWithMetadata(mediaId), /* params= */ null));
|
LibraryResult.ofItem(createMediaItemWithMetadata(mediaId), /* params= */ null));
|
||||||
@ -589,11 +617,29 @@ public class MockMediaLibraryService extends MediaLibraryService {
|
|||||||
mediaItem
|
mediaItem
|
||||||
.mediaMetadata
|
.mediaMetadata
|
||||||
.buildUpon()
|
.buildUpon()
|
||||||
|
.setSupportedCommands(
|
||||||
|
ImmutableList.of(
|
||||||
|
MediaBrowserConstants.COMMAND_PLAYLIST_ADD,
|
||||||
|
MediaBrowserConstants.COMMAND_RADIO))
|
||||||
.setArtworkData(getArtworkData(), MediaMetadata.PICTURE_TYPE_FRONT_COVER)
|
.setArtworkData(getArtworkData(), MediaMetadata.PICTURE_TYPE_FRONT_COVER)
|
||||||
.build();
|
.build();
|
||||||
return mediaItem.buildUpon().setMediaMetadata(mediaMetadataWithArtwork).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) {
|
private static MediaItem createPlayableMediaItem(String mediaId) {
|
||||||
Bundle extras = new Bundle();
|
Bundle extras = new Bundle();
|
||||||
extras.putInt(EXTRAS_KEY_COMPLETION_STATUS, EXTRAS_VALUE_COMPLETION_STATUS_PARTIALLY_PLAYED);
|
extras.putInt(EXTRAS_KEY_COMPLETION_STATUS, EXTRAS_VALUE_COMPLETION_STATUS_PARTIALLY_PLAYED);
|
||||||
|
Loading…
x
Reference in New Issue
Block a user