Add RequestMetadata to MediaItem
These fields can be used to transport additional request properties when the requester doesn't know the details needed for the actual playback (i.e. the LocalConfiguration). PiperOrigin-RevId: 451857093
This commit is contained in:
parent
b25d00a795
commit
6d776a5ae4
@ -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}.
|
||||
*
|
||||
* <p>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}.
|
||||
*
|
||||
* <p>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<RequestMetadata> CREATOR =
|
||||
bundle ->
|
||||
new RequestMetadata.Builder()
|
||||
.setMediaUri(bundle.getParcelable(keyForField(FIELD_MEDIA_URI)))
|
||||
.setSearchQuery(bundle.getString(keyForField(FIELD_SEARCH_QUERY)))
|
||||
.setExtras(bundle.getBundle(keyForField(FIELD_EXTRAS)))
|
||||
.build();
|
||||
|
||||
private static String keyForField(@RequestMetadata.FieldNumber int field) {
|
||||
return Integer.toString(field, Character.MAX_RADIX);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The default media ID that is used if the media ID is not explicitly set by {@link
|
||||
* Builder#setMediaId(String)}.
|
||||
@ -1765,6 +1915,9 @@ public final class MediaItem implements Bundleable {
|
||||
*/
|
||||
@UnstableApi @Deprecated public final ClippingProperties clippingProperties;
|
||||
|
||||
/** The media {@link RequestMetadata}. */
|
||||
public final RequestMetadata requestMetadata;
|
||||
|
||||
// Using PlaybackProperties and ClippingProperties until they're deleted.
|
||||
@SuppressWarnings("deprecation")
|
||||
private MediaItem(
|
||||
@ -1772,7 +1925,8 @@ public final class MediaItem implements Bundleable {
|
||||
ClippingProperties clippingConfiguration,
|
||||
@Nullable PlaybackProperties localConfiguration,
|
||||
LiveConfiguration liveConfiguration,
|
||||
MediaMetadata mediaMetadata) {
|
||||
MediaMetadata mediaMetadata,
|
||||
RequestMetadata requestMetadata) {
|
||||
this.mediaId = mediaId;
|
||||
this.localConfiguration = localConfiguration;
|
||||
this.playbackProperties = localConfiguration;
|
||||
@ -1780,6 +1934,7 @@ public final class MediaItem implements Bundleable {
|
||||
this.mediaMetadata = mediaMetadata;
|
||||
this.clippingConfiguration = clippingConfiguration;
|
||||
this.clippingProperties = clippingConfiguration;
|
||||
this.requestMetadata = requestMetadata;
|
||||
}
|
||||
|
||||
/** Returns a {@link Builder} initialized with the values of this instance. */
|
||||
@ -1802,7 +1957,8 @@ public final class MediaItem implements Bundleable {
|
||||
&& clippingConfiguration.equals(other.clippingConfiguration)
|
||||
&& Util.areEqual(localConfiguration, other.localConfiguration)
|
||||
&& Util.areEqual(liveConfiguration, other.liveConfiguration)
|
||||
&& Util.areEqual(mediaMetadata, other.mediaMetadata);
|
||||
&& Util.areEqual(mediaMetadata, other.mediaMetadata)
|
||||
&& Util.areEqual(requestMetadata, other.requestMetadata);
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -1812,6 +1968,7 @@ public final class MediaItem implements Bundleable {
|
||||
result = 31 * result + liveConfiguration.hashCode();
|
||||
result = 31 * result + clippingConfiguration.hashCode();
|
||||
result = 31 * result + mediaMetadata.hashCode();
|
||||
result = 31 * result + requestMetadata.hashCode();
|
||||
return result;
|
||||
}
|
||||
|
||||
@ -1824,7 +1981,8 @@ public final class MediaItem implements Bundleable {
|
||||
FIELD_MEDIA_ID,
|
||||
FIELD_LIVE_CONFIGURATION,
|
||||
FIELD_MEDIA_METADATA,
|
||||
FIELD_CLIPPING_PROPERTIES
|
||||
FIELD_CLIPPING_PROPERTIES,
|
||||
FIELD_REQUEST_METADATA
|
||||
})
|
||||
private @interface FieldNumber {}
|
||||
|
||||
@ -1832,6 +1990,7 @@ public final class MediaItem implements Bundleable {
|
||||
private static final int FIELD_LIVE_CONFIGURATION = 1;
|
||||
private static final int FIELD_MEDIA_METADATA = 2;
|
||||
private static final int FIELD_CLIPPING_PROPERTIES = 3;
|
||||
private static final int FIELD_REQUEST_METADATA = 4;
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
@ -1847,6 +2006,7 @@ public final class MediaItem implements Bundleable {
|
||||
bundle.putBundle(keyForField(FIELD_LIVE_CONFIGURATION), liveConfiguration.toBundle());
|
||||
bundle.putBundle(keyForField(FIELD_MEDIA_METADATA), mediaMetadata.toBundle());
|
||||
bundle.putBundle(keyForField(FIELD_CLIPPING_PROPERTIES), clippingConfiguration.toBundle());
|
||||
bundle.putBundle(keyForField(FIELD_REQUEST_METADATA), requestMetadata.toBundle());
|
||||
return bundle;
|
||||
}
|
||||
|
||||
@ -1883,12 +2043,20 @@ public final class MediaItem implements Bundleable {
|
||||
} else {
|
||||
clippingConfiguration = ClippingConfiguration.CREATOR.fromBundle(clippingConfigurationBundle);
|
||||
}
|
||||
@Nullable Bundle requestMetadataBundle = bundle.getBundle(keyForField(FIELD_REQUEST_METADATA));
|
||||
RequestMetadata requestMetadata;
|
||||
if (requestMetadataBundle == null) {
|
||||
requestMetadata = RequestMetadata.EMPTY;
|
||||
} else {
|
||||
requestMetadata = RequestMetadata.CREATOR.fromBundle(requestMetadataBundle);
|
||||
}
|
||||
return new MediaItem(
|
||||
mediaId,
|
||||
clippingConfiguration,
|
||||
/* localConfiguration= */ null,
|
||||
liveConfiguration,
|
||||
mediaMetadata);
|
||||
mediaMetadata,
|
||||
requestMetadata);
|
||||
}
|
||||
|
||||
private static String keyForField(@FieldNumber int field) {
|
||||
|
@ -19,6 +19,8 @@ import static com.google.common.truth.Truth.assertThat;
|
||||
import static org.junit.Assert.assertThrows;
|
||||
|
||||
import android.net.Uri;
|
||||
import android.os.Bundle;
|
||||
import androidx.media3.common.MediaItem.RequestMetadata;
|
||||
import androidx.test.ext.junit.runners.AndroidJUnit4;
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
@ -579,6 +581,24 @@ public class MediaItemTest {
|
||||
assertThat(mediaItem.liveConfiguration.maxOffsetMs).isEqualTo(1234);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void builder_setRequestMetadata_setsRequestMetadata() {
|
||||
Bundle extras = new Bundle();
|
||||
extras.putString("key", "value");
|
||||
RequestMetadata requestMetadata =
|
||||
new RequestMetadata.Builder()
|
||||
.setMediaUri(Uri.parse("http://test.test"))
|
||||
.setSearchQuery("Play media!")
|
||||
.setExtras(extras)
|
||||
.build();
|
||||
|
||||
MediaItem mediaItem =
|
||||
new MediaItem.Builder().setMediaId("mediaID").setRequestMetadata(requestMetadata).build();
|
||||
|
||||
assertThat(mediaItem.requestMetadata).isEqualTo(requestMetadata);
|
||||
assertThat(mediaItem.requestMetadata.extras.getString("key")).isEqualTo("value");
|
||||
}
|
||||
|
||||
@Test
|
||||
@SuppressWarnings("deprecation") // Testing deprecated setter methods
|
||||
public void buildUpon_individualSetters_equalsToOriginal() {
|
||||
@ -677,6 +697,11 @@ public class MediaItemTest {
|
||||
.setLabel("label")
|
||||
.setId("id")
|
||||
.build()))
|
||||
.setRequestMetadata(
|
||||
new RequestMetadata.Builder()
|
||||
.setMediaUri(Uri.parse("http://test.test"))
|
||||
.setSearchQuery("search")
|
||||
.build())
|
||||
.setTag(new Object())
|
||||
.build();
|
||||
|
||||
@ -704,6 +729,11 @@ public class MediaItemTest {
|
||||
.setClipRelativeToDefaultPosition(true)
|
||||
.setClipRelativeToLiveWindow(true)
|
||||
.setClipStartsAtKeyFrame(true)
|
||||
.setRequestMetadata(
|
||||
new RequestMetadata.Builder()
|
||||
.setMediaUri(Uri.parse("http://test.test"))
|
||||
.setSearchQuery("search")
|
||||
.build())
|
||||
.build();
|
||||
|
||||
assertThat(mediaItem.localConfiguration).isNull();
|
||||
|
Loading…
x
Reference in New Issue
Block a user