MediaDescriptionCompat.java revision b78a7edc82d18094e93383f96b161f267a976f3e
1/*
2 * Copyright (C) 2014 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 */
16package android.support.v4.media;
17
18import static android.support.annotation.RestrictTo.Scope.LIBRARY_GROUP;
19
20import android.graphics.Bitmap;
21import android.net.Uri;
22import android.os.Build;
23import android.os.Bundle;
24import android.os.Parcel;
25import android.os.Parcelable;
26import android.support.annotation.Nullable;
27import android.support.annotation.RestrictTo;
28import android.text.TextUtils;
29
30/**
31 * A simple set of metadata for a media item suitable for display. This can be
32 * created using the Builder or retrieved from existing metadata using
33 * {@link MediaMetadataCompat#getDescription()}.
34 */
35public final class MediaDescriptionCompat implements Parcelable {
36    /**
37     * Used as a long extra field to indicate the bluetooth folder type of the media item as
38     * specified in the section 6.10.2.2 of the Bluetooth AVRCP 1.5. This is valid only for
39     * {@link MediaBrowserCompat.MediaItem} with
40     * {@link MediaBrowserCompat.MediaItem#FLAG_BROWSABLE}. The value should be one of the
41     * following:
42     * <ul>
43     * <li>{@link #BT_FOLDER_TYPE_MIXED}</li>
44     * <li>{@link #BT_FOLDER_TYPE_TITLES}</li>
45     * <li>{@link #BT_FOLDER_TYPE_ALBUMS}</li>
46     * <li>{@link #BT_FOLDER_TYPE_ARTISTS}</li>
47     * <li>{@link #BT_FOLDER_TYPE_GENRES}</li>
48     * <li>{@link #BT_FOLDER_TYPE_PLAYLISTS}</li>
49     * <li>{@link #BT_FOLDER_TYPE_YEARS}</li>
50     * </ul>
51     *
52     * @see #getExtras()
53     */
54    public static final String EXTRA_BT_FOLDER_TYPE = "android.media.extra.BT_FOLDER_TYPE";
55
56    /**
57     * The type of folder that is unknown or contains media elements of mixed types as specified in
58     * the section 6.10.2.2 of the Bluetooth AVRCP 1.5.
59     */
60    public static final long BT_FOLDER_TYPE_MIXED = 0;
61
62    /**
63     * The type of folder that contains media elements only as specified in the section 6.10.2.2 of
64     * the Bluetooth AVRCP 1.5.
65     */
66    public static final long BT_FOLDER_TYPE_TITLES = 1;
67
68    /**
69     * The type of folder that contains folders categorized by album as specified in the section
70     * 6.10.2.2 of the Bluetooth AVRCP 1.5.
71     */
72    public static final long BT_FOLDER_TYPE_ALBUMS = 2;
73
74    /**
75     * The type of folder that contains folders categorized by artist as specified in the section
76     * 6.10.2.2 of the Bluetooth AVRCP 1.5.
77     */
78    public static final long BT_FOLDER_TYPE_ARTISTS = 3;
79
80    /**
81     * The type of folder that contains folders categorized by genre as specified in the section
82     * 6.10.2.2 of the Bluetooth AVRCP 1.5.
83     */
84    public static final long BT_FOLDER_TYPE_GENRES = 4;
85
86    /**
87     * The type of folder that contains folders categorized by playlist as specified in the section
88     * 6.10.2.2 of the Bluetooth AVRCP 1.5.
89     */
90    public static final long BT_FOLDER_TYPE_PLAYLISTS = 5;
91
92    /**
93     * The type of folder that contains folders categorized by year as specified in the section
94     * 6.10.2.2 of the Bluetooth AVRCP 1.5.
95     */
96    public static final long BT_FOLDER_TYPE_YEARS = 6;
97
98    /**
99     * Used as a long extra field to indicate the download status of the media item. The value
100     * should be one of the following:
101     * <ul>
102     * <li>{@link #STATUS_NOT_DOWNLOADED}</li>
103     * <li>{@link #STATUS_DOWNLOADING}</li>
104     * <li>{@link #STATUS_DOWNLOADED}</li>
105     * </ul>
106     *
107     * @see #getExtras()
108     */
109    public static final String EXTRA_DOWNLOAD_STATUS = "android.media.extra.DOWNLOAD_STATUS";
110
111    /**
112     * The status value to indicate the media item is not downloaded.
113     *
114     * @see #EXTRA_DOWNLOAD_STATUS
115     */
116    public static final long STATUS_NOT_DOWNLOADED = 0;
117
118    /**
119     * The status value to indicate the media item is being downloaded.
120     *
121     * @see #EXTRA_DOWNLOAD_STATUS
122     */
123    public static final long STATUS_DOWNLOADING = 1;
124
125    /**
126     * The status value to indicate the media item is downloaded for later offline playback.
127     *
128     * @see #EXTRA_DOWNLOAD_STATUS
129     */
130    public static final long STATUS_DOWNLOADED = 2;
131
132    /**
133     * Custom key to store a media URI on API 21-22 devices (before it became part of the
134     * framework class) when parceling/converting to and from framework objects.
135     *
136     * @hide
137     */
138    @RestrictTo(LIBRARY_GROUP)
139    public static final String DESCRIPTION_KEY_MEDIA_URI =
140            "android.support.v4.media.description.MEDIA_URI";
141    /**
142     * Custom key to store whether the original Bundle provided by the developer was null
143     *
144     * @hide
145     */
146    @RestrictTo(LIBRARY_GROUP)
147    public static final String DESCRIPTION_KEY_NULL_BUNDLE_FLAG =
148            "android.support.v4.media.description.NULL_BUNDLE_FLAG";
149    /**
150     * A unique persistent id for the content or null.
151     */
152    private final String mMediaId;
153    /**
154     * A primary title suitable for display or null.
155     */
156    private final CharSequence mTitle;
157    /**
158     * A subtitle suitable for display or null.
159     */
160    private final CharSequence mSubtitle;
161    /**
162     * A description suitable for display or null.
163     */
164    private final CharSequence mDescription;
165    /**
166     * A bitmap icon suitable for display or null.
167     */
168    private final Bitmap mIcon;
169    /**
170     * A Uri for an icon suitable for display or null.
171     */
172    private final Uri mIconUri;
173    /**
174     * Extras for opaque use by apps/system.
175     */
176    private final Bundle mExtras;
177    /**
178     * A Uri to identify this content.
179     */
180    private final Uri mMediaUri;
181
182    /**
183     * A cached copy of the equivalent framework object.
184     */
185    private Object mDescriptionObj;
186
187    MediaDescriptionCompat(String mediaId, CharSequence title, CharSequence subtitle,
188            CharSequence description, Bitmap icon, Uri iconUri, Bundle extras, Uri mediaUri) {
189        mMediaId = mediaId;
190        mTitle = title;
191        mSubtitle = subtitle;
192        mDescription = description;
193        mIcon = icon;
194        mIconUri = iconUri;
195        mExtras = extras;
196        mMediaUri = mediaUri;
197    }
198
199    MediaDescriptionCompat(Parcel in) {
200        mMediaId = in.readString();
201        mTitle = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(in);
202        mSubtitle = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(in);
203        mDescription = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(in);
204        mIcon = in.readParcelable(null);
205        mIconUri = in.readParcelable(null);
206        mExtras = in.readBundle();
207        mMediaUri = in.readParcelable(null);
208    }
209
210    /**
211     * Returns the media id or null. See
212     * {@link MediaMetadataCompat#METADATA_KEY_MEDIA_ID}.
213     */
214    @Nullable
215    public String getMediaId() {
216        return mMediaId;
217    }
218
219    /**
220     * Returns a title suitable for display or null.
221     *
222     * @return A title or null.
223     */
224    @Nullable
225    public CharSequence getTitle() {
226        return mTitle;
227    }
228
229    /**
230     * Returns a subtitle suitable for display or null.
231     *
232     * @return A subtitle or null.
233     */
234    @Nullable
235    public CharSequence getSubtitle() {
236        return mSubtitle;
237    }
238
239    /**
240     * Returns a description suitable for display or null.
241     *
242     * @return A description or null.
243     */
244    @Nullable
245    public CharSequence getDescription() {
246        return mDescription;
247    }
248
249    /**
250     * Returns a bitmap icon suitable for display or null.
251     *
252     * @return An icon or null.
253     */
254    @Nullable
255    public Bitmap getIconBitmap() {
256        return mIcon;
257    }
258
259    /**
260     * Returns a Uri for an icon suitable for display or null.
261     *
262     * @return An icon uri or null.
263     */
264    @Nullable
265    public Uri getIconUri() {
266        return mIconUri;
267    }
268
269    /**
270     * Returns any extras that were added to the description.
271     *
272     * @return A bundle of extras or null.
273     */
274    @Nullable
275    public Bundle getExtras() {
276        return mExtras;
277    }
278
279    /**
280     * Returns a Uri representing this content or null.
281     *
282     * @return A media Uri or null.
283     */
284    @Nullable
285    public Uri getMediaUri() {
286        return mMediaUri;
287    }
288
289    @Override
290    public int describeContents() {
291        return 0;
292    }
293
294    @Override
295    public void writeToParcel(Parcel dest, int flags) {
296        if (Build.VERSION.SDK_INT < 21) {
297            dest.writeString(mMediaId);
298            TextUtils.writeToParcel(mTitle, dest, flags);
299            TextUtils.writeToParcel(mSubtitle, dest, flags);
300            TextUtils.writeToParcel(mDescription, dest, flags);
301            dest.writeParcelable(mIcon, flags);
302            dest.writeParcelable(mIconUri, flags);
303            dest.writeBundle(mExtras);
304            dest.writeParcelable(mMediaUri, flags);
305        } else {
306            MediaDescriptionCompatApi21.writeToParcel(getMediaDescription(), dest, flags);
307        }
308    }
309
310    @Override
311    public String toString() {
312        return mTitle + ", " + mSubtitle + ", " + mDescription;
313    }
314
315    /**
316     * Gets the underlying framework {@link android.media.MediaDescription}
317     * object.
318     * <p>
319     * This method is only supported on
320     * {@link android.os.Build.VERSION_CODES#LOLLIPOP} and later.
321     * </p>
322     *
323     * @return An equivalent {@link android.media.MediaDescription} object, or
324     *         null if none.
325     */
326    public Object getMediaDescription() {
327        if (mDescriptionObj != null || Build.VERSION.SDK_INT < 21) {
328            return mDescriptionObj;
329        }
330        Object bob = MediaDescriptionCompatApi21.Builder.newInstance();
331        MediaDescriptionCompatApi21.Builder.setMediaId(bob, mMediaId);
332        MediaDescriptionCompatApi21.Builder.setTitle(bob, mTitle);
333        MediaDescriptionCompatApi21.Builder.setSubtitle(bob, mSubtitle);
334        MediaDescriptionCompatApi21.Builder.setDescription(bob, mDescription);
335        MediaDescriptionCompatApi21.Builder.setIconBitmap(bob, mIcon);
336        MediaDescriptionCompatApi21.Builder.setIconUri(bob, mIconUri);
337        // Media URI was not added until API 23, so add it to the Bundle of extras to
338        // ensure the data is not lost - this ensures that
339        // fromMediaDescription(getMediaDescription(mediaDescriptionCompat)) returns
340        // an equivalent MediaDescriptionCompat on all API levels
341        Bundle extras = mExtras;
342        if (Build.VERSION.SDK_INT < 23 && mMediaUri != null) {
343            if (extras == null) {
344                extras = new Bundle();
345                extras.putBoolean(DESCRIPTION_KEY_NULL_BUNDLE_FLAG, true);
346            }
347            extras.putParcelable(DESCRIPTION_KEY_MEDIA_URI, mMediaUri);
348        }
349        MediaDescriptionCompatApi21.Builder.setExtras(bob, extras);
350        if (Build.VERSION.SDK_INT >= 23) {
351            MediaDescriptionCompatApi23.Builder.setMediaUri(bob, mMediaUri);
352        }
353        mDescriptionObj = MediaDescriptionCompatApi21.Builder.build(bob);
354
355        return mDescriptionObj;
356    }
357
358    /**
359     * Creates an instance from a framework
360     * {@link android.media.MediaDescription} object.
361     * <p>
362     * This method is only supported on API 21+.
363     * </p>
364     *
365     * @param descriptionObj A {@link android.media.MediaDescription} object, or
366     *            null if none.
367     * @return An equivalent {@link MediaMetadataCompat} object, or null if
368     *         none.
369     */
370    public static MediaDescriptionCompat fromMediaDescription(Object descriptionObj) {
371        if (descriptionObj != null && Build.VERSION.SDK_INT >= 21) {
372            Builder bob = new Builder();
373            bob.setMediaId(MediaDescriptionCompatApi21.getMediaId(descriptionObj));
374            bob.setTitle(MediaDescriptionCompatApi21.getTitle(descriptionObj));
375            bob.setSubtitle(MediaDescriptionCompatApi21.getSubtitle(descriptionObj));
376            bob.setDescription(MediaDescriptionCompatApi21.getDescription(descriptionObj));
377            bob.setIconBitmap(MediaDescriptionCompatApi21.getIconBitmap(descriptionObj));
378            bob.setIconUri(MediaDescriptionCompatApi21.getIconUri(descriptionObj));
379            Bundle extras = MediaDescriptionCompatApi21.getExtras(descriptionObj);
380            Uri mediaUri = extras == null ? null :
381                    (Uri) extras.getParcelable(DESCRIPTION_KEY_MEDIA_URI);
382            if (mediaUri != null) {
383                if (extras.containsKey(DESCRIPTION_KEY_NULL_BUNDLE_FLAG) && extras.size() == 2) {
384                    // The extras were only created for the media URI, so we set it back to null to
385                    // ensure mediaDescriptionCompat.getExtras() equals
386                    // fromMediaDescription(getMediaDescription(mediaDescriptionCompat)).getExtras()
387                    extras = null;
388                } else {
389                    // Remove media URI keys to ensure mediaDescriptionCompat.getExtras().keySet()
390                    // equals fromMediaDescription(getMediaDescription(mediaDescriptionCompat))
391                    // .getExtras().keySet()
392                    extras.remove(DESCRIPTION_KEY_MEDIA_URI);
393                    extras.remove(DESCRIPTION_KEY_NULL_BUNDLE_FLAG);
394                }
395            }
396            bob.setExtras(extras);
397            if (mediaUri != null) {
398                bob.setMediaUri(mediaUri);
399            } else if (Build.VERSION.SDK_INT >= 23) {
400                bob.setMediaUri(MediaDescriptionCompatApi23.getMediaUri(descriptionObj));
401            }
402            MediaDescriptionCompat descriptionCompat = bob.build();
403            descriptionCompat.mDescriptionObj = descriptionObj;
404
405            return descriptionCompat;
406        } else {
407            return null;
408        }
409    }
410
411    public static final Parcelable.Creator<MediaDescriptionCompat> CREATOR =
412            new Parcelable.Creator<MediaDescriptionCompat>() {
413            @Override
414                public MediaDescriptionCompat createFromParcel(Parcel in) {
415                    if (Build.VERSION.SDK_INT < 21) {
416                        return new MediaDescriptionCompat(in);
417                    } else {
418                        return fromMediaDescription(MediaDescriptionCompatApi21.fromParcel(in));
419                    }
420                }
421
422            @Override
423                public MediaDescriptionCompat[] newArray(int size) {
424                    return new MediaDescriptionCompat[size];
425                }
426            };
427
428    /**
429     * Builder for {@link MediaDescriptionCompat} objects.
430     */
431    public static final class Builder {
432        private String mMediaId;
433        private CharSequence mTitle;
434        private CharSequence mSubtitle;
435        private CharSequence mDescription;
436        private Bitmap mIcon;
437        private Uri mIconUri;
438        private Bundle mExtras;
439        private Uri mMediaUri;
440
441        /**
442         * Creates an initially empty builder.
443         */
444        public Builder() {
445        }
446
447        /**
448         * Sets the media id.
449         *
450         * @param mediaId The unique id for the item or null.
451         * @return this
452         */
453        public Builder setMediaId(@Nullable String mediaId) {
454            mMediaId = mediaId;
455            return this;
456        }
457
458        /**
459         * Sets the title.
460         *
461         * @param title A title suitable for display to the user or null.
462         * @return this
463         */
464        public Builder setTitle(@Nullable CharSequence title) {
465            mTitle = title;
466            return this;
467        }
468
469        /**
470         * Sets the subtitle.
471         *
472         * @param subtitle A subtitle suitable for display to the user or null.
473         * @return this
474         */
475        public Builder setSubtitle(@Nullable CharSequence subtitle) {
476            mSubtitle = subtitle;
477            return this;
478        }
479
480        /**
481         * Sets the description.
482         *
483         * @param description A description suitable for display to the user or
484         *            null.
485         * @return this
486         */
487        public Builder setDescription(@Nullable CharSequence description) {
488            mDescription = description;
489            return this;
490        }
491
492        /**
493         * Sets the icon.
494         *
495         * @param icon A {@link Bitmap} icon suitable for display to the user or
496         *            null.
497         * @return this
498         */
499        public Builder setIconBitmap(@Nullable Bitmap icon) {
500            mIcon = icon;
501            return this;
502        }
503
504        /**
505         * Sets the icon uri.
506         *
507         * @param iconUri A {@link Uri} for an icon suitable for display to the
508         *            user or null.
509         * @return this
510         */
511        public Builder setIconUri(@Nullable Uri iconUri) {
512            mIconUri = iconUri;
513            return this;
514        }
515
516        /**
517         * Sets a bundle of extras.
518         *
519         * @param extras The extras to include with this description or null.
520         * @return this
521         */
522        public Builder setExtras(@Nullable Bundle extras) {
523            mExtras = extras;
524            return this;
525        }
526
527        /**
528         * Sets the media uri.
529         *
530         * @param mediaUri The content's {@link Uri} for the item or null.
531         * @return this
532         */
533        public Builder setMediaUri(@Nullable Uri mediaUri) {
534            mMediaUri = mediaUri;
535            return this;
536        }
537
538        /**
539         * Creates a {@link MediaDescriptionCompat} instance with the specified
540         * fields.
541         *
542         * @return A MediaDescriptionCompat instance.
543         */
544        public MediaDescriptionCompat build() {
545            return new MediaDescriptionCompat(mMediaId, mTitle, mSubtitle, mDescription, mIcon,
546                    mIconUri, mExtras, mMediaUri);
547        }
548    }
549}
550