TouchExplorer.java revision ded133c446fa9d0d23e6bde19a66fb2ce3980491
1736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov/*
2736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov ** Copyright 2011, The Android Open Source Project
3736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov **
4736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov ** Licensed under the Apache License, Version 2.0 (the "License");
5736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov ** you may not use this file except in compliance with the License.
6736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov ** You may obtain a copy of the License at
7736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov **
8736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov **     http://www.apache.org/licenses/LICENSE-2.0
9736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov **
10736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov ** Unless required by applicable law or agreed to in writing, software
11736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov ** distributed under the License is distributed on an "AS IS" BASIS,
12736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov ** See the License for the specific language governing permissions and
14736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov ** limitations under the License.
15736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov */
16736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov
17736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganovpackage com.android.server.accessibility;
18736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov
19736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganovimport android.content.Context;
204213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganovimport android.gesture.Gesture;
214213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganovimport android.gesture.GestureLibraries;
224213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganovimport android.gesture.GestureLibrary;
234213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganovimport android.gesture.GesturePoint;
24ea6fbc0981564f7bbf4c6fbb63af0175415121ceCasey Burkhardtimport android.gesture.GestureStore;
254213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganovimport android.gesture.GestureStroke;
264213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganovimport android.gesture.Prediction;
277498efdc5e163d6b4a11db941c7d13c169d37284Svet Ganovimport android.graphics.Point;
28e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganovimport android.graphics.Rect;
29736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganovimport android.os.Handler;
30e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganovimport android.os.SystemClock;
31736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganovimport android.util.Slog;
32736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganovimport android.view.MotionEvent;
33e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganovimport android.view.MotionEvent.PointerCoords;
34e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganovimport android.view.MotionEvent.PointerProperties;
354213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganovimport android.view.VelocityTracker;
36f5a07905a3e025f95472a3f8d9935263e49ad6d3Svetoslav Ganovimport android.view.ViewConfiguration;
37f5a07905a3e025f95472a3f8d9935263e49ad6d3Svetoslav Ganovimport android.view.WindowManagerPolicy;
3886783474fdec98a22bc22e224462767eab13e273Svetoslav Ganovimport android.view.accessibility.AccessibilityEvent;
3977276b60851a158ad3e142cb3b091d57ae5ceffbSvetoslav Ganovimport android.view.accessibility.AccessibilityManager;
40736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov
414213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganovimport com.android.internal.R;
42f5a07905a3e025f95472a3f8d9935263e49ad6d3Svetoslav Ganov
434213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganovimport java.util.ArrayList;
44736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganovimport java.util.Arrays;
4584044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslavimport java.util.List;
46736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov
47736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov/**
48736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov * This class is a strategy for performing touch exploration. It
49736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov * transforms the motion event stream by modifying, adding, replacing,
50736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov * and consuming certain events. The interaction model is:
51736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov *
52736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov * <ol>
53e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov *   <li>1. One finger moving slow around performs touch exploration.</li>
54e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov *   <li>2. One finger moving fast around performs gestures.</li>
55e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov *   <li>3. Two close fingers moving in the same direction perform a drag.</li>
56e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov *   <li>4. Multi-finger gestures are delivered to view hierarchy.</li>
5784044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav *   <li>5. Two fingers moving in different directions are considered a multi-finger gesture.</li>
5884044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav *   <li>7. Double tapping clicks on the on the last touch explored location if it was in
59e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov *          a window that does not take focus, otherwise the click is within the accessibility
60e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov *          focused rectangle.</li>
61e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov *   <li>7. Tapping and holding for a while performs a long press in a similar fashion
62e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov *          as the click above.</li>
63736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov * <ol>
64736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov *
65736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov * @hide
66736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov */
671cf70bbf96930662cab0e699d70b62865766ff52Svetoslav Ganovclass TouchExplorer implements EventStreamTransformation {
684213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov
69736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov    private static final boolean DEBUG = false;
70736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov
71736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov    // Tag for logging received events.
724213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov    private static final String LOG_TAG = "TouchExplorer";
73736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov
74736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov    // States this explorer can be in.
75736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov    private static final int STATE_TOUCH_EXPLORING = 0x00000001;
76736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov    private static final int STATE_DRAGGING = 0x00000002;
77736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov    private static final int STATE_DELEGATING = 0x00000004;
784213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov    private static final int STATE_GESTURE_DETECTING = 0x00000005;
79736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov
80ded133c446fa9d0d23e6bde19a66fb2ce3980491Svetoslav    private static final int CLICK_LOCATION_NONE = 0;
81ded133c446fa9d0d23e6bde19a66fb2ce3980491Svetoslav    private static final int CLICK_LOCATION_ACCESSIBILITY_FOCUS = 1;
82ded133c446fa9d0d23e6bde19a66fb2ce3980491Svetoslav    private static final int CLICK_LOCATION_LAST_TOUCH_EXPLORED = 2;
83ded133c446fa9d0d23e6bde19a66fb2ce3980491Svetoslav
84a3315270e0502189a6fa4de20b9db2bf042cfd2aSvetoslav    // The maximum of the cosine between the vectors of two moving
85736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov    // pointers so they can be considered moving in the same direction.
8612a024ca681d877fe16b7e087356f7aff175a218Svetoslav Ganov    private static final float MAX_DRAGGING_ANGLE_COS = 0.525321989f; // cos(pi/4)
87736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov
88f804420d6e37748b75478406e989c69303756980Svetoslav Ganov    // Constant referring to the ids bits of all pointers.
89f804420d6e37748b75478406e989c69303756980Svetoslav Ganov    private static final int ALL_POINTER_ID_BITS = 0xFFFFFFFF;
90736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov
914213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov    // This constant captures the current implementation detail that
924213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov    // pointer IDs are between 0 and 31 inclusive (subject to change).
934213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov    // (See MAX_POINTER_ID in frameworks/base/include/ui/Input.h)
94e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov    private static final int MAX_POINTER_COUNT = 32;
954213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov
964213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov    // Invalid pointer ID.
97e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov    private static final int INVALID_POINTER_ID = -1;
98e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov
99e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov    // The velocity above which we detect gestures.
100e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov    private static final int GESTURE_DETECTION_VELOCITY_DIP = 1000;
101e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov
102e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov    // The minimal distance before we take the middle of the distance between
103e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov    // the two dragging pointers as opposed to use the location of the primary one.
104e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov    private static final int MIN_POINTER_DISTANCE_TO_USE_MIDDLE_LOCATION_DIP = 200;
1054213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov
10695068e5d1bea47091e97955f271c789264994550Svetoslav Ganov    // The timeout after which we are no longer trying to detect a gesture.
10795068e5d1bea47091e97955f271c789264994550Svetoslav Ganov    private static final int EXIT_GESTURE_DETECTION_TIMEOUT = 2000;
10895068e5d1bea47091e97955f271c789264994550Svetoslav Ganov
109e47957a0bbe2164467ff6e7a566b0c9e4689cdc9Svetoslav Ganov    // Timeout before trying to decide what the user is trying to do.
110e47957a0bbe2164467ff6e7a566b0c9e4689cdc9Svetoslav Ganov    private final int mDetermineUserIntentTimeout;
111e47957a0bbe2164467ff6e7a566b0c9e4689cdc9Svetoslav Ganov
112e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov    // Timeout within which we try to detect a tap.
113e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov    private final int mTapTimeout;
114e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov
115e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov    // Timeout within which we try to detect a double tap.
116e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov    private final int mDoubleTapTimeout;
117e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov
118e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov    // Slop between the down and up tap to be a tap.
119e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov    private final int mTouchSlop;
120e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov
121e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov    // Slop between the first and second tap to be a double tap.
122e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov    private final int mDoubleTapSlop;
123736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov
124736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov    // The current state of the touch explorer.
125736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov    private int mCurrentState = STATE_TOUCH_EXPLORING;
126736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov
127736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov    // The ID of the pointer used for dragging.
128736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov    private int mDraggingPointerId;
129736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov
130736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov    // Handler for performing asynchronous operations.
131736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov    private final Handler mHandler;
132736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov
13384044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav    // Command for delayed sending of a hover enter and move event.
13484044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav    private final SendHoverEnterAndMoveDelayed mSendHoverEnterAndMoveDelayed;
135e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov
136e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov    // Command for delayed sending of a hover exit event.
13784044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav    private final SendHoverExitDelayed mSendHoverExitDelayed;
138736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov
139f068fed6c4c3fc2003aec19b6e7e892358179b02Svetoslav Ganov    // Command for delayed sending of touch exploration end events.
140f068fed6c4c3fc2003aec19b6e7e892358179b02Svetoslav Ganov    private final SendAccessibilityEventDelayed mSendTouchExplorationEndDelayed;
141f068fed6c4c3fc2003aec19b6e7e892358179b02Svetoslav Ganov
142f068fed6c4c3fc2003aec19b6e7e892358179b02Svetoslav Ganov    // Command for delayed sending of touch interaction end events.
143f068fed6c4c3fc2003aec19b6e7e892358179b02Svetoslav Ganov    private final SendAccessibilityEventDelayed mSendTouchInteractionEndDelayed;
144fe304b893968887323b93764caafa66ee8ad44deSvetoslav Ganov
145f5a07905a3e025f95472a3f8d9935263e49ad6d3Svetoslav Ganov    // Command for delayed sending of a long press.
146f5a07905a3e025f95472a3f8d9935263e49ad6d3Svetoslav Ganov    private final PerformLongPressDelayed mPerformLongPressDelayed;
147f5a07905a3e025f95472a3f8d9935263e49ad6d3Svetoslav Ganov
14895068e5d1bea47091e97955f271c789264994550Svetoslav Ganov    // Command for exiting gesture detection mode after a timeout.
14995068e5d1bea47091e97955f271c789264994550Svetoslav Ganov    private final ExitGestureDetectionModeDelayed mExitGestureDetectionModeDelayed;
15095068e5d1bea47091e97955f271c789264994550Svetoslav Ganov
151e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov    // Helper to detect and react to double tap in touch explore mode.
152e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov    private final DoubleTapDetector mDoubleTapDetector;
153e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov
154e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov    // The scaled minimal distance before we take the middle of the distance between
155e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov    // the two dragging pointers as opposed to use the location of the primary one.
156e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov    private final int mScaledMinPointerDistanceToUseMiddleLocation;
157e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov
158e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov    // The scaled velocity above which we detect gestures.
159e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov    private final int mScaledGestureDetectionVelocity;
160e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov
1611cf70bbf96930662cab0e699d70b62865766ff52Svetoslav Ganov    // The handler to which to delegate events.
1621cf70bbf96930662cab0e699d70b62865766ff52Svetoslav Ganov    private EventStreamTransformation mNext;
1631cf70bbf96930662cab0e699d70b62865766ff52Svetoslav Ganov
164e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov    // Helper to track gesture velocity.
16545af84a483165f06c04d74baba67f90da29c6ad2Svetoslav Ganov    private final VelocityTracker mVelocityTracker = VelocityTracker.obtain();
1664213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov
167e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov    // Helper class to track received pointers.
1684213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov    private final ReceivedPointerTracker mReceivedPointerTracker;
1694213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov
170e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov    // Helper class to track injected pointers.
1714213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov    private final InjectedPointerTracker mInjectedPointerTracker;
1724213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov
173e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov    // Handle to the accessibility manager service.
174e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov    private final AccessibilityManagerService mAms;
1754213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov
176e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov    // Temporary rectangle to avoid instantiation.
177e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov    private final Rect mTempRect = new Rect();
1784213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov
1797498efdc5e163d6b4a11db941c7d13c169d37284Svet Ganov    // Temporary point to avoid instantiation.
1807498efdc5e163d6b4a11db941c7d13c169d37284Svet Ganov    private final Point mTempPoint = new Point();
1817498efdc5e163d6b4a11db941c7d13c169d37284Svet Ganov
18277276b60851a158ad3e142cb3b091d57ae5ceffbSvetoslav Ganov    // Context in which this explorer operates.
18377276b60851a158ad3e142cb3b091d57ae5ceffbSvetoslav Ganov    private final Context mContext;
18477276b60851a158ad3e142cb3b091d57ae5ceffbSvetoslav Ganov
185e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov    // The X of the previous event.
186e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov    private float mPreviousX;
187e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov
188e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov    // The Y of the previous event.
189e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov    private float mPreviousY;
190e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov
191e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov    // Buffer for storing points for gesture detection.
192e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov    private final ArrayList<GesturePoint> mStrokeBuffer = new ArrayList<GesturePoint>(100);
193e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov
194e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov    // The minimal delta between moves to add a gesture point.
195e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov    private static final int TOUCH_TOLERANCE = 3;
196e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov
197e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov    // The minimal score for accepting a predicted gesture.
198e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov    private static final float MIN_PREDICTION_SCORE = 2.0f;
199e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov
200e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov    // The library for gesture detection.
201e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov    private GestureLibrary mGestureLibrary;
202e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov
203e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov    // The long pressing pointer id if coordinate remapping is needed.
204aeb8d0ed0d98d398a66a092c418f4f2bca8719e0Svetoslav Ganov    private int mLongPressingPointerId = -1;
205e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov
206e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov    // The long pressing pointer X if coordinate remapping is needed.
207e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov    private int mLongPressingPointerDeltaX;
208e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov
209e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov    // The long pressing pointer Y if coordinate remapping is needed.
210e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov    private int mLongPressingPointerDeltaY;
2114213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov
21276716c5a180aa471c6973ca7aa03c7f2da677823Svetoslav Ganov    // The id of the last touch explored window.
21376716c5a180aa471c6973ca7aa03c7f2da677823Svetoslav Ganov    private int mLastTouchedWindowId;
21476716c5a180aa471c6973ca7aa03c7f2da677823Svetoslav Ganov
215f068fed6c4c3fc2003aec19b6e7e892358179b02Svetoslav Ganov    // Whether touch exploration is in progress.
216f068fed6c4c3fc2003aec19b6e7e892358179b02Svetoslav Ganov    private boolean mTouchExplorationInProgress;
21777276b60851a158ad3e142cb3b091d57ae5ceffbSvetoslav Ganov
218736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov    /**
219736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov     * Creates a new instance.
220736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov     *
221736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov     * @param inputFilter The input filter associated with this explorer.
222736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov     * @param context A context handle for accessing resources.
223736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov     */
2241cf70bbf96930662cab0e699d70b62865766ff52Svetoslav Ganov    public TouchExplorer(Context context, AccessibilityManagerService service) {
22577276b60851a158ad3e142cb3b091d57ae5ceffbSvetoslav Ganov        mContext = context;
226e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov        mAms = service;
22784044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav        mReceivedPointerTracker = new ReceivedPointerTracker();
2284213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov        mInjectedPointerTracker = new InjectedPointerTracker();
229e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov        mTapTimeout = ViewConfiguration.getTapTimeout();
23077276b60851a158ad3e142cb3b091d57ae5ceffbSvetoslav Ganov        mDetermineUserIntentTimeout = ViewConfiguration.getDoubleTapTimeout();
231e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov        mDoubleTapTimeout = ViewConfiguration.getDoubleTapTimeout();
232e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov        mTouchSlop = ViewConfiguration.get(context).getScaledTouchSlop();
233e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov        mDoubleTapSlop = ViewConfiguration.get(context).getScaledDoubleTapSlop();
234736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov        mHandler = new Handler(context.getMainLooper());
235f5a07905a3e025f95472a3f8d9935263e49ad6d3Svetoslav Ganov        mPerformLongPressDelayed = new PerformLongPressDelayed();
23695068e5d1bea47091e97955f271c789264994550Svetoslav Ganov        mExitGestureDetectionModeDelayed = new ExitGestureDetectionModeDelayed();
2374213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov        mGestureLibrary = GestureLibraries.fromRawResource(context, R.raw.accessibility_gestures);
238ea6fbc0981564f7bbf4c6fbb63af0175415121ceCasey Burkhardt        mGestureLibrary.setOrientationStyle(8);
239ea6fbc0981564f7bbf4c6fbb63af0175415121ceCasey Burkhardt        mGestureLibrary.setSequenceType(GestureStore.SEQUENCE_SENSITIVE);
2404213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov        mGestureLibrary.load();
24184044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav        mSendHoverEnterAndMoveDelayed = new SendHoverEnterAndMoveDelayed();
24284044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav        mSendHoverExitDelayed = new SendHoverExitDelayed();
243f068fed6c4c3fc2003aec19b6e7e892358179b02Svetoslav Ganov        mSendTouchExplorationEndDelayed = new SendAccessibilityEventDelayed(
244f068fed6c4c3fc2003aec19b6e7e892358179b02Svetoslav Ganov                AccessibilityEvent.TYPE_TOUCH_EXPLORATION_GESTURE_END,
245f068fed6c4c3fc2003aec19b6e7e892358179b02Svetoslav Ganov                mDetermineUserIntentTimeout);
246f068fed6c4c3fc2003aec19b6e7e892358179b02Svetoslav Ganov        mSendTouchInteractionEndDelayed = new SendAccessibilityEventDelayed(
247f068fed6c4c3fc2003aec19b6e7e892358179b02Svetoslav Ganov                AccessibilityEvent.TYPE_TOUCH_INTERACTION_END,
248f068fed6c4c3fc2003aec19b6e7e892358179b02Svetoslav Ganov                mDetermineUserIntentTimeout);
249e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov        mDoubleTapDetector = new DoubleTapDetector();
250e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov        final float density = context.getResources().getDisplayMetrics().density;
251e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov        mScaledMinPointerDistanceToUseMiddleLocation =
252e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov            (int) (MIN_POINTER_DISTANCE_TO_USE_MIDDLE_LOCATION_DIP * density);
253e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov        mScaledGestureDetectionVelocity = (int) (GESTURE_DETECTION_VELOCITY_DIP * density);
254e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov    }
255e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov
256e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov    public void clear() {
257e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov        // If we have not received an event then we are in initial
258e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov        // state. Therefore, there is not need to clean anything.
259e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov        MotionEvent event = mReceivedPointerTracker.getLastReceivedEvent();
260e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov        if (event != null) {
261e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov            clear(mReceivedPointerTracker.getLastReceivedEvent(), WindowManagerPolicy.FLAG_TRUSTED);
262e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov        }
263736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov    }
264736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov
2651cf70bbf96930662cab0e699d70b62865766ff52Svetoslav Ganov    public void onDestroy() {
2661cf70bbf96930662cab0e699d70b62865766ff52Svetoslav Ganov        // TODO: Implement
2671cf70bbf96930662cab0e699d70b62865766ff52Svetoslav Ganov    }
2681cf70bbf96930662cab0e699d70b62865766ff52Svetoslav Ganov
2691cf70bbf96930662cab0e699d70b62865766ff52Svetoslav Ganov    private void clear(MotionEvent event, int policyFlags) {
270e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov        switch (mCurrentState) {
271e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov            case STATE_TOUCH_EXPLORING: {
272e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov                // If a touch exploration gesture is in progress send events for its end.
273f068fed6c4c3fc2003aec19b6e7e892358179b02Svetoslav Ganov                sendHoverExitAndTouchExplorationGestureEndIfNeeded(policyFlags);
274e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov            } break;
275e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov            case STATE_DRAGGING: {
276e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov                mDraggingPointerId = INVALID_POINTER_ID;
277e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov                // Send exit to all pointers that we have delivered.
278e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov                sendUpForInjectedDownPointers(event, policyFlags);
279e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov            } break;
280e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov            case STATE_DELEGATING: {
281e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov                // Send exit to all pointers that we have delivered.
282e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov                sendUpForInjectedDownPointers(event, policyFlags);
283e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov            } break;
284e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov            case STATE_GESTURE_DETECTING: {
285e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov                // Clear the current stroke.
286e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov                mStrokeBuffer.clear();
287e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov            } break;
288e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov        }
289e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov        // Remove all pending callbacks.
29084044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav        mSendHoverEnterAndMoveDelayed.cancel();
29184044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav        mSendHoverExitDelayed.cancel();
29284044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav        mPerformLongPressDelayed.cancel();
29384044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav        mExitGestureDetectionModeDelayed.cancel();
29484044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav        mSendTouchExplorationEndDelayed.cancel();
29584044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav        mSendTouchInteractionEndDelayed.cancel();
296e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov        // Reset the pointer trackers.
297e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov        mReceivedPointerTracker.clear();
298e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov        mInjectedPointerTracker.clear();
299e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov        // Clear the double tap detector
300e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov        mDoubleTapDetector.clear();
301e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov        // Go to initial state.
302e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov        // Clear the long pressing pointer remap data.
303e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov        mLongPressingPointerId = -1;
304e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov        mLongPressingPointerDeltaX = 0;
305e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov        mLongPressingPointerDeltaY = 0;
306e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov        mCurrentState = STATE_TOUCH_EXPLORING;
3071cf70bbf96930662cab0e699d70b62865766ff52Svetoslav Ganov        if (mNext != null) {
3081cf70bbf96930662cab0e699d70b62865766ff52Svetoslav Ganov            mNext.clear();
3091cf70bbf96930662cab0e699d70b62865766ff52Svetoslav Ganov        }
310f068fed6c4c3fc2003aec19b6e7e892358179b02Svetoslav Ganov        mTouchExplorationInProgress = false;
311f772cba59760d1ad9eb5cb9205b2e2e9126e488dSvetoslav Ganov        mAms.onTouchInteractionEnd();
3121cf70bbf96930662cab0e699d70b62865766ff52Svetoslav Ganov    }
3131cf70bbf96930662cab0e699d70b62865766ff52Svetoslav Ganov
3141cf70bbf96930662cab0e699d70b62865766ff52Svetoslav Ganov    @Override
3151cf70bbf96930662cab0e699d70b62865766ff52Svetoslav Ganov    public void setNext(EventStreamTransformation next) {
3161cf70bbf96930662cab0e699d70b62865766ff52Svetoslav Ganov        mNext = next;
317736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov    }
318736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov
3191cf70bbf96930662cab0e699d70b62865766ff52Svetoslav Ganov    @Override
32045af84a483165f06c04d74baba67f90da29c6ad2Svetoslav Ganov    public void onMotionEvent(MotionEvent event, MotionEvent rawEvent, int policyFlags) {
321736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov        if (DEBUG) {
3224213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov            Slog.d(LOG_TAG, "Received event: " + event + ", policyFlags=0x"
323736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov                    + Integer.toHexString(policyFlags));
3244213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov            Slog.d(LOG_TAG, getStateSymbolicName(mCurrentState));
325736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov        }
326736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov
32745af84a483165f06c04d74baba67f90da29c6ad2Svetoslav Ganov        mReceivedPointerTracker.onMotionEvent(rawEvent);
328736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov
329736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov        switch(mCurrentState) {
330736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov            case STATE_TOUCH_EXPLORING: {
33145af84a483165f06c04d74baba67f90da29c6ad2Svetoslav Ganov                handleMotionEventStateTouchExploring(event, rawEvent, policyFlags);
332736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov            } break;
333736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov            case STATE_DRAGGING: {
334736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov                handleMotionEventStateDragging(event, policyFlags);
335736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov            } break;
336736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov            case STATE_DELEGATING: {
337736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov                handleMotionEventStateDelegating(event, policyFlags);
338736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov            } break;
3394213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov            case STATE_GESTURE_DETECTING: {
34045af84a483165f06c04d74baba67f90da29c6ad2Svetoslav Ganov                handleMotionEventGestureDetecting(rawEvent, policyFlags);
3414213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov            } break;
3424213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov            default:
343736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov                throw new IllegalStateException("Illegal state: " + mCurrentState);
344736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov        }
345736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov    }
346736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov
34786783474fdec98a22bc22e224462767eab13e273Svetoslav Ganov    public void onAccessibilityEvent(AccessibilityEvent event) {
34877276b60851a158ad3e142cb3b091d57ae5ceffbSvetoslav Ganov        final int eventType = event.getEventType();
34977276b60851a158ad3e142cb3b091d57ae5ceffbSvetoslav Ganov
35077276b60851a158ad3e142cb3b091d57ae5ceffbSvetoslav Ganov        // The event for gesture end should be strictly after the
35177276b60851a158ad3e142cb3b091d57ae5ceffbSvetoslav Ganov        // last hover exit event.
352f068fed6c4c3fc2003aec19b6e7e892358179b02Svetoslav Ganov        if (mSendTouchExplorationEndDelayed.isPending()
3538b681cb8813454aac8a626bf3d7adaa8beca4d75Svetoslav Ganov                && eventType == AccessibilityEvent.TYPE_VIEW_HOVER_EXIT) {
35484044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav                    mSendTouchExplorationEndDelayed.cancel();
3558b681cb8813454aac8a626bf3d7adaa8beca4d75Svetoslav Ganov            sendAccessibilityEvent(AccessibilityEvent.TYPE_TOUCH_EXPLORATION_GESTURE_END);
35677276b60851a158ad3e142cb3b091d57ae5ceffbSvetoslav Ganov        }
35777276b60851a158ad3e142cb3b091d57ae5ceffbSvetoslav Ganov
35877276b60851a158ad3e142cb3b091d57ae5ceffbSvetoslav Ganov        // The event for touch interaction end should be strictly after the
35977276b60851a158ad3e142cb3b091d57ae5ceffbSvetoslav Ganov        // last hover exit and the touch exploration gesture end events.
360f068fed6c4c3fc2003aec19b6e7e892358179b02Svetoslav Ganov        if (mSendTouchInteractionEndDelayed.isPending()
3618b681cb8813454aac8a626bf3d7adaa8beca4d75Svetoslav Ganov                && eventType == AccessibilityEvent.TYPE_VIEW_HOVER_EXIT) {
36284044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav            mSendTouchInteractionEndDelayed.cancel();
3638b681cb8813454aac8a626bf3d7adaa8beca4d75Svetoslav Ganov            sendAccessibilityEvent(AccessibilityEvent.TYPE_TOUCH_INTERACTION_END);
36477276b60851a158ad3e142cb3b091d57ae5ceffbSvetoslav Ganov        }
36577276b60851a158ad3e142cb3b091d57ae5ceffbSvetoslav Ganov
36686783474fdec98a22bc22e224462767eab13e273Svetoslav Ganov        // If a new window opens or the accessibility focus moves we no longer
36786783474fdec98a22bc22e224462767eab13e273Svetoslav Ganov        // want to click/long press on the last touch explored location.
36886783474fdec98a22bc22e224462767eab13e273Svetoslav Ganov        switch (eventType) {
36986783474fdec98a22bc22e224462767eab13e273Svetoslav Ganov            case AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED:
37086783474fdec98a22bc22e224462767eab13e273Svetoslav Ganov            case AccessibilityEvent.TYPE_VIEW_ACCESSIBILITY_FOCUSED: {
3715d043ce8cc2f588fdfb336cc843fb3b07b196f83Svetoslav Ganov                if (mInjectedPointerTracker.mLastInjectedHoverEventForClick != null) {
3725d043ce8cc2f588fdfb336cc843fb3b07b196f83Svetoslav Ganov                    mInjectedPointerTracker.mLastInjectedHoverEventForClick.recycle();
3735d043ce8cc2f588fdfb336cc843fb3b07b196f83Svetoslav Ganov                    mInjectedPointerTracker.mLastInjectedHoverEventForClick = null;
37486783474fdec98a22bc22e224462767eab13e273Svetoslav Ganov                }
37576716c5a180aa471c6973ca7aa03c7f2da677823Svetoslav Ganov                mLastTouchedWindowId = -1;
37676716c5a180aa471c6973ca7aa03c7f2da677823Svetoslav Ganov            } break;
37776716c5a180aa471c6973ca7aa03c7f2da677823Svetoslav Ganov            case AccessibilityEvent.TYPE_VIEW_HOVER_ENTER:
37876716c5a180aa471c6973ca7aa03c7f2da677823Svetoslav Ganov            case AccessibilityEvent.TYPE_VIEW_HOVER_EXIT: {
37976716c5a180aa471c6973ca7aa03c7f2da677823Svetoslav Ganov                mLastTouchedWindowId = event.getWindowId();
38086783474fdec98a22bc22e224462767eab13e273Svetoslav Ganov            } break;
38186783474fdec98a22bc22e224462767eab13e273Svetoslav Ganov        }
3821cf70bbf96930662cab0e699d70b62865766ff52Svetoslav Ganov        if (mNext != null) {
3831cf70bbf96930662cab0e699d70b62865766ff52Svetoslav Ganov            mNext.onAccessibilityEvent(event);
3841cf70bbf96930662cab0e699d70b62865766ff52Svetoslav Ganov        }
38586783474fdec98a22bc22e224462767eab13e273Svetoslav Ganov    }
38686783474fdec98a22bc22e224462767eab13e273Svetoslav Ganov
387736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov    /**
388736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov     * Handles a motion event in touch exploring state.
389736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov     *
390736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov     * @param event The event to be handled.
39145af84a483165f06c04d74baba67f90da29c6ad2Svetoslav Ganov     * @param rawEvent The raw (unmodified) motion event.
392736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov     * @param policyFlags The policy flags associated with the event.
393736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov     */
39445af84a483165f06c04d74baba67f90da29c6ad2Svetoslav Ganov    private void handleMotionEventStateTouchExploring(MotionEvent event, MotionEvent rawEvent,
39545af84a483165f06c04d74baba67f90da29c6ad2Svetoslav Ganov            int policyFlags) {
3964213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov        ReceivedPointerTracker receivedTracker = mReceivedPointerTracker;
3974213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov
39845af84a483165f06c04d74baba67f90da29c6ad2Svetoslav Ganov        mVelocityTracker.addMovement(rawEvent);
399736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov
400e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov        mDoubleTapDetector.onMotionEvent(event, policyFlags);
401e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov
402736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov        switch (event.getActionMasked()) {
40384044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav            case MotionEvent.ACTION_DOWN: {
4046ae8a24fc045bc7970f2843fa9baf06aff15e22dSvetoslav Ganov                mAms.onTouchInteractionStart();
40584044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav
406e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov                // Pre-feed the motion events to the gesture detector since we
407e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov                // have a distance slop before getting into gesture detection
408e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov                // mode and not using the points within this slop significantly
409e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov                // decreases the quality of gesture recognition.
41045af84a483165f06c04d74baba67f90da29c6ad2Svetoslav Ganov                handleMotionEventGestureDetecting(rawEvent, policyFlags);
41184044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav                sendAccessibilityEvent(AccessibilityEvent.TYPE_TOUCH_INTERACTION_START);
412736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov
41384044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav                // If we still have not notified the user for the last
41484044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav                // touch, we figure out what to do. If were waiting
41584044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav                // we resent the delayed callback and wait again.
41684044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav                mSendHoverEnterAndMoveDelayed.cancel();
41784044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav                mSendHoverExitDelayed.cancel();
41884044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav                mPerformLongPressDelayed.cancel();
419f068fed6c4c3fc2003aec19b6e7e892358179b02Svetoslav Ganov
42084044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav                if (mSendTouchExplorationEndDelayed.isPending()) {
42184044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav                    mSendTouchExplorationEndDelayed.forceSendAndRemove();
42284044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav                }
423f068fed6c4c3fc2003aec19b6e7e892358179b02Svetoslav Ganov
42484044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav                if (mSendTouchInteractionEndDelayed.isPending()) {
42584044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav                    mSendTouchInteractionEndDelayed.forceSendAndRemove();
42684044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav                }
427fe304b893968887323b93764caafa66ee8ad44deSvetoslav Ganov
42884044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav                // If we have the first tap, schedule a long press and break
42984044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav                // since we do not want to schedule hover enter because
43084044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav                // the delayed callback will kick in before the long click.
43184044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav                // This would lead to a state transition resulting in long
43284044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav                // pressing the item below the double taped area which is
43384044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav                // not necessary where accessibility focus is.
43484044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav                if (mDoubleTapDetector.firstTapDetected()) {
43584044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav                    // We got a tap now post a long press action.
43684044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav                    mPerformLongPressDelayed.post(event, policyFlags);
43784044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav                    break;
438736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov                }
43984044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav                if (!mTouchExplorationInProgress) {
44038c992841b5f6bc80359dbf60d31aa7b994160fcSvetoslav Ganov                    if (!mSendHoverEnterAndMoveDelayed.isPending()) {
44138c992841b5f6bc80359dbf60d31aa7b994160fcSvetoslav Ganov                        // Deliver hover enter with a delay to have a chance
44238c992841b5f6bc80359dbf60d31aa7b994160fcSvetoslav Ganov                        // to detect what the user is trying to do.
44338c992841b5f6bc80359dbf60d31aa7b994160fcSvetoslav Ganov                        final int pointerId = receivedTracker.getPrimaryPointerId();
44438c992841b5f6bc80359dbf60d31aa7b994160fcSvetoslav Ganov                        final int pointerIdBits = (1 << pointerId);
44538c992841b5f6bc80359dbf60d31aa7b994160fcSvetoslav Ganov                        mSendHoverEnterAndMoveDelayed.post(event, true, pointerIdBits,
44638c992841b5f6bc80359dbf60d31aa7b994160fcSvetoslav Ganov                                policyFlags);
447a3315270e0502189a6fa4de20b9db2bf042cfd2aSvetoslav                    } else {
448a3315270e0502189a6fa4de20b9db2bf042cfd2aSvetoslav                        // Cache the event until we discern exploration from gesturing.
449a3315270e0502189a6fa4de20b9db2bf042cfd2aSvetoslav                        mSendHoverEnterAndMoveDelayed.addEvent(event);
45038c992841b5f6bc80359dbf60d31aa7b994160fcSvetoslav Ganov                    }
45184044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav                }
45284044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav            } break;
45384044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav            case MotionEvent.ACTION_POINTER_DOWN: {
454a3315270e0502189a6fa4de20b9db2bf042cfd2aSvetoslav                // Another finger down means that if we have not started to deliver
455a3315270e0502189a6fa4de20b9db2bf042cfd2aSvetoslav                // hover events, we will not have to. The code for ACTION_MOVE will
456a3315270e0502189a6fa4de20b9db2bf042cfd2aSvetoslav                // decide what we will actually do next.
457a3315270e0502189a6fa4de20b9db2bf042cfd2aSvetoslav                mSendHoverEnterAndMoveDelayed.cancel();
458a3315270e0502189a6fa4de20b9db2bf042cfd2aSvetoslav                mSendHoverExitDelayed.cancel();
459a3315270e0502189a6fa4de20b9db2bf042cfd2aSvetoslav                mPerformLongPressDelayed.cancel();
460736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov            } break;
461736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov            case MotionEvent.ACTION_MOVE: {
46284044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav                final int pointerId = receivedTracker.getPrimaryPointerId();
46391feae3c5994bd4768cea3507c62c65746adcfa6Svetoslav Ganov                final int pointerIndex = event.findPointerIndex(pointerId);
46491feae3c5994bd4768cea3507c62c65746adcfa6Svetoslav Ganov                final int pointerIdBits = (1 << pointerId);
46584044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav                switch (event.getPointerCount()) {
466736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov                    case 1: {
467e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov                        // We have not started sending events since we try to
468e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov                        // figure out what the user is doing.
46984044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav                        if (mSendHoverEnterAndMoveDelayed.isPending()) {
470e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov                            // Pre-feed the motion events to the gesture detector since we
471e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov                            // have a distance slop before getting into gesture detection
472e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov                            // mode and not using the points within this slop significantly
473e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov                            // decreases the quality of gesture recognition.
47445af84a483165f06c04d74baba67f90da29c6ad2Svetoslav Ganov                            handleMotionEventGestureDetecting(rawEvent, policyFlags);
47584044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav
47684044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav                            // Cache the event until we discern exploration from gesturing.
47784044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav                            mSendHoverEnterAndMoveDelayed.addEvent(event);
47884044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav
47945af84a483165f06c04d74baba67f90da29c6ad2Svetoslav Ganov                            // It is *important* to use the distance traveled by the pointers
48045af84a483165f06c04d74baba67f90da29c6ad2Svetoslav Ganov                            // on the screen which may or may not be magnified.
4814213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov                            final float deltaX = receivedTracker.getReceivedPointerDownX(pointerId)
48245af84a483165f06c04d74baba67f90da29c6ad2Svetoslav Ganov                                - rawEvent.getX(pointerIndex);
4834213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov                            final float deltaY = receivedTracker.getReceivedPointerDownY(pointerId)
48445af84a483165f06c04d74baba67f90da29c6ad2Svetoslav Ganov                                - rawEvent.getY(pointerIndex);
485736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov                            final double moveDelta = Math.hypot(deltaX, deltaY);
486e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov                            // The user has moved enough for us to decide.
487e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov                            if (moveDelta > mDoubleTapSlop) {
488e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov                                // Check whether the user is performing a gesture. We
489e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov                                // detect gestures if the pointer is moving above a
490e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov                                // given velocity.
4914213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov                                mVelocityTracker.computeCurrentVelocity(1000);
4924213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov                                final float maxAbsVelocity = Math.max(
4934213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov                                        Math.abs(mVelocityTracker.getXVelocity(pointerId)),
4944213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov                                        Math.abs(mVelocityTracker.getYVelocity(pointerId)));
495e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov                                if (maxAbsVelocity > mScaledGestureDetectionVelocity) {
496e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov                                    // We have to perform gesture detection, so
497e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov                                    // clear the current state and try to detect.
4984213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov                                    mCurrentState = STATE_GESTURE_DETECTING;
49945af84a483165f06c04d74baba67f90da29c6ad2Svetoslav Ganov                                    mVelocityTracker.clear();
50084044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav                                    mSendHoverEnterAndMoveDelayed.cancel();
50184044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav                                    mSendHoverExitDelayed.cancel();
50284044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav                                    mPerformLongPressDelayed.cancel();
50395068e5d1bea47091e97955f271c789264994550Svetoslav Ganov                                    mExitGestureDetectionModeDelayed.post();
50477276b60851a158ad3e142cb3b091d57ae5ceffbSvetoslav Ganov                                    // Send accessibility event to announce the start
50577276b60851a158ad3e142cb3b091d57ae5ceffbSvetoslav Ganov                                    // of gesture recognition.
50677276b60851a158ad3e142cb3b091d57ae5ceffbSvetoslav Ganov                                    sendAccessibilityEvent(
50777276b60851a158ad3e142cb3b091d57ae5ceffbSvetoslav Ganov                                            AccessibilityEvent.TYPE_GESTURE_DETECTION_START);
508e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov                                } else {
509e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov                                    // We have just decided that the user is touch,
510e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov                                    // exploring so start sending events.
51184044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav                                    mSendHoverEnterAndMoveDelayed.forceSendAndRemove();
51284044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav                                    mSendHoverExitDelayed.cancel();
51384044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav                                    mPerformLongPressDelayed.cancel();
514e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov                                    sendMotionEvent(event, MotionEvent.ACTION_HOVER_MOVE,
51591feae3c5994bd4768cea3507c62c65746adcfa6Svetoslav Ganov                                            pointerIdBits, policyFlags);
51691feae3c5994bd4768cea3507c62c65746adcfa6Svetoslav Ganov                                }
517e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov                                break;
518736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov                            }
519736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov                        } else {
520f068fed6c4c3fc2003aec19b6e7e892358179b02Svetoslav Ganov                            // Cancel the long press if pending and the user
521f068fed6c4c3fc2003aec19b6e7e892358179b02Svetoslav Ganov                            // moved more than the slop.
522f068fed6c4c3fc2003aec19b6e7e892358179b02Svetoslav Ganov                            if (mPerformLongPressDelayed.isPending()) {
523f068fed6c4c3fc2003aec19b6e7e892358179b02Svetoslav Ganov                                final float deltaX =
524f068fed6c4c3fc2003aec19b6e7e892358179b02Svetoslav Ganov                                        receivedTracker.getReceivedPointerDownX(pointerId)
525f068fed6c4c3fc2003aec19b6e7e892358179b02Svetoslav Ganov                                        - rawEvent.getX(pointerIndex);
526f068fed6c4c3fc2003aec19b6e7e892358179b02Svetoslav Ganov                                final float deltaY =
527f068fed6c4c3fc2003aec19b6e7e892358179b02Svetoslav Ganov                                        receivedTracker.getReceivedPointerDownY(pointerId)
528f068fed6c4c3fc2003aec19b6e7e892358179b02Svetoslav Ganov                                        - rawEvent.getY(pointerIndex);
529f068fed6c4c3fc2003aec19b6e7e892358179b02Svetoslav Ganov                                final double moveDelta = Math.hypot(deltaX, deltaY);
530f068fed6c4c3fc2003aec19b6e7e892358179b02Svetoslav Ganov                                // The user has moved enough for us to decide.
531f068fed6c4c3fc2003aec19b6e7e892358179b02Svetoslav Ganov                                if (moveDelta > mTouchSlop) {
53284044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav                                    mPerformLongPressDelayed.cancel();
533f068fed6c4c3fc2003aec19b6e7e892358179b02Svetoslav Ganov                                }
534f068fed6c4c3fc2003aec19b6e7e892358179b02Svetoslav Ganov                            }
535a3315270e0502189a6fa4de20b9db2bf042cfd2aSvetoslav                            if (mTouchExplorationInProgress) {
536a3315270e0502189a6fa4de20b9db2bf042cfd2aSvetoslav                                sendTouchExplorationGestureStartAndHoverEnterIfNeeded(policyFlags);
537a3315270e0502189a6fa4de20b9db2bf042cfd2aSvetoslav                                sendMotionEvent(event, MotionEvent.ACTION_HOVER_MOVE, pointerIdBits,
538a3315270e0502189a6fa4de20b9db2bf042cfd2aSvetoslav                                        policyFlags);
539e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov                            }
540736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov                        }
541e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov                    } break;
542e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov                    case 2: {
543e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov                        // More than one pointer so the user is not touch exploring
544e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov                        // and now we have to decide whether to delegate or drag.
54584044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav                        if (mSendHoverEnterAndMoveDelayed.isPending()) {
546e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov                            // We have not started sending events so cancel
547e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov                            // scheduled sending events.
54884044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav                            mSendHoverEnterAndMoveDelayed.cancel();
54984044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav                            mSendHoverExitDelayed.cancel();
55084044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav                            mPerformLongPressDelayed.cancel();
551e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov                        } else {
55284044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav                            mPerformLongPressDelayed.cancel();
553a3315270e0502189a6fa4de20b9db2bf042cfd2aSvetoslav                            if (mTouchExplorationInProgress) {
554a3315270e0502189a6fa4de20b9db2bf042cfd2aSvetoslav                                // If the user is touch exploring the second pointer may be
555a3315270e0502189a6fa4de20b9db2bf042cfd2aSvetoslav                                // performing a double tap to activate an item without need
556a3315270e0502189a6fa4de20b9db2bf042cfd2aSvetoslav                                // for the user to lift his exploring finger.
557a3315270e0502189a6fa4de20b9db2bf042cfd2aSvetoslav                                // It is *important* to use the distance traveled by the pointers
558a3315270e0502189a6fa4de20b9db2bf042cfd2aSvetoslav                                // on the screen which may or may not be magnified.
559a3315270e0502189a6fa4de20b9db2bf042cfd2aSvetoslav                                final float deltaX = receivedTracker.getReceivedPointerDownX(
560a3315270e0502189a6fa4de20b9db2bf042cfd2aSvetoslav                                        pointerId) - rawEvent.getX(pointerIndex);
561a3315270e0502189a6fa4de20b9db2bf042cfd2aSvetoslav                                final float deltaY = receivedTracker.getReceivedPointerDownY(
562a3315270e0502189a6fa4de20b9db2bf042cfd2aSvetoslav                                        pointerId) - rawEvent.getY(pointerIndex);
563a3315270e0502189a6fa4de20b9db2bf042cfd2aSvetoslav                                final double moveDelta = Math.hypot(deltaX, deltaY);
564a3315270e0502189a6fa4de20b9db2bf042cfd2aSvetoslav                                if (moveDelta < mDoubleTapSlop) {
565a3315270e0502189a6fa4de20b9db2bf042cfd2aSvetoslav                                    break;
566a3315270e0502189a6fa4de20b9db2bf042cfd2aSvetoslav                                }
567a3315270e0502189a6fa4de20b9db2bf042cfd2aSvetoslav                                // We are sending events so send exit and gesture
568a3315270e0502189a6fa4de20b9db2bf042cfd2aSvetoslav                                // end since we transition to another state.
569a3315270e0502189a6fa4de20b9db2bf042cfd2aSvetoslav                                sendHoverExitAndTouchExplorationGestureEndIfNeeded(policyFlags);
570736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov                            }
571736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov                        }
572e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov
573e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov                        // We know that a new state transition is to happen and the
574e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov                        // new state will not be gesture recognition, so clear the
575e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov                        // stashed gesture strokes.
576e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov                        mStrokeBuffer.clear();
577736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov
578736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov                        if (isDraggingGesture(event)) {
579736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov                            // Two pointers moving in the same direction within
580736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov                            // a given distance perform a drag.
58100f7b3f76515d1c6fbe5cf9fee9d3760787c03cdSvetoslav Ganov                            mCurrentState = STATE_DRAGGING;
58291feae3c5994bd4768cea3507c62c65746adcfa6Svetoslav Ganov                            mDraggingPointerId = pointerId;
583c4842c11932ea4f60fe7ae09b0a59660207e1587Svetoslav Ganov                            event.setEdgeFlags(receivedTracker.getLastReceivedDownEdgeFlags());
58491feae3c5994bd4768cea3507c62c65746adcfa6Svetoslav Ganov                            sendMotionEvent(event, MotionEvent.ACTION_DOWN, pointerIdBits,
58591feae3c5994bd4768cea3507c62c65746adcfa6Svetoslav Ganov                                    policyFlags);
586736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov                        } else {
587736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov                            // Two pointers moving arbitrary are delegated to the view hierarchy.
588736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov                            mCurrentState = STATE_DELEGATING;
58984044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav                            sendDownForAllNotInjectedPointers(event, policyFlags);
590736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov                        }
59145af84a483165f06c04d74baba67f90da29c6ad2Svetoslav Ganov                        mVelocityTracker.clear();
592736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov                    } break;
593736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov                    default: {
594e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov                        // More than one pointer so the user is not touch exploring
595e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov                        // and now we have to decide whether to delegate or drag.
59684044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav                        if (mSendHoverEnterAndMoveDelayed.isPending()) {
597e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov                            // We have not started sending events so cancel
598e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov                            // scheduled sending events.
59984044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav                            mSendHoverEnterAndMoveDelayed.cancel();
60084044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav                            mSendHoverExitDelayed.cancel();
60184044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav                            mPerformLongPressDelayed.cancel();
602e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov                        } else {
60384044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav                            mPerformLongPressDelayed.cancel();
604e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov                            // We are sending events so send exit and gesture
605e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov                            // end since we transition to another state.
606f068fed6c4c3fc2003aec19b6e7e892358179b02Svetoslav Ganov                            sendHoverExitAndTouchExplorationGestureEndIfNeeded(policyFlags);
607e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov                        }
608736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov
609736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov                        // More than two pointers are delegated to the view hierarchy.
610736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov                        mCurrentState = STATE_DELEGATING;
61184044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav                        sendDownForAllNotInjectedPointers(event, policyFlags);
61245af84a483165f06c04d74baba67f90da29c6ad2Svetoslav Ganov                        mVelocityTracker.clear();
613736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov                    }
614736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov                }
615736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov            } break;
61684044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav            case MotionEvent.ACTION_UP: {
617f772cba59760d1ad9eb5cb9205b2e2e9126e488dSvetoslav Ganov                mAms.onTouchInteractionEnd();
618e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov                // We know that we do not need the pre-fed gesture points are not
619e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov                // needed anymore since the last pointer just went up.
620e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov                mStrokeBuffer.clear();
62184044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav                final int pointerId = event.getPointerId(event.getActionIndex());
62291feae3c5994bd4768cea3507c62c65746adcfa6Svetoslav Ganov                final int pointerIdBits = (1 << pointerId);
623736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov
62484044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav                mPerformLongPressDelayed.cancel();
62584044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav                mVelocityTracker.clear();
626f5a07905a3e025f95472a3f8d9935263e49ad6d3Svetoslav Ganov
62784044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav                if (mSendHoverEnterAndMoveDelayed.isPending()) {
62884044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav                    // If we have not delivered the enter schedule an exit.
62984044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav                    mSendHoverExitDelayed.post(event, pointerIdBits, policyFlags);
63084044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav                } else {
63184044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav                    // The user is touch exploring so we send events for end.
63284044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav                    sendHoverExitAndTouchExplorationGestureEndIfNeeded(policyFlags);
63384044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav                }
634f068fed6c4c3fc2003aec19b6e7e892358179b02Svetoslav Ganov
63584044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav                if (!mSendTouchInteractionEndDelayed.isPending()) {
63684044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav                    mSendTouchInteractionEndDelayed.post();
637736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov                }
63884044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav
639736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov            } break;
640736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov            case MotionEvent.ACTION_CANCEL: {
641e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov                clear(event, policyFlags);
642736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov            } break;
643736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov        }
644736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov    }
645736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov
646736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov    /**
647736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov     * Handles a motion event in dragging state.
648736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov     *
649736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov     * @param event The event to be handled.
650736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov     * @param policyFlags The policy flags associated with the event.
651736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov     */
652736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov    private void handleMotionEventStateDragging(MotionEvent event, int policyFlags) {
65391feae3c5994bd4768cea3507c62c65746adcfa6Svetoslav Ganov        final int pointerIdBits = (1 << mDraggingPointerId);
654736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov        switch (event.getActionMasked()) {
655736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov            case MotionEvent.ACTION_DOWN: {
656736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov                throw new IllegalStateException("Dragging state can be reached only if two "
657736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov                        + "pointers are already down");
658736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov            }
659736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov            case MotionEvent.ACTION_POINTER_DOWN: {
660736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov                // We are in dragging state so we have two pointers and another one
661736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov                // goes down => delegate the three pointers to the view hierarchy
662736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov                mCurrentState = STATE_DELEGATING;
663ec33d56300aa417efb4a055786d73d1bf23a6a85Svetoslav Ganov                if (mDraggingPointerId != INVALID_POINTER_ID) {
664ec33d56300aa417efb4a055786d73d1bf23a6a85Svetoslav Ganov                    sendMotionEvent(event, MotionEvent.ACTION_UP, pointerIdBits, policyFlags);
665ec33d56300aa417efb4a055786d73d1bf23a6a85Svetoslav Ganov                }
66684044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav                sendDownForAllNotInjectedPointers(event, policyFlags);
667736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov            } break;
668736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov            case MotionEvent.ACTION_MOVE: {
66984044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav                switch (event.getPointerCount()) {
6702e1c66bd53d30d2148afaa4b393b60cd59976d65Svetoslav Ganov                    case 1: {
6712e1c66bd53d30d2148afaa4b393b60cd59976d65Svetoslav Ganov                        // do nothing
6722e1c66bd53d30d2148afaa4b393b60cd59976d65Svetoslav Ganov                    } break;
673736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov                    case 2: {
674736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov                        if (isDraggingGesture(event)) {
67584044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav                            final float firstPtrX = event.getX(0);
67684044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav                            final float firstPtrY = event.getY(0);
67784044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav                            final float secondPtrX = event.getX(1);
67884044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav                            final float secondPtrY = event.getY(1);
679e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov
680e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov                            final float deltaX = firstPtrX - secondPtrX;
681e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov                            final float deltaY = firstPtrY - secondPtrY;
682e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov                            final double distance = Math.hypot(deltaX, deltaY);
683e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov
684e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov                            if (distance > mScaledMinPointerDistanceToUseMiddleLocation) {
685e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov                                event.setLocation(deltaX / 2, deltaY / 2);
686e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov                            }
687e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov
688736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov                            // If still dragging send a drag event.
68991feae3c5994bd4768cea3507c62c65746adcfa6Svetoslav Ganov                            sendMotionEvent(event, MotionEvent.ACTION_MOVE, pointerIdBits,
69091feae3c5994bd4768cea3507c62c65746adcfa6Svetoslav Ganov                                    policyFlags);
691736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov                        } else {
692736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov                            // The two pointers are moving either in different directions or
693736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov                            // no close enough => delegate the gesture to the view hierarchy.
694736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov                            mCurrentState = STATE_DELEGATING;
695736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov                            // Send an event to the end of the drag gesture.
69691feae3c5994bd4768cea3507c62c65746adcfa6Svetoslav Ganov                            sendMotionEvent(event, MotionEvent.ACTION_UP, pointerIdBits,
69791feae3c5994bd4768cea3507c62c65746adcfa6Svetoslav Ganov                                    policyFlags);
69884044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav                            // Deliver all pointers to the view hierarchy.
69984044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav                            sendDownForAllNotInjectedPointers(event, policyFlags);
700736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov                        }
701736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov                    } break;
702736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov                    default: {
703736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov                        mCurrentState = STATE_DELEGATING;
704736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov                        // Send an event to the end of the drag gesture.
70591feae3c5994bd4768cea3507c62c65746adcfa6Svetoslav Ganov                        sendMotionEvent(event, MotionEvent.ACTION_UP, pointerIdBits,
70691feae3c5994bd4768cea3507c62c65746adcfa6Svetoslav Ganov                                policyFlags);
70784044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav                        // Deliver all pointers to the view hierarchy.
70884044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav                        sendDownForAllNotInjectedPointers(event, policyFlags);
709736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov                    }
710736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov                }
711736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov            } break;
712aeb8d0ed0d98d398a66a092c418f4f2bca8719e0Svetoslav Ganov            case MotionEvent.ACTION_POINTER_UP: {
713aeb8d0ed0d98d398a66a092c418f4f2bca8719e0Svetoslav Ganov                 final int pointerId = event.getPointerId(event.getActionIndex());
714aeb8d0ed0d98d398a66a092c418f4f2bca8719e0Svetoslav Ganov                 if (pointerId == mDraggingPointerId) {
715aeb8d0ed0d98d398a66a092c418f4f2bca8719e0Svetoslav Ganov                     mDraggingPointerId = INVALID_POINTER_ID;
716aeb8d0ed0d98d398a66a092c418f4f2bca8719e0Svetoslav Ganov                     // Send an event to the end of the drag gesture.
717aeb8d0ed0d98d398a66a092c418f4f2bca8719e0Svetoslav Ganov                     sendMotionEvent(event, MotionEvent.ACTION_UP, pointerIdBits, policyFlags);
718aeb8d0ed0d98d398a66a092c418f4f2bca8719e0Svetoslav Ganov                 }
719aeb8d0ed0d98d398a66a092c418f4f2bca8719e0Svetoslav Ganov            } break;
7202e1c66bd53d30d2148afaa4b393b60cd59976d65Svetoslav Ganov            case MotionEvent.ACTION_UP: {
721f772cba59760d1ad9eb5cb9205b2e2e9126e488dSvetoslav Ganov                mAms.onTouchInteractionEnd();
72277276b60851a158ad3e142cb3b091d57ae5ceffbSvetoslav Ganov                // Announce the end of a new touch interaction.
72377276b60851a158ad3e142cb3b091d57ae5ceffbSvetoslav Ganov                sendAccessibilityEvent(
72477276b60851a158ad3e142cb3b091d57ae5ceffbSvetoslav Ganov                        AccessibilityEvent.TYPE_TOUCH_INTERACTION_END);
725aeb8d0ed0d98d398a66a092c418f4f2bca8719e0Svetoslav Ganov                final int pointerId = event.getPointerId(event.getActionIndex());
726aeb8d0ed0d98d398a66a092c418f4f2bca8719e0Svetoslav Ganov                if (pointerId == mDraggingPointerId) {
727aeb8d0ed0d98d398a66a092c418f4f2bca8719e0Svetoslav Ganov                    mDraggingPointerId = INVALID_POINTER_ID;
728aeb8d0ed0d98d398a66a092c418f4f2bca8719e0Svetoslav Ganov                    // Send an event to the end of the drag gesture.
729aeb8d0ed0d98d398a66a092c418f4f2bca8719e0Svetoslav Ganov                    sendMotionEvent(event, MotionEvent.ACTION_UP, pointerIdBits, policyFlags);
730aeb8d0ed0d98d398a66a092c418f4f2bca8719e0Svetoslav Ganov                }
7312e1c66bd53d30d2148afaa4b393b60cd59976d65Svetoslav Ganov                mCurrentState = STATE_TOUCH_EXPLORING;
7322e1c66bd53d30d2148afaa4b393b60cd59976d65Svetoslav Ganov            } break;
733736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov            case MotionEvent.ACTION_CANCEL: {
734e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov                clear(event, policyFlags);
735736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov            } break;
736736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov        }
737736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov    }
738736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov
739736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov    /**
740736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov     * Handles a motion event in delegating state.
741736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov     *
742736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov     * @param event The event to be handled.
743736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov     * @param policyFlags The policy flags associated with the event.
744736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov     */
7452e1c66bd53d30d2148afaa4b393b60cd59976d65Svetoslav Ganov    private void handleMotionEventStateDelegating(MotionEvent event, int policyFlags) {
746736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov        switch (event.getActionMasked()) {
747736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov            case MotionEvent.ACTION_DOWN: {
748736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov                throw new IllegalStateException("Delegating state can only be reached if "
749736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov                        + "there is at least one pointer down!");
750736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov            }
75184044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav            case MotionEvent.ACTION_UP: {
752a3315270e0502189a6fa4de20b9db2bf042cfd2aSvetoslav                // Offset the event if we are doing a long press as the
753a3315270e0502189a6fa4de20b9db2bf042cfd2aSvetoslav                // target is not necessarily under the user's finger.
754a3315270e0502189a6fa4de20b9db2bf042cfd2aSvetoslav                if (mLongPressingPointerId >= 0) {
755a3315270e0502189a6fa4de20b9db2bf042cfd2aSvetoslav                    event = offsetEvent(event, - mLongPressingPointerDeltaX,
756a3315270e0502189a6fa4de20b9db2bf042cfd2aSvetoslav                            - mLongPressingPointerDeltaY);
757a3315270e0502189a6fa4de20b9db2bf042cfd2aSvetoslav                    // Clear the long press state.
758a3315270e0502189a6fa4de20b9db2bf042cfd2aSvetoslav                    mLongPressingPointerId = -1;
759a3315270e0502189a6fa4de20b9db2bf042cfd2aSvetoslav                    mLongPressingPointerDeltaX = 0;
760a3315270e0502189a6fa4de20b9db2bf042cfd2aSvetoslav                    mLongPressingPointerDeltaY = 0;
761a3315270e0502189a6fa4de20b9db2bf042cfd2aSvetoslav                }
762a3315270e0502189a6fa4de20b9db2bf042cfd2aSvetoslav
763a3315270e0502189a6fa4de20b9db2bf042cfd2aSvetoslav                // Deliver the event.
764a3315270e0502189a6fa4de20b9db2bf042cfd2aSvetoslav                sendMotionEvent(event, event.getAction(), ALL_POINTER_ID_BITS, policyFlags);
765a3315270e0502189a6fa4de20b9db2bf042cfd2aSvetoslav
76684044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav                // Announce the end of a the touch interaction.
767a3315270e0502189a6fa4de20b9db2bf042cfd2aSvetoslav                mAms.onTouchInteractionEnd();
76884044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav                sendAccessibilityEvent(AccessibilityEvent.TYPE_TOUCH_INTERACTION_END);
769a3315270e0502189a6fa4de20b9db2bf042cfd2aSvetoslav
77084044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav                mCurrentState = STATE_TOUCH_EXPLORING;
771736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov            } break;
772736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov            case MotionEvent.ACTION_CANCEL: {
773e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov                clear(event, policyFlags);
774736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov            } break;
775a3315270e0502189a6fa4de20b9db2bf042cfd2aSvetoslav            default: {
776a3315270e0502189a6fa4de20b9db2bf042cfd2aSvetoslav                // Deliver the event.
777a3315270e0502189a6fa4de20b9db2bf042cfd2aSvetoslav                sendMotionEvent(event, event.getAction(), ALL_POINTER_ID_BITS, policyFlags);
778a3315270e0502189a6fa4de20b9db2bf042cfd2aSvetoslav            }
779736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov        }
780736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov    }
781736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov
7824213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov    private void handleMotionEventGestureDetecting(MotionEvent event, int policyFlags) {
7834213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov        switch (event.getActionMasked()) {
7844213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov            case MotionEvent.ACTION_DOWN: {
7854213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov                final float x = event.getX();
7864213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov                final float y = event.getY();
7874213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov                mPreviousX = x;
7884213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov                mPreviousY = y;
7894213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov                mStrokeBuffer.add(new GesturePoint(x, y, event.getEventTime()));
7904213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov            } break;
7914213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov            case MotionEvent.ACTION_MOVE: {
7924213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov                final float x = event.getX();
7934213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov                final float y = event.getY();
7944213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov                final float dX = Math.abs(x - mPreviousX);
7954213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov                final float dY = Math.abs(y - mPreviousY);
7964213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov                if (dX >= TOUCH_TOLERANCE || dY >= TOUCH_TOLERANCE) {
7974213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov                    mPreviousX = x;
7984213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov                    mPreviousY = y;
7994213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov                    mStrokeBuffer.add(new GesturePoint(x, y, event.getEventTime()));
8004213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov                }
8014213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov            } break;
802e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov            case MotionEvent.ACTION_UP: {
803f772cba59760d1ad9eb5cb9205b2e2e9126e488dSvetoslav Ganov                mAms.onTouchInteractionEnd();
80484044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav                // Announce the end of the gesture recognition.
80584044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav                sendAccessibilityEvent(AccessibilityEvent.TYPE_GESTURE_DETECTION_END);
80684044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav                // Announce the end of a the touch interaction.
80784044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav                sendAccessibilityEvent(AccessibilityEvent.TYPE_TOUCH_INTERACTION_END);
80877276b60851a158ad3e142cb3b091d57ae5ceffbSvetoslav Ganov
8094213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov                float x = event.getX();
8104213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov                float y = event.getY();
8114213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov                mStrokeBuffer.add(new GesturePoint(x, y, event.getEventTime()));
8124213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov
8134213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov                Gesture gesture = new Gesture();
8144213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov                gesture.addStroke(new GestureStroke(mStrokeBuffer));
8154213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov
8164213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov                ArrayList<Prediction> predictions = mGestureLibrary.recognize(gesture);
8174213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov                if (!predictions.isEmpty()) {
8184213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov                    Prediction bestPrediction = predictions.get(0);
8194213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov                    if (bestPrediction.score >= MIN_PREDICTION_SCORE) {
8204213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov                        if (DEBUG) {
8214213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov                            Slog.i(LOG_TAG, "gesture: " + bestPrediction.name + " score: "
8224213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov                                    + bestPrediction.score);
8234213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov                        }
8244213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov                        try {
8254213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov                            final int gestureId = Integer.parseInt(bestPrediction.name);
826e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov                            mAms.onGesture(gestureId);
8274213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov                        } catch (NumberFormatException nfe) {
8284213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov                            Slog.w(LOG_TAG, "Non numeric gesture id:" + bestPrediction.name);
8294213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov                        }
8304213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov                    }
8314213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov                }
8324213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov
8334213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov                mStrokeBuffer.clear();
83484044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav                mExitGestureDetectionModeDelayed.cancel();
8354213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov                mCurrentState = STATE_TOUCH_EXPLORING;
8364213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov            } break;
8374213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov            case MotionEvent.ACTION_CANCEL: {
838e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov                clear(event, policyFlags);
8394213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov            } break;
8404213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov        }
8414213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov    }
8424213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov
843736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov    /**
84477276b60851a158ad3e142cb3b091d57ae5ceffbSvetoslav Ganov     * Sends an accessibility event of the given type.
84577276b60851a158ad3e142cb3b091d57ae5ceffbSvetoslav Ganov     *
84677276b60851a158ad3e142cb3b091d57ae5ceffbSvetoslav Ganov     * @param type The event type.
84777276b60851a158ad3e142cb3b091d57ae5ceffbSvetoslav Ganov     */
84877276b60851a158ad3e142cb3b091d57ae5ceffbSvetoslav Ganov    private void sendAccessibilityEvent(int type) {
84977276b60851a158ad3e142cb3b091d57ae5ceffbSvetoslav Ganov        AccessibilityManager accessibilityManager = AccessibilityManager.getInstance(mContext);
85077276b60851a158ad3e142cb3b091d57ae5ceffbSvetoslav Ganov        if (accessibilityManager.isEnabled()) {
85177276b60851a158ad3e142cb3b091d57ae5ceffbSvetoslav Ganov            AccessibilityEvent event = AccessibilityEvent.obtain(type);
8523d1c5a7236c4709550ca7c0cfa293fc5c974c56bAlan Viverette            event.setWindowId(mAms.getActiveWindowId());
85377276b60851a158ad3e142cb3b091d57ae5ceffbSvetoslav Ganov            accessibilityManager.sendAccessibilityEvent(event);
854f068fed6c4c3fc2003aec19b6e7e892358179b02Svetoslav Ganov            switch (type) {
855f068fed6c4c3fc2003aec19b6e7e892358179b02Svetoslav Ganov                case AccessibilityEvent.TYPE_TOUCH_EXPLORATION_GESTURE_START: {
856f068fed6c4c3fc2003aec19b6e7e892358179b02Svetoslav Ganov                    mTouchExplorationInProgress = true;
857f068fed6c4c3fc2003aec19b6e7e892358179b02Svetoslav Ganov                } break;
858f068fed6c4c3fc2003aec19b6e7e892358179b02Svetoslav Ganov                case AccessibilityEvent.TYPE_TOUCH_EXPLORATION_GESTURE_END: {
859f068fed6c4c3fc2003aec19b6e7e892358179b02Svetoslav Ganov                    mTouchExplorationInProgress = false;
860f068fed6c4c3fc2003aec19b6e7e892358179b02Svetoslav Ganov                } break;
861f068fed6c4c3fc2003aec19b6e7e892358179b02Svetoslav Ganov            }
86277276b60851a158ad3e142cb3b091d57ae5ceffbSvetoslav Ganov        }
86377276b60851a158ad3e142cb3b091d57ae5ceffbSvetoslav Ganov    }
86477276b60851a158ad3e142cb3b091d57ae5ceffbSvetoslav Ganov
86577276b60851a158ad3e142cb3b091d57ae5ceffbSvetoslav Ganov    /**
86684044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav     * Sends down events to the view hierarchy for all pointers which are
867736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov     * not already being delivered i.e. pointers that are not yet injected.
868736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov     *
869736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov     * @param prototype The prototype from which to create the injected events.
870736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov     * @param policyFlags The policy flags associated with the event.
871736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov     */
87284044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav    private void sendDownForAllNotInjectedPointers(MotionEvent prototype, int policyFlags) {
8734213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov        InjectedPointerTracker injectedPointers = mInjectedPointerTracker;
87484044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav
87584044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav        // Inject the injected pointers.
876f804420d6e37748b75478406e989c69303756980Svetoslav Ganov        int pointerIdBits = 0;
877f804420d6e37748b75478406e989c69303756980Svetoslav Ganov        final int pointerCount = prototype.getPointerCount();
878f804420d6e37748b75478406e989c69303756980Svetoslav Ganov        for (int i = 0; i < pointerCount; i++) {
879f804420d6e37748b75478406e989c69303756980Svetoslav Ganov            final int pointerId = prototype.getPointerId(i);
880f804420d6e37748b75478406e989c69303756980Svetoslav Ganov            // Do not send event for already delivered pointers.
88184044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav            if (!injectedPointers.isInjectedPointerDown(pointerId)) {
88284044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav                pointerIdBits |= (1 << pointerId);
88384044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav                final int action = computeInjectionAction(MotionEvent.ACTION_DOWN, i);
88484044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav                sendMotionEvent(prototype, action, pointerIdBits, policyFlags);
885736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov            }
886f804420d6e37748b75478406e989c69303756980Svetoslav Ganov        }
887f804420d6e37748b75478406e989c69303756980Svetoslav Ganov    }
888736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov
889f804420d6e37748b75478406e989c69303756980Svetoslav Ganov    /**
890e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov     * Sends the exit events if needed. Such events are hover exit and touch explore
891e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov     * gesture end.
892e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov     *
893e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov     * @param policyFlags The policy flags associated with the event.
894e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov     */
895f068fed6c4c3fc2003aec19b6e7e892358179b02Svetoslav Ganov    private void sendHoverExitAndTouchExplorationGestureEndIfNeeded(int policyFlags) {
896e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov        MotionEvent event = mInjectedPointerTracker.getLastInjectedHoverEvent();
897e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov        if (event != null && event.getActionMasked() != MotionEvent.ACTION_HOVER_EXIT) {
898e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov            final int pointerIdBits = event.getPointerIdBits();
899f068fed6c4c3fc2003aec19b6e7e892358179b02Svetoslav Ganov            if (!mSendTouchExplorationEndDelayed.isPending()) {
900f068fed6c4c3fc2003aec19b6e7e892358179b02Svetoslav Ganov                mSendTouchExplorationEndDelayed.post();
901fe304b893968887323b93764caafa66ee8ad44deSvetoslav Ganov            }
902e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov            sendMotionEvent(event, MotionEvent.ACTION_HOVER_EXIT, pointerIdBits, policyFlags);
903e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov        }
904e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov    }
905e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov
906e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov    /**
907e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov     * Sends the enter events if needed. Such events are hover enter and touch explore
908e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov     * gesture start.
909f804420d6e37748b75478406e989c69303756980Svetoslav Ganov     *
910f804420d6e37748b75478406e989c69303756980Svetoslav Ganov     * @param policyFlags The policy flags associated with the event.
911f804420d6e37748b75478406e989c69303756980Svetoslav Ganov     */
912f068fed6c4c3fc2003aec19b6e7e892358179b02Svetoslav Ganov    private void sendTouchExplorationGestureStartAndHoverEnterIfNeeded(int policyFlags) {
913e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov        MotionEvent event = mInjectedPointerTracker.getLastInjectedHoverEvent();
914e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov        if (event != null && event.getActionMasked() == MotionEvent.ACTION_HOVER_EXIT) {
915e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov            final int pointerIdBits = event.getPointerIdBits();
916f068fed6c4c3fc2003aec19b6e7e892358179b02Svetoslav Ganov            sendAccessibilityEvent(AccessibilityEvent.TYPE_TOUCH_EXPLORATION_GESTURE_START);
917e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov            sendMotionEvent(event, MotionEvent.ACTION_HOVER_ENTER, pointerIdBits, policyFlags);
918736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov        }
919736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov    }
920736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov
921736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov    /**
92284044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav     * Sends up events to the view hierarchy for all pointers which are
923736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov     * already being delivered i.e. pointers that are injected.
924736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov     *
925736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov     * @param prototype The prototype from which to create the injected events.
926736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov     * @param policyFlags The policy flags associated with the event.
927736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov     */
928736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov    private void sendUpForInjectedDownPointers(MotionEvent prototype, int policyFlags) {
9294213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov        final InjectedPointerTracker injectedTracked = mInjectedPointerTracker;
930f804420d6e37748b75478406e989c69303756980Svetoslav Ganov        int pointerIdBits = 0;
931f804420d6e37748b75478406e989c69303756980Svetoslav Ganov        final int pointerCount = prototype.getPointerCount();
932736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov        for (int i = 0; i < pointerCount; i++) {
933736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov            final int pointerId = prototype.getPointerId(i);
934736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov            // Skip non injected down pointers.
9354213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov            if (!injectedTracked.isInjectedPointerDown(pointerId)) {
936736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov                continue;
937736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov            }
938f804420d6e37748b75478406e989c69303756980Svetoslav Ganov            pointerIdBits |= (1 << pointerId);
939f804420d6e37748b75478406e989c69303756980Svetoslav Ganov            final int action = computeInjectionAction(MotionEvent.ACTION_UP, i);
940f804420d6e37748b75478406e989c69303756980Svetoslav Ganov            sendMotionEvent(prototype, action, pointerIdBits, policyFlags);
941736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov        }
942736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov    }
943736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov
944736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov    /**
945736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov     * Sends an up and down events.
946736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov     *
947736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov     * @param prototype The prototype from which to create the injected events.
948736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov     * @param policyFlags The policy flags associated with the event.
949ded133c446fa9d0d23e6bde19a66fb2ce3980491Svetoslav     * @param targetAccessibilityFocus Whether the event targets the accessibility focus.
950736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov     */
951ded133c446fa9d0d23e6bde19a66fb2ce3980491Svetoslav    private void sendActionDownAndUp(MotionEvent prototype, int policyFlags,
952ded133c446fa9d0d23e6bde19a66fb2ce3980491Svetoslav            boolean targetAccessibilityFocus) {
95384044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav        // Tap with the pointer that last explored.
954bd206d129fdd1777b9f9646a834d7fc342a8941eSvetoslav Ganov        final int pointerId = prototype.getPointerId(prototype.getActionIndex());
955f804420d6e37748b75478406e989c69303756980Svetoslav Ganov        final int pointerIdBits = (1 << pointerId);
956ded133c446fa9d0d23e6bde19a66fb2ce3980491Svetoslav        prototype.setTargetAccessibilityFocus(targetAccessibilityFocus);
957f804420d6e37748b75478406e989c69303756980Svetoslav Ganov        sendMotionEvent(prototype, MotionEvent.ACTION_DOWN, pointerIdBits, policyFlags);
958ded133c446fa9d0d23e6bde19a66fb2ce3980491Svetoslav        prototype.setTargetAccessibilityFocus(targetAccessibilityFocus);
959f804420d6e37748b75478406e989c69303756980Svetoslav Ganov        sendMotionEvent(prototype, MotionEvent.ACTION_UP, pointerIdBits, policyFlags);
960736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov    }
961736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov
962736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov    /**
96391feae3c5994bd4768cea3507c62c65746adcfa6Svetoslav Ganov     * Sends an event.
964736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov     *
96500f7b3f76515d1c6fbe5cf9fee9d3760787c03cdSvetoslav Ganov     * @param prototype The prototype from which to create the injected events.
96691feae3c5994bd4768cea3507c62c65746adcfa6Svetoslav Ganov     * @param action The action of the event.
96791feae3c5994bd4768cea3507c62c65746adcfa6Svetoslav Ganov     * @param pointerIdBits The bits of the pointers to send.
968736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov     * @param policyFlags The policy flags associated with the event.
969736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov     */
97091feae3c5994bd4768cea3507c62c65746adcfa6Svetoslav Ganov    private void sendMotionEvent(MotionEvent prototype, int action, int pointerIdBits,
97191feae3c5994bd4768cea3507c62c65746adcfa6Svetoslav Ganov            int policyFlags) {
972f804420d6e37748b75478406e989c69303756980Svetoslav Ganov        prototype.setAction(action);
973f804420d6e37748b75478406e989c69303756980Svetoslav Ganov
974f804420d6e37748b75478406e989c69303756980Svetoslav Ganov        MotionEvent event = null;
975f804420d6e37748b75478406e989c69303756980Svetoslav Ganov        if (pointerIdBits == ALL_POINTER_ID_BITS) {
976f804420d6e37748b75478406e989c69303756980Svetoslav Ganov            event = prototype;
977f804420d6e37748b75478406e989c69303756980Svetoslav Ganov        } else {
978f804420d6e37748b75478406e989c69303756980Svetoslav Ganov            event = prototype.split(pointerIdBits);
979f804420d6e37748b75478406e989c69303756980Svetoslav Ganov        }
980f804420d6e37748b75478406e989c69303756980Svetoslav Ganov        if (action == MotionEvent.ACTION_DOWN) {
981f804420d6e37748b75478406e989c69303756980Svetoslav Ganov            event.setDownTime(event.getEventTime());
982f804420d6e37748b75478406e989c69303756980Svetoslav Ganov        } else {
9834213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov            event.setDownTime(mInjectedPointerTracker.getLastInjectedDownEventTime());
984f804420d6e37748b75478406e989c69303756980Svetoslav Ganov        }
985f804420d6e37748b75478406e989c69303756980Svetoslav Ganov
986e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov        // If the user is long pressing but the long pressing pointer
987e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov        // was not exactly over the accessibility focused item we need
988e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov        // to remap the location of that pointer so the user does not
989e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov        // have to explicitly touch explore something to be able to
990e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov        // long press it, or even worse to avoid the user long pressing
991e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov        // on the wrong item since click and long press behave differently.
992e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov        if (mLongPressingPointerId >= 0) {
993a3315270e0502189a6fa4de20b9db2bf042cfd2aSvetoslav            event = offsetEvent(event, - mLongPressingPointerDeltaX,
994a3315270e0502189a6fa4de20b9db2bf042cfd2aSvetoslav                    - mLongPressingPointerDeltaY);
995e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov        }
996e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov
997f804420d6e37748b75478406e989c69303756980Svetoslav Ganov        if (DEBUG) {
9984213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov            Slog.d(LOG_TAG, "Injecting event: " + event + ", policyFlags=0x"
999f804420d6e37748b75478406e989c69303756980Svetoslav Ganov                    + Integer.toHexString(policyFlags));
1000f804420d6e37748b75478406e989c69303756980Svetoslav Ganov        }
1001f804420d6e37748b75478406e989c69303756980Svetoslav Ganov
1002f804420d6e37748b75478406e989c69303756980Svetoslav Ganov        // Make sure that the user will see the event.
1003f804420d6e37748b75478406e989c69303756980Svetoslav Ganov        policyFlags |= WindowManagerPolicy.FLAG_PASS_TO_USER;
10041cf70bbf96930662cab0e699d70b62865766ff52Svetoslav Ganov        if (mNext != null) {
100545af84a483165f06c04d74baba67f90da29c6ad2Svetoslav Ganov            // TODO: For now pass null for the raw event since the touch
100645af84a483165f06c04d74baba67f90da29c6ad2Svetoslav Ganov            //       explorer is the last event transformation and it does
100745af84a483165f06c04d74baba67f90da29c6ad2Svetoslav Ganov            //       not care about the raw event.
100845af84a483165f06c04d74baba67f90da29c6ad2Svetoslav Ganov            mNext.onMotionEvent(event, null, policyFlags);
10091cf70bbf96930662cab0e699d70b62865766ff52Svetoslav Ganov        }
1010f804420d6e37748b75478406e989c69303756980Svetoslav Ganov
10114213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov        mInjectedPointerTracker.onMotionEvent(event);
10124213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov
1013f804420d6e37748b75478406e989c69303756980Svetoslav Ganov        if (event != prototype) {
1014f804420d6e37748b75478406e989c69303756980Svetoslav Ganov            event.recycle();
1015f804420d6e37748b75478406e989c69303756980Svetoslav Ganov        }
1016736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov    }
1017736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov
1018736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov    /**
1019a3315270e0502189a6fa4de20b9db2bf042cfd2aSvetoslav     * Offsets all pointers in the given event by adding the specified X and Y
1020a3315270e0502189a6fa4de20b9db2bf042cfd2aSvetoslav     * offsets.
1021a3315270e0502189a6fa4de20b9db2bf042cfd2aSvetoslav     *
1022a3315270e0502189a6fa4de20b9db2bf042cfd2aSvetoslav     * @param event The event to offset.
1023a3315270e0502189a6fa4de20b9db2bf042cfd2aSvetoslav     * @param offsetX The X offset.
1024a3315270e0502189a6fa4de20b9db2bf042cfd2aSvetoslav     * @param offsetY The Y offset.
1025a3315270e0502189a6fa4de20b9db2bf042cfd2aSvetoslav     * @return An event with the offset pointers or the original event if both
1026a3315270e0502189a6fa4de20b9db2bf042cfd2aSvetoslav     *         offsets are zero.
1027a3315270e0502189a6fa4de20b9db2bf042cfd2aSvetoslav     */
1028a3315270e0502189a6fa4de20b9db2bf042cfd2aSvetoslav    private MotionEvent offsetEvent(MotionEvent event, int offsetX, int offsetY) {
1029a3315270e0502189a6fa4de20b9db2bf042cfd2aSvetoslav        if (offsetX == 0 && offsetY == 0) {
1030a3315270e0502189a6fa4de20b9db2bf042cfd2aSvetoslav            return event;
1031a3315270e0502189a6fa4de20b9db2bf042cfd2aSvetoslav        }
1032a3315270e0502189a6fa4de20b9db2bf042cfd2aSvetoslav        final int remappedIndex = event.findPointerIndex(mLongPressingPointerId);
1033a3315270e0502189a6fa4de20b9db2bf042cfd2aSvetoslav        final int pointerCount = event.getPointerCount();
1034a3315270e0502189a6fa4de20b9db2bf042cfd2aSvetoslav        PointerProperties[] props = PointerProperties.createArray(pointerCount);
1035a3315270e0502189a6fa4de20b9db2bf042cfd2aSvetoslav        PointerCoords[] coords = PointerCoords.createArray(pointerCount);
1036a3315270e0502189a6fa4de20b9db2bf042cfd2aSvetoslav        for (int i = 0; i < pointerCount; i++) {
1037a3315270e0502189a6fa4de20b9db2bf042cfd2aSvetoslav            event.getPointerProperties(i, props[i]);
1038a3315270e0502189a6fa4de20b9db2bf042cfd2aSvetoslav            event.getPointerCoords(i, coords[i]);
1039a3315270e0502189a6fa4de20b9db2bf042cfd2aSvetoslav            if (i == remappedIndex) {
1040a3315270e0502189a6fa4de20b9db2bf042cfd2aSvetoslav                coords[i].x += offsetX;
1041a3315270e0502189a6fa4de20b9db2bf042cfd2aSvetoslav                coords[i].y += offsetY;
1042a3315270e0502189a6fa4de20b9db2bf042cfd2aSvetoslav            }
1043a3315270e0502189a6fa4de20b9db2bf042cfd2aSvetoslav        }
1044a3315270e0502189a6fa4de20b9db2bf042cfd2aSvetoslav        return MotionEvent.obtain(event.getDownTime(),
1045a3315270e0502189a6fa4de20b9db2bf042cfd2aSvetoslav                event.getEventTime(), event.getAction(), event.getPointerCount(),
1046a3315270e0502189a6fa4de20b9db2bf042cfd2aSvetoslav                props, coords, event.getMetaState(), event.getButtonState(),
1047a3315270e0502189a6fa4de20b9db2bf042cfd2aSvetoslav                1.0f, 1.0f, event.getDeviceId(), event.getEdgeFlags(),
1048a3315270e0502189a6fa4de20b9db2bf042cfd2aSvetoslav                event.getSource(), event.getFlags());
1049a3315270e0502189a6fa4de20b9db2bf042cfd2aSvetoslav    }
1050a3315270e0502189a6fa4de20b9db2bf042cfd2aSvetoslav
1051a3315270e0502189a6fa4de20b9db2bf042cfd2aSvetoslav    /**
1052736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov     * Computes the action for an injected event based on a masked action
1053736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov     * and a pointer index.
1054736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov     *
1055736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov     * @param actionMasked The masked action.
1056736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov     * @param pointerIndex The index of the pointer which has changed.
1057736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov     * @return The action to be used for injection.
1058736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov     */
1059736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov    private int computeInjectionAction(int actionMasked, int pointerIndex) {
1060736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov        switch (actionMasked) {
1061736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov            case MotionEvent.ACTION_DOWN:
1062736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov            case MotionEvent.ACTION_POINTER_DOWN: {
10634213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov                InjectedPointerTracker injectedTracker = mInjectedPointerTracker;
1064736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov                // Compute the action based on how many down pointers are injected.
10654213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov                if (injectedTracker.getInjectedPointerDownCount() == 0) {
1066736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov                    return MotionEvent.ACTION_DOWN;
1067736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov                } else {
1068736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov                    return (pointerIndex << MotionEvent.ACTION_POINTER_INDEX_SHIFT)
1069736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov                        | MotionEvent.ACTION_POINTER_DOWN;
1070736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov                }
1071736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov            }
1072736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov            case MotionEvent.ACTION_POINTER_UP: {
10734213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov                InjectedPointerTracker injectedTracker = mInjectedPointerTracker;
1074736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov                // Compute the action based on how many down pointers are injected.
10754213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov                if (injectedTracker.getInjectedPointerDownCount() == 1) {
1076736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov                    return MotionEvent.ACTION_UP;
1077736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov                } else {
1078736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov                    return (pointerIndex << MotionEvent.ACTION_POINTER_INDEX_SHIFT)
1079736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov                        | MotionEvent.ACTION_POINTER_UP;
1080736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov                }
1081736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov            }
1082736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov            default:
1083736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov                return actionMasked;
1084736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov        }
1085736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov    }
1086736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov
1087e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov    private class DoubleTapDetector {
1088e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov        private MotionEvent mDownEvent;
1089e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov        private MotionEvent mFirstTapEvent;
1090e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov
1091e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov        public void onMotionEvent(MotionEvent event, int policyFlags) {
10921cf70bbf96930662cab0e699d70b62865766ff52Svetoslav Ganov            final int actionIndex = event.getActionIndex();
1093e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov            final int action = event.getActionMasked();
1094e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov            switch (action) {
1095e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov                case MotionEvent.ACTION_DOWN:
1096e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov                case MotionEvent.ACTION_POINTER_DOWN: {
10971cf70bbf96930662cab0e699d70b62865766ff52Svetoslav Ganov                    if (mFirstTapEvent != null
10981cf70bbf96930662cab0e699d70b62865766ff52Svetoslav Ganov                            && !GestureUtils.isSamePointerContext(mFirstTapEvent, event)) {
1099e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov                        clear();
1100e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov                    }
1101e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov                    mDownEvent = MotionEvent.obtain(event);
1102e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov                } break;
1103e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov                case MotionEvent.ACTION_UP:
1104e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov                case MotionEvent.ACTION_POINTER_UP: {
1105e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov                    if (mDownEvent == null) {
1106e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov                        return;
1107e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov                    }
11081cf70bbf96930662cab0e699d70b62865766ff52Svetoslav Ganov                    if (!GestureUtils.isSamePointerContext(mDownEvent, event)) {
1109e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov                        clear();
1110e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov                        return;
1111e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov                    }
11121cf70bbf96930662cab0e699d70b62865766ff52Svetoslav Ganov                    if (GestureUtils.isTap(mDownEvent, event, mTapTimeout, mTouchSlop,
11131cf70bbf96930662cab0e699d70b62865766ff52Svetoslav Ganov                            actionIndex)) {
11141cf70bbf96930662cab0e699d70b62865766ff52Svetoslav Ganov                        if (mFirstTapEvent == null || GestureUtils.isTimedOut(mFirstTapEvent,
11151cf70bbf96930662cab0e699d70b62865766ff52Svetoslav Ganov                                event, mDoubleTapTimeout)) {
1116e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov                            mFirstTapEvent = MotionEvent.obtain(event);
1117e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov                            mDownEvent.recycle();
1118e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov                            mDownEvent = null;
1119e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov                            return;
1120e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov                        }
11211cf70bbf96930662cab0e699d70b62865766ff52Svetoslav Ganov                        if (GestureUtils.isMultiTap(mFirstTapEvent, event, mDoubleTapTimeout,
11221cf70bbf96930662cab0e699d70b62865766ff52Svetoslav Ganov                                mDoubleTapSlop, actionIndex)) {
1123e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov                            onDoubleTap(event, policyFlags);
1124e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov                            mFirstTapEvent.recycle();
1125e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov                            mFirstTapEvent = null;
1126e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov                            mDownEvent.recycle();
1127e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov                            mDownEvent = null;
1128e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov                            return;
1129e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov                        }
1130e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov                        mFirstTapEvent.recycle();
1131e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov                        mFirstTapEvent = null;
1132e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov                    } else {
1133e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov                        if (mFirstTapEvent != null) {
1134e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov                            mFirstTapEvent.recycle();
1135e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov                            mFirstTapEvent = null;
1136e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov                        }
1137e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov                    }
1138e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov                    mDownEvent.recycle();
1139e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov                    mDownEvent = null;
1140e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov                } break;
1141e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov            }
1142e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov        }
1143e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov
1144e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov        public void onDoubleTap(MotionEvent secondTapUp, int policyFlags) {
1145e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov            // This should never be called when more than two pointers are down.
1146e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov            if (secondTapUp.getPointerCount() > 2) {
1147e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov                return;
1148e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov            }
1149e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov
1150e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov            // Remove pending event deliveries.
115184044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav            mSendHoverEnterAndMoveDelayed.cancel();
115284044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav            mSendHoverExitDelayed.cancel();
115384044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav            mPerformLongPressDelayed.cancel();
1154e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov
1155f068fed6c4c3fc2003aec19b6e7e892358179b02Svetoslav Ganov            if (mSendTouchExplorationEndDelayed.isPending()) {
1156f068fed6c4c3fc2003aec19b6e7e892358179b02Svetoslav Ganov                mSendTouchExplorationEndDelayed.forceSendAndRemove();
1157f068fed6c4c3fc2003aec19b6e7e892358179b02Svetoslav Ganov            }
1158f068fed6c4c3fc2003aec19b6e7e892358179b02Svetoslav Ganov            if (mSendTouchInteractionEndDelayed.isPending()) {
1159f068fed6c4c3fc2003aec19b6e7e892358179b02Svetoslav Ganov                mSendTouchInteractionEndDelayed.forceSendAndRemove();
1160f068fed6c4c3fc2003aec19b6e7e892358179b02Svetoslav Ganov            }
1161e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov
1162e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov            final int pointerId = secondTapUp.getPointerId(secondTapUp.getActionIndex());
1163e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov            final int pointerIndex = secondTapUp.findPointerIndex(pointerId);
116486783474fdec98a22bc22e224462767eab13e273Svetoslav Ganov
116547b9c1524fe20b9ae2b210acdcad64f764df68e0Svetoslav            Point clickLocation = mTempPoint;
1166ded133c446fa9d0d23e6bde19a66fb2ce3980491Svetoslav            final int result = computeClickLocation(clickLocation);
1167ded133c446fa9d0d23e6bde19a66fb2ce3980491Svetoslav            if (result == CLICK_LOCATION_NONE) {
116847b9c1524fe20b9ae2b210acdcad64f764df68e0Svetoslav                return;
1169e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov            }
1170e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov
1171e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov            // Do the click.
1172e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov            PointerProperties[] properties = new PointerProperties[1];
1173e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov            properties[0] = new PointerProperties();
1174e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov            secondTapUp.getPointerProperties(pointerIndex, properties[0]);
1175e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov            PointerCoords[] coords = new PointerCoords[1];
1176e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov            coords[0] = new PointerCoords();
117747b9c1524fe20b9ae2b210acdcad64f764df68e0Svetoslav            coords[0].x = clickLocation.x;
117847b9c1524fe20b9ae2b210acdcad64f764df68e0Svetoslav            coords[0].y = clickLocation.y;
1179e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov            MotionEvent event = MotionEvent.obtain(secondTapUp.getDownTime(),
1180e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov                    secondTapUp.getEventTime(), MotionEvent.ACTION_DOWN, 1, properties,
1181e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov                    coords, 0, 0, 1.0f, 1.0f, secondTapUp.getDeviceId(), 0,
1182e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov                    secondTapUp.getSource(), secondTapUp.getFlags());
1183ded133c446fa9d0d23e6bde19a66fb2ce3980491Svetoslav            final boolean targetAccessibilityFocus = (result == CLICK_LOCATION_ACCESSIBILITY_FOCUS);
1184ded133c446fa9d0d23e6bde19a66fb2ce3980491Svetoslav            sendActionDownAndUp(event, policyFlags, targetAccessibilityFocus);
1185e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov            event.recycle();
1186e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov        }
1187e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov
1188e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov        public void clear() {
1189e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov            if (mDownEvent != null) {
1190e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov                mDownEvent.recycle();
1191e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov                mDownEvent = null;
1192e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov            }
1193e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov            if (mFirstTapEvent != null) {
1194e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov                mFirstTapEvent.recycle();
1195e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov                mFirstTapEvent = null;
1196e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov            }
1197e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov        }
1198e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov
1199e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov        public boolean firstTapDetected() {
1200e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov            return mFirstTapEvent != null
1201e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov                && SystemClock.uptimeMillis() - mFirstTapEvent.getEventTime() < mDoubleTapTimeout;
1202e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov        }
1203e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov    }
1204e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov
1205736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov    /**
1206736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov     * Determines whether a two pointer gesture is a dragging one.
1207736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov     *
1208736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov     * @param event The event with the pointer data.
1209736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov     * @return True if the gesture is a dragging one.
1210736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov     */
1211736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov    private boolean isDraggingGesture(MotionEvent event) {
12124213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov        ReceivedPointerTracker receivedTracker = mReceivedPointerTracker;
1213736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov
121484044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav        final float firstPtrX = event.getX(0);
121584044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav        final float firstPtrY = event.getY(0);
121684044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav        final float secondPtrX = event.getX(1);
121784044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav        final float secondPtrY = event.getY(1);
1218736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov
121984044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav        final float firstPtrDownX = receivedTracker.getReceivedPointerDownX(0);
122084044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav        final float firstPtrDownY = receivedTracker.getReceivedPointerDownY(0);
122184044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav        final float secondPtrDownX = receivedTracker.getReceivedPointerDownX(1);
122284044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav        final float secondPtrDownY = receivedTracker.getReceivedPointerDownY(1);
1223736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov
12241cf70bbf96930662cab0e699d70b62865766ff52Svetoslav Ganov        return GestureUtils.isDraggingGesture(firstPtrDownX, firstPtrDownY, secondPtrDownX,
12251cf70bbf96930662cab0e699d70b62865766ff52Svetoslav Ganov                secondPtrDownY, firstPtrX, firstPtrY, secondPtrX, secondPtrY,
12261cf70bbf96930662cab0e699d70b62865766ff52Svetoslav Ganov                MAX_DRAGGING_ANGLE_COS);
1227736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov    }
1228736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov
1229ded133c446fa9d0d23e6bde19a66fb2ce3980491Svetoslav    private int computeClickLocation(Point outLocation) {
123047b9c1524fe20b9ae2b210acdcad64f764df68e0Svetoslav        MotionEvent lastExploreEvent = mInjectedPointerTracker.getLastInjectedHoverEventForClick();
123147b9c1524fe20b9ae2b210acdcad64f764df68e0Svetoslav        if (lastExploreEvent != null) {
123247b9c1524fe20b9ae2b210acdcad64f764df68e0Svetoslav            final int lastExplorePointerIndex = lastExploreEvent.getActionIndex();
123347b9c1524fe20b9ae2b210acdcad64f764df68e0Svetoslav            outLocation.x = (int) lastExploreEvent.getX(lastExplorePointerIndex);
123447b9c1524fe20b9ae2b210acdcad64f764df68e0Svetoslav            outLocation.y = (int) lastExploreEvent.getY(lastExplorePointerIndex);
123547b9c1524fe20b9ae2b210acdcad64f764df68e0Svetoslav            if (!mAms.accessibilityFocusOnlyInActiveWindow()
123647b9c1524fe20b9ae2b210acdcad64f764df68e0Svetoslav                    || mLastTouchedWindowId == mAms.getActiveWindowId()) {
1237ded133c446fa9d0d23e6bde19a66fb2ce3980491Svetoslav                if (mAms.getAccessibilityFocusClickPointInScreen(outLocation)) {
1238ded133c446fa9d0d23e6bde19a66fb2ce3980491Svetoslav                    return CLICK_LOCATION_ACCESSIBILITY_FOCUS;
1239ded133c446fa9d0d23e6bde19a66fb2ce3980491Svetoslav                } else {
1240ded133c446fa9d0d23e6bde19a66fb2ce3980491Svetoslav                    return CLICK_LOCATION_LAST_TOUCH_EXPLORED;
1241ded133c446fa9d0d23e6bde19a66fb2ce3980491Svetoslav                }
124247b9c1524fe20b9ae2b210acdcad64f764df68e0Svetoslav            }
124347b9c1524fe20b9ae2b210acdcad64f764df68e0Svetoslav        }
124447b9c1524fe20b9ae2b210acdcad64f764df68e0Svetoslav        if (mAms.getAccessibilityFocusClickPointInScreen(outLocation)) {
1245ded133c446fa9d0d23e6bde19a66fb2ce3980491Svetoslav            return CLICK_LOCATION_ACCESSIBILITY_FOCUS;
124647b9c1524fe20b9ae2b210acdcad64f764df68e0Svetoslav        }
1247ded133c446fa9d0d23e6bde19a66fb2ce3980491Svetoslav        return CLICK_LOCATION_NONE;
124847b9c1524fe20b9ae2b210acdcad64f764df68e0Svetoslav    }
124947b9c1524fe20b9ae2b210acdcad64f764df68e0Svetoslav
1250736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov    /**
125151cccf0845b36539d42503495f0689d487712b3aSvetoslav Ganov     * Gets the symbolic name of a state.
125251cccf0845b36539d42503495f0689d487712b3aSvetoslav Ganov     *
125351cccf0845b36539d42503495f0689d487712b3aSvetoslav Ganov     * @param state A state.
125451cccf0845b36539d42503495f0689d487712b3aSvetoslav Ganov     * @return The state symbolic name.
125551cccf0845b36539d42503495f0689d487712b3aSvetoslav Ganov     */
125651cccf0845b36539d42503495f0689d487712b3aSvetoslav Ganov    private static String getStateSymbolicName(int state) {
125751cccf0845b36539d42503495f0689d487712b3aSvetoslav Ganov        switch (state) {
125851cccf0845b36539d42503495f0689d487712b3aSvetoslav Ganov            case STATE_TOUCH_EXPLORING:
125951cccf0845b36539d42503495f0689d487712b3aSvetoslav Ganov                return "STATE_TOUCH_EXPLORING";
126051cccf0845b36539d42503495f0689d487712b3aSvetoslav Ganov            case STATE_DRAGGING:
126151cccf0845b36539d42503495f0689d487712b3aSvetoslav Ganov                return "STATE_DRAGGING";
126251cccf0845b36539d42503495f0689d487712b3aSvetoslav Ganov            case STATE_DELEGATING:
126351cccf0845b36539d42503495f0689d487712b3aSvetoslav Ganov                return "STATE_DELEGATING";
12644213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov            case STATE_GESTURE_DETECTING:
12654213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov                return "STATE_GESTURE_DETECTING";
126651cccf0845b36539d42503495f0689d487712b3aSvetoslav Ganov            default:
126751cccf0845b36539d42503495f0689d487712b3aSvetoslav Ganov                throw new IllegalArgumentException("Unknown state: " + state);
126851cccf0845b36539d42503495f0689d487712b3aSvetoslav Ganov        }
126951cccf0845b36539d42503495f0689d487712b3aSvetoslav Ganov    }
127051cccf0845b36539d42503495f0689d487712b3aSvetoslav Ganov
127151cccf0845b36539d42503495f0689d487712b3aSvetoslav Ganov    /**
127295068e5d1bea47091e97955f271c789264994550Svetoslav Ganov     * Class for delayed exiting from gesture detecting mode.
127395068e5d1bea47091e97955f271c789264994550Svetoslav Ganov     */
127495068e5d1bea47091e97955f271c789264994550Svetoslav Ganov    private final class ExitGestureDetectionModeDelayed implements Runnable {
127595068e5d1bea47091e97955f271c789264994550Svetoslav Ganov
127695068e5d1bea47091e97955f271c789264994550Svetoslav Ganov        public void post() {
127795068e5d1bea47091e97955f271c789264994550Svetoslav Ganov            mHandler.postDelayed(this, EXIT_GESTURE_DETECTION_TIMEOUT);
127895068e5d1bea47091e97955f271c789264994550Svetoslav Ganov        }
127995068e5d1bea47091e97955f271c789264994550Svetoslav Ganov
128084044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav        public void cancel() {
128195068e5d1bea47091e97955f271c789264994550Svetoslav Ganov            mHandler.removeCallbacks(this);
128295068e5d1bea47091e97955f271c789264994550Svetoslav Ganov        }
128395068e5d1bea47091e97955f271c789264994550Svetoslav Ganov
128495068e5d1bea47091e97955f271c789264994550Svetoslav Ganov        @Override
128595068e5d1bea47091e97955f271c789264994550Svetoslav Ganov        public void run() {
1286aed4b6f812674bc60a04470013ca449e5c114fa5Svetoslav Ganov            // Announce the end of gesture recognition.
1287aed4b6f812674bc60a04470013ca449e5c114fa5Svetoslav Ganov            sendAccessibilityEvent(AccessibilityEvent.TYPE_GESTURE_DETECTION_END);
1288aed4b6f812674bc60a04470013ca449e5c114fa5Svetoslav Ganov            // Clearing puts is in touch exploration state with a finger already
1289aed4b6f812674bc60a04470013ca449e5c114fa5Svetoslav Ganov            // down, so announce the transition to exploration state.
1290aed4b6f812674bc60a04470013ca449e5c114fa5Svetoslav Ganov            sendAccessibilityEvent(AccessibilityEvent.TYPE_TOUCH_EXPLORATION_GESTURE_START);
129195068e5d1bea47091e97955f271c789264994550Svetoslav Ganov            clear();
129295068e5d1bea47091e97955f271c789264994550Svetoslav Ganov        }
129395068e5d1bea47091e97955f271c789264994550Svetoslav Ganov    }
129495068e5d1bea47091e97955f271c789264994550Svetoslav Ganov
129595068e5d1bea47091e97955f271c789264994550Svetoslav Ganov    /**
12964213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov     * Class for delayed sending of long press.
12974213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov     */
12984213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov    private final class PerformLongPressDelayed implements Runnable {
12994213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov        private MotionEvent mEvent;
13004213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov        private int mPolicyFlags;
13014213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov
1302e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov        public void post(MotionEvent prototype, int policyFlags) {
13034213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov            mEvent = MotionEvent.obtain(prototype);
13044213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov            mPolicyFlags = policyFlags;
1305e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov            mHandler.postDelayed(this, ViewConfiguration.getLongPressTimeout());
13064213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov        }
13074213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov
130884044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav        public void cancel() {
130984044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav            if (mEvent != null) {
13104213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov                mHandler.removeCallbacks(this);
13114213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov                clear();
13124213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov            }
13134213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov        }
13144213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov
131584044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav        private boolean isPending() {
131684044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav            return mHandler.hasCallbacks(this);
13174213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov        }
13184213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov
13194213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov        @Override
13204213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov        public void run() {
132184044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav            // Pointers should not be zero when running this command.
132284044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav            if (mReceivedPointerTracker.getLastReceivedEvent().getPointerCount() == 0) {
1323ebac1b79c4a355d8cd73b49df059deb00d7aa256Svetoslav Ganov                return;
1324ebac1b79c4a355d8cd73b49df059deb00d7aa256Svetoslav Ganov            }
1325ebac1b79c4a355d8cd73b49df059deb00d7aa256Svetoslav Ganov
1326238099c0dbbdc66b8443552126680ad1c7cab17dSvetoslav Ganov            final int pointerId = mEvent.getPointerId(mEvent.getActionIndex());
1327238099c0dbbdc66b8443552126680ad1c7cab17dSvetoslav Ganov            final int pointerIndex = mEvent.findPointerIndex(pointerId);
1328238099c0dbbdc66b8443552126680ad1c7cab17dSvetoslav Ganov
132947b9c1524fe20b9ae2b210acdcad64f764df68e0Svetoslav            Point clickLocation = mTempPoint;
1330ded133c446fa9d0d23e6bde19a66fb2ce3980491Svetoslav            final int result = computeClickLocation(clickLocation);
1331ded133c446fa9d0d23e6bde19a66fb2ce3980491Svetoslav
1332ded133c446fa9d0d23e6bde19a66fb2ce3980491Svetoslav            if (result == CLICK_LOCATION_NONE) {
133347b9c1524fe20b9ae2b210acdcad64f764df68e0Svetoslav                return;
1334e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov            }
1335238099c0dbbdc66b8443552126680ad1c7cab17dSvetoslav Ganov
133686783474fdec98a22bc22e224462767eab13e273Svetoslav Ganov            mLongPressingPointerId = pointerId;
133747b9c1524fe20b9ae2b210acdcad64f764df68e0Svetoslav            mLongPressingPointerDeltaX = (int) mEvent.getX(pointerIndex) - clickLocation.x;
133847b9c1524fe20b9ae2b210acdcad64f764df68e0Svetoslav            mLongPressingPointerDeltaY = (int) mEvent.getY(pointerIndex) - clickLocation.y;
133986783474fdec98a22bc22e224462767eab13e273Svetoslav Ganov
1340f068fed6c4c3fc2003aec19b6e7e892358179b02Svetoslav Ganov            sendHoverExitAndTouchExplorationGestureEndIfNeeded(mPolicyFlags);
13414213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov
1342e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov            mCurrentState = STATE_DELEGATING;
134384044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav            sendDownForAllNotInjectedPointers(mEvent, mPolicyFlags);
13444213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov            clear();
13454213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov        }
13464213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov
13474213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov        private void clear() {
13484213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov            mEvent.recycle();
13494213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov            mEvent = null;
13504213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov            mPolicyFlags = 0;
13514213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov        }
13524213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov    }
13534213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov
13544213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov    /**
135584044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav     * Class for delayed sending of hover enter and move events.
13564213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov     */
135784044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav    class SendHoverEnterAndMoveDelayed implements Runnable {
135884044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav        private final String LOG_TAG_SEND_HOVER_DELAYED = "SendHoverEnterAndMoveDelayed";
1359e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov
136084044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav        private final List<MotionEvent> mEvents = new ArrayList<MotionEvent>();
1361e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov
13624213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov        private int mPointerIdBits;
13634213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov        private int mPolicyFlags;
13644213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov
136584044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav        public void post(MotionEvent event, boolean touchExplorationInProgress,
136677276b60851a158ad3e142cb3b091d57ae5ceffbSvetoslav Ganov                int pointerIdBits, int policyFlags) {
136784044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav            cancel();
136884044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav            addEvent(event);
13694213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov            mPointerIdBits = pointerIdBits;
13704213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov            mPolicyFlags = policyFlags;
1371e47957a0bbe2164467ff6e7a566b0c9e4689cdc9Svetoslav Ganov            mHandler.postDelayed(this, mDetermineUserIntentTimeout);
1372e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov        }
1373e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov
137484044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav        public void addEvent(MotionEvent event) {
137584044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav            mEvents.add(MotionEvent.obtain(event));
137684044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav        }
137784044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav
137884044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav        public void cancel() {
1379e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov            if (isPending()) {
138084044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav                mHandler.removeCallbacks(this);
138184044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav                clear();
138284044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav            }
138384044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav        }
138484044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav
138584044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav        private boolean isPending() {
138684044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav            return mHandler.hasCallbacks(this);
138784044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav        }
138884044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav
138984044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav        private void clear() {
139084044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav            mPointerIdBits = -1;
139184044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav            mPolicyFlags = 0;
139284044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav            final int eventCount = mEvents.size();
139384044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav            for (int i = eventCount - 1; i >= 0; i--) {
139484044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav                mEvents.remove(i).recycle();
1395e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov            }
1396e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov        }
1397e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov
139884044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav        public void forceSendAndRemove() {
1399e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov            if (isPending()) {
140084044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav                run();
140184044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav                cancel();
1402e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov            }
14034213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov        }
14044213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov
140584044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav        public void run() {
140684044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav            // Send an accessibility event to announce the touch exploration start.
140784044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav            sendAccessibilityEvent(AccessibilityEvent.TYPE_TOUCH_EXPLORATION_GESTURE_START);
140884044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav
140984044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav            if (!mEvents.isEmpty()) {
141084044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav                // Deliver a down event.
141184044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav                sendMotionEvent(mEvents.get(0), MotionEvent.ACTION_HOVER_ENTER,
141284044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav                        mPointerIdBits, mPolicyFlags);
141384044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav                if (DEBUG) {
141484044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav                    Slog.d(LOG_TAG_SEND_HOVER_DELAYED,
141584044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav                            "Injecting motion event: ACTION_HOVER_ENTER");
141684044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav                }
141784044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav
141884044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav                // Deliver move events.
141984044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav                final int eventCount = mEvents.size();
142084044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav                for (int i = 1; i < eventCount; i++) {
142184044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav                    sendMotionEvent(mEvents.get(i), MotionEvent.ACTION_HOVER_MOVE,
142284044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav                            mPointerIdBits, mPolicyFlags);
142384044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav                    if (DEBUG) {
142484044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav                        Slog.d(LOG_TAG_SEND_HOVER_DELAYED,
142584044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav                                "Injecting motion event: ACTION_HOVER_MOVE");
142684044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav                    }
142784044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav                }
142884044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav            }
14294213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov            clear();
14304213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov        }
143184044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav    }
143284044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav
143384044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav    /**
143484044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav     * Class for delayed sending of hover exit events.
143584044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav     */
143684044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav    class SendHoverExitDelayed implements Runnable {
143784044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav        private final String LOG_TAG_SEND_HOVER_DELAYED = "SendHoverExitDelayed";
143884044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav
143984044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav        private MotionEvent mPrototype;
144084044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav        private int mPointerIdBits;
144184044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav        private int mPolicyFlags;
144284044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav
144384044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav        public void post(MotionEvent prototype, int pointerIdBits, int policyFlags) {
144484044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav            cancel();
144584044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav            mPrototype = MotionEvent.obtain(prototype);
144684044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav            mPointerIdBits = pointerIdBits;
144784044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav            mPolicyFlags = policyFlags;
144884044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav            mHandler.postDelayed(this, mDetermineUserIntentTimeout);
144984044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav        }
145084044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav
145184044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav        public void cancel() {
145284044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav            if (isPending()) {
145384044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav                mHandler.removeCallbacks(this);
145484044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav                clear();
145584044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav            }
145684044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav        }
14574213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov
1458e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov        private boolean isPending() {
145984044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav            return mHandler.hasCallbacks(this);
14604213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov        }
14614213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov
14624213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov        private void clear() {
1463e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov            mPrototype.recycle();
1464e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov            mPrototype = null;
14654213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov            mPointerIdBits = -1;
14664213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov            mPolicyFlags = 0;
14674213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov        }
14684213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov
14694213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov        public void forceSendAndRemove() {
1470e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov            if (isPending()) {
14714213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov                run();
147284044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav                cancel();
14734213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov            }
14744213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov        }
14754213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov
14764213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov        public void run() {
14774213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov            if (DEBUG) {
147884044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav                Slog.d(LOG_TAG_SEND_HOVER_DELAYED, "Injecting motion event:"
147984044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav                        + " ACTION_HOVER_EXIT");
14804213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov            }
148184044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav            sendMotionEvent(mPrototype, MotionEvent.ACTION_HOVER_EXIT,
148284044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav                    mPointerIdBits, mPolicyFlags);
148384044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav            if (!mSendTouchExplorationEndDelayed.isPending()) {
148484044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav                mSendTouchExplorationEndDelayed.cancel();
148584044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav                mSendTouchExplorationEndDelayed.post();
148684044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav            }
148784044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav            if (mSendTouchInteractionEndDelayed.isPending()) {
148884044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav                  mSendTouchInteractionEndDelayed.cancel();
148984044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav                mSendTouchInteractionEndDelayed.post();
1490e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov            }
14914213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov            clear();
14924213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov        }
14934213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov    }
14944213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov
1495f068fed6c4c3fc2003aec19b6e7e892358179b02Svetoslav Ganov    private class SendAccessibilityEventDelayed implements Runnable {
1496f068fed6c4c3fc2003aec19b6e7e892358179b02Svetoslav Ganov        private final int mEventType;
1497f068fed6c4c3fc2003aec19b6e7e892358179b02Svetoslav Ganov        private final int mDelay;
1498f068fed6c4c3fc2003aec19b6e7e892358179b02Svetoslav Ganov
1499f068fed6c4c3fc2003aec19b6e7e892358179b02Svetoslav Ganov        public SendAccessibilityEventDelayed(int eventType, int delay) {
1500f068fed6c4c3fc2003aec19b6e7e892358179b02Svetoslav Ganov            mEventType = eventType;
1501f068fed6c4c3fc2003aec19b6e7e892358179b02Svetoslav Ganov            mDelay = delay;
1502f068fed6c4c3fc2003aec19b6e7e892358179b02Svetoslav Ganov        }
1503fe304b893968887323b93764caafa66ee8ad44deSvetoslav Ganov
150484044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav        public void cancel() {
1505fe304b893968887323b93764caafa66ee8ad44deSvetoslav Ganov            mHandler.removeCallbacks(this);
1506fe304b893968887323b93764caafa66ee8ad44deSvetoslav Ganov        }
1507fe304b893968887323b93764caafa66ee8ad44deSvetoslav Ganov
1508fe304b893968887323b93764caafa66ee8ad44deSvetoslav Ganov        public void post() {
1509f068fed6c4c3fc2003aec19b6e7e892358179b02Svetoslav Ganov            mHandler.postDelayed(this, mDelay);
1510fe304b893968887323b93764caafa66ee8ad44deSvetoslav Ganov        }
1511fe304b893968887323b93764caafa66ee8ad44deSvetoslav Ganov
1512fe304b893968887323b93764caafa66ee8ad44deSvetoslav Ganov        public boolean isPending() {
1513fe304b893968887323b93764caafa66ee8ad44deSvetoslav Ganov            return mHandler.hasCallbacks(this);
1514fe304b893968887323b93764caafa66ee8ad44deSvetoslav Ganov        }
1515fe304b893968887323b93764caafa66ee8ad44deSvetoslav Ganov
1516fe304b893968887323b93764caafa66ee8ad44deSvetoslav Ganov        public void forceSendAndRemove() {
1517fe304b893968887323b93764caafa66ee8ad44deSvetoslav Ganov            if (isPending()) {
1518fe304b893968887323b93764caafa66ee8ad44deSvetoslav Ganov                run();
151984044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav                cancel();
1520fe304b893968887323b93764caafa66ee8ad44deSvetoslav Ganov            }
1521fe304b893968887323b93764caafa66ee8ad44deSvetoslav Ganov        }
1522fe304b893968887323b93764caafa66ee8ad44deSvetoslav Ganov
1523fe304b893968887323b93764caafa66ee8ad44deSvetoslav Ganov        @Override
1524fe304b893968887323b93764caafa66ee8ad44deSvetoslav Ganov        public void run() {
1525f068fed6c4c3fc2003aec19b6e7e892358179b02Svetoslav Ganov            sendAccessibilityEvent(mEventType);
1526fe304b893968887323b93764caafa66ee8ad44deSvetoslav Ganov        }
1527fe304b893968887323b93764caafa66ee8ad44deSvetoslav Ganov    }
1528fe304b893968887323b93764caafa66ee8ad44deSvetoslav Ganov
15294213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov    @Override
15304213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov    public String toString() {
15314213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov        return LOG_TAG;
15324213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov    }
15334213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov
15344213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov    class InjectedPointerTracker {
15354213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov        private static final String LOG_TAG_INJECTED_POINTER_TRACKER = "InjectedPointerTracker";
15364213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov
15374213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov        // Keep track of which pointers sent to the system are down.
15384213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov        private int mInjectedPointersDown;
15394213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov
15404213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov        // The time of the last injected down.
15414213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov        private long mLastInjectedDownEventTime;
15424213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov
1543e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov        // The last injected hover event.
1544e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov        private MotionEvent mLastInjectedHoverEvent;
15454213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov
15465d043ce8cc2f588fdfb336cc843fb3b07b196f83Svetoslav Ganov        // The last injected hover event used for performing clicks.
15475d043ce8cc2f588fdfb336cc843fb3b07b196f83Svetoslav Ganov        private MotionEvent mLastInjectedHoverEventForClick;
15485d043ce8cc2f588fdfb336cc843fb3b07b196f83Svetoslav Ganov
15494213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov        /**
15504213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov         * Processes an injected {@link MotionEvent} event.
15514213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov         *
15524213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov         * @param event The event to process.
15534213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov         */
15544213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov        public void onMotionEvent(MotionEvent event) {
15554213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov            final int action = event.getActionMasked();
15564213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov            switch (action) {
15574213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov                case MotionEvent.ACTION_DOWN:
15584213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov                case MotionEvent.ACTION_POINTER_DOWN: {
15594213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov                    final int pointerId = event.getPointerId(event.getActionIndex());
15604213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov                    final int pointerFlag = (1 << pointerId);
15614213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov                    mInjectedPointersDown |= pointerFlag;
15624213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov                    mLastInjectedDownEventTime = event.getDownTime();
15634213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov                } break;
15644213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov                case MotionEvent.ACTION_UP:
15654213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov                case MotionEvent.ACTION_POINTER_UP: {
15664213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov                    final int pointerId = event.getPointerId(event.getActionIndex());
15674213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov                    final int pointerFlag = (1 << pointerId);
15684213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov                    mInjectedPointersDown &= ~pointerFlag;
15694213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov                    if (mInjectedPointersDown == 0) {
15704213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov                        mLastInjectedDownEventTime = 0;
15714213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov                    }
15724213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov                } break;
15734213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov                case MotionEvent.ACTION_HOVER_ENTER:
15744213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov                case MotionEvent.ACTION_HOVER_MOVE:
15754213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov                case MotionEvent.ACTION_HOVER_EXIT: {
1576e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov                    if (mLastInjectedHoverEvent != null) {
1577e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov                        mLastInjectedHoverEvent.recycle();
1578e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov                    }
1579e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov                    mLastInjectedHoverEvent = MotionEvent.obtain(event);
15805d043ce8cc2f588fdfb336cc843fb3b07b196f83Svetoslav Ganov                    if (mLastInjectedHoverEventForClick != null) {
15815d043ce8cc2f588fdfb336cc843fb3b07b196f83Svetoslav Ganov                        mLastInjectedHoverEventForClick.recycle();
15825d043ce8cc2f588fdfb336cc843fb3b07b196f83Svetoslav Ganov                    }
15835d043ce8cc2f588fdfb336cc843fb3b07b196f83Svetoslav Ganov                    mLastInjectedHoverEventForClick = MotionEvent.obtain(event);
15844213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov                } break;
15854213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov            }
15864213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov            if (DEBUG) {
1587e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov                Slog.i(LOG_TAG_INJECTED_POINTER_TRACKER, "Injected pointer:\n" + toString());
15884213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov            }
15894213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov        }
15904213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov
15914213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov        /**
15924213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov         * Clears the internals state.
15934213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov         */
15944213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov        public void clear() {
15954213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov            mInjectedPointersDown = 0;
15964213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov        }
15974213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov
15984213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov        /**
15994213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov         * @return The time of the last injected down event.
16004213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov         */
16014213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov        public long getLastInjectedDownEventTime() {
16024213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov            return mLastInjectedDownEventTime;
16034213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov        }
16044213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov
16054213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov        /**
16064213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov         * @return The number of down pointers injected to the view hierarchy.
16074213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov         */
16084213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov        public int getInjectedPointerDownCount() {
16094213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov            return Integer.bitCount(mInjectedPointersDown);
16104213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov        }
16114213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov
16124213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov        /**
16134213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov         * @return The bits of the injected pointers that are down.
16144213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov         */
16154213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov        public int getInjectedPointersDown() {
16164213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov            return mInjectedPointersDown;
16174213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov        }
16184213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov
16194213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov        /**
16204213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov         * Whether an injected pointer is down.
16214213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov         *
16224213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov         * @param pointerId The unique pointer id.
16234213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov         * @return True if the pointer is down.
16244213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov         */
16254213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov        public boolean isInjectedPointerDown(int pointerId) {
16264213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov            final int pointerFlag = (1 << pointerId);
16274213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov            return (mInjectedPointersDown & pointerFlag) != 0;
16284213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov        }
16294213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov
16304213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov        /**
1631e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov         * @return The the last injected hover event.
16324213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov         */
1633e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov        public MotionEvent getLastInjectedHoverEvent() {
1634e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov            return mLastInjectedHoverEvent;
16354213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov        }
16364213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov
16375d043ce8cc2f588fdfb336cc843fb3b07b196f83Svetoslav Ganov        /**
16385d043ce8cc2f588fdfb336cc843fb3b07b196f83Svetoslav Ganov         * @return The the last injected hover event.
16395d043ce8cc2f588fdfb336cc843fb3b07b196f83Svetoslav Ganov         */
16405d043ce8cc2f588fdfb336cc843fb3b07b196f83Svetoslav Ganov        public MotionEvent getLastInjectedHoverEventForClick() {
16415d043ce8cc2f588fdfb336cc843fb3b07b196f83Svetoslav Ganov            return mLastInjectedHoverEventForClick;
16425d043ce8cc2f588fdfb336cc843fb3b07b196f83Svetoslav Ganov        }
16435d043ce8cc2f588fdfb336cc843fb3b07b196f83Svetoslav Ganov
16444213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov        @Override
16454213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov        public String toString() {
16464213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov            StringBuilder builder = new StringBuilder();
16474213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov            builder.append("=========================");
16484213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov            builder.append("\nDown pointers #");
16494213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov            builder.append(Integer.bitCount(mInjectedPointersDown));
16504213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov            builder.append(" [ ");
16514213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov            for (int i = 0; i < MAX_POINTER_COUNT; i++) {
16524213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov                if ((mInjectedPointersDown & i) != 0) {
16534213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov                    builder.append(i);
16544213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov                    builder.append(" ");
16554213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov                }
16564213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov            }
16574213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov            builder.append("]");
16584213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov            builder.append("\n=========================");
16594213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov            return builder.toString();
16604213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov        }
16614213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov    }
16624213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov
16634213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov    class ReceivedPointerTracker {
16644213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov        private static final String LOG_TAG_RECEIVED_POINTER_TRACKER = "ReceivedPointerTracker";
1665736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov
1666736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov        // Keep track of where and when a pointer went down.
1667736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov        private final float[] mReceivedPointerDownX = new float[MAX_POINTER_COUNT];
1668736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov        private final float[] mReceivedPointerDownY = new float[MAX_POINTER_COUNT];
1669736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov        private final long[] mReceivedPointerDownTime = new long[MAX_POINTER_COUNT];
1670736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov
1671736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov        // Which pointers are down.
1672736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov        private int mReceivedPointersDown;
1673736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov
1674c4842c11932ea4f60fe7ae09b0a59660207e1587Svetoslav Ganov        // The edge flags of the last received down event.
1675c4842c11932ea4f60fe7ae09b0a59660207e1587Svetoslav Ganov        private int mLastReceivedDownEdgeFlags;
1676c4842c11932ea4f60fe7ae09b0a59660207e1587Svetoslav Ganov
167784044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav        // Primary pointer which is either the first that went down
167884044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav        // or if it goes up the next one that most recently went down.
167984044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav        private int mPrimaryPointerId;
1680736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov
1681736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov        // Keep track of the last up pointer data.
1682736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov        private long mLastReceivedUpPointerDownTime;
16834213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov        private float mLastReceivedUpPointerDownX;
16844213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov        private float mLastReceivedUpPointerDownY;
1685736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov
1686e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov        private MotionEvent mLastReceivedEvent;
1687e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov
1688736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov        /**
1689736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov         * Clears the internals state.
1690736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov         */
1691736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov        public void clear() {
1692736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov            Arrays.fill(mReceivedPointerDownX, 0);
1693736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov            Arrays.fill(mReceivedPointerDownY, 0);
1694736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov            Arrays.fill(mReceivedPointerDownTime, 0);
1695736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov            mReceivedPointersDown = 0;
169684044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav            mPrimaryPointerId = 0;
1697736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov            mLastReceivedUpPointerDownTime = 0;
16984213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov            mLastReceivedUpPointerDownX = 0;
16994213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov            mLastReceivedUpPointerDownY = 0;
1700736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov        }
1701736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov
1702736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov        /**
1703736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov         * Processes a received {@link MotionEvent} event.
1704736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov         *
1705736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov         * @param event The event to process.
1706736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov         */
17074213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov        public void onMotionEvent(MotionEvent event) {
1708e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov            if (mLastReceivedEvent != null) {
1709e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov                mLastReceivedEvent.recycle();
1710e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov            }
1711e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov            mLastReceivedEvent = MotionEvent.obtain(event);
1712e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov
1713736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov            final int action = event.getActionMasked();
1714736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov            switch (action) {
1715736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov                case MotionEvent.ACTION_DOWN: {
171600f7b3f76515d1c6fbe5cf9fee9d3760787c03cdSvetoslav Ganov                    handleReceivedPointerDown(event.getActionIndex(), event);
1717736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov                } break;
1718736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov                case MotionEvent.ACTION_POINTER_DOWN: {
1719736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov                    handleReceivedPointerDown(event.getActionIndex(), event);
1720736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov                } break;
1721736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov                case MotionEvent.ACTION_UP: {
172200f7b3f76515d1c6fbe5cf9fee9d3760787c03cdSvetoslav Ganov                    handleReceivedPointerUp(event.getActionIndex(), event);
1723736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov                } break;
1724736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov                case MotionEvent.ACTION_POINTER_UP: {
1725736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov                    handleReceivedPointerUp(event.getActionIndex(), event);
1726736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov                } break;
1727736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov            }
1728736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov            if (DEBUG) {
172938c992841b5f6bc80359dbf60d31aa7b994160fcSvetoslav Ganov                Slog.i(LOG_TAG_RECEIVED_POINTER_TRACKER, "Received pointer:\n" + toString());
1730736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov            }
1731736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov        }
1732736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov
1733736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov        /**
1734e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov         * @return The last received event.
1735e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov         */
1736e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov        public MotionEvent getLastReceivedEvent() {
1737e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov            return mLastReceivedEvent;
1738e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov        }
1739e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov
1740e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov        /**
17414213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov         * @return The number of received pointers that are down.
1742736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov         */
17434213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov        public int getReceivedPointerDownCount() {
17444213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov            return Integer.bitCount(mReceivedPointersDown);
1745736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov        }
1746736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov
1747736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov        /**
1748736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov         * Whether an received pointer is down.
1749736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov         *
1750736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov         * @param pointerId The unique pointer id.
1751736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov         * @return True if the pointer is down.
1752736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov         */
1753736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov        public boolean isReceivedPointerDown(int pointerId) {
1754736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov            final int pointerFlag = (1 << pointerId);
1755736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov            return (mReceivedPointersDown & pointerFlag) != 0;
1756736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov        }
1757736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov
1758736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov        /**
1759736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov         * @param pointerId The unique pointer id.
1760736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov         * @return The X coordinate where the pointer went down.
1761736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov         */
1762736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov        public float getReceivedPointerDownX(int pointerId) {
1763736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov            return mReceivedPointerDownX[pointerId];
1764736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov        }
1765736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov
1766736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov        /**
1767736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov         * @param pointerId The unique pointer id.
1768736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov         * @return The Y coordinate where the pointer went down.
1769736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov         */
1770736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov        public float getReceivedPointerDownY(int pointerId) {
1771736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov            return mReceivedPointerDownY[pointerId];
1772736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov        }
1773736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov
1774736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov        /**
1775736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov         * @param pointerId The unique pointer id.
1776736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov         * @return The time when the pointer went down.
1777736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov         */
1778736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov        public long getReceivedPointerDownTime(int pointerId) {
1779736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov            return mReceivedPointerDownTime[pointerId];
1780736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov        }
1781736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov
1782736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov        /**
1783736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov         * @return The id of the primary pointer.
1784736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov         */
178584044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav        public int getPrimaryPointerId() {
178684044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav            if (mPrimaryPointerId == INVALID_POINTER_ID) {
178738c992841b5f6bc80359dbf60d31aa7b994160fcSvetoslav Ganov                mPrimaryPointerId = findPrimaryPointerId();
1788736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov            }
178984044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav            return mPrimaryPointerId;
1790736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov        }
1791736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov
1792736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov        /**
1793736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov         * @return The time when the last up received pointer went down.
1794736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov         */
1795736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov        public long getLastReceivedUpPointerDownTime() {
1796736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov            return mLastReceivedUpPointerDownTime;
1797736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov        }
1798736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov
1799736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov        /**
18004213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov         * @return The down X of the last received pointer that went up.
1801736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov         */
18024213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov        public float getLastReceivedUpPointerDownX() {
18034213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov            return mLastReceivedUpPointerDownX;
1804736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov        }
1805736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov
1806736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov        /**
18074213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov         * @return The down Y of the last received pointer that went up.
1808736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov         */
18094213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov        public float getLastReceivedUpPointerDownY() {
18104213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov            return mLastReceivedUpPointerDownY;
1811736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov        }
1812736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov
1813736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov        /**
1814c4842c11932ea4f60fe7ae09b0a59660207e1587Svetoslav Ganov         * @return The edge flags of the last received down event.
1815c4842c11932ea4f60fe7ae09b0a59660207e1587Svetoslav Ganov         */
1816c4842c11932ea4f60fe7ae09b0a59660207e1587Svetoslav Ganov        public int getLastReceivedDownEdgeFlags() {
1817c4842c11932ea4f60fe7ae09b0a59660207e1587Svetoslav Ganov            return mLastReceivedDownEdgeFlags;
1818c4842c11932ea4f60fe7ae09b0a59660207e1587Svetoslav Ganov        }
1819c4842c11932ea4f60fe7ae09b0a59660207e1587Svetoslav Ganov
1820c4842c11932ea4f60fe7ae09b0a59660207e1587Svetoslav Ganov        /**
1821736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov         * Handles a received pointer down event.
1822736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov         *
1823736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov         * @param pointerIndex The index of the pointer that has changed.
1824736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov         * @param event The event to be handled.
1825736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov         */
1826736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov        private void handleReceivedPointerDown(int pointerIndex, MotionEvent event) {
1827736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov            final int pointerId = event.getPointerId(pointerIndex);
1828736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov            final int pointerFlag = (1 << pointerId);
1829736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov
1830736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov            mLastReceivedUpPointerDownTime = 0;
18314213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov            mLastReceivedUpPointerDownX = 0;
18324213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov            mLastReceivedUpPointerDownX = 0;
1833736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov
1834c4842c11932ea4f60fe7ae09b0a59660207e1587Svetoslav Ganov            mLastReceivedDownEdgeFlags = event.getEdgeFlags();
1835c4842c11932ea4f60fe7ae09b0a59660207e1587Svetoslav Ganov
1836736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov            mReceivedPointersDown |= pointerFlag;
1837736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov            mReceivedPointerDownX[pointerId] = event.getX(pointerIndex);
1838736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov            mReceivedPointerDownY[pointerId] = event.getY(pointerIndex);
1839736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov            mReceivedPointerDownTime[pointerId] = event.getEventTime();
1840736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov
184184044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav            mPrimaryPointerId = pointerId;
1842736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov        }
1843736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov
1844736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov        /**
1845736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov         * Handles a received pointer up event.
1846736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov         *
1847736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov         * @param pointerIndex The index of the pointer that has changed.
1848736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov         * @param event The event to be handled.
1849736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov         */
1850736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov        private void handleReceivedPointerUp(int pointerIndex, MotionEvent event) {
1851736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov            final int pointerId = event.getPointerId(pointerIndex);
1852736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov            final int pointerFlag = (1 << pointerId);
1853736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov
1854736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov            mLastReceivedUpPointerDownTime = getReceivedPointerDownTime(pointerId);
18554213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov            mLastReceivedUpPointerDownX = mReceivedPointerDownX[pointerId];
18564213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov            mLastReceivedUpPointerDownY = mReceivedPointerDownY[pointerId];
1857736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov
1858736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov            mReceivedPointersDown &= ~pointerFlag;
1859736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov            mReceivedPointerDownX[pointerId] = 0;
1860736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov            mReceivedPointerDownY[pointerId] = 0;
1861736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov            mReceivedPointerDownTime[pointerId] = 0;
1862736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov
186384044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav            if (mPrimaryPointerId == pointerId) {
186484044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav                mPrimaryPointerId = INVALID_POINTER_ID;
1865736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov            }
1866736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov        }
1867736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov
1868736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov        /**
186938c992841b5f6bc80359dbf60d31aa7b994160fcSvetoslav Ganov         * @return The primary pointer id.
1870736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov         */
187138c992841b5f6bc80359dbf60d31aa7b994160fcSvetoslav Ganov        private int findPrimaryPointerId() {
187284044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav            int primaryPointerId = INVALID_POINTER_ID;
1873736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov            long minDownTime = Long.MAX_VALUE;
187438c992841b5f6bc80359dbf60d31aa7b994160fcSvetoslav Ganov
187584044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav            // Find the pointer that went down first.
187638c992841b5f6bc80359dbf60d31aa7b994160fcSvetoslav Ganov            int pointerIdBits = mReceivedPointersDown;
187738c992841b5f6bc80359dbf60d31aa7b994160fcSvetoslav Ganov            while (pointerIdBits > 0) {
187838c992841b5f6bc80359dbf60d31aa7b994160fcSvetoslav Ganov                final int pointerId = Integer.numberOfTrailingZeros(pointerIdBits);
187938c992841b5f6bc80359dbf60d31aa7b994160fcSvetoslav Ganov                pointerIdBits &= ~(1 << pointerId);
188038c992841b5f6bc80359dbf60d31aa7b994160fcSvetoslav Ganov                final long downPointerTime = mReceivedPointerDownTime[pointerId];
188184044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav                if (downPointerTime < minDownTime) {
188284044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav                    minDownTime = downPointerTime;
188338c992841b5f6bc80359dbf60d31aa7b994160fcSvetoslav Ganov                    primaryPointerId = pointerId;
1884736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov                }
1885736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov            }
188684044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav            return primaryPointerId;
1887736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov        }
1888736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov
1889736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov        @Override
1890736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov        public String toString() {
1891736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov            StringBuilder builder = new StringBuilder();
1892736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov            builder.append("=========================");
1893736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov            builder.append("\nDown pointers #");
1894736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov            builder.append(getReceivedPointerDownCount());
1895736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov            builder.append(" [ ");
1896736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov            for (int i = 0; i < MAX_POINTER_COUNT; i++) {
1897736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov                if (isReceivedPointerDown(i)) {
1898736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov                    builder.append(i);
1899736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov                    builder.append(" ");
1900736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov                }
1901736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov            }
1902736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov            builder.append("]");
190384044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav            builder.append("\nPrimary pointer id [ ");
190484044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav            builder.append(getPrimaryPointerId());
1905736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov            builder.append(" ]");
1906736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov            builder.append("\n=========================");
1907736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov            return builder.toString();
1908736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov        }
1909736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov    }
1910736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov}
1911