Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Listen to MediaSession callbacks when using MediaSessionConnector #6446

Closed
jakoss opened this issue Sep 19, 2019 · 10 comments
Closed

Listen to MediaSession callbacks when using MediaSessionConnector #6446

jakoss opened this issue Sep 19, 2019 · 10 comments
Assignees
Labels

Comments

@jakoss
Copy link

jakoss commented Sep 19, 2019

[REQUIRED] Searched documentation and issues

I searched information online and poked around MediaSessionConnector code.

My issue is similar to #6057, but it's solution doesn't really work for me (explanation for that is in question itself).

[REQUIRED] Question

I need to report to FirebaseAnalytics events such as Play, Pause, Next and Previous (explicit user actions, not underlying changes in player state). When talking about UI - no problem here. But i stuck on trying to handle these events from PlayerNotificationManager or MediaSession for that matter.

The first idea was to use MediaSession callback. That would be the easiest way to do that. But MediaSession can have only one callback, and MediaSessionConnector requires it. When using PlayerNotificationManager it provides me with play/pause/next/previous buttons, but i can't seem to get onClick callback for them.

I managed to solve Next/Previous issue by reporting it from QueueNavigator using methods onSkipToNext and onSkipToPrevious. But i can't find a reliable way to handle play/pause the same way. I tried this code:

override fun onPlayerStateChanged(playWhenReady: Boolean, playbackState: Int) {
                if (playWhenReady && playbackState == Player.STATE_READY) {
                    val meta = exoPlayer.currentTag as MediaDescriptionCompat?
                    meta?.let { mediaDescription ->
                        // report play
                    }
                } else if (!playWhenReady) {
                    val meta = exoPlayer.currentTag as MediaDescriptionCompat?
                    meta?.let { mediaDescription ->
                        // report pause
                    }
                }
            }

But i can't rely on it, since this callback represents change in player state and not explicit user action (i need to report user clicking on play button, not the player state itself). So when clicking Next button i got 2 events - next and then play (i need only next).

Is there any way around that? I mean - without handling MediaSession all by myself?

@AquilesCanta
Copy link
Contributor

@marcbaechinger, mind having a look?

@marcbaechinger
Copy link
Contributor

When you are using the PlayerView, PlayerNotificationManager and MediaSessionConnector there are three ways a user can issue player commands:

PlayerControlView: You said you solved this already. Probably by using playerControlView.setControlDispatcher(controlDispatcher) which allows you to intercept the clicks, add an event to you analytics pipeline and then delegate the command to the player.

PlayerNotificationManager: has the same setControlDispatcher(controlDispatcher) method. You can use probably even use the same instance if you don't care from where the events are coming.

MediaSessionConnector: has the same setControlDispatcher(controlDispatcher) method. You can use probably even use the same instance if you don't care from where the events are coming.

In the case of the media session connector you need to make sure that all actions are enabled. By default this should be the following:

PlaybackStateCompat.ACTION_PLAY_PAUSE
| PlaybackStateCompat.ACTION_PLAY
| PlaybackStateCompat.ACTION_PAUSE
| PlaybackStateCompat.ACTION_SEEK_TO
| PlaybackStateCompat.ACTION_FAST_FORWARD
| PlaybackStateCompat.ACTION_REWIND
| PlaybackStateCompat.ACTION_STOP
| PlaybackStateCompat.ACTION_SET_REPEAT_MODE
| PlaybackStateCompat.ACTION_SET_SHUFFLE_MODE

@jakoss
Copy link
Author

jakoss commented Sep 25, 2019

Thanks, dispatchSetPlayWhenReady seems to be working great for us

@jakoss jakoss closed this as completed Sep 25, 2019
@tonihei
Copy link
Collaborator

tonihei commented Sep 25, 2019

Just to follow-up on this - why is listening to player events not sufficient in this case? The player events are either user triggered or player triggered and you can tell by the type of event where it's coming from. So, for example, if you want to listen to user-triggered play/pause events, just listen to onPlayerStateChanged and check the playWhenReady value.

@tonihei tonihei reopened this Sep 25, 2019
@jakoss
Copy link
Author

jakoss commented Sep 25, 2019

In my original question i showed code that do exactly that. But using that i got Play event on track change (so user clicked Next and i got 2 events: Next and then Play. If i could work around that in onPlayerStateChanged it would be sufficient

@tonihei
Copy link
Collaborator

tonihei commented Sep 25, 2019

You shouldn't check the playbackState (=state set by the player), just playWhenReady( =play/pause intention by the user):

var currentPlayWhenReady : Boolean = false

override fun onPlayerStateChanged(playWhenReady: Boolean, playbackState: Int) {
  if (playWhenReady != currentPlayWhenReady) {
    currentPlayWhenReady = playWhenReady
    if (playWhenReady) {
      // Report play event.
    } else {
      // Report pause event.
    }
  }
}

@jakoss
Copy link
Author

jakoss commented Sep 25, 2019

That works too. Using dispatchSetPlayWhenReady doesn't require additional variable, so i think it's a little bit better for us. Still - both solutions are working

@tonihei
Copy link
Collaborator

tonihei commented Sep 25, 2019

Yes, I agree it's more direct. Just requires more work if you have multiple ways of interacting with the player (e.g. MediaSession + normal in-app-UI). And the callback would happen for all cases. And we are thinking of getting rid of the ControlDispatcher class in the future as it's not ideal from a design point of view.

@tonihei
Copy link
Collaborator

tonihei commented Sep 25, 2019

Closing again - question has been answered twice now :)

@tonihei tonihei closed this as completed Sep 25, 2019
@jakoss
Copy link
Author

jakoss commented Sep 25, 2019

We are interacting by MediaSession for normal in-app-UI, so it's single ControlDispatcher on MediaSessionConnector for us :)

@google google locked and limited conversation to collaborators Nov 25, 2019
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
Projects
None yet
Development

No branches or pull requests

4 participants