Loading packages/SystemUI/multivalentTests/src/com/android/systemui/touchpad/tutorial/ui/gesture/BackGestureRecognizerTest.kt +20 −0 Original line number Diff line number Diff line Loading @@ -60,6 +60,26 @@ class BackGestureRecognizerTest : SysuiTestCase() { ) } @Test fun triggersProgressRelativeToDistance() { assertProgressWhileMovingFingers(deltaX = -SWIPE_DISTANCE / 2, expectedProgress = 0.5f) assertProgressWhileMovingFingers(deltaX = SWIPE_DISTANCE / 2, expectedProgress = 0.5f) assertProgressWhileMovingFingers(deltaX = -SWIPE_DISTANCE, expectedProgress = 1f) assertProgressWhileMovingFingers(deltaX = SWIPE_DISTANCE, expectedProgress = 1f) } private fun assertProgressWhileMovingFingers(deltaX: Float, expectedProgress: Float) { assertStateAfterEvents( events = ThreeFingerGesture.eventsForGestureInProgress { move(deltaX = deltaX) }, expectedState = InProgress(progress = expectedProgress), ) } @Test fun triggeredProgressIsNoBiggerThanOne() { assertProgressWhileMovingFingers(deltaX = SWIPE_DISTANCE * 2, expectedProgress = 1f) } @Test fun doesntTriggerGestureFinished_onGestureDistanceTooShort() { assertStateAfterEvents( Loading packages/SystemUI/multivalentTests/src/com/android/systemui/touchpad/tutorial/ui/gesture/EasterEggGestureTest.kt +2 −1 Original line number Diff line number Diff line Loading @@ -104,7 +104,8 @@ class EasterEggGestureTest : SysuiTestCase() { } private fun assertStateAfterTwoFingerGesture(gesturePath: List<Point>, wasTriggered: Boolean) { val events = TwoFingerGesture.createEvents { gesturePath.forEach { (x, y) -> move(x, y) } } val events = TwoFingerGesture.eventsForFullGesture { gesturePath.forEach { (x, y) -> move(x, y) } } assertStateAfterEvents(events = events, wasTriggered = wasTriggered) } Loading packages/SystemUI/multivalentTests/src/com/android/systemui/touchpad/tutorial/ui/gesture/HomeGestureRecognizerTest.kt +21 −0 Original line number Diff line number Diff line Loading @@ -55,6 +55,27 @@ class HomeGestureRecognizerTest : SysuiTestCase() { ) } @Test fun triggersProgressRelativeToDistance() { assertProgressWhileMovingFingers(deltaY = -SWIPE_DISTANCE / 2, expectedProgress = 0.5f) assertProgressWhileMovingFingers(deltaY = -SWIPE_DISTANCE, expectedProgress = 1f) } private fun assertProgressWhileMovingFingers(deltaY: Float, expectedProgress: Float) { assertStateAfterEvents( events = ThreeFingerGesture.eventsForGestureInProgress { move(deltaY = deltaY) }, expectedState = InProgress(progress = expectedProgress), ) } @Test fun triggeredProgressIsBetweenZeroAndOne() { // going in the wrong direction assertProgressWhileMovingFingers(deltaY = SWIPE_DISTANCE / 2, expectedProgress = 0f) // going further than required distance assertProgressWhileMovingFingers(deltaY = -SWIPE_DISTANCE * 2, expectedProgress = 1f) } @Test fun doesntTriggerGestureFinished_onGestureDistanceTooShort() { assertStateAfterEvents( Loading packages/SystemUI/multivalentTests/src/com/android/systemui/touchpad/tutorial/ui/gesture/RecentAppsGestureRecognizerTest.kt +22 −1 Original line number Diff line number Diff line Loading @@ -77,10 +77,31 @@ class RecentAppsGestureRecognizerTest : SysuiTestCase() { fun triggersGestureProgressForThreeFingerGestureStarted() { assertStateAfterEvents( events = ThreeFingerGesture.startEvents(x = 0f, y = 0f), expectedState = InProgress(), expectedState = InProgress(progress = 0f), ) } @Test fun triggersProgressRelativeToDistance() { assertProgressWhileMovingFingers(deltaY = -SWIPE_DISTANCE / 2, expectedProgress = 0.5f) assertProgressWhileMovingFingers(deltaY = -SWIPE_DISTANCE, expectedProgress = 1f) } private fun assertProgressWhileMovingFingers(deltaY: Float, expectedProgress: Float) { assertStateAfterEvents( events = ThreeFingerGesture.eventsForGestureInProgress { move(deltaY = deltaY) }, expectedState = InProgress(progress = expectedProgress), ) } @Test fun triggeredProgressIsBetweenZeroAndOne() { // going in the wrong direction assertProgressWhileMovingFingers(deltaY = SWIPE_DISTANCE / 2, expectedProgress = 0f) // going further than required distance assertProgressWhileMovingFingers(deltaY = -SWIPE_DISTANCE * 2, expectedProgress = 1f) } @Test fun doesntTriggerGestureFinished_onGestureDistanceTooShort() { assertStateAfterEvents( Loading packages/SystemUI/multivalentTests/src/com/android/systemui/touchpad/tutorial/ui/gesture/TouchpadGestureBuilder.kt +99 −41 Original line number Diff line number Diff line Loading @@ -25,11 +25,23 @@ import android.view.MotionEvent.ACTION_UP import com.android.systemui.touchpad.tutorial.ui.gesture.MultiFingerGesture.Companion.DEFAULT_X import com.android.systemui.touchpad.tutorial.ui.gesture.MultiFingerGesture.Companion.DEFAULT_Y /** Given gesture move events can build list of [MotionEvent]s included in that gesture */ interface GestureEventsBuilder { /** * Interface for gesture builders which support creating list of [MotionEvent] for common swipe * gestures. For simple usage see swipe* methods or use [createEvents] for more specific scenarios. * Creates full gesture including provided move events. This means returned events include DOWN, * MOVE and UP. Note that move event's x and y is always relative to the starting one. */ interface MultiFingerGesture { fun eventsForFullGesture(moveEvents: MoveEventsBuilder.() -> Unit): List<MotionEvent> /** * Creates partial gesture including provided move events. This means returned events include * DOWN and MOVE. Note that move event's x and y is always relative to the starting one. */ fun eventsForGestureInProgress(moveEvents: MoveEventsBuilder.() -> Unit): List<MotionEvent> } /** Support creating list of [MotionEvent] for common swipe gestures. */ interface MultiFingerGesture : GestureEventsBuilder { companion object { const val SWIPE_DISTANCE = 100f Loading @@ -37,27 +49,41 @@ interface MultiFingerGesture { const val DEFAULT_Y = 500f } fun swipeUp(distancePx: Float = SWIPE_DISTANCE) = createEvents { move(deltaY = -distancePx) } fun swipeDown(distancePx: Float = SWIPE_DISTANCE) = createEvents { move(deltaY = distancePx) } fun swipeUp(distancePx: Float = SWIPE_DISTANCE) = eventsForFullGesture { move(deltaY = -distancePx) } fun swipeRight(distancePx: Float = SWIPE_DISTANCE) = createEvents { move(deltaX = distancePx) } fun swipeDown(distancePx: Float = SWIPE_DISTANCE) = eventsForFullGesture { move(deltaY = distancePx) } fun swipeLeft(distancePx: Float = SWIPE_DISTANCE) = createEvents { move(deltaX = -distancePx) } fun swipeRight(distancePx: Float = SWIPE_DISTANCE) = eventsForFullGesture { move(deltaX = distancePx) } /** * Creates gesture with provided move events. Note that move event's x and y is always relative * to the starting one */ fun createEvents(moveEvents: GestureBuilder.() -> Unit): List<MotionEvent> fun swipeLeft(distancePx: Float = SWIPE_DISTANCE) = eventsForFullGesture { move(deltaX = -distancePx) } } object ThreeFingerGesture : MultiFingerGesture { override fun createEvents(moveEvents: GestureBuilder.() -> Unit): List<MotionEvent> { return touchpadGesture( private val moveEventsBuilder = MoveEventsBuilder(::threeFingerEvent) override fun eventsForFullGesture(moveEvents: MoveEventsBuilder.() -> Unit): List<MotionEvent> { return buildGesture( startEvents = { x, y -> startEvents(x, y) }, moveEvents = moveEventsBuilder.getEvents(moveEvents), endEvents = { x, y -> endEvents(x, y) }, ) } override fun eventsForGestureInProgress( moveEvents: MoveEventsBuilder.() -> Unit ): List<MotionEvent> { return buildGesture( startEvents = { x, y -> startEvents(x, y) }, moveEvents = GestureBuilder(::threeFingerEvent).apply { moveEvents() }.events, endEvents = { x, y -> endEvents(x, y) } moveEvents = moveEventsBuilder.getEvents(moveEvents), ) } Loading @@ -65,7 +91,7 @@ object ThreeFingerGesture : MultiFingerGesture { return listOf( threeFingerEvent(ACTION_DOWN, x, y), threeFingerEvent(ACTION_POINTER_DOWN, x, y), threeFingerEvent(ACTION_POINTER_DOWN, x, y) threeFingerEvent(ACTION_POINTER_DOWN, x, y), ) } Loading @@ -73,32 +99,43 @@ object ThreeFingerGesture : MultiFingerGesture { return listOf( threeFingerEvent(ACTION_POINTER_UP, x, y), threeFingerEvent(ACTION_POINTER_UP, x, y), threeFingerEvent(ACTION_UP, x, y) threeFingerEvent(ACTION_UP, x, y), ) } private fun threeFingerEvent( action: Int, x: Float = DEFAULT_X, y: Float = DEFAULT_Y y: Float = DEFAULT_Y, ): MotionEvent { return touchpadEvent( action = action, x = x, y = y, classification = MotionEvent.CLASSIFICATION_MULTI_FINGER_SWIPE, axisValues = mapOf(MotionEvent.AXIS_GESTURE_SWIPE_FINGER_COUNT to 3f) axisValues = mapOf(MotionEvent.AXIS_GESTURE_SWIPE_FINGER_COUNT to 3f), ) } } object FourFingerGesture : MultiFingerGesture { override fun createEvents(moveEvents: GestureBuilder.() -> Unit): List<MotionEvent> { return touchpadGesture( private val moveEventsBuilder = MoveEventsBuilder(::fourFingerEvent) override fun eventsForFullGesture(moveEvents: MoveEventsBuilder.() -> Unit): List<MotionEvent> { return buildGesture( startEvents = { x, y -> startEvents(x, y) }, moveEvents = moveEventsBuilder.getEvents(moveEvents), endEvents = { x, y -> endEvents(x, y) }, ) } override fun eventsForGestureInProgress( moveEvents: MoveEventsBuilder.() -> Unit ): List<MotionEvent> { return buildGesture( startEvents = { x, y -> startEvents(x, y) }, moveEvents = GestureBuilder(::fourFingerEvent).apply { moveEvents() }.events, endEvents = { x, y -> endEvents(x, y) } moveEvents = moveEventsBuilder.getEvents(moveEvents), ) } Loading @@ -107,7 +144,7 @@ object FourFingerGesture : MultiFingerGesture { fourFingerEvent(ACTION_DOWN, x, y), fourFingerEvent(ACTION_POINTER_DOWN, x, y), fourFingerEvent(ACTION_POINTER_DOWN, x, y), fourFingerEvent(ACTION_POINTER_DOWN, x, y) fourFingerEvent(ACTION_POINTER_DOWN, x, y), ) } Loading @@ -116,61 +153,74 @@ object FourFingerGesture : MultiFingerGesture { fourFingerEvent(ACTION_POINTER_UP, x, y), fourFingerEvent(ACTION_POINTER_UP, x, y), fourFingerEvent(ACTION_POINTER_UP, x, y), fourFingerEvent(ACTION_UP, x, y) fourFingerEvent(ACTION_UP, x, y), ) } private fun fourFingerEvent( action: Int, x: Float = DEFAULT_X, y: Float = DEFAULT_Y y: Float = DEFAULT_Y, ): MotionEvent { return touchpadEvent( action = action, x = x, y = y, classification = MotionEvent.CLASSIFICATION_MULTI_FINGER_SWIPE, axisValues = mapOf(MotionEvent.AXIS_GESTURE_SWIPE_FINGER_COUNT to 4f) axisValues = mapOf(MotionEvent.AXIS_GESTURE_SWIPE_FINGER_COUNT to 4f), ) } } object TwoFingerGesture : MultiFingerGesture { override fun createEvents(moveEvents: GestureBuilder.() -> Unit): List<MotionEvent> { return touchpadGesture( startEvents = { x, y -> listOf(twoFingerEvent(ACTION_DOWN, x, y)) }, moveEvents = GestureBuilder(::twoFingerEvent).apply { moveEvents() }.events, endEvents = { x, y -> listOf(twoFingerEvent(ACTION_UP, x, y)) } private val moveEventsBuilder = MoveEventsBuilder(::twoFingerEvent) override fun eventsForFullGesture(moveEvents: MoveEventsBuilder.() -> Unit): List<MotionEvent> { return buildGesture( startEvents = { x, y -> startEvents(x, y) }, moveEvents = moveEventsBuilder.getEvents(moveEvents), endEvents = { x, y -> listOf(twoFingerEvent(ACTION_UP, x, y)) }, ) } override fun eventsForGestureInProgress( moveEvents: MoveEventsBuilder.() -> Unit ): List<MotionEvent> { return buildGesture( startEvents = { x, y -> startEvents(x, y) }, moveEvents = moveEventsBuilder.getEvents(moveEvents), ) } private fun startEvents(x: Float, y: Float) = listOf(twoFingerEvent(ACTION_DOWN, x, y)) private fun twoFingerEvent( action: Int, x: Float = DEFAULT_X, y: Float = DEFAULT_Y y: Float = DEFAULT_Y, ): MotionEvent { return touchpadEvent( action = action, x = x, y = y, classification = MotionEvent.CLASSIFICATION_TWO_FINGER_SWIPE, axisValues = mapOf(MotionEvent.AXIS_GESTURE_SWIPE_FINGER_COUNT to 2f) axisValues = mapOf(MotionEvent.AXIS_GESTURE_SWIPE_FINGER_COUNT to 2f), ) } } private fun touchpadGesture( private fun buildGesture( startEvents: (Float, Float) -> List<MotionEvent>, moveEvents: List<MotionEvent>, endEvents: (Float, Float) -> List<MotionEvent> endEvents: (Float, Float) -> List<MotionEvent> = { _, _ -> emptyList() }, ): List<MotionEvent> { val lastX = moveEvents.last().x val lastY = moveEvents.last().y return startEvents(DEFAULT_X, DEFAULT_Y) + moveEvents + endEvents(lastX, lastY) } class GestureBuilder internal constructor(val eventBuilder: (Int, Float, Float) -> MotionEvent) { class MoveEventsBuilder internal constructor(val eventBuilder: (Int, Float, Float) -> MotionEvent) { val events = mutableListOf<MotionEvent>() Loading @@ -178,3 +228,11 @@ class GestureBuilder internal constructor(val eventBuilder: (Int, Float, Float) events.add(eventBuilder(ACTION_MOVE, DEFAULT_X + deltaX, DEFAULT_Y + deltaY)) } } private fun MoveEventsBuilder.getEvents( moveEvents: MoveEventsBuilder.() -> Unit ): List<MotionEvent> { events.clear() this.moveEvents() return events } Loading
packages/SystemUI/multivalentTests/src/com/android/systemui/touchpad/tutorial/ui/gesture/BackGestureRecognizerTest.kt +20 −0 Original line number Diff line number Diff line Loading @@ -60,6 +60,26 @@ class BackGestureRecognizerTest : SysuiTestCase() { ) } @Test fun triggersProgressRelativeToDistance() { assertProgressWhileMovingFingers(deltaX = -SWIPE_DISTANCE / 2, expectedProgress = 0.5f) assertProgressWhileMovingFingers(deltaX = SWIPE_DISTANCE / 2, expectedProgress = 0.5f) assertProgressWhileMovingFingers(deltaX = -SWIPE_DISTANCE, expectedProgress = 1f) assertProgressWhileMovingFingers(deltaX = SWIPE_DISTANCE, expectedProgress = 1f) } private fun assertProgressWhileMovingFingers(deltaX: Float, expectedProgress: Float) { assertStateAfterEvents( events = ThreeFingerGesture.eventsForGestureInProgress { move(deltaX = deltaX) }, expectedState = InProgress(progress = expectedProgress), ) } @Test fun triggeredProgressIsNoBiggerThanOne() { assertProgressWhileMovingFingers(deltaX = SWIPE_DISTANCE * 2, expectedProgress = 1f) } @Test fun doesntTriggerGestureFinished_onGestureDistanceTooShort() { assertStateAfterEvents( Loading
packages/SystemUI/multivalentTests/src/com/android/systemui/touchpad/tutorial/ui/gesture/EasterEggGestureTest.kt +2 −1 Original line number Diff line number Diff line Loading @@ -104,7 +104,8 @@ class EasterEggGestureTest : SysuiTestCase() { } private fun assertStateAfterTwoFingerGesture(gesturePath: List<Point>, wasTriggered: Boolean) { val events = TwoFingerGesture.createEvents { gesturePath.forEach { (x, y) -> move(x, y) } } val events = TwoFingerGesture.eventsForFullGesture { gesturePath.forEach { (x, y) -> move(x, y) } } assertStateAfterEvents(events = events, wasTriggered = wasTriggered) } Loading
packages/SystemUI/multivalentTests/src/com/android/systemui/touchpad/tutorial/ui/gesture/HomeGestureRecognizerTest.kt +21 −0 Original line number Diff line number Diff line Loading @@ -55,6 +55,27 @@ class HomeGestureRecognizerTest : SysuiTestCase() { ) } @Test fun triggersProgressRelativeToDistance() { assertProgressWhileMovingFingers(deltaY = -SWIPE_DISTANCE / 2, expectedProgress = 0.5f) assertProgressWhileMovingFingers(deltaY = -SWIPE_DISTANCE, expectedProgress = 1f) } private fun assertProgressWhileMovingFingers(deltaY: Float, expectedProgress: Float) { assertStateAfterEvents( events = ThreeFingerGesture.eventsForGestureInProgress { move(deltaY = deltaY) }, expectedState = InProgress(progress = expectedProgress), ) } @Test fun triggeredProgressIsBetweenZeroAndOne() { // going in the wrong direction assertProgressWhileMovingFingers(deltaY = SWIPE_DISTANCE / 2, expectedProgress = 0f) // going further than required distance assertProgressWhileMovingFingers(deltaY = -SWIPE_DISTANCE * 2, expectedProgress = 1f) } @Test fun doesntTriggerGestureFinished_onGestureDistanceTooShort() { assertStateAfterEvents( Loading
packages/SystemUI/multivalentTests/src/com/android/systemui/touchpad/tutorial/ui/gesture/RecentAppsGestureRecognizerTest.kt +22 −1 Original line number Diff line number Diff line Loading @@ -77,10 +77,31 @@ class RecentAppsGestureRecognizerTest : SysuiTestCase() { fun triggersGestureProgressForThreeFingerGestureStarted() { assertStateAfterEvents( events = ThreeFingerGesture.startEvents(x = 0f, y = 0f), expectedState = InProgress(), expectedState = InProgress(progress = 0f), ) } @Test fun triggersProgressRelativeToDistance() { assertProgressWhileMovingFingers(deltaY = -SWIPE_DISTANCE / 2, expectedProgress = 0.5f) assertProgressWhileMovingFingers(deltaY = -SWIPE_DISTANCE, expectedProgress = 1f) } private fun assertProgressWhileMovingFingers(deltaY: Float, expectedProgress: Float) { assertStateAfterEvents( events = ThreeFingerGesture.eventsForGestureInProgress { move(deltaY = deltaY) }, expectedState = InProgress(progress = expectedProgress), ) } @Test fun triggeredProgressIsBetweenZeroAndOne() { // going in the wrong direction assertProgressWhileMovingFingers(deltaY = SWIPE_DISTANCE / 2, expectedProgress = 0f) // going further than required distance assertProgressWhileMovingFingers(deltaY = -SWIPE_DISTANCE * 2, expectedProgress = 1f) } @Test fun doesntTriggerGestureFinished_onGestureDistanceTooShort() { assertStateAfterEvents( Loading
packages/SystemUI/multivalentTests/src/com/android/systemui/touchpad/tutorial/ui/gesture/TouchpadGestureBuilder.kt +99 −41 Original line number Diff line number Diff line Loading @@ -25,11 +25,23 @@ import android.view.MotionEvent.ACTION_UP import com.android.systemui.touchpad.tutorial.ui.gesture.MultiFingerGesture.Companion.DEFAULT_X import com.android.systemui.touchpad.tutorial.ui.gesture.MultiFingerGesture.Companion.DEFAULT_Y /** Given gesture move events can build list of [MotionEvent]s included in that gesture */ interface GestureEventsBuilder { /** * Interface for gesture builders which support creating list of [MotionEvent] for common swipe * gestures. For simple usage see swipe* methods or use [createEvents] for more specific scenarios. * Creates full gesture including provided move events. This means returned events include DOWN, * MOVE and UP. Note that move event's x and y is always relative to the starting one. */ interface MultiFingerGesture { fun eventsForFullGesture(moveEvents: MoveEventsBuilder.() -> Unit): List<MotionEvent> /** * Creates partial gesture including provided move events. This means returned events include * DOWN and MOVE. Note that move event's x and y is always relative to the starting one. */ fun eventsForGestureInProgress(moveEvents: MoveEventsBuilder.() -> Unit): List<MotionEvent> } /** Support creating list of [MotionEvent] for common swipe gestures. */ interface MultiFingerGesture : GestureEventsBuilder { companion object { const val SWIPE_DISTANCE = 100f Loading @@ -37,27 +49,41 @@ interface MultiFingerGesture { const val DEFAULT_Y = 500f } fun swipeUp(distancePx: Float = SWIPE_DISTANCE) = createEvents { move(deltaY = -distancePx) } fun swipeDown(distancePx: Float = SWIPE_DISTANCE) = createEvents { move(deltaY = distancePx) } fun swipeUp(distancePx: Float = SWIPE_DISTANCE) = eventsForFullGesture { move(deltaY = -distancePx) } fun swipeRight(distancePx: Float = SWIPE_DISTANCE) = createEvents { move(deltaX = distancePx) } fun swipeDown(distancePx: Float = SWIPE_DISTANCE) = eventsForFullGesture { move(deltaY = distancePx) } fun swipeLeft(distancePx: Float = SWIPE_DISTANCE) = createEvents { move(deltaX = -distancePx) } fun swipeRight(distancePx: Float = SWIPE_DISTANCE) = eventsForFullGesture { move(deltaX = distancePx) } /** * Creates gesture with provided move events. Note that move event's x and y is always relative * to the starting one */ fun createEvents(moveEvents: GestureBuilder.() -> Unit): List<MotionEvent> fun swipeLeft(distancePx: Float = SWIPE_DISTANCE) = eventsForFullGesture { move(deltaX = -distancePx) } } object ThreeFingerGesture : MultiFingerGesture { override fun createEvents(moveEvents: GestureBuilder.() -> Unit): List<MotionEvent> { return touchpadGesture( private val moveEventsBuilder = MoveEventsBuilder(::threeFingerEvent) override fun eventsForFullGesture(moveEvents: MoveEventsBuilder.() -> Unit): List<MotionEvent> { return buildGesture( startEvents = { x, y -> startEvents(x, y) }, moveEvents = moveEventsBuilder.getEvents(moveEvents), endEvents = { x, y -> endEvents(x, y) }, ) } override fun eventsForGestureInProgress( moveEvents: MoveEventsBuilder.() -> Unit ): List<MotionEvent> { return buildGesture( startEvents = { x, y -> startEvents(x, y) }, moveEvents = GestureBuilder(::threeFingerEvent).apply { moveEvents() }.events, endEvents = { x, y -> endEvents(x, y) } moveEvents = moveEventsBuilder.getEvents(moveEvents), ) } Loading @@ -65,7 +91,7 @@ object ThreeFingerGesture : MultiFingerGesture { return listOf( threeFingerEvent(ACTION_DOWN, x, y), threeFingerEvent(ACTION_POINTER_DOWN, x, y), threeFingerEvent(ACTION_POINTER_DOWN, x, y) threeFingerEvent(ACTION_POINTER_DOWN, x, y), ) } Loading @@ -73,32 +99,43 @@ object ThreeFingerGesture : MultiFingerGesture { return listOf( threeFingerEvent(ACTION_POINTER_UP, x, y), threeFingerEvent(ACTION_POINTER_UP, x, y), threeFingerEvent(ACTION_UP, x, y) threeFingerEvent(ACTION_UP, x, y), ) } private fun threeFingerEvent( action: Int, x: Float = DEFAULT_X, y: Float = DEFAULT_Y y: Float = DEFAULT_Y, ): MotionEvent { return touchpadEvent( action = action, x = x, y = y, classification = MotionEvent.CLASSIFICATION_MULTI_FINGER_SWIPE, axisValues = mapOf(MotionEvent.AXIS_GESTURE_SWIPE_FINGER_COUNT to 3f) axisValues = mapOf(MotionEvent.AXIS_GESTURE_SWIPE_FINGER_COUNT to 3f), ) } } object FourFingerGesture : MultiFingerGesture { override fun createEvents(moveEvents: GestureBuilder.() -> Unit): List<MotionEvent> { return touchpadGesture( private val moveEventsBuilder = MoveEventsBuilder(::fourFingerEvent) override fun eventsForFullGesture(moveEvents: MoveEventsBuilder.() -> Unit): List<MotionEvent> { return buildGesture( startEvents = { x, y -> startEvents(x, y) }, moveEvents = moveEventsBuilder.getEvents(moveEvents), endEvents = { x, y -> endEvents(x, y) }, ) } override fun eventsForGestureInProgress( moveEvents: MoveEventsBuilder.() -> Unit ): List<MotionEvent> { return buildGesture( startEvents = { x, y -> startEvents(x, y) }, moveEvents = GestureBuilder(::fourFingerEvent).apply { moveEvents() }.events, endEvents = { x, y -> endEvents(x, y) } moveEvents = moveEventsBuilder.getEvents(moveEvents), ) } Loading @@ -107,7 +144,7 @@ object FourFingerGesture : MultiFingerGesture { fourFingerEvent(ACTION_DOWN, x, y), fourFingerEvent(ACTION_POINTER_DOWN, x, y), fourFingerEvent(ACTION_POINTER_DOWN, x, y), fourFingerEvent(ACTION_POINTER_DOWN, x, y) fourFingerEvent(ACTION_POINTER_DOWN, x, y), ) } Loading @@ -116,61 +153,74 @@ object FourFingerGesture : MultiFingerGesture { fourFingerEvent(ACTION_POINTER_UP, x, y), fourFingerEvent(ACTION_POINTER_UP, x, y), fourFingerEvent(ACTION_POINTER_UP, x, y), fourFingerEvent(ACTION_UP, x, y) fourFingerEvent(ACTION_UP, x, y), ) } private fun fourFingerEvent( action: Int, x: Float = DEFAULT_X, y: Float = DEFAULT_Y y: Float = DEFAULT_Y, ): MotionEvent { return touchpadEvent( action = action, x = x, y = y, classification = MotionEvent.CLASSIFICATION_MULTI_FINGER_SWIPE, axisValues = mapOf(MotionEvent.AXIS_GESTURE_SWIPE_FINGER_COUNT to 4f) axisValues = mapOf(MotionEvent.AXIS_GESTURE_SWIPE_FINGER_COUNT to 4f), ) } } object TwoFingerGesture : MultiFingerGesture { override fun createEvents(moveEvents: GestureBuilder.() -> Unit): List<MotionEvent> { return touchpadGesture( startEvents = { x, y -> listOf(twoFingerEvent(ACTION_DOWN, x, y)) }, moveEvents = GestureBuilder(::twoFingerEvent).apply { moveEvents() }.events, endEvents = { x, y -> listOf(twoFingerEvent(ACTION_UP, x, y)) } private val moveEventsBuilder = MoveEventsBuilder(::twoFingerEvent) override fun eventsForFullGesture(moveEvents: MoveEventsBuilder.() -> Unit): List<MotionEvent> { return buildGesture( startEvents = { x, y -> startEvents(x, y) }, moveEvents = moveEventsBuilder.getEvents(moveEvents), endEvents = { x, y -> listOf(twoFingerEvent(ACTION_UP, x, y)) }, ) } override fun eventsForGestureInProgress( moveEvents: MoveEventsBuilder.() -> Unit ): List<MotionEvent> { return buildGesture( startEvents = { x, y -> startEvents(x, y) }, moveEvents = moveEventsBuilder.getEvents(moveEvents), ) } private fun startEvents(x: Float, y: Float) = listOf(twoFingerEvent(ACTION_DOWN, x, y)) private fun twoFingerEvent( action: Int, x: Float = DEFAULT_X, y: Float = DEFAULT_Y y: Float = DEFAULT_Y, ): MotionEvent { return touchpadEvent( action = action, x = x, y = y, classification = MotionEvent.CLASSIFICATION_TWO_FINGER_SWIPE, axisValues = mapOf(MotionEvent.AXIS_GESTURE_SWIPE_FINGER_COUNT to 2f) axisValues = mapOf(MotionEvent.AXIS_GESTURE_SWIPE_FINGER_COUNT to 2f), ) } } private fun touchpadGesture( private fun buildGesture( startEvents: (Float, Float) -> List<MotionEvent>, moveEvents: List<MotionEvent>, endEvents: (Float, Float) -> List<MotionEvent> endEvents: (Float, Float) -> List<MotionEvent> = { _, _ -> emptyList() }, ): List<MotionEvent> { val lastX = moveEvents.last().x val lastY = moveEvents.last().y return startEvents(DEFAULT_X, DEFAULT_Y) + moveEvents + endEvents(lastX, lastY) } class GestureBuilder internal constructor(val eventBuilder: (Int, Float, Float) -> MotionEvent) { class MoveEventsBuilder internal constructor(val eventBuilder: (Int, Float, Float) -> MotionEvent) { val events = mutableListOf<MotionEvent>() Loading @@ -178,3 +228,11 @@ class GestureBuilder internal constructor(val eventBuilder: (Int, Float, Float) events.add(eventBuilder(ACTION_MOVE, DEFAULT_X + deltaX, DEFAULT_Y + deltaY)) } } private fun MoveEventsBuilder.getEvents( moveEvents: MoveEventsBuilder.() -> Unit ): List<MotionEvent> { events.clear() this.moveEvents() return events }