Merge changes Ia49455ed,Ib0b8d038 into main
* changes:
Simplifying TouchpadGestureMonitor interface
Refactoring recent app gesture recognition logic
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/touchpad/tutorial/ui/gesture/BackGestureMonitorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/touchpad/tutorial/ui/gesture/BackGestureMonitorTest.kt
index ba9fa92..cd18925 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/touchpad/tutorial/ui/gesture/BackGestureMonitorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/touchpad/tutorial/ui/gesture/BackGestureMonitorTest.kt
@@ -25,6 +25,7 @@
import com.android.systemui.touchpad.tutorial.ui.gesture.GestureState.NotStarted
import com.android.systemui.touchpad.tutorial.ui.gesture.MultiFingerGesture.Companion.SWIPE_DISTANCE
import com.google.common.truth.Truth.assertThat
+import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
@@ -34,10 +35,12 @@
private var gestureState: GestureState = NotStarted
private val gestureMonitor =
- BackGestureMonitor(
- gestureDistanceThresholdPx = SWIPE_DISTANCE.toInt(),
- gestureStateChangedCallback = { gestureState = it },
- )
+ BackGestureMonitor(gestureDistanceThresholdPx = SWIPE_DISTANCE.toInt())
+
+ @Before
+ fun before() {
+ gestureMonitor.addGestureStateCallback { gestureState = it }
+ }
@Test
fun triggersGestureFinishedForThreeFingerGestureRight() {
@@ -82,7 +85,7 @@
}
private fun assertStateAfterEvents(events: List<MotionEvent>, expectedState: GestureState) {
- events.forEach { gestureMonitor.processTouchpadEvent(it) }
+ events.forEach { gestureMonitor.accept(it) }
assertThat(gestureState).isEqualTo(expectedState)
}
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/touchpad/tutorial/ui/gesture/EasterEggGestureTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/touchpad/tutorial/ui/gesture/EasterEggGestureTest.kt
index a83ed56..3f1633a 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/touchpad/tutorial/ui/gesture/EasterEggGestureTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/touchpad/tutorial/ui/gesture/EasterEggGestureTest.kt
@@ -36,10 +36,7 @@
private var triggered = false
private val handler =
TouchpadGestureHandler(
- BackGestureMonitor(
- gestureDistanceThresholdPx = SWIPE_DISTANCE.toInt(),
- gestureStateChangedCallback = {},
- ),
+ BackGestureMonitor(gestureDistanceThresholdPx = SWIPE_DISTANCE.toInt()),
EasterEggGestureMonitor(callback = { triggered = true }),
)
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/touchpad/tutorial/ui/gesture/HomeGestureMonitorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/touchpad/tutorial/ui/gesture/HomeGestureMonitorTest.kt
index 59cc026..edf0e56 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/touchpad/tutorial/ui/gesture/HomeGestureMonitorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/touchpad/tutorial/ui/gesture/HomeGestureMonitorTest.kt
@@ -25,6 +25,7 @@
import com.android.systemui.touchpad.tutorial.ui.gesture.GestureState.NotStarted
import com.android.systemui.touchpad.tutorial.ui.gesture.MultiFingerGesture.Companion.SWIPE_DISTANCE
import com.google.common.truth.Truth.assertThat
+import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
@@ -34,10 +35,12 @@
private var gestureState: GestureState = GestureState.NotStarted
private val gestureMonitor =
- HomeGestureMonitor(
- gestureDistanceThresholdPx = SWIPE_DISTANCE.toInt(),
- gestureStateChangedCallback = { gestureState = it },
- )
+ HomeGestureMonitor(gestureDistanceThresholdPx = SWIPE_DISTANCE.toInt())
+
+ @Before
+ fun before() {
+ gestureMonitor.addGestureStateCallback { gestureState = it }
+ }
@Test
fun triggersGestureFinishedForThreeFingerGestureUp() {
@@ -78,7 +81,7 @@
}
private fun assertStateAfterEvents(events: List<MotionEvent>, expectedState: GestureState) {
- events.forEach { gestureMonitor.processTouchpadEvent(it) }
+ events.forEach { gestureMonitor.accept(it) }
assertThat(gestureState).isEqualTo(expectedState)
}
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/touchpad/tutorial/ui/gesture/RecentAppsGestureMonitorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/touchpad/tutorial/ui/gesture/RecentAppsGestureMonitorTest.kt
index 7eac6bb..f68e773 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/touchpad/tutorial/ui/gesture/RecentAppsGestureMonitorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/touchpad/tutorial/ui/gesture/RecentAppsGestureMonitorTest.kt
@@ -26,6 +26,7 @@
import com.android.systemui.touchpad.tutorial.ui.gesture.GestureState.NotStarted
import com.android.systemui.touchpad.tutorial.ui.gesture.MultiFingerGesture.Companion.SWIPE_DISTANCE
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
@@ -44,7 +45,7 @@
}
private var gestureState: GestureState = GestureState.NotStarted
- private val velocityTracker =
+ private val velocityTracker1D =
mock<VelocityTracker1D> {
// by default return correct speed for the gesture - as if pointer is slowing down
on { calculateVelocity() } doReturn SLOW
@@ -52,11 +53,15 @@
private val gestureMonitor =
RecentAppsGestureMonitor(
gestureDistanceThresholdPx = SWIPE_DISTANCE.toInt(),
- gestureStateChangedCallback = { gestureState = it },
velocityThresholdPxPerMs = THRESHOLD_VELOCITY_PX_PER_MS,
- velocityTracker = velocityTracker,
+ velocityTracker = VerticalVelocityTracker(velocityTracker1D),
)
+ @Before
+ fun before() {
+ gestureMonitor.addGestureStateCallback { gestureState = it }
+ }
+
@Test
fun triggersGestureFinishedForThreeFingerGestureUp() {
assertStateAfterEvents(events = ThreeFingerGesture.swipeUp(), expectedState = Finished)
@@ -64,7 +69,7 @@
@Test
fun doesntTriggerGestureFinished_onGestureSpeedTooHigh() {
- whenever(velocityTracker.calculateVelocity()).thenReturn(FAST)
+ whenever(velocityTracker1D.calculateVelocity()).thenReturn(FAST)
assertStateAfterEvents(events = ThreeFingerGesture.swipeUp(), expectedState = NotStarted)
}
@@ -102,7 +107,7 @@
}
private fun assertStateAfterEvents(events: List<MotionEvent>, expectedState: GestureState) {
- events.forEach { gestureMonitor.processTouchpadEvent(it) }
+ events.forEach { gestureMonitor.accept(it) }
assertThat(gestureState).isEqualTo(expectedState)
}
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/touchpad/tutorial/ui/gesture/TouchpadGestureHandlerTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/touchpad/tutorial/ui/gesture/TouchpadGestureHandlerTest.kt
index 4d26366..9f7ea679 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/touchpad/tutorial/ui/gesture/TouchpadGestureHandlerTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/touchpad/tutorial/ui/gesture/TouchpadGestureHandlerTest.kt
@@ -27,6 +27,7 @@
import com.android.systemui.SysuiTestCase
import com.android.systemui.touchpad.tutorial.ui.gesture.MultiFingerGesture.Companion.SWIPE_DISTANCE
import com.google.common.truth.Truth.assertThat
+import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
@@ -35,14 +36,14 @@
class TouchpadGestureHandlerTest : SysuiTestCase() {
private var gestureState: GestureState = GestureState.NotStarted
- private val handler =
- TouchpadGestureHandler(
- BackGestureMonitor(
- gestureDistanceThresholdPx = SWIPE_DISTANCE.toInt(),
- gestureStateChangedCallback = { gestureState = it },
- ),
- EasterEggGestureMonitor {},
- )
+ private val gestureMonitor =
+ BackGestureMonitor(gestureDistanceThresholdPx = SWIPE_DISTANCE.toInt())
+ private val handler = TouchpadGestureHandler(gestureMonitor, EasterEggGestureMonitor {})
+
+ @Before
+ fun before() {
+ gestureMonitor.addGestureStateCallback { gestureState = it }
+ }
@Test
fun handlesEventsFromTouchpad() {
diff --git a/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/composable/BackGestureTutorialScreen.kt b/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/composable/BackGestureTutorialScreen.kt
index bfc5429..6879a34 100644
--- a/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/composable/BackGestureTutorialScreen.kt
+++ b/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/composable/BackGestureTutorialScreen.kt
@@ -27,10 +27,7 @@
import com.android.systemui.touchpad.tutorial.ui.gesture.BackGestureMonitor
@Composable
-fun BackGestureTutorialScreen(
- onDoneButtonClicked: () -> Unit,
- onBack: () -> Unit,
-) {
+fun BackGestureTutorialScreen(onDoneButtonClicked: () -> Unit, onBack: () -> Unit) {
val screenConfig =
TutorialScreenConfig(
colors = rememberScreenColors(),
@@ -39,18 +36,20 @@
titleResId = R.string.touchpad_back_gesture_action_title,
bodyResId = R.string.touchpad_back_gesture_guidance,
titleSuccessResId = R.string.touchpad_back_gesture_success_title,
- bodySuccessResId = R.string.touchpad_back_gesture_success_body
+ bodySuccessResId = R.string.touchpad_back_gesture_success_body,
),
animations =
TutorialScreenConfig.Animations(
educationResId = R.raw.trackpad_back_edu,
- successResId = R.raw.trackpad_back_success
- )
+ successResId = R.raw.trackpad_back_success,
+ ),
)
val gestureMonitorProvider =
DistanceBasedGestureMonitorProvider(
monitorFactory = { distanceThresholdPx, gestureStateCallback ->
- BackGestureMonitor(distanceThresholdPx, gestureStateCallback)
+ BackGestureMonitor(distanceThresholdPx).also {
+ it.addGestureStateCallback(gestureStateCallback)
+ }
}
)
GestureTutorialScreen(screenConfig, gestureMonitorProvider, onDoneButtonClicked, onBack)
@@ -67,7 +66,7 @@
rememberColorFilterProperty(".tertiaryFixedDim", tertiaryFixedDim),
rememberColorFilterProperty(".onTertiaryFixed", onTertiaryFixed),
rememberColorFilterProperty(".onTertiary", onTertiary),
- rememberColorFilterProperty(".onTertiaryFixedVariant", onTertiaryFixedVariant)
+ rememberColorFilterProperty(".onTertiaryFixedVariant", onTertiaryFixedVariant),
)
val screenColors =
remember(dynamicProperties) {
diff --git a/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/composable/HomeGestureTutorialScreen.kt b/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/composable/HomeGestureTutorialScreen.kt
index f2fec5f..a55fa44 100644
--- a/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/composable/HomeGestureTutorialScreen.kt
+++ b/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/composable/HomeGestureTutorialScreen.kt
@@ -26,10 +26,7 @@
import com.android.systemui.touchpad.tutorial.ui.gesture.HomeGestureMonitor
@Composable
-fun HomeGestureTutorialScreen(
- onDoneButtonClicked: () -> Unit,
- onBack: () -> Unit,
-) {
+fun HomeGestureTutorialScreen(onDoneButtonClicked: () -> Unit, onBack: () -> Unit) {
val screenConfig =
TutorialScreenConfig(
colors = rememberScreenColors(),
@@ -38,18 +35,20 @@
titleResId = R.string.touchpad_home_gesture_action_title,
bodyResId = R.string.touchpad_home_gesture_guidance,
titleSuccessResId = R.string.touchpad_home_gesture_success_title,
- bodySuccessResId = R.string.touchpad_home_gesture_success_body
+ bodySuccessResId = R.string.touchpad_home_gesture_success_body,
),
animations =
TutorialScreenConfig.Animations(
educationResId = R.raw.trackpad_home_edu,
- successResId = R.raw.trackpad_home_success
- )
+ successResId = R.raw.trackpad_home_success,
+ ),
)
val gestureMonitorProvider =
DistanceBasedGestureMonitorProvider(
monitorFactory = { distanceThresholdPx, gestureStateCallback ->
- HomeGestureMonitor(distanceThresholdPx, gestureStateCallback)
+ HomeGestureMonitor(distanceThresholdPx).also {
+ it.addGestureStateCallback(gestureStateCallback)
+ }
}
)
GestureTutorialScreen(screenConfig, gestureMonitorProvider, onDoneButtonClicked, onBack)
@@ -64,7 +63,7 @@
rememberLottieDynamicProperties(
rememberColorFilterProperty(".primaryFixedDim", primaryFixedDim),
rememberColorFilterProperty(".onPrimaryFixed", onPrimaryFixed),
- rememberColorFilterProperty(".onPrimaryFixedVariant", onPrimaryFixedVariant)
+ rememberColorFilterProperty(".onPrimaryFixedVariant", onPrimaryFixedVariant),
)
val screenColors =
remember(dynamicProperties) {
diff --git a/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/composable/RecentAppsGestureTutorialScreen.kt b/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/composable/RecentAppsGestureTutorialScreen.kt
index b2fb6cd..6ee15aa 100644
--- a/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/composable/RecentAppsGestureTutorialScreen.kt
+++ b/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/composable/RecentAppsGestureTutorialScreen.kt
@@ -29,10 +29,7 @@
import com.android.systemui.touchpad.tutorial.ui.gesture.TouchpadGestureMonitor
@Composable
-fun RecentAppsGestureTutorialScreen(
- onDoneButtonClicked: () -> Unit,
- onBack: () -> Unit,
-) {
+fun RecentAppsGestureTutorialScreen(onDoneButtonClicked: () -> Unit, onBack: () -> Unit) {
val screenConfig =
TutorialScreenConfig(
colors = rememberScreenColors(),
@@ -41,20 +38,20 @@
titleResId = R.string.touchpad_recent_apps_gesture_action_title,
bodyResId = R.string.touchpad_recent_apps_gesture_guidance,
titleSuccessResId = R.string.touchpad_recent_apps_gesture_success_title,
- bodySuccessResId = R.string.touchpad_recent_apps_gesture_success_body
+ bodySuccessResId = R.string.touchpad_recent_apps_gesture_success_body,
),
animations =
TutorialScreenConfig.Animations(
educationResId = R.raw.trackpad_recent_apps_edu,
- successResId = R.raw.trackpad_recent_apps_success
- )
+ successResId = R.raw.trackpad_recent_apps_success,
+ ),
)
val gestureMonitorProvider =
object : GestureMonitorProvider {
@Composable
override fun rememberGestureMonitor(
resources: Resources,
- gestureStateChangedCallback: (GestureState) -> Unit
+ gestureStateChangedCallback: (GestureState) -> Unit,
): TouchpadGestureMonitor {
val distanceThresholdPx =
resources.getDimensionPixelSize(
@@ -63,11 +60,9 @@
val velocityThresholdPxPerMs =
resources.getDimension(R.dimen.touchpad_recent_apps_gesture_velocity_threshold)
return remember(distanceThresholdPx, velocityThresholdPxPerMs) {
- RecentAppsGestureMonitor(
- distanceThresholdPx,
- gestureStateChangedCallback,
- velocityThresholdPxPerMs
- )
+ RecentAppsGestureMonitor(distanceThresholdPx, velocityThresholdPxPerMs).also {
+ it.addGestureStateCallback(gestureStateChangedCallback)
+ }
}
}
}
@@ -83,7 +78,7 @@
rememberLottieDynamicProperties(
rememberColorFilterProperty(".secondaryFixedDim", secondaryFixedDim),
rememberColorFilterProperty(".onSecondaryFixed", onSecondaryFixed),
- rememberColorFilterProperty(".onSecondaryFixedVariant", onSecondaryFixedVariant)
+ rememberColorFilterProperty(".onSecondaryFixedVariant", onSecondaryFixedVariant),
)
val screenColors =
remember(dynamicProperties) {
diff --git a/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/gesture/BackGestureMonitor.kt b/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/gesture/BackGestureMonitor.kt
index ecb5574..490f04d 100644
--- a/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/gesture/BackGestureMonitor.kt
+++ b/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/gesture/BackGestureMonitor.kt
@@ -20,18 +20,21 @@
import kotlin.math.abs
/** Monitors for touchpad back gesture, that is three fingers swiping left or right */
-class BackGestureMonitor(
- private val gestureDistanceThresholdPx: Int,
- override val gestureStateChangedCallback: (GestureState) -> Unit,
-) : TouchpadGestureMonitor {
- private val distanceTracker = DistanceTracker()
+class BackGestureMonitor(private val gestureDistanceThresholdPx: Int) : TouchpadGestureMonitor {
- override fun processTouchpadEvent(event: MotionEvent) {
+ private val distanceTracker = DistanceTracker()
+ private var gestureStateChangedCallback: (GestureState) -> Unit = {}
+
+ override fun addGestureStateCallback(callback: (GestureState) -> Unit) {
+ gestureStateChangedCallback = callback
+ }
+
+ override fun accept(event: MotionEvent) {
if (!isThreeFingerTouchpadSwipe(event)) return
- val distanceState = distanceTracker.processEvent(event)
- updateGestureStateBasedOnDistance(
+ val gestureState = distanceTracker.processEvent(event)
+ updateGestureState(
gestureStateChangedCallback,
- distanceState,
+ gestureState,
isFinished = { abs(it.deltaX) >= gestureDistanceThresholdPx },
progress = { 0f },
)
diff --git a/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/gesture/DistanceTracker.kt b/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/gesture/DistanceTracker.kt
index 70d9366..d482358 100644
--- a/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/gesture/DistanceTracker.kt
+++ b/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/gesture/DistanceTracker.kt
@@ -38,10 +38,10 @@
}
}
-sealed interface DistanceGestureState
+sealed class DistanceGestureState(val deltaX: Float, val deltaY: Float)
-class Started(val deltaX: Float, val deltaY: Float) : DistanceGestureState
+class Started(deltaX: Float, deltaY: Float) : DistanceGestureState(deltaX, deltaY)
-class Moving(val deltaX: Float, val deltaY: Float) : DistanceGestureState
+class Moving(deltaX: Float, deltaY: Float) : DistanceGestureState(deltaX, deltaY)
-class Finished(val deltaX: Float, val deltaY: Float) : DistanceGestureState
+class Finished(deltaX: Float, deltaY: Float) : DistanceGestureState(deltaX, deltaY)
diff --git a/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/gesture/GestureStateUpdates.kt b/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/gesture/GestureStateUpdates.kt
index c1caeb3..f194677 100644
--- a/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/gesture/GestureStateUpdates.kt
+++ b/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/gesture/GestureStateUpdates.kt
@@ -16,11 +16,8 @@
package com.android.systemui.touchpad.tutorial.ui.gesture
-/**
- * Helper function for gesture recognizers to have common state triggering logic based on distance
- * only.
- */
-inline fun updateGestureStateBasedOnDistance(
+/** Helper function for gesture recognizers to have common state triggering logic */
+inline fun updateGestureState(
gestureStateChangedCallback: (GestureState) -> Unit,
gestureState: DistanceGestureState?,
isFinished: (Finished) -> Boolean,
diff --git a/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/gesture/HomeGestureMonitor.kt b/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/gesture/HomeGestureMonitor.kt
index fdcf9de..83d4f56 100644
--- a/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/gesture/HomeGestureMonitor.kt
+++ b/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/gesture/HomeGestureMonitor.kt
@@ -19,18 +19,21 @@
import android.view.MotionEvent
/** Monitors for touchpad home gesture, that is three fingers swiping up */
-class HomeGestureMonitor(
- private val gestureDistanceThresholdPx: Int,
- override val gestureStateChangedCallback: (GestureState) -> Unit,
-) : TouchpadGestureMonitor {
- private val distanceTracker = DistanceTracker()
+class HomeGestureMonitor(private val gestureDistanceThresholdPx: Int) : TouchpadGestureMonitor {
- override fun processTouchpadEvent(event: MotionEvent) {
+ private val distanceTracker = DistanceTracker()
+ private var gestureStateChangedCallback: (GestureState) -> Unit = {}
+
+ override fun addGestureStateCallback(callback: (GestureState) -> Unit) {
+ gestureStateChangedCallback = callback
+ }
+
+ override fun accept(event: MotionEvent) {
if (!isThreeFingerTouchpadSwipe(event)) return
- val distanceState = distanceTracker.processEvent(event)
- updateGestureStateBasedOnDistance(
+ val gestureState = distanceTracker.processEvent(event)
+ updateGestureState(
gestureStateChangedCallback,
- distanceState,
+ gestureState,
isFinished = { -it.deltaY >= gestureDistanceThresholdPx },
progress = { 0f },
)
diff --git a/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/gesture/RecentAppsGestureMonitor.kt b/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/gesture/RecentAppsGestureMonitor.kt
index dd31ce3..1731bb8 100644
--- a/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/gesture/RecentAppsGestureMonitor.kt
+++ b/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/gesture/RecentAppsGestureMonitor.kt
@@ -17,7 +17,6 @@
package com.android.systemui.touchpad.tutorial.ui.gesture
import android.view.MotionEvent
-import androidx.compose.ui.input.pointer.util.VelocityTracker1D
import kotlin.math.abs
/**
@@ -27,45 +26,30 @@
*/
class RecentAppsGestureMonitor(
private val gestureDistanceThresholdPx: Int,
- override val gestureStateChangedCallback: (GestureState) -> Unit,
private val velocityThresholdPxPerMs: Float,
- private val velocityTracker: VelocityTracker1D = VelocityTracker1D(isDataDifferential = false),
+ private val distanceTracker: DistanceTracker = DistanceTracker(),
+ private val velocityTracker: VerticalVelocityTracker = VerticalVelocityTracker(),
) : TouchpadGestureMonitor {
- private var xStart = 0f
- private var yStart = 0f
+ private var gestureStateChangedCallback: (GestureState) -> Unit = {}
- override fun processTouchpadEvent(event: MotionEvent) {
- val action = event.actionMasked
- velocityTracker.addDataPoint(event.eventTime, event.y)
- when (action) {
- MotionEvent.ACTION_DOWN -> {
- if (isThreeFingerTouchpadSwipe(event)) {
- xStart = event.x
- yStart = event.y
- gestureStateChangedCallback(GestureState.InProgress())
- }
- }
- MotionEvent.ACTION_UP -> {
- if (isThreeFingerTouchpadSwipe(event) && isRecentAppsGesture(event)) {
- gestureStateChangedCallback(GestureState.Finished)
- } else {
- gestureStateChangedCallback(GestureState.NotStarted)
- }
- velocityTracker.resetTracking()
- }
- MotionEvent.ACTION_CANCEL -> {
- velocityTracker.resetTracking()
- }
- }
+ override fun addGestureStateCallback(callback: (GestureState) -> Unit) {
+ gestureStateChangedCallback = callback
}
- private fun isRecentAppsGesture(event: MotionEvent): Boolean {
- // below is trying to mirror behavior of TriggerSwipeUpTouchTracker#onGestureEnd.
- // We're diving velocity by 1000, to have the same unit of measure: pixels/ms.
- val swipeDistance = yStart - event.y
- val velocity = velocityTracker.calculateVelocity() / 1000
- return swipeDistance >= gestureDistanceThresholdPx &&
- abs(velocity) <= velocityThresholdPxPerMs
+ override fun accept(event: MotionEvent) {
+ if (!isThreeFingerTouchpadSwipe(event)) return
+ val gestureState = distanceTracker.processEvent(event)
+ velocityTracker.accept(event)
+
+ updateGestureState(
+ gestureStateChangedCallback,
+ gestureState,
+ isFinished = { state ->
+ -state.deltaY >= gestureDistanceThresholdPx &&
+ abs(velocityTracker.calculateVelocity().value) <= velocityThresholdPxPerMs
+ },
+ progress = { 0f },
+ )
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/gesture/TouchpadGestureHandler.kt b/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/gesture/TouchpadGestureHandler.kt
index 88671d4..4b82ba1 100644
--- a/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/gesture/TouchpadGestureHandler.kt
+++ b/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/gesture/TouchpadGestureHandler.kt
@@ -18,13 +18,14 @@
import android.view.InputDevice
import android.view.MotionEvent
+import java.util.function.Consumer
/**
* Allows listening to touchpadGesture and calling onDone when gesture was triggered. Can have all
* motion events passed to [onMotionEvent] and will filter touchpad events accordingly
*/
class TouchpadGestureHandler(
- private val gestureMonitor: TouchpadGestureMonitor,
+ private val gestureMonitor: Consumer<MotionEvent>,
private val easterEggGestureMonitor: EasterEggGestureMonitor,
) {
@@ -40,7 +41,7 @@
if (isTwoFingerSwipe(event)) {
easterEggGestureMonitor.processTouchpadEvent(event)
} else {
- gestureMonitor.processTouchpadEvent(event)
+ gestureMonitor.accept(event)
}
true
} else {
diff --git a/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/gesture/TouchpadGestureMonitor.kt b/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/gesture/TouchpadGestureMonitor.kt
index 4655c98..9216821 100644
--- a/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/gesture/TouchpadGestureMonitor.kt
+++ b/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/gesture/TouchpadGestureMonitor.kt
@@ -17,15 +17,11 @@
package com.android.systemui.touchpad.tutorial.ui.gesture
import android.view.MotionEvent
+import java.util.function.Consumer
-/**
- * Monitor for touchpad gestures that calls [gestureStateChangedCallback] when [GestureState]
- * changes. All tracked motion events should be passed to [processTouchpadEvent]
- */
-interface TouchpadGestureMonitor {
- val gestureStateChangedCallback: (GestureState) -> Unit
-
- fun processTouchpadEvent(event: MotionEvent)
+/** Monitor for touchpad gestures that can notify callback when [GestureState] changes. */
+interface TouchpadGestureMonitor : Consumer<MotionEvent> {
+ fun addGestureStateCallback(callback: (GestureState) -> Unit)
}
fun isThreeFingerTouchpadSwipe(event: MotionEvent) = isNFingerTouchpadSwipe(event, fingerCount = 3)
diff --git a/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/gesture/VelocityTracker.kt b/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/gesture/VelocityTracker.kt
new file mode 100644
index 0000000..9b38eca
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/gesture/VelocityTracker.kt
@@ -0,0 +1,51 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.touchpad.tutorial.ui.gesture
+
+import android.view.MotionEvent
+import androidx.compose.ui.input.pointer.util.VelocityTracker1D
+import java.util.function.Consumer
+
+/** Velocity in pixels/ms. */
+@JvmInline value class Velocity(val value: Float)
+
+/**
+ * Tracks velocity for processed MotionEvents. Useful for recognizing gestures based on velocity.
+ */
+interface VelocityTracker : Consumer<MotionEvent> {
+
+ fun calculateVelocity(): Velocity
+}
+
+class VerticalVelocityTracker(
+ private val velocityTracker: VelocityTracker1D = VelocityTracker1D(isDataDifferential = false)
+) : VelocityTracker {
+
+ override fun accept(event: MotionEvent) {
+ val action = event.actionMasked
+ if (action == MotionEvent.ACTION_DOWN) {
+ velocityTracker.resetTracking()
+ }
+ velocityTracker.addDataPoint(event.eventTime, event.y)
+ }
+
+ /**
+ * Calculates velocity on demand - this calculation can be expensive so shouldn't be called
+ * after every event.
+ */
+ override fun calculateVelocity() = Velocity(velocityTracker.calculateVelocity() / 1000)
+}