简体   繁体   中英

How can I make SeekBar indicate current position even when the app is resumed?

When the audio player app is resumed while playing audio, the SeekBar resets to 0. During audio playback, the SeekBar updates progress. However, when the screen is resumed, the SeekBar starts from the beginning without indicating the player's current position. When you press the pause button and then press the play button, it plays at the current position again. In updateProgress() method, there is long currentPosition = mLastPlaybackState.getPosition(); I think this code doesn't indicate the current position when resumed.

I implemented SeekBar update progress based on the UAMP FullScreenActivity

This is my NowPlayingAcitivy.java:

private PlaybackStateCompat mLastPlaybackState;
private final Handler mHandler = new Handler();
private final Runnable mUpdateProgressTask = new Runnable() {
    @Override
    public void run() {
        updateProgress();
    }
};
private final ScheduledExecutorService mExecutorService =
        Executors.newSingleThreadScheduledExecutor();
private ScheduledFuture<?> mScheduledFuture;

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    mNowPlayingBinding = DataBindingUtil.setContentView(
            this, R.layout.activity_now_playing);
    createMediaBrowserCompat();

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

        @Override
        public void onStartTrackingTouch(SeekBar seekBar) {
            // Cancel the future returned by scheduleAtFixedRate() to stop the SeekBar from progressing
            stopSeekbarUpdate();
        }

        @Override
        public void onStopTrackingTouch(SeekBar seekBar) {
            MediaControllerCompat.getMediaController(NowPlayingActivity.this)
                    .getTransportControls().seekTo(seekBar.getProgress());
            // Create and execute a periodic action to update the SeekBar progress
            scheduleSeekbarUpdate();
        }
    });
}

private void createMediaBrowserCompat() {
    mMediaBrowser = new MediaBrowserCompat(this,
            new ComponentName(this, PodcastService.class),
            mConnectionCallbacks,
            null);
}

@Override
protected void onStart() {
    super.onStart();
    mMediaBrowser.connect();
}

@Override
protected void onResume() {
    super.onResume();
    setVolumeControlStream(AudioManager.STREAM_MUSIC);
}

@Override
protected void onStop() {
    super.onStop();
    if (MediaControllerCompat.getMediaController(this) != null) {
        MediaControllerCompat.getMediaController(this).unregisterCallback(controllerCallback);
    }
    mMediaBrowser.disconnect();       
}

@Override
protected void onDestroy() {
    super.onDestroy();
    stopSeekbarUpdate();
    mExecutorService.shutdown();
}

private final MediaBrowserCompat.ConnectionCallback mConnectionCallbacks =
        new MediaBrowserCompat.ConnectionCallback() {
    @Override
    public void onConnected() {
        MediaSessionCompat.Token token = mMediaBrowser.getSessionToken();

        MediaControllerCompat mediaController = null;
        try {
            mediaController = new MediaControllerCompat(NowPlayingActivity.this, token);
        } catch (RemoteException e) {
            Timber.e("Error creating media controller");
        }

        MediaControllerCompat.setMediaController(NowPlayingActivity.this,
                mediaController);

        buildTransportControls();
    }

    @Override
    public void onConnectionSuspended() {
        super.onConnectionSuspended();
    }

    @Override
    public void onConnectionFailed() {
        super.onConnectionFailed();
    }
};

void buildTransportControls() {
    mNowPlayingBinding.playingInfo.ibPlayPause.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View v) {
            PlaybackStateCompat pbState =
                    MediaControllerCompat.getMediaController(NowPlayingActivity.this).getPlaybackState();
            if (pbState != null) {
                MediaControllerCompat.TransportControls controls =
                        MediaControllerCompat.getMediaController(NowPlayingActivity.this).getTransportControls();
                switch (pbState.getState()) {
                    case PlaybackStateCompat.STATE_PLAYING: // fall through
                    case PlaybackStateCompat.STATE_BUFFERING:
                        controls.pause();
                        stopSeekbarUpdate();
                        break;
                    case PlaybackStateCompat.STATE_PAUSED:
                    case PlaybackStateCompat.STATE_STOPPED:
                        controls.play();
                        scheduleSeekbarUpdate();
                        break;
                    default:
                        Timber.d("onClick with state " + pbState);
                }
            }
        }
    });

    MediaControllerCompat mediaController =
            MediaControllerCompat.getMediaController(NowPlayingActivity.this);

    MediaMetadataCompat metadata = mediaController.getMetadata();
    PlaybackStateCompat pbState = mediaController.getPlaybackState();

    updatePlaybackState(pbState);

    if (metadata != null) {
        // Get the episode duration from the metadata and sets the end time to the textView
        updateDuration(metadata);
    }
    // Set the current progress to the current position
    updateProgress();

    if (pbState != null && (pbState.getState() == PlaybackStateCompat.STATE_PLAYING ||
            pbState.getState() == PlaybackStateCompat.STATE_BUFFERING)) {
        scheduleSeekbarUpdate();
    }

    mediaController.registerCallback(controllerCallback);
}

