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; 44736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov 45736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov/** 46736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov * This class is a strategy for performing touch exploration. It 47736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov * transforms the motion event stream by modifying, adding, replacing, 48736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov * and consuming certain events. The interaction model is: 49736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov * 50736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov * <ol> 51e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov * <li>1. One finger moving slow around performs touch exploration.</li> 52e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov * <li>2. One finger moving fast around performs gestures.</li> 53e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov * <li>3. Two close fingers moving in the same direction perform a drag.</li> 54e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov * <li>4. Multi-finger gestures are delivered to view hierarchy.</li> 55e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov * <li>5. Pointers that have not moved more than a specified distance after they 56736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov * went down are considered inactive.</li> 57e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov * <li>6. Two fingers moving in different directions are considered a multi-finger gesture.</li> 58e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov * <li>7. Double tapping clicks on the on the last touch explored location of it was in 59e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov * a window that does not take focus, otherwise the click is within the accessibility 60e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov * focused rectangle.</li> 61e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov * <li>7. Tapping and holding for a while performs a long press in a similar fashion 62e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov * as the click above.</li> 63736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov * <ol> 64736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov * 65736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov * @hide 66736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov */ 671cf70bbf96930662cab0e699d70b62865766ff52Svetoslav Ganovclass TouchExplorer implements EventStreamTransformation { 684213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov 69736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov private static final boolean DEBUG = false; 70736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov 71736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov // Tag for logging received events. 724213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov private static final String LOG_TAG = "TouchExplorer"; 73736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov 74736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov // States this explorer can be in. 75736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov private static final int STATE_TOUCH_EXPLORING = 0x00000001; 76736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov private static final int STATE_DRAGGING = 0x00000002; 77736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov private static final int STATE_DELEGATING = 0x00000004; 784213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov private static final int STATE_GESTURE_DETECTING = 0x00000005; 79736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov 80736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov // The minimum of the cosine between the vectors of two moving 81736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov // pointers so they can be considered moving in the same direction. 8212a024ca681d877fe16b7e087356f7aff175a218Svetoslav Ganov private static final float MAX_DRAGGING_ANGLE_COS = 0.525321989f; // cos(pi/4) 83736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov 84f804420d6e37748b75478406e989c69303756980Svetoslav Ganov // Constant referring to the ids bits of all pointers. 85f804420d6e37748b75478406e989c69303756980Svetoslav Ganov private static final int ALL_POINTER_ID_BITS = 0xFFFFFFFF; 86736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov 874213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov // This constant captures the current implementation detail that 884213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov // pointer IDs are between 0 and 31 inclusive (subject to change). 894213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov // (See MAX_POINTER_ID in frameworks/base/include/ui/Input.h) 90e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov private static final int MAX_POINTER_COUNT = 32; 914213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov 924213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov // Invalid pointer ID. 93e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov private static final int INVALID_POINTER_ID = -1; 94e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov 95e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov // The velocity above which we detect gestures. 96e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov private static final int GESTURE_DETECTION_VELOCITY_DIP = 1000; 97e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov 98e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov // The minimal distance before we take the middle of the distance between 99e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov // the two dragging pointers as opposed to use the location of the primary one. 100e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov private static final int MIN_POINTER_DISTANCE_TO_USE_MIDDLE_LOCATION_DIP = 200; 1014213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov 10295068e5d1bea47091e97955f271c789264994550Svetoslav Ganov // The timeout after which we are no longer trying to detect a gesture. 10395068e5d1bea47091e97955f271c789264994550Svetoslav Ganov private static final int EXIT_GESTURE_DETECTION_TIMEOUT = 2000; 10495068e5d1bea47091e97955f271c789264994550Svetoslav Ganov 105736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov // Temporary array for storing pointer IDs. 106736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov private final int[] mTempPointerIds = new int[MAX_POINTER_COUNT]; 107736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov 108e47957a0bbe2164467ff6e7a566b0c9e4689cdc9Svetoslav Ganov // Timeout before trying to decide what the user is trying to do. 109e47957a0bbe2164467ff6e7a566b0c9e4689cdc9Svetoslav Ganov private final int mDetermineUserIntentTimeout; 110e47957a0bbe2164467ff6e7a566b0c9e4689cdc9Svetoslav Ganov 111e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov // Timeout within which we try to detect a tap. 112e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov private final int mTapTimeout; 113e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov 114e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov // Timeout within which we try to detect a double tap. 115e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov private final int mDoubleTapTimeout; 116e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov 117e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov // Slop between the down and up tap to be a tap. 118e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov private final int mTouchSlop; 119e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov 120e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov // Slop between the first and second tap to be a double tap. 121e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov private final int mDoubleTapSlop; 122736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov 123736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov // The current state of the touch explorer. 124736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov private int mCurrentState = STATE_TOUCH_EXPLORING; 125736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov 126736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov // The ID of the pointer used for dragging. 127736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov private int mDraggingPointerId; 128736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov 129736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov // Handler for performing asynchronous operations. 130736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov private final Handler mHandler; 131736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov 132e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov // Command for delayed sending of a hover enter event. 133e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov private final SendHoverDelayed mSendHoverEnterDelayed; 134e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov 135e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov // Command for delayed sending of a hover exit event. 136e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov private final SendHoverDelayed mSendHoverExitDelayed; 137736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov 138f068fed6c4c3fc2003aec19b6e7e892358179b02Svetoslav Ganov // Command for delayed sending of touch exploration end events. 139f068fed6c4c3fc2003aec19b6e7e892358179b02Svetoslav Ganov private final SendAccessibilityEventDelayed mSendTouchExplorationEndDelayed; 140f068fed6c4c3fc2003aec19b6e7e892358179b02Svetoslav Ganov 141f068fed6c4c3fc2003aec19b6e7e892358179b02Svetoslav Ganov // Command for delayed sending of touch interaction end events. 142f068fed6c4c3fc2003aec19b6e7e892358179b02Svetoslav Ganov private final SendAccessibilityEventDelayed mSendTouchInteractionEndDelayed; 143fe304b893968887323b93764caafa66ee8ad44deSvetoslav Ganov 144f5a07905a3e025f95472a3f8d9935263e49ad6d3Svetoslav Ganov // Command for delayed sending of a long press. 145f5a07905a3e025f95472a3f8d9935263e49ad6d3Svetoslav Ganov private final PerformLongPressDelayed mPerformLongPressDelayed; 146f5a07905a3e025f95472a3f8d9935263e49ad6d3Svetoslav Ganov 14795068e5d1bea47091e97955f271c789264994550Svetoslav Ganov // Command for exiting gesture detection mode after a timeout. 14895068e5d1bea47091e97955f271c789264994550Svetoslav Ganov private final ExitGestureDetectionModeDelayed mExitGestureDetectionModeDelayed; 14995068e5d1bea47091e97955f271c789264994550Svetoslav Ganov 150e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov // Helper to detect and react to double tap in touch explore mode. 151e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov private final DoubleTapDetector mDoubleTapDetector; 152e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov 153e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov // The scaled minimal distance before we take the middle of the distance between 154e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov // the two dragging pointers as opposed to use the location of the primary one. 155e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov private final int mScaledMinPointerDistanceToUseMiddleLocation; 156e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov 157e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov // The scaled velocity above which we detect gestures. 158e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov private final int mScaledGestureDetectionVelocity; 159e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov 1601cf70bbf96930662cab0e699d70b62865766ff52Svetoslav Ganov // The handler to which to delegate events. 1611cf70bbf96930662cab0e699d70b62865766ff52Svetoslav Ganov private EventStreamTransformation mNext; 1621cf70bbf96930662cab0e699d70b62865766ff52Svetoslav Ganov 163e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov // Helper to track gesture velocity. 16445af84a483165f06c04d74baba67f90da29c6ad2Svetoslav Ganov private final VelocityTracker mVelocityTracker = VelocityTracker.obtain(); 1654213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov 166e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov // Helper class to track received pointers. 1674213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov private final ReceivedPointerTracker mReceivedPointerTracker; 1684213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov 169e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov // Helper class to track injected pointers. 1704213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov private final InjectedPointerTracker mInjectedPointerTracker; 1714213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov 172e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov // Handle to the accessibility manager service. 173e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov private final AccessibilityManagerService mAms; 1744213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov 175e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov // Temporary rectangle to avoid instantiation. 176e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov private final Rect mTempRect = new Rect(); 1774213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov 17877276b60851a158ad3e142cb3b091d57ae5ceffbSvetoslav Ganov // Context in which this explorer operates. 17977276b60851a158ad3e142cb3b091d57ae5ceffbSvetoslav Ganov private final Context mContext; 18077276b60851a158ad3e142cb3b091d57ae5ceffbSvetoslav Ganov 181e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov // The X of the previous event. 182e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov private float mPreviousX; 183e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov 184e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov // The Y of the previous event. 185e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov private float mPreviousY; 186e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov 187e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov // Buffer for storing points for gesture detection. 188e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov private final ArrayList<GesturePoint> mStrokeBuffer = new ArrayList<GesturePoint>(100); 189e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov 190e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov // The minimal delta between moves to add a gesture point. 191e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov private static final int TOUCH_TOLERANCE = 3; 192e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov 193e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov // The minimal score for accepting a predicted gesture. 194e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov private static final float MIN_PREDICTION_SCORE = 2.0f; 195e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov 196e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov // The library for gesture detection. 197e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov private GestureLibrary mGestureLibrary; 198e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov 199e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov // The long pressing pointer id if coordinate remapping is needed. 200aeb8d0ed0d98d398a66a092c418f4f2bca8719e0Svetoslav Ganov private int mLongPressingPointerId = -1; 201e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov 202e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov // The long pressing pointer X if coordinate remapping is needed. 203e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov private int mLongPressingPointerDeltaX; 204e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov 205e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov // The long pressing pointer Y if coordinate remapping is needed. 206e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov private int mLongPressingPointerDeltaY; 2074213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov 208385d9f24b5ce2acb86c0dc192ce702718ab01c39Svetoslav Ganov // The id of the last touch explored window. 209385d9f24b5ce2acb86c0dc192ce702718ab01c39Svetoslav Ganov private int mLastTouchedWindowId; 210385d9f24b5ce2acb86c0dc192ce702718ab01c39Svetoslav Ganov 211f068fed6c4c3fc2003aec19b6e7e892358179b02Svetoslav Ganov // Whether touch exploration is in progress. 212f068fed6c4c3fc2003aec19b6e7e892358179b02Svetoslav Ganov private boolean mTouchExplorationInProgress; 21377276b60851a158ad3e142cb3b091d57ae5ceffbSvetoslav Ganov 214736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov /** 215736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov * Creates a new instance. 216736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov * 217736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov * @param inputFilter The input filter associated with this explorer. 218736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov * @param context A context handle for accessing resources. 219736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov */ 2201cf70bbf96930662cab0e699d70b62865766ff52Svetoslav Ganov public TouchExplorer(Context context, AccessibilityManagerService service) { 22177276b60851a158ad3e142cb3b091d57ae5ceffbSvetoslav Ganov mContext = context; 222e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov mAms = service; 2234213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov mReceivedPointerTracker = new ReceivedPointerTracker(context); 2244213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov mInjectedPointerTracker = new InjectedPointerTracker(); 225e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov mTapTimeout = ViewConfiguration.getTapTimeout(); 22677276b60851a158ad3e142cb3b091d57ae5ceffbSvetoslav Ganov mDetermineUserIntentTimeout = ViewConfiguration.getDoubleTapTimeout(); 227e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov mDoubleTapTimeout = ViewConfiguration.getDoubleTapTimeout(); 228e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov mTouchSlop = ViewConfiguration.get(context).getScaledTouchSlop(); 229e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov mDoubleTapSlop = ViewConfiguration.get(context).getScaledDoubleTapSlop(); 230736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov mHandler = new Handler(context.getMainLooper()); 231f5a07905a3e025f95472a3f8d9935263e49ad6d3Svetoslav Ganov mPerformLongPressDelayed = new PerformLongPressDelayed(); 23295068e5d1bea47091e97955f271c789264994550Svetoslav Ganov mExitGestureDetectionModeDelayed = new ExitGestureDetectionModeDelayed(); 2334213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov mGestureLibrary = GestureLibraries.fromRawResource(context, R.raw.accessibility_gestures); 234ea6fbc0981564f7bbf4c6fbb63af0175415121ceCasey Burkhardt mGestureLibrary.setOrientationStyle(8); 235ea6fbc0981564f7bbf4c6fbb63af0175415121ceCasey Burkhardt mGestureLibrary.setSequenceType(GestureStore.SEQUENCE_SENSITIVE); 2364213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov mGestureLibrary.load(); 237e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov mSendHoverEnterDelayed = new SendHoverDelayed(MotionEvent.ACTION_HOVER_ENTER, true); 238e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov mSendHoverExitDelayed = new SendHoverDelayed(MotionEvent.ACTION_HOVER_EXIT, false); 239f068fed6c4c3fc2003aec19b6e7e892358179b02Svetoslav Ganov mSendTouchExplorationEndDelayed = new SendAccessibilityEventDelayed( 240f068fed6c4c3fc2003aec19b6e7e892358179b02Svetoslav Ganov AccessibilityEvent.TYPE_TOUCH_EXPLORATION_GESTURE_END, 241f068fed6c4c3fc2003aec19b6e7e892358179b02Svetoslav Ganov mDetermineUserIntentTimeout); 242f068fed6c4c3fc2003aec19b6e7e892358179b02Svetoslav Ganov mSendTouchInteractionEndDelayed = new SendAccessibilityEventDelayed( 243f068fed6c4c3fc2003aec19b6e7e892358179b02Svetoslav Ganov AccessibilityEvent.TYPE_TOUCH_INTERACTION_END, 244f068fed6c4c3fc2003aec19b6e7e892358179b02Svetoslav Ganov mDetermineUserIntentTimeout); 245e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov mDoubleTapDetector = new DoubleTapDetector(); 246e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov final float density = context.getResources().getDisplayMetrics().density; 247e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov mScaledMinPointerDistanceToUseMiddleLocation = 248e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov (int) (MIN_POINTER_DISTANCE_TO_USE_MIDDLE_LOCATION_DIP * density); 249e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov mScaledGestureDetectionVelocity = (int) (GESTURE_DETECTION_VELOCITY_DIP * density); 250e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov } 251e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov 252e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov public void clear() { 253e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov // If we have not received an event then we are in initial 254e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov // state. Therefore, there is not need to clean anything. 255e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov MotionEvent event = mReceivedPointerTracker.getLastReceivedEvent(); 256e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov if (event != null) { 257e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov clear(mReceivedPointerTracker.getLastReceivedEvent(), WindowManagerPolicy.FLAG_TRUSTED); 258e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov } 259736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov } 260736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov 2611cf70bbf96930662cab0e699d70b62865766ff52Svetoslav Ganov public void onDestroy() { 2621cf70bbf96930662cab0e699d70b62865766ff52Svetoslav Ganov // TODO: Implement 2631cf70bbf96930662cab0e699d70b62865766ff52Svetoslav Ganov } 2641cf70bbf96930662cab0e699d70b62865766ff52Svetoslav Ganov 2651cf70bbf96930662cab0e699d70b62865766ff52Svetoslav Ganov private void clear(MotionEvent event, int policyFlags) { 266e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov switch (mCurrentState) { 267e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov case STATE_TOUCH_EXPLORING: { 268e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov // If a touch exploration gesture is in progress send events for its end. 269f068fed6c4c3fc2003aec19b6e7e892358179b02Svetoslav Ganov sendHoverExitAndTouchExplorationGestureEndIfNeeded(policyFlags); 270e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov } break; 271e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov case STATE_DRAGGING: { 272e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov mDraggingPointerId = INVALID_POINTER_ID; 273e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov // Send exit to all pointers that we have delivered. 274e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov sendUpForInjectedDownPointers(event, policyFlags); 275e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov } break; 276e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov case STATE_DELEGATING: { 277e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov // Send exit to all pointers that we have delivered. 278e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov sendUpForInjectedDownPointers(event, policyFlags); 279e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov } break; 280e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov case STATE_GESTURE_DETECTING: { 281e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov // Clear the current stroke. 282e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov mStrokeBuffer.clear(); 283e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov } break; 284e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov } 285e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov // Remove all pending callbacks. 286e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov mSendHoverEnterDelayed.remove(); 287e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov mSendHoverExitDelayed.remove(); 288e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov mPerformLongPressDelayed.remove(); 28995068e5d1bea47091e97955f271c789264994550Svetoslav Ganov mExitGestureDetectionModeDelayed.remove(); 290f068fed6c4c3fc2003aec19b6e7e892358179b02Svetoslav Ganov mSendTouchExplorationEndDelayed.remove(); 291f068fed6c4c3fc2003aec19b6e7e892358179b02Svetoslav Ganov mSendTouchInteractionEndDelayed.remove(); 292e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov // Reset the pointer trackers. 293e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov mReceivedPointerTracker.clear(); 294e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov mInjectedPointerTracker.clear(); 295e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov // Clear the double tap detector 296e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov mDoubleTapDetector.clear(); 297e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov // Go to initial state. 298e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov // Clear the long pressing pointer remap data. 299e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov mLongPressingPointerId = -1; 300e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov mLongPressingPointerDeltaX = 0; 301e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov mLongPressingPointerDeltaY = 0; 302e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov mCurrentState = STATE_TOUCH_EXPLORING; 3031cf70bbf96930662cab0e699d70b62865766ff52Svetoslav Ganov if (mNext != null) { 3041cf70bbf96930662cab0e699d70b62865766ff52Svetoslav Ganov mNext.clear(); 3051cf70bbf96930662cab0e699d70b62865766ff52Svetoslav Ganov } 306f068fed6c4c3fc2003aec19b6e7e892358179b02Svetoslav Ganov mTouchExplorationInProgress = false; 307f772cba59760d1ad9eb5cb9205b2e2e9126e488dSvetoslav Ganov mAms.onTouchInteractionEnd(); 3081cf70bbf96930662cab0e699d70b62865766ff52Svetoslav Ganov } 3091cf70bbf96930662cab0e699d70b62865766ff52Svetoslav Ganov 3101cf70bbf96930662cab0e699d70b62865766ff52Svetoslav Ganov @Override 3111cf70bbf96930662cab0e699d70b62865766ff52Svetoslav Ganov public void setNext(EventStreamTransformation next) { 3121cf70bbf96930662cab0e699d70b62865766ff52Svetoslav Ganov mNext = next; 313736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov } 314736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov 3151cf70bbf96930662cab0e699d70b62865766ff52Svetoslav Ganov @Override 31645af84a483165f06c04d74baba67f90da29c6ad2Svetoslav Ganov public void onMotionEvent(MotionEvent event, MotionEvent rawEvent, int policyFlags) { 317736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov if (DEBUG) { 3184213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov Slog.d(LOG_TAG, "Received event: " + event + ", policyFlags=0x" 319736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov + Integer.toHexString(policyFlags)); 3204213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov Slog.d(LOG_TAG, getStateSymbolicName(mCurrentState)); 321736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov } 322736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov 32345af84a483165f06c04d74baba67f90da29c6ad2Svetoslav Ganov mReceivedPointerTracker.onMotionEvent(rawEvent); 324736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov 325736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov switch(mCurrentState) { 326736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov case STATE_TOUCH_EXPLORING: { 32745af84a483165f06c04d74baba67f90da29c6ad2Svetoslav Ganov handleMotionEventStateTouchExploring(event, rawEvent, policyFlags); 328736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov } break; 329736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov case STATE_DRAGGING: { 330736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov handleMotionEventStateDragging(event, policyFlags); 331736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov } break; 332736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov case STATE_DELEGATING: { 333736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov handleMotionEventStateDelegating(event, policyFlags); 334736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov } break; 3354213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov case STATE_GESTURE_DETECTING: { 33645af84a483165f06c04d74baba67f90da29c6ad2Svetoslav Ganov handleMotionEventGestureDetecting(rawEvent, policyFlags); 3374213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov } break; 3384213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov default: 339736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov throw new IllegalStateException("Illegal state: " + mCurrentState); 340736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov } 341736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov } 342736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov 34386783474fdec98a22bc22e224462767eab13e273Svetoslav Ganov public void onAccessibilityEvent(AccessibilityEvent event) { 34477276b60851a158ad3e142cb3b091d57ae5ceffbSvetoslav Ganov final int eventType = event.getEventType(); 34577276b60851a158ad3e142cb3b091d57ae5ceffbSvetoslav Ganov 34677276b60851a158ad3e142cb3b091d57ae5ceffbSvetoslav Ganov // The event for gesture end should be strictly after the 34777276b60851a158ad3e142cb3b091d57ae5ceffbSvetoslav Ganov // last hover exit event. 348f068fed6c4c3fc2003aec19b6e7e892358179b02Svetoslav Ganov if (mSendTouchExplorationEndDelayed.isPending() 3498b681cb8813454aac8a626bf3d7adaa8beca4d75Svetoslav Ganov && eventType == AccessibilityEvent.TYPE_VIEW_HOVER_EXIT) { 350f068fed6c4c3fc2003aec19b6e7e892358179b02Svetoslav Ganov mSendTouchExplorationEndDelayed.remove(); 3518b681cb8813454aac8a626bf3d7adaa8beca4d75Svetoslav Ganov sendAccessibilityEvent(AccessibilityEvent.TYPE_TOUCH_EXPLORATION_GESTURE_END); 35277276b60851a158ad3e142cb3b091d57ae5ceffbSvetoslav Ganov } 35377276b60851a158ad3e142cb3b091d57ae5ceffbSvetoslav Ganov 35477276b60851a158ad3e142cb3b091d57ae5ceffbSvetoslav Ganov // The event for touch interaction end should be strictly after the 35577276b60851a158ad3e142cb3b091d57ae5ceffbSvetoslav Ganov // last hover exit and the touch exploration gesture end events. 356f068fed6c4c3fc2003aec19b6e7e892358179b02Svetoslav Ganov if (mSendTouchInteractionEndDelayed.isPending() 3578b681cb8813454aac8a626bf3d7adaa8beca4d75Svetoslav Ganov && eventType == AccessibilityEvent.TYPE_VIEW_HOVER_EXIT) { 358f068fed6c4c3fc2003aec19b6e7e892358179b02Svetoslav Ganov mSendTouchInteractionEndDelayed.remove(); 3598b681cb8813454aac8a626bf3d7adaa8beca4d75Svetoslav Ganov sendAccessibilityEvent(AccessibilityEvent.TYPE_TOUCH_INTERACTION_END); 36077276b60851a158ad3e142cb3b091d57ae5ceffbSvetoslav Ganov } 36177276b60851a158ad3e142cb3b091d57ae5ceffbSvetoslav Ganov 36286783474fdec98a22bc22e224462767eab13e273Svetoslav Ganov // If a new window opens or the accessibility focus moves we no longer 36386783474fdec98a22bc22e224462767eab13e273Svetoslav Ganov // want to click/long press on the last touch explored location. 36486783474fdec98a22bc22e224462767eab13e273Svetoslav Ganov switch (eventType) { 36586783474fdec98a22bc22e224462767eab13e273Svetoslav Ganov case AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED: 36686783474fdec98a22bc22e224462767eab13e273Svetoslav Ganov case AccessibilityEvent.TYPE_VIEW_ACCESSIBILITY_FOCUSED: { 3675d043ce8cc2f588fdfb336cc843fb3b07b196f83Svetoslav Ganov if (mInjectedPointerTracker.mLastInjectedHoverEventForClick != null) { 3685d043ce8cc2f588fdfb336cc843fb3b07b196f83Svetoslav Ganov mInjectedPointerTracker.mLastInjectedHoverEventForClick.recycle(); 3695d043ce8cc2f588fdfb336cc843fb3b07b196f83Svetoslav Ganov mInjectedPointerTracker.mLastInjectedHoverEventForClick = null; 37086783474fdec98a22bc22e224462767eab13e273Svetoslav Ganov } 371385d9f24b5ce2acb86c0dc192ce702718ab01c39Svetoslav Ganov mLastTouchedWindowId = -1; 372385d9f24b5ce2acb86c0dc192ce702718ab01c39Svetoslav Ganov } break; 373385d9f24b5ce2acb86c0dc192ce702718ab01c39Svetoslav Ganov case AccessibilityEvent.TYPE_VIEW_HOVER_ENTER: 374385d9f24b5ce2acb86c0dc192ce702718ab01c39Svetoslav Ganov case AccessibilityEvent.TYPE_VIEW_HOVER_EXIT: { 375385d9f24b5ce2acb86c0dc192ce702718ab01c39Svetoslav Ganov mLastTouchedWindowId = event.getWindowId(); 37686783474fdec98a22bc22e224462767eab13e273Svetoslav Ganov } break; 37786783474fdec98a22bc22e224462767eab13e273Svetoslav Ganov } 3781cf70bbf96930662cab0e699d70b62865766ff52Svetoslav Ganov if (mNext != null) { 3791cf70bbf96930662cab0e699d70b62865766ff52Svetoslav Ganov mNext.onAccessibilityEvent(event); 3801cf70bbf96930662cab0e699d70b62865766ff52Svetoslav Ganov } 38186783474fdec98a22bc22e224462767eab13e273Svetoslav Ganov } 38286783474fdec98a22bc22e224462767eab13e273Svetoslav Ganov 383736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov /** 384736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov * Handles a motion event in touch exploring state. 385736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov * 386736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov * @param event The event to be handled. 38745af84a483165f06c04d74baba67f90da29c6ad2Svetoslav Ganov * @param rawEvent The raw (unmodified) motion event. 388736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov * @param policyFlags The policy flags associated with the event. 389736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov */ 39045af84a483165f06c04d74baba67f90da29c6ad2Svetoslav Ganov private void handleMotionEventStateTouchExploring(MotionEvent event, MotionEvent rawEvent, 39145af84a483165f06c04d74baba67f90da29c6ad2Svetoslav Ganov int policyFlags) { 3924213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov ReceivedPointerTracker receivedTracker = mReceivedPointerTracker; 3934213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov final int activePointerCount = receivedTracker.getActivePointerCount(); 3944213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov 39545af84a483165f06c04d74baba67f90da29c6ad2Svetoslav Ganov mVelocityTracker.addMovement(rawEvent); 396736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov 397e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov mDoubleTapDetector.onMotionEvent(event, policyFlags); 398e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov 399736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov switch (event.getActionMasked()) { 400f5a07905a3e025f95472a3f8d9935263e49ad6d3Svetoslav Ganov case MotionEvent.ACTION_DOWN: 4016ae8a24fc045bc7970f2843fa9baf06aff15e22dSvetoslav Ganov mAms.onTouchInteractionStart(); 402e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov // Pre-feed the motion events to the gesture detector since we 403e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov // have a distance slop before getting into gesture detection 404e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov // mode and not using the points within this slop significantly 405e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov // decreases the quality of gesture recognition. 40645af84a483165f06c04d74baba67f90da29c6ad2Svetoslav Ganov handleMotionEventGestureDetecting(rawEvent, policyFlags); 407e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov //$FALL-THROUGH$ 408736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov case MotionEvent.ACTION_POINTER_DOWN: { 409736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov switch (activePointerCount) { 410736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov case 0: { 411736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov throw new IllegalStateException("The must always be one active pointer in" 412736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov + "touch exploring state!"); 413736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov } 414736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov case 1: { 415e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov // If we still have not notified the user for the last 416e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov // touch, we figure out what to do. If were waiting 417e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov // we resent the delayed callback and wait again. 418e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov if (mSendHoverEnterDelayed.isPending()) { 419e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov mSendHoverEnterDelayed.remove(); 420e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov mSendHoverExitDelayed.remove(); 421736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov } 422736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov 423f068fed6c4c3fc2003aec19b6e7e892358179b02Svetoslav Ganov if (mSendTouchExplorationEndDelayed.isPending()) { 424f068fed6c4c3fc2003aec19b6e7e892358179b02Svetoslav Ganov mSendTouchExplorationEndDelayed.forceSendAndRemove(); 425f068fed6c4c3fc2003aec19b6e7e892358179b02Svetoslav Ganov } 426f068fed6c4c3fc2003aec19b6e7e892358179b02Svetoslav Ganov 427f068fed6c4c3fc2003aec19b6e7e892358179b02Svetoslav Ganov if (mSendTouchInteractionEndDelayed.isPending()) { 428f068fed6c4c3fc2003aec19b6e7e892358179b02Svetoslav Ganov mSendTouchInteractionEndDelayed.forceSendAndRemove(); 429f068fed6c4c3fc2003aec19b6e7e892358179b02Svetoslav Ganov } 430f068fed6c4c3fc2003aec19b6e7e892358179b02Svetoslav Ganov 431f068fed6c4c3fc2003aec19b6e7e892358179b02Svetoslav Ganov // Every pointer that goes down is active until it moves or 432f068fed6c4c3fc2003aec19b6e7e892358179b02Svetoslav Ganov // another one goes down. Hence, having more than one pointer 433f068fed6c4c3fc2003aec19b6e7e892358179b02Svetoslav Ganov // down we have already send the interaction start event. 434f068fed6c4c3fc2003aec19b6e7e892358179b02Svetoslav Ganov if (event.getPointerCount() == 1) { 435f068fed6c4c3fc2003aec19b6e7e892358179b02Svetoslav Ganov sendAccessibilityEvent( 436f068fed6c4c3fc2003aec19b6e7e892358179b02Svetoslav Ganov AccessibilityEvent.TYPE_TOUCH_INTERACTION_START); 437fe304b893968887323b93764caafa66ee8ad44deSvetoslav Ganov } 438fe304b893968887323b93764caafa66ee8ad44deSvetoslav Ganov 439ebac1b79c4a355d8cd73b49df059deb00d7aa256Svetoslav Ganov mPerformLongPressDelayed.remove(); 440ebac1b79c4a355d8cd73b49df059deb00d7aa256Svetoslav Ganov 441e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov // If we have the first tap schedule a long press and break 442e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov // since we do not want to schedule hover enter because 443e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov // the delayed callback will kick in before the long click. 444e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov // This would lead to a state transition resulting in long 445e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov // pressing the item below the double taped area which is 446e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov // not necessary where accessibility focus is. 447e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov if (mDoubleTapDetector.firstTapDetected()) { 448e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov // We got a tap now post a long press action. 449e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov mPerformLongPressDelayed.post(event, policyFlags); 450f5a07905a3e025f95472a3f8d9935263e49ad6d3Svetoslav Ganov break; 451736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov } 452f068fed6c4c3fc2003aec19b6e7e892358179b02Svetoslav Ganov if (!mTouchExplorationInProgress) { 453f068fed6c4c3fc2003aec19b6e7e892358179b02Svetoslav Ganov // Deliver hover enter with a delay to have a chance 454f068fed6c4c3fc2003aec19b6e7e892358179b02Svetoslav Ganov // to detect what the user is trying to do. 455f068fed6c4c3fc2003aec19b6e7e892358179b02Svetoslav Ganov final int pointerId = receivedTracker.getPrimaryActivePointerId(); 456f068fed6c4c3fc2003aec19b6e7e892358179b02Svetoslav Ganov final int pointerIdBits = (1 << pointerId); 457f068fed6c4c3fc2003aec19b6e7e892358179b02Svetoslav Ganov mSendHoverEnterDelayed.post(event, true, pointerIdBits, policyFlags); 458f068fed6c4c3fc2003aec19b6e7e892358179b02Svetoslav Ganov } 459736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov } break; 460736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov default: { 461736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov /* do nothing - let the code for ACTION_MOVE decide what to do */ 462736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov } break; 463736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov } 464736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov } break; 465736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov case MotionEvent.ACTION_MOVE: { 4664213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov final int pointerId = receivedTracker.getPrimaryActivePointerId(); 46791feae3c5994bd4768cea3507c62c65746adcfa6Svetoslav Ganov final int pointerIndex = event.findPointerIndex(pointerId); 46891feae3c5994bd4768cea3507c62c65746adcfa6Svetoslav Ganov final int pointerIdBits = (1 << pointerId); 469736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov switch (activePointerCount) { 470736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov case 0: { 471736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov /* do nothing - no active pointers so we swallow the event */ 472736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov } break; 473736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov case 1: { 474e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov // We have not started sending events since we try to 475e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov // figure out what the user is doing. 476e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov if (mSendHoverEnterDelayed.isPending()) { 477e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov // Pre-feed the motion events to the gesture detector since we 478e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov // have a distance slop before getting into gesture detection 479e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov // mode and not using the points within this slop significantly 480e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov // decreases the quality of gesture recognition. 48145af84a483165f06c04d74baba67f90da29c6ad2Svetoslav Ganov handleMotionEventGestureDetecting(rawEvent, policyFlags); 48245af84a483165f06c04d74baba67f90da29c6ad2Svetoslav Ganov // It is *important* to use the distance traveled by the pointers 48345af84a483165f06c04d74baba67f90da29c6ad2Svetoslav Ganov // on the screen which may or may not be magnified. 4844213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov final float deltaX = receivedTracker.getReceivedPointerDownX(pointerId) 48545af84a483165f06c04d74baba67f90da29c6ad2Svetoslav Ganov - rawEvent.getX(pointerIndex); 4864213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov final float deltaY = receivedTracker.getReceivedPointerDownY(pointerId) 48745af84a483165f06c04d74baba67f90da29c6ad2Svetoslav Ganov - rawEvent.getY(pointerIndex); 488736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov final double moveDelta = Math.hypot(deltaX, deltaY); 489e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov // The user has moved enough for us to decide. 490e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov if (moveDelta > mDoubleTapSlop) { 491e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov // Check whether the user is performing a gesture. We 492e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov // detect gestures if the pointer is moving above a 493e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov // given velocity. 4944213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov mVelocityTracker.computeCurrentVelocity(1000); 4954213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov final float maxAbsVelocity = Math.max( 4964213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov Math.abs(mVelocityTracker.getXVelocity(pointerId)), 4974213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov Math.abs(mVelocityTracker.getYVelocity(pointerId))); 498e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov if (maxAbsVelocity > mScaledGestureDetectionVelocity) { 499e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov // We have to perform gesture detection, so 500e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov // clear the current state and try to detect. 5014213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov mCurrentState = STATE_GESTURE_DETECTING; 50245af84a483165f06c04d74baba67f90da29c6ad2Svetoslav Ganov mVelocityTracker.clear(); 503e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov mSendHoverEnterDelayed.remove(); 504e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov mSendHoverExitDelayed.remove(); 505e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov mPerformLongPressDelayed.remove(); 50695068e5d1bea47091e97955f271c789264994550Svetoslav Ganov mExitGestureDetectionModeDelayed.post(); 50777276b60851a158ad3e142cb3b091d57ae5ceffbSvetoslav Ganov // Send accessibility event to announce the start 50877276b60851a158ad3e142cb3b091d57ae5ceffbSvetoslav Ganov // of gesture recognition. 50977276b60851a158ad3e142cb3b091d57ae5ceffbSvetoslav Ganov sendAccessibilityEvent( 51077276b60851a158ad3e142cb3b091d57ae5ceffbSvetoslav Ganov AccessibilityEvent.TYPE_GESTURE_DETECTION_START); 511e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov } else { 512e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov // We have just decided that the user is touch, 513e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov // exploring so start sending events. 514e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov mSendHoverEnterDelayed.forceSendAndRemove(); 515e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov mSendHoverExitDelayed.remove(); 516ebac1b79c4a355d8cd73b49df059deb00d7aa256Svetoslav Ganov mPerformLongPressDelayed.remove(); 517e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov sendMotionEvent(event, MotionEvent.ACTION_HOVER_MOVE, 51891feae3c5994bd4768cea3507c62c65746adcfa6Svetoslav Ganov pointerIdBits, policyFlags); 51991feae3c5994bd4768cea3507c62c65746adcfa6Svetoslav Ganov } 520e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov break; 521736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov } 522736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov } else { 523f068fed6c4c3fc2003aec19b6e7e892358179b02Svetoslav Ganov // Cancel the long press if pending and the user 524f068fed6c4c3fc2003aec19b6e7e892358179b02Svetoslav Ganov // moved more than the slop. 525f068fed6c4c3fc2003aec19b6e7e892358179b02Svetoslav Ganov if (mPerformLongPressDelayed.isPending()) { 526f068fed6c4c3fc2003aec19b6e7e892358179b02Svetoslav Ganov final float deltaX = 527f068fed6c4c3fc2003aec19b6e7e892358179b02Svetoslav Ganov receivedTracker.getReceivedPointerDownX(pointerId) 528f068fed6c4c3fc2003aec19b6e7e892358179b02Svetoslav Ganov - rawEvent.getX(pointerIndex); 529f068fed6c4c3fc2003aec19b6e7e892358179b02Svetoslav Ganov final float deltaY = 530f068fed6c4c3fc2003aec19b6e7e892358179b02Svetoslav Ganov receivedTracker.getReceivedPointerDownY(pointerId) 531f068fed6c4c3fc2003aec19b6e7e892358179b02Svetoslav Ganov - rawEvent.getY(pointerIndex); 532f068fed6c4c3fc2003aec19b6e7e892358179b02Svetoslav Ganov final double moveDelta = Math.hypot(deltaX, deltaY); 533f068fed6c4c3fc2003aec19b6e7e892358179b02Svetoslav Ganov // The user has moved enough for us to decide. 534f068fed6c4c3fc2003aec19b6e7e892358179b02Svetoslav Ganov if (moveDelta > mTouchSlop) { 535f068fed6c4c3fc2003aec19b6e7e892358179b02Svetoslav Ganov mPerformLongPressDelayed.remove(); 536f068fed6c4c3fc2003aec19b6e7e892358179b02Svetoslav Ganov } 537f068fed6c4c3fc2003aec19b6e7e892358179b02Svetoslav Ganov } 538e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov // The user is wither double tapping or performing long 539e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov // press so do not send move events yet. 540e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov if (mDoubleTapDetector.firstTapDetected()) { 541e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov break; 542e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov } 543f068fed6c4c3fc2003aec19b6e7e892358179b02Svetoslav Ganov sendTouchExplorationGestureStartAndHoverEnterIfNeeded(policyFlags); 54491feae3c5994bd4768cea3507c62c65746adcfa6Svetoslav Ganov sendMotionEvent(event, MotionEvent.ACTION_HOVER_MOVE, pointerIdBits, 545736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov policyFlags); 546736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov } 547e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov } break; 548e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov case 2: { 549e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov // More than one pointer so the user is not touch exploring 550e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov // and now we have to decide whether to delegate or drag. 551e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov if (mSendHoverEnterDelayed.isPending()) { 552e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov // We have not started sending events so cancel 553e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov // scheduled sending events. 554e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov mSendHoverEnterDelayed.remove(); 555e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov mSendHoverExitDelayed.remove(); 556e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov mPerformLongPressDelayed.remove(); 557e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov } else { 558ebac1b79c4a355d8cd73b49df059deb00d7aa256Svetoslav Ganov mPerformLongPressDelayed.remove(); 559e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov // If the user is touch exploring the second pointer may be 560e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov // performing a double tap to activate an item without need 561e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov // for the user to lift his exploring finger. 56245af84a483165f06c04d74baba67f90da29c6ad2Svetoslav Ganov // It is *important* to use the distance traveled by the pointers 56345af84a483165f06c04d74baba67f90da29c6ad2Svetoslav Ganov // on the screen which may or may not be magnified. 564e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov final float deltaX = receivedTracker.getReceivedPointerDownX(pointerId) 56545af84a483165f06c04d74baba67f90da29c6ad2Svetoslav Ganov - rawEvent.getX(pointerIndex); 566e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov final float deltaY = receivedTracker.getReceivedPointerDownY(pointerId) 56745af84a483165f06c04d74baba67f90da29c6ad2Svetoslav Ganov - rawEvent.getY(pointerIndex); 568e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov final double moveDelta = Math.hypot(deltaX, deltaY); 569e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov if (moveDelta < mDoubleTapSlop) { 57047e02711d78ecac9112aa7f66e5664cdc46fb3d1Svetoslav Ganov break; 571736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov } 572e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov // We are sending events so send exit and gesture 573e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov // end since we transition to another state. 574f068fed6c4c3fc2003aec19b6e7e892358179b02Svetoslav Ganov sendHoverExitAndTouchExplorationGestureEndIfNeeded(policyFlags); 575736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov } 576e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov 577e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov // We know that a new state transition is to happen and the 578e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov // new state will not be gesture recognition, so clear the 579e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov // stashed gesture strokes. 580e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov mStrokeBuffer.clear(); 581736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov 582736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov if (isDraggingGesture(event)) { 583736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov // Two pointers moving in the same direction within 584736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov // a given distance perform a drag. 58500f7b3f76515d1c6fbe5cf9fee9d3760787c03cdSvetoslav Ganov mCurrentState = STATE_DRAGGING; 58691feae3c5994bd4768cea3507c62c65746adcfa6Svetoslav Ganov mDraggingPointerId = pointerId; 587c4842c11932ea4f60fe7ae09b0a59660207e1587Svetoslav Ganov event.setEdgeFlags(receivedTracker.getLastReceivedDownEdgeFlags()); 58891feae3c5994bd4768cea3507c62c65746adcfa6Svetoslav Ganov sendMotionEvent(event, MotionEvent.ACTION_DOWN, pointerIdBits, 58991feae3c5994bd4768cea3507c62c65746adcfa6Svetoslav Ganov policyFlags); 590736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov } else { 591736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov // Two pointers moving arbitrary are delegated to the view hierarchy. 592736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov mCurrentState = STATE_DELEGATING; 593736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov sendDownForAllActiveNotInjectedPointers(event, policyFlags); 594736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov } 59545af84a483165f06c04d74baba67f90da29c6ad2Svetoslav Ganov mVelocityTracker.clear(); 596736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov } break; 597736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov default: { 598e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov // More than one pointer so the user is not touch exploring 599e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov // and now we have to decide whether to delegate or drag. 600e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov if (mSendHoverEnterDelayed.isPending()) { 601e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov // We have not started sending events so cancel 602e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov // scheduled sending events. 603e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov mSendHoverEnterDelayed.remove(); 604e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov mSendHoverExitDelayed.remove(); 605e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov mPerformLongPressDelayed.remove(); 606e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov } else { 607ebac1b79c4a355d8cd73b49df059deb00d7aa256Svetoslav Ganov mPerformLongPressDelayed.remove(); 608e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov // We are sending events so send exit and gesture 609e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov // end since we transition to another state. 610f068fed6c4c3fc2003aec19b6e7e892358179b02Svetoslav Ganov sendHoverExitAndTouchExplorationGestureEndIfNeeded(policyFlags); 611e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov } 612736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov 613736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov // More than two pointers are delegated to the view hierarchy. 614736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov mCurrentState = STATE_DELEGATING; 615736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov sendDownForAllActiveNotInjectedPointers(event, policyFlags); 61645af84a483165f06c04d74baba67f90da29c6ad2Svetoslav Ganov mVelocityTracker.clear(); 617736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov } 618736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov } 619736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov } break; 620736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov case MotionEvent.ACTION_UP: 621f772cba59760d1ad9eb5cb9205b2e2e9126e488dSvetoslav Ganov mAms.onTouchInteractionEnd(); 622e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov // We know that we do not need the pre-fed gesture points are not 623e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov // needed anymore since the last pointer just went up. 624e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov mStrokeBuffer.clear(); 625e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov //$FALL-THROUGH$ 626736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov case MotionEvent.ACTION_POINTER_UP: { 6274213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov final int pointerId = receivedTracker.getLastReceivedUpPointerId(); 62891feae3c5994bd4768cea3507c62c65746adcfa6Svetoslav Ganov final int pointerIdBits = (1 << pointerId); 629736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov switch (activePointerCount) { 630736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov case 0: { 631736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov // If the pointer that went up was not active we have nothing to do. 6324213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov if (!receivedTracker.wasLastReceivedUpPointerActive()) { 633736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov break; 634736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov } 635736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov 636f5a07905a3e025f95472a3f8d9935263e49ad6d3Svetoslav Ganov mPerformLongPressDelayed.remove(); 637f5a07905a3e025f95472a3f8d9935263e49ad6d3Svetoslav Ganov 638e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov // If we have not delivered the enter schedule exit. 639e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov if (mSendHoverEnterDelayed.isPending()) { 64077276b60851a158ad3e142cb3b091d57ae5ceffbSvetoslav Ganov mSendHoverExitDelayed.post(event, false, pointerIdBits, policyFlags); 641736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov } else { 642e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov // The user is touch exploring so we send events for end. 643f068fed6c4c3fc2003aec19b6e7e892358179b02Svetoslav Ganov sendHoverExitAndTouchExplorationGestureEndIfNeeded(policyFlags); 644f068fed6c4c3fc2003aec19b6e7e892358179b02Svetoslav Ganov } 645f068fed6c4c3fc2003aec19b6e7e892358179b02Svetoslav Ganov 646f068fed6c4c3fc2003aec19b6e7e892358179b02Svetoslav Ganov if (!mSendTouchInteractionEndDelayed.isPending()) { 647f068fed6c4c3fc2003aec19b6e7e892358179b02Svetoslav Ganov mSendTouchInteractionEndDelayed.post(); 648736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov } 649736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov } break; 650736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov } 65145af84a483165f06c04d74baba67f90da29c6ad2Svetoslav Ganov mVelocityTracker.clear(); 652736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov } break; 653736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov case MotionEvent.ACTION_CANCEL: { 654e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov clear(event, policyFlags); 655736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov } break; 656736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov } 657736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov } 658736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov 659736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov /** 660736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov * Handles a motion event in dragging state. 661736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov * 662736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov * @param event The event to be handled. 663736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov * @param policyFlags The policy flags associated with the event. 664736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov */ 665736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov private void handleMotionEventStateDragging(MotionEvent event, int policyFlags) { 66691feae3c5994bd4768cea3507c62c65746adcfa6Svetoslav Ganov final int pointerIdBits = (1 << mDraggingPointerId); 667736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov switch (event.getActionMasked()) { 668736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov case MotionEvent.ACTION_DOWN: { 669736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov throw new IllegalStateException("Dragging state can be reached only if two " 670736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov + "pointers are already down"); 671736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov } 672736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov case MotionEvent.ACTION_POINTER_DOWN: { 673736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov // We are in dragging state so we have two pointers and another one 674736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov // goes down => delegate the three pointers to the view hierarchy 675736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov mCurrentState = STATE_DELEGATING; 676ec33d56300aa417efb4a055786d73d1bf23a6a85Svetoslav Ganov if (mDraggingPointerId != INVALID_POINTER_ID) { 677ec33d56300aa417efb4a055786d73d1bf23a6a85Svetoslav Ganov sendMotionEvent(event, MotionEvent.ACTION_UP, pointerIdBits, policyFlags); 678ec33d56300aa417efb4a055786d73d1bf23a6a85Svetoslav Ganov } 679736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov sendDownForAllActiveNotInjectedPointers(event, policyFlags); 680736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov } break; 681736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov case MotionEvent.ACTION_MOVE: { 6824213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov final int activePointerCount = mReceivedPointerTracker.getActivePointerCount(); 683736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov switch (activePointerCount) { 6842e1c66bd53d30d2148afaa4b393b60cd59976d65Svetoslav Ganov case 1: { 6852e1c66bd53d30d2148afaa4b393b60cd59976d65Svetoslav Ganov // do nothing 6862e1c66bd53d30d2148afaa4b393b60cd59976d65Svetoslav Ganov } break; 687736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov case 2: { 688736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov if (isDraggingGesture(event)) { 689e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov // If the dragging pointer are closer that a given distance we 690e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov // use the location of the primary one. Otherwise, we take the 691e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov // middle between the pointers. 692e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov int[] pointerIds = mTempPointerIds; 693e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov mReceivedPointerTracker.populateActivePointerIds(pointerIds); 694e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov 695e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov final int firstPtrIndex = event.findPointerIndex(pointerIds[0]); 696e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov final int secondPtrIndex = event.findPointerIndex(pointerIds[1]); 697e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov 698e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov final float firstPtrX = event.getX(firstPtrIndex); 699e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov final float firstPtrY = event.getY(firstPtrIndex); 700e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov final float secondPtrX = event.getX(secondPtrIndex); 701e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov final float secondPtrY = event.getY(secondPtrIndex); 702e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov 703e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov final float deltaX = firstPtrX - secondPtrX; 704e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov final float deltaY = firstPtrY - secondPtrY; 705e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov final double distance = Math.hypot(deltaX, deltaY); 706e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov 707e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov if (distance > mScaledMinPointerDistanceToUseMiddleLocation) { 708e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov event.setLocation(deltaX / 2, deltaY / 2); 709e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov } 710e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov 711736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov // If still dragging send a drag event. 71291feae3c5994bd4768cea3507c62c65746adcfa6Svetoslav Ganov sendMotionEvent(event, MotionEvent.ACTION_MOVE, pointerIdBits, 71391feae3c5994bd4768cea3507c62c65746adcfa6Svetoslav Ganov policyFlags); 714736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov } else { 715736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov // The two pointers are moving either in different directions or 716736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov // no close enough => delegate the gesture to the view hierarchy. 717736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov mCurrentState = STATE_DELEGATING; 718736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov // Send an event to the end of the drag gesture. 71991feae3c5994bd4768cea3507c62c65746adcfa6Svetoslav Ganov sendMotionEvent(event, MotionEvent.ACTION_UP, pointerIdBits, 72091feae3c5994bd4768cea3507c62c65746adcfa6Svetoslav Ganov policyFlags); 721736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov // Deliver all active pointers to the view hierarchy. 722736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov sendDownForAllActiveNotInjectedPointers(event, policyFlags); 723736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov } 724736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov } break; 725736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov default: { 726736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov mCurrentState = STATE_DELEGATING; 727736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov // Send an event to the end of the drag gesture. 72891feae3c5994bd4768cea3507c62c65746adcfa6Svetoslav Ganov sendMotionEvent(event, MotionEvent.ACTION_UP, pointerIdBits, 72991feae3c5994bd4768cea3507c62c65746adcfa6Svetoslav Ganov policyFlags); 730736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov // Deliver all active pointers to the view hierarchy. 731736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov sendDownForAllActiveNotInjectedPointers(event, policyFlags); 732736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov } 733736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov } 734736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov } break; 735aeb8d0ed0d98d398a66a092c418f4f2bca8719e0Svetoslav Ganov case MotionEvent.ACTION_POINTER_UP: { 736aeb8d0ed0d98d398a66a092c418f4f2bca8719e0Svetoslav Ganov final int pointerId = event.getPointerId(event.getActionIndex()); 737aeb8d0ed0d98d398a66a092c418f4f2bca8719e0Svetoslav Ganov if (pointerId == mDraggingPointerId) { 738aeb8d0ed0d98d398a66a092c418f4f2bca8719e0Svetoslav Ganov mDraggingPointerId = INVALID_POINTER_ID; 739aeb8d0ed0d98d398a66a092c418f4f2bca8719e0Svetoslav Ganov // Send an event to the end of the drag gesture. 740aeb8d0ed0d98d398a66a092c418f4f2bca8719e0Svetoslav Ganov sendMotionEvent(event, MotionEvent.ACTION_UP, pointerIdBits, policyFlags); 741aeb8d0ed0d98d398a66a092c418f4f2bca8719e0Svetoslav Ganov } 742aeb8d0ed0d98d398a66a092c418f4f2bca8719e0Svetoslav Ganov } break; 7432e1c66bd53d30d2148afaa4b393b60cd59976d65Svetoslav Ganov case MotionEvent.ACTION_UP: { 744f772cba59760d1ad9eb5cb9205b2e2e9126e488dSvetoslav Ganov mAms.onTouchInteractionEnd(); 74577276b60851a158ad3e142cb3b091d57ae5ceffbSvetoslav Ganov // Announce the end of a new touch interaction. 74677276b60851a158ad3e142cb3b091d57ae5ceffbSvetoslav Ganov sendAccessibilityEvent( 74777276b60851a158ad3e142cb3b091d57ae5ceffbSvetoslav Ganov AccessibilityEvent.TYPE_TOUCH_INTERACTION_END); 748aeb8d0ed0d98d398a66a092c418f4f2bca8719e0Svetoslav Ganov final int pointerId = event.getPointerId(event.getActionIndex()); 749aeb8d0ed0d98d398a66a092c418f4f2bca8719e0Svetoslav Ganov if (pointerId == mDraggingPointerId) { 750aeb8d0ed0d98d398a66a092c418f4f2bca8719e0Svetoslav Ganov mDraggingPointerId = INVALID_POINTER_ID; 751aeb8d0ed0d98d398a66a092c418f4f2bca8719e0Svetoslav Ganov // Send an event to the end of the drag gesture. 752aeb8d0ed0d98d398a66a092c418f4f2bca8719e0Svetoslav Ganov sendMotionEvent(event, MotionEvent.ACTION_UP, pointerIdBits, policyFlags); 753aeb8d0ed0d98d398a66a092c418f4f2bca8719e0Svetoslav Ganov } 7542e1c66bd53d30d2148afaa4b393b60cd59976d65Svetoslav Ganov mCurrentState = STATE_TOUCH_EXPLORING; 7552e1c66bd53d30d2148afaa4b393b60cd59976d65Svetoslav Ganov } break; 756736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov case MotionEvent.ACTION_CANCEL: { 757e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov clear(event, policyFlags); 758736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov } break; 759736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov } 760736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov } 761736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov 762736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov /** 763736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov * Handles a motion event in delegating state. 764736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov * 765736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov * @param event The event to be handled. 766736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov * @param policyFlags The policy flags associated with the event. 767736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov */ 7682e1c66bd53d30d2148afaa4b393b60cd59976d65Svetoslav Ganov private void handleMotionEventStateDelegating(MotionEvent event, int policyFlags) { 769736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov switch (event.getActionMasked()) { 770736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov case MotionEvent.ACTION_DOWN: { 771736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov throw new IllegalStateException("Delegating state can only be reached if " 772736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov + "there is at least one pointer down!"); 773736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov } 774736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov case MotionEvent.ACTION_MOVE: { 775736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov // Check whether some other pointer became active because they have moved 776736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov // a given distance and if such exist send them to the view hierarchy 7774213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov final int notInjectedCount = getNotInjectedActivePointerCount( 7784213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov mReceivedPointerTracker, mInjectedPointerTracker); 779736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov if (notInjectedCount > 0) { 780f804420d6e37748b75478406e989c69303756980Svetoslav Ganov MotionEvent prototype = MotionEvent.obtain(event); 781f804420d6e37748b75478406e989c69303756980Svetoslav Ganov sendDownForAllActiveNotInjectedPointers(prototype, policyFlags); 782736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov } 783736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov } break; 784e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov case MotionEvent.ACTION_UP: 78577276b60851a158ad3e142cb3b091d57ae5ceffbSvetoslav Ganov // Announce the end of a new touch interaction. 78677276b60851a158ad3e142cb3b091d57ae5ceffbSvetoslav Ganov sendAccessibilityEvent( 78777276b60851a158ad3e142cb3b091d57ae5ceffbSvetoslav Ganov AccessibilityEvent.TYPE_TOUCH_INTERACTION_END); 78877276b60851a158ad3e142cb3b091d57ae5ceffbSvetoslav Ganov //$FALL-THROUGH$ 789736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov case MotionEvent.ACTION_POINTER_UP: { 790f772cba59760d1ad9eb5cb9205b2e2e9126e488dSvetoslav Ganov mAms.onTouchInteractionEnd(); 791e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov mLongPressingPointerId = -1; 792e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov mLongPressingPointerDeltaX = 0; 793e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov mLongPressingPointerDeltaY = 0; 794736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov // No active pointers => go to initial state. 7954213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov if (mReceivedPointerTracker.getActivePointerCount() == 0) { 796736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov mCurrentState = STATE_TOUCH_EXPLORING; 797736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov } 798736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov } break; 799736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov case MotionEvent.ACTION_CANCEL: { 800e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov clear(event, policyFlags); 801736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov } break; 802736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov } 803736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov // Deliver the event striping out inactive pointers. 804736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov sendMotionEventStripInactivePointers(event, policyFlags); 805736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov } 806736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov 8074213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov private void handleMotionEventGestureDetecting(MotionEvent event, int policyFlags) { 8084213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov switch (event.getActionMasked()) { 8094213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov case MotionEvent.ACTION_DOWN: { 8104213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov final float x = event.getX(); 8114213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov final float y = event.getY(); 8124213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov mPreviousX = x; 8134213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov mPreviousY = y; 8144213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov mStrokeBuffer.add(new GesturePoint(x, y, event.getEventTime())); 8154213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov } break; 8164213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov case MotionEvent.ACTION_MOVE: { 8174213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov final float x = event.getX(); 8184213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov final float y = event.getY(); 8194213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov final float dX = Math.abs(x - mPreviousX); 8204213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov final float dY = Math.abs(y - mPreviousY); 8214213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov if (dX >= TOUCH_TOLERANCE || dY >= TOUCH_TOLERANCE) { 8224213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov mPreviousX = x; 8234213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov mPreviousY = y; 8244213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov mStrokeBuffer.add(new GesturePoint(x, y, event.getEventTime())); 8254213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov } 8264213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov } break; 827e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov case MotionEvent.ACTION_UP: { 828f772cba59760d1ad9eb5cb9205b2e2e9126e488dSvetoslav Ganov mAms.onTouchInteractionEnd(); 82977276b60851a158ad3e142cb3b091d57ae5ceffbSvetoslav Ganov // Announce the end of gesture recognition. 83077276b60851a158ad3e142cb3b091d57ae5ceffbSvetoslav Ganov sendAccessibilityEvent( 83177276b60851a158ad3e142cb3b091d57ae5ceffbSvetoslav Ganov AccessibilityEvent.TYPE_GESTURE_DETECTION_END); 83277276b60851a158ad3e142cb3b091d57ae5ceffbSvetoslav Ganov // Announce the end of a new touch interaction. 83377276b60851a158ad3e142cb3b091d57ae5ceffbSvetoslav Ganov sendAccessibilityEvent( 83477276b60851a158ad3e142cb3b091d57ae5ceffbSvetoslav Ganov AccessibilityEvent.TYPE_TOUCH_INTERACTION_END); 83577276b60851a158ad3e142cb3b091d57ae5ceffbSvetoslav Ganov 8364213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov float x = event.getX(); 8374213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov float y = event.getY(); 8384213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov mStrokeBuffer.add(new GesturePoint(x, y, event.getEventTime())); 8394213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov 8404213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov Gesture gesture = new Gesture(); 8414213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov gesture.addStroke(new GestureStroke(mStrokeBuffer)); 8424213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov 8434213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov ArrayList<Prediction> predictions = mGestureLibrary.recognize(gesture); 8444213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov if (!predictions.isEmpty()) { 8454213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov Prediction bestPrediction = predictions.get(0); 8464213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov if (bestPrediction.score >= MIN_PREDICTION_SCORE) { 8474213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov if (DEBUG) { 8484213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov Slog.i(LOG_TAG, "gesture: " + bestPrediction.name + " score: " 8494213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov + bestPrediction.score); 8504213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov } 8514213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov try { 8524213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov final int gestureId = Integer.parseInt(bestPrediction.name); 853e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov mAms.onGesture(gestureId); 8544213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov } catch (NumberFormatException nfe) { 8554213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov Slog.w(LOG_TAG, "Non numeric gesture id:" + bestPrediction.name); 8564213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov } 8574213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov } 8584213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov } 8594213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov 8604213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov mStrokeBuffer.clear(); 86195068e5d1bea47091e97955f271c789264994550Svetoslav Ganov mExitGestureDetectionModeDelayed.remove(); 8624213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov mCurrentState = STATE_TOUCH_EXPLORING; 8634213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov } break; 8644213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov case MotionEvent.ACTION_CANCEL: { 865e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov clear(event, policyFlags); 8664213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov } break; 8674213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov } 8684213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov } 8694213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov 870736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov /** 87177276b60851a158ad3e142cb3b091d57ae5ceffbSvetoslav Ganov * Sends an accessibility event of the given type. 87277276b60851a158ad3e142cb3b091d57ae5ceffbSvetoslav Ganov * 87377276b60851a158ad3e142cb3b091d57ae5ceffbSvetoslav Ganov * @param type The event type. 87477276b60851a158ad3e142cb3b091d57ae5ceffbSvetoslav Ganov */ 87577276b60851a158ad3e142cb3b091d57ae5ceffbSvetoslav Ganov private void sendAccessibilityEvent(int type) { 87677276b60851a158ad3e142cb3b091d57ae5ceffbSvetoslav Ganov AccessibilityManager accessibilityManager = AccessibilityManager.getInstance(mContext); 87777276b60851a158ad3e142cb3b091d57ae5ceffbSvetoslav Ganov if (accessibilityManager.isEnabled()) { 87877276b60851a158ad3e142cb3b091d57ae5ceffbSvetoslav Ganov AccessibilityEvent event = AccessibilityEvent.obtain(type); 87977276b60851a158ad3e142cb3b091d57ae5ceffbSvetoslav Ganov accessibilityManager.sendAccessibilityEvent(event); 880f068fed6c4c3fc2003aec19b6e7e892358179b02Svetoslav Ganov switch (type) { 881f068fed6c4c3fc2003aec19b6e7e892358179b02Svetoslav Ganov case AccessibilityEvent.TYPE_TOUCH_EXPLORATION_GESTURE_START: { 882f068fed6c4c3fc2003aec19b6e7e892358179b02Svetoslav Ganov mTouchExplorationInProgress = true; 883f068fed6c4c3fc2003aec19b6e7e892358179b02Svetoslav Ganov } break; 884f068fed6c4c3fc2003aec19b6e7e892358179b02Svetoslav Ganov case AccessibilityEvent.TYPE_TOUCH_EXPLORATION_GESTURE_END: { 885f068fed6c4c3fc2003aec19b6e7e892358179b02Svetoslav Ganov mTouchExplorationInProgress = false; 886f068fed6c4c3fc2003aec19b6e7e892358179b02Svetoslav Ganov } break; 887f068fed6c4c3fc2003aec19b6e7e892358179b02Svetoslav Ganov } 88877276b60851a158ad3e142cb3b091d57ae5ceffbSvetoslav Ganov } 88977276b60851a158ad3e142cb3b091d57ae5ceffbSvetoslav Ganov } 89077276b60851a158ad3e142cb3b091d57ae5ceffbSvetoslav Ganov 89177276b60851a158ad3e142cb3b091d57ae5ceffbSvetoslav Ganov /** 892736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov * Sends down events to the view hierarchy for all active pointers which are 893736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov * not already being delivered i.e. pointers that are not yet injected. 894736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov * 895736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov * @param prototype The prototype from which to create the injected events. 896736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov * @param policyFlags The policy flags associated with the event. 897736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov */ 898736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov private void sendDownForAllActiveNotInjectedPointers(MotionEvent prototype, int policyFlags) { 8994213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov ReceivedPointerTracker receivedPointers = mReceivedPointerTracker; 9004213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov InjectedPointerTracker injectedPointers = mInjectedPointerTracker; 901f804420d6e37748b75478406e989c69303756980Svetoslav Ganov int pointerIdBits = 0; 902f804420d6e37748b75478406e989c69303756980Svetoslav Ganov final int pointerCount = prototype.getPointerCount(); 903736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov 904f804420d6e37748b75478406e989c69303756980Svetoslav Ganov // Find which pointers are already injected. 905f804420d6e37748b75478406e989c69303756980Svetoslav Ganov for (int i = 0; i < pointerCount; i++) { 906736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov final int pointerId = prototype.getPointerId(i); 9074213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov if (injectedPointers.isInjectedPointerDown(pointerId)) { 908f804420d6e37748b75478406e989c69303756980Svetoslav Ganov pointerIdBits |= (1 << pointerId); 909f804420d6e37748b75478406e989c69303756980Svetoslav Ganov } 910f804420d6e37748b75478406e989c69303756980Svetoslav Ganov } 911736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov 912f804420d6e37748b75478406e989c69303756980Svetoslav Ganov // Inject the active and not injected pointers. 913f804420d6e37748b75478406e989c69303756980Svetoslav Ganov for (int i = 0; i < pointerCount; i++) { 914f804420d6e37748b75478406e989c69303756980Svetoslav Ganov final int pointerId = prototype.getPointerId(i); 915736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov // Skip inactive pointers. 9164213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov if (!receivedPointers.isActivePointer(pointerId)) { 917736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov continue; 918736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov } 919f804420d6e37748b75478406e989c69303756980Svetoslav Ganov // Do not send event for already delivered pointers. 9204213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov if (injectedPointers.isInjectedPointerDown(pointerId)) { 921736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov continue; 922736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov } 923f804420d6e37748b75478406e989c69303756980Svetoslav Ganov pointerIdBits |= (1 << pointerId); 924f804420d6e37748b75478406e989c69303756980Svetoslav Ganov final int action = computeInjectionAction(MotionEvent.ACTION_DOWN, i); 925f804420d6e37748b75478406e989c69303756980Svetoslav Ganov sendMotionEvent(prototype, action, pointerIdBits, policyFlags); 926f804420d6e37748b75478406e989c69303756980Svetoslav Ganov } 927f804420d6e37748b75478406e989c69303756980Svetoslav Ganov } 928736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov 929f804420d6e37748b75478406e989c69303756980Svetoslav Ganov /** 930e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov * Sends the exit events if needed. Such events are hover exit and touch explore 931e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov * gesture end. 932e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov * 933e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov * @param policyFlags The policy flags associated with the event. 934e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov */ 935f068fed6c4c3fc2003aec19b6e7e892358179b02Svetoslav Ganov private void sendHoverExitAndTouchExplorationGestureEndIfNeeded(int policyFlags) { 936e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov MotionEvent event = mInjectedPointerTracker.getLastInjectedHoverEvent(); 937e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov if (event != null && event.getActionMasked() != MotionEvent.ACTION_HOVER_EXIT) { 938e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov final int pointerIdBits = event.getPointerIdBits(); 939f068fed6c4c3fc2003aec19b6e7e892358179b02Svetoslav Ganov if (!mSendTouchExplorationEndDelayed.isPending()) { 940f068fed6c4c3fc2003aec19b6e7e892358179b02Svetoslav Ganov mSendTouchExplorationEndDelayed.post(); 941fe304b893968887323b93764caafa66ee8ad44deSvetoslav Ganov } 942e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov sendMotionEvent(event, MotionEvent.ACTION_HOVER_EXIT, pointerIdBits, policyFlags); 943e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov } 944e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov } 945e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov 946e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov /** 947e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov * Sends the enter events if needed. Such events are hover enter and touch explore 948e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov * gesture start. 949f804420d6e37748b75478406e989c69303756980Svetoslav Ganov * 950f804420d6e37748b75478406e989c69303756980Svetoslav Ganov * @param policyFlags The policy flags associated with the event. 951f804420d6e37748b75478406e989c69303756980Svetoslav Ganov */ 952f068fed6c4c3fc2003aec19b6e7e892358179b02Svetoslav Ganov private void sendTouchExplorationGestureStartAndHoverEnterIfNeeded(int policyFlags) { 953e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov MotionEvent event = mInjectedPointerTracker.getLastInjectedHoverEvent(); 954e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov if (event != null && event.getActionMasked() == MotionEvent.ACTION_HOVER_EXIT) { 955e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov final int pointerIdBits = event.getPointerIdBits(); 956f068fed6c4c3fc2003aec19b6e7e892358179b02Svetoslav Ganov sendAccessibilityEvent(AccessibilityEvent.TYPE_TOUCH_EXPLORATION_GESTURE_START); 957e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov sendMotionEvent(event, MotionEvent.ACTION_HOVER_ENTER, pointerIdBits, policyFlags); 958736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov } 959736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov } 960736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov 961736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov /** 962736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov * Sends up events to the view hierarchy for all active pointers which are 963736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov * already being delivered i.e. pointers that are injected. 964736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov * 965736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov * @param prototype The prototype from which to create the injected events. 966736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov * @param policyFlags The policy flags associated with the event. 967736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov */ 968736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov private void sendUpForInjectedDownPointers(MotionEvent prototype, int policyFlags) { 9694213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov final InjectedPointerTracker injectedTracked = mInjectedPointerTracker; 970f804420d6e37748b75478406e989c69303756980Svetoslav Ganov int pointerIdBits = 0; 971f804420d6e37748b75478406e989c69303756980Svetoslav Ganov final int pointerCount = prototype.getPointerCount(); 972736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov for (int i = 0; i < pointerCount; i++) { 973736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov final int pointerId = prototype.getPointerId(i); 974736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov // Skip non injected down pointers. 9754213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov if (!injectedTracked.isInjectedPointerDown(pointerId)) { 976736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov continue; 977736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov } 978f804420d6e37748b75478406e989c69303756980Svetoslav Ganov pointerIdBits |= (1 << pointerId); 979f804420d6e37748b75478406e989c69303756980Svetoslav Ganov final int action = computeInjectionAction(MotionEvent.ACTION_UP, i); 980f804420d6e37748b75478406e989c69303756980Svetoslav Ganov sendMotionEvent(prototype, action, pointerIdBits, policyFlags); 981736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov } 982736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov } 983736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov 984736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov /** 985736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov * Sends a motion event by first stripping the inactive pointers. 986736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov * 987736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov * @param prototype The prototype from which to create the injected event. 988736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov * @param policyFlags The policy flags associated with the event. 989736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov */ 990736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov private void sendMotionEventStripInactivePointers(MotionEvent prototype, int policyFlags) { 9914213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov ReceivedPointerTracker receivedTracker = mReceivedPointerTracker; 992736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov 993736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov // All pointers active therefore we just inject the event as is. 9944213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov if (prototype.getPointerCount() == receivedTracker.getActivePointerCount()) { 995f804420d6e37748b75478406e989c69303756980Svetoslav Ganov sendMotionEvent(prototype, prototype.getAction(), ALL_POINTER_ID_BITS, policyFlags); 996736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov return; 997736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov } 998736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov 999736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov // No active pointers and the one that just went up was not 1000736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov // active, therefore we have nothing to do. 10014213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov if (receivedTracker.getActivePointerCount() == 0 10024213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov && !receivedTracker.wasLastReceivedUpPointerActive()) { 1003736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov return; 1004736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov } 1005736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov 1006f804420d6e37748b75478406e989c69303756980Svetoslav Ganov // If the action pointer going up/down is not active we have nothing to do. 1007f804420d6e37748b75478406e989c69303756980Svetoslav Ganov // However, for moves we keep going to report moves of active pointers. 1008f804420d6e37748b75478406e989c69303756980Svetoslav Ganov final int actionMasked = prototype.getActionMasked(); 1009f804420d6e37748b75478406e989c69303756980Svetoslav Ganov final int actionPointerId = prototype.getPointerId(prototype.getActionIndex()); 1010f804420d6e37748b75478406e989c69303756980Svetoslav Ganov if (actionMasked != MotionEvent.ACTION_MOVE) { 10114213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov if (!receivedTracker.isActiveOrWasLastActiveUpPointer(actionPointerId)) { 1012f804420d6e37748b75478406e989c69303756980Svetoslav Ganov return; 1013f804420d6e37748b75478406e989c69303756980Svetoslav Ganov } 1014f804420d6e37748b75478406e989c69303756980Svetoslav Ganov } 1015f804420d6e37748b75478406e989c69303756980Svetoslav Ganov 1016f804420d6e37748b75478406e989c69303756980Svetoslav Ganov // If the pointer is active or the pointer that just went up 1017f804420d6e37748b75478406e989c69303756980Svetoslav Ganov // was active we keep the pointer data in the event. 101891feae3c5994bd4768cea3507c62c65746adcfa6Svetoslav Ganov int pointerIdBits = 0; 101991feae3c5994bd4768cea3507c62c65746adcfa6Svetoslav Ganov final int pointerCount = prototype.getPointerCount(); 102091feae3c5994bd4768cea3507c62c65746adcfa6Svetoslav Ganov for (int pointerIndex = 0; pointerIndex < pointerCount; pointerIndex++) { 102191feae3c5994bd4768cea3507c62c65746adcfa6Svetoslav Ganov final int pointerId = prototype.getPointerId(pointerIndex); 10224213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov if (receivedTracker.isActiveOrWasLastActiveUpPointer(pointerId)) { 102391feae3c5994bd4768cea3507c62c65746adcfa6Svetoslav Ganov pointerIdBits |= (1 << pointerId); 1024736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov } 1025736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov } 1026f804420d6e37748b75478406e989c69303756980Svetoslav Ganov sendMotionEvent(prototype, prototype.getAction(), pointerIdBits, policyFlags); 1027736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov } 1028736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov 1029736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov /** 1030736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov * Sends an up and down events. 1031736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov * 1032736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov * @param prototype The prototype from which to create the injected events. 1033736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov * @param policyFlags The policy flags associated with the event. 1034736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov */ 1035736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov private void sendActionDownAndUp(MotionEvent prototype, int policyFlags) { 1036bd206d129fdd1777b9f9646a834d7fc342a8941eSvetoslav Ganov // Tap with the pointer that last explored - we may have inactive pointers. 1037bd206d129fdd1777b9f9646a834d7fc342a8941eSvetoslav Ganov final int pointerId = prototype.getPointerId(prototype.getActionIndex()); 1038f804420d6e37748b75478406e989c69303756980Svetoslav Ganov final int pointerIdBits = (1 << pointerId); 1039f804420d6e37748b75478406e989c69303756980Svetoslav Ganov sendMotionEvent(prototype, MotionEvent.ACTION_DOWN, pointerIdBits, policyFlags); 1040f804420d6e37748b75478406e989c69303756980Svetoslav Ganov sendMotionEvent(prototype, MotionEvent.ACTION_UP, pointerIdBits, policyFlags); 1041736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov } 1042736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov 1043736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov /** 104491feae3c5994bd4768cea3507c62c65746adcfa6Svetoslav Ganov * Sends an event. 1045736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov * 104600f7b3f76515d1c6fbe5cf9fee9d3760787c03cdSvetoslav Ganov * @param prototype The prototype from which to create the injected events. 104791feae3c5994bd4768cea3507c62c65746adcfa6Svetoslav Ganov * @param action The action of the event. 104891feae3c5994bd4768cea3507c62c65746adcfa6Svetoslav Ganov * @param pointerIdBits The bits of the pointers to send. 1049736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov * @param policyFlags The policy flags associated with the event. 1050736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov */ 105191feae3c5994bd4768cea3507c62c65746adcfa6Svetoslav Ganov private void sendMotionEvent(MotionEvent prototype, int action, int pointerIdBits, 105291feae3c5994bd4768cea3507c62c65746adcfa6Svetoslav Ganov int policyFlags) { 1053f804420d6e37748b75478406e989c69303756980Svetoslav Ganov prototype.setAction(action); 1054f804420d6e37748b75478406e989c69303756980Svetoslav Ganov 1055f804420d6e37748b75478406e989c69303756980Svetoslav Ganov MotionEvent event = null; 1056f804420d6e37748b75478406e989c69303756980Svetoslav Ganov if (pointerIdBits == ALL_POINTER_ID_BITS) { 1057f804420d6e37748b75478406e989c69303756980Svetoslav Ganov event = prototype; 1058f804420d6e37748b75478406e989c69303756980Svetoslav Ganov } else { 1059f804420d6e37748b75478406e989c69303756980Svetoslav Ganov event = prototype.split(pointerIdBits); 1060f804420d6e37748b75478406e989c69303756980Svetoslav Ganov } 1061f804420d6e37748b75478406e989c69303756980Svetoslav Ganov if (action == MotionEvent.ACTION_DOWN) { 1062f804420d6e37748b75478406e989c69303756980Svetoslav Ganov event.setDownTime(event.getEventTime()); 1063f804420d6e37748b75478406e989c69303756980Svetoslav Ganov } else { 10644213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov event.setDownTime(mInjectedPointerTracker.getLastInjectedDownEventTime()); 1065f804420d6e37748b75478406e989c69303756980Svetoslav Ganov } 1066f804420d6e37748b75478406e989c69303756980Svetoslav Ganov 1067e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov // If the user is long pressing but the long pressing pointer 1068e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov // was not exactly over the accessibility focused item we need 1069e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov // to remap the location of that pointer so the user does not 1070e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov // have to explicitly touch explore something to be able to 1071e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov // long press it, or even worse to avoid the user long pressing 1072e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov // on the wrong item since click and long press behave differently. 1073e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov if (mLongPressingPointerId >= 0) { 1074e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov final int remappedIndex = event.findPointerIndex(mLongPressingPointerId); 1075e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov final int pointerCount = event.getPointerCount(); 1076e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov PointerProperties[] props = PointerProperties.createArray(pointerCount); 1077e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov PointerCoords[] coords = PointerCoords.createArray(pointerCount); 1078e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov for (int i = 0; i < pointerCount; i++) { 1079e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov event.getPointerProperties(i, props[i]); 1080e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov event.getPointerCoords(i, coords[i]); 1081e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov if (i == remappedIndex) { 1082e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov coords[i].x -= mLongPressingPointerDeltaX; 1083e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov coords[i].y -= mLongPressingPointerDeltaY; 1084e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov } 1085e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov } 1086e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov MotionEvent remapped = MotionEvent.obtain(event.getDownTime(), 1087e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov event.getEventTime(), event.getAction(), event.getPointerCount(), 1088e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov props, coords, event.getMetaState(), event.getButtonState(), 1089e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov 1.0f, 1.0f, event.getDeviceId(), event.getEdgeFlags(), 1090e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov event.getSource(), event.getFlags()); 1091e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov if (event != prototype) { 1092e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov event.recycle(); 1093e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov } 1094e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov event = remapped; 1095e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov } 1096e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov 1097f804420d6e37748b75478406e989c69303756980Svetoslav Ganov if (DEBUG) { 10984213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov Slog.d(LOG_TAG, "Injecting event: " + event + ", policyFlags=0x" 1099f804420d6e37748b75478406e989c69303756980Svetoslav Ganov + Integer.toHexString(policyFlags)); 1100f804420d6e37748b75478406e989c69303756980Svetoslav Ganov } 1101f804420d6e37748b75478406e989c69303756980Svetoslav Ganov 1102f804420d6e37748b75478406e989c69303756980Svetoslav Ganov // Make sure that the user will see the event. 1103f804420d6e37748b75478406e989c69303756980Svetoslav Ganov policyFlags |= WindowManagerPolicy.FLAG_PASS_TO_USER; 11041cf70bbf96930662cab0e699d70b62865766ff52Svetoslav Ganov if (mNext != null) { 110545af84a483165f06c04d74baba67f90da29c6ad2Svetoslav Ganov // TODO: For now pass null for the raw event since the touch 110645af84a483165f06c04d74baba67f90da29c6ad2Svetoslav Ganov // explorer is the last event transformation and it does 110745af84a483165f06c04d74baba67f90da29c6ad2Svetoslav Ganov // not care about the raw event. 110845af84a483165f06c04d74baba67f90da29c6ad2Svetoslav Ganov mNext.onMotionEvent(event, null, policyFlags); 11091cf70bbf96930662cab0e699d70b62865766ff52Svetoslav Ganov } 1110f804420d6e37748b75478406e989c69303756980Svetoslav Ganov 11114213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov mInjectedPointerTracker.onMotionEvent(event); 11124213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov 1113f804420d6e37748b75478406e989c69303756980Svetoslav Ganov if (event != prototype) { 1114f804420d6e37748b75478406e989c69303756980Svetoslav Ganov event.recycle(); 1115f804420d6e37748b75478406e989c69303756980Svetoslav Ganov } 1116736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov } 1117736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov 1118736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov /** 1119736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov * Computes the action for an injected event based on a masked action 1120736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov * and a pointer index. 1121736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov * 1122736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov * @param actionMasked The masked action. 1123736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov * @param pointerIndex The index of the pointer which has changed. 1124736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov * @return The action to be used for injection. 1125736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov */ 1126736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov private int computeInjectionAction(int actionMasked, int pointerIndex) { 1127736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov switch (actionMasked) { 1128736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov case MotionEvent.ACTION_DOWN: 1129736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov case MotionEvent.ACTION_POINTER_DOWN: { 11304213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov InjectedPointerTracker injectedTracker = mInjectedPointerTracker; 1131736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov // Compute the action based on how many down pointers are injected. 11324213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov if (injectedTracker.getInjectedPointerDownCount() == 0) { 1133736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov return MotionEvent.ACTION_DOWN; 1134736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov } else { 1135736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov return (pointerIndex << MotionEvent.ACTION_POINTER_INDEX_SHIFT) 1136736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov | MotionEvent.ACTION_POINTER_DOWN; 1137736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov } 1138736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov } 1139736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov case MotionEvent.ACTION_POINTER_UP: { 11404213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov InjectedPointerTracker injectedTracker = mInjectedPointerTracker; 1141736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov // Compute the action based on how many down pointers are injected. 11424213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov if (injectedTracker.getInjectedPointerDownCount() == 1) { 1143736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov return MotionEvent.ACTION_UP; 1144736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov } else { 1145736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov return (pointerIndex << MotionEvent.ACTION_POINTER_INDEX_SHIFT) 1146736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov | MotionEvent.ACTION_POINTER_UP; 1147736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov } 1148736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov } 1149736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov default: 1150736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov return actionMasked; 1151736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov } 1152736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov } 1153736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov 1154e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov private class DoubleTapDetector { 1155e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov private MotionEvent mDownEvent; 1156e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov private MotionEvent mFirstTapEvent; 1157e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov 1158e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov public void onMotionEvent(MotionEvent event, int policyFlags) { 11591cf70bbf96930662cab0e699d70b62865766ff52Svetoslav Ganov final int actionIndex = event.getActionIndex(); 1160e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov final int action = event.getActionMasked(); 1161e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov switch (action) { 1162e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov case MotionEvent.ACTION_DOWN: 1163e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov case MotionEvent.ACTION_POINTER_DOWN: { 11641cf70bbf96930662cab0e699d70b62865766ff52Svetoslav Ganov if (mFirstTapEvent != null 11651cf70bbf96930662cab0e699d70b62865766ff52Svetoslav Ganov && !GestureUtils.isSamePointerContext(mFirstTapEvent, event)) { 1166e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov clear(); 1167e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov } 1168e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov mDownEvent = MotionEvent.obtain(event); 1169e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov } break; 1170e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov case MotionEvent.ACTION_UP: 1171e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov case MotionEvent.ACTION_POINTER_UP: { 1172e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov if (mDownEvent == null) { 1173e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov return; 1174e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov } 11751cf70bbf96930662cab0e699d70b62865766ff52Svetoslav Ganov if (!GestureUtils.isSamePointerContext(mDownEvent, event)) { 1176e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov clear(); 1177e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov return; 1178e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov } 11791cf70bbf96930662cab0e699d70b62865766ff52Svetoslav Ganov if (GestureUtils.isTap(mDownEvent, event, mTapTimeout, mTouchSlop, 11801cf70bbf96930662cab0e699d70b62865766ff52Svetoslav Ganov actionIndex)) { 11811cf70bbf96930662cab0e699d70b62865766ff52Svetoslav Ganov if (mFirstTapEvent == null || GestureUtils.isTimedOut(mFirstTapEvent, 11821cf70bbf96930662cab0e699d70b62865766ff52Svetoslav Ganov event, mDoubleTapTimeout)) { 1183e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov mFirstTapEvent = MotionEvent.obtain(event); 1184e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov mDownEvent.recycle(); 1185e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov mDownEvent = null; 1186e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov return; 1187e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov } 11881cf70bbf96930662cab0e699d70b62865766ff52Svetoslav Ganov if (GestureUtils.isMultiTap(mFirstTapEvent, event, mDoubleTapTimeout, 11891cf70bbf96930662cab0e699d70b62865766ff52Svetoslav Ganov mDoubleTapSlop, actionIndex)) { 1190e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov onDoubleTap(event, policyFlags); 1191e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov mFirstTapEvent.recycle(); 1192e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov mFirstTapEvent = null; 1193e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov mDownEvent.recycle(); 1194e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov mDownEvent = null; 1195e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov return; 1196e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov } 1197e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov mFirstTapEvent.recycle(); 1198e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov mFirstTapEvent = null; 1199e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov } else { 1200e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov if (mFirstTapEvent != null) { 1201e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov mFirstTapEvent.recycle(); 1202e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov mFirstTapEvent = null; 1203e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov } 1204e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov } 1205e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov mDownEvent.recycle(); 1206e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov mDownEvent = null; 1207e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov } break; 1208e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov } 1209e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov } 1210e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov 1211e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov public void onDoubleTap(MotionEvent secondTapUp, int policyFlags) { 1212e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov // This should never be called when more than two pointers are down. 1213e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov if (secondTapUp.getPointerCount() > 2) { 1214e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov return; 1215e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov } 1216e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov 1217e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov // Remove pending event deliveries. 121858d37b55bd228032355360ea3303e46a804e0516Svetoslav Ganov mSendHoverEnterDelayed.remove(); 121958d37b55bd228032355360ea3303e46a804e0516Svetoslav Ganov mSendHoverExitDelayed.remove(); 1220e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov mPerformLongPressDelayed.remove(); 1221e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov 1222f068fed6c4c3fc2003aec19b6e7e892358179b02Svetoslav Ganov if (mSendTouchExplorationEndDelayed.isPending()) { 1223f068fed6c4c3fc2003aec19b6e7e892358179b02Svetoslav Ganov mSendTouchExplorationEndDelayed.forceSendAndRemove(); 1224f068fed6c4c3fc2003aec19b6e7e892358179b02Svetoslav Ganov } 1225f068fed6c4c3fc2003aec19b6e7e892358179b02Svetoslav Ganov if (mSendTouchInteractionEndDelayed.isPending()) { 1226f068fed6c4c3fc2003aec19b6e7e892358179b02Svetoslav Ganov mSendTouchInteractionEndDelayed.forceSendAndRemove(); 1227f068fed6c4c3fc2003aec19b6e7e892358179b02Svetoslav Ganov } 1228e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov 122986783474fdec98a22bc22e224462767eab13e273Svetoslav Ganov int clickLocationX; 123086783474fdec98a22bc22e224462767eab13e273Svetoslav Ganov int clickLocationY; 1231e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov 1232e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov final int pointerId = secondTapUp.getPointerId(secondTapUp.getActionIndex()); 1233e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov final int pointerIndex = secondTapUp.findPointerIndex(pointerId); 123486783474fdec98a22bc22e224462767eab13e273Svetoslav Ganov 12355d043ce8cc2f588fdfb336cc843fb3b07b196f83Svetoslav Ganov MotionEvent lastExploreEvent = 12365d043ce8cc2f588fdfb336cc843fb3b07b196f83Svetoslav Ganov mInjectedPointerTracker.getLastInjectedHoverEventForClick(); 123786783474fdec98a22bc22e224462767eab13e273Svetoslav Ganov if (lastExploreEvent == null) { 123886783474fdec98a22bc22e224462767eab13e273Svetoslav Ganov // No last touch explored event but there is accessibility focus in 123986783474fdec98a22bc22e224462767eab13e273Svetoslav Ganov // the active window. We click in the middle of the focus bounds. 124086783474fdec98a22bc22e224462767eab13e273Svetoslav Ganov Rect focusBounds = mTempRect; 124186783474fdec98a22bc22e224462767eab13e273Svetoslav Ganov if (mAms.getAccessibilityFocusBoundsInActiveWindow(focusBounds)) { 124286783474fdec98a22bc22e224462767eab13e273Svetoslav Ganov clickLocationX = focusBounds.centerX(); 124386783474fdec98a22bc22e224462767eab13e273Svetoslav Ganov clickLocationY = focusBounds.centerY(); 124486783474fdec98a22bc22e224462767eab13e273Svetoslav Ganov } else { 124586783474fdec98a22bc22e224462767eab13e273Svetoslav Ganov // Out of luck - do nothing. 124686783474fdec98a22bc22e224462767eab13e273Svetoslav Ganov return; 124786783474fdec98a22bc22e224462767eab13e273Svetoslav Ganov } 124886783474fdec98a22bc22e224462767eab13e273Svetoslav Ganov } else { 124986783474fdec98a22bc22e224462767eab13e273Svetoslav Ganov // If the click is within the active window but not within the 125086783474fdec98a22bc22e224462767eab13e273Svetoslav Ganov // accessibility focus bounds we click in the focus center. 125186783474fdec98a22bc22e224462767eab13e273Svetoslav Ganov final int lastExplorePointerIndex = lastExploreEvent.getActionIndex(); 125286783474fdec98a22bc22e224462767eab13e273Svetoslav Ganov clickLocationX = (int) lastExploreEvent.getX(lastExplorePointerIndex); 125386783474fdec98a22bc22e224462767eab13e273Svetoslav Ganov clickLocationY = (int) lastExploreEvent.getY(lastExplorePointerIndex); 125486783474fdec98a22bc22e224462767eab13e273Svetoslav Ganov Rect activeWindowBounds = mTempRect; 1255385d9f24b5ce2acb86c0dc192ce702718ab01c39Svetoslav Ganov if (mLastTouchedWindowId == mAms.getActiveWindowId()) { 1256385d9f24b5ce2acb86c0dc192ce702718ab01c39Svetoslav Ganov mAms.getActiveWindowBounds(activeWindowBounds); 1257385d9f24b5ce2acb86c0dc192ce702718ab01c39Svetoslav Ganov if (activeWindowBounds.contains(clickLocationX, clickLocationY)) { 1258385d9f24b5ce2acb86c0dc192ce702718ab01c39Svetoslav Ganov Rect focusBounds = mTempRect; 1259385d9f24b5ce2acb86c0dc192ce702718ab01c39Svetoslav Ganov if (mAms.getAccessibilityFocusBoundsInActiveWindow(focusBounds)) { 1260385d9f24b5ce2acb86c0dc192ce702718ab01c39Svetoslav Ganov if (!focusBounds.contains(clickLocationX, clickLocationY)) { 1261385d9f24b5ce2acb86c0dc192ce702718ab01c39Svetoslav Ganov clickLocationX = focusBounds.centerX(); 1262385d9f24b5ce2acb86c0dc192ce702718ab01c39Svetoslav Ganov clickLocationY = focusBounds.centerY(); 1263385d9f24b5ce2acb86c0dc192ce702718ab01c39Svetoslav Ganov } 126486783474fdec98a22bc22e224462767eab13e273Svetoslav Ganov } 126586783474fdec98a22bc22e224462767eab13e273Svetoslav Ganov } 1266e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov } 1267e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov } 1268e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov 1269e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov // Do the click. 1270e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov PointerProperties[] properties = new PointerProperties[1]; 1271e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov properties[0] = new PointerProperties(); 1272e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov secondTapUp.getPointerProperties(pointerIndex, properties[0]); 1273e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov PointerCoords[] coords = new PointerCoords[1]; 1274e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov coords[0] = new PointerCoords(); 127586783474fdec98a22bc22e224462767eab13e273Svetoslav Ganov coords[0].x = clickLocationX; 127686783474fdec98a22bc22e224462767eab13e273Svetoslav Ganov coords[0].y = clickLocationY; 1277e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov MotionEvent event = MotionEvent.obtain(secondTapUp.getDownTime(), 1278e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov secondTapUp.getEventTime(), MotionEvent.ACTION_DOWN, 1, properties, 1279e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov coords, 0, 0, 1.0f, 1.0f, secondTapUp.getDeviceId(), 0, 1280e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov secondTapUp.getSource(), secondTapUp.getFlags()); 1281e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov sendActionDownAndUp(event, policyFlags); 1282e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov event.recycle(); 1283e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov } 1284e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov 1285e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov public void clear() { 1286e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov if (mDownEvent != null) { 1287e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov mDownEvent.recycle(); 1288e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov mDownEvent = null; 1289e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov } 1290e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov if (mFirstTapEvent != null) { 1291e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov mFirstTapEvent.recycle(); 1292e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov mFirstTapEvent = null; 1293e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov } 1294e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov } 1295e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov 1296e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov public boolean firstTapDetected() { 1297e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov return mFirstTapEvent != null 1298e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov && SystemClock.uptimeMillis() - mFirstTapEvent.getEventTime() < mDoubleTapTimeout; 1299e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov } 1300e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov } 1301e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov 1302736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov /** 1303736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov * Determines whether a two pointer gesture is a dragging one. 1304736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov * 1305736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov * @param event The event with the pointer data. 1306736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov * @return True if the gesture is a dragging one. 1307736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov */ 1308736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov private boolean isDraggingGesture(MotionEvent event) { 13094213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov ReceivedPointerTracker receivedTracker = mReceivedPointerTracker; 1310736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov int[] pointerIds = mTempPointerIds; 13114213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov receivedTracker.populateActivePointerIds(pointerIds); 1312736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov 1313736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov final int firstPtrIndex = event.findPointerIndex(pointerIds[0]); 1314736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov final int secondPtrIndex = event.findPointerIndex(pointerIds[1]); 1315736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov 1316736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov final float firstPtrX = event.getX(firstPtrIndex); 1317736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov final float firstPtrY = event.getY(firstPtrIndex); 1318736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov final float secondPtrX = event.getX(secondPtrIndex); 1319736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov final float secondPtrY = event.getY(secondPtrIndex); 1320736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov 13211cf70bbf96930662cab0e699d70b62865766ff52Svetoslav Ganov final float firstPtrDownX = receivedTracker.getReceivedPointerDownX(firstPtrIndex); 13221cf70bbf96930662cab0e699d70b62865766ff52Svetoslav Ganov final float firstPtrDownY = receivedTracker.getReceivedPointerDownY(firstPtrIndex); 13231cf70bbf96930662cab0e699d70b62865766ff52Svetoslav Ganov final float secondPtrDownX = receivedTracker.getReceivedPointerDownX(secondPtrIndex); 13241cf70bbf96930662cab0e699d70b62865766ff52Svetoslav Ganov final float secondPtrDownY = receivedTracker.getReceivedPointerDownY(secondPtrIndex); 1325736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov 13261cf70bbf96930662cab0e699d70b62865766ff52Svetoslav Ganov return GestureUtils.isDraggingGesture(firstPtrDownX, firstPtrDownY, secondPtrDownX, 13271cf70bbf96930662cab0e699d70b62865766ff52Svetoslav Ganov secondPtrDownY, firstPtrX, firstPtrY, secondPtrX, secondPtrY, 13281cf70bbf96930662cab0e699d70b62865766ff52Svetoslav Ganov MAX_DRAGGING_ANGLE_COS); 1329736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov } 1330736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov 1331736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov /** 133251cccf0845b36539d42503495f0689d487712b3aSvetoslav Ganov * Gets the symbolic name of a state. 133351cccf0845b36539d42503495f0689d487712b3aSvetoslav Ganov * 133451cccf0845b36539d42503495f0689d487712b3aSvetoslav Ganov * @param state A state. 133551cccf0845b36539d42503495f0689d487712b3aSvetoslav Ganov * @return The state symbolic name. 133651cccf0845b36539d42503495f0689d487712b3aSvetoslav Ganov */ 133751cccf0845b36539d42503495f0689d487712b3aSvetoslav Ganov private static String getStateSymbolicName(int state) { 133851cccf0845b36539d42503495f0689d487712b3aSvetoslav Ganov switch (state) { 133951cccf0845b36539d42503495f0689d487712b3aSvetoslav Ganov case STATE_TOUCH_EXPLORING: 134051cccf0845b36539d42503495f0689d487712b3aSvetoslav Ganov return "STATE_TOUCH_EXPLORING"; 134151cccf0845b36539d42503495f0689d487712b3aSvetoslav Ganov case STATE_DRAGGING: 134251cccf0845b36539d42503495f0689d487712b3aSvetoslav Ganov return "STATE_DRAGGING"; 134351cccf0845b36539d42503495f0689d487712b3aSvetoslav Ganov case STATE_DELEGATING: 134451cccf0845b36539d42503495f0689d487712b3aSvetoslav Ganov return "STATE_DELEGATING"; 13454213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov case STATE_GESTURE_DETECTING: 13464213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov return "STATE_GESTURE_DETECTING"; 134751cccf0845b36539d42503495f0689d487712b3aSvetoslav Ganov default: 134851cccf0845b36539d42503495f0689d487712b3aSvetoslav Ganov throw new IllegalArgumentException("Unknown state: " + state); 134951cccf0845b36539d42503495f0689d487712b3aSvetoslav Ganov } 135051cccf0845b36539d42503495f0689d487712b3aSvetoslav Ganov } 135151cccf0845b36539d42503495f0689d487712b3aSvetoslav Ganov 135251cccf0845b36539d42503495f0689d487712b3aSvetoslav Ganov /** 13534213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov * @return The number of non injected active pointers. 1354736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov */ 13554213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov private int getNotInjectedActivePointerCount(ReceivedPointerTracker receivedTracker, 13564213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov InjectedPointerTracker injectedTracker) { 13574213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov final int pointerState = receivedTracker.getActivePointers() 13584213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov & ~injectedTracker.getInjectedPointersDown(); 13594213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov return Integer.bitCount(pointerState); 13604213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov } 13614213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov 13624213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov /** 136395068e5d1bea47091e97955f271c789264994550Svetoslav Ganov * Class for delayed exiting from gesture detecting mode. 136495068e5d1bea47091e97955f271c789264994550Svetoslav Ganov */ 136595068e5d1bea47091e97955f271c789264994550Svetoslav Ganov private final class ExitGestureDetectionModeDelayed implements Runnable { 136695068e5d1bea47091e97955f271c789264994550Svetoslav Ganov 136795068e5d1bea47091e97955f271c789264994550Svetoslav Ganov public void post() { 136895068e5d1bea47091e97955f271c789264994550Svetoslav Ganov mHandler.postDelayed(this, EXIT_GESTURE_DETECTION_TIMEOUT); 136995068e5d1bea47091e97955f271c789264994550Svetoslav Ganov } 137095068e5d1bea47091e97955f271c789264994550Svetoslav Ganov 137195068e5d1bea47091e97955f271c789264994550Svetoslav Ganov public void remove() { 137295068e5d1bea47091e97955f271c789264994550Svetoslav Ganov mHandler.removeCallbacks(this); 137395068e5d1bea47091e97955f271c789264994550Svetoslav Ganov } 137495068e5d1bea47091e97955f271c789264994550Svetoslav Ganov 137595068e5d1bea47091e97955f271c789264994550Svetoslav Ganov @Override 137695068e5d1bea47091e97955f271c789264994550Svetoslav Ganov public void run() { 1377aed4b6f812674bc60a04470013ca449e5c114fa5Svetoslav Ganov // Announce the end of gesture recognition. 1378aed4b6f812674bc60a04470013ca449e5c114fa5Svetoslav Ganov sendAccessibilityEvent(AccessibilityEvent.TYPE_GESTURE_DETECTION_END); 1379aed4b6f812674bc60a04470013ca449e5c114fa5Svetoslav Ganov // Clearing puts is in touch exploration state with a finger already 1380aed4b6f812674bc60a04470013ca449e5c114fa5Svetoslav Ganov // down, so announce the transition to exploration state. 1381aed4b6f812674bc60a04470013ca449e5c114fa5Svetoslav Ganov sendAccessibilityEvent(AccessibilityEvent.TYPE_TOUCH_EXPLORATION_GESTURE_START); 138295068e5d1bea47091e97955f271c789264994550Svetoslav Ganov clear(); 138395068e5d1bea47091e97955f271c789264994550Svetoslav Ganov } 138495068e5d1bea47091e97955f271c789264994550Svetoslav Ganov } 138595068e5d1bea47091e97955f271c789264994550Svetoslav Ganov 138695068e5d1bea47091e97955f271c789264994550Svetoslav Ganov /** 13874213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov * Class for delayed sending of long press. 13884213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov */ 13894213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov private final class PerformLongPressDelayed implements Runnable { 13904213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov private MotionEvent mEvent; 13914213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov private int mPolicyFlags; 13924213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov 1393e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov public void post(MotionEvent prototype, int policyFlags) { 13944213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov mEvent = MotionEvent.obtain(prototype); 13954213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov mPolicyFlags = policyFlags; 1396e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov mHandler.postDelayed(this, ViewConfiguration.getLongPressTimeout()); 13974213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov } 13984213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov 13994213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov public void remove() { 140077276b60851a158ad3e142cb3b091d57ae5ceffbSvetoslav Ganov if (isPending()) { 14014213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov mHandler.removeCallbacks(this); 14024213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov clear(); 14034213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov } 14044213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov } 14054213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov 140677276b60851a158ad3e142cb3b091d57ae5ceffbSvetoslav Ganov public boolean isPending() { 14074213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov return (mEvent != null); 14084213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov } 14094213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov 14104213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov @Override 14114213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov public void run() { 1412ebac1b79c4a355d8cd73b49df059deb00d7aa256Svetoslav Ganov // Active pointers should not be zero when running this command. 1413ebac1b79c4a355d8cd73b49df059deb00d7aa256Svetoslav Ganov if (mReceivedPointerTracker.getActivePointerCount() == 0) { 1414ebac1b79c4a355d8cd73b49df059deb00d7aa256Svetoslav Ganov return; 1415ebac1b79c4a355d8cd73b49df059deb00d7aa256Svetoslav Ganov } 1416ebac1b79c4a355d8cd73b49df059deb00d7aa256Svetoslav Ganov 141786783474fdec98a22bc22e224462767eab13e273Svetoslav Ganov int clickLocationX; 141886783474fdec98a22bc22e224462767eab13e273Svetoslav Ganov int clickLocationY; 1419238099c0dbbdc66b8443552126680ad1c7cab17dSvetoslav Ganov 1420238099c0dbbdc66b8443552126680ad1c7cab17dSvetoslav Ganov final int pointerId = mEvent.getPointerId(mEvent.getActionIndex()); 1421238099c0dbbdc66b8443552126680ad1c7cab17dSvetoslav Ganov final int pointerIndex = mEvent.findPointerIndex(pointerId); 1422238099c0dbbdc66b8443552126680ad1c7cab17dSvetoslav Ganov 14235d043ce8cc2f588fdfb336cc843fb3b07b196f83Svetoslav Ganov MotionEvent lastExploreEvent = 14245d043ce8cc2f588fdfb336cc843fb3b07b196f83Svetoslav Ganov mInjectedPointerTracker.getLastInjectedHoverEventForClick(); 142586783474fdec98a22bc22e224462767eab13e273Svetoslav Ganov if (lastExploreEvent == null) { 142686783474fdec98a22bc22e224462767eab13e273Svetoslav Ganov // No last touch explored event but there is accessibility focus in 142786783474fdec98a22bc22e224462767eab13e273Svetoslav Ganov // the active window. We click in the middle of the focus bounds. 142886783474fdec98a22bc22e224462767eab13e273Svetoslav Ganov Rect focusBounds = mTempRect; 142986783474fdec98a22bc22e224462767eab13e273Svetoslav Ganov if (mAms.getAccessibilityFocusBoundsInActiveWindow(focusBounds)) { 143086783474fdec98a22bc22e224462767eab13e273Svetoslav Ganov clickLocationX = focusBounds.centerX(); 143186783474fdec98a22bc22e224462767eab13e273Svetoslav Ganov clickLocationY = focusBounds.centerY(); 143286783474fdec98a22bc22e224462767eab13e273Svetoslav Ganov } else { 143386783474fdec98a22bc22e224462767eab13e273Svetoslav Ganov // Out of luck - do nothing. 143486783474fdec98a22bc22e224462767eab13e273Svetoslav Ganov return; 143586783474fdec98a22bc22e224462767eab13e273Svetoslav Ganov } 1436e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov } else { 143786783474fdec98a22bc22e224462767eab13e273Svetoslav Ganov // If the click is within the active window but not within the 143886783474fdec98a22bc22e224462767eab13e273Svetoslav Ganov // accessibility focus bounds we click in the focus center. 143986783474fdec98a22bc22e224462767eab13e273Svetoslav Ganov final int lastExplorePointerIndex = lastExploreEvent.getActionIndex(); 144086783474fdec98a22bc22e224462767eab13e273Svetoslav Ganov clickLocationX = (int) lastExploreEvent.getX(lastExplorePointerIndex); 144186783474fdec98a22bc22e224462767eab13e273Svetoslav Ganov clickLocationY = (int) lastExploreEvent.getY(lastExplorePointerIndex); 144286783474fdec98a22bc22e224462767eab13e273Svetoslav Ganov Rect activeWindowBounds = mTempRect; 1443385d9f24b5ce2acb86c0dc192ce702718ab01c39Svetoslav Ganov if (mLastTouchedWindowId == mAms.getActiveWindowId()) { 1444385d9f24b5ce2acb86c0dc192ce702718ab01c39Svetoslav Ganov mAms.getActiveWindowBounds(activeWindowBounds); 1445385d9f24b5ce2acb86c0dc192ce702718ab01c39Svetoslav Ganov if (activeWindowBounds.contains(clickLocationX, clickLocationY)) { 1446385d9f24b5ce2acb86c0dc192ce702718ab01c39Svetoslav Ganov Rect focusBounds = mTempRect; 1447385d9f24b5ce2acb86c0dc192ce702718ab01c39Svetoslav Ganov if (mAms.getAccessibilityFocusBoundsInActiveWindow(focusBounds)) { 1448385d9f24b5ce2acb86c0dc192ce702718ab01c39Svetoslav Ganov if (!focusBounds.contains(clickLocationX, clickLocationY)) { 1449385d9f24b5ce2acb86c0dc192ce702718ab01c39Svetoslav Ganov clickLocationX = focusBounds.centerX(); 1450385d9f24b5ce2acb86c0dc192ce702718ab01c39Svetoslav Ganov clickLocationY = focusBounds.centerY(); 1451385d9f24b5ce2acb86c0dc192ce702718ab01c39Svetoslav Ganov } 145286783474fdec98a22bc22e224462767eab13e273Svetoslav Ganov } 145386783474fdec98a22bc22e224462767eab13e273Svetoslav Ganov } 145486783474fdec98a22bc22e224462767eab13e273Svetoslav Ganov } 1455e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov } 1456238099c0dbbdc66b8443552126680ad1c7cab17dSvetoslav Ganov 145786783474fdec98a22bc22e224462767eab13e273Svetoslav Ganov mLongPressingPointerId = pointerId; 145886783474fdec98a22bc22e224462767eab13e273Svetoslav Ganov mLongPressingPointerDeltaX = (int) mEvent.getX(pointerIndex) - clickLocationX; 145986783474fdec98a22bc22e224462767eab13e273Svetoslav Ganov mLongPressingPointerDeltaY = (int) mEvent.getY(pointerIndex) - clickLocationY; 146086783474fdec98a22bc22e224462767eab13e273Svetoslav Ganov 1461f068fed6c4c3fc2003aec19b6e7e892358179b02Svetoslav Ganov sendHoverExitAndTouchExplorationGestureEndIfNeeded(mPolicyFlags); 14624213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov 1463e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov mCurrentState = STATE_DELEGATING; 14644213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov sendDownForAllActiveNotInjectedPointers(mEvent, mPolicyFlags); 14654213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov clear(); 14664213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov } 14674213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov 14684213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov private void clear() { 146977276b60851a158ad3e142cb3b091d57ae5ceffbSvetoslav Ganov if (!isPending()) { 14704213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov return; 14714213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov } 14724213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov mEvent.recycle(); 14734213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov mEvent = null; 14744213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov mPolicyFlags = 0; 14754213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov } 14764213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov } 14774213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov 14784213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov /** 14794213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov * Class for delayed sending of hover events. 14804213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov */ 1481e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov class SendHoverDelayed implements Runnable { 1482e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov private final String LOG_TAG_SEND_HOVER_DELAYED = SendHoverDelayed.class.getName(); 1483e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov 1484e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov private final int mHoverAction; 1485e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov private final boolean mGestureStarted; 1486e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov 1487e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov private MotionEvent mPrototype; 14884213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov private int mPointerIdBits; 14894213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov private int mPolicyFlags; 14904213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov 1491e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov public SendHoverDelayed(int hoverAction, boolean gestureStarted) { 1492e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov mHoverAction = hoverAction; 1493e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov mGestureStarted = gestureStarted; 1494e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov } 1495e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov 149677276b60851a158ad3e142cb3b091d57ae5ceffbSvetoslav Ganov public void post(MotionEvent prototype, boolean touchExplorationInProgress, 149777276b60851a158ad3e142cb3b091d57ae5ceffbSvetoslav Ganov int pointerIdBits, int policyFlags) { 14984213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov remove(); 1499e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov mPrototype = MotionEvent.obtain(prototype); 15004213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov mPointerIdBits = pointerIdBits; 15014213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov mPolicyFlags = policyFlags; 1502e47957a0bbe2164467ff6e7a566b0c9e4689cdc9Svetoslav Ganov mHandler.postDelayed(this, mDetermineUserIntentTimeout); 1503e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov } 1504e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov 1505e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov public float getX() { 1506e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov if (isPending()) { 1507e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov return mPrototype.getX(); 1508e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov } 1509e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov return 0; 1510e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov } 1511e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov 1512e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov public float getY() { 1513e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov if (isPending()) { 1514e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov return mPrototype.getY(); 1515e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov } 1516e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov return 0; 15174213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov } 15184213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov 15194213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov public void remove() { 15204213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov mHandler.removeCallbacks(this); 15214213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov clear(); 15224213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov } 15234213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov 1524e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov private boolean isPending() { 1525e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov return (mPrototype != null); 15264213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov } 15274213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov 15284213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov private void clear() { 1529e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov if (!isPending()) { 15304213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov return; 15314213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov } 1532e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov mPrototype.recycle(); 1533e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov mPrototype = null; 15344213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov mPointerIdBits = -1; 15354213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov mPolicyFlags = 0; 15364213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov } 15374213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov 15384213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov public void forceSendAndRemove() { 1539e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov if (isPending()) { 15404213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov run(); 15414213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov remove(); 15424213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov } 15434213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov } 15444213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov 15454213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov public void run() { 15464213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov if (DEBUG) { 1547e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov Slog.d(LOG_TAG_SEND_HOVER_DELAYED, "Injecting motion event: " 1548e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov + MotionEvent.actionToString(mHoverAction)); 1549e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov Slog.d(LOG_TAG_SEND_HOVER_DELAYED, mGestureStarted ? 1550e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov "touchExplorationGestureStarted" : "touchExplorationGestureEnded"); 15514213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov } 1552f068fed6c4c3fc2003aec19b6e7e892358179b02Svetoslav Ganov if (mGestureStarted) { 1553f068fed6c4c3fc2003aec19b6e7e892358179b02Svetoslav Ganov sendAccessibilityEvent(AccessibilityEvent.TYPE_TOUCH_EXPLORATION_GESTURE_START); 1554e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov } else { 1555f068fed6c4c3fc2003aec19b6e7e892358179b02Svetoslav Ganov if (!mSendTouchExplorationEndDelayed.isPending()) { 1556f068fed6c4c3fc2003aec19b6e7e892358179b02Svetoslav Ganov mSendTouchExplorationEndDelayed.post(); 1557f068fed6c4c3fc2003aec19b6e7e892358179b02Svetoslav Ganov } 1558f068fed6c4c3fc2003aec19b6e7e892358179b02Svetoslav Ganov if (mSendTouchInteractionEndDelayed.isPending()) { 1559f068fed6c4c3fc2003aec19b6e7e892358179b02Svetoslav Ganov mSendTouchInteractionEndDelayed.remove(); 1560f068fed6c4c3fc2003aec19b6e7e892358179b02Svetoslav Ganov mSendTouchInteractionEndDelayed.post(); 156177276b60851a158ad3e142cb3b091d57ae5ceffbSvetoslav Ganov } 1562e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov } 1563e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov sendMotionEvent(mPrototype, mHoverAction, mPointerIdBits, mPolicyFlags); 15644213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov clear(); 15654213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov } 15664213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov } 15674213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov 1568f068fed6c4c3fc2003aec19b6e7e892358179b02Svetoslav Ganov private class SendAccessibilityEventDelayed implements Runnable { 1569f068fed6c4c3fc2003aec19b6e7e892358179b02Svetoslav Ganov private final int mEventType; 1570f068fed6c4c3fc2003aec19b6e7e892358179b02Svetoslav Ganov private final int mDelay; 1571f068fed6c4c3fc2003aec19b6e7e892358179b02Svetoslav Ganov 1572f068fed6c4c3fc2003aec19b6e7e892358179b02Svetoslav Ganov public SendAccessibilityEventDelayed(int eventType, int delay) { 1573f068fed6c4c3fc2003aec19b6e7e892358179b02Svetoslav Ganov mEventType = eventType; 1574f068fed6c4c3fc2003aec19b6e7e892358179b02Svetoslav Ganov mDelay = delay; 1575f068fed6c4c3fc2003aec19b6e7e892358179b02Svetoslav Ganov } 1576fe304b893968887323b93764caafa66ee8ad44deSvetoslav Ganov 1577fe304b893968887323b93764caafa66ee8ad44deSvetoslav Ganov public void remove() { 1578fe304b893968887323b93764caafa66ee8ad44deSvetoslav Ganov mHandler.removeCallbacks(this); 1579fe304b893968887323b93764caafa66ee8ad44deSvetoslav Ganov } 1580fe304b893968887323b93764caafa66ee8ad44deSvetoslav Ganov 1581fe304b893968887323b93764caafa66ee8ad44deSvetoslav Ganov public void post() { 1582f068fed6c4c3fc2003aec19b6e7e892358179b02Svetoslav Ganov mHandler.postDelayed(this, mDelay); 1583fe304b893968887323b93764caafa66ee8ad44deSvetoslav Ganov } 1584fe304b893968887323b93764caafa66ee8ad44deSvetoslav Ganov 1585fe304b893968887323b93764caafa66ee8ad44deSvetoslav Ganov public boolean isPending() { 1586fe304b893968887323b93764caafa66ee8ad44deSvetoslav Ganov return mHandler.hasCallbacks(this); 1587fe304b893968887323b93764caafa66ee8ad44deSvetoslav Ganov } 1588fe304b893968887323b93764caafa66ee8ad44deSvetoslav Ganov 1589fe304b893968887323b93764caafa66ee8ad44deSvetoslav Ganov public void forceSendAndRemove() { 1590fe304b893968887323b93764caafa66ee8ad44deSvetoslav Ganov if (isPending()) { 1591fe304b893968887323b93764caafa66ee8ad44deSvetoslav Ganov run(); 1592fe304b893968887323b93764caafa66ee8ad44deSvetoslav Ganov remove(); 1593fe304b893968887323b93764caafa66ee8ad44deSvetoslav Ganov } 1594fe304b893968887323b93764caafa66ee8ad44deSvetoslav Ganov } 1595fe304b893968887323b93764caafa66ee8ad44deSvetoslav Ganov 1596fe304b893968887323b93764caafa66ee8ad44deSvetoslav Ganov @Override 1597fe304b893968887323b93764caafa66ee8ad44deSvetoslav Ganov public void run() { 1598f068fed6c4c3fc2003aec19b6e7e892358179b02Svetoslav Ganov sendAccessibilityEvent(mEventType); 1599fe304b893968887323b93764caafa66ee8ad44deSvetoslav Ganov } 1600fe304b893968887323b93764caafa66ee8ad44deSvetoslav Ganov } 1601fe304b893968887323b93764caafa66ee8ad44deSvetoslav Ganov 16024213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov @Override 16034213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov public String toString() { 16044213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov return LOG_TAG; 16054213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov } 16064213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov 16074213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov class InjectedPointerTracker { 16084213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov private static final String LOG_TAG_INJECTED_POINTER_TRACKER = "InjectedPointerTracker"; 16094213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov 16104213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov // Keep track of which pointers sent to the system are down. 16114213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov private int mInjectedPointersDown; 16124213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov 16134213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov // The time of the last injected down. 16144213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov private long mLastInjectedDownEventTime; 16154213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov 1616e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov // The last injected hover event. 1617e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov private MotionEvent mLastInjectedHoverEvent; 16184213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov 16195d043ce8cc2f588fdfb336cc843fb3b07b196f83Svetoslav Ganov // The last injected hover event used for performing clicks. 16205d043ce8cc2f588fdfb336cc843fb3b07b196f83Svetoslav Ganov private MotionEvent mLastInjectedHoverEventForClick; 16215d043ce8cc2f588fdfb336cc843fb3b07b196f83Svetoslav Ganov 16224213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov /** 16234213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov * Processes an injected {@link MotionEvent} event. 16244213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov * 16254213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov * @param event The event to process. 16264213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov */ 16274213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov public void onMotionEvent(MotionEvent event) { 16284213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov final int action = event.getActionMasked(); 16294213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov switch (action) { 16304213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov case MotionEvent.ACTION_DOWN: 16314213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov case MotionEvent.ACTION_POINTER_DOWN: { 16324213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov final int pointerId = event.getPointerId(event.getActionIndex()); 16334213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov final int pointerFlag = (1 << pointerId); 16344213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov mInjectedPointersDown |= pointerFlag; 16354213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov mLastInjectedDownEventTime = event.getDownTime(); 16364213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov } break; 16374213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov case MotionEvent.ACTION_UP: 16384213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov case MotionEvent.ACTION_POINTER_UP: { 16394213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov final int pointerId = event.getPointerId(event.getActionIndex()); 16404213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov final int pointerFlag = (1 << pointerId); 16414213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov mInjectedPointersDown &= ~pointerFlag; 16424213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov if (mInjectedPointersDown == 0) { 16434213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov mLastInjectedDownEventTime = 0; 16444213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov } 16454213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov } break; 16464213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov case MotionEvent.ACTION_HOVER_ENTER: 16474213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov case MotionEvent.ACTION_HOVER_MOVE: 16484213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov case MotionEvent.ACTION_HOVER_EXIT: { 1649e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov if (mLastInjectedHoverEvent != null) { 1650e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov mLastInjectedHoverEvent.recycle(); 1651e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov } 1652e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov mLastInjectedHoverEvent = MotionEvent.obtain(event); 16535d043ce8cc2f588fdfb336cc843fb3b07b196f83Svetoslav Ganov if (mLastInjectedHoverEventForClick != null) { 16545d043ce8cc2f588fdfb336cc843fb3b07b196f83Svetoslav Ganov mLastInjectedHoverEventForClick.recycle(); 16555d043ce8cc2f588fdfb336cc843fb3b07b196f83Svetoslav Ganov } 16565d043ce8cc2f588fdfb336cc843fb3b07b196f83Svetoslav Ganov mLastInjectedHoverEventForClick = MotionEvent.obtain(event); 16574213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov } break; 16584213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov } 16594213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov if (DEBUG) { 1660e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov Slog.i(LOG_TAG_INJECTED_POINTER_TRACKER, "Injected pointer:\n" + toString()); 16614213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov } 16624213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov } 16634213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov 16644213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov /** 16654213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov * Clears the internals state. 16664213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov */ 16674213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov public void clear() { 16684213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov mInjectedPointersDown = 0; 16694213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov } 16704213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov 16714213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov /** 16724213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov * @return The time of the last injected down event. 16734213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov */ 16744213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov public long getLastInjectedDownEventTime() { 16754213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov return mLastInjectedDownEventTime; 16764213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov } 16774213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov 16784213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov /** 16794213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov * @return The number of down pointers injected to the view hierarchy. 16804213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov */ 16814213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov public int getInjectedPointerDownCount() { 16824213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov return Integer.bitCount(mInjectedPointersDown); 16834213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov } 16844213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov 16854213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov /** 16864213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov * @return The bits of the injected pointers that are down. 16874213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov */ 16884213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov public int getInjectedPointersDown() { 16894213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov return mInjectedPointersDown; 16904213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov } 16914213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov 16924213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov /** 16934213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov * Whether an injected pointer is down. 16944213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov * 16954213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov * @param pointerId The unique pointer id. 16964213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov * @return True if the pointer is down. 16974213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov */ 16984213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov public boolean isInjectedPointerDown(int pointerId) { 16994213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov final int pointerFlag = (1 << pointerId); 17004213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov return (mInjectedPointersDown & pointerFlag) != 0; 17014213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov } 17024213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov 17034213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov /** 1704e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov * @return The the last injected hover event. 17054213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov */ 1706e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov public MotionEvent getLastInjectedHoverEvent() { 1707e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov return mLastInjectedHoverEvent; 17084213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov } 17094213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov 17105d043ce8cc2f588fdfb336cc843fb3b07b196f83Svetoslav Ganov /** 17115d043ce8cc2f588fdfb336cc843fb3b07b196f83Svetoslav Ganov * @return The the last injected hover event. 17125d043ce8cc2f588fdfb336cc843fb3b07b196f83Svetoslav Ganov */ 17135d043ce8cc2f588fdfb336cc843fb3b07b196f83Svetoslav Ganov public MotionEvent getLastInjectedHoverEventForClick() { 17145d043ce8cc2f588fdfb336cc843fb3b07b196f83Svetoslav Ganov return mLastInjectedHoverEventForClick; 17155d043ce8cc2f588fdfb336cc843fb3b07b196f83Svetoslav Ganov } 17165d043ce8cc2f588fdfb336cc843fb3b07b196f83Svetoslav Ganov 17174213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov @Override 17184213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov public String toString() { 17194213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov StringBuilder builder = new StringBuilder(); 17204213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov builder.append("========================="); 17214213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov builder.append("\nDown pointers #"); 17224213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov builder.append(Integer.bitCount(mInjectedPointersDown)); 17234213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov builder.append(" [ "); 17244213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov for (int i = 0; i < MAX_POINTER_COUNT; i++) { 17254213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov if ((mInjectedPointersDown & i) != 0) { 17264213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov builder.append(i); 17274213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov builder.append(" "); 17284213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov } 17294213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov } 17304213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov builder.append("]"); 17314213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov builder.append("\n========================="); 17324213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov return builder.toString(); 17334213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov } 17344213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov } 17354213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov 17364213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov class ReceivedPointerTracker { 17374213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov private static final String LOG_TAG_RECEIVED_POINTER_TRACKER = "ReceivedPointerTracker"; 1738736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov 1739736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov // The coefficient by which to multiply 1740736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov // ViewConfiguration.#getScaledTouchSlop() 1741736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov // to compute #mThresholdActivePointer. 1742736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov private static final int COEFFICIENT_ACTIVE_POINTER = 2; 1743736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov 1744736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov // Pointers that moved less than mThresholdActivePointer 1745736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov // are considered active i.e. are ignored. 1746736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov private final double mThresholdActivePointer; 1747736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov 1748736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov // Keep track of where and when a pointer went down. 1749736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov private final float[] mReceivedPointerDownX = new float[MAX_POINTER_COUNT]; 1750736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov private final float[] mReceivedPointerDownY = new float[MAX_POINTER_COUNT]; 1751736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov private final long[] mReceivedPointerDownTime = new long[MAX_POINTER_COUNT]; 1752736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov 1753736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov // Which pointers are down. 1754736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov private int mReceivedPointersDown; 1755736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov 1756c4842c11932ea4f60fe7ae09b0a59660207e1587Svetoslav Ganov // The edge flags of the last received down event. 1757c4842c11932ea4f60fe7ae09b0a59660207e1587Svetoslav Ganov private int mLastReceivedDownEdgeFlags; 1758c4842c11932ea4f60fe7ae09b0a59660207e1587Svetoslav Ganov 1759736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov // Which down pointers are active. 1760736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov private int mActivePointers; 1761736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov 1762736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov // Primary active pointer which is either the first that went down 1763736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov // or if it goes up the next active that most recently went down. 1764736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov private int mPrimaryActivePointerId; 1765736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov 1766736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov // Flag indicating that there is at least one active pointer moving. 1767736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov private boolean mHasMovingActivePointer; 1768736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov 1769736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov // Keep track of the last up pointer data. 1770736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov private long mLastReceivedUpPointerDownTime; 1771736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov private int mLastReceivedUpPointerId; 1772736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov private boolean mLastReceivedUpPointerActive; 17734213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov private float mLastReceivedUpPointerDownX; 17744213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov private float mLastReceivedUpPointerDownY; 1775736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov 1776e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov private MotionEvent mLastReceivedEvent; 1777e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov 1778736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov /** 1779736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov * Creates a new instance. 1780736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov * 1781736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov * @param context Context for looking up resources. 1782736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov */ 17834213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov public ReceivedPointerTracker(Context context) { 1784736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov mThresholdActivePointer = 178576c0dd48279531cb31e2a284a270c535664cbf81Svetoslav Ganov ViewConfiguration.get(context).getScaledTouchSlop() * COEFFICIENT_ACTIVE_POINTER; 1786736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov } 1787736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov 1788736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov /** 1789736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov * Clears the internals state. 1790736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov */ 1791736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov public void clear() { 1792736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov Arrays.fill(mReceivedPointerDownX, 0); 1793736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov Arrays.fill(mReceivedPointerDownY, 0); 1794736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov Arrays.fill(mReceivedPointerDownTime, 0); 1795736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov mReceivedPointersDown = 0; 1796736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov mActivePointers = 0; 1797736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov mPrimaryActivePointerId = 0; 1798736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov mHasMovingActivePointer = false; 1799736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov mLastReceivedUpPointerDownTime = 0; 1800736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov mLastReceivedUpPointerId = 0; 1801736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov mLastReceivedUpPointerActive = false; 18024213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov mLastReceivedUpPointerDownX = 0; 18034213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov mLastReceivedUpPointerDownY = 0; 1804736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov } 1805736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov 1806736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov /** 1807736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov * Processes a received {@link MotionEvent} event. 1808736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov * 1809736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov * @param event The event to process. 1810736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov */ 18114213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov public void onMotionEvent(MotionEvent event) { 1812e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov if (mLastReceivedEvent != null) { 1813e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov mLastReceivedEvent.recycle(); 1814e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov } 1815e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov mLastReceivedEvent = MotionEvent.obtain(event); 1816e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov 1817736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov final int action = event.getActionMasked(); 1818736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov switch (action) { 1819736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov case MotionEvent.ACTION_DOWN: { 182000f7b3f76515d1c6fbe5cf9fee9d3760787c03cdSvetoslav Ganov handleReceivedPointerDown(event.getActionIndex(), event); 1821736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov } break; 1822736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov case MotionEvent.ACTION_POINTER_DOWN: { 1823736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov handleReceivedPointerDown(event.getActionIndex(), event); 1824736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov } break; 1825736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov case MotionEvent.ACTION_MOVE: { 1826736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov handleReceivedPointerMove(event); 1827736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov } break; 1828736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov case MotionEvent.ACTION_UP: { 182900f7b3f76515d1c6fbe5cf9fee9d3760787c03cdSvetoslav Ganov handleReceivedPointerUp(event.getActionIndex(), event); 1830736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov } break; 1831736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov case MotionEvent.ACTION_POINTER_UP: { 1832736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov handleReceivedPointerUp(event.getActionIndex(), event); 1833736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov } break; 1834736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov } 1835736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov if (DEBUG) { 18364213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov Slog.i(LOG_TAG_RECEIVED_POINTER_TRACKER, "Received pointer: " + toString()); 1837736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov } 1838736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov } 1839736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov 1840736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov /** 1841e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov * @return The last received event. 1842e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov */ 1843e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov public MotionEvent getLastReceivedEvent() { 1844e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov return mLastReceivedEvent; 1845e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov } 1846e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov 1847e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov /** 18484213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov * @return The number of received pointers that are down. 1849736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov */ 18504213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov public int getReceivedPointerDownCount() { 18514213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov return Integer.bitCount(mReceivedPointersDown); 1852736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov } 1853736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov 1854736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov /** 18554213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov * @return The bits of the pointers that are active. 1856736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov */ 18574213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov public int getActivePointers() { 18584213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov return mActivePointers; 1859736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov } 1860736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov 1861736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov /** 1862736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov * @return The number of down input pointers that are active. 1863736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov */ 1864736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov public int getActivePointerCount() { 1865736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov return Integer.bitCount(mActivePointers); 1866736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov } 1867736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov 1868736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov /** 1869736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov * Whether an received pointer is down. 1870736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov * 1871736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov * @param pointerId The unique pointer id. 1872736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov * @return True if the pointer is down. 1873736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov */ 1874736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov public boolean isReceivedPointerDown(int pointerId) { 1875736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov final int pointerFlag = (1 << pointerId); 1876736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov return (mReceivedPointersDown & pointerFlag) != 0; 1877736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov } 1878736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov 1879736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov /** 1880736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov * Whether an input pointer is active. 1881736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov * 1882736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov * @param pointerId The unique pointer id. 1883736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov * @return True if the pointer is active. 1884736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov */ 1885736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov public boolean isActivePointer(int pointerId) { 1886736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov final int pointerFlag = (1 << pointerId); 1887736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov return (mActivePointers & pointerFlag) != 0; 1888736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov } 1889736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov 1890736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov /** 1891736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov * @param pointerId The unique pointer id. 1892736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov * @return The X coordinate where the pointer went down. 1893736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov */ 1894736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov public float getReceivedPointerDownX(int pointerId) { 1895736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov return mReceivedPointerDownX[pointerId]; 1896736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov } 1897736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov 1898736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov /** 1899736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov * @param pointerId The unique pointer id. 1900736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov * @return The Y coordinate where the pointer went down. 1901736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov */ 1902736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov public float getReceivedPointerDownY(int pointerId) { 1903736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov return mReceivedPointerDownY[pointerId]; 1904736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov } 1905736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov 1906736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov /** 1907736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov * @param pointerId The unique pointer id. 1908736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov * @return The time when the pointer went down. 1909736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov */ 1910736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov public long getReceivedPointerDownTime(int pointerId) { 1911736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov return mReceivedPointerDownTime[pointerId]; 1912736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov } 1913736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov 1914736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov /** 1915736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov * @return The id of the primary pointer. 1916736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov */ 1917736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov public int getPrimaryActivePointerId() { 1918736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov if (mPrimaryActivePointerId == INVALID_POINTER_ID) { 1919736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov mPrimaryActivePointerId = findPrimaryActivePointer(); 1920736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov } 1921736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov return mPrimaryActivePointerId; 1922736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov } 1923736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov 1924736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov /** 1925736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov * @return The time when the last up received pointer went down. 1926736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov */ 1927736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov public long getLastReceivedUpPointerDownTime() { 1928736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov return mLastReceivedUpPointerDownTime; 1929736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov } 1930736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov 1931736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov /** 1932736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov * @return The id of the last received pointer that went up. 1933736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov */ 1934736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov public int getLastReceivedUpPointerId() { 1935736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov return mLastReceivedUpPointerId; 1936736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov } 1937736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov 19384213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov 1939736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov /** 19404213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov * @return The down X of the last received pointer that went up. 1941736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov */ 19424213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov public float getLastReceivedUpPointerDownX() { 19434213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov return mLastReceivedUpPointerDownX; 1944736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov } 1945736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov 1946736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov /** 19474213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov * @return The down Y of the last received pointer that went up. 1948736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov */ 19494213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov public float getLastReceivedUpPointerDownY() { 19504213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov return mLastReceivedUpPointerDownY; 1951736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov } 1952736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov 1953736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov /** 1954c4842c11932ea4f60fe7ae09b0a59660207e1587Svetoslav Ganov * @return The edge flags of the last received down event. 1955c4842c11932ea4f60fe7ae09b0a59660207e1587Svetoslav Ganov */ 1956c4842c11932ea4f60fe7ae09b0a59660207e1587Svetoslav Ganov public int getLastReceivedDownEdgeFlags() { 1957c4842c11932ea4f60fe7ae09b0a59660207e1587Svetoslav Ganov return mLastReceivedDownEdgeFlags; 1958c4842c11932ea4f60fe7ae09b0a59660207e1587Svetoslav Ganov } 1959c4842c11932ea4f60fe7ae09b0a59660207e1587Svetoslav Ganov 1960c4842c11932ea4f60fe7ae09b0a59660207e1587Svetoslav Ganov /** 19614213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov * @return Whether the last received pointer that went up was active. 1962736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov */ 19634213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov public boolean wasLastReceivedUpPointerActive() { 19644213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov return mLastReceivedUpPointerActive; 1965736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov } 1966736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov /** 1967736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov * Populates the active pointer IDs to the given array. 1968736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov * <p> 1969736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov * Note: The client is responsible for providing large enough array. 1970736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov * 1971736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov * @param outPointerIds The array to which to write the active pointers. 1972736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov */ 1973736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov public void populateActivePointerIds(int[] outPointerIds) { 1974736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov int index = 0; 1975736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov for (int idBits = mActivePointers; idBits != 0; ) { 1976736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov final int id = Integer.numberOfTrailingZeros(idBits); 1977736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov idBits &= ~(1 << id); 1978736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov outPointerIds[index] = id; 1979736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov index++; 1980736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov } 1981736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov } 1982736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov 1983736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov /** 1984736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov * @param pointerId The unique pointer id. 1985736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov * @return Whether the pointer is active or was the last active than went up. 1986736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov */ 19874213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov public boolean isActiveOrWasLastActiveUpPointer(int pointerId) { 1988736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov return (isActivePointer(pointerId) 1989736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov || (mLastReceivedUpPointerId == pointerId 1990736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov && mLastReceivedUpPointerActive)); 1991736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov } 1992736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov 1993736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov /** 1994736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov * Handles a received pointer down event. 1995736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov * 1996736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov * @param pointerIndex The index of the pointer that has changed. 1997736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov * @param event The event to be handled. 1998736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov */ 1999736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov private void handleReceivedPointerDown(int pointerIndex, MotionEvent event) { 2000736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov final int pointerId = event.getPointerId(pointerIndex); 2001736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov final int pointerFlag = (1 << pointerId); 2002736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov 2003736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov mLastReceivedUpPointerId = 0; 2004736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov mLastReceivedUpPointerDownTime = 0; 2005736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov mLastReceivedUpPointerActive = false; 20064213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov mLastReceivedUpPointerDownX = 0; 20074213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov mLastReceivedUpPointerDownX = 0; 2008736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov 2009c4842c11932ea4f60fe7ae09b0a59660207e1587Svetoslav Ganov mLastReceivedDownEdgeFlags = event.getEdgeFlags(); 2010c4842c11932ea4f60fe7ae09b0a59660207e1587Svetoslav Ganov 2011736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov mReceivedPointersDown |= pointerFlag; 2012736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov mReceivedPointerDownX[pointerId] = event.getX(pointerIndex); 2013736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov mReceivedPointerDownY[pointerId] = event.getY(pointerIndex); 2014736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov mReceivedPointerDownTime[pointerId] = event.getEventTime(); 2015736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov 2016736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov if (!mHasMovingActivePointer) { 2017736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov // If still no moving active pointers every 2018736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov // down pointer is the only active one. 2019736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov mActivePointers = pointerFlag; 2020736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov mPrimaryActivePointerId = pointerId; 2021736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov } else { 2022736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov // If at least one moving active pointer every 2023736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov // subsequent down pointer is active. 2024736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov mActivePointers |= pointerFlag; 2025736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov } 2026736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov } 2027736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov 2028736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov /** 2029736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov * Handles a received pointer move event. 2030736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov * 2031736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov * @param event The event to be handled. 2032736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov */ 2033736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov private void handleReceivedPointerMove(MotionEvent event) { 2034736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov detectActivePointers(event); 2035736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov } 2036736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov 2037736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov /** 2038736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov * Handles a received pointer up event. 2039736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov * 2040736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov * @param pointerIndex The index of the pointer that has changed. 2041736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov * @param event The event to be handled. 2042736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov */ 2043736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov private void handleReceivedPointerUp(int pointerIndex, MotionEvent event) { 2044736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov final int pointerId = event.getPointerId(pointerIndex); 2045736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov final int pointerFlag = (1 << pointerId); 2046736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov 2047736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov mLastReceivedUpPointerId = pointerId; 2048736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov mLastReceivedUpPointerDownTime = getReceivedPointerDownTime(pointerId); 2049736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov mLastReceivedUpPointerActive = isActivePointer(pointerId); 20504213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov mLastReceivedUpPointerDownX = mReceivedPointerDownX[pointerId]; 20514213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov mLastReceivedUpPointerDownY = mReceivedPointerDownY[pointerId]; 2052736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov 2053736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov mReceivedPointersDown &= ~pointerFlag; 2054736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov mActivePointers &= ~pointerFlag; 2055736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov mReceivedPointerDownX[pointerId] = 0; 2056736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov mReceivedPointerDownY[pointerId] = 0; 2057736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov mReceivedPointerDownTime[pointerId] = 0; 2058736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov 2059736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov if (mActivePointers == 0) { 2060736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov mHasMovingActivePointer = false; 2061736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov } 2062736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov if (mPrimaryActivePointerId == pointerId) { 2063736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov mPrimaryActivePointerId = INVALID_POINTER_ID; 2064736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov } 2065736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov } 2066736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov 2067736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov /** 2068736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov * Detects the active pointers in an event. 2069736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov * 2070736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov * @param event The event to examine. 2071736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov */ 2072736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov private void detectActivePointers(MotionEvent event) { 2073736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov for (int i = 0, count = event.getPointerCount(); i < count; i++) { 2074736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov final int pointerId = event.getPointerId(i); 2075736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov if (mHasMovingActivePointer) { 2076736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov // If already active => nothing to do. 2077736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov if (isActivePointer(pointerId)) { 2078736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov continue; 2079736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov } 2080736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov } 2081736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov // Active pointers are ones that moved more than a given threshold. 2082736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov final float pointerDeltaMove = computePointerDeltaMove(i, event); 2083736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov if (pointerDeltaMove > mThresholdActivePointer) { 2084736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov final int pointerFlag = (1 << pointerId); 2085736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov mActivePointers |= pointerFlag; 2086736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov mHasMovingActivePointer = true; 2087736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov } 2088736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov } 2089736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov } 2090736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov 2091736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov /** 2092736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov * @return The primary active pointer. 2093736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov */ 2094736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov private int findPrimaryActivePointer() { 2095736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov int primaryActivePointerId = INVALID_POINTER_ID; 2096736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov long minDownTime = Long.MAX_VALUE; 2097736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov // Find the active pointer that went down first. 2098736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov for (int i = 0, count = mReceivedPointerDownTime.length; i < count; i++) { 2099736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov if (isActivePointer(i)) { 2100736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov final long downPointerTime = mReceivedPointerDownTime[i]; 2101736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov if (downPointerTime < minDownTime) { 2102736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov minDownTime = downPointerTime; 2103736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov primaryActivePointerId = i; 2104736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov } 2105736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov } 2106736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov } 2107736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov return primaryActivePointerId; 2108736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov } 2109736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov 2110736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov /** 2111736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov * Computes the move for a given action pointer index since the 2112736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov * corresponding pointer went down. 2113736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov * 2114736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov * @param pointerIndex The action pointer index. 2115736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov * @param event The event to examine. 2116736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov * @return The distance the pointer has moved. 2117736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov */ 2118736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov private float computePointerDeltaMove(int pointerIndex, MotionEvent event) { 2119736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov final int pointerId = event.getPointerId(pointerIndex); 2120736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov final float deltaX = event.getX(pointerIndex) - mReceivedPointerDownX[pointerId]; 2121736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov final float deltaY = event.getY(pointerIndex) - mReceivedPointerDownY[pointerId]; 2122736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov return (float) Math.hypot(deltaX, deltaY); 2123736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov } 2124736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov 2125736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov @Override 2126736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov public String toString() { 2127736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov StringBuilder builder = new StringBuilder(); 2128736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov builder.append("========================="); 2129736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov builder.append("\nDown pointers #"); 2130736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov builder.append(getReceivedPointerDownCount()); 2131736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov builder.append(" [ "); 2132736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov for (int i = 0; i < MAX_POINTER_COUNT; i++) { 2133736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov if (isReceivedPointerDown(i)) { 2134736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov builder.append(i); 2135736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov builder.append(" "); 2136736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov } 2137736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov } 2138736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov builder.append("]"); 2139736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov builder.append("\nActive pointers #"); 2140736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov builder.append(getActivePointerCount()); 2141736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov builder.append(" [ "); 2142736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov for (int i = 0; i < MAX_POINTER_COUNT; i++) { 2143736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov if (isActivePointer(i)) { 2144736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov builder.append(i); 2145736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov builder.append(" "); 2146736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov } 2147736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov } 2148736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov builder.append("]"); 2149736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov builder.append("\nPrimary active pointer id [ "); 2150736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov builder.append(getPrimaryActivePointerId()); 2151736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov builder.append(" ]"); 2152736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov builder.append("\n========================="); 2153736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov return builder.toString(); 2154736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov } 2155736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov } 2156736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov} 2157