1 package com.android.server.accessibility.gestures; 2 3 import android.graphics.PointF; 4 import android.util.MathUtils; 5 import android.view.MotionEvent; 6 7 /** 8 * Some helper functions for gesture detection. 9 */ 10 public final class GestureUtils { 11 12 public static int MM_PER_CM = 10; 13 public static float CM_PER_INCH = 2.54f; 14 GestureUtils()15 private GestureUtils() { 16 /* cannot be instantiated */ 17 } 18 isMultiTap(MotionEvent firstUp, MotionEvent secondUp, int multiTapTimeSlop, int multiTapDistanceSlop)19 public static boolean isMultiTap(MotionEvent firstUp, MotionEvent secondUp, 20 int multiTapTimeSlop, int multiTapDistanceSlop) { 21 if (firstUp == null || secondUp == null) return false; 22 return eventsWithinTimeAndDistanceSlop(firstUp, secondUp, multiTapTimeSlop, 23 multiTapDistanceSlop); 24 } 25 eventsWithinTimeAndDistanceSlop(MotionEvent first, MotionEvent second, int timeout, int distance)26 private static boolean eventsWithinTimeAndDistanceSlop(MotionEvent first, MotionEvent second, 27 int timeout, int distance) { 28 if (isTimedOut(first, second, timeout)) { 29 return false; 30 } 31 final double deltaMove = distance(first, second); 32 if (deltaMove >= distance) { 33 return false; 34 } 35 return true; 36 } 37 distance(MotionEvent first, MotionEvent second)38 public static double distance(MotionEvent first, MotionEvent second) { 39 return MathUtils.dist(first.getX(), first.getY(), second.getX(), second.getY()); 40 } 41 42 /** 43 * Returns the minimum distance between {@code pointerDown} and each pointer of 44 * {@link MotionEvent}. 45 * 46 * @param pointerDown The action pointer location of the {@link MotionEvent} with 47 * {@link MotionEvent#ACTION_DOWN} or {@link MotionEvent#ACTION_POINTER_DOWN} 48 * @param moveEvent The {@link MotionEvent} with {@link MotionEvent#ACTION_MOVE} 49 * @return the movement of the pointer. 50 */ distanceClosestPointerToPoint(PointF pointerDown, MotionEvent moveEvent)51 public static double distanceClosestPointerToPoint(PointF pointerDown, MotionEvent moveEvent) { 52 float movement = Float.MAX_VALUE; 53 for (int i = 0; i < moveEvent.getPointerCount(); i++) { 54 final float moveDelta = MathUtils.dist(pointerDown.x, pointerDown.y, moveEvent.getX(i), 55 moveEvent.getY(i)); 56 if (movement > moveDelta) { 57 movement = moveDelta; 58 } 59 } 60 return movement; 61 } 62 isTimedOut(MotionEvent firstUp, MotionEvent secondUp, int timeout)63 public static boolean isTimedOut(MotionEvent firstUp, MotionEvent secondUp, int timeout) { 64 final long deltaTime = secondUp.getEventTime() - firstUp.getEventTime(); 65 return (deltaTime >= timeout); 66 } 67 68 /** 69 * Determines whether a two pointer gesture is a dragging one. 70 * 71 * @return True if the gesture is a dragging one. 72 */ isDraggingGesture(float firstPtrDownX, float firstPtrDownY, float secondPtrDownX, float secondPtrDownY, float firstPtrX, float firstPtrY, float secondPtrX, float secondPtrY, float maxDraggingAngleCos)73 public static boolean isDraggingGesture(float firstPtrDownX, float firstPtrDownY, 74 float secondPtrDownX, float secondPtrDownY, float firstPtrX, float firstPtrY, 75 float secondPtrX, float secondPtrY, float maxDraggingAngleCos) { 76 77 // Check if the pointers are moving in the same direction. 78 final float firstDeltaX = firstPtrX - firstPtrDownX; 79 final float firstDeltaY = firstPtrY - firstPtrDownY; 80 81 if (firstDeltaX == 0 && firstDeltaY == 0) { 82 return true; 83 } 84 85 final float firstMagnitude = (float) Math.hypot(firstDeltaX, firstDeltaY); 86 final float firstXNormalized = 87 (firstMagnitude > 0) ? firstDeltaX / firstMagnitude : firstDeltaX; 88 final float firstYNormalized = 89 (firstMagnitude > 0) ? firstDeltaY / firstMagnitude : firstDeltaY; 90 91 final float secondDeltaX = secondPtrX - secondPtrDownX; 92 final float secondDeltaY = secondPtrY - secondPtrDownY; 93 94 if (secondDeltaX == 0 && secondDeltaY == 0) { 95 return true; 96 } 97 98 final float secondMagnitude = (float) Math.hypot(secondDeltaX, secondDeltaY); 99 final float secondXNormalized = 100 (secondMagnitude > 0) ? secondDeltaX / secondMagnitude : secondDeltaX; 101 final float secondYNormalized = 102 (secondMagnitude > 0) ? secondDeltaY / secondMagnitude : secondDeltaY; 103 104 final float angleCos = 105 firstXNormalized * secondXNormalized + firstYNormalized * secondYNormalized; 106 107 if (angleCos < maxDraggingAngleCos) { 108 return false; 109 } 110 111 return true; 112 } 113 114 /** 115 * Gets the index of the pointer that went up or down from a motion event. 116 */ getActionIndex(MotionEvent event)117 public static int getActionIndex(MotionEvent event) { 118 return (event.getAction() 119 & MotionEvent.ACTION_POINTER_INDEX_MASK) >> MotionEvent.ACTION_POINTER_INDEX_SHIFT; 120 } 121 } 122