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

Commit 43f5be5f authored by Michal Brzezinski's avatar Michal Brzezinski
Browse files

Adding HomeGestureMonitor

It listens to touchpad three fingers swipe up gesture but is not used anywhere yet.
Also extracting common three finger gesture logic to ThreeFingerGestureMonitor.

Bug: 346576895
Test: HomeGestureMonitorTest
Flag: com.android.systemui.shared.new_touchpad_gestures_tutorial
Change-Id: If8f5b7e6a5ce8cbf5da9b9d582bc33f2dc9cf950
parent bb4c4360
Loading
Loading
Loading
Loading
+16 −28
Original line number Diff line number Diff line
@@ -16,38 +16,26 @@

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

import android.view.MotionEvent
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 kotlin.math.abs

/** Monitors for touchpad back gesture, that is three fingers swiping left or right */
class BackGestureMonitor(
    override val gestureDistanceThresholdPx: Int,
    override val gestureStateChangedCallback: (GestureState) -> Unit
) : TouchpadGestureMonitor {

    private var xStart = 0f

    override fun processTouchpadEvent(event: MotionEvent) {
        val action = event.actionMasked
        when (action) {
            MotionEvent.ACTION_DOWN -> {
                if (isThreeFingerTouchpadSwipe(event)) {
                    xStart = event.x
                    gestureStateChangedCallback(IN_PROGRESS)
                }
            }
            MotionEvent.ACTION_UP -> {
                if (isThreeFingerTouchpadSwipe(event)) {
                    val distance = abs(event.x - xStart)
                    if (distance >= gestureDistanceThresholdPx) {
                        gestureStateChangedCallback(FINISHED)
                    } else {
                        gestureStateChangedCallback(NOT_STARTED)
                    }
                }
            }
        }
    }
}
) :
    TouchpadGestureMonitor by ThreeFingerGestureMonitor(
        gestureDistanceThresholdPx = gestureDistanceThresholdPx,
        gestureStateChangedCallback = gestureStateChangedCallback,
        donePredicate =
            object : GestureDonePredicate {
                override fun wasGestureDone(
                    startX: Float,
                    startY: Float,
                    endX: Float,
                    endY: Float
                ): Boolean {
                    val distance = abs(endX - startX)
                    return distance >= gestureDistanceThresholdPx
                }
            }
    )
+39 −0
Original line number Diff line number Diff line
/*
 * 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

/** Monitors for touchpad home gesture, that is three fingers swiping up */
class HomeGestureMonitor(
    override val gestureDistanceThresholdPx: Int,
    override val gestureStateChangedCallback: (GestureState) -> Unit
) :
    TouchpadGestureMonitor by ThreeFingerGestureMonitor(
        gestureDistanceThresholdPx = gestureDistanceThresholdPx,
        gestureStateChangedCallback = gestureStateChangedCallback,
        donePredicate =
            object : GestureDonePredicate {
                override fun wasGestureDone(
                    startX: Float,
                    startY: Float,
                    endX: Float,
                    endY: Float
                ): Boolean {
                    val distance = startY - endY
                    return distance >= gestureDistanceThresholdPx
                }
            }
    )
+59 −0
Original line number Diff line number Diff line
/*
 * 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

interface GestureDonePredicate {
    /**
     * Should return if gesture was finished. The only events this predicate receives are ACTION_UP.
     */
    fun wasGestureDone(startX: Float, startY: Float, endX: Float, endY: Float): Boolean
}

/** Common implementation for all three-finger gesture monitors */
class ThreeFingerGestureMonitor(
    override val gestureDistanceThresholdPx: Int,
    override val gestureStateChangedCallback: (GestureState) -> Unit,
    private val donePredicate: GestureDonePredicate
) : TouchpadGestureMonitor {

    private var xStart = 0f
    private var yStart = 0f

    override fun processTouchpadEvent(event: MotionEvent) {
        val action = event.actionMasked
        when (action) {
            MotionEvent.ACTION_DOWN -> {
                if (isThreeFingerTouchpadSwipe(event)) {
                    xStart = event.x
                    yStart = event.y
                    gestureStateChangedCallback(GestureState.IN_PROGRESS)
                }
            }
            MotionEvent.ACTION_UP -> {
                if (isThreeFingerTouchpadSwipe(event)) {
                    if (donePredicate.wasGestureDone(xStart, yStart, event.x, event.y)) {
                        gestureStateChangedCallback(GestureState.FINISHED)
                    } else {
                        gestureStateChangedCallback(GestureState.NOT_STARTED)
                    }
                }
            }
        }
    }
}
+87 −0
Original line number Diff line number Diff line
/*
 * 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.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

@SmallTest
@RunWith(AndroidJUnit4::class)
class HomeGestureMonitorTest : SysuiTestCase() {

    private var gestureState = NOT_STARTED
    private val gestureMonitor =
        HomeGestureMonitor(
            gestureDistanceThresholdPx = SWIPE_DISTANCE.toInt(),
            gestureStateChangedCallback = { gestureState = it }
        )

    @Test
    fun triggersGestureFinishedForThreeFingerGestureUp() {
        assertStateAfterEvents(events = ThreeFingerGesture.swipeUp(), expectedState = FINISHED)
    }

    @Test
    fun triggersGestureProgressForThreeFingerGestureStarted() {
        assertStateAfterEvents(
            events = ThreeFingerGesture.startEvents(x = 0f, y = 0f),
            expectedState = IN_PROGRESS
        )
    }

    @Test
    fun doesntTriggerGestureFinished_onGestureDistanceTooShort() {
        assertStateAfterEvents(
            events = ThreeFingerGesture.swipeUp(distancePx = SWIPE_DISTANCE / 2),
            expectedState = NOT_STARTED
        )
    }

    @Test
    fun doesntTriggerGestureFinished_onThreeFingersSwipeInOtherDirections() {
        assertStateAfterEvents(events = ThreeFingerGesture.swipeDown(), expectedState = NOT_STARTED)
        assertStateAfterEvents(events = ThreeFingerGesture.swipeLeft(), expectedState = NOT_STARTED)
        assertStateAfterEvents(
            events = ThreeFingerGesture.swipeRight(),
            expectedState = NOT_STARTED
        )
    }

    @Test
    fun doesntTriggerGestureFinished_onTwoFingersSwipe() {
        assertStateAfterEvents(events = TwoFingerGesture.swipeUp(), expectedState = NOT_STARTED)
    }

    @Test
    fun doesntTriggerGestureFinished_onFourFingersSwipe() {
        assertStateAfterEvents(events = FourFingerGesture.swipeUp(), expectedState = NOT_STARTED)
    }

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