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