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