Merge "Cancel vertical seekbar grab" into udc-dev

This commit is contained in:
Michael Mikhail
2023-04-15 10:30:30 +00:00
committed by Android (Google) Code Review
2 changed files with 77 additions and 7 deletions

View File

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

View File

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