google / ExoPlayer

This project is deprecated and stale. The latest ExoPlayer code is available in https://github.com/androidx/media
https://developer.android.com/media/media3/exoplayer
Apache License 2.0
21.74k stars 6.03k forks source link

Provide an option to make navigation behave like REPEAT_MODE_ALL even with REPEAT_MODE_OFF #7221

Open pawaom opened 4 years ago

pawaom commented 4 years ago

Force to show next and previous button for first and last song in notification and Lock screen controls when player.setRepeatMode(Player.REPEAT_MODE_ONE); and player.setRepeatMode(Player.REPEAT_MODE_OFF);

The normal behavior of a music player is 1) when player.setRepeatMode(Player.REPEAT_MODE_ONE); and player.setRepeatMode(Player.REPEAT_MODE_OFF); to show previous and next button to allow users to go back to the first or last song when they desire to, when either the first or last song is played

what actually happens in exoplayer notification and lock screen control 1) when user is on Last song with REPEAT_MODE_ONE or REPEAT_MODE_OFF , the next button is invisible, not allowing user to click the next button to go to the "FIRST" song 2) when user is on Last song with REPEAT_MODE_ONE or REPEAT_MODE_OFF ,the previous button is visible,but nothing happens, not allowing user to click the previous button to go to the "LAST" song

I am trying to override this WRONG process

using this code

