After using browsable or playable items to build your content hierarchy, apply content styles to determine how those items display in the car. Use these content styles:

Figure 1. List items prioritize titles and metadata over images.

Figure 2. Grid items prioritize images over titles and metadata.
Set default content styles
You can set global defaults for how your media items are displayed. To do so,
include specific constants in the BrowserRoot
extras bundle returned by your
service's onGetRoot
implementation and look for these constants
to determine the appropriate style.
These extras can be used as keys in the bundle:
DESCRIPTION_EXTRAS_KEY_CONTENT_STYLE_BROWSABLE
: A presentation hint for all browsable items within the browse tree.DESCRIPTION_EXTRAS_KEY_CONTENT_STYLE_PLAYABLE
: A presentation hint for all playable items within the browse tree.
These keys can map to the these integer constant values to influence the presentation of those items:
DESCRIPTION_EXTRAS_VALUE_CONTENT_STYLE_LIST_ITEM
: Corresponding items presented as list items.DESCRIPTION_EXTRAS_VALUE_CONTENT_STYLE_GRID_ITEM
: Corresponding items presented as grid items.DESCRIPTION_EXTRAS_VALUE_CONTENT_STYLE_CATEGORY_LIST_ITEM
: Corresponding items presented as "category" list items, similar to ordinary list items, but margins are applied around the items' icons. This improves the appearance of small icons. Icons must be tintable vector drawables. This hint is expected to be provided only for browsable items.DESCRIPTION_EXTRAS_VALUE_CONTENT_STYLE_CATEGORY_GRID_ITEM
: Corresponding items presented as "category" grid items and are similar to ordinary grid items, but margins are applied around the items' icons. This improves the appearance of small icons. The icons must be tintable vector drawables. This hint is expected to be provided only for browsable items.
This code snippet shows how to set the default content style for browsable items to grids and playable items to lists:
Kotlin
import androidx.media.utils.MediaConstants
@Nullable
override fun onGetRoot(
@NonNull clientPackageName: String,
clientUid: Int,
@Nullable rootHints: Bundle
): BrowserRoot {
val extras = Bundle()
extras.putInt(
MediaConstants.DESCRIPTION_EXTRAS_KEY_CONTENT_STYLE_BROWSABLE,
MediaConstants.DESCRIPTION_EXTRAS_VALUE_CONTENT_STYLE_GRID_ITEM)
extras.putInt(
MediaConstants.DESCRIPTION_EXTRAS_KEY_CONTENT_STYLE_PLAYABLE,
MediaConstants.DESCRIPTION_EXTRAS_VALUE_CONTENT_STYLE_LIST_ITEM)
return BrowserRoot(ROOT_ID, extras)
}
Java
import androidx.media.utils.MediaConstants;
@Nullable
@Override
public BrowserRoot onGetRoot(
@NonNull String clientPackageName,
int clientUid,
@Nullable Bundle rootHints) {
Bundle extras = new Bundle();
extras.putInt(
MediaConstants.DESCRIPTION_EXTRAS_KEY_CONTENT_STYLE_BROWSABLE,
MediaConstants.DESCRIPTION_EXTRAS_VALUE_CONTENT_STYLE_GRID_ITEM);
extras.putInt(
MediaConstants.DESCRIPTION_EXTRAS_KEY_CONTENT_STYLE_PLAYABLE,
MediaConstants.DESCRIPTION_EXTRAS_VALUE_CONTENT_STYLE_LIST_ITEM);
return new BrowserRoot(ROOT_ID, extras);
}
Set per-item content styles
You can override the default content style for any browsable media item's
descendants, as well as for any media item. To override the default for a
browsable media item's descendants, create an extras bundle in the
MediaDescription
of the media item and add the same previously mentioned
hints:
DESCRIPTION_EXTRAS_KEY_CONTENT_STYLE_PLAYABLE
applies to that item's playable descendants.DESCRIPTION_EXTRAS_KEY_CONTENT_STYLE_BROWSABLE
applies to that item's browsable descendants.
To override the default for a specific media item (not its descendants), create
an extras bundle in the MediaDescription
of the media item. Then, add a hint
with the key DESCRIPTION_EXTRAS_KEY_CONTENT_STYLE_SINGLE_ITEM
. Use the
same values described previously to specify that item's presentation.
This code snippet shows how to create a browsable MediaItem
that overrides
the default content style for itself and its descendants. It styles itself as
a category list item, its browsable descendants as list items, and its playable
descendants as grid items.
Kotlin
import androidx.media.utils.MediaConstants
private fun createBrowsableMediaItem(
mediaId: String,
folderName: String,
iconUri: Uri
): MediaBrowser.MediaItem {
val mediaDescriptionBuilder = MediaDescription.Builder()
mediaDescriptionBuilder.setMediaId(mediaId)
mediaDescriptionBuilder.setTitle(folderName)
mediaDescriptionBuilder.setIconUri(iconUri)
val extras = Bundle()
extras.putInt(
MediaConstants.DESCRIPTION_EXTRAS_KEY_CONTENT_STYLE_SINGLE_ITEM,
MediaConstants.DESCRIPTION_EXTRAS_VALUE_CONTENT_STYLE_CATEGORY_LIST_ITEM)
extras.putInt(
MediaConstants.DESCRIPTION_EXTRAS_KEY_CONTENT_STYLE_BROWSABLE,
MediaConstants.DESCRIPTION_EXTRAS_VALUE_CONTENT_STYLE_LIST_ITEM)
extras.putInt(
MediaConstants.DESCRIPTION_EXTRAS_KEY_CONTENT_STYLE_PLAYABLE,
MediaConstants.DESCRIPTION_EXTRAS_VALUE_CONTENT_STYLE_GRID_ITEM)
mediaDescriptionBuilder.setExtras(extras)
return MediaBrowser.MediaItem(
mediaDescriptionBuilder.build(), MediaBrowser.MediaItem.FLAG_BROWSABLE)
}
Java
import androidx.media.utils.MediaConstants;
private MediaBrowser.MediaItem createBrowsableMediaItem(
String mediaId,
String folderName,
Uri iconUri) {
MediaDescription.Builder mediaDescriptionBuilder = new MediaDescription.Builder();
mediaDescriptionBuilder.setMediaId(mediaId);
mediaDescriptionBuilder.setTitle(folderName);
mediaDescriptionBuilder.setIconUri(iconUri);
Bundle extras = new Bundle();
extras.putInt(
MediaConstants.DESCRIPTION_EXTRAS_KEY_CONTENT_STYLE_SINGLE_ITEM,
MediaConstants.DESCRIPTION_EXTRAS_VALUE_CONTENT_STYLE_CATEGORY_LIST_ITEM);
extras.putInt(
MediaConstants.DESCRIPTION_EXTRAS_KEY_CONTENT_STYLE_BROWSABLE,
MediaConstants.DESCRIPTION_EXTRAS_VALUE_CONTENT_STYLE_LIST_ITEM);
extras.putInt(
MediaConstants.DESCRIPTION_EXTRAS_KEY_CONTENT_STYLE_PLAYABLE,
MediaConstants.DESCRIPTION_EXTRAS_VALUE_CONTENT_STYLE_GRID_ITEM);
mediaDescriptionBuilder.setExtras(extras);
return new MediaBrowser.MediaItem(
mediaDescriptionBuilder.build(), MediaBrowser.MediaItem.FLAG_BROWSABLE);
}
Group items using title hints
To group related media items, use a per-item hint. Every media item in a group
must declare an extras bundle in its MediaDescription
. This bundle must
include a mapping with the key
DESCRIPTION_EXTRAS_KEY_CONTENT_STYLE_GROUP_TITLE
and an identical string
value. Localize this string, as it is used for the group's title.
This code snippet shows how to create a MediaItem
with a subgroup heading of
Songs
:
Kotlin
import androidx.media.utils.MediaConstants
private fun createMediaItem(
mediaId: String,
folderName: String,
iconUri: Uri
): MediaBrowser.MediaItem {
val mediaDescriptionBuilder = MediaDescription.Builder()
mediaDescriptionBuilder.setMediaId(mediaId)
mediaDescriptionBuilder.setTitle(folderName)
mediaDescriptionBuilder.setIconUri(iconUri)
val extras = Bundle()
extras.putString(
MediaConstants.DESCRIPTION_EXTRAS_KEY_CONTENT_STYLE_GROUP_TITLE,
"Songs")
mediaDescriptionBuilder.setExtras(extras)
return MediaBrowser.MediaItem(
mediaDescriptionBuilder.build(), /* playable or browsable flag*/)
}
Java
import androidx.media.utils.MediaConstants;
private MediaBrowser.MediaItem createMediaItem(String mediaId, String folderName, Uri iconUri) {
MediaDescription.Builder mediaDescriptionBuilder = new MediaDescription.Builder();
mediaDescriptionBuilder.setMediaId(mediaId);
mediaDescriptionBuilder.setTitle(folderName);
mediaDescriptionBuilder.setIconUri(iconUri);
Bundle extras = new Bundle();
extras.putString(
MediaConstants.DESCRIPTION_EXTRAS_KEY_CONTENT_STYLE_GROUP_TITLE,
"Songs");
mediaDescriptionBuilder.setExtras(extras);
return new MediaBrowser.MediaItem(
mediaDescriptionBuilder.build(), /* playable or browsable flag*/);
}
Your app must pass all media items that you want to group together as a contiguous block. For example, consider displaying two groups of media items, "Songs" and "Albums", in that order. If your app passes five media items in this order, Android Auto and AAOS interpret them as four separate groups:
- Media item A with
extras.putString(MediaConstants.DESCRIPTION_EXTRAS_KEY_CONTENT_STYLE_GROUP_TITLE, "Songs")
- Media item B with
extras.putString(MediaConstants.DESCRIPTION_EXTRAS_KEY_CONTENT_STYLE_GROUP_TITLE, "Albums")
- Media item C with
extras.putString(MediaConstants.DESCRIPTION_EXTRAS_KEY_CONTENT_STYLE_GROUP_TITLE, "Songs")
- Media item D with
extras.putString(MediaConstants.DESCRIPTION_EXTRAS_KEY_CONTENT_STYLE_GROUP_TITLE, "Songs")
- Media item E with
extras.putString(MediaConstants.DESCRIPTION_EXTRAS_KEY_CONTENT_STYLE_GROUP_TITLE, "Albums")
This results in these four groups:
- Group 1, called "Songs", containing media item A
- Group 2, called "Albums", containing media item B
- Group 3, called "Songs", containing media items C and D
- Group 4, called "Albums", containing media item E
To display these items in two groups, your app must instead pass the media items in this order:
- Media item A with
extras.putString(MediaConstants.DESCRIPTION_EXTRAS_KEY_CONTENT_STYLE_GROUP_TITLE, "Songs")
- Media item C with
extras.putString(MediaConstants.DESCRIPTION_EXTRAS_KEY_CONTENT_STYLE_GROUP_TITLE, "Songs")
- Media item D with
extras.putString(MediaConstants.DESCRIPTION_EXTRAS_KEY_CONTENT_STYLE_GROUP_TITLE, "Songs")
- Media item B with
extras.putString(MediaConstants.DESCRIPTION_EXTRAS_KEY_CONTENT_STYLE_GROUP_TITLE, "Albums")
- Media item E with
extras.putString(MediaConstants.DESCRIPTION_EXTRAS_KEY_CONTENT_STYLE_GROUP_TITLE, "Albums")
Display additional metadata indicators
You can include additional metadata indicators to provide at-a-glance information for content in the media browser tree and during playback.
In the browse tree, Android Auto and AAOS read the extras associated with an item and display the indicators. During media playback, Android Auto and AAOS read the metadata for the media session and look for specific constants to determine which indicators to display.

