Update progress without binder calls
The implementation is pretty much a copy of MediaSessionRecord#getStateWithUpdatedPosition. Bug: 154352658 Test: manual - played music and checked that seek bar updated Change-Id: Id494f01dd18095f5df6c0806f34a7a5c92ddaf35
This commit is contained in:
@@ -19,6 +19,7 @@ package com.android.systemui.media
|
||||
import android.media.MediaMetadata
|
||||
import android.media.session.MediaController
|
||||
import android.media.session.PlaybackState
|
||||
import android.os.SystemClock
|
||||
import android.view.MotionEvent
|
||||
import android.view.View
|
||||
import android.widget.SeekBar
|
||||
@@ -31,6 +32,38 @@ import com.android.systemui.util.concurrency.DelayableExecutor
|
||||
|
||||
private const val POSITION_UPDATE_INTERVAL_MILLIS = 100L
|
||||
|
||||
private fun PlaybackState.isInMotion(): Boolean {
|
||||
return this.state == PlaybackState.STATE_PLAYING ||
|
||||
this.state == PlaybackState.STATE_FAST_FORWARDING ||
|
||||
this.state == PlaybackState.STATE_REWINDING
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the playback position while accounting for the time since the [PlaybackState] was last
|
||||
* retrieved.
|
||||
*
|
||||
* This method closely follows the implementation of
|
||||
* [MediaSessionRecord#getStateWithUpdatedPosition].
|
||||
*/
|
||||
private fun PlaybackState.computePosition(duration: Long): Long {
|
||||
var currentPosition = this.position
|
||||
if (this.isInMotion()) {
|
||||
val updateTime = this.getLastPositionUpdateTime()
|
||||
val currentTime = SystemClock.elapsedRealtime()
|
||||
if (updateTime > 0) {
|
||||
var position = (this.playbackSpeed * (currentTime - updateTime)).toLong() +
|
||||
this.getPosition()
|
||||
if (duration >= 0 && position > duration) {
|
||||
position = duration.toLong()
|
||||
} else if (position < 0) {
|
||||
position = 0
|
||||
}
|
||||
currentPosition = position
|
||||
}
|
||||
}
|
||||
return currentPosition
|
||||
}
|
||||
|
||||
/** ViewModel for seek bar in QS media player. */
|
||||
class SeekBarViewModel(val bgExecutor: DelayableExecutor) {
|
||||
|
||||
@@ -98,7 +131,8 @@ class SeekBarViewModel(val bgExecutor: DelayableExecutor) {
|
||||
|
||||
@AnyThread
|
||||
private fun checkPlaybackPosition(): Runnable = bgExecutor.executeDelayed({
|
||||
val currentPosition = controller?.playbackState?.position?.toInt()
|
||||
val duration = _data?.duration ?: -1
|
||||
val currentPosition = playbackState?.computePosition(duration.toLong())?.toInt()
|
||||
if (currentPosition != null && _data.elapsedTime != currentPosition) {
|
||||
_data = _data.copy(elapsedTime = currentPosition)
|
||||
}
|
||||
@@ -109,13 +143,7 @@ class SeekBarViewModel(val bgExecutor: DelayableExecutor) {
|
||||
|
||||
@WorkerThread
|
||||
private fun shouldPollPlaybackPosition(): Boolean {
|
||||
val state = playbackState?.state
|
||||
val moving = if (state == null) false else
|
||||
state == PlaybackState.STATE_PLAYING ||
|
||||
state == PlaybackState.STATE_BUFFERING ||
|
||||
state == PlaybackState.STATE_FAST_FORWARDING ||
|
||||
state == PlaybackState.STATE_REWINDING
|
||||
return moving && listening
|
||||
return listening && playbackState?.isInMotion() ?: false
|
||||
}
|
||||
|
||||
/** Gets a listener to attach to the seek bar to handle seeking. */
|
||||
|
||||
@@ -362,28 +362,21 @@ public class SeekBarViewModelTest : SysuiTestCase() {
|
||||
|
||||
@Test
|
||||
fun taskUpdatesProgress() {
|
||||
// GIVEN that the PlaybackState contins the current position
|
||||
val position = 200L
|
||||
// GIVEN that the PlaybackState contins the initial position
|
||||
val initialPosition = 0L
|
||||
val state = PlaybackState.Builder().run {
|
||||
setState(PlaybackState.STATE_PLAYING, position, 1f)
|
||||
setState(PlaybackState.STATE_PLAYING, initialPosition, 1f)
|
||||
build()
|
||||
}
|
||||
whenever(mockController.getPlaybackState()).thenReturn(state)
|
||||
viewModel.updateController(mockController, Color.RED)
|
||||
// AND the playback state advances
|
||||
val nextPosition = 300L
|
||||
val nextState = PlaybackState.Builder().run {
|
||||
setState(PlaybackState.STATE_PLAYING, nextPosition, 1f)
|
||||
build()
|
||||
}
|
||||
whenever(mockController.getPlaybackState()).thenReturn(nextState)
|
||||
// WHEN the task runs
|
||||
with(fakeExecutor) {
|
||||
advanceClockToNext()
|
||||
runAllReady()
|
||||
}
|
||||
// THEN elapsed time is captured
|
||||
assertThat(viewModel.progress.value!!.elapsedTime).isEqualTo(nextPosition.toInt())
|
||||
// THEN elapsed time has increased
|
||||
assertThat(viewModel.progress.value!!.elapsedTime).isGreaterThan(initialPosition.toInt())
|
||||
}
|
||||
|
||||
@Test
|
||||
|
||||
Reference in New Issue
Block a user