Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

SeekTo does not trigger onPlaybackStateChanged in MediaControllerCompat

I have this media player and everything else works properly, I start the song, it gets through 2 states, the last one being PlaybackStateCompat.STATE_PLAYING

Therefore we can say the callback is registered. The problem is whenever I trigger the SeekBar listener, the method seekTo does not trigger the onPlaybackStateChanged and because this does not happen, I cannot update the SeekBar to its new progress. This only concerns the UI. The music actually is playing from the progress after modifying the SeekBar progress. What's more, the trackDurationTextView doesn't get updated either. It changes its value while I'm dragging my thumb across the seek bar but once I release it, it goes to its initial progress, same as the seek bar.

Here is where there callback is registered.

@Override
    public void onServiceConnected(ComponentName componentName, IBinder service) {
        if (service instanceof MediaPlayerService.ServiceBinder) {
            try {
                mediaController = new MediaControllerCompat(this, ((MediaPlayerService.ServiceBinder) service).getService().getMediaSessionToken());
            } catch (RemoteException e) {
                e.printStackTrace();
            }
            mediaController.registerCallback(mMediaControllerCallback);
        }
    }

This is how the callback is initialized:

 private MediaControllerCompat.Callback mMediaControllerCallback = new MediaControllerCompat.Callback() {
        @Override
        public void onPlaybackStateChanged(@NonNull PlaybackStateCompat state) {
            updatePlaybackState(state);
        }

        @Override
        public void onMetadataChanged(MediaMetadataCompat metadata) {
            if (metadata != null) {
                updateMediaDescription(metadata.getDescription());
                updateDuration(metadata);
            }
        }
    };

This is the OnSeekBarChangeListener

trackSeekBar.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {
            @Override
            public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
                trackDurationTextView.setText(DateUtils.formatElapsedTime(progress / 1000));
            }

            @Override
            public void onStartTrackingTouch(SeekBar seekBar) {
                stopSeekbarUpdate();
            }

            @Override
            public void onStopTrackingTouch(SeekBar seekBar) {
                //TODO: look here! the seek bar position is not kept when the user slides it!
                mediaController.getTransportControls().seekTo(seekBar.getProgress());
                scheduleSeekBarUpdate();
            }
        });


 private void updateProgress() {
        if (mLastPlaybackState == null) {
            return;
        }
        long currentPosition = mLastPlaybackState.getPosition();
        if (mLastPlaybackState.getState() != PlaybackStateCompat.STATE_PAUSED) {
            // Calculate the elapsed time between the last position update and now and unless
            // paused, we can assume (delta * speed) + current position is approximately the
            // latest position. This ensure that we do not repeatedly call the getPlaybackState()
            // on MediaControllerCompat.
            long timeDelta = SystemClock.elapsedRealtime() -
                    mLastPlaybackState.getLastPositionUpdateTime();

            currentPosition += (int) timeDelta * mLastPlaybackState.getPlaybackSpeed();
        }
        trackSeekBar.setProgress((int) currentPosition);
    }

This is the place where mLastPlayBackState.getPosition should return the new value after the seek bar is updated, but it always returns 0 since the onPlaybackStateChanged never gets called. Any ideas why?

like image 508
funkycookie Avatar asked Oct 30 '22 19:10

funkycookie


1 Answers

It appears that MediaControllerCompat.Callback.onPlaybackStateChanged(PlaybackStateCompat state) is only called if the MediaSessionCompat's PlaybackStateCompat's state changes. If the PlaybackStateCompat's position changes while the state remains unchanged, onPlaybackStateChanged(...) never gets called. So I assume by "onPlaybackStateChanged", the API is more specifically referring to the PlaybackStateCompat's state and not the PlaybackStateCompat itself.

To get around this, I temporarily update the PlaybackStateCompat's state to PlaybackStateCompat.STATE_REWINDING or PlaybackStateCompat.STATE_FAST_FORWARDING after a seek command to trigger onPlaybackStateChanged(...) on the UI so that I can update the SeekBar's position and elapsed time TextView's text to the new playback position.

UPDATE: I can no longer reproduce this issue. I'm not sure what exactly I changed to fix it either. My first suggestion for you is to verify that you are calling setState(state) on your mediaSession object where state is the PlaybackStateCompat that contains your updated position value. This wasn't the solution for me, but it's where I would look first.

like image 98
Ryan Avatar answered Nov 18 '22 10:11

Ryan