androidx / androidx/media

ExoPlayer.setPauseAtEndOfMediaItems(false) are not working in media3 1.9.0

Open
#2,984 7 comments 0 reactions 1 assignee View on GitHub

@marcbaechinger is already working on this.

Since Jan 5, 2026.

bug
Dominant language
Java
Stars
3k
Forks
955
Avg merge
12d 14h
Merged PRs (30d)
2

Description

Version

Media3 1.9.0

More version details

With Media3 v1.8.0 the function working fine, no sending pause -> play signal to Player.Listener.onIsPlayingChanged
With Media3 v1.9.0 it send the pause -> play signal to Player.Listener.onIsPlayingChanged

Devices that reproduce the issue

Poco x6 5G - android 15 (my personal device)

Devices that do not reproduce the issue

i have no idea

Reproducible in the demo app?

Not tested

Reproduction steps

Im using MediaSessionService, here my settup:

@UnstableApi
class TTSService : MediaSessionService() {

    private val ttsManager by inject<TTSManager>()
    private var audioFocusChangeListener =
        AudioManager.OnAudioFocusChangeListener { focusChange ->
            when (focusChange) {
                AudioManager.AUDIOFOCUS_GAIN -> {
                    ttsManager.checkResume()
                }

                AudioManager.AUDIOFOCUS_LOSS_TRANSIENT -> {
                    ttsManager.checkPause()
                }

                AudioManager.AUDIOFOCUS_LOSS -> {
                    ttsManager.checkPause()
                }

                AudioManager.AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK -> {
                    ttsManager.checkPause()
                }
            }
        }
    private val playbackAttributes = AudioAttributes.Builder()
        .setUsage(AudioAttributes.USAGE_MEDIA)
        .setContentType(AudioAttributes.CONTENT_TYPE_MUSIC)
        .build()
    private val focusRequest = AudioFocusRequest.Builder(AudioManager.AUDIOFOCUS_GAIN_TRANSIENT_EXCLUSIVE)
        .setAudioAttributes(playbackAttributes)
        .setAcceptsDelayedFocusGain(true)
        .setOnAudioFocusChangeListener(audioFocusChangeListener)
        .build()

    private lateinit var audioManager: AudioManager
    private var mediaSession: MediaSession? = null

    private val customCommandStop = SessionCommand(ACTION_STOP, Bundle.EMPTY)
    private val customCommandNext = SessionCommand(ACTION_NEXT, Bundle.EMPTY)
    private val customCommandPrevious = SessionCommand(ACTION_PREVIOUS, Bundle.EMPTY)

    override fun onCreate() {
        super.onCreate()

        val stopButton = CommandButton.Builder(CommandButton.ICON_STOP)
            .setCustomIconResId(R.drawable.ic_notification_stop)
            .setDisplayName("Stop")
            .setSessionCommand(customCommandStop)
            .build()
        val nextButton = CommandButton.Builder(CommandButton.ICON_SKIP_FORWARD)
            .setCustomIconResId(R.drawable.ic_notification_skip_to_next)
            .setDisplayName("Next Chapter")
            .setSessionCommand(customCommandNext)
            .build()
        val previousButton = CommandButton.Builder(CommandButton.ICON_SKIP_BACK)
            .setCustomIconResId(R.drawable.ic_notification_skip_to_back)
            .setDisplayName("Previous Chapter")
            .setSessionCommand(customCommandPrevious)
            .build()

        val myAudioAttributes = androidx.media3.common.AudioAttributes.Builder()
            .setContentType(C.AUDIO_CONTENT_TYPE_MUSIC)
            .setUsage(C.USAGE_MEDIA)
            .build()
        val player = ExoPlayer.Builder(this)
            .setAudioAttributes(myAudioAttributes, false)
            .setHandleAudioBecomingNoisy(true)
            .setPauseAtEndOfMediaItems(false)
            .build()
            .apply {
                repeatMode = Player.REPEAT_MODE_ALL
                shuffleModeEnabled = true
            }

        val forwardingPlayer = object : ForwardingPlayer(player) {
            override fun getAvailableCommands(): Player.Commands {
                return super.getAvailableCommands()
                    .buildUpon()
                    .remove(COMMAND_SEEK_TO_NEXT)
                    .remove(COMMAND_SEEK_TO_PREVIOUS)
                    .remove(COMMAND_SEEK_IN_CURRENT_MEDIA_ITEM)
                    .build()
            }
        }

        mediaSession = MediaSession.Builder(this, forwardingPlayer)
            .setCallback(MyCallback())
            .setCustomLayout(
                ImmutableList.of(previousButton, nextButton, stopButton)
            )
            .build()

        audioManager = getSystemService(AUDIO_SERVICE) as AudioManager
        audioManager.requestAudioFocus(focusRequest)
    }

