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

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

Merge "Refactoring gesture recognition logic" into main

parents e42d180a 8a22cd58
Loading
Loading
Loading
Loading
+17 −19
Original line number Diff line number Diff line
@@ -16,26 +16,24 @@

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

import android.view.MotionEvent
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 by ThreeFingerDistanceBasedGestureMonitor(
        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
    private val gestureDistanceThresholdPx: Int,
    override val gestureStateChangedCallback: (GestureState) -> Unit,
) : TouchpadGestureMonitor {
    private val distanceTracker = DistanceTracker()

    override fun processTouchpadEvent(event: MotionEvent) {
        if (!isThreeFingerTouchpadSwipe(event)) return
        val distanceState = distanceTracker.processEvent(event)
        updateGestureStateBasedOnDistance(
            gestureStateChangedCallback,
            distanceState,
            isFinished = { abs(it.deltaX) >= gestureDistanceThresholdPx },
            progress = { 0f },
        )
    }
}
    )
+47 −0
Original line number Diff line number Diff line
@@ -18,46 +18,30 @@ 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.
 * Tracks distance change for processed MotionEvents. Useful for recognizing gestures based on
 * distance travelled instead of specific position on the screen.
 */
    fun wasGestureDone(startX: Float, startY: Float, endX: Float, endY: Float): Boolean
}

/**
 * Common implementation for three-finger gesture monitors that are only distance-based. E.g. recent
 * apps gesture is not only distance-based because it requires going over threshold distance and
 * slowing down the movement.
 */
class ThreeFingerDistanceBasedGestureMonitor(
    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) {
class DistanceTracker(var startX: Float = 0f, var startY: Float = 0f) {
    fun processEvent(event: MotionEvent): DistanceGestureState? {
        val action = event.actionMasked
        when (action) {
        return when (action) {
            MotionEvent.ACTION_DOWN -> {
                if (isThreeFingerTouchpadSwipe(event)) {
                    xStart = event.x
                    yStart = event.y
                    gestureStateChangedCallback(GestureState.InProgress())
                }
            }
            MotionEvent.ACTION_UP -> {
                if (isThreeFingerTouchpadSwipe(event)) {
                    if (donePredicate.wasGestureDone(xStart, yStart, event.x, event.y)) {
                        gestureStateChangedCallback(GestureState.Finished)
                    } else {
                        gestureStateChangedCallback(GestureState.NotStarted)
                    }
                }
                startX = event.x
                startY = event.y
                Started(event.x, event.y)
            }
            MotionEvent.ACTION_MOVE -> Moving(event.x - startX, event.y - startY)
            MotionEvent.ACTION_UP -> Finished(event.x - startX, event.y - startY)
            else -> null
        }
    }
}

sealed interface DistanceGestureState

class Started(val deltaX: Float, val deltaY: Float) : DistanceGestureState

class Moving(val deltaX: Float, val deltaY: Float) : DistanceGestureState

class Finished(val deltaX: Float, val deltaY: Float) : DistanceGestureState
+43 −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

/**
 * Helper function for gesture recognizers to have common state triggering logic based on distance
 * only.
 */
inline fun updateGestureStateBasedOnDistance(
    gestureStateChangedCallback: (GestureState) -> Unit,
    gestureState: DistanceGestureState?,
    isFinished: (Finished) -> Boolean,
    progress: (Moving) -> Float,
) {
    when (gestureState) {
        is Finished -> {
            if (isFinished(gestureState)) {
                gestureStateChangedCallback(GestureState.Finished)
            } else {
                gestureStateChangedCallback(GestureState.NotStarted)
            }
        }
        is Moving -> {
            gestureStateChangedCallback(GestureState.InProgress(progress(gestureState)))
        }
        is Started -> gestureStateChangedCallback(GestureState.InProgress())
        else -> {}
    }
}
+18 −19
Original line number Diff line number Diff line
@@ -16,24 +16,23 @@

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

import android.view.MotionEvent

/** Monitors for touchpad home gesture, that is three fingers swiping up */
class HomeGestureMonitor(
    override val gestureDistanceThresholdPx: Int,
    override val gestureStateChangedCallback: (GestureState) -> Unit
) :
    TouchpadGestureMonitor by ThreeFingerDistanceBasedGestureMonitor(
        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
    private val gestureDistanceThresholdPx: Int,
    override val gestureStateChangedCallback: (GestureState) -> Unit,
) : TouchpadGestureMonitor {
    private val distanceTracker = DistanceTracker()

    override fun processTouchpadEvent(event: MotionEvent) {
        if (!isThreeFingerTouchpadSwipe(event)) return
        val distanceState = distanceTracker.processEvent(event)
        updateGestureStateBasedOnDistance(
            gestureStateChangedCallback,
            distanceState,
            isFinished = { -it.deltaY >= gestureDistanceThresholdPx },
            progress = { 0f },
        )
    }
}
    )
+1 −1
Original line number Diff line number Diff line
@@ -26,7 +26,7 @@ import kotlin.math.abs
 * is based on [com.android.quickstep.util.TriggerSwipeUpTouchTracker]
 */
class RecentAppsGestureMonitor(
    override val gestureDistanceThresholdPx: Int,
    private val gestureDistanceThresholdPx: Int,
    override val gestureStateChangedCallback: (GestureState) -> Unit,
    private val velocityThresholdPxPerMs: Float,
    private val velocityTracker: VelocityTracker1D = VelocityTracker1D(isDataDifferential = false),
Loading