Loading services/accessibility/java/com/android/server/accessibility/AccessibilityGestureDetector.java +87 −11 Original line number Diff line number Diff line Loading @@ -27,6 +27,8 @@ import android.gesture.Prediction; import android.util.Slog; import android.view.GestureDetector; import android.view.MotionEvent; import android.view.VelocityTracker; import android.view.ViewConfiguration; import com.android.internal.R; Loading @@ -46,7 +48,9 @@ class AccessibilityGestureDetector extends GestureDetector.SimpleOnGestureListen public interface Listener { public void onDoubleTapAndHold(MotionEvent event, int policyFlags); public boolean onDoubleTap(MotionEvent event, int policyFlags); public boolean onGesture(int gestureId); public boolean onGestureCompleted(int gestureId); public void onGestureStarted(); public void onGestureCancelled(MotionEvent event, int policyFlags); } private final Listener mListener; Loading @@ -64,6 +68,10 @@ class AccessibilityGestureDetector extends GestureDetector.SimpleOnGestureListen // Indicates that motion events are being collected to match a gesture. private boolean mRecognizingGesture; // Indicates that we've collected enough data to be sure it could be a // gesture. private boolean mGestureConfirmed; // Indicates that motion events from the second pointer are being checked // for a double tap. private boolean mSecondFingerDoubleTap; Loading @@ -81,18 +89,41 @@ class AccessibilityGestureDetector extends GestureDetector.SimpleOnGestureListen // The Y of the previous event. private float mPreviousY; // The X of the down event. private float mBaseX; // The Y of the down event. private float mBaseY; // Slop between the first and second tap to be a double tap. private final int mDoubleTapSlop; // The scaled velocity above which we detect gestures. private final int mScaledGestureDetectionVelocity; // Buffer for storing points for gesture detection. private final ArrayList<GesturePoint> mStrokeBuffer = new ArrayList<GesturePoint>(100); // Helper to track gesture velocity. private final VelocityTracker mVelocityTracker = VelocityTracker.obtain(); // The minimal delta between moves to add a gesture point. private static final int TOUCH_TOLERANCE = 3; // The minimal score for accepting a predicted gesture. private static final float MIN_PREDICTION_SCORE = 2.0f; // The velocity above which we detect gestures. Expressed in DIPs/Second. private static final int GESTURE_DETECTION_VELOCITY_DIP = 1000; // Constant used to calculate velocity in seconds. private static final int VELOCITY_UNITS_SECONDS = 1000; AccessibilityGestureDetector(Context context, Listener listener) { mListener = listener; mDoubleTapSlop = ViewConfiguration.get(context).getScaledDoubleTapSlop(); mGestureDetector = new GestureDetector(context, this); mGestureDetector.setOnDoubleTapListener(this); Loading @@ -100,9 +131,14 @@ class AccessibilityGestureDetector extends GestureDetector.SimpleOnGestureListen mGestureLibrary.setOrientationStyle(8 /* GestureStore.ORIENTATION_SENSITIVE_8 */); mGestureLibrary.setSequenceType(GestureStore.SEQUENCE_SENSITIVE); mGestureLibrary.load(); final float density = context.getResources().getDisplayMetrics().density; mScaledGestureDetectionVelocity = (int) (GESTURE_DETECTION_VELOCITY_DIP * density); } public boolean onMotionEvent(MotionEvent event, int policyFlags) { mVelocityTracker.addMovement(event); final float x = event.getX(); final float y = event.getY(); Loading @@ -112,14 +148,48 @@ class AccessibilityGestureDetector extends GestureDetector.SimpleOnGestureListen mDoubleTapDetected = false; mSecondFingerDoubleTap = false; mRecognizingGesture = true; mGestureConfirmed = false; mBaseX = x; mBaseY = y; mPreviousX = x; mPreviousY = y; mStrokeBuffer.clear(); mStrokeBuffer.add(new GesturePoint(x, y, event.getEventTime())); mVelocityTracker.clear(); mVelocityTracker.addMovement(event); break; case MotionEvent.ACTION_MOVE: if (mRecognizingGesture) { if (!mGestureConfirmed) { mVelocityTracker.addMovement(event); // It is *important* to use the distance traveled by the pointers // on the screen which may or may not be magnified. final float deltaX = mBaseX - event.getX(0); final float deltaY = mBaseY - event.getY(0); final double moveDelta = Math.hypot(deltaX, deltaY); // The user has moved enough for us to decide. if (moveDelta > mDoubleTapSlop) { // Check whether the user is performing a gesture. We // detect gestures if the pointer is moving above a // given velocity. mVelocityTracker.computeCurrentVelocity(VELOCITY_UNITS_SECONDS); final float maxAbsVelocity = Math.max( Math.abs(mVelocityTracker.getXVelocity(0)), Math.abs(mVelocityTracker.getYVelocity(0))); if (maxAbsVelocity > mScaledGestureDetectionVelocity) { // We have to perform gesture detection, so // notify the listener. mGestureConfirmed = true; mListener.onGestureStarted(); } else { // This won't match any gesture, so notify the // listener. cancelGesture(); mListener.onGestureCancelled(event, policyFlags); } } } final float dX = Math.abs(x - mPreviousX); final float dY = Math.abs(y - mPreviousY); if (dX >= TOUCH_TOLERANCE || dY >= TOUCH_TOLERANCE) { Loading @@ -134,12 +204,13 @@ class AccessibilityGestureDetector extends GestureDetector.SimpleOnGestureListen if (maybeFinishDoubleTap(event, policyFlags)) { return true; } if (mRecognizingGesture) { if (mGestureConfirmed) { mStrokeBuffer.add(new GesturePoint(x, y, event.getEventTime())); if (recognizeGesture()) { return true; if (!recognizeGesture()) { mListener.onGestureCancelled(event, policyFlags); } return true; } break; Loading Loading @@ -167,6 +238,10 @@ class AccessibilityGestureDetector extends GestureDetector.SimpleOnGestureListen return true; } break; case MotionEvent.ACTION_CANCEL: clear(); break; } // If we're detecting taps on the second finger, map events from the Loading Loading @@ -194,18 +269,13 @@ class AccessibilityGestureDetector extends GestureDetector.SimpleOnGestureListen mDoubleTapDetected = false; mSecondFingerDoubleTap = false; cancelGesture(); mStrokeBuffer.clear(); mVelocityTracker.clear(); } public boolean firstTapDetected() { return mFirstTapDetected; } public void cancelGesture() { mRecognizingGesture = false; mStrokeBuffer.clear(); } @Override public void onLongPress(MotionEvent e) { maybeSendLongPress(e, mPolicyFlags); Loading Loading @@ -251,6 +321,12 @@ class AccessibilityGestureDetector extends GestureDetector.SimpleOnGestureListen return mListener.onDoubleTap(event, policyFlags); } private void cancelGesture() { mRecognizingGesture = false; mGestureConfirmed = false; mStrokeBuffer.clear(); } private boolean recognizeGesture() { Gesture gesture = new Gesture(); gesture.addStroke(new GestureStroke(mStrokeBuffer)); Loading @@ -265,7 +341,7 @@ class AccessibilityGestureDetector extends GestureDetector.SimpleOnGestureListen } try { final int gestureId = Integer.parseInt(bestPrediction.name); if (mListener.onGesture(gestureId)) { if (mListener.onGestureCompleted(gestureId)) { return true; } } catch (NumberFormatException nfe) { Loading services/accessibility/java/com/android/server/accessibility/TouchExplorer.java +53 −98 Original line number Diff line number Diff line Loading @@ -25,7 +25,6 @@ import android.view.KeyEvent; import android.view.MotionEvent; import android.view.MotionEvent.PointerCoords; import android.view.MotionEvent.PointerProperties; import android.view.VelocityTracker; import android.view.ViewConfiguration; import android.view.WindowManagerPolicy; import android.view.accessibility.AccessibilityEvent; Loading Loading @@ -87,9 +86,6 @@ class TouchExplorer implements EventStreamTransformation, AccessibilityGestureDe // Invalid pointer ID. private static final int INVALID_POINTER_ID = -1; // The velocity above which we detect gestures. private static final int GESTURE_DETECTION_VELOCITY_DIP = 1000; // The minimal distance before we take the middle of the distance between // the two dragging pointers as opposed to use the location of the primary one. private static final int MIN_POINTER_DISTANCE_TO_USE_MIDDLE_LOCATION_DIP = 200; Loading Loading @@ -134,15 +130,9 @@ class TouchExplorer implements EventStreamTransformation, AccessibilityGestureDe // the two dragging pointers as opposed to use the location of the primary one. private final int mScaledMinPointerDistanceToUseMiddleLocation; // The scaled velocity above which we detect gestures. private final int mScaledGestureDetectionVelocity; // The handler to which to delegate events. private EventStreamTransformation mNext; // Helper to track gesture velocity. private final VelocityTracker mVelocityTracker = VelocityTracker.obtain(); // Helper class to track received pointers. private final ReceivedPointerTracker mReceivedPointerTracker; Loading Loading @@ -200,7 +190,6 @@ class TouchExplorer implements EventStreamTransformation, AccessibilityGestureDe final float density = context.getResources().getDisplayMetrics().density; mScaledMinPointerDistanceToUseMiddleLocation = (int) (MIN_POINTER_DISTANCE_TO_USE_MIDDLE_LOCATION_DIP * density); mScaledGestureDetectionVelocity = (int) (GESTURE_DETECTION_VELOCITY_DIP * density); } @Override Loading Loading @@ -289,11 +278,19 @@ class TouchExplorer implements EventStreamTransformation, AccessibilityGestureDe mReceivedPointerTracker.onMotionEvent(rawEvent); if (mGestureDetector.onMotionEvent(event, policyFlags)) { // The motion detector is interested in the movements in physical space, // so it uses the rawEvent to ignore magnification and other // transformations. if (mGestureDetector.onMotionEvent(rawEvent, policyFlags)) { // Event was handled by the gesture detector. return; } if (event.getActionMasked() == MotionEvent.ACTION_CANCEL) { clear(event, policyFlags); return; } switch(mCurrentState) { case STATE_TOUCH_EXPLORING: { handleMotionEventStateTouchExploring(event, rawEvent, policyFlags); Loading @@ -305,7 +302,7 @@ class TouchExplorer implements EventStreamTransformation, AccessibilityGestureDe handleMotionEventStateDelegating(event, policyFlags); } break; case STATE_GESTURE_DETECTING: { handleMotionEventGestureDetecting(rawEvent, policyFlags); // Already handled. } break; default: throw new IllegalStateException("Illegal state: " + mCurrentState); Loading Loading @@ -440,26 +437,50 @@ class TouchExplorer implements EventStreamTransformation, AccessibilityGestureDe } @Override public boolean onGesture(int gestureId) { public boolean onGestureCompleted(int gestureId) { if (mCurrentState != STATE_GESTURE_DETECTING) { return false; } mAms.onTouchInteractionEnd(); // Announce the end of the gesture recognition. sendAccessibilityEvent(AccessibilityEvent.TYPE_GESTURE_DETECTION_END); // Announce the end of a the touch interaction. sendAccessibilityEvent(AccessibilityEvent.TYPE_TOUCH_INTERACTION_END); endGestureDetection(); mAms.onGesture(gestureId); mExitGestureDetectionModeDelayed.cancel(); mCurrentState = STATE_TOUCH_EXPLORING; return true; } @Override public void onGestureStarted() { // We have to perform gesture detection, so // clear the current state and try to detect. mCurrentState = STATE_GESTURE_DETECTING; mSendHoverEnterAndMoveDelayed.cancel(); mSendHoverExitDelayed.cancel(); mExitGestureDetectionModeDelayed.post(); // Send accessibility event to announce the start // of gesture recognition. sendAccessibilityEvent(AccessibilityEvent.TYPE_GESTURE_DETECTION_START); } @Override public void onGestureCancelled(MotionEvent event, int policyFlags) { if (mCurrentState == STATE_GESTURE_DETECTING) { endGestureDetection(); } else if (mCurrentState == STATE_TOUCH_EXPLORING) { final int pointerId = mReceivedPointerTracker.getPrimaryPointerId(); final int pointerIdBits = (1 << pointerId); // Cache the event until we discern exploration from gesturing. mSendHoverEnterAndMoveDelayed.addEvent(event); // We have just decided that the user is touch, // exploring so start sending events. mSendHoverEnterAndMoveDelayed.forceSendAndRemove(); mSendHoverExitDelayed.cancel(); sendMotionEvent(event, MotionEvent.ACTION_HOVER_MOVE, pointerIdBits, policyFlags); } } /** * Handles a motion event in touch exploring state. * Loading @@ -471,8 +492,6 @@ class TouchExplorer implements EventStreamTransformation, AccessibilityGestureDe int policyFlags) { ReceivedPointerTracker receivedTracker = mReceivedPointerTracker; mVelocityTracker.addMovement(rawEvent); switch (event.getActionMasked()) { case MotionEvent.ACTION_DOWN: { mAms.onTouchInteractionStart(); Loading Loading @@ -525,46 +544,6 @@ class TouchExplorer implements EventStreamTransformation, AccessibilityGestureDe if (mSendHoverEnterAndMoveDelayed.isPending()) { // Cache the event until we discern exploration from gesturing. mSendHoverEnterAndMoveDelayed.addEvent(event); // It is *important* to use the distance traveled by the pointers // on the screen which may or may not be magnified. final float deltaX = receivedTracker.getReceivedPointerDownX(pointerId) - rawEvent.getX(pointerIndex); final float deltaY = receivedTracker.getReceivedPointerDownY(pointerId) - rawEvent.getY(pointerIndex); final double moveDelta = Math.hypot(deltaX, deltaY); // The user has moved enough for us to decide. if (moveDelta > mDoubleTapSlop) { // Check whether the user is performing a gesture. We // detect gestures if the pointer is moving above a // given velocity. mVelocityTracker.computeCurrentVelocity(1000); final float maxAbsVelocity = Math.max( Math.abs(mVelocityTracker.getXVelocity(pointerId)), Math.abs(mVelocityTracker.getYVelocity(pointerId))); if (maxAbsVelocity > mScaledGestureDetectionVelocity) { // We have to perform gesture detection, so // clear the current state and try to detect. mCurrentState = STATE_GESTURE_DETECTING; mVelocityTracker.clear(); mSendHoverEnterAndMoveDelayed.cancel(); mSendHoverExitDelayed.cancel(); mExitGestureDetectionModeDelayed.post(); // Send accessibility event to announce the start // of gesture recognition. sendAccessibilityEvent( AccessibilityEvent.TYPE_GESTURE_DETECTION_START); } else { // We have just decided that the user is touch, // exploring so start sending events. mGestureDetector.cancelGesture(); mSendHoverEnterAndMoveDelayed.forceSendAndRemove(); mSendHoverExitDelayed.cancel(); sendMotionEvent(event, MotionEvent.ACTION_HOVER_MOVE, pointerIdBits, policyFlags); } break; } } else { if (mTouchExplorationInProgress) { sendTouchExplorationGestureStartAndHoverEnterIfNeeded(policyFlags); Loading Loading @@ -602,11 +581,6 @@ class TouchExplorer implements EventStreamTransformation, AccessibilityGestureDe } } // We know that a new state transition is to happen and the // new state will not be gesture recognition, so cancel // the gesture. mGestureDetector.cancelGesture(); if (isDraggingGesture(event)) { // Two pointers moving in the same direction within // a given distance perform a drag. Loading @@ -620,7 +594,6 @@ class TouchExplorer implements EventStreamTransformation, AccessibilityGestureDe mCurrentState = STATE_DELEGATING; sendDownForAllNotInjectedPointers(event, policyFlags); } mVelocityTracker.clear(); } break; default: { // More than one pointer so the user is not touch exploring Loading @@ -639,7 +612,6 @@ class TouchExplorer implements EventStreamTransformation, AccessibilityGestureDe // More than two pointers are delegated to the view hierarchy. mCurrentState = STATE_DELEGATING; sendDownForAllNotInjectedPointers(event, policyFlags); mVelocityTracker.clear(); } } } break; Loading @@ -648,8 +620,6 @@ class TouchExplorer implements EventStreamTransformation, AccessibilityGestureDe final int pointerId = event.getPointerId(event.getActionIndex()); final int pointerIdBits = (1 << pointerId); mVelocityTracker.clear(); if (mSendHoverEnterAndMoveDelayed.isPending()) { // If we have not delivered the enter schedule an exit. mSendHoverExitDelayed.post(event, pointerIdBits, policyFlags); Loading @@ -663,9 +633,6 @@ class TouchExplorer implements EventStreamTransformation, AccessibilityGestureDe } } break; case MotionEvent.ACTION_CANCEL: { clear(event, policyFlags); } break; } } Loading Loading @@ -756,9 +723,6 @@ class TouchExplorer implements EventStreamTransformation, AccessibilityGestureDe } mCurrentState = STATE_TOUCH_EXPLORING; } break; case MotionEvent.ACTION_CANCEL: { clear(event, policyFlags); } break; } } Loading Loading @@ -795,9 +759,6 @@ class TouchExplorer implements EventStreamTransformation, AccessibilityGestureDe mCurrentState = STATE_TOUCH_EXPLORING; } break; case MotionEvent.ACTION_CANCEL: { clear(event, policyFlags); } break; default: { // Deliver the event. sendMotionEvent(event, event.getAction(), ALL_POINTER_ID_BITS, policyFlags); Loading @@ -805,10 +766,9 @@ class TouchExplorer implements EventStreamTransformation, AccessibilityGestureDe } } private void handleMotionEventGestureDetecting(MotionEvent event, int policyFlags) { switch (event.getActionMasked()) { case MotionEvent.ACTION_UP: { private void endGestureDetection() { mAms.onTouchInteractionEnd(); // Announce the end of the gesture recognition. sendAccessibilityEvent(AccessibilityEvent.TYPE_GESTURE_DETECTION_END); // Announce the end of a the touch interaction. Loading @@ -816,11 +776,6 @@ class TouchExplorer implements EventStreamTransformation, AccessibilityGestureDe mExitGestureDetectionModeDelayed.cancel(); mCurrentState = STATE_TOUCH_EXPLORING; } break; case MotionEvent.ACTION_CANCEL: { clear(event, policyFlags); } break; } } /** Loading Loading
services/accessibility/java/com/android/server/accessibility/AccessibilityGestureDetector.java +87 −11 Original line number Diff line number Diff line Loading @@ -27,6 +27,8 @@ import android.gesture.Prediction; import android.util.Slog; import android.view.GestureDetector; import android.view.MotionEvent; import android.view.VelocityTracker; import android.view.ViewConfiguration; import com.android.internal.R; Loading @@ -46,7 +48,9 @@ class AccessibilityGestureDetector extends GestureDetector.SimpleOnGestureListen public interface Listener { public void onDoubleTapAndHold(MotionEvent event, int policyFlags); public boolean onDoubleTap(MotionEvent event, int policyFlags); public boolean onGesture(int gestureId); public boolean onGestureCompleted(int gestureId); public void onGestureStarted(); public void onGestureCancelled(MotionEvent event, int policyFlags); } private final Listener mListener; Loading @@ -64,6 +68,10 @@ class AccessibilityGestureDetector extends GestureDetector.SimpleOnGestureListen // Indicates that motion events are being collected to match a gesture. private boolean mRecognizingGesture; // Indicates that we've collected enough data to be sure it could be a // gesture. private boolean mGestureConfirmed; // Indicates that motion events from the second pointer are being checked // for a double tap. private boolean mSecondFingerDoubleTap; Loading @@ -81,18 +89,41 @@ class AccessibilityGestureDetector extends GestureDetector.SimpleOnGestureListen // The Y of the previous event. private float mPreviousY; // The X of the down event. private float mBaseX; // The Y of the down event. private float mBaseY; // Slop between the first and second tap to be a double tap. private final int mDoubleTapSlop; // The scaled velocity above which we detect gestures. private final int mScaledGestureDetectionVelocity; // Buffer for storing points for gesture detection. private final ArrayList<GesturePoint> mStrokeBuffer = new ArrayList<GesturePoint>(100); // Helper to track gesture velocity. private final VelocityTracker mVelocityTracker = VelocityTracker.obtain(); // The minimal delta between moves to add a gesture point. private static final int TOUCH_TOLERANCE = 3; // The minimal score for accepting a predicted gesture. private static final float MIN_PREDICTION_SCORE = 2.0f; // The velocity above which we detect gestures. Expressed in DIPs/Second. private static final int GESTURE_DETECTION_VELOCITY_DIP = 1000; // Constant used to calculate velocity in seconds. private static final int VELOCITY_UNITS_SECONDS = 1000; AccessibilityGestureDetector(Context context, Listener listener) { mListener = listener; mDoubleTapSlop = ViewConfiguration.get(context).getScaledDoubleTapSlop(); mGestureDetector = new GestureDetector(context, this); mGestureDetector.setOnDoubleTapListener(this); Loading @@ -100,9 +131,14 @@ class AccessibilityGestureDetector extends GestureDetector.SimpleOnGestureListen mGestureLibrary.setOrientationStyle(8 /* GestureStore.ORIENTATION_SENSITIVE_8 */); mGestureLibrary.setSequenceType(GestureStore.SEQUENCE_SENSITIVE); mGestureLibrary.load(); final float density = context.getResources().getDisplayMetrics().density; mScaledGestureDetectionVelocity = (int) (GESTURE_DETECTION_VELOCITY_DIP * density); } public boolean onMotionEvent(MotionEvent event, int policyFlags) { mVelocityTracker.addMovement(event); final float x = event.getX(); final float y = event.getY(); Loading @@ -112,14 +148,48 @@ class AccessibilityGestureDetector extends GestureDetector.SimpleOnGestureListen mDoubleTapDetected = false; mSecondFingerDoubleTap = false; mRecognizingGesture = true; mGestureConfirmed = false; mBaseX = x; mBaseY = y; mPreviousX = x; mPreviousY = y; mStrokeBuffer.clear(); mStrokeBuffer.add(new GesturePoint(x, y, event.getEventTime())); mVelocityTracker.clear(); mVelocityTracker.addMovement(event); break; case MotionEvent.ACTION_MOVE: if (mRecognizingGesture) { if (!mGestureConfirmed) { mVelocityTracker.addMovement(event); // It is *important* to use the distance traveled by the pointers // on the screen which may or may not be magnified. final float deltaX = mBaseX - event.getX(0); final float deltaY = mBaseY - event.getY(0); final double moveDelta = Math.hypot(deltaX, deltaY); // The user has moved enough for us to decide. if (moveDelta > mDoubleTapSlop) { // Check whether the user is performing a gesture. We // detect gestures if the pointer is moving above a // given velocity. mVelocityTracker.computeCurrentVelocity(VELOCITY_UNITS_SECONDS); final float maxAbsVelocity = Math.max( Math.abs(mVelocityTracker.getXVelocity(0)), Math.abs(mVelocityTracker.getYVelocity(0))); if (maxAbsVelocity > mScaledGestureDetectionVelocity) { // We have to perform gesture detection, so // notify the listener. mGestureConfirmed = true; mListener.onGestureStarted(); } else { // This won't match any gesture, so notify the // listener. cancelGesture(); mListener.onGestureCancelled(event, policyFlags); } } } final float dX = Math.abs(x - mPreviousX); final float dY = Math.abs(y - mPreviousY); if (dX >= TOUCH_TOLERANCE || dY >= TOUCH_TOLERANCE) { Loading @@ -134,12 +204,13 @@ class AccessibilityGestureDetector extends GestureDetector.SimpleOnGestureListen if (maybeFinishDoubleTap(event, policyFlags)) { return true; } if (mRecognizingGesture) { if (mGestureConfirmed) { mStrokeBuffer.add(new GesturePoint(x, y, event.getEventTime())); if (recognizeGesture()) { return true; if (!recognizeGesture()) { mListener.onGestureCancelled(event, policyFlags); } return true; } break; Loading Loading @@ -167,6 +238,10 @@ class AccessibilityGestureDetector extends GestureDetector.SimpleOnGestureListen return true; } break; case MotionEvent.ACTION_CANCEL: clear(); break; } // If we're detecting taps on the second finger, map events from the Loading Loading @@ -194,18 +269,13 @@ class AccessibilityGestureDetector extends GestureDetector.SimpleOnGestureListen mDoubleTapDetected = false; mSecondFingerDoubleTap = false; cancelGesture(); mStrokeBuffer.clear(); mVelocityTracker.clear(); } public boolean firstTapDetected() { return mFirstTapDetected; } public void cancelGesture() { mRecognizingGesture = false; mStrokeBuffer.clear(); } @Override public void onLongPress(MotionEvent e) { maybeSendLongPress(e, mPolicyFlags); Loading Loading @@ -251,6 +321,12 @@ class AccessibilityGestureDetector extends GestureDetector.SimpleOnGestureListen return mListener.onDoubleTap(event, policyFlags); } private void cancelGesture() { mRecognizingGesture = false; mGestureConfirmed = false; mStrokeBuffer.clear(); } private boolean recognizeGesture() { Gesture gesture = new Gesture(); gesture.addStroke(new GestureStroke(mStrokeBuffer)); Loading @@ -265,7 +341,7 @@ class AccessibilityGestureDetector extends GestureDetector.SimpleOnGestureListen } try { final int gestureId = Integer.parseInt(bestPrediction.name); if (mListener.onGesture(gestureId)) { if (mListener.onGestureCompleted(gestureId)) { return true; } } catch (NumberFormatException nfe) { Loading
services/accessibility/java/com/android/server/accessibility/TouchExplorer.java +53 −98 Original line number Diff line number Diff line Loading @@ -25,7 +25,6 @@ import android.view.KeyEvent; import android.view.MotionEvent; import android.view.MotionEvent.PointerCoords; import android.view.MotionEvent.PointerProperties; import android.view.VelocityTracker; import android.view.ViewConfiguration; import android.view.WindowManagerPolicy; import android.view.accessibility.AccessibilityEvent; Loading Loading @@ -87,9 +86,6 @@ class TouchExplorer implements EventStreamTransformation, AccessibilityGestureDe // Invalid pointer ID. private static final int INVALID_POINTER_ID = -1; // The velocity above which we detect gestures. private static final int GESTURE_DETECTION_VELOCITY_DIP = 1000; // The minimal distance before we take the middle of the distance between // the two dragging pointers as opposed to use the location of the primary one. private static final int MIN_POINTER_DISTANCE_TO_USE_MIDDLE_LOCATION_DIP = 200; Loading Loading @@ -134,15 +130,9 @@ class TouchExplorer implements EventStreamTransformation, AccessibilityGestureDe // the two dragging pointers as opposed to use the location of the primary one. private final int mScaledMinPointerDistanceToUseMiddleLocation; // The scaled velocity above which we detect gestures. private final int mScaledGestureDetectionVelocity; // The handler to which to delegate events. private EventStreamTransformation mNext; // Helper to track gesture velocity. private final VelocityTracker mVelocityTracker = VelocityTracker.obtain(); // Helper class to track received pointers. private final ReceivedPointerTracker mReceivedPointerTracker; Loading Loading @@ -200,7 +190,6 @@ class TouchExplorer implements EventStreamTransformation, AccessibilityGestureDe final float density = context.getResources().getDisplayMetrics().density; mScaledMinPointerDistanceToUseMiddleLocation = (int) (MIN_POINTER_DISTANCE_TO_USE_MIDDLE_LOCATION_DIP * density); mScaledGestureDetectionVelocity = (int) (GESTURE_DETECTION_VELOCITY_DIP * density); } @Override Loading Loading @@ -289,11 +278,19 @@ class TouchExplorer implements EventStreamTransformation, AccessibilityGestureDe mReceivedPointerTracker.onMotionEvent(rawEvent); if (mGestureDetector.onMotionEvent(event, policyFlags)) { // The motion detector is interested in the movements in physical space, // so it uses the rawEvent to ignore magnification and other // transformations. if (mGestureDetector.onMotionEvent(rawEvent, policyFlags)) { // Event was handled by the gesture detector. return; } if (event.getActionMasked() == MotionEvent.ACTION_CANCEL) { clear(event, policyFlags); return; } switch(mCurrentState) { case STATE_TOUCH_EXPLORING: { handleMotionEventStateTouchExploring(event, rawEvent, policyFlags); Loading @@ -305,7 +302,7 @@ class TouchExplorer implements EventStreamTransformation, AccessibilityGestureDe handleMotionEventStateDelegating(event, policyFlags); } break; case STATE_GESTURE_DETECTING: { handleMotionEventGestureDetecting(rawEvent, policyFlags); // Already handled. } break; default: throw new IllegalStateException("Illegal state: " + mCurrentState); Loading Loading @@ -440,26 +437,50 @@ class TouchExplorer implements EventStreamTransformation, AccessibilityGestureDe } @Override public boolean onGesture(int gestureId) { public boolean onGestureCompleted(int gestureId) { if (mCurrentState != STATE_GESTURE_DETECTING) { return false; } mAms.onTouchInteractionEnd(); // Announce the end of the gesture recognition. sendAccessibilityEvent(AccessibilityEvent.TYPE_GESTURE_DETECTION_END); // Announce the end of a the touch interaction. sendAccessibilityEvent(AccessibilityEvent.TYPE_TOUCH_INTERACTION_END); endGestureDetection(); mAms.onGesture(gestureId); mExitGestureDetectionModeDelayed.cancel(); mCurrentState = STATE_TOUCH_EXPLORING; return true; } @Override public void onGestureStarted() { // We have to perform gesture detection, so // clear the current state and try to detect. mCurrentState = STATE_GESTURE_DETECTING; mSendHoverEnterAndMoveDelayed.cancel(); mSendHoverExitDelayed.cancel(); mExitGestureDetectionModeDelayed.post(); // Send accessibility event to announce the start // of gesture recognition. sendAccessibilityEvent(AccessibilityEvent.TYPE_GESTURE_DETECTION_START); } @Override public void onGestureCancelled(MotionEvent event, int policyFlags) { if (mCurrentState == STATE_GESTURE_DETECTING) { endGestureDetection(); } else if (mCurrentState == STATE_TOUCH_EXPLORING) { final int pointerId = mReceivedPointerTracker.getPrimaryPointerId(); final int pointerIdBits = (1 << pointerId); // Cache the event until we discern exploration from gesturing. mSendHoverEnterAndMoveDelayed.addEvent(event); // We have just decided that the user is touch, // exploring so start sending events. mSendHoverEnterAndMoveDelayed.forceSendAndRemove(); mSendHoverExitDelayed.cancel(); sendMotionEvent(event, MotionEvent.ACTION_HOVER_MOVE, pointerIdBits, policyFlags); } } /** * Handles a motion event in touch exploring state. * Loading @@ -471,8 +492,6 @@ class TouchExplorer implements EventStreamTransformation, AccessibilityGestureDe int policyFlags) { ReceivedPointerTracker receivedTracker = mReceivedPointerTracker; mVelocityTracker.addMovement(rawEvent); switch (event.getActionMasked()) { case MotionEvent.ACTION_DOWN: { mAms.onTouchInteractionStart(); Loading Loading @@ -525,46 +544,6 @@ class TouchExplorer implements EventStreamTransformation, AccessibilityGestureDe if (mSendHoverEnterAndMoveDelayed.isPending()) { // Cache the event until we discern exploration from gesturing. mSendHoverEnterAndMoveDelayed.addEvent(event); // It is *important* to use the distance traveled by the pointers // on the screen which may or may not be magnified. final float deltaX = receivedTracker.getReceivedPointerDownX(pointerId) - rawEvent.getX(pointerIndex); final float deltaY = receivedTracker.getReceivedPointerDownY(pointerId) - rawEvent.getY(pointerIndex); final double moveDelta = Math.hypot(deltaX, deltaY); // The user has moved enough for us to decide. if (moveDelta > mDoubleTapSlop) { // Check whether the user is performing a gesture. We // detect gestures if the pointer is moving above a // given velocity. mVelocityTracker.computeCurrentVelocity(1000); final float maxAbsVelocity = Math.max( Math.abs(mVelocityTracker.getXVelocity(pointerId)), Math.abs(mVelocityTracker.getYVelocity(pointerId))); if (maxAbsVelocity > mScaledGestureDetectionVelocity) { // We have to perform gesture detection, so // clear the current state and try to detect. mCurrentState = STATE_GESTURE_DETECTING; mVelocityTracker.clear(); mSendHoverEnterAndMoveDelayed.cancel(); mSendHoverExitDelayed.cancel(); mExitGestureDetectionModeDelayed.post(); // Send accessibility event to announce the start // of gesture recognition. sendAccessibilityEvent( AccessibilityEvent.TYPE_GESTURE_DETECTION_START); } else { // We have just decided that the user is touch, // exploring so start sending events. mGestureDetector.cancelGesture(); mSendHoverEnterAndMoveDelayed.forceSendAndRemove(); mSendHoverExitDelayed.cancel(); sendMotionEvent(event, MotionEvent.ACTION_HOVER_MOVE, pointerIdBits, policyFlags); } break; } } else { if (mTouchExplorationInProgress) { sendTouchExplorationGestureStartAndHoverEnterIfNeeded(policyFlags); Loading Loading @@ -602,11 +581,6 @@ class TouchExplorer implements EventStreamTransformation, AccessibilityGestureDe } } // We know that a new state transition is to happen and the // new state will not be gesture recognition, so cancel // the gesture. mGestureDetector.cancelGesture(); if (isDraggingGesture(event)) { // Two pointers moving in the same direction within // a given distance perform a drag. Loading @@ -620,7 +594,6 @@ class TouchExplorer implements EventStreamTransformation, AccessibilityGestureDe mCurrentState = STATE_DELEGATING; sendDownForAllNotInjectedPointers(event, policyFlags); } mVelocityTracker.clear(); } break; default: { // More than one pointer so the user is not touch exploring Loading @@ -639,7 +612,6 @@ class TouchExplorer implements EventStreamTransformation, AccessibilityGestureDe // More than two pointers are delegated to the view hierarchy. mCurrentState = STATE_DELEGATING; sendDownForAllNotInjectedPointers(event, policyFlags); mVelocityTracker.clear(); } } } break; Loading @@ -648,8 +620,6 @@ class TouchExplorer implements EventStreamTransformation, AccessibilityGestureDe final int pointerId = event.getPointerId(event.getActionIndex()); final int pointerIdBits = (1 << pointerId); mVelocityTracker.clear(); if (mSendHoverEnterAndMoveDelayed.isPending()) { // If we have not delivered the enter schedule an exit. mSendHoverExitDelayed.post(event, pointerIdBits, policyFlags); Loading @@ -663,9 +633,6 @@ class TouchExplorer implements EventStreamTransformation, AccessibilityGestureDe } } break; case MotionEvent.ACTION_CANCEL: { clear(event, policyFlags); } break; } } Loading Loading @@ -756,9 +723,6 @@ class TouchExplorer implements EventStreamTransformation, AccessibilityGestureDe } mCurrentState = STATE_TOUCH_EXPLORING; } break; case MotionEvent.ACTION_CANCEL: { clear(event, policyFlags); } break; } } Loading Loading @@ -795,9 +759,6 @@ class TouchExplorer implements EventStreamTransformation, AccessibilityGestureDe mCurrentState = STATE_TOUCH_EXPLORING; } break; case MotionEvent.ACTION_CANCEL: { clear(event, policyFlags); } break; default: { // Deliver the event. sendMotionEvent(event, event.getAction(), ALL_POINTER_ID_BITS, policyFlags); Loading @@ -805,10 +766,9 @@ class TouchExplorer implements EventStreamTransformation, AccessibilityGestureDe } } private void handleMotionEventGestureDetecting(MotionEvent event, int policyFlags) { switch (event.getActionMasked()) { case MotionEvent.ACTION_UP: { private void endGestureDetection() { mAms.onTouchInteractionEnd(); // Announce the end of the gesture recognition. sendAccessibilityEvent(AccessibilityEvent.TYPE_GESTURE_DETECTION_END); // Announce the end of a the touch interaction. Loading @@ -816,11 +776,6 @@ class TouchExplorer implements EventStreamTransformation, AccessibilityGestureDe mExitGestureDetectionModeDelayed.cancel(); mCurrentState = STATE_TOUCH_EXPLORING; } break; case MotionEvent.ACTION_CANCEL: { clear(event, policyFlags); } break; } } /** Loading