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