diff --git a/libraries/common/src/main/java/androidx/media3/common/MediaItem.java b/libraries/common/src/main/java/androidx/media3/common/MediaItem.java index edd78a8ee0..8d85132c19 100644 --- a/libraries/common/src/main/java/androidx/media3/common/MediaItem.java +++ b/libraries/common/src/main/java/androidx/media3/common/MediaItem.java @@ -85,6 +85,7 @@ public final class MediaItem implements Bundleable { // TODO: Change this to LiveConfiguration once all the deprecated individual setters // are removed. private LiveConfiguration.Builder liveConfiguration; + private RequestMetadata requestMetadata; /** Creates a builder. */ @SuppressWarnings("deprecation") // Temporarily uses DrmConfiguration.Builder() constructor. @@ -94,6 +95,7 @@ public final class MediaItem implements Bundleable { streamKeys = Collections.emptyList(); subtitleConfigurations = ImmutableList.of(); liveConfiguration = new LiveConfiguration.Builder(); + requestMetadata = RequestMetadata.EMPTY; } private Builder(MediaItem mediaItem) { @@ -102,6 +104,7 @@ public final class MediaItem implements Bundleable { mediaId = mediaItem.mediaId; mediaMetadata = mediaItem.mediaMetadata; liveConfiguration = mediaItem.liveConfiguration.buildUpon(); + requestMetadata = mediaItem.requestMetadata; @Nullable LocalConfiguration localConfiguration = mediaItem.localConfiguration; if (localConfiguration != null) { customCacheKey = localConfiguration.customCacheKey; @@ -526,6 +529,12 @@ public final class MediaItem implements Bundleable { return this; } + /** Sets the request metadata. */ + public Builder setRequestMetadata(RequestMetadata requestMetadata) { + this.requestMetadata = requestMetadata; + return this; + } + /** Returns a new {@link MediaItem} instance with the current builder values. */ @SuppressWarnings("deprecation") // Using PlaybackProperties while it exists. public MediaItem build() { @@ -550,7 +559,8 @@ public final class MediaItem implements Bundleable { clippingConfiguration.buildClippingProperties(), localConfiguration, liveConfiguration.build(), - mediaMetadata != null ? mediaMetadata : MediaMetadata.EMPTY); + mediaMetadata != null ? mediaMetadata : MediaMetadata.EMPTY, + requestMetadata); } } @@ -1730,6 +1740,146 @@ public final class MediaItem implements Bundleable { } } + /** + * Metadata that helps the player to understand a playback request represented by a {@link + * MediaItem}. + * + *
This metadata is most useful for cases where playback requests are forwarded to other player + * instances (e.g. from a {@link android.media.session.MediaController}) and the player creating + * the request doesn't know the required {@link LocalConfiguration} for playback. + */ + public static final class RequestMetadata implements Bundleable { + + /** Empty request metadata. */ + public static final RequestMetadata EMPTY = new Builder().build(); + + /** Builder for {@link RequestMetadata} instances. */ + public static final class Builder { + + @Nullable private Uri mediaUri; + @Nullable private String searchQuery; + @Nullable private Bundle extras; + + /** Constructs an instance. */ + public Builder() {} + + private Builder(RequestMetadata requestMetadata) { + this.mediaUri = requestMetadata.mediaUri; + this.searchQuery = requestMetadata.searchQuery; + this.extras = requestMetadata.extras; + } + + /** Sets the URI of the requested media, or null if not known or applicable. */ + public Builder setMediaUri(@Nullable Uri mediaUri) { + this.mediaUri = mediaUri; + return this; + } + + /** Sets the search query for the requested media, or null if not applicable. */ + public Builder setSearchQuery(@Nullable String searchQuery) { + this.searchQuery = searchQuery; + return this; + } + + /** Sets optional extras {@link Bundle}. */ + public Builder setExtras(@Nullable Bundle extras) { + this.extras = extras; + return this; + } + + /** Builds the request metadata. */ + public RequestMetadata build() { + return new RequestMetadata(this); + } + } + + /** The URI of the requested media, or null if not known or applicable. */ + @Nullable public final Uri mediaUri; + + /** The search query for the requested media, or null if not applicable. */ + @Nullable public final String searchQuery; + + /** + * Optional extras {@link Bundle}. + * + *
Given the complexities of checking the equality of two {@link Bundle}s, this is not
+ * considered in the {@link #equals(Object)} or {@link #hashCode()}.
+ */
+ @Nullable public final Bundle extras;
+
+ private RequestMetadata(Builder builder) {
+ this.mediaUri = builder.mediaUri;
+ this.searchQuery = builder.searchQuery;
+ this.extras = builder.extras;
+ }
+
+ /** Returns a {@link Builder} initialized with the values of this instance. */
+ public Builder buildUpon() {
+ return new Builder(this);
+ }
+
+ @Override
+ public boolean equals(@Nullable Object o) {
+ if (this == o) {
+ return true;
+ }
+ if (!(o instanceof RequestMetadata)) {
+ return false;
+ }
+ RequestMetadata that = (RequestMetadata) o;
+ return Util.areEqual(mediaUri, that.mediaUri) && Util.areEqual(searchQuery, that.searchQuery);
+ }
+
+ @Override
+ public int hashCode() {
+ int result = mediaUri == null ? 0 : mediaUri.hashCode();
+ result = 31 * result + (searchQuery == null ? 0 : searchQuery.hashCode());
+ return result;
+ }
+
+ // Bundleable implementation.
+
+ @Documented
+ @Retention(RetentionPolicy.SOURCE)
+ @Target(TYPE_USE)
+ @IntDef({FIELD_MEDIA_URI, FIELD_SEARCH_QUERY, FIELD_EXTRAS})
+ private @interface FieldNumber {}
+
+ private static final int FIELD_MEDIA_URI = 0;
+ private static final int FIELD_SEARCH_QUERY = 1;
+ private static final int FIELD_EXTRAS = 2;
+
+ @UnstableApi
+ @Override
+ public Bundle toBundle() {
+ Bundle bundle = new Bundle();
+ if (mediaUri != null) {
+ bundle.putParcelable(keyForField(FIELD_MEDIA_URI), mediaUri);
+ }
+ if (searchQuery != null) {
+ bundle.putString(keyForField(FIELD_SEARCH_QUERY), searchQuery);
+ }
+ if (extras != null) {
+ bundle.putBundle(keyForField(FIELD_EXTRAS), extras);
+ }
+ return bundle;
+ }
+
+ /** Object that can restore {@link RequestMetadata} from a {@link Bundle}. */
+ @UnstableApi
+ public static final Creator