MediaMetadataCompat.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.RestrictTo;
27import android.support.annotation.StringDef;
28import android.support.v4.media.session.MediaControllerCompat.TransportControls;
29import android.support.v4.util.ArrayMap;
30import android.text.TextUtils;
31import android.util.Log;
32
33import java.lang.annotation.Retention;
34import java.lang.annotation.RetentionPolicy;
35import java.util.Set;
36
37/**
38 * Contains metadata about an item, such as the title, artist, etc.
39 */
40public final class MediaMetadataCompat implements Parcelable {
41    private static final String TAG = "MediaMetadata";
42
43    /**
44     * The title of the media.
45     */
46    public static final String METADATA_KEY_TITLE = "android.media.metadata.TITLE";
47
48    /**
49     * The artist of the media.
50     */
51    public static final String METADATA_KEY_ARTIST = "android.media.metadata.ARTIST";
52
53    /**
54     * The duration of the media in ms. A negative duration indicates that the
55     * duration is unknown (or infinite).
56     */
57    public static final String METADATA_KEY_DURATION = "android.media.metadata.DURATION";
58
59    /**
60     * The album title for the media.
61     */
62    public static final String METADATA_KEY_ALBUM = "android.media.metadata.ALBUM";
63
64    /**
65     * The author of the media.
66     */
67    public static final String METADATA_KEY_AUTHOR = "android.media.metadata.AUTHOR";
68
69    /**
70     * The writer of the media.
71     */
72    public static final String METADATA_KEY_WRITER = "android.media.metadata.WRITER";
73
74    /**
75     * The composer of the media.
76     */
77    public static final String METADATA_KEY_COMPOSER = "android.media.metadata.COMPOSER";
78
79    /**
80     * The compilation status of the media.
81     */
82    public static final String METADATA_KEY_COMPILATION = "android.media.metadata.COMPILATION";
83
84    /**
85     * The date the media was created or published. The format is unspecified
86     * but RFC 3339 is recommended.
87     */
88    public static final String METADATA_KEY_DATE = "android.media.metadata.DATE";
89
90    /**
91     * The year the media was created or published as a long.
92     */
93    public static final String METADATA_KEY_YEAR = "android.media.metadata.YEAR";
94
95    /**
96     * The genre of the media.
97     */
98    public static final String METADATA_KEY_GENRE = "android.media.metadata.GENRE";
99
100    /**
101     * The track number for the media.
102     */
103    public static final String METADATA_KEY_TRACK_NUMBER = "android.media.metadata.TRACK_NUMBER";
104
105    /**
106     * The number of tracks in the media's original source.
107     */
108    public static final String METADATA_KEY_NUM_TRACKS = "android.media.metadata.NUM_TRACKS";
109
110    /**
111     * The disc number for the media's original source.
112     */
113    public static final String METADATA_KEY_DISC_NUMBER = "android.media.metadata.DISC_NUMBER";
114
115    /**
116     * The artist for the album of the media's original source.
117     */
118    public static final String METADATA_KEY_ALBUM_ARTIST = "android.media.metadata.ALBUM_ARTIST";
119
120    /**
121     * The artwork for the media as a {@link Bitmap}.
122     *
123     * The artwork should be relatively small and may be scaled down
124     * if it is too large. For higher resolution artwork
125     * {@link #METADATA_KEY_ART_URI} should be used instead.
126     */
127    public static final String METADATA_KEY_ART = "android.media.metadata.ART";
128
129    /**
130     * The artwork for the media as a Uri style String.
131     */
132    public static final String METADATA_KEY_ART_URI = "android.media.metadata.ART_URI";
133
134    /**
135     * The artwork for the album of the media's original source as a
136     * {@link Bitmap}.
137     * The artwork should be relatively small and may be scaled down
138     * if it is too large. For higher resolution artwork
139     * {@link #METADATA_KEY_ALBUM_ART_URI} should be used instead.
140     */
141    public static final String METADATA_KEY_ALBUM_ART = "android.media.metadata.ALBUM_ART";
142
143    /**
144     * The artwork for the album of the media's original source as a Uri style
145     * String.
146     */
147    public static final String METADATA_KEY_ALBUM_ART_URI = "android.media.metadata.ALBUM_ART_URI";
148
149    /**
150     * The user's rating for the media.
151     *
152     * @see RatingCompat
153     */
154    public static final String METADATA_KEY_USER_RATING = "android.media.metadata.USER_RATING";
155
156    /**
157     * The overall rating for the media.
158     *
159     * @see RatingCompat
160     */
161    public static final String METADATA_KEY_RATING = "android.media.metadata.RATING";
162
163    /**
164     * A title that is suitable for display to the user. This will generally be
165     * the same as {@link #METADATA_KEY_TITLE} but may differ for some formats.
166     * When displaying media described by this metadata this should be preferred
167     * if present.
168     */
169    public static final String METADATA_KEY_DISPLAY_TITLE = "android.media.metadata.DISPLAY_TITLE";
170
171    /**
172     * A subtitle that is suitable for display to the user. When displaying a
173     * second line for media described by this metadata this should be preferred
174     * to other fields if present.
175     */
176    public static final String METADATA_KEY_DISPLAY_SUBTITLE
177            = "android.media.metadata.DISPLAY_SUBTITLE";
178
179    /**
180     * A description that is suitable for display to the user. When displaying
181     * more information for media described by this metadata this should be
182     * preferred to other fields if present.
183     */
184    public static final String METADATA_KEY_DISPLAY_DESCRIPTION
185            = "android.media.metadata.DISPLAY_DESCRIPTION";
186
187    /**
188     * An icon or thumbnail that is suitable for display to the user. When
189     * displaying an icon for media described by this metadata this should be
190     * preferred to other fields if present. This must be a {@link Bitmap}.
191     *
192     * The icon should be relatively small and may be scaled down
193     * if it is too large. For higher resolution artwork
194     * {@link #METADATA_KEY_DISPLAY_ICON_URI} should be used instead.
195     */
196    public static final String METADATA_KEY_DISPLAY_ICON
197            = "android.media.metadata.DISPLAY_ICON";
198
199    /**
200     * An icon or thumbnail that is suitable for display to the user. When
201     * displaying more information for media described by this metadata the
202     * display description should be preferred to other fields when present.
203     * This must be a Uri style String.
204     */
205    public static final String METADATA_KEY_DISPLAY_ICON_URI
206            = "android.media.metadata.DISPLAY_ICON_URI";
207
208    /**
209     * A String key for identifying the content. This value is specific to the
210     * service providing the content. If used, this should be a persistent
211     * unique key for the underlying content.
212     */
213    public static final String METADATA_KEY_MEDIA_ID = "android.media.metadata.MEDIA_ID";
214
215    /**
216     * A Uri formatted String representing the content. This value is specific to the
217     * service providing the content. It may be used with
218     * {@link TransportControls#playFromUri(Uri, Bundle)}
219     * to initiate playback when provided by a {@link MediaBrowserCompat} connected to
220     * the same app.
221     */
222    public static final String METADATA_KEY_MEDIA_URI = "android.media.metadata.MEDIA_URI";
223
224    /**
225     * The bluetooth folder type of the media specified in the section 6.10.2.2 of the Bluetooth
226     * AVRCP 1.5. It should be one of the following:
227     * <ul>
228     * <li>{@link MediaDescriptionCompat#BT_FOLDER_TYPE_MIXED}</li>
229     * <li>{@link MediaDescriptionCompat#BT_FOLDER_TYPE_TITLES}</li>
230     * <li>{@link MediaDescriptionCompat#BT_FOLDER_TYPE_ALBUMS}</li>
231     * <li>{@link MediaDescriptionCompat#BT_FOLDER_TYPE_ARTISTS}</li>
232     * <li>{@link MediaDescriptionCompat#BT_FOLDER_TYPE_GENRES}</li>
233     * <li>{@link MediaDescriptionCompat#BT_FOLDER_TYPE_PLAYLISTS}</li>
234     * <li>{@link MediaDescriptionCompat#BT_FOLDER_TYPE_YEARS}</li>
235     * </ul>
236     */
237    public static final String METADATA_KEY_BT_FOLDER_TYPE
238            = "android.media.metadata.BT_FOLDER_TYPE";
239
240    /**
241     * Whether the media is an advertisement. A value of 0 indicates it is not an advertisement. A
242     * value of 1 or non-zero indicates it is an advertisement. If not specified, this value is set
243     * to 0 by default.
244     */
245    public static final String METADATA_KEY_ADVERTISEMENT = "android.media.metadata.ADVERTISEMENT";
246
247    /**
248     * The download status of the media which will be used for later offline playback. It should be
249     * one of the following:
250     *
251     * <ul>
252     * <li>{@link MediaDescriptionCompat#STATUS_NOT_DOWNLOADED}</li>
253     * <li>{@link MediaDescriptionCompat#STATUS_DOWNLOADING}</li>
254     * <li>{@link MediaDescriptionCompat#STATUS_DOWNLOADED}</li>
255     * </ul>
256     */
257    public static final String METADATA_KEY_DOWNLOAD_STATUS =
258            "android.media.metadata.DOWNLOAD_STATUS";
259
260    /**
261     * @hide
262     */
263    @RestrictTo(LIBRARY_GROUP)
264    @StringDef({METADATA_KEY_TITLE, METADATA_KEY_ARTIST, METADATA_KEY_ALBUM, METADATA_KEY_AUTHOR,
265            METADATA_KEY_WRITER, METADATA_KEY_COMPOSER, METADATA_KEY_COMPILATION,
266            METADATA_KEY_DATE, METADATA_KEY_GENRE, METADATA_KEY_ALBUM_ARTIST, METADATA_KEY_ART_URI,
267            METADATA_KEY_ALBUM_ART_URI, METADATA_KEY_DISPLAY_TITLE, METADATA_KEY_DISPLAY_SUBTITLE,
268            METADATA_KEY_DISPLAY_DESCRIPTION, METADATA_KEY_DISPLAY_ICON_URI,
269            METADATA_KEY_MEDIA_ID, METADATA_KEY_MEDIA_URI})
270    @Retention(RetentionPolicy.SOURCE)
271    public @interface TextKey {}
272
273    /**
274     * @hide
275     */
276    @RestrictTo(LIBRARY_GROUP)
277    @StringDef({METADATA_KEY_DURATION, METADATA_KEY_YEAR, METADATA_KEY_TRACK_NUMBER,
278            METADATA_KEY_NUM_TRACKS, METADATA_KEY_DISC_NUMBER, METADATA_KEY_BT_FOLDER_TYPE,
279            METADATA_KEY_ADVERTISEMENT, METADATA_KEY_DOWNLOAD_STATUS})
280    @Retention(RetentionPolicy.SOURCE)
281    public @interface LongKey {}
282
283    /**
284     * @hide
285     */
286    @RestrictTo(LIBRARY_GROUP)
287    @StringDef({METADATA_KEY_ART, METADATA_KEY_ALBUM_ART, METADATA_KEY_DISPLAY_ICON})
288    @Retention(RetentionPolicy.SOURCE)
289    public @interface BitmapKey {}
290
291    /**
292     * @hide
293     */
294    @RestrictTo(LIBRARY_GROUP)
295    @StringDef({METADATA_KEY_USER_RATING, METADATA_KEY_RATING})
296    @Retention(RetentionPolicy.SOURCE)
297    public @interface RatingKey {}
298
299    static final int METADATA_TYPE_LONG = 0;
300    static final int METADATA_TYPE_TEXT = 1;
301    static final int METADATA_TYPE_BITMAP = 2;
302    static final int METADATA_TYPE_RATING = 3;
303    static final ArrayMap<String, Integer> METADATA_KEYS_TYPE;
304
305    static {
306        METADATA_KEYS_TYPE = new ArrayMap<String, Integer>();
307        METADATA_KEYS_TYPE.put(METADATA_KEY_TITLE, METADATA_TYPE_TEXT);
308        METADATA_KEYS_TYPE.put(METADATA_KEY_ARTIST, METADATA_TYPE_TEXT);
309        METADATA_KEYS_TYPE.put(METADATA_KEY_DURATION, METADATA_TYPE_LONG);
310        METADATA_KEYS_TYPE.put(METADATA_KEY_ALBUM, METADATA_TYPE_TEXT);
311        METADATA_KEYS_TYPE.put(METADATA_KEY_AUTHOR, METADATA_TYPE_TEXT);
312        METADATA_KEYS_TYPE.put(METADATA_KEY_WRITER, METADATA_TYPE_TEXT);
313        METADATA_KEYS_TYPE.put(METADATA_KEY_COMPOSER, METADATA_TYPE_TEXT);
314        METADATA_KEYS_TYPE.put(METADATA_KEY_COMPILATION, METADATA_TYPE_TEXT);
315        METADATA_KEYS_TYPE.put(METADATA_KEY_DATE, METADATA_TYPE_TEXT);
316        METADATA_KEYS_TYPE.put(METADATA_KEY_YEAR, METADATA_TYPE_LONG);
317        METADATA_KEYS_TYPE.put(METADATA_KEY_GENRE, METADATA_TYPE_TEXT);
318        METADATA_KEYS_TYPE.put(METADATA_KEY_TRACK_NUMBER, METADATA_TYPE_LONG);
319        METADATA_KEYS_TYPE.put(METADATA_KEY_NUM_TRACKS, METADATA_TYPE_LONG);
320        METADATA_KEYS_TYPE.put(METADATA_KEY_DISC_NUMBER, METADATA_TYPE_LONG);
321        METADATA_KEYS_TYPE.put(METADATA_KEY_ALBUM_ARTIST, METADATA_TYPE_TEXT);
322        METADATA_KEYS_TYPE.put(METADATA_KEY_ART, METADATA_TYPE_BITMAP);
323        METADATA_KEYS_TYPE.put(METADATA_KEY_ART_URI, METADATA_TYPE_TEXT);
324        METADATA_KEYS_TYPE.put(METADATA_KEY_ALBUM_ART, METADATA_TYPE_BITMAP);
325        METADATA_KEYS_TYPE.put(METADATA_KEY_ALBUM_ART_URI, METADATA_TYPE_TEXT);
326        METADATA_KEYS_TYPE.put(METADATA_KEY_USER_RATING, METADATA_TYPE_RATING);
327        METADATA_KEYS_TYPE.put(METADATA_KEY_RATING, METADATA_TYPE_RATING);
328        METADATA_KEYS_TYPE.put(METADATA_KEY_DISPLAY_TITLE, METADATA_TYPE_TEXT);
329        METADATA_KEYS_TYPE.put(METADATA_KEY_DISPLAY_SUBTITLE, METADATA_TYPE_TEXT);
330        METADATA_KEYS_TYPE.put(METADATA_KEY_DISPLAY_DESCRIPTION, METADATA_TYPE_TEXT);
331        METADATA_KEYS_TYPE.put(METADATA_KEY_DISPLAY_ICON, METADATA_TYPE_BITMAP);
332        METADATA_KEYS_TYPE.put(METADATA_KEY_DISPLAY_ICON_URI, METADATA_TYPE_TEXT);
333        METADATA_KEYS_TYPE.put(METADATA_KEY_MEDIA_ID, METADATA_TYPE_TEXT);
334        METADATA_KEYS_TYPE.put(METADATA_KEY_BT_FOLDER_TYPE, METADATA_TYPE_LONG);
335        METADATA_KEYS_TYPE.put(METADATA_KEY_MEDIA_URI, METADATA_TYPE_TEXT);
336        METADATA_KEYS_TYPE.put(METADATA_KEY_ADVERTISEMENT, METADATA_TYPE_LONG);
337        METADATA_KEYS_TYPE.put(METADATA_KEY_DOWNLOAD_STATUS, METADATA_TYPE_LONG);
338    }
339
340    private static final @TextKey String[] PREFERRED_DESCRIPTION_ORDER = {
341            METADATA_KEY_TITLE,
342            METADATA_KEY_ARTIST,
343            METADATA_KEY_ALBUM,
344            METADATA_KEY_ALBUM_ARTIST,
345            METADATA_KEY_WRITER,
346            METADATA_KEY_AUTHOR,
347            METADATA_KEY_COMPOSER
348    };
349
350    private static final @BitmapKey String[] PREFERRED_BITMAP_ORDER = {
351            METADATA_KEY_DISPLAY_ICON,
352            METADATA_KEY_ART,
353            METADATA_KEY_ALBUM_ART
354    };
355
356    private static final @TextKey String[] PREFERRED_URI_ORDER = {
357            METADATA_KEY_DISPLAY_ICON_URI,
358            METADATA_KEY_ART_URI,
359            METADATA_KEY_ALBUM_ART_URI
360    };
361
362    final Bundle mBundle;
363    private Object mMetadataObj;
364    private MediaDescriptionCompat mDescription;
365
366    MediaMetadataCompat(Bundle bundle) {
367        mBundle = new Bundle(bundle);
368    }
369
370    MediaMetadataCompat(Parcel in) {
371        mBundle = in.readBundle();
372    }
373
374    /**
375     * Returns true if the given key is contained in the metadata
376     *
377     * @param key a String key
378     * @return true if the key exists in this metadata, false otherwise
379     */
380    public boolean containsKey(String key) {
381        return mBundle.containsKey(key);
382    }
383
384    /**
385     * Returns the value associated with the given key, or null if no mapping of
386     * the desired type exists for the given key or a null value is explicitly
387     * associated with the key.
388     *
389     * @param key The key the value is stored under
390     * @return a CharSequence value, or null
391     */
392    public CharSequence getText(@TextKey String key) {
393        return mBundle.getCharSequence(key);
394    }
395
396    /**
397     * Returns the value associated with the given key, or null if no mapping of
398     * the desired type exists for the given key or a null value is explicitly
399     * associated with the key.
400     *
401     * @param key The key the value is stored under
402     * @return a String value, or null
403     */
404    public String getString(@TextKey String key) {
405        CharSequence text = mBundle.getCharSequence(key);
406        if (text != null) {
407            return text.toString();
408        }
409        return null;
410    }
411
412    /**
413     * Returns the value associated with the given key, or 0L if no long exists
414     * for the given key.
415     *
416     * @param key The key the value is stored under
417     * @return a long value
418     */
419    public long getLong(@LongKey String key) {
420        return mBundle.getLong(key, 0);
421    }
422
423    /**
424     * Return a {@link RatingCompat} for the given key or null if no rating exists for
425     * the given key.
426     *
427     * @param key The key the value is stored under
428     * @return A {@link RatingCompat} or null
429     */
430    public RatingCompat getRating(@RatingKey String key) {
431        RatingCompat rating = null;
432        try {
433            if (Build.VERSION.SDK_INT >= 19) {
434                // On platform version 19 or higher, mBundle stores a Rating object. Convert it to
435                // RatingCompat.
436                rating = RatingCompat.fromRating(mBundle.getParcelable(key));
437            } else {
438                rating = mBundle.getParcelable(key);
439            }
440        } catch (Exception e) {
441            // ignore, value was not a bitmap
442            Log.w(TAG, "Failed to retrieve a key as Rating.", e);
443        }
444        return rating;
445    }
446
447    /**
448     * Return a {@link Bitmap} for the given key or null if no bitmap exists for
449     * the given key.
450     *
451     * @param key The key the value is stored under
452     * @return A {@link Bitmap} or null
453     */
454    public Bitmap getBitmap(@BitmapKey String key) {
455        Bitmap bmp = null;
456        try {
457            bmp = mBundle.getParcelable(key);
458        } catch (Exception e) {
459            // ignore, value was not a bitmap
460            Log.w(TAG, "Failed to retrieve a key as Bitmap.", e);
461        }
462        return bmp;
463    }
464
465    /**
466     * Returns a simple description of this metadata for display purposes.
467     *
468     * @return A simple description of this metadata.
469     */
470    public MediaDescriptionCompat getDescription() {
471        if (mDescription != null) {
472            return mDescription;
473        }
474
475        String mediaId = getString(METADATA_KEY_MEDIA_ID);
476
477        CharSequence[] text = new CharSequence[3];
478        Bitmap icon = null;
479        Uri iconUri = null;
480
481        // First handle the case where display data is set already
482        CharSequence displayText = getText(METADATA_KEY_DISPLAY_TITLE);
483        if (!TextUtils.isEmpty(displayText)) {
484            // If they have a display title use only display data, otherwise use
485            // our best bets
486            text[0] = displayText;
487            text[1] = getText(METADATA_KEY_DISPLAY_SUBTITLE);
488            text[2] = getText(METADATA_KEY_DISPLAY_DESCRIPTION);
489        } else {
490            // Use whatever fields we can
491            int textIndex = 0;
492            int keyIndex = 0;
493            while (textIndex < text.length && keyIndex < PREFERRED_DESCRIPTION_ORDER.length) {
494                CharSequence next = getText(PREFERRED_DESCRIPTION_ORDER[keyIndex++]);
495                if (!TextUtils.isEmpty(next)) {
496                    // Fill in the next empty bit of text
497                    text[textIndex++] = next;
498                }
499            }
500        }
501
502        // Get the best art bitmap we can find
503        for (int i = 0; i < PREFERRED_BITMAP_ORDER.length; i++) {
504            Bitmap next = getBitmap(PREFERRED_BITMAP_ORDER[i]);
505            if (next != null) {
506                icon = next;
507                break;
508            }
509        }
510
511        // Get the best Uri we can find
512        for (int i = 0; i < PREFERRED_URI_ORDER.length; i++) {
513            String next = getString(PREFERRED_URI_ORDER[i]);
514            if (!TextUtils.isEmpty(next)) {
515                iconUri = Uri.parse(next);
516                break;
517            }
518        }
519
520        Uri mediaUri = null;
521        String mediaUriStr = getString(METADATA_KEY_MEDIA_URI);
522        if (!TextUtils.isEmpty(mediaUriStr)) {
523            mediaUri = Uri.parse(mediaUriStr);
524        }
525
526        MediaDescriptionCompat.Builder bob = new MediaDescriptionCompat.Builder();
527        bob.setMediaId(mediaId);
528        bob.setTitle(text[0]);
529        bob.setSubtitle(text[1]);
530        bob.setDescription(text[2]);
531        bob.setIconBitmap(icon);
532        bob.setIconUri(iconUri);
533        bob.setMediaUri(mediaUri);
534
535        Bundle bundle = new Bundle();
536        if (mBundle.containsKey(METADATA_KEY_BT_FOLDER_TYPE)) {
537            bundle.putLong(MediaDescriptionCompat.EXTRA_BT_FOLDER_TYPE,
538                    getLong(METADATA_KEY_BT_FOLDER_TYPE));
539        }
540        if (mBundle.containsKey(METADATA_KEY_DOWNLOAD_STATUS)) {
541            bundle.putLong(MediaDescriptionCompat.EXTRA_DOWNLOAD_STATUS,
542                    getLong(METADATA_KEY_DOWNLOAD_STATUS));
543        }
544        if (!bundle.isEmpty()) {
545            bob.setExtras(bundle);
546        }
547        mDescription = bob.build();
548
549        return mDescription;
550    }
551
552    @Override
553    public int describeContents() {
554        return 0;
555    }
556
557    @Override
558    public void writeToParcel(Parcel dest, int flags) {
559        dest.writeBundle(mBundle);
560    }
561
562    /**
563     * Get the number of fields in this metadata.
564     *
565     * @return The number of fields in the metadata.
566     */
567    public int size() {
568        return mBundle.size();
569    }
570
571    /**
572     * Returns a Set containing the Strings used as keys in this metadata.
573     *
574     * @return a Set of String keys
575     */
576    public Set<String> keySet() {
577        return mBundle.keySet();
578    }
579
580    /**
581     * Gets the bundle backing the metadata object. This is available to support
582     * backwards compatibility. Apps should not modify the bundle directly.
583     *
584     * @return The Bundle backing this metadata.
585     */
586    public Bundle getBundle() {
587        return mBundle;
588    }
589
590    /**
591     * Creates an instance from a framework {@link android.media.MediaMetadata}
592     * object.
593     * <p>
594     * This method is only supported on
595     * {@link android.os.Build.VERSION_CODES#LOLLIPOP} and later.
596     * </p>
597     *
598     * @param metadataObj A {@link android.media.MediaMetadata} object, or null
599     *            if none.
600     * @return An equivalent {@link MediaMetadataCompat} object, or null if
601     *         none.
602     */
603    public static MediaMetadataCompat fromMediaMetadata(Object metadataObj) {
604        if (metadataObj != null && Build.VERSION.SDK_INT >= 21) {
605            Parcel p = Parcel.obtain();
606            MediaMetadataCompatApi21.writeToParcel(metadataObj, p, 0);
607            p.setDataPosition(0);
608            MediaMetadataCompat metadata = MediaMetadataCompat.CREATOR.createFromParcel(p);
609            p.recycle();
610            metadata.mMetadataObj = metadataObj;
611            return metadata;
612        } else {
613            return null;
614        }
615    }
616
617    /**
618     * Gets the underlying framework {@link android.media.MediaMetadata} object.
619     * <p>
620     * This method is only supported on
621     * {@link android.os.Build.VERSION_CODES#LOLLIPOP} and later.
622     * </p>
623     *
624     * @return An equivalent {@link android.media.MediaMetadata} object, or null
625     *         if none.
626     */
627    public Object getMediaMetadata() {
628        if (mMetadataObj == null && Build.VERSION.SDK_INT >= 21) {
629            Parcel p = Parcel.obtain();
630            writeToParcel(p, 0);
631            p.setDataPosition(0);
632            mMetadataObj = MediaMetadataCompatApi21.createFromParcel(p);
633            p.recycle();
634        }
635        return mMetadataObj;
636    }
637
638    public static final Parcelable.Creator<MediaMetadataCompat> CREATOR =
639            new Parcelable.Creator<MediaMetadataCompat>() {
640                @Override
641                public MediaMetadataCompat createFromParcel(Parcel in) {
642                    return new MediaMetadataCompat(in);
643                }
644
645                @Override
646                public MediaMetadataCompat[] newArray(int size) {
647                    return new MediaMetadataCompat[size];
648                }
649            };
650
651    /**
652     * Use to build MediaMetadata objects. The system defined metadata keys must
653     * use the appropriate data type.
654     */
655    public static final class Builder {
656        private final Bundle mBundle;
657
658        /**
659         * Create an empty Builder. Any field that should be included in the
660         * {@link MediaMetadataCompat} must be added.
661         */
662        public Builder() {
663            mBundle = new Bundle();
664        }
665
666        /**
667         * Create a Builder using a {@link MediaMetadataCompat} instance to set the
668         * initial values. All fields in the source metadata will be included in
669         * the new metadata. Fields can be overwritten by adding the same key.
670         *
671         * @param source
672         */
673        public Builder(MediaMetadataCompat source) {
674            mBundle = new Bundle(source.mBundle);
675        }
676
677        /**
678         * Create a Builder using a {@link MediaMetadataCompat} instance to set
679         * initial values, but replace bitmaps with a scaled down copy if they
680         * are larger than maxBitmapSize.
681         * <p>
682         * This also deep-copies the bitmaps for {@link #METADATA_KEY_ART} and
683         * {@link #METADATA_KEY_ALBUM_ART} on
684         * {@link android.os.Build.VERSION_CODES#ICE_CREAM_SANDWICH} and later
685         * to prevent bitmaps from being recycled by RCC.
686         *
687         * @param source The original metadata to copy.
688         * @param maxBitmapSize The maximum height/width for bitmaps contained
689         *            in the metadata.
690         * @hide
691         */
692        @RestrictTo(LIBRARY_GROUP)
693        public Builder(MediaMetadataCompat source, int maxBitmapSize) {
694            this(source);
695            for (String key : mBundle.keySet()) {
696                Object value = mBundle.get(key);
697                if (value != null && value instanceof Bitmap) {
698                    Bitmap bmp = (Bitmap) value;
699                    if (bmp.getHeight() > maxBitmapSize || bmp.getWidth() > maxBitmapSize) {
700                        putBitmap(key, scaleBitmap(bmp, maxBitmapSize));
701                    } else if (Build.VERSION.SDK_INT >= 14 &&
702                            (key.equals(METADATA_KEY_ART) || key.equals(METADATA_KEY_ALBUM_ART))) {
703                        putBitmap(key, bmp.copy(bmp.getConfig(), false));
704                    }
705                }
706            }
707        }
708
709        /**
710         * Put a CharSequence value into the metadata. Custom keys may be used,
711         * but if the METADATA_KEYs defined in this class are used they may only
712         * be one of the following:
713         * <ul>
714         * <li>{@link #METADATA_KEY_TITLE}</li>
715         * <li>{@link #METADATA_KEY_ARTIST}</li>
716         * <li>{@link #METADATA_KEY_ALBUM}</li>
717         * <li>{@link #METADATA_KEY_AUTHOR}</li>
718         * <li>{@link #METADATA_KEY_WRITER}</li>
719         * <li>{@link #METADATA_KEY_COMPOSER}</li>
720         * <li>{@link #METADATA_KEY_DATE}</li>
721         * <li>{@link #METADATA_KEY_GENRE}</li>
722         * <li>{@link #METADATA_KEY_ALBUM_ARTIST}</li>
723         * <li>{@link #METADATA_KEY_ART_URI}</li>
724         * <li>{@link #METADATA_KEY_ALBUM_ART_URI}</li>
725         * <li>{@link #METADATA_KEY_DISPLAY_TITLE}</li>
726         * <li>{@link #METADATA_KEY_DISPLAY_SUBTITLE}</li>
727         * <li>{@link #METADATA_KEY_DISPLAY_DESCRIPTION}</li>
728         * <li>{@link #METADATA_KEY_DISPLAY_ICON_URI}</li>
729         * </ul>
730         *
731         * @param key The key for referencing this value
732         * @param value The CharSequence value to store
733         * @return The Builder to allow chaining
734         */
735        public Builder putText(@TextKey String key, CharSequence value) {
736            if (METADATA_KEYS_TYPE.containsKey(key)) {
737                if (METADATA_KEYS_TYPE.get(key) != METADATA_TYPE_TEXT) {
738                    throw new IllegalArgumentException("The " + key
739                            + " key cannot be used to put a CharSequence");
740                }
741            }
742            mBundle.putCharSequence(key, value);
743            return this;
744        }
745
746        /**
747         * Put a String value into the metadata. Custom keys may be used, but if
748         * the METADATA_KEYs defined in this class are used they may only be one
749         * of the following:
750         * <ul>
751         * <li>{@link #METADATA_KEY_TITLE}</li>
752         * <li>{@link #METADATA_KEY_ARTIST}</li>
753         * <li>{@link #METADATA_KEY_ALBUM}</li>
754         * <li>{@link #METADATA_KEY_AUTHOR}</li>
755         * <li>{@link #METADATA_KEY_WRITER}</li>
756         * <li>{@link #METADATA_KEY_COMPOSER}</li>
757         * <li>{@link #METADATA_KEY_DATE}</li>
758         * <li>{@link #METADATA_KEY_GENRE}</li>
759         * <li>{@link #METADATA_KEY_ALBUM_ARTIST}</li>
760         * <li>{@link #METADATA_KEY_ART_URI}</li>
761         * <li>{@link #METADATA_KEY_ALBUM_ART_URI}</li>
762         * <li>{@link #METADATA_KEY_DISPLAY_TITLE}</li>
763         * <li>{@link #METADATA_KEY_DISPLAY_SUBTITLE}</li>
764         * <li>{@link #METADATA_KEY_DISPLAY_DESCRIPTION}</li>
765         * <li>{@link #METADATA_KEY_DISPLAY_ICON_URI}</li>
766         * </ul>
767         *
768         * @param key The key for referencing this value
769         * @param value The String value to store
770         * @return The Builder to allow chaining
771         */
772        public Builder putString(@TextKey String key, String value) {
773            if (METADATA_KEYS_TYPE.containsKey(key)) {
774                if (METADATA_KEYS_TYPE.get(key) != METADATA_TYPE_TEXT) {
775                    throw new IllegalArgumentException("The " + key
776                            + " key cannot be used to put a String");
777                }
778            }
779            mBundle.putCharSequence(key, value);
780            return this;
781        }
782
783        /**
784         * Put a long value into the metadata. Custom keys may be used, but if
785         * the METADATA_KEYs defined in this class are used they may only be one
786         * of the following:
787         * <ul>
788         * <li>{@link #METADATA_KEY_DURATION}</li>
789         * <li>{@link #METADATA_KEY_TRACK_NUMBER}</li>
790         * <li>{@link #METADATA_KEY_NUM_TRACKS}</li>
791         * <li>{@link #METADATA_KEY_DISC_NUMBER}</li>
792         * <li>{@link #METADATA_KEY_YEAR}</li>
793         * <li>{@link #METADATA_KEY_BT_FOLDER_TYPE}</li>
794         * <li>{@link #METADATA_KEY_ADVERTISEMENT}</li>
795         * <li>{@link #METADATA_KEY_DOWNLOAD_STATUS}</li>
796         * </ul>
797         *
798         * @param key The key for referencing this value
799         * @param value The String value to store
800         * @return The Builder to allow chaining
801         */
802        public Builder putLong(@LongKey String key, long value) {
803            if (METADATA_KEYS_TYPE.containsKey(key)) {
804                if (METADATA_KEYS_TYPE.get(key) != METADATA_TYPE_LONG) {
805                    throw new IllegalArgumentException("The " + key
806                            + " key cannot be used to put a long");
807                }
808            }
809            mBundle.putLong(key, value);
810            return this;
811        }
812
813        /**
814         * Put a {@link RatingCompat} into the metadata. Custom keys may be used, but
815         * if the METADATA_KEYs defined in this class are used they may only be
816         * one of the following:
817         * <ul>
818         * <li>{@link #METADATA_KEY_RATING}</li>
819         * <li>{@link #METADATA_KEY_USER_RATING}</li>
820         * </ul>
821         *
822         * @param key The key for referencing this value
823         * @param value The String value to store
824         * @return The Builder to allow chaining
825         */
826        public Builder putRating(@RatingKey String key, RatingCompat value) {
827            if (METADATA_KEYS_TYPE.containsKey(key)) {
828                if (METADATA_KEYS_TYPE.get(key) != METADATA_TYPE_RATING) {
829                    throw new IllegalArgumentException("The " + key
830                            + " key cannot be used to put a Rating");
831                }
832            }
833            if (Build.VERSION.SDK_INT >= 19) {
834                // On platform version 19 or higher, use Rating instead of RatingCompat so mBundle
835                // can be unmarshalled.
836                mBundle.putParcelable(key, (Parcelable) value.getRating());
837            } else {
838                mBundle.putParcelable(key, value);
839            }
840            return this;
841        }
842
843        /**
844         * Put a {@link Bitmap} into the metadata. Custom keys may be used, but
845         * if the METADATA_KEYs defined in this class are used they may only be
846         * one of the following:
847         * <ul>
848         * <li>{@link #METADATA_KEY_ART}</li>
849         * <li>{@link #METADATA_KEY_ALBUM_ART}</li>
850         * <li>{@link #METADATA_KEY_DISPLAY_ICON}</li>
851         * </ul>
852         * Large bitmaps may be scaled down when
853         * {@link android.support.v4.media.session.MediaSessionCompat#setMetadata} is called.
854         * To pass full resolution images {@link Uri Uris} should be used with
855         * {@link #putString}.
856         *
857         * @param key The key for referencing this value
858         * @param value The Bitmap to store
859         * @return The Builder to allow chaining
860         */
861        public Builder putBitmap(@BitmapKey String key, Bitmap value) {
862            if (METADATA_KEYS_TYPE.containsKey(key)) {
863                if (METADATA_KEYS_TYPE.get(key) != METADATA_TYPE_BITMAP) {
864                    throw new IllegalArgumentException("The " + key
865                            + " key cannot be used to put a Bitmap");
866                }
867            }
868            mBundle.putParcelable(key, value);
869            return this;
870        }
871
872        /**
873         * Creates a {@link MediaMetadataCompat} instance with the specified fields.
874         *
875         * @return The new MediaMetadata instance
876         */
877        public MediaMetadataCompat build() {
878            return new MediaMetadataCompat(mBundle);
879        }
880
881        private Bitmap scaleBitmap(Bitmap bmp, int maxSize) {
882            float maxSizeF = maxSize;
883            float widthScale = maxSizeF / bmp.getWidth();
884            float heightScale = maxSizeF / bmp.getHeight();
885            float scale = Math.min(widthScale, heightScale);
886            int height = (int) (bmp.getHeight() * scale);
887            int width = (int) (bmp.getWidth() * scale);
888            return Bitmap.createScaledBitmap(bmp, width, height, true);
889        }
890    }
891
892}
893