11cf70bbf96930662cab0e699d70b62865766ff52Svetoslav Ganovpackage com.android.server.accessibility;
21cf70bbf96930662cab0e699d70b62865766ff52Svetoslav Ganov
31cf70bbf96930662cab0e699d70b62865766ff52Svetoslav Ganovimport android.util.MathUtils;
41cf70bbf96930662cab0e699d70b62865766ff52Svetoslav Ganovimport android.view.MotionEvent;
51cf70bbf96930662cab0e699d70b62865766ff52Svetoslav Ganov
61cf70bbf96930662cab0e699d70b62865766ff52Svetoslav Ganov/**
71cf70bbf96930662cab0e699d70b62865766ff52Svetoslav Ganov * Some helper functions for gesture detection.
81cf70bbf96930662cab0e699d70b62865766ff52Svetoslav Ganov */
91cf70bbf96930662cab0e699d70b62865766ff52Svetoslav Ganovfinal class GestureUtils {
101cf70bbf96930662cab0e699d70b62865766ff52Svetoslav Ganov
111cf70bbf96930662cab0e699d70b62865766ff52Svetoslav Ganov    private GestureUtils() {
121cf70bbf96930662cab0e699d70b62865766ff52Svetoslav Ganov        /* cannot be instantiated */
131cf70bbf96930662cab0e699d70b62865766ff52Svetoslav Ganov    }
141cf70bbf96930662cab0e699d70b62865766ff52Svetoslav Ganov
151cf70bbf96930662cab0e699d70b62865766ff52Svetoslav Ganov    public static boolean isTap(MotionEvent down, MotionEvent up, int tapTimeSlop,
161cf70bbf96930662cab0e699d70b62865766ff52Svetoslav Ganov            int tapDistanceSlop, int actionIndex) {
171cf70bbf96930662cab0e699d70b62865766ff52Svetoslav Ganov        return eventsWithinTimeAndDistanceSlop(down, up, tapTimeSlop, tapDistanceSlop, actionIndex);
181cf70bbf96930662cab0e699d70b62865766ff52Svetoslav Ganov    }
191cf70bbf96930662cab0e699d70b62865766ff52Svetoslav Ganov
201cf70bbf96930662cab0e699d70b62865766ff52Svetoslav Ganov    public static boolean isMultiTap(MotionEvent firstUp, MotionEvent secondUp,
211cf70bbf96930662cab0e699d70b62865766ff52Svetoslav Ganov            int multiTapTimeSlop, int multiTapDistanceSlop, int actionIndex) {
221cf70bbf96930662cab0e699d70b62865766ff52Svetoslav Ganov        return eventsWithinTimeAndDistanceSlop(firstUp, secondUp, multiTapTimeSlop,
231cf70bbf96930662cab0e699d70b62865766ff52Svetoslav Ganov                multiTapDistanceSlop, actionIndex);
241cf70bbf96930662cab0e699d70b62865766ff52Svetoslav Ganov    }
251cf70bbf96930662cab0e699d70b62865766ff52Svetoslav Ganov
261cf70bbf96930662cab0e699d70b62865766ff52Svetoslav Ganov    private static boolean eventsWithinTimeAndDistanceSlop(MotionEvent first, MotionEvent second,
271cf70bbf96930662cab0e699d70b62865766ff52Svetoslav Ganov            int timeout, int distance, int actionIndex) {
281cf70bbf96930662cab0e699d70b62865766ff52Svetoslav Ganov        if (isTimedOut(first, second, timeout)) {
291cf70bbf96930662cab0e699d70b62865766ff52Svetoslav Ganov            return false;
301cf70bbf96930662cab0e699d70b62865766ff52Svetoslav Ganov        }
311cf70bbf96930662cab0e699d70b62865766ff52Svetoslav Ganov        final double deltaMove = computeDistance(first, second, actionIndex);
321cf70bbf96930662cab0e699d70b62865766ff52Svetoslav Ganov        if (deltaMove >= distance) {
331cf70bbf96930662cab0e699d70b62865766ff52Svetoslav Ganov            return false;
341cf70bbf96930662cab0e699d70b62865766ff52Svetoslav Ganov        }
351cf70bbf96930662cab0e699d70b62865766ff52Svetoslav Ganov        return true;
361cf70bbf96930662cab0e699d70b62865766ff52Svetoslav Ganov    }
371cf70bbf96930662cab0e699d70b62865766ff52Svetoslav Ganov
381cf70bbf96930662cab0e699d70b62865766ff52Svetoslav Ganov    public static double computeDistance(MotionEvent first, MotionEvent second, int pointerIndex) {
391cf70bbf96930662cab0e699d70b62865766ff52Svetoslav Ganov         return MathUtils.dist(first.getX(pointerIndex), first.getY(pointerIndex),
401cf70bbf96930662cab0e699d70b62865766ff52Svetoslav Ganov                 second.getX(pointerIndex), second.getY(pointerIndex));
411cf70bbf96930662cab0e699d70b62865766ff52Svetoslav Ganov    }
421cf70bbf96930662cab0e699d70b62865766ff52Svetoslav Ganov
431cf70bbf96930662cab0e699d70b62865766ff52Svetoslav Ganov    public static boolean isTimedOut(MotionEvent firstUp, MotionEvent secondUp, int timeout) {
441cf70bbf96930662cab0e699d70b62865766ff52Svetoslav Ganov        final long deltaTime = secondUp.getEventTime() - firstUp.getEventTime();
451cf70bbf96930662cab0e699d70b62865766ff52Svetoslav Ganov        return (deltaTime >= timeout);
461cf70bbf96930662cab0e699d70b62865766ff52Svetoslav Ganov    }
471cf70bbf96930662cab0e699d70b62865766ff52Svetoslav Ganov
481cf70bbf96930662cab0e699d70b62865766ff52Svetoslav Ganov    public static boolean isSamePointerContext(MotionEvent first, MotionEvent second) {
491cf70bbf96930662cab0e699d70b62865766ff52Svetoslav Ganov        return (first.getPointerIdBits() == second.getPointerIdBits()
501cf70bbf96930662cab0e699d70b62865766ff52Svetoslav Ganov                && first.getPointerId(first.getActionIndex())
511cf70bbf96930662cab0e699d70b62865766ff52Svetoslav Ganov                        == second.getPointerId(second.getActionIndex()));
521cf70bbf96930662cab0e699d70b62865766ff52Svetoslav Ganov    }
531cf70bbf96930662cab0e699d70b62865766ff52Svetoslav Ganov
541cf70bbf96930662cab0e699d70b62865766ff52Svetoslav Ganov    /**
551cf70bbf96930662cab0e699d70b62865766ff52Svetoslav Ganov     * Determines whether a two pointer gesture is a dragging one.
561cf70bbf96930662cab0e699d70b62865766ff52Svetoslav Ganov     *
571cf70bbf96930662cab0e699d70b62865766ff52Svetoslav Ganov     * @param event The event with the pointer data.
581cf70bbf96930662cab0e699d70b62865766ff52Svetoslav Ganov     * @return True if the gesture is a dragging one.
591cf70bbf96930662cab0e699d70b62865766ff52Svetoslav Ganov     */
601cf70bbf96930662cab0e699d70b62865766ff52Svetoslav Ganov    public static boolean isDraggingGesture(float firstPtrDownX, float firstPtrDownY,
611cf70bbf96930662cab0e699d70b62865766ff52Svetoslav Ganov            float secondPtrDownX, float secondPtrDownY, float firstPtrX, float firstPtrY,
621cf70bbf96930662cab0e699d70b62865766ff52Svetoslav Ganov            float secondPtrX, float secondPtrY, float maxDraggingAngleCos) {
631cf70bbf96930662cab0e699d70b62865766ff52Svetoslav Ganov
641cf70bbf96930662cab0e699d70b62865766ff52Svetoslav Ganov        // Check if the pointers are moving in the same direction.
651cf70bbf96930662cab0e699d70b62865766ff52Svetoslav Ganov        final float firstDeltaX = firstPtrX - firstPtrDownX;
661cf70bbf96930662cab0e699d70b62865766ff52Svetoslav Ganov        final float firstDeltaY = firstPtrY - firstPtrDownY;
671cf70bbf96930662cab0e699d70b62865766ff52Svetoslav Ganov
681cf70bbf96930662cab0e699d70b62865766ff52Svetoslav Ganov        if (firstDeltaX == 0 && firstDeltaY == 0) {
691cf70bbf96930662cab0e699d70b62865766ff52Svetoslav Ganov            return true;
701cf70bbf96930662cab0e699d70b62865766ff52Svetoslav Ganov        }
711cf70bbf96930662cab0e699d70b62865766ff52Svetoslav Ganov
7233253a4baa6279f81a73425b49dfb6abe5f5416eNeil Fuller        final float firstMagnitude = (float) Math.hypot(firstDeltaX, firstDeltaY);
731cf70bbf96930662cab0e699d70b62865766ff52Svetoslav Ganov        final float firstXNormalized =
741cf70bbf96930662cab0e699d70b62865766ff52Svetoslav Ganov            (firstMagnitude > 0) ? firstDeltaX / firstMagnitude : firstDeltaX;
751cf70bbf96930662cab0e699d70b62865766ff52Svetoslav Ganov        final float firstYNormalized =
761cf70bbf96930662cab0e699d70b62865766ff52Svetoslav Ganov            (firstMagnitude > 0) ? firstDeltaY / firstMagnitude : firstDeltaY;
771cf70bbf96930662cab0e699d70b62865766ff52Svetoslav Ganov
781cf70bbf96930662cab0e699d70b62865766ff52Svetoslav Ganov        final float secondDeltaX = secondPtrX - secondPtrDownX;
791cf70bbf96930662cab0e699d70b62865766ff52Svetoslav Ganov        final float secondDeltaY = secondPtrY - secondPtrDownY;
801cf70bbf96930662cab0e699d70b62865766ff52Svetoslav Ganov
811cf70bbf96930662cab0e699d70b62865766ff52Svetoslav Ganov        if (secondDeltaX == 0 && secondDeltaY == 0) {
821cf70bbf96930662cab0e699d70b62865766ff52Svetoslav Ganov            return true;
831cf70bbf96930662cab0e699d70b62865766ff52Svetoslav Ganov        }
841cf70bbf96930662cab0e699d70b62865766ff52Svetoslav Ganov
8533253a4baa6279f81a73425b49dfb6abe5f5416eNeil Fuller        final float secondMagnitude = (float) Math.hypot(secondDeltaX, secondDeltaY);
861cf70bbf96930662cab0e699d70b62865766ff52Svetoslav Ganov        final float secondXNormalized =
871cf70bbf96930662cab0e699d70b62865766ff52Svetoslav Ganov            (secondMagnitude > 0) ? secondDeltaX / secondMagnitude : secondDeltaX;
881cf70bbf96930662cab0e699d70b62865766ff52Svetoslav Ganov        final float secondYNormalized =
891cf70bbf96930662cab0e699d70b62865766ff52Svetoslav Ganov            (secondMagnitude > 0) ? secondDeltaY / secondMagnitude : secondDeltaY;
901cf70bbf96930662cab0e699d70b62865766ff52Svetoslav Ganov
911cf70bbf96930662cab0e699d70b62865766ff52Svetoslav Ganov        final float angleCos =
921cf70bbf96930662cab0e699d70b62865766ff52Svetoslav Ganov            firstXNormalized * secondXNormalized + firstYNormalized * secondYNormalized;
931cf70bbf96930662cab0e699d70b62865766ff52Svetoslav Ganov
941cf70bbf96930662cab0e699d70b62865766ff52Svetoslav Ganov        if (angleCos < maxDraggingAngleCos) {
951cf70bbf96930662cab0e699d70b62865766ff52Svetoslav Ganov            return false;
961cf70bbf96930662cab0e699d70b62865766ff52Svetoslav Ganov        }
971cf70bbf96930662cab0e699d70b62865766ff52Svetoslav Ganov
981cf70bbf96930662cab0e699d70b62865766ff52Svetoslav Ganov        return true;
991cf70bbf96930662cab0e699d70b62865766ff52Svetoslav Ganov    }
1001cf70bbf96930662cab0e699d70b62865766ff52Svetoslav Ganov}
101