diff --git a/packages/SystemUI/src/com/android/systemui/classifier/Classifier.java b/packages/SystemUI/src/com/android/systemui/classifier/Classifier.java index c2922968b58ec..701df8981ca26 100644 --- a/packages/SystemUI/src/com/android/systemui/classifier/Classifier.java +++ b/packages/SystemUI/src/com/android/systemui/classifier/Classifier.java @@ -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 {} diff --git a/packages/SystemUI/src/com/android/systemui/classifier/DistanceClassifier.java b/packages/SystemUI/src/com/android/systemui/classifier/DistanceClassifier.java index 5e4f149d3ca38..f8ee49add04b3 100644 --- a/packages/SystemUI/src/com/android/systemui/classifier/DistanceClassifier.java +++ b/packages/SystemUI/src/com/android/systemui/classifier/DistanceClassifier.java @@ -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 diff --git a/packages/SystemUI/src/com/android/systemui/classifier/ProximityClassifier.java b/packages/SystemUI/src/com/android/systemui/classifier/ProximityClassifier.java index 07f94e792a934..e8c83b1e49dda 100644 --- a/packages/SystemUI/src/com/android/systemui/classifier/ProximityClassifier.java +++ b/packages/SystemUI/src/com/android/systemui/classifier/ProximityClassifier.java @@ -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); } diff --git a/packages/SystemUI/src/com/android/systemui/classifier/TypeClassifier.java b/packages/SystemUI/src/com/android/systemui/classifier/TypeClassifier.java index 776bc88ad6bfc..f576a5af8907b 100644 --- a/packages/SystemUI/src/com/android/systemui/classifier/TypeClassifier.java +++ b/packages/SystemUI/src/com/android/systemui/classifier/TypeClassifier.java @@ -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; diff --git a/packages/SystemUI/src/com/android/systemui/classifier/ZigZagClassifier.java b/packages/SystemUI/src/com/android/systemui/classifier/ZigZagClassifier.java index de2bdf7ded755..840982cbcc64d 100644 --- a/packages/SystemUI/src/com/android/systemui/classifier/ZigZagClassifier.java +++ b/packages/SystemUI/src/com/android/systemui/classifier/ZigZagClassifier.java @@ -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); diff --git a/packages/SystemUI/src/com/android/systemui/media/SeekBarViewModel.kt b/packages/SystemUI/src/com/android/systemui/media/SeekBarViewModel.kt index 0359c6325749b..17ebfecdd1fa8 100644 --- a/packages/SystemUI/src/com/android/systemui/media/SeekBarViewModel.kt +++ b/packages/SystemUI/src/com/android/systemui/media/SeekBarViewModel.kt @@ -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. diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/SeekBarViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/SeekBarViewModelTest.kt index 82aa6123917e0..597334033895a 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/media/SeekBarViewModelTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/media/SeekBarViewModelTest.kt @@ -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