public final class

MediaMetadata

extends CustomVersionedParcelable

 java.lang.Object

androidx.versionedparcelable.CustomVersionedParcelable

↳androidx.media2.common.MediaMetadata

Gradle dependencies

compile group: 'androidx.media2', name: 'media2-common', version: '1.2.1'

  • groupId: androidx.media2
  • artifactId: media2-common
  • version: 1.2.1

Artifact androidx.media2:media2-common:1.2.1 it located at Google repository (https://maven.google.com/)

Overview

Contains metadata about an item, such as the title, artist, etc. This is optional, but you'd better to provide this as much as possible when you're using media widget and/or session APIs.

The media widget components build its UI based on the metadata here. For an example, MediaControlView will show title from the metadata.

The MediaLibraryService.MediaLibrarySession would require some metadata values when it provides MediaItems to MediaBrowser.

Topics covered here:

  1. Media ID
  2. Browsable type
  3. Playable
  4. Duration
  5. User rating

Media ID

If set, the media ID must be the persistent key for the underlying media contents, so MediaController and MediaBrowser can store the information and reuse it later. Some APIs requires a media ID (e.g. MediaController.setRating(String, Rating), so you'd better specify one.

Typical example of using media ID is the URI of the contents, but use it with the caution because the metadata is shared across the process in plain text.

The MediaLibraryService.MediaLibrarySession would require it for the library root, so MediaBrowser can call subsequent MediaBrowser.getChildren(String, int, int, MediaLibraryService.LibraryParams) with the ID.

Browsable type

Browsable defines whether the media item has children and type of children if any. With this, MediaBrowser can know whether the subsequent MediaBrowser.getChildren(String, int, int, MediaLibraryService.LibraryParams) would successfully run.

The MediaLibraryService.MediaLibrarySession would require the explicit browsable type for the media items returned by the MediaLibraryService.MediaLibrarySession.MediaLibrarySessionCallback.

Playable type

Playable defines whether the media item can be played or not. It may be possible for a playlist to contain a media item which isn't playable in order to show a disabled media item.

The MediaLibraryService.MediaLibrarySession would require the explicit playable value for the media items returned by the MediaLibraryService.MediaLibrarySession.MediaLibrarySessionCallback.

Duration

The duration is the length of the contents. The MediaController can only get the duration through the metadata. This tells when would the playback ends, and also tells about the allowed range of MediaController.seekTo(long).

If it's not set by developer, MediaSession would update the duration in the metadata with the SessionPlayer.getDuration().

User rating

Prefer to have unrated user rating instead of null, so MediaController can know the possible user rating type for calling MediaController.setRating(String, Rating).

Summary

Fields
public static final longBROWSABLE_TYPE_ALBUMS

The type of browsable that contains browsable items categorized by album.

public static final longBROWSABLE_TYPE_ARTISTS

The type of browsable that contains browsable items categorized by artist.

public static final longBROWSABLE_TYPE_GENRES

The type of browsable that contains browsable items categorized by genre.

public static final longBROWSABLE_TYPE_MIXED

The type of browsable that is unknown or contains media items of mixed types.

public static final longBROWSABLE_TYPE_NONE

The type of browsable for non-browsable media item.

public static final longBROWSABLE_TYPE_PLAYLISTS

The type of browsable that contains browsable items categorized by playlist.

public static final longBROWSABLE_TYPE_TITLES

The type of browsable that only contains playable media items.

public static final longBROWSABLE_TYPE_YEARS

The type of browsable that contains browsable items categorized by year.

public static final java.lang.StringMETADATA_KEY_ADVERTISEMENT

The metadata key for a java.lang.Long typed value to retrieve the information about whether the media is an advertisement.

public static final java.lang.StringMETADATA_KEY_ALBUM

The metadata key for a java.lang.CharSequence or java.lang.String typed value to retrieve the information about the album title for the media.

public static final java.lang.StringMETADATA_KEY_ALBUM_ART

The metadata key for a Bitmap typed value to retrieve the information about the artwork for the album of the media's original source.

public static final java.lang.StringMETADATA_KEY_ALBUM_ART_URI

The metadata key for a java.lang.CharSequence or java.lang.String typed value to retrieve the information about the Uri of the artwork for the album of the media's original source.

public static final java.lang.StringMETADATA_KEY_ALBUM_ARTIST

The metadata key for a java.lang.CharSequence or java.lang.String typed value to retrieve the information about the artist for the album of the media's original source.

public static final java.lang.StringMETADATA_KEY_ART

The metadata key for a Bitmap typed value to retrieve the information about the artwork for the media.

public static final java.lang.StringMETADATA_KEY_ART_URI

The metadata key for a java.lang.CharSequence or java.lang.String typed value to retrieve the information about Uri of the artwork for the media.

public static final java.lang.StringMETADATA_KEY_ARTIST

The metadata key for a java.lang.CharSequence or java.lang.String typed value to retrieve the information about the artist of the media.

public static final java.lang.StringMETADATA_KEY_AUTHOR

The metadata key for a java.lang.CharSequence or java.lang.String typed value to retrieve the information about the author of the media.

public static final java.lang.StringMETADATA_KEY_BROWSABLE

The metadata key for a java.lang.Long typed value to retrieve the information about the type of browsable.

public static final java.lang.StringMETADATA_KEY_COMPILATION

The metadata key for a java.lang.CharSequence or java.lang.String typed value to retrieve the information about the compilation status of the media.

public static final java.lang.StringMETADATA_KEY_COMPOSER

The metadata key for a java.lang.CharSequence or java.lang.String typed value to retrieve the information about the composer of the media.

public static final java.lang.StringMETADATA_KEY_DATE

The metadata key for a java.lang.CharSequence or java.lang.String typed value to retrieve the information about the date the media was created or published.

public static final java.lang.StringMETADATA_KEY_DISC_NUMBER

The metadata key for a java.lang.Long typed value to retrieve the information about the disc number for the media's original source.

public static final java.lang.StringMETADATA_KEY_DISPLAY_DESCRIPTION

The metadata key for a java.lang.CharSequence or java.lang.String typed value to retrieve the information about the description that is suitable for display to the user.

public static final java.lang.StringMETADATA_KEY_DISPLAY_ICON

The metadata key for a Bitmap typed value to retrieve the information about the icon or thumbnail that is suitable for display to the user.

public static final java.lang.StringMETADATA_KEY_DISPLAY_ICON_URI

The metadata key for a java.lang.CharSequence or java.lang.String typed value to retrieve the information about the Uri of icon or thumbnail that is suitable for display to the user.

public static final java.lang.StringMETADATA_KEY_DISPLAY_SUBTITLE

The metadata key for a java.lang.CharSequence or java.lang.String typed value to retrieve the information about the subtitle that is suitable for display to the user.

public static final java.lang.StringMETADATA_KEY_DISPLAY_TITLE

The metadata key for a java.lang.CharSequence or java.lang.String typed value to retrieve the information about the title that is suitable for display to the user.

public static final java.lang.StringMETADATA_KEY_DOWNLOAD_STATUS

The metadata key for a java.lang.Long typed value to retrieve the information about the download status of the media which will be used for later offline playback.

public static final java.lang.StringMETADATA_KEY_DURATION

The metadata key for a java.lang.Long typed value to retrieve the information about the duration of the media in ms.

public static final java.lang.StringMETADATA_KEY_EXTRAS

A extra.

public static final java.lang.StringMETADATA_KEY_GENRE

The metadata key for a java.lang.CharSequence or java.lang.String typed value to retrieve the information about the genre of the media.

public static final java.lang.StringMETADATA_KEY_MEDIA_ID

The metadata key for a java.lang.CharSequence or java.lang.String typed value to retrieve the information about the media ID of the content.

public static final java.lang.StringMETADATA_KEY_MEDIA_URI

The metadata key for a java.lang.CharSequence or java.lang.String typed value to retrieve the information about the Uri of the content.

public static final java.lang.StringMETADATA_KEY_NUM_TRACKS

The metadata key for a java.lang.Long typed value to retrieve the information about the number of tracks in the media's original source.

public static final java.lang.StringMETADATA_KEY_PLAYABLE

The metadata key for a java.lang.Long typed value to retrieve the information about whether the media is playable.

public static final java.lang.StringMETADATA_KEY_RADIO_FREQUENCY

public static final java.lang.StringMETADATA_KEY_RADIO_PROGRAM_NAME

public static final java.lang.StringMETADATA_KEY_RATING

The metadata key for a Rating typed value to retrieve the information about the overall rating for the media.

public static final java.lang.StringMETADATA_KEY_TITLE

The metadata key for a java.lang.CharSequence or java.lang.String typed value to retrieve the information about the title of the media.

public static final java.lang.StringMETADATA_KEY_TRACK_NUMBER

The metadata key for a java.lang.Long typed value to retrieve the information about the track number for the media.

public static final java.lang.StringMETADATA_KEY_USER_RATING

The metadata key for a Rating typed value to retrieve the information about the user's rating for the media.

public static final java.lang.StringMETADATA_KEY_WRITER

The metadata key for a java.lang.CharSequence or java.lang.String typed value to retrieve the information about the writer of the media.

public static final java.lang.StringMETADATA_KEY_YEAR

The metadata key for a java.lang.Long typed value to retrieve the information about the year the media was created or published.

public static final longSTATUS_DOWNLOADED

The status value to indicate the media item is downloaded for later offline playback.

public static final longSTATUS_DOWNLOADING

The status value to indicate the media item is being downloaded.

public static final longSTATUS_NOT_DOWNLOADED

The status value to indicate the media item is not downloaded.

Methods
public booleancontainsKey(java.lang.String key)

Returns true if the given key is contained in the metadata

public BitmapgetBitmap(java.lang.String key)

Return a Bitmap for the given key or null if no bitmap exists for the given key.

public BundlegetExtras()

Get the extra from the metadata object.

public floatgetFloat(java.lang.String key)

Return the value associated with the given key, or 0.0f if no long exists for the given key.

public longgetLong(java.lang.String key)

Returns the value associated with the given key, or 0L if no long exists for the given key.

public java.lang.StringgetMediaId()

Returns the media id, or null if the id doesn't exist.

public java.lang.ObjectgetObject(java.lang.String key)

Gets the object which matches the given key in the backing bundle.

public RatinggetRating(java.lang.String key)

Return a Rating for the given key or null if no rating exists for the given key.

public java.lang.StringgetString(java.lang.String key)

Returns the value associated with the given key, or null if no mapping of the desired type exists for the given key or a null value is explicitly associated with the key.

public java.lang.CharSequencegetText(java.lang.String key)

Returns the value associated with the given key, or null if no mapping of the desired type exists for the given key or a null value is explicitly associated with the key.

public java.util.Set<java.lang.String>keySet()

Returns a Set containing the Strings used as keys in this metadata.

public voidonPostParceling()

public voidonPreParceling(boolean isStream)

public intsize()

Get the number of fields in this metadata.

public java.lang.StringtoString()

from java.lang.Objectclone, equals, finalize, getClass, hashCode, notify, notifyAll, wait, wait, wait

Fields

public static final java.lang.String METADATA_KEY_TITLE

The metadata key for a java.lang.CharSequence or java.lang.String typed value to retrieve the information about the title of the media.

See also: MediaMetadata.Builder.putText(String, CharSequence), MediaMetadata.Builder.putString(String, String), MediaMetadata.getText(String), MediaMetadata.getString(String)

public static final java.lang.String METADATA_KEY_ARTIST

The metadata key for a java.lang.CharSequence or java.lang.String typed value to retrieve the information about the artist of the media.

See also: MediaMetadata.Builder.putText(String, CharSequence), MediaMetadata.Builder.putString(String, String), MediaMetadata.getText(String), MediaMetadata.getString(String)

public static final java.lang.String METADATA_KEY_DURATION

The metadata key for a java.lang.Long typed value to retrieve the information about the duration of the media in ms. A negative duration indicates that the duration is unknown (or infinite).

See also: MediaMetadata.Builder.putLong(String, long), MediaMetadata.getLong(String)

public static final java.lang.String METADATA_KEY_ALBUM

The metadata key for a java.lang.CharSequence or java.lang.String typed value to retrieve the information about the album title for the media.

See also: MediaMetadata.Builder.putText(String, CharSequence), MediaMetadata.Builder.putString(String, String), MediaMetadata.getText(String), MediaMetadata.getString(String)

public static final java.lang.String METADATA_KEY_AUTHOR

The metadata key for a java.lang.CharSequence or java.lang.String typed value to retrieve the information about the author of the media.

See also: MediaMetadata.Builder.putText(String, CharSequence), MediaMetadata.Builder.putString(String, String), MediaMetadata.getText(String), MediaMetadata.getString(String)

public static final java.lang.String METADATA_KEY_WRITER

The metadata key for a java.lang.CharSequence or java.lang.String typed value to retrieve the information about the writer of the media.

See also: MediaMetadata.Builder.putText(String, CharSequence), MediaMetadata.Builder.putString(String, String), MediaMetadata.getText(String), MediaMetadata.getString(String)

public static final java.lang.String METADATA_KEY_COMPOSER

The metadata key for a java.lang.CharSequence or java.lang.String typed value to retrieve the information about the composer of the media.

See also: MediaMetadata.Builder.putText(String, CharSequence), MediaMetadata.Builder.putString(String, String), MediaMetadata.getText(String), MediaMetadata.getString(String)

public static final java.lang.String METADATA_KEY_COMPILATION

The metadata key for a java.lang.CharSequence or java.lang.String typed value to retrieve the information about the compilation status of the media.

See also: MediaMetadata.Builder.putText(String, CharSequence), MediaMetadata.Builder.putString(String, String), MediaMetadata.getText(String), MediaMetadata.getString(String)

public static final java.lang.String METADATA_KEY_DATE

The metadata key for a java.lang.CharSequence or java.lang.String typed value to retrieve the information about the date the media was created or published. The format is unspecified but RFC 3339 is recommended.

See also: MediaMetadata.Builder.putText(String, CharSequence), MediaMetadata.Builder.putString(String, String), MediaMetadata.getText(String), MediaMetadata.getString(String)

public static final java.lang.String METADATA_KEY_YEAR

The metadata key for a java.lang.Long typed value to retrieve the information about the year the media was created or published.

See also: MediaMetadata.Builder.putLong(String, long), MediaMetadata.getLong(String)

public static final java.lang.String METADATA_KEY_GENRE

The metadata key for a java.lang.CharSequence or java.lang.String typed value to retrieve the information about the genre of the media.

See also: MediaMetadata.Builder.putText(String, CharSequence), MediaMetadata.Builder.putString(String, String), MediaMetadata.getText(String), MediaMetadata.getString(String)

public static final java.lang.String METADATA_KEY_TRACK_NUMBER

The metadata key for a java.lang.Long typed value to retrieve the information about the track number for the media.

See also: MediaMetadata.Builder.putLong(String, long), MediaMetadata.getLong(String)

public static final java.lang.String METADATA_KEY_NUM_TRACKS

The metadata key for a java.lang.Long typed value to retrieve the information about the number of tracks in the media's original source.

See also: MediaMetadata.Builder.putLong(String, long), MediaMetadata.getLong(String)

public static final java.lang.String METADATA_KEY_DISC_NUMBER

The metadata key for a java.lang.Long typed value to retrieve the information about the disc number for the media's original source.

See also: MediaMetadata.Builder.putLong(String, long), MediaMetadata.getLong(String)

public static final java.lang.String METADATA_KEY_ALBUM_ARTIST

The metadata key for a java.lang.CharSequence or java.lang.String typed value to retrieve the information about the artist for the album of the media's original source.

See also: MediaMetadata.Builder.putText(String, CharSequence), MediaMetadata.Builder.putString(String, String), MediaMetadata.getText(String), MediaMetadata.getString(String)

public static final java.lang.String METADATA_KEY_ART

The metadata key for a Bitmap typed value to retrieve the information about the artwork for the media. The artwork should be relatively small and may be scaled down if it is too large. For higher resolution artwork, MediaMetadata.METADATA_KEY_ART_URI should be used instead.

See also: MediaMetadata.Builder.putBitmap(String, Bitmap), MediaMetadata.getBitmap(String)

public static final java.lang.String METADATA_KEY_ART_URI

The metadata key for a java.lang.CharSequence or java.lang.String typed value to retrieve the information about Uri of the artwork for the media.

See also: MediaMetadata.Builder.putText(String, CharSequence), MediaMetadata.Builder.putString(String, String), MediaMetadata.getText(String), MediaMetadata.getString(String)

public static final java.lang.String METADATA_KEY_ALBUM_ART

The metadata key for a Bitmap typed value to retrieve the information about the artwork for the album of the media's original source. The artwork should be relatively small and may be scaled down if it is too large. For higher resolution artwork, MediaMetadata.METADATA_KEY_ALBUM_ART_URI should be used instead.

See also: MediaMetadata.Builder.putBitmap(String, Bitmap), MediaMetadata.getBitmap(String)

public static final java.lang.String METADATA_KEY_ALBUM_ART_URI

The metadata key for a java.lang.CharSequence or java.lang.String typed value to retrieve the information about the Uri of the artwork for the album of the media's original source.

See also: MediaMetadata.Builder.putText(String, CharSequence), MediaMetadata.Builder.putString(String, String), MediaMetadata.getText(String), MediaMetadata.getString(String)

public static final java.lang.String METADATA_KEY_USER_RATING

The metadata key for a Rating typed value to retrieve the information about the user's rating for the media. Prefer to have unrated user rating instead of null, so MediaController can know the possible user rating type.

See also: MediaMetadata.Builder.putRating(String, Rating), MediaMetadata.getRating(String)

public static final java.lang.String METADATA_KEY_RATING

The metadata key for a Rating typed value to retrieve the information about the overall rating for the media.

See also: MediaMetadata.Builder.putRating(String, Rating), MediaMetadata.getRating(String)

public static final java.lang.String METADATA_KEY_DISPLAY_TITLE

The metadata key for a java.lang.CharSequence or java.lang.String typed value to retrieve the information about the title that is suitable for display to the user. It will generally be the same as MediaMetadata.METADATA_KEY_TITLE but may differ for some formats. When displaying media described by this metadata, this should be preferred if present.

See also: MediaMetadata.Builder.putText(String, CharSequence), MediaMetadata.Builder.putString(String, String), MediaMetadata.getText(String), MediaMetadata.getString(String)

public static final java.lang.String METADATA_KEY_DISPLAY_SUBTITLE

The metadata key for a java.lang.CharSequence or java.lang.String typed value to retrieve the information about the subtitle that is suitable for display to the user. When displaying a second line for media described by this metadata, this should be preferred to other fields if present.

See also: MediaMetadata.Builder.putText(String, CharSequence), MediaMetadata.Builder.putString(String, String), MediaMetadata.getText(String), MediaMetadata.getString(String)

public static final java.lang.String METADATA_KEY_DISPLAY_DESCRIPTION

The metadata key for a java.lang.CharSequence or java.lang.String typed value to retrieve the information about the description that is suitable for display to the user. When displaying more information for media described by this metadata, this should be preferred to other fields if present.

See also: MediaMetadata.Builder.putText(String, CharSequence), MediaMetadata.Builder.putString(String, String), MediaMetadata.getText(String), MediaMetadata.getString(String)

public static final java.lang.String METADATA_KEY_DISPLAY_ICON

The metadata key for a Bitmap typed value to retrieve the information about the icon or thumbnail that is suitable for display to the user. When displaying an icon for media described by this metadata, this should be preferred to other fields if present.

The icon should be relatively small and may be scaled down if it is too large. For higher resolution artwork, MediaMetadata.METADATA_KEY_DISPLAY_ICON_URI should be used instead.

See also: MediaMetadata.Builder.putBitmap(String, Bitmap), MediaMetadata.getBitmap(String)

public static final java.lang.String METADATA_KEY_DISPLAY_ICON_URI

The metadata key for a java.lang.CharSequence or java.lang.String typed value to retrieve the information about the Uri of icon or thumbnail that is suitable for display to the user. When displaying more information for media described by this metadata, the display description should be preferred to other fields when present.

See also: MediaMetadata.Builder.putText(String, CharSequence), MediaMetadata.Builder.putString(String, String), MediaMetadata.getText(String), MediaMetadata.getString(String)

public static final java.lang.String METADATA_KEY_MEDIA_ID

The metadata key for a java.lang.CharSequence or java.lang.String typed value to retrieve the information about the media ID of the content. This value is specific to the service providing the content. If used, this should be a persistent key for the underlying content. This ID is used by MediaController and MediaBrowser.

See also: MediaMetadata.Builder.putText(String, CharSequence), MediaMetadata.Builder.putString(String, String), MediaMetadata.getText(String), MediaMetadata.getString(String)

public static final java.lang.String METADATA_KEY_MEDIA_URI

The metadata key for a java.lang.CharSequence or java.lang.String typed value to retrieve the information about the Uri of the content. This value is specific to the service providing the content.

See also: MediaMetadata.Builder.putText(String, CharSequence), MediaMetadata.Builder.putString(String, String), MediaMetadata.getText(String), MediaMetadata.getString(String)

public static final java.lang.String METADATA_KEY_RADIO_FREQUENCY

See also: MediaMetadata.Builder.putFloat(String, float), MediaMetadata.getFloat(String)

public static final java.lang.String METADATA_KEY_RADIO_PROGRAM_NAME

See also: MediaMetadata.Builder.putText(String, CharSequence), MediaMetadata.Builder.putString(String, String), MediaMetadata.getText(String), MediaMetadata.getString(String)

public static final java.lang.String METADATA_KEY_BROWSABLE

The metadata key for a java.lang.Long typed value to retrieve the information about the type of browsable. It should be one of the following:

The values other than MediaMetadata.BROWSABLE_TYPE_NONE mean that the media item has children.[

This matches with the bluetooth folder type of the media specified in the section 6.10.2.2 of the Bluetooth AVRCP 1.5.

See also: MediaMetadata.Builder.putLong(String, long), MediaMetadata.getLong(String)

public static final long BROWSABLE_TYPE_NONE

The type of browsable for non-browsable media item.

public static final long BROWSABLE_TYPE_MIXED

The type of browsable that is unknown or contains media items of mixed types.

This value matches with the folder type 'Mixed' as specified in the section 6.10.2.2 of the Bluetooth AVRCP 1.5.

public static final long BROWSABLE_TYPE_TITLES

The type of browsable that only contains playable media items.

This value matches with the folder type 'Titles' as specified in the section 6.10.2.2 of the Bluetooth AVRCP 1.5.

public static final long BROWSABLE_TYPE_ALBUMS

The type of browsable that contains browsable items categorized by album.

This value matches with the folder type 'Albums' as specified in the section 6.10.2.2 of the Bluetooth AVRCP 1.5.

public static final long BROWSABLE_TYPE_ARTISTS

The type of browsable that contains browsable items categorized by artist.

This value matches with the folder type 'Artists' as specified in the section 6.10.2.2 of the Bluetooth AVRCP 1.5.

public static final long BROWSABLE_TYPE_GENRES

The type of browsable that contains browsable items categorized by genre.

This value matches with the folder type 'Genres' as specified in the section 6.10.2.2 of the Bluetooth AVRCP 1.5.

public static final long BROWSABLE_TYPE_PLAYLISTS

The type of browsable that contains browsable items categorized by playlist.

This value matches with the folder type 'Playlists' as specified in the section 6.10.2.2 of the Bluetooth AVRCP 1.5.

public static final long BROWSABLE_TYPE_YEARS

The type of browsable that contains browsable items categorized by year.

This value matches with the folder type 'Years' as specified in the section 6.10.2.2 of the Bluetooth AVRCP 1.5.

public static final java.lang.String METADATA_KEY_PLAYABLE

The metadata key for a java.lang.Long typed value to retrieve the information about whether the media is playable. A value of 0 indicates it is not a playable item. A value of 1 or non-zero indicates it is playable.

See also: MediaMetadata.Builder.putLong(String, long), MediaMetadata.getLong(String)

public static final java.lang.String METADATA_KEY_ADVERTISEMENT

The metadata key for a java.lang.Long typed value to retrieve the information about whether the media is an advertisement. A value of 0 indicates it is not an advertisement. A value of 1 or non-zero indicates it is an advertisement. If not specified, this value is set to 0 by default.

See also: MediaMetadata.Builder.putLong(String, long), MediaMetadata.getLong(String)

public static final java.lang.String METADATA_KEY_DOWNLOAD_STATUS

The metadata key for a java.lang.Long typed value to retrieve the information about the download status of the media which will be used for later offline playback. It should be one of the following:

See also: MediaMetadata.Builder.putLong(String, long), MediaMetadata.getLong(String)

public static final long STATUS_NOT_DOWNLOADED

The status value to indicate the media item is not downloaded.

See also: MediaMetadata.METADATA_KEY_DOWNLOAD_STATUS

public static final long STATUS_DOWNLOADING

The status value to indicate the media item is being downloaded.

See also: MediaMetadata.METADATA_KEY_DOWNLOAD_STATUS

public static final long STATUS_DOWNLOADED

The status value to indicate the media item is downloaded for later offline playback.

See also: MediaMetadata.METADATA_KEY_DOWNLOAD_STATUS

public static final java.lang.String METADATA_KEY_EXTRAS

A extra.

Methods

public boolean containsKey(java.lang.String key)

Returns true if the given key is contained in the metadata

Parameters:

key: a String key

Returns:

true if the key exists in this metadata, false otherwise

public java.lang.CharSequence getText(java.lang.String key)

Returns the value associated with the given key, or null if no mapping of the desired type exists for the given key or a null value is explicitly associated with the key.

Parameters:

key: The key the value is stored under

Returns:

a CharSequence value, or null

public java.lang.String getMediaId()

Returns the media id, or null if the id doesn't exist.

This is equivalent to the MediaMetadata.getString(String) with the MediaMetadata.METADATA_KEY_MEDIA_ID.

Returns:

media id. Can be null

See also: MediaMetadata.METADATA_KEY_MEDIA_ID

public java.lang.String getString(java.lang.String key)

Returns the value associated with the given key, or null if no mapping of the desired type exists for the given key or a null value is explicitly associated with the key.

Parameters:

key: The key the value is stored under

Returns:

a String value, or null

public long getLong(java.lang.String key)

Returns the value associated with the given key, or 0L if no long exists for the given key.

Parameters:

key: The key the value is stored under

Returns:

a long value

public Rating getRating(java.lang.String key)

Return a Rating for the given key or null if no rating exists for the given key.

For the MediaMetadata.METADATA_KEY_USER_RATING, A null return value means that user rating cannot be set by MediaController.

Parameters:

key: The key the value is stored under

Returns:

A Rating or null

public float getFloat(java.lang.String key)

Return the value associated with the given key, or 0.0f if no long exists for the given key.

Parameters:

key: The key the value is stored under

Returns:

a float value

public Bitmap getBitmap(java.lang.String key)

Return a Bitmap for the given key or null if no bitmap exists for the given key.

Parameters:

key: The key the value is stored under

Returns:

A Bitmap or null

public Bundle getExtras()

Get the extra from the metadata object.

Returns:

A or null

public int size()

Get the number of fields in this metadata.

Returns:

The number of fields in the metadata.

public java.util.Set<java.lang.String> keySet()

Returns a Set containing the Strings used as keys in this metadata.

Returns:

a Set of String keys

public java.lang.String toString()

public java.lang.Object getObject(java.lang.String key)

Gets the object which matches the given key in the backing bundle.

public void onPreParceling(boolean isStream)

public void onPostParceling()

Source

/*
 * Copyright 2019 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package androidx.media2.common;

import static androidx.annotation.RestrictTo.Scope.LIBRARY;
import static androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP;

import android.graphics.Bitmap;
import android.net.Uri;
import android.os.Bundle;
import android.util.Log;

import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.annotation.RestrictTo;
import androidx.annotation.StringDef;
import androidx.collection.ArrayMap;
import androidx.core.graphics.BitmapCompat;
import androidx.versionedparcelable.CustomVersionedParcelable;
import androidx.versionedparcelable.NonParcelField;
import androidx.versionedparcelable.ParcelField;
import androidx.versionedparcelable.ParcelImpl;
import androidx.versionedparcelable.ParcelUtils;
import androidx.versionedparcelable.VersionedParcelable;
import androidx.versionedparcelable.VersionedParcelize;

import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;

/**
 * Contains metadata about an item, such as the title, artist, etc. This is optional, but you'd
 * better to provide this as much as possible when you're using media widget and/or session APIs.
 * <p>
 * The media widget components build its UI based on the metadata here. For an example,
 * {@link androidx.media2.widget.MediaControlView} will show title from the metadata.
 * <p>
 * The {@link androidx.media2.session.MediaLibraryService.MediaLibrarySession} would require
 * some metadata values when it provides
 * {@link MediaItem}s to {@link androidx.media2.session.MediaBrowser}.
 * <p>
 * Topics covered here:
 * <ol>
 * <li><a href="#MediaId">Media ID</a>
 * <li><a href="#Browsable">Browsable type</a>
 * <li><a href="#Playable">Playable</a>
 * <li><a href="#Duration">Duration</a>
 * <li><a href="#UserRating">User rating</a>
 * </ol>
 * <h3 id="MediaId">{@link MediaMetadata#METADATA_KEY_MEDIA_ID Media ID}</h3>
 * <p>
 * If set, the media ID must be the persistent key for the underlying media contents, so
 * {@link androidx.media2.session.MediaController} and {@link androidx.media2.session.MediaBrowser}
 * can store the information and reuse it later. Some APIs requires a media ID (e.g.
 * {@link androidx.media2.session.MediaController#setRating}, so you'd better specify
 * one.
 * <p>
 * Typical example of using media ID is the URI of the contents, but use it with the caution because
 * the metadata is shared across the process in plain text.
 * <p>
 * The {@link androidx.media2.session.MediaLibraryService.MediaLibrarySession} would require it
 * for the library root, so {@link androidx.media2.session.MediaBrowser} can call subsequent
 * {@link androidx.media2.session.MediaBrowser#getChildren} with the ID.
 * <h3 id="Browsable">{@link MediaMetadata#METADATA_KEY_BROWSABLE Browsable type}</h3>
 * <p>
 * Browsable defines whether the media item has children and type of children if any. With this,
 * {@link androidx.media2.session.MediaBrowser} can know whether the subsequent
 * {@link androidx.media2.session.MediaBrowser#getChildren} would successfully run.
 * <p>
 * The {@link androidx.media2.session.MediaLibraryService.MediaLibrarySession} would require
 * the explicit browsable type for the media items returned by the
 * {@link
 * androidx.media2.session.MediaLibraryService.MediaLibrarySession.MediaLibrarySessionCallback}.
 * <h3 id="Playable">{@link MediaMetadata#METADATA_KEY_PLAYABLE Playable type}</h3>
 * <p>
 * Playable defines whether the media item can be played or not. It may be possible for a playlist
 * to contain a media item which isn't playable in order to show a disabled media item.
 * <p>
 * The {@link androidx.media2.session.MediaLibraryService.MediaLibrarySession} would require
 * the explicit playable value for the media items returned by the
 * {@link
 * androidx.media2.session.MediaLibraryService.MediaLibrarySession.MediaLibrarySessionCallback}.
 * <h3 id="Duration">{@link MediaMetadata#METADATA_KEY_DURATION Duration}</h3>
 * The duration is the length of the contents. The {@link androidx.media2.session.MediaController}
 * can only get the duration through the metadata. This tells when would the playback ends, and also
 * tells about the allowed range of {@link androidx.media2.session.MediaController#seekTo(long)}.
 * <p>
 * If it's not set by developer, {@link androidx.media2.session.MediaSession} would update
 * the duration in the metadata with the {@link SessionPlayer#getDuration()}.
 * <h3 id="UserRating">{@link MediaMetadata#METADATA_KEY_USER_RATING User rating}</h3>
 * <p>
 * Prefer to have unrated user rating instead of {@code null}, so
 * {@link androidx.media2.session.MediaController} can know the possible user rating type
 * for calling {@link androidx.media2.session.MediaController#setRating(String, Rating)}.
 */
// New version of MediaMetadata with following changes
//   - Don't implement Parcelable for updatable support.
//   - Also support MediaDescription features. MediaDescription is deprecated instead because
//     it was insufficient for controller to display media contents. (e.g. duration is missing)
@VersionedParcelize(isCustom = true)
public final class MediaMetadata extends CustomVersionedParcelable {
    private static final String TAG = "MediaMetadata";

    /**
     * The metadata key for a {@link CharSequence} or {@link String} typed value to retrieve the
     * information about the title of the media.
     *
     * @see Builder#putText(String, CharSequence)
     * @see Builder#putString(String, String)
     * @see #getText(String)
     * @see #getString(String)
     */
    public static final String METADATA_KEY_TITLE = android.media.MediaMetadata.METADATA_KEY_TITLE;

    /**
     * The metadata key for a {@link CharSequence} or {@link String} typed value to retrieve the
     * information about the artist of the media.
     *
     * @see Builder#putText(String, CharSequence)
     * @see Builder#putString(String, String)
     * @see #getText(String)
     * @see #getString(String)
     */
    public static final String METADATA_KEY_ARTIST =
            android.media.MediaMetadata.METADATA_KEY_ARTIST;

    /**
     * The metadata key for a {@link Long} typed value to retrieve the information about the
     * duration of the media in ms. A negative duration indicates that the duration is unknown
     * (or infinite).
     *
     * @see Builder#putLong(String, long)
     * @see #getLong(String)
     */
    public static final String METADATA_KEY_DURATION =
            android.media.MediaMetadata.METADATA_KEY_DURATION;

    /**
     * The metadata key for a {@link CharSequence} or {@link String} typed value to retrieve the
     * information about the album title for the media.
     *
     * @see Builder#putText(String, CharSequence)
     * @see Builder#putString(String, String)
     * @see #getText(String)
     * @see #getString(String)
     */
    public static final String METADATA_KEY_ALBUM =
            android.media.MediaMetadata.METADATA_KEY_ALBUM;

    /**
     * The metadata key for a {@link CharSequence} or {@link String} typed value to retrieve the
     * information about the author of the media.
     *
     * @see Builder#putText(String, CharSequence)
     * @see Builder#putString(String, String)
     * @see #getText(String)
     * @see #getString(String)
     */
    public static final String METADATA_KEY_AUTHOR =
            android.media.MediaMetadata.METADATA_KEY_AUTHOR;

    /**
     * The metadata key for a {@link CharSequence} or {@link String} typed value to retrieve the
     * information about the writer of the media.
     *
     * @see Builder#putText(String, CharSequence)
     * @see Builder#putString(String, String)
     * @see #getText(String)
     * @see #getString(String)
     */
    public static final String METADATA_KEY_WRITER =
            android.media.MediaMetadata.METADATA_KEY_WRITER;

    /**
     * The metadata key for a {@link CharSequence} or {@link String} typed value to retrieve the
     * information about the composer of the media.
     *
     * @see Builder#putText(String, CharSequence)
     * @see Builder#putString(String, String)
     * @see #getText(String)
     * @see #getString(String)
     */
    public static final String METADATA_KEY_COMPOSER =
            android.media.MediaMetadata.METADATA_KEY_COMPOSER;

    /**
     * The metadata key for a {@link CharSequence} or {@link String} typed value to retrieve the
     * information about the compilation status of the media.
     *
     * @see Builder#putText(String, CharSequence)
     * @see Builder#putString(String, String)
     * @see #getText(String)
     * @see #getString(String)
     */
    public static final String METADATA_KEY_COMPILATION =
            android.media.MediaMetadata.METADATA_KEY_COMPILATION;

    /**
     * The metadata key for a {@link CharSequence} or {@link String} typed value to retrieve the
     * information about the date the media was created or published.
     * The format is unspecified but RFC 3339 is recommended.
     *
     * @see Builder#putText(String, CharSequence)
     * @see Builder#putString(String, String)
     * @see #getText(String)
     * @see #getString(String)
     */
    public static final String METADATA_KEY_DATE = android.media.MediaMetadata.METADATA_KEY_DATE;

    /**
     * The metadata key for a {@link Long} typed value to retrieve the information about the year
     * the media was created or published.
     *
     * @see Builder#putLong(String, long)
     * @see #getLong(String)
     */
    public static final String METADATA_KEY_YEAR = android.media.MediaMetadata.METADATA_KEY_YEAR;

    /**
     * The metadata key for a {@link CharSequence} or {@link String} typed value to retrieve the
     * information about the genre of the media.
     *
     * @see Builder#putText(String, CharSequence)
     * @see Builder#putString(String, String)
     * @see #getText(String)
     * @see #getString(String)
     */
    public static final String METADATA_KEY_GENRE = android.media.MediaMetadata.METADATA_KEY_GENRE;

    /**
     * The metadata key for a {@link Long} typed value to retrieve the information about the
     * track number for the media.
     *
     * @see Builder#putLong(String, long)
     * @see #getLong(String)
     */
    public static final String METADATA_KEY_TRACK_NUMBER =
            android.media.MediaMetadata.METADATA_KEY_TRACK_NUMBER;

    /**
     * The metadata key for a {@link Long} typed value to retrieve the information about the
     * number of tracks in the media's original source.
     *
     * @see Builder#putLong(String, long)
     * @see #getLong(String)
     */
    public static final String METADATA_KEY_NUM_TRACKS =
            android.media.MediaMetadata.METADATA_KEY_NUM_TRACKS;

    /**
     * The metadata key for a {@link Long} typed value to retrieve the information about the
     * disc number for the media's original source.
     *
     * @see Builder#putLong(String, long)
     * @see #getLong(String)
     */
    public static final String METADATA_KEY_DISC_NUMBER =
            android.media.MediaMetadata.METADATA_KEY_DISC_NUMBER;

    /**
     * The metadata key for a {@link CharSequence} or {@link String} typed value to retrieve the
     * information about the artist for the album of the media's original source.
     *
     * @see Builder#putText(String, CharSequence)
     * @see Builder#putString(String, String)
     * @see #getText(String)
     * @see #getString(String)
     */
    public static final String METADATA_KEY_ALBUM_ARTIST =
            android.media.MediaMetadata.METADATA_KEY_ALBUM_ARTIST;

    /**
     * The metadata key for a {@link Bitmap} typed value to retrieve the information about the
     * artwork for the media.
     * The artwork should be relatively small and may be scaled down if it is too large.
     * For higher resolution artwork, {@link #METADATA_KEY_ART_URI} should be used instead.
     *
     * @see Builder#putBitmap(String, Bitmap)
     * @see #getBitmap(String)
     */
    public static final String METADATA_KEY_ART = android.media.MediaMetadata.METADATA_KEY_ART;

    /**
     * The metadata key for a {@link CharSequence} or {@link String} typed value to retrieve the
     * information about Uri of the artwork for the media.
     *
     * @see Builder#putText(String, CharSequence)
     * @see Builder#putString(String, String)
     * @see #getText(String)
     * @see #getString(String)
     */
    public static final String METADATA_KEY_ART_URI =
            android.media.MediaMetadata.METADATA_KEY_ART_URI;

    /**
     * The metadata key for a {@link Bitmap} typed value to retrieve the information about the
     * artwork for the album of the media's original source.
     * The artwork should be relatively small and may be scaled down if it is too large.
     * For higher resolution artwork, {@link #METADATA_KEY_ALBUM_ART_URI} should be used instead.
     *
     * @see Builder#putBitmap(String, Bitmap)
     * @see #getBitmap(String)
     */
    public static final String METADATA_KEY_ALBUM_ART =
            android.media.MediaMetadata.METADATA_KEY_ALBUM_ART;

    /**
     * The metadata key for a {@link CharSequence} or {@link String} typed value to retrieve the
     * information about the Uri of the artwork for the album of the media's original source.
     *
     * @see Builder#putText(String, CharSequence)
     * @see Builder#putString(String, String)
     * @see #getText(String)
     * @see #getString(String)
     */
    public static final String METADATA_KEY_ALBUM_ART_URI =
            android.media.MediaMetadata.METADATA_KEY_ALBUM_ART_URI;

    /**
     * The metadata key for a {@link Rating} typed value to retrieve the information about the
     * user's rating for the media. Prefer to have unrated user rating instead of {@code null}, so
     * {@link androidx.media2.session.MediaController} can know the possible user rating type.
     *
     * @see Builder#putRating(String, Rating)
     * @see #getRating(String)
     * @see <a href="#UserRating">User rating</a>
     */
    public static final String METADATA_KEY_USER_RATING =
            android.media.MediaMetadata.METADATA_KEY_USER_RATING;

    /**
     * The metadata key for a {@link Rating} typed value to retrieve the information about the
     * overall rating for the media.
     *
     * @see Builder#putRating(String, Rating)
     * @see #getRating(String)
     */
    public static final String METADATA_KEY_RATING =
            android.media.MediaMetadata.METADATA_KEY_RATING;

    /**
     * The metadata key for a {@link CharSequence} or {@link String} typed value to retrieve the
     * information about the title that is suitable for display to the user.
     * It will generally be the same as {@link #METADATA_KEY_TITLE} but may differ for some formats.
     * When displaying media described by this metadata, this should be preferred if present.
     *
     * @see Builder#putText(String, CharSequence)
     * @see Builder#putString(String, String)
     * @see #getText(String)
     * @see #getString(String)
     */
    public static final String METADATA_KEY_DISPLAY_TITLE =
            android.media.MediaMetadata.METADATA_KEY_DISPLAY_TITLE;

    /**
     * The metadata key for a {@link CharSequence} or {@link String} typed value to retrieve the
     * information about the subtitle that is suitable for display to the user.
     * When displaying a second line for media described by this metadata, this should be preferred
     * to other fields if present.
     *
     * @see Builder#putText(String, CharSequence)
     * @see Builder#putString(String, String)
     * @see #getText(String)
     * @see #getString(String)
     */
    public static final String METADATA_KEY_DISPLAY_SUBTITLE =
            android.media.MediaMetadata.METADATA_KEY_DISPLAY_SUBTITLE;

    /**
     * The metadata key for a {@link CharSequence} or {@link String} typed value to retrieve the
     * information about the description that is suitable for display to the user.
     * When displaying more information for media described by this metadata,
     * this should be preferred to other fields if present.
     *
     * @see Builder#putText(String, CharSequence)
     * @see Builder#putString(String, String)
     * @see #getText(String)
     * @see #getString(String)
     */
    public static final String METADATA_KEY_DISPLAY_DESCRIPTION =
            android.media.MediaMetadata.METADATA_KEY_DISPLAY_DESCRIPTION;

    /**
     * The metadata key for a {@link Bitmap} typed value to retrieve the information about the icon
     * or thumbnail that is suitable for display to the user.
     * When displaying an icon for media described by this metadata, this should be preferred to
     * other fields if present.
     * <p>
     * The icon should be relatively small and may be scaled down if it is too large.
     * For higher resolution artwork, {@link #METADATA_KEY_DISPLAY_ICON_URI} should be used instead.
     *
     * @see Builder#putBitmap(String, Bitmap)
     * @see #getBitmap(String)
     */
    public static final String METADATA_KEY_DISPLAY_ICON =
            android.media.MediaMetadata.METADATA_KEY_DISPLAY_ICON;

    /**
     * The metadata key for a {@link CharSequence} or {@link String} typed value to retrieve the
     * information about the Uri of icon or thumbnail that is suitable for display to the user.
     * When displaying more information for media described by this metadata, the
     * display description should be preferred to other fields when present.
     *
     * @see Builder#putText(String, CharSequence)
     * @see Builder#putString(String, String)
     * @see #getText(String)
     * @see #getString(String)
     */
    public static final String METADATA_KEY_DISPLAY_ICON_URI =
            android.media.MediaMetadata.METADATA_KEY_DISPLAY_ICON_URI;

    /**
     * The metadata key for a {@link CharSequence} or {@link String} typed value to retrieve the
     * information about the media ID of the content. This value is specific to the
     * service providing the content. If used, this should be a persistent key for the underlying
     * content. This ID is used by {@link androidx.media2.session.MediaController} and
     * {@link androidx.media2.session.MediaBrowser}.
     *
     * @see Builder#putText(String, CharSequence)
     * @see Builder#putString(String, String)
     * @see #getText(String)
     * @see #getString(String)
     * @see <a href="#MediaID">Media ID</a>
     */
    public static final String METADATA_KEY_MEDIA_ID =
            android.media.MediaMetadata.METADATA_KEY_MEDIA_ID;

    /**
     * The metadata key for a {@link CharSequence} or {@link String} typed value to retrieve the
     * information about the Uri of the content. This value is specific to the service providing the
     * content.
     *
     * @see Builder#putText(String, CharSequence)
     * @see Builder#putString(String, String)
     * @see #getText(String)
     * @see #getString(String)
     */
    public static final String METADATA_KEY_MEDIA_URI =
            android.media.MediaMetadata.METADATA_KEY_MEDIA_URI;

    /**
     * @hide
     * The metadata key for a {@link Float} typed value to retrieve the information about the
     * radio frequency if this metadata represents radio content.
     *
     * @see Builder#putFloat(String, float)
     * @see #getFloat(String)
     */
    @RestrictTo(LIBRARY)
    public static final String METADATA_KEY_RADIO_FREQUENCY =
            "androidx.media2.metadata.RADIO_FREQUENCY";

    /**
     * @hide
     * The metadata key for a {@link CharSequence} or {@link String} typed value to retrieve the
     * information about the radio program name if this metadata represents radio content.
     *
     * @see MediaMetadata.Builder#putText(String, CharSequence)
     * @see MediaMetadata.Builder#putString(String, String)
     * @see #getText(String)
     * @see #getString(String)
     */
    @RestrictTo(LIBRARY)
    public static final String METADATA_KEY_RADIO_PROGRAM_NAME =
            "androidx.media2.metadata.RADIO_PROGRAM_NAME";

    /**
     * The metadata key for a {@link Long} typed value to retrieve the information about the type
     * of browsable. It should be one of the following:
     * <ul>
     * <li>{@link #BROWSABLE_TYPE_NONE}</li>
     * <li>{@link #BROWSABLE_TYPE_MIXED}</li>
     * <li>{@link #BROWSABLE_TYPE_TITLES}</li>
     * <li>{@link #BROWSABLE_TYPE_ALBUMS}</li>
     * <li>{@link #BROWSABLE_TYPE_ARTISTS}</li>
     * <li>{@link #BROWSABLE_TYPE_GENRES}</li>
     * <li>{@link #BROWSABLE_TYPE_PLAYLISTS}</li>
     * <li>{@link #BROWSABLE_TYPE_YEARS}</li>
     * </ul>
     * <p>
     * The values other than {@link #BROWSABLE_TYPE_NONE} mean that the media item has children.[
     * <p>
     * This matches with the bluetooth folder type of the media specified in the section 6.10.2.2 of
     * the Bluetooth AVRCP 1.5.
     *
     * @see Builder#putLong(String, long)
     * @see #getLong(String)
     * @see <a href="#Browsable">Browsable</a>
     */
    public static final String METADATA_KEY_BROWSABLE =
            "androidx.media2.metadata.BROWSABLE";

    /**
     * The type of browsable for non-browsable media item.
     */
    public static final long BROWSABLE_TYPE_NONE = -1;

    /**
     * The type of browsable that is unknown or contains media items of mixed types.
     * <p>
     * This value matches with the folder type 'Mixed' as specified in the section 6.10.2.2 of the
     * Bluetooth AVRCP 1.5.
     */
    public static final long BROWSABLE_TYPE_MIXED = 0;

    /**
     * The type of browsable that only contains playable media items.
     * <p>
     * This value matches with the folder type 'Titles' as specified in the section 6.10.2.2 of the
     * Bluetooth AVRCP 1.5.
     */
    public static final long BROWSABLE_TYPE_TITLES = 1;

    /**
     * The type of browsable that contains browsable items categorized by album.
     * <p>
     * This value matches with the folder type 'Albums' as specified in the section 6.10.2.2 of the
     * Bluetooth AVRCP 1.5.
     */
    public static final long BROWSABLE_TYPE_ALBUMS = 2;

    /**
     * The type of browsable that contains browsable items categorized by artist.
     * <p>
     * This value matches with the folder type 'Artists' as specified in the section 6.10.2.2 of the
     * Bluetooth AVRCP 1.5.
     */
    public static final long BROWSABLE_TYPE_ARTISTS = 3;

    /**
     * The type of browsable that contains browsable items categorized by genre.
     * <p>
     * This value matches with the folder type 'Genres' as specified in the section 6.10.2.2 of the
     * Bluetooth AVRCP 1.5.
     */
    public static final long BROWSABLE_TYPE_GENRES = 4;

    /**
     * The type of browsable that contains browsable items categorized by playlist.
     * <p>
     * This value matches with the folder type 'Playlists' as specified in the section 6.10.2.2 of
     * the Bluetooth AVRCP 1.5.
     */
    public static final long BROWSABLE_TYPE_PLAYLISTS = 5;

    /**
     * The type of browsable that contains browsable items categorized by year.
     * <p>
     * This value matches with the folder type 'Years' as specified in the section 6.10.2.2 of the
     * Bluetooth AVRCP 1.5.
     */
    public static final long BROWSABLE_TYPE_YEARS = 6;

    /**
     * The metadata key for a {@link Long} typed value to retrieve the information about whether
     * the media is playable. A value of 0 indicates it is not a playable item.
     * A value of 1 or non-zero indicates it is playable.
     *
     * @see Builder#putLong(String, long)
     * @see #getLong(String)
     * @see <a href="#Playable">Playable</a>
     */
    public static final String METADATA_KEY_PLAYABLE = "androidx.media2.metadata.PLAYABLE";

    /**
     * The metadata key for a {@link Long} typed value to retrieve the information about whether
     * the media is an advertisement. A value of 0 indicates it is not an advertisement.
     * A value of 1 or non-zero indicates it is an advertisement.
     * If not specified, this value is set to 0 by default.
     *
     * @see Builder#putLong(String, long)
     * @see #getLong(String)
     */
    public static final String METADATA_KEY_ADVERTISEMENT =
            "androidx.media2.metadata.ADVERTISEMENT";

    /**
     * The metadata key for a {@link Long} typed value to retrieve the information about the
     * download status of the media which will be used for later offline playback. It should be
     * one of the following:
     *
     * <ul>
     * <li>{@link #STATUS_NOT_DOWNLOADED}</li>
     * <li>{@link #STATUS_DOWNLOADING}</li>
     * <li>{@link #STATUS_DOWNLOADED}</li>
     * </ul>
     *
     * @see Builder#putLong(String, long)
     * @see #getLong(String)
     */
    public static final String METADATA_KEY_DOWNLOAD_STATUS =
            "androidx.media2.metadata.DOWNLOAD_STATUS";

    /**
     * The status value to indicate the media item is not downloaded.
     *
     * @see #METADATA_KEY_DOWNLOAD_STATUS
     */
    public static final long STATUS_NOT_DOWNLOADED = 0;

    /**
     * The status value to indicate the media item is being downloaded.
     *
     * @see #METADATA_KEY_DOWNLOAD_STATUS
     */
    public static final long STATUS_DOWNLOADING = 1;

    /**
     * The status value to indicate the media item is downloaded for later offline playback.
     *
     * @see #METADATA_KEY_DOWNLOAD_STATUS
     */
    public static final long STATUS_DOWNLOADED = 2;

    /**
     * A {@link Bundle} extra.
     */
    public static final String METADATA_KEY_EXTRAS = "androidx.media2.metadata.EXTRAS";

    /**
     * @hide
     */
    @RestrictTo(LIBRARY)
    @StringDef({METADATA_KEY_TITLE, METADATA_KEY_ARTIST, METADATA_KEY_ALBUM, METADATA_KEY_AUTHOR,
            METADATA_KEY_WRITER, METADATA_KEY_COMPOSER, METADATA_KEY_COMPILATION,
            METADATA_KEY_DATE, METADATA_KEY_GENRE, METADATA_KEY_ALBUM_ARTIST, METADATA_KEY_ART_URI,
            METADATA_KEY_ALBUM_ART_URI, METADATA_KEY_DISPLAY_TITLE, METADATA_KEY_DISPLAY_SUBTITLE,
            METADATA_KEY_DISPLAY_DESCRIPTION, METADATA_KEY_DISPLAY_ICON_URI,
            METADATA_KEY_MEDIA_ID, METADATA_KEY_MEDIA_URI, METADATA_KEY_RADIO_PROGRAM_NAME})
    @Retention(RetentionPolicy.SOURCE)
    public @interface TextKey {}

    /**
     * @hide
     */
    @RestrictTo(LIBRARY)
    @StringDef({METADATA_KEY_DURATION, METADATA_KEY_YEAR, METADATA_KEY_TRACK_NUMBER,
            METADATA_KEY_NUM_TRACKS, METADATA_KEY_DISC_NUMBER, METADATA_KEY_BROWSABLE,
            METADATA_KEY_PLAYABLE, METADATA_KEY_ADVERTISEMENT, METADATA_KEY_DOWNLOAD_STATUS})
    @Retention(RetentionPolicy.SOURCE)
    public @interface LongKey {}

    /**
     * @hide
     */
    @RestrictTo(LIBRARY)
    @StringDef({METADATA_KEY_ART, METADATA_KEY_ALBUM_ART, METADATA_KEY_DISPLAY_ICON})
    @Retention(RetentionPolicy.SOURCE)
    public @interface BitmapKey {}

    /**
     * @hide
     */
    @RestrictTo(LIBRARY)
    @StringDef({METADATA_KEY_USER_RATING, METADATA_KEY_RATING})
    @Retention(RetentionPolicy.SOURCE)
    public @interface RatingKey {}

    /**
     * @hide
     */
    @RestrictTo(LIBRARY)
    @StringDef({METADATA_KEY_RADIO_FREQUENCY})
    @Retention(RetentionPolicy.SOURCE)
    public @interface FloatKey {}

    /**
     * @hide
     */
    @RestrictTo(LIBRARY)
    @StringDef({METADATA_KEY_EXTRAS})
    @Retention(RetentionPolicy.SOURCE)
    public @interface BundleKey {}

    static final int METADATA_TYPE_LONG = 0;
    static final int METADATA_TYPE_TEXT = 1;
    static final int METADATA_TYPE_BITMAP = 2;
    static final int METADATA_TYPE_RATING = 3;
    static final int METADATA_TYPE_FLOAT = 4;
    static final int METADATA_TYPE_BUNDLE = 5;
    static final ArrayMap<String, Integer> METADATA_KEYS_TYPE;

    static {
        METADATA_KEYS_TYPE = new ArrayMap<>();
        METADATA_KEYS_TYPE.put(METADATA_KEY_TITLE, METADATA_TYPE_TEXT);
        METADATA_KEYS_TYPE.put(METADATA_KEY_ARTIST, METADATA_TYPE_TEXT);
        METADATA_KEYS_TYPE.put(METADATA_KEY_DURATION, METADATA_TYPE_LONG);
        METADATA_KEYS_TYPE.put(METADATA_KEY_ALBUM, METADATA_TYPE_TEXT);
        METADATA_KEYS_TYPE.put(METADATA_KEY_AUTHOR, METADATA_TYPE_TEXT);
        METADATA_KEYS_TYPE.put(METADATA_KEY_WRITER, METADATA_TYPE_TEXT);
        METADATA_KEYS_TYPE.put(METADATA_KEY_COMPOSER, METADATA_TYPE_TEXT);
        METADATA_KEYS_TYPE.put(METADATA_KEY_COMPILATION, METADATA_TYPE_TEXT);
        METADATA_KEYS_TYPE.put(METADATA_KEY_DATE, METADATA_TYPE_TEXT);
        METADATA_KEYS_TYPE.put(METADATA_KEY_YEAR, METADATA_TYPE_LONG);
        METADATA_KEYS_TYPE.put(METADATA_KEY_GENRE, METADATA_TYPE_TEXT);
        METADATA_KEYS_TYPE.put(METADATA_KEY_TRACK_NUMBER, METADATA_TYPE_LONG);
        METADATA_KEYS_TYPE.put(METADATA_KEY_NUM_TRACKS, METADATA_TYPE_LONG);
        METADATA_KEYS_TYPE.put(METADATA_KEY_DISC_NUMBER, METADATA_TYPE_LONG);
        METADATA_KEYS_TYPE.put(METADATA_KEY_ALBUM_ARTIST, METADATA_TYPE_TEXT);
        METADATA_KEYS_TYPE.put(METADATA_KEY_ART, METADATA_TYPE_BITMAP);
        METADATA_KEYS_TYPE.put(METADATA_KEY_ART_URI, METADATA_TYPE_TEXT);
        METADATA_KEYS_TYPE.put(METADATA_KEY_ALBUM_ART, METADATA_TYPE_BITMAP);
        METADATA_KEYS_TYPE.put(METADATA_KEY_ALBUM_ART_URI, METADATA_TYPE_TEXT);
        METADATA_KEYS_TYPE.put(METADATA_KEY_USER_RATING, METADATA_TYPE_RATING);
        METADATA_KEYS_TYPE.put(METADATA_KEY_RATING, METADATA_TYPE_RATING);
        METADATA_KEYS_TYPE.put(METADATA_KEY_DISPLAY_TITLE, METADATA_TYPE_TEXT);
        METADATA_KEYS_TYPE.put(METADATA_KEY_DISPLAY_SUBTITLE, METADATA_TYPE_TEXT);
        METADATA_KEYS_TYPE.put(METADATA_KEY_DISPLAY_DESCRIPTION, METADATA_TYPE_TEXT);
        METADATA_KEYS_TYPE.put(METADATA_KEY_DISPLAY_ICON, METADATA_TYPE_BITMAP);
        METADATA_KEYS_TYPE.put(METADATA_KEY_DISPLAY_ICON_URI, METADATA_TYPE_TEXT);
        METADATA_KEYS_TYPE.put(METADATA_KEY_MEDIA_ID, METADATA_TYPE_TEXT);
        METADATA_KEYS_TYPE.put(METADATA_KEY_MEDIA_URI, METADATA_TYPE_TEXT);
        METADATA_KEYS_TYPE.put(METADATA_KEY_RADIO_FREQUENCY, METADATA_TYPE_FLOAT);
        METADATA_KEYS_TYPE.put(METADATA_KEY_RADIO_PROGRAM_NAME, METADATA_TYPE_TEXT);
        METADATA_KEYS_TYPE.put(METADATA_KEY_BROWSABLE, METADATA_TYPE_LONG);
        METADATA_KEYS_TYPE.put(METADATA_KEY_PLAYABLE, METADATA_TYPE_LONG);
        METADATA_KEYS_TYPE.put(METADATA_KEY_ADVERTISEMENT, METADATA_TYPE_LONG);
        METADATA_KEYS_TYPE.put(METADATA_KEY_DOWNLOAD_STATUS, METADATA_TYPE_LONG);
        METADATA_KEYS_TYPE.put(METADATA_KEY_EXTRAS, METADATA_TYPE_BUNDLE);
    }

    // Parceled via mParcelableNoBitmapBundle for non-Bitmap values, and mBitmapListSlice for Bitmap
    // values.
    @NonParcelField
    Bundle mBundle;
    // For parceling mBundle's non-Bitmap values. Should be only used by onPreParceling() and
    // onPostParceling().
    @ParcelField(1)
    Bundle mParcelableWithoutBitmapBundle;

    // For parceling mBundle's Bitmap values. Should be only used by onPreParceling() and
    // onPostParceling().
    @ParcelField(2)
    ParcelImplListSlice mBitmapListSlice;

    // WARNING: Adding a new ParcelField may break old library users (b/152830728)

    /**
     * Used for VersionedParcelable
     */
    MediaMetadata() {
    }

    MediaMetadata(Bundle bundle) {
        mBundle = new Bundle(bundle);
        mBundle.setClassLoader(MediaMetadata.class.getClassLoader());
    }

    /**
     * Returns true if the given key is contained in the metadata
     *
     * @param key a String key
     * @return true if the key exists in this metadata, false otherwise
     */
    public boolean containsKey(@NonNull String key) {
        if (key == null) {
            throw new NullPointerException("key shouldn't be null");
        }
        return mBundle.containsKey(key);
    }

    /**
     * Returns the value associated with the given key, or null if no mapping of
     * the desired type exists for the given key or a null value is explicitly
     * associated with the key.
     *
     * @param key The key the value is stored under
     * @return a CharSequence value, or null
     */
    public @Nullable CharSequence getText(@NonNull @TextKey String key) {
        if (key == null) {
            throw new NullPointerException("key shouldn't be null");
        }
        return mBundle.getCharSequence(key);
    }

    /**
     * Returns the media id, or {@code null} if the id doesn't exist.
     *<p>
     * This is equivalent to the {@link #getString(String)} with the {@link #METADATA_KEY_MEDIA_ID}.
     *
     * @return media id. Can be {@code null}
     * @see #METADATA_KEY_MEDIA_ID
     */
    // TODO(jaewan): Hide -- no setMediaId()
    public @Nullable String getMediaId() {
        return getString(METADATA_KEY_MEDIA_ID);
    }

    /**
     * Returns the value associated with the given key, or null if no mapping of
     * the desired type exists for the given key or a null value is explicitly
     * associated with the key.
     *
     * @param key The key the value is stored under
     * @return a String value, or null
     */
    public @Nullable String getString(@NonNull @TextKey String key) {
        if (key == null) {
            throw new NullPointerException("key shouldn't be null");
        }
        CharSequence text = mBundle.getCharSequence(key);
        if (text != null) {
            return text.toString();
        }
        return null;
    }

    /**
     * Returns the value associated with the given key, or 0L if no long exists
     * for the given key.
     *
     * @param key The key the value is stored under
     * @return a long value
     */
    public long getLong(@NonNull @LongKey String key) {
        if (key == null) {
            throw new NullPointerException("key shouldn't be null");
        }
        return mBundle.getLong(key, 0);
    }

    /**
     * Return a {@link Rating} for the given key or null if no rating exists for
     * the given key.
     * <p>
     * For the {@link #METADATA_KEY_USER_RATING}, A {@code null} return value means that user rating
     * cannot be set by {@link androidx.media2.session.MediaController}.
     *
     * @param key The key the value is stored under
     * @return A {@link Rating} or {@code null}
     */
    public @Nullable Rating getRating(@NonNull @RatingKey String key) {
        if (key == null) {
            throw new NullPointerException("key shouldn't be null");
        }
        Rating rating = null;
        try {
            rating = ParcelUtils.getVersionedParcelable(mBundle, key);
        } catch (Exception e) {
            // ignore, value was not a rating
            Log.w(TAG, "Failed to retrieve a key as Rating.", e);
        }
        return rating;
    }

    /**
     * Return the value associated with the given key, or 0.0f if no long exists
     * for the given key.
     *
     * @param key The key the value is stored under
     * @return a float value
     */
    public float getFloat(@NonNull @FloatKey String key) {
        if (key == null) {
            throw new NullPointerException("key shouldn't be null");
        }
        return mBundle.getFloat(key);
    }

    /**
     * Return a {@link Bitmap} for the given key or null if no bitmap exists for
     * the given key.
     *
     * @param key The key the value is stored under
     * @return A {@link Bitmap} or null
     */
    public @Nullable Bitmap getBitmap(@NonNull @BitmapKey String key) {
        if (key == null) {
            throw new NullPointerException("key shouldn't be null");
        }
        Bitmap bmp = null;
        try {
            bmp = mBundle.getParcelable(key);
        } catch (Exception e) {
            // ignore, value was not a bitmap
            Log.w(TAG, "Failed to retrieve a key as Bitmap.", e);
        }
        return bmp;
    }

    /**
     * Get the extra {@link Bundle} from the metadata object.
     *
     * @return A {@link Bundle} or {@code null}
     */
    public @Nullable Bundle getExtras() {
        try {
            return mBundle.getBundle(METADATA_KEY_EXTRAS);
        } catch (Exception e) {
            // ignore, value was not an bundle
            Log.w(TAG, "Failed to retrieve an extra");
        }
        return null;
    }

    /**
     * Get the number of fields in this metadata.
     *
     * @return The number of fields in the metadata.
     */
    public int size() {
        return mBundle.size();
    }

    /**
     * Returns a Set containing the Strings used as keys in this metadata.
     *
     * @return a Set of String keys
     */
    public @NonNull Set<String> keySet() {
        return mBundle.keySet();
    }

    @Override
    public String toString() {
        return mBundle.toString();
    }

    /**
     * Gets the object which matches the given key in the backing bundle.
     *
     * @hide
     */
    @RestrictTo(LIBRARY_GROUP)
    public @Nullable Object getObject(@NonNull String key) {
        if (key == null) {
            throw new NullPointerException("key shouldn't be null");
        }
        return mBundle.get(key);
    }

    /**
     * @hide
     */
    @Override
    @RestrictTo(LIBRARY)
    @SuppressWarnings("SynchronizeOnNonFinalField") // mBundle is effectively final.
    public void onPreParceling(boolean isStream) {
        synchronized (mBundle) {
            if (mParcelableWithoutBitmapBundle == null) {
                mParcelableWithoutBitmapBundle = new Bundle(mBundle);
                List<ParcelImpl> parcelImplList = new ArrayList<>();
                for (String key : mBundle.keySet()) {
                    Object value = mBundle.get(key);
                    if (!(value instanceof Bitmap)) {
                        // Note: Null bitmap is sent through mParcelableNoBitmapBundle.
                        continue;
                    }
                    Bitmap bitmap = (Bitmap) value;
                    parcelImplList.add(MediaParcelUtils.toParcelable(new BitmapEntry(key, bitmap)));
                    mParcelableWithoutBitmapBundle.remove(key);
                }
                mBitmapListSlice = new ParcelImplListSlice(parcelImplList);
            }
        }
    }

    /**
     * @hide
     */
    @Override
    @RestrictTo(LIBRARY)
    public void onPostParceling() {
        mBundle = (mParcelableWithoutBitmapBundle != null)
                ? mParcelableWithoutBitmapBundle : new Bundle();
        if (mBitmapListSlice != null) {
            List<ParcelImpl> parcelImplList = mBitmapListSlice.getList();
            for (ParcelImpl parcelImpl : parcelImplList) {
                BitmapEntry entry = MediaParcelUtils.fromParcelable(parcelImpl);
                mBundle.putParcelable(entry.getKey(), entry.getBitmap());
            }
        }
    }

    /**
     * Use to build MediaMetadatax objects. The system defined metadata keys must
     * use the appropriate data type.
     */
    public static final class Builder {
        final Bundle mBundle;

        /**
         * Create an empty Builder. Any field that should be included in the
         * {@link MediaMetadata} must be added.
         */
        public Builder() {
            mBundle = new Bundle();
        }

        /**
         * Create a Builder using a {@link MediaMetadata} instance to set the
         * initial values. All fields in the source metadata will be included in
         * the new metadata. Fields can be overwritten by adding the same key.
         *
         * @param source
         */
        public Builder(@NonNull MediaMetadata source) {
            mBundle = new Bundle(source.mBundle);
        }

        /**
         * Only for the backward compatibility.
         *
         * @param bundle
         */
        Builder(Bundle bundle) {
            mBundle = new Bundle(bundle);
        }

        /**
         * Put a CharSequence value into the metadata. Custom keys may be used,
         * but if the METADATA_KEYs defined in this class are used they may only
         * be one of the following:
         * <ul>
         * <li>{@link #METADATA_KEY_TITLE}</li>
         * <li>{@link #METADATA_KEY_ARTIST}</li>
         * <li>{@link #METADATA_KEY_ALBUM}</li>
         * <li>{@link #METADATA_KEY_AUTHOR}</li>
         * <li>{@link #METADATA_KEY_WRITER}</li>
         * <li>{@link #METADATA_KEY_COMPOSER}</li>
         * <li>{@link #METADATA_KEY_COMPILATION}</li>
         * <li>{@link #METADATA_KEY_DATE}</li>
         * <li>{@link #METADATA_KEY_GENRE}</li>
         * <li>{@link #METADATA_KEY_ALBUM_ARTIST}</li>
         * <li>{@link #METADATA_KEY_ART_URI}</li>
         * <li>{@link #METADATA_KEY_ALBUM_ART_URI}</li>
         * <li>{@link #METADATA_KEY_DISPLAY_TITLE}</li>
         * <li>{@link #METADATA_KEY_DISPLAY_SUBTITLE}</li>
         * <li>{@link #METADATA_KEY_DISPLAY_DESCRIPTION}</li>
         * <li>{@link #METADATA_KEY_DISPLAY_ICON_URI}</li>
         * <li>{@link #METADATA_KEY_MEDIA_ID}</li>
         * <li>{@link #METADATA_KEY_MEDIA_URI}</li>
         * </ul>
         *
         * @param key The key for referencing this value
         * @param value The CharSequence value to store
         * @return The Builder to allow chaining
         */
        public @NonNull Builder putText(@NonNull @TextKey String key,
                @Nullable CharSequence value) {
            if (key == null) {
                throw new NullPointerException("key shouldn't be null");
            }
            if (METADATA_KEYS_TYPE.containsKey(key)) {
                if (METADATA_KEYS_TYPE.get(key) != METADATA_TYPE_TEXT) {
                    throw new IllegalArgumentException("The " + key
                            + " key cannot be used to put a CharSequence");
                }
            }
            mBundle.putCharSequence(key, value);
            return this;
        }

        /**
         * Put a String value into the metadata. Custom keys may be used, but if
         * the METADATA_KEYs defined in this class are used they may only be one
         * of the following:
         * <ul>
         * <li>{@link #METADATA_KEY_TITLE}</li>
         * <li>{@link #METADATA_KEY_ARTIST}</li>
         * <li>{@link #METADATA_KEY_ALBUM}</li>
         * <li>{@link #METADATA_KEY_AUTHOR}</li>
         * <li>{@link #METADATA_KEY_WRITER}</li>
         * <li>{@link #METADATA_KEY_COMPOSER}</li>
         * <li>{@link #METADATA_KEY_COMPILATION}</li>
         * <li>{@link #METADATA_KEY_DATE}</li>
         * <li>{@link #METADATA_KEY_GENRE}</li>
         * <li>{@link #METADATA_KEY_ALBUM_ARTIST}</li>
         * <li>{@link #METADATA_KEY_ART_URI}</li>
         * <li>{@link #METADATA_KEY_ALBUM_ART_URI}</li>
         * <li>{@link #METADATA_KEY_DISPLAY_TITLE}</li>
         * <li>{@link #METADATA_KEY_DISPLAY_SUBTITLE}</li>
         * <li>{@link #METADATA_KEY_DISPLAY_DESCRIPTION}</li>
         * <li>{@link #METADATA_KEY_DISPLAY_ICON_URI}</li>
         * <li>{@link #METADATA_KEY_MEDIA_ID}</li>
         * <li>{@link #METADATA_KEY_MEDIA_URI}</li>
         * </ul>
         *
         * @param key The key for referencing this value
         * @param value The String value to store
         * @return The Builder to allow chaining
         */
        public @NonNull Builder putString(@NonNull @TextKey String key,
                @Nullable String value) {
            if (key == null) {
                throw new NullPointerException("key shouldn't be null");
            }
            if (METADATA_KEYS_TYPE.containsKey(key)) {
                if (METADATA_KEYS_TYPE.get(key) != METADATA_TYPE_TEXT) {
                    throw new IllegalArgumentException("The " + key
                            + " key cannot be used to put a String");
                }
            }
            mBundle.putCharSequence(key, value);
            return this;
        }

        /**
         * Put a long value into the metadata. Custom keys may be used, but if
         * the METADATA_KEYs defined in this class are used they may only be one
         * of the following:
         * <ul>
         * <li>{@link #METADATA_KEY_DURATION}</li>
         * <li>{@link #METADATA_KEY_TRACK_NUMBER}</li>
         * <li>{@link #METADATA_KEY_NUM_TRACKS}</li>
         * <li>{@link #METADATA_KEY_DISC_NUMBER}</li>
         * <li>{@link #METADATA_KEY_YEAR}</li>
         * <li>{@link #METADATA_KEY_BROWSABLE}</li>
         * <li>{@link #METADATA_KEY_PLAYABLE}</li>
         * <li>{@link #METADATA_KEY_ADVERTISEMENT}</li>
         * <li>{@link #METADATA_KEY_DOWNLOAD_STATUS}</li>
         * </ul>
         *
         * @param key The key for referencing this value
         * @param value The String value to store
         * @return The Builder to allow chaining
         */
        public @NonNull Builder putLong(@NonNull @LongKey String key, long value) {
            if (key == null) {
                throw new NullPointerException("key shouldn't be null");
            }
            if (METADATA_KEYS_TYPE.containsKey(key)) {
                if (METADATA_KEYS_TYPE.get(key) != METADATA_TYPE_LONG) {
                    throw new IllegalArgumentException("The " + key
                            + " key cannot be used to put a long");
                }
            }
            mBundle.putLong(key, value);
            return this;
        }

        /**
         * Put a {@link Rating} into the metadata. Custom keys may be used, but
         * if the METADATA_KEYs defined in this class are used they may only be
         * one of the following:
         * <ul>
         * <li>{@link #METADATA_KEY_RATING}</li>
         * <li>{@link #METADATA_KEY_USER_RATING}</li>
         * </ul>
         *
         * @param key The key for referencing this value
         * @param value The String value to store
         * @return The Builder to allow chaining
         */
        public @NonNull Builder putRating(@NonNull @RatingKey String key,
                @Nullable Rating value) {
            if (key == null) {
                throw new NullPointerException("key shouldn't be null");
            }
            if (METADATA_KEYS_TYPE.containsKey(key)) {
                if (METADATA_KEYS_TYPE.get(key) != METADATA_TYPE_RATING) {
                    throw new IllegalArgumentException("The " + key
                            + " key cannot be used to put a Rating");
                }
            }
            ParcelUtils.putVersionedParcelable(mBundle, key, value);
            return this;
        }

        /**
         * Put a {@link Bitmap} into the metadata. Custom keys may be used, but
         * if the METADATA_KEYs defined in this class are used they may only be
         * one of the following:
         * <ul>
         * <li>{@link #METADATA_KEY_ART}</li>
         * <li>{@link #METADATA_KEY_ALBUM_ART}</li>
         * <li>{@link #METADATA_KEY_DISPLAY_ICON}</li>
         * </ul>
         * Large bitmaps may be scaled down when it is passed to the other process.
         * To pass full resolution images {@link Uri Uris} should be used with
         * {@link #putString}.
         *
         * @param key The key for referencing this value
         * @param value The Bitmap to store
         * @return The Builder to allow chaining
         */
        public @NonNull Builder putBitmap(@NonNull @BitmapKey String key,
                @Nullable Bitmap value) {
            if (key == null) {
                throw new NullPointerException("key shouldn't be null");
            }
            if (METADATA_KEYS_TYPE.containsKey(key)) {
                if (METADATA_KEYS_TYPE.get(key) != METADATA_TYPE_BITMAP) {
                    throw new IllegalArgumentException("The " + key
                            + " key cannot be used to put a Bitmap");
                }
            }
            mBundle.putParcelable(key, value);
            return this;
        }

        /**
         * Put a float value into the metadata. Custom keys may be used.
         *
         * @param key The key for referencing this value
         * @param value The float value to store
         * @return The Builder to allow chaining
         */
        public @NonNull Builder putFloat(@NonNull @LongKey String key, float value) {
            if (key == null) {
                throw new NullPointerException("key shouldn't be null");
            }
            if (METADATA_KEYS_TYPE.containsKey(key)) {
                if (METADATA_KEYS_TYPE.get(key) != METADATA_TYPE_FLOAT) {
                    throw new IllegalArgumentException("The " + key
                            + " key cannot be used to put a float");
                }
            }
            mBundle.putFloat(key, value);
            return this;
        }

        /**
         * Set a bundle of extras.
         *
         * @param extras The extras to include with this description or null.
         * @return The Builder to allow chaining
         */
        public @NonNull Builder setExtras(@Nullable Bundle extras) {
            mBundle.putBundle(METADATA_KEY_EXTRAS, extras);
            return this;
        }

        /**
         * Creates a {@link MediaMetadata} instance with the specified fields.
         *
         * @return The new MediaMetadatax instance
         */
        public @NonNull MediaMetadata build() {
            return new MediaMetadata(mBundle);
        }
    }

    /**
     * Stores a bitmap and the matching key. Used for sending bitmaps to other process one-by-one.
     */
    @VersionedParcelize
    static final class BitmapEntry implements VersionedParcelable {
        static final int BITMAP_SIZE_LIMIT_IN_BYTES = 256 * 1024; // 256 KB

        @ParcelField(1)
        String mKey;

        @ParcelField(2)
        Bitmap mBitmap;

        // WARNING: Adding a new ParcelField may break old library users (b/152830728)

        /**
         * Used for VersionedParcelable
         */
        BitmapEntry() {
        }

        BitmapEntry(@NonNull String key, @NonNull Bitmap bitmap) {
            mKey = key;
            mBitmap = bitmap;

            // Scale bitmap if it is large.
            final int sizeInBytes = getBitmapSizeInBytes(mBitmap);
            if (sizeInBytes > BITMAP_SIZE_LIMIT_IN_BYTES) {
                int oldWidth = bitmap.getWidth();
                int oldHeight = bitmap.getHeight();

                double scaleFactor = Math.sqrt(BITMAP_SIZE_LIMIT_IN_BYTES / (double) sizeInBytes);
                int newWidth = (int) (oldWidth * scaleFactor);
                int newHeight = (int) (oldHeight * scaleFactor);
                Log.i(TAG, "Scaling large bitmap of " + oldWidth + "x" + oldHeight + " into "
                        + newWidth + "x" + newHeight);
                mBitmap = Bitmap.createScaledBitmap(bitmap, newWidth, newHeight, true);
            }
        }

        String getKey() {
            return mKey;
        }

        Bitmap getBitmap() {
            return mBitmap;
        }

        private int getBitmapSizeInBytes(Bitmap bitmap) {
            return BitmapCompat.getAllocationByteCount(bitmap);
        }
    }
}