Figure 3. Playback view with metadata.

Figure 4. Browse view for unplayed content.
These constants can be used in both MediaItem
description extras and
MediaMetadata
extras:
EXTRA_DOWNLOAD_STATUS
: Indicates the download status of an item. Use this constant as the key. These long constants are possible values:STATUS_DOWNLOADED
: Item is fully downloaded.STATUS_DOWNLOADING
: Item is in the process of being downloaded.STATUS_NOT_DOWNLOADED
: Item isn't downloaded.
METADATA_KEY_IS_EXPLICIT
: Indicates the item contains explicit content. To indicate an item is explicit, use this constant as the key and the longMETADATA_VALUE_ATTRIBUTE_PRESENT
as the value.
These constants can only be used in MediaItem
description extras:
DESCRIPTION_EXTRAS_KEY_COMPLETION_STATUS
: Indicates the completion state of long-form content, such as podcast episodes and audiobooks. Use this constant as the key. These integer constants are possible values:DESCRIPTION_EXTRAS_VALUE_COMPLETION_STATUS_NOT_PLAYED
: Item hasn't been played.DESCRIPTION_EXTRAS_VALUE_COMPLETION_STATUS_PARTIALLY_PLAYED
: Item is partially played, and the current position is somewhere in the middle.DESCRIPTION_EXTRAS_VALUE_COMPLETION_STATUS_FULLY_PLAYED
: Item is completed.
DESCRIPTION_EXTRAS_KEY_COMPLETION_PERCENTAGE
: Indicates completion progress on long-form content as a double between 0.0 and 1.0, inclusive. This provides more information about thePARTIALLY_PLAYING
state, allowing Android Auto or AAOS to display a more meaningful progress indicator, such as a progress bar. If you use this extra, see Update the progress bar in browse view while content plays to learn how to keep this indicator up to date after the initial impression.
To display indicators that appear while the user is browsing the media browse
tree, create an extras bundle that includes one or more of these constants.
Then, pass that bundle to the MediaDescription.Builder.setExtras
method.
This snippet shows how to display indicators for an explicit media item that is 70% complete:
Kotlin
import androidx.media.utils.MediaConstants
val extras = Bundle()
extras.putLong(
MediaConstants.METADATA_KEY_IS_EXPLICIT,
MediaConstants.METADATA_VALUE_ATTRIBUTE_PRESENT)
extras.putInt(
MediaConstants.DESCRIPTION_EXTRAS_KEY_COMPLETION_STATUS,
MediaConstants.DESCRIPTION_EXTRAS_VALUE_COMPLETION_STATUS_PARTIALLY_PLAYED)
extras.putDouble(
MediaConstants.DESCRIPTION_EXTRAS_KEY_COMPLETION_PERCENTAGE, 0.7)
val description =
MediaDescriptionCompat.Builder()
.setMediaId(/*...*/)
.setTitle(resources.getString(/*...*/))
.setExtras(extras)
.build()
return MediaBrowserCompat.MediaItem(description, /* flags */)
Java
import androidx.media.utils.MediaConstants;
Bundle extras = new Bundle();
extras.putLong(
MediaConstants.METADATA_KEY_IS_EXPLICIT,
MediaConstants.METADATA_VALUE_ATTRIBUTE_PRESENT);
extras.putInt(
MediaConstants.DESCRIPTION_EXTRAS_KEY_COMPLETION_STATUS,
MediaConstants.DESCRIPTION_EXTRAS_VALUE_COMPLETION_STATUS_PARTIALLY_PLAYED);
extras.putDouble(
MediaConstants.DESCRIPTION_EXTRAS_KEY_COMPLETION_PERCENTAGE, 0.7);
MediaDescriptionCompat description =
new MediaDescriptionCompat.Builder()
.setMediaId(/*...*/)
.setTitle(resources.getString(/*...*/))
.setExtras(extras)
.build();
return new MediaBrowserCompat.MediaItem(description, /* flags */);
To display indicators for a media item that is currently playing, declare
values for METADATA_KEY_IS_EXPLICIT
or EXTRA_DOWNLOAD_STATUS
in your
mediaSession
's MediaMetadataCompat
.
This code snippet shows how to indicate that the song in the playback view is explicit and downloaded:
Kotlin
import androidx.media.utils.MediaConstants
mediaSession.setMetadata(
MediaMetadataCompat.Builder()
.putString(
MediaMetadataCompat.METADATA_KEY_DISPLAY_TITLE, "Song Name")
.putString(
MediaMetadataCompat.METADATA_KEY_DISPLAY_SUBTITLE, "Artist name")
.putString(
MediaMetadataCompat.METADATA_KEY_ALBUM_ART_URI,
albumArtUri.toString())
.putLong(
MediaConstants.METADATA_KEY_IS_EXPLICIT,
MediaConstants.METADATA_VALUE_ATTRIBUTE_PRESENT)
.putLong(
MediaDescriptionCompat.EXTRA_DOWNLOAD_STATUS,
MediaDescriptionCompat.STATUS_DOWNLOADED)
.build())
Java
import androidx.media.utils.MediaConstants;
mediaSession.setMetadata(
new MediaMetadataCompat.Builder()
.putString(
MediaMetadataCompat.METADATA_KEY_DISPLAY_TITLE, "Song Name")
.putString(
MediaMetadataCompat.METADATA_KEY_DISPLAY_SUBTITLE, "Artist name")
.putString(
MediaMetadataCompat.METADATA_KEY_ALBUM_ART_URI,
albumArtUri.toString())
.putLong(
MediaConstants.METADATA_KEY_IS_EXPLICIT,
MediaConstants.METADATA_VALUE_ATTRIBUTE_PRESENT)
.putLong(
MediaDescriptionCompat.EXTRA_DOWNLOAD_STATUS,
MediaDescriptionCompat.STATUS_DOWNLOADED)
.build());
Update the progress bar in browse view while content plays
As previously mentioned, you can use the
DESCRIPTION_EXTRAS_KEY_COMPLETION_PERCENTAGE
extra to show a progress
bar for partially played content in the browse view. However, if a user
continues playing the partially played content, that indicator becomes
inaccurate over time.
So that Android Auto and AAOS keep the progress bar up to date, supply
additional information in MediaMetadataCompat
and PlaybackStateCompat
to
link ongoing content to media items in the browse view.
For a media item to have an automatically updating progress bar, these requirements must be met:
When created, the
MediaItem
must sendDESCRIPTION_EXTRAS_KEY_COMPLETION_PERCENTAGE
in its extras with a value between0.0
and1.0
, inclusive.The
MediaMetadataCompat
must sendMETADATA_KEY_MEDIA_ID
with a string value equal to the media ID passed to theMediaItem
.The
PlaybackStateCompat
must include an extra with the keyPLAYBACK_STATE_EXTRAS_KEY_MEDIA_ID
that maps to a string value equal to the media ID passed to theMediaItem
.
This code snippet shows how to indicate that the playing item is linked to an item in the browse view:
Kotlin
import androidx.media.utils.MediaConstants
// When the MediaItem is constructed to show in the browse view.
// Suppose the item was 25% complete when the user launched the browse view.
val mediaItemExtras = Bundle()
mediaItemExtras.putDouble(
MediaConstants.DESCRIPTION_EXTRAS_KEY_COMPLETION_PERCENTAGE, 0.25)
val description =
MediaDescriptionCompat.Builder()
.setMediaId("my-media-id")
.setExtras(mediaItemExtras)
// ...and any other setters.
.build()
return MediaBrowserCompat.MediaItem(description, /* flags */)
// Elsewhere, when the user has selected MediaItem for playback.
mediaSession.setMetadata(
MediaMetadataCompat.Builder()
.putString(MediaMetadata.METADATA_KEY_MEDIA_ID, "my-media-id")
// ...and any other setters.
.build())
val playbackStateExtras = Bundle()
playbackStateExtras.putString(
MediaConstants.PLAYBACK_STATE_EXTRAS_KEY_MEDIA_ID, "my-media-id")
mediaSession.setPlaybackState(
PlaybackStateCompat.Builder()
.setExtras(playbackStateExtras)
// ...and any other setters.
.build())
Java
import androidx.media.utils.MediaConstants;
// When the MediaItem is constructed to show in the browse view.
// Suppose the item was 25% complete when the user launched the browse view.
Bundle mediaItemExtras = new Bundle();
mediaItemExtras.putDouble(
MediaConstants.DESCRIPTION_EXTRAS_KEY_COMPLETION_PERCENTAGE, 0.25);
MediaDescriptionCompat description =
new MediaDescriptionCompat.Builder()
.setMediaId("my-media-id")
.setExtras(mediaItemExtras)
// ...and any other setters.
.build();
return new MediaBrowserCompat.MediaItem(description, /* flags */);
// Elsewhere, when the user has selected MediaItem for playback.
mediaSession.setMetadata(
new MediaMetadataCompat.Builder()
.putString(MediaMetadata.METADATA_KEY_MEDIA_ID, "my-media-id")
// ...and any other setters.
.build());
Bundle playbackStateExtras = new Bundle();
playbackStateExtras.putString(
MediaConstants.PLAYBACK_STATE_EXTRAS_KEY_MEDIA_ID, "my-media-id");
mediaSession.setPlaybackState(
new PlaybackStateCompat.Builder()
.setExtras(playbackStateExtras)
// ...and any other setters.
.build());
P
Even unplayed or fully played content can display an automatically updating
progress bar. This occurs if the corresponding media items include the
DESCRIPTION_EXTRAS_KEY_COMPLETION_PERCENTAGE
extra with a value of 0.0
(for unplayed) or 1.0
(for fully played). After the user selects these media
items, Android Auto and AAOS display the progress bar over other progress
indicators.