blob: ec304184835640b291cb790d5a008c3c447d6d6a [file] [log] [blame]
package com.android.server.accessibility.gestures;
import android.graphics.PointF;
import android.util.MathUtils;
import android.view.MotionEvent;
/**
* Some helper functions for gesture detection.
*/
public final class GestureUtils {
public static int MM_PER_CM = 10;
public static float CM_PER_INCH = 2.54f;
private GestureUtils() {
/* cannot be instantiated */
}
public static boolean isMultiTap(MotionEvent firstUp, MotionEvent secondUp,
int multiTapTimeSlop, int multiTapDistanceSlop) {
if (firstUp == null || secondUp == null) return false;
return eventsWithinTimeAndDistanceSlop(firstUp, secondUp, multiTapTimeSlop,
multiTapDistanceSlop);
}
private static boolean eventsWithinTimeAndDistanceSlop(MotionEvent first, MotionEvent second,
int timeout, int distance) {
if (isTimedOut(first, second, timeout)) {
return false;
}
final double deltaMove = distance(first, second);
if (deltaMove >= distance) {
return false;
}
return true;
}
public static double distance(MotionEvent first, MotionEvent second) {
return MathUtils.dist(first.getX(), first.getY(), second.getX(), second.getY());
}
/**
* Returns the minimum distance between {@code pointerDown} and each pointer of
* {@link MotionEvent}.
*
* @param pointerDown The action pointer location of the {@link MotionEvent} with
* {@link MotionEvent#ACTION_DOWN} or {@link MotionEvent#ACTION_POINTER_DOWN}
* @param moveEvent The {@link MotionEvent} with {@link MotionEvent#ACTION_MOVE}
* @return the movement of the pointer.
*/
public static double distanceClosestPointerToPoint(PointF pointerDown, MotionEvent moveEvent) {
float movement = Float.MAX_VALUE;
for (int i = 0; i < moveEvent.getPointerCount(); i++) {
final float moveDelta = MathUtils.dist(pointerDown.x, pointerDown.y, moveEvent.getX(i),
moveEvent.getY(i));
if (movement > moveDelta) {
movement = moveDelta;
}
}
return movement;
}
public static boolean isTimedOut(MotionEvent firstUp, MotionEvent secondUp, int timeout) {
final long deltaTime = secondUp.getEventTime() - firstUp.getEventTime();
return (deltaTime >= timeout);
}
/**
* Determines whether a two pointer gesture is a dragging one.
*
* @return True if the gesture is a dragging one.
*/
public static boolean isDraggingGesture(float firstPtrDownX, float firstPtrDownY,
float secondPtrDownX, float secondPtrDownY, float firstPtrX, float firstPtrY,
float secondPtrX, float secondPtrY, float maxDraggingAngleCos) {
// Check if the pointers are moving in the same direction.
final float firstDeltaX = firstPtrX - firstPtrDownX;
final float firstDeltaY = firstPtrY - firstPtrDownY;
if (firstDeltaX == 0 && firstDeltaY == 0) {
return true;
}
final float firstMagnitude = (float) Math.hypot(firstDeltaX, firstDeltaY);
final float firstXNormalized =
(firstMagnitude > 0) ? firstDeltaX / firstMagnitude : firstDeltaX;
final float firstYNormalized =
(firstMagnitude > 0) ? firstDeltaY / firstMagnitude : firstDeltaY;
final float secondDeltaX = secondPtrX - secondPtrDownX;
final float secondDeltaY = secondPtrY - secondPtrDownY;
if (secondDeltaX == 0 && secondDeltaY == 0) {
return true;
}
final float secondMagnitude = (float) Math.hypot(secondDeltaX, secondDeltaY);
final float secondXNormalized =
(secondMagnitude > 0) ? secondDeltaX / secondMagnitude : secondDeltaX;
final float secondYNormalized =
(secondMagnitude > 0) ? secondDeltaY / secondMagnitude : secondDeltaY;
final float angleCos =
firstXNormalized * secondXNormalized + firstYNormalized * secondYNormalized;
if (angleCos < maxDraggingAngleCos) {
return false;
}
return true;
}
/**
* Gets the index of the pointer that went up or down from a motion event.
*/
public static int getActionIndex(MotionEvent event) {
return (event.getAction()
& MotionEvent.ACTION_POINTER_INDEX_MASK) >> MotionEvent.ACTION_POINTER_INDEX_SHIFT;
}
}