Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 02661e0a authored by Michał Brzeziński's avatar Michał Brzeziński Committed by Android (Google) Code Review
Browse files

Merge "Refactoring tests to use reusable set of touchpad gestures" into main

parents 71278caa 783cd127
Loading
Loading
Loading
Loading
+5 −4
Original line number Diff line number Diff line
@@ -70,11 +70,11 @@ import com.airbnb.lottie.compose.rememberLottieDynamicProperties
import com.airbnb.lottie.compose.rememberLottieDynamicProperty
import com.android.compose.theme.LocalAndroidColorScheme
import com.android.systemui.res.R
import com.android.systemui.touchpad.tutorial.ui.gesture.BackGestureMonitor
import com.android.systemui.touchpad.tutorial.ui.gesture.GestureState
import com.android.systemui.touchpad.tutorial.ui.gesture.GestureState.FINISHED
import com.android.systemui.touchpad.tutorial.ui.gesture.GestureState.IN_PROGRESS
import com.android.systemui.touchpad.tutorial.ui.gesture.GestureState.NOT_STARTED
import com.android.systemui.touchpad.tutorial.ui.gesture.TouchpadGesture.BACK
import com.android.systemui.touchpad.tutorial.ui.gesture.TouchpadGestureHandler

data class TutorialScreenColors(
@@ -99,9 +99,10 @@ fun BackGestureTutorialScreen(
    val gestureHandler =
        remember(swipeDistanceThresholdPx) {
            TouchpadGestureHandler(
                BACK,
                BackGestureMonitor(
                    swipeDistanceThresholdPx,
                onGestureStateChanged = { gestureState = it }
                    gestureStateChangedCallback = { gestureState = it }
                ),
            )
        }
    TouchpadGesturesHandlingBox(gestureHandler, gestureState) {
+0 −17
Original line number Diff line number Diff line
@@ -22,18 +22,6 @@ import com.android.systemui.touchpad.tutorial.ui.gesture.GestureState.IN_PROGRES
import com.android.systemui.touchpad.tutorial.ui.gesture.GestureState.NOT_STARTED
import kotlin.math.abs

/**
 * Monitor for touchpad gestures that calls [gestureStateChangedCallback] when [GestureState]
 * changes. All tracked motion events should be passed to [processTouchpadEvent]
 */
interface TouchpadGestureMonitor {

    val gestureDistanceThresholdPx: Int
    val gestureStateChangedCallback: (GestureState) -> Unit

    fun processTouchpadEvent(event: MotionEvent)
}

class BackGestureMonitor(
    override val gestureDistanceThresholdPx: Int,
    override val gestureStateChangedCallback: (GestureState) -> Unit
@@ -62,9 +50,4 @@ class BackGestureMonitor(
            }
        }
    }

    private fun isThreeFingerTouchpadSwipe(event: MotionEvent): Boolean {
        return event.classification == MotionEvent.CLASSIFICATION_MULTI_FINGER_SWIPE &&
            event.getAxisValue(MotionEvent.AXIS_GESTURE_SWIPE_FINGER_COUNT) == 3f
    }
}
+2 −7
Original line number Diff line number Diff line
@@ -24,14 +24,9 @@ import android.view.MotionEvent
 * motion events passed to [onMotionEvent] and will filter touchpad events accordingly
 */
class TouchpadGestureHandler(
    touchpadGesture: TouchpadGesture,
    swipeDistanceThresholdPx: Int,
    onGestureStateChanged: (GestureState) -> Unit
    private val gestureMonitor: TouchpadGestureMonitor,
) {

    private val gestureRecognition =
        touchpadGesture.toMonitor(swipeDistanceThresholdPx, onStateChanged = onGestureStateChanged)

    fun onMotionEvent(event: MotionEvent): Boolean {
        // events from touchpad have SOURCE_MOUSE and not SOURCE_TOUCHPAD because of legacy reasons
        val isFromTouchpad =
@@ -41,7 +36,7 @@ class TouchpadGestureHandler(
            event.actionMasked == MotionEvent.ACTION_DOWN &&
                event.isButtonPressed(MotionEvent.BUTTON_PRIMARY)
        return if (isFromTouchpad && !buttonClick) {
            gestureRecognition.processTouchpadEvent(event)
            gestureMonitor.processTouchpadEvent(event)
            true
        } else {
            false
+40 −0
Original line number Diff line number Diff line
@@ -16,17 +16,25 @@

package com.android.systemui.touchpad.tutorial.ui.gesture

enum class TouchpadGesture {
    BACK,
    HOME;

    fun toMonitor(
        swipeDistanceThresholdPx: Int,
        onStateChanged: (GestureState) -> Unit
    ): TouchpadGestureMonitor {
        return when (this) {
            BACK -> BackGestureMonitor(swipeDistanceThresholdPx, onStateChanged)
            else -> throw IllegalArgumentException("Not implemented yet")
        }
import android.view.MotionEvent

/**
 * Monitor for touchpad gestures that calls [gestureStateChangedCallback] when [GestureState]
 * changes. All tracked motion events should be passed to [processTouchpadEvent]
 */
interface TouchpadGestureMonitor {

    val gestureDistanceThresholdPx: Int
    val gestureStateChangedCallback: (GestureState) -> Unit

    fun processTouchpadEvent(event: MotionEvent)
}

fun isThreeFingerTouchpadSwipe(event: MotionEvent) = isNFingerTouchpadSwipe(event, fingerCount = 3)

fun isFourFingerTouchpadSwipe(event: MotionEvent) = isNFingerTouchpadSwipe(event, fingerCount = 4)

private fun isNFingerTouchpadSwipe(event: MotionEvent, fingerCount: Int): Boolean {
    return event.classification == MotionEvent.CLASSIFICATION_MULTI_FINGER_SWIPE &&
        event.getAxisValue(MotionEvent.AXIS_GESTURE_SWIPE_FINGER_COUNT) == fingerCount.toFloat()
}
+20 −112
Original line number Diff line number Diff line
@@ -17,20 +17,13 @@
package com.android.systemui.touchpad.tutorial.ui.gesture

import android.view.MotionEvent
import android.view.MotionEvent.ACTION_DOWN
import android.view.MotionEvent.ACTION_MOVE
import android.view.MotionEvent.ACTION_POINTER_DOWN
import android.view.MotionEvent.ACTION_POINTER_UP
import android.view.MotionEvent.ACTION_UP
import android.view.MotionEvent.AXIS_GESTURE_SWIPE_FINGER_COUNT
import android.view.MotionEvent.CLASSIFICATION_MULTI_FINGER_SWIPE
import android.view.MotionEvent.CLASSIFICATION_TWO_FINGER_SWIPE
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.touchpad.tutorial.ui.gesture.GestureState.FINISHED
import com.android.systemui.touchpad.tutorial.ui.gesture.GestureState.IN_PROGRESS
import com.android.systemui.touchpad.tutorial.ui.gesture.GestureState.NOT_STARTED
import com.android.systemui.touchpad.tutorial.ui.gesture.MultiFingerGesture.Companion.SWIPE_DISTANCE
import com.google.common.truth.Truth.assertThat
import org.junit.Test
import org.junit.runner.RunWith
@@ -46,135 +39,50 @@ class BackGestureMonitorTest : SysuiTestCase() {
            gestureStateChangedCallback = { gestureState = it }
        )

    companion object {
        const val SWIPE_DISTANCE = 100f
    }

    @Test
    fun triggersGestureFinishedForThreeFingerGestureRight() {
        val events =
            listOf(
                threeFingerEvent(ACTION_DOWN, x = 0f, y = 0f),
                threeFingerEvent(ACTION_POINTER_DOWN, x = 0f, y = 0f),
                threeFingerEvent(ACTION_POINTER_DOWN, x = 0f, y = 0f),
                threeFingerEvent(ACTION_MOVE, x = SWIPE_DISTANCE / 2, y = 0f),
                threeFingerEvent(ACTION_POINTER_UP, x = SWIPE_DISTANCE, y = 0f),
                threeFingerEvent(ACTION_POINTER_UP, x = SWIPE_DISTANCE, y = 0f),
                threeFingerEvent(ACTION_UP, x = SWIPE_DISTANCE, y = 0f),
            )

        events.forEach { gestureMonitor.processTouchpadEvent(it) }

        assertThat(gestureState).isEqualTo(FINISHED)
        assertStateAfterEvents(events = ThreeFingerGesture.swipeRight(), expectedState = FINISHED)
    }

    @Test
    fun triggersGestureFinishedForThreeFingerGestureLeft() {
        val events =
            listOf(
                threeFingerEvent(ACTION_DOWN, x = SWIPE_DISTANCE, y = 0f),
                threeFingerEvent(ACTION_POINTER_DOWN, x = SWIPE_DISTANCE, y = 0f),
                threeFingerEvent(ACTION_POINTER_DOWN, x = SWIPE_DISTANCE, y = 0f),
                threeFingerEvent(ACTION_MOVE, x = SWIPE_DISTANCE / 2, y = 0f),
                threeFingerEvent(ACTION_POINTER_UP, x = 0f, y = 0f),
                threeFingerEvent(ACTION_POINTER_UP, x = 0f, y = 0f),
                threeFingerEvent(ACTION_UP, x = 0f, y = 0f),
            )

        events.forEach { gestureMonitor.processTouchpadEvent(it) }

        assertThat(gestureState).isEqualTo(FINISHED)
        assertStateAfterEvents(events = ThreeFingerGesture.swipeLeft(), expectedState = FINISHED)
    }

    @Test
    fun triggersGestureProgressForThreeFingerGestureStarted() {
        val events =
            listOf(
                threeFingerEvent(ACTION_DOWN, x = SWIPE_DISTANCE, y = 0f),
                threeFingerEvent(ACTION_POINTER_DOWN, x = SWIPE_DISTANCE, y = 0f),
                threeFingerEvent(ACTION_POINTER_DOWN, x = SWIPE_DISTANCE, y = 0f),
        assertStateAfterEvents(
            events = ThreeFingerGesture.startEvents(x = 0f, y = 0f),
            expectedState = IN_PROGRESS
        )

        events.forEach { gestureMonitor.processTouchpadEvent(it) }

        assertThat(gestureState).isEqualTo(IN_PROGRESS)
    }

    private fun threeFingerEvent(action: Int, x: Float, y: Float): MotionEvent {
        return motionEvent(
            action = action,
            x = x,
            y = y,
            classification = CLASSIFICATION_MULTI_FINGER_SWIPE,
            axisValues = mapOf(AXIS_GESTURE_SWIPE_FINGER_COUNT to 3f)
    @Test
    fun doesntTriggerGestureFinished_onGestureDistanceTooShort() {
        assertStateAfterEvents(
            events = ThreeFingerGesture.swipeLeft(distancePx = SWIPE_DISTANCE / 2),
            expectedState = NOT_STARTED
        )
    }

    @Test
    fun doesntTriggerGestureFinished_onThreeFingersSwipeUp() {
        val events =
            listOf(
                threeFingerEvent(ACTION_DOWN, x = 0f, y = 0f),
                threeFingerEvent(ACTION_POINTER_DOWN, x = 0f, y = 0f),
                threeFingerEvent(ACTION_POINTER_DOWN, x = 0f, y = 0f),
                threeFingerEvent(ACTION_MOVE, x = 0f, y = SWIPE_DISTANCE / 2),
                threeFingerEvent(ACTION_POINTER_UP, x = 0f, y = SWIPE_DISTANCE),
                threeFingerEvent(ACTION_POINTER_UP, x = 0f, y = SWIPE_DISTANCE),
                threeFingerEvent(ACTION_UP, x = 0f, y = SWIPE_DISTANCE),
            )

        events.forEach { gestureMonitor.processTouchpadEvent(it) }

        assertThat(gestureState).isEqualTo(NOT_STARTED)
    fun doesntTriggerGestureFinished_onThreeFingersSwipeInOtherDirections() {
        assertStateAfterEvents(events = ThreeFingerGesture.swipeUp(), expectedState = NOT_STARTED)
        assertStateAfterEvents(events = ThreeFingerGesture.swipeDown(), expectedState = NOT_STARTED)
    }

    @Test
    fun doesntTriggerGestureFinished_onTwoFingersSwipe() {
        fun twoFingerEvent(action: Int, x: Float, y: Float) =
            motionEvent(
                action = action,
                x = x,
                y = y,
                classification = CLASSIFICATION_TWO_FINGER_SWIPE,
                axisValues = mapOf(AXIS_GESTURE_SWIPE_FINGER_COUNT to 2f)
            )
        val events =
            listOf(
                twoFingerEvent(ACTION_DOWN, x = 0f, y = 0f),
                twoFingerEvent(ACTION_MOVE, x = SWIPE_DISTANCE / 2, y = 0f),
                twoFingerEvent(ACTION_UP, x = SWIPE_DISTANCE, y = 0f),
            )

        events.forEach { gestureMonitor.processTouchpadEvent(it) }

        assertThat(gestureState).isEqualTo(NOT_STARTED)
        assertStateAfterEvents(events = TwoFingerGesture.swipeRight(), expectedState = NOT_STARTED)
    }

    @Test
    fun doesntTriggerGestureFinished_onFourFingersSwipe() {
        fun fourFingerEvent(action: Int, x: Float, y: Float) =
            motionEvent(
                action = action,
                x = x,
                y = y,
                classification = CLASSIFICATION_MULTI_FINGER_SWIPE,
                axisValues = mapOf(AXIS_GESTURE_SWIPE_FINGER_COUNT to 4f)
            )
        val events =
            listOf(
                fourFingerEvent(ACTION_DOWN, x = 0f, y = 0f),
                fourFingerEvent(ACTION_POINTER_DOWN, x = 0f, y = 0f),
                fourFingerEvent(ACTION_POINTER_DOWN, x = 0f, y = 0f),
                fourFingerEvent(ACTION_POINTER_DOWN, x = 0f, y = 0f),
                fourFingerEvent(ACTION_MOVE, x = SWIPE_DISTANCE / 2, y = 0f),
                fourFingerEvent(ACTION_POINTER_UP, x = SWIPE_DISTANCE, y = 0f),
                fourFingerEvent(ACTION_POINTER_UP, x = SWIPE_DISTANCE, y = 0f),
                fourFingerEvent(ACTION_POINTER_UP, x = SWIPE_DISTANCE, y = 0f),
                fourFingerEvent(ACTION_UP, x = SWIPE_DISTANCE, y = 0f),
            )
        assertStateAfterEvents(events = FourFingerGesture.swipeRight(), expectedState = NOT_STARTED)
    }

    private fun assertStateAfterEvents(events: List<MotionEvent>, expectedState: GestureState) {
        events.forEach { gestureMonitor.processTouchpadEvent(it) }

        assertThat(gestureState).isEqualTo(NOT_STARTED)
        assertThat(gestureState).isEqualTo(expectedState)
    }
}
Loading