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