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