    override fun onDestroy() {
        mediaSession?.run {
            player.release()
            release()
            mediaSession = null
        }
        audioManager.abandonAudioFocusRequest(focusRequest)
        super.onDestroy()
    }

    override fun onGetSession(controllerInfo: MediaSession.ControllerInfo): MediaSession? {
        return mediaSession
    }

    override fun onTaskRemoved(rootIntent: Intent?) {
        stopSelf()
    }

    private inner class MyCallback : MediaSession.Callback {
        override fun onConnect(
            session: MediaSession,
            controller: MediaSession.ControllerInfo
        ): MediaSession.ConnectionResult {
            return MediaSession.ConnectionResult.AcceptedResultBuilder(session)
                .setAvailablePlayerCommands(
                    MediaSession.ConnectionResult.DEFAULT_PLAYER_COMMANDS.buildUpon()
                        .remove(Player.COMMAND_SEEK_TO_NEXT)
                        .remove(Player.COMMAND_SEEK_TO_NEXT_MEDIA_ITEM)
                        .remove(Player.COMMAND_SEEK_TO_PREVIOUS)
                        .remove(Player.COMMAND_SEEK_TO_PREVIOUS_MEDIA_ITEM)
                        .remove(Player.COMMAND_SEEK_IN_CURRENT_MEDIA_ITEM)
                        .build()
                )
                .setAvailableSessionCommands(
                    MediaSession.ConnectionResult.DEFAULT_SESSION_COMMANDS.buildUpon()
                        .add(customCommandPrevious)
                        .add(customCommandNext)
                        .add(customCommandStop)
                        .build()
                )
                .build()
        }

        override fun onCustomCommand(
            session: MediaSession,
            controller: MediaSession.ControllerInfo,
            customCommand: SessionCommand,
            args: Bundle
        ): ListenableFuture<SessionResult> {
            return when (customCommand.customAction) {
                ACTION_STOP -> {
                    ttsManager.stopReading(true)
                    return Futures.immediateFuture(SessionResult(SessionResult.RESULT_SUCCESS))
                }

                ACTION_NEXT -> {
                    if (ttsManager.getIsTTSActivated()) {
                        ttsManager.checkPlayNextChapter()
                    } else {
                        mediaSession?.run {
                            player.seekToNextMediaItem()
                        }
                    }
                    return Futures.immediateFuture(SessionResult(SessionResult.RESULT_SUCCESS))
                }

                ACTION_PREVIOUS -> {
                    if (ttsManager.getIsTTSActivated()) {
                        ttsManager.checkPlayPreviousChapter()
                    } else {
                        mediaSession?.run {
                            player.seekToPreviousMediaItem()
                        }
                    }
                    return Futures.immediateFuture(SessionResult(SessionResult.RESULT_SUCCESS))
                }

                else -> {
                    super.onCustomCommand(session, controller, customCommand, args)
                }
            }
        }
    }

    companion object {
        const val ACTION_STOP = "ACTION_STOP"
        const val ACTION_NEXT = "ACTION_NEXT"
        const val ACTION_PREVIOUS = "ACTION_PREVIOUS"
    }
}

and i have another Player.Listener in another class that override the onIsPlayingChanged function.

Expected result

With Media3 v1.8.0, i didnt saw any Pause->Play update on the default media notification when player reach the end of a media item. And my Player.Listener are not receive any signal (which is what i expected)

Actual result

With Media3 v1.9.0, i saw Pause->Play update on the default media notification when player reach the end of a media item. And my Player.Listener are receiving the Pause->Play signal (which break my current code logic)

Media

Not applicable

Bug Report
  • You will email the zip file produced by adb bugreport to android-media-github@google.com after filing this issue.

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.