Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

onSkipToNext() and onSkipToPrevious() of MediaSessionCompat.Callback does not get triggered

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);
    }
like image 976
channae Avatar asked May 25 '18 05:05

channae


1 Answers

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.

like image 176
Mr Prezbo Avatar answered Nov 16 '22 18:11

Mr Prezbo