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