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