Android MediaSessionCompat Callbacks not firing

1.9k Views Asked by At

I'm creating an audiobook player, and I'm using MediaSessionCompat related classes to handle notifications. My code is heavily inspired by the android-MediaBrowserService samples ( https://github.com/googlearchive/android-MediaBrowserService ), and i'm not quite understanding it all for the moment ( the createContentIntent espicially )

Here is my simple class in charge of building notifications from a bookPlayer providing metadata and playbackstate data

class PlayerNotificationManager(val playerService: PlayerService) {

    val CHANNEL_ID = "pylvain.gamma"
    val NOTIFICATION_ID = 412
    private val REQUEST_CODE = 501

    val notificationManager =
        playerService.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager

    private val playAction: NotificationCompat.Action =
        NotificationCompat.Action (
            android.R.drawable.ic_media_pause,
            "PAUSE",
            buildMediaButtonPendingIntent(
                playerService,
                PlaybackStateCompat.ACTION_PAUSE
            )
        )

    fun getNotification(bookPlayer: BookPlayer): Notification  {

        if (isAndroidOOrHigher()) createChannel()

        val style = androidx.media.app.NotificationCompat.MediaStyle()
            .setMediaSession(playerService.sessionToken)
            .setShowCancelButton(true)
            .setShowActionsInCompactView(0)
            .setCancelButtonIntent(
                buildMediaButtonPendingIntent(
                    playerService,
                    PlaybackStateCompat.ACTION_STOP
                )
            )

        val builder = NotificationCompat.Builder(playerService, CHANNEL_ID)
            .addAction(playAction)
            .setStyle(style)
            .setSmallIcon(R.drawable.ic_music_rest_quarter)
            .setContentIntent(createContentIntent())
            .setVisibility(NotificationCompat.VISIBILITY_PUBLIC)

        return builder.build()

    }

    private fun createContentIntent(): PendingIntent { //TODO: Understand that
        Timber.i("Creating Intent")
        val openUI = Intent(playerService, MainActivity::class.java)
        openUI.flags = Intent.FLAG_ACTIVITY_SINGLE_TOP
        return PendingIntent.getActivity(
            playerService, 0, openUI, PendingIntent.FLAG_UPDATE_CURRENT
        )
    }

The notification is showing perfectly fine with metadata

Here is my MediaBrowserService handling the media session, where I registered the callbacks. The bookplayer is constructed and injected with Koin. :

class PlayerService : MediaBrowserServiceCompat() {

    private lateinit var playerNotificationManager: PlayerNotificationManager
    lateinit var session: MediaSessionCompat

    private val bookPlayer: BookPlayer by inject()

    override fun onCreate() {
        super.onCreate()

        session = MediaSessionCompat(this, "MusicService")
        session.apply {
            setFlags(
                MediaSessionCompat.FLAG_HANDLES_MEDIA_BUTTONS or
                        MediaSessionCompat.FLAG_HANDLES_TRANSPORT_CONTROLS
            )
            setPlaybackState(bookPlayer.playbackState)
            setMetadata(bookPlayer.getMetadata())
            setCallback(callbacks)
            setActive(true)
        }

        setSessionToken(session.sessionToken)
        playerNotificationManager = PlayerNotificationManager(this)

        val notification = playerNotificationManager.getNotification(bookPlayer)

        startForeground(playerNotificationManager.NOTIFICATION_ID, notification)

    }

    override fun onTaskRemoved(rootIntent: Intent?) { //TODO
        super.onTaskRemoved(rootIntent)
        stopSelf()
    }

    override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
        return super.onStartCommand(intent, flags, startId)
    }

    override fun onGetRoot(
        clientPackageName: String,
        clientUid: Int,
        rootHints: Bundle?
    ): BrowserRoot? {
        return BrowserRoot("root", null)
    }

    override fun onLoadChildren(
        parentId: String, result: Result<MutableList<MediaBrowserCompat.MediaItem>>
    ) {
        result.sendResult(null);
    }
    
    override fun onDestroy() {
        session.release()
    }


    val callbacks = object : MediaSessionCompat.Callback() {

        override fun onCommand(command: String?, extras: Bundle?, cb: ResultReceiver?) {
            Timber.i("Test")
            super.onCommand(command, extras, cb)
        }

        override fun onPrepare() {
            Timber.i("Preparing")
        }

        override fun onPlay() {
            Timber.i("Playing")
            bookPlayer.pause()
        }

        override fun onPause() {
            Timber.i("Pausing")
            bookPlayer.pause()
        }

        override fun onSkipToNext() {}
        override fun onSkipToPrevious() {}
        override fun onStop() {}
        override fun onSeekTo(pos: Long) {}
        override fun onMediaButtonEvent(mediaButtonIntent: Intent): Boolean = true

    }

}

Then the service is started from the main activity with

startService(Intent(mainContext, PlayerService::class.java))

I also added this to my Android manifest


        <service android:name=".playerservice.PlayerService">
            <intent-filter>
                <action android:name="android.media.browse.MediaBrowserService" />
            </intent-filter>
        </service>


        <receiver android:name="androidx.media.session.MediaButtonReceiver">
            <intent-filter>
                <action android:name="android.intent.action.MEDIA_BUTTON" />
            </intent-filter>
        </receiver>

None of the callbacks are called whenever I push the button. when i do, the app log the following text :

D/MediaBrowserCompat: Connecting to a MediaBrowserService.

and nothing happens ... I've searched the entire internet and I'm completely clueless, but it's surely something simple. Can someone help me ? Thank you very much in advance <3

2

There are 2 best solutions below

3
On

The callback worked ... Just not the way intended. It turns out that the play action button was calling

override fun onMediaButtonEvent(mediaButtonIntent: Intent): Boolean = true

I deleted the function, and ... It works ...

Thank you for your attention !

0
On

If you want to get media button, you have to play something. Try to play dummy audio, when your service is started

    // play dummy audio
    AudioTrack at = new AudioTrack(AudioManager.STREAM_MUSIC, 48000, AudioFormat.CHANNEL_OUT_STEREO, AudioFormat.ENCODING_PCM_16BIT,
            AudioTrack.getMinBufferSize(48000, AudioFormat.CHANNEL_OUT_STEREO, AudioFormat.ENCODING_PCM_16BIT), AudioTrack.MODE_STREAM);
    at.play();

    // a little sleep 
    at.stop();
    at.release();

https://stackoverflow.com/a/50678833/9891730 - this is my answer before