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 // Proactively read bitmaps from known bitmap keys, to ensure that they're unparceled and 434 // added to mBundle's internal map. This ensures that the GC accounts for the underlying 435 // allocations, which it does not do if the bitmaps remain parceled (see b/215820910). 436 // TODO(b/223225532): Remove this workaround once the underlying allocations are properly 437 // tracked in NativeAllocationsRegistry. 438 getBitmap(METADATA_KEY_ART); 439 getBitmap(METADATA_KEY_ALBUM_ART); 440 getBitmap(METADATA_KEY_DISPLAY_ICON); 441 } 442 443 /** 444 * Returns true if the given key is contained in the metadata 445 * 446 * @param key a String key 447 * @return true if the key exists in this metadata, false otherwise 448 */ containsKey(String key)449 public boolean containsKey(String key) { 450 return mBundle.containsKey(key); 451 } 452 453 /** 454 * Returns the value associated with the given key, or null if no mapping of 455 * the desired type exists for the given key or a null value is explicitly 456 * associated with the key. 457 * 458 * @param key The key the value is stored under 459 * @return a CharSequence value, or null 460 */ getText(@extKey String key)461 public CharSequence getText(@TextKey String key) { 462 return mBundle.getCharSequence(key); 463 } 464 465 /** 466 * Returns the text value associated with the given key as a String, or null 467 * if no mapping of the desired type exists for the given key or a null 468 * value is explicitly associated with the key. This is equivalent to 469 * calling {@link #getText getText().toString()} if the value is not null. 470 * 471 * @param key The key the value is stored under 472 * @return a String value, or null 473 */ getString(@extKey String key)474 public String getString(@TextKey String key) { 475 CharSequence text = getText(key); 476 if (text != null) { 477 return text.toString(); 478 } 479 return null; 480 } 481 482 /** 483 * Returns the value associated with the given key, or 0L if no long exists 484 * for the given key. 485 * 486 * @param key The key the value is stored under 487 * @return a long value 488 */ getLong(@ongKey String key)489 public long getLong(@LongKey String key) { 490 return mBundle.getLong(key, 0); 491 } 492 493 /** 494 * Returns a {@link Rating} for the given key or null if no rating exists 495 * for the given key. 496 * 497 * @param key The key the value is stored under 498 * @return A {@link Rating} or null 499 */ getRating(@atingKey String key)500 public Rating getRating(@RatingKey String key) { 501 Rating rating = null; 502 try { 503 rating = mBundle.getParcelable(key, android.media.Rating.class); 504 } catch (Exception e) { 505 // ignore, value was not a bitmap 506 Log.w(TAG, "Failed to retrieve a key as Rating.", e); 507 } 508 return rating; 509 } 510 511 /** 512 * Returns a {@link Bitmap} for the given key or null if no bitmap exists 513 * for the given key. 514 * 515 * @param key The key the value is stored under 516 * @return A {@link Bitmap} or null 517 */ getBitmap(@itmapKey String key)518 public Bitmap getBitmap(@BitmapKey String key) { 519 Bitmap bmp = null; 520 try { 521 bmp = mBundle.getParcelable(key, android.graphics.Bitmap.class); 522 } catch (Exception e) { 523 // ignore, value was not a bitmap 524 Log.w(TAG, "Failed to retrieve a key as Bitmap.", e); 525 } 526 return bmp; 527 } 528 529 /** 530 * Gets the width/height limit (in pixels) for the bitmaps when this metadata was created. 531 * This method always returns a positive value. 532 * <p> 533 * If it returns {@link Integer#MAX_VALUE}, then no scaling down was applied to the bitmaps 534 * when this metadata was created. 535 * <p> 536 * If it returns another positive value, then all the bitmaps in this metadata has width/height 537 * not greater than this limit. Bitmaps may have been scaled down according to the limit. 538 * <p> 539 * 540 * @see Builder#setBitmapDimensionLimit(int) 541 */ getBitmapDimensionLimit()542 public @IntRange(from = 1) int getBitmapDimensionLimit() { 543 return mBitmapDimensionLimit; 544 } 545 546 @Override describeContents()547 public int describeContents() { 548 return 0; 549 } 550 551 @Override writeToParcel(Parcel dest, int flags)552 public void writeToParcel(Parcel dest, int flags) { 553 dest.writeBundle(mBundle); 554 dest.writeInt(mBitmapDimensionLimit); 555 } 556 557 /** 558 * Returns the number of fields in this metadata. 559 * 560 * @return The number of fields in the metadata. 561 */ size()562 public int size() { 563 return mBundle.size(); 564 } 565 566 /** 567 * Returns a Set containing the Strings used as keys in this metadata. 568 * 569 * @return a Set of String keys 570 */ keySet()571 public Set<String> keySet() { 572 return mBundle.keySet(); 573 } 574 575 /** 576 * Returns a simple description of this metadata for display purposes. 577 * 578 * @return A simple description of this metadata. 579 */ getDescription()580 public @NonNull MediaDescription getDescription() { 581 if (mDescription != null) { 582 return mDescription; 583 } 584 585 String mediaId = getString(METADATA_KEY_MEDIA_ID); 586 587 CharSequence[] text = new CharSequence[3]; 588 Bitmap icon = null; 589 Uri iconUri = null; 590 591 // First handle the case where display data is set already 592 CharSequence displayText = getText(METADATA_KEY_DISPLAY_TITLE); 593 if (!TextUtils.isEmpty(displayText)) { 594 // If they have a display title use only display data, otherwise use 595 // our best bets 596 text[0] = displayText; 597 text[1] = getText(METADATA_KEY_DISPLAY_SUBTITLE); 598 text[2] = getText(METADATA_KEY_DISPLAY_DESCRIPTION); 599 } else { 600 // Use whatever fields we can 601 int textIndex = 0; 602 int keyIndex = 0; 603 while (textIndex < text.length && keyIndex < PREFERRED_DESCRIPTION_ORDER.length) { 604 CharSequence next = getText(PREFERRED_DESCRIPTION_ORDER[keyIndex++]); 605 if (!TextUtils.isEmpty(next)) { 606 // Fill in the next empty bit of text 607 text[textIndex++] = next; 608 } 609 } 610 } 611 612 // Get the best art bitmap we can find 613 for (int i = 0; i < PREFERRED_BITMAP_ORDER.length; i++) { 614 Bitmap next = getBitmap(PREFERRED_BITMAP_ORDER[i]); 615 if (next != null) { 616 icon = next; 617 break; 618 } 619 } 620 621 // Get the best Uri we can find 622 for (int i = 0; i < PREFERRED_URI_ORDER.length; i++) { 623 String next = getString(PREFERRED_URI_ORDER[i]); 624 if (!TextUtils.isEmpty(next)) { 625 iconUri = Uri.parse(next); 626 break; 627 } 628 } 629 630 Uri mediaUri = null; 631 String mediaUriStr = getString(METADATA_KEY_MEDIA_URI); 632 if (!TextUtils.isEmpty(mediaUriStr)) { 633 mediaUri = Uri.parse(mediaUriStr); 634 } 635 636 MediaDescription.Builder bob = new MediaDescription.Builder(); 637 bob.setMediaId(mediaId); 638 bob.setTitle(text[0]); 639 bob.setSubtitle(text[1]); 640 bob.setDescription(text[2]); 641 bob.setIconBitmap(icon); 642 bob.setIconUri(iconUri); 643 bob.setMediaUri(mediaUri); 644 if (mBundle.containsKey(METADATA_KEY_BT_FOLDER_TYPE)) { 645 Bundle bundle = new Bundle(); 646 bundle.putLong(MediaDescription.EXTRA_BT_FOLDER_TYPE, 647 getLong(METADATA_KEY_BT_FOLDER_TYPE)); 648 bob.setExtras(bundle); 649 } 650 mDescription = bob.build(); 651 652 return mDescription; 653 } 654 655 /** 656 * Helper for getting the String key used by {@link MediaMetadata} from the 657 * integer key that {@link MediaMetadataEditor} uses. 658 * 659 * @param editorKey The key used by the editor 660 * @return The key used by this class or null if no mapping exists 661 * @hide 662 */ 663 @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) getKeyFromMetadataEditorKey(int editorKey)664 public static String getKeyFromMetadataEditorKey(int editorKey) { 665 return EDITOR_KEY_MAPPING.get(editorKey, null); 666 } 667 668 public static final @android.annotation.NonNull Parcelable.Creator<MediaMetadata> CREATOR = 669 new Parcelable.Creator<MediaMetadata>() { 670 @Override 671 public MediaMetadata createFromParcel(Parcel in) { 672 return new MediaMetadata(in); 673 } 674 675 @Override 676 public MediaMetadata[] newArray(int size) { 677 return new MediaMetadata[size]; 678 } 679 }; 680 681 /** 682 * Compares the contents of this object to another MediaMetadata object. It 683 * does not compare Bitmaps and Ratings as the media player can choose to 684 * forgo these fields depending on how you retrieve the MediaMetadata. 685 * 686 * @param o The Metadata object to compare this object against 687 * @return Whether or not the two objects have matching fields (excluding 688 * Bitmaps and Ratings) 689 */ 690 @Override equals(Object o)691 public boolean equals(Object o) { 692 if (o == this) { 693 return true; 694 } 695 696 if (!(o instanceof MediaMetadata)) { 697 return false; 698 } 699 700 final MediaMetadata m = (MediaMetadata) o; 701 702 for (int i = 0; i < METADATA_KEYS_TYPE.size(); i++) { 703 String key = METADATA_KEYS_TYPE.keyAt(i); 704 switch (METADATA_KEYS_TYPE.valueAt(i)) { 705 case METADATA_TYPE_TEXT: 706 if (!Objects.equals(getString(key), m.getString(key))) { 707 return false; 708 } 709 break; 710 case METADATA_TYPE_LONG: 711 if (getLong(key) != m.getLong(key)) { 712 return false; 713 } 714 break; 715 default: 716 // Ignore ratings and bitmaps when comparing 717 break; 718 } 719 } 720 721 return true; 722 } 723 724 @Override hashCode()725 public int hashCode() { 726 int hashCode = 17; 727 728 for (int i = 0; i < METADATA_KEYS_TYPE.size(); i++) { 729 String key = METADATA_KEYS_TYPE.keyAt(i); 730 switch (METADATA_KEYS_TYPE.valueAt(i)) { 731 case METADATA_TYPE_TEXT: 732 hashCode = 31 * hashCode + Objects.hash(getString(key)); 733 break; 734 case METADATA_TYPE_LONG: 735 hashCode = 31 * hashCode + Long.hashCode(getLong(key)); 736 break; 737 default: 738 // Ignore ratings and bitmaps when comparing 739 break; 740 } 741 } 742 743 return hashCode; 744 } 745 746 /** 747 * Use to build MediaMetadata objects. The system defined metadata keys must 748 * use the appropriate data type. 749 */ 750 public static final class Builder { 751 private final Bundle mBundle; 752 private int mBitmapDimensionLimit = Integer.MAX_VALUE; 753 754 /** 755 * Create an empty Builder. Any field that should be included in the 756 * {@link MediaMetadata} must be added. 757 */ Builder()758 public Builder() { 759 mBundle = new Bundle(); 760 } 761 762 /** 763 * Create a Builder using a {@link MediaMetadata} instance to set the 764 * initial values. All fields in the source metadata will be included in 765 * the new metadata. Fields can be overwritten by adding the same key. 766 * 767 * @param source 768 */ Builder(MediaMetadata source)769 public Builder(MediaMetadata source) { 770 mBundle = new Bundle(source.mBundle); 771 mBitmapDimensionLimit = source.mBitmapDimensionLimit; 772 } 773 774 /** 775 * Put a CharSequence value into the metadata. Custom keys may be used, 776 * but if the METADATA_KEYs defined in this class are used they may only 777 * be one of the following: 778 * <ul> 779 * <li>{@link #METADATA_KEY_TITLE}</li> 780 * <li>{@link #METADATA_KEY_ARTIST}</li> 781 * <li>{@link #METADATA_KEY_ALBUM}</li> 782 * <li>{@link #METADATA_KEY_AUTHOR}</li> 783 * <li>{@link #METADATA_KEY_WRITER}</li> 784 * <li>{@link #METADATA_KEY_COMPOSER}</li> 785 * <li>{@link #METADATA_KEY_DATE}</li> 786 * <li>{@link #METADATA_KEY_GENRE}</li> 787 * <li>{@link #METADATA_KEY_ALBUM_ARTIST}</li> 788 * <li>{@link #METADATA_KEY_ART_URI}</li> 789 * <li>{@link #METADATA_KEY_ALBUM_ART_URI}</li> 790 * <li>{@link #METADATA_KEY_DISPLAY_TITLE}</li> 791 * <li>{@link #METADATA_KEY_DISPLAY_SUBTITLE}</li> 792 * <li>{@link #METADATA_KEY_DISPLAY_DESCRIPTION}</li> 793 * <li>{@link #METADATA_KEY_DISPLAY_ICON_URI}</li> 794 * </ul> 795 * 796 * @param key The key for referencing this value 797 * @param value The CharSequence value to store 798 * @return The Builder to allow chaining 799 */ putText(@extKey String key, CharSequence value)800 public Builder putText(@TextKey String key, CharSequence value) { 801 if (METADATA_KEYS_TYPE.containsKey(key)) { 802 if (METADATA_KEYS_TYPE.get(key) != METADATA_TYPE_TEXT) { 803 throw new IllegalArgumentException("The " + key 804 + " key cannot be used to put a CharSequence"); 805 } 806 } 807 mBundle.putCharSequence(key, value); 808 return this; 809 } 810 811 /** 812 * Put a String value into the metadata. Custom keys may be used, but if 813 * the METADATA_KEYs defined in this class are used they may only be one 814 * of the following: 815 * <ul> 816 * <li>{@link #METADATA_KEY_TITLE}</li> 817 * <li>{@link #METADATA_KEY_ARTIST}</li> 818 * <li>{@link #METADATA_KEY_ALBUM}</li> 819 * <li>{@link #METADATA_KEY_AUTHOR}</li> 820 * <li>{@link #METADATA_KEY_WRITER}</li> 821 * <li>{@link #METADATA_KEY_COMPOSER}</li> 822 * <li>{@link #METADATA_KEY_DATE}</li> 823 * <li>{@link #METADATA_KEY_GENRE}</li> 824 * <li>{@link #METADATA_KEY_ALBUM_ARTIST}</li> 825 * <li>{@link #METADATA_KEY_ART_URI}</li> 826 * <li>{@link #METADATA_KEY_ALBUM_ART_URI}</li> 827 * <li>{@link #METADATA_KEY_DISPLAY_TITLE}</li> 828 * <li>{@link #METADATA_KEY_DISPLAY_SUBTITLE}</li> 829 * <li>{@link #METADATA_KEY_DISPLAY_DESCRIPTION}</li> 830 * <li>{@link #METADATA_KEY_DISPLAY_ICON_URI}</li> 831 * </ul> 832 * <p> 833 * Uris for artwork should use the content:// style and support 834 * {@link ContentResolver#EXTRA_SIZE} for retrieving scaled artwork 835 * through {@link ContentResolver#openTypedAssetFileDescriptor(Uri, 836 * String, Bundle)}. 837 * 838 * @param key The key for referencing this value 839 * @param value The String value to store 840 * @return The Builder to allow chaining 841 */ putString(@extKey String key, String value)842 public Builder putString(@TextKey String key, String value) { 843 if (METADATA_KEYS_TYPE.containsKey(key)) { 844 if (METADATA_KEYS_TYPE.get(key) != METADATA_TYPE_TEXT) { 845 throw new IllegalArgumentException("The " + key 846 + " key cannot be used to put a String"); 847 } 848 } 849 mBundle.putCharSequence(key, value); 850 return this; 851 } 852 853 /** 854 * Put a long value into the metadata. Custom keys may be used, but if 855 * the METADATA_KEYs defined in this class are used they may only be one 856 * of the following: 857 * <ul> 858 * <li>{@link #METADATA_KEY_DURATION}</li> 859 * <li>{@link #METADATA_KEY_TRACK_NUMBER}</li> 860 * <li>{@link #METADATA_KEY_NUM_TRACKS}</li> 861 * <li>{@link #METADATA_KEY_DISC_NUMBER}</li> 862 * <li>{@link #METADATA_KEY_YEAR}</li> 863 * </ul> 864 * 865 * @param key The key for referencing this value 866 * @param value The long value to store 867 * @return The Builder to allow chaining 868 */ putLong(@ongKey String key, long value)869 public Builder putLong(@LongKey String key, long value) { 870 if (METADATA_KEYS_TYPE.containsKey(key)) { 871 if (METADATA_KEYS_TYPE.get(key) != METADATA_TYPE_LONG) { 872 throw new IllegalArgumentException("The " + key 873 + " key cannot be used to put a long"); 874 } 875 } 876 mBundle.putLong(key, value); 877 return this; 878 } 879 880 /** 881 * Put a {@link Rating} into the metadata. Custom keys may be used, but 882 * if the METADATA_KEYs defined in this class are used they may only be 883 * one of the following: 884 * <ul> 885 * <li>{@link #METADATA_KEY_RATING}</li> 886 * <li>{@link #METADATA_KEY_USER_RATING}</li> 887 * </ul> 888 * 889 * @param key The key for referencing this value 890 * @param value The Rating value to store 891 * @return The Builder to allow chaining 892 */ putRating(@atingKey String key, Rating value)893 public Builder putRating(@RatingKey String key, Rating value) { 894 if (METADATA_KEYS_TYPE.containsKey(key)) { 895 if (METADATA_KEYS_TYPE.get(key) != METADATA_TYPE_RATING) { 896 throw new IllegalArgumentException("The " + key 897 + " key cannot be used to put a Rating"); 898 } 899 } 900 mBundle.putParcelable(key, value); 901 return this; 902 } 903 904 /** 905 * Put a {@link Bitmap} into the metadata. Custom keys may be used, but 906 * if the METADATA_KEYs defined in this class are used they may only be 907 * one of the following: 908 * <ul> 909 * <li>{@link #METADATA_KEY_ART}</li> 910 * <li>{@link #METADATA_KEY_ALBUM_ART}</li> 911 * <li>{@link #METADATA_KEY_DISPLAY_ICON}</li> 912 * </ul> 913 * <p> 914 * Large bitmaps may be scaled down by the system with 915 * {@link Builder#setBitmapDimensionLimit(int)} when {@link MediaSession#setMetadata} 916 * is called. To pass full resolution images {@link Uri Uris} should be used with 917 * {@link #putString}. 918 * 919 * @param key The key for referencing this value 920 * @param value The Bitmap to store 921 * @return The Builder to allow chaining 922 */ putBitmap(@itmapKey String key, Bitmap value)923 public Builder putBitmap(@BitmapKey String key, Bitmap value) { 924 if (METADATA_KEYS_TYPE.containsKey(key)) { 925 if (METADATA_KEYS_TYPE.get(key) != METADATA_TYPE_BITMAP) { 926 throw new IllegalArgumentException("The " + key 927 + " key cannot be used to put a Bitmap"); 928 } 929 } 930 mBundle.putParcelable(key, value); 931 return this; 932 } 933 934 /** 935 * Sets the maximum width/height (in pixels) for the bitmaps in the metadata. 936 * Bitmaps will be replaced with scaled down copies if their width (or height) is 937 * larger than {@code bitmapDimensionLimit}. 938 * <p> 939 * In order to unset the limit, pass {@link Integer#MAX_VALUE} as 940 * {@code bitmapDimensionLimit}. 941 * 942 * @param bitmapDimensionLimit The maximum width/height (in pixels) for bitmaps 943 * contained in the metadata. Non-positive values are ignored. 944 * Pass {@link Integer#MAX_VALUE} to unset the limit. 945 */ 946 @NonNull setBitmapDimensionLimit(@ntRangefrom = 1) int bitmapDimensionLimit)947 public Builder setBitmapDimensionLimit(@IntRange(from = 1) int bitmapDimensionLimit) { 948 if (bitmapDimensionLimit > 0) { 949 mBitmapDimensionLimit = bitmapDimensionLimit; 950 } else { 951 Log.w(TAG, "setBitmapDimensionLimit(): Ignoring non-positive bitmapDimensionLimit: " 952 + bitmapDimensionLimit); 953 } 954 return this; 955 } 956 957 /** 958 * Creates a {@link MediaMetadata} instance with the specified fields. 959 * 960 * @return The new MediaMetadata instance 961 */ build()962 public MediaMetadata build() { 963 if (mBitmapDimensionLimit != Integer.MAX_VALUE) { 964 for (String key : mBundle.keySet()) { 965 Object value = mBundle.get(key); 966 if (value instanceof Bitmap) { 967 Bitmap bmp = (Bitmap) value; 968 if (bmp.getHeight() > mBitmapDimensionLimit 969 || bmp.getWidth() > mBitmapDimensionLimit) { 970 putBitmap(key, scaleBitmap(bmp, mBitmapDimensionLimit)); 971 } 972 } 973 } 974 } 975 return new MediaMetadata(mBundle, mBitmapDimensionLimit); 976 } 977 scaleBitmap(Bitmap bmp, int maxDimension)978 private Bitmap scaleBitmap(Bitmap bmp, int maxDimension) { 979 float maxDimensionF = maxDimension; 980 float widthScale = maxDimensionF / bmp.getWidth(); 981 float heightScale = maxDimensionF / bmp.getHeight(); 982 float scale = Math.min(widthScale, heightScale); 983 int height = (int) (bmp.getHeight() * scale); 984 int width = (int) (bmp.getWidth() * scale); 985 return Bitmap.createScaledBitmap(bmp, width, height, true); 986 } 987 } 988 } 989