Как я могу заставить панель поиска указывать текущее положение, даже когда приложение возобновлено?

#java #android #seekbar

#java #Android #панель поиска

Вопрос:

Когда приложение аудиоплеера возобновляется во время воспроизведения звука, панель поиска сбрасывается на 0. Во время воспроизведения звука панель поиска обновляет прогресс. Однако, когда экран возобновляется, панель поиска запускается с самого начала без указания текущего положения игрока. Когда вы нажимаете кнопку паузы, а затем кнопку воспроизведения, оно снова воспроизводится в текущей позиции. В updateProgress() методе есть long currentPosition = mLastPlaybackState.getPosition(); , я думаю, что этот код не указывает текущую позицию при возобновлении.

Я реализовал прогресс обновления панели поиска на основе UAMP FullScreenActivity

Это мой 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 amp;amp; (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());
    }
}    
  

Это мой 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 amp;amp; 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 amp;amp; 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());
    }
}
  

Редактировать: полный исходный код доступен здесь.

Комментарии:

1. Теперь вы знаете, как использовать SharedPreferences?

2. Да, я знаю. Но не могли бы вы сказать мне более точно, куда поместить SharedPreferences для редактирования данных или для извлечения данных из SharedPreferences?

Ответ №1:

Для установки прогресса состояния на основе значения следует использовать setProgress(value) метод.

при паузе музыка сохраняет значение из панели поиска в виде целого числа, затем при возобновлении получает это значение и помещает его в качестве параметра в setProgress() методе.

когда вы приостанавливаете музыку, чтобы сохранить значение:

  @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);
                }
            });

}
  

при возобновлении воспроизведения музыки извлеките это значение:

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

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

    mSeekBarRate.setProgress(value);
}
  

Надеюсь, это поможет вам.

Комментарии:

1. Спасибо за ответ. Я внедрил ваше решение, но это не решает проблему

2. вы установили Log в своем коде? что именно не так?

3. Я не устанавливал журнал. Если нажать кнопку «Домой» во время воспроизведения звука в NowPlayingActivity, а затем перезапустить приложение, панель поиска по-прежнему сбрасывается на 0