imansdn
imansdn

Reputation: 1257

Handle media button actions manually in media3

How can I manually handle media button actions with Media3 in a peaceful and compatible way without using MediaSessionConnector?

Previously, I used to handle media button commands using MediaSessionConnector and MediaSessionCompat, but due to Android 13 changes, I migrated to Media3 and removed the MediaSessionConnector, here is what I had before:

mediaSessionConnector.setMediaButtonEventHandler { player, mediaButtonEvent ->
                
                val event: KeyEvent? = mediaButtonEvent.getParcelableExtra(Intent.EXTRA_KEY_EVENT)
                when (event?.keyCode) {
                    KeyEvent.KEYCODE_MEDIA_NEXT -> {
                        if (event.action == KeyEvent.ACTION_UP) {
                            nextSegment()
                        }
                        true
                    }
                    KeyEvent.KEYCODE_MEDIA_PREVIOUS -> {
                        if (event.action == KeyEvent.ACTION_UP) {
                            previousSegment()
                        }
                        true
                    }
                    KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE -> {
                        if (event.action == KeyEvent.ACTION_UP) {
                            onPlayPause()
                        }
                        true
                    }
                    else -> false
                }
            }

as you see I have some custom behaviors for backward and forward action to handle back and forth segments in our application. but recently due to android 13 changes I went through the migration guide (from exo to media3) and removed the media session connector but I can no longer handle the media button receiver manually.

Even I tried to register my own button receiver with android.intent.action.MEDIA_BUTTON action but still is not triggering that. then I checked the MediaSession (from media3) implementation and found out it register a MediaButtonReceive itself with the same action with below implementation which overrides mine and it only works for play/pause, (what I am looking for is double tap for fast-forward action while I need my customize functioning to go to next segment), :

here is google MediaButtonReceive:

private final class MediaButtonReceiver extends BroadcastReceiver {

    @Override
    public void onReceive(Context context, Intent intent) {
      if (!Util.areEqual(intent.getAction(), Intent.ACTION_MEDIA_BUTTON)) {
        return;
      }
      Uri sessionUri = intent.getData();
      if (!Util.areEqual(sessionUri, sessionUri)) {
        return;
      }
      KeyEvent keyEvent = intent.getParcelableExtra(Intent.EXTRA_KEY_EVENT);
      if (keyEvent == null) {
        return;
      }
      getSessionCompat().getController().dispatchMediaButtonEvent(keyEvent);
    }
  }

what I understand so far the MediaSession has an internal package private mediaSessionCompat which is used for some part of logic like dispatchMediaEvent.

------ a workaround which didn't work-----

I also attempted a workaround by defining MediaSessionCompat again alongside the actual MediaSession and setting a callback for it and override onMediaButtonEvent but it's not triggering that callback.

here is how I defined that:

  mediaSessionCompat = MediaSessionCompat(applicationContext, MEDIA_SESSION_TAG)
  mediaSessionCompat?.setSessionActivity(mediaSession?.sessionActivity)
  mediaSessionCompat?.setCallback{
       override fun onMediaButtonEvent(mediaButtonEvent: Intent?): Boolean {..}
  }

Is there any other peaceful and compatible way to handle media button actions manually in media3? Any insights or alternative approaches would be appreciated.

Upvotes: 1

Views: 1446

Answers (1)

bittiger tomorrow
bittiger tomorrow

Reputation: 21

  1. PlayBackService
class PlaybackService : MediaSessionService(){
    private var mediaSession: MediaSession? = null
    
    ...

    private fun thisIsWhereWeCreateMedia3Session(){
        mediaSession = MediaSession.Builder(this, player).setCallback(MyMediaSessionCallback()).build()
    }

}
  1. MyMediaSessionCallback
class MyMediaSessionCallback : MediaSession.Callback {
    @UnstableApi
    override fun onMediaButtonEvent(
        session: MediaSession,
        controllerInfo: ControllerInfo,
        intent: Intent
    ): Boolean {
        val ke =
            intent.extras!!.getParcelable(EXTRA_KEY_EVENT, KeyEvent::class.java) ?: return false
        
        when (ke.keyCode) {
            KEYCODE_MEDIA_PLAY_PAUSE -> {
                //do whatever you want
                return true
            }

            KEYCODE_MEDIA_PLAY -> {
                //do whatever you want
                return true
            }

            KEYCODE_MEDIA_PAUSE -> {
                //do nothing, just reutrn false
                return false
            }

            KEYCODE_MEDIA_STOP -> {
                //do whatever you want
                return true
            }

            KEYCODE_MEDIA_NEXT -> {
                //do whatever you want
                return true
            }

            KEYCODE_MEDIA_PREVIOUS -> {
                //do whatever you want
                return true
            }

            KEYCODE_MEDIA_REWIND -> {
                //do whatever you want
                return true
            }
        }
        return super.onMediaButtonEvent(session, controllerInfo, intent)
    }
}
  1. AndroidManifest.xml
<service
    android:name=".service.PlaybackService"
    android:foregroundServiceType="mediaPlayback"
    android:exported="true">
    <intent-filter>
        <action android:name="androidx.media3.session.MediaSessionService" />
        <action android:name="android.intent.action.MEDIA_BUTTON" />
        <action android:name="android.media.browse.MediaBrowserService" />
    </intent-filter>
</service>

Upvotes: -1

Related Questions