Exoplayer: Provide option to include next/prev button in lock screen/compact mode

Created on 11 Mar 2019  路  8Comments  路  Source: google/ExoPlayer

how to show previous and next button on lockscreen with MediaSession extension for ExoPlayer

I am using
https://github.com/google/ExoPlayer/tree/release-v2/extensions/mediasession

implementation 'com.google.android.exoplayer:exoplayer-core:2.8.1'
    implementation 'com.google.android.exoplayer:exoplayer-ui:2.8.1'
    implementation 'com.google.android.exoplayer:extension-mediasession:2.8.1'

and code is from
https://www.youtube.com/watch?v=svdq1BWl4r8
and
https://github.com/google/Exoplayer/tree/io18

the media sessions code is

mediaSession = new MediaSessionCompat(context, MEDIA_SESSION_TAG);
mediaSession.setActive(true);
playerNotificationManager.setMediaSessionToken(mediaSession.getSessionToken());

mediaSessionConnector = new MediaSessionConnector(mediaSession);
mediaSessionConnector.setQueueNavigator(new TimelineQueueNavigator(mediaSession) {
  @Override
  public MediaDescriptionCompat getMediaDescription(Player player, int windowIndex) {
    return Samples.getMediaDescription(context, SAMPLES[windowIndex]);
  }
});
mediaSessionConnector.setPlayer(player, null);

what i want is to show previous and next button like this

https://cdn-images-1.medium.com/max/800/1*xgjqZoP67CVMAGCtOLhCPA.png

from this

https://medium.com/google-exoplayer/the-mediasession-extension-for-exoplayer-82b9619deb2d

but what i get is

Screenshot_2019-03-11-14-50-03-952

The code has been tested on lenovo A7000 OS marshmallow

and

moto c OS nougat

how to show previous and next button on lockscreen with MediaSession extension for ExoPlayer

enhancement

Most helpful comment

I marked this issue as an enhancement.

We should think about providing an option which lets developer include prev/next button in compact mode of the notification without needing to override the getActionIndicesForCompactView(List<String> actionNames, Player player):int[] method.

This would improve the interplay between PlayerNotifciationManager and MediaSessionConnector specifically for the lock screen use case which appears to be an important bit for media app usability.

All 8 comments

This issue does not seem to follow the issue template. Make sure you provide all the required information.

@marcbaechinger would you kindly look at this?

This is not determined by the MediaSession but by the MediaStyle notification to which the mediaSession is attached.

The MediaStyle object has a method mediaStyle.setShowActionsInCompactView(int... actionIndex)which lets us define which actions should be shown in compact view. Please note that this changes the look of both the NotificationCompat in the drawer and the notification in the lock screen.

When doing a notification with ExoPlayer I'd recommend to use the PlayerNotificationManager of the ui module. By default the PlayerNotificationManager only sets the play or pause button to be shown in compact mode. You can customize this behaviour by overriding the method getActionIndicesForCompactView(List<String> actionNames, Player player):int[].

You need to be careful though that you only return indices of actions which are actually in the list of actionNames at that very moment. Specifically for the next/previous actions. If you are using an index which is not valid, your app crashes. I haven't tested this in-depth but it's possibly something like this:

@Overrride
protected int[] getActionIndicesForCompactView(List<String> actionNames, Player player) {
    int pauseActionIndex = actionNames.indexOf(ACTION_PAUSE);
    int playActionIndex = actionNames.indexOf(ACTION_PLAY);
    int skipNextActionIndex = actionNames.indexOf(ACTION_NEXT);
    int skipPreviousActionIndex = actionNames.indexOf(ACTION_PREVIOUS);

    int numberOfActionIndices = (pauseActionIndex != -1 || playActionIndex != -1) ? 1 : 0;
    if (skipNextActionIndex != -1) {
      numberOfActionIndices++;
    }
    if (skipPreviousActionIndex != -1) {
      numberOfActionIndices++;
    }

    int[] actionIndices = new int[numberOfActionIndices];
    int actionCounter = 0;
    if (skipPreviousActionIndex != -1) {
      actionIndices[actionCounter++] = skipPreviousActionIndex;
    }
    if (pauseActionIndex != -1) {
      actionIndices[actionCounter++] = pauseActionIndex;
    }
    if (playActionIndex != -1) {
      actionIndices[actionCounter++] = playActionIndex;
    }
    if (skipNextActionIndex != -1) {
      actionIndices[actionCounter] = skipNextActionIndex;
    }
    return actionIndices;
  }

since you have not tried the code, this is my code

