MediaItem2.java revision fbbf807584a0fbe7a01a0aa9920330cad45689aa
1/*
2 * Copyright 2018 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package androidx.media;
18
19import static androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP;
20
21import android.os.Bundle;
22import android.text.TextUtils;
23
24import androidx.annotation.IntDef;
25import androidx.annotation.NonNull;
26import androidx.annotation.Nullable;
27import androidx.annotation.RestrictTo;
28
29import java.lang.annotation.Retention;
30import java.lang.annotation.RetentionPolicy;
31import java.util.UUID;
32
33/**
34 * @hide
35 * A class with information on a single media item with the metadata information.
36 * Media item are application dependent so we cannot guarantee that they contain the right values.
37 * <p>
38 * When it's sent to a controller or browser, it's anonymized and data descriptor wouldn't be sent.
39 * <p>
40 * This object isn't a thread safe.
41 */
42@RestrictTo(LIBRARY_GROUP)
43public class MediaItem2 {
44    /** @hide */
45    @RestrictTo(LIBRARY_GROUP)
46    @Retention(RetentionPolicy.SOURCE)
47    @IntDef(flag = true, value = { FLAG_BROWSABLE, FLAG_PLAYABLE })
48    public @interface Flags { }
49
50    /**
51     * Flag: Indicates that the item has children of its own.
52     */
53    public static final int FLAG_BROWSABLE = 1 << 0;
54
55    /**
56     * Flag: Indicates that the item is playable.
57     * <p>
58     * The id of this item may be passed to
59     * {@link MediaController2#playFromMediaId(String, Bundle)}
60     */
61    public static final int FLAG_PLAYABLE = 1 << 1;
62
63    private static final String KEY_ID = "android.media.mediaitem2.id";
64    private static final String KEY_FLAGS = "android.media.mediaitem2.flags";
65    private static final String KEY_METADATA = "android.media.mediaitem2.metadata";
66    private static final String KEY_UUID = "android.media.mediaitem2.uuid";
67
68    private final String mId;
69    private final int mFlags;
70    private final UUID mUUID;
71    private MediaMetadata2 mMetadata;
72    private DataSourceDesc mDataSourceDesc;
73
74    private MediaItem2(@NonNull String mediaId, @Nullable DataSourceDesc dsd,
75            @Nullable MediaMetadata2 metadata, @Flags int flags) {
76        this(mediaId, dsd, metadata, flags, null);
77    }
78
79    private MediaItem2(@NonNull String mediaId, @Nullable DataSourceDesc dsd,
80            @Nullable MediaMetadata2 metadata, @Flags int flags, @Nullable UUID uuid) {
81        if (mediaId == null) {
82            throw new IllegalArgumentException("mediaId shouldn't be null");
83        }
84        if (metadata != null && !TextUtils.equals(mediaId, metadata.getMediaId())) {
85            throw new IllegalArgumentException("metadata's id should be matched with the mediaid");
86        }
87
88        mId = mediaId;
89        mDataSourceDesc = dsd;
90        mMetadata = metadata;
91        mFlags = flags;
92        mUUID = (uuid == null) ? UUID.randomUUID() : uuid;
93    }
94    /**
95     * Return this object as a bundle to share between processes.
96     *
97     * @return a new bundle instance
98     */
99    public Bundle toBundle() {
100        Bundle bundle = new Bundle();
101        bundle.putString(KEY_ID, mId);
102        bundle.putInt(KEY_FLAGS, mFlags);
103        if (mMetadata != null) {
104            bundle.putBundle(KEY_METADATA, mMetadata.toBundle());
105        }
106        bundle.putString(KEY_UUID, mUUID.toString());
107        return bundle;
108    }
109
110    /**
111     * Create a MediaItem2 from the {@link Bundle}.
112     *
113     * @param bundle The bundle which was published by {@link MediaItem2#toBundle()}.
114     * @return The newly created MediaItem2
115     */
116    public static MediaItem2 fromBundle(Bundle bundle) {
117        if (bundle == null) {
118            return null;
119        }
120        final String uuidString = bundle.getString(KEY_UUID);
121        return fromBundle(bundle, UUID.fromString(uuidString));
122    }
123
124    /**
125     * Create a MediaItem2 from the {@link Bundle} with the specified {@link UUID}.
126     * If {@link UUID}
127     * can be null for creating new.
128     *
129     * @param bundle The bundle which was published by {@link MediaItem2#toBundle()}.
130     * @param uuid A {@link UUID} to override. Can be {@link null} for override.
131     * @return The newly created MediaItem2
132     */
133    static MediaItem2 fromBundle(@NonNull Bundle bundle, @Nullable UUID uuid) {
134        if (bundle == null) {
135            return null;
136        }
137        final String id = bundle.getString(KEY_ID);
138        final Bundle metadataBundle = bundle.getBundle(KEY_METADATA);
139        final MediaMetadata2 metadata = metadataBundle != null
140                ? MediaMetadata2.fromBundle(metadataBundle) : null;
141        final int flags = bundle.getInt(KEY_FLAGS);
142        return new MediaItem2(id, null, metadata, flags, uuid);
143    }
144
145    @Override
146    public String toString() {
147        final StringBuilder sb = new StringBuilder("MediaItem2{");
148        sb.append("mFlags=").append(mFlags);
149        sb.append(", mMetadata=").append(mMetadata);
150        sb.append('}');
151        return sb.toString();
152    }
153
154    /**
155     * Gets the flags of the item.
156     */
157    public @Flags int getFlags() {
158        return mFlags;
159    }
160
161    /**
162     * Returns whether this item is browsable.
163     * @see #FLAG_BROWSABLE
164     */
165    public boolean isBrowsable() {
166        return (mFlags & FLAG_BROWSABLE) != 0;
167    }
168
169    /**
170     * Returns whether this item is playable.
171     * @see #FLAG_PLAYABLE
172     */
173    public boolean isPlayable() {
174        return (mFlags & FLAG_PLAYABLE) != 0;
175    }
176
177    /**
178     * Set a metadata. If the metadata is not null, its id should be matched with this instance's
179     * media id.
180     *
181     * @param metadata metadata to update
182     */
183    public void setMetadata(@Nullable MediaMetadata2 metadata) {
184        if (metadata != null && !TextUtils.equals(mId, metadata.getMediaId())) {
185            throw new IllegalArgumentException("metadata's id should be matched with the mediaId");
186        }
187        mMetadata = metadata;
188    }
189
190    /**
191     * Returns the metadata of the media.
192     */
193    public @Nullable MediaMetadata2 getMetadata() {
194        return mMetadata;
195    }
196
197    /**
198     * Returns the media id for this item.
199     */
200    public /*@NonNull*/ String getMediaId() {
201        return mId;
202    }
203
204    /**
205     * Return the {@link DataSourceDesc}
206     * <p>
207     * Can be {@code null} if the MediaItem2 came from another process and anonymized
208     *
209     * @return data source descriptor
210     */
211    public @Nullable DataSourceDesc getDataSourceDesc() {
212        return mDataSourceDesc;
213    }
214
215    @Override
216    public int hashCode() {
217        return mUUID.hashCode();
218    }
219
220    @Override
221    public boolean equals(Object obj) {
222        if (!(obj instanceof MediaItem2)) {
223            return false;
224        }
225        MediaItem2 other = (MediaItem2) obj;
226        return mUUID.equals(other.mUUID);
227    }
228
229    /**
230     * Build {@link MediaItem2}
231     */
232    public static final class Builder {
233        private @Flags int mFlags;
234        private String mMediaId;
235        private MediaMetadata2 mMetadata;
236        private DataSourceDesc mDataSourceDesc;
237
238        /**
239         * Constructor for {@link Builder}
240         *
241         * @param flags
242         */
243        public Builder(@Flags int flags) {
244            mFlags = flags;
245        }
246
247        /**
248         * Set the media id of this instance. {@code null} for unset.
249         * <p>
250         * Media id is used to identify a media contents between session and controller.
251         * <p>
252         * If the metadata is set with the {@link #setMetadata(MediaMetadata2)} and it has
253         * media id, id from {@link #setMediaId(String)} will be ignored and metadata's id will be
254         * used instead. If the id isn't set neither by {@link #setMediaId(String)} nor
255         * {@link #setMetadata(MediaMetadata2)}, id will be automatically generated.
256         *
257         * @param mediaId media id
258         * @return this instance for chaining
259         */
260        public Builder setMediaId(@Nullable String mediaId) {
261            mMediaId = mediaId;
262            return this;
263        }
264
265        /**
266         * Set the metadata of this instance. {@code null} for unset.
267         * <p>
268         * If the metadata is set with the {@link #setMetadata(MediaMetadata2)} and it has
269         * media id, id from {@link #setMediaId(String)} will be ignored and metadata's id will be
270         * used instead. If the id isn't set neither by {@link #setMediaId(String)} nor
271         * {@link #setMetadata(MediaMetadata2)}, id will be automatically generated.
272         *
273         * @param metadata metadata
274         * @return this instance for chaining
275         */
276        public Builder setMetadata(@Nullable MediaMetadata2 metadata) {
277            mMetadata = metadata;
278            return this;
279        }
280
281        /**
282         * Set the data source descriptor for this instance. {@code null} for unset.
283         *
284         * @param dataSourceDesc data source descriptor
285         * @return this instance for chaining
286         */
287        public Builder setDataSourceDesc(@Nullable DataSourceDesc dataSourceDesc) {
288            mDataSourceDesc = dataSourceDesc;
289            return this;
290        }
291
292        /**
293         * Build {@link MediaItem2}.
294         *
295         * @return a new {@link MediaItem2}.
296         */
297        public MediaItem2 build() {
298            String id = (mMetadata != null)
299                    ? mMetadata.getString(MediaMetadata2.METADATA_KEY_MEDIA_ID) : null;
300            if (id == null) {
301                //  TODO(jaewan): Double check if its sufficient (e.g. Use UUID instead?)
302                id = (mMediaId != null) ? mMediaId : toString();
303            }
304            return new MediaItem2(id, mDataSourceDesc, mMetadata, mFlags);
305        }
306    }
307}
308