TouchExplorer.java revision 7498efdc5e163d6b4a11db941c7d13c169d37284
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 80a3315270e0502189a6fa4de20b9db2bf042cfd2aSvetoslav // The maximum of the cosine between the vectors of two moving 81736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov // pointers so they can be considered moving in the same direction. 8212a024ca681d877fe16b7e087356f7aff175a218Svetoslav Ganov private static final float MAX_DRAGGING_ANGLE_COS = 0.525321989f; // cos(pi/4) 83736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov 84f804420d6e37748b75478406e989c69303756980Svetoslav Ganov // Constant referring to the ids bits of all pointers. 85f804420d6e37748b75478406e989c69303756980Svetoslav Ganov private static final int ALL_POINTER_ID_BITS = 0xFFFFFFFF; 86736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov 874213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov // This constant captures the current implementation detail that 884213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov // pointer IDs are between 0 and 31 inclusive (subject to change). 894213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov // (See MAX_POINTER_ID in frameworks/base/include/ui/Input.h) 90e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov private static final int MAX_POINTER_COUNT = 32; 914213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov 924213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov // Invalid pointer ID. 93e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov private static final int INVALID_POINTER_ID = -1; 94e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov 95e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov // The velocity above which we detect gestures. 96e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov private static final int GESTURE_DETECTION_VELOCITY_DIP = 1000; 97e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov 98e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov // The minimal distance before we take the middle of the distance between 99e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov // the two dragging pointers as opposed to use the location of the primary one. 100e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov private static final int MIN_POINTER_DISTANCE_TO_USE_MIDDLE_LOCATION_DIP = 200; 1014213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov 10295068e5d1bea47091e97955f271c789264994550Svetoslav Ganov // The timeout after which we are no longer trying to detect a gesture. 10395068e5d1bea47091e97955f271c789264994550Svetoslav Ganov private static final int EXIT_GESTURE_DETECTION_TIMEOUT = 2000; 10495068e5d1bea47091e97955f271c789264994550Svetoslav Ganov 105e47957a0bbe2164467ff6e7a566b0c9e4689cdc9Svetoslav Ganov // Timeout before trying to decide what the user is trying to do. 106e47957a0bbe2164467ff6e7a566b0c9e4689cdc9Svetoslav Ganov private final int mDetermineUserIntentTimeout; 107e47957a0bbe2164467ff6e7a566b0c9e4689cdc9Svetoslav Ganov 108e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov // Timeout within which we try to detect a tap. 109e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov private final int mTapTimeout; 110e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov 111e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov // Timeout within which we try to detect a double tap. 112e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov private final int mDoubleTapTimeout; 113e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov 114e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov // Slop between the down and up tap to be a tap. 115e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov private final int mTouchSlop; 116e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov 117e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov // Slop between the first and second tap to be a double tap. 118e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov private final int mDoubleTapSlop; 119736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov 120736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov // The current state of the touch explorer. 121736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov private int mCurrentState = STATE_TOUCH_EXPLORING; 122736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov 123736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov // The ID of the pointer used for dragging. 124736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov private int mDraggingPointerId; 125736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov 126736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov // Handler for performing asynchronous operations. 127736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov private final Handler mHandler; 128736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov 12984044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav // Command for delayed sending of a hover enter and move event. 13084044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav private final SendHoverEnterAndMoveDelayed mSendHoverEnterAndMoveDelayed; 131e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov 132e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov // Command for delayed sending of a hover exit event. 13384044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav private final SendHoverExitDelayed mSendHoverExitDelayed; 134736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov 135f068fed6c4c3fc2003aec19b6e7e892358179b02Svetoslav Ganov // Command for delayed sending of touch exploration end events. 136f068fed6c4c3fc2003aec19b6e7e892358179b02Svetoslav Ganov private final SendAccessibilityEventDelayed mSendTouchExplorationEndDelayed; 137f068fed6c4c3fc2003aec19b6e7e892358179b02Svetoslav Ganov 138f068fed6c4c3fc2003aec19b6e7e892358179b02Svetoslav Ganov // Command for delayed sending of touch interaction end events. 139f068fed6c4c3fc2003aec19b6e7e892358179b02Svetoslav Ganov private final SendAccessibilityEventDelayed mSendTouchInteractionEndDelayed; 140fe304b893968887323b93764caafa66ee8ad44deSvetoslav Ganov 141f5a07905a3e025f95472a3f8d9935263e49ad6d3Svetoslav Ganov // Command for delayed sending of a long press. 142f5a07905a3e025f95472a3f8d9935263e49ad6d3Svetoslav Ganov private final PerformLongPressDelayed mPerformLongPressDelayed; 143f5a07905a3e025f95472a3f8d9935263e49ad6d3Svetoslav Ganov 14495068e5d1bea47091e97955f271c789264994550Svetoslav Ganov // Command for exiting gesture detection mode after a timeout. 14595068e5d1bea47091e97955f271c789264994550Svetoslav Ganov private final ExitGestureDetectionModeDelayed mExitGestureDetectionModeDelayed; 14695068e5d1bea47091e97955f271c789264994550Svetoslav Ganov 147e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov // Helper to detect and react to double tap in touch explore mode. 148e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov private final DoubleTapDetector mDoubleTapDetector; 149e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov 150e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov // The scaled minimal distance before we take the middle of the distance between 151e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov // the two dragging pointers as opposed to use the location of the primary one. 152e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov private final int mScaledMinPointerDistanceToUseMiddleLocation; 153e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov 154e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov // The scaled velocity above which we detect gestures. 155e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov private final int mScaledGestureDetectionVelocity; 156e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov 1571cf70bbf96930662cab0e699d70b62865766ff52Svetoslav Ganov // The handler to which to delegate events. 1581cf70bbf96930662cab0e699d70b62865766ff52Svetoslav Ganov private EventStreamTransformation mNext; 1591cf70bbf96930662cab0e699d70b62865766ff52Svetoslav Ganov 160e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov // Helper to track gesture velocity. 16145af84a483165f06c04d74baba67f90da29c6ad2Svetoslav Ganov private final VelocityTracker mVelocityTracker = VelocityTracker.obtain(); 1624213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov 163e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov // Helper class to track received pointers. 1644213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov private final ReceivedPointerTracker mReceivedPointerTracker; 1654213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov 166e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov // Helper class to track injected pointers. 1674213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov private final InjectedPointerTracker mInjectedPointerTracker; 1684213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov 169e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov // Handle to the accessibility manager service. 170e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov private final AccessibilityManagerService mAms; 1714213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov 172e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov // Temporary rectangle to avoid instantiation. 173e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov private final Rect mTempRect = new Rect(); 1744213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov 1757498efdc5e163d6b4a11db941c7d13c169d37284Svet Ganov // Temporary point to avoid instantiation. 1767498efdc5e163d6b4a11db941c7d13c169d37284Svet Ganov private final Point mTempPoint = new Point(); 1777498efdc5e163d6b4a11db941c7d13c169d37284Svet Ganov 17877276b60851a158ad3e142cb3b091d57ae5ceffbSvetoslav Ganov // Context in which this explorer operates. 17977276b60851a158ad3e142cb3b091d57ae5ceffbSvetoslav Ganov private final Context mContext; 18077276b60851a158ad3e142cb3b091d57ae5ceffbSvetoslav Ganov 181e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov // The X of the previous event. 182e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov private float mPreviousX; 183e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov 184e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov // The Y of the previous event. 185e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov private float mPreviousY; 186e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov 187e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov // Buffer for storing points for gesture detection. 188e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov private final ArrayList<GesturePoint> mStrokeBuffer = new ArrayList<GesturePoint>(100); 189e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov 190e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov // The minimal delta between moves to add a gesture point. 191e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov private static final int TOUCH_TOLERANCE = 3; 192e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov 193e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov // The minimal score for accepting a predicted gesture. 194e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov private static final float MIN_PREDICTION_SCORE = 2.0f; 195e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov 196e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov // The library for gesture detection. 197e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov private GestureLibrary mGestureLibrary; 198e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov 199e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov // The long pressing pointer id if coordinate remapping is needed. 200aeb8d0ed0d98d398a66a092c418f4f2bca8719e0Svetoslav Ganov private int mLongPressingPointerId = -1; 201e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov 202e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov // The long pressing pointer X if coordinate remapping is needed. 203e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov private int mLongPressingPointerDeltaX; 204e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov 205e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov // The long pressing pointer Y if coordinate remapping is needed. 206e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov private int mLongPressingPointerDeltaY; 2074213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov 208385d9f24b5ce2acb86c0dc192ce702718ab01c39Svetoslav Ganov // The id of the last touch explored window. 209385d9f24b5ce2acb86c0dc192ce702718ab01c39Svetoslav Ganov private int mLastTouchedWindowId; 210385d9f24b5ce2acb86c0dc192ce702718ab01c39Svetoslav Ganov 211f068fed6c4c3fc2003aec19b6e7e892358179b02Svetoslav Ganov // Whether touch exploration is in progress. 212f068fed6c4c3fc2003aec19b6e7e892358179b02Svetoslav Ganov private boolean mTouchExplorationInProgress; 21377276b60851a158ad3e142cb3b091d57ae5ceffbSvetoslav Ganov 214736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov /** 215736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov * Creates a new instance. 216736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov * 217736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov * @param inputFilter The input filter associated with this explorer. 218736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov * @param context A context handle for accessing resources. 219736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov */ 2201cf70bbf96930662cab0e699d70b62865766ff52Svetoslav Ganov public TouchExplorer(Context context, AccessibilityManagerService service) { 22177276b60851a158ad3e142cb3b091d57ae5ceffbSvetoslav Ganov mContext = context; 222e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov mAms = service; 22384044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav mReceivedPointerTracker = new ReceivedPointerTracker(); 2244213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov mInjectedPointerTracker = new InjectedPointerTracker(); 225e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov mTapTimeout = ViewConfiguration.getTapTimeout(); 22677276b60851a158ad3e142cb3b091d57ae5ceffbSvetoslav Ganov mDetermineUserIntentTimeout = ViewConfiguration.getDoubleTapTimeout(); 227e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov mDoubleTapTimeout = ViewConfiguration.getDoubleTapTimeout(); 228e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov mTouchSlop = ViewConfiguration.get(context).getScaledTouchSlop(); 229e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov mDoubleTapSlop = ViewConfiguration.get(context).getScaledDoubleTapSlop(); 230736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov mHandler = new Handler(context.getMainLooper()); 231f5a07905a3e025f95472a3f8d9935263e49ad6d3Svetoslav Ganov mPerformLongPressDelayed = new PerformLongPressDelayed(); 23295068e5d1bea47091e97955f271c789264994550Svetoslav Ganov mExitGestureDetectionModeDelayed = new ExitGestureDetectionModeDelayed(); 2334213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov mGestureLibrary = GestureLibraries.fromRawResource(context, R.raw.accessibility_gestures); 234ea6fbc0981564f7bbf4c6fbb63af0175415121ceCasey Burkhardt mGestureLibrary.setOrientationStyle(8); 235ea6fbc0981564f7bbf4c6fbb63af0175415121ceCasey Burkhardt mGestureLibrary.setSequenceType(GestureStore.SEQUENCE_SENSITIVE); 2364213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov mGestureLibrary.load(); 23784044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav mSendHoverEnterAndMoveDelayed = new SendHoverEnterAndMoveDelayed(); 23884044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav mSendHoverExitDelayed = new SendHoverExitDelayed(); 239f068fed6c4c3fc2003aec19b6e7e892358179b02Svetoslav Ganov mSendTouchExplorationEndDelayed = new SendAccessibilityEventDelayed( 240f068fed6c4c3fc2003aec19b6e7e892358179b02Svetoslav Ganov AccessibilityEvent.TYPE_TOUCH_EXPLORATION_GESTURE_END, 241f068fed6c4c3fc2003aec19b6e7e892358179b02Svetoslav Ganov mDetermineUserIntentTimeout); 242f068fed6c4c3fc2003aec19b6e7e892358179b02Svetoslav Ganov mSendTouchInteractionEndDelayed = new SendAccessibilityEventDelayed( 243f068fed6c4c3fc2003aec19b6e7e892358179b02Svetoslav Ganov AccessibilityEvent.TYPE_TOUCH_INTERACTION_END, 244f068fed6c4c3fc2003aec19b6e7e892358179b02Svetoslav Ganov mDetermineUserIntentTimeout); 245e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov mDoubleTapDetector = new DoubleTapDetector(); 246e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov final float density = context.getResources().getDisplayMetrics().density; 247e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov mScaledMinPointerDistanceToUseMiddleLocation = 248e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov (int) (MIN_POINTER_DISTANCE_TO_USE_MIDDLE_LOCATION_DIP * density); 249e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov mScaledGestureDetectionVelocity = (int) (GESTURE_DETECTION_VELOCITY_DIP * density); 250e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov } 251e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov 252e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov public void clear() { 253e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov // If we have not received an event then we are in initial 254e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov // state. Therefore, there is not need to clean anything. 255e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov MotionEvent event = mReceivedPointerTracker.getLastReceivedEvent(); 256e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov if (event != null) { 257e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov clear(mReceivedPointerTracker.getLastReceivedEvent(), WindowManagerPolicy.FLAG_TRUSTED); 258e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov } 259736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov } 260736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov 2611cf70bbf96930662cab0e699d70b62865766ff52Svetoslav Ganov public void onDestroy() { 2621cf70bbf96930662cab0e699d70b62865766ff52Svetoslav Ganov // TODO: Implement 2631cf70bbf96930662cab0e699d70b62865766ff52Svetoslav Ganov } 2641cf70bbf96930662cab0e699d70b62865766ff52Svetoslav Ganov 2651cf70bbf96930662cab0e699d70b62865766ff52Svetoslav Ganov private void clear(MotionEvent event, int policyFlags) { 266e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov switch (mCurrentState) { 267e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov case STATE_TOUCH_EXPLORING: { 268e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov // If a touch exploration gesture is in progress send events for its end. 269f068fed6c4c3fc2003aec19b6e7e892358179b02Svetoslav Ganov sendHoverExitAndTouchExplorationGestureEndIfNeeded(policyFlags); 270e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov } break; 271e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov case STATE_DRAGGING: { 272e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov mDraggingPointerId = INVALID_POINTER_ID; 273e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov // Send exit to all pointers that we have delivered. 274e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov sendUpForInjectedDownPointers(event, policyFlags); 275e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov } break; 276e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov case STATE_DELEGATING: { 277e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov // Send exit to all pointers that we have delivered. 278e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov sendUpForInjectedDownPointers(event, policyFlags); 279e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov } break; 280e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov case STATE_GESTURE_DETECTING: { 281e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov // Clear the current stroke. 282e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov mStrokeBuffer.clear(); 283e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov } break; 284e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov } 285e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov // Remove all pending callbacks. 28684044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav mSendHoverEnterAndMoveDelayed.cancel(); 28784044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav mSendHoverExitDelayed.cancel(); 28884044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav mPerformLongPressDelayed.cancel(); 28984044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav mExitGestureDetectionModeDelayed.cancel(); 29084044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav mSendTouchExplorationEndDelayed.cancel(); 29184044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav mSendTouchInteractionEndDelayed.cancel(); 292e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov // Reset the pointer trackers. 293e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov mReceivedPointerTracker.clear(); 294e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov mInjectedPointerTracker.clear(); 295e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov // Clear the double tap detector 296e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov mDoubleTapDetector.clear(); 297e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov // Go to initial state. 298e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov // Clear the long pressing pointer remap data. 299e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov mLongPressingPointerId = -1; 300e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov mLongPressingPointerDeltaX = 0; 301e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov mLongPressingPointerDeltaY = 0; 302e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov mCurrentState = STATE_TOUCH_EXPLORING; 3031cf70bbf96930662cab0e699d70b62865766ff52Svetoslav Ganov if (mNext != null) { 3041cf70bbf96930662cab0e699d70b62865766ff52Svetoslav Ganov mNext.clear(); 3051cf70bbf96930662cab0e699d70b62865766ff52Svetoslav Ganov } 306f068fed6c4c3fc2003aec19b6e7e892358179b02Svetoslav Ganov mTouchExplorationInProgress = false; 307f772cba59760d1ad9eb5cb9205b2e2e9126e488dSvetoslav Ganov mAms.onTouchInteractionEnd(); 3081cf70bbf96930662cab0e699d70b62865766ff52Svetoslav Ganov } 3091cf70bbf96930662cab0e699d70b62865766ff52Svetoslav Ganov 3101cf70bbf96930662cab0e699d70b62865766ff52Svetoslav Ganov @Override 3111cf70bbf96930662cab0e699d70b62865766ff52Svetoslav Ganov public void setNext(EventStreamTransformation next) { 3121cf70bbf96930662cab0e699d70b62865766ff52Svetoslav Ganov mNext = next; 313736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov } 314736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov 3151cf70bbf96930662cab0e699d70b62865766ff52Svetoslav Ganov @Override 31645af84a483165f06c04d74baba67f90da29c6ad2Svetoslav Ganov public void onMotionEvent(MotionEvent event, MotionEvent rawEvent, int policyFlags) { 317736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov if (DEBUG) { 3184213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov Slog.d(LOG_TAG, "Received event: " + event + ", policyFlags=0x" 319736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov + Integer.toHexString(policyFlags)); 3204213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov Slog.d(LOG_TAG, getStateSymbolicName(mCurrentState)); 321736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov } 322736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov 32345af84a483165f06c04d74baba67f90da29c6ad2Svetoslav Ganov mReceivedPointerTracker.onMotionEvent(rawEvent); 324736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov 325736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov switch(mCurrentState) { 326736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov case STATE_TOUCH_EXPLORING: { 32745af84a483165f06c04d74baba67f90da29c6ad2Svetoslav Ganov handleMotionEventStateTouchExploring(event, rawEvent, policyFlags); 328736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov } break; 329736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov case STATE_DRAGGING: { 330736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov handleMotionEventStateDragging(event, policyFlags); 331736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov } break; 332736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov case STATE_DELEGATING: { 333736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov handleMotionEventStateDelegating(event, policyFlags); 334736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov } break; 3354213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov case STATE_GESTURE_DETECTING: { 33645af84a483165f06c04d74baba67f90da29c6ad2Svetoslav Ganov handleMotionEventGestureDetecting(rawEvent, policyFlags); 3374213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov } break; 3384213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov default: 339736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov throw new IllegalStateException("Illegal state: " + mCurrentState); 340736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov } 341736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov } 342736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov 34386783474fdec98a22bc22e224462767eab13e273Svetoslav Ganov public void onAccessibilityEvent(AccessibilityEvent event) { 34477276b60851a158ad3e142cb3b091d57ae5ceffbSvetoslav Ganov final int eventType = event.getEventType(); 34577276b60851a158ad3e142cb3b091d57ae5ceffbSvetoslav Ganov 34677276b60851a158ad3e142cb3b091d57ae5ceffbSvetoslav Ganov // The event for gesture end should be strictly after the 34777276b60851a158ad3e142cb3b091d57ae5ceffbSvetoslav Ganov // last hover exit event. 348f068fed6c4c3fc2003aec19b6e7e892358179b02Svetoslav Ganov if (mSendTouchExplorationEndDelayed.isPending() 3498b681cb8813454aac8a626bf3d7adaa8beca4d75Svetoslav Ganov && eventType == AccessibilityEvent.TYPE_VIEW_HOVER_EXIT) { 35084044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav mSendTouchExplorationEndDelayed.cancel(); 3518b681cb8813454aac8a626bf3d7adaa8beca4d75Svetoslav Ganov sendAccessibilityEvent(AccessibilityEvent.TYPE_TOUCH_EXPLORATION_GESTURE_END); 35277276b60851a158ad3e142cb3b091d57ae5ceffbSvetoslav Ganov } 35377276b60851a158ad3e142cb3b091d57ae5ceffbSvetoslav Ganov 35477276b60851a158ad3e142cb3b091d57ae5ceffbSvetoslav Ganov // The event for touch interaction end should be strictly after the 35577276b60851a158ad3e142cb3b091d57ae5ceffbSvetoslav Ganov // last hover exit and the touch exploration gesture end events. 356f068fed6c4c3fc2003aec19b6e7e892358179b02Svetoslav Ganov if (mSendTouchInteractionEndDelayed.isPending() 3578b681cb8813454aac8a626bf3d7adaa8beca4d75Svetoslav Ganov && eventType == AccessibilityEvent.TYPE_VIEW_HOVER_EXIT) { 35884044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav mSendTouchInteractionEndDelayed.cancel(); 3598b681cb8813454aac8a626bf3d7adaa8beca4d75Svetoslav Ganov sendAccessibilityEvent(AccessibilityEvent.TYPE_TOUCH_INTERACTION_END); 36077276b60851a158ad3e142cb3b091d57ae5ceffbSvetoslav Ganov } 36177276b60851a158ad3e142cb3b091d57ae5ceffbSvetoslav Ganov 36286783474fdec98a22bc22e224462767eab13e273Svetoslav Ganov // If a new window opens or the accessibility focus moves we no longer 36386783474fdec98a22bc22e224462767eab13e273Svetoslav Ganov // want to click/long press on the last touch explored location. 36486783474fdec98a22bc22e224462767eab13e273Svetoslav Ganov switch (eventType) { 36586783474fdec98a22bc22e224462767eab13e273Svetoslav Ganov case AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED: 36686783474fdec98a22bc22e224462767eab13e273Svetoslav Ganov case AccessibilityEvent.TYPE_VIEW_ACCESSIBILITY_FOCUSED: { 3675d043ce8cc2f588fdfb336cc843fb3b07b196f83Svetoslav Ganov if (mInjectedPointerTracker.mLastInjectedHoverEventForClick != null) { 3685d043ce8cc2f588fdfb336cc843fb3b07b196f83Svetoslav Ganov mInjectedPointerTracker.mLastInjectedHoverEventForClick.recycle(); 3695d043ce8cc2f588fdfb336cc843fb3b07b196f83Svetoslav Ganov mInjectedPointerTracker.mLastInjectedHoverEventForClick = null; 37086783474fdec98a22bc22e224462767eab13e273Svetoslav Ganov } 371385d9f24b5ce2acb86c0dc192ce702718ab01c39Svetoslav Ganov mLastTouchedWindowId = -1; 372385d9f24b5ce2acb86c0dc192ce702718ab01c39Svetoslav Ganov } break; 373385d9f24b5ce2acb86c0dc192ce702718ab01c39Svetoslav Ganov case AccessibilityEvent.TYPE_VIEW_HOVER_ENTER: 374385d9f24b5ce2acb86c0dc192ce702718ab01c39Svetoslav Ganov case AccessibilityEvent.TYPE_VIEW_HOVER_EXIT: { 375385d9f24b5ce2acb86c0dc192ce702718ab01c39Svetoslav Ganov mLastTouchedWindowId = event.getWindowId(); 37686783474fdec98a22bc22e224462767eab13e273Svetoslav Ganov } break; 37786783474fdec98a22bc22e224462767eab13e273Svetoslav Ganov } 3781cf70bbf96930662cab0e699d70b62865766ff52Svetoslav Ganov if (mNext != null) { 3791cf70bbf96930662cab0e699d70b62865766ff52Svetoslav Ganov mNext.onAccessibilityEvent(event); 3801cf70bbf96930662cab0e699d70b62865766ff52Svetoslav Ganov } 38186783474fdec98a22bc22e224462767eab13e273Svetoslav Ganov } 38286783474fdec98a22bc22e224462767eab13e273Svetoslav Ganov 383736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov /** 384736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov * Handles a motion event in touch exploring state. 385736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov * 386736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov * @param event The event to be handled. 38745af84a483165f06c04d74baba67f90da29c6ad2Svetoslav Ganov * @param rawEvent The raw (unmodified) motion event. 388736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov * @param policyFlags The policy flags associated with the event. 389736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov */ 39045af84a483165f06c04d74baba67f90da29c6ad2Svetoslav Ganov private void handleMotionEventStateTouchExploring(MotionEvent event, MotionEvent rawEvent, 39145af84a483165f06c04d74baba67f90da29c6ad2Svetoslav Ganov int policyFlags) { 3924213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov ReceivedPointerTracker receivedTracker = mReceivedPointerTracker; 3934213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov 39445af84a483165f06c04d74baba67f90da29c6ad2Svetoslav Ganov mVelocityTracker.addMovement(rawEvent); 395736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov 396e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov mDoubleTapDetector.onMotionEvent(event, policyFlags); 397e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov 398736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov switch (event.getActionMasked()) { 39984044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav case MotionEvent.ACTION_DOWN: { 4006ae8a24fc045bc7970f2843fa9baf06aff15e22dSvetoslav Ganov mAms.onTouchInteractionStart(); 40184044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav 402e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov // Pre-feed the motion events to the gesture detector since we 403e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov // have a distance slop before getting into gesture detection 404e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov // mode and not using the points within this slop significantly 405e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov // decreases the quality of gesture recognition. 40645af84a483165f06c04d74baba67f90da29c6ad2Svetoslav Ganov handleMotionEventGestureDetecting(rawEvent, policyFlags); 40784044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav sendAccessibilityEvent(AccessibilityEvent.TYPE_TOUCH_INTERACTION_START); 408736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov 40984044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav // If we still have not notified the user for the last 41084044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav // touch, we figure out what to do. If were waiting 41184044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav // we resent the delayed callback and wait again. 41284044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav mSendHoverEnterAndMoveDelayed.cancel(); 41384044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav mSendHoverExitDelayed.cancel(); 41484044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav mPerformLongPressDelayed.cancel(); 415f068fed6c4c3fc2003aec19b6e7e892358179b02Svetoslav Ganov 41684044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav if (mSendTouchExplorationEndDelayed.isPending()) { 41784044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav mSendTouchExplorationEndDelayed.forceSendAndRemove(); 41884044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav } 419f068fed6c4c3fc2003aec19b6e7e892358179b02Svetoslav Ganov 42084044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav if (mSendTouchInteractionEndDelayed.isPending()) { 42184044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav mSendTouchInteractionEndDelayed.forceSendAndRemove(); 42284044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav } 423fe304b893968887323b93764caafa66ee8ad44deSvetoslav Ganov 42484044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav // If we have the first tap, schedule a long press and break 42584044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav // since we do not want to schedule hover enter because 42684044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav // the delayed callback will kick in before the long click. 42784044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav // This would lead to a state transition resulting in long 42884044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav // pressing the item below the double taped area which is 42984044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav // not necessary where accessibility focus is. 43084044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav if (mDoubleTapDetector.firstTapDetected()) { 43184044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav // We got a tap now post a long press action. 43284044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav mPerformLongPressDelayed.post(event, policyFlags); 43384044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav break; 434736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov } 43584044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav if (!mTouchExplorationInProgress) { 43638c992841b5f6bc80359dbf60d31aa7b994160fcSvetoslav Ganov if (!mSendHoverEnterAndMoveDelayed.isPending()) { 43738c992841b5f6bc80359dbf60d31aa7b994160fcSvetoslav Ganov // Deliver hover enter with a delay to have a chance 43838c992841b5f6bc80359dbf60d31aa7b994160fcSvetoslav Ganov // to detect what the user is trying to do. 43938c992841b5f6bc80359dbf60d31aa7b994160fcSvetoslav Ganov final int pointerId = receivedTracker.getPrimaryPointerId(); 44038c992841b5f6bc80359dbf60d31aa7b994160fcSvetoslav Ganov final int pointerIdBits = (1 << pointerId); 44138c992841b5f6bc80359dbf60d31aa7b994160fcSvetoslav Ganov mSendHoverEnterAndMoveDelayed.post(event, true, pointerIdBits, 44238c992841b5f6bc80359dbf60d31aa7b994160fcSvetoslav Ganov policyFlags); 443a3315270e0502189a6fa4de20b9db2bf042cfd2aSvetoslav } else { 444a3315270e0502189a6fa4de20b9db2bf042cfd2aSvetoslav // Cache the event until we discern exploration from gesturing. 445a3315270e0502189a6fa4de20b9db2bf042cfd2aSvetoslav mSendHoverEnterAndMoveDelayed.addEvent(event); 44638c992841b5f6bc80359dbf60d31aa7b994160fcSvetoslav Ganov } 44784044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav } 44884044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav } break; 44984044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav case MotionEvent.ACTION_POINTER_DOWN: { 450a3315270e0502189a6fa4de20b9db2bf042cfd2aSvetoslav // Another finger down means that if we have not started to deliver 451a3315270e0502189a6fa4de20b9db2bf042cfd2aSvetoslav // hover events, we will not have to. The code for ACTION_MOVE will 452a3315270e0502189a6fa4de20b9db2bf042cfd2aSvetoslav // decide what we will actually do next. 453a3315270e0502189a6fa4de20b9db2bf042cfd2aSvetoslav mSendHoverEnterAndMoveDelayed.cancel(); 454a3315270e0502189a6fa4de20b9db2bf042cfd2aSvetoslav mSendHoverExitDelayed.cancel(); 455a3315270e0502189a6fa4de20b9db2bf042cfd2aSvetoslav mPerformLongPressDelayed.cancel(); 456736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov } break; 457736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov case MotionEvent.ACTION_MOVE: { 45884044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav final int pointerId = receivedTracker.getPrimaryPointerId(); 45991feae3c5994bd4768cea3507c62c65746adcfa6Svetoslav Ganov final int pointerIndex = event.findPointerIndex(pointerId); 46091feae3c5994bd4768cea3507c62c65746adcfa6Svetoslav Ganov final int pointerIdBits = (1 << pointerId); 46184044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav switch (event.getPointerCount()) { 462736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov case 1: { 463e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov // We have not started sending events since we try to 464e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov // figure out what the user is doing. 46584044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav if (mSendHoverEnterAndMoveDelayed.isPending()) { 466e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov // Pre-feed the motion events to the gesture detector since we 467e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov // have a distance slop before getting into gesture detection 468e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov // mode and not using the points within this slop significantly 469e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov // decreases the quality of gesture recognition. 47045af84a483165f06c04d74baba67f90da29c6ad2Svetoslav Ganov handleMotionEventGestureDetecting(rawEvent, policyFlags); 47184044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav 47284044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav // Cache the event until we discern exploration from gesturing. 47384044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav mSendHoverEnterAndMoveDelayed.addEvent(event); 47484044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav 47545af84a483165f06c04d74baba67f90da29c6ad2Svetoslav Ganov // It is *important* to use the distance traveled by the pointers 47645af84a483165f06c04d74baba67f90da29c6ad2Svetoslav Ganov // on the screen which may or may not be magnified. 4774213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov final float deltaX = receivedTracker.getReceivedPointerDownX(pointerId) 47845af84a483165f06c04d74baba67f90da29c6ad2Svetoslav Ganov - rawEvent.getX(pointerIndex); 4794213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov final float deltaY = receivedTracker.getReceivedPointerDownY(pointerId) 48045af84a483165f06c04d74baba67f90da29c6ad2Svetoslav Ganov - rawEvent.getY(pointerIndex); 481736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov final double moveDelta = Math.hypot(deltaX, deltaY); 482e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov // The user has moved enough for us to decide. 483e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov if (moveDelta > mDoubleTapSlop) { 484e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov // Check whether the user is performing a gesture. We 485e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov // detect gestures if the pointer is moving above a 486e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov // given velocity. 4874213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov mVelocityTracker.computeCurrentVelocity(1000); 4884213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov final float maxAbsVelocity = Math.max( 4894213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov Math.abs(mVelocityTracker.getXVelocity(pointerId)), 4904213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov Math.abs(mVelocityTracker.getYVelocity(pointerId))); 491e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov if (maxAbsVelocity > mScaledGestureDetectionVelocity) { 492e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov // We have to perform gesture detection, so 493e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov // clear the current state and try to detect. 4944213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov mCurrentState = STATE_GESTURE_DETECTING; 49545af84a483165f06c04d74baba67f90da29c6ad2Svetoslav Ganov mVelocityTracker.clear(); 49684044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav mSendHoverEnterAndMoveDelayed.cancel(); 49784044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav mSendHoverExitDelayed.cancel(); 49884044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav mPerformLongPressDelayed.cancel(); 49995068e5d1bea47091e97955f271c789264994550Svetoslav Ganov mExitGestureDetectionModeDelayed.post(); 50077276b60851a158ad3e142cb3b091d57ae5ceffbSvetoslav Ganov // Send accessibility event to announce the start 50177276b60851a158ad3e142cb3b091d57ae5ceffbSvetoslav Ganov // of gesture recognition. 50277276b60851a158ad3e142cb3b091d57ae5ceffbSvetoslav Ganov sendAccessibilityEvent( 50377276b60851a158ad3e142cb3b091d57ae5ceffbSvetoslav Ganov AccessibilityEvent.TYPE_GESTURE_DETECTION_START); 504e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov } else { 505e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov // We have just decided that the user is touch, 506e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov // exploring so start sending events. 50784044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav mSendHoverEnterAndMoveDelayed.forceSendAndRemove(); 50884044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav mSendHoverExitDelayed.cancel(); 50984044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav mPerformLongPressDelayed.cancel(); 510e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov sendMotionEvent(event, MotionEvent.ACTION_HOVER_MOVE, 51191feae3c5994bd4768cea3507c62c65746adcfa6Svetoslav Ganov pointerIdBits, policyFlags); 51291feae3c5994bd4768cea3507c62c65746adcfa6Svetoslav Ganov } 513e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov break; 514736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov } 515736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov } else { 516f068fed6c4c3fc2003aec19b6e7e892358179b02Svetoslav Ganov // Cancel the long press if pending and the user 517f068fed6c4c3fc2003aec19b6e7e892358179b02Svetoslav Ganov // moved more than the slop. 518f068fed6c4c3fc2003aec19b6e7e892358179b02Svetoslav Ganov if (mPerformLongPressDelayed.isPending()) { 519f068fed6c4c3fc2003aec19b6e7e892358179b02Svetoslav Ganov final float deltaX = 520f068fed6c4c3fc2003aec19b6e7e892358179b02Svetoslav Ganov receivedTracker.getReceivedPointerDownX(pointerId) 521f068fed6c4c3fc2003aec19b6e7e892358179b02Svetoslav Ganov - rawEvent.getX(pointerIndex); 522f068fed6c4c3fc2003aec19b6e7e892358179b02Svetoslav Ganov final float deltaY = 523f068fed6c4c3fc2003aec19b6e7e892358179b02Svetoslav Ganov receivedTracker.getReceivedPointerDownY(pointerId) 524f068fed6c4c3fc2003aec19b6e7e892358179b02Svetoslav Ganov - rawEvent.getY(pointerIndex); 525f068fed6c4c3fc2003aec19b6e7e892358179b02Svetoslav Ganov final double moveDelta = Math.hypot(deltaX, deltaY); 526f068fed6c4c3fc2003aec19b6e7e892358179b02Svetoslav Ganov // The user has moved enough for us to decide. 527f068fed6c4c3fc2003aec19b6e7e892358179b02Svetoslav Ganov if (moveDelta > mTouchSlop) { 52884044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav mPerformLongPressDelayed.cancel(); 529f068fed6c4c3fc2003aec19b6e7e892358179b02Svetoslav Ganov } 530f068fed6c4c3fc2003aec19b6e7e892358179b02Svetoslav Ganov } 531a3315270e0502189a6fa4de20b9db2bf042cfd2aSvetoslav if (mTouchExplorationInProgress) { 532a3315270e0502189a6fa4de20b9db2bf042cfd2aSvetoslav sendTouchExplorationGestureStartAndHoverEnterIfNeeded(policyFlags); 533a3315270e0502189a6fa4de20b9db2bf042cfd2aSvetoslav sendMotionEvent(event, MotionEvent.ACTION_HOVER_MOVE, pointerIdBits, 534a3315270e0502189a6fa4de20b9db2bf042cfd2aSvetoslav policyFlags); 535e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov } 536736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov } 537e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov } break; 538e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov case 2: { 539e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov // More than one pointer so the user is not touch exploring 540e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov // and now we have to decide whether to delegate or drag. 54184044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav if (mSendHoverEnterAndMoveDelayed.isPending()) { 542e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov // We have not started sending events so cancel 543e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov // scheduled sending events. 54484044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav mSendHoverEnterAndMoveDelayed.cancel(); 54584044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav mSendHoverExitDelayed.cancel(); 54684044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav mPerformLongPressDelayed.cancel(); 547e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov } else { 54884044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav mPerformLongPressDelayed.cancel(); 549a3315270e0502189a6fa4de20b9db2bf042cfd2aSvetoslav if (mTouchExplorationInProgress) { 550a3315270e0502189a6fa4de20b9db2bf042cfd2aSvetoslav // If the user is touch exploring the second pointer may be 551a3315270e0502189a6fa4de20b9db2bf042cfd2aSvetoslav // performing a double tap to activate an item without need 552a3315270e0502189a6fa4de20b9db2bf042cfd2aSvetoslav // for the user to lift his exploring finger. 553a3315270e0502189a6fa4de20b9db2bf042cfd2aSvetoslav // It is *important* to use the distance traveled by the pointers 554a3315270e0502189a6fa4de20b9db2bf042cfd2aSvetoslav // on the screen which may or may not be magnified. 555a3315270e0502189a6fa4de20b9db2bf042cfd2aSvetoslav final float deltaX = receivedTracker.getReceivedPointerDownX( 556a3315270e0502189a6fa4de20b9db2bf042cfd2aSvetoslav pointerId) - rawEvent.getX(pointerIndex); 557a3315270e0502189a6fa4de20b9db2bf042cfd2aSvetoslav final float deltaY = receivedTracker.getReceivedPointerDownY( 558a3315270e0502189a6fa4de20b9db2bf042cfd2aSvetoslav pointerId) - rawEvent.getY(pointerIndex); 559a3315270e0502189a6fa4de20b9db2bf042cfd2aSvetoslav final double moveDelta = Math.hypot(deltaX, deltaY); 560a3315270e0502189a6fa4de20b9db2bf042cfd2aSvetoslav if (moveDelta < mDoubleTapSlop) { 561a3315270e0502189a6fa4de20b9db2bf042cfd2aSvetoslav break; 562a3315270e0502189a6fa4de20b9db2bf042cfd2aSvetoslav } 563a3315270e0502189a6fa4de20b9db2bf042cfd2aSvetoslav // We are sending events so send exit and gesture 564a3315270e0502189a6fa4de20b9db2bf042cfd2aSvetoslav // end since we transition to another state. 565a3315270e0502189a6fa4de20b9db2bf042cfd2aSvetoslav sendHoverExitAndTouchExplorationGestureEndIfNeeded(policyFlags); 566736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov } 567736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov } 568e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov 569e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov // We know that a new state transition is to happen and the 570e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov // new state will not be gesture recognition, so clear the 571e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov // stashed gesture strokes. 572e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov mStrokeBuffer.clear(); 573736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov 574736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov if (isDraggingGesture(event)) { 575736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov // Two pointers moving in the same direction within 576736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov // a given distance perform a drag. 57700f7b3f76515d1c6fbe5cf9fee9d3760787c03cdSvetoslav Ganov mCurrentState = STATE_DRAGGING; 57891feae3c5994bd4768cea3507c62c65746adcfa6Svetoslav Ganov mDraggingPointerId = pointerId; 579c4842c11932ea4f60fe7ae09b0a59660207e1587Svetoslav Ganov event.setEdgeFlags(receivedTracker.getLastReceivedDownEdgeFlags()); 58091feae3c5994bd4768cea3507c62c65746adcfa6Svetoslav Ganov sendMotionEvent(event, MotionEvent.ACTION_DOWN, pointerIdBits, 58191feae3c5994bd4768cea3507c62c65746adcfa6Svetoslav Ganov policyFlags); 582736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov } else { 583736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov // Two pointers moving arbitrary are delegated to the view hierarchy. 584736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov mCurrentState = STATE_DELEGATING; 58584044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav sendDownForAllNotInjectedPointers(event, policyFlags); 586736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov } 58745af84a483165f06c04d74baba67f90da29c6ad2Svetoslav Ganov mVelocityTracker.clear(); 588736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov } break; 589736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov default: { 590e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov // More than one pointer so the user is not touch exploring 591e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov // and now we have to decide whether to delegate or drag. 59284044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav if (mSendHoverEnterAndMoveDelayed.isPending()) { 593e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov // We have not started sending events so cancel 594e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov // scheduled sending events. 59584044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav mSendHoverEnterAndMoveDelayed.cancel(); 59684044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav mSendHoverExitDelayed.cancel(); 59784044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav mPerformLongPressDelayed.cancel(); 598e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov } else { 59984044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav mPerformLongPressDelayed.cancel(); 600e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov // We are sending events so send exit and gesture 601e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov // end since we transition to another state. 602f068fed6c4c3fc2003aec19b6e7e892358179b02Svetoslav Ganov sendHoverExitAndTouchExplorationGestureEndIfNeeded(policyFlags); 603e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov } 604736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov 605736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov // More than two pointers are delegated to the view hierarchy. 606736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov mCurrentState = STATE_DELEGATING; 60784044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav sendDownForAllNotInjectedPointers(event, policyFlags); 60845af84a483165f06c04d74baba67f90da29c6ad2Svetoslav Ganov mVelocityTracker.clear(); 609736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov } 610736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov } 611736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov } break; 61284044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav case MotionEvent.ACTION_UP: { 613f772cba59760d1ad9eb5cb9205b2e2e9126e488dSvetoslav Ganov mAms.onTouchInteractionEnd(); 614e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov // We know that we do not need the pre-fed gesture points are not 615e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov // needed anymore since the last pointer just went up. 616e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov mStrokeBuffer.clear(); 61784044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav final int pointerId = event.getPointerId(event.getActionIndex()); 61891feae3c5994bd4768cea3507c62c65746adcfa6Svetoslav Ganov final int pointerIdBits = (1 << pointerId); 619736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov 62084044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav mPerformLongPressDelayed.cancel(); 62184044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav mVelocityTracker.clear(); 622f5a07905a3e025f95472a3f8d9935263e49ad6d3Svetoslav Ganov 62384044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav if (mSendHoverEnterAndMoveDelayed.isPending()) { 62484044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav // If we have not delivered the enter schedule an exit. 62584044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav mSendHoverExitDelayed.post(event, pointerIdBits, policyFlags); 62684044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav } else { 62784044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav // The user is touch exploring so we send events for end. 62884044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav sendHoverExitAndTouchExplorationGestureEndIfNeeded(policyFlags); 62984044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav } 630f068fed6c4c3fc2003aec19b6e7e892358179b02Svetoslav Ganov 63184044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav if (!mSendTouchInteractionEndDelayed.isPending()) { 63284044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav mSendTouchInteractionEndDelayed.post(); 633736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov } 63484044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav 635736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov } break; 636736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov case MotionEvent.ACTION_CANCEL: { 637e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov clear(event, policyFlags); 638736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov } break; 639736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov } 640736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov } 641736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov 642736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov /** 643736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov * Handles a motion event in dragging state. 644736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov * 645736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov * @param event The event to be handled. 646736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov * @param policyFlags The policy flags associated with the event. 647736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov */ 648736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov private void handleMotionEventStateDragging(MotionEvent event, int policyFlags) { 64991feae3c5994bd4768cea3507c62c65746adcfa6Svetoslav Ganov final int pointerIdBits = (1 << mDraggingPointerId); 650736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov switch (event.getActionMasked()) { 651736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov case MotionEvent.ACTION_DOWN: { 652736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov throw new IllegalStateException("Dragging state can be reached only if two " 653736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov + "pointers are already down"); 654736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov } 655736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov case MotionEvent.ACTION_POINTER_DOWN: { 656736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov // We are in dragging state so we have two pointers and another one 657736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov // goes down => delegate the three pointers to the view hierarchy 658736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov mCurrentState = STATE_DELEGATING; 659ec33d56300aa417efb4a055786d73d1bf23a6a85Svetoslav Ganov if (mDraggingPointerId != INVALID_POINTER_ID) { 660ec33d56300aa417efb4a055786d73d1bf23a6a85Svetoslav Ganov sendMotionEvent(event, MotionEvent.ACTION_UP, pointerIdBits, policyFlags); 661ec33d56300aa417efb4a055786d73d1bf23a6a85Svetoslav Ganov } 66284044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav sendDownForAllNotInjectedPointers(event, policyFlags); 663736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov } break; 664736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov case MotionEvent.ACTION_MOVE: { 66584044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav switch (event.getPointerCount()) { 6662e1c66bd53d30d2148afaa4b393b60cd59976d65Svetoslav Ganov case 1: { 6672e1c66bd53d30d2148afaa4b393b60cd59976d65Svetoslav Ganov // do nothing 6682e1c66bd53d30d2148afaa4b393b60cd59976d65Svetoslav Ganov } break; 669736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov case 2: { 670736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov if (isDraggingGesture(event)) { 67184044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav final float firstPtrX = event.getX(0); 67284044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav final float firstPtrY = event.getY(0); 67384044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav final float secondPtrX = event.getX(1); 67484044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav final float secondPtrY = event.getY(1); 675e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov 676e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov final float deltaX = firstPtrX - secondPtrX; 677e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov final float deltaY = firstPtrY - secondPtrY; 678e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov final double distance = Math.hypot(deltaX, deltaY); 679e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov 680e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov if (distance > mScaledMinPointerDistanceToUseMiddleLocation) { 681e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov event.setLocation(deltaX / 2, deltaY / 2); 682e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov } 683e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov 684736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov // If still dragging send a drag event. 68591feae3c5994bd4768cea3507c62c65746adcfa6Svetoslav Ganov sendMotionEvent(event, MotionEvent.ACTION_MOVE, pointerIdBits, 68691feae3c5994bd4768cea3507c62c65746adcfa6Svetoslav Ganov policyFlags); 687736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov } else { 688736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov // The two pointers are moving either in different directions or 689736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov // no close enough => delegate the gesture to the view hierarchy. 690736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov mCurrentState = STATE_DELEGATING; 691736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov // Send an event to the end of the drag gesture. 69291feae3c5994bd4768cea3507c62c65746adcfa6Svetoslav Ganov sendMotionEvent(event, MotionEvent.ACTION_UP, pointerIdBits, 69391feae3c5994bd4768cea3507c62c65746adcfa6Svetoslav Ganov policyFlags); 69484044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav // Deliver all pointers to the view hierarchy. 69584044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav sendDownForAllNotInjectedPointers(event, policyFlags); 696736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov } 697736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov } break; 698736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov default: { 699736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov mCurrentState = STATE_DELEGATING; 700736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov // Send an event to the end of the drag gesture. 70191feae3c5994bd4768cea3507c62c65746adcfa6Svetoslav Ganov sendMotionEvent(event, MotionEvent.ACTION_UP, pointerIdBits, 70291feae3c5994bd4768cea3507c62c65746adcfa6Svetoslav Ganov policyFlags); 70384044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav // Deliver all pointers to the view hierarchy. 70484044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav sendDownForAllNotInjectedPointers(event, policyFlags); 705736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov } 706736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov } 707736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov } break; 708aeb8d0ed0d98d398a66a092c418f4f2bca8719e0Svetoslav Ganov case MotionEvent.ACTION_POINTER_UP: { 709aeb8d0ed0d98d398a66a092c418f4f2bca8719e0Svetoslav Ganov final int pointerId = event.getPointerId(event.getActionIndex()); 710aeb8d0ed0d98d398a66a092c418f4f2bca8719e0Svetoslav Ganov if (pointerId == mDraggingPointerId) { 711aeb8d0ed0d98d398a66a092c418f4f2bca8719e0Svetoslav Ganov mDraggingPointerId = INVALID_POINTER_ID; 712aeb8d0ed0d98d398a66a092c418f4f2bca8719e0Svetoslav Ganov // Send an event to the end of the drag gesture. 713aeb8d0ed0d98d398a66a092c418f4f2bca8719e0Svetoslav Ganov sendMotionEvent(event, MotionEvent.ACTION_UP, pointerIdBits, policyFlags); 714aeb8d0ed0d98d398a66a092c418f4f2bca8719e0Svetoslav Ganov } 715aeb8d0ed0d98d398a66a092c418f4f2bca8719e0Svetoslav Ganov } break; 7162e1c66bd53d30d2148afaa4b393b60cd59976d65Svetoslav Ganov case MotionEvent.ACTION_UP: { 717f772cba59760d1ad9eb5cb9205b2e2e9126e488dSvetoslav Ganov mAms.onTouchInteractionEnd(); 71877276b60851a158ad3e142cb3b091d57ae5ceffbSvetoslav Ganov // Announce the end of a new touch interaction. 71977276b60851a158ad3e142cb3b091d57ae5ceffbSvetoslav Ganov sendAccessibilityEvent( 72077276b60851a158ad3e142cb3b091d57ae5ceffbSvetoslav Ganov AccessibilityEvent.TYPE_TOUCH_INTERACTION_END); 721aeb8d0ed0d98d398a66a092c418f4f2bca8719e0Svetoslav Ganov final int pointerId = event.getPointerId(event.getActionIndex()); 722aeb8d0ed0d98d398a66a092c418f4f2bca8719e0Svetoslav Ganov if (pointerId == mDraggingPointerId) { 723aeb8d0ed0d98d398a66a092c418f4f2bca8719e0Svetoslav Ganov mDraggingPointerId = INVALID_POINTER_ID; 724aeb8d0ed0d98d398a66a092c418f4f2bca8719e0Svetoslav Ganov // Send an event to the end of the drag gesture. 725aeb8d0ed0d98d398a66a092c418f4f2bca8719e0Svetoslav Ganov sendMotionEvent(event, MotionEvent.ACTION_UP, pointerIdBits, policyFlags); 726aeb8d0ed0d98d398a66a092c418f4f2bca8719e0Svetoslav Ganov } 7272e1c66bd53d30d2148afaa4b393b60cd59976d65Svetoslav Ganov mCurrentState = STATE_TOUCH_EXPLORING; 7282e1c66bd53d30d2148afaa4b393b60cd59976d65Svetoslav Ganov } break; 729736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov case MotionEvent.ACTION_CANCEL: { 730e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov clear(event, policyFlags); 731736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov } break; 732736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov } 733736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov } 734736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov 735736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov /** 736736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov * Handles a motion event in delegating state. 737736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov * 738736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov * @param event The event to be handled. 739736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov * @param policyFlags The policy flags associated with the event. 740736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov */ 7412e1c66bd53d30d2148afaa4b393b60cd59976d65Svetoslav Ganov private void handleMotionEventStateDelegating(MotionEvent event, int policyFlags) { 742736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov switch (event.getActionMasked()) { 743736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov case MotionEvent.ACTION_DOWN: { 744736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov throw new IllegalStateException("Delegating state can only be reached if " 745736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov + "there is at least one pointer down!"); 746736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov } 74784044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav case MotionEvent.ACTION_UP: { 748a3315270e0502189a6fa4de20b9db2bf042cfd2aSvetoslav // Offset the event if we are doing a long press as the 749a3315270e0502189a6fa4de20b9db2bf042cfd2aSvetoslav // target is not necessarily under the user's finger. 750a3315270e0502189a6fa4de20b9db2bf042cfd2aSvetoslav if (mLongPressingPointerId >= 0) { 751a3315270e0502189a6fa4de20b9db2bf042cfd2aSvetoslav event = offsetEvent(event, - mLongPressingPointerDeltaX, 752a3315270e0502189a6fa4de20b9db2bf042cfd2aSvetoslav - mLongPressingPointerDeltaY); 753a3315270e0502189a6fa4de20b9db2bf042cfd2aSvetoslav // Clear the long press state. 754a3315270e0502189a6fa4de20b9db2bf042cfd2aSvetoslav mLongPressingPointerId = -1; 755a3315270e0502189a6fa4de20b9db2bf042cfd2aSvetoslav mLongPressingPointerDeltaX = 0; 756a3315270e0502189a6fa4de20b9db2bf042cfd2aSvetoslav mLongPressingPointerDeltaY = 0; 757a3315270e0502189a6fa4de20b9db2bf042cfd2aSvetoslav } 758a3315270e0502189a6fa4de20b9db2bf042cfd2aSvetoslav 759a3315270e0502189a6fa4de20b9db2bf042cfd2aSvetoslav // Deliver the event. 760a3315270e0502189a6fa4de20b9db2bf042cfd2aSvetoslav sendMotionEvent(event, event.getAction(), ALL_POINTER_ID_BITS, policyFlags); 761a3315270e0502189a6fa4de20b9db2bf042cfd2aSvetoslav 76284044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav // Announce the end of a the touch interaction. 763a3315270e0502189a6fa4de20b9db2bf042cfd2aSvetoslav mAms.onTouchInteractionEnd(); 76484044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav sendAccessibilityEvent(AccessibilityEvent.TYPE_TOUCH_INTERACTION_END); 765a3315270e0502189a6fa4de20b9db2bf042cfd2aSvetoslav 76684044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav mCurrentState = STATE_TOUCH_EXPLORING; 767736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov } break; 768736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov case MotionEvent.ACTION_CANCEL: { 769e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov clear(event, policyFlags); 770736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov } break; 771a3315270e0502189a6fa4de20b9db2bf042cfd2aSvetoslav default: { 772a3315270e0502189a6fa4de20b9db2bf042cfd2aSvetoslav // Deliver the event. 773a3315270e0502189a6fa4de20b9db2bf042cfd2aSvetoslav sendMotionEvent(event, event.getAction(), ALL_POINTER_ID_BITS, policyFlags); 774a3315270e0502189a6fa4de20b9db2bf042cfd2aSvetoslav } 775736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov } 776736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov } 777736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov 7784213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov private void handleMotionEventGestureDetecting(MotionEvent event, int policyFlags) { 7794213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov switch (event.getActionMasked()) { 7804213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov case MotionEvent.ACTION_DOWN: { 7814213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov final float x = event.getX(); 7824213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov final float y = event.getY(); 7834213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov mPreviousX = x; 7844213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov mPreviousY = y; 7854213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov mStrokeBuffer.add(new GesturePoint(x, y, event.getEventTime())); 7864213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov } break; 7874213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov case MotionEvent.ACTION_MOVE: { 7884213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov final float x = event.getX(); 7894213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov final float y = event.getY(); 7904213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov final float dX = Math.abs(x - mPreviousX); 7914213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov final float dY = Math.abs(y - mPreviousY); 7924213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov if (dX >= TOUCH_TOLERANCE || dY >= TOUCH_TOLERANCE) { 7934213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov mPreviousX = x; 7944213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov mPreviousY = y; 7954213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov mStrokeBuffer.add(new GesturePoint(x, y, event.getEventTime())); 7964213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov } 7974213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov } break; 798e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov case MotionEvent.ACTION_UP: { 799f772cba59760d1ad9eb5cb9205b2e2e9126e488dSvetoslav Ganov mAms.onTouchInteractionEnd(); 80084044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav // Announce the end of the gesture recognition. 80184044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav sendAccessibilityEvent(AccessibilityEvent.TYPE_GESTURE_DETECTION_END); 80284044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav // Announce the end of a the touch interaction. 80384044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav sendAccessibilityEvent(AccessibilityEvent.TYPE_TOUCH_INTERACTION_END); 80477276b60851a158ad3e142cb3b091d57ae5ceffbSvetoslav Ganov 8054213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov float x = event.getX(); 8064213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov float y = event.getY(); 8074213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov mStrokeBuffer.add(new GesturePoint(x, y, event.getEventTime())); 8084213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov 8094213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov Gesture gesture = new Gesture(); 8104213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov gesture.addStroke(new GestureStroke(mStrokeBuffer)); 8114213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov 8124213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov ArrayList<Prediction> predictions = mGestureLibrary.recognize(gesture); 8134213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov if (!predictions.isEmpty()) { 8144213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov Prediction bestPrediction = predictions.get(0); 8154213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov if (bestPrediction.score >= MIN_PREDICTION_SCORE) { 8164213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov if (DEBUG) { 8174213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov Slog.i(LOG_TAG, "gesture: " + bestPrediction.name + " score: " 8184213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov + bestPrediction.score); 8194213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov } 8204213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov try { 8214213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov final int gestureId = Integer.parseInt(bestPrediction.name); 822e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov mAms.onGesture(gestureId); 8234213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov } catch (NumberFormatException nfe) { 8244213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov Slog.w(LOG_TAG, "Non numeric gesture id:" + bestPrediction.name); 8254213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov } 8264213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov } 8274213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov } 8284213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov 8294213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov mStrokeBuffer.clear(); 83084044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav mExitGestureDetectionModeDelayed.cancel(); 8314213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov mCurrentState = STATE_TOUCH_EXPLORING; 8324213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov } break; 8334213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov case MotionEvent.ACTION_CANCEL: { 834e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov clear(event, policyFlags); 8354213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov } break; 8364213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov } 8374213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov } 8384213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov 839736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov /** 84077276b60851a158ad3e142cb3b091d57ae5ceffbSvetoslav Ganov * Sends an accessibility event of the given type. 84177276b60851a158ad3e142cb3b091d57ae5ceffbSvetoslav Ganov * 84277276b60851a158ad3e142cb3b091d57ae5ceffbSvetoslav Ganov * @param type The event type. 84377276b60851a158ad3e142cb3b091d57ae5ceffbSvetoslav Ganov */ 84477276b60851a158ad3e142cb3b091d57ae5ceffbSvetoslav Ganov private void sendAccessibilityEvent(int type) { 84577276b60851a158ad3e142cb3b091d57ae5ceffbSvetoslav Ganov AccessibilityManager accessibilityManager = AccessibilityManager.getInstance(mContext); 84677276b60851a158ad3e142cb3b091d57ae5ceffbSvetoslav Ganov if (accessibilityManager.isEnabled()) { 84777276b60851a158ad3e142cb3b091d57ae5ceffbSvetoslav Ganov AccessibilityEvent event = AccessibilityEvent.obtain(type); 8483d1c5a7236c4709550ca7c0cfa293fc5c974c56bAlan Viverette event.setWindowId(mAms.getActiveWindowId()); 84977276b60851a158ad3e142cb3b091d57ae5ceffbSvetoslav Ganov accessibilityManager.sendAccessibilityEvent(event); 850f068fed6c4c3fc2003aec19b6e7e892358179b02Svetoslav Ganov switch (type) { 851f068fed6c4c3fc2003aec19b6e7e892358179b02Svetoslav Ganov case AccessibilityEvent.TYPE_TOUCH_EXPLORATION_GESTURE_START: { 852f068fed6c4c3fc2003aec19b6e7e892358179b02Svetoslav Ganov mTouchExplorationInProgress = true; 853f068fed6c4c3fc2003aec19b6e7e892358179b02Svetoslav Ganov } break; 854f068fed6c4c3fc2003aec19b6e7e892358179b02Svetoslav Ganov case AccessibilityEvent.TYPE_TOUCH_EXPLORATION_GESTURE_END: { 855f068fed6c4c3fc2003aec19b6e7e892358179b02Svetoslav Ganov mTouchExplorationInProgress = false; 856f068fed6c4c3fc2003aec19b6e7e892358179b02Svetoslav Ganov } break; 857f068fed6c4c3fc2003aec19b6e7e892358179b02Svetoslav Ganov } 85877276b60851a158ad3e142cb3b091d57ae5ceffbSvetoslav Ganov } 85977276b60851a158ad3e142cb3b091d57ae5ceffbSvetoslav Ganov } 86077276b60851a158ad3e142cb3b091d57ae5ceffbSvetoslav Ganov 86177276b60851a158ad3e142cb3b091d57ae5ceffbSvetoslav Ganov /** 86284044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav * Sends down events to the view hierarchy for all pointers which are 863736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov * not already being delivered i.e. pointers that are not yet injected. 864736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov * 865736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov * @param prototype The prototype from which to create the injected events. 866736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov * @param policyFlags The policy flags associated with the event. 867736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov */ 86884044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav private void sendDownForAllNotInjectedPointers(MotionEvent prototype, int policyFlags) { 8694213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov InjectedPointerTracker injectedPointers = mInjectedPointerTracker; 87084044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav 87184044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav // Inject the injected pointers. 872f804420d6e37748b75478406e989c69303756980Svetoslav Ganov int pointerIdBits = 0; 873f804420d6e37748b75478406e989c69303756980Svetoslav Ganov final int pointerCount = prototype.getPointerCount(); 874f804420d6e37748b75478406e989c69303756980Svetoslav Ganov for (int i = 0; i < pointerCount; i++) { 875f804420d6e37748b75478406e989c69303756980Svetoslav Ganov final int pointerId = prototype.getPointerId(i); 876f804420d6e37748b75478406e989c69303756980Svetoslav Ganov // Do not send event for already delivered pointers. 87784044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav if (!injectedPointers.isInjectedPointerDown(pointerId)) { 87884044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav pointerIdBits |= (1 << pointerId); 87984044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav final int action = computeInjectionAction(MotionEvent.ACTION_DOWN, i); 88084044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav sendMotionEvent(prototype, action, pointerIdBits, policyFlags); 881736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov } 882f804420d6e37748b75478406e989c69303756980Svetoslav Ganov } 883f804420d6e37748b75478406e989c69303756980Svetoslav Ganov } 884736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov 885f804420d6e37748b75478406e989c69303756980Svetoslav Ganov /** 886e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov * Sends the exit events if needed. Such events are hover exit and touch explore 887e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov * gesture end. 888e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov * 889e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov * @param policyFlags The policy flags associated with the event. 890e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov */ 891f068fed6c4c3fc2003aec19b6e7e892358179b02Svetoslav Ganov private void sendHoverExitAndTouchExplorationGestureEndIfNeeded(int policyFlags) { 892e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov MotionEvent event = mInjectedPointerTracker.getLastInjectedHoverEvent(); 893e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov if (event != null && event.getActionMasked() != MotionEvent.ACTION_HOVER_EXIT) { 894e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov final int pointerIdBits = event.getPointerIdBits(); 895f068fed6c4c3fc2003aec19b6e7e892358179b02Svetoslav Ganov if (!mSendTouchExplorationEndDelayed.isPending()) { 896f068fed6c4c3fc2003aec19b6e7e892358179b02Svetoslav Ganov mSendTouchExplorationEndDelayed.post(); 897fe304b893968887323b93764caafa66ee8ad44deSvetoslav Ganov } 898e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov sendMotionEvent(event, MotionEvent.ACTION_HOVER_EXIT, pointerIdBits, policyFlags); 899e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov } 900e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov } 901e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov 902e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov /** 903e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov * Sends the enter events if needed. Such events are hover enter and touch explore 904e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov * gesture start. 905f804420d6e37748b75478406e989c69303756980Svetoslav Ganov * 906f804420d6e37748b75478406e989c69303756980Svetoslav Ganov * @param policyFlags The policy flags associated with the event. 907f804420d6e37748b75478406e989c69303756980Svetoslav Ganov */ 908f068fed6c4c3fc2003aec19b6e7e892358179b02Svetoslav Ganov private void sendTouchExplorationGestureStartAndHoverEnterIfNeeded(int policyFlags) { 909e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov MotionEvent event = mInjectedPointerTracker.getLastInjectedHoverEvent(); 910e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov if (event != null && event.getActionMasked() == MotionEvent.ACTION_HOVER_EXIT) { 911e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov final int pointerIdBits = event.getPointerIdBits(); 912f068fed6c4c3fc2003aec19b6e7e892358179b02Svetoslav Ganov sendAccessibilityEvent(AccessibilityEvent.TYPE_TOUCH_EXPLORATION_GESTURE_START); 913e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov sendMotionEvent(event, MotionEvent.ACTION_HOVER_ENTER, pointerIdBits, policyFlags); 914736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov } 915736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov } 916736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov 917736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov /** 91884044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav * Sends up events to the view hierarchy for all pointers which are 919736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov * already being delivered i.e. pointers that are injected. 920736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov * 921736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov * @param prototype The prototype from which to create the injected events. 922736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov * @param policyFlags The policy flags associated with the event. 923736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov */ 924736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov private void sendUpForInjectedDownPointers(MotionEvent prototype, int policyFlags) { 9254213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov final InjectedPointerTracker injectedTracked = mInjectedPointerTracker; 926f804420d6e37748b75478406e989c69303756980Svetoslav Ganov int pointerIdBits = 0; 927f804420d6e37748b75478406e989c69303756980Svetoslav Ganov final int pointerCount = prototype.getPointerCount(); 928736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov for (int i = 0; i < pointerCount; i++) { 929736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov final int pointerId = prototype.getPointerId(i); 930736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov // Skip non injected down pointers. 9314213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov if (!injectedTracked.isInjectedPointerDown(pointerId)) { 932736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov continue; 933736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov } 934f804420d6e37748b75478406e989c69303756980Svetoslav Ganov pointerIdBits |= (1 << pointerId); 935f804420d6e37748b75478406e989c69303756980Svetoslav Ganov final int action = computeInjectionAction(MotionEvent.ACTION_UP, i); 936f804420d6e37748b75478406e989c69303756980Svetoslav Ganov sendMotionEvent(prototype, action, pointerIdBits, policyFlags); 937736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov } 938736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov } 939736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov 940736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov /** 941736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov * Sends an up and down events. 942736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov * 943736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov * @param prototype The prototype from which to create the injected events. 944736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov * @param policyFlags The policy flags associated with the event. 945736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov */ 946736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov private void sendActionDownAndUp(MotionEvent prototype, int policyFlags) { 94784044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav // Tap with the pointer that last explored. 948bd206d129fdd1777b9f9646a834d7fc342a8941eSvetoslav Ganov final int pointerId = prototype.getPointerId(prototype.getActionIndex()); 949f804420d6e37748b75478406e989c69303756980Svetoslav Ganov final int pointerIdBits = (1 << pointerId); 950f804420d6e37748b75478406e989c69303756980Svetoslav Ganov sendMotionEvent(prototype, MotionEvent.ACTION_DOWN, pointerIdBits, policyFlags); 951f804420d6e37748b75478406e989c69303756980Svetoslav Ganov sendMotionEvent(prototype, MotionEvent.ACTION_UP, pointerIdBits, policyFlags); 952736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov } 953736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov 954736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov /** 95591feae3c5994bd4768cea3507c62c65746adcfa6Svetoslav Ganov * Sends an event. 956736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov * 95700f7b3f76515d1c6fbe5cf9fee9d3760787c03cdSvetoslav Ganov * @param prototype The prototype from which to create the injected events. 95891feae3c5994bd4768cea3507c62c65746adcfa6Svetoslav Ganov * @param action The action of the event. 95991feae3c5994bd4768cea3507c62c65746adcfa6Svetoslav Ganov * @param pointerIdBits The bits of the pointers to send. 960736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov * @param policyFlags The policy flags associated with the event. 961736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov */ 96291feae3c5994bd4768cea3507c62c65746adcfa6Svetoslav Ganov private void sendMotionEvent(MotionEvent prototype, int action, int pointerIdBits, 96391feae3c5994bd4768cea3507c62c65746adcfa6Svetoslav Ganov int policyFlags) { 964f804420d6e37748b75478406e989c69303756980Svetoslav Ganov prototype.setAction(action); 965f804420d6e37748b75478406e989c69303756980Svetoslav Ganov 966f804420d6e37748b75478406e989c69303756980Svetoslav Ganov MotionEvent event = null; 967f804420d6e37748b75478406e989c69303756980Svetoslav Ganov if (pointerIdBits == ALL_POINTER_ID_BITS) { 968f804420d6e37748b75478406e989c69303756980Svetoslav Ganov event = prototype; 969f804420d6e37748b75478406e989c69303756980Svetoslav Ganov } else { 970f804420d6e37748b75478406e989c69303756980Svetoslav Ganov event = prototype.split(pointerIdBits); 971f804420d6e37748b75478406e989c69303756980Svetoslav Ganov } 972f804420d6e37748b75478406e989c69303756980Svetoslav Ganov if (action == MotionEvent.ACTION_DOWN) { 973f804420d6e37748b75478406e989c69303756980Svetoslav Ganov event.setDownTime(event.getEventTime()); 974f804420d6e37748b75478406e989c69303756980Svetoslav Ganov } else { 9754213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov event.setDownTime(mInjectedPointerTracker.getLastInjectedDownEventTime()); 976f804420d6e37748b75478406e989c69303756980Svetoslav Ganov } 977f804420d6e37748b75478406e989c69303756980Svetoslav Ganov 978e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov // If the user is long pressing but the long pressing pointer 979e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov // was not exactly over the accessibility focused item we need 980e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov // to remap the location of that pointer so the user does not 981e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov // have to explicitly touch explore something to be able to 982e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov // long press it, or even worse to avoid the user long pressing 983e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov // on the wrong item since click and long press behave differently. 984e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov if (mLongPressingPointerId >= 0) { 985a3315270e0502189a6fa4de20b9db2bf042cfd2aSvetoslav event = offsetEvent(event, - mLongPressingPointerDeltaX, 986a3315270e0502189a6fa4de20b9db2bf042cfd2aSvetoslav - mLongPressingPointerDeltaY); 987e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov } 988e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov 989f804420d6e37748b75478406e989c69303756980Svetoslav Ganov if (DEBUG) { 9904213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov Slog.d(LOG_TAG, "Injecting event: " + event + ", policyFlags=0x" 991f804420d6e37748b75478406e989c69303756980Svetoslav Ganov + Integer.toHexString(policyFlags)); 992f804420d6e37748b75478406e989c69303756980Svetoslav Ganov } 993f804420d6e37748b75478406e989c69303756980Svetoslav Ganov 994f804420d6e37748b75478406e989c69303756980Svetoslav Ganov // Make sure that the user will see the event. 995f804420d6e37748b75478406e989c69303756980Svetoslav Ganov policyFlags |= WindowManagerPolicy.FLAG_PASS_TO_USER; 9961cf70bbf96930662cab0e699d70b62865766ff52Svetoslav Ganov if (mNext != null) { 99745af84a483165f06c04d74baba67f90da29c6ad2Svetoslav Ganov // TODO: For now pass null for the raw event since the touch 99845af84a483165f06c04d74baba67f90da29c6ad2Svetoslav Ganov // explorer is the last event transformation and it does 99945af84a483165f06c04d74baba67f90da29c6ad2Svetoslav Ganov // not care about the raw event. 100045af84a483165f06c04d74baba67f90da29c6ad2Svetoslav Ganov mNext.onMotionEvent(event, null, policyFlags); 10011cf70bbf96930662cab0e699d70b62865766ff52Svetoslav Ganov } 1002f804420d6e37748b75478406e989c69303756980Svetoslav Ganov 10034213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov mInjectedPointerTracker.onMotionEvent(event); 10044213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov 1005f804420d6e37748b75478406e989c69303756980Svetoslav Ganov if (event != prototype) { 1006f804420d6e37748b75478406e989c69303756980Svetoslav Ganov event.recycle(); 1007f804420d6e37748b75478406e989c69303756980Svetoslav Ganov } 1008736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov } 1009736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov 1010736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov /** 1011a3315270e0502189a6fa4de20b9db2bf042cfd2aSvetoslav * Offsets all pointers in the given event by adding the specified X and Y 1012a3315270e0502189a6fa4de20b9db2bf042cfd2aSvetoslav * offsets. 1013a3315270e0502189a6fa4de20b9db2bf042cfd2aSvetoslav * 1014a3315270e0502189a6fa4de20b9db2bf042cfd2aSvetoslav * @param event The event to offset. 1015a3315270e0502189a6fa4de20b9db2bf042cfd2aSvetoslav * @param offsetX The X offset. 1016a3315270e0502189a6fa4de20b9db2bf042cfd2aSvetoslav * @param offsetY The Y offset. 1017a3315270e0502189a6fa4de20b9db2bf042cfd2aSvetoslav * @return An event with the offset pointers or the original event if both 1018a3315270e0502189a6fa4de20b9db2bf042cfd2aSvetoslav * offsets are zero. 1019a3315270e0502189a6fa4de20b9db2bf042cfd2aSvetoslav */ 1020a3315270e0502189a6fa4de20b9db2bf042cfd2aSvetoslav private MotionEvent offsetEvent(MotionEvent event, int offsetX, int offsetY) { 1021a3315270e0502189a6fa4de20b9db2bf042cfd2aSvetoslav if (offsetX == 0 && offsetY == 0) { 1022a3315270e0502189a6fa4de20b9db2bf042cfd2aSvetoslav return event; 1023a3315270e0502189a6fa4de20b9db2bf042cfd2aSvetoslav } 1024a3315270e0502189a6fa4de20b9db2bf042cfd2aSvetoslav final int remappedIndex = event.findPointerIndex(mLongPressingPointerId); 1025a3315270e0502189a6fa4de20b9db2bf042cfd2aSvetoslav final int pointerCount = event.getPointerCount(); 1026a3315270e0502189a6fa4de20b9db2bf042cfd2aSvetoslav PointerProperties[] props = PointerProperties.createArray(pointerCount); 1027a3315270e0502189a6fa4de20b9db2bf042cfd2aSvetoslav PointerCoords[] coords = PointerCoords.createArray(pointerCount); 1028a3315270e0502189a6fa4de20b9db2bf042cfd2aSvetoslav for (int i = 0; i < pointerCount; i++) { 1029a3315270e0502189a6fa4de20b9db2bf042cfd2aSvetoslav event.getPointerProperties(i, props[i]); 1030a3315270e0502189a6fa4de20b9db2bf042cfd2aSvetoslav event.getPointerCoords(i, coords[i]); 1031a3315270e0502189a6fa4de20b9db2bf042cfd2aSvetoslav if (i == remappedIndex) { 1032a3315270e0502189a6fa4de20b9db2bf042cfd2aSvetoslav coords[i].x += offsetX; 1033a3315270e0502189a6fa4de20b9db2bf042cfd2aSvetoslav coords[i].y += offsetY; 1034a3315270e0502189a6fa4de20b9db2bf042cfd2aSvetoslav } 1035a3315270e0502189a6fa4de20b9db2bf042cfd2aSvetoslav } 1036a3315270e0502189a6fa4de20b9db2bf042cfd2aSvetoslav return MotionEvent.obtain(event.getDownTime(), 1037a3315270e0502189a6fa4de20b9db2bf042cfd2aSvetoslav event.getEventTime(), event.getAction(), event.getPointerCount(), 1038a3315270e0502189a6fa4de20b9db2bf042cfd2aSvetoslav props, coords, event.getMetaState(), event.getButtonState(), 1039a3315270e0502189a6fa4de20b9db2bf042cfd2aSvetoslav 1.0f, 1.0f, event.getDeviceId(), event.getEdgeFlags(), 1040a3315270e0502189a6fa4de20b9db2bf042cfd2aSvetoslav event.getSource(), event.getFlags()); 1041a3315270e0502189a6fa4de20b9db2bf042cfd2aSvetoslav } 1042a3315270e0502189a6fa4de20b9db2bf042cfd2aSvetoslav 1043a3315270e0502189a6fa4de20b9db2bf042cfd2aSvetoslav /** 1044736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov * Computes the action for an injected event based on a masked action 1045736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov * and a pointer index. 1046736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov * 1047736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov * @param actionMasked The masked action. 1048736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov * @param pointerIndex The index of the pointer which has changed. 1049736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov * @return The action to be used for injection. 1050736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov */ 1051736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov private int computeInjectionAction(int actionMasked, int pointerIndex) { 1052736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov switch (actionMasked) { 1053736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov case MotionEvent.ACTION_DOWN: 1054736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov case MotionEvent.ACTION_POINTER_DOWN: { 10554213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov InjectedPointerTracker injectedTracker = mInjectedPointerTracker; 1056736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov // Compute the action based on how many down pointers are injected. 10574213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov if (injectedTracker.getInjectedPointerDownCount() == 0) { 1058736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov return MotionEvent.ACTION_DOWN; 1059736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov } else { 1060736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov return (pointerIndex << MotionEvent.ACTION_POINTER_INDEX_SHIFT) 1061736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov | MotionEvent.ACTION_POINTER_DOWN; 1062736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov } 1063736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov } 1064736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov case MotionEvent.ACTION_POINTER_UP: { 10654213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov InjectedPointerTracker injectedTracker = mInjectedPointerTracker; 1066736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov // Compute the action based on how many down pointers are injected. 10674213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov if (injectedTracker.getInjectedPointerDownCount() == 1) { 1068736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov return MotionEvent.ACTION_UP; 1069736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov } else { 1070736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov return (pointerIndex << MotionEvent.ACTION_POINTER_INDEX_SHIFT) 1071736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov | MotionEvent.ACTION_POINTER_UP; 1072736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov } 1073736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov } 1074736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov default: 1075736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov return actionMasked; 1076736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov } 1077736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov } 1078736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov 1079e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov private class DoubleTapDetector { 1080e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov private MotionEvent mDownEvent; 1081e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov private MotionEvent mFirstTapEvent; 1082e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov 1083e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov public void onMotionEvent(MotionEvent event, int policyFlags) { 10841cf70bbf96930662cab0e699d70b62865766ff52Svetoslav Ganov final int actionIndex = event.getActionIndex(); 1085e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov final int action = event.getActionMasked(); 1086e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov switch (action) { 1087e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov case MotionEvent.ACTION_DOWN: 1088e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov case MotionEvent.ACTION_POINTER_DOWN: { 10891cf70bbf96930662cab0e699d70b62865766ff52Svetoslav Ganov if (mFirstTapEvent != null 10901cf70bbf96930662cab0e699d70b62865766ff52Svetoslav Ganov && !GestureUtils.isSamePointerContext(mFirstTapEvent, event)) { 1091e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov clear(); 1092e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov } 1093e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov mDownEvent = MotionEvent.obtain(event); 1094e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov } break; 1095e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov case MotionEvent.ACTION_UP: 1096e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov case MotionEvent.ACTION_POINTER_UP: { 1097e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov if (mDownEvent == null) { 1098e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov return; 1099e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov } 11001cf70bbf96930662cab0e699d70b62865766ff52Svetoslav Ganov if (!GestureUtils.isSamePointerContext(mDownEvent, event)) { 1101e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov clear(); 1102e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov return; 1103e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov } 11041cf70bbf96930662cab0e699d70b62865766ff52Svetoslav Ganov if (GestureUtils.isTap(mDownEvent, event, mTapTimeout, mTouchSlop, 11051cf70bbf96930662cab0e699d70b62865766ff52Svetoslav Ganov actionIndex)) { 11061cf70bbf96930662cab0e699d70b62865766ff52Svetoslav Ganov if (mFirstTapEvent == null || GestureUtils.isTimedOut(mFirstTapEvent, 11071cf70bbf96930662cab0e699d70b62865766ff52Svetoslav Ganov event, mDoubleTapTimeout)) { 1108e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov mFirstTapEvent = MotionEvent.obtain(event); 1109e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov mDownEvent.recycle(); 1110e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov mDownEvent = null; 1111e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov return; 1112e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov } 11131cf70bbf96930662cab0e699d70b62865766ff52Svetoslav Ganov if (GestureUtils.isMultiTap(mFirstTapEvent, event, mDoubleTapTimeout, 11141cf70bbf96930662cab0e699d70b62865766ff52Svetoslav Ganov mDoubleTapSlop, actionIndex)) { 1115e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov onDoubleTap(event, policyFlags); 1116e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov mFirstTapEvent.recycle(); 1117e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov mFirstTapEvent = null; 1118e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov mDownEvent.recycle(); 1119e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov mDownEvent = null; 1120e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov return; 1121e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov } 1122e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov mFirstTapEvent.recycle(); 1123e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov mFirstTapEvent = null; 1124e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov } else { 1125e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov if (mFirstTapEvent != null) { 1126e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov mFirstTapEvent.recycle(); 1127e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov mFirstTapEvent = null; 1128e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov } 1129e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov } 1130e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov mDownEvent.recycle(); 1131e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov mDownEvent = null; 1132e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov } break; 1133e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov } 1134e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov } 1135e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov 1136e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov public void onDoubleTap(MotionEvent secondTapUp, int policyFlags) { 1137e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov // This should never be called when more than two pointers are down. 1138e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov if (secondTapUp.getPointerCount() > 2) { 1139e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov return; 1140e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov } 1141e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov 1142e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov // Remove pending event deliveries. 114384044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav mSendHoverEnterAndMoveDelayed.cancel(); 114484044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav mSendHoverExitDelayed.cancel(); 114584044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav mPerformLongPressDelayed.cancel(); 1146e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov 1147f068fed6c4c3fc2003aec19b6e7e892358179b02Svetoslav Ganov if (mSendTouchExplorationEndDelayed.isPending()) { 1148f068fed6c4c3fc2003aec19b6e7e892358179b02Svetoslav Ganov mSendTouchExplorationEndDelayed.forceSendAndRemove(); 1149f068fed6c4c3fc2003aec19b6e7e892358179b02Svetoslav Ganov } 1150f068fed6c4c3fc2003aec19b6e7e892358179b02Svetoslav Ganov if (mSendTouchInteractionEndDelayed.isPending()) { 1151f068fed6c4c3fc2003aec19b6e7e892358179b02Svetoslav Ganov mSendTouchInteractionEndDelayed.forceSendAndRemove(); 1152f068fed6c4c3fc2003aec19b6e7e892358179b02Svetoslav Ganov } 1153e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov 115486783474fdec98a22bc22e224462767eab13e273Svetoslav Ganov int clickLocationX; 115586783474fdec98a22bc22e224462767eab13e273Svetoslav Ganov int clickLocationY; 1156e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov 1157e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov final int pointerId = secondTapUp.getPointerId(secondTapUp.getActionIndex()); 1158e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov final int pointerIndex = secondTapUp.findPointerIndex(pointerId); 115986783474fdec98a22bc22e224462767eab13e273Svetoslav Ganov 11605d043ce8cc2f588fdfb336cc843fb3b07b196f83Svetoslav Ganov MotionEvent lastExploreEvent = 11615d043ce8cc2f588fdfb336cc843fb3b07b196f83Svetoslav Ganov mInjectedPointerTracker.getLastInjectedHoverEventForClick(); 116286783474fdec98a22bc22e224462767eab13e273Svetoslav Ganov if (lastExploreEvent == null) { 116386783474fdec98a22bc22e224462767eab13e273Svetoslav Ganov // No last touch explored event but there is accessibility focus in 11647498efdc5e163d6b4a11db941c7d13c169d37284Svet Ganov // the active window. We click in the focus bounds. 11657498efdc5e163d6b4a11db941c7d13c169d37284Svet Ganov Point point = mTempPoint; 11667498efdc5e163d6b4a11db941c7d13c169d37284Svet Ganov if (mAms.getAccessibilityFocusClickPointInScreen(point)) { 11677498efdc5e163d6b4a11db941c7d13c169d37284Svet Ganov clickLocationX = point.x; 11687498efdc5e163d6b4a11db941c7d13c169d37284Svet Ganov clickLocationY = point.y; 116986783474fdec98a22bc22e224462767eab13e273Svetoslav Ganov } else { 117086783474fdec98a22bc22e224462767eab13e273Svetoslav Ganov // Out of luck - do nothing. 117186783474fdec98a22bc22e224462767eab13e273Svetoslav Ganov return; 117286783474fdec98a22bc22e224462767eab13e273Svetoslav Ganov } 117386783474fdec98a22bc22e224462767eab13e273Svetoslav Ganov } else { 117486783474fdec98a22bc22e224462767eab13e273Svetoslav Ganov // If the click is within the active window but not within the 11757498efdc5e163d6b4a11db941c7d13c169d37284Svet Ganov // accessibility focus bounds we click in the focus bounds. 117686783474fdec98a22bc22e224462767eab13e273Svetoslav Ganov final int lastExplorePointerIndex = lastExploreEvent.getActionIndex(); 117786783474fdec98a22bc22e224462767eab13e273Svetoslav Ganov clickLocationX = (int) lastExploreEvent.getX(lastExplorePointerIndex); 117886783474fdec98a22bc22e224462767eab13e273Svetoslav Ganov clickLocationY = (int) lastExploreEvent.getY(lastExplorePointerIndex); 117986783474fdec98a22bc22e224462767eab13e273Svetoslav Ganov Rect activeWindowBounds = mTempRect; 1180385d9f24b5ce2acb86c0dc192ce702718ab01c39Svetoslav Ganov if (mLastTouchedWindowId == mAms.getActiveWindowId()) { 1181385d9f24b5ce2acb86c0dc192ce702718ab01c39Svetoslav Ganov mAms.getActiveWindowBounds(activeWindowBounds); 1182385d9f24b5ce2acb86c0dc192ce702718ab01c39Svetoslav Ganov if (activeWindowBounds.contains(clickLocationX, clickLocationY)) { 11837498efdc5e163d6b4a11db941c7d13c169d37284Svet Ganov Point point = mTempPoint; 11847498efdc5e163d6b4a11db941c7d13c169d37284Svet Ganov if (mAms.getAccessibilityFocusClickPointInScreen(point)) { 11857498efdc5e163d6b4a11db941c7d13c169d37284Svet Ganov clickLocationX = point.x; 11867498efdc5e163d6b4a11db941c7d13c169d37284Svet Ganov clickLocationY = point.y; 118786783474fdec98a22bc22e224462767eab13e273Svetoslav Ganov } 118886783474fdec98a22bc22e224462767eab13e273Svetoslav Ganov } 1189e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov } 1190e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov } 1191e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov 1192e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov // Do the click. 1193e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov PointerProperties[] properties = new PointerProperties[1]; 1194e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov properties[0] = new PointerProperties(); 1195e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov secondTapUp.getPointerProperties(pointerIndex, properties[0]); 1196e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov PointerCoords[] coords = new PointerCoords[1]; 1197e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov coords[0] = new PointerCoords(); 119886783474fdec98a22bc22e224462767eab13e273Svetoslav Ganov coords[0].x = clickLocationX; 119986783474fdec98a22bc22e224462767eab13e273Svetoslav Ganov coords[0].y = clickLocationY; 1200e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov MotionEvent event = MotionEvent.obtain(secondTapUp.getDownTime(), 1201e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov secondTapUp.getEventTime(), MotionEvent.ACTION_DOWN, 1, properties, 1202e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov coords, 0, 0, 1.0f, 1.0f, secondTapUp.getDeviceId(), 0, 1203e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov secondTapUp.getSource(), secondTapUp.getFlags()); 1204e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov sendActionDownAndUp(event, policyFlags); 1205e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov event.recycle(); 1206e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov } 1207e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov 1208e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov public void clear() { 1209e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov if (mDownEvent != null) { 1210e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov mDownEvent.recycle(); 1211e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov mDownEvent = null; 1212e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov } 1213e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov if (mFirstTapEvent != null) { 1214e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov mFirstTapEvent.recycle(); 1215e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov mFirstTapEvent = null; 1216e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov } 1217e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov } 1218e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov 1219e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov public boolean firstTapDetected() { 1220e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov return mFirstTapEvent != null 1221e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov && SystemClock.uptimeMillis() - mFirstTapEvent.getEventTime() < mDoubleTapTimeout; 1222e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov } 1223e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov } 1224e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov 1225736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov /** 1226736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov * Determines whether a two pointer gesture is a dragging one. 1227736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov * 1228736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov * @param event The event with the pointer data. 1229736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov * @return True if the gesture is a dragging one. 1230736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov */ 1231736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov private boolean isDraggingGesture(MotionEvent event) { 12324213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov ReceivedPointerTracker receivedTracker = mReceivedPointerTracker; 1233736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov 123484044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav final float firstPtrX = event.getX(0); 123584044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav final float firstPtrY = event.getY(0); 123684044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav final float secondPtrX = event.getX(1); 123784044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav final float secondPtrY = event.getY(1); 1238736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov 123984044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav final float firstPtrDownX = receivedTracker.getReceivedPointerDownX(0); 124084044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav final float firstPtrDownY = receivedTracker.getReceivedPointerDownY(0); 124184044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav final float secondPtrDownX = receivedTracker.getReceivedPointerDownX(1); 124284044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav final float secondPtrDownY = receivedTracker.getReceivedPointerDownY(1); 1243736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov 12441cf70bbf96930662cab0e699d70b62865766ff52Svetoslav Ganov return GestureUtils.isDraggingGesture(firstPtrDownX, firstPtrDownY, secondPtrDownX, 12451cf70bbf96930662cab0e699d70b62865766ff52Svetoslav Ganov secondPtrDownY, firstPtrX, firstPtrY, secondPtrX, secondPtrY, 12461cf70bbf96930662cab0e699d70b62865766ff52Svetoslav Ganov MAX_DRAGGING_ANGLE_COS); 1247736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov } 1248736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov 1249736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov /** 125051cccf0845b36539d42503495f0689d487712b3aSvetoslav Ganov * Gets the symbolic name of a state. 125151cccf0845b36539d42503495f0689d487712b3aSvetoslav Ganov * 125251cccf0845b36539d42503495f0689d487712b3aSvetoslav Ganov * @param state A state. 125351cccf0845b36539d42503495f0689d487712b3aSvetoslav Ganov * @return The state symbolic name. 125451cccf0845b36539d42503495f0689d487712b3aSvetoslav Ganov */ 125551cccf0845b36539d42503495f0689d487712b3aSvetoslav Ganov private static String getStateSymbolicName(int state) { 125651cccf0845b36539d42503495f0689d487712b3aSvetoslav Ganov switch (state) { 125751cccf0845b36539d42503495f0689d487712b3aSvetoslav Ganov case STATE_TOUCH_EXPLORING: 125851cccf0845b36539d42503495f0689d487712b3aSvetoslav Ganov return "STATE_TOUCH_EXPLORING"; 125951cccf0845b36539d42503495f0689d487712b3aSvetoslav Ganov case STATE_DRAGGING: 126051cccf0845b36539d42503495f0689d487712b3aSvetoslav Ganov return "STATE_DRAGGING"; 126151cccf0845b36539d42503495f0689d487712b3aSvetoslav Ganov case STATE_DELEGATING: 126251cccf0845b36539d42503495f0689d487712b3aSvetoslav Ganov return "STATE_DELEGATING"; 12634213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov case STATE_GESTURE_DETECTING: 12644213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov return "STATE_GESTURE_DETECTING"; 126551cccf0845b36539d42503495f0689d487712b3aSvetoslav Ganov default: 126651cccf0845b36539d42503495f0689d487712b3aSvetoslav Ganov throw new IllegalArgumentException("Unknown state: " + state); 126751cccf0845b36539d42503495f0689d487712b3aSvetoslav Ganov } 126851cccf0845b36539d42503495f0689d487712b3aSvetoslav Ganov } 126951cccf0845b36539d42503495f0689d487712b3aSvetoslav Ganov 127051cccf0845b36539d42503495f0689d487712b3aSvetoslav Ganov /** 127195068e5d1bea47091e97955f271c789264994550Svetoslav Ganov * Class for delayed exiting from gesture detecting mode. 127295068e5d1bea47091e97955f271c789264994550Svetoslav Ganov */ 127395068e5d1bea47091e97955f271c789264994550Svetoslav Ganov private final class ExitGestureDetectionModeDelayed implements Runnable { 127495068e5d1bea47091e97955f271c789264994550Svetoslav Ganov 127595068e5d1bea47091e97955f271c789264994550Svetoslav Ganov public void post() { 127695068e5d1bea47091e97955f271c789264994550Svetoslav Ganov mHandler.postDelayed(this, EXIT_GESTURE_DETECTION_TIMEOUT); 127795068e5d1bea47091e97955f271c789264994550Svetoslav Ganov } 127895068e5d1bea47091e97955f271c789264994550Svetoslav Ganov 127984044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav public void cancel() { 128095068e5d1bea47091e97955f271c789264994550Svetoslav Ganov mHandler.removeCallbacks(this); 128195068e5d1bea47091e97955f271c789264994550Svetoslav Ganov } 128295068e5d1bea47091e97955f271c789264994550Svetoslav Ganov 128395068e5d1bea47091e97955f271c789264994550Svetoslav Ganov @Override 128495068e5d1bea47091e97955f271c789264994550Svetoslav Ganov public void run() { 1285aed4b6f812674bc60a04470013ca449e5c114fa5Svetoslav Ganov // Announce the end of gesture recognition. 1286aed4b6f812674bc60a04470013ca449e5c114fa5Svetoslav Ganov sendAccessibilityEvent(AccessibilityEvent.TYPE_GESTURE_DETECTION_END); 1287aed4b6f812674bc60a04470013ca449e5c114fa5Svetoslav Ganov // Clearing puts is in touch exploration state with a finger already 1288aed4b6f812674bc60a04470013ca449e5c114fa5Svetoslav Ganov // down, so announce the transition to exploration state. 1289aed4b6f812674bc60a04470013ca449e5c114fa5Svetoslav Ganov sendAccessibilityEvent(AccessibilityEvent.TYPE_TOUCH_EXPLORATION_GESTURE_START); 129095068e5d1bea47091e97955f271c789264994550Svetoslav Ganov clear(); 129195068e5d1bea47091e97955f271c789264994550Svetoslav Ganov } 129295068e5d1bea47091e97955f271c789264994550Svetoslav Ganov } 129395068e5d1bea47091e97955f271c789264994550Svetoslav Ganov 129495068e5d1bea47091e97955f271c789264994550Svetoslav Ganov /** 12954213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov * Class for delayed sending of long press. 12964213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov */ 12974213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov private final class PerformLongPressDelayed implements Runnable { 12984213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov private MotionEvent mEvent; 12994213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov private int mPolicyFlags; 13004213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov 1301e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov public void post(MotionEvent prototype, int policyFlags) { 13024213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov mEvent = MotionEvent.obtain(prototype); 13034213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov mPolicyFlags = policyFlags; 1304e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov mHandler.postDelayed(this, ViewConfiguration.getLongPressTimeout()); 13054213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov } 13064213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov 130784044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav public void cancel() { 130884044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav if (mEvent != null) { 13094213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov mHandler.removeCallbacks(this); 13104213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov clear(); 13114213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov } 13124213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov } 13134213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov 131484044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav private boolean isPending() { 131584044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav return mHandler.hasCallbacks(this); 13164213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov } 13174213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov 13184213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov @Override 13194213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov public void run() { 132084044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav // Pointers should not be zero when running this command. 132184044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav if (mReceivedPointerTracker.getLastReceivedEvent().getPointerCount() == 0) { 1322ebac1b79c4a355d8cd73b49df059deb00d7aa256Svetoslav Ganov return; 1323ebac1b79c4a355d8cd73b49df059deb00d7aa256Svetoslav Ganov } 1324ebac1b79c4a355d8cd73b49df059deb00d7aa256Svetoslav Ganov 132586783474fdec98a22bc22e224462767eab13e273Svetoslav Ganov int clickLocationX; 132686783474fdec98a22bc22e224462767eab13e273Svetoslav Ganov int clickLocationY; 1327238099c0dbbdc66b8443552126680ad1c7cab17dSvetoslav Ganov 1328238099c0dbbdc66b8443552126680ad1c7cab17dSvetoslav Ganov final int pointerId = mEvent.getPointerId(mEvent.getActionIndex()); 1329238099c0dbbdc66b8443552126680ad1c7cab17dSvetoslav Ganov final int pointerIndex = mEvent.findPointerIndex(pointerId); 1330238099c0dbbdc66b8443552126680ad1c7cab17dSvetoslav Ganov 13315d043ce8cc2f588fdfb336cc843fb3b07b196f83Svetoslav Ganov MotionEvent lastExploreEvent = 13325d043ce8cc2f588fdfb336cc843fb3b07b196f83Svetoslav Ganov mInjectedPointerTracker.getLastInjectedHoverEventForClick(); 133386783474fdec98a22bc22e224462767eab13e273Svetoslav Ganov if (lastExploreEvent == null) { 133486783474fdec98a22bc22e224462767eab13e273Svetoslav Ganov // No last touch explored event but there is accessibility focus in 13357498efdc5e163d6b4a11db941c7d13c169d37284Svet Ganov // the active window. We click in the focus bounds. 13367498efdc5e163d6b4a11db941c7d13c169d37284Svet Ganov Point point = mTempPoint; 13377498efdc5e163d6b4a11db941c7d13c169d37284Svet Ganov if (mAms.getAccessibilityFocusClickPointInScreen(point)) { 13387498efdc5e163d6b4a11db941c7d13c169d37284Svet Ganov clickLocationX = point.x; 13397498efdc5e163d6b4a11db941c7d13c169d37284Svet Ganov clickLocationY = point.y; 134086783474fdec98a22bc22e224462767eab13e273Svetoslav Ganov } else { 134186783474fdec98a22bc22e224462767eab13e273Svetoslav Ganov // Out of luck - do nothing. 134286783474fdec98a22bc22e224462767eab13e273Svetoslav Ganov return; 134386783474fdec98a22bc22e224462767eab13e273Svetoslav Ganov } 1344e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov } else { 134586783474fdec98a22bc22e224462767eab13e273Svetoslav Ganov // If the click is within the active window but not within the 13467498efdc5e163d6b4a11db941c7d13c169d37284Svet Ganov // accessibility focus bounds we click in the focus bounds. 134786783474fdec98a22bc22e224462767eab13e273Svetoslav Ganov final int lastExplorePointerIndex = lastExploreEvent.getActionIndex(); 134886783474fdec98a22bc22e224462767eab13e273Svetoslav Ganov clickLocationX = (int) lastExploreEvent.getX(lastExplorePointerIndex); 134986783474fdec98a22bc22e224462767eab13e273Svetoslav Ganov clickLocationY = (int) lastExploreEvent.getY(lastExplorePointerIndex); 135086783474fdec98a22bc22e224462767eab13e273Svetoslav Ganov Rect activeWindowBounds = mTempRect; 1351385d9f24b5ce2acb86c0dc192ce702718ab01c39Svetoslav Ganov if (mLastTouchedWindowId == mAms.getActiveWindowId()) { 1352385d9f24b5ce2acb86c0dc192ce702718ab01c39Svetoslav Ganov mAms.getActiveWindowBounds(activeWindowBounds); 1353385d9f24b5ce2acb86c0dc192ce702718ab01c39Svetoslav Ganov if (activeWindowBounds.contains(clickLocationX, clickLocationY)) { 13547498efdc5e163d6b4a11db941c7d13c169d37284Svet Ganov Point point = mTempPoint; 13557498efdc5e163d6b4a11db941c7d13c169d37284Svet Ganov if (mAms.getAccessibilityFocusClickPointInScreen(point)) { 13567498efdc5e163d6b4a11db941c7d13c169d37284Svet Ganov clickLocationX = point.x; 13577498efdc5e163d6b4a11db941c7d13c169d37284Svet Ganov clickLocationY = point.y; 135886783474fdec98a22bc22e224462767eab13e273Svetoslav Ganov } 135986783474fdec98a22bc22e224462767eab13e273Svetoslav Ganov } 136086783474fdec98a22bc22e224462767eab13e273Svetoslav Ganov } 1361e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov } 1362238099c0dbbdc66b8443552126680ad1c7cab17dSvetoslav Ganov 136386783474fdec98a22bc22e224462767eab13e273Svetoslav Ganov mLongPressingPointerId = pointerId; 136486783474fdec98a22bc22e224462767eab13e273Svetoslav Ganov mLongPressingPointerDeltaX = (int) mEvent.getX(pointerIndex) - clickLocationX; 136586783474fdec98a22bc22e224462767eab13e273Svetoslav Ganov mLongPressingPointerDeltaY = (int) mEvent.getY(pointerIndex) - clickLocationY; 136686783474fdec98a22bc22e224462767eab13e273Svetoslav Ganov 1367f068fed6c4c3fc2003aec19b6e7e892358179b02Svetoslav Ganov sendHoverExitAndTouchExplorationGestureEndIfNeeded(mPolicyFlags); 13684213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov 1369e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov mCurrentState = STATE_DELEGATING; 137084044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav sendDownForAllNotInjectedPointers(mEvent, mPolicyFlags); 13714213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov clear(); 13724213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov } 13734213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov 13744213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov private void clear() { 13754213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov mEvent.recycle(); 13764213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov mEvent = null; 13774213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov mPolicyFlags = 0; 13784213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov } 13794213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov } 13804213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov 13814213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov /** 138284044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav * Class for delayed sending of hover enter and move events. 13834213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov */ 138484044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav class SendHoverEnterAndMoveDelayed implements Runnable { 138584044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav private final String LOG_TAG_SEND_HOVER_DELAYED = "SendHoverEnterAndMoveDelayed"; 1386e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov 138784044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav private final List<MotionEvent> mEvents = new ArrayList<MotionEvent>(); 1388e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov 13894213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov private int mPointerIdBits; 13904213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov private int mPolicyFlags; 13914213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov 139284044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav public void post(MotionEvent event, boolean touchExplorationInProgress, 139377276b60851a158ad3e142cb3b091d57ae5ceffbSvetoslav Ganov int pointerIdBits, int policyFlags) { 139484044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav cancel(); 139584044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav addEvent(event); 13964213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov mPointerIdBits = pointerIdBits; 13974213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov mPolicyFlags = policyFlags; 1398e47957a0bbe2164467ff6e7a566b0c9e4689cdc9Svetoslav Ganov mHandler.postDelayed(this, mDetermineUserIntentTimeout); 1399e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov } 1400e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov 140184044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav public void addEvent(MotionEvent event) { 140284044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav mEvents.add(MotionEvent.obtain(event)); 140384044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav } 140484044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav 140584044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav public void cancel() { 1406e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov if (isPending()) { 140784044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav mHandler.removeCallbacks(this); 140884044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav clear(); 140984044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav } 141084044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav } 141184044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav 141284044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav private boolean isPending() { 141384044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav return mHandler.hasCallbacks(this); 141484044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav } 141584044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav 141684044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav private void clear() { 141784044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav mPointerIdBits = -1; 141884044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav mPolicyFlags = 0; 141984044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav final int eventCount = mEvents.size(); 142084044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav for (int i = eventCount - 1; i >= 0; i--) { 142184044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav mEvents.remove(i).recycle(); 1422e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov } 1423e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov } 1424e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov 142584044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav public void forceSendAndRemove() { 1426e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov if (isPending()) { 142784044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav run(); 142884044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav cancel(); 1429e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov } 14304213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov } 14314213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov 143284044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav public void run() { 143384044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav // Send an accessibility event to announce the touch exploration start. 143484044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav sendAccessibilityEvent(AccessibilityEvent.TYPE_TOUCH_EXPLORATION_GESTURE_START); 143584044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav 143684044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav if (!mEvents.isEmpty()) { 143784044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav // Deliver a down event. 143884044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav sendMotionEvent(mEvents.get(0), MotionEvent.ACTION_HOVER_ENTER, 143984044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav mPointerIdBits, mPolicyFlags); 144084044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav if (DEBUG) { 144184044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav Slog.d(LOG_TAG_SEND_HOVER_DELAYED, 144284044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav "Injecting motion event: ACTION_HOVER_ENTER"); 144384044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav } 144484044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav 144584044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav // Deliver move events. 144684044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav final int eventCount = mEvents.size(); 144784044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav for (int i = 1; i < eventCount; i++) { 144884044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav sendMotionEvent(mEvents.get(i), MotionEvent.ACTION_HOVER_MOVE, 144984044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav mPointerIdBits, mPolicyFlags); 145084044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav if (DEBUG) { 145184044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav Slog.d(LOG_TAG_SEND_HOVER_DELAYED, 145284044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav "Injecting motion event: ACTION_HOVER_MOVE"); 145384044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav } 145484044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav } 145584044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav } 14564213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov clear(); 14574213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov } 145884044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav } 145984044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav 146084044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav /** 146184044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav * Class for delayed sending of hover exit events. 146284044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav */ 146384044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav class SendHoverExitDelayed implements Runnable { 146484044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav private final String LOG_TAG_SEND_HOVER_DELAYED = "SendHoverExitDelayed"; 146584044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav 146684044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav private MotionEvent mPrototype; 146784044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav private int mPointerIdBits; 146884044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav private int mPolicyFlags; 146984044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav 147084044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav public void post(MotionEvent prototype, int pointerIdBits, int policyFlags) { 147184044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav cancel(); 147284044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav mPrototype = MotionEvent.obtain(prototype); 147384044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav mPointerIdBits = pointerIdBits; 147484044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav mPolicyFlags = policyFlags; 147584044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav mHandler.postDelayed(this, mDetermineUserIntentTimeout); 147684044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav } 147784044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav 147884044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav public void cancel() { 147984044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav if (isPending()) { 148084044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav mHandler.removeCallbacks(this); 148184044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav clear(); 148284044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav } 148384044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav } 14844213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov 1485e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov private boolean isPending() { 148684044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav return mHandler.hasCallbacks(this); 14874213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov } 14884213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov 14894213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov private void clear() { 1490e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov mPrototype.recycle(); 1491e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov mPrototype = null; 14924213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov mPointerIdBits = -1; 14934213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov mPolicyFlags = 0; 14944213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov } 14954213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov 14964213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov public void forceSendAndRemove() { 1497e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov if (isPending()) { 14984213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov run(); 149984044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav cancel(); 15004213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov } 15014213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov } 15024213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov 15034213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov public void run() { 15044213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov if (DEBUG) { 150584044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav Slog.d(LOG_TAG_SEND_HOVER_DELAYED, "Injecting motion event:" 150684044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav + " ACTION_HOVER_EXIT"); 15074213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov } 150884044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav sendMotionEvent(mPrototype, MotionEvent.ACTION_HOVER_EXIT, 150984044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav mPointerIdBits, mPolicyFlags); 151084044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav if (!mSendTouchExplorationEndDelayed.isPending()) { 151184044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav mSendTouchExplorationEndDelayed.cancel(); 151284044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav mSendTouchExplorationEndDelayed.post(); 151384044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav } 151484044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav if (mSendTouchInteractionEndDelayed.isPending()) { 151584044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav mSendTouchInteractionEndDelayed.cancel(); 151684044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav mSendTouchInteractionEndDelayed.post(); 1517e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov } 15184213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov clear(); 15194213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov } 15204213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov } 15214213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov 1522f068fed6c4c3fc2003aec19b6e7e892358179b02Svetoslav Ganov private class SendAccessibilityEventDelayed implements Runnable { 1523f068fed6c4c3fc2003aec19b6e7e892358179b02Svetoslav Ganov private final int mEventType; 1524f068fed6c4c3fc2003aec19b6e7e892358179b02Svetoslav Ganov private final int mDelay; 1525f068fed6c4c3fc2003aec19b6e7e892358179b02Svetoslav Ganov 1526f068fed6c4c3fc2003aec19b6e7e892358179b02Svetoslav Ganov public SendAccessibilityEventDelayed(int eventType, int delay) { 1527f068fed6c4c3fc2003aec19b6e7e892358179b02Svetoslav Ganov mEventType = eventType; 1528f068fed6c4c3fc2003aec19b6e7e892358179b02Svetoslav Ganov mDelay = delay; 1529f068fed6c4c3fc2003aec19b6e7e892358179b02Svetoslav Ganov } 1530fe304b893968887323b93764caafa66ee8ad44deSvetoslav Ganov 153184044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav public void cancel() { 1532fe304b893968887323b93764caafa66ee8ad44deSvetoslav Ganov mHandler.removeCallbacks(this); 1533fe304b893968887323b93764caafa66ee8ad44deSvetoslav Ganov } 1534fe304b893968887323b93764caafa66ee8ad44deSvetoslav Ganov 1535fe304b893968887323b93764caafa66ee8ad44deSvetoslav Ganov public void post() { 1536f068fed6c4c3fc2003aec19b6e7e892358179b02Svetoslav Ganov mHandler.postDelayed(this, mDelay); 1537fe304b893968887323b93764caafa66ee8ad44deSvetoslav Ganov } 1538fe304b893968887323b93764caafa66ee8ad44deSvetoslav Ganov 1539fe304b893968887323b93764caafa66ee8ad44deSvetoslav Ganov public boolean isPending() { 1540fe304b893968887323b93764caafa66ee8ad44deSvetoslav Ganov return mHandler.hasCallbacks(this); 1541fe304b893968887323b93764caafa66ee8ad44deSvetoslav Ganov } 1542fe304b893968887323b93764caafa66ee8ad44deSvetoslav Ganov 1543fe304b893968887323b93764caafa66ee8ad44deSvetoslav Ganov public void forceSendAndRemove() { 1544fe304b893968887323b93764caafa66ee8ad44deSvetoslav Ganov if (isPending()) { 1545fe304b893968887323b93764caafa66ee8ad44deSvetoslav Ganov run(); 154684044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav cancel(); 1547fe304b893968887323b93764caafa66ee8ad44deSvetoslav Ganov } 1548fe304b893968887323b93764caafa66ee8ad44deSvetoslav Ganov } 1549fe304b893968887323b93764caafa66ee8ad44deSvetoslav Ganov 1550fe304b893968887323b93764caafa66ee8ad44deSvetoslav Ganov @Override 1551fe304b893968887323b93764caafa66ee8ad44deSvetoslav Ganov public void run() { 1552f068fed6c4c3fc2003aec19b6e7e892358179b02Svetoslav Ganov sendAccessibilityEvent(mEventType); 1553fe304b893968887323b93764caafa66ee8ad44deSvetoslav Ganov } 1554fe304b893968887323b93764caafa66ee8ad44deSvetoslav Ganov } 1555fe304b893968887323b93764caafa66ee8ad44deSvetoslav Ganov 15564213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov @Override 15574213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov public String toString() { 15584213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov return LOG_TAG; 15594213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov } 15604213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov 15614213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov class InjectedPointerTracker { 15624213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov private static final String LOG_TAG_INJECTED_POINTER_TRACKER = "InjectedPointerTracker"; 15634213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov 15644213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov // Keep track of which pointers sent to the system are down. 15654213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov private int mInjectedPointersDown; 15664213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov 15674213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov // The time of the last injected down. 15684213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov private long mLastInjectedDownEventTime; 15694213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov 1570e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov // The last injected hover event. 1571e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov private MotionEvent mLastInjectedHoverEvent; 15724213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov 15735d043ce8cc2f588fdfb336cc843fb3b07b196f83Svetoslav Ganov // The last injected hover event used for performing clicks. 15745d043ce8cc2f588fdfb336cc843fb3b07b196f83Svetoslav Ganov private MotionEvent mLastInjectedHoverEventForClick; 15755d043ce8cc2f588fdfb336cc843fb3b07b196f83Svetoslav Ganov 15764213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov /** 15774213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov * Processes an injected {@link MotionEvent} event. 15784213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov * 15794213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov * @param event The event to process. 15804213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov */ 15814213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov public void onMotionEvent(MotionEvent event) { 15824213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov final int action = event.getActionMasked(); 15834213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov switch (action) { 15844213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov case MotionEvent.ACTION_DOWN: 15854213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov case MotionEvent.ACTION_POINTER_DOWN: { 15864213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov final int pointerId = event.getPointerId(event.getActionIndex()); 15874213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov final int pointerFlag = (1 << pointerId); 15884213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov mInjectedPointersDown |= pointerFlag; 15894213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov mLastInjectedDownEventTime = event.getDownTime(); 15904213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov } break; 15914213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov case MotionEvent.ACTION_UP: 15924213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov case MotionEvent.ACTION_POINTER_UP: { 15934213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov final int pointerId = event.getPointerId(event.getActionIndex()); 15944213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov final int pointerFlag = (1 << pointerId); 15954213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov mInjectedPointersDown &= ~pointerFlag; 15964213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov if (mInjectedPointersDown == 0) { 15974213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov mLastInjectedDownEventTime = 0; 15984213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov } 15994213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov } break; 16004213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov case MotionEvent.ACTION_HOVER_ENTER: 16014213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov case MotionEvent.ACTION_HOVER_MOVE: 16024213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov case MotionEvent.ACTION_HOVER_EXIT: { 1603e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov if (mLastInjectedHoverEvent != null) { 1604e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov mLastInjectedHoverEvent.recycle(); 1605e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov } 1606e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov mLastInjectedHoverEvent = MotionEvent.obtain(event); 16075d043ce8cc2f588fdfb336cc843fb3b07b196f83Svetoslav Ganov if (mLastInjectedHoverEventForClick != null) { 16085d043ce8cc2f588fdfb336cc843fb3b07b196f83Svetoslav Ganov mLastInjectedHoverEventForClick.recycle(); 16095d043ce8cc2f588fdfb336cc843fb3b07b196f83Svetoslav Ganov } 16105d043ce8cc2f588fdfb336cc843fb3b07b196f83Svetoslav Ganov mLastInjectedHoverEventForClick = MotionEvent.obtain(event); 16114213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov } break; 16124213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov } 16134213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov if (DEBUG) { 1614e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov Slog.i(LOG_TAG_INJECTED_POINTER_TRACKER, "Injected pointer:\n" + toString()); 16154213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov } 16164213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov } 16174213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov 16184213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov /** 16194213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov * Clears the internals state. 16204213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov */ 16214213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov public void clear() { 16224213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov mInjectedPointersDown = 0; 16234213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov } 16244213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov 16254213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov /** 16264213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov * @return The time of the last injected down event. 16274213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov */ 16284213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov public long getLastInjectedDownEventTime() { 16294213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov return mLastInjectedDownEventTime; 16304213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov } 16314213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov 16324213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov /** 16334213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov * @return The number of down pointers injected to the view hierarchy. 16344213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov */ 16354213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov public int getInjectedPointerDownCount() { 16364213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov return Integer.bitCount(mInjectedPointersDown); 16374213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov } 16384213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov 16394213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov /** 16404213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov * @return The bits of the injected pointers that are down. 16414213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov */ 16424213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov public int getInjectedPointersDown() { 16434213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov return mInjectedPointersDown; 16444213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov } 16454213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov 16464213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov /** 16474213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov * Whether an injected pointer is down. 16484213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov * 16494213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov * @param pointerId The unique pointer id. 16504213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov * @return True if the pointer is down. 16514213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov */ 16524213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov public boolean isInjectedPointerDown(int pointerId) { 16534213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov final int pointerFlag = (1 << pointerId); 16544213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov return (mInjectedPointersDown & pointerFlag) != 0; 16554213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov } 16564213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov 16574213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov /** 1658e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov * @return The the last injected hover event. 16594213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov */ 1660e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov public MotionEvent getLastInjectedHoverEvent() { 1661e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov return mLastInjectedHoverEvent; 16624213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov } 16634213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov 16645d043ce8cc2f588fdfb336cc843fb3b07b196f83Svetoslav Ganov /** 16655d043ce8cc2f588fdfb336cc843fb3b07b196f83Svetoslav Ganov * @return The the last injected hover event. 16665d043ce8cc2f588fdfb336cc843fb3b07b196f83Svetoslav Ganov */ 16675d043ce8cc2f588fdfb336cc843fb3b07b196f83Svetoslav Ganov public MotionEvent getLastInjectedHoverEventForClick() { 16685d043ce8cc2f588fdfb336cc843fb3b07b196f83Svetoslav Ganov return mLastInjectedHoverEventForClick; 16695d043ce8cc2f588fdfb336cc843fb3b07b196f83Svetoslav Ganov } 16705d043ce8cc2f588fdfb336cc843fb3b07b196f83Svetoslav Ganov 16714213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov @Override 16724213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov public String toString() { 16734213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov StringBuilder builder = new StringBuilder(); 16744213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov builder.append("========================="); 16754213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov builder.append("\nDown pointers #"); 16764213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov builder.append(Integer.bitCount(mInjectedPointersDown)); 16774213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov builder.append(" [ "); 16784213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov for (int i = 0; i < MAX_POINTER_COUNT; i++) { 16794213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov if ((mInjectedPointersDown & i) != 0) { 16804213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov builder.append(i); 16814213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov builder.append(" "); 16824213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov } 16834213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov } 16844213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov builder.append("]"); 16854213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov builder.append("\n========================="); 16864213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov return builder.toString(); 16874213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov } 16884213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov } 16894213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov 16904213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov class ReceivedPointerTracker { 16914213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov private static final String LOG_TAG_RECEIVED_POINTER_TRACKER = "ReceivedPointerTracker"; 1692736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov 1693736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov // Keep track of where and when a pointer went down. 1694736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov private final float[] mReceivedPointerDownX = new float[MAX_POINTER_COUNT]; 1695736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov private final float[] mReceivedPointerDownY = new float[MAX_POINTER_COUNT]; 1696736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov private final long[] mReceivedPointerDownTime = new long[MAX_POINTER_COUNT]; 1697736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov 1698736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov // Which pointers are down. 1699736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov private int mReceivedPointersDown; 1700736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov 1701c4842c11932ea4f60fe7ae09b0a59660207e1587Svetoslav Ganov // The edge flags of the last received down event. 1702c4842c11932ea4f60fe7ae09b0a59660207e1587Svetoslav Ganov private int mLastReceivedDownEdgeFlags; 1703c4842c11932ea4f60fe7ae09b0a59660207e1587Svetoslav Ganov 170484044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav // Primary pointer which is either the first that went down 170584044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav // or if it goes up the next one that most recently went down. 170684044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav private int mPrimaryPointerId; 1707736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov 1708736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov // Keep track of the last up pointer data. 1709736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov private long mLastReceivedUpPointerDownTime; 17104213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov private float mLastReceivedUpPointerDownX; 17114213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov private float mLastReceivedUpPointerDownY; 1712736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov 1713e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov private MotionEvent mLastReceivedEvent; 1714e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov 1715736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov /** 1716736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov * Clears the internals state. 1717736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov */ 1718736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov public void clear() { 1719736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov Arrays.fill(mReceivedPointerDownX, 0); 1720736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov Arrays.fill(mReceivedPointerDownY, 0); 1721736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov Arrays.fill(mReceivedPointerDownTime, 0); 1722736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov mReceivedPointersDown = 0; 172384044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav mPrimaryPointerId = 0; 1724736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov mLastReceivedUpPointerDownTime = 0; 17254213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov mLastReceivedUpPointerDownX = 0; 17264213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov mLastReceivedUpPointerDownY = 0; 1727736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov } 1728736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov 1729736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov /** 1730736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov * Processes a received {@link MotionEvent} event. 1731736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov * 1732736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov * @param event The event to process. 1733736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov */ 17344213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov public void onMotionEvent(MotionEvent event) { 1735e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov if (mLastReceivedEvent != null) { 1736e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov mLastReceivedEvent.recycle(); 1737e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov } 1738e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov mLastReceivedEvent = MotionEvent.obtain(event); 1739e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov 1740736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov final int action = event.getActionMasked(); 1741736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov switch (action) { 1742736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov case MotionEvent.ACTION_DOWN: { 174300f7b3f76515d1c6fbe5cf9fee9d3760787c03cdSvetoslav Ganov handleReceivedPointerDown(event.getActionIndex(), event); 1744736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov } break; 1745736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov case MotionEvent.ACTION_POINTER_DOWN: { 1746736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov handleReceivedPointerDown(event.getActionIndex(), event); 1747736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov } break; 1748736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov case MotionEvent.ACTION_UP: { 174900f7b3f76515d1c6fbe5cf9fee9d3760787c03cdSvetoslav Ganov handleReceivedPointerUp(event.getActionIndex(), event); 1750736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov } break; 1751736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov case MotionEvent.ACTION_POINTER_UP: { 1752736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov handleReceivedPointerUp(event.getActionIndex(), event); 1753736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov } break; 1754736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov } 1755736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov if (DEBUG) { 175638c992841b5f6bc80359dbf60d31aa7b994160fcSvetoslav Ganov Slog.i(LOG_TAG_RECEIVED_POINTER_TRACKER, "Received pointer:\n" + toString()); 1757736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov } 1758736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov } 1759736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov 1760736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov /** 1761e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov * @return The last received event. 1762e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov */ 1763e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov public MotionEvent getLastReceivedEvent() { 1764e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov return mLastReceivedEvent; 1765e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov } 1766e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov 1767e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov /** 17684213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov * @return The number of received pointers that are down. 1769736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov */ 17704213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov public int getReceivedPointerDownCount() { 17714213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov return Integer.bitCount(mReceivedPointersDown); 1772736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov } 1773736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov 1774736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov /** 1775736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov * Whether an received pointer is down. 1776736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov * 1777736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov * @param pointerId The unique pointer id. 1778736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov * @return True if the pointer is down. 1779736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov */ 1780736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov public boolean isReceivedPointerDown(int pointerId) { 1781736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov final int pointerFlag = (1 << pointerId); 1782736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov return (mReceivedPointersDown & pointerFlag) != 0; 1783736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov } 1784736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov 1785736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov /** 1786736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov * @param pointerId The unique pointer id. 1787736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov * @return The X coordinate where the pointer went down. 1788736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov */ 1789736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov public float getReceivedPointerDownX(int pointerId) { 1790736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov return mReceivedPointerDownX[pointerId]; 1791736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov } 1792736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov 1793736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov /** 1794736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov * @param pointerId The unique pointer id. 1795736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov * @return The Y coordinate where the pointer went down. 1796736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov */ 1797736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov public float getReceivedPointerDownY(int pointerId) { 1798736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov return mReceivedPointerDownY[pointerId]; 1799736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov } 1800736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov 1801736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov /** 1802736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov * @param pointerId The unique pointer id. 1803736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov * @return The time when the pointer went down. 1804736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov */ 1805736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov public long getReceivedPointerDownTime(int pointerId) { 1806736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov return mReceivedPointerDownTime[pointerId]; 1807736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov } 1808736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov 1809736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov /** 1810736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov * @return The id of the primary pointer. 1811736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov */ 181284044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav public int getPrimaryPointerId() { 181384044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav if (mPrimaryPointerId == INVALID_POINTER_ID) { 181438c992841b5f6bc80359dbf60d31aa7b994160fcSvetoslav Ganov mPrimaryPointerId = findPrimaryPointerId(); 1815736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov } 181684044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav return mPrimaryPointerId; 1817736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov } 1818736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov 1819736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov /** 1820736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov * @return The time when the last up received pointer went down. 1821736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov */ 1822736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov public long getLastReceivedUpPointerDownTime() { 1823736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov return mLastReceivedUpPointerDownTime; 1824736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov } 1825736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov 1826736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov /** 18274213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov * @return The down X of the last received pointer that went up. 1828736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov */ 18294213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov public float getLastReceivedUpPointerDownX() { 18304213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov return mLastReceivedUpPointerDownX; 1831736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov } 1832736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov 1833736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov /** 18344213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov * @return The down Y of the last received pointer that went up. 1835736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov */ 18364213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov public float getLastReceivedUpPointerDownY() { 18374213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov return mLastReceivedUpPointerDownY; 1838736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov } 1839736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov 1840736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov /** 1841c4842c11932ea4f60fe7ae09b0a59660207e1587Svetoslav Ganov * @return The edge flags of the last received down event. 1842c4842c11932ea4f60fe7ae09b0a59660207e1587Svetoslav Ganov */ 1843c4842c11932ea4f60fe7ae09b0a59660207e1587Svetoslav Ganov public int getLastReceivedDownEdgeFlags() { 1844c4842c11932ea4f60fe7ae09b0a59660207e1587Svetoslav Ganov return mLastReceivedDownEdgeFlags; 1845c4842c11932ea4f60fe7ae09b0a59660207e1587Svetoslav Ganov } 1846c4842c11932ea4f60fe7ae09b0a59660207e1587Svetoslav Ganov 1847c4842c11932ea4f60fe7ae09b0a59660207e1587Svetoslav Ganov /** 1848736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov * Handles a received pointer down event. 1849736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov * 1850736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov * @param pointerIndex The index of the pointer that has changed. 1851736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov * @param event The event to be handled. 1852736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov */ 1853736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov private void handleReceivedPointerDown(int pointerIndex, MotionEvent event) { 1854736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov final int pointerId = event.getPointerId(pointerIndex); 1855736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov final int pointerFlag = (1 << pointerId); 1856736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov 1857736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov mLastReceivedUpPointerDownTime = 0; 18584213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov mLastReceivedUpPointerDownX = 0; 18594213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov mLastReceivedUpPointerDownX = 0; 1860736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov 1861c4842c11932ea4f60fe7ae09b0a59660207e1587Svetoslav Ganov mLastReceivedDownEdgeFlags = event.getEdgeFlags(); 1862c4842c11932ea4f60fe7ae09b0a59660207e1587Svetoslav Ganov 1863736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov mReceivedPointersDown |= pointerFlag; 1864736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov mReceivedPointerDownX[pointerId] = event.getX(pointerIndex); 1865736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov mReceivedPointerDownY[pointerId] = event.getY(pointerIndex); 1866736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov mReceivedPointerDownTime[pointerId] = event.getEventTime(); 1867736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov 186884044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav mPrimaryPointerId = pointerId; 1869736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov } 1870736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov 1871736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov /** 1872736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov * Handles a received pointer up event. 1873736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov * 1874736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov * @param pointerIndex The index of the pointer that has changed. 1875736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov * @param event The event to be handled. 1876736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov */ 1877736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov private void handleReceivedPointerUp(int pointerIndex, MotionEvent event) { 1878736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov final int pointerId = event.getPointerId(pointerIndex); 1879736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov final int pointerFlag = (1 << pointerId); 1880736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov 1881736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov mLastReceivedUpPointerDownTime = getReceivedPointerDownTime(pointerId); 18824213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov mLastReceivedUpPointerDownX = mReceivedPointerDownX[pointerId]; 18834213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov mLastReceivedUpPointerDownY = mReceivedPointerDownY[pointerId]; 1884736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov 1885736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov mReceivedPointersDown &= ~pointerFlag; 1886736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov mReceivedPointerDownX[pointerId] = 0; 1887736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov mReceivedPointerDownY[pointerId] = 0; 1888736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov mReceivedPointerDownTime[pointerId] = 0; 1889736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov 189084044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav if (mPrimaryPointerId == pointerId) { 189184044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav mPrimaryPointerId = INVALID_POINTER_ID; 1892736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov } 1893736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov } 1894736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov 1895736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov /** 189638c992841b5f6bc80359dbf60d31aa7b994160fcSvetoslav Ganov * @return The primary pointer id. 1897736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov */ 189838c992841b5f6bc80359dbf60d31aa7b994160fcSvetoslav Ganov private int findPrimaryPointerId() { 189984044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav int primaryPointerId = INVALID_POINTER_ID; 1900736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov long minDownTime = Long.MAX_VALUE; 190138c992841b5f6bc80359dbf60d31aa7b994160fcSvetoslav Ganov 190284044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav // Find the pointer that went down first. 190338c992841b5f6bc80359dbf60d31aa7b994160fcSvetoslav Ganov int pointerIdBits = mReceivedPointersDown; 190438c992841b5f6bc80359dbf60d31aa7b994160fcSvetoslav Ganov while (pointerIdBits > 0) { 190538c992841b5f6bc80359dbf60d31aa7b994160fcSvetoslav Ganov final int pointerId = Integer.numberOfTrailingZeros(pointerIdBits); 190638c992841b5f6bc80359dbf60d31aa7b994160fcSvetoslav Ganov pointerIdBits &= ~(1 << pointerId); 190738c992841b5f6bc80359dbf60d31aa7b994160fcSvetoslav Ganov final long downPointerTime = mReceivedPointerDownTime[pointerId]; 190884044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav if (downPointerTime < minDownTime) { 190984044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav minDownTime = downPointerTime; 191038c992841b5f6bc80359dbf60d31aa7b994160fcSvetoslav Ganov primaryPointerId = pointerId; 1911736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov } 1912736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov } 191384044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav return primaryPointerId; 1914736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov } 1915736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov 1916736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov @Override 1917736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov public String toString() { 1918736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov StringBuilder builder = new StringBuilder(); 1919736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov builder.append("========================="); 1920736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov builder.append("\nDown pointers #"); 1921736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov builder.append(getReceivedPointerDownCount()); 1922736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov builder.append(" [ "); 1923736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov for (int i = 0; i < MAX_POINTER_COUNT; i++) { 1924736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov if (isReceivedPointerDown(i)) { 1925736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov builder.append(i); 1926736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov builder.append(" "); 1927736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov } 1928736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov } 1929736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov builder.append("]"); 193084044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav builder.append("\nPrimary pointer id [ "); 193184044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav builder.append(getPrimaryPointerId()); 1932736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov builder.append(" ]"); 1933736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov builder.append("\n========================="); 1934736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov return builder.toString(); 1935736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov } 1936736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov } 1937736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov} 1938