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 721cf70bbf96930662cab0e699d70b62865766ff52Svetoslav Ganov final float firstMagnitude = 731cf70bbf96930662cab0e699d70b62865766ff52Svetoslav Ganov (float) Math.sqrt(firstDeltaX * firstDeltaX + firstDeltaY * firstDeltaY); 741cf70bbf96930662cab0e699d70b62865766ff52Svetoslav Ganov final float firstXNormalized = 751cf70bbf96930662cab0e699d70b62865766ff52Svetoslav Ganov (firstMagnitude > 0) ? firstDeltaX / firstMagnitude : firstDeltaX; 761cf70bbf96930662cab0e699d70b62865766ff52Svetoslav Ganov final float firstYNormalized = 771cf70bbf96930662cab0e699d70b62865766ff52Svetoslav Ganov (firstMagnitude > 0) ? firstDeltaY / firstMagnitude : firstDeltaY; 781cf70bbf96930662cab0e699d70b62865766ff52Svetoslav Ganov 791cf70bbf96930662cab0e699d70b62865766ff52Svetoslav Ganov final float secondDeltaX = secondPtrX - secondPtrDownX; 801cf70bbf96930662cab0e699d70b62865766ff52Svetoslav Ganov final float secondDeltaY = secondPtrY - secondPtrDownY; 811cf70bbf96930662cab0e699d70b62865766ff52Svetoslav Ganov 821cf70bbf96930662cab0e699d70b62865766ff52Svetoslav Ganov if (secondDeltaX == 0 && secondDeltaY == 0) { 831cf70bbf96930662cab0e699d70b62865766ff52Svetoslav Ganov return true; 841cf70bbf96930662cab0e699d70b62865766ff52Svetoslav Ganov } 851cf70bbf96930662cab0e699d70b62865766ff52Svetoslav Ganov 861cf70bbf96930662cab0e699d70b62865766ff52Svetoslav Ganov final float secondMagnitude = 871cf70bbf96930662cab0e699d70b62865766ff52Svetoslav Ganov (float) Math.sqrt(secondDeltaX * secondDeltaX + secondDeltaY * secondDeltaY); 881cf70bbf96930662cab0e699d70b62865766ff52Svetoslav Ganov final float secondXNormalized = 891cf70bbf96930662cab0e699d70b62865766ff52Svetoslav Ganov (secondMagnitude > 0) ? secondDeltaX / secondMagnitude : secondDeltaX; 901cf70bbf96930662cab0e699d70b62865766ff52Svetoslav Ganov final float secondYNormalized = 911cf70bbf96930662cab0e699d70b62865766ff52Svetoslav Ganov (secondMagnitude > 0) ? secondDeltaY / secondMagnitude : secondDeltaY; 921cf70bbf96930662cab0e699d70b62865766ff52Svetoslav Ganov 931cf70bbf96930662cab0e699d70b62865766ff52Svetoslav Ganov final float angleCos = 941cf70bbf96930662cab0e699d70b62865766ff52Svetoslav Ganov firstXNormalized * secondXNormalized + firstYNormalized * secondYNormalized; 951cf70bbf96930662cab0e699d70b62865766ff52Svetoslav Ganov 961cf70bbf96930662cab0e699d70b62865766ff52Svetoslav Ganov if (angleCos < maxDraggingAngleCos) { 971cf70bbf96930662cab0e699d70b62865766ff52Svetoslav Ganov return false; 981cf70bbf96930662cab0e699d70b62865766ff52Svetoslav Ganov } 991cf70bbf96930662cab0e699d70b62865766ff52Svetoslav Ganov 1001cf70bbf96930662cab0e699d70b62865766ff52Svetoslav Ganov return true; 1011cf70bbf96930662cab0e699d70b62865766ff52Svetoslav Ganov } 1021cf70bbf96930662cab0e699d70b62865766ff52Svetoslav Ganov} 103