1736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov/* 2736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov ** Copyright 2011, The Android Open Source Project 3736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov ** 4736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov ** Licensed under the Apache License, Version 2.0 (the "License"); 5736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov ** you may not use this file except in compliance with the License. 6736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov ** You may obtain a copy of the License at 7736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov ** 8736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov ** http://www.apache.org/licenses/LICENSE-2.0 9736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov ** 10736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov ** Unless required by applicable law or agreed to in writing, software 11736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov ** distributed under the License is distributed on an "AS IS" BASIS, 12736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov ** See the License for the specific language governing permissions and 14736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov ** limitations under the License. 15736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov */ 16736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov 17736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganovpackage com.android.server.accessibility; 18736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov 19736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganovimport android.content.Context; 204213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganovimport android.gesture.Gesture; 214213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganovimport android.gesture.GestureLibraries; 224213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganovimport android.gesture.GestureLibrary; 234213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganovimport android.gesture.GesturePoint; 24ea6fbc0981564f7bbf4c6fbb63af0175415121ceCasey Burkhardtimport android.gesture.GestureStore; 254213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganovimport android.gesture.GestureStroke; 264213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganovimport android.gesture.Prediction; 277498efdc5e163d6b4a11db941c7d13c169d37284Svet Ganovimport android.graphics.Point; 28e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganovimport android.graphics.Rect; 29736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganovimport android.os.Handler; 30e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganovimport android.os.SystemClock; 31736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganovimport android.util.Slog; 32736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganovimport android.view.MotionEvent; 33e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganovimport android.view.MotionEvent.PointerCoords; 34e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganovimport android.view.MotionEvent.PointerProperties; 354213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganovimport android.view.VelocityTracker; 36f5a07905a3e025f95472a3f8d9935263e49ad6d3Svetoslav Ganovimport android.view.ViewConfiguration; 37f5a07905a3e025f95472a3f8d9935263e49ad6d3Svetoslav Ganovimport android.view.WindowManagerPolicy; 3886783474fdec98a22bc22e224462767eab13e273Svetoslav Ganovimport android.view.accessibility.AccessibilityEvent; 3977276b60851a158ad3e142cb3b091d57ae5ceffbSvetoslav Ganovimport android.view.accessibility.AccessibilityManager; 40736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov 414213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganovimport com.android.internal.R; 42f5a07905a3e025f95472a3f8d9935263e49ad6d3Svetoslav Ganov 434213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganovimport java.util.ArrayList; 44736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganovimport java.util.Arrays; 4584044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslavimport java.util.List; 46736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov 47736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov/** 48736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov * This class is a strategy for performing touch exploration. It 49736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov * transforms the motion event stream by modifying, adding, replacing, 50736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov * and consuming certain events. The interaction model is: 51736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov * 52736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov * <ol> 53e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov * <li>1. One finger moving slow around performs touch exploration.</li> 54e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov * <li>2. One finger moving fast around performs gestures.</li> 55e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov * <li>3. Two close fingers moving in the same direction perform a drag.</li> 56e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov * <li>4. Multi-finger gestures are delivered to view hierarchy.</li> 5784044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav * <li>5. Two fingers moving in different directions are considered a multi-finger gesture.</li> 5884044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav * <li>7. Double tapping clicks on the on the last touch explored location if it was in 59e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov * a window that does not take focus, otherwise the click is within the accessibility 60e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov * focused rectangle.</li> 61e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov * <li>7. Tapping and holding for a while performs a long press in a similar fashion 62e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov * as the click above.</li> 63736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov * <ol> 64736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov * 65736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov * @hide 66736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov */ 671cf70bbf96930662cab0e699d70b62865766ff52Svetoslav Ganovclass TouchExplorer implements EventStreamTransformation { 684213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov 69736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov private static final boolean DEBUG = false; 70736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov 71736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov // Tag for logging received events. 724213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov private static final String LOG_TAG = "TouchExplorer"; 73736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov 74736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov // States this explorer can be in. 75736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov private static final int STATE_TOUCH_EXPLORING = 0x00000001; 76736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov private static final int STATE_DRAGGING = 0x00000002; 77736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov private static final int STATE_DELEGATING = 0x00000004; 784213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov private static final int STATE_GESTURE_DETECTING = 0x00000005; 79736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov 80ded133c446fa9d0d23e6bde19a66fb2ce3980491Svetoslav private static final int CLICK_LOCATION_NONE = 0; 81ded133c446fa9d0d23e6bde19a66fb2ce3980491Svetoslav private static final int CLICK_LOCATION_ACCESSIBILITY_FOCUS = 1; 82ded133c446fa9d0d23e6bde19a66fb2ce3980491Svetoslav private static final int CLICK_LOCATION_LAST_TOUCH_EXPLORED = 2; 83ded133c446fa9d0d23e6bde19a66fb2ce3980491Svetoslav 84a3315270e0502189a6fa4de20b9db2bf042cfd2aSvetoslav // The maximum of the cosine between the vectors of two moving 85736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov // pointers so they can be considered moving in the same direction. 8612a024ca681d877fe16b7e087356f7aff175a218Svetoslav Ganov private static final float MAX_DRAGGING_ANGLE_COS = 0.525321989f; // cos(pi/4) 87736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov 88f804420d6e37748b75478406e989c69303756980Svetoslav Ganov // Constant referring to the ids bits of all pointers. 89f804420d6e37748b75478406e989c69303756980Svetoslav Ganov private static final int ALL_POINTER_ID_BITS = 0xFFFFFFFF; 90736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov 914213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov // This constant captures the current implementation detail that 924213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov // pointer IDs are between 0 and 31 inclusive (subject to change). 934213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov // (See MAX_POINTER_ID in frameworks/base/include/ui/Input.h) 94e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov private static final int MAX_POINTER_COUNT = 32; 954213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov 964213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov // Invalid pointer ID. 97e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov private static final int INVALID_POINTER_ID = -1; 98e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov 99e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov // The velocity above which we detect gestures. 100e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov private static final int GESTURE_DETECTION_VELOCITY_DIP = 1000; 101e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov 102e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov // The minimal distance before we take the middle of the distance between 103e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov // the two dragging pointers as opposed to use the location of the primary one. 104e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov private static final int MIN_POINTER_DISTANCE_TO_USE_MIDDLE_LOCATION_DIP = 200; 1054213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov 10695068e5d1bea47091e97955f271c789264994550Svetoslav Ganov // The timeout after which we are no longer trying to detect a gesture. 10795068e5d1bea47091e97955f271c789264994550Svetoslav Ganov private static final int EXIT_GESTURE_DETECTION_TIMEOUT = 2000; 10895068e5d1bea47091e97955f271c789264994550Svetoslav Ganov 109e47957a0bbe2164467ff6e7a566b0c9e4689cdc9Svetoslav Ganov // Timeout before trying to decide what the user is trying to do. 110e47957a0bbe2164467ff6e7a566b0c9e4689cdc9Svetoslav Ganov private final int mDetermineUserIntentTimeout; 111e47957a0bbe2164467ff6e7a566b0c9e4689cdc9Svetoslav Ganov 112e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov // Timeout within which we try to detect a tap. 113e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov private final int mTapTimeout; 114e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov 115e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov // Timeout within which we try to detect a double tap. 116e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov private final int mDoubleTapTimeout; 117e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov 118e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov // Slop between the down and up tap to be a tap. 119e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov private final int mTouchSlop; 120e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov 121e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov // Slop between the first and second tap to be a double tap. 122e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov private final int mDoubleTapSlop; 123736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov 124736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov // The current state of the touch explorer. 125736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov private int mCurrentState = STATE_TOUCH_EXPLORING; 126736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov 127736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov // The ID of the pointer used for dragging. 128736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov private int mDraggingPointerId; 129736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov 130736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov // Handler for performing asynchronous operations. 131736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov private final Handler mHandler; 132736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov 13384044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav // Command for delayed sending of a hover enter and move event. 13484044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav private final SendHoverEnterAndMoveDelayed mSendHoverEnterAndMoveDelayed; 135e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov 136e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov // Command for delayed sending of a hover exit event. 13784044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav private final SendHoverExitDelayed mSendHoverExitDelayed; 138736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov 139f068fed6c4c3fc2003aec19b6e7e892358179b02Svetoslav Ganov // Command for delayed sending of touch exploration end events. 140f068fed6c4c3fc2003aec19b6e7e892358179b02Svetoslav Ganov private final SendAccessibilityEventDelayed mSendTouchExplorationEndDelayed; 141f068fed6c4c3fc2003aec19b6e7e892358179b02Svetoslav Ganov 142f068fed6c4c3fc2003aec19b6e7e892358179b02Svetoslav Ganov // Command for delayed sending of touch interaction end events. 143f068fed6c4c3fc2003aec19b6e7e892358179b02Svetoslav Ganov private final SendAccessibilityEventDelayed mSendTouchInteractionEndDelayed; 144fe304b893968887323b93764caafa66ee8ad44deSvetoslav Ganov 145f5a07905a3e025f95472a3f8d9935263e49ad6d3Svetoslav Ganov // Command for delayed sending of a long press. 146f5a07905a3e025f95472a3f8d9935263e49ad6d3Svetoslav Ganov private final PerformLongPressDelayed mPerformLongPressDelayed; 147f5a07905a3e025f95472a3f8d9935263e49ad6d3Svetoslav Ganov 14895068e5d1bea47091e97955f271c789264994550Svetoslav Ganov // Command for exiting gesture detection mode after a timeout. 14995068e5d1bea47091e97955f271c789264994550Svetoslav Ganov private final ExitGestureDetectionModeDelayed mExitGestureDetectionModeDelayed; 15095068e5d1bea47091e97955f271c789264994550Svetoslav Ganov 151e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov // Helper to detect and react to double tap in touch explore mode. 152e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov private final DoubleTapDetector mDoubleTapDetector; 153e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov 154e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov // The scaled minimal distance before we take the middle of the distance between 155e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov // the two dragging pointers as opposed to use the location of the primary one. 156e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov private final int mScaledMinPointerDistanceToUseMiddleLocation; 157e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov 158e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov // The scaled velocity above which we detect gestures. 159e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov private final int mScaledGestureDetectionVelocity; 160e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov 1611cf70bbf96930662cab0e699d70b62865766ff52Svetoslav Ganov // The handler to which to delegate events. 1621cf70bbf96930662cab0e699d70b62865766ff52Svetoslav Ganov private EventStreamTransformation mNext; 1631cf70bbf96930662cab0e699d70b62865766ff52Svetoslav Ganov 164e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov // Helper to track gesture velocity. 16545af84a483165f06c04d74baba67f90da29c6ad2Svetoslav Ganov private final VelocityTracker mVelocityTracker = VelocityTracker.obtain(); 1664213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov 167e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov // Helper class to track received pointers. 1684213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov private final ReceivedPointerTracker mReceivedPointerTracker; 1694213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov 170e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov // Helper class to track injected pointers. 1714213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov private final InjectedPointerTracker mInjectedPointerTracker; 1724213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov 173e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov // Handle to the accessibility manager service. 174e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov private final AccessibilityManagerService mAms; 1754213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov 176e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov // Temporary rectangle to avoid instantiation. 177e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov private final Rect mTempRect = new Rect(); 1784213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov 1797498efdc5e163d6b4a11db941c7d13c169d37284Svet Ganov // Temporary point to avoid instantiation. 1807498efdc5e163d6b4a11db941c7d13c169d37284Svet Ganov private final Point mTempPoint = new Point(); 1817498efdc5e163d6b4a11db941c7d13c169d37284Svet Ganov 18277276b60851a158ad3e142cb3b091d57ae5ceffbSvetoslav Ganov // Context in which this explorer operates. 18377276b60851a158ad3e142cb3b091d57ae5ceffbSvetoslav Ganov private final Context mContext; 18477276b60851a158ad3e142cb3b091d57ae5ceffbSvetoslav Ganov 185e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov // The X of the previous event. 186e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov private float mPreviousX; 187e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov 188e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov // The Y of the previous event. 189e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov private float mPreviousY; 190e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov 191e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov // Buffer for storing points for gesture detection. 192e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov private final ArrayList<GesturePoint> mStrokeBuffer = new ArrayList<GesturePoint>(100); 193e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov 194e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov // The minimal delta between moves to add a gesture point. 195e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov private static final int TOUCH_TOLERANCE = 3; 196e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov 197e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov // The minimal score for accepting a predicted gesture. 198e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov private static final float MIN_PREDICTION_SCORE = 2.0f; 199e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov 200e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov // The library for gesture detection. 201e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov private GestureLibrary mGestureLibrary; 202e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov 203e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov // The long pressing pointer id if coordinate remapping is needed. 204aeb8d0ed0d98d398a66a092c418f4f2bca8719e0Svetoslav Ganov private int mLongPressingPointerId = -1; 205e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov 206e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov // The long pressing pointer X if coordinate remapping is needed. 207e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov private int mLongPressingPointerDeltaX; 208e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov 209e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov // The long pressing pointer Y if coordinate remapping is needed. 210e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov private int mLongPressingPointerDeltaY; 2114213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov 21276716c5a180aa471c6973ca7aa03c7f2da677823Svetoslav Ganov // The id of the last touch explored window. 21376716c5a180aa471c6973ca7aa03c7f2da677823Svetoslav Ganov private int mLastTouchedWindowId; 21476716c5a180aa471c6973ca7aa03c7f2da677823Svetoslav Ganov 215f068fed6c4c3fc2003aec19b6e7e892358179b02Svetoslav Ganov // Whether touch exploration is in progress. 216f068fed6c4c3fc2003aec19b6e7e892358179b02Svetoslav Ganov private boolean mTouchExplorationInProgress; 21777276b60851a158ad3e142cb3b091d57ae5ceffbSvetoslav Ganov 218736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov /** 219736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov * Creates a new instance. 220736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov * 221736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov * @param inputFilter The input filter associated with this explorer. 222736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov * @param context A context handle for accessing resources. 223736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov */ 2241cf70bbf96930662cab0e699d70b62865766ff52Svetoslav Ganov public TouchExplorer(Context context, AccessibilityManagerService service) { 22577276b60851a158ad3e142cb3b091d57ae5ceffbSvetoslav Ganov mContext = context; 226e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov mAms = service; 22784044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav mReceivedPointerTracker = new ReceivedPointerTracker(); 2284213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov mInjectedPointerTracker = new InjectedPointerTracker(); 229e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov mTapTimeout = ViewConfiguration.getTapTimeout(); 23077276b60851a158ad3e142cb3b091d57ae5ceffbSvetoslav Ganov mDetermineUserIntentTimeout = ViewConfiguration.getDoubleTapTimeout(); 231e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov mDoubleTapTimeout = ViewConfiguration.getDoubleTapTimeout(); 232e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov mTouchSlop = ViewConfiguration.get(context).getScaledTouchSlop(); 233e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov mDoubleTapSlop = ViewConfiguration.get(context).getScaledDoubleTapSlop(); 234736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov mHandler = new Handler(context.getMainLooper()); 235f5a07905a3e025f95472a3f8d9935263e49ad6d3Svetoslav Ganov mPerformLongPressDelayed = new PerformLongPressDelayed(); 23695068e5d1bea47091e97955f271c789264994550Svetoslav Ganov mExitGestureDetectionModeDelayed = new ExitGestureDetectionModeDelayed(); 2374213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov mGestureLibrary = GestureLibraries.fromRawResource(context, R.raw.accessibility_gestures); 238ea6fbc0981564f7bbf4c6fbb63af0175415121ceCasey Burkhardt mGestureLibrary.setOrientationStyle(8); 239ea6fbc0981564f7bbf4c6fbb63af0175415121ceCasey Burkhardt mGestureLibrary.setSequenceType(GestureStore.SEQUENCE_SENSITIVE); 2404213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov mGestureLibrary.load(); 24184044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav mSendHoverEnterAndMoveDelayed = new SendHoverEnterAndMoveDelayed(); 24284044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav mSendHoverExitDelayed = new SendHoverExitDelayed(); 243f068fed6c4c3fc2003aec19b6e7e892358179b02Svetoslav Ganov mSendTouchExplorationEndDelayed = new SendAccessibilityEventDelayed( 244f068fed6c4c3fc2003aec19b6e7e892358179b02Svetoslav Ganov AccessibilityEvent.TYPE_TOUCH_EXPLORATION_GESTURE_END, 245f068fed6c4c3fc2003aec19b6e7e892358179b02Svetoslav Ganov mDetermineUserIntentTimeout); 246f068fed6c4c3fc2003aec19b6e7e892358179b02Svetoslav Ganov mSendTouchInteractionEndDelayed = new SendAccessibilityEventDelayed( 247f068fed6c4c3fc2003aec19b6e7e892358179b02Svetoslav Ganov AccessibilityEvent.TYPE_TOUCH_INTERACTION_END, 248f068fed6c4c3fc2003aec19b6e7e892358179b02Svetoslav Ganov mDetermineUserIntentTimeout); 249e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov mDoubleTapDetector = new DoubleTapDetector(); 250e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov final float density = context.getResources().getDisplayMetrics().density; 251e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov mScaledMinPointerDistanceToUseMiddleLocation = 252e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov (int) (MIN_POINTER_DISTANCE_TO_USE_MIDDLE_LOCATION_DIP * density); 253e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov mScaledGestureDetectionVelocity = (int) (GESTURE_DETECTION_VELOCITY_DIP * density); 254e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov } 255e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov 256e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov public void clear() { 257e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov // If we have not received an event then we are in initial 258e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov // state. Therefore, there is not need to clean anything. 259e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov MotionEvent event = mReceivedPointerTracker.getLastReceivedEvent(); 260e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov if (event != null) { 261e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov clear(mReceivedPointerTracker.getLastReceivedEvent(), WindowManagerPolicy.FLAG_TRUSTED); 262e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov } 263736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov } 264736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov 2651cf70bbf96930662cab0e699d70b62865766ff52Svetoslav Ganov public void onDestroy() { 2661cf70bbf96930662cab0e699d70b62865766ff52Svetoslav Ganov // TODO: Implement 2671cf70bbf96930662cab0e699d70b62865766ff52Svetoslav Ganov } 2681cf70bbf96930662cab0e699d70b62865766ff52Svetoslav Ganov 2691cf70bbf96930662cab0e699d70b62865766ff52Svetoslav Ganov private void clear(MotionEvent event, int policyFlags) { 270e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov switch (mCurrentState) { 271e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov case STATE_TOUCH_EXPLORING: { 272e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov // If a touch exploration gesture is in progress send events for its end. 273f068fed6c4c3fc2003aec19b6e7e892358179b02Svetoslav Ganov sendHoverExitAndTouchExplorationGestureEndIfNeeded(policyFlags); 274e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov } break; 275e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov case STATE_DRAGGING: { 276e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov mDraggingPointerId = INVALID_POINTER_ID; 277e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov // Send exit to all pointers that we have delivered. 278e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov sendUpForInjectedDownPointers(event, policyFlags); 279e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov } break; 280e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov case STATE_DELEGATING: { 281e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov // Send exit to all pointers that we have delivered. 282e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov sendUpForInjectedDownPointers(event, policyFlags); 283e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov } break; 284e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov case STATE_GESTURE_DETECTING: { 285e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov // Clear the current stroke. 286e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov mStrokeBuffer.clear(); 287e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov } break; 288e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov } 289e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov // Remove all pending callbacks. 29084044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav mSendHoverEnterAndMoveDelayed.cancel(); 29184044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav mSendHoverExitDelayed.cancel(); 29284044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav mPerformLongPressDelayed.cancel(); 29384044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav mExitGestureDetectionModeDelayed.cancel(); 29484044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav mSendTouchExplorationEndDelayed.cancel(); 29584044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav mSendTouchInteractionEndDelayed.cancel(); 296e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov // Reset the pointer trackers. 297e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov mReceivedPointerTracker.clear(); 298e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov mInjectedPointerTracker.clear(); 299e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov // Clear the double tap detector 300e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov mDoubleTapDetector.clear(); 301e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov // Go to initial state. 302e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov // Clear the long pressing pointer remap data. 303e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov mLongPressingPointerId = -1; 304e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov mLongPressingPointerDeltaX = 0; 305e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov mLongPressingPointerDeltaY = 0; 306e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov mCurrentState = STATE_TOUCH_EXPLORING; 3071cf70bbf96930662cab0e699d70b62865766ff52Svetoslav Ganov if (mNext != null) { 3081cf70bbf96930662cab0e699d70b62865766ff52Svetoslav Ganov mNext.clear(); 3091cf70bbf96930662cab0e699d70b62865766ff52Svetoslav Ganov } 310f068fed6c4c3fc2003aec19b6e7e892358179b02Svetoslav Ganov mTouchExplorationInProgress = false; 311f772cba59760d1ad9eb5cb9205b2e2e9126e488dSvetoslav Ganov mAms.onTouchInteractionEnd(); 3121cf70bbf96930662cab0e699d70b62865766ff52Svetoslav Ganov } 3131cf70bbf96930662cab0e699d70b62865766ff52Svetoslav Ganov 3141cf70bbf96930662cab0e699d70b62865766ff52Svetoslav Ganov @Override 3151cf70bbf96930662cab0e699d70b62865766ff52Svetoslav Ganov public void setNext(EventStreamTransformation next) { 3161cf70bbf96930662cab0e699d70b62865766ff52Svetoslav Ganov mNext = next; 317736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov } 318736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov 3191cf70bbf96930662cab0e699d70b62865766ff52Svetoslav Ganov @Override 32045af84a483165f06c04d74baba67f90da29c6ad2Svetoslav Ganov public void onMotionEvent(MotionEvent event, MotionEvent rawEvent, int policyFlags) { 321736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov if (DEBUG) { 3224213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov Slog.d(LOG_TAG, "Received event: " + event + ", policyFlags=0x" 323736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov + Integer.toHexString(policyFlags)); 3244213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov Slog.d(LOG_TAG, getStateSymbolicName(mCurrentState)); 325736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov } 326736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov 32745af84a483165f06c04d74baba67f90da29c6ad2Svetoslav Ganov mReceivedPointerTracker.onMotionEvent(rawEvent); 328736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov 329736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov switch(mCurrentState) { 330736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov case STATE_TOUCH_EXPLORING: { 33145af84a483165f06c04d74baba67f90da29c6ad2Svetoslav Ganov handleMotionEventStateTouchExploring(event, rawEvent, policyFlags); 332736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov } break; 333736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov case STATE_DRAGGING: { 334736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov handleMotionEventStateDragging(event, policyFlags); 335736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov } break; 336736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov case STATE_DELEGATING: { 337736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov handleMotionEventStateDelegating(event, policyFlags); 338736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov } break; 3394213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov case STATE_GESTURE_DETECTING: { 34045af84a483165f06c04d74baba67f90da29c6ad2Svetoslav Ganov handleMotionEventGestureDetecting(rawEvent, policyFlags); 3414213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov } break; 3424213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov default: 343736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov throw new IllegalStateException("Illegal state: " + mCurrentState); 344736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov } 345736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov } 346736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov 34786783474fdec98a22bc22e224462767eab13e273Svetoslav Ganov public void onAccessibilityEvent(AccessibilityEvent event) { 34877276b60851a158ad3e142cb3b091d57ae5ceffbSvetoslav Ganov final int eventType = event.getEventType(); 34977276b60851a158ad3e142cb3b091d57ae5ceffbSvetoslav Ganov 35077276b60851a158ad3e142cb3b091d57ae5ceffbSvetoslav Ganov // The event for gesture end should be strictly after the 35177276b60851a158ad3e142cb3b091d57ae5ceffbSvetoslav Ganov // last hover exit event. 352f068fed6c4c3fc2003aec19b6e7e892358179b02Svetoslav Ganov if (mSendTouchExplorationEndDelayed.isPending() 3538b681cb8813454aac8a626bf3d7adaa8beca4d75Svetoslav Ganov && eventType == AccessibilityEvent.TYPE_VIEW_HOVER_EXIT) { 35484044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav mSendTouchExplorationEndDelayed.cancel(); 3558b681cb8813454aac8a626bf3d7adaa8beca4d75Svetoslav Ganov sendAccessibilityEvent(AccessibilityEvent.TYPE_TOUCH_EXPLORATION_GESTURE_END); 35677276b60851a158ad3e142cb3b091d57ae5ceffbSvetoslav Ganov } 35777276b60851a158ad3e142cb3b091d57ae5ceffbSvetoslav Ganov 35877276b60851a158ad3e142cb3b091d57ae5ceffbSvetoslav Ganov // The event for touch interaction end should be strictly after the 35977276b60851a158ad3e142cb3b091d57ae5ceffbSvetoslav Ganov // last hover exit and the touch exploration gesture end events. 360f068fed6c4c3fc2003aec19b6e7e892358179b02Svetoslav Ganov if (mSendTouchInteractionEndDelayed.isPending() 3618b681cb8813454aac8a626bf3d7adaa8beca4d75Svetoslav Ganov && eventType == AccessibilityEvent.TYPE_VIEW_HOVER_EXIT) { 36284044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav mSendTouchInteractionEndDelayed.cancel(); 3638b681cb8813454aac8a626bf3d7adaa8beca4d75Svetoslav Ganov sendAccessibilityEvent(AccessibilityEvent.TYPE_TOUCH_INTERACTION_END); 36477276b60851a158ad3e142cb3b091d57ae5ceffbSvetoslav Ganov } 36577276b60851a158ad3e142cb3b091d57ae5ceffbSvetoslav Ganov 36686783474fdec98a22bc22e224462767eab13e273Svetoslav Ganov // If a new window opens or the accessibility focus moves we no longer 36786783474fdec98a22bc22e224462767eab13e273Svetoslav Ganov // want to click/long press on the last touch explored location. 36886783474fdec98a22bc22e224462767eab13e273Svetoslav Ganov switch (eventType) { 36986783474fdec98a22bc22e224462767eab13e273Svetoslav Ganov case AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED: 37086783474fdec98a22bc22e224462767eab13e273Svetoslav Ganov case AccessibilityEvent.TYPE_VIEW_ACCESSIBILITY_FOCUSED: { 3715d043ce8cc2f588fdfb336cc843fb3b07b196f83Svetoslav Ganov if (mInjectedPointerTracker.mLastInjectedHoverEventForClick != null) { 3725d043ce8cc2f588fdfb336cc843fb3b07b196f83Svetoslav Ganov mInjectedPointerTracker.mLastInjectedHoverEventForClick.recycle(); 3735d043ce8cc2f588fdfb336cc843fb3b07b196f83Svetoslav Ganov mInjectedPointerTracker.mLastInjectedHoverEventForClick = null; 37486783474fdec98a22bc22e224462767eab13e273Svetoslav Ganov } 37576716c5a180aa471c6973ca7aa03c7f2da677823Svetoslav Ganov mLastTouchedWindowId = -1; 37676716c5a180aa471c6973ca7aa03c7f2da677823Svetoslav Ganov } break; 37776716c5a180aa471c6973ca7aa03c7f2da677823Svetoslav Ganov case AccessibilityEvent.TYPE_VIEW_HOVER_ENTER: 37876716c5a180aa471c6973ca7aa03c7f2da677823Svetoslav Ganov case AccessibilityEvent.TYPE_VIEW_HOVER_EXIT: { 37976716c5a180aa471c6973ca7aa03c7f2da677823Svetoslav Ganov mLastTouchedWindowId = event.getWindowId(); 38086783474fdec98a22bc22e224462767eab13e273Svetoslav Ganov } break; 38186783474fdec98a22bc22e224462767eab13e273Svetoslav Ganov } 3821cf70bbf96930662cab0e699d70b62865766ff52Svetoslav Ganov if (mNext != null) { 3831cf70bbf96930662cab0e699d70b62865766ff52Svetoslav Ganov mNext.onAccessibilityEvent(event); 3841cf70bbf96930662cab0e699d70b62865766ff52Svetoslav Ganov } 38586783474fdec98a22bc22e224462767eab13e273Svetoslav Ganov } 38686783474fdec98a22bc22e224462767eab13e273Svetoslav Ganov 387736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov /** 388736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov * Handles a motion event in touch exploring state. 389736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov * 390736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov * @param event The event to be handled. 39145af84a483165f06c04d74baba67f90da29c6ad2Svetoslav Ganov * @param rawEvent The raw (unmodified) motion event. 392736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov * @param policyFlags The policy flags associated with the event. 393736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov */ 39445af84a483165f06c04d74baba67f90da29c6ad2Svetoslav Ganov private void handleMotionEventStateTouchExploring(MotionEvent event, MotionEvent rawEvent, 39545af84a483165f06c04d74baba67f90da29c6ad2Svetoslav Ganov int policyFlags) { 3964213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov ReceivedPointerTracker receivedTracker = mReceivedPointerTracker; 3974213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov 39845af84a483165f06c04d74baba67f90da29c6ad2Svetoslav Ganov mVelocityTracker.addMovement(rawEvent); 399736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov 400e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov mDoubleTapDetector.onMotionEvent(event, policyFlags); 401e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov 402736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov switch (event.getActionMasked()) { 40384044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav case MotionEvent.ACTION_DOWN: { 4046ae8a24fc045bc7970f2843fa9baf06aff15e22dSvetoslav Ganov mAms.onTouchInteractionStart(); 40584044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav 406e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov // Pre-feed the motion events to the gesture detector since we 407e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov // have a distance slop before getting into gesture detection 408e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov // mode and not using the points within this slop significantly 409e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov // decreases the quality of gesture recognition. 41045af84a483165f06c04d74baba67f90da29c6ad2Svetoslav Ganov handleMotionEventGestureDetecting(rawEvent, policyFlags); 41184044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav sendAccessibilityEvent(AccessibilityEvent.TYPE_TOUCH_INTERACTION_START); 412736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov 41384044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav // If we still have not notified the user for the last 41484044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav // touch, we figure out what to do. If were waiting 41584044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav // we resent the delayed callback and wait again. 41684044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav mSendHoverEnterAndMoveDelayed.cancel(); 41784044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav mSendHoverExitDelayed.cancel(); 41884044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav mPerformLongPressDelayed.cancel(); 419f068fed6c4c3fc2003aec19b6e7e892358179b02Svetoslav Ganov 42084044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav if (mSendTouchExplorationEndDelayed.isPending()) { 42184044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav mSendTouchExplorationEndDelayed.forceSendAndRemove(); 42284044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav } 423f068fed6c4c3fc2003aec19b6e7e892358179b02Svetoslav Ganov 42484044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav if (mSendTouchInteractionEndDelayed.isPending()) { 42584044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav mSendTouchInteractionEndDelayed.forceSendAndRemove(); 42684044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav } 427fe304b893968887323b93764caafa66ee8ad44deSvetoslav Ganov 42884044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav // If we have the first tap, schedule a long press and break 42984044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav // since we do not want to schedule hover enter because 43084044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav // the delayed callback will kick in before the long click. 43184044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav // This would lead to a state transition resulting in long 43284044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav // pressing the item below the double taped area which is 43384044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav // not necessary where accessibility focus is. 43484044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav if (mDoubleTapDetector.firstTapDetected()) { 43584044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav // We got a tap now post a long press action. 43684044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav mPerformLongPressDelayed.post(event, policyFlags); 43784044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav break; 438736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov } 43984044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav if (!mTouchExplorationInProgress) { 44038c992841b5f6bc80359dbf60d31aa7b994160fcSvetoslav Ganov if (!mSendHoverEnterAndMoveDelayed.isPending()) { 44138c992841b5f6bc80359dbf60d31aa7b994160fcSvetoslav Ganov // Deliver hover enter with a delay to have a chance 44238c992841b5f6bc80359dbf60d31aa7b994160fcSvetoslav Ganov // to detect what the user is trying to do. 44338c992841b5f6bc80359dbf60d31aa7b994160fcSvetoslav Ganov final int pointerId = receivedTracker.getPrimaryPointerId(); 44438c992841b5f6bc80359dbf60d31aa7b994160fcSvetoslav Ganov final int pointerIdBits = (1 << pointerId); 44538c992841b5f6bc80359dbf60d31aa7b994160fcSvetoslav Ganov mSendHoverEnterAndMoveDelayed.post(event, true, pointerIdBits, 44638c992841b5f6bc80359dbf60d31aa7b994160fcSvetoslav Ganov policyFlags); 447a3315270e0502189a6fa4de20b9db2bf042cfd2aSvetoslav } else { 448a3315270e0502189a6fa4de20b9db2bf042cfd2aSvetoslav // Cache the event until we discern exploration from gesturing. 449a3315270e0502189a6fa4de20b9db2bf042cfd2aSvetoslav mSendHoverEnterAndMoveDelayed.addEvent(event); 45038c992841b5f6bc80359dbf60d31aa7b994160fcSvetoslav Ganov } 45184044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav } 45284044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav } break; 45384044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav case MotionEvent.ACTION_POINTER_DOWN: { 454a3315270e0502189a6fa4de20b9db2bf042cfd2aSvetoslav // Another finger down means that if we have not started to deliver 455a3315270e0502189a6fa4de20b9db2bf042cfd2aSvetoslav // hover events, we will not have to. The code for ACTION_MOVE will 456a3315270e0502189a6fa4de20b9db2bf042cfd2aSvetoslav // decide what we will actually do next. 457a3315270e0502189a6fa4de20b9db2bf042cfd2aSvetoslav mSendHoverEnterAndMoveDelayed.cancel(); 458a3315270e0502189a6fa4de20b9db2bf042cfd2aSvetoslav mSendHoverExitDelayed.cancel(); 459a3315270e0502189a6fa4de20b9db2bf042cfd2aSvetoslav mPerformLongPressDelayed.cancel(); 460736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov } break; 461736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov case MotionEvent.ACTION_MOVE: { 46284044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav final int pointerId = receivedTracker.getPrimaryPointerId(); 46391feae3c5994bd4768cea3507c62c65746adcfa6Svetoslav Ganov final int pointerIndex = event.findPointerIndex(pointerId); 46491feae3c5994bd4768cea3507c62c65746adcfa6Svetoslav Ganov final int pointerIdBits = (1 << pointerId); 46584044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav switch (event.getPointerCount()) { 466736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov case 1: { 467e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov // We have not started sending events since we try to 468e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov // figure out what the user is doing. 46984044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav if (mSendHoverEnterAndMoveDelayed.isPending()) { 470e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov // Pre-feed the motion events to the gesture detector since we 471e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov // have a distance slop before getting into gesture detection 472e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov // mode and not using the points within this slop significantly 473e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov // decreases the quality of gesture recognition. 47445af84a483165f06c04d74baba67f90da29c6ad2Svetoslav Ganov handleMotionEventGestureDetecting(rawEvent, policyFlags); 47584044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav 47684044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav // Cache the event until we discern exploration from gesturing. 47784044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav mSendHoverEnterAndMoveDelayed.addEvent(event); 47884044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav 47945af84a483165f06c04d74baba67f90da29c6ad2Svetoslav Ganov // It is *important* to use the distance traveled by the pointers 48045af84a483165f06c04d74baba67f90da29c6ad2Svetoslav Ganov // on the screen which may or may not be magnified. 4814213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov final float deltaX = receivedTracker.getReceivedPointerDownX(pointerId) 48245af84a483165f06c04d74baba67f90da29c6ad2Svetoslav Ganov - rawEvent.getX(pointerIndex); 4834213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov final float deltaY = receivedTracker.getReceivedPointerDownY(pointerId) 48445af84a483165f06c04d74baba67f90da29c6ad2Svetoslav Ganov - rawEvent.getY(pointerIndex); 485736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov final double moveDelta = Math.hypot(deltaX, deltaY); 486e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov // The user has moved enough for us to decide. 487e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov if (moveDelta > mDoubleTapSlop) { 488e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov // Check whether the user is performing a gesture. We 489e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov // detect gestures if the pointer is moving above a 490e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov // given velocity. 4914213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov mVelocityTracker.computeCurrentVelocity(1000); 4924213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov final float maxAbsVelocity = Math.max( 4934213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov Math.abs(mVelocityTracker.getXVelocity(pointerId)), 4944213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov Math.abs(mVelocityTracker.getYVelocity(pointerId))); 495e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov if (maxAbsVelocity > mScaledGestureDetectionVelocity) { 496e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov // We have to perform gesture detection, so 497e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov // clear the current state and try to detect. 4984213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov mCurrentState = STATE_GESTURE_DETECTING; 49945af84a483165f06c04d74baba67f90da29c6ad2Svetoslav Ganov mVelocityTracker.clear(); 50084044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav mSendHoverEnterAndMoveDelayed.cancel(); 50184044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav mSendHoverExitDelayed.cancel(); 50284044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav mPerformLongPressDelayed.cancel(); 50395068e5d1bea47091e97955f271c789264994550Svetoslav Ganov mExitGestureDetectionModeDelayed.post(); 50477276b60851a158ad3e142cb3b091d57ae5ceffbSvetoslav Ganov // Send accessibility event to announce the start 50577276b60851a158ad3e142cb3b091d57ae5ceffbSvetoslav Ganov // of gesture recognition. 50677276b60851a158ad3e142cb3b091d57ae5ceffbSvetoslav Ganov sendAccessibilityEvent( 50777276b60851a158ad3e142cb3b091d57ae5ceffbSvetoslav Ganov AccessibilityEvent.TYPE_GESTURE_DETECTION_START); 508e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov } else { 509e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov // We have just decided that the user is touch, 510e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov // exploring so start sending events. 51184044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav mSendHoverEnterAndMoveDelayed.forceSendAndRemove(); 51284044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav mSendHoverExitDelayed.cancel(); 51384044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav mPerformLongPressDelayed.cancel(); 514e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov sendMotionEvent(event, MotionEvent.ACTION_HOVER_MOVE, 51591feae3c5994bd4768cea3507c62c65746adcfa6Svetoslav Ganov pointerIdBits, policyFlags); 51691feae3c5994bd4768cea3507c62c65746adcfa6Svetoslav Ganov } 517e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov break; 518736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov } 519736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov } else { 520f068fed6c4c3fc2003aec19b6e7e892358179b02Svetoslav Ganov // Cancel the long press if pending and the user 521f068fed6c4c3fc2003aec19b6e7e892358179b02Svetoslav Ganov // moved more than the slop. 522f068fed6c4c3fc2003aec19b6e7e892358179b02Svetoslav Ganov if (mPerformLongPressDelayed.isPending()) { 523f068fed6c4c3fc2003aec19b6e7e892358179b02Svetoslav Ganov final float deltaX = 524f068fed6c4c3fc2003aec19b6e7e892358179b02Svetoslav Ganov receivedTracker.getReceivedPointerDownX(pointerId) 525f068fed6c4c3fc2003aec19b6e7e892358179b02Svetoslav Ganov - rawEvent.getX(pointerIndex); 526f068fed6c4c3fc2003aec19b6e7e892358179b02Svetoslav Ganov final float deltaY = 527f068fed6c4c3fc2003aec19b6e7e892358179b02Svetoslav Ganov receivedTracker.getReceivedPointerDownY(pointerId) 528f068fed6c4c3fc2003aec19b6e7e892358179b02Svetoslav Ganov - rawEvent.getY(pointerIndex); 529f068fed6c4c3fc2003aec19b6e7e892358179b02Svetoslav Ganov final double moveDelta = Math.hypot(deltaX, deltaY); 530f068fed6c4c3fc2003aec19b6e7e892358179b02Svetoslav Ganov // The user has moved enough for us to decide. 531f068fed6c4c3fc2003aec19b6e7e892358179b02Svetoslav Ganov if (moveDelta > mTouchSlop) { 53284044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav mPerformLongPressDelayed.cancel(); 533f068fed6c4c3fc2003aec19b6e7e892358179b02Svetoslav Ganov } 534f068fed6c4c3fc2003aec19b6e7e892358179b02Svetoslav Ganov } 535a3315270e0502189a6fa4de20b9db2bf042cfd2aSvetoslav if (mTouchExplorationInProgress) { 536a3315270e0502189a6fa4de20b9db2bf042cfd2aSvetoslav sendTouchExplorationGestureStartAndHoverEnterIfNeeded(policyFlags); 537a3315270e0502189a6fa4de20b9db2bf042cfd2aSvetoslav sendMotionEvent(event, MotionEvent.ACTION_HOVER_MOVE, pointerIdBits, 538a3315270e0502189a6fa4de20b9db2bf042cfd2aSvetoslav policyFlags); 539e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov } 540736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov } 541e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov } break; 542e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov case 2: { 543e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov // More than one pointer so the user is not touch exploring 544e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov // and now we have to decide whether to delegate or drag. 54584044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav if (mSendHoverEnterAndMoveDelayed.isPending()) { 546e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov // We have not started sending events so cancel 547e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov // scheduled sending events. 54884044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav mSendHoverEnterAndMoveDelayed.cancel(); 54984044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav mSendHoverExitDelayed.cancel(); 55084044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav mPerformLongPressDelayed.cancel(); 551e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov } else { 55284044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav mPerformLongPressDelayed.cancel(); 553a3315270e0502189a6fa4de20b9db2bf042cfd2aSvetoslav if (mTouchExplorationInProgress) { 554a3315270e0502189a6fa4de20b9db2bf042cfd2aSvetoslav // If the user is touch exploring the second pointer may be 555a3315270e0502189a6fa4de20b9db2bf042cfd2aSvetoslav // performing a double tap to activate an item without need 556a3315270e0502189a6fa4de20b9db2bf042cfd2aSvetoslav // for the user to lift his exploring finger. 557a3315270e0502189a6fa4de20b9db2bf042cfd2aSvetoslav // It is *important* to use the distance traveled by the pointers 558a3315270e0502189a6fa4de20b9db2bf042cfd2aSvetoslav // on the screen which may or may not be magnified. 559a3315270e0502189a6fa4de20b9db2bf042cfd2aSvetoslav final float deltaX = receivedTracker.getReceivedPointerDownX( 560a3315270e0502189a6fa4de20b9db2bf042cfd2aSvetoslav pointerId) - rawEvent.getX(pointerIndex); 561a3315270e0502189a6fa4de20b9db2bf042cfd2aSvetoslav final float deltaY = receivedTracker.getReceivedPointerDownY( 562a3315270e0502189a6fa4de20b9db2bf042cfd2aSvetoslav pointerId) - rawEvent.getY(pointerIndex); 563a3315270e0502189a6fa4de20b9db2bf042cfd2aSvetoslav final double moveDelta = Math.hypot(deltaX, deltaY); 564a3315270e0502189a6fa4de20b9db2bf042cfd2aSvetoslav if (moveDelta < mDoubleTapSlop) { 565a3315270e0502189a6fa4de20b9db2bf042cfd2aSvetoslav break; 566a3315270e0502189a6fa4de20b9db2bf042cfd2aSvetoslav } 567a3315270e0502189a6fa4de20b9db2bf042cfd2aSvetoslav // We are sending events so send exit and gesture 568a3315270e0502189a6fa4de20b9db2bf042cfd2aSvetoslav // end since we transition to another state. 569a3315270e0502189a6fa4de20b9db2bf042cfd2aSvetoslav sendHoverExitAndTouchExplorationGestureEndIfNeeded(policyFlags); 570736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov } 571736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov } 572e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov 573e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov // We know that a new state transition is to happen and the 574e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov // new state will not be gesture recognition, so clear the 575e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov // stashed gesture strokes. 576e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov mStrokeBuffer.clear(); 577736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov 578736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov if (isDraggingGesture(event)) { 579736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov // Two pointers moving in the same direction within 580736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov // a given distance perform a drag. 58100f7b3f76515d1c6fbe5cf9fee9d3760787c03cdSvetoslav Ganov mCurrentState = STATE_DRAGGING; 58291feae3c5994bd4768cea3507c62c65746adcfa6Svetoslav Ganov mDraggingPointerId = pointerId; 583c4842c11932ea4f60fe7ae09b0a59660207e1587Svetoslav Ganov event.setEdgeFlags(receivedTracker.getLastReceivedDownEdgeFlags()); 58491feae3c5994bd4768cea3507c62c65746adcfa6Svetoslav Ganov sendMotionEvent(event, MotionEvent.ACTION_DOWN, pointerIdBits, 58591feae3c5994bd4768cea3507c62c65746adcfa6Svetoslav Ganov policyFlags); 586736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov } else { 587736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov // Two pointers moving arbitrary are delegated to the view hierarchy. 588736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov mCurrentState = STATE_DELEGATING; 58984044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav sendDownForAllNotInjectedPointers(event, policyFlags); 590736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov } 59145af84a483165f06c04d74baba67f90da29c6ad2Svetoslav Ganov mVelocityTracker.clear(); 592736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov } break; 593736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov default: { 594e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov // More than one pointer so the user is not touch exploring 595e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov // and now we have to decide whether to delegate or drag. 59684044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav if (mSendHoverEnterAndMoveDelayed.isPending()) { 597e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov // We have not started sending events so cancel 598e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov // scheduled sending events. 59984044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav mSendHoverEnterAndMoveDelayed.cancel(); 60084044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav mSendHoverExitDelayed.cancel(); 60184044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav mPerformLongPressDelayed.cancel(); 602e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov } else { 60384044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav mPerformLongPressDelayed.cancel(); 604e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov // We are sending events so send exit and gesture 605e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov // end since we transition to another state. 606f068fed6c4c3fc2003aec19b6e7e892358179b02Svetoslav Ganov sendHoverExitAndTouchExplorationGestureEndIfNeeded(policyFlags); 607e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov } 608736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov 609736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov // More than two pointers are delegated to the view hierarchy. 610736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov mCurrentState = STATE_DELEGATING; 61184044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav sendDownForAllNotInjectedPointers(event, policyFlags); 61245af84a483165f06c04d74baba67f90da29c6ad2Svetoslav Ganov mVelocityTracker.clear(); 613736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov } 614736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov } 615736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov } break; 61684044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav case MotionEvent.ACTION_UP: { 617f772cba59760d1ad9eb5cb9205b2e2e9126e488dSvetoslav Ganov mAms.onTouchInteractionEnd(); 618e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov // We know that we do not need the pre-fed gesture points are not 619e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov // needed anymore since the last pointer just went up. 620e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov mStrokeBuffer.clear(); 62184044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav final int pointerId = event.getPointerId(event.getActionIndex()); 62291feae3c5994bd4768cea3507c62c65746adcfa6Svetoslav Ganov final int pointerIdBits = (1 << pointerId); 623736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov 62484044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav mPerformLongPressDelayed.cancel(); 62584044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav mVelocityTracker.clear(); 626f5a07905a3e025f95472a3f8d9935263e49ad6d3Svetoslav Ganov 62784044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav if (mSendHoverEnterAndMoveDelayed.isPending()) { 62884044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav // If we have not delivered the enter schedule an exit. 62984044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav mSendHoverExitDelayed.post(event, pointerIdBits, policyFlags); 63084044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav } else { 63184044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav // The user is touch exploring so we send events for end. 63284044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav sendHoverExitAndTouchExplorationGestureEndIfNeeded(policyFlags); 63384044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav } 634f068fed6c4c3fc2003aec19b6e7e892358179b02Svetoslav Ganov 63584044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav if (!mSendTouchInteractionEndDelayed.isPending()) { 63684044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav mSendTouchInteractionEndDelayed.post(); 637736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov } 63884044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav 639736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov } break; 640736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov case MotionEvent.ACTION_CANCEL: { 641e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov clear(event, policyFlags); 642736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov } break; 643736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov } 644736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov } 645736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov 646736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov /** 647736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov * Handles a motion event in dragging state. 648736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov * 649736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov * @param event The event to be handled. 650736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov * @param policyFlags The policy flags associated with the event. 651736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov */ 652736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov private void handleMotionEventStateDragging(MotionEvent event, int policyFlags) { 65391feae3c5994bd4768cea3507c62c65746adcfa6Svetoslav Ganov final int pointerIdBits = (1 << mDraggingPointerId); 654736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov switch (event.getActionMasked()) { 655736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov case MotionEvent.ACTION_DOWN: { 656736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov throw new IllegalStateException("Dragging state can be reached only if two " 657736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov + "pointers are already down"); 658736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov } 659736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov case MotionEvent.ACTION_POINTER_DOWN: { 660736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov // We are in dragging state so we have two pointers and another one 661736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov // goes down => delegate the three pointers to the view hierarchy 662736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov mCurrentState = STATE_DELEGATING; 663ec33d56300aa417efb4a055786d73d1bf23a6a85Svetoslav Ganov if (mDraggingPointerId != INVALID_POINTER_ID) { 664ec33d56300aa417efb4a055786d73d1bf23a6a85Svetoslav Ganov sendMotionEvent(event, MotionEvent.ACTION_UP, pointerIdBits, policyFlags); 665ec33d56300aa417efb4a055786d73d1bf23a6a85Svetoslav Ganov } 66684044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav sendDownForAllNotInjectedPointers(event, policyFlags); 667736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov } break; 668736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov case MotionEvent.ACTION_MOVE: { 66984044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav switch (event.getPointerCount()) { 6702e1c66bd53d30d2148afaa4b393b60cd59976d65Svetoslav Ganov case 1: { 6712e1c66bd53d30d2148afaa4b393b60cd59976d65Svetoslav Ganov // do nothing 6722e1c66bd53d30d2148afaa4b393b60cd59976d65Svetoslav Ganov } break; 673736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov case 2: { 674736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov if (isDraggingGesture(event)) { 67584044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav final float firstPtrX = event.getX(0); 67684044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav final float firstPtrY = event.getY(0); 67784044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav final float secondPtrX = event.getX(1); 67884044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav final float secondPtrY = event.getY(1); 679e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov 680e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov final float deltaX = firstPtrX - secondPtrX; 681e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov final float deltaY = firstPtrY - secondPtrY; 682e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov final double distance = Math.hypot(deltaX, deltaY); 683e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov 684e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov if (distance > mScaledMinPointerDistanceToUseMiddleLocation) { 685e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov event.setLocation(deltaX / 2, deltaY / 2); 686e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov } 687e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov 688736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov // If still dragging send a drag event. 68991feae3c5994bd4768cea3507c62c65746adcfa6Svetoslav Ganov sendMotionEvent(event, MotionEvent.ACTION_MOVE, pointerIdBits, 69091feae3c5994bd4768cea3507c62c65746adcfa6Svetoslav Ganov policyFlags); 691736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov } else { 692736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov // The two pointers are moving either in different directions or 693736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov // no close enough => delegate the gesture to the view hierarchy. 694736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov mCurrentState = STATE_DELEGATING; 695736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov // Send an event to the end of the drag gesture. 69691feae3c5994bd4768cea3507c62c65746adcfa6Svetoslav Ganov sendMotionEvent(event, MotionEvent.ACTION_UP, pointerIdBits, 69791feae3c5994bd4768cea3507c62c65746adcfa6Svetoslav Ganov policyFlags); 69884044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav // Deliver all pointers to the view hierarchy. 69984044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav sendDownForAllNotInjectedPointers(event, policyFlags); 700736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov } 701736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov } break; 702736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov default: { 703736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov mCurrentState = STATE_DELEGATING; 704736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov // Send an event to the end of the drag gesture. 70591feae3c5994bd4768cea3507c62c65746adcfa6Svetoslav Ganov sendMotionEvent(event, MotionEvent.ACTION_UP, pointerIdBits, 70691feae3c5994bd4768cea3507c62c65746adcfa6Svetoslav Ganov policyFlags); 70784044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav // Deliver all pointers to the view hierarchy. 70884044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav sendDownForAllNotInjectedPointers(event, policyFlags); 709736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov } 710736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov } 711736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov } break; 712aeb8d0ed0d98d398a66a092c418f4f2bca8719e0Svetoslav Ganov case MotionEvent.ACTION_POINTER_UP: { 713aeb8d0ed0d98d398a66a092c418f4f2bca8719e0Svetoslav Ganov final int pointerId = event.getPointerId(event.getActionIndex()); 714aeb8d0ed0d98d398a66a092c418f4f2bca8719e0Svetoslav Ganov if (pointerId == mDraggingPointerId) { 715aeb8d0ed0d98d398a66a092c418f4f2bca8719e0Svetoslav Ganov mDraggingPointerId = INVALID_POINTER_ID; 716aeb8d0ed0d98d398a66a092c418f4f2bca8719e0Svetoslav Ganov // Send an event to the end of the drag gesture. 717aeb8d0ed0d98d398a66a092c418f4f2bca8719e0Svetoslav Ganov sendMotionEvent(event, MotionEvent.ACTION_UP, pointerIdBits, policyFlags); 718aeb8d0ed0d98d398a66a092c418f4f2bca8719e0Svetoslav Ganov } 719aeb8d0ed0d98d398a66a092c418f4f2bca8719e0Svetoslav Ganov } break; 7202e1c66bd53d30d2148afaa4b393b60cd59976d65Svetoslav Ganov case MotionEvent.ACTION_UP: { 721f772cba59760d1ad9eb5cb9205b2e2e9126e488dSvetoslav Ganov mAms.onTouchInteractionEnd(); 72277276b60851a158ad3e142cb3b091d57ae5ceffbSvetoslav Ganov // Announce the end of a new touch interaction. 72377276b60851a158ad3e142cb3b091d57ae5ceffbSvetoslav Ganov sendAccessibilityEvent( 72477276b60851a158ad3e142cb3b091d57ae5ceffbSvetoslav Ganov AccessibilityEvent.TYPE_TOUCH_INTERACTION_END); 725aeb8d0ed0d98d398a66a092c418f4f2bca8719e0Svetoslav Ganov final int pointerId = event.getPointerId(event.getActionIndex()); 726aeb8d0ed0d98d398a66a092c418f4f2bca8719e0Svetoslav Ganov if (pointerId == mDraggingPointerId) { 727aeb8d0ed0d98d398a66a092c418f4f2bca8719e0Svetoslav Ganov mDraggingPointerId = INVALID_POINTER_ID; 728aeb8d0ed0d98d398a66a092c418f4f2bca8719e0Svetoslav Ganov // Send an event to the end of the drag gesture. 729aeb8d0ed0d98d398a66a092c418f4f2bca8719e0Svetoslav Ganov sendMotionEvent(event, MotionEvent.ACTION_UP, pointerIdBits, policyFlags); 730aeb8d0ed0d98d398a66a092c418f4f2bca8719e0Svetoslav Ganov } 7312e1c66bd53d30d2148afaa4b393b60cd59976d65Svetoslav Ganov mCurrentState = STATE_TOUCH_EXPLORING; 7322e1c66bd53d30d2148afaa4b393b60cd59976d65Svetoslav Ganov } break; 733736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov case MotionEvent.ACTION_CANCEL: { 734e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov clear(event, policyFlags); 735736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov } break; 736736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov } 737736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov } 738736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov 739736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov /** 740736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov * Handles a motion event in delegating state. 741736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov * 742736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov * @param event The event to be handled. 743736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov * @param policyFlags The policy flags associated with the event. 744736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov */ 7452e1c66bd53d30d2148afaa4b393b60cd59976d65Svetoslav Ganov private void handleMotionEventStateDelegating(MotionEvent event, int policyFlags) { 746736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov switch (event.getActionMasked()) { 747736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov case MotionEvent.ACTION_DOWN: { 748736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov throw new IllegalStateException("Delegating state can only be reached if " 749736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov + "there is at least one pointer down!"); 750736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov } 75184044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav case MotionEvent.ACTION_UP: { 752a3315270e0502189a6fa4de20b9db2bf042cfd2aSvetoslav // Offset the event if we are doing a long press as the 753a3315270e0502189a6fa4de20b9db2bf042cfd2aSvetoslav // target is not necessarily under the user's finger. 754a3315270e0502189a6fa4de20b9db2bf042cfd2aSvetoslav if (mLongPressingPointerId >= 0) { 755a3315270e0502189a6fa4de20b9db2bf042cfd2aSvetoslav event = offsetEvent(event, - mLongPressingPointerDeltaX, 756a3315270e0502189a6fa4de20b9db2bf042cfd2aSvetoslav - mLongPressingPointerDeltaY); 757a3315270e0502189a6fa4de20b9db2bf042cfd2aSvetoslav // Clear the long press state. 758a3315270e0502189a6fa4de20b9db2bf042cfd2aSvetoslav mLongPressingPointerId = -1; 759a3315270e0502189a6fa4de20b9db2bf042cfd2aSvetoslav mLongPressingPointerDeltaX = 0; 760a3315270e0502189a6fa4de20b9db2bf042cfd2aSvetoslav mLongPressingPointerDeltaY = 0; 761a3315270e0502189a6fa4de20b9db2bf042cfd2aSvetoslav } 762a3315270e0502189a6fa4de20b9db2bf042cfd2aSvetoslav 763a3315270e0502189a6fa4de20b9db2bf042cfd2aSvetoslav // Deliver the event. 764a3315270e0502189a6fa4de20b9db2bf042cfd2aSvetoslav sendMotionEvent(event, event.getAction(), ALL_POINTER_ID_BITS, policyFlags); 765a3315270e0502189a6fa4de20b9db2bf042cfd2aSvetoslav 76684044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav // Announce the end of a the touch interaction. 767a3315270e0502189a6fa4de20b9db2bf042cfd2aSvetoslav mAms.onTouchInteractionEnd(); 76884044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav sendAccessibilityEvent(AccessibilityEvent.TYPE_TOUCH_INTERACTION_END); 769a3315270e0502189a6fa4de20b9db2bf042cfd2aSvetoslav 77084044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav mCurrentState = STATE_TOUCH_EXPLORING; 771736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov } break; 772736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov case MotionEvent.ACTION_CANCEL: { 773e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov clear(event, policyFlags); 774736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov } break; 775a3315270e0502189a6fa4de20b9db2bf042cfd2aSvetoslav default: { 776a3315270e0502189a6fa4de20b9db2bf042cfd2aSvetoslav // Deliver the event. 777a3315270e0502189a6fa4de20b9db2bf042cfd2aSvetoslav sendMotionEvent(event, event.getAction(), ALL_POINTER_ID_BITS, policyFlags); 778a3315270e0502189a6fa4de20b9db2bf042cfd2aSvetoslav } 779736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov } 780736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov } 781736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov 7824213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov private void handleMotionEventGestureDetecting(MotionEvent event, int policyFlags) { 7834213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov switch (event.getActionMasked()) { 7844213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov case MotionEvent.ACTION_DOWN: { 7854213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov final float x = event.getX(); 7864213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov final float y = event.getY(); 7874213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov mPreviousX = x; 7884213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov mPreviousY = y; 7894213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov mStrokeBuffer.add(new GesturePoint(x, y, event.getEventTime())); 7904213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov } break; 7914213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov case MotionEvent.ACTION_MOVE: { 7924213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov final float x = event.getX(); 7934213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov final float y = event.getY(); 7944213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov final float dX = Math.abs(x - mPreviousX); 7954213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov final float dY = Math.abs(y - mPreviousY); 7964213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov if (dX >= TOUCH_TOLERANCE || dY >= TOUCH_TOLERANCE) { 7974213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov mPreviousX = x; 7984213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov mPreviousY = y; 7994213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov mStrokeBuffer.add(new GesturePoint(x, y, event.getEventTime())); 8004213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov } 8014213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov } break; 802e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov case MotionEvent.ACTION_UP: { 803f772cba59760d1ad9eb5cb9205b2e2e9126e488dSvetoslav Ganov mAms.onTouchInteractionEnd(); 80484044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav // Announce the end of the gesture recognition. 80584044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav sendAccessibilityEvent(AccessibilityEvent.TYPE_GESTURE_DETECTION_END); 80684044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav // Announce the end of a the touch interaction. 80784044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav sendAccessibilityEvent(AccessibilityEvent.TYPE_TOUCH_INTERACTION_END); 80877276b60851a158ad3e142cb3b091d57ae5ceffbSvetoslav Ganov 8094213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov float x = event.getX(); 8104213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov float y = event.getY(); 8114213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov mStrokeBuffer.add(new GesturePoint(x, y, event.getEventTime())); 8124213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov 8134213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov Gesture gesture = new Gesture(); 8144213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov gesture.addStroke(new GestureStroke(mStrokeBuffer)); 8154213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov 8164213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov ArrayList<Prediction> predictions = mGestureLibrary.recognize(gesture); 8174213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov if (!predictions.isEmpty()) { 8184213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov Prediction bestPrediction = predictions.get(0); 8194213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov if (bestPrediction.score >= MIN_PREDICTION_SCORE) { 8204213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov if (DEBUG) { 8214213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov Slog.i(LOG_TAG, "gesture: " + bestPrediction.name + " score: " 8224213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov + bestPrediction.score); 8234213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov } 8244213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov try { 8254213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov final int gestureId = Integer.parseInt(bestPrediction.name); 826e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov mAms.onGesture(gestureId); 8274213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov } catch (NumberFormatException nfe) { 8284213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov Slog.w(LOG_TAG, "Non numeric gesture id:" + bestPrediction.name); 8294213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov } 8304213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov } 8314213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov } 8324213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov 8334213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov mStrokeBuffer.clear(); 83484044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav mExitGestureDetectionModeDelayed.cancel(); 8354213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov mCurrentState = STATE_TOUCH_EXPLORING; 8364213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov } break; 8374213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov case MotionEvent.ACTION_CANCEL: { 838e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov clear(event, policyFlags); 8394213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov } break; 8404213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov } 8414213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov } 8424213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov 843736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov /** 84477276b60851a158ad3e142cb3b091d57ae5ceffbSvetoslav Ganov * Sends an accessibility event of the given type. 84577276b60851a158ad3e142cb3b091d57ae5ceffbSvetoslav Ganov * 84677276b60851a158ad3e142cb3b091d57ae5ceffbSvetoslav Ganov * @param type The event type. 84777276b60851a158ad3e142cb3b091d57ae5ceffbSvetoslav Ganov */ 84877276b60851a158ad3e142cb3b091d57ae5ceffbSvetoslav Ganov private void sendAccessibilityEvent(int type) { 84977276b60851a158ad3e142cb3b091d57ae5ceffbSvetoslav Ganov AccessibilityManager accessibilityManager = AccessibilityManager.getInstance(mContext); 85077276b60851a158ad3e142cb3b091d57ae5ceffbSvetoslav Ganov if (accessibilityManager.isEnabled()) { 85177276b60851a158ad3e142cb3b091d57ae5ceffbSvetoslav Ganov AccessibilityEvent event = AccessibilityEvent.obtain(type); 8523d1c5a7236c4709550ca7c0cfa293fc5c974c56bAlan Viverette event.setWindowId(mAms.getActiveWindowId()); 85377276b60851a158ad3e142cb3b091d57ae5ceffbSvetoslav Ganov accessibilityManager.sendAccessibilityEvent(event); 854f068fed6c4c3fc2003aec19b6e7e892358179b02Svetoslav Ganov switch (type) { 855f068fed6c4c3fc2003aec19b6e7e892358179b02Svetoslav Ganov case AccessibilityEvent.TYPE_TOUCH_EXPLORATION_GESTURE_START: { 856f068fed6c4c3fc2003aec19b6e7e892358179b02Svetoslav Ganov mTouchExplorationInProgress = true; 857f068fed6c4c3fc2003aec19b6e7e892358179b02Svetoslav Ganov } break; 858f068fed6c4c3fc2003aec19b6e7e892358179b02Svetoslav Ganov case AccessibilityEvent.TYPE_TOUCH_EXPLORATION_GESTURE_END: { 859f068fed6c4c3fc2003aec19b6e7e892358179b02Svetoslav Ganov mTouchExplorationInProgress = false; 860f068fed6c4c3fc2003aec19b6e7e892358179b02Svetoslav Ganov } break; 861f068fed6c4c3fc2003aec19b6e7e892358179b02Svetoslav Ganov } 86277276b60851a158ad3e142cb3b091d57ae5ceffbSvetoslav Ganov } 86377276b60851a158ad3e142cb3b091d57ae5ceffbSvetoslav Ganov } 86477276b60851a158ad3e142cb3b091d57ae5ceffbSvetoslav Ganov 86577276b60851a158ad3e142cb3b091d57ae5ceffbSvetoslav Ganov /** 86684044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav * Sends down events to the view hierarchy for all pointers which are 867736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov * not already being delivered i.e. pointers that are not yet injected. 868736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov * 869736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov * @param prototype The prototype from which to create the injected events. 870736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov * @param policyFlags The policy flags associated with the event. 871736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov */ 87284044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav private void sendDownForAllNotInjectedPointers(MotionEvent prototype, int policyFlags) { 8734213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov InjectedPointerTracker injectedPointers = mInjectedPointerTracker; 87484044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav 87584044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav // Inject the injected pointers. 876f804420d6e37748b75478406e989c69303756980Svetoslav Ganov int pointerIdBits = 0; 877f804420d6e37748b75478406e989c69303756980Svetoslav Ganov final int pointerCount = prototype.getPointerCount(); 878f804420d6e37748b75478406e989c69303756980Svetoslav Ganov for (int i = 0; i < pointerCount; i++) { 879f804420d6e37748b75478406e989c69303756980Svetoslav Ganov final int pointerId = prototype.getPointerId(i); 880f804420d6e37748b75478406e989c69303756980Svetoslav Ganov // Do not send event for already delivered pointers. 88184044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav if (!injectedPointers.isInjectedPointerDown(pointerId)) { 88284044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav pointerIdBits |= (1 << pointerId); 88384044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav final int action = computeInjectionAction(MotionEvent.ACTION_DOWN, i); 88484044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav sendMotionEvent(prototype, action, pointerIdBits, policyFlags); 885736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov } 886f804420d6e37748b75478406e989c69303756980Svetoslav Ganov } 887f804420d6e37748b75478406e989c69303756980Svetoslav Ganov } 888736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov 889f804420d6e37748b75478406e989c69303756980Svetoslav Ganov /** 890e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov * Sends the exit events if needed. Such events are hover exit and touch explore 891e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov * gesture end. 892e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov * 893e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov * @param policyFlags The policy flags associated with the event. 894e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov */ 895f068fed6c4c3fc2003aec19b6e7e892358179b02Svetoslav Ganov private void sendHoverExitAndTouchExplorationGestureEndIfNeeded(int policyFlags) { 896e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov MotionEvent event = mInjectedPointerTracker.getLastInjectedHoverEvent(); 897e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov if (event != null && event.getActionMasked() != MotionEvent.ACTION_HOVER_EXIT) { 898e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov final int pointerIdBits = event.getPointerIdBits(); 899f068fed6c4c3fc2003aec19b6e7e892358179b02Svetoslav Ganov if (!mSendTouchExplorationEndDelayed.isPending()) { 900f068fed6c4c3fc2003aec19b6e7e892358179b02Svetoslav Ganov mSendTouchExplorationEndDelayed.post(); 901fe304b893968887323b93764caafa66ee8ad44deSvetoslav Ganov } 902e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov sendMotionEvent(event, MotionEvent.ACTION_HOVER_EXIT, pointerIdBits, policyFlags); 903e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov } 904e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov } 905e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov 906e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov /** 907e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov * Sends the enter events if needed. Such events are hover enter and touch explore 908e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov * gesture start. 909f804420d6e37748b75478406e989c69303756980Svetoslav Ganov * 910f804420d6e37748b75478406e989c69303756980Svetoslav Ganov * @param policyFlags The policy flags associated with the event. 911f804420d6e37748b75478406e989c69303756980Svetoslav Ganov */ 912f068fed6c4c3fc2003aec19b6e7e892358179b02Svetoslav Ganov private void sendTouchExplorationGestureStartAndHoverEnterIfNeeded(int policyFlags) { 913e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov MotionEvent event = mInjectedPointerTracker.getLastInjectedHoverEvent(); 914e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov if (event != null && event.getActionMasked() == MotionEvent.ACTION_HOVER_EXIT) { 915e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov final int pointerIdBits = event.getPointerIdBits(); 916f068fed6c4c3fc2003aec19b6e7e892358179b02Svetoslav Ganov sendAccessibilityEvent(AccessibilityEvent.TYPE_TOUCH_EXPLORATION_GESTURE_START); 917e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov sendMotionEvent(event, MotionEvent.ACTION_HOVER_ENTER, pointerIdBits, policyFlags); 918736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov } 919736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov } 920736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov 921736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov /** 92284044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav * Sends up events to the view hierarchy for all pointers which are 923736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov * already being delivered i.e. pointers that are injected. 924736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov * 925736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov * @param prototype The prototype from which to create the injected events. 926736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov * @param policyFlags The policy flags associated with the event. 927736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov */ 928736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov private void sendUpForInjectedDownPointers(MotionEvent prototype, int policyFlags) { 9294213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov final InjectedPointerTracker injectedTracked = mInjectedPointerTracker; 930f804420d6e37748b75478406e989c69303756980Svetoslav Ganov int pointerIdBits = 0; 931f804420d6e37748b75478406e989c69303756980Svetoslav Ganov final int pointerCount = prototype.getPointerCount(); 932736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov for (int i = 0; i < pointerCount; i++) { 933736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov final int pointerId = prototype.getPointerId(i); 934736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov // Skip non injected down pointers. 9354213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov if (!injectedTracked.isInjectedPointerDown(pointerId)) { 936736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov continue; 937736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov } 938f804420d6e37748b75478406e989c69303756980Svetoslav Ganov pointerIdBits |= (1 << pointerId); 939f804420d6e37748b75478406e989c69303756980Svetoslav Ganov final int action = computeInjectionAction(MotionEvent.ACTION_UP, i); 940f804420d6e37748b75478406e989c69303756980Svetoslav Ganov sendMotionEvent(prototype, action, pointerIdBits, policyFlags); 941736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov } 942736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov } 943736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov 944736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov /** 945736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov * Sends an up and down events. 946736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov * 947736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov * @param prototype The prototype from which to create the injected events. 948736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov * @param policyFlags The policy flags associated with the event. 949ded133c446fa9d0d23e6bde19a66fb2ce3980491Svetoslav * @param targetAccessibilityFocus Whether the event targets the accessibility focus. 950736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov */ 951ded133c446fa9d0d23e6bde19a66fb2ce3980491Svetoslav private void sendActionDownAndUp(MotionEvent prototype, int policyFlags, 952ded133c446fa9d0d23e6bde19a66fb2ce3980491Svetoslav boolean targetAccessibilityFocus) { 95384044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav // Tap with the pointer that last explored. 954bd206d129fdd1777b9f9646a834d7fc342a8941eSvetoslav Ganov final int pointerId = prototype.getPointerId(prototype.getActionIndex()); 955f804420d6e37748b75478406e989c69303756980Svetoslav Ganov final int pointerIdBits = (1 << pointerId); 956ded133c446fa9d0d23e6bde19a66fb2ce3980491Svetoslav prototype.setTargetAccessibilityFocus(targetAccessibilityFocus); 957f804420d6e37748b75478406e989c69303756980Svetoslav Ganov sendMotionEvent(prototype, MotionEvent.ACTION_DOWN, pointerIdBits, policyFlags); 958ded133c446fa9d0d23e6bde19a66fb2ce3980491Svetoslav prototype.setTargetAccessibilityFocus(targetAccessibilityFocus); 959f804420d6e37748b75478406e989c69303756980Svetoslav Ganov sendMotionEvent(prototype, MotionEvent.ACTION_UP, pointerIdBits, policyFlags); 960736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov } 961736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov 962736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov /** 96391feae3c5994bd4768cea3507c62c65746adcfa6Svetoslav Ganov * Sends an event. 964736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov * 96500f7b3f76515d1c6fbe5cf9fee9d3760787c03cdSvetoslav Ganov * @param prototype The prototype from which to create the injected events. 96691feae3c5994bd4768cea3507c62c65746adcfa6Svetoslav Ganov * @param action The action of the event. 96791feae3c5994bd4768cea3507c62c65746adcfa6Svetoslav Ganov * @param pointerIdBits The bits of the pointers to send. 968736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov * @param policyFlags The policy flags associated with the event. 969736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov */ 97091feae3c5994bd4768cea3507c62c65746adcfa6Svetoslav Ganov private void sendMotionEvent(MotionEvent prototype, int action, int pointerIdBits, 97191feae3c5994bd4768cea3507c62c65746adcfa6Svetoslav Ganov int policyFlags) { 972f804420d6e37748b75478406e989c69303756980Svetoslav Ganov prototype.setAction(action); 973f804420d6e37748b75478406e989c69303756980Svetoslav Ganov 974f804420d6e37748b75478406e989c69303756980Svetoslav Ganov MotionEvent event = null; 975f804420d6e37748b75478406e989c69303756980Svetoslav Ganov if (pointerIdBits == ALL_POINTER_ID_BITS) { 976f804420d6e37748b75478406e989c69303756980Svetoslav Ganov event = prototype; 977f804420d6e37748b75478406e989c69303756980Svetoslav Ganov } else { 978f804420d6e37748b75478406e989c69303756980Svetoslav Ganov event = prototype.split(pointerIdBits); 979f804420d6e37748b75478406e989c69303756980Svetoslav Ganov } 980f804420d6e37748b75478406e989c69303756980Svetoslav Ganov if (action == MotionEvent.ACTION_DOWN) { 981f804420d6e37748b75478406e989c69303756980Svetoslav Ganov event.setDownTime(event.getEventTime()); 982f804420d6e37748b75478406e989c69303756980Svetoslav Ganov } else { 9834213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov event.setDownTime(mInjectedPointerTracker.getLastInjectedDownEventTime()); 984f804420d6e37748b75478406e989c69303756980Svetoslav Ganov } 985f804420d6e37748b75478406e989c69303756980Svetoslav Ganov 986e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov // If the user is long pressing but the long pressing pointer 987e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov // was not exactly over the accessibility focused item we need 988e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov // to remap the location of that pointer so the user does not 989e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov // have to explicitly touch explore something to be able to 990e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov // long press it, or even worse to avoid the user long pressing 991e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov // on the wrong item since click and long press behave differently. 992e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov if (mLongPressingPointerId >= 0) { 993a3315270e0502189a6fa4de20b9db2bf042cfd2aSvetoslav event = offsetEvent(event, - mLongPressingPointerDeltaX, 994a3315270e0502189a6fa4de20b9db2bf042cfd2aSvetoslav - mLongPressingPointerDeltaY); 995e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov } 996e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov 997f804420d6e37748b75478406e989c69303756980Svetoslav Ganov if (DEBUG) { 9984213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov Slog.d(LOG_TAG, "Injecting event: " + event + ", policyFlags=0x" 999f804420d6e37748b75478406e989c69303756980Svetoslav Ganov + Integer.toHexString(policyFlags)); 1000f804420d6e37748b75478406e989c69303756980Svetoslav Ganov } 1001f804420d6e37748b75478406e989c69303756980Svetoslav Ganov 1002f804420d6e37748b75478406e989c69303756980Svetoslav Ganov // Make sure that the user will see the event. 1003f804420d6e37748b75478406e989c69303756980Svetoslav Ganov policyFlags |= WindowManagerPolicy.FLAG_PASS_TO_USER; 10041cf70bbf96930662cab0e699d70b62865766ff52Svetoslav Ganov if (mNext != null) { 100545af84a483165f06c04d74baba67f90da29c6ad2Svetoslav Ganov // TODO: For now pass null for the raw event since the touch 100645af84a483165f06c04d74baba67f90da29c6ad2Svetoslav Ganov // explorer is the last event transformation and it does 100745af84a483165f06c04d74baba67f90da29c6ad2Svetoslav Ganov // not care about the raw event. 100845af84a483165f06c04d74baba67f90da29c6ad2Svetoslav Ganov mNext.onMotionEvent(event, null, policyFlags); 10091cf70bbf96930662cab0e699d70b62865766ff52Svetoslav Ganov } 1010f804420d6e37748b75478406e989c69303756980Svetoslav Ganov 10114213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov mInjectedPointerTracker.onMotionEvent(event); 10124213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov 1013f804420d6e37748b75478406e989c69303756980Svetoslav Ganov if (event != prototype) { 1014f804420d6e37748b75478406e989c69303756980Svetoslav Ganov event.recycle(); 1015f804420d6e37748b75478406e989c69303756980Svetoslav Ganov } 1016736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov } 1017736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov 1018736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov /** 1019a3315270e0502189a6fa4de20b9db2bf042cfd2aSvetoslav * Offsets all pointers in the given event by adding the specified X and Y 1020a3315270e0502189a6fa4de20b9db2bf042cfd2aSvetoslav * offsets. 1021a3315270e0502189a6fa4de20b9db2bf042cfd2aSvetoslav * 1022a3315270e0502189a6fa4de20b9db2bf042cfd2aSvetoslav * @param event The event to offset. 1023a3315270e0502189a6fa4de20b9db2bf042cfd2aSvetoslav * @param offsetX The X offset. 1024a3315270e0502189a6fa4de20b9db2bf042cfd2aSvetoslav * @param offsetY The Y offset. 1025a3315270e0502189a6fa4de20b9db2bf042cfd2aSvetoslav * @return An event with the offset pointers or the original event if both 1026a3315270e0502189a6fa4de20b9db2bf042cfd2aSvetoslav * offsets are zero. 1027a3315270e0502189a6fa4de20b9db2bf042cfd2aSvetoslav */ 1028a3315270e0502189a6fa4de20b9db2bf042cfd2aSvetoslav private MotionEvent offsetEvent(MotionEvent event, int offsetX, int offsetY) { 1029a3315270e0502189a6fa4de20b9db2bf042cfd2aSvetoslav if (offsetX == 0 && offsetY == 0) { 1030a3315270e0502189a6fa4de20b9db2bf042cfd2aSvetoslav return event; 1031a3315270e0502189a6fa4de20b9db2bf042cfd2aSvetoslav } 1032a3315270e0502189a6fa4de20b9db2bf042cfd2aSvetoslav final int remappedIndex = event.findPointerIndex(mLongPressingPointerId); 1033a3315270e0502189a6fa4de20b9db2bf042cfd2aSvetoslav final int pointerCount = event.getPointerCount(); 1034a3315270e0502189a6fa4de20b9db2bf042cfd2aSvetoslav PointerProperties[] props = PointerProperties.createArray(pointerCount); 1035a3315270e0502189a6fa4de20b9db2bf042cfd2aSvetoslav PointerCoords[] coords = PointerCoords.createArray(pointerCount); 1036a3315270e0502189a6fa4de20b9db2bf042cfd2aSvetoslav for (int i = 0; i < pointerCount; i++) { 1037a3315270e0502189a6fa4de20b9db2bf042cfd2aSvetoslav event.getPointerProperties(i, props[i]); 1038a3315270e0502189a6fa4de20b9db2bf042cfd2aSvetoslav event.getPointerCoords(i, coords[i]); 1039a3315270e0502189a6fa4de20b9db2bf042cfd2aSvetoslav if (i == remappedIndex) { 1040a3315270e0502189a6fa4de20b9db2bf042cfd2aSvetoslav coords[i].x += offsetX; 1041a3315270e0502189a6fa4de20b9db2bf042cfd2aSvetoslav coords[i].y += offsetY; 1042a3315270e0502189a6fa4de20b9db2bf042cfd2aSvetoslav } 1043a3315270e0502189a6fa4de20b9db2bf042cfd2aSvetoslav } 1044a3315270e0502189a6fa4de20b9db2bf042cfd2aSvetoslav return MotionEvent.obtain(event.getDownTime(), 1045a3315270e0502189a6fa4de20b9db2bf042cfd2aSvetoslav event.getEventTime(), event.getAction(), event.getPointerCount(), 1046a3315270e0502189a6fa4de20b9db2bf042cfd2aSvetoslav props, coords, event.getMetaState(), event.getButtonState(), 1047a3315270e0502189a6fa4de20b9db2bf042cfd2aSvetoslav 1.0f, 1.0f, event.getDeviceId(), event.getEdgeFlags(), 1048a3315270e0502189a6fa4de20b9db2bf042cfd2aSvetoslav event.getSource(), event.getFlags()); 1049a3315270e0502189a6fa4de20b9db2bf042cfd2aSvetoslav } 1050a3315270e0502189a6fa4de20b9db2bf042cfd2aSvetoslav 1051a3315270e0502189a6fa4de20b9db2bf042cfd2aSvetoslav /** 1052736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov * Computes the action for an injected event based on a masked action 1053736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov * and a pointer index. 1054736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov * 1055736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov * @param actionMasked The masked action. 1056736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov * @param pointerIndex The index of the pointer which has changed. 1057736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov * @return The action to be used for injection. 1058736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov */ 1059736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov private int computeInjectionAction(int actionMasked, int pointerIndex) { 1060736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov switch (actionMasked) { 1061736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov case MotionEvent.ACTION_DOWN: 1062736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov case MotionEvent.ACTION_POINTER_DOWN: { 10634213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov InjectedPointerTracker injectedTracker = mInjectedPointerTracker; 1064736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov // Compute the action based on how many down pointers are injected. 10654213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov if (injectedTracker.getInjectedPointerDownCount() == 0) { 1066736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov return MotionEvent.ACTION_DOWN; 1067736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov } else { 1068736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov return (pointerIndex << MotionEvent.ACTION_POINTER_INDEX_SHIFT) 1069736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov | MotionEvent.ACTION_POINTER_DOWN; 1070736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov } 1071736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov } 1072736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov case MotionEvent.ACTION_POINTER_UP: { 10734213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov InjectedPointerTracker injectedTracker = mInjectedPointerTracker; 1074736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov // Compute the action based on how many down pointers are injected. 10754213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov if (injectedTracker.getInjectedPointerDownCount() == 1) { 1076736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov return MotionEvent.ACTION_UP; 1077736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov } else { 1078736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov return (pointerIndex << MotionEvent.ACTION_POINTER_INDEX_SHIFT) 1079736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov | MotionEvent.ACTION_POINTER_UP; 1080736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov } 1081736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov } 1082736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov default: 1083736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov return actionMasked; 1084736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov } 1085736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov } 1086736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov 1087e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov private class DoubleTapDetector { 1088e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov private MotionEvent mDownEvent; 1089e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov private MotionEvent mFirstTapEvent; 1090e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov 1091e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov public void onMotionEvent(MotionEvent event, int policyFlags) { 10921cf70bbf96930662cab0e699d70b62865766ff52Svetoslav Ganov final int actionIndex = event.getActionIndex(); 1093e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov final int action = event.getActionMasked(); 1094e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov switch (action) { 1095e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov case MotionEvent.ACTION_DOWN: 1096e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov case MotionEvent.ACTION_POINTER_DOWN: { 10971cf70bbf96930662cab0e699d70b62865766ff52Svetoslav Ganov if (mFirstTapEvent != null 10981cf70bbf96930662cab0e699d70b62865766ff52Svetoslav Ganov && !GestureUtils.isSamePointerContext(mFirstTapEvent, event)) { 1099e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov clear(); 1100e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov } 1101e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov mDownEvent = MotionEvent.obtain(event); 1102e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov } break; 1103e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov case MotionEvent.ACTION_UP: 1104e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov case MotionEvent.ACTION_POINTER_UP: { 1105e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov if (mDownEvent == null) { 1106e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov return; 1107e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov } 11081cf70bbf96930662cab0e699d70b62865766ff52Svetoslav Ganov if (!GestureUtils.isSamePointerContext(mDownEvent, event)) { 1109e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov clear(); 1110e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov return; 1111e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov } 11121cf70bbf96930662cab0e699d70b62865766ff52Svetoslav Ganov if (GestureUtils.isTap(mDownEvent, event, mTapTimeout, mTouchSlop, 11131cf70bbf96930662cab0e699d70b62865766ff52Svetoslav Ganov actionIndex)) { 11141cf70bbf96930662cab0e699d70b62865766ff52Svetoslav Ganov if (mFirstTapEvent == null || GestureUtils.isTimedOut(mFirstTapEvent, 11151cf70bbf96930662cab0e699d70b62865766ff52Svetoslav Ganov event, mDoubleTapTimeout)) { 1116e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov mFirstTapEvent = MotionEvent.obtain(event); 1117e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov mDownEvent.recycle(); 1118e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov mDownEvent = null; 1119e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov return; 1120e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov } 11211cf70bbf96930662cab0e699d70b62865766ff52Svetoslav Ganov if (GestureUtils.isMultiTap(mFirstTapEvent, event, mDoubleTapTimeout, 11221cf70bbf96930662cab0e699d70b62865766ff52Svetoslav Ganov mDoubleTapSlop, actionIndex)) { 1123e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov onDoubleTap(event, policyFlags); 1124e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov mFirstTapEvent.recycle(); 1125e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov mFirstTapEvent = null; 1126e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov mDownEvent.recycle(); 1127e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov mDownEvent = null; 1128e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov return; 1129e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov } 1130e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov mFirstTapEvent.recycle(); 1131e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov mFirstTapEvent = null; 1132e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov } else { 1133e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov if (mFirstTapEvent != null) { 1134e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov mFirstTapEvent.recycle(); 1135e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov mFirstTapEvent = null; 1136e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov } 1137e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov } 1138e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov mDownEvent.recycle(); 1139e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov mDownEvent = null; 1140e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov } break; 1141e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov } 1142e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov } 1143e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov 1144e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov public void onDoubleTap(MotionEvent secondTapUp, int policyFlags) { 1145e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov // This should never be called when more than two pointers are down. 1146e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov if (secondTapUp.getPointerCount() > 2) { 1147e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov return; 1148e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov } 1149e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov 1150e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov // Remove pending event deliveries. 115184044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav mSendHoverEnterAndMoveDelayed.cancel(); 115284044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav mSendHoverExitDelayed.cancel(); 115384044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav mPerformLongPressDelayed.cancel(); 1154e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov 1155f068fed6c4c3fc2003aec19b6e7e892358179b02Svetoslav Ganov if (mSendTouchExplorationEndDelayed.isPending()) { 1156f068fed6c4c3fc2003aec19b6e7e892358179b02Svetoslav Ganov mSendTouchExplorationEndDelayed.forceSendAndRemove(); 1157f068fed6c4c3fc2003aec19b6e7e892358179b02Svetoslav Ganov } 1158f068fed6c4c3fc2003aec19b6e7e892358179b02Svetoslav Ganov if (mSendTouchInteractionEndDelayed.isPending()) { 1159f068fed6c4c3fc2003aec19b6e7e892358179b02Svetoslav Ganov mSendTouchInteractionEndDelayed.forceSendAndRemove(); 1160f068fed6c4c3fc2003aec19b6e7e892358179b02Svetoslav Ganov } 1161e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov 1162e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov final int pointerId = secondTapUp.getPointerId(secondTapUp.getActionIndex()); 1163e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov final int pointerIndex = secondTapUp.findPointerIndex(pointerId); 116486783474fdec98a22bc22e224462767eab13e273Svetoslav Ganov 116547b9c1524fe20b9ae2b210acdcad64f764df68e0Svetoslav Point clickLocation = mTempPoint; 1166ded133c446fa9d0d23e6bde19a66fb2ce3980491Svetoslav final int result = computeClickLocation(clickLocation); 1167ded133c446fa9d0d23e6bde19a66fb2ce3980491Svetoslav if (result == CLICK_LOCATION_NONE) { 116847b9c1524fe20b9ae2b210acdcad64f764df68e0Svetoslav return; 1169e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov } 1170e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov 1171e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov // Do the click. 1172e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov PointerProperties[] properties = new PointerProperties[1]; 1173e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov properties[0] = new PointerProperties(); 1174e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov secondTapUp.getPointerProperties(pointerIndex, properties[0]); 1175e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov PointerCoords[] coords = new PointerCoords[1]; 1176e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov coords[0] = new PointerCoords(); 117747b9c1524fe20b9ae2b210acdcad64f764df68e0Svetoslav coords[0].x = clickLocation.x; 117847b9c1524fe20b9ae2b210acdcad64f764df68e0Svetoslav coords[0].y = clickLocation.y; 1179e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov MotionEvent event = MotionEvent.obtain(secondTapUp.getDownTime(), 1180e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov secondTapUp.getEventTime(), MotionEvent.ACTION_DOWN, 1, properties, 1181e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov coords, 0, 0, 1.0f, 1.0f, secondTapUp.getDeviceId(), 0, 1182e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov secondTapUp.getSource(), secondTapUp.getFlags()); 1183ded133c446fa9d0d23e6bde19a66fb2ce3980491Svetoslav final boolean targetAccessibilityFocus = (result == CLICK_LOCATION_ACCESSIBILITY_FOCUS); 1184ded133c446fa9d0d23e6bde19a66fb2ce3980491Svetoslav sendActionDownAndUp(event, policyFlags, targetAccessibilityFocus); 1185e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov event.recycle(); 1186e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov } 1187e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov 1188e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov public void clear() { 1189e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov if (mDownEvent != null) { 1190e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov mDownEvent.recycle(); 1191e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov mDownEvent = null; 1192e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov } 1193e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov if (mFirstTapEvent != null) { 1194e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov mFirstTapEvent.recycle(); 1195e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov mFirstTapEvent = null; 1196e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov } 1197e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov } 1198e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov 1199e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov public boolean firstTapDetected() { 1200e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov return mFirstTapEvent != null 1201e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov && SystemClock.uptimeMillis() - mFirstTapEvent.getEventTime() < mDoubleTapTimeout; 1202e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov } 1203e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov } 1204e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov 1205736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov /** 1206736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov * Determines whether a two pointer gesture is a dragging one. 1207736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov * 1208736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov * @param event The event with the pointer data. 1209736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov * @return True if the gesture is a dragging one. 1210736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov */ 1211736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov private boolean isDraggingGesture(MotionEvent event) { 12124213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov ReceivedPointerTracker receivedTracker = mReceivedPointerTracker; 1213736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov 121484044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav final float firstPtrX = event.getX(0); 121584044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav final float firstPtrY = event.getY(0); 121684044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav final float secondPtrX = event.getX(1); 121784044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav final float secondPtrY = event.getY(1); 1218736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov 121984044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav final float firstPtrDownX = receivedTracker.getReceivedPointerDownX(0); 122084044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav final float firstPtrDownY = receivedTracker.getReceivedPointerDownY(0); 122184044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav final float secondPtrDownX = receivedTracker.getReceivedPointerDownX(1); 122284044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav final float secondPtrDownY = receivedTracker.getReceivedPointerDownY(1); 1223736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov 12241cf70bbf96930662cab0e699d70b62865766ff52Svetoslav Ganov return GestureUtils.isDraggingGesture(firstPtrDownX, firstPtrDownY, secondPtrDownX, 12251cf70bbf96930662cab0e699d70b62865766ff52Svetoslav Ganov secondPtrDownY, firstPtrX, firstPtrY, secondPtrX, secondPtrY, 12261cf70bbf96930662cab0e699d70b62865766ff52Svetoslav Ganov MAX_DRAGGING_ANGLE_COS); 1227736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov } 1228736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov 1229ded133c446fa9d0d23e6bde19a66fb2ce3980491Svetoslav private int computeClickLocation(Point outLocation) { 123047b9c1524fe20b9ae2b210acdcad64f764df68e0Svetoslav MotionEvent lastExploreEvent = mInjectedPointerTracker.getLastInjectedHoverEventForClick(); 123147b9c1524fe20b9ae2b210acdcad64f764df68e0Svetoslav if (lastExploreEvent != null) { 123247b9c1524fe20b9ae2b210acdcad64f764df68e0Svetoslav final int lastExplorePointerIndex = lastExploreEvent.getActionIndex(); 123347b9c1524fe20b9ae2b210acdcad64f764df68e0Svetoslav outLocation.x = (int) lastExploreEvent.getX(lastExplorePointerIndex); 123447b9c1524fe20b9ae2b210acdcad64f764df68e0Svetoslav outLocation.y = (int) lastExploreEvent.getY(lastExplorePointerIndex); 123547b9c1524fe20b9ae2b210acdcad64f764df68e0Svetoslav if (!mAms.accessibilityFocusOnlyInActiveWindow() 123647b9c1524fe20b9ae2b210acdcad64f764df68e0Svetoslav || mLastTouchedWindowId == mAms.getActiveWindowId()) { 1237ded133c446fa9d0d23e6bde19a66fb2ce3980491Svetoslav if (mAms.getAccessibilityFocusClickPointInScreen(outLocation)) { 1238ded133c446fa9d0d23e6bde19a66fb2ce3980491Svetoslav return CLICK_LOCATION_ACCESSIBILITY_FOCUS; 1239ded133c446fa9d0d23e6bde19a66fb2ce3980491Svetoslav } else { 1240ded133c446fa9d0d23e6bde19a66fb2ce3980491Svetoslav return CLICK_LOCATION_LAST_TOUCH_EXPLORED; 1241ded133c446fa9d0d23e6bde19a66fb2ce3980491Svetoslav } 124247b9c1524fe20b9ae2b210acdcad64f764df68e0Svetoslav } 124347b9c1524fe20b9ae2b210acdcad64f764df68e0Svetoslav } 124447b9c1524fe20b9ae2b210acdcad64f764df68e0Svetoslav if (mAms.getAccessibilityFocusClickPointInScreen(outLocation)) { 1245ded133c446fa9d0d23e6bde19a66fb2ce3980491Svetoslav return CLICK_LOCATION_ACCESSIBILITY_FOCUS; 124647b9c1524fe20b9ae2b210acdcad64f764df68e0Svetoslav } 1247ded133c446fa9d0d23e6bde19a66fb2ce3980491Svetoslav return CLICK_LOCATION_NONE; 124847b9c1524fe20b9ae2b210acdcad64f764df68e0Svetoslav } 124947b9c1524fe20b9ae2b210acdcad64f764df68e0Svetoslav 1250736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov /** 125151cccf0845b36539d42503495f0689d487712b3aSvetoslav Ganov * Gets the symbolic name of a state. 125251cccf0845b36539d42503495f0689d487712b3aSvetoslav Ganov * 125351cccf0845b36539d42503495f0689d487712b3aSvetoslav Ganov * @param state A state. 125451cccf0845b36539d42503495f0689d487712b3aSvetoslav Ganov * @return The state symbolic name. 125551cccf0845b36539d42503495f0689d487712b3aSvetoslav Ganov */ 125651cccf0845b36539d42503495f0689d487712b3aSvetoslav Ganov private static String getStateSymbolicName(int state) { 125751cccf0845b36539d42503495f0689d487712b3aSvetoslav Ganov switch (state) { 125851cccf0845b36539d42503495f0689d487712b3aSvetoslav Ganov case STATE_TOUCH_EXPLORING: 125951cccf0845b36539d42503495f0689d487712b3aSvetoslav Ganov return "STATE_TOUCH_EXPLORING"; 126051cccf0845b36539d42503495f0689d487712b3aSvetoslav Ganov case STATE_DRAGGING: 126151cccf0845b36539d42503495f0689d487712b3aSvetoslav Ganov return "STATE_DRAGGING"; 126251cccf0845b36539d42503495f0689d487712b3aSvetoslav Ganov case STATE_DELEGATING: 126351cccf0845b36539d42503495f0689d487712b3aSvetoslav Ganov return "STATE_DELEGATING"; 12644213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov case STATE_GESTURE_DETECTING: 12654213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov return "STATE_GESTURE_DETECTING"; 126651cccf0845b36539d42503495f0689d487712b3aSvetoslav Ganov default: 126751cccf0845b36539d42503495f0689d487712b3aSvetoslav Ganov throw new IllegalArgumentException("Unknown state: " + state); 126851cccf0845b36539d42503495f0689d487712b3aSvetoslav Ganov } 126951cccf0845b36539d42503495f0689d487712b3aSvetoslav Ganov } 127051cccf0845b36539d42503495f0689d487712b3aSvetoslav Ganov 127151cccf0845b36539d42503495f0689d487712b3aSvetoslav Ganov /** 127295068e5d1bea47091e97955f271c789264994550Svetoslav Ganov * Class for delayed exiting from gesture detecting mode. 127395068e5d1bea47091e97955f271c789264994550Svetoslav Ganov */ 127495068e5d1bea47091e97955f271c789264994550Svetoslav Ganov private final class ExitGestureDetectionModeDelayed implements Runnable { 127595068e5d1bea47091e97955f271c789264994550Svetoslav Ganov 127695068e5d1bea47091e97955f271c789264994550Svetoslav Ganov public void post() { 127795068e5d1bea47091e97955f271c789264994550Svetoslav Ganov mHandler.postDelayed(this, EXIT_GESTURE_DETECTION_TIMEOUT); 127895068e5d1bea47091e97955f271c789264994550Svetoslav Ganov } 127995068e5d1bea47091e97955f271c789264994550Svetoslav Ganov 128084044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav public void cancel() { 128195068e5d1bea47091e97955f271c789264994550Svetoslav Ganov mHandler.removeCallbacks(this); 128295068e5d1bea47091e97955f271c789264994550Svetoslav Ganov } 128395068e5d1bea47091e97955f271c789264994550Svetoslav Ganov 128495068e5d1bea47091e97955f271c789264994550Svetoslav Ganov @Override 128595068e5d1bea47091e97955f271c789264994550Svetoslav Ganov public void run() { 1286aed4b6f812674bc60a04470013ca449e5c114fa5Svetoslav Ganov // Announce the end of gesture recognition. 1287aed4b6f812674bc60a04470013ca449e5c114fa5Svetoslav Ganov sendAccessibilityEvent(AccessibilityEvent.TYPE_GESTURE_DETECTION_END); 1288aed4b6f812674bc60a04470013ca449e5c114fa5Svetoslav Ganov // Clearing puts is in touch exploration state with a finger already 1289aed4b6f812674bc60a04470013ca449e5c114fa5Svetoslav Ganov // down, so announce the transition to exploration state. 1290aed4b6f812674bc60a04470013ca449e5c114fa5Svetoslav Ganov sendAccessibilityEvent(AccessibilityEvent.TYPE_TOUCH_EXPLORATION_GESTURE_START); 129195068e5d1bea47091e97955f271c789264994550Svetoslav Ganov clear(); 129295068e5d1bea47091e97955f271c789264994550Svetoslav Ganov } 129395068e5d1bea47091e97955f271c789264994550Svetoslav Ganov } 129495068e5d1bea47091e97955f271c789264994550Svetoslav Ganov 129595068e5d1bea47091e97955f271c789264994550Svetoslav Ganov /** 12964213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov * Class for delayed sending of long press. 12974213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov */ 12984213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov private final class PerformLongPressDelayed implements Runnable { 12994213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov private MotionEvent mEvent; 13004213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov private int mPolicyFlags; 13014213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov 1302e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov public void post(MotionEvent prototype, int policyFlags) { 13034213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov mEvent = MotionEvent.obtain(prototype); 13044213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov mPolicyFlags = policyFlags; 1305e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov mHandler.postDelayed(this, ViewConfiguration.getLongPressTimeout()); 13064213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov } 13074213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov 130884044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav public void cancel() { 130984044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav if (mEvent != null) { 13104213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov mHandler.removeCallbacks(this); 13114213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov clear(); 13124213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov } 13134213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov } 13144213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov 131584044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav private boolean isPending() { 131684044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav return mHandler.hasCallbacks(this); 13174213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov } 13184213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov 13194213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov @Override 13204213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov public void run() { 132184044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav // Pointers should not be zero when running this command. 132284044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav if (mReceivedPointerTracker.getLastReceivedEvent().getPointerCount() == 0) { 1323ebac1b79c4a355d8cd73b49df059deb00d7aa256Svetoslav Ganov return; 1324ebac1b79c4a355d8cd73b49df059deb00d7aa256Svetoslav Ganov } 1325ebac1b79c4a355d8cd73b49df059deb00d7aa256Svetoslav Ganov 1326238099c0dbbdc66b8443552126680ad1c7cab17dSvetoslav Ganov final int pointerId = mEvent.getPointerId(mEvent.getActionIndex()); 1327238099c0dbbdc66b8443552126680ad1c7cab17dSvetoslav Ganov final int pointerIndex = mEvent.findPointerIndex(pointerId); 1328238099c0dbbdc66b8443552126680ad1c7cab17dSvetoslav Ganov 132947b9c1524fe20b9ae2b210acdcad64f764df68e0Svetoslav Point clickLocation = mTempPoint; 1330ded133c446fa9d0d23e6bde19a66fb2ce3980491Svetoslav final int result = computeClickLocation(clickLocation); 1331ded133c446fa9d0d23e6bde19a66fb2ce3980491Svetoslav 1332ded133c446fa9d0d23e6bde19a66fb2ce3980491Svetoslav if (result == CLICK_LOCATION_NONE) { 133347b9c1524fe20b9ae2b210acdcad64f764df68e0Svetoslav return; 1334e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov } 1335238099c0dbbdc66b8443552126680ad1c7cab17dSvetoslav Ganov 133686783474fdec98a22bc22e224462767eab13e273Svetoslav Ganov mLongPressingPointerId = pointerId; 133747b9c1524fe20b9ae2b210acdcad64f764df68e0Svetoslav mLongPressingPointerDeltaX = (int) mEvent.getX(pointerIndex) - clickLocation.x; 133847b9c1524fe20b9ae2b210acdcad64f764df68e0Svetoslav mLongPressingPointerDeltaY = (int) mEvent.getY(pointerIndex) - clickLocation.y; 133986783474fdec98a22bc22e224462767eab13e273Svetoslav Ganov 1340f068fed6c4c3fc2003aec19b6e7e892358179b02Svetoslav Ganov sendHoverExitAndTouchExplorationGestureEndIfNeeded(mPolicyFlags); 13414213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov 1342e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov mCurrentState = STATE_DELEGATING; 134384044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav sendDownForAllNotInjectedPointers(mEvent, mPolicyFlags); 13444213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov clear(); 13454213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov } 13464213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov 13474213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov private void clear() { 13484213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov mEvent.recycle(); 13494213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov mEvent = null; 13504213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov mPolicyFlags = 0; 13514213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov } 13524213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov } 13534213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov 13544213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov /** 135584044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav * Class for delayed sending of hover enter and move events. 13564213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov */ 135784044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav class SendHoverEnterAndMoveDelayed implements Runnable { 135884044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav private final String LOG_TAG_SEND_HOVER_DELAYED = "SendHoverEnterAndMoveDelayed"; 1359e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov 136084044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav private final List<MotionEvent> mEvents = new ArrayList<MotionEvent>(); 1361e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov 13624213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov private int mPointerIdBits; 13634213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov private int mPolicyFlags; 13644213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov 136584044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav public void post(MotionEvent event, boolean touchExplorationInProgress, 136677276b60851a158ad3e142cb3b091d57ae5ceffbSvetoslav Ganov int pointerIdBits, int policyFlags) { 136784044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav cancel(); 136884044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav addEvent(event); 13694213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov mPointerIdBits = pointerIdBits; 13704213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov mPolicyFlags = policyFlags; 1371e47957a0bbe2164467ff6e7a566b0c9e4689cdc9Svetoslav Ganov mHandler.postDelayed(this, mDetermineUserIntentTimeout); 1372e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov } 1373e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov 137484044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav public void addEvent(MotionEvent event) { 137584044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav mEvents.add(MotionEvent.obtain(event)); 137684044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav } 137784044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav 137884044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav public void cancel() { 1379e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov if (isPending()) { 138084044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav mHandler.removeCallbacks(this); 138184044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav clear(); 138284044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav } 138384044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav } 138484044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav 138584044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav private boolean isPending() { 138684044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav return mHandler.hasCallbacks(this); 138784044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav } 138884044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav 138984044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav private void clear() { 139084044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav mPointerIdBits = -1; 139184044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav mPolicyFlags = 0; 139284044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav final int eventCount = mEvents.size(); 139384044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav for (int i = eventCount - 1; i >= 0; i--) { 139484044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav mEvents.remove(i).recycle(); 1395e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov } 1396e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov } 1397e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov 139884044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav public void forceSendAndRemove() { 1399e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov if (isPending()) { 140084044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav run(); 140184044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav cancel(); 1402e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov } 14034213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov } 14044213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov 140584044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav public void run() { 140684044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav // Send an accessibility event to announce the touch exploration start. 140784044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav sendAccessibilityEvent(AccessibilityEvent.TYPE_TOUCH_EXPLORATION_GESTURE_START); 140884044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav 140984044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav if (!mEvents.isEmpty()) { 141084044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav // Deliver a down event. 141184044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav sendMotionEvent(mEvents.get(0), MotionEvent.ACTION_HOVER_ENTER, 141284044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav mPointerIdBits, mPolicyFlags); 141384044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav if (DEBUG) { 141484044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav Slog.d(LOG_TAG_SEND_HOVER_DELAYED, 141584044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav "Injecting motion event: ACTION_HOVER_ENTER"); 141684044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav } 141784044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav 141884044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav // Deliver move events. 141984044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav final int eventCount = mEvents.size(); 142084044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav for (int i = 1; i < eventCount; i++) { 142184044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav sendMotionEvent(mEvents.get(i), MotionEvent.ACTION_HOVER_MOVE, 142284044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav mPointerIdBits, mPolicyFlags); 142384044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav if (DEBUG) { 142484044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav Slog.d(LOG_TAG_SEND_HOVER_DELAYED, 142584044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav "Injecting motion event: ACTION_HOVER_MOVE"); 142684044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav } 142784044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav } 142884044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav } 14294213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov clear(); 14304213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov } 143184044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav } 143284044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav 143384044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav /** 143484044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav * Class for delayed sending of hover exit events. 143584044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav */ 143684044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav class SendHoverExitDelayed implements Runnable { 143784044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav private final String LOG_TAG_SEND_HOVER_DELAYED = "SendHoverExitDelayed"; 143884044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav 143984044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav private MotionEvent mPrototype; 144084044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav private int mPointerIdBits; 144184044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav private int mPolicyFlags; 144284044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav 144384044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav public void post(MotionEvent prototype, int pointerIdBits, int policyFlags) { 144484044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav cancel(); 144584044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav mPrototype = MotionEvent.obtain(prototype); 144684044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav mPointerIdBits = pointerIdBits; 144784044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav mPolicyFlags = policyFlags; 144884044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav mHandler.postDelayed(this, mDetermineUserIntentTimeout); 144984044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav } 145084044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav 145184044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav public void cancel() { 145284044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav if (isPending()) { 145384044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav mHandler.removeCallbacks(this); 145484044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav clear(); 145584044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav } 145684044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav } 14574213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov 1458e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov private boolean isPending() { 145984044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav return mHandler.hasCallbacks(this); 14604213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov } 14614213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov 14624213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov private void clear() { 1463e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov mPrototype.recycle(); 1464e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov mPrototype = null; 14654213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov mPointerIdBits = -1; 14664213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov mPolicyFlags = 0; 14674213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov } 14684213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov 14694213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov public void forceSendAndRemove() { 1470e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov if (isPending()) { 14714213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov run(); 147284044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav cancel(); 14734213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov } 14744213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov } 14754213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov 14764213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov public void run() { 14774213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov if (DEBUG) { 147884044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav Slog.d(LOG_TAG_SEND_HOVER_DELAYED, "Injecting motion event:" 147984044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav + " ACTION_HOVER_EXIT"); 14804213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov } 148184044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav sendMotionEvent(mPrototype, MotionEvent.ACTION_HOVER_EXIT, 148284044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav mPointerIdBits, mPolicyFlags); 148384044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav if (!mSendTouchExplorationEndDelayed.isPending()) { 148484044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav mSendTouchExplorationEndDelayed.cancel(); 148584044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav mSendTouchExplorationEndDelayed.post(); 148684044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav } 148784044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav if (mSendTouchInteractionEndDelayed.isPending()) { 148884044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav mSendTouchInteractionEndDelayed.cancel(); 148984044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav mSendTouchInteractionEndDelayed.post(); 1490e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov } 14914213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov clear(); 14924213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov } 14934213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov } 14944213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov 1495f068fed6c4c3fc2003aec19b6e7e892358179b02Svetoslav Ganov private class SendAccessibilityEventDelayed implements Runnable { 1496f068fed6c4c3fc2003aec19b6e7e892358179b02Svetoslav Ganov private final int mEventType; 1497f068fed6c4c3fc2003aec19b6e7e892358179b02Svetoslav Ganov private final int mDelay; 1498f068fed6c4c3fc2003aec19b6e7e892358179b02Svetoslav Ganov 1499f068fed6c4c3fc2003aec19b6e7e892358179b02Svetoslav Ganov public SendAccessibilityEventDelayed(int eventType, int delay) { 1500f068fed6c4c3fc2003aec19b6e7e892358179b02Svetoslav Ganov mEventType = eventType; 1501f068fed6c4c3fc2003aec19b6e7e892358179b02Svetoslav Ganov mDelay = delay; 1502f068fed6c4c3fc2003aec19b6e7e892358179b02Svetoslav Ganov } 1503fe304b893968887323b93764caafa66ee8ad44deSvetoslav Ganov 150484044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav public void cancel() { 1505fe304b893968887323b93764caafa66ee8ad44deSvetoslav Ganov mHandler.removeCallbacks(this); 1506fe304b893968887323b93764caafa66ee8ad44deSvetoslav Ganov } 1507fe304b893968887323b93764caafa66ee8ad44deSvetoslav Ganov 1508fe304b893968887323b93764caafa66ee8ad44deSvetoslav Ganov public void post() { 1509f068fed6c4c3fc2003aec19b6e7e892358179b02Svetoslav Ganov mHandler.postDelayed(this, mDelay); 1510fe304b893968887323b93764caafa66ee8ad44deSvetoslav Ganov } 1511fe304b893968887323b93764caafa66ee8ad44deSvetoslav Ganov 1512fe304b893968887323b93764caafa66ee8ad44deSvetoslav Ganov public boolean isPending() { 1513fe304b893968887323b93764caafa66ee8ad44deSvetoslav Ganov return mHandler.hasCallbacks(this); 1514fe304b893968887323b93764caafa66ee8ad44deSvetoslav Ganov } 1515fe304b893968887323b93764caafa66ee8ad44deSvetoslav Ganov 1516fe304b893968887323b93764caafa66ee8ad44deSvetoslav Ganov public void forceSendAndRemove() { 1517fe304b893968887323b93764caafa66ee8ad44deSvetoslav Ganov if (isPending()) { 1518fe304b893968887323b93764caafa66ee8ad44deSvetoslav Ganov run(); 151984044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav cancel(); 1520fe304b893968887323b93764caafa66ee8ad44deSvetoslav Ganov } 1521fe304b893968887323b93764caafa66ee8ad44deSvetoslav Ganov } 1522fe304b893968887323b93764caafa66ee8ad44deSvetoslav Ganov 1523fe304b893968887323b93764caafa66ee8ad44deSvetoslav Ganov @Override 1524fe304b893968887323b93764caafa66ee8ad44deSvetoslav Ganov public void run() { 1525f068fed6c4c3fc2003aec19b6e7e892358179b02Svetoslav Ganov sendAccessibilityEvent(mEventType); 1526fe304b893968887323b93764caafa66ee8ad44deSvetoslav Ganov } 1527fe304b893968887323b93764caafa66ee8ad44deSvetoslav Ganov } 1528fe304b893968887323b93764caafa66ee8ad44deSvetoslav Ganov 15294213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov @Override 15304213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov public String toString() { 15314213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov return LOG_TAG; 15324213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov } 15334213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov 15344213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov class InjectedPointerTracker { 15354213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov private static final String LOG_TAG_INJECTED_POINTER_TRACKER = "InjectedPointerTracker"; 15364213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov 15374213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov // Keep track of which pointers sent to the system are down. 15384213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov private int mInjectedPointersDown; 15394213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov 15404213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov // The time of the last injected down. 15414213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov private long mLastInjectedDownEventTime; 15424213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov 1543e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov // The last injected hover event. 1544e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov private MotionEvent mLastInjectedHoverEvent; 15454213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov 15465d043ce8cc2f588fdfb336cc843fb3b07b196f83Svetoslav Ganov // The last injected hover event used for performing clicks. 15475d043ce8cc2f588fdfb336cc843fb3b07b196f83Svetoslav Ganov private MotionEvent mLastInjectedHoverEventForClick; 15485d043ce8cc2f588fdfb336cc843fb3b07b196f83Svetoslav Ganov 15494213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov /** 15504213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov * Processes an injected {@link MotionEvent} event. 15514213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov * 15524213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov * @param event The event to process. 15534213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov */ 15544213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov public void onMotionEvent(MotionEvent event) { 15554213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov final int action = event.getActionMasked(); 15564213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov switch (action) { 15574213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov case MotionEvent.ACTION_DOWN: 15584213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov case MotionEvent.ACTION_POINTER_DOWN: { 15594213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov final int pointerId = event.getPointerId(event.getActionIndex()); 15604213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov final int pointerFlag = (1 << pointerId); 15614213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov mInjectedPointersDown |= pointerFlag; 15624213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov mLastInjectedDownEventTime = event.getDownTime(); 15634213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov } break; 15644213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov case MotionEvent.ACTION_UP: 15654213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov case MotionEvent.ACTION_POINTER_UP: { 15664213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov final int pointerId = event.getPointerId(event.getActionIndex()); 15674213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov final int pointerFlag = (1 << pointerId); 15684213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov mInjectedPointersDown &= ~pointerFlag; 15694213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov if (mInjectedPointersDown == 0) { 15704213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov mLastInjectedDownEventTime = 0; 15714213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov } 15724213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov } break; 15734213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov case MotionEvent.ACTION_HOVER_ENTER: 15744213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov case MotionEvent.ACTION_HOVER_MOVE: 15754213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov case MotionEvent.ACTION_HOVER_EXIT: { 1576e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov if (mLastInjectedHoverEvent != null) { 1577e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov mLastInjectedHoverEvent.recycle(); 1578e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov } 1579e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov mLastInjectedHoverEvent = MotionEvent.obtain(event); 15805d043ce8cc2f588fdfb336cc843fb3b07b196f83Svetoslav Ganov if (mLastInjectedHoverEventForClick != null) { 15815d043ce8cc2f588fdfb336cc843fb3b07b196f83Svetoslav Ganov mLastInjectedHoverEventForClick.recycle(); 15825d043ce8cc2f588fdfb336cc843fb3b07b196f83Svetoslav Ganov } 15835d043ce8cc2f588fdfb336cc843fb3b07b196f83Svetoslav Ganov mLastInjectedHoverEventForClick = MotionEvent.obtain(event); 15844213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov } break; 15854213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov } 15864213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov if (DEBUG) { 1587e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov Slog.i(LOG_TAG_INJECTED_POINTER_TRACKER, "Injected pointer:\n" + toString()); 15884213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov } 15894213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov } 15904213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov 15914213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov /** 15924213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov * Clears the internals state. 15934213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov */ 15944213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov public void clear() { 15954213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov mInjectedPointersDown = 0; 15964213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov } 15974213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov 15984213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov /** 15994213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov * @return The time of the last injected down event. 16004213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov */ 16014213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov public long getLastInjectedDownEventTime() { 16024213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov return mLastInjectedDownEventTime; 16034213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov } 16044213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov 16054213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov /** 16064213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov * @return The number of down pointers injected to the view hierarchy. 16074213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov */ 16084213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov public int getInjectedPointerDownCount() { 16094213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov return Integer.bitCount(mInjectedPointersDown); 16104213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov } 16114213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov 16124213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov /** 16134213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov * @return The bits of the injected pointers that are down. 16144213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov */ 16154213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov public int getInjectedPointersDown() { 16164213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov return mInjectedPointersDown; 16174213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov } 16184213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov 16194213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov /** 16204213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov * Whether an injected pointer is down. 16214213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov * 16224213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov * @param pointerId The unique pointer id. 16234213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov * @return True if the pointer is down. 16244213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov */ 16254213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov public boolean isInjectedPointerDown(int pointerId) { 16264213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov final int pointerFlag = (1 << pointerId); 16274213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov return (mInjectedPointersDown & pointerFlag) != 0; 16284213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov } 16294213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov 16304213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov /** 1631e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov * @return The the last injected hover event. 16324213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov */ 1633e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov public MotionEvent getLastInjectedHoverEvent() { 1634e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov return mLastInjectedHoverEvent; 16354213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov } 16364213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov 16375d043ce8cc2f588fdfb336cc843fb3b07b196f83Svetoslav Ganov /** 16385d043ce8cc2f588fdfb336cc843fb3b07b196f83Svetoslav Ganov * @return The the last injected hover event. 16395d043ce8cc2f588fdfb336cc843fb3b07b196f83Svetoslav Ganov */ 16405d043ce8cc2f588fdfb336cc843fb3b07b196f83Svetoslav Ganov public MotionEvent getLastInjectedHoverEventForClick() { 16415d043ce8cc2f588fdfb336cc843fb3b07b196f83Svetoslav Ganov return mLastInjectedHoverEventForClick; 16425d043ce8cc2f588fdfb336cc843fb3b07b196f83Svetoslav Ganov } 16435d043ce8cc2f588fdfb336cc843fb3b07b196f83Svetoslav Ganov 16444213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov @Override 16454213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov public String toString() { 16464213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov StringBuilder builder = new StringBuilder(); 16474213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov builder.append("========================="); 16484213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov builder.append("\nDown pointers #"); 16494213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov builder.append(Integer.bitCount(mInjectedPointersDown)); 16504213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov builder.append(" [ "); 16514213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov for (int i = 0; i < MAX_POINTER_COUNT; i++) { 16524213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov if ((mInjectedPointersDown & i) != 0) { 16534213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov builder.append(i); 16544213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov builder.append(" "); 16554213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov } 16564213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov } 16574213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov builder.append("]"); 16584213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov builder.append("\n========================="); 16594213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov return builder.toString(); 16604213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov } 16614213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov } 16624213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov 16634213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov class ReceivedPointerTracker { 16644213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov private static final String LOG_TAG_RECEIVED_POINTER_TRACKER = "ReceivedPointerTracker"; 1665736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov 1666736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov // Keep track of where and when a pointer went down. 1667736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov private final float[] mReceivedPointerDownX = new float[MAX_POINTER_COUNT]; 1668736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov private final float[] mReceivedPointerDownY = new float[MAX_POINTER_COUNT]; 1669736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov private final long[] mReceivedPointerDownTime = new long[MAX_POINTER_COUNT]; 1670736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov 1671736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov // Which pointers are down. 1672736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov private int mReceivedPointersDown; 1673736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov 1674c4842c11932ea4f60fe7ae09b0a59660207e1587Svetoslav Ganov // The edge flags of the last received down event. 1675c4842c11932ea4f60fe7ae09b0a59660207e1587Svetoslav Ganov private int mLastReceivedDownEdgeFlags; 1676c4842c11932ea4f60fe7ae09b0a59660207e1587Svetoslav Ganov 167784044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav // Primary pointer which is either the first that went down 167884044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav // or if it goes up the next one that most recently went down. 167984044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav private int mPrimaryPointerId; 1680736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov 1681736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov // Keep track of the last up pointer data. 1682736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov private long mLastReceivedUpPointerDownTime; 16834213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov private float mLastReceivedUpPointerDownX; 16844213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov private float mLastReceivedUpPointerDownY; 1685736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov 1686e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov private MotionEvent mLastReceivedEvent; 1687e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov 1688736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov /** 1689736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov * Clears the internals state. 1690736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov */ 1691736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov public void clear() { 1692736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov Arrays.fill(mReceivedPointerDownX, 0); 1693736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov Arrays.fill(mReceivedPointerDownY, 0); 1694736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov Arrays.fill(mReceivedPointerDownTime, 0); 1695736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov mReceivedPointersDown = 0; 169684044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav mPrimaryPointerId = 0; 1697736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov mLastReceivedUpPointerDownTime = 0; 16984213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov mLastReceivedUpPointerDownX = 0; 16994213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov mLastReceivedUpPointerDownY = 0; 1700736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov } 1701736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov 1702736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov /** 1703736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov * Processes a received {@link MotionEvent} event. 1704736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov * 1705736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov * @param event The event to process. 1706736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov */ 17074213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov public void onMotionEvent(MotionEvent event) { 1708e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov if (mLastReceivedEvent != null) { 1709e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov mLastReceivedEvent.recycle(); 1710e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov } 1711e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov mLastReceivedEvent = MotionEvent.obtain(event); 1712e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov 1713736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov final int action = event.getActionMasked(); 1714736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov switch (action) { 1715736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov case MotionEvent.ACTION_DOWN: { 171600f7b3f76515d1c6fbe5cf9fee9d3760787c03cdSvetoslav Ganov handleReceivedPointerDown(event.getActionIndex(), event); 1717736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov } break; 1718736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov case MotionEvent.ACTION_POINTER_DOWN: { 1719736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov handleReceivedPointerDown(event.getActionIndex(), event); 1720736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov } break; 1721736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov case MotionEvent.ACTION_UP: { 172200f7b3f76515d1c6fbe5cf9fee9d3760787c03cdSvetoslav Ganov handleReceivedPointerUp(event.getActionIndex(), event); 1723736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov } break; 1724736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov case MotionEvent.ACTION_POINTER_UP: { 1725736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov handleReceivedPointerUp(event.getActionIndex(), event); 1726736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov } break; 1727736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov } 1728736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov if (DEBUG) { 172938c992841b5f6bc80359dbf60d31aa7b994160fcSvetoslav Ganov Slog.i(LOG_TAG_RECEIVED_POINTER_TRACKER, "Received pointer:\n" + toString()); 1730736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov } 1731736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov } 1732736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov 1733736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov /** 1734e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov * @return The last received event. 1735e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov */ 1736e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov public MotionEvent getLastReceivedEvent() { 1737e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov return mLastReceivedEvent; 1738e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov } 1739e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov 1740e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov /** 17414213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov * @return The number of received pointers that are down. 1742736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov */ 17434213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov public int getReceivedPointerDownCount() { 17444213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov return Integer.bitCount(mReceivedPointersDown); 1745736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov } 1746736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov 1747736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov /** 1748736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov * Whether an received pointer is down. 1749736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov * 1750736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov * @param pointerId The unique pointer id. 1751736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov * @return True if the pointer is down. 1752736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov */ 1753736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov public boolean isReceivedPointerDown(int pointerId) { 1754736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov final int pointerFlag = (1 << pointerId); 1755736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov return (mReceivedPointersDown & pointerFlag) != 0; 1756736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov } 1757736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov 1758736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov /** 1759736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov * @param pointerId The unique pointer id. 1760736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov * @return The X coordinate where the pointer went down. 1761736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov */ 1762736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov public float getReceivedPointerDownX(int pointerId) { 1763736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov return mReceivedPointerDownX[pointerId]; 1764736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov } 1765736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov 1766736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov /** 1767736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov * @param pointerId The unique pointer id. 1768736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov * @return The Y coordinate where the pointer went down. 1769736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov */ 1770736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov public float getReceivedPointerDownY(int pointerId) { 1771736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov return mReceivedPointerDownY[pointerId]; 1772736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov } 1773736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov 1774736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov /** 1775736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov * @param pointerId The unique pointer id. 1776736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov * @return The time when the pointer went down. 1777736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov */ 1778736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov public long getReceivedPointerDownTime(int pointerId) { 1779736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov return mReceivedPointerDownTime[pointerId]; 1780736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov } 1781736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov 1782736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov /** 1783736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov * @return The id of the primary pointer. 1784736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov */ 178584044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav public int getPrimaryPointerId() { 178684044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav if (mPrimaryPointerId == INVALID_POINTER_ID) { 178738c992841b5f6bc80359dbf60d31aa7b994160fcSvetoslav Ganov mPrimaryPointerId = findPrimaryPointerId(); 1788736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov } 178984044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav return mPrimaryPointerId; 1790736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov } 1791736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov 1792736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov /** 1793736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov * @return The time when the last up received pointer went down. 1794736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov */ 1795736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov public long getLastReceivedUpPointerDownTime() { 1796736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov return mLastReceivedUpPointerDownTime; 1797736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov } 1798736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov 1799736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov /** 18004213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov * @return The down X of the last received pointer that went up. 1801736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov */ 18024213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov public float getLastReceivedUpPointerDownX() { 18034213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov return mLastReceivedUpPointerDownX; 1804736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov } 1805736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov 1806736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov /** 18074213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov * @return The down Y of the last received pointer that went up. 1808736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov */ 18094213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov public float getLastReceivedUpPointerDownY() { 18104213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov return mLastReceivedUpPointerDownY; 1811736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov } 1812736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov 1813736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov /** 1814c4842c11932ea4f60fe7ae09b0a59660207e1587Svetoslav Ganov * @return The edge flags of the last received down event. 1815c4842c11932ea4f60fe7ae09b0a59660207e1587Svetoslav Ganov */ 1816c4842c11932ea4f60fe7ae09b0a59660207e1587Svetoslav Ganov public int getLastReceivedDownEdgeFlags() { 1817c4842c11932ea4f60fe7ae09b0a59660207e1587Svetoslav Ganov return mLastReceivedDownEdgeFlags; 1818c4842c11932ea4f60fe7ae09b0a59660207e1587Svetoslav Ganov } 1819c4842c11932ea4f60fe7ae09b0a59660207e1587Svetoslav Ganov 1820c4842c11932ea4f60fe7ae09b0a59660207e1587Svetoslav Ganov /** 1821736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov * Handles a received pointer down event. 1822736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov * 1823736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov * @param pointerIndex The index of the pointer that has changed. 1824736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov * @param event The event to be handled. 1825736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov */ 1826736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov private void handleReceivedPointerDown(int pointerIndex, MotionEvent event) { 1827736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov final int pointerId = event.getPointerId(pointerIndex); 1828736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov final int pointerFlag = (1 << pointerId); 1829736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov 1830736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov mLastReceivedUpPointerDownTime = 0; 18314213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov mLastReceivedUpPointerDownX = 0; 18324213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov mLastReceivedUpPointerDownX = 0; 1833736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov 1834c4842c11932ea4f60fe7ae09b0a59660207e1587Svetoslav Ganov mLastReceivedDownEdgeFlags = event.getEdgeFlags(); 1835c4842c11932ea4f60fe7ae09b0a59660207e1587Svetoslav Ganov 1836736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov mReceivedPointersDown |= pointerFlag; 1837736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov mReceivedPointerDownX[pointerId] = event.getX(pointerIndex); 1838736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov mReceivedPointerDownY[pointerId] = event.getY(pointerIndex); 1839736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov mReceivedPointerDownTime[pointerId] = event.getEventTime(); 1840736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov 184184044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav mPrimaryPointerId = pointerId; 1842736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov } 1843736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov 1844736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov /** 1845736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov * Handles a received pointer up event. 1846736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov * 1847736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov * @param pointerIndex The index of the pointer that has changed. 1848736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov * @param event The event to be handled. 1849736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov */ 1850736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov private void handleReceivedPointerUp(int pointerIndex, MotionEvent event) { 1851736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov final int pointerId = event.getPointerId(pointerIndex); 1852736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov final int pointerFlag = (1 << pointerId); 1853736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov 1854736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov mLastReceivedUpPointerDownTime = getReceivedPointerDownTime(pointerId); 18554213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov mLastReceivedUpPointerDownX = mReceivedPointerDownX[pointerId]; 18564213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov mLastReceivedUpPointerDownY = mReceivedPointerDownY[pointerId]; 1857736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov 1858736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov mReceivedPointersDown &= ~pointerFlag; 1859736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov mReceivedPointerDownX[pointerId] = 0; 1860736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov mReceivedPointerDownY[pointerId] = 0; 1861736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov mReceivedPointerDownTime[pointerId] = 0; 1862736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov 186384044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav if (mPrimaryPointerId == pointerId) { 186484044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav mPrimaryPointerId = INVALID_POINTER_ID; 1865736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov } 1866736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov } 1867736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov 1868736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov /** 186938c992841b5f6bc80359dbf60d31aa7b994160fcSvetoslav Ganov * @return The primary pointer id. 1870736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov */ 187138c992841b5f6bc80359dbf60d31aa7b994160fcSvetoslav Ganov private int findPrimaryPointerId() { 187284044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav int primaryPointerId = INVALID_POINTER_ID; 1873736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov long minDownTime = Long.MAX_VALUE; 187438c992841b5f6bc80359dbf60d31aa7b994160fcSvetoslav Ganov 187584044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav // Find the pointer that went down first. 187638c992841b5f6bc80359dbf60d31aa7b994160fcSvetoslav Ganov int pointerIdBits = mReceivedPointersDown; 187738c992841b5f6bc80359dbf60d31aa7b994160fcSvetoslav Ganov while (pointerIdBits > 0) { 187838c992841b5f6bc80359dbf60d31aa7b994160fcSvetoslav Ganov final int pointerId = Integer.numberOfTrailingZeros(pointerIdBits); 187938c992841b5f6bc80359dbf60d31aa7b994160fcSvetoslav Ganov pointerIdBits &= ~(1 << pointerId); 188038c992841b5f6bc80359dbf60d31aa7b994160fcSvetoslav Ganov final long downPointerTime = mReceivedPointerDownTime[pointerId]; 188184044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav if (downPointerTime < minDownTime) { 188284044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav minDownTime = downPointerTime; 188338c992841b5f6bc80359dbf60d31aa7b994160fcSvetoslav Ganov primaryPointerId = pointerId; 1884736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov } 1885736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov } 188684044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav return primaryPointerId; 1887736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov } 1888736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov 1889736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov @Override 1890736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov public String toString() { 1891736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov StringBuilder builder = new StringBuilder(); 1892736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov builder.append("========================="); 1893736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov builder.append("\nDown pointers #"); 1894736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov builder.append(getReceivedPointerDownCount()); 1895736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov builder.append(" [ "); 1896736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov for (int i = 0; i < MAX_POINTER_COUNT; i++) { 1897736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov if (isReceivedPointerDown(i)) { 1898736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov builder.append(i); 1899736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov builder.append(" "); 1900736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov } 1901736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov } 1902736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov builder.append("]"); 190384044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav builder.append("\nPrimary pointer id [ "); 190484044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav builder.append(getPrimaryPointerId()); 1905736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov builder.append(" ]"); 1906736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov builder.append("\n========================="); 1907736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov return builder.toString(); 1908736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov } 1909736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov } 1910736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov} 1911