fix(android): keep skip buttons visible in notification

Returning RESULT_ERROR_NOT_SUPPORTED from onPlayerCommandRequest caused
Media3 to mark the skip commands as failed and hide the buttons. Now
returns RESULT_SUCCESS so the notification keeps them enabled, while
ForwardingPlayer no-ops seekToNext/Previous so ExoPlayer never tries to
advance its single-item queue — JS still owns all queue navigation.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
HoneyandClaude Sonnet 4.6 committed 2026-06-08 20:06:33 +02:00
1 parent 128437821a
commit 76efa393ea
1 file changed
+11 -2
@@ -102,6 +102,8 @@ class MusicService : MediaSessionService() {
// ExoPlayer drops those commands when its queue has only one item (single-track model),
// which makes Media3 hide the skip buttons in the notification. JS still owns queue
// logic — the MediaSession callback intercepts the commands and delegates to JS.
// seekToNext* are no-oped here so ExoPlayer never tries to advance its single-item
// queue; JS handles all actual navigation via stateListener callbacks.
val sessionPlayer = object : ForwardingPlayer(player) {
override fun getAvailableCommands(): Player.Commands =
super.getAvailableCommands().buildUpon()
@@ -118,6 +120,11 @@ class MusicService : MediaSessionService() {
Player.COMMAND_SEEK_TO_PREVIOUS_MEDIA_ITEM -> true
else -> super.isCommandAvailable(command)
}
override fun seekToNext() {}
override fun seekToNextMediaItem() {}
override fun seekToPrevious() {}
override fun seekToPreviousMediaItem() {}
}
// Tapping the notification opens the app directly to the Now Playing screen.
@@ -145,12 +152,14 @@ class MusicService : MediaSessionService() {
Player.COMMAND_SEEK_TO_NEXT,
Player.COMMAND_SEEK_TO_NEXT_MEDIA_ITEM -> {
stateListener?.onSkipToNext()
androidx.media3.session.SessionResult.RESULT_ERROR_NOT_SUPPORTED
// Return SUCCESS so Media3 keeps the button enabled in the notification.
// The ForwardingPlayer no-ops seekToNext so ExoPlayer does nothing.
androidx.media3.session.SessionResult.RESULT_SUCCESS
}
Player.COMMAND_SEEK_TO_PREVIOUS,
Player.COMMAND_SEEK_TO_PREVIOUS_MEDIA_ITEM -> {
stateListener?.onSkipToPrevious()
androidx.media3.session.SessionResult.RESULT_ERROR_NOT_SUPPORTED
androidx.media3.session.SessionResult.RESULT_SUCCESS
}
else -> androidx.media3.session.SessionResult.RESULT_SUCCESS
}