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