Loading packages/SystemUI/multivalentTests/src/com/android/systemui/touchpad/tutorial/ui/gesture/HomeGestureRecognizerTest.kt +21 −1 Original line number Diff line number Diff line Loading @@ -20,10 +20,12 @@ import android.view.MotionEvent import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.android.systemui.SysuiTestCase import com.android.systemui.testKosmos import com.android.systemui.touchpad.tutorial.ui.gesture.GestureState.Finished import com.android.systemui.touchpad.tutorial.ui.gesture.GestureState.InProgress import com.android.systemui.touchpad.tutorial.ui.gesture.GestureState.NotStarted import com.android.systemui.touchpad.tutorial.ui.gesture.MultiFingerGesture.Companion.SWIPE_DISTANCE import com.android.systemui.touchpad.ui.gesture.fakeVelocityTracker import com.google.common.truth.Truth.assertThat import org.junit.Before import org.junit.Test Loading @@ -33,12 +35,24 @@ import org.junit.runner.RunWith @RunWith(AndroidJUnit4::class) class HomeGestureRecognizerTest : SysuiTestCase() { companion object { const val THRESHOLD_VELOCITY_PX_PER_MS = 1f const val SLOW = THRESHOLD_VELOCITY_PX_PER_MS - 0.01f const val FAST = THRESHOLD_VELOCITY_PX_PER_MS + 0.01f } private var gestureState: GestureState = GestureState.NotStarted private var velocityTracker = testKosmos().fakeVelocityTracker private val gestureRecognizer = HomeGestureRecognizer(gestureDistanceThresholdPx = SWIPE_DISTANCE.toInt()) HomeGestureRecognizer( gestureDistanceThresholdPx = SWIPE_DISTANCE.toInt(), velocityThresholdPxPerMs = THRESHOLD_VELOCITY_PX_PER_MS, velocityTracker = velocityTracker, ) @Before fun before() { velocityTracker.setVelocity(Velocity(FAST)) gestureRecognizer.addGestureStateCallback { gestureState = it } } Loading @@ -47,6 +61,12 @@ class HomeGestureRecognizerTest : SysuiTestCase() { assertStateAfterEvents(events = ThreeFingerGesture.swipeUp(), expectedState = Finished) } @Test fun doesntTriggerGestureFinished_onGestureSpeedTooSlow() { velocityTracker.setVelocity(Velocity(SLOW)) assertStateAfterEvents(events = ThreeFingerGesture.swipeUp(), expectedState = NotStarted) } @Test fun triggersGestureProgressForThreeFingerGestureStarted() { assertStateAfterEvents( Loading packages/SystemUI/multivalentTests/src/com/android/systemui/touchpad/tutorial/ui/gesture/RecentAppsGestureRecognizerTest.kt +9 −14 Original line number Diff line number Diff line Loading @@ -17,21 +17,19 @@ package com.android.systemui.touchpad.tutorial.ui.gesture import android.view.MotionEvent import androidx.compose.ui.input.pointer.util.VelocityTracker1D import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.android.systemui.SysuiTestCase import com.android.systemui.testKosmos import com.android.systemui.touchpad.tutorial.ui.gesture.GestureState.Finished import com.android.systemui.touchpad.tutorial.ui.gesture.GestureState.InProgress import com.android.systemui.touchpad.tutorial.ui.gesture.GestureState.NotStarted import com.android.systemui.touchpad.tutorial.ui.gesture.MultiFingerGesture.Companion.SWIPE_DISTANCE import com.android.systemui.touchpad.ui.gesture.fakeVelocityTracker import com.google.common.truth.Truth.assertThat import org.junit.Before import org.junit.Test import org.junit.runner.RunWith import org.mockito.kotlin.doReturn import org.mockito.kotlin.mock import org.mockito.kotlin.whenever @SmallTest @RunWith(AndroidJUnit4::class) Loading @@ -39,26 +37,23 @@ class RecentAppsGestureRecognizerTest : SysuiTestCase() { companion object { const val THRESHOLD_VELOCITY_PX_PER_MS = 0.1f // multiply by 1000 to get px/ms instead of px/s which is unit used by velocity tracker const val SLOW = THRESHOLD_VELOCITY_PX_PER_MS * 1000 - 1 const val FAST = THRESHOLD_VELOCITY_PX_PER_MS * 1000 + 1 const val SLOW = THRESHOLD_VELOCITY_PX_PER_MS - 0.01f const val FAST = THRESHOLD_VELOCITY_PX_PER_MS + 0.01f } private var velocityTracker = testKosmos().fakeVelocityTracker private var gestureState: GestureState = GestureState.NotStarted private val velocityTracker1D = mock<VelocityTracker1D> { // by default return correct speed for the gesture - as if pointer is slowing down on { calculateVelocity() } doReturn SLOW } private val gestureRecognizer = RecentAppsGestureRecognizer( gestureDistanceThresholdPx = SWIPE_DISTANCE.toInt(), velocityThresholdPxPerMs = THRESHOLD_VELOCITY_PX_PER_MS, velocityTracker = VerticalVelocityTracker(velocityTracker1D), velocityTracker = velocityTracker, ) @Before fun before() { velocityTracker.setVelocity(Velocity(SLOW)) gestureRecognizer.addGestureStateCallback { gestureState = it } } Loading @@ -69,7 +64,7 @@ class RecentAppsGestureRecognizerTest : SysuiTestCase() { @Test fun doesntTriggerGestureFinished_onGestureSpeedTooHigh() { whenever(velocityTracker1D.calculateVelocity()).thenReturn(FAST) velocityTracker.setVelocity(Velocity(FAST)) assertStateAfterEvents(events = ThreeFingerGesture.swipeUp(), expectedState = NotStarted) } Loading packages/SystemUI/res/values/dimens.xml +5 −1 Original line number Diff line number Diff line Loading @@ -1995,12 +1995,16 @@ <dimen name="backlight_indicator_step_large_radius">28dp</dimen> <!-- Touchpad gestures tutorial--> <!-- This value is in unit of dp/ms <!-- This value is in dp/ms. TriggerSwipeUpTouchTracker (which is base for gesture tutorial implementation) uses value of 0.5dp but from manual testing it's too high and doesn't really feel like it's forcing slowing down. Also for tutorial it should be fine to lean to the side of being more strict rather than not strict enough and not teaching user the proper gesture as a result.--> <dimen name="touchpad_recent_apps_gesture_velocity_threshold">0.05dp</dimen> <!-- This value is in dp/ms. As above, it's not tied to system-wide value (defined in launcher's quickstep_fling_threshold_speed) because for tutorial it's fine to be more strict. --> <dimen name="touchpad_home_gesture_velocity_threshold">0.5dp</dimen> <!-- Normal gesture threshold is system_gestures_distance_threshold but for tutorial we can exaggerate gesture, which also works much better with live tracking --> <dimen name="touchpad_tutorial_gestures_distance_threshold">48dp</dimen> Loading packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/composable/HomeGestureTutorialScreen.kt +2 −1 Original line number Diff line number Diff line Loading @@ -63,7 +63,8 @@ fun HomeGestureTutorialScreen(onDoneButtonClicked: () -> Unit, onBack: () -> Uni private fun rememberHomeGestureRecognizer(resources: Resources): GestureRecognizer { val distance = resources.getDimensionPixelSize(R.dimen.touchpad_tutorial_gestures_distance_threshold) return remember(distance) { HomeGestureRecognizer(distance) } val velocity = resources.getDimension(R.dimen.touchpad_home_gesture_velocity_threshold) return remember(distance) { HomeGestureRecognizer(distance, velocity) } } @Composable Loading packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/gesture/HomeGestureRecognizer.kt +11 −2 Original line number Diff line number Diff line Loading @@ -19,9 +19,14 @@ package com.android.systemui.touchpad.tutorial.ui.gesture import android.util.MathUtils import android.view.MotionEvent import com.android.systemui.touchpad.tutorial.ui.gesture.GestureState.InProgress import kotlin.math.abs /** Recognizes touchpad home gesture, that is - using three fingers on touchpad - swiping up. */ class HomeGestureRecognizer(private val gestureDistanceThresholdPx: Int) : GestureRecognizer { class HomeGestureRecognizer( private val gestureDistanceThresholdPx: Int, private val velocityThresholdPxPerMs: Float, private val velocityTracker: VelocityTracker = VerticalVelocityTracker(), ) : GestureRecognizer { private val distanceTracker = DistanceTracker() private var gestureStateChangedCallback: (GestureState) -> Unit = {} Loading @@ -37,10 +42,14 @@ class HomeGestureRecognizer(private val gestureDistanceThresholdPx: Int) : Gestu override fun accept(event: MotionEvent) { if (!isThreeFingerTouchpadSwipe(event)) return val gestureState = distanceTracker.processEvent(event) velocityTracker.accept(event) updateGestureState( gestureStateChangedCallback, gestureState, isFinished = { -it.deltaY >= gestureDistanceThresholdPx }, isFinished = { -it.deltaY >= gestureDistanceThresholdPx && abs(velocityTracker.calculateVelocity().value) >= velocityThresholdPxPerMs }, progress = { InProgress(MathUtils.saturate(-it.deltaY / gestureDistanceThresholdPx)) }, ) } Loading Loading
packages/SystemUI/multivalentTests/src/com/android/systemui/touchpad/tutorial/ui/gesture/HomeGestureRecognizerTest.kt +21 −1 Original line number Diff line number Diff line Loading @@ -20,10 +20,12 @@ import android.view.MotionEvent import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.android.systemui.SysuiTestCase import com.android.systemui.testKosmos import com.android.systemui.touchpad.tutorial.ui.gesture.GestureState.Finished import com.android.systemui.touchpad.tutorial.ui.gesture.GestureState.InProgress import com.android.systemui.touchpad.tutorial.ui.gesture.GestureState.NotStarted import com.android.systemui.touchpad.tutorial.ui.gesture.MultiFingerGesture.Companion.SWIPE_DISTANCE import com.android.systemui.touchpad.ui.gesture.fakeVelocityTracker import com.google.common.truth.Truth.assertThat import org.junit.Before import org.junit.Test Loading @@ -33,12 +35,24 @@ import org.junit.runner.RunWith @RunWith(AndroidJUnit4::class) class HomeGestureRecognizerTest : SysuiTestCase() { companion object { const val THRESHOLD_VELOCITY_PX_PER_MS = 1f const val SLOW = THRESHOLD_VELOCITY_PX_PER_MS - 0.01f const val FAST = THRESHOLD_VELOCITY_PX_PER_MS + 0.01f } private var gestureState: GestureState = GestureState.NotStarted private var velocityTracker = testKosmos().fakeVelocityTracker private val gestureRecognizer = HomeGestureRecognizer(gestureDistanceThresholdPx = SWIPE_DISTANCE.toInt()) HomeGestureRecognizer( gestureDistanceThresholdPx = SWIPE_DISTANCE.toInt(), velocityThresholdPxPerMs = THRESHOLD_VELOCITY_PX_PER_MS, velocityTracker = velocityTracker, ) @Before fun before() { velocityTracker.setVelocity(Velocity(FAST)) gestureRecognizer.addGestureStateCallback { gestureState = it } } Loading @@ -47,6 +61,12 @@ class HomeGestureRecognizerTest : SysuiTestCase() { assertStateAfterEvents(events = ThreeFingerGesture.swipeUp(), expectedState = Finished) } @Test fun doesntTriggerGestureFinished_onGestureSpeedTooSlow() { velocityTracker.setVelocity(Velocity(SLOW)) assertStateAfterEvents(events = ThreeFingerGesture.swipeUp(), expectedState = NotStarted) } @Test fun triggersGestureProgressForThreeFingerGestureStarted() { assertStateAfterEvents( Loading
packages/SystemUI/multivalentTests/src/com/android/systemui/touchpad/tutorial/ui/gesture/RecentAppsGestureRecognizerTest.kt +9 −14 Original line number Diff line number Diff line Loading @@ -17,21 +17,19 @@ package com.android.systemui.touchpad.tutorial.ui.gesture import android.view.MotionEvent import androidx.compose.ui.input.pointer.util.VelocityTracker1D import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.android.systemui.SysuiTestCase import com.android.systemui.testKosmos import com.android.systemui.touchpad.tutorial.ui.gesture.GestureState.Finished import com.android.systemui.touchpad.tutorial.ui.gesture.GestureState.InProgress import com.android.systemui.touchpad.tutorial.ui.gesture.GestureState.NotStarted import com.android.systemui.touchpad.tutorial.ui.gesture.MultiFingerGesture.Companion.SWIPE_DISTANCE import com.android.systemui.touchpad.ui.gesture.fakeVelocityTracker import com.google.common.truth.Truth.assertThat import org.junit.Before import org.junit.Test import org.junit.runner.RunWith import org.mockito.kotlin.doReturn import org.mockito.kotlin.mock import org.mockito.kotlin.whenever @SmallTest @RunWith(AndroidJUnit4::class) Loading @@ -39,26 +37,23 @@ class RecentAppsGestureRecognizerTest : SysuiTestCase() { companion object { const val THRESHOLD_VELOCITY_PX_PER_MS = 0.1f // multiply by 1000 to get px/ms instead of px/s which is unit used by velocity tracker const val SLOW = THRESHOLD_VELOCITY_PX_PER_MS * 1000 - 1 const val FAST = THRESHOLD_VELOCITY_PX_PER_MS * 1000 + 1 const val SLOW = THRESHOLD_VELOCITY_PX_PER_MS - 0.01f const val FAST = THRESHOLD_VELOCITY_PX_PER_MS + 0.01f } private var velocityTracker = testKosmos().fakeVelocityTracker private var gestureState: GestureState = GestureState.NotStarted private val velocityTracker1D = mock<VelocityTracker1D> { // by default return correct speed for the gesture - as if pointer is slowing down on { calculateVelocity() } doReturn SLOW } private val gestureRecognizer = RecentAppsGestureRecognizer( gestureDistanceThresholdPx = SWIPE_DISTANCE.toInt(), velocityThresholdPxPerMs = THRESHOLD_VELOCITY_PX_PER_MS, velocityTracker = VerticalVelocityTracker(velocityTracker1D), velocityTracker = velocityTracker, ) @Before fun before() { velocityTracker.setVelocity(Velocity(SLOW)) gestureRecognizer.addGestureStateCallback { gestureState = it } } Loading @@ -69,7 +64,7 @@ class RecentAppsGestureRecognizerTest : SysuiTestCase() { @Test fun doesntTriggerGestureFinished_onGestureSpeedTooHigh() { whenever(velocityTracker1D.calculateVelocity()).thenReturn(FAST) velocityTracker.setVelocity(Velocity(FAST)) assertStateAfterEvents(events = ThreeFingerGesture.swipeUp(), expectedState = NotStarted) } Loading
packages/SystemUI/res/values/dimens.xml +5 −1 Original line number Diff line number Diff line Loading @@ -1995,12 +1995,16 @@ <dimen name="backlight_indicator_step_large_radius">28dp</dimen> <!-- Touchpad gestures tutorial--> <!-- This value is in unit of dp/ms <!-- This value is in dp/ms. TriggerSwipeUpTouchTracker (which is base for gesture tutorial implementation) uses value of 0.5dp but from manual testing it's too high and doesn't really feel like it's forcing slowing down. Also for tutorial it should be fine to lean to the side of being more strict rather than not strict enough and not teaching user the proper gesture as a result.--> <dimen name="touchpad_recent_apps_gesture_velocity_threshold">0.05dp</dimen> <!-- This value is in dp/ms. As above, it's not tied to system-wide value (defined in launcher's quickstep_fling_threshold_speed) because for tutorial it's fine to be more strict. --> <dimen name="touchpad_home_gesture_velocity_threshold">0.5dp</dimen> <!-- Normal gesture threshold is system_gestures_distance_threshold but for tutorial we can exaggerate gesture, which also works much better with live tracking --> <dimen name="touchpad_tutorial_gestures_distance_threshold">48dp</dimen> Loading
packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/composable/HomeGestureTutorialScreen.kt +2 −1 Original line number Diff line number Diff line Loading @@ -63,7 +63,8 @@ fun HomeGestureTutorialScreen(onDoneButtonClicked: () -> Unit, onBack: () -> Uni private fun rememberHomeGestureRecognizer(resources: Resources): GestureRecognizer { val distance = resources.getDimensionPixelSize(R.dimen.touchpad_tutorial_gestures_distance_threshold) return remember(distance) { HomeGestureRecognizer(distance) } val velocity = resources.getDimension(R.dimen.touchpad_home_gesture_velocity_threshold) return remember(distance) { HomeGestureRecognizer(distance, velocity) } } @Composable Loading
packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/gesture/HomeGestureRecognizer.kt +11 −2 Original line number Diff line number Diff line Loading @@ -19,9 +19,14 @@ package com.android.systemui.touchpad.tutorial.ui.gesture import android.util.MathUtils import android.view.MotionEvent import com.android.systemui.touchpad.tutorial.ui.gesture.GestureState.InProgress import kotlin.math.abs /** Recognizes touchpad home gesture, that is - using three fingers on touchpad - swiping up. */ class HomeGestureRecognizer(private val gestureDistanceThresholdPx: Int) : GestureRecognizer { class HomeGestureRecognizer( private val gestureDistanceThresholdPx: Int, private val velocityThresholdPxPerMs: Float, private val velocityTracker: VelocityTracker = VerticalVelocityTracker(), ) : GestureRecognizer { private val distanceTracker = DistanceTracker() private var gestureStateChangedCallback: (GestureState) -> Unit = {} Loading @@ -37,10 +42,14 @@ class HomeGestureRecognizer(private val gestureDistanceThresholdPx: Int) : Gestu override fun accept(event: MotionEvent) { if (!isThreeFingerTouchpadSwipe(event)) return val gestureState = distanceTracker.processEvent(event) velocityTracker.accept(event) updateGestureState( gestureStateChangedCallback, gestureState, isFinished = { -it.deltaY >= gestureDistanceThresholdPx }, isFinished = { -it.deltaY >= gestureDistanceThresholdPx && abs(velocityTracker.calculateVelocity().value) >= velocityThresholdPxPerMs }, progress = { InProgress(MathUtils.saturate(-it.deltaY / gestureDistanceThresholdPx)) }, ) } Loading