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