final Context context = this;

    player = ExoPlayerFactory.newSimpleInstance(context, new DefaultTrackSelector());
    DefaultDataSourceFactory dataSourceFactory = new DefaultDataSourceFactory(
            context, Util.getUserAgent(context, getString(R.string.application_name)));
    CacheDataSourceFactory cacheDataSourceFactory = new CacheDataSourceFactory(
            DownloadUtil.getCache(context),
            dataSourceFactory,
            CacheDataSource.FLAG_IGNORE_CACHE_ON_ERROR);
    ConcatenatingMediaSource concatenatingMediaSource = new ConcatenatingMediaSource();
    for (Samples.Sample sample : SAMPLES) {
        MediaSource mediaSource = new ExtractorMediaSource.Factory(cacheDataSourceFactory)
                .createMediaSource(sample.uri);
        concatenatingMediaSource.addMediaSource(mediaSource);
    }
    player.prepare(concatenatingMediaSource);
    player.setPlayWhenReady(true);
        playerNotificationManager =  customAction.createWithNotificationChannel(
            context,
            PLAYBACK_CHANNEL_ID,
            R.string.playback_channel_name,
            PLAYBACK_NOTIFICATION_ID,
            new PlayerNotificationManager.MediaDescriptionAdapter() {
                @Override
                public String getCurrentContentTitle(Player player) {
                    return SAMPLES[player.getCurrentWindowIndex()].title;
                }

                @Nullable
                @Override
                public PendingIntent createCurrentContentIntent(Player player) {
                    return null;
                }

                @Nullable
                @Override
                public String getCurrentContentText(Player player) {
                    return SAMPLES[player.getCurrentWindowIndex()].description;
                }

                @Nullable
                @Override
                public Bitmap getCurrentLargeIcon(Player player, PlayerNotificationManager.BitmapCallback callback) {
                    return Samples.getBitmap(
                            context, SAMPLES[player.getCurrentWindowIndex()].bitmapResource);
                }
            }
    );

    playerNotificationManager.setNotificationListener(new
NotificationListener() {
    @Override
    public void onNotificationStarted ( int notificationId, Notification notification){
        startForeground(notificationId, notification);
    }
    @Override
    public void onNotificationCancelled ( int notificationId){
        stopSelf();
    }
}
);

    playerNotificationManager.setPlayer(player);
    playerNotificationManager.setUseChronometer(false);
    playerNotificationManager.setUseNavigationActions(true);
mediaSession =new  MediaSessionCompat(context, MEDIA_SESSION_TAG);
    mediaSession.setActive(true);
    playerNotificationManager.setMediaSessionToken(mediaSession.getSessionToken());
mediaSessionConnector =new

MediaSessionConnector(mediaSession);
    mediaSessionConnector.setQueueNavigator(new

TimelineQueueNavigator(mediaSession) {
    @Override
    public MediaDescriptionCompat getMediaDescription (Player player,int windowIndex){
        return Samples.getMediaDescription(context, SAMPLES[windowIndex]);
    }
});
    mediaSessionConnector.setPlayer(player,null,null);

    private class customAction extends PlayerNotificationManager {
    public customAction(Context context, String channelId, int notificationId, MediaDescriptionAdapter mediaDescriptionAdapter) {
        super(context, channelId, notificationId, mediaDescriptionAdapter);
    }

    @Override
    protected int[] getActionIndicesForCompactView(List<String> actionNames, Player player) {
        int pauseActionIndex = actionNames.indexOf(ACTION_PAUSE);
        int playActionIndex = actionNames.indexOf(ACTION_PLAY);
        int skipNextActionIndex = actionNames.indexOf(ACTION_NEXT);
        int skipPreviousActionIndex = actionNames.indexOf(ACTION_PREVIOUS);

        int numberOfActionIndices = (pauseActionIndex != -1 || playActionIndex != -1) ? 1 : 0;

        if (skipNextActionIndex != -1) {
            numberOfActionIndices++;
        }
        if (skipPreviousActionIndex != -1) {
            numberOfActionIndices++;
        }

        int[] actionIndices = new int[3];
        int actionCounter = 0;
        if (skipPreviousActionIndex != -1) {
            actionIndices[actionCounter++] = skipPreviousActionIndex;
        }
        if (pauseActionIndex != -1) {
            actionIndices[actionCounter++] = pauseActionIndex;
        }
        if (playActionIndex != -1) {
            actionIndices[actionCounter++] = playActionIndex;
        }
        if (skipNextActionIndex != -1) {
            actionIndices[actionCounter] = skipNextActionIndex;
        }
        return actionIndices;
    }
}

How ever still it doesnot return all the 3 buttons required, it just returns only play/pause button

for the original project code you can try

https://github.com/google/Exoplayer/tree/io18

can you please provide a solution, can it be added as a feature in future release to customize
getActionIndicesForCompactView(List actionNames, Player player):int[]

What enhancement is this issue tracking? It's unclear from the thread above. Could we rename the title to better reflect this, also? Thanks!

I marked this issue as an enhancement.

We should think about providing an option which lets developer include prev/next button in compact mode of the notification without needing to override the getActionIndicesForCompactView(List<String> actionNames, Player player):int[] method.

This would improve the interplay between PlayerNotifciationManager and MediaSessionConnector specifically for the lock screen use case which appears to be an important bit for media app usability.

any time line when this will be included in future releases

This was in the 2.10.0 release.

Was this page helpful?
0 / 5 - 0 ratings