How to properly order media controls on Notification and Android Auto?
Maintainers usually reply within 4 days
@tonihei is already working on this.
Since Aug 5, 2026.
Assessment
This issue has not been assessed yet.
Description
Hello,
I'm getting different order on different devices when using the SLOTs api:
I have this enum:
enum class PlayerSessionCommand(
val commandButton: CommandButton,
) {
REWIND(
commandButton = CommandButton.Builder(ICON_SKIP_BACK_10)
.setPlayerCommand(Player.COMMAND_SEEK_BACK)
.setDisplayName("Rewind")
.setSlots(CommandButton.SLOT_BACK)
.setExtras(Bundle().apply {
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.TIRAMISU)
putInt(DefaultMediaNotificationProvider.COMMAND_KEY_COMPACT_VIEW_INDEX, 0)
})
.build(),
),
PLAY(
commandButton = CommandButton.Builder(ICON_PLAY)
.setPlayerCommand(Player.COMMAND_PLAY_PAUSE)
.setDisplayName("Play")
.setExtras(Bundle().apply {
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.TIRAMISU)
putInt(DefaultMediaNotificationProvider.COMMAND_KEY_COMPACT_VIEW_INDEX, 1)
})
.build()
),
PAUSE(
commandButton = CommandButton.Builder(ICON_PAUSE)
.setPlayerCommand(Player.COMMAND_PLAY_PAUSE)
.setDisplayName("Pause")
.setExtras(Bundle().apply {
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.TIRAMISU)
putInt(DefaultMediaNotificationProvider.COMMAND_KEY_COMPACT_VIEW_INDEX, 1)
})
.build()
),
FORWARD(
commandButton = CommandButton.Builder(ICON_SKIP_FORWARD_10)
.setPlayerCommand(Player.COMMAND_SEEK_FORWARD)
.setDisplayName("Forward")
.setSlots(CommandButton.SLOT_FORWARD)
.setExtras(Bundle().apply {
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.TIRAMISU)
putInt(DefaultMediaNotificationProvider.COMMAND_KEY_COMPACT_VIEW_INDEX, 2)
})
.build(),
),
PREVIOUS(
commandButton = CommandButton.Builder(ICON_PREVIOUS)
.setPlayerCommand(Player.COMMAND_SEEK_TO_PREVIOUS_MEDIA_ITEM)
.setDisplayName("Previous")
.setSlots(
CommandButton.SLOT_OVERFLOW
)
.build()
),
NEXT(
commandButton = CommandButton.Builder(ICON_NEXT)
.setPlayerCommand(Player.COMMAND_SEEK_TO_NEXT_MEDIA_ITEM)
.setDisplayName("Next")
.setSlots(
CommandButton.SLOT_OVERFLOW
)
.build(),
)
}
and I'm using it like this:
private val playerSessionCommandButtons = listOf(
PlayerSessionCommand.PREVIOUS,
PlayerSessionCommand.REWIND,
PlayerSessionCommand.FORWARD,
PlayerSessionCommand.NEXT
).map { it.commandButton }
fun update(
session: MediaSession,
mediaItem: MediaItem?
) {
if (mediaItem == null) return
val layout = buildLayout(mediaItem)
updateSessionCommands(session, mediaItem, layout)
}
private fun updateSessionCommands(
session: MediaSession,
mediaItem: MediaItem,
layout: List<CommandButton>
) {
val playerCommands = if (mediaItem.isLive) {
DEFAULT_PLAYER_COMMANDS.buildUpon()
.removeAll(
COMMAND_SEEK_TO_PREVIOUS,
COMMAND_SEEK_TO_PREVIOUS_MEDIA_ITEM,
COMMAND_SEEK_TO_NEXT,
COMMAND_SEEK_TO_NEXT_MEDIA_ITEM,
COMMAND_GET_TIMELINE // removes from Queue from Auto
)
.build()
} else {
val hasPreviousItem = session.player.hasPreviousMediaItem()
val hasNextMediaItem = session.player.hasNextMediaItem()
DEFAULT_PLAYER_COMMANDS.buildUpon().apply {
if (!hasPreviousItem)
removeAll(COMMAND_SEEK_TO_PREVIOUS, COMMAND_SEEK_TO_PREVIOUS_MEDIA_ITEM)
if (!hasNextMediaItem)
removeAll(COMMAND_SEEK_TO_NEXT, COMMAND_SEEK_TO_NEXT_MEDIA_ITEM)
}.build()
}
session.setMediaButtonPreferences(layout)
session.connectedControllers.forEach {
session.setAvailableCommands(
it, DEFAULT_SESSION_COMMANDS, playerCommands
)
}
}
private fun buildLayout(mediaItem: MediaItem): List<CommandButton> {
return when {
mediaItem.isLive -> {
// empty list can be returned as well, but this makes the intent clearer
listOf(
PlayerSessionCommand.PLAY,
PlayerSessionCommand.PAUSE
).map { it.commandButton }
}
else -> playerSessionCommandButtons
}
}
I've tested on Android auto, Samsung (Api 36.1) and Vivo (Api 36.0):
| first media item | Nth media item | last media item | |
|---|---|---|---|
| samsung | |||
| vivo | |||
| android auto |
The vivo and AA seem to be ordering the same way, while samsung it's slightly different. Is it possible to make the ordering for all for the first item the same as what vivo/aa are doing, while for the last item what samsung is doing?
While on the topic of slots, I've noticed there are these 2 constants:
/**
* A slot in a playback control UI for secondary backward-directed playback actions, most commonly
* used for previous or rewind actions.
*/
@UnstableApi public static final int SLOT_BACK_SECONDARY = 4;
/**
* A slot in a playback control UI for secondary forward-directed playback actions, most commonly
* used for next or fast-forward actions.
*/
@UnstableApi public static final int SLOT_FORWARD_SECONDARY = 5;
But I just couldn't get them to work as per the comment, that's why I'm using SLOT_OVERFLOW in my enum builder.
EDIT: android auto version on both devices: 17.4.663004-release
- Dominant language
- Java
- Stars
- 3.1k
- Forks
- 998
- Avg merge
- 6d 8h
- Merged PRs (30d)
- 2
Getting set up
- No Dockerfile or Docker Compose file
- No pull request template
- Read the contributing guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
More from androidx/media
-
Waive ads when seek/start at the beginningPossibly taken @microkatz claimed this today. Openenhancement
androidx/media#3496 · 2 comments · 1 assignee ·
Maintainers usually reply within 4 days
-
Since 1.9.0, skip buttons in SLOT_BACK / SLOT_FORWARD reach legacy controllers as custom actionsPossibly taken @marcbaechinger claimed this today. Openbug
androidx/media#3495 · 1 comment · 1 assignee ·
Maintainers usually reply within 4 days
-
Allow configuring asset-list prefetch timing per HLS interstitialPossibly taken @marcbaechinger claimed this 1 day ago. Openenhancement needs triage
androidx/media#3494 · 1 assignee ·
Maintainers usually reply within 4 days
-
Offloaded audio left paused keeps AudioFlinger's AudioOffload wake lock forever, device never suspendsPossibly taken @microkatz claimed this 1 day ago. Open
androidx/media#3493 · 1 assignee ·
Maintainers usually reply within 4 days
-
Allow opting into frame-index seeking for MP3s without reliable seek metadataPossibly taken @icbaker claimed this today. Open
Difficulty 4/5 3-5 days Newbie friendliness 48/100
androidx/media#3492 · 1 assignee ·
Maintainers usually reply within 4 days
Similar issues
-
Difficulty 2/5 1-3 hours Newbie friendliness 67/100
quarkiverse/quarkus-google-cloud-services#1188 ·
Maintainers usually reply within 1 day
-
Component: Frontend Component: Web Component Type: Maintenance
Difficulty 2/5 1-3 hours Newbie friendliness 76/100
it-at-m/refarch-templates#1944 ·
Maintainers usually reply within 2 days
-
priority: p2 type: bug
Difficulty 2/5 1-3 hours Newbie friendliness 68/100
googleapis/java-genai#1372 ·
Maintainers usually reply within 1 day
-
Difficulty 2/5 1-3 hours Newbie friendliness 60/100
-
GeoSeries scale and skew tests fail with pytest 9.2.0Possibly taken @jiayuasu claimed this today. Open
Difficulty 2/5 1-3 hours Newbie friendliness 82/100
Maintainers usually reply within 1 day