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

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

Merge "Adding HomeGestureMonitor" into main

parents 6dd8be5e 43f5be5f
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)
    }
}