MediaControllerCompat.Callback controllerCallback = new MediaControllerCompat.Callback() {
    @Override
    public void onMetadataChanged(MediaMetadataCompat metadata) {
        super.onMetadataChanged(metadata);
        if (metadata != null) {
           updateDuration(metadata);
        }
    }

    @Override
    public void onPlaybackStateChanged(PlaybackStateCompat state) {
        super.onPlaybackStateChanged(state);
        // Update the playback state
        updatePlaybackState(state);
    }
};

/**
 * Creates and executes a periodic action that becomes enabled first after the given initial delay,
 * and subsequently with the given period;that is executions will commence after initialDelay
 * then initialDelay + period, then initialDelay + 2 * period, and so on.
 */
private void scheduleSeekbarUpdate() {
    stopSeekbarUpdate();
    if (!mExecutorService.isShutdown()) {
        mScheduleFuture = mExecutorService.scheduleAtFixedRate(
                new Runnable() {
                    @Override
                    public void run() {
                        mHandler.post(mUpdateProgressTask);
                    }
                }, 100,
                1000, TimeUnit.MILLISECONDS);
    }
}

/**
 * Cancels the future returned by scheduleAtFixedRate() to stop the SeekBar from progressing.
 */
private void stopSeekbarUpdate() {
    if (mScheduledFuture != null) {
        mScheduledFuture.cancel(false);
    }
}

/**
 * Gets the episode duration from the metadata and sets the end time to be displayed in the TextView.
 */
private void updateDuration(MediaMetadataCompat metadata) {
    if (metadata == null) {
        return;
    }
    int duration = (int) metadata.getLong(MediaMetadataCompat.METADATA_KEY_DURATION)
            * 1000;
    mNowPlayingBinding.playingInfo.seekBar.setMax(duration);
    mNowPlayingBinding.playingInfo.tvEnd.setText(
            DateUtils.formatElapsedTime(duration / 1000));
}

/**
 * Calculates the current position (distance = timeDelta * velocity) and sets the current progress
 * to the current position.
 */
private void updateProgress() {
    if (mLastPlaybackState == null) {
        return;
    }
    long currentPosition = mLastPlaybackState.getPosition();
    if (mLastPlaybackState.getState() == PlaybackStateCompat.STATE_PLAYING) {
        // 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();
    }
    mNowPlayingBinding.playingInfo.seekBar.setProgress((int) currentPosition);
}

private void updatePlaybackState(PlaybackStateCompat state) {
    if (state == null) {
        return;
    }
    mLastPlaybackState = state;
    switch (state.getState()) {
        case PlaybackStateCompat.STATE_PLAYING:
            hideLoading();
            mNowPlayingBinding.playingInfo.ibPlayPause.setImageResource(R.drawable.exo_controls_pause);
            scheduleSeekbarUpdate();
            break;
        case PlaybackStateCompat.STATE_PAUSED:
            hideLoading();
            mNowPlayingBinding.playingInfo.ibPlayPause.setImageResource(R.drawable.exo_controls_play);
            stopSeekbarUpdate();
            break;
        case PlaybackStateCompat.STATE_NONE:
        case PlaybackStateCompat.STATE_STOPPED:
            hideLoading();
            mNowPlayingBinding.playingInfo.ibPlayPause.setImageResource(R.drawable.exo_controls_play);
            stopSeekbarUpdate();
            break;
        case PlaybackStateCompat.STATE_BUFFERING:
            showLoading();
            mNowPlayingBinding.playingInfo.ibPlayPause.setImageResource(R.drawable.exo_controls_play);
            stopSeekbarUpdate();
            break;
        default:
            Timber.d("Unhandled state " + state.getState());
    }
}    

This is my PodcastService.java:

public class PodcastService extends MediaBrowserServiceCompat implements Player.EventListener {

    @Override
    public void onCreate() {
        super.onCreate();

        initializeMediaSession();
    }

