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