Migrate media constants from androidx.media.util.MediaConstants

Adds root extras and metadata extras to MockMediaLibraryService and MockMediaBrowserCompatService and completed test cases for asserting
interoperability with a media1 or Media3 browser.

PiperOrigin-RevId: 480854842
This commit is contained in:
bachinger 2022-10-13 11:18:46 +00:00 committed by Marc Baechinger
parent 7e8f0f0918
commit 006a519a0e
9 changed files with 583 additions and 49 deletions

View File

@ -40,7 +40,7 @@ project.ext {
androidxConstraintLayoutVersion = '2.0.4'
androidxCoreVersion = '1.7.0'
androidxFuturesVersion = '1.1.0'
androidxMediaVersion = '1.4.3'
androidxMediaVersion = '1.6.0'
androidxMedia2Version = '1.2.0'
androidxMultidexVersion = '2.0.1'
androidxRecyclerViewVersion = '1.2.1'

View File

@ -15,18 +15,77 @@
*/
package androidx.media3.session;
import android.app.PendingIntent;
import android.content.Intent;
import android.os.Bundle;
import android.os.Parcelable;
import android.support.v4.media.session.MediaControllerCompat;
import android.support.v4.media.session.MediaSessionCompat;
import android.support.v4.media.session.PlaybackStateCompat;
import androidx.media3.common.MediaItem;
import androidx.media3.common.MediaMetadata;
import androidx.media3.common.util.UnstableApi;
import androidx.media3.session.MediaLibraryService.LibraryParams;
import androidx.media3.session.MediaLibraryService.MediaLibrarySession;
/** Constants that can be shared between media session and controller. */
public final class MediaConstants {
/**
* Bundle key to indicate a preference that a region of space for the skip to next control should
* always be blocked out in the UI, even when the seek to next standard action is not supported.
* The legacy error code for expired authentication.
*
* <p>Use this error code to indicate an expired authentication when {@linkplain
* LibraryResult#ofError(int, LibraryParams) creating a library result} for an unsuccessful
* service call.
*
* @see PlaybackStateCompat#ERROR_CODE_AUTHENTICATION_EXPIRED
*/
public static final int ERROR_CODE_AUTHENTICATION_EXPIRED_COMPAT = 3;
/**
* The extras key for the localized error resolution string.
*
* <p>Use this key to populate the extras bundle of the {@link LibraryParams} when {@linkplain
* LibraryResult#ofError(int, LibraryParams) creating a library result} for an unsuccessful
* service call.
*/
public static final String EXTRAS_KEY_ERROR_RESOLUTION_ACTION_LABEL_COMPAT =
androidx.media.utils.MediaConstants.PLAYBACK_STATE_EXTRAS_KEY_ERROR_RESOLUTION_ACTION_LABEL;
/**
* The extras key for the error resolution intent.
*
* <p>Use this key to populate the extras bundle of the {@link LibraryParams} when {@linkplain
* LibraryResult#ofError(int, LibraryParams) creating a library result} for an unsuccessful
* service call.
*/
public static final String EXTRAS_KEY_ERROR_RESOLUTION_ACTION_INTENT_COMPAT =
androidx.media.utils.MediaConstants.PLAYBACK_STATE_EXTRAS_KEY_ERROR_RESOLUTION_ACTION_INTENT;
/**
* {@link Bundle} key used to store a {@link PendingIntent}. When launched, the {@link
* PendingIntent} should allow users to resolve the current playback state error.
*
* <p>Applications must also set the error message and {@link
* #EXTRAS_KEY_ERROR_RESOLUTION_ACTION_LABEL_COMPAT} for cases in which the intent cannot be auto
* launched.
*
* <p>Use this key to populate the extras bundle of the {@link LibraryParams} when {@linkplain
* LibraryResult#ofError(int, LibraryParams) creating a library result} for an unsuccessful
* service call. Must be inserted {@linkplain Bundle#putParcelable(String, Parcelable) into the
* bundle as a parcelable}.
*
* <p>TYPE: {@link PendingIntent}.
*/
@UnstableApi
public static final String EXTRAS_KEY_ERROR_RESOLUTION_USING_CAR_APP_LIBRARY_INTENT_COMPAT =
androidx.media.utils.MediaConstants
.PLAYBACK_STATE_EXTRAS_KEY_ERROR_RESOLUTION_USING_CAR_APP_LIBRARY_INTENT;
/**
* {@link Bundle} key to indicate a preference that a region of space for the skip to next control
* should always be blocked out in the UI, even when the seek to next standard action is not
* supported.
*
* <p>This may be used when the session temporarily disallows {@link
* androidx.media3.common.Player#COMMAND_SEEK_TO_NEXT} by design.
@ -39,12 +98,12 @@ public final class MediaConstants {
* @see androidx.media3.common.Player#COMMAND_SEEK_TO_NEXT_MEDIA_ITEM
*/
public static final String EXTRAS_KEY_SLOT_RESERVATION_SEEK_TO_NEXT =
"android.media.playback.ALWAYS_RESERVE_SPACE_FOR.ACTION_SKIP_TO_NEXT";
androidx.media.utils.MediaConstants.SESSION_EXTRAS_KEY_SLOT_RESERVATION_SKIP_TO_NEXT;
/**
* Bundle key to indicate a preference that a region of space for the skip to previous control
* should always be blocked out in the UI, even when the seek to previous standard action is not
* supported.
* {@link Bundle} key to indicate a preference that a region of space for the skip to previous
* control should always be blocked out in the UI, even when the seek to previous standard action
* is not supported.
*
* <p>This may be used when the session temporarily disallows {@link
* androidx.media3.common.Player#COMMAND_SEEK_TO_PREVIOUS} by design.
@ -57,43 +116,309 @@ public final class MediaConstants {
* @see androidx.media3.common.Player#COMMAND_SEEK_TO_PREVIOUS_MEDIA_ITEM
*/
public static final String EXTRAS_KEY_SLOT_RESERVATION_SEEK_TO_PREV =
"android.media.playback.ALWAYS_RESERVE_SPACE_FOR.ACTION_SKIP_TO_PREVIOUS";
androidx.media.utils.MediaConstants.SESSION_EXTRAS_KEY_SLOT_RESERVATION_SKIP_TO_PREV;
/**
* The extras key for the localized error resolution string.
* {@link Bundle} key used in {@link MediaMetadata#extras} to indicate the playback completion
* status of the corresponding {@link MediaItem}.
*
* <p>Use this key to populate the extras bundle of the {@link LibraryParams} when {@link
* LibraryResult#ofError(int, LibraryParams) creating a LibraryResult} for an unsuccessful service
* call.
* <p>TYPE: int. Possible values are separate constants.
*
* @see
* androidx.media.utils.MediaConstants#PLAYBACK_STATE_EXTRAS_KEY_ERROR_RESOLUTION_ACTION_LABEL
* @see MediaMetadata.Builder#setExtras(Bundle)
* @see MediaMetadata#extras
* @see #EXTRAS_VALUE_COMPLETION_STATUS_NOT_PLAYED
* @see #EXTRAS_VALUE_COMPLETION_STATUS_PARTIALLY_PLAYED
* @see #EXTRAS_VALUE_COMPLETION_STATUS_FULLY_PLAYED
*/
public static final String EXTRAS_KEY_ERROR_RESOLUTION_ACTION_LABEL_COMPAT =
"android.media.extras.ERROR_RESOLUTION_ACTION_LABEL";
/**
* The extras key for the error resolution intent.
*
* <p>Use this key to populate the extras bundle of the {@link LibraryParams} when {@link
* LibraryResult#ofError(int, LibraryParams) creating a LibraryResult} for an unsuccessful service
* call.
*
* @see
* androidx.media.utils.MediaConstants#PLAYBACK_STATE_EXTRAS_KEY_ERROR_RESOLUTION_ACTION_INTENT
*/
public static final String EXTRAS_KEY_ERROR_RESOLUTION_ACTION_INTENT_COMPAT =
"android.media.extras.ERROR_RESOLUTION_ACTION_INTENT";
@UnstableApi
public static final String EXTRAS_KEY_COMPLETION_STATUS =
androidx.media.utils.MediaConstants.DESCRIPTION_EXTRAS_KEY_COMPLETION_STATUS;
/**
* The legacy error code for expired authentication.
* {@link Bundle} value used in {@link MediaMetadata#extras} to indicate that the corresponding
* {@link MediaItem} has not been played by the user.
*
* <p>Use this error code to indicate an expired authentication when {@link
* LibraryResult#ofError(int, LibraryParams) creating a LibraryResult} for an unsuccessful service
* call.
*
* @see PlaybackStateCompat#ERROR_CODE_AUTHENTICATION_EXPIRED
* @see MediaMetadata.Builder#setExtras(Bundle)
* @see MediaMetadata#extras
* @see #EXTRAS_KEY_COMPLETION_STATUS
*/
public static final int ERROR_CODE_AUTHENTICATION_EXPIRED_COMPAT = 3;
@UnstableApi
public static final int EXTRAS_VALUE_COMPLETION_STATUS_NOT_PLAYED =
androidx.media.utils.MediaConstants.DESCRIPTION_EXTRAS_VALUE_COMPLETION_STATUS_NOT_PLAYED;
/**
* {@link Bundle} value used in {@link MediaMetadata#extras} to indicate that the corresponding
* {@link MediaItem} has been partially played by the user.
*
* @see MediaMetadata.Builder#setExtras(Bundle)
* @see MediaMetadata#extras
* @see #EXTRAS_KEY_COMPLETION_STATUS
*/
@UnstableApi
public static final int EXTRAS_VALUE_COMPLETION_STATUS_PARTIALLY_PLAYED =
androidx.media.utils.MediaConstants
.DESCRIPTION_EXTRAS_VALUE_COMPLETION_STATUS_PARTIALLY_PLAYED;
/**
* {@link Bundle} value used in {@link MediaMetadata#extras} to indicate that the corresponding
* {@link MediaItem} has been fully played by the user.
*
* @see MediaMetadata.Builder#setExtras(Bundle)
* @see MediaMetadata#extras
* @see #EXTRAS_KEY_COMPLETION_STATUS
*/
@UnstableApi
public static final int EXTRAS_VALUE_COMPLETION_STATUS_FULLY_PLAYED =
androidx.media.utils.MediaConstants.DESCRIPTION_EXTRAS_VALUE_COMPLETION_STATUS_FULLY_PLAYED;
/**
* {@link Bundle} key used in {@link MediaMetadata#extras} to indicate an amount of completion
* progress for the corresponding {@link MediaItem}. This extra augments {@link
* #EXTRAS_VALUE_COMPLETION_STATUS_PARTIALLY_PLAYED the partially played status} by indicating how
* much has been played by the user.
*
* <p>TYPE: double, a value between 0.0 and 1.0, inclusive. 0.0 indicates no completion progress
* (item is not started) and 1.0 indicates full completion progress (item is fully played). Values
* in between indicate partial progress (for example, 0.75 indicates the item is 75% complete).
*
* @see MediaMetadata.Builder#setExtras(Bundle)
* @see MediaMetadata#extras
*/
@UnstableApi
public static final String EXTRAS_KEY_COMPLETION_PERCENTAGE =
androidx.media.utils.MediaConstants.DESCRIPTION_EXTRAS_KEY_COMPLETION_PERCENTAGE;
/**
* {@link Bundle} key used to indicate a preference about how playable instances of {@link
* MediaItem} are presented.
*
* <p>If exposed through {@link LibraryParams#extras} of the {@link LibraryResult} returned by
* {@link MediaBrowser#getLibraryRoot}, the preference applies to all playable items within the
* browse tree.
*
* <p>If exposed through {@link MediaMetadata#extras} of a {@linkplain MediaMetadata#folderType
* browsable media item}, the preference applies to only the immediate playable children. It takes
* precedence over preferences received with {@link MediaBrowser#getLibraryRoot}.
*
* <p>TYPE: int. Possible values are separate constants.
*
* @see MediaBrowser#getLibraryRoot(LibraryParams)
* @see LibraryResult#params
* @see MediaMetadata.Builder#setExtras(Bundle)
* @see MediaMetadata#extras
* @see #EXTRAS_VALUE_CONTENT_STYLE_LIST_ITEM
* @see #EXTRAS_VALUE_CONTENT_STYLE_GRID_ITEM
*/
@UnstableApi
public static final String EXTRAS_KEY_CONTENT_STYLE_PLAYABLE =
androidx.media.utils.MediaConstants.DESCRIPTION_EXTRAS_KEY_CONTENT_STYLE_PLAYABLE;
/**
* {@link Bundle} key used to indicate a preference about how browsable instances of {@link
* MediaItem} are presented.
*
* <p>If exposed through {@link LibraryParams#extras} of the {@link LibraryResult} returned by
* {@link MediaBrowser#getLibraryRoot}, the preference applies to all browsable items within the
* browse tree.
*
* <p>If exposed through {@link MediaMetadata#extras} of a {@linkplain MediaMetadata#folderType
* browsable media item}, the preference applies to only the immediate browsable children. It
* takes precedence over preferences received with {@link
* MediaBrowser#getLibraryRoot(LibraryParams)}.
*
* <p>TYPE: int. Possible values are separate constants.
*
* @see MediaBrowser#getLibraryRoot(LibraryParams)
* @see LibraryResult#params
* @see MediaMetadata.Builder#setExtras(Bundle)
* @see MediaMetadata#extras
* @see #EXTRAS_VALUE_CONTENT_STYLE_LIST_ITEM
* @see #EXTRAS_VALUE_CONTENT_STYLE_GRID_ITEM
* @see #EXTRAS_VALUE_CONTENT_STYLE_CATEGORY_LIST_ITEM
* @see #EXTRAS_VALUE_CONTENT_STYLE_CATEGORY_GRID_ITEM
*/
@UnstableApi
public static final String EXTRAS_KEY_CONTENT_STYLE_BROWSABLE =
androidx.media.utils.MediaConstants.DESCRIPTION_EXTRAS_KEY_CONTENT_STYLE_BROWSABLE;
/**
* {@link Bundle} key used in {@link MediaMetadata#extras} to indicate a preference about how the
* corresponding {@link MediaItem} is presented.
*
* <p>This preference takes precedence over those expressed by {@link
* #EXTRAS_KEY_CONTENT_STYLE_PLAYABLE} and {@link #EXTRAS_KEY_CONTENT_STYLE_BROWSABLE}.
*
* <p>TYPE: int. Possible values are separate constants.
*
* @see MediaMetadata.Builder#setExtras(Bundle)
* @see MediaMetadata#extras
* @see #EXTRAS_VALUE_CONTENT_STYLE_LIST_ITEM
* @see #EXTRAS_VALUE_CONTENT_STYLE_GRID_ITEM
* @see #EXTRAS_VALUE_CONTENT_STYLE_CATEGORY_LIST_ITEM
* @see #EXTRAS_VALUE_CONTENT_STYLE_CATEGORY_GRID_ITEM
*/
@UnstableApi
public static final String EXTRAS_KEY_CONTENT_STYLE_SINGLE_ITEM =
androidx.media.utils.MediaConstants.DESCRIPTION_EXTRAS_KEY_CONTENT_STYLE_SINGLE_ITEM;
/**
* {@link Bundle} value used in {@link MediaMetadata#extras} to indicate a preference that certain
* instances of {@link MediaItem} should be presented as list items.
*
* @see MediaMetadata.Builder#setExtras(Bundle)
* @see MediaMetadata#extras
* @see #EXTRAS_KEY_CONTENT_STYLE_BROWSABLE
* @see #EXTRAS_KEY_CONTENT_STYLE_PLAYABLE
*/
@UnstableApi
public static final int EXTRAS_VALUE_CONTENT_STYLE_LIST_ITEM =
androidx.media.utils.MediaConstants.DESCRIPTION_EXTRAS_VALUE_CONTENT_STYLE_LIST_ITEM;
/**
* {@link Bundle} value used in {@link MediaMetadata#extras} to indicate a preference that certain
* instances of {@link MediaItem} should be presented as grid items.
*
* @see MediaMetadata.Builder#setExtras(Bundle)
* @see MediaMetadata#extras
* @see #EXTRAS_KEY_CONTENT_STYLE_BROWSABLE
* @see #EXTRAS_KEY_CONTENT_STYLE_PLAYABLE
*/
@UnstableApi
public static final int EXTRAS_VALUE_CONTENT_STYLE_GRID_ITEM =
androidx.media.utils.MediaConstants.DESCRIPTION_EXTRAS_VALUE_CONTENT_STYLE_GRID_ITEM;
/**
* {@link Bundle} value used in {@link MediaMetadata#extras} to indicate a preference that
* browsable instances of {@link MediaItem} should be presented as "category" list items. This
* means the items provide icons that render well when they do <strong>not</strong> fill all of
* the available area.
*
* @see MediaMetadata.Builder#setExtras(Bundle)
* @see MediaMetadata#extras
* @see #EXTRAS_KEY_CONTENT_STYLE_BROWSABLE
*/
@UnstableApi
public static final int EXTRAS_VALUE_CONTENT_STYLE_CATEGORY_LIST_ITEM =
androidx.media.utils.MediaConstants.DESCRIPTION_EXTRAS_VALUE_CONTENT_STYLE_CATEGORY_LIST_ITEM;
/**
* {@link Bundle} value used in {@link MediaMetadata#extras} to indicate a preference that
* browsable instances of {@link MediaItem} should be presented as "category" grid items. This
* means the items provide icons that render well when they do <strong>not</strong> fill all of
* the available area.
*
* @see MediaMetadata.Builder#setExtras(Bundle)
* @see MediaMetadata#extras
* @see #EXTRAS_KEY_CONTENT_STYLE_BROWSABLE
*/
@UnstableApi
public static final int EXTRAS_VALUE_CONTENT_STYLE_CATEGORY_GRID_ITEM =
androidx.media.utils.MediaConstants.DESCRIPTION_EXTRAS_VALUE_CONTENT_STYLE_CATEGORY_GRID_ITEM;
/**
* {@link Bundle} key used in {@link MediaMetadata#extras} to indicate that certain instances of
* {@link MediaItem} are related as a group, with a title that is specified through the bundle
* value. Items that are children of the same browsable node and have the same title are members
* of the same group. An app may present a group's items as a contiguous block and display the
* title alongside the group.
*
* <p>TYPE: String. Should be human readable and localized.
*
* @see MediaMetadata.Builder#setExtras(Bundle)
* @see MediaMetadata#extras
*/
@UnstableApi
public static final String EXTRAS_KEY_CONTENT_STYLE_GROUP_TITLE =
androidx.media.utils.MediaConstants.DESCRIPTION_EXTRAS_KEY_CONTENT_STYLE_GROUP_TITLE;
/**
* {@link Bundle} key used in {@link MediaMetadata#extras} to indicate that the corresponding
* {@link MediaItem} has explicit content (that is, user discretion is advised when viewing or
* listening to this content).
*
* <p>TYPE: long (to enable, use value {@link #EXTRAS_VALUE_ATTRIBUTE_PRESENT})
*
* @see MediaMetadata.Builder#setExtras(Bundle)
* @see MediaMetadata#extras
*/
@UnstableApi public static final String EXTRAS_KEY_IS_EXPLICIT = "android.media.IS_EXPLICIT";
/**
* {@link Bundle} key used in {@link MediaMetadata#extras} to indicate that the corresponding
* {@link MediaItem} is an advertisement.
*
* <p>TYPE: long (to enable, use value {@link #EXTRAS_VALUE_ATTRIBUTE_PRESENT})
*
* @see MediaMetadata.Builder#setExtras(Bundle)
* @see MediaMetadata#extras
*/
@UnstableApi
public static final String EXTRAS_KEY_IS_ADVERTISEMENT =
androidx.media.utils.MediaConstants.METADATA_KEY_IS_ADVERTISEMENT;
/**
* {@link Bundle} value used to indicate the presence of an attribute described by its
* corresponding key.
*/
@UnstableApi public static final long EXTRAS_VALUE_ATTRIBUTE_PRESENT = 1L;
/**
* {@link Bundle} key used in {@link LibraryParams#extras} passed to {@link
* MediaLibrarySession.Callback#onGetLibraryRoot(MediaLibrarySession, MediaSession.ControllerInfo,
* LibraryParams)} to indicate the maximum number of children of the root node that can be
* supported by the {@link MediaBrowser}. Excess root children may be omitted or made less
* discoverable.
*
* <p>TYPE: int
*
* @see MediaLibrarySession.Callback#onGetLibraryRoot(MediaLibrarySession,
* MediaSession.ControllerInfo, LibraryParams)
* @see LibraryParams#extras
*/
@UnstableApi
public static final String EXTRAS_KEY_ROOT_CHILDREN_LIMIT =
androidx.media.utils.MediaConstants.BROWSER_ROOT_HINTS_KEY_ROOT_CHILDREN_LIMIT;
/**
* {@link Bundle} key used in {@link LibraryParams#extras} passed by the {@link MediaBrowser} as
* root hints to {@link MediaLibrarySession.Callback#onGetLibraryRoot(MediaLibrarySession,
* MediaSession.ControllerInfo, LibraryParams)} to indicate the recommended size, in pixels, for
* media art bitmaps. Much smaller images may not render well, and much larger images may cause
* inefficient resource consumption.
*
* @see MediaBrowser#getLibraryRoot(LibraryParams)
* @see MediaLibrarySession.Callback#onGetLibraryRoot(MediaLibrarySession,
* MediaSession.ControllerInfo, LibraryParams)
* @see LibraryParams#extras
*/
@UnstableApi
public static final String EXTRAS_KEY_MEDIA_ART_SIZE_PIXELS =
androidx.media.utils.MediaConstants.BROWSER_ROOT_HINTS_KEY_MEDIA_ART_SIZE_PIXELS;
/**
* {@link Bundle} key used to indicate that the media app that provides the service supports
* showing a settings page.
*
* <p>Use this key to populate the {@link LibraryParams#extras} of the {@link LibraryResult}
* returned by {@link MediaLibrarySession.Callback#onGetLibraryRoot(MediaLibrarySession,
* MediaSession.ControllerInfo, LibraryParams)}. Use this key with {@link
* Bundle#putParcelable(String, Parcelable)} to put a {@link PendingIntent} that is created using
* {@code CarPendingIntent#getCarApp()}.
*
* <p>The {@link Intent} carried by the pending intent needs to have the component name set to a
* <a href="http://developer.android.com/training/cars/apps#create-carappservice">Car App Library
* service</a> that needs to exist in the same application package as the media browser service.
*
* <p>TYPE: {@link PendingIntent}.
*
* @see MediaLibrarySession.Callback#onGetLibraryRoot(MediaLibrarySession,
* MediaSession.ControllerInfo, LibraryParams)
* @see LibraryParams#extras
*/
@UnstableApi
public static final String EXTRAS_KEY_APPLICATION_PREFERENCES_USING_CAR_APP_LIBRARY_INTENT =
androidx.media.utils.MediaConstants
.BROWSER_SERVICE_EXTRAS_KEY_APPLICATION_PREFERENCES_USING_CAR_APP_LIBRARY_INTENT;
/* package */ static final String SESSION_COMMAND_ON_CAPTIONING_ENABLED_CHANGED =
"androidx.media3.session.SESSION_COMMAND_ON_CAPTIONING_ENABLED_CHANGED";

View File

@ -26,6 +26,8 @@ public class MediaBrowserConstants {
public static final String ROOT_ID = "rootId";
public static final Bundle ROOT_EXTRAS = new Bundle();
public static final String ROOT_EXTRAS_KEY = "root_extras_key";
public static final int ROOT_EXTRAS_VALUE = 4321;
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";
@ -71,7 +73,7 @@ public class MediaBrowserConstants {
public static final String CUSTOM_ACTION_ASSERT_PARAMS = "assertParams";
static {
ROOT_EXTRAS.putString(ROOT_ID, ROOT_ID);
ROOT_EXTRAS.putInt(ROOT_EXTRAS_KEY, ROOT_EXTRAS_VALUE);
CUSTOM_ACTION_EXTRAS.putString(CUSTOM_ACTION, CUSTOM_ACTION);

View File

@ -24,6 +24,8 @@ public class MediaBrowserServiceCompatConstants {
public static final String TEST_CONNECT_REJECTED = "testConnect_rejected";
public static final String TEST_ON_CHILDREN_CHANGED_SUBSCRIBE_AND_UNSUBSCRIBE =
"testOnChildrenChanged_subscribeAndUnsubscribe";
public static final String TEST_GET_LIBRARY_ROOT = "getLibraryRoot_correctExtraKeyAndValue";
public static final String TEST_GET_CHILDREN = "getChildren_correctMetadataExtras";
private MediaBrowserServiceCompatConstants() {}
}

View File

@ -15,6 +15,8 @@
*/
package androidx.media3.session;
import static androidx.media3.session.MediaConstants.EXTRAS_KEY_COMPLETION_STATUS;
import static androidx.media3.session.MediaConstants.EXTRAS_VALUE_COMPLETION_STATUS_PARTIALLY_PLAYED;
import static androidx.media3.test.session.common.CommonConstants.METADATA_ARTWORK_URI;
import static androidx.media3.test.session.common.CommonConstants.METADATA_DESCRIPTION;
import static androidx.media3.test.session.common.CommonConstants.METADATA_EXTRA_KEY;
@ -38,6 +40,8 @@ import static androidx.media3.test.session.common.MediaBrowserConstants.PARENT_I
import static androidx.media3.test.session.common.MediaBrowserConstants.PARENT_ID_LONG_LIST;
import static androidx.media3.test.session.common.MediaBrowserConstants.PARENT_ID_NO_CHILDREN;
import static androidx.media3.test.session.common.MediaBrowserConstants.ROOT_EXTRAS;
import static androidx.media3.test.session.common.MediaBrowserConstants.ROOT_EXTRAS_KEY;
import static androidx.media3.test.session.common.MediaBrowserConstants.ROOT_EXTRAS_VALUE;
import static androidx.media3.test.session.common.MediaBrowserConstants.ROOT_ID;
import static androidx.media3.test.session.common.MediaBrowserConstants.SEARCH_QUERY;
import static androidx.media3.test.session.common.MediaBrowserConstants.SEARCH_QUERY_EMPTY_RESULT;
@ -67,6 +71,7 @@ import androidx.media3.test.session.common.TestUtils;
import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.ext.truth.os.BundleSubject;
import androidx.test.filters.LargeTest;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.atomic.AtomicReference;
@ -96,6 +101,11 @@ public class MediaBrowserCompatWithMediaLibraryServiceTest
});
connectAndWait();
assertThat(browserCompat.getRoot()).isEqualTo(ROOT_ID);
assertThat(
browserCompat
.getExtras()
.getInt(ROOT_EXTRAS_KEY, /* defaultValue= */ ROOT_EXTRAS_VALUE + 1))
.isEqualTo(ROOT_EXTRAS_VALUE);
// Note: Cannot use equals() here because browser compat's extra contains server version,
// extra binder, and extra messenger.
@ -202,22 +212,18 @@ public class MediaBrowserCompatWithMediaLibraryServiceTest
@Test
public void getChildren() throws InterruptedException {
String testParentId = PARENT_ID;
connectAndWait();
CountDownLatch latch = new CountDownLatch(1);
List<MediaItem> receivedChildren = new ArrayList<>();
final String[] receivedParentId = new String[1];
browserCompat.subscribe(
testParentId,
new SubscriptionCallback() {
@Override
public void onChildrenLoaded(String parentId, List<MediaItem> children) {
assertThat(parentId).isEqualTo(testParentId);
assertThat(children).isNotNull();
assertThat(children.size()).isEqualTo(GET_CHILDREN_RESULT.size());
// Compare the given results with originals.
for (int i = 0; i < children.size(); i++) {
assertThat(children.get(i).getMediaId()).isEqualTo(GET_CHILDREN_RESULT.get(i));
}
receivedParentId[0] = parentId;
receivedChildren.addAll(children);
latch.countDown();
}
@ -226,7 +232,23 @@ public class MediaBrowserCompatWithMediaLibraryServiceTest
assertWithMessage("").fail();
}
});
assertThat(latch.await(TIMEOUT_MS, MILLISECONDS)).isTrue();
assertThat(receivedParentId[0]).isEqualTo(testParentId);
assertThat(receivedChildren).hasSize(GET_CHILDREN_RESULT.size());
// Compare the given results with originals.
for (int i = 0; i < receivedChildren.size(); i++) {
MediaItem mediaItem = receivedChildren.get(i);
assertThat(mediaItem.getMediaId()).isEqualTo(GET_CHILDREN_RESULT.get(i));
assertThat(
mediaItem
.getDescription()
.getExtras()
.getInt(
EXTRAS_KEY_COMPLETION_STATUS,
/* defaultValue= */ EXTRAS_VALUE_COMPLETION_STATUS_PARTIALLY_PLAYED + 1))
.isEqualTo(EXTRAS_VALUE_COMPLETION_STATUS_PARTIALLY_PLAYED);
}
}
@Test

View File

@ -17,12 +17,17 @@ package androidx.media3.session;
import static androidx.media3.session.LibraryResult.RESULT_ERROR_BAD_VALUE;
import static androidx.media3.session.LibraryResult.RESULT_SUCCESS;
import static androidx.media3.session.MediaConstants.EXTRAS_KEY_COMPLETION_STATUS;
import static androidx.media3.session.MediaConstants.EXTRAS_VALUE_COMPLETION_STATUS_PARTIALLY_PLAYED;
import static androidx.media3.test.session.common.CommonConstants.MOCK_MEDIA3_LIBRARY_SERVICE;
import static androidx.media3.test.session.common.MediaBrowserConstants.CUSTOM_ACTION_ASSERT_PARAMS;
import static androidx.media3.test.session.common.MediaBrowserConstants.LONG_LIST_COUNT;
import static androidx.media3.test.session.common.MediaBrowserConstants.NOTIFY_CHILDREN_CHANGED_EXTRAS;
import static androidx.media3.test.session.common.MediaBrowserConstants.NOTIFY_CHILDREN_CHANGED_ITEM_COUNT;
import static androidx.media3.test.session.common.MediaBrowserConstants.PARENT_ID;
import static androidx.media3.test.session.common.MediaBrowserConstants.ROOT_EXTRAS;
import static androidx.media3.test.session.common.MediaBrowserConstants.ROOT_EXTRAS_KEY;
import static androidx.media3.test.session.common.MediaBrowserConstants.ROOT_EXTRAS_VALUE;
import static androidx.media3.test.session.common.MediaBrowserConstants.ROOT_ID;
import static androidx.media3.test.session.common.MediaBrowserConstants.SUBSCRIBE_ID_NOTIFY_CHILDREN_CHANGED_TO_ALL;
import static androidx.media3.test.session.common.MediaBrowserConstants.SUBSCRIBE_ID_NOTIFY_CHILDREN_CHANGED_TO_ALL_WITH_NON_SUBSCRIBED_ID;
@ -98,6 +103,45 @@ public class MediaBrowserListenerTest extends MediaControllerListenerTest {
assertThat(TestUtils.equals(ROOT_EXTRAS, result.params.extras)).isTrue();
}
@Test
public void getLibraryRoot_correctExtraKeyAndValue() throws Exception {
MediaBrowser browser = createBrowser();
LibraryResult<MediaItem> resultForLibraryRoot =
threadTestRule
.getHandler()
.postAndSync(() -> browser.getLibraryRoot(new LibraryParams.Builder().build()))
.get(TIMEOUT_MS, MILLISECONDS);
Bundle extras = resultForLibraryRoot.params.extras;
assertThat(extras.getInt(ROOT_EXTRAS_KEY, /* defaultValue= */ ROOT_EXTRAS_VALUE + 1))
.isEqualTo(ROOT_EXTRAS_VALUE);
}
@Test
public void getChildren_correctMetadataExtras() throws Exception {
LibraryParams params = MediaTestUtils.createLibraryParams();
MediaBrowser browser = createBrowser();
LibraryResult<ImmutableList<MediaItem>> libraryResult =
threadTestRule
.getHandler()
.postAndSync(
() -> browser.getChildren(PARENT_ID, /* page= */ 4, /* pageSize= */ 10, params))
.get(TIMEOUT_MS, MILLISECONDS);
assertThat(libraryResult.resultCode).isEqualTo(RESULT_SUCCESS);
assertThat(libraryResult.value).isNotEmpty();
for (MediaItem mediaItem : libraryResult.value) {
int status =
mediaItem.mediaMetadata.extras.getInt(
EXTRAS_KEY_COMPLETION_STATUS,
/* defaultValue= */ EXTRAS_VALUE_COMPLETION_STATUS_PARTIALLY_PLAYED + 1);
assertThat(status).isEqualTo(EXTRAS_VALUE_COMPLETION_STATUS_PARTIALLY_PLAYED);
}
}
@Test
public void getItem_browsable() throws Exception {
String mediaId = MediaBrowserConstants.MEDIA_ID_GET_BROWSABLE_ITEM;
@ -144,14 +188,15 @@ public class MediaBrowserListenerTest extends MediaControllerListenerTest {
}
@Test
public void getChildren() throws Exception {
String parentId = MediaBrowserConstants.PARENT_ID;
public void getChildren_correctLibraryResultWithExtras() throws Exception {
String parentId = PARENT_ID;
int page = 4;
int pageSize = 10;
LibraryParams params = MediaTestUtils.createLibraryParams();
MediaBrowser browser = createBrowser();
setExpectedLibraryParam(browser, params);
LibraryResult<ImmutableList<MediaItem>> result =
threadTestRule
.getHandler()
@ -159,7 +204,6 @@ public class MediaBrowserListenerTest extends MediaControllerListenerTest {
.get(TIMEOUT_MS, MILLISECONDS);
assertThat(result.resultCode).isEqualTo(RESULT_SUCCESS);
MediaTestUtils.assertLibraryParamsEquals(params, result.params);
MediaTestUtils.assertPaginatedListHasIds(
result.value, MediaBrowserConstants.GET_CHILDREN_RESULT, page, pageSize);
}

View File

@ -16,8 +16,15 @@
package androidx.media3.session;
import static androidx.media3.session.LibraryResult.RESULT_SUCCESS;
import static androidx.media3.session.MediaConstants.EXTRAS_KEY_COMPLETION_STATUS;
import static androidx.media3.session.MediaConstants.EXTRAS_VALUE_COMPLETION_STATUS_PARTIALLY_PLAYED;
import static androidx.media3.test.session.common.CommonConstants.MOCK_MEDIA_BROWSER_SERVICE_COMPAT;
import static androidx.media3.test.session.common.MediaBrowserConstants.PARENT_ID;
import static androidx.media3.test.session.common.MediaBrowserConstants.ROOT_EXTRAS_KEY;
import static androidx.media3.test.session.common.MediaBrowserConstants.ROOT_EXTRAS_VALUE;
import static androidx.media3.test.session.common.MediaBrowserServiceCompatConstants.TEST_CONNECT_REJECTED;
import static androidx.media3.test.session.common.MediaBrowserServiceCompatConstants.TEST_GET_CHILDREN;
import static androidx.media3.test.session.common.MediaBrowserServiceCompatConstants.TEST_GET_LIBRARY_ROOT;
import static androidx.media3.test.session.common.MediaBrowserServiceCompatConstants.TEST_ON_CHILDREN_CHANGED_SUBSCRIBE_AND_UNSUBSCRIBE;
import static androidx.media3.test.session.common.TestUtils.TIMEOUT_MS;
import static com.google.common.truth.Truth.assertThat;
@ -25,13 +32,16 @@ import static java.util.concurrent.TimeUnit.MILLISECONDS;
import static org.junit.Assert.assertThrows;
import android.content.Context;
import android.os.Bundle;
import androidx.annotation.Nullable;
import androidx.media.MediaBrowserServiceCompat;
import androidx.media3.common.MediaItem;
import androidx.media3.session.MediaLibraryService.LibraryParams;
import androidx.media3.test.session.common.HandlerThreadTestRule;
import androidx.test.core.app.ApplicationProvider;
import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.LargeTest;
import com.google.common.collect.ImmutableList;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutionException;
import org.junit.After;
@ -137,4 +147,56 @@ public class MediaBrowserListenerWithMediaBrowserServiceCompatTest {
// Wait for some time. Exception will be thrown in the listener if error happens.
Thread.sleep(TIMEOUT_MS);
}
@Test
public void getLibraryRoot_correctExtraKeyAndValue() throws Exception {
remoteService.setProxyForTest(TEST_GET_LIBRARY_ROOT);
MediaBrowser browser = createBrowser(/* listener= */ null);
LibraryResult<MediaItem> resultForLibraryRoot =
threadTestRule
.getHandler()
.postAndSync(() -> browser.getLibraryRoot(new LibraryParams.Builder().build()))
.get(TIMEOUT_MS, MILLISECONDS);
Bundle extras = resultForLibraryRoot.params.extras;
assertThat(extras.getInt(ROOT_EXTRAS_KEY, ROOT_EXTRAS_VALUE + 1)).isEqualTo(ROOT_EXTRAS_VALUE);
}
@Test
public void getChildren_correctMetadataExtras() throws Exception {
LibraryParams params = MediaTestUtils.createLibraryParams();
remoteService.setProxyForTest(TEST_GET_CHILDREN);
MediaBrowser browser = createBrowser(/* listener= */ null);
LibraryResult<ImmutableList<MediaItem>> libraryResult =
threadTestRule
.getHandler()
.postAndSync(
() -> browser.getChildren(PARENT_ID, /* page= */ 4, /* pageSize= */ 10, params))
.get(TIMEOUT_MS, MILLISECONDS);
assertThat(libraryResult.resultCode).isEqualTo(RESULT_SUCCESS);
assertThat(libraryResult.value).hasSize(MockMediaBrowserServiceCompat.MEDIA_ITEMS.size());
for (int i = 0; i < libraryResult.value.size(); i++) {
int status =
libraryResult
.value
.get(i)
.mediaMetadata
.extras
.getInt(
EXTRAS_KEY_COMPLETION_STATUS,
/* defaultValue= */ EXTRAS_VALUE_COMPLETION_STATUS_PARTIALLY_PLAYED + 1);
int expectedStatus =
MockMediaBrowserServiceCompat.MEDIA_ITEMS
.get(i)
.getDescription()
.getExtras()
.getInt(
EXTRAS_KEY_COMPLETION_STATUS,
/* defaultValue= */ EXTRAS_VALUE_COMPLETION_STATUS_PARTIALLY_PLAYED + 1);
assertThat(status).isEqualTo(expectedStatus);
}
}
}

View File

@ -15,21 +15,33 @@
*/
package androidx.media3.session;
import static androidx.media3.session.MediaConstants.EXTRAS_KEY_COMPLETION_STATUS;
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.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;
import static androidx.media3.test.session.common.MediaBrowserServiceCompatConstants.TEST_CONNECT_REJECTED;
import static androidx.media3.test.session.common.MediaBrowserServiceCompatConstants.TEST_GET_CHILDREN;
import static androidx.media3.test.session.common.MediaBrowserServiceCompatConstants.TEST_GET_LIBRARY_ROOT;
import static androidx.media3.test.session.common.MediaBrowserServiceCompatConstants.TEST_ON_CHILDREN_CHANGED_SUBSCRIBE_AND_UNSUBSCRIBE;
import android.content.Intent;
import android.os.Bundle;
import android.os.IBinder;
import android.os.RemoteException;
import android.support.v4.media.MediaBrowserCompat;
import android.support.v4.media.MediaBrowserCompat.MediaItem;
import android.support.v4.media.MediaDescriptionCompat;
import android.support.v4.media.session.MediaSessionCompat;
import android.support.v4.media.session.MediaSessionCompat.Callback;
import androidx.annotation.GuardedBy;
import androidx.media.MediaBrowserServiceCompat;
import androidx.media3.common.util.UnstableApi;
import androidx.media3.test.session.common.IRemoteMediaBrowserServiceCompat;
import androidx.media3.test.session.common.MediaBrowserServiceCompatConstants;
import com.google.common.collect.ImmutableList;
import java.lang.reflect.Method;
import java.util.Collections;
import java.util.List;
@ -38,6 +50,12 @@ import java.util.List;
@UnstableApi
public class MockMediaBrowserServiceCompat extends MediaBrowserServiceCompat {
/**
* Immutable list of media items sent to controllers for {@link
* MediaBrowserServiceCompatConstants#TEST_GET_CHILDREN}.
*/
public static final ImmutableList<MediaItem> MEDIA_ITEMS = createMediaItems();
private static final String TAG = "MockMBSCompat";
private static final Object lock = new Object();
@ -226,6 +244,12 @@ public class MockMediaBrowserServiceCompat extends MediaBrowserServiceCompat {
case TEST_ON_CHILDREN_CHANGED_SUBSCRIBE_AND_UNSUBSCRIBE:
setProxyForTestOnChildrenChanged_subscribeAndUnsubscribe();
break;
case TEST_GET_LIBRARY_ROOT:
setProxyForTestGetLibraryRoot_correctExtraKeyAndValue();
break;
case TEST_GET_CHILDREN:
setProxyForTestGetChildren_correctMetadataExtras();
break;
default:
throw new IllegalArgumentException("Unknown testName: " + testName);
}
@ -257,5 +281,54 @@ public class MockMediaBrowserServiceCompat extends MediaBrowserServiceCompat {
}
});
}
private void setProxyForTestGetChildren_correctMetadataExtras() {
setMediaBrowserServiceProxy(
new MockMediaBrowserServiceCompat.Proxy() {
@Override
public void onLoadChildren(String parentId, Result<List<MediaItem>> result) {
onLoadChildren(parentId, result, new Bundle());
}
@Override
public void onLoadChildren(
String parentId, Result<List<MediaItem>> result, Bundle bundle) {
result.sendResult(MEDIA_ITEMS);
}
});
}
private void setProxyForTestGetLibraryRoot_correctExtraKeyAndValue() {
setMediaBrowserServiceProxy(
new MockMediaBrowserServiceCompat.Proxy() {
@Override
public BrowserRoot onGetRoot(
String clientPackageName, int clientUid, Bundle rootHints) {
return new BrowserRoot(ROOT_ID, ROOT_EXTRAS);
}
});
}
}
private static ImmutableList<MediaItem> createMediaItems() {
int[] completionStates =
new int[] {
EXTRAS_VALUE_COMPLETION_STATUS_NOT_PLAYED,
EXTRAS_VALUE_COMPLETION_STATUS_PARTIALLY_PLAYED,
EXTRAS_VALUE_COMPLETION_STATUS_FULLY_PLAYED
};
ImmutableList.Builder<MediaItem> builder = new ImmutableList.Builder<>();
for (int i = 0; i < 3; i++) {
Bundle extras = new Bundle();
extras.putInt(EXTRAS_KEY_COMPLETION_STATUS, completionStates[i]);
builder.add(
new MediaBrowserCompat.MediaItem(
new MediaDescriptionCompat.Builder()
.setMediaId("media-id-" + i)
.setExtras(extras)
.build(),
/* flags= */ 0));
}
return builder.build();
}
}

