How to properly order media controls on Notification and Android Auto?
@tonihei is already working on this.
Since Aug 5, 2026.
- Dominant language
- Java
- Stars
- 3k
- Forks
- 955
- Avg merge
- 12d 14h
- Merged PRs (30d)
- 2
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
Contributor 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.
Assessment
This issue has not been assessed yet.