Merge "Cancel vertical seekbar grab" into udc-dev
This commit is contained in:
committed by
Android (Google) Code Review
commit
9e9c5aac4b
@@ -27,6 +27,7 @@ import android.view.View
|
||||
import android.view.ViewConfiguration
|
||||
import android.widget.SeekBar
|
||||
import androidx.annotation.AnyThread
|
||||
import androidx.annotation.VisibleForTesting
|
||||
import androidx.annotation.WorkerThread
|
||||
import androidx.core.view.GestureDetectorCompat
|
||||
import androidx.lifecycle.LiveData
|
||||
@@ -37,6 +38,7 @@ import com.android.systemui.plugins.FalsingManager
|
||||
import com.android.systemui.statusbar.NotificationMediaManager
|
||||
import com.android.systemui.util.concurrency.RepeatableExecutor
|
||||
import javax.inject.Inject
|
||||
import kotlin.math.abs
|
||||
|
||||
private const val POSITION_UPDATE_INTERVAL_MILLIS = 100L
|
||||
private const val MIN_FLING_VELOCITY_SCALE_FACTOR = 10
|
||||
@@ -325,6 +327,10 @@ constructor(
|
||||
return SeekBarChangeListener(this, falsingManager)
|
||||
}
|
||||
|
||||
/** first and last motion events of seekbar grab. */
|
||||
@VisibleForTesting var firstMotionEvent: MotionEvent? = null
|
||||
@VisibleForTesting var lastMotionEvent: MotionEvent? = null
|
||||
|
||||
/** Attach touch handlers to the seek bar view. */
|
||||
fun attachTouchHandlers(bar: SeekBar) {
|
||||
bar.setOnSeekBarChangeListener(seekBarListener)
|
||||
@@ -351,6 +357,23 @@ constructor(
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This method specifies if user made a bad seekbar grab or not. If the vertical distance from
|
||||
* first touch on seekbar is more than the horizontal distance, this means that the seekbar grab
|
||||
* is more vertical and should be rejected. Seekbar accepts horizontal grabs only.
|
||||
*
|
||||
* Single tap has the same first and last motion event, it is counted as a valid grab.
|
||||
*
|
||||
* @return whether the touch on seekbar is valid.
|
||||
*/
|
||||
private fun isValidSeekbarGrab(): Boolean {
|
||||
if (firstMotionEvent == null || lastMotionEvent == null) {
|
||||
return true
|
||||
}
|
||||
return abs(firstMotionEvent!!.x - lastMotionEvent!!.x) >=
|
||||
abs(firstMotionEvent!!.y - lastMotionEvent!!.y)
|
||||
}
|
||||
|
||||
/** Listener interface to be notified when the user starts or stops scrubbing. */
|
||||
interface ScrubbingChangeListener {
|
||||
fun onScrubbingChanged(scrubbing: Boolean)
|
||||
@@ -376,7 +399,7 @@ constructor(
|
||||
}
|
||||
|
||||
override fun onStopTrackingTouch(bar: SeekBar) {
|
||||
if (falsingManager.isFalseTouch(MEDIA_SEEKBAR)) {
|
||||
if (!viewModel.isValidSeekbarGrab() || falsingManager.isFalseTouch(MEDIA_SEEKBAR)) {
|
||||
viewModel.onSeekFalse()
|
||||
}
|
||||
viewModel.onSeek(bar.progress.toLong())
|
||||
@@ -424,6 +447,8 @@ constructor(
|
||||
return false
|
||||
}
|
||||
detector.onTouchEvent(event)
|
||||
// Store the last motion event done on seekbar.
|
||||
viewModel.lastMotionEvent = event.copy()
|
||||
return !shouldGoToSeekBar
|
||||
}
|
||||
|
||||
@@ -468,6 +493,8 @@ constructor(
|
||||
if (shouldGoToSeekBar) {
|
||||
bar.parent?.requestDisallowInterceptTouchEvent(true)
|
||||
}
|
||||
// Store the first motion event done on seekbar.
|
||||
viewModel.firstMotionEvent = event.copy()
|
||||
return shouldGoToSeekBar
|
||||
}
|
||||
|
||||
|
||||
@@ -22,6 +22,7 @@ import android.media.session.MediaSession
|
||||
import android.media.session.PlaybackState
|
||||
import android.testing.AndroidTestingRunner
|
||||
import android.testing.TestableLooper
|
||||
import android.view.MotionEvent
|
||||
import android.widget.SeekBar
|
||||
import androidx.arch.core.executor.ArchTaskExecutor
|
||||
import androidx.arch.core.executor.TaskExecutor
|
||||
@@ -466,20 +467,62 @@ public class SeekBarViewModelTest : SysuiTestCase() {
|
||||
whenever(mockController.getTransportControls()).thenReturn(mockTransport)
|
||||
whenever(falsingManager.isFalseTouch(Classifier.MEDIA_SEEKBAR)).thenReturn(true)
|
||||
whenever(falsingManager.isFalseTap(anyInt())).thenReturn(true)
|
||||
viewModel.updateController(mockController)
|
||||
val pos = 169
|
||||
|
||||
viewModel.attachTouchHandlers(mockBar)
|
||||
viewModel.updateController(mockController)
|
||||
val pos = 40
|
||||
val bar = SeekBar(context).apply { progress = pos }
|
||||
with(viewModel.seekBarListener) {
|
||||
onStartTrackingTouch(mockBar)
|
||||
onProgressChanged(mockBar, pos, true)
|
||||
onStopTrackingTouch(mockBar)
|
||||
onStartTrackingTouch(bar)
|
||||
onStopTrackingTouch(bar)
|
||||
}
|
||||
fakeExecutor.runAllReady()
|
||||
|
||||
// THEN transport controls should not be used
|
||||
verify(mockTransport, never()).seekTo(pos.toLong())
|
||||
}
|
||||
|
||||
@Test
|
||||
fun onSeekbarGrabInvalidTouch() {
|
||||
whenever(mockController.getTransportControls()).thenReturn(mockTransport)
|
||||
viewModel.firstMotionEvent =
|
||||
MotionEvent.obtain(12L, 13L, MotionEvent.ACTION_DOWN, 76F, 0F, 0)
|
||||
viewModel.lastMotionEvent = MotionEvent.obtain(12L, 14L, MotionEvent.ACTION_UP, 78F, 4F, 0)
|
||||
val pos = 78
|
||||
|
||||
viewModel.updateController(mockController)
|
||||
// WHEN user ends drag
|
||||
val bar = SeekBar(context).apply { progress = pos }
|
||||
with(viewModel.seekBarListener) {
|
||||
onStartTrackingTouch(bar)
|
||||
onStopTrackingTouch(bar)
|
||||
}
|
||||
fakeExecutor.runAllReady()
|
||||
|
||||
// THEN transport controls should not be used
|
||||
verify(mockTransport, never()).seekTo(pos.toLong())
|
||||
}
|
||||
|
||||
@Test
|
||||
fun onSeekbarGrabValidTouch() {
|
||||
whenever(mockController.transportControls).thenReturn(mockTransport)
|
||||
viewModel.firstMotionEvent =
|
||||
MotionEvent.obtain(12L, 13L, MotionEvent.ACTION_DOWN, 36F, 0F, 0)
|
||||
viewModel.lastMotionEvent = MotionEvent.obtain(12L, 14L, MotionEvent.ACTION_UP, 40F, 1F, 0)
|
||||
val pos = 40
|
||||
|
||||
viewModel.updateController(mockController)
|
||||
// WHEN user ends drag
|
||||
val bar = SeekBar(context).apply { progress = pos }
|
||||
with(viewModel.seekBarListener) {
|
||||
onStartTrackingTouch(bar)
|
||||
onStopTrackingTouch(bar)
|
||||
}
|
||||
fakeExecutor.runAllReady()
|
||||
|
||||
// THEN transport controls should be used
|
||||
verify(mockTransport).seekTo(pos.toLong())
|
||||
}
|
||||
|
||||
@Test
|
||||
fun queuePollTaskWhenPlaying() {
|
||||
// GIVEN that the track is playing
|
||||
|
||||
Reference in New Issue
Block a user