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