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