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