public void Show notification() {
        playerNotificationManager = CustomPlayerNotificationManager.createWithNotificationChannel(AudioPlayerService.this,
                ConstantValues.PLAYBACK_CHANNEL_ID, R.string.app_name, R.string.app_name, ConstantValues.PLAYBACK_NOTIFICATION_ID,
                new CustomPlayerNotificationManager.MediaDescriptionAdapter() {
                    @Override
                    public String getCurrentContentTitle(Player player) {
                        Title = SongsList.get(player.getCurrentWindowIndex())._title;
                        AlbumName = SongsList.get(player.getCurrentWindowIndex())._album;
                        SongPathway = SongsList.get(player.getCurrentWindowIndex())._path;
                        Album_ID = SongsList.get(player.getCurrentWindowIndex())._albumId;
                        MediaID = SongsList.get(player.getCurrentWindowIndex())._id;
                        return Title;
                    }

                    @Nullable
                    @Override
                    public PendingIntent createCurrentContentIntent(Player player) {
                        Intent intent = new Intent(AudioPlayerService.this, Blankact.class);
                        intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TOP);
                        return PendingIntent.getActivity(AudioPlayerService.this, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT);
                    }

                    @Nullable
                    @Override
                    public String getCurrentContentText(Player player) {
                        return AlbumName;
                    }

                    @Nullable
                    @Override
                    public Bitmap getCurrentLargeIcon(Player player, CustomPlayerNotificationManager.BitmapCallback
                            callback) {
                        bitmap = otherUtils.getAlbumartImage(AudioPlayerService.this, SongsList.get(player.getCurrentWindowIndex())._albumId);
                        return bitmap;
                    }
                },
                new CustomPlayerNotificationManager.NotificationListener() {

                    @Override
                    public void onNotificationCancelled(int notificationId, boolean dismissedByUser) {
                        shouldsave = false;
                        Intent intent = new Intent(AudioPlayerService.this, ColseActivity.class);
                        intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
                        startActivity(intent);
                        stopped = "stopped";
                        stopForeground(true);
                        stopSelf();
                    }
                    @Override
                    public void onNotificationPosted(int notificationId, Notification notification, boolean ongoing) {
                        startForeground(notificationId, notification);
                    }
                }
        );

        playerNotificationManager.setPriority(NotificationCompat.PRIORITY_HIGH);
        playerNotificationManager.setPlayer(player);
        playerNotificationManager.setFastForwardIncrementMs(0);
        playerNotificationManager.setRewindIncrementMs(0);
        playerNotificationManager.setUseChronometer(false);
        playerNotificationManager.setUsePlayPauseActions(true);
        playerNotificationManager.setUseStopAction(true);
        playerNotificationManager.setUseNavigationActionsInCompactView(true);
        mediaSession = new MediaSessionCompat(AudioPlayerService.this, ConstantValues.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 getMediaDescriptionl(SongsList, windowIndex);
            }
        });
        mediaSessionConnector.setPlayer(player);

        playerNotificationManager.setControlDispatcher(new DefaultControlDispatcher() {
            @Override
            public boolean dispatchSetPlayWhenReady(Player player, boolean playWhenReady) {
                // You can modify this code to call through to the player or not.
                player.setPlayWhenReady(playWhenReady);
                if (!player.isPlaying()) {
                    if (player.getRepeatMode() == Player.REPEAT_MODE_OFF && !player.hasNext()) {
                        if (player.getCurrentPosition() > (player.getDuration() - 1000)) {
                            playnextnow();
                        }
                    }
                }
                // Toast.makeText(AudioPlayerService.this, "playWhenReady ", Toast.LENGTH_SHORT).show();
                // Return true if you called through to the player, and false otherwise.
                return true;
            }

        });
    }

    public class CustomPlayerNotificationManager extends PlayerNotificationManager {

        private Context context;
        private Map<String, NotificationCompat.Action> actionMap2 = new HashMap<>();

        public CustomPlayerNotificationManager(Context context, String channelId, int notificationId, MediaDescriptionAdapter mediaDescriptionAdapter) {
            super(context, channelId, notificationId, mediaDescriptionAdapter);
        }

        public CustomPlayerNotificationManager(Context context, String channelId, int notificationId, MediaDescriptionAdapter mediaDescriptionAdapter, @Nullable NotificationListener notificationListener) {
            super(context, channelId, notificationId, mediaDescriptionAdapter, notificationListener);
            this.context = context;
        }

        public CustomPlayerNotificationManager(Context context, String channelId, int notificationId, MediaDescriptionAdapter mediaDescriptionAdapter, @Nullable CustomActionReceiver customActionReceiver) {
            super(context, channelId, notificationId, mediaDescriptionAdapter, customActionReceiver);
        }

        public CustomPlayerNotificationManager(Context context, String channelId, int notificationId, MediaDescriptionAdapter mediaDescriptionAdapter, @Nullable NotificationListener notificationListener, @Nullable CustomActionReceiver customActionReceiver) {
            super(context, channelId, notificationId, mediaDescriptionAdapter, notificationListener, customActionReceiver);
        }

        private boolean isPlaying(Player player) {
            return player.getPlaybackState() != Player.STATE_ENDED
                    && player.getPlaybackState() != Player.STATE_IDLE
                    && player.getPlayWhenReady();
        }

        @Override
        protected List<String> getActions(Player player) {
            boolean enablePrevious = true;
            boolean enableRewind = false;
            boolean enableFastForward = false;
            boolean enableNext = true;
            boolean useStopAction = true;
            boolean useNavigationActions = true;
            boolean usePlayPauseActions = true;
            List<String> stringActions = new ArrayList<>();
            if (useNavigationActions && enablePrevious) {
                stringActions.add(ACTION_PREVIOUS);
            }
            if (enableRewind) {
                stringActions.add(ACTION_REWIND);
            }
            if (usePlayPauseActions) {
                if (player.isPlaying()) {
                    stringActions.add(ACTION_PAUSE);
                } else {
                    stringActions.add(ACTION_PLAY);
                }
            }
            if (enableFastForward) {
                stringActions.add(ACTION_FAST_FORWARD);
            }
            if (useNavigationActions && enableNext) {
                stringActions.add(ACTION_NEXT);
            }

            if (useStopAction) {
                stringActions.add(ACTION_STOP);
            }
            return stringActions;
        }
    }

But nothing works , can any one suggest what should I do

What I am getting is

Screenshot_2020-04-12-11-02-14-035 Screenshot_2020-04-12-11-11-41-857

What I want is Screenshot_2020-04-12-11-02-39-743 Screenshot_2020-04-12-11-13-40-515

