|  | /* | 
|  | * Copyright 2018 The Android Open Source Project | 
|  | * | 
|  | * Licensed under the Apache License, Version 2.0 (the "License"); | 
|  | * you may not use this file except in compliance with the License. | 
|  | * You may obtain a copy of the License at | 
|  | * | 
|  | *      http://www.apache.org/licenses/LICENSE-2.0 | 
|  | * | 
|  | * Unless required by applicable law or agreed to in writing, software | 
|  | * distributed under the License is distributed on an "AS IS" BASIS, | 
|  | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | 
|  | * See the License for the specific language governing permissions and | 
|  | * limitations under the License. | 
|  | */ | 
|  |  | 
|  | package androidx.media; | 
|  |  | 
|  | import static androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP; | 
|  |  | 
|  | import android.os.Bundle; | 
|  | import android.text.TextUtils; | 
|  |  | 
|  | import androidx.annotation.IntDef; | 
|  | import androidx.annotation.NonNull; | 
|  | import androidx.annotation.Nullable; | 
|  | import androidx.annotation.RestrictTo; | 
|  |  | 
|  | import java.lang.annotation.Retention; | 
|  | import java.lang.annotation.RetentionPolicy; | 
|  | import java.util.UUID; | 
|  |  | 
|  | /** | 
|  | * A class with information on a single media item with the metadata information. | 
|  | * Media item are application dependent so we cannot guarantee that they contain the right values. | 
|  | * <p> | 
|  | * When it's sent to a controller or browser, it's anonymized and data descriptor wouldn't be sent. | 
|  | * <p> | 
|  | * This object isn't a thread safe. | 
|  | */ | 
|  | public class MediaItem2 { | 
|  | /** @hide */ | 
|  | @RestrictTo(LIBRARY_GROUP) | 
|  | @Retention(RetentionPolicy.SOURCE) | 
|  | @IntDef(flag = true, value = { FLAG_BROWSABLE, FLAG_PLAYABLE }) | 
|  | public @interface Flags { } | 
|  |  | 
|  | /** | 
|  | * Flag: Indicates that the item has children of its own. | 
|  | */ | 
|  | public static final int FLAG_BROWSABLE = 1 << 0; | 
|  |  | 
|  | /** | 
|  | * Flag: Indicates that the item is playable. | 
|  | * <p> | 
|  | * The id of this item may be passed to | 
|  | * {@link MediaController2#playFromMediaId(String, Bundle)} | 
|  | */ | 
|  | public static final int FLAG_PLAYABLE = 1 << 1; | 
|  |  | 
|  | private static final String KEY_ID = "android.media.mediaitem2.id"; | 
|  | private static final String KEY_FLAGS = "android.media.mediaitem2.flags"; | 
|  | private static final String KEY_METADATA = "android.media.mediaitem2.metadata"; | 
|  | private static final String KEY_UUID = "android.media.mediaitem2.uuid"; | 
|  |  | 
|  | private final String mId; | 
|  | private final int mFlags; | 
|  | private final UUID mUUID; | 
|  | private MediaMetadata2 mMetadata; | 
|  | private DataSourceDesc mDataSourceDesc; | 
|  |  | 
|  | private MediaItem2(@NonNull String mediaId, @Nullable DataSourceDesc dsd, | 
|  | @Nullable MediaMetadata2 metadata, @Flags int flags) { | 
|  | this(mediaId, dsd, metadata, flags, null); | 
|  | } | 
|  |  | 
|  | private MediaItem2(@NonNull String mediaId, @Nullable DataSourceDesc dsd, | 
|  | @Nullable MediaMetadata2 metadata, @Flags int flags, @Nullable UUID uuid) { | 
|  | if (mediaId == null) { | 
|  | throw new IllegalArgumentException("mediaId shouldn't be null"); | 
|  | } | 
|  | if (metadata != null && !TextUtils.equals(mediaId, metadata.getMediaId())) { | 
|  | throw new IllegalArgumentException("metadata's id should be matched with the mediaid"); | 
|  | } | 
|  |  | 
|  | mId = mediaId; | 
|  | mDataSourceDesc = dsd; | 
|  | mMetadata = metadata; | 
|  | mFlags = flags; | 
|  | mUUID = (uuid == null) ? UUID.randomUUID() : uuid; | 
|  | } | 
|  | /** | 
|  | * Return this object as a bundle to share between processes. | 
|  | * | 
|  | * @return a new bundle instance | 
|  | */ | 
|  | public Bundle toBundle() { | 
|  | Bundle bundle = new Bundle(); | 
|  | bundle.putString(KEY_ID, mId); | 
|  | bundle.putInt(KEY_FLAGS, mFlags); | 
|  | if (mMetadata != null) { | 
|  | bundle.putBundle(KEY_METADATA, mMetadata.toBundle()); | 
|  | } | 
|  | bundle.putString(KEY_UUID, mUUID.toString()); | 
|  | return bundle; | 
|  | } | 
|  |  | 
|  | /** | 
|  | * Create a MediaItem2 from the {@link Bundle}. | 
|  | * | 
|  | * @param bundle The bundle which was published by {@link MediaItem2#toBundle()}. | 
|  | * @return The newly created MediaItem2 | 
|  | */ | 
|  | public static MediaItem2 fromBundle(Bundle bundle) { | 
|  | if (bundle == null) { | 
|  | return null; | 
|  | } | 
|  | final String uuidString = bundle.getString(KEY_UUID); | 
|  | return fromBundle(bundle, UUID.fromString(uuidString)); | 
|  | } | 
|  |  | 
|  | /** | 
|  | * Create a MediaItem2 from the {@link Bundle} with the specified {@link UUID}. | 
|  | * If {@link UUID} | 
|  | * can be null for creating new. | 
|  | * | 
|  | * @param bundle The bundle which was published by {@link MediaItem2#toBundle()}. | 
|  | * @param uuid A {@link UUID} to override. Can be {@link null} for override. | 
|  | * @return The newly created MediaItem2 | 
|  | */ | 
|  | static MediaItem2 fromBundle(@NonNull Bundle bundle, @Nullable UUID uuid) { | 
|  | if (bundle == null) { | 
|  | return null; | 
|  | } | 
|  | final String id = bundle.getString(KEY_ID); | 
|  | final Bundle metadataBundle = bundle.getBundle(KEY_METADATA); | 
|  | final MediaMetadata2 metadata = metadataBundle != null | 
|  | ? MediaMetadata2.fromBundle(metadataBundle) : null; | 
|  | final int flags = bundle.getInt(KEY_FLAGS); | 
|  | return new MediaItem2(id, null, metadata, flags, uuid); | 
|  | } | 
|  |  | 
|  | @Override | 
|  | public String toString() { | 
|  | final StringBuilder sb = new StringBuilder("MediaItem2{"); | 
|  | sb.append("mFlags=").append(mFlags); | 
|  | sb.append(", mMetadata=").append(mMetadata); | 
|  | sb.append('}'); | 
|  | return sb.toString(); | 
|  | } | 
|  |  | 
|  | /** | 
|  | * Gets the flags of the item. | 
|  | */ | 
|  | public @Flags int getFlags() { | 
|  | return mFlags; | 
|  | } | 
|  |  | 
|  | /** | 
|  | * Returns whether this item is browsable. | 
|  | * @see #FLAG_BROWSABLE | 
|  | */ | 
|  | public boolean isBrowsable() { | 
|  | return (mFlags & FLAG_BROWSABLE) != 0; | 
|  | } | 
|  |  | 
|  | /** | 
|  | * Returns whether this item is playable. | 
|  | * @see #FLAG_PLAYABLE | 
|  | */ | 
|  | public boolean isPlayable() { | 
|  | return (mFlags & FLAG_PLAYABLE) != 0; | 
|  | } | 
|  |  | 
|  | /** | 
|  | * Set a metadata. If the metadata is not null, its id should be matched with this instance's | 
|  | * media id. | 
|  | * | 
|  | * @param metadata metadata to update | 
|  | */ | 
|  | public void setMetadata(@Nullable MediaMetadata2 metadata) { | 
|  | if (metadata != null && !TextUtils.equals(mId, metadata.getMediaId())) { | 
|  | throw new IllegalArgumentException("metadata's id should be matched with the mediaId"); | 
|  | } | 
|  | mMetadata = metadata; | 
|  | } | 
|  |  | 
|  | /** | 
|  | * Returns the metadata of the media. | 
|  | */ | 
|  | public @Nullable MediaMetadata2 getMetadata() { | 
|  | return mMetadata; | 
|  | } | 
|  |  | 
|  | /** | 
|  | * Returns the media id for this item. | 
|  | */ | 
|  | public /*@NonNull*/ String getMediaId() { | 
|  | return mId; | 
|  | } | 
|  |  | 
|  | /** | 
|  | * Return the {@link DataSourceDesc} | 
|  | * <p> | 
|  | * Can be {@code null} if the MediaItem2 came from another process and anonymized | 
|  | * | 
|  | * @return data source descriptor | 
|  | */ | 
|  | public @Nullable DataSourceDesc getDataSourceDesc() { | 
|  | return mDataSourceDesc; | 
|  | } | 
|  |  | 
|  | @Override | 
|  | public int hashCode() { | 
|  | return mUUID.hashCode(); | 
|  | } | 
|  |  | 
|  | @Override | 
|  | public boolean equals(Object obj) { | 
|  | if (!(obj instanceof MediaItem2)) { | 
|  | return false; | 
|  | } | 
|  | MediaItem2 other = (MediaItem2) obj; | 
|  | return mUUID.equals(other.mUUID); | 
|  | } | 
|  |  | 
|  | /** | 
|  | * Build {@link MediaItem2} | 
|  | */ | 
|  | public static final class Builder { | 
|  | private @Flags int mFlags; | 
|  | private String mMediaId; | 
|  | private MediaMetadata2 mMetadata; | 
|  | private DataSourceDesc mDataSourceDesc; | 
|  |  | 
|  | /** | 
|  | * Constructor for {@link Builder} | 
|  | * | 
|  | * @param flags | 
|  | */ | 
|  | public Builder(@Flags int flags) { | 
|  | mFlags = flags; | 
|  | } | 
|  |  | 
|  | /** | 
|  | * Set the media id of this instance. {@code null} for unset. | 
|  | * <p> | 
|  | * Media id is used to identify a media contents between session and controller. | 
|  | * <p> | 
|  | * If the metadata is set with the {@link #setMetadata(MediaMetadata2)} and it has | 
|  | * media id, id from {@link #setMediaId(String)} will be ignored and metadata's id will be | 
|  | * used instead. If the id isn't set neither by {@link #setMediaId(String)} nor | 
|  | * {@link #setMetadata(MediaMetadata2)}, id will be automatically generated. | 
|  | * | 
|  | * @param mediaId media id | 
|  | * @return this instance for chaining | 
|  | */ | 
|  | public Builder setMediaId(@Nullable String mediaId) { | 
|  | mMediaId = mediaId; | 
|  | return this; | 
|  | } | 
|  |  | 
|  | /** | 
|  | * Set the metadata of this instance. {@code null} for unset. | 
|  | * <p> | 
|  | * If the metadata is set with the {@link #setMetadata(MediaMetadata2)} and it has | 
|  | * media id, id from {@link #setMediaId(String)} will be ignored and metadata's id will be | 
|  | * used instead. If the id isn't set neither by {@link #setMediaId(String)} nor | 
|  | * {@link #setMetadata(MediaMetadata2)}, id will be automatically generated. | 
|  | * | 
|  | * @param metadata metadata | 
|  | * @return this instance for chaining | 
|  | */ | 
|  | public Builder setMetadata(@Nullable MediaMetadata2 metadata) { | 
|  | mMetadata = metadata; | 
|  | return this; | 
|  | } | 
|  |  | 
|  | /** | 
|  | * Set the data source descriptor for this instance. {@code null} for unset. | 
|  | * | 
|  | * @param dataSourceDesc data source descriptor | 
|  | * @return this instance for chaining | 
|  | */ | 
|  | public Builder setDataSourceDesc(@Nullable DataSourceDesc dataSourceDesc) { | 
|  | mDataSourceDesc = dataSourceDesc; | 
|  | return this; | 
|  | } | 
|  |  | 
|  | /** | 
|  | * Build {@link MediaItem2}. | 
|  | * | 
|  | * @return a new {@link MediaItem2}. | 
|  | */ | 
|  | public MediaItem2 build() { | 
|  | String id = (mMetadata != null) | 
|  | ? mMetadata.getString(MediaMetadata2.METADATA_KEY_MEDIA_ID) : null; | 
|  | if (id == null) { | 
|  | id = (mMediaId != null) ? mMediaId : toString(); | 
|  | } | 
|  | return new MediaItem2(id, mDataSourceDesc, mMetadata, mFlags); | 
|  | } | 
|  | } | 
|  | } |