1package com.android.server.accessibility;
2
3import android.util.MathUtils;
4import android.view.MotionEvent;
5
6/**
7 * Some helper functions for gesture detection.
8 */
9final class GestureUtils {
10
11    private GestureUtils() {
12        /* cannot be instantiated */
13    }
14
15    public static boolean isMultiTap(MotionEvent firstUp, MotionEvent secondUp,
16            int multiTapTimeSlop, int multiTapDistanceSlop) {
17        if (firstUp == null || secondUp == null) return false;
18        return eventsWithinTimeAndDistanceSlop(firstUp, secondUp, multiTapTimeSlop,
19                multiTapDistanceSlop);
20    }
21
22    private static boolean eventsWithinTimeAndDistanceSlop(MotionEvent first, MotionEvent second,
23            int timeout, int distance) {
24        if (isTimedOut(first, second, timeout)) {
25            return false;
26        }
27        final double deltaMove = distance(first, second);
28        if (deltaMove >= distance) {
29            return false;
30        }
31        return true;
32    }
33
34    public static double distance(MotionEvent first, MotionEvent second) {
35        return MathUtils.dist(first.getX(), first.getY(), second.getX(), second.getY());
36    }
37
38    public static boolean isTimedOut(MotionEvent firstUp, MotionEvent secondUp, int timeout) {
39        final long deltaTime = secondUp.getEventTime() - firstUp.getEventTime();
40        return (deltaTime >= timeout);
41    }
42
43    /**
44     * Determines whether a two pointer gesture is a dragging one.
45     *
46     * @return True if the gesture is a dragging one.
47     */
48    public static boolean isDraggingGesture(float firstPtrDownX, float firstPtrDownY,
49            float secondPtrDownX, float secondPtrDownY, float firstPtrX, float firstPtrY,
50            float secondPtrX, float secondPtrY, float maxDraggingAngleCos) {
51
52        // Check if the pointers are moving in the same direction.
53        final float firstDeltaX = firstPtrX - firstPtrDownX;
54        final float firstDeltaY = firstPtrY - firstPtrDownY;
55
56        if (firstDeltaX == 0 && firstDeltaY == 0) {
57            return true;
58        }
59
60        final float firstMagnitude = (float) Math.hypot(firstDeltaX, firstDeltaY);
61        final float firstXNormalized =
62            (firstMagnitude > 0) ? firstDeltaX / firstMagnitude : firstDeltaX;
63        final float firstYNormalized =
64            (firstMagnitude > 0) ? firstDeltaY / firstMagnitude : firstDeltaY;
65
66        final float secondDeltaX = secondPtrX - secondPtrDownX;
67        final float secondDeltaY = secondPtrY - secondPtrDownY;
68
69        if (secondDeltaX == 0 && secondDeltaY == 0) {
70            return true;
71        }
72
73        final float secondMagnitude = (float) Math.hypot(secondDeltaX, secondDeltaY);
74        final float secondXNormalized =
75            (secondMagnitude > 0) ? secondDeltaX / secondMagnitude : secondDeltaX;
76        final float secondYNormalized =
77            (secondMagnitude > 0) ? secondDeltaY / secondMagnitude : secondDeltaY;
78
79        final float angleCos =
80            firstXNormalized * secondXNormalized + firstYNormalized * secondYNormalized;
81
82        if (angleCos < maxDraggingAngleCos) {
83            return false;
84        }
85
86        return true;
87    }
88}
89