for both Notification and Lock Screen Controls

icbaker commented 4 years ago

I'd like to make sure I've understood exactly what behaviour you're expecting and what you're seeing, both with and without your code changes.

Let's assume we've got a playlist with 3 items:

My understanding of your expectations:

And what you're actually seeing with 'stock' ExoPlayer:

AFAICT from your screenshots your code changes haven't had any effect on this behaviour - is that right?

Marc can help you with customising the behaviour & visibility of these buttons.

pawaom commented 4 years ago

You got the scenario mostly right, using your own example I would explain

Songs A, B, C

Player is in REPEAT_MODE_OFF or REPEAT_MODE_ONE

scenario 1

currently we are playing Song C

When we click previous button it should go to the previous song,ie song B, instead sometime it repeats the same song which I have explained here

and the next button is invisible

what I want is to show the next button and allow user to go to Song A, when this button is clicked

scenario 2

currently we are playing Song A

Player is in REPEAT_MODE_OFF or REPEAT_MODE_ONE

When we click previous button it should go to the previous song,ie song C, instead it Always repeats the same song

I understand this might be the expected behavior as per the developers, but what i want is allow us as developers the flexibility to play the songs in an order we would like

marcbaechinger commented 4 years ago

I think the behaviour you are describing works as expected. I agree that the current implementation can be seen as opinionated, but that's probably the case for however we implement it :). I agree that your approach makes sense as well for some users. I'm hesitating a bit to change the default behaviour from one opinionated variant to another after having the current implementation in place since quite a while with no input on not being useful.

However, I totally agree, we should provide ways so you can implement your custom behaviour. I think this should already be possible with dev-v2, so please let me know whether this is the case.

Like already mentioned in the other issue you filed, customization of previous/next is available only in the dev-v2 branch or in the future, when the change has landed in release-v2.

Scenario 1 You need to override PlayerNotificationManager.getActions() and return ACTION_NEXT even when at the end of the playlist. Currently ACTION_NEXT is only included when there is a next item, which is not the case when repeating is off. In addition you need to provide your custom ControlDispatcher and pass it to PlayerNotificationManager.setControlDispatcher(). You need to override dispatchNext(Player player) accordingly.

Scenario 2 You need to provide your custom ControlDispatcher and pass it to PlayerNotificationManager.setControlDispatcher(). You need to override dispatchPrevious(Player player) accordingly.

Please let me know whether this helps.

pawaom commented 4 years ago

I agree the default behaviour, is one approach, What we suggested was based on feedback we got from testing with a select group of users. to summarize their opinion, it was 1) users expect a button to be active (in all cases not disable) 2) if a button was visible earlier it should be visible always (making it invisible makes it difficult to follow the user experience flow )

example

Screenshot_2020-04-12-11-02-39-743

looks totally different in this situation

Screenshot_2020-04-12-11-02-14-035

all we wanted was an option to access the next button so that we could use

 if (player.hasNext()) {
            player.next();
        } else {
            player.seekTo(0, C.TIME_UNSET);
        }

if an option called playerNotificationManager.setnextbutton(true);

similar to

    ```
    playerNotificationManager.setUseChronometer(false);
    playerNotificationManager.setUsePlayPauseActions(true);
    playerNotificationManager.setUseStopAction(true);
    ```

was available and we could access it , to pass the above code, would have made things a lot easier, I understand your approach to override PlayerNotificationManager.getActions() or use custom ControlDispatcher

but it would be easier if a simpler method was available

thanks,

pawaom commented 4 years ago

thanks for including this as an enhancement , please include this feature in the mediasessions part as well, (lock screen controls) as shown in the screenshot I have shared above @marcbaechinger

pawaom commented 4 years ago

recently I cam across this

https://github.com/google/ExoPlayer/pull/7437

@marcbaechinger I just want to know , if this enhancement be included in this release

thanks

marcbaechinger commented 4 years ago

We don't have any progress on this I'm afraid.