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