    private void initializeMediaSession() {

        mMediaSession = new MediaSessionCompat(PodcastService.this, TAG);
        mMediaSession.setFlags(
                MediaSessionCompat.FLAG_HANDLES_MEDIA_BUTTONS |
                        MediaSessionCompat.FLAG_HANDLES_TRANSPORT_CONTROLS);

        mStateBuilder = new PlaybackStateCompat.Builder()
                .setActions(
                        PlaybackStateCompat.ACTION_PLAY |
                                PlaybackStateCompat.ACTION_PAUSE |
                                PlaybackStateCompat.ACTION_REWIND |
                                PlaybackStateCompat.ACTION_FAST_FORWARD |
                                PlaybackStateCompat.ACTION_PLAY_PAUSE);
        mMediaSession.setPlaybackState(mStateBuilder.build());

        mMediaSession.setCallback(new MySessionCallback());
        setSessionToken(mMediaSession.getSessionToken());

        mMediaSession.setSessionActivity(PendingIntent.getActivity(this,
                11,
                new Intent(this, NowPlayingActivity.class),
                PendingIntent.FLAG_UPDATE_CURRENT));
    }

    private void initializePlayer() {
        if (mExoPlayer == null) {
            DefaultRenderersFactory defaultRenderersFactory = new DefaultRenderersFactory(this);
            TrackSelector trackSelector = new DefaultTrackSelector();
            LoadControl loadControl = new DefaultLoadControl();
            mExoPlayer = ExoPlayerFactory.newSimpleInstance(this, defaultRenderersFactory,
                    trackSelector, loadControl);

            mExoPlayer.addListener(this);

            // Prepare the MediaSource
            Uri mediaUri = Uri.parse(mUrl);
            MediaSource mediaSource = buildMediaSource(mediaUri);
            mExoPlayer.prepare(mediaSource);

            mExoPlayer.setPlayWhenReady(true);
        }
    }

    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        if (intent == null || intent.getAction() == null) {
            Timber.e("intent in onStartCommand is null");
            return START_STICKY;
        }
        // Check if the old player should be released
        if (intent.getAction() != null && intent.getAction().equals(ACTION_RELEASE_OLD_PLAYER)) {
            if (mExoPlayer != null) {
                mExoPlayer.stop();
                releasePlayer();
            }
        }
        Bundle b = intent.getBundleExtra(EXTRA_ITEM);
        if (b != null) {
            mItem = b.getParcelable(EXTRA_ITEM);
            mUrl = mItem.getEnclosures().get(0).getUrl();
        }

        initializePlayer();

        // Convert hh:mm:ss string to seconds to put it into the metadata
        long duration = PodUtils.getDurationInMilliSeconds(mItem);
        MediaMetadataCompat metadata = new MediaMetadataCompat.Builder()
                .putLong(MediaMetadataCompat.METADATA_KEY_DURATION, duration).build();
        mMediaSession.setMetadata(metadata);

