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:
Robert Snoeberger
2020-05-01 00:29:15 -04:00
parent 48f0527199
commit 9285c747e2
2 changed files with 41 additions and 20 deletions

View File

@@ -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. */

View File

@@ -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