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