Apply falsing manager on seekbar
Check false taps and touches on seekbar change listener whenever touch is finished. This ensures that all falsing actions are handled besides OnFling events that are handled. Bug: 243040927 Test: manual Test: atest SeekBarViewModelTest Change-Id: I60bcc3196cc2a4b3410c7851f798bfaf87d9e456
This commit is contained in:
@@ -45,6 +45,7 @@ public abstract class Classifier {
|
||||
public static final int QS_SWIPE_SIDE = 15;
|
||||
public static final int BACK_GESTURE = 16;
|
||||
public static final int QS_SWIPE_NESTED = 17;
|
||||
public static final int MEDIA_SEEKBAR = 18;
|
||||
|
||||
@IntDef({
|
||||
QUICK_SETTINGS,
|
||||
@@ -65,7 +66,8 @@ public abstract class Classifier {
|
||||
LOCK_ICON,
|
||||
QS_SWIPE_SIDE,
|
||||
QS_SWIPE_NESTED,
|
||||
BACK_GESTURE
|
||||
BACK_GESTURE,
|
||||
MEDIA_SEEKBAR,
|
||||
})
|
||||
@Retention(RetentionPolicy.SOURCE)
|
||||
public @interface InteractionType {}
|
||||
|
||||
@@ -23,6 +23,7 @@ import static com.android.internal.config.sysui.SystemUiDeviceConfigFlags.BRIGHT
|
||||
import static com.android.internal.config.sysui.SystemUiDeviceConfigFlags.BRIGHTLINE_FALSING_DISTANCE_VERTICAL_FLING_THRESHOLD_IN;
|
||||
import static com.android.internal.config.sysui.SystemUiDeviceConfigFlags.BRIGHTLINE_FALSING_DISTANCE_VERTICAL_SWIPE_THRESHOLD_IN;
|
||||
import static com.android.systemui.classifier.Classifier.BRIGHTNESS_SLIDER;
|
||||
import static com.android.systemui.classifier.Classifier.MEDIA_SEEKBAR;
|
||||
import static com.android.systemui.classifier.Classifier.QS_COLLAPSE;
|
||||
import static com.android.systemui.classifier.Classifier.QS_SWIPE_NESTED;
|
||||
import static com.android.systemui.classifier.Classifier.SHADE_DRAG;
|
||||
@@ -153,6 +154,7 @@ class DistanceClassifier extends FalsingClassifier {
|
||||
@Classifier.InteractionType int interactionType,
|
||||
double historyBelief, double historyConfidence) {
|
||||
if (interactionType == BRIGHTNESS_SLIDER
|
||||
|| interactionType == MEDIA_SEEKBAR
|
||||
|| interactionType == SHADE_DRAG
|
||||
|| interactionType == QS_COLLAPSE
|
||||
|| interactionType == Classifier.UDFPS_AUTHENTICATION
|
||||
|
||||
@@ -18,6 +18,7 @@ package com.android.systemui.classifier;
|
||||
|
||||
import static com.android.internal.config.sysui.SystemUiDeviceConfigFlags.BRIGHTLINE_FALSING_PROXIMITY_PERCENT_COVERED_THRESHOLD;
|
||||
import static com.android.systemui.classifier.Classifier.BRIGHTNESS_SLIDER;
|
||||
import static com.android.systemui.classifier.Classifier.MEDIA_SEEKBAR;
|
||||
import static com.android.systemui.classifier.Classifier.QS_COLLAPSE;
|
||||
import static com.android.systemui.classifier.Classifier.QS_SWIPE_SIDE;
|
||||
import static com.android.systemui.classifier.Classifier.QUICK_SETTINGS;
|
||||
@@ -119,7 +120,8 @@ class ProximityClassifier extends FalsingClassifier {
|
||||
@Classifier.InteractionType int interactionType,
|
||||
double historyBelief, double historyConfidence) {
|
||||
if (interactionType == QUICK_SETTINGS || interactionType == BRIGHTNESS_SLIDER
|
||||
|| interactionType == QS_COLLAPSE || interactionType == QS_SWIPE_SIDE) {
|
||||
|| interactionType == QS_COLLAPSE || interactionType == QS_SWIPE_SIDE
|
||||
|| interactionType == MEDIA_SEEKBAR) {
|
||||
return Result.passed(0);
|
||||
}
|
||||
|
||||
|
||||
@@ -20,6 +20,7 @@ package com.android.systemui.classifier;
|
||||
import static com.android.systemui.classifier.Classifier.BOUNCER_UNLOCK;
|
||||
import static com.android.systemui.classifier.Classifier.BRIGHTNESS_SLIDER;
|
||||
import static com.android.systemui.classifier.Classifier.LEFT_AFFORDANCE;
|
||||
import static com.android.systemui.classifier.Classifier.MEDIA_SEEKBAR;
|
||||
import static com.android.systemui.classifier.Classifier.NOTIFICATION_DISMISS;
|
||||
import static com.android.systemui.classifier.Classifier.NOTIFICATION_DRAG_DOWN;
|
||||
import static com.android.systemui.classifier.Classifier.PULSE_EXPAND;
|
||||
@@ -93,6 +94,10 @@ public class TypeClassifier extends FalsingClassifier {
|
||||
case QS_SWIPE_NESTED:
|
||||
wrongDirection = !vertical;
|
||||
break;
|
||||
case MEDIA_SEEKBAR:
|
||||
confidence = 0;
|
||||
wrongDirection = vertical;
|
||||
break;
|
||||
default:
|
||||
wrongDirection = true;
|
||||
break;
|
||||
|
||||
@@ -22,6 +22,7 @@ import static com.android.internal.config.sysui.SystemUiDeviceConfigFlags.BRIGHT
|
||||
import static com.android.internal.config.sysui.SystemUiDeviceConfigFlags.BRIGHTLINE_FALSING_ZIGZAG_Y_SECONDARY_DEVIANCE;
|
||||
import static com.android.systemui.classifier.Classifier.BRIGHTNESS_SLIDER;
|
||||
import static com.android.systemui.classifier.Classifier.LOCK_ICON;
|
||||
import static com.android.systemui.classifier.Classifier.MEDIA_SEEKBAR;
|
||||
import static com.android.systemui.classifier.Classifier.SHADE_DRAG;
|
||||
|
||||
import android.graphics.Point;
|
||||
@@ -91,6 +92,7 @@ class ZigZagClassifier extends FalsingClassifier {
|
||||
@Classifier.InteractionType int interactionType,
|
||||
double historyBelief, double historyConfidence) {
|
||||
if (interactionType == BRIGHTNESS_SLIDER
|
||||
|| interactionType == MEDIA_SEEKBAR
|
||||
|| interactionType == SHADE_DRAG
|
||||
|| interactionType == LOCK_ICON) {
|
||||
return Result.passed(0);
|
||||
|
||||
@@ -30,7 +30,10 @@ import androidx.annotation.WorkerThread
|
||||
import androidx.core.view.GestureDetectorCompat
|
||||
import androidx.lifecycle.LiveData
|
||||
import androidx.lifecycle.MutableLiveData
|
||||
import com.android.systemui.classifier.Classifier.MEDIA_SEEKBAR
|
||||
import com.android.systemui.dagger.qualifiers.Background
|
||||
import com.android.systemui.plugins.FalsingManager
|
||||
import com.android.systemui.plugins.FalsingManager.LOW_PENALTY
|
||||
import com.android.systemui.statusbar.NotificationMediaManager
|
||||
import com.android.systemui.util.concurrency.RepeatableExecutor
|
||||
import javax.inject.Inject
|
||||
@@ -72,7 +75,8 @@ private fun PlaybackState.computePosition(duration: Long): Long {
|
||||
|
||||
/** ViewModel for seek bar in QS media player. */
|
||||
class SeekBarViewModel @Inject constructor(
|
||||
@Background private val bgExecutor: RepeatableExecutor
|
||||
@Background private val bgExecutor: RepeatableExecutor,
|
||||
private val falsingManager: FalsingManager,
|
||||
) {
|
||||
private var _data = Progress(false, false, false, false, null, 0)
|
||||
set(value) {
|
||||
@@ -275,7 +279,7 @@ class SeekBarViewModel @Inject constructor(
|
||||
/** Gets a listener to attach to the seek bar to handle seeking. */
|
||||
val seekBarListener: SeekBar.OnSeekBarChangeListener
|
||||
get() {
|
||||
return SeekBarChangeListener(this)
|
||||
return SeekBarChangeListener(this, falsingManager)
|
||||
}
|
||||
|
||||
/** Attach touch handlers to the seek bar view. */
|
||||
@@ -315,7 +319,8 @@ class SeekBarViewModel @Inject constructor(
|
||||
}
|
||||
|
||||
private class SeekBarChangeListener(
|
||||
val viewModel: SeekBarViewModel
|
||||
val viewModel: SeekBarViewModel,
|
||||
val falsingManager: FalsingManager,
|
||||
) : SeekBar.OnSeekBarChangeListener {
|
||||
override fun onProgressChanged(bar: SeekBar, progress: Int, fromUser: Boolean) {
|
||||
if (fromUser) {
|
||||
@@ -328,6 +333,13 @@ class SeekBarViewModel @Inject constructor(
|
||||
}
|
||||
|
||||
override fun onStopTrackingTouch(bar: SeekBar) {
|
||||
// in addition to the normal functionality of both functions.
|
||||
// isFalseTouch returns true if there is a real/false tap since it is not a move.
|
||||
// isFalseTap returns true if there is a real/false move since it is not a tap.
|
||||
if (falsingManager.isFalseTouch(MEDIA_SEEKBAR) &&
|
||||
falsingManager.isFalseTap(LOW_PENALTY)) {
|
||||
viewModel.onSeekFalse()
|
||||
}
|
||||
viewModel.onSeek(bar.progress.toLong())
|
||||
}
|
||||
}
|
||||
@@ -340,7 +352,7 @@ class SeekBarViewModel @Inject constructor(
|
||||
*/
|
||||
private class SeekBarTouchListener(
|
||||
private val viewModel: SeekBarViewModel,
|
||||
private val bar: SeekBar
|
||||
private val bar: SeekBar,
|
||||
) : View.OnTouchListener, GestureDetector.OnGestureListener {
|
||||
|
||||
// Gesture detector helps decide which touch events to intercept.
|
||||
|
||||
@@ -26,13 +26,13 @@ import android.widget.SeekBar
|
||||
import androidx.arch.core.executor.ArchTaskExecutor
|
||||
import androidx.arch.core.executor.TaskExecutor
|
||||
import androidx.test.filters.SmallTest
|
||||
|
||||
import com.android.systemui.SysuiTestCase
|
||||
import com.android.systemui.classifier.Classifier
|
||||
import com.android.systemui.plugins.FalsingManager
|
||||
import com.android.systemui.util.concurrency.FakeExecutor
|
||||
import com.android.systemui.util.concurrency.FakeRepeatableExecutor
|
||||
import com.android.systemui.util.time.FakeSystemClock
|
||||
import com.google.common.truth.Truth.assertThat
|
||||
|
||||
import org.junit.After
|
||||
import org.junit.Before
|
||||
import org.junit.Ignore
|
||||
@@ -47,8 +47,8 @@ import org.mockito.Mockito.mock
|
||||
import org.mockito.Mockito.never
|
||||
import org.mockito.Mockito.times
|
||||
import org.mockito.Mockito.verify
|
||||
import org.mockito.junit.MockitoJUnit
|
||||
import org.mockito.Mockito.`when` as whenever
|
||||
import org.mockito.junit.MockitoJUnit
|
||||
|
||||
@SmallTest
|
||||
@RunWith(AndroidTestingRunner::class)
|
||||
@@ -70,6 +70,8 @@ public class SeekBarViewModelTest : SysuiTestCase() {
|
||||
}
|
||||
@Mock private lateinit var mockController: MediaController
|
||||
@Mock private lateinit var mockTransport: MediaController.TransportControls
|
||||
@Mock private lateinit var falsingManager: FalsingManager
|
||||
@Mock private lateinit var mockBar: SeekBar
|
||||
private val token1 = MediaSession.Token(1, null)
|
||||
private val token2 = MediaSession.Token(2, null)
|
||||
|
||||
@@ -78,9 +80,10 @@ public class SeekBarViewModelTest : SysuiTestCase() {
|
||||
@Before
|
||||
fun setUp() {
|
||||
fakeExecutor = FakeExecutor(FakeSystemClock())
|
||||
viewModel = SeekBarViewModel(FakeRepeatableExecutor(fakeExecutor))
|
||||
viewModel = SeekBarViewModel(FakeRepeatableExecutor(fakeExecutor), falsingManager)
|
||||
viewModel.logSeek = { }
|
||||
whenever(mockController.sessionToken).thenReturn(token1)
|
||||
whenever(mockBar.context).thenReturn(context)
|
||||
|
||||
// LiveData to run synchronously
|
||||
ArchTaskExecutor.getInstance().setDelegate(taskExecutor)
|
||||
@@ -453,6 +456,25 @@ public class SeekBarViewModelTest : SysuiTestCase() {
|
||||
verify(mockTransport).seekTo(eq(pos.toLong()))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun onFalseTapOrTouch() {
|
||||
whenever(mockController.getTransportControls()).thenReturn(mockTransport)
|
||||
whenever(falsingManager.isFalseTouch(Classifier.MEDIA_SEEKBAR)).thenReturn(true)
|
||||
whenever(falsingManager.isFalseTap(FalsingManager.LOW_PENALTY)).thenReturn(true)
|
||||
viewModel.updateController(mockController)
|
||||
val pos = 169
|
||||
|
||||
viewModel.attachTouchHandlers(mockBar)
|
||||
with(viewModel.seekBarListener) {
|
||||
onStartTrackingTouch(mockBar)
|
||||
onProgressChanged(mockBar, pos, true)
|
||||
onStopTrackingTouch(mockBar)
|
||||
}
|
||||
|
||||
// THEN transport controls should not be used
|
||||
verify(mockTransport, never()).seekTo(pos.toLong())
|
||||
}
|
||||
|
||||
@Test
|
||||
fun queuePollTaskWhenPlaying() {
|
||||
// GIVEN that the track is playing
|
||||
|
||||
Reference in New Issue
Block a user