1736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov/* 2736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov ** Copyright 2011, The Android Open Source Project 3736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov ** 4736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov ** Licensed under the Apache License, Version 2.0 (the "License"); 5736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov ** you may not use this file except in compliance with the License. 6736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov ** You may obtain a copy of the License at 7736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov ** 8736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov ** http://www.apache.org/licenses/LICENSE-2.0 9736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov ** 10736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov ** Unless required by applicable law or agreed to in writing, software 11736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov ** distributed under the License is distributed on an "AS IS" BASIS, 12736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov ** See the License for the specific language governing permissions and 14736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov ** limitations under the License. 15736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov */ 16736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov 17736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganovpackage com.android.server.accessibility; 18736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov 19736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganovimport android.content.Context; 204213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganovimport android.gesture.Gesture; 214213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganovimport android.gesture.GestureLibraries; 224213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganovimport android.gesture.GestureLibrary; 234213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganovimport android.gesture.GesturePoint; 24ea6fbc0981564f7bbf4c6fbb63af0175415121ceCasey Burkhardtimport android.gesture.GestureStore; 254213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganovimport android.gesture.GestureStroke; 264213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganovimport android.gesture.Prediction; 27e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganovimport android.graphics.Rect; 28736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganovimport android.os.Handler; 29e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganovimport android.os.SystemClock; 30736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganovimport android.util.Slog; 31736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganovimport android.view.MotionEvent; 32e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganovimport android.view.MotionEvent.PointerCoords; 33e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganovimport android.view.MotionEvent.PointerProperties; 344213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganovimport android.view.VelocityTracker; 35f5a07905a3e025f95472a3f8d9935263e49ad6d3Svetoslav Ganovimport android.view.ViewConfiguration; 36f5a07905a3e025f95472a3f8d9935263e49ad6d3Svetoslav Ganovimport android.view.WindowManagerPolicy; 3786783474fdec98a22bc22e224462767eab13e273Svetoslav Ganovimport android.view.accessibility.AccessibilityEvent; 3877276b60851a158ad3e142cb3b091d57ae5ceffbSvetoslav Ganovimport android.view.accessibility.AccessibilityManager; 39736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov 404213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganovimport com.android.internal.R; 41f5a07905a3e025f95472a3f8d9935263e49ad6d3Svetoslav Ganov 424213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganovimport java.util.ArrayList; 43736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganovimport java.util.Arrays; 4484044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslavimport java.util.List; 45736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov 46736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov/** 47736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov * This class is a strategy for performing touch exploration. It 48736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov * transforms the motion event stream by modifying, adding, replacing, 49736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov * and consuming certain events. The interaction model is: 50736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov * 51736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov * <ol> 52e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov * <li>1. One finger moving slow around performs touch exploration.</li> 53e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov * <li>2. One finger moving fast around performs gestures.</li> 54e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov * <li>3. Two close fingers moving in the same direction perform a drag.</li> 55e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov * <li>4. Multi-finger gestures are delivered to view hierarchy.</li> 5684044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav * <li>5. Two fingers moving in different directions are considered a multi-finger gesture.</li> 5784044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav * <li>7. Double tapping clicks on the on the last touch explored location if it was in 58e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov * a window that does not take focus, otherwise the click is within the accessibility 59e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov * focused rectangle.</li> 60e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov * <li>7. Tapping and holding for a while performs a long press in a similar fashion 61e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov * as the click above.</li> 62736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov * <ol> 63736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov * 64736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov * @hide 65736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov */ 661cf70bbf96930662cab0e699d70b62865766ff52Svetoslav Ganovclass TouchExplorer implements EventStreamTransformation { 674213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov 68736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov private static final boolean DEBUG = false; 69736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov 70736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov // Tag for logging received events. 714213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov private static final String LOG_TAG = "TouchExplorer"; 72736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov 73736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov // States this explorer can be in. 74736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov private static final int STATE_TOUCH_EXPLORING = 0x00000001; 75736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov private static final int STATE_DRAGGING = 0x00000002; 76736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov private static final int STATE_DELEGATING = 0x00000004; 774213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov private static final int STATE_GESTURE_DETECTING = 0x00000005; 78736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov 79736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov // The minimum of the cosine between the vectors of two moving 80736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov // pointers so they can be considered moving in the same direction. 8112a024ca681d877fe16b7e087356f7aff175a218Svetoslav Ganov private static final float MAX_DRAGGING_ANGLE_COS = 0.525321989f; // cos(pi/4) 82736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov 83f804420d6e37748b75478406e989c69303756980Svetoslav Ganov // Constant referring to the ids bits of all pointers. 84f804420d6e37748b75478406e989c69303756980Svetoslav Ganov private static final int ALL_POINTER_ID_BITS = 0xFFFFFFFF; 85736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov 864213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov // This constant captures the current implementation detail that 874213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov // pointer IDs are between 0 and 31 inclusive (subject to change). 884213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov // (See MAX_POINTER_ID in frameworks/base/include/ui/Input.h) 89e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov private static final int MAX_POINTER_COUNT = 32; 904213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov 914213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov // Invalid pointer ID. 92e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov private static final int INVALID_POINTER_ID = -1; 93e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov 94e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov // The velocity above which we detect gestures. 95e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov private static final int GESTURE_DETECTION_VELOCITY_DIP = 1000; 96e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov 97e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov // The minimal distance before we take the middle of the distance between 98e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov // the two dragging pointers as opposed to use the location of the primary one. 99e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov private static final int MIN_POINTER_DISTANCE_TO_USE_MIDDLE_LOCATION_DIP = 200; 1004213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov 10195068e5d1bea47091e97955f271c789264994550Svetoslav Ganov // The timeout after which we are no longer trying to detect a gesture. 10295068e5d1bea47091e97955f271c789264994550Svetoslav Ganov private static final int EXIT_GESTURE_DETECTION_TIMEOUT = 2000; 10395068e5d1bea47091e97955f271c789264994550Svetoslav Ganov 104e47957a0bbe2164467ff6e7a566b0c9e4689cdc9Svetoslav Ganov // Timeout before trying to decide what the user is trying to do. 105e47957a0bbe2164467ff6e7a566b0c9e4689cdc9Svetoslav Ganov private final int mDetermineUserIntentTimeout; 106e47957a0bbe2164467ff6e7a566b0c9e4689cdc9Svetoslav Ganov 107e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov // Timeout within which we try to detect a tap. 108e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov private final int mTapTimeout; 109e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov 110e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov // Timeout within which we try to detect a double tap. 111e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov private final int mDoubleTapTimeout; 112e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov 113e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov // Slop between the down and up tap to be a tap. 114e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov private final int mTouchSlop; 115e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov 116e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov // Slop between the first and second tap to be a double tap. 117e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov private final int mDoubleTapSlop; 118736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov 119736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov // The current state of the touch explorer. 120736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov private int mCurrentState = STATE_TOUCH_EXPLORING; 121736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov 122736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov // The ID of the pointer used for dragging. 123736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov private int mDraggingPointerId; 124736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov 125736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov // Handler for performing asynchronous operations. 126736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov private final Handler mHandler; 127736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov 12884044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav // Command for delayed sending of a hover enter and move event. 12984044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav private final SendHoverEnterAndMoveDelayed mSendHoverEnterAndMoveDelayed; 130e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov 131e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov // Command for delayed sending of a hover exit event. 13284044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav private final SendHoverExitDelayed mSendHoverExitDelayed; 133736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov 134f068fed6c4c3fc2003aec19b6e7e892358179b02Svetoslav Ganov // Command for delayed sending of touch exploration end events. 135f068fed6c4c3fc2003aec19b6e7e892358179b02Svetoslav Ganov private final SendAccessibilityEventDelayed mSendTouchExplorationEndDelayed; 136f068fed6c4c3fc2003aec19b6e7e892358179b02Svetoslav Ganov 137f068fed6c4c3fc2003aec19b6e7e892358179b02Svetoslav Ganov // Command for delayed sending of touch interaction end events. 138f068fed6c4c3fc2003aec19b6e7e892358179b02Svetoslav Ganov private final SendAccessibilityEventDelayed mSendTouchInteractionEndDelayed; 139fe304b893968887323b93764caafa66ee8ad44deSvetoslav Ganov 140f5a07905a3e025f95472a3f8d9935263e49ad6d3Svetoslav Ganov // Command for delayed sending of a long press. 141f5a07905a3e025f95472a3f8d9935263e49ad6d3Svetoslav Ganov private final PerformLongPressDelayed mPerformLongPressDelayed; 142f5a07905a3e025f95472a3f8d9935263e49ad6d3Svetoslav Ganov 14395068e5d1bea47091e97955f271c789264994550Svetoslav Ganov // Command for exiting gesture detection mode after a timeout. 14495068e5d1bea47091e97955f271c789264994550Svetoslav Ganov private final ExitGestureDetectionModeDelayed mExitGestureDetectionModeDelayed; 14595068e5d1bea47091e97955f271c789264994550Svetoslav Ganov 146e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov // Helper to detect and react to double tap in touch explore mode. 147e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov private final DoubleTapDetector mDoubleTapDetector; 148e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov 149e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov // The scaled minimal distance before we take the middle of the distance between 150e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov // the two dragging pointers as opposed to use the location of the primary one. 151e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov private final int mScaledMinPointerDistanceToUseMiddleLocation; 152e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov 153e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov // The scaled velocity above which we detect gestures. 154e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov private final int mScaledGestureDetectionVelocity; 155e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov 1561cf70bbf96930662cab0e699d70b62865766ff52Svetoslav Ganov // The handler to which to delegate events. 1571cf70bbf96930662cab0e699d70b62865766ff52Svetoslav Ganov private EventStreamTransformation mNext; 1581cf70bbf96930662cab0e699d70b62865766ff52Svetoslav Ganov 159e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov // Helper to track gesture velocity. 16045af84a483165f06c04d74baba67f90da29c6ad2Svetoslav Ganov private final VelocityTracker mVelocityTracker = VelocityTracker.obtain(); 1614213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov 162e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov // Helper class to track received pointers. 1634213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov private final ReceivedPointerTracker mReceivedPointerTracker; 1644213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov 165e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov // Helper class to track injected pointers. 1664213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov private final InjectedPointerTracker mInjectedPointerTracker; 1674213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov 168e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov // Handle to the accessibility manager service. 169e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov private final AccessibilityManagerService mAms; 1704213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov 171e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov // Temporary rectangle to avoid instantiation. 172e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov private final Rect mTempRect = new Rect(); 1734213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov 17477276b60851a158ad3e142cb3b091d57ae5ceffbSvetoslav Ganov // Context in which this explorer operates. 17577276b60851a158ad3e142cb3b091d57ae5ceffbSvetoslav Ganov private final Context mContext; 17677276b60851a158ad3e142cb3b091d57ae5ceffbSvetoslav Ganov 177e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov // The X of the previous event. 178e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov private float mPreviousX; 179e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov 180e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov // The Y of the previous event. 181e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov private float mPreviousY; 182e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov 183e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov // Buffer for storing points for gesture detection. 184e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov private final ArrayList<GesturePoint> mStrokeBuffer = new ArrayList<GesturePoint>(100); 185e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov 186e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov // The minimal delta between moves to add a gesture point. 187e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov private static final int TOUCH_TOLERANCE = 3; 188e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov 189e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov // The minimal score for accepting a predicted gesture. 190e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov private static final float MIN_PREDICTION_SCORE = 2.0f; 191e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov 192e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov // The library for gesture detection. 193e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov private GestureLibrary mGestureLibrary; 194e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov 195e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov // The long pressing pointer id if coordinate remapping is needed. 196aeb8d0ed0d98d398a66a092c418f4f2bca8719e0Svetoslav Ganov private int mLongPressingPointerId = -1; 197e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov 198e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov // The long pressing pointer X if coordinate remapping is needed. 199e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov private int mLongPressingPointerDeltaX; 200e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov 201e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov // The long pressing pointer Y if coordinate remapping is needed. 202e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov private int mLongPressingPointerDeltaY; 2034213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov 204385d9f24b5ce2acb86c0dc192ce702718ab01c39Svetoslav Ganov // The id of the last touch explored window. 205385d9f24b5ce2acb86c0dc192ce702718ab01c39Svetoslav Ganov private int mLastTouchedWindowId; 206385d9f24b5ce2acb86c0dc192ce702718ab01c39Svetoslav Ganov 207f068fed6c4c3fc2003aec19b6e7e892358179b02Svetoslav Ganov // Whether touch exploration is in progress. 208f068fed6c4c3fc2003aec19b6e7e892358179b02Svetoslav Ganov private boolean mTouchExplorationInProgress; 20977276b60851a158ad3e142cb3b091d57ae5ceffbSvetoslav Ganov 210736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov /** 211736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov * Creates a new instance. 212736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov * 213736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov * @param inputFilter The input filter associated with this explorer. 214736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov * @param context A context handle for accessing resources. 215736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov */ 2161cf70bbf96930662cab0e699d70b62865766ff52Svetoslav Ganov public TouchExplorer(Context context, AccessibilityManagerService service) { 21777276b60851a158ad3e142cb3b091d57ae5ceffbSvetoslav Ganov mContext = context; 218e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov mAms = service; 21984044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav mReceivedPointerTracker = new ReceivedPointerTracker(); 2204213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov mInjectedPointerTracker = new InjectedPointerTracker(); 221e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov mTapTimeout = ViewConfiguration.getTapTimeout(); 22277276b60851a158ad3e142cb3b091d57ae5ceffbSvetoslav Ganov mDetermineUserIntentTimeout = ViewConfiguration.getDoubleTapTimeout(); 223e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov mDoubleTapTimeout = ViewConfiguration.getDoubleTapTimeout(); 224e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov mTouchSlop = ViewConfiguration.get(context).getScaledTouchSlop(); 225e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov mDoubleTapSlop = ViewConfiguration.get(context).getScaledDoubleTapSlop(); 226736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov mHandler = new Handler(context.getMainLooper()); 227f5a07905a3e025f95472a3f8d9935263e49ad6d3Svetoslav Ganov mPerformLongPressDelayed = new PerformLongPressDelayed(); 22895068e5d1bea47091e97955f271c789264994550Svetoslav Ganov mExitGestureDetectionModeDelayed = new ExitGestureDetectionModeDelayed(); 2294213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov mGestureLibrary = GestureLibraries.fromRawResource(context, R.raw.accessibility_gestures); 230ea6fbc0981564f7bbf4c6fbb63af0175415121ceCasey Burkhardt mGestureLibrary.setOrientationStyle(8); 231ea6fbc0981564f7bbf4c6fbb63af0175415121ceCasey Burkhardt mGestureLibrary.setSequenceType(GestureStore.SEQUENCE_SENSITIVE); 2324213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov mGestureLibrary.load(); 23384044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav mSendHoverEnterAndMoveDelayed = new SendHoverEnterAndMoveDelayed(); 23484044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav mSendHoverExitDelayed = new SendHoverExitDelayed(); 235f068fed6c4c3fc2003aec19b6e7e892358179b02Svetoslav Ganov mSendTouchExplorationEndDelayed = new SendAccessibilityEventDelayed( 236f068fed6c4c3fc2003aec19b6e7e892358179b02Svetoslav Ganov AccessibilityEvent.TYPE_TOUCH_EXPLORATION_GESTURE_END, 237f068fed6c4c3fc2003aec19b6e7e892358179b02Svetoslav Ganov mDetermineUserIntentTimeout); 238f068fed6c4c3fc2003aec19b6e7e892358179b02Svetoslav Ganov mSendTouchInteractionEndDelayed = new SendAccessibilityEventDelayed( 239f068fed6c4c3fc2003aec19b6e7e892358179b02Svetoslav Ganov AccessibilityEvent.TYPE_TOUCH_INTERACTION_END, 240f068fed6c4c3fc2003aec19b6e7e892358179b02Svetoslav Ganov mDetermineUserIntentTimeout); 241e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov mDoubleTapDetector = new DoubleTapDetector(); 242e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov final float density = context.getResources().getDisplayMetrics().density; 243e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov mScaledMinPointerDistanceToUseMiddleLocation = 244e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov (int) (MIN_POINTER_DISTANCE_TO_USE_MIDDLE_LOCATION_DIP * density); 245e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov mScaledGestureDetectionVelocity = (int) (GESTURE_DETECTION_VELOCITY_DIP * density); 246e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov } 247e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov 248e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov public void clear() { 249e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov // If we have not received an event then we are in initial 250e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov // state. Therefore, there is not need to clean anything. 251e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov MotionEvent event = mReceivedPointerTracker.getLastReceivedEvent(); 252e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov if (event != null) { 253e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov clear(mReceivedPointerTracker.getLastReceivedEvent(), WindowManagerPolicy.FLAG_TRUSTED); 254e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov } 255736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov } 256736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov 2571cf70bbf96930662cab0e699d70b62865766ff52Svetoslav Ganov public void onDestroy() { 2581cf70bbf96930662cab0e699d70b62865766ff52Svetoslav Ganov // TODO: Implement 2591cf70bbf96930662cab0e699d70b62865766ff52Svetoslav Ganov } 2601cf70bbf96930662cab0e699d70b62865766ff52Svetoslav Ganov 2611cf70bbf96930662cab0e699d70b62865766ff52Svetoslav Ganov private void clear(MotionEvent event, int policyFlags) { 262e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov switch (mCurrentState) { 263e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov case STATE_TOUCH_EXPLORING: { 264e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov // If a touch exploration gesture is in progress send events for its end. 265f068fed6c4c3fc2003aec19b6e7e892358179b02Svetoslav Ganov sendHoverExitAndTouchExplorationGestureEndIfNeeded(policyFlags); 266e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov } break; 267e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov case STATE_DRAGGING: { 268e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov mDraggingPointerId = INVALID_POINTER_ID; 269e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov // Send exit to all pointers that we have delivered. 270e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov sendUpForInjectedDownPointers(event, policyFlags); 271e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov } break; 272e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov case STATE_DELEGATING: { 273e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov // Send exit to all pointers that we have delivered. 274e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov sendUpForInjectedDownPointers(event, policyFlags); 275e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov } break; 276e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov case STATE_GESTURE_DETECTING: { 277e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov // Clear the current stroke. 278e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov mStrokeBuffer.clear(); 279e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov } break; 280e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov } 281e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov // Remove all pending callbacks. 28284044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav mSendHoverEnterAndMoveDelayed.cancel(); 28384044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav mSendHoverExitDelayed.cancel(); 28484044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav mPerformLongPressDelayed.cancel(); 28584044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav mExitGestureDetectionModeDelayed.cancel(); 28684044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav mSendTouchExplorationEndDelayed.cancel(); 28784044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav mSendTouchInteractionEndDelayed.cancel(); 288e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov // Reset the pointer trackers. 289e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov mReceivedPointerTracker.clear(); 290e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov mInjectedPointerTracker.clear(); 291e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov // Clear the double tap detector 292e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov mDoubleTapDetector.clear(); 293e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov // Go to initial state. 294e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov // Clear the long pressing pointer remap data. 295e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov mLongPressingPointerId = -1; 296e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov mLongPressingPointerDeltaX = 0; 297e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov mLongPressingPointerDeltaY = 0; 298e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov mCurrentState = STATE_TOUCH_EXPLORING; 2991cf70bbf96930662cab0e699d70b62865766ff52Svetoslav Ganov if (mNext != null) { 3001cf70bbf96930662cab0e699d70b62865766ff52Svetoslav Ganov mNext.clear(); 3011cf70bbf96930662cab0e699d70b62865766ff52Svetoslav Ganov } 302f068fed6c4c3fc2003aec19b6e7e892358179b02Svetoslav Ganov mTouchExplorationInProgress = false; 303f772cba59760d1ad9eb5cb9205b2e2e9126e488dSvetoslav Ganov mAms.onTouchInteractionEnd(); 3041cf70bbf96930662cab0e699d70b62865766ff52Svetoslav Ganov } 3051cf70bbf96930662cab0e699d70b62865766ff52Svetoslav Ganov 3061cf70bbf96930662cab0e699d70b62865766ff52Svetoslav Ganov @Override 3071cf70bbf96930662cab0e699d70b62865766ff52Svetoslav Ganov public void setNext(EventStreamTransformation next) { 3081cf70bbf96930662cab0e699d70b62865766ff52Svetoslav Ganov mNext = next; 309736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov } 310736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov 3111cf70bbf96930662cab0e699d70b62865766ff52Svetoslav Ganov @Override 31245af84a483165f06c04d74baba67f90da29c6ad2Svetoslav Ganov public void onMotionEvent(MotionEvent event, MotionEvent rawEvent, int policyFlags) { 313736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov if (DEBUG) { 3144213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov Slog.d(LOG_TAG, "Received event: " + event + ", policyFlags=0x" 315736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov + Integer.toHexString(policyFlags)); 3164213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov Slog.d(LOG_TAG, getStateSymbolicName(mCurrentState)); 317736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov } 318736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov 31945af84a483165f06c04d74baba67f90da29c6ad2Svetoslav Ganov mReceivedPointerTracker.onMotionEvent(rawEvent); 320736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov 321736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov switch(mCurrentState) { 322736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov case STATE_TOUCH_EXPLORING: { 32345af84a483165f06c04d74baba67f90da29c6ad2Svetoslav Ganov handleMotionEventStateTouchExploring(event, rawEvent, policyFlags); 324736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov } break; 325736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov case STATE_DRAGGING: { 326736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov handleMotionEventStateDragging(event, policyFlags); 327736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov } break; 328736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov case STATE_DELEGATING: { 329736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov handleMotionEventStateDelegating(event, policyFlags); 330736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov } break; 3314213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov case STATE_GESTURE_DETECTING: { 33245af84a483165f06c04d74baba67f90da29c6ad2Svetoslav Ganov handleMotionEventGestureDetecting(rawEvent, policyFlags); 3334213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov } break; 3344213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov default: 335736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov throw new IllegalStateException("Illegal state: " + mCurrentState); 336736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov } 337736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov } 338736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov 33986783474fdec98a22bc22e224462767eab13e273Svetoslav Ganov public void onAccessibilityEvent(AccessibilityEvent event) { 34077276b60851a158ad3e142cb3b091d57ae5ceffbSvetoslav Ganov final int eventType = event.getEventType(); 34177276b60851a158ad3e142cb3b091d57ae5ceffbSvetoslav Ganov 34277276b60851a158ad3e142cb3b091d57ae5ceffbSvetoslav Ganov // The event for gesture end should be strictly after the 34377276b60851a158ad3e142cb3b091d57ae5ceffbSvetoslav Ganov // last hover exit event. 344f068fed6c4c3fc2003aec19b6e7e892358179b02Svetoslav Ganov if (mSendTouchExplorationEndDelayed.isPending() 3458b681cb8813454aac8a626bf3d7adaa8beca4d75Svetoslav Ganov && eventType == AccessibilityEvent.TYPE_VIEW_HOVER_EXIT) { 34684044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav mSendTouchExplorationEndDelayed.cancel(); 3478b681cb8813454aac8a626bf3d7adaa8beca4d75Svetoslav Ganov sendAccessibilityEvent(AccessibilityEvent.TYPE_TOUCH_EXPLORATION_GESTURE_END); 34877276b60851a158ad3e142cb3b091d57ae5ceffbSvetoslav Ganov } 34977276b60851a158ad3e142cb3b091d57ae5ceffbSvetoslav Ganov 35077276b60851a158ad3e142cb3b091d57ae5ceffbSvetoslav Ganov // The event for touch interaction end should be strictly after the 35177276b60851a158ad3e142cb3b091d57ae5ceffbSvetoslav Ganov // last hover exit and the touch exploration gesture end events. 352f068fed6c4c3fc2003aec19b6e7e892358179b02Svetoslav Ganov if (mSendTouchInteractionEndDelayed.isPending() 3538b681cb8813454aac8a626bf3d7adaa8beca4d75Svetoslav Ganov && eventType == AccessibilityEvent.TYPE_VIEW_HOVER_EXIT) { 35484044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav mSendTouchInteractionEndDelayed.cancel(); 3558b681cb8813454aac8a626bf3d7adaa8beca4d75Svetoslav Ganov sendAccessibilityEvent(AccessibilityEvent.TYPE_TOUCH_INTERACTION_END); 35677276b60851a158ad3e142cb3b091d57ae5ceffbSvetoslav Ganov } 35777276b60851a158ad3e142cb3b091d57ae5ceffbSvetoslav Ganov 35886783474fdec98a22bc22e224462767eab13e273Svetoslav Ganov // If a new window opens or the accessibility focus moves we no longer 35986783474fdec98a22bc22e224462767eab13e273Svetoslav Ganov // want to click/long press on the last touch explored location. 36086783474fdec98a22bc22e224462767eab13e273Svetoslav Ganov switch (eventType) { 36186783474fdec98a22bc22e224462767eab13e273Svetoslav Ganov case AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED: 36286783474fdec98a22bc22e224462767eab13e273Svetoslav Ganov case AccessibilityEvent.TYPE_VIEW_ACCESSIBILITY_FOCUSED: { 3635d043ce8cc2f588fdfb336cc843fb3b07b196f83Svetoslav Ganov if (mInjectedPointerTracker.mLastInjectedHoverEventForClick != null) { 3645d043ce8cc2f588fdfb336cc843fb3b07b196f83Svetoslav Ganov mInjectedPointerTracker.mLastInjectedHoverEventForClick.recycle(); 3655d043ce8cc2f588fdfb336cc843fb3b07b196f83Svetoslav Ganov mInjectedPointerTracker.mLastInjectedHoverEventForClick = null; 36686783474fdec98a22bc22e224462767eab13e273Svetoslav Ganov } 367385d9f24b5ce2acb86c0dc192ce702718ab01c39Svetoslav Ganov mLastTouchedWindowId = -1; 368385d9f24b5ce2acb86c0dc192ce702718ab01c39Svetoslav Ganov } break; 369385d9f24b5ce2acb86c0dc192ce702718ab01c39Svetoslav Ganov case AccessibilityEvent.TYPE_VIEW_HOVER_ENTER: 370385d9f24b5ce2acb86c0dc192ce702718ab01c39Svetoslav Ganov case AccessibilityEvent.TYPE_VIEW_HOVER_EXIT: { 371385d9f24b5ce2acb86c0dc192ce702718ab01c39Svetoslav Ganov mLastTouchedWindowId = event.getWindowId(); 37286783474fdec98a22bc22e224462767eab13e273Svetoslav Ganov } break; 37386783474fdec98a22bc22e224462767eab13e273Svetoslav Ganov } 3741cf70bbf96930662cab0e699d70b62865766ff52Svetoslav Ganov if (mNext != null) { 3751cf70bbf96930662cab0e699d70b62865766ff52Svetoslav Ganov mNext.onAccessibilityEvent(event); 3761cf70bbf96930662cab0e699d70b62865766ff52Svetoslav Ganov } 37786783474fdec98a22bc22e224462767eab13e273Svetoslav Ganov } 37886783474fdec98a22bc22e224462767eab13e273Svetoslav Ganov 379736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov /** 380736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov * Handles a motion event in touch exploring state. 381736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov * 382736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov * @param event The event to be handled. 38345af84a483165f06c04d74baba67f90da29c6ad2Svetoslav Ganov * @param rawEvent The raw (unmodified) motion event. 384736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov * @param policyFlags The policy flags associated with the event. 385736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov */ 38645af84a483165f06c04d74baba67f90da29c6ad2Svetoslav Ganov private void handleMotionEventStateTouchExploring(MotionEvent event, MotionEvent rawEvent, 38745af84a483165f06c04d74baba67f90da29c6ad2Svetoslav Ganov int policyFlags) { 3884213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov ReceivedPointerTracker receivedTracker = mReceivedPointerTracker; 3894213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov 39045af84a483165f06c04d74baba67f90da29c6ad2Svetoslav Ganov mVelocityTracker.addMovement(rawEvent); 391736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov 392e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov mDoubleTapDetector.onMotionEvent(event, policyFlags); 393e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov 394736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov switch (event.getActionMasked()) { 39584044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav case MotionEvent.ACTION_DOWN: { 3966ae8a24fc045bc7970f2843fa9baf06aff15e22dSvetoslav Ganov mAms.onTouchInteractionStart(); 39784044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav 398e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov // Pre-feed the motion events to the gesture detector since we 399e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov // have a distance slop before getting into gesture detection 400e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov // mode and not using the points within this slop significantly 401e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov // decreases the quality of gesture recognition. 40245af84a483165f06c04d74baba67f90da29c6ad2Svetoslav Ganov handleMotionEventGestureDetecting(rawEvent, policyFlags); 40384044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav sendAccessibilityEvent(AccessibilityEvent.TYPE_TOUCH_INTERACTION_START); 404736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov 40584044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav // If we still have not notified the user for the last 40684044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav // touch, we figure out what to do. If were waiting 40784044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav // we resent the delayed callback and wait again. 40884044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav mSendHoverEnterAndMoveDelayed.cancel(); 40984044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav mSendHoverExitDelayed.cancel(); 41084044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav mPerformLongPressDelayed.cancel(); 411f068fed6c4c3fc2003aec19b6e7e892358179b02Svetoslav Ganov 41284044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav if (mSendTouchExplorationEndDelayed.isPending()) { 41384044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav mSendTouchExplorationEndDelayed.forceSendAndRemove(); 41484044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav } 415f068fed6c4c3fc2003aec19b6e7e892358179b02Svetoslav Ganov 41684044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav if (mSendTouchInteractionEndDelayed.isPending()) { 41784044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav mSendTouchInteractionEndDelayed.forceSendAndRemove(); 41884044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav } 419fe304b893968887323b93764caafa66ee8ad44deSvetoslav Ganov 42084044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav // If we have the first tap, schedule a long press and break 42184044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav // since we do not want to schedule hover enter because 42284044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav // the delayed callback will kick in before the long click. 42384044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav // This would lead to a state transition resulting in long 42484044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav // pressing the item below the double taped area which is 42584044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav // not necessary where accessibility focus is. 42684044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav if (mDoubleTapDetector.firstTapDetected()) { 42784044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav // We got a tap now post a long press action. 42884044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav mPerformLongPressDelayed.post(event, policyFlags); 42984044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav break; 430736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov } 43184044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav if (!mTouchExplorationInProgress) { 43238c992841b5f6bc80359dbf60d31aa7b994160fcSvetoslav Ganov if (!mSendHoverEnterAndMoveDelayed.isPending()) { 43338c992841b5f6bc80359dbf60d31aa7b994160fcSvetoslav Ganov // Deliver hover enter with a delay to have a chance 43438c992841b5f6bc80359dbf60d31aa7b994160fcSvetoslav Ganov // to detect what the user is trying to do. 43538c992841b5f6bc80359dbf60d31aa7b994160fcSvetoslav Ganov final int pointerId = receivedTracker.getPrimaryPointerId(); 43638c992841b5f6bc80359dbf60d31aa7b994160fcSvetoslav Ganov final int pointerIdBits = (1 << pointerId); 43738c992841b5f6bc80359dbf60d31aa7b994160fcSvetoslav Ganov mSendHoverEnterAndMoveDelayed.post(event, true, pointerIdBits, 43838c992841b5f6bc80359dbf60d31aa7b994160fcSvetoslav Ganov policyFlags); 43938c992841b5f6bc80359dbf60d31aa7b994160fcSvetoslav Ganov } 44038c992841b5f6bc80359dbf60d31aa7b994160fcSvetoslav Ganov // Cache the event until we discern exploration from gesturing. 44138c992841b5f6bc80359dbf60d31aa7b994160fcSvetoslav Ganov mSendHoverEnterAndMoveDelayed.addEvent(event); 44284044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav } 44384044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav } break; 44484044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav case MotionEvent.ACTION_POINTER_DOWN: { 44584044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav /* do nothing - let the code for ACTION_MOVE decide what to do */ 446736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov } break; 447736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov case MotionEvent.ACTION_MOVE: { 44884044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav final int pointerId = receivedTracker.getPrimaryPointerId(); 44991feae3c5994bd4768cea3507c62c65746adcfa6Svetoslav Ganov final int pointerIndex = event.findPointerIndex(pointerId); 45091feae3c5994bd4768cea3507c62c65746adcfa6Svetoslav Ganov final int pointerIdBits = (1 << pointerId); 45184044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav switch (event.getPointerCount()) { 452736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov case 1: { 453e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov // We have not started sending events since we try to 454e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov // figure out what the user is doing. 45584044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav if (mSendHoverEnterAndMoveDelayed.isPending()) { 456e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov // Pre-feed the motion events to the gesture detector since we 457e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov // have a distance slop before getting into gesture detection 458e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov // mode and not using the points within this slop significantly 459e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov // decreases the quality of gesture recognition. 46045af84a483165f06c04d74baba67f90da29c6ad2Svetoslav Ganov handleMotionEventGestureDetecting(rawEvent, policyFlags); 46184044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav 46284044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav // Cache the event until we discern exploration from gesturing. 46384044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav mSendHoverEnterAndMoveDelayed.addEvent(event); 46484044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav 46545af84a483165f06c04d74baba67f90da29c6ad2Svetoslav Ganov // It is *important* to use the distance traveled by the pointers 46645af84a483165f06c04d74baba67f90da29c6ad2Svetoslav Ganov // on the screen which may or may not be magnified. 4674213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov final float deltaX = receivedTracker.getReceivedPointerDownX(pointerId) 46845af84a483165f06c04d74baba67f90da29c6ad2Svetoslav Ganov - rawEvent.getX(pointerIndex); 4694213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov final float deltaY = receivedTracker.getReceivedPointerDownY(pointerId) 47045af84a483165f06c04d74baba67f90da29c6ad2Svetoslav Ganov - rawEvent.getY(pointerIndex); 471736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov final double moveDelta = Math.hypot(deltaX, deltaY); 472e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov // The user has moved enough for us to decide. 473e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov if (moveDelta > mDoubleTapSlop) { 474e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov // Check whether the user is performing a gesture. We 475e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov // detect gestures if the pointer is moving above a 476e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov // given velocity. 4774213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov mVelocityTracker.computeCurrentVelocity(1000); 4784213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov final float maxAbsVelocity = Math.max( 4794213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov Math.abs(mVelocityTracker.getXVelocity(pointerId)), 4804213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov Math.abs(mVelocityTracker.getYVelocity(pointerId))); 481e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov if (maxAbsVelocity > mScaledGestureDetectionVelocity) { 482e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov // We have to perform gesture detection, so 483e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov // clear the current state and try to detect. 4844213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov mCurrentState = STATE_GESTURE_DETECTING; 48545af84a483165f06c04d74baba67f90da29c6ad2Svetoslav Ganov mVelocityTracker.clear(); 48684044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav mSendHoverEnterAndMoveDelayed.cancel(); 48784044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav mSendHoverExitDelayed.cancel(); 48884044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav mPerformLongPressDelayed.cancel(); 48995068e5d1bea47091e97955f271c789264994550Svetoslav Ganov mExitGestureDetectionModeDelayed.post(); 49077276b60851a158ad3e142cb3b091d57ae5ceffbSvetoslav Ganov // Send accessibility event to announce the start 49177276b60851a158ad3e142cb3b091d57ae5ceffbSvetoslav Ganov // of gesture recognition. 49277276b60851a158ad3e142cb3b091d57ae5ceffbSvetoslav Ganov sendAccessibilityEvent( 49377276b60851a158ad3e142cb3b091d57ae5ceffbSvetoslav Ganov AccessibilityEvent.TYPE_GESTURE_DETECTION_START); 494e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov } else { 495e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov // We have just decided that the user is touch, 496e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov // exploring so start sending events. 49784044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav mSendHoverEnterAndMoveDelayed.forceSendAndRemove(); 49884044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav mSendHoverExitDelayed.cancel(); 49984044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav mPerformLongPressDelayed.cancel(); 500e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov sendMotionEvent(event, MotionEvent.ACTION_HOVER_MOVE, 50191feae3c5994bd4768cea3507c62c65746adcfa6Svetoslav Ganov pointerIdBits, policyFlags); 50291feae3c5994bd4768cea3507c62c65746adcfa6Svetoslav Ganov } 503e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov break; 504736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov } 505736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov } else { 506f068fed6c4c3fc2003aec19b6e7e892358179b02Svetoslav Ganov // Cancel the long press if pending and the user 507f068fed6c4c3fc2003aec19b6e7e892358179b02Svetoslav Ganov // moved more than the slop. 508f068fed6c4c3fc2003aec19b6e7e892358179b02Svetoslav Ganov if (mPerformLongPressDelayed.isPending()) { 509f068fed6c4c3fc2003aec19b6e7e892358179b02Svetoslav Ganov final float deltaX = 510f068fed6c4c3fc2003aec19b6e7e892358179b02Svetoslav Ganov receivedTracker.getReceivedPointerDownX(pointerId) 511f068fed6c4c3fc2003aec19b6e7e892358179b02Svetoslav Ganov - rawEvent.getX(pointerIndex); 512f068fed6c4c3fc2003aec19b6e7e892358179b02Svetoslav Ganov final float deltaY = 513f068fed6c4c3fc2003aec19b6e7e892358179b02Svetoslav Ganov receivedTracker.getReceivedPointerDownY(pointerId) 514f068fed6c4c3fc2003aec19b6e7e892358179b02Svetoslav Ganov - rawEvent.getY(pointerIndex); 515f068fed6c4c3fc2003aec19b6e7e892358179b02Svetoslav Ganov final double moveDelta = Math.hypot(deltaX, deltaY); 516f068fed6c4c3fc2003aec19b6e7e892358179b02Svetoslav Ganov // The user has moved enough for us to decide. 517f068fed6c4c3fc2003aec19b6e7e892358179b02Svetoslav Ganov if (moveDelta > mTouchSlop) { 51884044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav mPerformLongPressDelayed.cancel(); 519f068fed6c4c3fc2003aec19b6e7e892358179b02Svetoslav Ganov } 520f068fed6c4c3fc2003aec19b6e7e892358179b02Svetoslav Ganov } 52184044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav // The user is either double tapping or performing a long 52284044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav // press, so do not send move events yet. 523e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov if (mDoubleTapDetector.firstTapDetected()) { 524e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov break; 525e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov } 526f068fed6c4c3fc2003aec19b6e7e892358179b02Svetoslav Ganov sendTouchExplorationGestureStartAndHoverEnterIfNeeded(policyFlags); 52791feae3c5994bd4768cea3507c62c65746adcfa6Svetoslav Ganov sendMotionEvent(event, MotionEvent.ACTION_HOVER_MOVE, pointerIdBits, 528736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov policyFlags); 529736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov } 530e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov } break; 531e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov case 2: { 532e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov // More than one pointer so the user is not touch exploring 533e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov // and now we have to decide whether to delegate or drag. 53484044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav if (mSendHoverEnterAndMoveDelayed.isPending()) { 535e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov // We have not started sending events so cancel 536e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov // scheduled sending events. 53784044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav mSendHoverEnterAndMoveDelayed.cancel(); 53884044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav mSendHoverExitDelayed.cancel(); 53984044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav mPerformLongPressDelayed.cancel(); 540e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov } else { 54184044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav mPerformLongPressDelayed.cancel(); 542e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov // If the user is touch exploring the second pointer may be 543e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov // performing a double tap to activate an item without need 544e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov // for the user to lift his exploring finger. 54545af84a483165f06c04d74baba67f90da29c6ad2Svetoslav Ganov // It is *important* to use the distance traveled by the pointers 54645af84a483165f06c04d74baba67f90da29c6ad2Svetoslav Ganov // on the screen which may or may not be magnified. 547e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov final float deltaX = receivedTracker.getReceivedPointerDownX(pointerId) 54845af84a483165f06c04d74baba67f90da29c6ad2Svetoslav Ganov - rawEvent.getX(pointerIndex); 549e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov final float deltaY = receivedTracker.getReceivedPointerDownY(pointerId) 55045af84a483165f06c04d74baba67f90da29c6ad2Svetoslav Ganov - rawEvent.getY(pointerIndex); 551e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov final double moveDelta = Math.hypot(deltaX, deltaY); 552e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov if (moveDelta < mDoubleTapSlop) { 55347e02711d78ecac9112aa7f66e5664cdc46fb3d1Svetoslav Ganov break; 554736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov } 555e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov // We are sending events so send exit and gesture 556e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov // end since we transition to another state. 557f068fed6c4c3fc2003aec19b6e7e892358179b02Svetoslav Ganov sendHoverExitAndTouchExplorationGestureEndIfNeeded(policyFlags); 558736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov } 559e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov 560e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov // We know that a new state transition is to happen and the 561e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov // new state will not be gesture recognition, so clear the 562e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov // stashed gesture strokes. 563e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov mStrokeBuffer.clear(); 564736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov 565736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov if (isDraggingGesture(event)) { 566736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov // Two pointers moving in the same direction within 567736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov // a given distance perform a drag. 56800f7b3f76515d1c6fbe5cf9fee9d3760787c03cdSvetoslav Ganov mCurrentState = STATE_DRAGGING; 56991feae3c5994bd4768cea3507c62c65746adcfa6Svetoslav Ganov mDraggingPointerId = pointerId; 570c4842c11932ea4f60fe7ae09b0a59660207e1587Svetoslav Ganov event.setEdgeFlags(receivedTracker.getLastReceivedDownEdgeFlags()); 57191feae3c5994bd4768cea3507c62c65746adcfa6Svetoslav Ganov sendMotionEvent(event, MotionEvent.ACTION_DOWN, pointerIdBits, 57291feae3c5994bd4768cea3507c62c65746adcfa6Svetoslav Ganov policyFlags); 573736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov } else { 574736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov // Two pointers moving arbitrary are delegated to the view hierarchy. 575736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov mCurrentState = STATE_DELEGATING; 57684044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav sendDownForAllNotInjectedPointers(event, policyFlags); 577736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov } 57845af84a483165f06c04d74baba67f90da29c6ad2Svetoslav Ganov mVelocityTracker.clear(); 579736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov } break; 580736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov default: { 581e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov // More than one pointer so the user is not touch exploring 582e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov // and now we have to decide whether to delegate or drag. 58384044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav if (mSendHoverEnterAndMoveDelayed.isPending()) { 584e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov // We have not started sending events so cancel 585e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov // scheduled sending events. 58684044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav mSendHoverEnterAndMoveDelayed.cancel(); 58784044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav mSendHoverExitDelayed.cancel(); 58884044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav mPerformLongPressDelayed.cancel(); 589e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov } else { 59084044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav mPerformLongPressDelayed.cancel(); 591e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov // We are sending events so send exit and gesture 592e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov // end since we transition to another state. 593f068fed6c4c3fc2003aec19b6e7e892358179b02Svetoslav Ganov sendHoverExitAndTouchExplorationGestureEndIfNeeded(policyFlags); 594e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov } 595736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov 596736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov // More than two pointers are delegated to the view hierarchy. 597736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov mCurrentState = STATE_DELEGATING; 59884044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav sendDownForAllNotInjectedPointers(event, policyFlags); 59945af84a483165f06c04d74baba67f90da29c6ad2Svetoslav Ganov mVelocityTracker.clear(); 600736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov } 601736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov } 602736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov } break; 60384044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav case MotionEvent.ACTION_UP: { 604f772cba59760d1ad9eb5cb9205b2e2e9126e488dSvetoslav Ganov mAms.onTouchInteractionEnd(); 605e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov // We know that we do not need the pre-fed gesture points are not 606e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov // needed anymore since the last pointer just went up. 607e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov mStrokeBuffer.clear(); 60884044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav final int pointerId = event.getPointerId(event.getActionIndex()); 60991feae3c5994bd4768cea3507c62c65746adcfa6Svetoslav Ganov final int pointerIdBits = (1 << pointerId); 610736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov 61184044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav mPerformLongPressDelayed.cancel(); 61284044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav mVelocityTracker.clear(); 613f5a07905a3e025f95472a3f8d9935263e49ad6d3Svetoslav Ganov 61484044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav if (mSendHoverEnterAndMoveDelayed.isPending()) { 61584044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav // If we have not delivered the enter schedule an exit. 61684044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav mSendHoverExitDelayed.post(event, pointerIdBits, policyFlags); 61784044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav } else { 61884044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav // The user is touch exploring so we send events for end. 61984044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav sendHoverExitAndTouchExplorationGestureEndIfNeeded(policyFlags); 62084044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav } 621f068fed6c4c3fc2003aec19b6e7e892358179b02Svetoslav Ganov 62284044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav if (!mSendTouchInteractionEndDelayed.isPending()) { 62384044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav mSendTouchInteractionEndDelayed.post(); 624736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov } 62584044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav 626736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov } break; 627736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov case MotionEvent.ACTION_CANCEL: { 628e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov clear(event, policyFlags); 629736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov } break; 630736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov } 631736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov } 632736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov 633736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov /** 634736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov * Handles a motion event in dragging state. 635736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov * 636736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov * @param event The event to be handled. 637736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov * @param policyFlags The policy flags associated with the event. 638736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov */ 639736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov private void handleMotionEventStateDragging(MotionEvent event, int policyFlags) { 64091feae3c5994bd4768cea3507c62c65746adcfa6Svetoslav Ganov final int pointerIdBits = (1 << mDraggingPointerId); 641736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov switch (event.getActionMasked()) { 642736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov case MotionEvent.ACTION_DOWN: { 643736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov throw new IllegalStateException("Dragging state can be reached only if two " 644736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov + "pointers are already down"); 645736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov } 646736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov case MotionEvent.ACTION_POINTER_DOWN: { 647736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov // We are in dragging state so we have two pointers and another one 648736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov // goes down => delegate the three pointers to the view hierarchy 649736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov mCurrentState = STATE_DELEGATING; 650ec33d56300aa417efb4a055786d73d1bf23a6a85Svetoslav Ganov if (mDraggingPointerId != INVALID_POINTER_ID) { 651ec33d56300aa417efb4a055786d73d1bf23a6a85Svetoslav Ganov sendMotionEvent(event, MotionEvent.ACTION_UP, pointerIdBits, policyFlags); 652ec33d56300aa417efb4a055786d73d1bf23a6a85Svetoslav Ganov } 65384044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav sendDownForAllNotInjectedPointers(event, policyFlags); 654736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov } break; 655736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov case MotionEvent.ACTION_MOVE: { 65684044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav switch (event.getPointerCount()) { 6572e1c66bd53d30d2148afaa4b393b60cd59976d65Svetoslav Ganov case 1: { 6582e1c66bd53d30d2148afaa4b393b60cd59976d65Svetoslav Ganov // do nothing 6592e1c66bd53d30d2148afaa4b393b60cd59976d65Svetoslav Ganov } break; 660736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov case 2: { 661736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov if (isDraggingGesture(event)) { 66284044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav final float firstPtrX = event.getX(0); 66384044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav final float firstPtrY = event.getY(0); 66484044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav final float secondPtrX = event.getX(1); 66584044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav final float secondPtrY = event.getY(1); 666e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov 667e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov final float deltaX = firstPtrX - secondPtrX; 668e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov final float deltaY = firstPtrY - secondPtrY; 669e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov final double distance = Math.hypot(deltaX, deltaY); 670e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov 671e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov if (distance > mScaledMinPointerDistanceToUseMiddleLocation) { 672e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov event.setLocation(deltaX / 2, deltaY / 2); 673e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov } 674e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov 675736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov // If still dragging send a drag event. 67691feae3c5994bd4768cea3507c62c65746adcfa6Svetoslav Ganov sendMotionEvent(event, MotionEvent.ACTION_MOVE, pointerIdBits, 67791feae3c5994bd4768cea3507c62c65746adcfa6Svetoslav Ganov policyFlags); 678736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov } else { 679736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov // The two pointers are moving either in different directions or 680736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov // no close enough => delegate the gesture to the view hierarchy. 681736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov mCurrentState = STATE_DELEGATING; 682736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov // Send an event to the end of the drag gesture. 68391feae3c5994bd4768cea3507c62c65746adcfa6Svetoslav Ganov sendMotionEvent(event, MotionEvent.ACTION_UP, pointerIdBits, 68491feae3c5994bd4768cea3507c62c65746adcfa6Svetoslav Ganov policyFlags); 68584044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav // Deliver all pointers to the view hierarchy. 68684044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav sendDownForAllNotInjectedPointers(event, policyFlags); 687736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov } 688736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov } break; 689736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov default: { 690736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov mCurrentState = STATE_DELEGATING; 691736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov // Send an event to the end of the drag gesture. 69291feae3c5994bd4768cea3507c62c65746adcfa6Svetoslav Ganov sendMotionEvent(event, MotionEvent.ACTION_UP, pointerIdBits, 69391feae3c5994bd4768cea3507c62c65746adcfa6Svetoslav Ganov policyFlags); 69484044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav // Deliver all pointers to the view hierarchy. 69584044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav sendDownForAllNotInjectedPointers(event, policyFlags); 696736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov } 697736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov } 698736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov } break; 699aeb8d0ed0d98d398a66a092c418f4f2bca8719e0Svetoslav Ganov case MotionEvent.ACTION_POINTER_UP: { 700aeb8d0ed0d98d398a66a092c418f4f2bca8719e0Svetoslav Ganov final int pointerId = event.getPointerId(event.getActionIndex()); 701aeb8d0ed0d98d398a66a092c418f4f2bca8719e0Svetoslav Ganov if (pointerId == mDraggingPointerId) { 702aeb8d0ed0d98d398a66a092c418f4f2bca8719e0Svetoslav Ganov mDraggingPointerId = INVALID_POINTER_ID; 703aeb8d0ed0d98d398a66a092c418f4f2bca8719e0Svetoslav Ganov // Send an event to the end of the drag gesture. 704aeb8d0ed0d98d398a66a092c418f4f2bca8719e0Svetoslav Ganov sendMotionEvent(event, MotionEvent.ACTION_UP, pointerIdBits, policyFlags); 705aeb8d0ed0d98d398a66a092c418f4f2bca8719e0Svetoslav Ganov } 706aeb8d0ed0d98d398a66a092c418f4f2bca8719e0Svetoslav Ganov } break; 7072e1c66bd53d30d2148afaa4b393b60cd59976d65Svetoslav Ganov case MotionEvent.ACTION_UP: { 708f772cba59760d1ad9eb5cb9205b2e2e9126e488dSvetoslav Ganov mAms.onTouchInteractionEnd(); 70977276b60851a158ad3e142cb3b091d57ae5ceffbSvetoslav Ganov // Announce the end of a new touch interaction. 71077276b60851a158ad3e142cb3b091d57ae5ceffbSvetoslav Ganov sendAccessibilityEvent( 71177276b60851a158ad3e142cb3b091d57ae5ceffbSvetoslav Ganov AccessibilityEvent.TYPE_TOUCH_INTERACTION_END); 712aeb8d0ed0d98d398a66a092c418f4f2bca8719e0Svetoslav Ganov final int pointerId = event.getPointerId(event.getActionIndex()); 713aeb8d0ed0d98d398a66a092c418f4f2bca8719e0Svetoslav Ganov if (pointerId == mDraggingPointerId) { 714aeb8d0ed0d98d398a66a092c418f4f2bca8719e0Svetoslav Ganov mDraggingPointerId = INVALID_POINTER_ID; 715aeb8d0ed0d98d398a66a092c418f4f2bca8719e0Svetoslav Ganov // Send an event to the end of the drag gesture. 716aeb8d0ed0d98d398a66a092c418f4f2bca8719e0Svetoslav Ganov sendMotionEvent(event, MotionEvent.ACTION_UP, pointerIdBits, policyFlags); 717aeb8d0ed0d98d398a66a092c418f4f2bca8719e0Svetoslav Ganov } 7182e1c66bd53d30d2148afaa4b393b60cd59976d65Svetoslav Ganov mCurrentState = STATE_TOUCH_EXPLORING; 7192e1c66bd53d30d2148afaa4b393b60cd59976d65Svetoslav Ganov } break; 720736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov case MotionEvent.ACTION_CANCEL: { 721e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov clear(event, policyFlags); 722736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov } break; 723736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov } 724736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov } 725736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov 726736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov /** 727736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov * Handles a motion event in delegating state. 728736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov * 729736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov * @param event The event to be handled. 730736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov * @param policyFlags The policy flags associated with the event. 731736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov */ 7322e1c66bd53d30d2148afaa4b393b60cd59976d65Svetoslav Ganov private void handleMotionEventStateDelegating(MotionEvent event, int policyFlags) { 733736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov switch (event.getActionMasked()) { 734736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov case MotionEvent.ACTION_DOWN: { 735736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov throw new IllegalStateException("Delegating state can only be reached if " 736736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov + "there is at least one pointer down!"); 737736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov } 73884044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav case MotionEvent.ACTION_UP: { 7393d1c5a7236c4709550ca7c0cfa293fc5c974c56bAlan Viverette mAms.onTouchInteractionEnd(); 74084044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav // Announce the end of a the touch interaction. 74184044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav sendAccessibilityEvent(AccessibilityEvent.TYPE_TOUCH_INTERACTION_END); 742e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov mLongPressingPointerId = -1; 743e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov mLongPressingPointerDeltaX = 0; 744e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov mLongPressingPointerDeltaY = 0; 74584044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav mCurrentState = STATE_TOUCH_EXPLORING; 746736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov } break; 747736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov case MotionEvent.ACTION_CANCEL: { 748e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov clear(event, policyFlags); 749736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov } break; 750736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov } 75184044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav // Deliver the event. 75284044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav sendMotionEvent(event, event.getAction(), ALL_POINTER_ID_BITS, policyFlags); 753736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov } 754736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov 7554213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov private void handleMotionEventGestureDetecting(MotionEvent event, int policyFlags) { 7564213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov switch (event.getActionMasked()) { 7574213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov case MotionEvent.ACTION_DOWN: { 7584213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov final float x = event.getX(); 7594213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov final float y = event.getY(); 7604213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov mPreviousX = x; 7614213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov mPreviousY = y; 7624213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov mStrokeBuffer.add(new GesturePoint(x, y, event.getEventTime())); 7634213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov } break; 7644213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov case MotionEvent.ACTION_MOVE: { 7654213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov final float x = event.getX(); 7664213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov final float y = event.getY(); 7674213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov final float dX = Math.abs(x - mPreviousX); 7684213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov final float dY = Math.abs(y - mPreviousY); 7694213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov if (dX >= TOUCH_TOLERANCE || dY >= TOUCH_TOLERANCE) { 7704213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov mPreviousX = x; 7714213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov mPreviousY = y; 7724213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov mStrokeBuffer.add(new GesturePoint(x, y, event.getEventTime())); 7734213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov } 7744213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov } break; 775e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov case MotionEvent.ACTION_UP: { 776f772cba59760d1ad9eb5cb9205b2e2e9126e488dSvetoslav Ganov mAms.onTouchInteractionEnd(); 77784044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav // Announce the end of the gesture recognition. 77884044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav sendAccessibilityEvent(AccessibilityEvent.TYPE_GESTURE_DETECTION_END); 77984044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav // Announce the end of a the touch interaction. 78084044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav sendAccessibilityEvent(AccessibilityEvent.TYPE_TOUCH_INTERACTION_END); 78177276b60851a158ad3e142cb3b091d57ae5ceffbSvetoslav Ganov 7824213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov float x = event.getX(); 7834213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov float y = event.getY(); 7844213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov mStrokeBuffer.add(new GesturePoint(x, y, event.getEventTime())); 7854213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov 7864213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov Gesture gesture = new Gesture(); 7874213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov gesture.addStroke(new GestureStroke(mStrokeBuffer)); 7884213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov 7894213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov ArrayList<Prediction> predictions = mGestureLibrary.recognize(gesture); 7904213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov if (!predictions.isEmpty()) { 7914213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov Prediction bestPrediction = predictions.get(0); 7924213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov if (bestPrediction.score >= MIN_PREDICTION_SCORE) { 7934213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov if (DEBUG) { 7944213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov Slog.i(LOG_TAG, "gesture: " + bestPrediction.name + " score: " 7954213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov + bestPrediction.score); 7964213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov } 7974213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov try { 7984213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov final int gestureId = Integer.parseInt(bestPrediction.name); 799e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov mAms.onGesture(gestureId); 8004213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov } catch (NumberFormatException nfe) { 8014213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov Slog.w(LOG_TAG, "Non numeric gesture id:" + bestPrediction.name); 8024213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov } 8034213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov } 8044213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov } 8054213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov 8064213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov mStrokeBuffer.clear(); 80784044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav mExitGestureDetectionModeDelayed.cancel(); 8084213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov mCurrentState = STATE_TOUCH_EXPLORING; 8094213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov } break; 8104213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov case MotionEvent.ACTION_CANCEL: { 811e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov clear(event, policyFlags); 8124213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov } break; 8134213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov } 8144213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov } 8154213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov 816736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov /** 81777276b60851a158ad3e142cb3b091d57ae5ceffbSvetoslav Ganov * Sends an accessibility event of the given type. 81877276b60851a158ad3e142cb3b091d57ae5ceffbSvetoslav Ganov * 81977276b60851a158ad3e142cb3b091d57ae5ceffbSvetoslav Ganov * @param type The event type. 82077276b60851a158ad3e142cb3b091d57ae5ceffbSvetoslav Ganov */ 82177276b60851a158ad3e142cb3b091d57ae5ceffbSvetoslav Ganov private void sendAccessibilityEvent(int type) { 82277276b60851a158ad3e142cb3b091d57ae5ceffbSvetoslav Ganov AccessibilityManager accessibilityManager = AccessibilityManager.getInstance(mContext); 82377276b60851a158ad3e142cb3b091d57ae5ceffbSvetoslav Ganov if (accessibilityManager.isEnabled()) { 82477276b60851a158ad3e142cb3b091d57ae5ceffbSvetoslav Ganov AccessibilityEvent event = AccessibilityEvent.obtain(type); 8253d1c5a7236c4709550ca7c0cfa293fc5c974c56bAlan Viverette event.setWindowId(mAms.getActiveWindowId()); 82677276b60851a158ad3e142cb3b091d57ae5ceffbSvetoslav Ganov accessibilityManager.sendAccessibilityEvent(event); 827f068fed6c4c3fc2003aec19b6e7e892358179b02Svetoslav Ganov switch (type) { 828f068fed6c4c3fc2003aec19b6e7e892358179b02Svetoslav Ganov case AccessibilityEvent.TYPE_TOUCH_EXPLORATION_GESTURE_START: { 829f068fed6c4c3fc2003aec19b6e7e892358179b02Svetoslav Ganov mTouchExplorationInProgress = true; 830f068fed6c4c3fc2003aec19b6e7e892358179b02Svetoslav Ganov } break; 831f068fed6c4c3fc2003aec19b6e7e892358179b02Svetoslav Ganov case AccessibilityEvent.TYPE_TOUCH_EXPLORATION_GESTURE_END: { 832f068fed6c4c3fc2003aec19b6e7e892358179b02Svetoslav Ganov mTouchExplorationInProgress = false; 833f068fed6c4c3fc2003aec19b6e7e892358179b02Svetoslav Ganov } break; 834f068fed6c4c3fc2003aec19b6e7e892358179b02Svetoslav Ganov } 83577276b60851a158ad3e142cb3b091d57ae5ceffbSvetoslav Ganov } 83677276b60851a158ad3e142cb3b091d57ae5ceffbSvetoslav Ganov } 83777276b60851a158ad3e142cb3b091d57ae5ceffbSvetoslav Ganov 83877276b60851a158ad3e142cb3b091d57ae5ceffbSvetoslav Ganov /** 83984044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav * Sends down events to the view hierarchy for all pointers which are 840736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov * not already being delivered i.e. pointers that are not yet injected. 841736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov * 842736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov * @param prototype The prototype from which to create the injected events. 843736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov * @param policyFlags The policy flags associated with the event. 844736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov */ 84584044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav private void sendDownForAllNotInjectedPointers(MotionEvent prototype, int policyFlags) { 8464213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov InjectedPointerTracker injectedPointers = mInjectedPointerTracker; 84784044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav 84884044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav // Inject the injected pointers. 849f804420d6e37748b75478406e989c69303756980Svetoslav Ganov int pointerIdBits = 0; 850f804420d6e37748b75478406e989c69303756980Svetoslav Ganov final int pointerCount = prototype.getPointerCount(); 851f804420d6e37748b75478406e989c69303756980Svetoslav Ganov for (int i = 0; i < pointerCount; i++) { 852f804420d6e37748b75478406e989c69303756980Svetoslav Ganov final int pointerId = prototype.getPointerId(i); 853f804420d6e37748b75478406e989c69303756980Svetoslav Ganov // Do not send event for already delivered pointers. 85484044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav if (!injectedPointers.isInjectedPointerDown(pointerId)) { 85584044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav pointerIdBits |= (1 << pointerId); 85684044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav final int action = computeInjectionAction(MotionEvent.ACTION_DOWN, i); 85784044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav sendMotionEvent(prototype, action, pointerIdBits, policyFlags); 858736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov } 859f804420d6e37748b75478406e989c69303756980Svetoslav Ganov } 860f804420d6e37748b75478406e989c69303756980Svetoslav Ganov } 861736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov 862f804420d6e37748b75478406e989c69303756980Svetoslav Ganov /** 863e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov * Sends the exit events if needed. Such events are hover exit and touch explore 864e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov * gesture end. 865e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov * 866e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov * @param policyFlags The policy flags associated with the event. 867e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov */ 868f068fed6c4c3fc2003aec19b6e7e892358179b02Svetoslav Ganov private void sendHoverExitAndTouchExplorationGestureEndIfNeeded(int policyFlags) { 869e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov MotionEvent event = mInjectedPointerTracker.getLastInjectedHoverEvent(); 870e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov if (event != null && event.getActionMasked() != MotionEvent.ACTION_HOVER_EXIT) { 871e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov final int pointerIdBits = event.getPointerIdBits(); 872f068fed6c4c3fc2003aec19b6e7e892358179b02Svetoslav Ganov if (!mSendTouchExplorationEndDelayed.isPending()) { 873f068fed6c4c3fc2003aec19b6e7e892358179b02Svetoslav Ganov mSendTouchExplorationEndDelayed.post(); 874fe304b893968887323b93764caafa66ee8ad44deSvetoslav Ganov } 875e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov sendMotionEvent(event, MotionEvent.ACTION_HOVER_EXIT, pointerIdBits, policyFlags); 876e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov } 877e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov } 878e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov 879e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov /** 880e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov * Sends the enter events if needed. Such events are hover enter and touch explore 881e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov * gesture start. 882f804420d6e37748b75478406e989c69303756980Svetoslav Ganov * 883f804420d6e37748b75478406e989c69303756980Svetoslav Ganov * @param policyFlags The policy flags associated with the event. 884f804420d6e37748b75478406e989c69303756980Svetoslav Ganov */ 885f068fed6c4c3fc2003aec19b6e7e892358179b02Svetoslav Ganov private void sendTouchExplorationGestureStartAndHoverEnterIfNeeded(int policyFlags) { 886e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov MotionEvent event = mInjectedPointerTracker.getLastInjectedHoverEvent(); 887e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov if (event != null && event.getActionMasked() == MotionEvent.ACTION_HOVER_EXIT) { 888e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov final int pointerIdBits = event.getPointerIdBits(); 889f068fed6c4c3fc2003aec19b6e7e892358179b02Svetoslav Ganov sendAccessibilityEvent(AccessibilityEvent.TYPE_TOUCH_EXPLORATION_GESTURE_START); 890e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov sendMotionEvent(event, MotionEvent.ACTION_HOVER_ENTER, pointerIdBits, policyFlags); 891736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov } 892736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov } 893736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov 894736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov /** 89584044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav * Sends up events to the view hierarchy for all pointers which are 896736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov * already being delivered i.e. pointers that are injected. 897736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov * 898736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov * @param prototype The prototype from which to create the injected events. 899736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov * @param policyFlags The policy flags associated with the event. 900736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov */ 901736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov private void sendUpForInjectedDownPointers(MotionEvent prototype, int policyFlags) { 9024213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov final InjectedPointerTracker injectedTracked = mInjectedPointerTracker; 903f804420d6e37748b75478406e989c69303756980Svetoslav Ganov int pointerIdBits = 0; 904f804420d6e37748b75478406e989c69303756980Svetoslav Ganov final int pointerCount = prototype.getPointerCount(); 905736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov for (int i = 0; i < pointerCount; i++) { 906736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov final int pointerId = prototype.getPointerId(i); 907736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov // Skip non injected down pointers. 9084213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov if (!injectedTracked.isInjectedPointerDown(pointerId)) { 909736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov continue; 910736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov } 911f804420d6e37748b75478406e989c69303756980Svetoslav Ganov pointerIdBits |= (1 << pointerId); 912f804420d6e37748b75478406e989c69303756980Svetoslav Ganov final int action = computeInjectionAction(MotionEvent.ACTION_UP, i); 913f804420d6e37748b75478406e989c69303756980Svetoslav Ganov sendMotionEvent(prototype, action, pointerIdBits, policyFlags); 914736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov } 915736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov } 916736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov 917736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov /** 918736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov * Sends an up and down events. 919736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov * 920736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov * @param prototype The prototype from which to create the injected events. 921736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov * @param policyFlags The policy flags associated with the event. 922736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov */ 923736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov private void sendActionDownAndUp(MotionEvent prototype, int policyFlags) { 92484044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav // Tap with the pointer that last explored. 925bd206d129fdd1777b9f9646a834d7fc342a8941eSvetoslav Ganov final int pointerId = prototype.getPointerId(prototype.getActionIndex()); 926f804420d6e37748b75478406e989c69303756980Svetoslav Ganov final int pointerIdBits = (1 << pointerId); 927f804420d6e37748b75478406e989c69303756980Svetoslav Ganov sendMotionEvent(prototype, MotionEvent.ACTION_DOWN, pointerIdBits, policyFlags); 928f804420d6e37748b75478406e989c69303756980Svetoslav Ganov sendMotionEvent(prototype, MotionEvent.ACTION_UP, pointerIdBits, policyFlags); 929736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov } 930736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov 931736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov /** 93291feae3c5994bd4768cea3507c62c65746adcfa6Svetoslav Ganov * Sends an event. 933736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov * 93400f7b3f76515d1c6fbe5cf9fee9d3760787c03cdSvetoslav Ganov * @param prototype The prototype from which to create the injected events. 93591feae3c5994bd4768cea3507c62c65746adcfa6Svetoslav Ganov * @param action The action of the event. 93691feae3c5994bd4768cea3507c62c65746adcfa6Svetoslav Ganov * @param pointerIdBits The bits of the pointers to send. 937736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov * @param policyFlags The policy flags associated with the event. 938736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov */ 93991feae3c5994bd4768cea3507c62c65746adcfa6Svetoslav Ganov private void sendMotionEvent(MotionEvent prototype, int action, int pointerIdBits, 94091feae3c5994bd4768cea3507c62c65746adcfa6Svetoslav Ganov int policyFlags) { 941f804420d6e37748b75478406e989c69303756980Svetoslav Ganov prototype.setAction(action); 942f804420d6e37748b75478406e989c69303756980Svetoslav Ganov 943f804420d6e37748b75478406e989c69303756980Svetoslav Ganov MotionEvent event = null; 944f804420d6e37748b75478406e989c69303756980Svetoslav Ganov if (pointerIdBits == ALL_POINTER_ID_BITS) { 945f804420d6e37748b75478406e989c69303756980Svetoslav Ganov event = prototype; 946f804420d6e37748b75478406e989c69303756980Svetoslav Ganov } else { 947f804420d6e37748b75478406e989c69303756980Svetoslav Ganov event = prototype.split(pointerIdBits); 948f804420d6e37748b75478406e989c69303756980Svetoslav Ganov } 949f804420d6e37748b75478406e989c69303756980Svetoslav Ganov if (action == MotionEvent.ACTION_DOWN) { 950f804420d6e37748b75478406e989c69303756980Svetoslav Ganov event.setDownTime(event.getEventTime()); 951f804420d6e37748b75478406e989c69303756980Svetoslav Ganov } else { 9524213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov event.setDownTime(mInjectedPointerTracker.getLastInjectedDownEventTime()); 953f804420d6e37748b75478406e989c69303756980Svetoslav Ganov } 954f804420d6e37748b75478406e989c69303756980Svetoslav Ganov 955e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov // If the user is long pressing but the long pressing pointer 956e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov // was not exactly over the accessibility focused item we need 957e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov // to remap the location of that pointer so the user does not 958e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov // have to explicitly touch explore something to be able to 959e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov // long press it, or even worse to avoid the user long pressing 960e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov // on the wrong item since click and long press behave differently. 961e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov if (mLongPressingPointerId >= 0) { 962e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov final int remappedIndex = event.findPointerIndex(mLongPressingPointerId); 963e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov final int pointerCount = event.getPointerCount(); 964e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov PointerProperties[] props = PointerProperties.createArray(pointerCount); 965e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov PointerCoords[] coords = PointerCoords.createArray(pointerCount); 966e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov for (int i = 0; i < pointerCount; i++) { 967e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov event.getPointerProperties(i, props[i]); 968e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov event.getPointerCoords(i, coords[i]); 969e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov if (i == remappedIndex) { 970e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov coords[i].x -= mLongPressingPointerDeltaX; 971e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov coords[i].y -= mLongPressingPointerDeltaY; 972e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov } 973e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov } 974e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov MotionEvent remapped = MotionEvent.obtain(event.getDownTime(), 975e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov event.getEventTime(), event.getAction(), event.getPointerCount(), 976e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov props, coords, event.getMetaState(), event.getButtonState(), 977e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov 1.0f, 1.0f, event.getDeviceId(), event.getEdgeFlags(), 978e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov event.getSource(), event.getFlags()); 979e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov if (event != prototype) { 980e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov event.recycle(); 981e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov } 982e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov event = remapped; 983e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov } 984e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov 985f804420d6e37748b75478406e989c69303756980Svetoslav Ganov if (DEBUG) { 9864213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov Slog.d(LOG_TAG, "Injecting event: " + event + ", policyFlags=0x" 987f804420d6e37748b75478406e989c69303756980Svetoslav Ganov + Integer.toHexString(policyFlags)); 988f804420d6e37748b75478406e989c69303756980Svetoslav Ganov } 989f804420d6e37748b75478406e989c69303756980Svetoslav Ganov 990f804420d6e37748b75478406e989c69303756980Svetoslav Ganov // Make sure that the user will see the event. 991f804420d6e37748b75478406e989c69303756980Svetoslav Ganov policyFlags |= WindowManagerPolicy.FLAG_PASS_TO_USER; 9921cf70bbf96930662cab0e699d70b62865766ff52Svetoslav Ganov if (mNext != null) { 99345af84a483165f06c04d74baba67f90da29c6ad2Svetoslav Ganov // TODO: For now pass null for the raw event since the touch 99445af84a483165f06c04d74baba67f90da29c6ad2Svetoslav Ganov // explorer is the last event transformation and it does 99545af84a483165f06c04d74baba67f90da29c6ad2Svetoslav Ganov // not care about the raw event. 99645af84a483165f06c04d74baba67f90da29c6ad2Svetoslav Ganov mNext.onMotionEvent(event, null, policyFlags); 9971cf70bbf96930662cab0e699d70b62865766ff52Svetoslav Ganov } 998f804420d6e37748b75478406e989c69303756980Svetoslav Ganov 9994213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov mInjectedPointerTracker.onMotionEvent(event); 10004213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov 1001f804420d6e37748b75478406e989c69303756980Svetoslav Ganov if (event != prototype) { 1002f804420d6e37748b75478406e989c69303756980Svetoslav Ganov event.recycle(); 1003f804420d6e37748b75478406e989c69303756980Svetoslav Ganov } 1004736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov } 1005736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov 1006736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov /** 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 1042e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov private class DoubleTapDetector { 1043e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov private MotionEvent mDownEvent; 1044e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov private MotionEvent mFirstTapEvent; 1045e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov 1046e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov public void onMotionEvent(MotionEvent event, int policyFlags) { 10471cf70bbf96930662cab0e699d70b62865766ff52Svetoslav Ganov final int actionIndex = event.getActionIndex(); 1048e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov final int action = event.getActionMasked(); 1049e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov switch (action) { 1050e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov case MotionEvent.ACTION_DOWN: 1051e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov case MotionEvent.ACTION_POINTER_DOWN: { 10521cf70bbf96930662cab0e699d70b62865766ff52Svetoslav Ganov if (mFirstTapEvent != null 10531cf70bbf96930662cab0e699d70b62865766ff52Svetoslav Ganov && !GestureUtils.isSamePointerContext(mFirstTapEvent, event)) { 1054e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov clear(); 1055e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov } 1056e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov mDownEvent = MotionEvent.obtain(event); 1057e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov } break; 1058e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov case MotionEvent.ACTION_UP: 1059e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov case MotionEvent.ACTION_POINTER_UP: { 1060e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov if (mDownEvent == null) { 1061e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov return; 1062e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov } 10631cf70bbf96930662cab0e699d70b62865766ff52Svetoslav Ganov if (!GestureUtils.isSamePointerContext(mDownEvent, event)) { 1064e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov clear(); 1065e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov return; 1066e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov } 10671cf70bbf96930662cab0e699d70b62865766ff52Svetoslav Ganov if (GestureUtils.isTap(mDownEvent, event, mTapTimeout, mTouchSlop, 10681cf70bbf96930662cab0e699d70b62865766ff52Svetoslav Ganov actionIndex)) { 10691cf70bbf96930662cab0e699d70b62865766ff52Svetoslav Ganov if (mFirstTapEvent == null || GestureUtils.isTimedOut(mFirstTapEvent, 10701cf70bbf96930662cab0e699d70b62865766ff52Svetoslav Ganov event, mDoubleTapTimeout)) { 1071e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov mFirstTapEvent = MotionEvent.obtain(event); 1072e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov mDownEvent.recycle(); 1073e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov mDownEvent = null; 1074e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov return; 1075e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov } 10761cf70bbf96930662cab0e699d70b62865766ff52Svetoslav Ganov if (GestureUtils.isMultiTap(mFirstTapEvent, event, mDoubleTapTimeout, 10771cf70bbf96930662cab0e699d70b62865766ff52Svetoslav Ganov mDoubleTapSlop, actionIndex)) { 1078e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov onDoubleTap(event, policyFlags); 1079e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov mFirstTapEvent.recycle(); 1080e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov mFirstTapEvent = null; 1081e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov mDownEvent.recycle(); 1082e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov mDownEvent = null; 1083e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov return; 1084e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov } 1085e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov mFirstTapEvent.recycle(); 1086e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov mFirstTapEvent = null; 1087e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov } else { 1088e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov if (mFirstTapEvent != null) { 1089e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov mFirstTapEvent.recycle(); 1090e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov mFirstTapEvent = null; 1091e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov } 1092e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov } 1093e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov mDownEvent.recycle(); 1094e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov mDownEvent = null; 1095e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov } break; 1096e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov } 1097e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov } 1098e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov 1099e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov public void onDoubleTap(MotionEvent secondTapUp, int policyFlags) { 1100e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov // This should never be called when more than two pointers are down. 1101e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov if (secondTapUp.getPointerCount() > 2) { 1102e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov return; 1103e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov } 1104e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov 1105e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov // Remove pending event deliveries. 110684044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav mSendHoverEnterAndMoveDelayed.cancel(); 110784044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav mSendHoverExitDelayed.cancel(); 110884044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav mPerformLongPressDelayed.cancel(); 1109e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov 1110f068fed6c4c3fc2003aec19b6e7e892358179b02Svetoslav Ganov if (mSendTouchExplorationEndDelayed.isPending()) { 1111f068fed6c4c3fc2003aec19b6e7e892358179b02Svetoslav Ganov mSendTouchExplorationEndDelayed.forceSendAndRemove(); 1112f068fed6c4c3fc2003aec19b6e7e892358179b02Svetoslav Ganov } 1113f068fed6c4c3fc2003aec19b6e7e892358179b02Svetoslav Ganov if (mSendTouchInteractionEndDelayed.isPending()) { 1114f068fed6c4c3fc2003aec19b6e7e892358179b02Svetoslav Ganov mSendTouchInteractionEndDelayed.forceSendAndRemove(); 1115f068fed6c4c3fc2003aec19b6e7e892358179b02Svetoslav Ganov } 1116e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov 111786783474fdec98a22bc22e224462767eab13e273Svetoslav Ganov int clickLocationX; 111886783474fdec98a22bc22e224462767eab13e273Svetoslav Ganov int clickLocationY; 1119e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov 1120e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov final int pointerId = secondTapUp.getPointerId(secondTapUp.getActionIndex()); 1121e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov final int pointerIndex = secondTapUp.findPointerIndex(pointerId); 112286783474fdec98a22bc22e224462767eab13e273Svetoslav Ganov 11235d043ce8cc2f588fdfb336cc843fb3b07b196f83Svetoslav Ganov MotionEvent lastExploreEvent = 11245d043ce8cc2f588fdfb336cc843fb3b07b196f83Svetoslav Ganov mInjectedPointerTracker.getLastInjectedHoverEventForClick(); 112586783474fdec98a22bc22e224462767eab13e273Svetoslav Ganov if (lastExploreEvent == null) { 112686783474fdec98a22bc22e224462767eab13e273Svetoslav Ganov // No last touch explored event but there is accessibility focus in 112786783474fdec98a22bc22e224462767eab13e273Svetoslav Ganov // the active window. We click in the middle of the focus bounds. 112886783474fdec98a22bc22e224462767eab13e273Svetoslav Ganov Rect focusBounds = mTempRect; 112986783474fdec98a22bc22e224462767eab13e273Svetoslav Ganov if (mAms.getAccessibilityFocusBoundsInActiveWindow(focusBounds)) { 113086783474fdec98a22bc22e224462767eab13e273Svetoslav Ganov clickLocationX = focusBounds.centerX(); 113186783474fdec98a22bc22e224462767eab13e273Svetoslav Ganov clickLocationY = focusBounds.centerY(); 113286783474fdec98a22bc22e224462767eab13e273Svetoslav Ganov } else { 113386783474fdec98a22bc22e224462767eab13e273Svetoslav Ganov // Out of luck - do nothing. 113486783474fdec98a22bc22e224462767eab13e273Svetoslav Ganov return; 113586783474fdec98a22bc22e224462767eab13e273Svetoslav Ganov } 113686783474fdec98a22bc22e224462767eab13e273Svetoslav Ganov } else { 113786783474fdec98a22bc22e224462767eab13e273Svetoslav Ganov // If the click is within the active window but not within the 113886783474fdec98a22bc22e224462767eab13e273Svetoslav Ganov // accessibility focus bounds we click in the focus center. 113986783474fdec98a22bc22e224462767eab13e273Svetoslav Ganov final int lastExplorePointerIndex = lastExploreEvent.getActionIndex(); 114086783474fdec98a22bc22e224462767eab13e273Svetoslav Ganov clickLocationX = (int) lastExploreEvent.getX(lastExplorePointerIndex); 114186783474fdec98a22bc22e224462767eab13e273Svetoslav Ganov clickLocationY = (int) lastExploreEvent.getY(lastExplorePointerIndex); 114286783474fdec98a22bc22e224462767eab13e273Svetoslav Ganov Rect activeWindowBounds = mTempRect; 1143385d9f24b5ce2acb86c0dc192ce702718ab01c39Svetoslav Ganov if (mLastTouchedWindowId == mAms.getActiveWindowId()) { 1144385d9f24b5ce2acb86c0dc192ce702718ab01c39Svetoslav Ganov mAms.getActiveWindowBounds(activeWindowBounds); 1145385d9f24b5ce2acb86c0dc192ce702718ab01c39Svetoslav Ganov if (activeWindowBounds.contains(clickLocationX, clickLocationY)) { 1146385d9f24b5ce2acb86c0dc192ce702718ab01c39Svetoslav Ganov Rect focusBounds = mTempRect; 1147385d9f24b5ce2acb86c0dc192ce702718ab01c39Svetoslav Ganov if (mAms.getAccessibilityFocusBoundsInActiveWindow(focusBounds)) { 1148385d9f24b5ce2acb86c0dc192ce702718ab01c39Svetoslav Ganov if (!focusBounds.contains(clickLocationX, clickLocationY)) { 1149385d9f24b5ce2acb86c0dc192ce702718ab01c39Svetoslav Ganov clickLocationX = focusBounds.centerX(); 1150385d9f24b5ce2acb86c0dc192ce702718ab01c39Svetoslav Ganov clickLocationY = focusBounds.centerY(); 1151385d9f24b5ce2acb86c0dc192ce702718ab01c39Svetoslav Ganov } 115286783474fdec98a22bc22e224462767eab13e273Svetoslav Ganov } 115386783474fdec98a22bc22e224462767eab13e273Svetoslav Ganov } 1154e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov } 1155e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov } 1156e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov 1157e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov // Do the click. 1158e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov PointerProperties[] properties = new PointerProperties[1]; 1159e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov properties[0] = new PointerProperties(); 1160e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov secondTapUp.getPointerProperties(pointerIndex, properties[0]); 1161e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov PointerCoords[] coords = new PointerCoords[1]; 1162e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov coords[0] = new PointerCoords(); 116386783474fdec98a22bc22e224462767eab13e273Svetoslav Ganov coords[0].x = clickLocationX; 116486783474fdec98a22bc22e224462767eab13e273Svetoslav Ganov coords[0].y = clickLocationY; 1165e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov MotionEvent event = MotionEvent.obtain(secondTapUp.getDownTime(), 1166e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov secondTapUp.getEventTime(), MotionEvent.ACTION_DOWN, 1, properties, 1167e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov coords, 0, 0, 1.0f, 1.0f, secondTapUp.getDeviceId(), 0, 1168e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov secondTapUp.getSource(), secondTapUp.getFlags()); 1169e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov sendActionDownAndUp(event, policyFlags); 1170e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov event.recycle(); 1171e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov } 1172e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov 1173e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov public void clear() { 1174e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov if (mDownEvent != null) { 1175e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov mDownEvent.recycle(); 1176e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov mDownEvent = null; 1177e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov } 1178e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov if (mFirstTapEvent != null) { 1179e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov mFirstTapEvent.recycle(); 1180e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov mFirstTapEvent = null; 1181e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov } 1182e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov } 1183e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov 1184e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov public boolean firstTapDetected() { 1185e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov return mFirstTapEvent != null 1186e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov && SystemClock.uptimeMillis() - mFirstTapEvent.getEventTime() < mDoubleTapTimeout; 1187e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov } 1188e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov } 1189e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov 1190736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov /** 1191736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov * Determines whether a two pointer gesture is a dragging one. 1192736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov * 1193736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov * @param event The event with the pointer data. 1194736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov * @return True if the gesture is a dragging one. 1195736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov */ 1196736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov private boolean isDraggingGesture(MotionEvent event) { 11974213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov ReceivedPointerTracker receivedTracker = mReceivedPointerTracker; 1198736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov 119984044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav final float firstPtrX = event.getX(0); 120084044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav final float firstPtrY = event.getY(0); 120184044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav final float secondPtrX = event.getX(1); 120284044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav final float secondPtrY = event.getY(1); 1203736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov 120484044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav final float firstPtrDownX = receivedTracker.getReceivedPointerDownX(0); 120584044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav final float firstPtrDownY = receivedTracker.getReceivedPointerDownY(0); 120684044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav final float secondPtrDownX = receivedTracker.getReceivedPointerDownX(1); 120784044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav final float secondPtrDownY = receivedTracker.getReceivedPointerDownY(1); 1208736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov 12091cf70bbf96930662cab0e699d70b62865766ff52Svetoslav Ganov return GestureUtils.isDraggingGesture(firstPtrDownX, firstPtrDownY, secondPtrDownX, 12101cf70bbf96930662cab0e699d70b62865766ff52Svetoslav Ganov secondPtrDownY, firstPtrX, firstPtrY, secondPtrX, secondPtrY, 12111cf70bbf96930662cab0e699d70b62865766ff52Svetoslav Ganov MAX_DRAGGING_ANGLE_COS); 1212736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov } 1213736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov 1214736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov /** 121551cccf0845b36539d42503495f0689d487712b3aSvetoslav Ganov * Gets the symbolic name of a state. 121651cccf0845b36539d42503495f0689d487712b3aSvetoslav Ganov * 121751cccf0845b36539d42503495f0689d487712b3aSvetoslav Ganov * @param state A state. 121851cccf0845b36539d42503495f0689d487712b3aSvetoslav Ganov * @return The state symbolic name. 121951cccf0845b36539d42503495f0689d487712b3aSvetoslav Ganov */ 122051cccf0845b36539d42503495f0689d487712b3aSvetoslav Ganov private static String getStateSymbolicName(int state) { 122151cccf0845b36539d42503495f0689d487712b3aSvetoslav Ganov switch (state) { 122251cccf0845b36539d42503495f0689d487712b3aSvetoslav Ganov case STATE_TOUCH_EXPLORING: 122351cccf0845b36539d42503495f0689d487712b3aSvetoslav Ganov return "STATE_TOUCH_EXPLORING"; 122451cccf0845b36539d42503495f0689d487712b3aSvetoslav Ganov case STATE_DRAGGING: 122551cccf0845b36539d42503495f0689d487712b3aSvetoslav Ganov return "STATE_DRAGGING"; 122651cccf0845b36539d42503495f0689d487712b3aSvetoslav Ganov case STATE_DELEGATING: 122751cccf0845b36539d42503495f0689d487712b3aSvetoslav Ganov return "STATE_DELEGATING"; 12284213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov case STATE_GESTURE_DETECTING: 12294213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov return "STATE_GESTURE_DETECTING"; 123051cccf0845b36539d42503495f0689d487712b3aSvetoslav Ganov default: 123151cccf0845b36539d42503495f0689d487712b3aSvetoslav Ganov throw new IllegalArgumentException("Unknown state: " + state); 123251cccf0845b36539d42503495f0689d487712b3aSvetoslav Ganov } 123351cccf0845b36539d42503495f0689d487712b3aSvetoslav Ganov } 123451cccf0845b36539d42503495f0689d487712b3aSvetoslav Ganov 123551cccf0845b36539d42503495f0689d487712b3aSvetoslav Ganov /** 123695068e5d1bea47091e97955f271c789264994550Svetoslav Ganov * Class for delayed exiting from gesture detecting mode. 123795068e5d1bea47091e97955f271c789264994550Svetoslav Ganov */ 123895068e5d1bea47091e97955f271c789264994550Svetoslav Ganov private final class ExitGestureDetectionModeDelayed implements Runnable { 123995068e5d1bea47091e97955f271c789264994550Svetoslav Ganov 124095068e5d1bea47091e97955f271c789264994550Svetoslav Ganov public void post() { 124195068e5d1bea47091e97955f271c789264994550Svetoslav Ganov mHandler.postDelayed(this, EXIT_GESTURE_DETECTION_TIMEOUT); 124295068e5d1bea47091e97955f271c789264994550Svetoslav Ganov } 124395068e5d1bea47091e97955f271c789264994550Svetoslav Ganov 124484044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav public void cancel() { 124595068e5d1bea47091e97955f271c789264994550Svetoslav Ganov mHandler.removeCallbacks(this); 124695068e5d1bea47091e97955f271c789264994550Svetoslav Ganov } 124795068e5d1bea47091e97955f271c789264994550Svetoslav Ganov 124895068e5d1bea47091e97955f271c789264994550Svetoslav Ganov @Override 124995068e5d1bea47091e97955f271c789264994550Svetoslav Ganov public void run() { 1250aed4b6f812674bc60a04470013ca449e5c114fa5Svetoslav Ganov // Announce the end of gesture recognition. 1251aed4b6f812674bc60a04470013ca449e5c114fa5Svetoslav Ganov sendAccessibilityEvent(AccessibilityEvent.TYPE_GESTURE_DETECTION_END); 1252aed4b6f812674bc60a04470013ca449e5c114fa5Svetoslav Ganov // Clearing puts is in touch exploration state with a finger already 1253aed4b6f812674bc60a04470013ca449e5c114fa5Svetoslav Ganov // down, so announce the transition to exploration state. 1254aed4b6f812674bc60a04470013ca449e5c114fa5Svetoslav Ganov sendAccessibilityEvent(AccessibilityEvent.TYPE_TOUCH_EXPLORATION_GESTURE_START); 125595068e5d1bea47091e97955f271c789264994550Svetoslav Ganov clear(); 125695068e5d1bea47091e97955f271c789264994550Svetoslav Ganov } 125795068e5d1bea47091e97955f271c789264994550Svetoslav Ganov } 125895068e5d1bea47091e97955f271c789264994550Svetoslav Ganov 125995068e5d1bea47091e97955f271c789264994550Svetoslav Ganov /** 12604213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov * Class for delayed sending of long press. 12614213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov */ 12624213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov private final class PerformLongPressDelayed implements Runnable { 12634213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov private MotionEvent mEvent; 12644213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov private int mPolicyFlags; 12654213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov 1266e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov public void post(MotionEvent prototype, int policyFlags) { 12674213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov mEvent = MotionEvent.obtain(prototype); 12684213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov mPolicyFlags = policyFlags; 1269e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov mHandler.postDelayed(this, ViewConfiguration.getLongPressTimeout()); 12704213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov } 12714213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov 127284044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav public void cancel() { 127384044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav if (mEvent != null) { 12744213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov mHandler.removeCallbacks(this); 12754213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov clear(); 12764213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov } 12774213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov } 12784213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov 127984044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav private boolean isPending() { 128084044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav return mHandler.hasCallbacks(this); 12814213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov } 12824213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov 12834213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov @Override 12844213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov public void run() { 128584044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav // Pointers should not be zero when running this command. 128684044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav if (mReceivedPointerTracker.getLastReceivedEvent().getPointerCount() == 0) { 1287ebac1b79c4a355d8cd73b49df059deb00d7aa256Svetoslav Ganov return; 1288ebac1b79c4a355d8cd73b49df059deb00d7aa256Svetoslav Ganov } 1289ebac1b79c4a355d8cd73b49df059deb00d7aa256Svetoslav Ganov 129086783474fdec98a22bc22e224462767eab13e273Svetoslav Ganov int clickLocationX; 129186783474fdec98a22bc22e224462767eab13e273Svetoslav Ganov int clickLocationY; 1292238099c0dbbdc66b8443552126680ad1c7cab17dSvetoslav Ganov 1293238099c0dbbdc66b8443552126680ad1c7cab17dSvetoslav Ganov final int pointerId = mEvent.getPointerId(mEvent.getActionIndex()); 1294238099c0dbbdc66b8443552126680ad1c7cab17dSvetoslav Ganov final int pointerIndex = mEvent.findPointerIndex(pointerId); 1295238099c0dbbdc66b8443552126680ad1c7cab17dSvetoslav Ganov 12965d043ce8cc2f588fdfb336cc843fb3b07b196f83Svetoslav Ganov MotionEvent lastExploreEvent = 12975d043ce8cc2f588fdfb336cc843fb3b07b196f83Svetoslav Ganov mInjectedPointerTracker.getLastInjectedHoverEventForClick(); 129886783474fdec98a22bc22e224462767eab13e273Svetoslav Ganov if (lastExploreEvent == null) { 129986783474fdec98a22bc22e224462767eab13e273Svetoslav Ganov // No last touch explored event but there is accessibility focus in 130086783474fdec98a22bc22e224462767eab13e273Svetoslav Ganov // the active window. We click in the middle of the focus bounds. 130186783474fdec98a22bc22e224462767eab13e273Svetoslav Ganov Rect focusBounds = mTempRect; 130286783474fdec98a22bc22e224462767eab13e273Svetoslav Ganov if (mAms.getAccessibilityFocusBoundsInActiveWindow(focusBounds)) { 130386783474fdec98a22bc22e224462767eab13e273Svetoslav Ganov clickLocationX = focusBounds.centerX(); 130486783474fdec98a22bc22e224462767eab13e273Svetoslav Ganov clickLocationY = focusBounds.centerY(); 130586783474fdec98a22bc22e224462767eab13e273Svetoslav Ganov } else { 130686783474fdec98a22bc22e224462767eab13e273Svetoslav Ganov // Out of luck - do nothing. 130786783474fdec98a22bc22e224462767eab13e273Svetoslav Ganov return; 130886783474fdec98a22bc22e224462767eab13e273Svetoslav Ganov } 1309e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov } else { 131086783474fdec98a22bc22e224462767eab13e273Svetoslav Ganov // If the click is within the active window but not within the 131186783474fdec98a22bc22e224462767eab13e273Svetoslav Ganov // accessibility focus bounds we click in the focus center. 131286783474fdec98a22bc22e224462767eab13e273Svetoslav Ganov final int lastExplorePointerIndex = lastExploreEvent.getActionIndex(); 131386783474fdec98a22bc22e224462767eab13e273Svetoslav Ganov clickLocationX = (int) lastExploreEvent.getX(lastExplorePointerIndex); 131486783474fdec98a22bc22e224462767eab13e273Svetoslav Ganov clickLocationY = (int) lastExploreEvent.getY(lastExplorePointerIndex); 131586783474fdec98a22bc22e224462767eab13e273Svetoslav Ganov Rect activeWindowBounds = mTempRect; 1316385d9f24b5ce2acb86c0dc192ce702718ab01c39Svetoslav Ganov if (mLastTouchedWindowId == mAms.getActiveWindowId()) { 1317385d9f24b5ce2acb86c0dc192ce702718ab01c39Svetoslav Ganov mAms.getActiveWindowBounds(activeWindowBounds); 1318385d9f24b5ce2acb86c0dc192ce702718ab01c39Svetoslav Ganov if (activeWindowBounds.contains(clickLocationX, clickLocationY)) { 1319385d9f24b5ce2acb86c0dc192ce702718ab01c39Svetoslav Ganov Rect focusBounds = mTempRect; 1320385d9f24b5ce2acb86c0dc192ce702718ab01c39Svetoslav Ganov if (mAms.getAccessibilityFocusBoundsInActiveWindow(focusBounds)) { 1321385d9f24b5ce2acb86c0dc192ce702718ab01c39Svetoslav Ganov if (!focusBounds.contains(clickLocationX, clickLocationY)) { 1322385d9f24b5ce2acb86c0dc192ce702718ab01c39Svetoslav Ganov clickLocationX = focusBounds.centerX(); 1323385d9f24b5ce2acb86c0dc192ce702718ab01c39Svetoslav Ganov clickLocationY = focusBounds.centerY(); 1324385d9f24b5ce2acb86c0dc192ce702718ab01c39Svetoslav Ganov } 132586783474fdec98a22bc22e224462767eab13e273Svetoslav Ganov } 132686783474fdec98a22bc22e224462767eab13e273Svetoslav Ganov } 132786783474fdec98a22bc22e224462767eab13e273Svetoslav Ganov } 1328e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov } 1329238099c0dbbdc66b8443552126680ad1c7cab17dSvetoslav Ganov 133086783474fdec98a22bc22e224462767eab13e273Svetoslav Ganov mLongPressingPointerId = pointerId; 133186783474fdec98a22bc22e224462767eab13e273Svetoslav Ganov mLongPressingPointerDeltaX = (int) mEvent.getX(pointerIndex) - clickLocationX; 133286783474fdec98a22bc22e224462767eab13e273Svetoslav Ganov mLongPressingPointerDeltaY = (int) mEvent.getY(pointerIndex) - clickLocationY; 133386783474fdec98a22bc22e224462767eab13e273Svetoslav Ganov 1334f068fed6c4c3fc2003aec19b6e7e892358179b02Svetoslav Ganov sendHoverExitAndTouchExplorationGestureEndIfNeeded(mPolicyFlags); 13354213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov 1336e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov mCurrentState = STATE_DELEGATING; 133784044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav sendDownForAllNotInjectedPointers(mEvent, mPolicyFlags); 13384213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov clear(); 13394213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov } 13404213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov 13414213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov private void clear() { 13424213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov mEvent.recycle(); 13434213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov mEvent = null; 13444213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov mPolicyFlags = 0; 13454213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov } 13464213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov } 13474213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov 13484213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov /** 134984044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav * Class for delayed sending of hover enter and move events. 13504213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov */ 135184044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav class SendHoverEnterAndMoveDelayed implements Runnable { 135284044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav private final String LOG_TAG_SEND_HOVER_DELAYED = "SendHoverEnterAndMoveDelayed"; 1353e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov 135484044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav private final List<MotionEvent> mEvents = new ArrayList<MotionEvent>(); 1355e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov 13564213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov private int mPointerIdBits; 13574213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov private int mPolicyFlags; 13584213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov 135984044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav public void post(MotionEvent event, boolean touchExplorationInProgress, 136077276b60851a158ad3e142cb3b091d57ae5ceffbSvetoslav Ganov int pointerIdBits, int policyFlags) { 136184044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav cancel(); 136284044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav addEvent(event); 13634213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov mPointerIdBits = pointerIdBits; 13644213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov mPolicyFlags = policyFlags; 1365e47957a0bbe2164467ff6e7a566b0c9e4689cdc9Svetoslav Ganov mHandler.postDelayed(this, mDetermineUserIntentTimeout); 1366e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov } 1367e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov 136884044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav public void addEvent(MotionEvent event) { 136984044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav mEvents.add(MotionEvent.obtain(event)); 137084044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav } 137184044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav 137284044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav public void cancel() { 1373e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov if (isPending()) { 137484044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav mHandler.removeCallbacks(this); 137584044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav clear(); 137684044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav } 137784044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav } 137884044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav 137984044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav private boolean isPending() { 138084044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav return mHandler.hasCallbacks(this); 138184044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav } 138284044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav 138384044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav private void clear() { 138484044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav mPointerIdBits = -1; 138584044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav mPolicyFlags = 0; 138684044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav final int eventCount = mEvents.size(); 138784044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav for (int i = eventCount - 1; i >= 0; i--) { 138884044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav mEvents.remove(i).recycle(); 1389e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov } 1390e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov } 1391e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov 139284044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav public void forceSendAndRemove() { 1393e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov if (isPending()) { 139484044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav run(); 139584044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav cancel(); 1396e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov } 13974213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov } 13984213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov 139984044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav public void run() { 140084044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav // Send an accessibility event to announce the touch exploration start. 140184044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav sendAccessibilityEvent(AccessibilityEvent.TYPE_TOUCH_EXPLORATION_GESTURE_START); 140284044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav 140384044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav if (!mEvents.isEmpty()) { 140484044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav // Deliver a down event. 140584044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav sendMotionEvent(mEvents.get(0), MotionEvent.ACTION_HOVER_ENTER, 140684044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav mPointerIdBits, mPolicyFlags); 140784044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav if (DEBUG) { 140884044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav Slog.d(LOG_TAG_SEND_HOVER_DELAYED, 140984044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav "Injecting motion event: ACTION_HOVER_ENTER"); 141084044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav } 141184044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav 141284044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav // Deliver move events. 141384044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav final int eventCount = mEvents.size(); 141484044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav for (int i = 1; i < eventCount; i++) { 141584044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav sendMotionEvent(mEvents.get(i), MotionEvent.ACTION_HOVER_MOVE, 141684044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav mPointerIdBits, mPolicyFlags); 141784044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav if (DEBUG) { 141884044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav Slog.d(LOG_TAG_SEND_HOVER_DELAYED, 141984044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav "Injecting motion event: ACTION_HOVER_MOVE"); 142084044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav } 142184044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav } 142284044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav } 14234213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov clear(); 14244213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov } 142584044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav } 142684044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav 142784044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav /** 142884044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav * Class for delayed sending of hover exit events. 142984044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav */ 143084044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav class SendHoverExitDelayed implements Runnable { 143184044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav private final String LOG_TAG_SEND_HOVER_DELAYED = "SendHoverExitDelayed"; 143284044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav 143384044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav private MotionEvent mPrototype; 143484044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav private int mPointerIdBits; 143584044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav private int mPolicyFlags; 143684044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav 143784044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav public void post(MotionEvent prototype, int pointerIdBits, int policyFlags) { 143884044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav cancel(); 143984044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav mPrototype = MotionEvent.obtain(prototype); 144084044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav mPointerIdBits = pointerIdBits; 144184044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav mPolicyFlags = policyFlags; 144284044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav mHandler.postDelayed(this, mDetermineUserIntentTimeout); 144384044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav } 144484044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav 144584044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav public void cancel() { 144684044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav if (isPending()) { 144784044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav mHandler.removeCallbacks(this); 144884044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav clear(); 144984044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav } 145084044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav } 14514213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov 1452e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov private boolean isPending() { 145384044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav return mHandler.hasCallbacks(this); 14544213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov } 14554213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov 14564213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov private void clear() { 1457e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov mPrototype.recycle(); 1458e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov mPrototype = null; 14594213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov mPointerIdBits = -1; 14604213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov mPolicyFlags = 0; 14614213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov } 14624213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov 14634213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov public void forceSendAndRemove() { 1464e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov if (isPending()) { 14654213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov run(); 146684044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav cancel(); 14674213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov } 14684213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov } 14694213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov 14704213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov public void run() { 14714213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov if (DEBUG) { 147284044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav Slog.d(LOG_TAG_SEND_HOVER_DELAYED, "Injecting motion event:" 147384044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav + " ACTION_HOVER_EXIT"); 14744213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov } 147584044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav sendMotionEvent(mPrototype, MotionEvent.ACTION_HOVER_EXIT, 147684044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav mPointerIdBits, mPolicyFlags); 147784044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav if (!mSendTouchExplorationEndDelayed.isPending()) { 147884044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav mSendTouchExplorationEndDelayed.cancel(); 147984044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav mSendTouchExplorationEndDelayed.post(); 148084044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav } 148184044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav if (mSendTouchInteractionEndDelayed.isPending()) { 148284044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav mSendTouchInteractionEndDelayed.cancel(); 148384044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav mSendTouchInteractionEndDelayed.post(); 1484e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov } 14854213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov clear(); 14864213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov } 14874213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov } 14884213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov 1489f068fed6c4c3fc2003aec19b6e7e892358179b02Svetoslav Ganov private class SendAccessibilityEventDelayed implements Runnable { 1490f068fed6c4c3fc2003aec19b6e7e892358179b02Svetoslav Ganov private final int mEventType; 1491f068fed6c4c3fc2003aec19b6e7e892358179b02Svetoslav Ganov private final int mDelay; 1492f068fed6c4c3fc2003aec19b6e7e892358179b02Svetoslav Ganov 1493f068fed6c4c3fc2003aec19b6e7e892358179b02Svetoslav Ganov public SendAccessibilityEventDelayed(int eventType, int delay) { 1494f068fed6c4c3fc2003aec19b6e7e892358179b02Svetoslav Ganov mEventType = eventType; 1495f068fed6c4c3fc2003aec19b6e7e892358179b02Svetoslav Ganov mDelay = delay; 1496f068fed6c4c3fc2003aec19b6e7e892358179b02Svetoslav Ganov } 1497fe304b893968887323b93764caafa66ee8ad44deSvetoslav Ganov 149884044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav public void cancel() { 1499fe304b893968887323b93764caafa66ee8ad44deSvetoslav Ganov mHandler.removeCallbacks(this); 1500fe304b893968887323b93764caafa66ee8ad44deSvetoslav Ganov } 1501fe304b893968887323b93764caafa66ee8ad44deSvetoslav Ganov 1502fe304b893968887323b93764caafa66ee8ad44deSvetoslav Ganov public void post() { 1503f068fed6c4c3fc2003aec19b6e7e892358179b02Svetoslav Ganov mHandler.postDelayed(this, mDelay); 1504fe304b893968887323b93764caafa66ee8ad44deSvetoslav Ganov } 1505fe304b893968887323b93764caafa66ee8ad44deSvetoslav Ganov 1506fe304b893968887323b93764caafa66ee8ad44deSvetoslav Ganov public boolean isPending() { 1507fe304b893968887323b93764caafa66ee8ad44deSvetoslav Ganov return mHandler.hasCallbacks(this); 1508fe304b893968887323b93764caafa66ee8ad44deSvetoslav Ganov } 1509fe304b893968887323b93764caafa66ee8ad44deSvetoslav Ganov 1510fe304b893968887323b93764caafa66ee8ad44deSvetoslav Ganov public void forceSendAndRemove() { 1511fe304b893968887323b93764caafa66ee8ad44deSvetoslav Ganov if (isPending()) { 1512fe304b893968887323b93764caafa66ee8ad44deSvetoslav Ganov run(); 151384044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav cancel(); 1514fe304b893968887323b93764caafa66ee8ad44deSvetoslav Ganov } 1515fe304b893968887323b93764caafa66ee8ad44deSvetoslav Ganov } 1516fe304b893968887323b93764caafa66ee8ad44deSvetoslav Ganov 1517fe304b893968887323b93764caafa66ee8ad44deSvetoslav Ganov @Override 1518fe304b893968887323b93764caafa66ee8ad44deSvetoslav Ganov public void run() { 1519f068fed6c4c3fc2003aec19b6e7e892358179b02Svetoslav Ganov sendAccessibilityEvent(mEventType); 1520fe304b893968887323b93764caafa66ee8ad44deSvetoslav Ganov } 1521fe304b893968887323b93764caafa66ee8ad44deSvetoslav Ganov } 1522fe304b893968887323b93764caafa66ee8ad44deSvetoslav Ganov 15234213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov @Override 15244213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov public String toString() { 15254213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov return LOG_TAG; 15264213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov } 15274213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov 15284213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov class InjectedPointerTracker { 15294213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov private static final String LOG_TAG_INJECTED_POINTER_TRACKER = "InjectedPointerTracker"; 15304213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov 15314213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov // Keep track of which pointers sent to the system are down. 15324213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov private int mInjectedPointersDown; 15334213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov 15344213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov // The time of the last injected down. 15354213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov private long mLastInjectedDownEventTime; 15364213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov 1537e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov // The last injected hover event. 1538e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov private MotionEvent mLastInjectedHoverEvent; 15394213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov 15405d043ce8cc2f588fdfb336cc843fb3b07b196f83Svetoslav Ganov // The last injected hover event used for performing clicks. 15415d043ce8cc2f588fdfb336cc843fb3b07b196f83Svetoslav Ganov private MotionEvent mLastInjectedHoverEventForClick; 15425d043ce8cc2f588fdfb336cc843fb3b07b196f83Svetoslav Ganov 15434213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov /** 15444213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov * Processes an injected {@link MotionEvent} event. 15454213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov * 15464213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov * @param event The event to process. 15474213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov */ 15484213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov public void onMotionEvent(MotionEvent event) { 15494213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov final int action = event.getActionMasked(); 15504213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov switch (action) { 15514213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov case MotionEvent.ACTION_DOWN: 15524213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov case MotionEvent.ACTION_POINTER_DOWN: { 15534213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov final int pointerId = event.getPointerId(event.getActionIndex()); 15544213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov final int pointerFlag = (1 << pointerId); 15554213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov mInjectedPointersDown |= pointerFlag; 15564213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov mLastInjectedDownEventTime = event.getDownTime(); 15574213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov } break; 15584213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov case MotionEvent.ACTION_UP: 15594213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov case MotionEvent.ACTION_POINTER_UP: { 15604213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov final int pointerId = event.getPointerId(event.getActionIndex()); 15614213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov final int pointerFlag = (1 << pointerId); 15624213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov mInjectedPointersDown &= ~pointerFlag; 15634213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov if (mInjectedPointersDown == 0) { 15644213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov mLastInjectedDownEventTime = 0; 15654213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov } 15664213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov } break; 15674213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov case MotionEvent.ACTION_HOVER_ENTER: 15684213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov case MotionEvent.ACTION_HOVER_MOVE: 15694213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov case MotionEvent.ACTION_HOVER_EXIT: { 1570e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov if (mLastInjectedHoverEvent != null) { 1571e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov mLastInjectedHoverEvent.recycle(); 1572e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov } 1573e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov mLastInjectedHoverEvent = MotionEvent.obtain(event); 15745d043ce8cc2f588fdfb336cc843fb3b07b196f83Svetoslav Ganov if (mLastInjectedHoverEventForClick != null) { 15755d043ce8cc2f588fdfb336cc843fb3b07b196f83Svetoslav Ganov mLastInjectedHoverEventForClick.recycle(); 15765d043ce8cc2f588fdfb336cc843fb3b07b196f83Svetoslav Ganov } 15775d043ce8cc2f588fdfb336cc843fb3b07b196f83Svetoslav Ganov mLastInjectedHoverEventForClick = MotionEvent.obtain(event); 15784213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov } break; 15794213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov } 15804213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov if (DEBUG) { 1581e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov Slog.i(LOG_TAG_INJECTED_POINTER_TRACKER, "Injected pointer:\n" + toString()); 15824213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov } 15834213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov } 15844213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov 15854213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov /** 15864213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov * Clears the internals state. 15874213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov */ 15884213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov public void clear() { 15894213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov mInjectedPointersDown = 0; 15904213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov } 15914213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov 15924213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov /** 15934213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov * @return The time of the last injected down event. 15944213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov */ 15954213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov public long getLastInjectedDownEventTime() { 15964213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov return mLastInjectedDownEventTime; 15974213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov } 15984213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov 15994213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov /** 16004213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov * @return The number of down pointers injected to the view hierarchy. 16014213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov */ 16024213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov public int getInjectedPointerDownCount() { 16034213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov return Integer.bitCount(mInjectedPointersDown); 16044213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov } 16054213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov 16064213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov /** 16074213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov * @return The bits of the injected pointers that are down. 16084213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov */ 16094213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov public int getInjectedPointersDown() { 16104213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov return mInjectedPointersDown; 16114213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov } 16124213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov 16134213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov /** 16144213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov * Whether an injected pointer is down. 16154213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov * 16164213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov * @param pointerId The unique pointer id. 16174213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov * @return True if the pointer is down. 16184213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov */ 16194213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov public boolean isInjectedPointerDown(int pointerId) { 16204213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov final int pointerFlag = (1 << pointerId); 16214213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov return (mInjectedPointersDown & pointerFlag) != 0; 16224213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov } 16234213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov 16244213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov /** 1625e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov * @return The the last injected hover event. 16264213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov */ 1627e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov public MotionEvent getLastInjectedHoverEvent() { 1628e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov return mLastInjectedHoverEvent; 16294213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov } 16304213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov 16315d043ce8cc2f588fdfb336cc843fb3b07b196f83Svetoslav Ganov /** 16325d043ce8cc2f588fdfb336cc843fb3b07b196f83Svetoslav Ganov * @return The the last injected hover event. 16335d043ce8cc2f588fdfb336cc843fb3b07b196f83Svetoslav Ganov */ 16345d043ce8cc2f588fdfb336cc843fb3b07b196f83Svetoslav Ganov public MotionEvent getLastInjectedHoverEventForClick() { 16355d043ce8cc2f588fdfb336cc843fb3b07b196f83Svetoslav Ganov return mLastInjectedHoverEventForClick; 16365d043ce8cc2f588fdfb336cc843fb3b07b196f83Svetoslav Ganov } 16375d043ce8cc2f588fdfb336cc843fb3b07b196f83Svetoslav Ganov 16384213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov @Override 16394213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov public String toString() { 16404213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov StringBuilder builder = new StringBuilder(); 16414213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov builder.append("========================="); 16424213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov builder.append("\nDown pointers #"); 16434213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov builder.append(Integer.bitCount(mInjectedPointersDown)); 16444213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov builder.append(" [ "); 16454213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov for (int i = 0; i < MAX_POINTER_COUNT; i++) { 16464213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov if ((mInjectedPointersDown & i) != 0) { 16474213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov builder.append(i); 16484213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov builder.append(" "); 16494213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov } 16504213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov } 16514213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov builder.append("]"); 16524213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov builder.append("\n========================="); 16534213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov return builder.toString(); 16544213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov } 16554213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov } 16564213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov 16574213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov class ReceivedPointerTracker { 16584213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov private static final String LOG_TAG_RECEIVED_POINTER_TRACKER = "ReceivedPointerTracker"; 1659736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov 1660736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov // Keep track of where and when a pointer went down. 1661736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov private final float[] mReceivedPointerDownX = new float[MAX_POINTER_COUNT]; 1662736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov private final float[] mReceivedPointerDownY = new float[MAX_POINTER_COUNT]; 1663736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov private final long[] mReceivedPointerDownTime = new long[MAX_POINTER_COUNT]; 1664736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov 1665736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov // Which pointers are down. 1666736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov private int mReceivedPointersDown; 1667736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov 1668c4842c11932ea4f60fe7ae09b0a59660207e1587Svetoslav Ganov // The edge flags of the last received down event. 1669c4842c11932ea4f60fe7ae09b0a59660207e1587Svetoslav Ganov private int mLastReceivedDownEdgeFlags; 1670c4842c11932ea4f60fe7ae09b0a59660207e1587Svetoslav Ganov 167184044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav // Primary pointer which is either the first that went down 167284044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav // or if it goes up the next one that most recently went down. 167384044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav private int mPrimaryPointerId; 1674736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov 1675736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov // Keep track of the last up pointer data. 1676736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov private long mLastReceivedUpPointerDownTime; 1677736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov private int mLastReceivedUpPointerId; 16784213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov private float mLastReceivedUpPointerDownX; 16794213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov private float mLastReceivedUpPointerDownY; 1680736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov 1681e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov private MotionEvent mLastReceivedEvent; 1682e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov 1683736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov /** 1684736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov * Clears the internals state. 1685736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov */ 1686736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov public void clear() { 1687736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov Arrays.fill(mReceivedPointerDownX, 0); 1688736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov Arrays.fill(mReceivedPointerDownY, 0); 1689736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov Arrays.fill(mReceivedPointerDownTime, 0); 1690736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov mReceivedPointersDown = 0; 169184044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav mPrimaryPointerId = 0; 1692736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov mLastReceivedUpPointerDownTime = 0; 1693736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov mLastReceivedUpPointerId = 0; 16944213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov mLastReceivedUpPointerDownX = 0; 16954213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov mLastReceivedUpPointerDownY = 0; 1696736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov } 1697736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov 1698736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov /** 1699736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov * Processes a received {@link MotionEvent} event. 1700736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov * 1701736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov * @param event The event to process. 1702736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov */ 17034213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov public void onMotionEvent(MotionEvent event) { 1704e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov if (mLastReceivedEvent != null) { 1705e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov mLastReceivedEvent.recycle(); 1706e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov } 1707e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov mLastReceivedEvent = MotionEvent.obtain(event); 1708e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov 1709736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov final int action = event.getActionMasked(); 1710736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov switch (action) { 1711736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov case MotionEvent.ACTION_DOWN: { 171200f7b3f76515d1c6fbe5cf9fee9d3760787c03cdSvetoslav Ganov handleReceivedPointerDown(event.getActionIndex(), event); 1713736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov } break; 1714736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov case MotionEvent.ACTION_POINTER_DOWN: { 1715736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov handleReceivedPointerDown(event.getActionIndex(), event); 1716736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov } break; 1717736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov case MotionEvent.ACTION_UP: { 171800f7b3f76515d1c6fbe5cf9fee9d3760787c03cdSvetoslav Ganov handleReceivedPointerUp(event.getActionIndex(), event); 1719736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov } break; 1720736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov case MotionEvent.ACTION_POINTER_UP: { 1721736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov handleReceivedPointerUp(event.getActionIndex(), event); 1722736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov } break; 1723736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov } 1724736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov if (DEBUG) { 172538c992841b5f6bc80359dbf60d31aa7b994160fcSvetoslav Ganov Slog.i(LOG_TAG_RECEIVED_POINTER_TRACKER, "Received pointer:\n" + toString()); 1726736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov } 1727736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov } 1728736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov 1729736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov /** 1730e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov * @return The last received event. 1731e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov */ 1732e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov public MotionEvent getLastReceivedEvent() { 1733e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov return mLastReceivedEvent; 1734e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov } 1735e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov 1736e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov /** 17374213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov * @return The number of received pointers that are down. 1738736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov */ 17394213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov public int getReceivedPointerDownCount() { 17404213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov return Integer.bitCount(mReceivedPointersDown); 1741736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov } 1742736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov 1743736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov /** 1744736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov * Whether an received pointer is down. 1745736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov * 1746736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov * @param pointerId The unique pointer id. 1747736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov * @return True if the pointer is down. 1748736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov */ 1749736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov public boolean isReceivedPointerDown(int pointerId) { 1750736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov final int pointerFlag = (1 << pointerId); 1751736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov return (mReceivedPointersDown & pointerFlag) != 0; 1752736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov } 1753736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov 1754736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov /** 1755736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov * @param pointerId The unique pointer id. 1756736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov * @return The X coordinate where the pointer went down. 1757736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov */ 1758736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov public float getReceivedPointerDownX(int pointerId) { 1759736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov return mReceivedPointerDownX[pointerId]; 1760736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov } 1761736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov 1762736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov /** 1763736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov * @param pointerId The unique pointer id. 1764736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov * @return The Y coordinate where the pointer went down. 1765736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov */ 1766736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov public float getReceivedPointerDownY(int pointerId) { 1767736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov return mReceivedPointerDownY[pointerId]; 1768736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov } 1769736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov 1770736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov /** 1771736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov * @param pointerId The unique pointer id. 1772736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov * @return The time when the pointer went down. 1773736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov */ 1774736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov public long getReceivedPointerDownTime(int pointerId) { 1775736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov return mReceivedPointerDownTime[pointerId]; 1776736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov } 1777736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov 1778736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov /** 1779736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov * @return The id of the primary pointer. 1780736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov */ 178184044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav public int getPrimaryPointerId() { 178284044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav if (mPrimaryPointerId == INVALID_POINTER_ID) { 178338c992841b5f6bc80359dbf60d31aa7b994160fcSvetoslav Ganov mPrimaryPointerId = findPrimaryPointerId(); 1784736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov } 178584044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav return mPrimaryPointerId; 1786736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov } 1787736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov 1788736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov /** 1789736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov * @return The time when the last up received pointer went down. 1790736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov */ 1791736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov public long getLastReceivedUpPointerDownTime() { 1792736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov return mLastReceivedUpPointerDownTime; 1793736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov } 1794736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov 1795736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov /** 17964213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov * @return The down X of the last received pointer that went up. 1797736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov */ 17984213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov public float getLastReceivedUpPointerDownX() { 17994213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov return mLastReceivedUpPointerDownX; 1800736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov } 1801736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov 1802736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov /** 18034213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov * @return The down Y of the last received pointer that went up. 1804736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov */ 18054213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov public float getLastReceivedUpPointerDownY() { 18064213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov return mLastReceivedUpPointerDownY; 1807736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov } 1808736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov 1809736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov /** 1810c4842c11932ea4f60fe7ae09b0a59660207e1587Svetoslav Ganov * @return The edge flags of the last received down event. 1811c4842c11932ea4f60fe7ae09b0a59660207e1587Svetoslav Ganov */ 1812c4842c11932ea4f60fe7ae09b0a59660207e1587Svetoslav Ganov public int getLastReceivedDownEdgeFlags() { 1813c4842c11932ea4f60fe7ae09b0a59660207e1587Svetoslav Ganov return mLastReceivedDownEdgeFlags; 1814c4842c11932ea4f60fe7ae09b0a59660207e1587Svetoslav Ganov } 1815c4842c11932ea4f60fe7ae09b0a59660207e1587Svetoslav Ganov 1816c4842c11932ea4f60fe7ae09b0a59660207e1587Svetoslav Ganov /** 1817736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov * Handles a received pointer down event. 1818736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov * 1819736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov * @param pointerIndex The index of the pointer that has changed. 1820736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov * @param event The event to be handled. 1821736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov */ 1822736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov private void handleReceivedPointerDown(int pointerIndex, MotionEvent event) { 1823736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov final int pointerId = event.getPointerId(pointerIndex); 1824736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov final int pointerFlag = (1 << pointerId); 1825736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov 1826736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov mLastReceivedUpPointerId = 0; 1827736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov mLastReceivedUpPointerDownTime = 0; 18284213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov mLastReceivedUpPointerDownX = 0; 18294213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov mLastReceivedUpPointerDownX = 0; 1830736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov 1831c4842c11932ea4f60fe7ae09b0a59660207e1587Svetoslav Ganov mLastReceivedDownEdgeFlags = event.getEdgeFlags(); 1832c4842c11932ea4f60fe7ae09b0a59660207e1587Svetoslav Ganov 1833736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov mReceivedPointersDown |= pointerFlag; 1834736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov mReceivedPointerDownX[pointerId] = event.getX(pointerIndex); 1835736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov mReceivedPointerDownY[pointerId] = event.getY(pointerIndex); 1836736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov mReceivedPointerDownTime[pointerId] = event.getEventTime(); 1837736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov 183884044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav mPrimaryPointerId = pointerId; 1839736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov } 1840736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov 1841736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov /** 1842736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov * Handles a received pointer up event. 1843736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov * 1844736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov * @param pointerIndex The index of the pointer that has changed. 1845736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov * @param event The event to be handled. 1846736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov */ 1847736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov private void handleReceivedPointerUp(int pointerIndex, MotionEvent event) { 1848736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov final int pointerId = event.getPointerId(pointerIndex); 1849736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov final int pointerFlag = (1 << pointerId); 1850736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov 1851736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov mLastReceivedUpPointerId = pointerId; 1852736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov mLastReceivedUpPointerDownTime = getReceivedPointerDownTime(pointerId); 18534213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov mLastReceivedUpPointerDownX = mReceivedPointerDownX[pointerId]; 18544213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov mLastReceivedUpPointerDownY = mReceivedPointerDownY[pointerId]; 1855736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov 1856736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov mReceivedPointersDown &= ~pointerFlag; 1857736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov mReceivedPointerDownX[pointerId] = 0; 1858736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov mReceivedPointerDownY[pointerId] = 0; 1859736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov mReceivedPointerDownTime[pointerId] = 0; 1860736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov 186184044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav if (mPrimaryPointerId == pointerId) { 186284044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav mPrimaryPointerId = INVALID_POINTER_ID; 1863736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov } 1864736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov } 1865736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov 1866736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov /** 186738c992841b5f6bc80359dbf60d31aa7b994160fcSvetoslav Ganov * @return The primary pointer id. 1868736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov */ 186938c992841b5f6bc80359dbf60d31aa7b994160fcSvetoslav Ganov private int findPrimaryPointerId() { 187084044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav int primaryPointerId = INVALID_POINTER_ID; 1871736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov long minDownTime = Long.MAX_VALUE; 187238c992841b5f6bc80359dbf60d31aa7b994160fcSvetoslav Ganov 187384044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav // Find the pointer that went down first. 187438c992841b5f6bc80359dbf60d31aa7b994160fcSvetoslav Ganov int pointerIdBits = mReceivedPointersDown; 187538c992841b5f6bc80359dbf60d31aa7b994160fcSvetoslav Ganov while (pointerIdBits > 0) { 187638c992841b5f6bc80359dbf60d31aa7b994160fcSvetoslav Ganov final int pointerId = Integer.numberOfTrailingZeros(pointerIdBits); 187738c992841b5f6bc80359dbf60d31aa7b994160fcSvetoslav Ganov pointerIdBits &= ~(1 << pointerId); 187838c992841b5f6bc80359dbf60d31aa7b994160fcSvetoslav Ganov final long downPointerTime = mReceivedPointerDownTime[pointerId]; 187984044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav if (downPointerTime < minDownTime) { 188084044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav minDownTime = downPointerTime; 188138c992841b5f6bc80359dbf60d31aa7b994160fcSvetoslav Ganov primaryPointerId = pointerId; 1882736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov } 1883736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov } 188484044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav return primaryPointerId; 1885736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov } 1886736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov 1887736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov @Override 1888736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov public String toString() { 1889736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov StringBuilder builder = new StringBuilder(); 1890736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov builder.append("========================="); 1891736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov builder.append("\nDown pointers #"); 1892736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov builder.append(getReceivedPointerDownCount()); 1893736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov builder.append(" [ "); 1894736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov for (int i = 0; i < MAX_POINTER_COUNT; i++) { 1895736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov if (isReceivedPointerDown(i)) { 1896736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov builder.append(i); 1897736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov builder.append(" "); 1898736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov } 1899736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov } 1900736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov builder.append("]"); 190184044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav builder.append("\nPrimary pointer id [ "); 190284044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav builder.append(getPrimaryPointerId()); 1903736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov builder.append(" ]"); 1904736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov builder.append("\n========================="); 1905736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov return builder.toString(); 1906736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov } 1907736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov } 1908736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov} 1909