Home | History | Annotate | Download | only in media
      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