I'm trying to implement a notification player in Android. I followed this guide to achieve this. However as my requirement I need to implement "skip to next" and "skip to previous" in the background notification player.
So far onPlay() and onPause() of MediaSessionCompact.Callback get triggered. However while debugging I noticed onSkipToNext() and onSkipToPrevious() callback methods does not get triggered when next and previous buttons are pressed in notification player.
Appreciate your input. I have added relevant code below.
BackgroundAudioService
private void showPlayingNotification() {
NotificationCompat.Builder builder = MediaStyleHelper.from(BackgroundAudioService.this, mMediaSessionCompat);
if (builder == null) {
return;
}
builder.addAction(new NotificationCompat.Action(android.R.drawable.ic_media_previous, "Previous", MediaButtonReceiver.buildMediaButtonPendingIntent(this, PlaybackStateCompat.ACTION_SKIP_TO_PREVIOUS)));
builder.addAction(new NotificationCompat.Action(android.R.drawable.ic_media_pause, "Pause", MediaButtonReceiver.buildMediaButtonPendingIntent(this, PlaybackStateCompat.ACTION_PLAY_PAUSE)));
builder.addAction(new NotificationCompat.Action(android.R.drawable.ic_media_next, "Next", MediaButtonReceiver.buildMediaButtonPendingIntent(this, PlaybackStateCompat.ACTION_SKIP_TO_NEXT)));
builder.setStyle(new NotificationCompat.MediaStyle().setShowActionsInCompactView(0).setMediaSession(mMediaSessionCompat.getSessionToken()));
builder.setSmallIcon(R.mipmap.ic_launcher);
NotificationManagerCompat.from(BackgroundAudioService.this).notify(1, builder.build());
}
private void showPausedNotification() {
NotificationCompat.Builder builder = MediaStyleHelper.from(this, mMediaSessionCompat);
if (builder == null) {
return;
}
builder.addAction(new NotificationCompat.Action(android.R.drawable.ic_media_previous, "Previous", MediaButtonReceiver.buildMediaButtonPendingIntent(this, PlaybackStateCompat.ACTION_SKIP_TO_PREVIOUS)));
builder.addAction(new NotificationCompat.Action(android.R.drawable.ic_media_play, "Play", MediaButtonReceiver.buildMediaButtonPendingIntent(this, PlaybackStateCompat.ACTION_PLAY_PAUSE)));
builder.addAction(new NotificationCompat.Action(android.R.drawable.ic_media_next, "Next", MediaButtonReceiver.buildMediaButtonPendingIntent(this, PlaybackStateCompat.ACTION_SKIP_TO_NEXT)));
builder.setStyle(new NotificationCompat.MediaStyle().setShowActionsInCompactView(0).setMediaSession(mMediaSessionCompat.getSessionToken()));
builder.setSmallIcon(R.mipmap.ic_launcher);
NotificationManagerCompat.from(this).notify(1, builder.build());
}
private MediaSessionCompat.Callback mMediaSessionCallback = new MediaSessionCompat.Callback() {
@Override
public void onPlay() {
super.onPlay();
if (!successfullyRetrievedAudioFocus()) {
return;
}
mMediaSessionCompat.setActive(true);
setMediaPlaybackState(PlaybackStateCompat.STATE_PLAYING);
Log.d("MyLog", "STATE_PLAYING");
showPlayingNotification();
mMediaPlayer.start();
}
@Override
public void onPause() {
super.onPause();
if (mMediaPlayer.isPlaying()) {
mMediaPlayer.pause();
setMediaPlaybackState(PlaybackStateCompat.STATE_PAUSED);
Log.d("MyLog", "STATE_PAUSED");
showPausedNotification();
}
}
@Override
public void onSkipToNext() {
super.onSkipToNext();
setMediaPlaybackState(PlaybackStateCompat.STATE_SKIPPING_TO_NEXT);
Log.d("MyLog", "STATE_SKIPPING_TO_NEXT");
}
@Override
public void onSkipToPrevious() {
super.onSkipToPrevious();
setMediaPlaybackState(PlaybackStateCompat.STATE_SKIPPING_TO_PREVIOUS);
Log.d("MyLog", "STATE_SKIPPING_TO_PREVIOUS");
}
}
MediaStyleHelper
public static NotificationCompat.Builder from(
Context context, MediaSessionCompat mediaSession) {
MediaControllerCompat controller = mediaSession.getController();
MediaMetadataCompat mediaMetadata = controller.getMetadata();
MediaDescriptionCompat description = mediaMetadata.getDescription();
NotificationCompat.Builder builder = new NotificationCompat.Builder(context);
builder
.setContentTitle(description.getTitle())
.setContentText(description.getSubtitle())
.setSubText(description.getDescription())
.setLargeIcon(description.getIconBitmap())
.setContentIntent(controller.getSessionActivity())
.setDeleteIntent(
MediaButtonReceiver.buildMediaButtonPendingIntent(context, PlaybackStateCompat.ACTION_STOP))
.setVisibility(NotificationCompat.VISIBILITY_PUBLIC);
return builder;
}
MainActivity's onCreate
mMediaBrowserCompat = new MediaBrowserCompat(this, new ComponentName(this, BackgroundAudioService.class),
mMediaBrowserCompatConnectionCallback, getIntent().getExtras());
MainActivity
private MediaBrowserCompat.ConnectionCallback mMediaBrowserCompatConnectionCallback = new MediaBrowserCompat.ConnectionCallback() {
@Override
public void onConnected() {
super.onConnected();
try {
mMediaControllerCompat = new MediaControllerCompat(MainActivity.this, mMediaBrowserCompat.getSessionToken());
mMediaControllerCompat.registerCallback(mMediaControllerCompatCallback);
setSupportMediaController(mMediaControllerCompat);
getSupportMediaController().getTransportControls().playFromMediaId(String.valueOf(R.raw.warner_tautz_off_broadway), null);
} catch( RemoteException e ) {
}
}
};
private MediaControllerCompat.Callback mMediaControllerCompatCallback = new MediaControllerCompat.Callback() {
@Override
public void onPlaybackStateChanged(PlaybackStateCompat state) {
super.onPlaybackStateChanged(state);
if( state == null ) {
return;
}
switch( state.getState() ) {
case PlaybackStateCompat.STATE_PLAYING: {
mCurrentState = STATE_PLAYING;
break;
}
case PlaybackStateCompat.STATE_PAUSED: {
mCurrentState = STATE_PAUSED;
break;
}
case PlaybackStateCompat.STATE_SKIPPING_TO_NEXT: {
mCurrentState = STATE_SKIPPING_TO_NEXT;
break;
}
case PlaybackStateCompat.STATE_SKIPPING_TO_PREVIOUS: {
mCurrentState = STATE_SKIPPING_TO_PREVIOUS;
}
}
}
};
Update
Seems like onMediaButtonEvent() gets triggered for next and previous in notification player with action name "android.intent.action.MEDIA_BUTTON" for both buttons.
@Override
public boolean onMediaButtonEvent(Intent mediaButtonEvent) {
Log.d("MyLog", "executing onMediaButtonEvent: " + mediaButtonEvent.getAction());
return super.onMediaButtonEvent(mediaButtonEvent);
}
Just ran into this issue after following the same guide.
Look for a method called setMediaPlaybackState(int state)
inside your BackgroundAudioService class. This is where you define a PlaybackStateCompat.Builder and define the current capabilities available on the session using setActions.
OnSkipToNext and onSkipToPrevious won't be called unless ACTION_SKIP_TO_NEXT and ACTION_SKIP_TO_PREVIOUS are passed in as supported actions. I managed to get it working after updating setActions to the following...
private void setMediaPlaybackState(int state) {
PlaybackStateCompat.Builder playbackstateBuilder = new PlaybackStateCompat.Builder();
switch (state) {
case PlaybackStateCompat.STATE_PLAYING: {
playbackstateBuilder.setActions(PlaybackStateCompat.ACTION_PLAY_PAUSE | PlaybackStateCompat.ACTION_PAUSE
| PlaybackStateCompat.ACTION_SKIP_TO_NEXT | PlaybackStateCompat.ACTION_SKIP_TO_PREVIOUS);
break;
}
default: {
playbackstateBuilder.setActions(PlaybackStateCompat.ACTION_PLAY_PAUSE | PlaybackStateCompat.ACTION_PLAY
| PlaybackStateCompat.ACTION_SKIP_TO_NEXT | PlaybackStateCompat.ACTION_SKIP_TO_PREVIOUS);
break;
}
}
playbackstateBuilder.setState(state, PlaybackStateCompat.PLAYBACK_POSITION_UNKNOWN, 0);
mMediaSessionCompat.setPlaybackState(playbackstateBuilder.build());
}
Be nice if there was a slightly cleaner way to do this but it seems that all actions need to be set in one hit and can't be split across multiple calls.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With