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  */
16 package android.media;
17 
18 import android.annotation.NonNull;
19 import android.content.ContentResolver;
20 import android.graphics.Bitmap;
21 import android.graphics.BitmapFactory;
22 import android.media.browse.MediaBrowser;
23 import android.media.session.MediaController;
24 import android.net.Uri;
25 import android.os.Bundle;
26 import android.os.Parcel;
27 import android.os.Parcelable;
28 import android.text.TextUtils;
29 import android.util.ArrayMap;
30 import android.util.Log;
31 import android.util.SparseArray;
32 
33 import java.util.ArrayList;
34 import java.util.Set;
35 
36 /**
37  * Contains metadata about an item, such as the title, artist, etc.
38  */
39 public 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>();
METADATA_KEYS_TYPE.put(METADATA_KEY_TITLE, METADATA_TYPE_TEXT)267         METADATA_KEYS_TYPE.put(METADATA_KEY_TITLE, METADATA_TYPE_TEXT);
METADATA_KEYS_TYPE.put(METADATA_KEY_ARTIST, METADATA_TYPE_TEXT)268         METADATA_KEYS_TYPE.put(METADATA_KEY_ARTIST, METADATA_TYPE_TEXT);
METADATA_KEYS_TYPE.put(METADATA_KEY_DURATION, METADATA_TYPE_LONG)269         METADATA_KEYS_TYPE.put(METADATA_KEY_DURATION, METADATA_TYPE_LONG);
METADATA_KEYS_TYPE.put(METADATA_KEY_ALBUM, METADATA_TYPE_TEXT)270         METADATA_KEYS_TYPE.put(METADATA_KEY_ALBUM, METADATA_TYPE_TEXT);
METADATA_KEYS_TYPE.put(METADATA_KEY_AUTHOR, METADATA_TYPE_TEXT)271         METADATA_KEYS_TYPE.put(METADATA_KEY_AUTHOR, METADATA_TYPE_TEXT);
METADATA_KEYS_TYPE.put(METADATA_KEY_WRITER, METADATA_TYPE_TEXT)272         METADATA_KEYS_TYPE.put(METADATA_KEY_WRITER, METADATA_TYPE_TEXT);
METADATA_KEYS_TYPE.put(METADATA_KEY_COMPOSER, METADATA_TYPE_TEXT)273         METADATA_KEYS_TYPE.put(METADATA_KEY_COMPOSER, METADATA_TYPE_TEXT);
METADATA_KEYS_TYPE.put(METADATA_KEY_COMPILATION, METADATA_TYPE_TEXT)274         METADATA_KEYS_TYPE.put(METADATA_KEY_COMPILATION, METADATA_TYPE_TEXT);
METADATA_KEYS_TYPE.put(METADATA_KEY_DATE, METADATA_TYPE_TEXT)275         METADATA_KEYS_TYPE.put(METADATA_KEY_DATE, METADATA_TYPE_TEXT);
METADATA_KEYS_TYPE.put(METADATA_KEY_YEAR, METADATA_TYPE_LONG)276         METADATA_KEYS_TYPE.put(METADATA_KEY_YEAR, METADATA_TYPE_LONG);
METADATA_KEYS_TYPE.put(METADATA_KEY_GENRE, METADATA_TYPE_TEXT)277         METADATA_KEYS_TYPE.put(METADATA_KEY_GENRE, METADATA_TYPE_TEXT);
METADATA_KEYS_TYPE.put(METADATA_KEY_TRACK_NUMBER, METADATA_TYPE_LONG)278         METADATA_KEYS_TYPE.put(METADATA_KEY_TRACK_NUMBER, METADATA_TYPE_LONG);
METADATA_KEYS_TYPE.put(METADATA_KEY_NUM_TRACKS, METADATA_TYPE_LONG)279         METADATA_KEYS_TYPE.put(METADATA_KEY_NUM_TRACKS, METADATA_TYPE_LONG);
METADATA_KEYS_TYPE.put(METADATA_KEY_DISC_NUMBER, METADATA_TYPE_LONG)280         METADATA_KEYS_TYPE.put(METADATA_KEY_DISC_NUMBER, METADATA_TYPE_LONG);
METADATA_KEYS_TYPE.put(METADATA_KEY_ALBUM_ARTIST, METADATA_TYPE_TEXT)281         METADATA_KEYS_TYPE.put(METADATA_KEY_ALBUM_ARTIST, METADATA_TYPE_TEXT);
METADATA_KEYS_TYPE.put(METADATA_KEY_ART, METADATA_TYPE_BITMAP)282         METADATA_KEYS_TYPE.put(METADATA_KEY_ART, METADATA_TYPE_BITMAP);
METADATA_KEYS_TYPE.put(METADATA_KEY_ART_URI, METADATA_TYPE_TEXT)283         METADATA_KEYS_TYPE.put(METADATA_KEY_ART_URI, METADATA_TYPE_TEXT);
METADATA_KEYS_TYPE.put(METADATA_KEY_ALBUM_ART, METADATA_TYPE_BITMAP)284         METADATA_KEYS_TYPE.put(METADATA_KEY_ALBUM_ART, METADATA_TYPE_BITMAP);
METADATA_KEYS_TYPE.put(METADATA_KEY_ALBUM_ART_URI, METADATA_TYPE_TEXT)285         METADATA_KEYS_TYPE.put(METADATA_KEY_ALBUM_ART_URI, METADATA_TYPE_TEXT);
METADATA_KEYS_TYPE.put(METADATA_KEY_USER_RATING, METADATA_TYPE_RATING)286         METADATA_KEYS_TYPE.put(METADATA_KEY_USER_RATING, METADATA_TYPE_RATING);
METADATA_KEYS_TYPE.put(METADATA_KEY_RATING, METADATA_TYPE_RATING)287         METADATA_KEYS_TYPE.put(METADATA_KEY_RATING, METADATA_TYPE_RATING);
METADATA_KEYS_TYPE.put(METADATA_KEY_DISPLAY_TITLE, METADATA_TYPE_TEXT)288         METADATA_KEYS_TYPE.put(METADATA_KEY_DISPLAY_TITLE, METADATA_TYPE_TEXT);
METADATA_KEYS_TYPE.put(METADATA_KEY_DISPLAY_SUBTITLE, METADATA_TYPE_TEXT)289         METADATA_KEYS_TYPE.put(METADATA_KEY_DISPLAY_SUBTITLE, METADATA_TYPE_TEXT);
METADATA_KEYS_TYPE.put(METADATA_KEY_DISPLAY_DESCRIPTION, METADATA_TYPE_TEXT)290         METADATA_KEYS_TYPE.put(METADATA_KEY_DISPLAY_DESCRIPTION, METADATA_TYPE_TEXT);
METADATA_KEYS_TYPE.put(METADATA_KEY_DISPLAY_ICON, METADATA_TYPE_BITMAP)291         METADATA_KEYS_TYPE.put(METADATA_KEY_DISPLAY_ICON, METADATA_TYPE_BITMAP);
METADATA_KEYS_TYPE.put(METADATA_KEY_DISPLAY_ICON_URI, METADATA_TYPE_TEXT)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>();
EDITOR_KEY_MAPPING.put(MediaMetadataEditor.BITMAP_KEY_ARTWORK, METADATA_KEY_ART)299         EDITOR_KEY_MAPPING.put(MediaMetadataEditor.BITMAP_KEY_ARTWORK, METADATA_KEY_ART);
EDITOR_KEY_MAPPING.put(MediaMetadataEditor.RATING_KEY_BY_OTHERS, METADATA_KEY_RATING)300         EDITOR_KEY_MAPPING.put(MediaMetadataEditor.RATING_KEY_BY_OTHERS, METADATA_KEY_RATING);
EDITOR_KEY_MAPPING.put(MediaMetadataEditor.RATING_KEY_BY_USER, METADATA_KEY_USER_RATING)301         EDITOR_KEY_MAPPING.put(MediaMetadataEditor.RATING_KEY_BY_USER, METADATA_KEY_USER_RATING);
EDITOR_KEY_MAPPING.put(MediaMetadataRetriever.METADATA_KEY_ALBUM, METADATA_KEY_ALBUM)302         EDITOR_KEY_MAPPING.put(MediaMetadataRetriever.METADATA_KEY_ALBUM, METADATA_KEY_ALBUM);
EDITOR_KEY_MAPPING.put(MediaMetadataRetriever.METADATA_KEY_ALBUMARTIST, METADATA_KEY_ALBUM_ARTIST)303         EDITOR_KEY_MAPPING.put(MediaMetadataRetriever.METADATA_KEY_ALBUMARTIST,
304                 METADATA_KEY_ALBUM_ARTIST);
EDITOR_KEY_MAPPING.put(MediaMetadataRetriever.METADATA_KEY_ARTIST, METADATA_KEY_ARTIST)305         EDITOR_KEY_MAPPING.put(MediaMetadataRetriever.METADATA_KEY_ARTIST, METADATA_KEY_ARTIST);
EDITOR_KEY_MAPPING.put(MediaMetadataRetriever.METADATA_KEY_AUTHOR, METADATA_KEY_AUTHOR)306         EDITOR_KEY_MAPPING.put(MediaMetadataRetriever.METADATA_KEY_AUTHOR, METADATA_KEY_AUTHOR);
EDITOR_KEY_MAPPING.put(MediaMetadataRetriever.METADATA_KEY_CD_TRACK_NUMBER, METADATA_KEY_TRACK_NUMBER)307         EDITOR_KEY_MAPPING.put(MediaMetadataRetriever.METADATA_KEY_CD_TRACK_NUMBER,
308                 METADATA_KEY_TRACK_NUMBER);
EDITOR_KEY_MAPPING.put(MediaMetadataRetriever.METADATA_KEY_COMPOSER, METADATA_KEY_COMPOSER)309         EDITOR_KEY_MAPPING.put(MediaMetadataRetriever.METADATA_KEY_COMPOSER, METADATA_KEY_COMPOSER);
EDITOR_KEY_MAPPING.put(MediaMetadataRetriever.METADATA_KEY_COMPILATION, METADATA_KEY_COMPILATION)310         EDITOR_KEY_MAPPING.put(MediaMetadataRetriever.METADATA_KEY_COMPILATION,
311                 METADATA_KEY_COMPILATION);
EDITOR_KEY_MAPPING.put(MediaMetadataRetriever.METADATA_KEY_DATE, METADATA_KEY_DATE)312         EDITOR_KEY_MAPPING.put(MediaMetadataRetriever.METADATA_KEY_DATE, METADATA_KEY_DATE);
EDITOR_KEY_MAPPING.put(MediaMetadataRetriever.METADATA_KEY_DISC_NUMBER, METADATA_KEY_DISC_NUMBER)313         EDITOR_KEY_MAPPING.put(MediaMetadataRetriever.METADATA_KEY_DISC_NUMBER,
314                 METADATA_KEY_DISC_NUMBER);
EDITOR_KEY_MAPPING.put(MediaMetadataRetriever.METADATA_KEY_DURATION, METADATA_KEY_DURATION)315         EDITOR_KEY_MAPPING.put(MediaMetadataRetriever.METADATA_KEY_DURATION, METADATA_KEY_DURATION);
EDITOR_KEY_MAPPING.put(MediaMetadataRetriever.METADATA_KEY_GENRE, METADATA_KEY_GENRE)316         EDITOR_KEY_MAPPING.put(MediaMetadataRetriever.METADATA_KEY_GENRE, METADATA_KEY_GENRE);
EDITOR_KEY_MAPPING.put(MediaMetadataRetriever.METADATA_KEY_NUM_TRACKS, METADATA_KEY_NUM_TRACKS)317         EDITOR_KEY_MAPPING.put(MediaMetadataRetriever.METADATA_KEY_NUM_TRACKS,
318                 METADATA_KEY_NUM_TRACKS);
EDITOR_KEY_MAPPING.put(MediaMetadataRetriever.METADATA_KEY_TITLE, METADATA_KEY_TITLE)319         EDITOR_KEY_MAPPING.put(MediaMetadataRetriever.METADATA_KEY_TITLE, METADATA_KEY_TITLE);
EDITOR_KEY_MAPPING.put(MediaMetadataRetriever.METADATA_KEY_WRITER, METADATA_KEY_WRITER)320         EDITOR_KEY_MAPPING.put(MediaMetadataRetriever.METADATA_KEY_WRITER, METADATA_KEY_WRITER);
EDITOR_KEY_MAPPING.put(MediaMetadataRetriever.METADATA_KEY_YEAR, METADATA_KEY_YEAR)321         EDITOR_KEY_MAPPING.put(MediaMetadataRetriever.METADATA_KEY_YEAR, METADATA_KEY_YEAR);
322     }
323 
324     private final Bundle mBundle;
325     private MediaDescription mDescription;
326 
MediaMetadata(Bundle bundle)327     private MediaMetadata(Bundle bundle) {
328         mBundle = new Bundle(bundle);
329     }
330 
MediaMetadata(Parcel in)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      */
containsKey(String key)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      */
getText(String key)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      */
getString(String key)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      */
getLong(String key)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      */
getRating(String key)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      */
getBitmap(String key)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
describeContents()422     public int describeContents() {
423         return 0;
424     }
425 
426     @Override
writeToParcel(Parcel dest, int flags)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      */
size()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      */
keySet()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      */
getDescription()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      */
getKeyFromMetadataEditorKey(int editorKey)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          */
Builder()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          */
Builder(MediaMetadata source)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          */
Builder(MediaMetadata source, int maxBitmapSize)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          */
putText(String key, CharSequence value)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          */
putString(String key, String value)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          */
putLong(String key, long value)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          */
putRating(String key, Rating value)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          */
putBitmap(String key, Bitmap value)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          */
build()754         public MediaMetadata build() {
755             return new MediaMetadata(mBundle);
756         }
757 
scaleBitmap(Bitmap bmp, int maxSize)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