View File

@ -16,8 +16,10 @@
package androidx.media3.session;
import static androidx.media3.common.util.Assertions.checkNotNull;
import static androidx.media3.session.MediaConstants.EXTRAS_KEY_COMPLETION_STATUS;
import static androidx.media3.session.MediaConstants.EXTRAS_KEY_ERROR_RESOLUTION_ACTION_INTENT_COMPAT;
import static androidx.media3.session.MediaConstants.EXTRAS_KEY_ERROR_RESOLUTION_ACTION_LABEL_COMPAT;
import static androidx.media3.session.MediaConstants.EXTRAS_VALUE_COMPLETION_STATUS_PARTIALLY_PLAYED;
import static androidx.media3.session.MediaTestUtils.assertLibraryParamsEquals;
import static androidx.media3.test.session.common.CommonConstants.SUPPORT_APP_PACKAGE_NAME;
import static androidx.media3.test.session.common.MediaBrowserConstants.CUSTOM_ACTION;
@ -63,7 +65,6 @@ import androidx.media3.common.MediaMetadata;
import androidx.media3.common.util.Log;
import androidx.media3.common.util.UnstableApi;
import androidx.media3.common.util.Util;
import androidx.media3.session.MediaLibraryService.MediaLibrarySession;
import androidx.media3.session.MediaSession.ControllerInfo;
import androidx.media3.test.session.common.CommonConstants;
import androidx.media3.test.session.common.TestHandler;
@ -425,10 +426,13 @@ public class MockMediaLibraryService extends MediaLibraryService {
}
private static MediaItem createPlayableMediaItem(String mediaId) {
Bundle extras = new Bundle();
extras.putInt(EXTRAS_KEY_COMPLETION_STATUS, EXTRAS_VALUE_COMPLETION_STATUS_PARTIALLY_PLAYED);
MediaMetadata mediaMetadata =
new MediaMetadata.Builder()
.setFolderType(MediaMetadata.FOLDER_TYPE_NONE)
.setIsPlayable(true)
.setExtras(extras)
.build();
return new MediaItem.Builder().setMediaId(mediaId).setMediaMetadata(mediaMetadata).build();
}