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