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; 207498efdc5e163d6b4a11db941c7d13c169d37284Svet Ganovimport android.graphics.Point; 21736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganovimport android.os.Handler; 22736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganovimport android.util.Slog; 23976724e81bca33dc48347f88633a37a195b9e4eaToni Barzicimport android.view.InputDevice; 24976724e81bca33dc48347f88633a37a195b9e4eaToni Barzicimport android.view.KeyEvent; 25736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganovimport android.view.MotionEvent; 26e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganovimport android.view.MotionEvent.PointerCoords; 27e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganovimport android.view.MotionEvent.PointerProperties; 28f5a07905a3e025f95472a3f8d9935263e49ad6d3Svetoslav Ganovimport android.view.ViewConfiguration; 29f5a07905a3e025f95472a3f8d9935263e49ad6d3Svetoslav Ganovimport android.view.WindowManagerPolicy; 3086783474fdec98a22bc22e224462767eab13e273Svetoslav Ganovimport android.view.accessibility.AccessibilityEvent; 3177276b60851a158ad3e142cb3b091d57ae5ceffbSvetoslav Ganovimport android.view.accessibility.AccessibilityManager; 32736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov 334213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganovimport java.util.ArrayList; 34736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganovimport java.util.Arrays; 3584044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslavimport java.util.List; 36736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov 37736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov/** 38736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov * This class is a strategy for performing touch exploration. It 39736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov * transforms the motion event stream by modifying, adding, replacing, 40736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov * and consuming certain events. The interaction model is: 41736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov * 42736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov * <ol> 43e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov * <li>1. One finger moving slow around performs touch exploration.</li> 44e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov * <li>2. One finger moving fast around performs gestures.</li> 45e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov * <li>3. Two close fingers moving in the same direction perform a drag.</li> 46e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov * <li>4. Multi-finger gestures are delivered to view hierarchy.</li> 4784044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav * <li>5. Two fingers moving in different directions are considered a multi-finger gesture.</li> 4884044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav * <li>7. Double tapping clicks on the on the last touch explored location if it was in 49e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov * a window that does not take focus, otherwise the click is within the accessibility 50e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov * focused rectangle.</li> 51e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov * <li>7. Tapping and holding for a while performs a long press in a similar fashion 52e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov * as the click above.</li> 53736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov * <ol> 54736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov * 55736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov * @hide 56736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov */ 57c18f2fdfcf8ec7a8534c032c4102739edb7f1c5eZachary Kuzniaclass TouchExplorer implements EventStreamTransformation, AccessibilityGestureDetector.Listener { 584213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov 59736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov private static final boolean DEBUG = false; 60736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov 61736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov // Tag for logging received events. 624213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov private static final String LOG_TAG = "TouchExplorer"; 63736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov 64736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov // States this explorer can be in. 65736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov private static final int STATE_TOUCH_EXPLORING = 0x00000001; 66736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov private static final int STATE_DRAGGING = 0x00000002; 67736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov private static final int STATE_DELEGATING = 0x00000004; 684213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov private static final int STATE_GESTURE_DETECTING = 0x00000005; 69736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov 70ded133c446fa9d0d23e6bde19a66fb2ce3980491Svetoslav private static final int CLICK_LOCATION_NONE = 0; 71ded133c446fa9d0d23e6bde19a66fb2ce3980491Svetoslav private static final int CLICK_LOCATION_ACCESSIBILITY_FOCUS = 1; 72ded133c446fa9d0d23e6bde19a66fb2ce3980491Svetoslav private static final int CLICK_LOCATION_LAST_TOUCH_EXPLORED = 2; 73ded133c446fa9d0d23e6bde19a66fb2ce3980491Svetoslav 74a3315270e0502189a6fa4de20b9db2bf042cfd2aSvetoslav // The maximum of the cosine between the vectors of two moving 75736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov // pointers so they can be considered moving in the same direction. 7612a024ca681d877fe16b7e087356f7aff175a218Svetoslav Ganov private static final float MAX_DRAGGING_ANGLE_COS = 0.525321989f; // cos(pi/4) 77736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov 78f804420d6e37748b75478406e989c69303756980Svetoslav Ganov // Constant referring to the ids bits of all pointers. 79f804420d6e37748b75478406e989c69303756980Svetoslav Ganov private static final int ALL_POINTER_ID_BITS = 0xFFFFFFFF; 80736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov 814213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov // This constant captures the current implementation detail that 824213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov // pointer IDs are between 0 and 31 inclusive (subject to change). 834213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov // (See MAX_POINTER_ID in frameworks/base/include/ui/Input.h) 84e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov private static final int MAX_POINTER_COUNT = 32; 854213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov 864213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov // Invalid pointer ID. 87e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov private static final int INVALID_POINTER_ID = -1; 88e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov 89e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov // The minimal distance before we take the middle of the distance between 90e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov // the two dragging pointers as opposed to use the location of the primary one. 91e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov private static final int MIN_POINTER_DISTANCE_TO_USE_MIDDLE_LOCATION_DIP = 200; 924213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov 9395068e5d1bea47091e97955f271c789264994550Svetoslav Ganov // The timeout after which we are no longer trying to detect a gesture. 9495068e5d1bea47091e97955f271c789264994550Svetoslav Ganov private static final int EXIT_GESTURE_DETECTION_TIMEOUT = 2000; 9595068e5d1bea47091e97955f271c789264994550Svetoslav Ganov 96e47957a0bbe2164467ff6e7a566b0c9e4689cdc9Svetoslav Ganov // Timeout before trying to decide what the user is trying to do. 97e47957a0bbe2164467ff6e7a566b0c9e4689cdc9Svetoslav Ganov private final int mDetermineUserIntentTimeout; 98e47957a0bbe2164467ff6e7a566b0c9e4689cdc9Svetoslav Ganov 99e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov // Slop between the first and second tap to be a double tap. 100e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov private final int mDoubleTapSlop; 101736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov 102736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov // The current state of the touch explorer. 103736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov private int mCurrentState = STATE_TOUCH_EXPLORING; 104736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov 105736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov // The ID of the pointer used for dragging. 106736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov private int mDraggingPointerId; 107736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov 108736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov // Handler for performing asynchronous operations. 109736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov private final Handler mHandler; 110736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov 11184044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav // Command for delayed sending of a hover enter and move event. 11284044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav private final SendHoverEnterAndMoveDelayed mSendHoverEnterAndMoveDelayed; 113e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov 114e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov // Command for delayed sending of a hover exit event. 11584044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav private final SendHoverExitDelayed mSendHoverExitDelayed; 116736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov 117f068fed6c4c3fc2003aec19b6e7e892358179b02Svetoslav Ganov // Command for delayed sending of touch exploration end events. 118f068fed6c4c3fc2003aec19b6e7e892358179b02Svetoslav Ganov private final SendAccessibilityEventDelayed mSendTouchExplorationEndDelayed; 119f068fed6c4c3fc2003aec19b6e7e892358179b02Svetoslav Ganov 120f068fed6c4c3fc2003aec19b6e7e892358179b02Svetoslav Ganov // Command for delayed sending of touch interaction end events. 121f068fed6c4c3fc2003aec19b6e7e892358179b02Svetoslav Ganov private final SendAccessibilityEventDelayed mSendTouchInteractionEndDelayed; 122fe304b893968887323b93764caafa66ee8ad44deSvetoslav Ganov 12395068e5d1bea47091e97955f271c789264994550Svetoslav Ganov // Command for exiting gesture detection mode after a timeout. 12495068e5d1bea47091e97955f271c789264994550Svetoslav Ganov private final ExitGestureDetectionModeDelayed mExitGestureDetectionModeDelayed; 12595068e5d1bea47091e97955f271c789264994550Svetoslav Ganov 126c18f2fdfcf8ec7a8534c032c4102739edb7f1c5eZachary Kuznia // Helper to detect gestures. 127c18f2fdfcf8ec7a8534c032c4102739edb7f1c5eZachary Kuznia private final AccessibilityGestureDetector mGestureDetector; 128e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov 129e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov // The scaled minimal distance before we take the middle of the distance between 130e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov // the two dragging pointers as opposed to use the location of the primary one. 131e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov private final int mScaledMinPointerDistanceToUseMiddleLocation; 132e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov 1331cf70bbf96930662cab0e699d70b62865766ff52Svetoslav Ganov // The handler to which to delegate events. 1341cf70bbf96930662cab0e699d70b62865766ff52Svetoslav Ganov private EventStreamTransformation mNext; 1351cf70bbf96930662cab0e699d70b62865766ff52Svetoslav Ganov 136e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov // Helper class to track received pointers. 1374213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov private final ReceivedPointerTracker mReceivedPointerTracker; 1384213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov 139e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov // Helper class to track injected pointers. 1404213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov private final InjectedPointerTracker mInjectedPointerTracker; 1414213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov 142e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov // Handle to the accessibility manager service. 143e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov private final AccessibilityManagerService mAms; 1444213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov 1457498efdc5e163d6b4a11db941c7d13c169d37284Svet Ganov // Temporary point to avoid instantiation. 1467498efdc5e163d6b4a11db941c7d13c169d37284Svet Ganov private final Point mTempPoint = new Point(); 1477498efdc5e163d6b4a11db941c7d13c169d37284Svet Ganov 14877276b60851a158ad3e142cb3b091d57ae5ceffbSvetoslav Ganov // Context in which this explorer operates. 14977276b60851a158ad3e142cb3b091d57ae5ceffbSvetoslav Ganov private final Context mContext; 15077276b60851a158ad3e142cb3b091d57ae5ceffbSvetoslav Ganov 151e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov // The long pressing pointer id if coordinate remapping is needed. 152aeb8d0ed0d98d398a66a092c418f4f2bca8719e0Svetoslav Ganov private int mLongPressingPointerId = -1; 153e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov 154e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov // The long pressing pointer X if coordinate remapping is needed. 155e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov private int mLongPressingPointerDeltaX; 156e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov 157e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov // The long pressing pointer Y if coordinate remapping is needed. 158e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov private int mLongPressingPointerDeltaY; 1594213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov 16076716c5a180aa471c6973ca7aa03c7f2da677823Svetoslav Ganov // The id of the last touch explored window. 16176716c5a180aa471c6973ca7aa03c7f2da677823Svetoslav Ganov private int mLastTouchedWindowId; 16276716c5a180aa471c6973ca7aa03c7f2da677823Svetoslav Ganov 163f068fed6c4c3fc2003aec19b6e7e892358179b02Svetoslav Ganov // Whether touch exploration is in progress. 164f068fed6c4c3fc2003aec19b6e7e892358179b02Svetoslav Ganov private boolean mTouchExplorationInProgress; 16577276b60851a158ad3e142cb3b091d57ae5ceffbSvetoslav Ganov 166736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov /** 167736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov * Creates a new instance. 168736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov * 169736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov * @param inputFilter The input filter associated with this explorer. 170736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov * @param context A context handle for accessing resources. 171736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov */ 1721cf70bbf96930662cab0e699d70b62865766ff52Svetoslav Ganov public TouchExplorer(Context context, AccessibilityManagerService service) { 17377276b60851a158ad3e142cb3b091d57ae5ceffbSvetoslav Ganov mContext = context; 174e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov mAms = service; 17584044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav mReceivedPointerTracker = new ReceivedPointerTracker(); 1764213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov mInjectedPointerTracker = new InjectedPointerTracker(); 17777276b60851a158ad3e142cb3b091d57ae5ceffbSvetoslav Ganov mDetermineUserIntentTimeout = ViewConfiguration.getDoubleTapTimeout(); 178e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov mDoubleTapSlop = ViewConfiguration.get(context).getScaledDoubleTapSlop(); 179736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov mHandler = new Handler(context.getMainLooper()); 18095068e5d1bea47091e97955f271c789264994550Svetoslav Ganov mExitGestureDetectionModeDelayed = new ExitGestureDetectionModeDelayed(); 18184044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav mSendHoverEnterAndMoveDelayed = new SendHoverEnterAndMoveDelayed(); 18284044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav mSendHoverExitDelayed = new SendHoverExitDelayed(); 183f068fed6c4c3fc2003aec19b6e7e892358179b02Svetoslav Ganov mSendTouchExplorationEndDelayed = new SendAccessibilityEventDelayed( 184f068fed6c4c3fc2003aec19b6e7e892358179b02Svetoslav Ganov AccessibilityEvent.TYPE_TOUCH_EXPLORATION_GESTURE_END, 185f068fed6c4c3fc2003aec19b6e7e892358179b02Svetoslav Ganov mDetermineUserIntentTimeout); 186f068fed6c4c3fc2003aec19b6e7e892358179b02Svetoslav Ganov mSendTouchInteractionEndDelayed = new SendAccessibilityEventDelayed( 187f068fed6c4c3fc2003aec19b6e7e892358179b02Svetoslav Ganov AccessibilityEvent.TYPE_TOUCH_INTERACTION_END, 188f068fed6c4c3fc2003aec19b6e7e892358179b02Svetoslav Ganov mDetermineUserIntentTimeout); 189c18f2fdfcf8ec7a8534c032c4102739edb7f1c5eZachary Kuznia mGestureDetector = new AccessibilityGestureDetector(context, this); 190e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov final float density = context.getResources().getDisplayMetrics().density; 191e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov mScaledMinPointerDistanceToUseMiddleLocation = 192e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov (int) (MIN_POINTER_DISTANCE_TO_USE_MIDDLE_LOCATION_DIP * density); 193e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov } 194e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov 195976724e81bca33dc48347f88633a37a195b9e4eaToni Barzic @Override 196976724e81bca33dc48347f88633a37a195b9e4eaToni Barzic public void clearEvents(int inputSource) { 197976724e81bca33dc48347f88633a37a195b9e4eaToni Barzic if (inputSource == InputDevice.SOURCE_TOUCHSCREEN) { 198976724e81bca33dc48347f88633a37a195b9e4eaToni Barzic clear(); 199976724e81bca33dc48347f88633a37a195b9e4eaToni Barzic } 200976724e81bca33dc48347f88633a37a195b9e4eaToni Barzic if (mNext != null) { 201976724e81bca33dc48347f88633a37a195b9e4eaToni Barzic mNext.clearEvents(inputSource); 202976724e81bca33dc48347f88633a37a195b9e4eaToni Barzic } 203976724e81bca33dc48347f88633a37a195b9e4eaToni Barzic } 204976724e81bca33dc48347f88633a37a195b9e4eaToni Barzic 205976724e81bca33dc48347f88633a37a195b9e4eaToni Barzic @Override 206976724e81bca33dc48347f88633a37a195b9e4eaToni Barzic public void onDestroy() { 207976724e81bca33dc48347f88633a37a195b9e4eaToni Barzic clear(); 208976724e81bca33dc48347f88633a37a195b9e4eaToni Barzic } 209976724e81bca33dc48347f88633a37a195b9e4eaToni Barzic 210976724e81bca33dc48347f88633a37a195b9e4eaToni Barzic private void clear() { 211e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov // If we have not received an event then we are in initial 212e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov // state. Therefore, there is not need to clean anything. 213e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov MotionEvent event = mReceivedPointerTracker.getLastReceivedEvent(); 214e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov if (event != null) { 215e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov clear(mReceivedPointerTracker.getLastReceivedEvent(), WindowManagerPolicy.FLAG_TRUSTED); 216e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov } 217736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov } 218736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov 2191cf70bbf96930662cab0e699d70b62865766ff52Svetoslav Ganov private void clear(MotionEvent event, int policyFlags) { 220e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov switch (mCurrentState) { 221e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov case STATE_TOUCH_EXPLORING: { 222e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov // If a touch exploration gesture is in progress send events for its end. 223f068fed6c4c3fc2003aec19b6e7e892358179b02Svetoslav Ganov sendHoverExitAndTouchExplorationGestureEndIfNeeded(policyFlags); 224e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov } break; 225e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov case STATE_DRAGGING: { 226e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov mDraggingPointerId = INVALID_POINTER_ID; 227e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov // Send exit to all pointers that we have delivered. 228e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov sendUpForInjectedDownPointers(event, policyFlags); 229e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov } break; 230e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov case STATE_DELEGATING: { 231e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov // Send exit to all pointers that we have delivered. 232e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov sendUpForInjectedDownPointers(event, policyFlags); 233e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov } break; 234e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov case STATE_GESTURE_DETECTING: { 235407df712e2a381e0227eb780786128a50bfcf5f5Zachary Kuznia // No state specific cleanup required. 236e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov } break; 237e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov } 238e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov // Remove all pending callbacks. 23984044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav mSendHoverEnterAndMoveDelayed.cancel(); 24084044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav mSendHoverExitDelayed.cancel(); 24184044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav mExitGestureDetectionModeDelayed.cancel(); 24284044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav mSendTouchExplorationEndDelayed.cancel(); 24384044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav mSendTouchInteractionEndDelayed.cancel(); 244e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov // Reset the pointer trackers. 245e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov mReceivedPointerTracker.clear(); 246e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov mInjectedPointerTracker.clear(); 247c18f2fdfcf8ec7a8534c032c4102739edb7f1c5eZachary Kuznia // Clear the gesture detector 248c18f2fdfcf8ec7a8534c032c4102739edb7f1c5eZachary Kuznia mGestureDetector.clear(); 249e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov // Go to initial state. 250e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov // Clear the long pressing pointer remap data. 251e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov mLongPressingPointerId = -1; 252e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov mLongPressingPointerDeltaX = 0; 253e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov mLongPressingPointerDeltaY = 0; 254e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov mCurrentState = STATE_TOUCH_EXPLORING; 255f068fed6c4c3fc2003aec19b6e7e892358179b02Svetoslav Ganov mTouchExplorationInProgress = false; 256f772cba59760d1ad9eb5cb9205b2e2e9126e488dSvetoslav Ganov mAms.onTouchInteractionEnd(); 2571cf70bbf96930662cab0e699d70b62865766ff52Svetoslav Ganov } 2581cf70bbf96930662cab0e699d70b62865766ff52Svetoslav Ganov 2591cf70bbf96930662cab0e699d70b62865766ff52Svetoslav Ganov @Override 2601cf70bbf96930662cab0e699d70b62865766ff52Svetoslav Ganov public void setNext(EventStreamTransformation next) { 2611cf70bbf96930662cab0e699d70b62865766ff52Svetoslav Ganov mNext = next; 262736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov } 263736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov 2641cf70bbf96930662cab0e699d70b62865766ff52Svetoslav Ganov @Override 26545af84a483165f06c04d74baba67f90da29c6ad2Svetoslav Ganov public void onMotionEvent(MotionEvent event, MotionEvent rawEvent, int policyFlags) { 266976724e81bca33dc48347f88633a37a195b9e4eaToni Barzic if (!event.isFromSource(InputDevice.SOURCE_TOUCHSCREEN)) { 267976724e81bca33dc48347f88633a37a195b9e4eaToni Barzic if (mNext != null) { 268976724e81bca33dc48347f88633a37a195b9e4eaToni Barzic mNext.onMotionEvent(event, rawEvent, policyFlags); 269976724e81bca33dc48347f88633a37a195b9e4eaToni Barzic } 270976724e81bca33dc48347f88633a37a195b9e4eaToni Barzic return; 271976724e81bca33dc48347f88633a37a195b9e4eaToni Barzic } 272976724e81bca33dc48347f88633a37a195b9e4eaToni Barzic 273736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov if (DEBUG) { 2744213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov Slog.d(LOG_TAG, "Received event: " + event + ", policyFlags=0x" 275736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov + Integer.toHexString(policyFlags)); 2764213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov Slog.d(LOG_TAG, getStateSymbolicName(mCurrentState)); 277736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov } 278736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov 27945af84a483165f06c04d74baba67f90da29c6ad2Svetoslav Ganov mReceivedPointerTracker.onMotionEvent(rawEvent); 280736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov 2819254b3e336a01ff79742882bd98860b48ebae379Zachary Kuznia // The motion detector is interested in the movements in physical space, 2829254b3e336a01ff79742882bd98860b48ebae379Zachary Kuznia // so it uses the rawEvent to ignore magnification and other 2839254b3e336a01ff79742882bd98860b48ebae379Zachary Kuznia // transformations. 2849254b3e336a01ff79742882bd98860b48ebae379Zachary Kuznia if (mGestureDetector.onMotionEvent(rawEvent, policyFlags)) { 285407df712e2a381e0227eb780786128a50bfcf5f5Zachary Kuznia // Event was handled by the gesture detector. 286407df712e2a381e0227eb780786128a50bfcf5f5Zachary Kuznia return; 287407df712e2a381e0227eb780786128a50bfcf5f5Zachary Kuznia } 288407df712e2a381e0227eb780786128a50bfcf5f5Zachary Kuznia 2899254b3e336a01ff79742882bd98860b48ebae379Zachary Kuznia if (event.getActionMasked() == MotionEvent.ACTION_CANCEL) { 2909254b3e336a01ff79742882bd98860b48ebae379Zachary Kuznia clear(event, policyFlags); 2919254b3e336a01ff79742882bd98860b48ebae379Zachary Kuznia return; 2929254b3e336a01ff79742882bd98860b48ebae379Zachary Kuznia } 2939254b3e336a01ff79742882bd98860b48ebae379Zachary Kuznia 294736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov switch(mCurrentState) { 295736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov case STATE_TOUCH_EXPLORING: { 29645af84a483165f06c04d74baba67f90da29c6ad2Svetoslav Ganov handleMotionEventStateTouchExploring(event, rawEvent, policyFlags); 297736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov } break; 298736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov case STATE_DRAGGING: { 299736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov handleMotionEventStateDragging(event, policyFlags); 300736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov } break; 301736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov case STATE_DELEGATING: { 302736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov handleMotionEventStateDelegating(event, policyFlags); 303736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov } break; 3044213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov case STATE_GESTURE_DETECTING: { 3059254b3e336a01ff79742882bd98860b48ebae379Zachary Kuznia // Already handled. 3064213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov } break; 3074213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov default: 308736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov throw new IllegalStateException("Illegal state: " + mCurrentState); 309736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov } 310736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov } 311736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov 312976724e81bca33dc48347f88633a37a195b9e4eaToni Barzic @Override 313976724e81bca33dc48347f88633a37a195b9e4eaToni Barzic public void onKeyEvent(KeyEvent event, int policyFlags) { 314976724e81bca33dc48347f88633a37a195b9e4eaToni Barzic if (mNext != null) { 315976724e81bca33dc48347f88633a37a195b9e4eaToni Barzic mNext.onKeyEvent(event, policyFlags); 316976724e81bca33dc48347f88633a37a195b9e4eaToni Barzic } 317976724e81bca33dc48347f88633a37a195b9e4eaToni Barzic } 318976724e81bca33dc48347f88633a37a195b9e4eaToni Barzic 319976724e81bca33dc48347f88633a37a195b9e4eaToni Barzic @Override 32086783474fdec98a22bc22e224462767eab13e273Svetoslav Ganov public void onAccessibilityEvent(AccessibilityEvent event) { 32177276b60851a158ad3e142cb3b091d57ae5ceffbSvetoslav Ganov final int eventType = event.getEventType(); 32277276b60851a158ad3e142cb3b091d57ae5ceffbSvetoslav Ganov 32377276b60851a158ad3e142cb3b091d57ae5ceffbSvetoslav Ganov // The event for gesture end should be strictly after the 32477276b60851a158ad3e142cb3b091d57ae5ceffbSvetoslav Ganov // last hover exit event. 325f068fed6c4c3fc2003aec19b6e7e892358179b02Svetoslav Ganov if (mSendTouchExplorationEndDelayed.isPending() 3268b681cb8813454aac8a626bf3d7adaa8beca4d75Svetoslav Ganov && eventType == AccessibilityEvent.TYPE_VIEW_HOVER_EXIT) { 32784044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav mSendTouchExplorationEndDelayed.cancel(); 3288b681cb8813454aac8a626bf3d7adaa8beca4d75Svetoslav Ganov sendAccessibilityEvent(AccessibilityEvent.TYPE_TOUCH_EXPLORATION_GESTURE_END); 32977276b60851a158ad3e142cb3b091d57ae5ceffbSvetoslav Ganov } 33077276b60851a158ad3e142cb3b091d57ae5ceffbSvetoslav Ganov 33177276b60851a158ad3e142cb3b091d57ae5ceffbSvetoslav Ganov // The event for touch interaction end should be strictly after the 33277276b60851a158ad3e142cb3b091d57ae5ceffbSvetoslav Ganov // last hover exit and the touch exploration gesture end events. 333f068fed6c4c3fc2003aec19b6e7e892358179b02Svetoslav Ganov if (mSendTouchInteractionEndDelayed.isPending() 3348b681cb8813454aac8a626bf3d7adaa8beca4d75Svetoslav Ganov && eventType == AccessibilityEvent.TYPE_VIEW_HOVER_EXIT) { 33584044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav mSendTouchInteractionEndDelayed.cancel(); 3368b681cb8813454aac8a626bf3d7adaa8beca4d75Svetoslav Ganov sendAccessibilityEvent(AccessibilityEvent.TYPE_TOUCH_INTERACTION_END); 33777276b60851a158ad3e142cb3b091d57ae5ceffbSvetoslav Ganov } 33877276b60851a158ad3e142cb3b091d57ae5ceffbSvetoslav Ganov 33986783474fdec98a22bc22e224462767eab13e273Svetoslav Ganov // If a new window opens or the accessibility focus moves we no longer 34086783474fdec98a22bc22e224462767eab13e273Svetoslav Ganov // want to click/long press on the last touch explored location. 34186783474fdec98a22bc22e224462767eab13e273Svetoslav Ganov switch (eventType) { 34286783474fdec98a22bc22e224462767eab13e273Svetoslav Ganov case AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED: 34386783474fdec98a22bc22e224462767eab13e273Svetoslav Ganov case AccessibilityEvent.TYPE_VIEW_ACCESSIBILITY_FOCUSED: { 3445d043ce8cc2f588fdfb336cc843fb3b07b196f83Svetoslav Ganov if (mInjectedPointerTracker.mLastInjectedHoverEventForClick != null) { 3455d043ce8cc2f588fdfb336cc843fb3b07b196f83Svetoslav Ganov mInjectedPointerTracker.mLastInjectedHoverEventForClick.recycle(); 3465d043ce8cc2f588fdfb336cc843fb3b07b196f83Svetoslav Ganov mInjectedPointerTracker.mLastInjectedHoverEventForClick = null; 34786783474fdec98a22bc22e224462767eab13e273Svetoslav Ganov } 34876716c5a180aa471c6973ca7aa03c7f2da677823Svetoslav Ganov mLastTouchedWindowId = -1; 34976716c5a180aa471c6973ca7aa03c7f2da677823Svetoslav Ganov } break; 35076716c5a180aa471c6973ca7aa03c7f2da677823Svetoslav Ganov case AccessibilityEvent.TYPE_VIEW_HOVER_ENTER: 35176716c5a180aa471c6973ca7aa03c7f2da677823Svetoslav Ganov case AccessibilityEvent.TYPE_VIEW_HOVER_EXIT: { 35276716c5a180aa471c6973ca7aa03c7f2da677823Svetoslav Ganov mLastTouchedWindowId = event.getWindowId(); 35386783474fdec98a22bc22e224462767eab13e273Svetoslav Ganov } break; 35486783474fdec98a22bc22e224462767eab13e273Svetoslav Ganov } 3551cf70bbf96930662cab0e699d70b62865766ff52Svetoslav Ganov if (mNext != null) { 3561cf70bbf96930662cab0e699d70b62865766ff52Svetoslav Ganov mNext.onAccessibilityEvent(event); 3571cf70bbf96930662cab0e699d70b62865766ff52Svetoslav Ganov } 35886783474fdec98a22bc22e224462767eab13e273Svetoslav Ganov } 35986783474fdec98a22bc22e224462767eab13e273Svetoslav Ganov 360c18f2fdfcf8ec7a8534c032c4102739edb7f1c5eZachary Kuznia @Override 361c18f2fdfcf8ec7a8534c032c4102739edb7f1c5eZachary Kuznia public void onDoubleTapAndHold(MotionEvent event, int policyFlags) { 362407df712e2a381e0227eb780786128a50bfcf5f5Zachary Kuznia // Ignore the event if we aren't touch exploring. 363407df712e2a381e0227eb780786128a50bfcf5f5Zachary Kuznia if (mCurrentState != STATE_TOUCH_EXPLORING) { 364407df712e2a381e0227eb780786128a50bfcf5f5Zachary Kuznia return; 365407df712e2a381e0227eb780786128a50bfcf5f5Zachary Kuznia } 366407df712e2a381e0227eb780786128a50bfcf5f5Zachary Kuznia 367c18f2fdfcf8ec7a8534c032c4102739edb7f1c5eZachary Kuznia // Pointers should not be zero when running this command. 368c18f2fdfcf8ec7a8534c032c4102739edb7f1c5eZachary Kuznia if (mReceivedPointerTracker.getLastReceivedEvent().getPointerCount() == 0) { 369c18f2fdfcf8ec7a8534c032c4102739edb7f1c5eZachary Kuznia return; 370c18f2fdfcf8ec7a8534c032c4102739edb7f1c5eZachary Kuznia } 371c18f2fdfcf8ec7a8534c032c4102739edb7f1c5eZachary Kuznia 372c18f2fdfcf8ec7a8534c032c4102739edb7f1c5eZachary Kuznia final int pointerIndex = event.getActionIndex(); 373c18f2fdfcf8ec7a8534c032c4102739edb7f1c5eZachary Kuznia final int pointerId = event.getPointerId(pointerIndex); 374c18f2fdfcf8ec7a8534c032c4102739edb7f1c5eZachary Kuznia 375c18f2fdfcf8ec7a8534c032c4102739edb7f1c5eZachary Kuznia Point clickLocation = mTempPoint; 376c18f2fdfcf8ec7a8534c032c4102739edb7f1c5eZachary Kuznia final int result = computeClickLocation(clickLocation); 377c18f2fdfcf8ec7a8534c032c4102739edb7f1c5eZachary Kuznia 378c18f2fdfcf8ec7a8534c032c4102739edb7f1c5eZachary Kuznia if (result == CLICK_LOCATION_NONE) { 379c18f2fdfcf8ec7a8534c032c4102739edb7f1c5eZachary Kuznia return; 380c18f2fdfcf8ec7a8534c032c4102739edb7f1c5eZachary Kuznia } 381c18f2fdfcf8ec7a8534c032c4102739edb7f1c5eZachary Kuznia 382c18f2fdfcf8ec7a8534c032c4102739edb7f1c5eZachary Kuznia mLongPressingPointerId = pointerId; 383c18f2fdfcf8ec7a8534c032c4102739edb7f1c5eZachary Kuznia mLongPressingPointerDeltaX = (int) event.getX(pointerIndex) - clickLocation.x; 384c18f2fdfcf8ec7a8534c032c4102739edb7f1c5eZachary Kuznia mLongPressingPointerDeltaY = (int) event.getY(pointerIndex) - clickLocation.y; 385c18f2fdfcf8ec7a8534c032c4102739edb7f1c5eZachary Kuznia 386c18f2fdfcf8ec7a8534c032c4102739edb7f1c5eZachary Kuznia sendHoverExitAndTouchExplorationGestureEndIfNeeded(policyFlags); 387c18f2fdfcf8ec7a8534c032c4102739edb7f1c5eZachary Kuznia 388c18f2fdfcf8ec7a8534c032c4102739edb7f1c5eZachary Kuznia mCurrentState = STATE_DELEGATING; 389c18f2fdfcf8ec7a8534c032c4102739edb7f1c5eZachary Kuznia sendDownForAllNotInjectedPointers(event, policyFlags); 390c18f2fdfcf8ec7a8534c032c4102739edb7f1c5eZachary Kuznia } 391c18f2fdfcf8ec7a8534c032c4102739edb7f1c5eZachary Kuznia 392c18f2fdfcf8ec7a8534c032c4102739edb7f1c5eZachary Kuznia @Override 393407df712e2a381e0227eb780786128a50bfcf5f5Zachary Kuznia public boolean onDoubleTap(MotionEvent event, int policyFlags) { 394407df712e2a381e0227eb780786128a50bfcf5f5Zachary Kuznia // Ignore the event if we aren't touch exploring. 395407df712e2a381e0227eb780786128a50bfcf5f5Zachary Kuznia if (mCurrentState != STATE_TOUCH_EXPLORING) { 396407df712e2a381e0227eb780786128a50bfcf5f5Zachary Kuznia return false; 397c18f2fdfcf8ec7a8534c032c4102739edb7f1c5eZachary Kuznia } 398c18f2fdfcf8ec7a8534c032c4102739edb7f1c5eZachary Kuznia 399c18f2fdfcf8ec7a8534c032c4102739edb7f1c5eZachary Kuznia // Remove pending event deliveries. 400c18f2fdfcf8ec7a8534c032c4102739edb7f1c5eZachary Kuznia mSendHoverEnterAndMoveDelayed.cancel(); 401c18f2fdfcf8ec7a8534c032c4102739edb7f1c5eZachary Kuznia mSendHoverExitDelayed.cancel(); 402c18f2fdfcf8ec7a8534c032c4102739edb7f1c5eZachary Kuznia 403c18f2fdfcf8ec7a8534c032c4102739edb7f1c5eZachary Kuznia if (mSendTouchExplorationEndDelayed.isPending()) { 404c18f2fdfcf8ec7a8534c032c4102739edb7f1c5eZachary Kuznia mSendTouchExplorationEndDelayed.forceSendAndRemove(); 405c18f2fdfcf8ec7a8534c032c4102739edb7f1c5eZachary Kuznia } 406c18f2fdfcf8ec7a8534c032c4102739edb7f1c5eZachary Kuznia if (mSendTouchInteractionEndDelayed.isPending()) { 407c18f2fdfcf8ec7a8534c032c4102739edb7f1c5eZachary Kuznia mSendTouchInteractionEndDelayed.forceSendAndRemove(); 408c18f2fdfcf8ec7a8534c032c4102739edb7f1c5eZachary Kuznia } 409c18f2fdfcf8ec7a8534c032c4102739edb7f1c5eZachary Kuznia 410c18f2fdfcf8ec7a8534c032c4102739edb7f1c5eZachary Kuznia final int pointerIndex = event.getActionIndex(); 411c18f2fdfcf8ec7a8534c032c4102739edb7f1c5eZachary Kuznia final int pointerId = event.getPointerId(pointerIndex); 412c18f2fdfcf8ec7a8534c032c4102739edb7f1c5eZachary Kuznia 413c18f2fdfcf8ec7a8534c032c4102739edb7f1c5eZachary Kuznia Point clickLocation = mTempPoint; 414c18f2fdfcf8ec7a8534c032c4102739edb7f1c5eZachary Kuznia final int result = computeClickLocation(clickLocation); 415c18f2fdfcf8ec7a8534c032c4102739edb7f1c5eZachary Kuznia if (result == CLICK_LOCATION_NONE) { 416407df712e2a381e0227eb780786128a50bfcf5f5Zachary Kuznia // We can't send a click to no location, but the gesture was still 417407df712e2a381e0227eb780786128a50bfcf5f5Zachary Kuznia // consumed. 418407df712e2a381e0227eb780786128a50bfcf5f5Zachary Kuznia return true; 419c18f2fdfcf8ec7a8534c032c4102739edb7f1c5eZachary Kuznia } 420c18f2fdfcf8ec7a8534c032c4102739edb7f1c5eZachary Kuznia 421c18f2fdfcf8ec7a8534c032c4102739edb7f1c5eZachary Kuznia // Do the click. 422c18f2fdfcf8ec7a8534c032c4102739edb7f1c5eZachary Kuznia PointerProperties[] properties = new PointerProperties[1]; 423c18f2fdfcf8ec7a8534c032c4102739edb7f1c5eZachary Kuznia properties[0] = new PointerProperties(); 424c18f2fdfcf8ec7a8534c032c4102739edb7f1c5eZachary Kuznia event.getPointerProperties(pointerIndex, properties[0]); 425c18f2fdfcf8ec7a8534c032c4102739edb7f1c5eZachary Kuznia PointerCoords[] coords = new PointerCoords[1]; 426c18f2fdfcf8ec7a8534c032c4102739edb7f1c5eZachary Kuznia coords[0] = new PointerCoords(); 427c18f2fdfcf8ec7a8534c032c4102739edb7f1c5eZachary Kuznia coords[0].x = clickLocation.x; 428c18f2fdfcf8ec7a8534c032c4102739edb7f1c5eZachary Kuznia coords[0].y = clickLocation.y; 429c18f2fdfcf8ec7a8534c032c4102739edb7f1c5eZachary Kuznia MotionEvent click_event = MotionEvent.obtain(event.getDownTime(), 430c18f2fdfcf8ec7a8534c032c4102739edb7f1c5eZachary Kuznia event.getEventTime(), MotionEvent.ACTION_DOWN, 1, properties, 431c18f2fdfcf8ec7a8534c032c4102739edb7f1c5eZachary Kuznia coords, 0, 0, 1.0f, 1.0f, event.getDeviceId(), 0, 432c18f2fdfcf8ec7a8534c032c4102739edb7f1c5eZachary Kuznia event.getSource(), event.getFlags()); 433c18f2fdfcf8ec7a8534c032c4102739edb7f1c5eZachary Kuznia final boolean targetAccessibilityFocus = (result == CLICK_LOCATION_ACCESSIBILITY_FOCUS); 434c18f2fdfcf8ec7a8534c032c4102739edb7f1c5eZachary Kuznia sendActionDownAndUp(click_event, policyFlags, targetAccessibilityFocus); 435c18f2fdfcf8ec7a8534c032c4102739edb7f1c5eZachary Kuznia click_event.recycle(); 436407df712e2a381e0227eb780786128a50bfcf5f5Zachary Kuznia return true; 437407df712e2a381e0227eb780786128a50bfcf5f5Zachary Kuznia } 438407df712e2a381e0227eb780786128a50bfcf5f5Zachary Kuznia 439407df712e2a381e0227eb780786128a50bfcf5f5Zachary Kuznia @Override 440bf1cf52663414eec1d80d23edc029bf6223adb3bZachary Kuznia public boolean onGestureStarted() { 441bf1cf52663414eec1d80d23edc029bf6223adb3bZachary Kuznia // We have to perform gesture detection, so 442bf1cf52663414eec1d80d23edc029bf6223adb3bZachary Kuznia // clear the current state and try to detect. 443bf1cf52663414eec1d80d23edc029bf6223adb3bZachary Kuznia mCurrentState = STATE_GESTURE_DETECTING; 444bf1cf52663414eec1d80d23edc029bf6223adb3bZachary Kuznia mSendHoverEnterAndMoveDelayed.cancel(); 445bf1cf52663414eec1d80d23edc029bf6223adb3bZachary Kuznia mSendHoverExitDelayed.cancel(); 446bf1cf52663414eec1d80d23edc029bf6223adb3bZachary Kuznia mExitGestureDetectionModeDelayed.post(); 447bf1cf52663414eec1d80d23edc029bf6223adb3bZachary Kuznia // Send accessibility event to announce the start 448bf1cf52663414eec1d80d23edc029bf6223adb3bZachary Kuznia // of gesture recognition. 449bf1cf52663414eec1d80d23edc029bf6223adb3bZachary Kuznia sendAccessibilityEvent(AccessibilityEvent.TYPE_GESTURE_DETECTION_START); 450bf1cf52663414eec1d80d23edc029bf6223adb3bZachary Kuznia return false; 451bf1cf52663414eec1d80d23edc029bf6223adb3bZachary Kuznia } 452bf1cf52663414eec1d80d23edc029bf6223adb3bZachary Kuznia 453bf1cf52663414eec1d80d23edc029bf6223adb3bZachary Kuznia @Override 4549254b3e336a01ff79742882bd98860b48ebae379Zachary Kuznia public boolean onGestureCompleted(int gestureId) { 455407df712e2a381e0227eb780786128a50bfcf5f5Zachary Kuznia if (mCurrentState != STATE_GESTURE_DETECTING) { 456407df712e2a381e0227eb780786128a50bfcf5f5Zachary Kuznia return false; 457407df712e2a381e0227eb780786128a50bfcf5f5Zachary Kuznia } 458407df712e2a381e0227eb780786128a50bfcf5f5Zachary Kuznia 4599254b3e336a01ff79742882bd98860b48ebae379Zachary Kuznia endGestureDetection(); 460407df712e2a381e0227eb780786128a50bfcf5f5Zachary Kuznia 461407df712e2a381e0227eb780786128a50bfcf5f5Zachary Kuznia mAms.onGesture(gestureId); 462407df712e2a381e0227eb780786128a50bfcf5f5Zachary Kuznia 463407df712e2a381e0227eb780786128a50bfcf5f5Zachary Kuznia return true; 464c18f2fdfcf8ec7a8534c032c4102739edb7f1c5eZachary Kuznia } 465c18f2fdfcf8ec7a8534c032c4102739edb7f1c5eZachary Kuznia 4669254b3e336a01ff79742882bd98860b48ebae379Zachary Kuznia @Override 467bf1cf52663414eec1d80d23edc029bf6223adb3bZachary Kuznia public boolean onGestureCancelled(MotionEvent event, int policyFlags) { 468bf1cf52663414eec1d80d23edc029bf6223adb3bZachary Kuznia if (mCurrentState == STATE_GESTURE_DETECTING) { 469bf1cf52663414eec1d80d23edc029bf6223adb3bZachary Kuznia endGestureDetection(); 470bf1cf52663414eec1d80d23edc029bf6223adb3bZachary Kuznia return true; 471bf1cf52663414eec1d80d23edc029bf6223adb3bZachary Kuznia } else if (mCurrentState == STATE_TOUCH_EXPLORING) { 472bf1cf52663414eec1d80d23edc029bf6223adb3bZachary Kuznia // If the finger is still moving, pass the event on. 473bf1cf52663414eec1d80d23edc029bf6223adb3bZachary Kuznia if (event.getActionMasked() == MotionEvent.ACTION_MOVE) { 474bf1cf52663414eec1d80d23edc029bf6223adb3bZachary Kuznia final int pointerId = mReceivedPointerTracker.getPrimaryPointerId(); 475bf1cf52663414eec1d80d23edc029bf6223adb3bZachary Kuznia final int pointerIdBits = (1 << pointerId); 476bf1cf52663414eec1d80d23edc029bf6223adb3bZachary Kuznia 477bf1cf52663414eec1d80d23edc029bf6223adb3bZachary Kuznia // We have just decided that the user is touch, 478bf1cf52663414eec1d80d23edc029bf6223adb3bZachary Kuznia // exploring so start sending events. 479bf1cf52663414eec1d80d23edc029bf6223adb3bZachary Kuznia mSendHoverEnterAndMoveDelayed.addEvent(event); 480bf1cf52663414eec1d80d23edc029bf6223adb3bZachary Kuznia mSendHoverEnterAndMoveDelayed.forceSendAndRemove(); 481bf1cf52663414eec1d80d23edc029bf6223adb3bZachary Kuznia mSendHoverExitDelayed.cancel(); 482bf1cf52663414eec1d80d23edc029bf6223adb3bZachary Kuznia sendMotionEvent(event, MotionEvent.ACTION_HOVER_MOVE, pointerIdBits, policyFlags); 483bf1cf52663414eec1d80d23edc029bf6223adb3bZachary Kuznia return true; 484bf1cf52663414eec1d80d23edc029bf6223adb3bZachary Kuznia } 485bf1cf52663414eec1d80d23edc029bf6223adb3bZachary Kuznia } 486bf1cf52663414eec1d80d23edc029bf6223adb3bZachary Kuznia return false; 4879254b3e336a01ff79742882bd98860b48ebae379Zachary Kuznia } 4889254b3e336a01ff79742882bd98860b48ebae379Zachary Kuznia 489736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov /** 490736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov * Handles a motion event in touch exploring state. 491736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov * 492736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov * @param event The event to be handled. 49345af84a483165f06c04d74baba67f90da29c6ad2Svetoslav Ganov * @param rawEvent The raw (unmodified) motion event. 494736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov * @param policyFlags The policy flags associated with the event. 495736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov */ 49645af84a483165f06c04d74baba67f90da29c6ad2Svetoslav Ganov private void handleMotionEventStateTouchExploring(MotionEvent event, MotionEvent rawEvent, 49745af84a483165f06c04d74baba67f90da29c6ad2Svetoslav Ganov int policyFlags) { 4984213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov ReceivedPointerTracker receivedTracker = mReceivedPointerTracker; 4994213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov 500736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov switch (event.getActionMasked()) { 50184044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav case MotionEvent.ACTION_DOWN: { 5026ae8a24fc045bc7970f2843fa9baf06aff15e22dSvetoslav Ganov mAms.onTouchInteractionStart(); 50384044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav 50484044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav sendAccessibilityEvent(AccessibilityEvent.TYPE_TOUCH_INTERACTION_START); 505736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov 50684044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav // If we still have not notified the user for the last 50784044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav // touch, we figure out what to do. If were waiting 50884044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav // we resent the delayed callback and wait again. 50984044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav mSendHoverEnterAndMoveDelayed.cancel(); 51084044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav mSendHoverExitDelayed.cancel(); 511f068fed6c4c3fc2003aec19b6e7e892358179b02Svetoslav Ganov 51284044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav if (mSendTouchExplorationEndDelayed.isPending()) { 51384044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav mSendTouchExplorationEndDelayed.forceSendAndRemove(); 51484044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav } 515f068fed6c4c3fc2003aec19b6e7e892358179b02Svetoslav Ganov 51684044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav if (mSendTouchInteractionEndDelayed.isPending()) { 51784044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav mSendTouchInteractionEndDelayed.forceSendAndRemove(); 51884044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav } 519fe304b893968887323b93764caafa66ee8ad44deSvetoslav Ganov 520c18f2fdfcf8ec7a8534c032c4102739edb7f1c5eZachary Kuznia if (!mGestureDetector.firstTapDetected() && !mTouchExplorationInProgress) { 52138c992841b5f6bc80359dbf60d31aa7b994160fcSvetoslav Ganov if (!mSendHoverEnterAndMoveDelayed.isPending()) { 52238c992841b5f6bc80359dbf60d31aa7b994160fcSvetoslav Ganov // Deliver hover enter with a delay to have a chance 52338c992841b5f6bc80359dbf60d31aa7b994160fcSvetoslav Ganov // to detect what the user is trying to do. 52438c992841b5f6bc80359dbf60d31aa7b994160fcSvetoslav Ganov final int pointerId = receivedTracker.getPrimaryPointerId(); 52538c992841b5f6bc80359dbf60d31aa7b994160fcSvetoslav Ganov final int pointerIdBits = (1 << pointerId); 52638c992841b5f6bc80359dbf60d31aa7b994160fcSvetoslav Ganov mSendHoverEnterAndMoveDelayed.post(event, true, pointerIdBits, 52738c992841b5f6bc80359dbf60d31aa7b994160fcSvetoslav Ganov policyFlags); 528a3315270e0502189a6fa4de20b9db2bf042cfd2aSvetoslav } else { 529a3315270e0502189a6fa4de20b9db2bf042cfd2aSvetoslav // Cache the event until we discern exploration from gesturing. 530a3315270e0502189a6fa4de20b9db2bf042cfd2aSvetoslav mSendHoverEnterAndMoveDelayed.addEvent(event); 53138c992841b5f6bc80359dbf60d31aa7b994160fcSvetoslav Ganov } 53284044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav } 53384044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav } break; 53484044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav case MotionEvent.ACTION_POINTER_DOWN: { 535a3315270e0502189a6fa4de20b9db2bf042cfd2aSvetoslav // Another finger down means that if we have not started to deliver 536a3315270e0502189a6fa4de20b9db2bf042cfd2aSvetoslav // hover events, we will not have to. The code for ACTION_MOVE will 537a3315270e0502189a6fa4de20b9db2bf042cfd2aSvetoslav // decide what we will actually do next. 538a3315270e0502189a6fa4de20b9db2bf042cfd2aSvetoslav mSendHoverEnterAndMoveDelayed.cancel(); 539a3315270e0502189a6fa4de20b9db2bf042cfd2aSvetoslav mSendHoverExitDelayed.cancel(); 540736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov } break; 541736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov case MotionEvent.ACTION_MOVE: { 54284044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav final int pointerId = receivedTracker.getPrimaryPointerId(); 54391feae3c5994bd4768cea3507c62c65746adcfa6Svetoslav Ganov final int pointerIndex = event.findPointerIndex(pointerId); 54491feae3c5994bd4768cea3507c62c65746adcfa6Svetoslav Ganov final int pointerIdBits = (1 << pointerId); 54584044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav switch (event.getPointerCount()) { 546736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov case 1: { 547e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov // We have not started sending events since we try to 548e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov // figure out what the user is doing. 54984044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav if (mSendHoverEnterAndMoveDelayed.isPending()) { 55084044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav // Cache the event until we discern exploration from gesturing. 55184044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav mSendHoverEnterAndMoveDelayed.addEvent(event); 552736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov } else { 553a3315270e0502189a6fa4de20b9db2bf042cfd2aSvetoslav if (mTouchExplorationInProgress) { 554a3315270e0502189a6fa4de20b9db2bf042cfd2aSvetoslav sendTouchExplorationGestureStartAndHoverEnterIfNeeded(policyFlags); 555a3315270e0502189a6fa4de20b9db2bf042cfd2aSvetoslav sendMotionEvent(event, MotionEvent.ACTION_HOVER_MOVE, pointerIdBits, 556a3315270e0502189a6fa4de20b9db2bf042cfd2aSvetoslav policyFlags); 557e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov } 558736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov } 559e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov } break; 560e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov case 2: { 561e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov // More than one pointer so the user is not touch exploring 562e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov // and now we have to decide whether to delegate or drag. 56384044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav if (mSendHoverEnterAndMoveDelayed.isPending()) { 564e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov // We have not started sending events so cancel 565e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov // scheduled sending events. 56684044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav mSendHoverEnterAndMoveDelayed.cancel(); 56784044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav mSendHoverExitDelayed.cancel(); 568e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov } else { 569a3315270e0502189a6fa4de20b9db2bf042cfd2aSvetoslav if (mTouchExplorationInProgress) { 570a3315270e0502189a6fa4de20b9db2bf042cfd2aSvetoslav // If the user is touch exploring the second pointer may be 571a3315270e0502189a6fa4de20b9db2bf042cfd2aSvetoslav // performing a double tap to activate an item without need 572a3315270e0502189a6fa4de20b9db2bf042cfd2aSvetoslav // for the user to lift his exploring finger. 573a3315270e0502189a6fa4de20b9db2bf042cfd2aSvetoslav // It is *important* to use the distance traveled by the pointers 574a3315270e0502189a6fa4de20b9db2bf042cfd2aSvetoslav // on the screen which may or may not be magnified. 575a3315270e0502189a6fa4de20b9db2bf042cfd2aSvetoslav final float deltaX = receivedTracker.getReceivedPointerDownX( 576a3315270e0502189a6fa4de20b9db2bf042cfd2aSvetoslav pointerId) - rawEvent.getX(pointerIndex); 577a3315270e0502189a6fa4de20b9db2bf042cfd2aSvetoslav final float deltaY = receivedTracker.getReceivedPointerDownY( 578a3315270e0502189a6fa4de20b9db2bf042cfd2aSvetoslav pointerId) - rawEvent.getY(pointerIndex); 579a3315270e0502189a6fa4de20b9db2bf042cfd2aSvetoslav final double moveDelta = Math.hypot(deltaX, deltaY); 580a3315270e0502189a6fa4de20b9db2bf042cfd2aSvetoslav if (moveDelta < mDoubleTapSlop) { 581a3315270e0502189a6fa4de20b9db2bf042cfd2aSvetoslav break; 582a3315270e0502189a6fa4de20b9db2bf042cfd2aSvetoslav } 583a3315270e0502189a6fa4de20b9db2bf042cfd2aSvetoslav // We are sending events so send exit and gesture 584a3315270e0502189a6fa4de20b9db2bf042cfd2aSvetoslav // end since we transition to another state. 585a3315270e0502189a6fa4de20b9db2bf042cfd2aSvetoslav sendHoverExitAndTouchExplorationGestureEndIfNeeded(policyFlags); 586736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov } 587736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov } 588e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov 589736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov if (isDraggingGesture(event)) { 590736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov // Two pointers moving in the same direction within 591736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov // a given distance perform a drag. 59200f7b3f76515d1c6fbe5cf9fee9d3760787c03cdSvetoslav Ganov mCurrentState = STATE_DRAGGING; 59391feae3c5994bd4768cea3507c62c65746adcfa6Svetoslav Ganov mDraggingPointerId = pointerId; 594c4842c11932ea4f60fe7ae09b0a59660207e1587Svetoslav Ganov event.setEdgeFlags(receivedTracker.getLastReceivedDownEdgeFlags()); 59591feae3c5994bd4768cea3507c62c65746adcfa6Svetoslav Ganov sendMotionEvent(event, MotionEvent.ACTION_DOWN, pointerIdBits, 59691feae3c5994bd4768cea3507c62c65746adcfa6Svetoslav Ganov policyFlags); 597736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov } else { 598736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov // Two pointers moving arbitrary are delegated to the view hierarchy. 599736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov mCurrentState = STATE_DELEGATING; 60084044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav sendDownForAllNotInjectedPointers(event, policyFlags); 601736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov } 602736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov } break; 603736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov default: { 604e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov // More than one pointer so the user is not touch exploring 605e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov // and now we have to decide whether to delegate or drag. 60684044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav if (mSendHoverEnterAndMoveDelayed.isPending()) { 607e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov // We have not started sending events so cancel 608e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov // scheduled sending events. 60984044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav mSendHoverEnterAndMoveDelayed.cancel(); 61084044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav mSendHoverExitDelayed.cancel(); 611e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov } else { 612e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov // We are sending events so send exit and gesture 613e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov // end since we transition to another state. 614f068fed6c4c3fc2003aec19b6e7e892358179b02Svetoslav Ganov sendHoverExitAndTouchExplorationGestureEndIfNeeded(policyFlags); 615e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov } 616736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov 617736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov // More than two pointers are delegated to the view hierarchy. 618736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov mCurrentState = STATE_DELEGATING; 61984044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav sendDownForAllNotInjectedPointers(event, policyFlags); 620736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov } 621736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov } 622736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov } break; 62384044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav case MotionEvent.ACTION_UP: { 624f772cba59760d1ad9eb5cb9205b2e2e9126e488dSvetoslav Ganov mAms.onTouchInteractionEnd(); 62584044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav final int pointerId = event.getPointerId(event.getActionIndex()); 62691feae3c5994bd4768cea3507c62c65746adcfa6Svetoslav Ganov final int pointerIdBits = (1 << pointerId); 627736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov 62884044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav if (mSendHoverEnterAndMoveDelayed.isPending()) { 62984044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav // If we have not delivered the enter schedule an exit. 63084044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav mSendHoverExitDelayed.post(event, pointerIdBits, policyFlags); 63184044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav } else { 63284044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav // The user is touch exploring so we send events for end. 63384044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav sendHoverExitAndTouchExplorationGestureEndIfNeeded(policyFlags); 63484044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav } 635f068fed6c4c3fc2003aec19b6e7e892358179b02Svetoslav Ganov 63684044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav if (!mSendTouchInteractionEndDelayed.isPending()) { 63784044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav mSendTouchInteractionEndDelayed.post(); 638736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov } 63984044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav 640736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov } break; 641736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov } 642736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov } 643736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov 644736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov /** 645736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov * Handles a motion event in dragging state. 646736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov * 647736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov * @param event The event to be handled. 648736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov * @param policyFlags The policy flags associated with the event. 649736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov */ 650736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov private void handleMotionEventStateDragging(MotionEvent event, int policyFlags) { 651ec1783828e541f22e93e67ec80511b9187bc93beZachary Kuznia int pointerIdBits = 0; 652ec1783828e541f22e93e67ec80511b9187bc93beZachary Kuznia // Clear the dragging pointer id if it's no longer valid. 653ec1783828e541f22e93e67ec80511b9187bc93beZachary Kuznia if (event.findPointerIndex(mDraggingPointerId) == -1) { 654ec1783828e541f22e93e67ec80511b9187bc93beZachary Kuznia Slog.e(LOG_TAG, "mDraggingPointerId doesn't match any pointers on current event. " + 655ec1783828e541f22e93e67ec80511b9187bc93beZachary Kuznia "mDraggingPointerId: " + Integer.toString(mDraggingPointerId) + 656ec1783828e541f22e93e67ec80511b9187bc93beZachary Kuznia ", Event: " + event); 657ec1783828e541f22e93e67ec80511b9187bc93beZachary Kuznia mDraggingPointerId = INVALID_POINTER_ID; 658ec1783828e541f22e93e67ec80511b9187bc93beZachary Kuznia } else { 659ec1783828e541f22e93e67ec80511b9187bc93beZachary Kuznia pointerIdBits = (1 << mDraggingPointerId); 660ec1783828e541f22e93e67ec80511b9187bc93beZachary Kuznia } 661736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov switch (event.getActionMasked()) { 662736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov case MotionEvent.ACTION_DOWN: { 663736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov throw new IllegalStateException("Dragging state can be reached only if two " 664736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov + "pointers are already down"); 665736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov } 666736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov case MotionEvent.ACTION_POINTER_DOWN: { 667736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov // We are in dragging state so we have two pointers and another one 668736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov // goes down => delegate the three pointers to the view hierarchy 669736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov mCurrentState = STATE_DELEGATING; 670ec33d56300aa417efb4a055786d73d1bf23a6a85Svetoslav Ganov if (mDraggingPointerId != INVALID_POINTER_ID) { 671ec33d56300aa417efb4a055786d73d1bf23a6a85Svetoslav Ganov sendMotionEvent(event, MotionEvent.ACTION_UP, pointerIdBits, policyFlags); 672ec33d56300aa417efb4a055786d73d1bf23a6a85Svetoslav Ganov } 67384044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav sendDownForAllNotInjectedPointers(event, policyFlags); 674736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov } break; 675736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov case MotionEvent.ACTION_MOVE: { 676ec1783828e541f22e93e67ec80511b9187bc93beZachary Kuznia if (mDraggingPointerId == INVALID_POINTER_ID) { 677ec1783828e541f22e93e67ec80511b9187bc93beZachary Kuznia break; 678ec1783828e541f22e93e67ec80511b9187bc93beZachary Kuznia } 67984044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav switch (event.getPointerCount()) { 6802e1c66bd53d30d2148afaa4b393b60cd59976d65Svetoslav Ganov case 1: { 6812e1c66bd53d30d2148afaa4b393b60cd59976d65Svetoslav Ganov // do nothing 6822e1c66bd53d30d2148afaa4b393b60cd59976d65Svetoslav Ganov } break; 683736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov case 2: { 684736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov if (isDraggingGesture(event)) { 68584044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav final float firstPtrX = event.getX(0); 68684044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav final float firstPtrY = event.getY(0); 68784044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav final float secondPtrX = event.getX(1); 68884044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav final float secondPtrY = event.getY(1); 689e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov 690e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov final float deltaX = firstPtrX - secondPtrX; 691e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov final float deltaY = firstPtrY - secondPtrY; 692e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov final double distance = Math.hypot(deltaX, deltaY); 693e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov 694e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov if (distance > mScaledMinPointerDistanceToUseMiddleLocation) { 695e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov event.setLocation(deltaX / 2, deltaY / 2); 696e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov } 697e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov 698736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov // If still dragging send a drag event. 69991feae3c5994bd4768cea3507c62c65746adcfa6Svetoslav Ganov sendMotionEvent(event, MotionEvent.ACTION_MOVE, pointerIdBits, 70091feae3c5994bd4768cea3507c62c65746adcfa6Svetoslav Ganov policyFlags); 701736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov } else { 702736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov // The two pointers are moving either in different directions or 703736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov // no close enough => delegate the gesture to the view hierarchy. 704736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov mCurrentState = STATE_DELEGATING; 705736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov // Send an event to the end of the drag gesture. 70691feae3c5994bd4768cea3507c62c65746adcfa6Svetoslav Ganov sendMotionEvent(event, MotionEvent.ACTION_UP, pointerIdBits, 70791feae3c5994bd4768cea3507c62c65746adcfa6Svetoslav Ganov policyFlags); 70884044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav // Deliver all pointers to the view hierarchy. 70984044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav sendDownForAllNotInjectedPointers(event, policyFlags); 710736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov } 711736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov } break; 712736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov default: { 713736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov mCurrentState = STATE_DELEGATING; 714736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov // Send an event to the end of the drag gesture. 71591feae3c5994bd4768cea3507c62c65746adcfa6Svetoslav Ganov sendMotionEvent(event, MotionEvent.ACTION_UP, pointerIdBits, 71691feae3c5994bd4768cea3507c62c65746adcfa6Svetoslav Ganov policyFlags); 71784044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav // Deliver all pointers to the view hierarchy. 71884044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav sendDownForAllNotInjectedPointers(event, policyFlags); 719736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov } 720736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov } 721736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov } break; 722aeb8d0ed0d98d398a66a092c418f4f2bca8719e0Svetoslav Ganov case MotionEvent.ACTION_POINTER_UP: { 723aeb8d0ed0d98d398a66a092c418f4f2bca8719e0Svetoslav Ganov final int pointerId = event.getPointerId(event.getActionIndex()); 724aeb8d0ed0d98d398a66a092c418f4f2bca8719e0Svetoslav Ganov if (pointerId == mDraggingPointerId) { 725aeb8d0ed0d98d398a66a092c418f4f2bca8719e0Svetoslav Ganov mDraggingPointerId = INVALID_POINTER_ID; 726aeb8d0ed0d98d398a66a092c418f4f2bca8719e0Svetoslav Ganov // Send an event to the end of the drag gesture. 727aeb8d0ed0d98d398a66a092c418f4f2bca8719e0Svetoslav Ganov sendMotionEvent(event, MotionEvent.ACTION_UP, pointerIdBits, policyFlags); 728aeb8d0ed0d98d398a66a092c418f4f2bca8719e0Svetoslav Ganov } 729aeb8d0ed0d98d398a66a092c418f4f2bca8719e0Svetoslav Ganov } break; 7302e1c66bd53d30d2148afaa4b393b60cd59976d65Svetoslav Ganov case MotionEvent.ACTION_UP: { 731f772cba59760d1ad9eb5cb9205b2e2e9126e488dSvetoslav Ganov mAms.onTouchInteractionEnd(); 73277276b60851a158ad3e142cb3b091d57ae5ceffbSvetoslav Ganov // Announce the end of a new touch interaction. 73377276b60851a158ad3e142cb3b091d57ae5ceffbSvetoslav Ganov sendAccessibilityEvent( 73477276b60851a158ad3e142cb3b091d57ae5ceffbSvetoslav Ganov AccessibilityEvent.TYPE_TOUCH_INTERACTION_END); 735aeb8d0ed0d98d398a66a092c418f4f2bca8719e0Svetoslav Ganov final int pointerId = event.getPointerId(event.getActionIndex()); 736aeb8d0ed0d98d398a66a092c418f4f2bca8719e0Svetoslav Ganov if (pointerId == mDraggingPointerId) { 737aeb8d0ed0d98d398a66a092c418f4f2bca8719e0Svetoslav Ganov mDraggingPointerId = INVALID_POINTER_ID; 738aeb8d0ed0d98d398a66a092c418f4f2bca8719e0Svetoslav Ganov // Send an event to the end of the drag gesture. 739aeb8d0ed0d98d398a66a092c418f4f2bca8719e0Svetoslav Ganov sendMotionEvent(event, MotionEvent.ACTION_UP, pointerIdBits, policyFlags); 740aeb8d0ed0d98d398a66a092c418f4f2bca8719e0Svetoslav Ganov } 7412e1c66bd53d30d2148afaa4b393b60cd59976d65Svetoslav Ganov mCurrentState = STATE_TOUCH_EXPLORING; 7422e1c66bd53d30d2148afaa4b393b60cd59976d65Svetoslav Ganov } break; 743736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov } 744736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov } 745736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov 746736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov /** 747736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov * Handles a motion event in delegating state. 748736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov * 749736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov * @param event The event to be handled. 750736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov * @param policyFlags The policy flags associated with the event. 751736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov */ 7522e1c66bd53d30d2148afaa4b393b60cd59976d65Svetoslav Ganov private void handleMotionEventStateDelegating(MotionEvent event, int policyFlags) { 753736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov switch (event.getActionMasked()) { 754736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov case MotionEvent.ACTION_DOWN: { 755736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov throw new IllegalStateException("Delegating state can only be reached if " 756736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov + "there is at least one pointer down!"); 757736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov } 75884044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav case MotionEvent.ACTION_UP: { 759a3315270e0502189a6fa4de20b9db2bf042cfd2aSvetoslav // Offset the event if we are doing a long press as the 760a3315270e0502189a6fa4de20b9db2bf042cfd2aSvetoslav // target is not necessarily under the user's finger. 761a3315270e0502189a6fa4de20b9db2bf042cfd2aSvetoslav if (mLongPressingPointerId >= 0) { 762a3315270e0502189a6fa4de20b9db2bf042cfd2aSvetoslav event = offsetEvent(event, - mLongPressingPointerDeltaX, 763a3315270e0502189a6fa4de20b9db2bf042cfd2aSvetoslav - mLongPressingPointerDeltaY); 764a3315270e0502189a6fa4de20b9db2bf042cfd2aSvetoslav // Clear the long press state. 765a3315270e0502189a6fa4de20b9db2bf042cfd2aSvetoslav mLongPressingPointerId = -1; 766a3315270e0502189a6fa4de20b9db2bf042cfd2aSvetoslav mLongPressingPointerDeltaX = 0; 767a3315270e0502189a6fa4de20b9db2bf042cfd2aSvetoslav mLongPressingPointerDeltaY = 0; 768a3315270e0502189a6fa4de20b9db2bf042cfd2aSvetoslav } 769a3315270e0502189a6fa4de20b9db2bf042cfd2aSvetoslav 770a3315270e0502189a6fa4de20b9db2bf042cfd2aSvetoslav // Deliver the event. 771a3315270e0502189a6fa4de20b9db2bf042cfd2aSvetoslav sendMotionEvent(event, event.getAction(), ALL_POINTER_ID_BITS, policyFlags); 772a3315270e0502189a6fa4de20b9db2bf042cfd2aSvetoslav 77384044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav // Announce the end of a the touch interaction. 774a3315270e0502189a6fa4de20b9db2bf042cfd2aSvetoslav mAms.onTouchInteractionEnd(); 77584044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav sendAccessibilityEvent(AccessibilityEvent.TYPE_TOUCH_INTERACTION_END); 776a3315270e0502189a6fa4de20b9db2bf042cfd2aSvetoslav 77784044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav mCurrentState = STATE_TOUCH_EXPLORING; 778736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov } break; 779a3315270e0502189a6fa4de20b9db2bf042cfd2aSvetoslav default: { 780a3315270e0502189a6fa4de20b9db2bf042cfd2aSvetoslav // Deliver the event. 781a3315270e0502189a6fa4de20b9db2bf042cfd2aSvetoslav sendMotionEvent(event, event.getAction(), ALL_POINTER_ID_BITS, policyFlags); 782a3315270e0502189a6fa4de20b9db2bf042cfd2aSvetoslav } 783736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov } 784736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov } 785736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov 7869254b3e336a01ff79742882bd98860b48ebae379Zachary Kuznia private void endGestureDetection() { 7879254b3e336a01ff79742882bd98860b48ebae379Zachary Kuznia mAms.onTouchInteractionEnd(); 78877276b60851a158ad3e142cb3b091d57ae5ceffbSvetoslav Ganov 7899254b3e336a01ff79742882bd98860b48ebae379Zachary Kuznia // Announce the end of the gesture recognition. 7909254b3e336a01ff79742882bd98860b48ebae379Zachary Kuznia sendAccessibilityEvent(AccessibilityEvent.TYPE_GESTURE_DETECTION_END); 7919254b3e336a01ff79742882bd98860b48ebae379Zachary Kuznia // Announce the end of a the touch interaction. 7929254b3e336a01ff79742882bd98860b48ebae379Zachary Kuznia sendAccessibilityEvent(AccessibilityEvent.TYPE_TOUCH_INTERACTION_END); 7939254b3e336a01ff79742882bd98860b48ebae379Zachary Kuznia 7949254b3e336a01ff79742882bd98860b48ebae379Zachary Kuznia mExitGestureDetectionModeDelayed.cancel(); 7959254b3e336a01ff79742882bd98860b48ebae379Zachary Kuznia mCurrentState = STATE_TOUCH_EXPLORING; 7964213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov } 7974213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov 798736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov /** 79977276b60851a158ad3e142cb3b091d57ae5ceffbSvetoslav Ganov * Sends an accessibility event of the given type. 80077276b60851a158ad3e142cb3b091d57ae5ceffbSvetoslav Ganov * 80177276b60851a158ad3e142cb3b091d57ae5ceffbSvetoslav Ganov * @param type The event type. 80277276b60851a158ad3e142cb3b091d57ae5ceffbSvetoslav Ganov */ 80377276b60851a158ad3e142cb3b091d57ae5ceffbSvetoslav Ganov private void sendAccessibilityEvent(int type) { 80477276b60851a158ad3e142cb3b091d57ae5ceffbSvetoslav Ganov AccessibilityManager accessibilityManager = AccessibilityManager.getInstance(mContext); 80577276b60851a158ad3e142cb3b091d57ae5ceffbSvetoslav Ganov if (accessibilityManager.isEnabled()) { 80677276b60851a158ad3e142cb3b091d57ae5ceffbSvetoslav Ganov AccessibilityEvent event = AccessibilityEvent.obtain(type); 8073d1c5a7236c4709550ca7c0cfa293fc5c974c56bAlan Viverette event.setWindowId(mAms.getActiveWindowId()); 80877276b60851a158ad3e142cb3b091d57ae5ceffbSvetoslav Ganov accessibilityManager.sendAccessibilityEvent(event); 809f068fed6c4c3fc2003aec19b6e7e892358179b02Svetoslav Ganov switch (type) { 810f068fed6c4c3fc2003aec19b6e7e892358179b02Svetoslav Ganov case AccessibilityEvent.TYPE_TOUCH_EXPLORATION_GESTURE_START: { 811f068fed6c4c3fc2003aec19b6e7e892358179b02Svetoslav Ganov mTouchExplorationInProgress = true; 812f068fed6c4c3fc2003aec19b6e7e892358179b02Svetoslav Ganov } break; 813f068fed6c4c3fc2003aec19b6e7e892358179b02Svetoslav Ganov case AccessibilityEvent.TYPE_TOUCH_EXPLORATION_GESTURE_END: { 814f068fed6c4c3fc2003aec19b6e7e892358179b02Svetoslav Ganov mTouchExplorationInProgress = false; 815f068fed6c4c3fc2003aec19b6e7e892358179b02Svetoslav Ganov } break; 816f068fed6c4c3fc2003aec19b6e7e892358179b02Svetoslav Ganov } 81777276b60851a158ad3e142cb3b091d57ae5ceffbSvetoslav Ganov } 81877276b60851a158ad3e142cb3b091d57ae5ceffbSvetoslav Ganov } 81977276b60851a158ad3e142cb3b091d57ae5ceffbSvetoslav Ganov 82077276b60851a158ad3e142cb3b091d57ae5ceffbSvetoslav Ganov /** 82184044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav * Sends down events to the view hierarchy for all pointers which are 822736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov * not already being delivered i.e. pointers that are not yet injected. 823736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov * 824736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov * @param prototype The prototype from which to create the injected events. 825736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov * @param policyFlags The policy flags associated with the event. 826736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov */ 82784044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav private void sendDownForAllNotInjectedPointers(MotionEvent prototype, int policyFlags) { 8284213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov InjectedPointerTracker injectedPointers = mInjectedPointerTracker; 82984044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav 83084044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav // Inject the injected pointers. 831f804420d6e37748b75478406e989c69303756980Svetoslav Ganov int pointerIdBits = 0; 832f804420d6e37748b75478406e989c69303756980Svetoslav Ganov final int pointerCount = prototype.getPointerCount(); 833f804420d6e37748b75478406e989c69303756980Svetoslav Ganov for (int i = 0; i < pointerCount; i++) { 834f804420d6e37748b75478406e989c69303756980Svetoslav Ganov final int pointerId = prototype.getPointerId(i); 835f804420d6e37748b75478406e989c69303756980Svetoslav Ganov // Do not send event for already delivered pointers. 83684044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav if (!injectedPointers.isInjectedPointerDown(pointerId)) { 83784044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav pointerIdBits |= (1 << pointerId); 83884044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav final int action = computeInjectionAction(MotionEvent.ACTION_DOWN, i); 83984044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav sendMotionEvent(prototype, action, pointerIdBits, policyFlags); 840736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov } 841f804420d6e37748b75478406e989c69303756980Svetoslav Ganov } 842f804420d6e37748b75478406e989c69303756980Svetoslav Ganov } 843736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov 844f804420d6e37748b75478406e989c69303756980Svetoslav Ganov /** 845e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov * Sends the exit events if needed. Such events are hover exit and touch explore 846e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov * gesture end. 847e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov * 848e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov * @param policyFlags The policy flags associated with the event. 849e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov */ 850f068fed6c4c3fc2003aec19b6e7e892358179b02Svetoslav Ganov private void sendHoverExitAndTouchExplorationGestureEndIfNeeded(int policyFlags) { 851e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov MotionEvent event = mInjectedPointerTracker.getLastInjectedHoverEvent(); 852e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov if (event != null && event.getActionMasked() != MotionEvent.ACTION_HOVER_EXIT) { 853e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov final int pointerIdBits = event.getPointerIdBits(); 854f068fed6c4c3fc2003aec19b6e7e892358179b02Svetoslav Ganov if (!mSendTouchExplorationEndDelayed.isPending()) { 855f068fed6c4c3fc2003aec19b6e7e892358179b02Svetoslav Ganov mSendTouchExplorationEndDelayed.post(); 856fe304b893968887323b93764caafa66ee8ad44deSvetoslav Ganov } 857e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov sendMotionEvent(event, MotionEvent.ACTION_HOVER_EXIT, pointerIdBits, policyFlags); 858e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov } 859e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov } 860e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov 861e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov /** 862e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov * Sends the enter events if needed. Such events are hover enter and touch explore 863e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov * gesture start. 864f804420d6e37748b75478406e989c69303756980Svetoslav Ganov * 865f804420d6e37748b75478406e989c69303756980Svetoslav Ganov * @param policyFlags The policy flags associated with the event. 866f804420d6e37748b75478406e989c69303756980Svetoslav Ganov */ 867f068fed6c4c3fc2003aec19b6e7e892358179b02Svetoslav Ganov private void sendTouchExplorationGestureStartAndHoverEnterIfNeeded(int policyFlags) { 868e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov MotionEvent event = mInjectedPointerTracker.getLastInjectedHoverEvent(); 869e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov if (event != null && event.getActionMasked() == MotionEvent.ACTION_HOVER_EXIT) { 870e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov final int pointerIdBits = event.getPointerIdBits(); 871f068fed6c4c3fc2003aec19b6e7e892358179b02Svetoslav Ganov sendAccessibilityEvent(AccessibilityEvent.TYPE_TOUCH_EXPLORATION_GESTURE_START); 872e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov sendMotionEvent(event, MotionEvent.ACTION_HOVER_ENTER, pointerIdBits, policyFlags); 873736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov } 874736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov } 875736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov 876736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov /** 87784044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav * Sends up events to the view hierarchy for all pointers which are 878736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov * already being delivered i.e. pointers that are injected. 879736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov * 880736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov * @param prototype The prototype from which to create the injected events. 881736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov * @param policyFlags The policy flags associated with the event. 882736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov */ 883736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov private void sendUpForInjectedDownPointers(MotionEvent prototype, int policyFlags) { 8844213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov final InjectedPointerTracker injectedTracked = mInjectedPointerTracker; 885f804420d6e37748b75478406e989c69303756980Svetoslav Ganov int pointerIdBits = 0; 886f804420d6e37748b75478406e989c69303756980Svetoslav Ganov final int pointerCount = prototype.getPointerCount(); 887736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov for (int i = 0; i < pointerCount; i++) { 888736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov final int pointerId = prototype.getPointerId(i); 889736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov // Skip non injected down pointers. 8904213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov if (!injectedTracked.isInjectedPointerDown(pointerId)) { 891736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov continue; 892736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov } 893f804420d6e37748b75478406e989c69303756980Svetoslav Ganov pointerIdBits |= (1 << pointerId); 894f804420d6e37748b75478406e989c69303756980Svetoslav Ganov final int action = computeInjectionAction(MotionEvent.ACTION_UP, i); 895f804420d6e37748b75478406e989c69303756980Svetoslav Ganov sendMotionEvent(prototype, action, pointerIdBits, policyFlags); 896736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov } 897736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov } 898736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov 899736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov /** 900736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov * Sends an up and down events. 901736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov * 902736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov * @param prototype The prototype from which to create the injected events. 903736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov * @param policyFlags The policy flags associated with the event. 904ded133c446fa9d0d23e6bde19a66fb2ce3980491Svetoslav * @param targetAccessibilityFocus Whether the event targets the accessibility focus. 905736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov */ 906ded133c446fa9d0d23e6bde19a66fb2ce3980491Svetoslav private void sendActionDownAndUp(MotionEvent prototype, int policyFlags, 907ded133c446fa9d0d23e6bde19a66fb2ce3980491Svetoslav boolean targetAccessibilityFocus) { 90884044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav // Tap with the pointer that last explored. 909bd206d129fdd1777b9f9646a834d7fc342a8941eSvetoslav Ganov final int pointerId = prototype.getPointerId(prototype.getActionIndex()); 910f804420d6e37748b75478406e989c69303756980Svetoslav Ganov final int pointerIdBits = (1 << pointerId); 911ded133c446fa9d0d23e6bde19a66fb2ce3980491Svetoslav prototype.setTargetAccessibilityFocus(targetAccessibilityFocus); 912f804420d6e37748b75478406e989c69303756980Svetoslav Ganov sendMotionEvent(prototype, MotionEvent.ACTION_DOWN, pointerIdBits, policyFlags); 913ded133c446fa9d0d23e6bde19a66fb2ce3980491Svetoslav prototype.setTargetAccessibilityFocus(targetAccessibilityFocus); 914f804420d6e37748b75478406e989c69303756980Svetoslav Ganov sendMotionEvent(prototype, MotionEvent.ACTION_UP, pointerIdBits, policyFlags); 915736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov } 916736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov 917736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov /** 91891feae3c5994bd4768cea3507c62c65746adcfa6Svetoslav Ganov * Sends an event. 919736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov * 92000f7b3f76515d1c6fbe5cf9fee9d3760787c03cdSvetoslav Ganov * @param prototype The prototype from which to create the injected events. 92191feae3c5994bd4768cea3507c62c65746adcfa6Svetoslav Ganov * @param action The action of the event. 92291feae3c5994bd4768cea3507c62c65746adcfa6Svetoslav Ganov * @param pointerIdBits The bits of the pointers to send. 923736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov * @param policyFlags The policy flags associated with the event. 924736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov */ 92591feae3c5994bd4768cea3507c62c65746adcfa6Svetoslav Ganov private void sendMotionEvent(MotionEvent prototype, int action, int pointerIdBits, 92691feae3c5994bd4768cea3507c62c65746adcfa6Svetoslav Ganov int policyFlags) { 927f804420d6e37748b75478406e989c69303756980Svetoslav Ganov prototype.setAction(action); 928f804420d6e37748b75478406e989c69303756980Svetoslav Ganov 929f804420d6e37748b75478406e989c69303756980Svetoslav Ganov MotionEvent event = null; 930f804420d6e37748b75478406e989c69303756980Svetoslav Ganov if (pointerIdBits == ALL_POINTER_ID_BITS) { 931f804420d6e37748b75478406e989c69303756980Svetoslav Ganov event = prototype; 932f804420d6e37748b75478406e989c69303756980Svetoslav Ganov } else { 933f804420d6e37748b75478406e989c69303756980Svetoslav Ganov event = prototype.split(pointerIdBits); 934f804420d6e37748b75478406e989c69303756980Svetoslav Ganov } 935f804420d6e37748b75478406e989c69303756980Svetoslav Ganov if (action == MotionEvent.ACTION_DOWN) { 936f804420d6e37748b75478406e989c69303756980Svetoslav Ganov event.setDownTime(event.getEventTime()); 937f804420d6e37748b75478406e989c69303756980Svetoslav Ganov } else { 9384213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov event.setDownTime(mInjectedPointerTracker.getLastInjectedDownEventTime()); 939f804420d6e37748b75478406e989c69303756980Svetoslav Ganov } 940f804420d6e37748b75478406e989c69303756980Svetoslav Ganov 941e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov // If the user is long pressing but the long pressing pointer 942e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov // was not exactly over the accessibility focused item we need 943e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov // to remap the location of that pointer so the user does not 944e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov // have to explicitly touch explore something to be able to 945e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov // long press it, or even worse to avoid the user long pressing 946e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov // on the wrong item since click and long press behave differently. 947e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov if (mLongPressingPointerId >= 0) { 948a3315270e0502189a6fa4de20b9db2bf042cfd2aSvetoslav event = offsetEvent(event, - mLongPressingPointerDeltaX, 949a3315270e0502189a6fa4de20b9db2bf042cfd2aSvetoslav - mLongPressingPointerDeltaY); 950e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov } 951e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov 952f804420d6e37748b75478406e989c69303756980Svetoslav Ganov if (DEBUG) { 9534213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov Slog.d(LOG_TAG, "Injecting event: " + event + ", policyFlags=0x" 954f804420d6e37748b75478406e989c69303756980Svetoslav Ganov + Integer.toHexString(policyFlags)); 955f804420d6e37748b75478406e989c69303756980Svetoslav Ganov } 956f804420d6e37748b75478406e989c69303756980Svetoslav Ganov 957f804420d6e37748b75478406e989c69303756980Svetoslav Ganov // Make sure that the user will see the event. 958f804420d6e37748b75478406e989c69303756980Svetoslav Ganov policyFlags |= WindowManagerPolicy.FLAG_PASS_TO_USER; 9591cf70bbf96930662cab0e699d70b62865766ff52Svetoslav Ganov if (mNext != null) { 96045af84a483165f06c04d74baba67f90da29c6ad2Svetoslav Ganov // TODO: For now pass null for the raw event since the touch 96145af84a483165f06c04d74baba67f90da29c6ad2Svetoslav Ganov // explorer is the last event transformation and it does 96245af84a483165f06c04d74baba67f90da29c6ad2Svetoslav Ganov // not care about the raw event. 96345af84a483165f06c04d74baba67f90da29c6ad2Svetoslav Ganov mNext.onMotionEvent(event, null, policyFlags); 9641cf70bbf96930662cab0e699d70b62865766ff52Svetoslav Ganov } 965f804420d6e37748b75478406e989c69303756980Svetoslav Ganov 9664213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov mInjectedPointerTracker.onMotionEvent(event); 9674213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov 968f804420d6e37748b75478406e989c69303756980Svetoslav Ganov if (event != prototype) { 969f804420d6e37748b75478406e989c69303756980Svetoslav Ganov event.recycle(); 970f804420d6e37748b75478406e989c69303756980Svetoslav Ganov } 971736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov } 972736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov 973736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov /** 974a3315270e0502189a6fa4de20b9db2bf042cfd2aSvetoslav * Offsets all pointers in the given event by adding the specified X and Y 975a3315270e0502189a6fa4de20b9db2bf042cfd2aSvetoslav * offsets. 976a3315270e0502189a6fa4de20b9db2bf042cfd2aSvetoslav * 977a3315270e0502189a6fa4de20b9db2bf042cfd2aSvetoslav * @param event The event to offset. 978a3315270e0502189a6fa4de20b9db2bf042cfd2aSvetoslav * @param offsetX The X offset. 979a3315270e0502189a6fa4de20b9db2bf042cfd2aSvetoslav * @param offsetY The Y offset. 980a3315270e0502189a6fa4de20b9db2bf042cfd2aSvetoslav * @return An event with the offset pointers or the original event if both 981a3315270e0502189a6fa4de20b9db2bf042cfd2aSvetoslav * offsets are zero. 982a3315270e0502189a6fa4de20b9db2bf042cfd2aSvetoslav */ 983a3315270e0502189a6fa4de20b9db2bf042cfd2aSvetoslav private MotionEvent offsetEvent(MotionEvent event, int offsetX, int offsetY) { 984a3315270e0502189a6fa4de20b9db2bf042cfd2aSvetoslav if (offsetX == 0 && offsetY == 0) { 985a3315270e0502189a6fa4de20b9db2bf042cfd2aSvetoslav return event; 986a3315270e0502189a6fa4de20b9db2bf042cfd2aSvetoslav } 987a3315270e0502189a6fa4de20b9db2bf042cfd2aSvetoslav final int remappedIndex = event.findPointerIndex(mLongPressingPointerId); 988a3315270e0502189a6fa4de20b9db2bf042cfd2aSvetoslav final int pointerCount = event.getPointerCount(); 989a3315270e0502189a6fa4de20b9db2bf042cfd2aSvetoslav PointerProperties[] props = PointerProperties.createArray(pointerCount); 990a3315270e0502189a6fa4de20b9db2bf042cfd2aSvetoslav PointerCoords[] coords = PointerCoords.createArray(pointerCount); 991a3315270e0502189a6fa4de20b9db2bf042cfd2aSvetoslav for (int i = 0; i < pointerCount; i++) { 992a3315270e0502189a6fa4de20b9db2bf042cfd2aSvetoslav event.getPointerProperties(i, props[i]); 993a3315270e0502189a6fa4de20b9db2bf042cfd2aSvetoslav event.getPointerCoords(i, coords[i]); 994a3315270e0502189a6fa4de20b9db2bf042cfd2aSvetoslav if (i == remappedIndex) { 995a3315270e0502189a6fa4de20b9db2bf042cfd2aSvetoslav coords[i].x += offsetX; 996a3315270e0502189a6fa4de20b9db2bf042cfd2aSvetoslav coords[i].y += offsetY; 997a3315270e0502189a6fa4de20b9db2bf042cfd2aSvetoslav } 998a3315270e0502189a6fa4de20b9db2bf042cfd2aSvetoslav } 999a3315270e0502189a6fa4de20b9db2bf042cfd2aSvetoslav return MotionEvent.obtain(event.getDownTime(), 1000a3315270e0502189a6fa4de20b9db2bf042cfd2aSvetoslav event.getEventTime(), event.getAction(), event.getPointerCount(), 1001a3315270e0502189a6fa4de20b9db2bf042cfd2aSvetoslav props, coords, event.getMetaState(), event.getButtonState(), 1002a3315270e0502189a6fa4de20b9db2bf042cfd2aSvetoslav 1.0f, 1.0f, event.getDeviceId(), event.getEdgeFlags(), 1003a3315270e0502189a6fa4de20b9db2bf042cfd2aSvetoslav event.getSource(), event.getFlags()); 1004a3315270e0502189a6fa4de20b9db2bf042cfd2aSvetoslav } 1005a3315270e0502189a6fa4de20b9db2bf042cfd2aSvetoslav 1006a3315270e0502189a6fa4de20b9db2bf042cfd2aSvetoslav /** 1007736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov * Computes the action for an injected event based on a masked action 1008736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov * and a pointer index. 1009736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov * 1010736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov * @param actionMasked The masked action. 1011736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov * @param pointerIndex The index of the pointer which has changed. 1012736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov * @return The action to be used for injection. 1013736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov */ 1014736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov private int computeInjectionAction(int actionMasked, int pointerIndex) { 1015736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov switch (actionMasked) { 1016736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov case MotionEvent.ACTION_DOWN: 1017736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov case MotionEvent.ACTION_POINTER_DOWN: { 10184213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov InjectedPointerTracker injectedTracker = mInjectedPointerTracker; 1019736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov // Compute the action based on how many down pointers are injected. 10204213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov if (injectedTracker.getInjectedPointerDownCount() == 0) { 1021736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov return MotionEvent.ACTION_DOWN; 1022736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov } else { 1023736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov return (pointerIndex << MotionEvent.ACTION_POINTER_INDEX_SHIFT) 1024736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov | MotionEvent.ACTION_POINTER_DOWN; 1025736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov } 1026736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov } 1027736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov case MotionEvent.ACTION_POINTER_UP: { 10284213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov InjectedPointerTracker injectedTracker = mInjectedPointerTracker; 1029736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov // Compute the action based on how many down pointers are injected. 10304213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov if (injectedTracker.getInjectedPointerDownCount() == 1) { 1031736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov return MotionEvent.ACTION_UP; 1032736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov } else { 1033736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov return (pointerIndex << MotionEvent.ACTION_POINTER_INDEX_SHIFT) 1034736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov | MotionEvent.ACTION_POINTER_UP; 1035736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov } 1036736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov } 1037736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov default: 1038736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov return actionMasked; 1039736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov } 1040736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov } 1041736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov 1042736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov /** 1043736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov * Determines whether a two pointer gesture is a dragging one. 1044736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov * 1045736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov * @param event The event with the pointer data. 1046736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov * @return True if the gesture is a dragging one. 1047736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov */ 1048736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov private boolean isDraggingGesture(MotionEvent event) { 10494213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov ReceivedPointerTracker receivedTracker = mReceivedPointerTracker; 1050736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov 105184044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav final float firstPtrX = event.getX(0); 105284044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav final float firstPtrY = event.getY(0); 105384044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav final float secondPtrX = event.getX(1); 105484044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav final float secondPtrY = event.getY(1); 1055736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov 105684044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav final float firstPtrDownX = receivedTracker.getReceivedPointerDownX(0); 105784044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav final float firstPtrDownY = receivedTracker.getReceivedPointerDownY(0); 105884044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav final float secondPtrDownX = receivedTracker.getReceivedPointerDownX(1); 105984044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav final float secondPtrDownY = receivedTracker.getReceivedPointerDownY(1); 1060736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov 10611cf70bbf96930662cab0e699d70b62865766ff52Svetoslav Ganov return GestureUtils.isDraggingGesture(firstPtrDownX, firstPtrDownY, secondPtrDownX, 10621cf70bbf96930662cab0e699d70b62865766ff52Svetoslav Ganov secondPtrDownY, firstPtrX, firstPtrY, secondPtrX, secondPtrY, 10631cf70bbf96930662cab0e699d70b62865766ff52Svetoslav Ganov MAX_DRAGGING_ANGLE_COS); 1064736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov } 1065736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov 1066ded133c446fa9d0d23e6bde19a66fb2ce3980491Svetoslav private int computeClickLocation(Point outLocation) { 106747b9c1524fe20b9ae2b210acdcad64f764df68e0Svetoslav MotionEvent lastExploreEvent = mInjectedPointerTracker.getLastInjectedHoverEventForClick(); 106847b9c1524fe20b9ae2b210acdcad64f764df68e0Svetoslav if (lastExploreEvent != null) { 106947b9c1524fe20b9ae2b210acdcad64f764df68e0Svetoslav final int lastExplorePointerIndex = lastExploreEvent.getActionIndex(); 107047b9c1524fe20b9ae2b210acdcad64f764df68e0Svetoslav outLocation.x = (int) lastExploreEvent.getX(lastExplorePointerIndex); 107147b9c1524fe20b9ae2b210acdcad64f764df68e0Svetoslav outLocation.y = (int) lastExploreEvent.getY(lastExplorePointerIndex); 107247b9c1524fe20b9ae2b210acdcad64f764df68e0Svetoslav if (!mAms.accessibilityFocusOnlyInActiveWindow() 107347b9c1524fe20b9ae2b210acdcad64f764df68e0Svetoslav || mLastTouchedWindowId == mAms.getActiveWindowId()) { 1074ded133c446fa9d0d23e6bde19a66fb2ce3980491Svetoslav if (mAms.getAccessibilityFocusClickPointInScreen(outLocation)) { 1075ded133c446fa9d0d23e6bde19a66fb2ce3980491Svetoslav return CLICK_LOCATION_ACCESSIBILITY_FOCUS; 1076ded133c446fa9d0d23e6bde19a66fb2ce3980491Svetoslav } else { 1077ded133c446fa9d0d23e6bde19a66fb2ce3980491Svetoslav return CLICK_LOCATION_LAST_TOUCH_EXPLORED; 1078ded133c446fa9d0d23e6bde19a66fb2ce3980491Svetoslav } 107947b9c1524fe20b9ae2b210acdcad64f764df68e0Svetoslav } 108047b9c1524fe20b9ae2b210acdcad64f764df68e0Svetoslav } 108147b9c1524fe20b9ae2b210acdcad64f764df68e0Svetoslav if (mAms.getAccessibilityFocusClickPointInScreen(outLocation)) { 1082ded133c446fa9d0d23e6bde19a66fb2ce3980491Svetoslav return CLICK_LOCATION_ACCESSIBILITY_FOCUS; 108347b9c1524fe20b9ae2b210acdcad64f764df68e0Svetoslav } 1084ded133c446fa9d0d23e6bde19a66fb2ce3980491Svetoslav return CLICK_LOCATION_NONE; 108547b9c1524fe20b9ae2b210acdcad64f764df68e0Svetoslav } 108647b9c1524fe20b9ae2b210acdcad64f764df68e0Svetoslav 1087736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov /** 108851cccf0845b36539d42503495f0689d487712b3aSvetoslav Ganov * Gets the symbolic name of a state. 108951cccf0845b36539d42503495f0689d487712b3aSvetoslav Ganov * 109051cccf0845b36539d42503495f0689d487712b3aSvetoslav Ganov * @param state A state. 109151cccf0845b36539d42503495f0689d487712b3aSvetoslav Ganov * @return The state symbolic name. 109251cccf0845b36539d42503495f0689d487712b3aSvetoslav Ganov */ 109351cccf0845b36539d42503495f0689d487712b3aSvetoslav Ganov private static String getStateSymbolicName(int state) { 109451cccf0845b36539d42503495f0689d487712b3aSvetoslav Ganov switch (state) { 109551cccf0845b36539d42503495f0689d487712b3aSvetoslav Ganov case STATE_TOUCH_EXPLORING: 109651cccf0845b36539d42503495f0689d487712b3aSvetoslav Ganov return "STATE_TOUCH_EXPLORING"; 109751cccf0845b36539d42503495f0689d487712b3aSvetoslav Ganov case STATE_DRAGGING: 109851cccf0845b36539d42503495f0689d487712b3aSvetoslav Ganov return "STATE_DRAGGING"; 109951cccf0845b36539d42503495f0689d487712b3aSvetoslav Ganov case STATE_DELEGATING: 110051cccf0845b36539d42503495f0689d487712b3aSvetoslav Ganov return "STATE_DELEGATING"; 11014213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov case STATE_GESTURE_DETECTING: 11024213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov return "STATE_GESTURE_DETECTING"; 110351cccf0845b36539d42503495f0689d487712b3aSvetoslav Ganov default: 110451cccf0845b36539d42503495f0689d487712b3aSvetoslav Ganov throw new IllegalArgumentException("Unknown state: " + state); 110551cccf0845b36539d42503495f0689d487712b3aSvetoslav Ganov } 110651cccf0845b36539d42503495f0689d487712b3aSvetoslav Ganov } 110751cccf0845b36539d42503495f0689d487712b3aSvetoslav Ganov 110851cccf0845b36539d42503495f0689d487712b3aSvetoslav Ganov /** 110995068e5d1bea47091e97955f271c789264994550Svetoslav Ganov * Class for delayed exiting from gesture detecting mode. 111095068e5d1bea47091e97955f271c789264994550Svetoslav Ganov */ 111195068e5d1bea47091e97955f271c789264994550Svetoslav Ganov private final class ExitGestureDetectionModeDelayed implements Runnable { 111295068e5d1bea47091e97955f271c789264994550Svetoslav Ganov 111395068e5d1bea47091e97955f271c789264994550Svetoslav Ganov public void post() { 111495068e5d1bea47091e97955f271c789264994550Svetoslav Ganov mHandler.postDelayed(this, EXIT_GESTURE_DETECTION_TIMEOUT); 111595068e5d1bea47091e97955f271c789264994550Svetoslav Ganov } 111695068e5d1bea47091e97955f271c789264994550Svetoslav Ganov 111784044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav public void cancel() { 111895068e5d1bea47091e97955f271c789264994550Svetoslav Ganov mHandler.removeCallbacks(this); 111995068e5d1bea47091e97955f271c789264994550Svetoslav Ganov } 112095068e5d1bea47091e97955f271c789264994550Svetoslav Ganov 112195068e5d1bea47091e97955f271c789264994550Svetoslav Ganov @Override 112295068e5d1bea47091e97955f271c789264994550Svetoslav Ganov public void run() { 1123aed4b6f812674bc60a04470013ca449e5c114fa5Svetoslav Ganov // Announce the end of gesture recognition. 1124aed4b6f812674bc60a04470013ca449e5c114fa5Svetoslav Ganov sendAccessibilityEvent(AccessibilityEvent.TYPE_GESTURE_DETECTION_END); 1125aed4b6f812674bc60a04470013ca449e5c114fa5Svetoslav Ganov // Clearing puts is in touch exploration state with a finger already 1126aed4b6f812674bc60a04470013ca449e5c114fa5Svetoslav Ganov // down, so announce the transition to exploration state. 1127aed4b6f812674bc60a04470013ca449e5c114fa5Svetoslav Ganov sendAccessibilityEvent(AccessibilityEvent.TYPE_TOUCH_EXPLORATION_GESTURE_START); 112895068e5d1bea47091e97955f271c789264994550Svetoslav Ganov clear(); 112995068e5d1bea47091e97955f271c789264994550Svetoslav Ganov } 113095068e5d1bea47091e97955f271c789264994550Svetoslav Ganov } 113195068e5d1bea47091e97955f271c789264994550Svetoslav Ganov 113295068e5d1bea47091e97955f271c789264994550Svetoslav Ganov /** 113384044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav * Class for delayed sending of hover enter and move events. 11344213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov */ 113584044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav class SendHoverEnterAndMoveDelayed implements Runnable { 113684044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav private final String LOG_TAG_SEND_HOVER_DELAYED = "SendHoverEnterAndMoveDelayed"; 1137e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov 113884044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav private final List<MotionEvent> mEvents = new ArrayList<MotionEvent>(); 1139e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov 11404213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov private int mPointerIdBits; 11414213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov private int mPolicyFlags; 11424213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov 114384044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav public void post(MotionEvent event, boolean touchExplorationInProgress, 114477276b60851a158ad3e142cb3b091d57ae5ceffbSvetoslav Ganov int pointerIdBits, int policyFlags) { 114584044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav cancel(); 114684044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav addEvent(event); 11474213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov mPointerIdBits = pointerIdBits; 11484213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov mPolicyFlags = policyFlags; 1149e47957a0bbe2164467ff6e7a566b0c9e4689cdc9Svetoslav Ganov mHandler.postDelayed(this, mDetermineUserIntentTimeout); 1150e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov } 1151e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov 115284044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav public void addEvent(MotionEvent event) { 115384044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav mEvents.add(MotionEvent.obtain(event)); 115484044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav } 115584044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav 115684044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav public void cancel() { 1157e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov if (isPending()) { 115884044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav mHandler.removeCallbacks(this); 115984044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav clear(); 116084044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav } 116184044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav } 116284044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav 116384044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav private boolean isPending() { 116484044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav return mHandler.hasCallbacks(this); 116584044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav } 116684044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav 116784044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav private void clear() { 116884044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav mPointerIdBits = -1; 116984044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav mPolicyFlags = 0; 117084044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav final int eventCount = mEvents.size(); 117184044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav for (int i = eventCount - 1; i >= 0; i--) { 117284044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav mEvents.remove(i).recycle(); 1173e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov } 1174e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov } 1175e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov 117684044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav public void forceSendAndRemove() { 1177e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov if (isPending()) { 117884044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav run(); 117984044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav cancel(); 1180e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov } 11814213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov } 11824213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov 118384044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav public void run() { 118484044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav // Send an accessibility event to announce the touch exploration start. 118584044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav sendAccessibilityEvent(AccessibilityEvent.TYPE_TOUCH_EXPLORATION_GESTURE_START); 118684044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav 118784044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav if (!mEvents.isEmpty()) { 118884044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav // Deliver a down event. 118984044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav sendMotionEvent(mEvents.get(0), MotionEvent.ACTION_HOVER_ENTER, 119084044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav mPointerIdBits, mPolicyFlags); 119184044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav if (DEBUG) { 119284044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav Slog.d(LOG_TAG_SEND_HOVER_DELAYED, 119384044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav "Injecting motion event: ACTION_HOVER_ENTER"); 119484044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav } 119584044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav 119684044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav // Deliver move events. 119784044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav final int eventCount = mEvents.size(); 119884044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav for (int i = 1; i < eventCount; i++) { 119984044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav sendMotionEvent(mEvents.get(i), MotionEvent.ACTION_HOVER_MOVE, 120084044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav mPointerIdBits, mPolicyFlags); 120184044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav if (DEBUG) { 120284044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav Slog.d(LOG_TAG_SEND_HOVER_DELAYED, 120384044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav "Injecting motion event: ACTION_HOVER_MOVE"); 120484044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav } 120584044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav } 120684044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav } 12074213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov clear(); 12084213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov } 120984044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav } 121084044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav 121184044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav /** 121284044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav * Class for delayed sending of hover exit events. 121384044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav */ 121484044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav class SendHoverExitDelayed implements Runnable { 121584044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav private final String LOG_TAG_SEND_HOVER_DELAYED = "SendHoverExitDelayed"; 121684044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav 121784044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav private MotionEvent mPrototype; 121884044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav private int mPointerIdBits; 121984044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav private int mPolicyFlags; 122084044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav 122184044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav public void post(MotionEvent prototype, int pointerIdBits, int policyFlags) { 122284044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav cancel(); 122384044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav mPrototype = MotionEvent.obtain(prototype); 122484044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav mPointerIdBits = pointerIdBits; 122584044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav mPolicyFlags = policyFlags; 122684044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav mHandler.postDelayed(this, mDetermineUserIntentTimeout); 122784044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav } 122884044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav 122984044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav public void cancel() { 123084044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav if (isPending()) { 123184044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav mHandler.removeCallbacks(this); 123284044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav clear(); 123384044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav } 123484044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav } 12354213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov 1236e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov private boolean isPending() { 123784044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav return mHandler.hasCallbacks(this); 12384213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov } 12394213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov 12404213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov private void clear() { 1241e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov mPrototype.recycle(); 1242e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov mPrototype = null; 12434213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov mPointerIdBits = -1; 12444213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov mPolicyFlags = 0; 12454213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov } 12464213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov 12474213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov public void forceSendAndRemove() { 1248e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov if (isPending()) { 12494213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov run(); 125084044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav cancel(); 12514213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov } 12524213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov } 12534213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov 12544213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov public void run() { 12554213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov if (DEBUG) { 125684044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav Slog.d(LOG_TAG_SEND_HOVER_DELAYED, "Injecting motion event:" 125784044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav + " ACTION_HOVER_EXIT"); 12584213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov } 125984044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav sendMotionEvent(mPrototype, MotionEvent.ACTION_HOVER_EXIT, 126084044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav mPointerIdBits, mPolicyFlags); 126184044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav if (!mSendTouchExplorationEndDelayed.isPending()) { 126284044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav mSendTouchExplorationEndDelayed.cancel(); 126384044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav mSendTouchExplorationEndDelayed.post(); 126484044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav } 126584044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav if (mSendTouchInteractionEndDelayed.isPending()) { 126684044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav mSendTouchInteractionEndDelayed.cancel(); 126784044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav mSendTouchInteractionEndDelayed.post(); 1268e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov } 12694213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov clear(); 12704213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov } 12714213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov } 12724213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov 1273f068fed6c4c3fc2003aec19b6e7e892358179b02Svetoslav Ganov private class SendAccessibilityEventDelayed implements Runnable { 1274f068fed6c4c3fc2003aec19b6e7e892358179b02Svetoslav Ganov private final int mEventType; 1275f068fed6c4c3fc2003aec19b6e7e892358179b02Svetoslav Ganov private final int mDelay; 1276f068fed6c4c3fc2003aec19b6e7e892358179b02Svetoslav Ganov 1277f068fed6c4c3fc2003aec19b6e7e892358179b02Svetoslav Ganov public SendAccessibilityEventDelayed(int eventType, int delay) { 1278f068fed6c4c3fc2003aec19b6e7e892358179b02Svetoslav Ganov mEventType = eventType; 1279f068fed6c4c3fc2003aec19b6e7e892358179b02Svetoslav Ganov mDelay = delay; 1280f068fed6c4c3fc2003aec19b6e7e892358179b02Svetoslav Ganov } 1281fe304b893968887323b93764caafa66ee8ad44deSvetoslav Ganov 128284044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav public void cancel() { 1283fe304b893968887323b93764caafa66ee8ad44deSvetoslav Ganov mHandler.removeCallbacks(this); 1284fe304b893968887323b93764caafa66ee8ad44deSvetoslav Ganov } 1285fe304b893968887323b93764caafa66ee8ad44deSvetoslav Ganov 1286fe304b893968887323b93764caafa66ee8ad44deSvetoslav Ganov public void post() { 1287f068fed6c4c3fc2003aec19b6e7e892358179b02Svetoslav Ganov mHandler.postDelayed(this, mDelay); 1288fe304b893968887323b93764caafa66ee8ad44deSvetoslav Ganov } 1289fe304b893968887323b93764caafa66ee8ad44deSvetoslav Ganov 1290fe304b893968887323b93764caafa66ee8ad44deSvetoslav Ganov public boolean isPending() { 1291fe304b893968887323b93764caafa66ee8ad44deSvetoslav Ganov return mHandler.hasCallbacks(this); 1292fe304b893968887323b93764caafa66ee8ad44deSvetoslav Ganov } 1293fe304b893968887323b93764caafa66ee8ad44deSvetoslav Ganov 1294fe304b893968887323b93764caafa66ee8ad44deSvetoslav Ganov public void forceSendAndRemove() { 1295fe304b893968887323b93764caafa66ee8ad44deSvetoslav Ganov if (isPending()) { 1296fe304b893968887323b93764caafa66ee8ad44deSvetoslav Ganov run(); 129784044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav cancel(); 1298fe304b893968887323b93764caafa66ee8ad44deSvetoslav Ganov } 1299fe304b893968887323b93764caafa66ee8ad44deSvetoslav Ganov } 1300fe304b893968887323b93764caafa66ee8ad44deSvetoslav Ganov 1301fe304b893968887323b93764caafa66ee8ad44deSvetoslav Ganov @Override 1302fe304b893968887323b93764caafa66ee8ad44deSvetoslav Ganov public void run() { 1303f068fed6c4c3fc2003aec19b6e7e892358179b02Svetoslav Ganov sendAccessibilityEvent(mEventType); 1304fe304b893968887323b93764caafa66ee8ad44deSvetoslav Ganov } 1305fe304b893968887323b93764caafa66ee8ad44deSvetoslav Ganov } 1306fe304b893968887323b93764caafa66ee8ad44deSvetoslav Ganov 13074213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov @Override 13084213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov public String toString() { 13094213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov return LOG_TAG; 13104213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov } 13114213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov 13124213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov class InjectedPointerTracker { 13134213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov private static final String LOG_TAG_INJECTED_POINTER_TRACKER = "InjectedPointerTracker"; 13144213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov 13154213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov // Keep track of which pointers sent to the system are down. 13164213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov private int mInjectedPointersDown; 13174213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov 13184213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov // The time of the last injected down. 13194213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov private long mLastInjectedDownEventTime; 13204213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov 1321e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov // The last injected hover event. 1322e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov private MotionEvent mLastInjectedHoverEvent; 13234213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov 13245d043ce8cc2f588fdfb336cc843fb3b07b196f83Svetoslav Ganov // The last injected hover event used for performing clicks. 13255d043ce8cc2f588fdfb336cc843fb3b07b196f83Svetoslav Ganov private MotionEvent mLastInjectedHoverEventForClick; 13265d043ce8cc2f588fdfb336cc843fb3b07b196f83Svetoslav Ganov 13274213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov /** 13284213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov * Processes an injected {@link MotionEvent} event. 13294213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov * 13304213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov * @param event The event to process. 13314213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov */ 13324213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov public void onMotionEvent(MotionEvent event) { 13334213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov final int action = event.getActionMasked(); 13344213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov switch (action) { 13354213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov case MotionEvent.ACTION_DOWN: 13364213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov case MotionEvent.ACTION_POINTER_DOWN: { 13374213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov final int pointerId = event.getPointerId(event.getActionIndex()); 13384213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov final int pointerFlag = (1 << pointerId); 13394213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov mInjectedPointersDown |= pointerFlag; 13404213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov mLastInjectedDownEventTime = event.getDownTime(); 13414213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov } break; 13424213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov case MotionEvent.ACTION_UP: 13434213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov case MotionEvent.ACTION_POINTER_UP: { 13444213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov final int pointerId = event.getPointerId(event.getActionIndex()); 13454213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov final int pointerFlag = (1 << pointerId); 13464213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov mInjectedPointersDown &= ~pointerFlag; 13474213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov if (mInjectedPointersDown == 0) { 13484213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov mLastInjectedDownEventTime = 0; 13494213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov } 13504213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov } break; 13514213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov case MotionEvent.ACTION_HOVER_ENTER: 13524213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov case MotionEvent.ACTION_HOVER_MOVE: 13534213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov case MotionEvent.ACTION_HOVER_EXIT: { 1354e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov if (mLastInjectedHoverEvent != null) { 1355e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov mLastInjectedHoverEvent.recycle(); 1356e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov } 1357e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov mLastInjectedHoverEvent = MotionEvent.obtain(event); 13585d043ce8cc2f588fdfb336cc843fb3b07b196f83Svetoslav Ganov if (mLastInjectedHoverEventForClick != null) { 13595d043ce8cc2f588fdfb336cc843fb3b07b196f83Svetoslav Ganov mLastInjectedHoverEventForClick.recycle(); 13605d043ce8cc2f588fdfb336cc843fb3b07b196f83Svetoslav Ganov } 13615d043ce8cc2f588fdfb336cc843fb3b07b196f83Svetoslav Ganov mLastInjectedHoverEventForClick = MotionEvent.obtain(event); 13624213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov } break; 13634213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov } 13644213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov if (DEBUG) { 1365e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov Slog.i(LOG_TAG_INJECTED_POINTER_TRACKER, "Injected pointer:\n" + toString()); 13664213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov } 13674213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov } 13684213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov 13694213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov /** 13704213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov * Clears the internals state. 13714213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov */ 13724213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov public void clear() { 13734213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov mInjectedPointersDown = 0; 13744213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov } 13754213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov 13764213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov /** 13774213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov * @return The time of the last injected down event. 13784213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov */ 13794213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov public long getLastInjectedDownEventTime() { 13804213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov return mLastInjectedDownEventTime; 13814213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov } 13824213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov 13834213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov /** 13844213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov * @return The number of down pointers injected to the view hierarchy. 13854213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov */ 13864213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov public int getInjectedPointerDownCount() { 13874213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov return Integer.bitCount(mInjectedPointersDown); 13884213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov } 13894213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov 13904213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov /** 13914213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov * @return The bits of the injected pointers that are down. 13924213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov */ 13934213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov public int getInjectedPointersDown() { 13944213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov return mInjectedPointersDown; 13954213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov } 13964213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov 13974213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov /** 13984213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov * Whether an injected pointer is down. 13994213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov * 14004213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov * @param pointerId The unique pointer id. 14014213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov * @return True if the pointer is down. 14024213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov */ 14034213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov public boolean isInjectedPointerDown(int pointerId) { 14044213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov final int pointerFlag = (1 << pointerId); 14054213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov return (mInjectedPointersDown & pointerFlag) != 0; 14064213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov } 14074213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov 14084213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov /** 1409e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov * @return The the last injected hover event. 14104213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov */ 1411e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov public MotionEvent getLastInjectedHoverEvent() { 1412e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov return mLastInjectedHoverEvent; 14134213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov } 14144213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov 14155d043ce8cc2f588fdfb336cc843fb3b07b196f83Svetoslav Ganov /** 14165d043ce8cc2f588fdfb336cc843fb3b07b196f83Svetoslav Ganov * @return The the last injected hover event. 14175d043ce8cc2f588fdfb336cc843fb3b07b196f83Svetoslav Ganov */ 14185d043ce8cc2f588fdfb336cc843fb3b07b196f83Svetoslav Ganov public MotionEvent getLastInjectedHoverEventForClick() { 14195d043ce8cc2f588fdfb336cc843fb3b07b196f83Svetoslav Ganov return mLastInjectedHoverEventForClick; 14205d043ce8cc2f588fdfb336cc843fb3b07b196f83Svetoslav Ganov } 14215d043ce8cc2f588fdfb336cc843fb3b07b196f83Svetoslav Ganov 14224213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov @Override 14234213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov public String toString() { 14244213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov StringBuilder builder = new StringBuilder(); 14254213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov builder.append("========================="); 14264213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov builder.append("\nDown pointers #"); 14274213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov builder.append(Integer.bitCount(mInjectedPointersDown)); 14284213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov builder.append(" [ "); 14294213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov for (int i = 0; i < MAX_POINTER_COUNT; i++) { 14304213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov if ((mInjectedPointersDown & i) != 0) { 14314213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov builder.append(i); 14324213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov builder.append(" "); 14334213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov } 14344213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov } 14354213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov builder.append("]"); 14364213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov builder.append("\n========================="); 14374213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov return builder.toString(); 14384213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov } 14394213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov } 14404213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov 14414213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov class ReceivedPointerTracker { 14424213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov private static final String LOG_TAG_RECEIVED_POINTER_TRACKER = "ReceivedPointerTracker"; 1443736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov 1444736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov // Keep track of where and when a pointer went down. 1445736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov private final float[] mReceivedPointerDownX = new float[MAX_POINTER_COUNT]; 1446736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov private final float[] mReceivedPointerDownY = new float[MAX_POINTER_COUNT]; 1447736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov private final long[] mReceivedPointerDownTime = new long[MAX_POINTER_COUNT]; 1448736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov 1449736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov // Which pointers are down. 1450736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov private int mReceivedPointersDown; 1451736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov 1452c4842c11932ea4f60fe7ae09b0a59660207e1587Svetoslav Ganov // The edge flags of the last received down event. 1453c4842c11932ea4f60fe7ae09b0a59660207e1587Svetoslav Ganov private int mLastReceivedDownEdgeFlags; 1454c4842c11932ea4f60fe7ae09b0a59660207e1587Svetoslav Ganov 145584044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav // Primary pointer which is either the first that went down 145684044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav // or if it goes up the next one that most recently went down. 145784044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav private int mPrimaryPointerId; 1458736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov 1459736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov // Keep track of the last up pointer data. 1460736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov private long mLastReceivedUpPointerDownTime; 14614213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov private float mLastReceivedUpPointerDownX; 14624213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov private float mLastReceivedUpPointerDownY; 1463736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov 1464e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov private MotionEvent mLastReceivedEvent; 1465e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov 1466736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov /** 1467736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov * Clears the internals state. 1468736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov */ 1469736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov public void clear() { 1470736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov Arrays.fill(mReceivedPointerDownX, 0); 1471736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov Arrays.fill(mReceivedPointerDownY, 0); 1472736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov Arrays.fill(mReceivedPointerDownTime, 0); 1473736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov mReceivedPointersDown = 0; 147484044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav mPrimaryPointerId = 0; 1475736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov mLastReceivedUpPointerDownTime = 0; 14764213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov mLastReceivedUpPointerDownX = 0; 14774213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov mLastReceivedUpPointerDownY = 0; 1478736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov } 1479736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov 1480736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov /** 1481736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov * Processes a received {@link MotionEvent} event. 1482736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov * 1483736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov * @param event The event to process. 1484736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov */ 14854213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov public void onMotionEvent(MotionEvent event) { 1486e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov if (mLastReceivedEvent != null) { 1487e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov mLastReceivedEvent.recycle(); 1488e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov } 1489e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov mLastReceivedEvent = MotionEvent.obtain(event); 1490e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov 1491736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov final int action = event.getActionMasked(); 1492736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov switch (action) { 1493736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov case MotionEvent.ACTION_DOWN: { 149400f7b3f76515d1c6fbe5cf9fee9d3760787c03cdSvetoslav Ganov handleReceivedPointerDown(event.getActionIndex(), event); 1495736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov } break; 1496736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov case MotionEvent.ACTION_POINTER_DOWN: { 1497736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov handleReceivedPointerDown(event.getActionIndex(), event); 1498736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov } break; 1499736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov case MotionEvent.ACTION_UP: { 150000f7b3f76515d1c6fbe5cf9fee9d3760787c03cdSvetoslav Ganov handleReceivedPointerUp(event.getActionIndex(), event); 1501736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov } break; 1502736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov case MotionEvent.ACTION_POINTER_UP: { 1503736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov handleReceivedPointerUp(event.getActionIndex(), event); 1504736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov } break; 1505736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov } 1506736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov if (DEBUG) { 150738c992841b5f6bc80359dbf60d31aa7b994160fcSvetoslav Ganov Slog.i(LOG_TAG_RECEIVED_POINTER_TRACKER, "Received pointer:\n" + toString()); 1508736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov } 1509736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov } 1510736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov 1511736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov /** 1512e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov * @return The last received event. 1513e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov */ 1514e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov public MotionEvent getLastReceivedEvent() { 1515e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov return mLastReceivedEvent; 1516e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov } 1517e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov 1518e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov /** 15194213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov * @return The number of received pointers that are down. 1520736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov */ 15214213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov public int getReceivedPointerDownCount() { 15224213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov return Integer.bitCount(mReceivedPointersDown); 1523736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov } 1524736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov 1525736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov /** 1526736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov * Whether an received pointer is down. 1527736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov * 1528736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov * @param pointerId The unique pointer id. 1529736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov * @return True if the pointer is down. 1530736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov */ 1531736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov public boolean isReceivedPointerDown(int pointerId) { 1532736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov final int pointerFlag = (1 << pointerId); 1533736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov return (mReceivedPointersDown & pointerFlag) != 0; 1534736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov } 1535736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov 1536736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov /** 1537736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov * @param pointerId The unique pointer id. 1538736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov * @return The X coordinate where the pointer went down. 1539736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov */ 1540736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov public float getReceivedPointerDownX(int pointerId) { 1541736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov return mReceivedPointerDownX[pointerId]; 1542736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov } 1543736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov 1544736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov /** 1545736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov * @param pointerId The unique pointer id. 1546736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov * @return The Y coordinate where the pointer went down. 1547736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov */ 1548736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov public float getReceivedPointerDownY(int pointerId) { 1549736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov return mReceivedPointerDownY[pointerId]; 1550736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov } 1551736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov 1552736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov /** 1553736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov * @param pointerId The unique pointer id. 1554736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov * @return The time when the pointer went down. 1555736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov */ 1556736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov public long getReceivedPointerDownTime(int pointerId) { 1557736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov return mReceivedPointerDownTime[pointerId]; 1558736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov } 1559736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov 1560736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov /** 1561736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov * @return The id of the primary pointer. 1562736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov */ 156384044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav public int getPrimaryPointerId() { 156484044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav if (mPrimaryPointerId == INVALID_POINTER_ID) { 156538c992841b5f6bc80359dbf60d31aa7b994160fcSvetoslav Ganov mPrimaryPointerId = findPrimaryPointerId(); 1566736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov } 156784044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav return mPrimaryPointerId; 1568736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov } 1569736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov 1570736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov /** 1571736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov * @return The time when the last up received pointer went down. 1572736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov */ 1573736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov public long getLastReceivedUpPointerDownTime() { 1574736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov return mLastReceivedUpPointerDownTime; 1575736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov } 1576736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov 1577736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov /** 15784213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov * @return The down X of the last received pointer that went up. 1579736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov */ 15804213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov public float getLastReceivedUpPointerDownX() { 15814213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov return mLastReceivedUpPointerDownX; 1582736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov } 1583736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov 1584736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov /** 15854213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov * @return The down Y of the last received pointer that went up. 1586736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov */ 15874213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov public float getLastReceivedUpPointerDownY() { 15884213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov return mLastReceivedUpPointerDownY; 1589736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov } 1590736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov 1591736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov /** 1592c4842c11932ea4f60fe7ae09b0a59660207e1587Svetoslav Ganov * @return The edge flags of the last received down event. 1593c4842c11932ea4f60fe7ae09b0a59660207e1587Svetoslav Ganov */ 1594c4842c11932ea4f60fe7ae09b0a59660207e1587Svetoslav Ganov public int getLastReceivedDownEdgeFlags() { 1595c4842c11932ea4f60fe7ae09b0a59660207e1587Svetoslav Ganov return mLastReceivedDownEdgeFlags; 1596c4842c11932ea4f60fe7ae09b0a59660207e1587Svetoslav Ganov } 1597c4842c11932ea4f60fe7ae09b0a59660207e1587Svetoslav Ganov 1598c4842c11932ea4f60fe7ae09b0a59660207e1587Svetoslav Ganov /** 1599736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov * Handles a received pointer down event. 1600736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov * 1601736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov * @param pointerIndex The index of the pointer that has changed. 1602736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov * @param event The event to be handled. 1603736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov */ 1604736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov private void handleReceivedPointerDown(int pointerIndex, MotionEvent event) { 1605736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov final int pointerId = event.getPointerId(pointerIndex); 1606736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov final int pointerFlag = (1 << pointerId); 1607736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov 1608736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov mLastReceivedUpPointerDownTime = 0; 16094213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov mLastReceivedUpPointerDownX = 0; 16104213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov mLastReceivedUpPointerDownX = 0; 1611736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov 1612c4842c11932ea4f60fe7ae09b0a59660207e1587Svetoslav Ganov mLastReceivedDownEdgeFlags = event.getEdgeFlags(); 1613c4842c11932ea4f60fe7ae09b0a59660207e1587Svetoslav Ganov 1614736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov mReceivedPointersDown |= pointerFlag; 1615736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov mReceivedPointerDownX[pointerId] = event.getX(pointerIndex); 1616736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov mReceivedPointerDownY[pointerId] = event.getY(pointerIndex); 1617736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov mReceivedPointerDownTime[pointerId] = event.getEventTime(); 1618736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov 161984044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav mPrimaryPointerId = pointerId; 1620736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov } 1621736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov 1622736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov /** 1623736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov * Handles a received pointer up event. 1624736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov * 1625736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov * @param pointerIndex The index of the pointer that has changed. 1626736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov * @param event The event to be handled. 1627736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov */ 1628736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov private void handleReceivedPointerUp(int pointerIndex, MotionEvent event) { 1629736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov final int pointerId = event.getPointerId(pointerIndex); 1630736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov final int pointerFlag = (1 << pointerId); 1631736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov 1632736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov mLastReceivedUpPointerDownTime = getReceivedPointerDownTime(pointerId); 16334213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov mLastReceivedUpPointerDownX = mReceivedPointerDownX[pointerId]; 16344213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov mLastReceivedUpPointerDownY = mReceivedPointerDownY[pointerId]; 1635736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov 1636736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov mReceivedPointersDown &= ~pointerFlag; 1637736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov mReceivedPointerDownX[pointerId] = 0; 1638736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov mReceivedPointerDownY[pointerId] = 0; 1639736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov mReceivedPointerDownTime[pointerId] = 0; 1640736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov 164184044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav if (mPrimaryPointerId == pointerId) { 164284044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav mPrimaryPointerId = INVALID_POINTER_ID; 1643736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov } 1644736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov } 1645736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov 1646736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov /** 164738c992841b5f6bc80359dbf60d31aa7b994160fcSvetoslav Ganov * @return The primary pointer id. 1648736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov */ 164938c992841b5f6bc80359dbf60d31aa7b994160fcSvetoslav Ganov private int findPrimaryPointerId() { 165084044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav int primaryPointerId = INVALID_POINTER_ID; 1651736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov long minDownTime = Long.MAX_VALUE; 165238c992841b5f6bc80359dbf60d31aa7b994160fcSvetoslav Ganov 165384044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav // Find the pointer that went down first. 165438c992841b5f6bc80359dbf60d31aa7b994160fcSvetoslav Ganov int pointerIdBits = mReceivedPointersDown; 165538c992841b5f6bc80359dbf60d31aa7b994160fcSvetoslav Ganov while (pointerIdBits > 0) { 165638c992841b5f6bc80359dbf60d31aa7b994160fcSvetoslav Ganov final int pointerId = Integer.numberOfTrailingZeros(pointerIdBits); 165738c992841b5f6bc80359dbf60d31aa7b994160fcSvetoslav Ganov pointerIdBits &= ~(1 << pointerId); 165838c992841b5f6bc80359dbf60d31aa7b994160fcSvetoslav Ganov final long downPointerTime = mReceivedPointerDownTime[pointerId]; 165984044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav if (downPointerTime < minDownTime) { 166084044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav minDownTime = downPointerTime; 166138c992841b5f6bc80359dbf60d31aa7b994160fcSvetoslav Ganov primaryPointerId = pointerId; 1662736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov } 1663736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov } 166484044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav return primaryPointerId; 1665736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov } 1666736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov 1667736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov @Override 1668736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov public String toString() { 1669736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov StringBuilder builder = new StringBuilder(); 1670736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov builder.append("========================="); 1671736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov builder.append("\nDown pointers #"); 1672736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov builder.append(getReceivedPointerDownCount()); 1673736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov builder.append(" [ "); 1674736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov for (int i = 0; i < MAX_POINTER_COUNT; i++) { 1675736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov if (isReceivedPointerDown(i)) { 1676736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov builder.append(i); 1677736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov builder.append(" "); 1678736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov } 1679736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov } 1680736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov builder.append("]"); 168184044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav builder.append("\nPrimary pointer id [ "); 168284044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav builder.append(getPrimaryPointerId()); 1683736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov builder.append(" ]"); 1684736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov builder.append("\n========================="); 1685736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov return builder.toString(); 1686736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov } 1687736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov } 1688736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov} 1689