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