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