        return START_STICKY;
    }

    private void releasePlayer() {
        mExoPlayer.release();
        mExoPlayer = null;
    }

    @Override
    public void onTaskRemoved(Intent rootIntent) {
        super.onTaskRemoved(rootIntent);
        if (mExoPlayer != null) {
            mExoPlayer.stop(true);
        }
        stopSelf();
    }


    @Override
    public void onDestroy() {
        mMediaSession.release();

        releasePlayer();
        super.onDestroy();
    }

    private MediaSource buildMediaSource(Uri mediaUri) {
        String userAgent = Util.getUserAgent(this, getString(R.string.app_name));
        DefaultDataSourceFactory dataSourceFactory = new DefaultDataSourceFactory(
                this, userAgent);

        CacheDataSourceFactory cacheDataSourceFactory =
                new CacheDataSourceFactory(
                        DownloadUtil.getCache(this),
                        dataSourceFactory, 
                        CacheDataSource.FLAG_IGNORE_CACHE_ON_ERROR);
        return new ExtractorMediaSource.Factory(cacheDataSourceFactory).createMediaSource(mediaUri);
    }

    @Nullable
    @Override
    public BrowserRoot onGetRoot(@NonNull String clientPackageName, int clientUid,
                                 @Nullable Bundle rootHints) {
        return new BrowserRoot("pod_root_id", null);
    }

    @Override
    public void onLoadChildren(@NonNull String parentMediaId,
                               @NonNull Result<List<MediaBrowserCompat.MediaItem>> result) {

        // Browsing not allowed
        if (TextUtils.equals("empty_root_id", parentMediaId)) {
            result.sendResult(null);
            return;
        }

        List<MediaBrowserCompat.MediaItem> mediaItems = new ArrayList<>();

        // Check if this is the root menu:
        if ("pod_root_id".equals(parentMediaId)) {
            // Build the MediaItem objects for the top level,
            // and put them in the mediaItems list...

        } else {
            // Examine the passed parentMediaId to see which submenu we're at,
            // and put the children of that menu in the mediaItems list...
        }
        result.sendResult(mediaItems);
    }

    private class MySessionCallback extends MediaSessionCompat.Callback {

        @Override
        public void onPlay() {
            startService(new Intent(getApplicationContext(), PodcastService.class));

            mMediaSession.setActive(true);

            // Start the player
            if (mExoPlayer != null) {
                mExoPlayer.setPlayWhenReady(true);
            }
        }

        @Override
        public void onPause() {
            mExoPlayer.setPlayWhenReady(false);
            stopForeground(false);
        }

        @Override
        public void onRewind() {
            mExoPlayer.seekTo(Math.max(mExoPlayer.getCurrentPosition() - 10000, 0));
        }

        @Override
        public void onFastForward() {
            long duration = mExoPlayer.getDuration();
            mExoPlayer.seekTo(Math.min(mExoPlayer.getCurrentPosition() + 30000, duration));
        }

        @Override
        public void onStop() {
            stopSelf();
            mMediaSession.setActive(false);
            mExoPlayer.stop();
            stopForeground(true);
        }

        @Override
        public void onSeekTo(long pos) {
            super.onSeekTo(pos);
            if (mExoPlayer != null) {
                mExoPlayer.seekTo((int) pos);
            }
        }
    }

    // Player Event Listeners

    @Override
    public void onTimelineChanged(Timeline timeline, @Nullable Object manifest, int reason) {
    }

    @Override
    public void onPlayerError(ExoPlaybackException error) {
    }

    @Override
    public void onPlayerStateChanged(boolean playWhenReady, int playbackState) {
        if (playbackState == Player.STATE_IDLE) {
            mStateBuilder.setState(PlaybackStateCompat.STATE_PAUSED,
                    mExoPlayer.getCurrentPosition(), 1f);
        } else if (playbackState == Player.STATE_BUFFERING) {
            mStateBuilder.setState(PlaybackStateCompat.STATE_BUFFERING,
                    mExoPlayer.getCurrentPosition(), 1f);
        } else if (playbackState == Player.STATE_READY && playWhenReady) {
            mStateBuilder.setState(PlaybackStateCompat.STATE_PLAYING,
                    mExoPlayer.getCurrentPosition(), 1f);
            Timber.d("onPlayerStateChanged: we are playing");
        } else if (playbackState == Player.STATE_READY) {
            mStateBuilder.setState(PlaybackStateCompat.STATE_PAUSED,
                    mExoPlayer.getCurrentPosition(), 1f);
            Timber.d("onPlayerStateChanged: we are paused");
        } else if (playbackState == Player.STATE_ENDED) {
            mStateBuilder.setState(PlaybackStateCompat.STATE_PAUSED,
                    mExoPlayer.getCurrentPosition(), 1f);
        } else {
            mStateBuilder.setState(PlaybackStateCompat.STATE_NONE,
                    mExoPlayer.getCurrentPosition(), 1f);
        }
        mMediaSession.setPlaybackState(mStateBuilder.build());
    }
}

Edit: The full source code is available here .

To set the state progress based on value should use setProgress(value) method.

when paused the music save value from seekBar as an Integer, then when resume it get that value and put it as a parameter in setProgress() method.

when you pause music to save the value:

 @Override
protected void onPause() {
    super.onPause();

    mSeekBarRate.setOnSeekBarChangeListener(
            new SeekBar.OnSeekBarChangeListener() {
                int progress = 0;

                @Override
                public void onProgressChanged(SeekBar mSeekBarRate, int progressValue, boolean fromUser) {
                    progress = progressValue;
                }

                @Override
                public void onStartTrackingTouch(SeekBar mSeekBarRate) {
                }

                @Override
                public void onStopTrackingTouch(SeekBar mSeekBarRate) {
                    SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(MainActivity.this);
                    SharedPreferences.Editor editor = prefs.edit();

                    editor.putInt("value", progress);
                }
            });

}

when you resume music retrieve that value:

@Override
protected void onStart() {
    super.onStart();

    SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(this);
    int value = prefs.getInt("value", 0);

    mSeekBarRate.setProgress(value);
}

Hope it Helps you.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM