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