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