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