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