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