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