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;
207498efdc5e163d6b4a11db941c7d13c169d37284Svet Ganovimport android.graphics.Point;
21736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganovimport android.os.Handler;
22736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganovimport android.util.Slog;
23976724e81bca33dc48347f88633a37a195b9e4eaToni Barzicimport android.view.InputDevice;
24976724e81bca33dc48347f88633a37a195b9e4eaToni Barzicimport android.view.KeyEvent;
25736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganovimport android.view.MotionEvent;
26e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganovimport android.view.MotionEvent.PointerCoords;
27e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganovimport android.view.MotionEvent.PointerProperties;
28f5a07905a3e025f95472a3f8d9935263e49ad6d3Svetoslav Ganovimport android.view.ViewConfiguration;
29f5a07905a3e025f95472a3f8d9935263e49ad6d3Svetoslav Ganovimport android.view.WindowManagerPolicy;
3086783474fdec98a22bc22e224462767eab13e273Svetoslav Ganovimport android.view.accessibility.AccessibilityEvent;
3177276b60851a158ad3e142cb3b091d57ae5ceffbSvetoslav Ganovimport android.view.accessibility.AccessibilityManager;
32736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov
334213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganovimport java.util.ArrayList;
34736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganovimport java.util.Arrays;
3584044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslavimport java.util.List;
36736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov
37736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov/**
38736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov * This class is a strategy for performing touch exploration. It
39736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov * transforms the motion event stream by modifying, adding, replacing,
40736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov * and consuming certain events. The interaction model is:
41736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov *
42736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov * <ol>
43e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov *   <li>1. One finger moving slow around performs touch exploration.</li>
44e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov *   <li>2. One finger moving fast around performs gestures.</li>
45e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov *   <li>3. Two close fingers moving in the same direction perform a drag.</li>
46e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov *   <li>4. Multi-finger gestures are delivered to view hierarchy.</li>
4784044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav *   <li>5. Two fingers moving in different directions are considered a multi-finger gesture.</li>
4884044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav *   <li>7. Double tapping clicks on the on the last touch explored location if it was in
49e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov *          a window that does not take focus, otherwise the click is within the accessibility
50e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov *          focused rectangle.</li>
51e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov *   <li>7. Tapping and holding for a while performs a long press in a similar fashion
52e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov *          as the click above.</li>
53736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov * <ol>
54736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov *
55736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov * @hide
56736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov */
57c18f2fdfcf8ec7a8534c032c4102739edb7f1c5eZachary Kuzniaclass TouchExplorer implements EventStreamTransformation, AccessibilityGestureDetector.Listener {
584213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov
59736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov    private static final boolean DEBUG = false;
60736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov
61736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov    // Tag for logging received events.
624213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov    private static final String LOG_TAG = "TouchExplorer";
63736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov
64736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov    // States this explorer can be in.
65736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov    private static final int STATE_TOUCH_EXPLORING = 0x00000001;
66736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov    private static final int STATE_DRAGGING = 0x00000002;
67736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov    private static final int STATE_DELEGATING = 0x00000004;
684213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov    private static final int STATE_GESTURE_DETECTING = 0x00000005;
69736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov
70ded133c446fa9d0d23e6bde19a66fb2ce3980491Svetoslav    private static final int CLICK_LOCATION_NONE = 0;
71ded133c446fa9d0d23e6bde19a66fb2ce3980491Svetoslav    private static final int CLICK_LOCATION_ACCESSIBILITY_FOCUS = 1;
72ded133c446fa9d0d23e6bde19a66fb2ce3980491Svetoslav    private static final int CLICK_LOCATION_LAST_TOUCH_EXPLORED = 2;
73ded133c446fa9d0d23e6bde19a66fb2ce3980491Svetoslav
74a3315270e0502189a6fa4de20b9db2bf042cfd2aSvetoslav    // The maximum of the cosine between the vectors of two moving
75736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov    // pointers so they can be considered moving in the same direction.
7612a024ca681d877fe16b7e087356f7aff175a218Svetoslav Ganov    private static final float MAX_DRAGGING_ANGLE_COS = 0.525321989f; // cos(pi/4)
77736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov
78f804420d6e37748b75478406e989c69303756980Svetoslav Ganov    // Constant referring to the ids bits of all pointers.
79f804420d6e37748b75478406e989c69303756980Svetoslav Ganov    private static final int ALL_POINTER_ID_BITS = 0xFFFFFFFF;
80736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov
814213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov    // This constant captures the current implementation detail that
824213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov    // pointer IDs are between 0 and 31 inclusive (subject to change).
834213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov    // (See MAX_POINTER_ID in frameworks/base/include/ui/Input.h)
84e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov    private static final int MAX_POINTER_COUNT = 32;
854213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov
864213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov    // Invalid pointer ID.
87e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov    private static final int INVALID_POINTER_ID = -1;
88e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov
89e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov    // The minimal distance before we take the middle of the distance between
90e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov    // the two dragging pointers as opposed to use the location of the primary one.
91e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov    private static final int MIN_POINTER_DISTANCE_TO_USE_MIDDLE_LOCATION_DIP = 200;
924213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov
9395068e5d1bea47091e97955f271c789264994550Svetoslav Ganov    // The timeout after which we are no longer trying to detect a gesture.
9495068e5d1bea47091e97955f271c789264994550Svetoslav Ganov    private static final int EXIT_GESTURE_DETECTION_TIMEOUT = 2000;
9595068e5d1bea47091e97955f271c789264994550Svetoslav Ganov
96e47957a0bbe2164467ff6e7a566b0c9e4689cdc9Svetoslav Ganov    // Timeout before trying to decide what the user is trying to do.
97e47957a0bbe2164467ff6e7a566b0c9e4689cdc9Svetoslav Ganov    private final int mDetermineUserIntentTimeout;
98e47957a0bbe2164467ff6e7a566b0c9e4689cdc9Svetoslav Ganov
99e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov    // Slop between the first and second tap to be a double tap.
100e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov    private final int mDoubleTapSlop;
101736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov
102736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov    // The current state of the touch explorer.
103736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov    private int mCurrentState = STATE_TOUCH_EXPLORING;
104736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov
105736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov    // The ID of the pointer used for dragging.
106736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov    private int mDraggingPointerId;
107736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov
108736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov    // Handler for performing asynchronous operations.
109736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov    private final Handler mHandler;
110736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov
11184044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav    // Command for delayed sending of a hover enter and move event.
11284044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav    private final SendHoverEnterAndMoveDelayed mSendHoverEnterAndMoveDelayed;
113e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov
114e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov    // Command for delayed sending of a hover exit event.
11584044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav    private final SendHoverExitDelayed mSendHoverExitDelayed;
116736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov
117f068fed6c4c3fc2003aec19b6e7e892358179b02Svetoslav Ganov    // Command for delayed sending of touch exploration end events.
118f068fed6c4c3fc2003aec19b6e7e892358179b02Svetoslav Ganov    private final SendAccessibilityEventDelayed mSendTouchExplorationEndDelayed;
119f068fed6c4c3fc2003aec19b6e7e892358179b02Svetoslav Ganov
120f068fed6c4c3fc2003aec19b6e7e892358179b02Svetoslav Ganov    // Command for delayed sending of touch interaction end events.
121f068fed6c4c3fc2003aec19b6e7e892358179b02Svetoslav Ganov    private final SendAccessibilityEventDelayed mSendTouchInteractionEndDelayed;
122fe304b893968887323b93764caafa66ee8ad44deSvetoslav Ganov
12395068e5d1bea47091e97955f271c789264994550Svetoslav Ganov    // Command for exiting gesture detection mode after a timeout.
12495068e5d1bea47091e97955f271c789264994550Svetoslav Ganov    private final ExitGestureDetectionModeDelayed mExitGestureDetectionModeDelayed;
12595068e5d1bea47091e97955f271c789264994550Svetoslav Ganov
126c18f2fdfcf8ec7a8534c032c4102739edb7f1c5eZachary Kuznia    // Helper to detect gestures.
127c18f2fdfcf8ec7a8534c032c4102739edb7f1c5eZachary Kuznia    private final AccessibilityGestureDetector mGestureDetector;
128e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov
129e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov    // The scaled minimal distance before we take the middle of the distance between
130e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov    // the two dragging pointers as opposed to use the location of the primary one.
131e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov    private final int mScaledMinPointerDistanceToUseMiddleLocation;
132e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov
1331cf70bbf96930662cab0e699d70b62865766ff52Svetoslav Ganov    // The handler to which to delegate events.
1341cf70bbf96930662cab0e699d70b62865766ff52Svetoslav Ganov    private EventStreamTransformation mNext;
1351cf70bbf96930662cab0e699d70b62865766ff52Svetoslav Ganov
136e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov    // Helper class to track received pointers.
1374213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov    private final ReceivedPointerTracker mReceivedPointerTracker;
1384213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov
139e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov    // Helper class to track injected pointers.
1404213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov    private final InjectedPointerTracker mInjectedPointerTracker;
1414213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov
142e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov    // Handle to the accessibility manager service.
143e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov    private final AccessibilityManagerService mAms;
1444213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov
1457498efdc5e163d6b4a11db941c7d13c169d37284Svet Ganov    // Temporary point to avoid instantiation.
1467498efdc5e163d6b4a11db941c7d13c169d37284Svet Ganov    private final Point mTempPoint = new Point();
1477498efdc5e163d6b4a11db941c7d13c169d37284Svet Ganov
14877276b60851a158ad3e142cb3b091d57ae5ceffbSvetoslav Ganov    // Context in which this explorer operates.
14977276b60851a158ad3e142cb3b091d57ae5ceffbSvetoslav Ganov    private final Context mContext;
15077276b60851a158ad3e142cb3b091d57ae5ceffbSvetoslav Ganov
151e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov    // The long pressing pointer id if coordinate remapping is needed.
152aeb8d0ed0d98d398a66a092c418f4f2bca8719e0Svetoslav Ganov    private int mLongPressingPointerId = -1;
153e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov
154e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov    // The long pressing pointer X if coordinate remapping is needed.
155e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov    private int mLongPressingPointerDeltaX;
156e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov
157e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov    // The long pressing pointer Y if coordinate remapping is needed.
158e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov    private int mLongPressingPointerDeltaY;
1594213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov
16076716c5a180aa471c6973ca7aa03c7f2da677823Svetoslav Ganov    // The id of the last touch explored window.
16176716c5a180aa471c6973ca7aa03c7f2da677823Svetoslav Ganov    private int mLastTouchedWindowId;
16276716c5a180aa471c6973ca7aa03c7f2da677823Svetoslav Ganov
163f068fed6c4c3fc2003aec19b6e7e892358179b02Svetoslav Ganov    // Whether touch exploration is in progress.
164f068fed6c4c3fc2003aec19b6e7e892358179b02Svetoslav Ganov    private boolean mTouchExplorationInProgress;
16577276b60851a158ad3e142cb3b091d57ae5ceffbSvetoslav Ganov
166736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov    /**
167736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov     * Creates a new instance.
168736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov     *
169736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov     * @param inputFilter The input filter associated with this explorer.
170736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov     * @param context A context handle for accessing resources.
171736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov     */
1721cf70bbf96930662cab0e699d70b62865766ff52Svetoslav Ganov    public TouchExplorer(Context context, AccessibilityManagerService service) {
17377276b60851a158ad3e142cb3b091d57ae5ceffbSvetoslav Ganov        mContext = context;
174e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov        mAms = service;
17584044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav        mReceivedPointerTracker = new ReceivedPointerTracker();
1764213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov        mInjectedPointerTracker = new InjectedPointerTracker();
17777276b60851a158ad3e142cb3b091d57ae5ceffbSvetoslav Ganov        mDetermineUserIntentTimeout = ViewConfiguration.getDoubleTapTimeout();
178e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov        mDoubleTapSlop = ViewConfiguration.get(context).getScaledDoubleTapSlop();
179736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov        mHandler = new Handler(context.getMainLooper());
18095068e5d1bea47091e97955f271c789264994550Svetoslav Ganov        mExitGestureDetectionModeDelayed = new ExitGestureDetectionModeDelayed();
18184044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav        mSendHoverEnterAndMoveDelayed = new SendHoverEnterAndMoveDelayed();
18284044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav        mSendHoverExitDelayed = new SendHoverExitDelayed();
183f068fed6c4c3fc2003aec19b6e7e892358179b02Svetoslav Ganov        mSendTouchExplorationEndDelayed = new SendAccessibilityEventDelayed(
184f068fed6c4c3fc2003aec19b6e7e892358179b02Svetoslav Ganov                AccessibilityEvent.TYPE_TOUCH_EXPLORATION_GESTURE_END,
185f068fed6c4c3fc2003aec19b6e7e892358179b02Svetoslav Ganov                mDetermineUserIntentTimeout);
186f068fed6c4c3fc2003aec19b6e7e892358179b02Svetoslav Ganov        mSendTouchInteractionEndDelayed = new SendAccessibilityEventDelayed(
187f068fed6c4c3fc2003aec19b6e7e892358179b02Svetoslav Ganov                AccessibilityEvent.TYPE_TOUCH_INTERACTION_END,
188f068fed6c4c3fc2003aec19b6e7e892358179b02Svetoslav Ganov                mDetermineUserIntentTimeout);
189c18f2fdfcf8ec7a8534c032c4102739edb7f1c5eZachary Kuznia        mGestureDetector = new AccessibilityGestureDetector(context, this);
190e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov        final float density = context.getResources().getDisplayMetrics().density;
191e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov        mScaledMinPointerDistanceToUseMiddleLocation =
192e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov            (int) (MIN_POINTER_DISTANCE_TO_USE_MIDDLE_LOCATION_DIP * density);
193e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov    }
194e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov
195976724e81bca33dc48347f88633a37a195b9e4eaToni Barzic    @Override
196976724e81bca33dc48347f88633a37a195b9e4eaToni Barzic    public void clearEvents(int inputSource) {
197976724e81bca33dc48347f88633a37a195b9e4eaToni Barzic        if (inputSource == InputDevice.SOURCE_TOUCHSCREEN) {
198976724e81bca33dc48347f88633a37a195b9e4eaToni Barzic            clear();
199976724e81bca33dc48347f88633a37a195b9e4eaToni Barzic        }
200976724e81bca33dc48347f88633a37a195b9e4eaToni Barzic        if (mNext != null) {
201976724e81bca33dc48347f88633a37a195b9e4eaToni Barzic            mNext.clearEvents(inputSource);
202976724e81bca33dc48347f88633a37a195b9e4eaToni Barzic        }
203976724e81bca33dc48347f88633a37a195b9e4eaToni Barzic    }
204976724e81bca33dc48347f88633a37a195b9e4eaToni Barzic
205976724e81bca33dc48347f88633a37a195b9e4eaToni Barzic    @Override
206976724e81bca33dc48347f88633a37a195b9e4eaToni Barzic    public void onDestroy() {
207976724e81bca33dc48347f88633a37a195b9e4eaToni Barzic        clear();
208976724e81bca33dc48347f88633a37a195b9e4eaToni Barzic    }
209976724e81bca33dc48347f88633a37a195b9e4eaToni Barzic
210976724e81bca33dc48347f88633a37a195b9e4eaToni Barzic    private void clear() {
211e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov        // If we have not received an event then we are in initial
212e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov        // state. Therefore, there is not need to clean anything.
213e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov        MotionEvent event = mReceivedPointerTracker.getLastReceivedEvent();
214e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov        if (event != null) {
215e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov            clear(mReceivedPointerTracker.getLastReceivedEvent(), WindowManagerPolicy.FLAG_TRUSTED);
216e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov        }
217736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov    }
218736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov
2191cf70bbf96930662cab0e699d70b62865766ff52Svetoslav Ganov    private void clear(MotionEvent event, int policyFlags) {
220e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov        switch (mCurrentState) {
221e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov            case STATE_TOUCH_EXPLORING: {
222e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov                // If a touch exploration gesture is in progress send events for its end.
223f068fed6c4c3fc2003aec19b6e7e892358179b02Svetoslav Ganov                sendHoverExitAndTouchExplorationGestureEndIfNeeded(policyFlags);
224e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov            } break;
225e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov            case STATE_DRAGGING: {
226e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov                mDraggingPointerId = INVALID_POINTER_ID;
227e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov                // Send exit to all pointers that we have delivered.
228e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov                sendUpForInjectedDownPointers(event, policyFlags);
229e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov            } break;
230e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov            case STATE_DELEGATING: {
231e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov                // Send exit to all pointers that we have delivered.
232e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov                sendUpForInjectedDownPointers(event, policyFlags);
233e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov            } break;
234e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov            case STATE_GESTURE_DETECTING: {
235407df712e2a381e0227eb780786128a50bfcf5f5Zachary Kuznia                // No state specific cleanup required.
236e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov            } break;
237e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov        }
238e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov        // Remove all pending callbacks.
23984044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav        mSendHoverEnterAndMoveDelayed.cancel();
24084044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav        mSendHoverExitDelayed.cancel();
24184044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav        mExitGestureDetectionModeDelayed.cancel();
24284044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav        mSendTouchExplorationEndDelayed.cancel();
24384044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav        mSendTouchInteractionEndDelayed.cancel();
244e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov        // Reset the pointer trackers.
245e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov        mReceivedPointerTracker.clear();
246e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov        mInjectedPointerTracker.clear();
247c18f2fdfcf8ec7a8534c032c4102739edb7f1c5eZachary Kuznia        // Clear the gesture detector
248c18f2fdfcf8ec7a8534c032c4102739edb7f1c5eZachary Kuznia        mGestureDetector.clear();
249e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov        // Go to initial state.
250e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov        // Clear the long pressing pointer remap data.
251e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov        mLongPressingPointerId = -1;
252e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov        mLongPressingPointerDeltaX = 0;
253e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov        mLongPressingPointerDeltaY = 0;
254e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov        mCurrentState = STATE_TOUCH_EXPLORING;
255f068fed6c4c3fc2003aec19b6e7e892358179b02Svetoslav Ganov        mTouchExplorationInProgress = false;
256f772cba59760d1ad9eb5cb9205b2e2e9126e488dSvetoslav Ganov        mAms.onTouchInteractionEnd();
2571cf70bbf96930662cab0e699d70b62865766ff52Svetoslav Ganov    }
2581cf70bbf96930662cab0e699d70b62865766ff52Svetoslav Ganov
2591cf70bbf96930662cab0e699d70b62865766ff52Svetoslav Ganov    @Override
2601cf70bbf96930662cab0e699d70b62865766ff52Svetoslav Ganov    public void setNext(EventStreamTransformation next) {
2611cf70bbf96930662cab0e699d70b62865766ff52Svetoslav Ganov        mNext = next;
262736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov    }
263736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov
2641cf70bbf96930662cab0e699d70b62865766ff52Svetoslav Ganov    @Override
26545af84a483165f06c04d74baba67f90da29c6ad2Svetoslav Ganov    public void onMotionEvent(MotionEvent event, MotionEvent rawEvent, int policyFlags) {
266976724e81bca33dc48347f88633a37a195b9e4eaToni Barzic        if (!event.isFromSource(InputDevice.SOURCE_TOUCHSCREEN)) {
267976724e81bca33dc48347f88633a37a195b9e4eaToni Barzic            if (mNext != null) {
268976724e81bca33dc48347f88633a37a195b9e4eaToni Barzic                mNext.onMotionEvent(event, rawEvent, policyFlags);
269976724e81bca33dc48347f88633a37a195b9e4eaToni Barzic            }
270976724e81bca33dc48347f88633a37a195b9e4eaToni Barzic            return;
271976724e81bca33dc48347f88633a37a195b9e4eaToni Barzic        }
272976724e81bca33dc48347f88633a37a195b9e4eaToni Barzic
273736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov        if (DEBUG) {
2744213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov            Slog.d(LOG_TAG, "Received event: " + event + ", policyFlags=0x"
275736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov                    + Integer.toHexString(policyFlags));
2764213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov            Slog.d(LOG_TAG, getStateSymbolicName(mCurrentState));
277736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov        }
278736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov
27945af84a483165f06c04d74baba67f90da29c6ad2Svetoslav Ganov        mReceivedPointerTracker.onMotionEvent(rawEvent);
280736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov
2819254b3e336a01ff79742882bd98860b48ebae379Zachary Kuznia        // The motion detector is interested in the movements in physical space,
2829254b3e336a01ff79742882bd98860b48ebae379Zachary Kuznia        // so it uses the rawEvent to ignore magnification and other
2839254b3e336a01ff79742882bd98860b48ebae379Zachary Kuznia        // transformations.
2849254b3e336a01ff79742882bd98860b48ebae379Zachary Kuznia        if (mGestureDetector.onMotionEvent(rawEvent, policyFlags)) {
285407df712e2a381e0227eb780786128a50bfcf5f5Zachary Kuznia            // Event was handled by the gesture detector.
286407df712e2a381e0227eb780786128a50bfcf5f5Zachary Kuznia            return;
287407df712e2a381e0227eb780786128a50bfcf5f5Zachary Kuznia        }
288407df712e2a381e0227eb780786128a50bfcf5f5Zachary Kuznia
2899254b3e336a01ff79742882bd98860b48ebae379Zachary Kuznia        if (event.getActionMasked() == MotionEvent.ACTION_CANCEL) {
2909254b3e336a01ff79742882bd98860b48ebae379Zachary Kuznia            clear(event, policyFlags);
2919254b3e336a01ff79742882bd98860b48ebae379Zachary Kuznia            return;
2929254b3e336a01ff79742882bd98860b48ebae379Zachary Kuznia        }
2939254b3e336a01ff79742882bd98860b48ebae379Zachary Kuznia
294736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov        switch(mCurrentState) {
295736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov            case STATE_TOUCH_EXPLORING: {
29645af84a483165f06c04d74baba67f90da29c6ad2Svetoslav Ganov                handleMotionEventStateTouchExploring(event, rawEvent, policyFlags);
297736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov            } break;
298736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov            case STATE_DRAGGING: {
299736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov                handleMotionEventStateDragging(event, policyFlags);
300736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov            } break;
301736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov            case STATE_DELEGATING: {
302736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov                handleMotionEventStateDelegating(event, policyFlags);
303736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov            } break;
3044213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov            case STATE_GESTURE_DETECTING: {
3059254b3e336a01ff79742882bd98860b48ebae379Zachary Kuznia                // Already handled.
3064213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov            } break;
3074213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov            default:
308736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov                throw new IllegalStateException("Illegal state: " + mCurrentState);
309736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov        }
310736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov    }
311736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov
312976724e81bca33dc48347f88633a37a195b9e4eaToni Barzic    @Override
313976724e81bca33dc48347f88633a37a195b9e4eaToni Barzic    public void onKeyEvent(KeyEvent event, int policyFlags) {
314976724e81bca33dc48347f88633a37a195b9e4eaToni Barzic        if (mNext != null) {
315976724e81bca33dc48347f88633a37a195b9e4eaToni Barzic            mNext.onKeyEvent(event, policyFlags);
316976724e81bca33dc48347f88633a37a195b9e4eaToni Barzic        }
317976724e81bca33dc48347f88633a37a195b9e4eaToni Barzic    }
318976724e81bca33dc48347f88633a37a195b9e4eaToni Barzic
319976724e81bca33dc48347f88633a37a195b9e4eaToni Barzic    @Override
32086783474fdec98a22bc22e224462767eab13e273Svetoslav Ganov    public void onAccessibilityEvent(AccessibilityEvent event) {
32177276b60851a158ad3e142cb3b091d57ae5ceffbSvetoslav Ganov        final int eventType = event.getEventType();
32277276b60851a158ad3e142cb3b091d57ae5ceffbSvetoslav Ganov
32377276b60851a158ad3e142cb3b091d57ae5ceffbSvetoslav Ganov        // The event for gesture end should be strictly after the
32477276b60851a158ad3e142cb3b091d57ae5ceffbSvetoslav Ganov        // last hover exit event.
325f068fed6c4c3fc2003aec19b6e7e892358179b02Svetoslav Ganov        if (mSendTouchExplorationEndDelayed.isPending()
3268b681cb8813454aac8a626bf3d7adaa8beca4d75Svetoslav Ganov                && eventType == AccessibilityEvent.TYPE_VIEW_HOVER_EXIT) {
32784044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav                    mSendTouchExplorationEndDelayed.cancel();
3288b681cb8813454aac8a626bf3d7adaa8beca4d75Svetoslav Ganov            sendAccessibilityEvent(AccessibilityEvent.TYPE_TOUCH_EXPLORATION_GESTURE_END);
32977276b60851a158ad3e142cb3b091d57ae5ceffbSvetoslav Ganov        }
33077276b60851a158ad3e142cb3b091d57ae5ceffbSvetoslav Ganov
33177276b60851a158ad3e142cb3b091d57ae5ceffbSvetoslav Ganov        // The event for touch interaction end should be strictly after the
33277276b60851a158ad3e142cb3b091d57ae5ceffbSvetoslav Ganov        // last hover exit and the touch exploration gesture end events.
333f068fed6c4c3fc2003aec19b6e7e892358179b02Svetoslav Ganov        if (mSendTouchInteractionEndDelayed.isPending()
3348b681cb8813454aac8a626bf3d7adaa8beca4d75Svetoslav Ganov                && eventType == AccessibilityEvent.TYPE_VIEW_HOVER_EXIT) {
33584044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav            mSendTouchInteractionEndDelayed.cancel();
3368b681cb8813454aac8a626bf3d7adaa8beca4d75Svetoslav Ganov            sendAccessibilityEvent(AccessibilityEvent.TYPE_TOUCH_INTERACTION_END);
33777276b60851a158ad3e142cb3b091d57ae5ceffbSvetoslav Ganov        }
33877276b60851a158ad3e142cb3b091d57ae5ceffbSvetoslav Ganov
33986783474fdec98a22bc22e224462767eab13e273Svetoslav Ganov        // If a new window opens or the accessibility focus moves we no longer
34086783474fdec98a22bc22e224462767eab13e273Svetoslav Ganov        // want to click/long press on the last touch explored location.
34186783474fdec98a22bc22e224462767eab13e273Svetoslav Ganov        switch (eventType) {
34286783474fdec98a22bc22e224462767eab13e273Svetoslav Ganov            case AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED:
34386783474fdec98a22bc22e224462767eab13e273Svetoslav Ganov            case AccessibilityEvent.TYPE_VIEW_ACCESSIBILITY_FOCUSED: {
3445d043ce8cc2f588fdfb336cc843fb3b07b196f83Svetoslav Ganov                if (mInjectedPointerTracker.mLastInjectedHoverEventForClick != null) {
3455d043ce8cc2f588fdfb336cc843fb3b07b196f83Svetoslav Ganov                    mInjectedPointerTracker.mLastInjectedHoverEventForClick.recycle();
3465d043ce8cc2f588fdfb336cc843fb3b07b196f83Svetoslav Ganov                    mInjectedPointerTracker.mLastInjectedHoverEventForClick = null;
34786783474fdec98a22bc22e224462767eab13e273Svetoslav Ganov                }
34876716c5a180aa471c6973ca7aa03c7f2da677823Svetoslav Ganov                mLastTouchedWindowId = -1;
34976716c5a180aa471c6973ca7aa03c7f2da677823Svetoslav Ganov            } break;
35076716c5a180aa471c6973ca7aa03c7f2da677823Svetoslav Ganov            case AccessibilityEvent.TYPE_VIEW_HOVER_ENTER:
35176716c5a180aa471c6973ca7aa03c7f2da677823Svetoslav Ganov            case AccessibilityEvent.TYPE_VIEW_HOVER_EXIT: {
35276716c5a180aa471c6973ca7aa03c7f2da677823Svetoslav Ganov                mLastTouchedWindowId = event.getWindowId();
35386783474fdec98a22bc22e224462767eab13e273Svetoslav Ganov            } break;
35486783474fdec98a22bc22e224462767eab13e273Svetoslav Ganov        }
3551cf70bbf96930662cab0e699d70b62865766ff52Svetoslav Ganov        if (mNext != null) {
3561cf70bbf96930662cab0e699d70b62865766ff52Svetoslav Ganov            mNext.onAccessibilityEvent(event);
3571cf70bbf96930662cab0e699d70b62865766ff52Svetoslav Ganov        }
35886783474fdec98a22bc22e224462767eab13e273Svetoslav Ganov    }
35986783474fdec98a22bc22e224462767eab13e273Svetoslav Ganov
360c18f2fdfcf8ec7a8534c032c4102739edb7f1c5eZachary Kuznia    @Override
361c18f2fdfcf8ec7a8534c032c4102739edb7f1c5eZachary Kuznia    public void onDoubleTapAndHold(MotionEvent event, int policyFlags) {
362407df712e2a381e0227eb780786128a50bfcf5f5Zachary Kuznia        // Ignore the event if we aren't touch exploring.
363407df712e2a381e0227eb780786128a50bfcf5f5Zachary Kuznia        if (mCurrentState != STATE_TOUCH_EXPLORING) {
364407df712e2a381e0227eb780786128a50bfcf5f5Zachary Kuznia            return;
365407df712e2a381e0227eb780786128a50bfcf5f5Zachary Kuznia        }
366407df712e2a381e0227eb780786128a50bfcf5f5Zachary Kuznia
367c18f2fdfcf8ec7a8534c032c4102739edb7f1c5eZachary Kuznia        // Pointers should not be zero when running this command.
368c18f2fdfcf8ec7a8534c032c4102739edb7f1c5eZachary Kuznia        if (mReceivedPointerTracker.getLastReceivedEvent().getPointerCount() == 0) {
369c18f2fdfcf8ec7a8534c032c4102739edb7f1c5eZachary Kuznia            return;
370c18f2fdfcf8ec7a8534c032c4102739edb7f1c5eZachary Kuznia        }
371c18f2fdfcf8ec7a8534c032c4102739edb7f1c5eZachary Kuznia
372c18f2fdfcf8ec7a8534c032c4102739edb7f1c5eZachary Kuznia        final int pointerIndex = event.getActionIndex();
373c18f2fdfcf8ec7a8534c032c4102739edb7f1c5eZachary Kuznia        final int pointerId = event.getPointerId(pointerIndex);
374c18f2fdfcf8ec7a8534c032c4102739edb7f1c5eZachary Kuznia
375c18f2fdfcf8ec7a8534c032c4102739edb7f1c5eZachary Kuznia        Point clickLocation = mTempPoint;
376c18f2fdfcf8ec7a8534c032c4102739edb7f1c5eZachary Kuznia        final int result = computeClickLocation(clickLocation);
377c18f2fdfcf8ec7a8534c032c4102739edb7f1c5eZachary Kuznia
378c18f2fdfcf8ec7a8534c032c4102739edb7f1c5eZachary Kuznia        if (result == CLICK_LOCATION_NONE) {
379c18f2fdfcf8ec7a8534c032c4102739edb7f1c5eZachary Kuznia            return;
380c18f2fdfcf8ec7a8534c032c4102739edb7f1c5eZachary Kuznia        }
381c18f2fdfcf8ec7a8534c032c4102739edb7f1c5eZachary Kuznia
382c18f2fdfcf8ec7a8534c032c4102739edb7f1c5eZachary Kuznia        mLongPressingPointerId = pointerId;
383c18f2fdfcf8ec7a8534c032c4102739edb7f1c5eZachary Kuznia        mLongPressingPointerDeltaX = (int) event.getX(pointerIndex) - clickLocation.x;
384c18f2fdfcf8ec7a8534c032c4102739edb7f1c5eZachary Kuznia        mLongPressingPointerDeltaY = (int) event.getY(pointerIndex) - clickLocation.y;
385c18f2fdfcf8ec7a8534c032c4102739edb7f1c5eZachary Kuznia
386c18f2fdfcf8ec7a8534c032c4102739edb7f1c5eZachary Kuznia        sendHoverExitAndTouchExplorationGestureEndIfNeeded(policyFlags);
387c18f2fdfcf8ec7a8534c032c4102739edb7f1c5eZachary Kuznia
388c18f2fdfcf8ec7a8534c032c4102739edb7f1c5eZachary Kuznia        mCurrentState = STATE_DELEGATING;
389c18f2fdfcf8ec7a8534c032c4102739edb7f1c5eZachary Kuznia        sendDownForAllNotInjectedPointers(event, policyFlags);
390c18f2fdfcf8ec7a8534c032c4102739edb7f1c5eZachary Kuznia    }
391c18f2fdfcf8ec7a8534c032c4102739edb7f1c5eZachary Kuznia
392c18f2fdfcf8ec7a8534c032c4102739edb7f1c5eZachary Kuznia    @Override
393407df712e2a381e0227eb780786128a50bfcf5f5Zachary Kuznia    public boolean onDoubleTap(MotionEvent event, int policyFlags) {
394407df712e2a381e0227eb780786128a50bfcf5f5Zachary Kuznia        // Ignore the event if we aren't touch exploring.
395407df712e2a381e0227eb780786128a50bfcf5f5Zachary Kuznia        if (mCurrentState != STATE_TOUCH_EXPLORING) {
396407df712e2a381e0227eb780786128a50bfcf5f5Zachary Kuznia            return false;
397c18f2fdfcf8ec7a8534c032c4102739edb7f1c5eZachary Kuznia        }
398c18f2fdfcf8ec7a8534c032c4102739edb7f1c5eZachary Kuznia
399c18f2fdfcf8ec7a8534c032c4102739edb7f1c5eZachary Kuznia        // Remove pending event deliveries.
400c18f2fdfcf8ec7a8534c032c4102739edb7f1c5eZachary Kuznia        mSendHoverEnterAndMoveDelayed.cancel();
401c18f2fdfcf8ec7a8534c032c4102739edb7f1c5eZachary Kuznia        mSendHoverExitDelayed.cancel();
402c18f2fdfcf8ec7a8534c032c4102739edb7f1c5eZachary Kuznia
403c18f2fdfcf8ec7a8534c032c4102739edb7f1c5eZachary Kuznia        if (mSendTouchExplorationEndDelayed.isPending()) {
404c18f2fdfcf8ec7a8534c032c4102739edb7f1c5eZachary Kuznia            mSendTouchExplorationEndDelayed.forceSendAndRemove();
405c18f2fdfcf8ec7a8534c032c4102739edb7f1c5eZachary Kuznia        }
406c18f2fdfcf8ec7a8534c032c4102739edb7f1c5eZachary Kuznia        if (mSendTouchInteractionEndDelayed.isPending()) {
407c18f2fdfcf8ec7a8534c032c4102739edb7f1c5eZachary Kuznia            mSendTouchInteractionEndDelayed.forceSendAndRemove();
408c18f2fdfcf8ec7a8534c032c4102739edb7f1c5eZachary Kuznia        }
409c18f2fdfcf8ec7a8534c032c4102739edb7f1c5eZachary Kuznia
410c18f2fdfcf8ec7a8534c032c4102739edb7f1c5eZachary Kuznia        final int pointerIndex = event.getActionIndex();
411c18f2fdfcf8ec7a8534c032c4102739edb7f1c5eZachary Kuznia        final int pointerId = event.getPointerId(pointerIndex);
412c18f2fdfcf8ec7a8534c032c4102739edb7f1c5eZachary Kuznia
413c18f2fdfcf8ec7a8534c032c4102739edb7f1c5eZachary Kuznia        Point clickLocation = mTempPoint;
414c18f2fdfcf8ec7a8534c032c4102739edb7f1c5eZachary Kuznia        final int result = computeClickLocation(clickLocation);
415c18f2fdfcf8ec7a8534c032c4102739edb7f1c5eZachary Kuznia        if (result == CLICK_LOCATION_NONE) {
416407df712e2a381e0227eb780786128a50bfcf5f5Zachary Kuznia            // We can't send a click to no location, but the gesture was still
417407df712e2a381e0227eb780786128a50bfcf5f5Zachary Kuznia            // consumed.
418407df712e2a381e0227eb780786128a50bfcf5f5Zachary Kuznia            return true;
419c18f2fdfcf8ec7a8534c032c4102739edb7f1c5eZachary Kuznia        }
420c18f2fdfcf8ec7a8534c032c4102739edb7f1c5eZachary Kuznia
421c18f2fdfcf8ec7a8534c032c4102739edb7f1c5eZachary Kuznia        // Do the click.
422c18f2fdfcf8ec7a8534c032c4102739edb7f1c5eZachary Kuznia        PointerProperties[] properties = new PointerProperties[1];
423c18f2fdfcf8ec7a8534c032c4102739edb7f1c5eZachary Kuznia        properties[0] = new PointerProperties();
424c18f2fdfcf8ec7a8534c032c4102739edb7f1c5eZachary Kuznia        event.getPointerProperties(pointerIndex, properties[0]);
425c18f2fdfcf8ec7a8534c032c4102739edb7f1c5eZachary Kuznia        PointerCoords[] coords = new PointerCoords[1];
426c18f2fdfcf8ec7a8534c032c4102739edb7f1c5eZachary Kuznia        coords[0] = new PointerCoords();
427c18f2fdfcf8ec7a8534c032c4102739edb7f1c5eZachary Kuznia        coords[0].x = clickLocation.x;
428c18f2fdfcf8ec7a8534c032c4102739edb7f1c5eZachary Kuznia        coords[0].y = clickLocation.y;
429c18f2fdfcf8ec7a8534c032c4102739edb7f1c5eZachary Kuznia        MotionEvent click_event = MotionEvent.obtain(event.getDownTime(),
430c18f2fdfcf8ec7a8534c032c4102739edb7f1c5eZachary Kuznia                event.getEventTime(), MotionEvent.ACTION_DOWN, 1, properties,
431c18f2fdfcf8ec7a8534c032c4102739edb7f1c5eZachary Kuznia                coords, 0, 0, 1.0f, 1.0f, event.getDeviceId(), 0,
432c18f2fdfcf8ec7a8534c032c4102739edb7f1c5eZachary Kuznia                event.getSource(), event.getFlags());
433c18f2fdfcf8ec7a8534c032c4102739edb7f1c5eZachary Kuznia        final boolean targetAccessibilityFocus = (result == CLICK_LOCATION_ACCESSIBILITY_FOCUS);
434c18f2fdfcf8ec7a8534c032c4102739edb7f1c5eZachary Kuznia        sendActionDownAndUp(click_event, policyFlags, targetAccessibilityFocus);
435c18f2fdfcf8ec7a8534c032c4102739edb7f1c5eZachary Kuznia        click_event.recycle();
436407df712e2a381e0227eb780786128a50bfcf5f5Zachary Kuznia        return true;
437407df712e2a381e0227eb780786128a50bfcf5f5Zachary Kuznia    }
438407df712e2a381e0227eb780786128a50bfcf5f5Zachary Kuznia
439407df712e2a381e0227eb780786128a50bfcf5f5Zachary Kuznia    @Override
440bf1cf52663414eec1d80d23edc029bf6223adb3bZachary Kuznia    public boolean onGestureStarted() {
441bf1cf52663414eec1d80d23edc029bf6223adb3bZachary Kuznia      // We have to perform gesture detection, so
442bf1cf52663414eec1d80d23edc029bf6223adb3bZachary Kuznia      // clear the current state and try to detect.
443bf1cf52663414eec1d80d23edc029bf6223adb3bZachary Kuznia      mCurrentState = STATE_GESTURE_DETECTING;
444bf1cf52663414eec1d80d23edc029bf6223adb3bZachary Kuznia      mSendHoverEnterAndMoveDelayed.cancel();
445bf1cf52663414eec1d80d23edc029bf6223adb3bZachary Kuznia      mSendHoverExitDelayed.cancel();
446bf1cf52663414eec1d80d23edc029bf6223adb3bZachary Kuznia      mExitGestureDetectionModeDelayed.post();
447bf1cf52663414eec1d80d23edc029bf6223adb3bZachary Kuznia      // Send accessibility event to announce the start
448bf1cf52663414eec1d80d23edc029bf6223adb3bZachary Kuznia      // of gesture recognition.
449bf1cf52663414eec1d80d23edc029bf6223adb3bZachary Kuznia      sendAccessibilityEvent(AccessibilityEvent.TYPE_GESTURE_DETECTION_START);
450bf1cf52663414eec1d80d23edc029bf6223adb3bZachary Kuznia      return false;
451bf1cf52663414eec1d80d23edc029bf6223adb3bZachary Kuznia    }
452bf1cf52663414eec1d80d23edc029bf6223adb3bZachary Kuznia
453bf1cf52663414eec1d80d23edc029bf6223adb3bZachary Kuznia    @Override
4549254b3e336a01ff79742882bd98860b48ebae379Zachary Kuznia    public boolean onGestureCompleted(int gestureId) {
455407df712e2a381e0227eb780786128a50bfcf5f5Zachary Kuznia        if (mCurrentState != STATE_GESTURE_DETECTING) {
456407df712e2a381e0227eb780786128a50bfcf5f5Zachary Kuznia            return false;
457407df712e2a381e0227eb780786128a50bfcf5f5Zachary Kuznia        }
458407df712e2a381e0227eb780786128a50bfcf5f5Zachary Kuznia
4599254b3e336a01ff79742882bd98860b48ebae379Zachary Kuznia        endGestureDetection();
460407df712e2a381e0227eb780786128a50bfcf5f5Zachary Kuznia
461407df712e2a381e0227eb780786128a50bfcf5f5Zachary Kuznia        mAms.onGesture(gestureId);
462407df712e2a381e0227eb780786128a50bfcf5f5Zachary Kuznia
463407df712e2a381e0227eb780786128a50bfcf5f5Zachary Kuznia        return true;
464c18f2fdfcf8ec7a8534c032c4102739edb7f1c5eZachary Kuznia    }
465c18f2fdfcf8ec7a8534c032c4102739edb7f1c5eZachary Kuznia
4669254b3e336a01ff79742882bd98860b48ebae379Zachary Kuznia    @Override
467bf1cf52663414eec1d80d23edc029bf6223adb3bZachary Kuznia    public boolean onGestureCancelled(MotionEvent event, int policyFlags) {
468bf1cf52663414eec1d80d23edc029bf6223adb3bZachary Kuznia        if (mCurrentState == STATE_GESTURE_DETECTING) {
469bf1cf52663414eec1d80d23edc029bf6223adb3bZachary Kuznia            endGestureDetection();
470bf1cf52663414eec1d80d23edc029bf6223adb3bZachary Kuznia            return true;
471bf1cf52663414eec1d80d23edc029bf6223adb3bZachary Kuznia        } else if (mCurrentState == STATE_TOUCH_EXPLORING) {
472bf1cf52663414eec1d80d23edc029bf6223adb3bZachary Kuznia            // If the finger is still moving, pass the event on.
473bf1cf52663414eec1d80d23edc029bf6223adb3bZachary Kuznia            if (event.getActionMasked() == MotionEvent.ACTION_MOVE) {
474bf1cf52663414eec1d80d23edc029bf6223adb3bZachary Kuznia                final int pointerId = mReceivedPointerTracker.getPrimaryPointerId();
475bf1cf52663414eec1d80d23edc029bf6223adb3bZachary Kuznia                final int pointerIdBits = (1 << pointerId);
476bf1cf52663414eec1d80d23edc029bf6223adb3bZachary Kuznia
477bf1cf52663414eec1d80d23edc029bf6223adb3bZachary Kuznia                // We have just decided that the user is touch,
478bf1cf52663414eec1d80d23edc029bf6223adb3bZachary Kuznia                // exploring so start sending events.
479bf1cf52663414eec1d80d23edc029bf6223adb3bZachary Kuznia                mSendHoverEnterAndMoveDelayed.addEvent(event);
480bf1cf52663414eec1d80d23edc029bf6223adb3bZachary Kuznia                mSendHoverEnterAndMoveDelayed.forceSendAndRemove();
481bf1cf52663414eec1d80d23edc029bf6223adb3bZachary Kuznia                mSendHoverExitDelayed.cancel();
482bf1cf52663414eec1d80d23edc029bf6223adb3bZachary Kuznia                sendMotionEvent(event, MotionEvent.ACTION_HOVER_MOVE, pointerIdBits, policyFlags);
483bf1cf52663414eec1d80d23edc029bf6223adb3bZachary Kuznia                return true;
484bf1cf52663414eec1d80d23edc029bf6223adb3bZachary Kuznia            }
485bf1cf52663414eec1d80d23edc029bf6223adb3bZachary Kuznia        }
486bf1cf52663414eec1d80d23edc029bf6223adb3bZachary Kuznia        return false;
4879254b3e336a01ff79742882bd98860b48ebae379Zachary Kuznia    }
4889254b3e336a01ff79742882bd98860b48ebae379Zachary Kuznia
489736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov    /**
490736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov     * Handles a motion event in touch exploring state.
491736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov     *
492736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov     * @param event The event to be handled.
49345af84a483165f06c04d74baba67f90da29c6ad2Svetoslav Ganov     * @param rawEvent The raw (unmodified) motion event.
494736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov     * @param policyFlags The policy flags associated with the event.
495736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov     */
49645af84a483165f06c04d74baba67f90da29c6ad2Svetoslav Ganov    private void handleMotionEventStateTouchExploring(MotionEvent event, MotionEvent rawEvent,
49745af84a483165f06c04d74baba67f90da29c6ad2Svetoslav Ganov            int policyFlags) {
4984213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov        ReceivedPointerTracker receivedTracker = mReceivedPointerTracker;
4994213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov
500736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov        switch (event.getActionMasked()) {
50184044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav            case MotionEvent.ACTION_DOWN: {
5026ae8a24fc045bc7970f2843fa9baf06aff15e22dSvetoslav Ganov                mAms.onTouchInteractionStart();
50384044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav
50484044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav                sendAccessibilityEvent(AccessibilityEvent.TYPE_TOUCH_INTERACTION_START);
505736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov
50684044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav                // If we still have not notified the user for the last
50784044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav                // touch, we figure out what to do. If were waiting
50884044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav                // we resent the delayed callback and wait again.
50984044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav                mSendHoverEnterAndMoveDelayed.cancel();
51084044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav                mSendHoverExitDelayed.cancel();
511f068fed6c4c3fc2003aec19b6e7e892358179b02Svetoslav Ganov
51284044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav                if (mSendTouchExplorationEndDelayed.isPending()) {
51384044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav                    mSendTouchExplorationEndDelayed.forceSendAndRemove();
51484044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav                }
515f068fed6c4c3fc2003aec19b6e7e892358179b02Svetoslav Ganov
51684044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav                if (mSendTouchInteractionEndDelayed.isPending()) {
51784044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav                    mSendTouchInteractionEndDelayed.forceSendAndRemove();
51884044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav                }
519fe304b893968887323b93764caafa66ee8ad44deSvetoslav Ganov
520c18f2fdfcf8ec7a8534c032c4102739edb7f1c5eZachary Kuznia                if (!mGestureDetector.firstTapDetected() && !mTouchExplorationInProgress) {
52138c992841b5f6bc80359dbf60d31aa7b994160fcSvetoslav Ganov                    if (!mSendHoverEnterAndMoveDelayed.isPending()) {
52238c992841b5f6bc80359dbf60d31aa7b994160fcSvetoslav Ganov                        // Deliver hover enter with a delay to have a chance
52338c992841b5f6bc80359dbf60d31aa7b994160fcSvetoslav Ganov                        // to detect what the user is trying to do.
52438c992841b5f6bc80359dbf60d31aa7b994160fcSvetoslav Ganov                        final int pointerId = receivedTracker.getPrimaryPointerId();
52538c992841b5f6bc80359dbf60d31aa7b994160fcSvetoslav Ganov                        final int pointerIdBits = (1 << pointerId);
52638c992841b5f6bc80359dbf60d31aa7b994160fcSvetoslav Ganov                        mSendHoverEnterAndMoveDelayed.post(event, true, pointerIdBits,
52738c992841b5f6bc80359dbf60d31aa7b994160fcSvetoslav Ganov                                policyFlags);
528a3315270e0502189a6fa4de20b9db2bf042cfd2aSvetoslav                    } else {
529a3315270e0502189a6fa4de20b9db2bf042cfd2aSvetoslav                        // Cache the event until we discern exploration from gesturing.
530a3315270e0502189a6fa4de20b9db2bf042cfd2aSvetoslav                        mSendHoverEnterAndMoveDelayed.addEvent(event);
53138c992841b5f6bc80359dbf60d31aa7b994160fcSvetoslav Ganov                    }
53284044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav                }
53384044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav            } break;
53484044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav            case MotionEvent.ACTION_POINTER_DOWN: {
535a3315270e0502189a6fa4de20b9db2bf042cfd2aSvetoslav                // Another finger down means that if we have not started to deliver
536a3315270e0502189a6fa4de20b9db2bf042cfd2aSvetoslav                // hover events, we will not have to. The code for ACTION_MOVE will
537a3315270e0502189a6fa4de20b9db2bf042cfd2aSvetoslav                // decide what we will actually do next.
538a3315270e0502189a6fa4de20b9db2bf042cfd2aSvetoslav                mSendHoverEnterAndMoveDelayed.cancel();
539a3315270e0502189a6fa4de20b9db2bf042cfd2aSvetoslav                mSendHoverExitDelayed.cancel();
540736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov            } break;
541736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov            case MotionEvent.ACTION_MOVE: {
54284044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav                final int pointerId = receivedTracker.getPrimaryPointerId();
54391feae3c5994bd4768cea3507c62c65746adcfa6Svetoslav Ganov                final int pointerIndex = event.findPointerIndex(pointerId);
54491feae3c5994bd4768cea3507c62c65746adcfa6Svetoslav Ganov                final int pointerIdBits = (1 << pointerId);
54584044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav                switch (event.getPointerCount()) {
546736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov                    case 1: {
547e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov                        // We have not started sending events since we try to
548e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov                        // figure out what the user is doing.
54984044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav                        if (mSendHoverEnterAndMoveDelayed.isPending()) {
55084044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav                            // Cache the event until we discern exploration from gesturing.
55184044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav                            mSendHoverEnterAndMoveDelayed.addEvent(event);
552736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov                        } else {
553a3315270e0502189a6fa4de20b9db2bf042cfd2aSvetoslav                            if (mTouchExplorationInProgress) {
554a3315270e0502189a6fa4de20b9db2bf042cfd2aSvetoslav                                sendTouchExplorationGestureStartAndHoverEnterIfNeeded(policyFlags);
555a3315270e0502189a6fa4de20b9db2bf042cfd2aSvetoslav                                sendMotionEvent(event, MotionEvent.ACTION_HOVER_MOVE, pointerIdBits,
556a3315270e0502189a6fa4de20b9db2bf042cfd2aSvetoslav                                        policyFlags);
557e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov                            }
558736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov                        }
559e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov                    } break;
560e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov                    case 2: {
561e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov                        // More than one pointer so the user is not touch exploring
562e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov                        // and now we have to decide whether to delegate or drag.
56384044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav                        if (mSendHoverEnterAndMoveDelayed.isPending()) {
564e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov                            // We have not started sending events so cancel
565e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov                            // scheduled sending events.
56684044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav                            mSendHoverEnterAndMoveDelayed.cancel();
56784044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav                            mSendHoverExitDelayed.cancel();
568e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov                        } else {
569a3315270e0502189a6fa4de20b9db2bf042cfd2aSvetoslav                            if (mTouchExplorationInProgress) {
570a3315270e0502189a6fa4de20b9db2bf042cfd2aSvetoslav                                // If the user is touch exploring the second pointer may be
571a3315270e0502189a6fa4de20b9db2bf042cfd2aSvetoslav                                // performing a double tap to activate an item without need
572a3315270e0502189a6fa4de20b9db2bf042cfd2aSvetoslav                                // for the user to lift his exploring finger.
573a3315270e0502189a6fa4de20b9db2bf042cfd2aSvetoslav                                // It is *important* to use the distance traveled by the pointers
574a3315270e0502189a6fa4de20b9db2bf042cfd2aSvetoslav                                // on the screen which may or may not be magnified.
575a3315270e0502189a6fa4de20b9db2bf042cfd2aSvetoslav                                final float deltaX = receivedTracker.getReceivedPointerDownX(
576a3315270e0502189a6fa4de20b9db2bf042cfd2aSvetoslav                                        pointerId) - rawEvent.getX(pointerIndex);
577a3315270e0502189a6fa4de20b9db2bf042cfd2aSvetoslav                                final float deltaY = receivedTracker.getReceivedPointerDownY(
578a3315270e0502189a6fa4de20b9db2bf042cfd2aSvetoslav                                        pointerId) - rawEvent.getY(pointerIndex);
579a3315270e0502189a6fa4de20b9db2bf042cfd2aSvetoslav                                final double moveDelta = Math.hypot(deltaX, deltaY);
580a3315270e0502189a6fa4de20b9db2bf042cfd2aSvetoslav                                if (moveDelta < mDoubleTapSlop) {
581a3315270e0502189a6fa4de20b9db2bf042cfd2aSvetoslav                                    break;
582a3315270e0502189a6fa4de20b9db2bf042cfd2aSvetoslav                                }
583a3315270e0502189a6fa4de20b9db2bf042cfd2aSvetoslav                                // We are sending events so send exit and gesture
584a3315270e0502189a6fa4de20b9db2bf042cfd2aSvetoslav                                // end since we transition to another state.
585a3315270e0502189a6fa4de20b9db2bf042cfd2aSvetoslav                                sendHoverExitAndTouchExplorationGestureEndIfNeeded(policyFlags);
586736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov                            }
587736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov                        }
588e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov
589736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov                        if (isDraggingGesture(event)) {
590736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov                            // Two pointers moving in the same direction within
591736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov                            // a given distance perform a drag.
59200f7b3f76515d1c6fbe5cf9fee9d3760787c03cdSvetoslav Ganov                            mCurrentState = STATE_DRAGGING;
59391feae3c5994bd4768cea3507c62c65746adcfa6Svetoslav Ganov                            mDraggingPointerId = pointerId;
594c4842c11932ea4f60fe7ae09b0a59660207e1587Svetoslav Ganov                            event.setEdgeFlags(receivedTracker.getLastReceivedDownEdgeFlags());
59591feae3c5994bd4768cea3507c62c65746adcfa6Svetoslav Ganov                            sendMotionEvent(event, MotionEvent.ACTION_DOWN, pointerIdBits,
59691feae3c5994bd4768cea3507c62c65746adcfa6Svetoslav Ganov                                    policyFlags);
597736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov                        } else {
598736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov                            // Two pointers moving arbitrary are delegated to the view hierarchy.
599736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov                            mCurrentState = STATE_DELEGATING;
60084044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav                            sendDownForAllNotInjectedPointers(event, policyFlags);
601736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov                        }
602736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov                    } break;
603736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov                    default: {
604e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov                        // More than one pointer so the user is not touch exploring
605e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov                        // and now we have to decide whether to delegate or drag.
60684044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav                        if (mSendHoverEnterAndMoveDelayed.isPending()) {
607e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov                            // We have not started sending events so cancel
608e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov                            // scheduled sending events.
60984044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav                            mSendHoverEnterAndMoveDelayed.cancel();
61084044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav                            mSendHoverExitDelayed.cancel();
611e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov                        } else {
612e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov                            // We are sending events so send exit and gesture
613e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov                            // end since we transition to another state.
614f068fed6c4c3fc2003aec19b6e7e892358179b02Svetoslav Ganov                            sendHoverExitAndTouchExplorationGestureEndIfNeeded(policyFlags);
615e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov                        }
616736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov
617736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov                        // More than two pointers are delegated to the view hierarchy.
618736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov                        mCurrentState = STATE_DELEGATING;
61984044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav                        sendDownForAllNotInjectedPointers(event, policyFlags);
620736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov                    }
621736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov                }
622736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov            } break;
62384044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav            case MotionEvent.ACTION_UP: {
624f772cba59760d1ad9eb5cb9205b2e2e9126e488dSvetoslav Ganov                mAms.onTouchInteractionEnd();
62584044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav                final int pointerId = event.getPointerId(event.getActionIndex());
62691feae3c5994bd4768cea3507c62c65746adcfa6Svetoslav Ganov                final int pointerIdBits = (1 << pointerId);
627736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov
62884044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav                if (mSendHoverEnterAndMoveDelayed.isPending()) {
62984044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav                    // If we have not delivered the enter schedule an exit.
63084044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav                    mSendHoverExitDelayed.post(event, pointerIdBits, policyFlags);
63184044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav                } else {
63284044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav                    // The user is touch exploring so we send events for end.
63384044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav                    sendHoverExitAndTouchExplorationGestureEndIfNeeded(policyFlags);
63484044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav                }
635f068fed6c4c3fc2003aec19b6e7e892358179b02Svetoslav Ganov
63684044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav                if (!mSendTouchInteractionEndDelayed.isPending()) {
63784044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav                    mSendTouchInteractionEndDelayed.post();
638736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov                }
63984044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav
640736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov            } break;
641736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov        }
642736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov    }
643736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov
644736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov    /**
645736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov     * Handles a motion event in dragging state.
646736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov     *
647736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov     * @param event The event to be handled.
648736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov     * @param policyFlags The policy flags associated with the event.
649736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov     */
650736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov    private void handleMotionEventStateDragging(MotionEvent event, int policyFlags) {
651ec1783828e541f22e93e67ec80511b9187bc93beZachary Kuznia        int pointerIdBits = 0;
652ec1783828e541f22e93e67ec80511b9187bc93beZachary Kuznia        // Clear the dragging pointer id if it's no longer valid.
653ec1783828e541f22e93e67ec80511b9187bc93beZachary Kuznia        if (event.findPointerIndex(mDraggingPointerId) == -1) {
654ec1783828e541f22e93e67ec80511b9187bc93beZachary Kuznia            Slog.e(LOG_TAG, "mDraggingPointerId doesn't match any pointers on current event. " +
655ec1783828e541f22e93e67ec80511b9187bc93beZachary Kuznia                    "mDraggingPointerId: " + Integer.toString(mDraggingPointerId) +
656ec1783828e541f22e93e67ec80511b9187bc93beZachary Kuznia                    ", Event: " + event);
657ec1783828e541f22e93e67ec80511b9187bc93beZachary Kuznia            mDraggingPointerId = INVALID_POINTER_ID;
658ec1783828e541f22e93e67ec80511b9187bc93beZachary Kuznia        } else {
659ec1783828e541f22e93e67ec80511b9187bc93beZachary Kuznia            pointerIdBits = (1 << mDraggingPointerId);
660ec1783828e541f22e93e67ec80511b9187bc93beZachary Kuznia        }
661736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov        switch (event.getActionMasked()) {
662736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov            case MotionEvent.ACTION_DOWN: {
663736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov                throw new IllegalStateException("Dragging state can be reached only if two "
664736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov                        + "pointers are already down");
665736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov            }
666736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov            case MotionEvent.ACTION_POINTER_DOWN: {
667736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov                // We are in dragging state so we have two pointers and another one
668736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov                // goes down => delegate the three pointers to the view hierarchy
669736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov                mCurrentState = STATE_DELEGATING;
670ec33d56300aa417efb4a055786d73d1bf23a6a85Svetoslav Ganov                if (mDraggingPointerId != INVALID_POINTER_ID) {
671ec33d56300aa417efb4a055786d73d1bf23a6a85Svetoslav Ganov                    sendMotionEvent(event, MotionEvent.ACTION_UP, pointerIdBits, policyFlags);
672ec33d56300aa417efb4a055786d73d1bf23a6a85Svetoslav Ganov                }
67384044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav                sendDownForAllNotInjectedPointers(event, policyFlags);
674736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov            } break;
675736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov            case MotionEvent.ACTION_MOVE: {
676ec1783828e541f22e93e67ec80511b9187bc93beZachary Kuznia                if (mDraggingPointerId == INVALID_POINTER_ID) {
677ec1783828e541f22e93e67ec80511b9187bc93beZachary Kuznia                    break;
678ec1783828e541f22e93e67ec80511b9187bc93beZachary Kuznia                }
67984044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav                switch (event.getPointerCount()) {
6802e1c66bd53d30d2148afaa4b393b60cd59976d65Svetoslav Ganov                    case 1: {
6812e1c66bd53d30d2148afaa4b393b60cd59976d65Svetoslav Ganov                        // do nothing
6822e1c66bd53d30d2148afaa4b393b60cd59976d65Svetoslav Ganov                    } break;
683736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov                    case 2: {
684736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov                        if (isDraggingGesture(event)) {
68584044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav                            final float firstPtrX = event.getX(0);
68684044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav                            final float firstPtrY = event.getY(0);
68784044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav                            final float secondPtrX = event.getX(1);
68884044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav                            final float secondPtrY = event.getY(1);
689e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov
690e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov                            final float deltaX = firstPtrX - secondPtrX;
691e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov                            final float deltaY = firstPtrY - secondPtrY;
692e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov                            final double distance = Math.hypot(deltaX, deltaY);
693e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov
694e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov                            if (distance > mScaledMinPointerDistanceToUseMiddleLocation) {
695e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov                                event.setLocation(deltaX / 2, deltaY / 2);
696e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov                            }
697e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov
698736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov                            // If still dragging send a drag event.
69991feae3c5994bd4768cea3507c62c65746adcfa6Svetoslav Ganov                            sendMotionEvent(event, MotionEvent.ACTION_MOVE, pointerIdBits,
70091feae3c5994bd4768cea3507c62c65746adcfa6Svetoslav Ganov                                    policyFlags);
701736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov                        } else {
702736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov                            // The two pointers are moving either in different directions or
703736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov                            // no close enough => delegate the gesture to the view hierarchy.
704736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov                            mCurrentState = STATE_DELEGATING;
705736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov                            // Send an event to the end of the drag gesture.
70691feae3c5994bd4768cea3507c62c65746adcfa6Svetoslav Ganov                            sendMotionEvent(event, MotionEvent.ACTION_UP, pointerIdBits,
70791feae3c5994bd4768cea3507c62c65746adcfa6Svetoslav Ganov                                    policyFlags);
70884044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav                            // Deliver all pointers to the view hierarchy.
70984044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav                            sendDownForAllNotInjectedPointers(event, policyFlags);
710736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov                        }
711736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov                    } break;
712736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov                    default: {
713736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov                        mCurrentState = STATE_DELEGATING;
714736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov                        // Send an event to the end of the drag gesture.
71591feae3c5994bd4768cea3507c62c65746adcfa6Svetoslav Ganov                        sendMotionEvent(event, MotionEvent.ACTION_UP, pointerIdBits,
71691feae3c5994bd4768cea3507c62c65746adcfa6Svetoslav Ganov                                policyFlags);
71784044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav                        // Deliver all pointers to the view hierarchy.
71884044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav                        sendDownForAllNotInjectedPointers(event, policyFlags);
719736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov                    }
720736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov                }
721736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov            } break;
722aeb8d0ed0d98d398a66a092c418f4f2bca8719e0Svetoslav Ganov            case MotionEvent.ACTION_POINTER_UP: {
723aeb8d0ed0d98d398a66a092c418f4f2bca8719e0Svetoslav Ganov                 final int pointerId = event.getPointerId(event.getActionIndex());
724aeb8d0ed0d98d398a66a092c418f4f2bca8719e0Svetoslav Ganov                 if (pointerId == mDraggingPointerId) {
725aeb8d0ed0d98d398a66a092c418f4f2bca8719e0Svetoslav Ganov                     mDraggingPointerId = INVALID_POINTER_ID;
726aeb8d0ed0d98d398a66a092c418f4f2bca8719e0Svetoslav Ganov                     // Send an event to the end of the drag gesture.
727aeb8d0ed0d98d398a66a092c418f4f2bca8719e0Svetoslav Ganov                     sendMotionEvent(event, MotionEvent.ACTION_UP, pointerIdBits, policyFlags);
728aeb8d0ed0d98d398a66a092c418f4f2bca8719e0Svetoslav Ganov                 }
729aeb8d0ed0d98d398a66a092c418f4f2bca8719e0Svetoslav Ganov            } break;
7302e1c66bd53d30d2148afaa4b393b60cd59976d65Svetoslav Ganov            case MotionEvent.ACTION_UP: {
731f772cba59760d1ad9eb5cb9205b2e2e9126e488dSvetoslav Ganov                mAms.onTouchInteractionEnd();
73277276b60851a158ad3e142cb3b091d57ae5ceffbSvetoslav Ganov                // Announce the end of a new touch interaction.
73377276b60851a158ad3e142cb3b091d57ae5ceffbSvetoslav Ganov                sendAccessibilityEvent(
73477276b60851a158ad3e142cb3b091d57ae5ceffbSvetoslav Ganov                        AccessibilityEvent.TYPE_TOUCH_INTERACTION_END);
735aeb8d0ed0d98d398a66a092c418f4f2bca8719e0Svetoslav Ganov                final int pointerId = event.getPointerId(event.getActionIndex());
736aeb8d0ed0d98d398a66a092c418f4f2bca8719e0Svetoslav Ganov                if (pointerId == mDraggingPointerId) {
737aeb8d0ed0d98d398a66a092c418f4f2bca8719e0Svetoslav Ganov                    mDraggingPointerId = INVALID_POINTER_ID;
738aeb8d0ed0d98d398a66a092c418f4f2bca8719e0Svetoslav Ganov                    // Send an event to the end of the drag gesture.
739aeb8d0ed0d98d398a66a092c418f4f2bca8719e0Svetoslav Ganov                    sendMotionEvent(event, MotionEvent.ACTION_UP, pointerIdBits, policyFlags);
740aeb8d0ed0d98d398a66a092c418f4f2bca8719e0Svetoslav Ganov                }
7412e1c66bd53d30d2148afaa4b393b60cd59976d65Svetoslav Ganov                mCurrentState = STATE_TOUCH_EXPLORING;
7422e1c66bd53d30d2148afaa4b393b60cd59976d65Svetoslav Ganov            } break;
743736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov        }
744736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov    }
745736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov
746736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov    /**
747736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov     * Handles a motion event in delegating state.
748736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov     *
749736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov     * @param event The event to be handled.
750736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov     * @param policyFlags The policy flags associated with the event.
751736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov     */
7522e1c66bd53d30d2148afaa4b393b60cd59976d65Svetoslav Ganov    private void handleMotionEventStateDelegating(MotionEvent event, int policyFlags) {
753736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov        switch (event.getActionMasked()) {
754736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov            case MotionEvent.ACTION_DOWN: {
755736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov                throw new IllegalStateException("Delegating state can only be reached if "
756736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov                        + "there is at least one pointer down!");
757736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov            }
75884044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav            case MotionEvent.ACTION_UP: {
759a3315270e0502189a6fa4de20b9db2bf042cfd2aSvetoslav                // Offset the event if we are doing a long press as the
760a3315270e0502189a6fa4de20b9db2bf042cfd2aSvetoslav                // target is not necessarily under the user's finger.
761a3315270e0502189a6fa4de20b9db2bf042cfd2aSvetoslav                if (mLongPressingPointerId >= 0) {
762a3315270e0502189a6fa4de20b9db2bf042cfd2aSvetoslav                    event = offsetEvent(event, - mLongPressingPointerDeltaX,
763a3315270e0502189a6fa4de20b9db2bf042cfd2aSvetoslav                            - mLongPressingPointerDeltaY);
764a3315270e0502189a6fa4de20b9db2bf042cfd2aSvetoslav                    // Clear the long press state.
765a3315270e0502189a6fa4de20b9db2bf042cfd2aSvetoslav                    mLongPressingPointerId = -1;
766a3315270e0502189a6fa4de20b9db2bf042cfd2aSvetoslav                    mLongPressingPointerDeltaX = 0;
767a3315270e0502189a6fa4de20b9db2bf042cfd2aSvetoslav                    mLongPressingPointerDeltaY = 0;
768a3315270e0502189a6fa4de20b9db2bf042cfd2aSvetoslav                }
769a3315270e0502189a6fa4de20b9db2bf042cfd2aSvetoslav
770a3315270e0502189a6fa4de20b9db2bf042cfd2aSvetoslav                // Deliver the event.
771a3315270e0502189a6fa4de20b9db2bf042cfd2aSvetoslav                sendMotionEvent(event, event.getAction(), ALL_POINTER_ID_BITS, policyFlags);
772a3315270e0502189a6fa4de20b9db2bf042cfd2aSvetoslav
77384044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav                // Announce the end of a the touch interaction.
774a3315270e0502189a6fa4de20b9db2bf042cfd2aSvetoslav                mAms.onTouchInteractionEnd();
77584044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav                sendAccessibilityEvent(AccessibilityEvent.TYPE_TOUCH_INTERACTION_END);
776a3315270e0502189a6fa4de20b9db2bf042cfd2aSvetoslav
77784044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav                mCurrentState = STATE_TOUCH_EXPLORING;
778736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov            } break;
779a3315270e0502189a6fa4de20b9db2bf042cfd2aSvetoslav            default: {
780a3315270e0502189a6fa4de20b9db2bf042cfd2aSvetoslav                // Deliver the event.
781a3315270e0502189a6fa4de20b9db2bf042cfd2aSvetoslav                sendMotionEvent(event, event.getAction(), ALL_POINTER_ID_BITS, policyFlags);
782a3315270e0502189a6fa4de20b9db2bf042cfd2aSvetoslav            }
783736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov        }
784736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov    }
785736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov
7869254b3e336a01ff79742882bd98860b48ebae379Zachary Kuznia    private void endGestureDetection() {
7879254b3e336a01ff79742882bd98860b48ebae379Zachary Kuznia        mAms.onTouchInteractionEnd();
78877276b60851a158ad3e142cb3b091d57ae5ceffbSvetoslav Ganov
7899254b3e336a01ff79742882bd98860b48ebae379Zachary Kuznia        // Announce the end of the gesture recognition.
7909254b3e336a01ff79742882bd98860b48ebae379Zachary Kuznia        sendAccessibilityEvent(AccessibilityEvent.TYPE_GESTURE_DETECTION_END);
7919254b3e336a01ff79742882bd98860b48ebae379Zachary Kuznia        // Announce the end of a the touch interaction.
7929254b3e336a01ff79742882bd98860b48ebae379Zachary Kuznia        sendAccessibilityEvent(AccessibilityEvent.TYPE_TOUCH_INTERACTION_END);
7939254b3e336a01ff79742882bd98860b48ebae379Zachary Kuznia
7949254b3e336a01ff79742882bd98860b48ebae379Zachary Kuznia        mExitGestureDetectionModeDelayed.cancel();
7959254b3e336a01ff79742882bd98860b48ebae379Zachary Kuznia        mCurrentState = STATE_TOUCH_EXPLORING;
7964213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov    }
7974213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov
798736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov    /**
79977276b60851a158ad3e142cb3b091d57ae5ceffbSvetoslav Ganov     * Sends an accessibility event of the given type.
80077276b60851a158ad3e142cb3b091d57ae5ceffbSvetoslav Ganov     *
80177276b60851a158ad3e142cb3b091d57ae5ceffbSvetoslav Ganov     * @param type The event type.
80277276b60851a158ad3e142cb3b091d57ae5ceffbSvetoslav Ganov     */
80377276b60851a158ad3e142cb3b091d57ae5ceffbSvetoslav Ganov    private void sendAccessibilityEvent(int type) {
80477276b60851a158ad3e142cb3b091d57ae5ceffbSvetoslav Ganov        AccessibilityManager accessibilityManager = AccessibilityManager.getInstance(mContext);
80577276b60851a158ad3e142cb3b091d57ae5ceffbSvetoslav Ganov        if (accessibilityManager.isEnabled()) {
80677276b60851a158ad3e142cb3b091d57ae5ceffbSvetoslav Ganov            AccessibilityEvent event = AccessibilityEvent.obtain(type);
8073d1c5a7236c4709550ca7c0cfa293fc5c974c56bAlan Viverette            event.setWindowId(mAms.getActiveWindowId());
80877276b60851a158ad3e142cb3b091d57ae5ceffbSvetoslav Ganov            accessibilityManager.sendAccessibilityEvent(event);
809f068fed6c4c3fc2003aec19b6e7e892358179b02Svetoslav Ganov            switch (type) {
810f068fed6c4c3fc2003aec19b6e7e892358179b02Svetoslav Ganov                case AccessibilityEvent.TYPE_TOUCH_EXPLORATION_GESTURE_START: {
811f068fed6c4c3fc2003aec19b6e7e892358179b02Svetoslav Ganov                    mTouchExplorationInProgress = true;
812f068fed6c4c3fc2003aec19b6e7e892358179b02Svetoslav Ganov                } break;
813f068fed6c4c3fc2003aec19b6e7e892358179b02Svetoslav Ganov                case AccessibilityEvent.TYPE_TOUCH_EXPLORATION_GESTURE_END: {
814f068fed6c4c3fc2003aec19b6e7e892358179b02Svetoslav Ganov                    mTouchExplorationInProgress = false;
815f068fed6c4c3fc2003aec19b6e7e892358179b02Svetoslav Ganov                } break;
816f068fed6c4c3fc2003aec19b6e7e892358179b02Svetoslav Ganov            }
81777276b60851a158ad3e142cb3b091d57ae5ceffbSvetoslav Ganov        }
81877276b60851a158ad3e142cb3b091d57ae5ceffbSvetoslav Ganov    }
81977276b60851a158ad3e142cb3b091d57ae5ceffbSvetoslav Ganov
82077276b60851a158ad3e142cb3b091d57ae5ceffbSvetoslav Ganov    /**
82184044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav     * Sends down events to the view hierarchy for all pointers which are
822736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov     * not already being delivered i.e. pointers that are not yet injected.
823736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov     *
824736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov     * @param prototype The prototype from which to create the injected events.
825736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov     * @param policyFlags The policy flags associated with the event.
826736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov     */
82784044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav    private void sendDownForAllNotInjectedPointers(MotionEvent prototype, int policyFlags) {
8284213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov        InjectedPointerTracker injectedPointers = mInjectedPointerTracker;
82984044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav
83084044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav        // Inject the injected pointers.
831f804420d6e37748b75478406e989c69303756980Svetoslav Ganov        int pointerIdBits = 0;
832f804420d6e37748b75478406e989c69303756980Svetoslav Ganov        final int pointerCount = prototype.getPointerCount();
833f804420d6e37748b75478406e989c69303756980Svetoslav Ganov        for (int i = 0; i < pointerCount; i++) {
834f804420d6e37748b75478406e989c69303756980Svetoslav Ganov            final int pointerId = prototype.getPointerId(i);
835f804420d6e37748b75478406e989c69303756980Svetoslav Ganov            // Do not send event for already delivered pointers.
83684044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav            if (!injectedPointers.isInjectedPointerDown(pointerId)) {
83784044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav                pointerIdBits |= (1 << pointerId);
83884044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav                final int action = computeInjectionAction(MotionEvent.ACTION_DOWN, i);
83984044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav                sendMotionEvent(prototype, action, pointerIdBits, policyFlags);
840736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov            }
841f804420d6e37748b75478406e989c69303756980Svetoslav Ganov        }
842f804420d6e37748b75478406e989c69303756980Svetoslav Ganov    }
843736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov
844f804420d6e37748b75478406e989c69303756980Svetoslav Ganov    /**
845e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov     * Sends the exit events if needed. Such events are hover exit and touch explore
846e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov     * gesture end.
847e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov     *
848e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov     * @param policyFlags The policy flags associated with the event.
849e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov     */
850f068fed6c4c3fc2003aec19b6e7e892358179b02Svetoslav Ganov    private void sendHoverExitAndTouchExplorationGestureEndIfNeeded(int policyFlags) {
851e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov        MotionEvent event = mInjectedPointerTracker.getLastInjectedHoverEvent();
852e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov        if (event != null && event.getActionMasked() != MotionEvent.ACTION_HOVER_EXIT) {
853e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov            final int pointerIdBits = event.getPointerIdBits();
854f068fed6c4c3fc2003aec19b6e7e892358179b02Svetoslav Ganov            if (!mSendTouchExplorationEndDelayed.isPending()) {
855f068fed6c4c3fc2003aec19b6e7e892358179b02Svetoslav Ganov                mSendTouchExplorationEndDelayed.post();
856fe304b893968887323b93764caafa66ee8ad44deSvetoslav Ganov            }
857e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov            sendMotionEvent(event, MotionEvent.ACTION_HOVER_EXIT, pointerIdBits, policyFlags);
858e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov        }
859e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov    }
860e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov
861e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov    /**
862e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov     * Sends the enter events if needed. Such events are hover enter and touch explore
863e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov     * gesture start.
864f804420d6e37748b75478406e989c69303756980Svetoslav Ganov     *
865f804420d6e37748b75478406e989c69303756980Svetoslav Ganov     * @param policyFlags The policy flags associated with the event.
866f804420d6e37748b75478406e989c69303756980Svetoslav Ganov     */
867f068fed6c4c3fc2003aec19b6e7e892358179b02Svetoslav Ganov    private void sendTouchExplorationGestureStartAndHoverEnterIfNeeded(int policyFlags) {
868e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov        MotionEvent event = mInjectedPointerTracker.getLastInjectedHoverEvent();
869e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov        if (event != null && event.getActionMasked() == MotionEvent.ACTION_HOVER_EXIT) {
870e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov            final int pointerIdBits = event.getPointerIdBits();
871f068fed6c4c3fc2003aec19b6e7e892358179b02Svetoslav Ganov            sendAccessibilityEvent(AccessibilityEvent.TYPE_TOUCH_EXPLORATION_GESTURE_START);
872e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov            sendMotionEvent(event, MotionEvent.ACTION_HOVER_ENTER, pointerIdBits, policyFlags);
873736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov        }
874736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov    }
875736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov
876736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov    /**
87784044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav     * Sends up events to the view hierarchy for all pointers which are
878736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov     * already being delivered i.e. pointers that are injected.
879736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov     *
880736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov     * @param prototype The prototype from which to create the injected events.
881736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov     * @param policyFlags The policy flags associated with the event.
882736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov     */
883736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov    private void sendUpForInjectedDownPointers(MotionEvent prototype, int policyFlags) {
8844213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov        final InjectedPointerTracker injectedTracked = mInjectedPointerTracker;
885f804420d6e37748b75478406e989c69303756980Svetoslav Ganov        int pointerIdBits = 0;
886f804420d6e37748b75478406e989c69303756980Svetoslav Ganov        final int pointerCount = prototype.getPointerCount();
887736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov        for (int i = 0; i < pointerCount; i++) {
888736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov            final int pointerId = prototype.getPointerId(i);
889736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov            // Skip non injected down pointers.
8904213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov            if (!injectedTracked.isInjectedPointerDown(pointerId)) {
891736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov                continue;
892736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov            }
893f804420d6e37748b75478406e989c69303756980Svetoslav Ganov            pointerIdBits |= (1 << pointerId);
894f804420d6e37748b75478406e989c69303756980Svetoslav Ganov            final int action = computeInjectionAction(MotionEvent.ACTION_UP, i);
895f804420d6e37748b75478406e989c69303756980Svetoslav Ganov            sendMotionEvent(prototype, action, pointerIdBits, policyFlags);
896736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov        }
897736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov    }
898736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov
899736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov    /**
900736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov     * Sends an up and down events.
901736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov     *
902736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov     * @param prototype The prototype from which to create the injected events.
903736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov     * @param policyFlags The policy flags associated with the event.
904ded133c446fa9d0d23e6bde19a66fb2ce3980491Svetoslav     * @param targetAccessibilityFocus Whether the event targets the accessibility focus.
905736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov     */
906ded133c446fa9d0d23e6bde19a66fb2ce3980491Svetoslav    private void sendActionDownAndUp(MotionEvent prototype, int policyFlags,
907ded133c446fa9d0d23e6bde19a66fb2ce3980491Svetoslav            boolean targetAccessibilityFocus) {
90884044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav        // Tap with the pointer that last explored.
909bd206d129fdd1777b9f9646a834d7fc342a8941eSvetoslav Ganov        final int pointerId = prototype.getPointerId(prototype.getActionIndex());
910f804420d6e37748b75478406e989c69303756980Svetoslav Ganov        final int pointerIdBits = (1 << pointerId);
911ded133c446fa9d0d23e6bde19a66fb2ce3980491Svetoslav        prototype.setTargetAccessibilityFocus(targetAccessibilityFocus);
912f804420d6e37748b75478406e989c69303756980Svetoslav Ganov        sendMotionEvent(prototype, MotionEvent.ACTION_DOWN, pointerIdBits, policyFlags);
913ded133c446fa9d0d23e6bde19a66fb2ce3980491Svetoslav        prototype.setTargetAccessibilityFocus(targetAccessibilityFocus);
914f804420d6e37748b75478406e989c69303756980Svetoslav Ganov        sendMotionEvent(prototype, MotionEvent.ACTION_UP, pointerIdBits, policyFlags);
915736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov    }
916736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov
917736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov    /**
91891feae3c5994bd4768cea3507c62c65746adcfa6Svetoslav Ganov     * Sends an event.
919736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov     *
92000f7b3f76515d1c6fbe5cf9fee9d3760787c03cdSvetoslav Ganov     * @param prototype The prototype from which to create the injected events.
92191feae3c5994bd4768cea3507c62c65746adcfa6Svetoslav Ganov     * @param action The action of the event.
92291feae3c5994bd4768cea3507c62c65746adcfa6Svetoslav Ganov     * @param pointerIdBits The bits of the pointers to send.
923736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov     * @param policyFlags The policy flags associated with the event.
924736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov     */
92591feae3c5994bd4768cea3507c62c65746adcfa6Svetoslav Ganov    private void sendMotionEvent(MotionEvent prototype, int action, int pointerIdBits,
92691feae3c5994bd4768cea3507c62c65746adcfa6Svetoslav Ganov            int policyFlags) {
927f804420d6e37748b75478406e989c69303756980Svetoslav Ganov        prototype.setAction(action);
928f804420d6e37748b75478406e989c69303756980Svetoslav Ganov
929f804420d6e37748b75478406e989c69303756980Svetoslav Ganov        MotionEvent event = null;
930f804420d6e37748b75478406e989c69303756980Svetoslav Ganov        if (pointerIdBits == ALL_POINTER_ID_BITS) {
931f804420d6e37748b75478406e989c69303756980Svetoslav Ganov            event = prototype;
932f804420d6e37748b75478406e989c69303756980Svetoslav Ganov        } else {
933f804420d6e37748b75478406e989c69303756980Svetoslav Ganov            event = prototype.split(pointerIdBits);
934f804420d6e37748b75478406e989c69303756980Svetoslav Ganov        }
935f804420d6e37748b75478406e989c69303756980Svetoslav Ganov        if (action == MotionEvent.ACTION_DOWN) {
936f804420d6e37748b75478406e989c69303756980Svetoslav Ganov            event.setDownTime(event.getEventTime());
937f804420d6e37748b75478406e989c69303756980Svetoslav Ganov        } else {
9384213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov            event.setDownTime(mInjectedPointerTracker.getLastInjectedDownEventTime());
939f804420d6e37748b75478406e989c69303756980Svetoslav Ganov        }
940f804420d6e37748b75478406e989c69303756980Svetoslav Ganov
941e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov        // If the user is long pressing but the long pressing pointer
942e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov        // was not exactly over the accessibility focused item we need
943e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov        // to remap the location of that pointer so the user does not
944e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov        // have to explicitly touch explore something to be able to
945e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov        // long press it, or even worse to avoid the user long pressing
946e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov        // on the wrong item since click and long press behave differently.
947e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov        if (mLongPressingPointerId >= 0) {
948a3315270e0502189a6fa4de20b9db2bf042cfd2aSvetoslav            event = offsetEvent(event, - mLongPressingPointerDeltaX,
949a3315270e0502189a6fa4de20b9db2bf042cfd2aSvetoslav                    - mLongPressingPointerDeltaY);
950e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov        }
951e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov
952f804420d6e37748b75478406e989c69303756980Svetoslav Ganov        if (DEBUG) {
9534213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov            Slog.d(LOG_TAG, "Injecting event: " + event + ", policyFlags=0x"
954f804420d6e37748b75478406e989c69303756980Svetoslav Ganov                    + Integer.toHexString(policyFlags));
955f804420d6e37748b75478406e989c69303756980Svetoslav Ganov        }
956f804420d6e37748b75478406e989c69303756980Svetoslav Ganov
957f804420d6e37748b75478406e989c69303756980Svetoslav Ganov        // Make sure that the user will see the event.
958f804420d6e37748b75478406e989c69303756980Svetoslav Ganov        policyFlags |= WindowManagerPolicy.FLAG_PASS_TO_USER;
9591cf70bbf96930662cab0e699d70b62865766ff52Svetoslav Ganov        if (mNext != null) {
96045af84a483165f06c04d74baba67f90da29c6ad2Svetoslav Ganov            // TODO: For now pass null for the raw event since the touch
96145af84a483165f06c04d74baba67f90da29c6ad2Svetoslav Ganov            //       explorer is the last event transformation and it does
96245af84a483165f06c04d74baba67f90da29c6ad2Svetoslav Ganov            //       not care about the raw event.
96345af84a483165f06c04d74baba67f90da29c6ad2Svetoslav Ganov            mNext.onMotionEvent(event, null, policyFlags);
9641cf70bbf96930662cab0e699d70b62865766ff52Svetoslav Ganov        }
965f804420d6e37748b75478406e989c69303756980Svetoslav Ganov
9664213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov        mInjectedPointerTracker.onMotionEvent(event);
9674213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov
968f804420d6e37748b75478406e989c69303756980Svetoslav Ganov        if (event != prototype) {
969f804420d6e37748b75478406e989c69303756980Svetoslav Ganov            event.recycle();
970f804420d6e37748b75478406e989c69303756980Svetoslav Ganov        }
971736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov    }
972736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov
973736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov    /**
974a3315270e0502189a6fa4de20b9db2bf042cfd2aSvetoslav     * Offsets all pointers in the given event by adding the specified X and Y
975a3315270e0502189a6fa4de20b9db2bf042cfd2aSvetoslav     * offsets.
976a3315270e0502189a6fa4de20b9db2bf042cfd2aSvetoslav     *
977a3315270e0502189a6fa4de20b9db2bf042cfd2aSvetoslav     * @param event The event to offset.
978a3315270e0502189a6fa4de20b9db2bf042cfd2aSvetoslav     * @param offsetX The X offset.
979a3315270e0502189a6fa4de20b9db2bf042cfd2aSvetoslav     * @param offsetY The Y offset.
980a3315270e0502189a6fa4de20b9db2bf042cfd2aSvetoslav     * @return An event with the offset pointers or the original event if both
981a3315270e0502189a6fa4de20b9db2bf042cfd2aSvetoslav     *         offsets are zero.
982a3315270e0502189a6fa4de20b9db2bf042cfd2aSvetoslav     */
983a3315270e0502189a6fa4de20b9db2bf042cfd2aSvetoslav    private MotionEvent offsetEvent(MotionEvent event, int offsetX, int offsetY) {
984a3315270e0502189a6fa4de20b9db2bf042cfd2aSvetoslav        if (offsetX == 0 && offsetY == 0) {
985a3315270e0502189a6fa4de20b9db2bf042cfd2aSvetoslav            return event;
986a3315270e0502189a6fa4de20b9db2bf042cfd2aSvetoslav        }
987a3315270e0502189a6fa4de20b9db2bf042cfd2aSvetoslav        final int remappedIndex = event.findPointerIndex(mLongPressingPointerId);
988a3315270e0502189a6fa4de20b9db2bf042cfd2aSvetoslav        final int pointerCount = event.getPointerCount();
989a3315270e0502189a6fa4de20b9db2bf042cfd2aSvetoslav        PointerProperties[] props = PointerProperties.createArray(pointerCount);
990a3315270e0502189a6fa4de20b9db2bf042cfd2aSvetoslav        PointerCoords[] coords = PointerCoords.createArray(pointerCount);
991a3315270e0502189a6fa4de20b9db2bf042cfd2aSvetoslav        for (int i = 0; i < pointerCount; i++) {
992a3315270e0502189a6fa4de20b9db2bf042cfd2aSvetoslav            event.getPointerProperties(i, props[i]);
993a3315270e0502189a6fa4de20b9db2bf042cfd2aSvetoslav            event.getPointerCoords(i, coords[i]);
994a3315270e0502189a6fa4de20b9db2bf042cfd2aSvetoslav            if (i == remappedIndex) {
995a3315270e0502189a6fa4de20b9db2bf042cfd2aSvetoslav                coords[i].x += offsetX;
996a3315270e0502189a6fa4de20b9db2bf042cfd2aSvetoslav                coords[i].y += offsetY;
997a3315270e0502189a6fa4de20b9db2bf042cfd2aSvetoslav            }
998a3315270e0502189a6fa4de20b9db2bf042cfd2aSvetoslav        }
999a3315270e0502189a6fa4de20b9db2bf042cfd2aSvetoslav        return MotionEvent.obtain(event.getDownTime(),
1000a3315270e0502189a6fa4de20b9db2bf042cfd2aSvetoslav                event.getEventTime(), event.getAction(), event.getPointerCount(),
1001a3315270e0502189a6fa4de20b9db2bf042cfd2aSvetoslav                props, coords, event.getMetaState(), event.getButtonState(),
1002a3315270e0502189a6fa4de20b9db2bf042cfd2aSvetoslav                1.0f, 1.0f, event.getDeviceId(), event.getEdgeFlags(),
1003a3315270e0502189a6fa4de20b9db2bf042cfd2aSvetoslav                event.getSource(), event.getFlags());
1004a3315270e0502189a6fa4de20b9db2bf042cfd2aSvetoslav    }
1005a3315270e0502189a6fa4de20b9db2bf042cfd2aSvetoslav
1006a3315270e0502189a6fa4de20b9db2bf042cfd2aSvetoslav    /**
1007736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov     * Computes the action for an injected event based on a masked action
1008736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov     * and a pointer index.
1009736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov     *
1010736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov     * @param actionMasked The masked action.
1011736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov     * @param pointerIndex The index of the pointer which has changed.
1012736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov     * @return The action to be used for injection.
1013736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov     */
1014736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov    private int computeInjectionAction(int actionMasked, int pointerIndex) {
1015736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov        switch (actionMasked) {
1016736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov            case MotionEvent.ACTION_DOWN:
1017736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov            case MotionEvent.ACTION_POINTER_DOWN: {
10184213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov                InjectedPointerTracker injectedTracker = mInjectedPointerTracker;
1019736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov                // Compute the action based on how many down pointers are injected.
10204213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov                if (injectedTracker.getInjectedPointerDownCount() == 0) {
1021736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov                    return MotionEvent.ACTION_DOWN;
1022736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov                } else {
1023736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov                    return (pointerIndex << MotionEvent.ACTION_POINTER_INDEX_SHIFT)
1024736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov                        | MotionEvent.ACTION_POINTER_DOWN;
1025736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov                }
1026736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov            }
1027736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov            case MotionEvent.ACTION_POINTER_UP: {
10284213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov                InjectedPointerTracker injectedTracker = mInjectedPointerTracker;
1029736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov                // Compute the action based on how many down pointers are injected.
10304213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov                if (injectedTracker.getInjectedPointerDownCount() == 1) {
1031736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov                    return MotionEvent.ACTION_UP;
1032736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov                } else {
1033736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov                    return (pointerIndex << MotionEvent.ACTION_POINTER_INDEX_SHIFT)
1034736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov                        | MotionEvent.ACTION_POINTER_UP;
1035736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov                }
1036736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov            }
1037736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov            default:
1038736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov                return actionMasked;
1039736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov        }
1040736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov    }
1041736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov
1042736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov    /**
1043736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov     * Determines whether a two pointer gesture is a dragging one.
1044736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov     *
1045736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov     * @param event The event with the pointer data.
1046736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov     * @return True if the gesture is a dragging one.
1047736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov     */
1048736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov    private boolean isDraggingGesture(MotionEvent event) {
10494213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov        ReceivedPointerTracker receivedTracker = mReceivedPointerTracker;
1050736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov
105184044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav        final float firstPtrX = event.getX(0);
105284044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav        final float firstPtrY = event.getY(0);
105384044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav        final float secondPtrX = event.getX(1);
105484044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav        final float secondPtrY = event.getY(1);
1055736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov
105684044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav        final float firstPtrDownX = receivedTracker.getReceivedPointerDownX(0);
105784044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav        final float firstPtrDownY = receivedTracker.getReceivedPointerDownY(0);
105884044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav        final float secondPtrDownX = receivedTracker.getReceivedPointerDownX(1);
105984044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav        final float secondPtrDownY = receivedTracker.getReceivedPointerDownY(1);
1060736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov
10611cf70bbf96930662cab0e699d70b62865766ff52Svetoslav Ganov        return GestureUtils.isDraggingGesture(firstPtrDownX, firstPtrDownY, secondPtrDownX,
10621cf70bbf96930662cab0e699d70b62865766ff52Svetoslav Ganov                secondPtrDownY, firstPtrX, firstPtrY, secondPtrX, secondPtrY,
10631cf70bbf96930662cab0e699d70b62865766ff52Svetoslav Ganov                MAX_DRAGGING_ANGLE_COS);
1064736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov    }
1065736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov
1066ded133c446fa9d0d23e6bde19a66fb2ce3980491Svetoslav    private int computeClickLocation(Point outLocation) {
106747b9c1524fe20b9ae2b210acdcad64f764df68e0Svetoslav        MotionEvent lastExploreEvent = mInjectedPointerTracker.getLastInjectedHoverEventForClick();
106847b9c1524fe20b9ae2b210acdcad64f764df68e0Svetoslav        if (lastExploreEvent != null) {
106947b9c1524fe20b9ae2b210acdcad64f764df68e0Svetoslav            final int lastExplorePointerIndex = lastExploreEvent.getActionIndex();
107047b9c1524fe20b9ae2b210acdcad64f764df68e0Svetoslav            outLocation.x = (int) lastExploreEvent.getX(lastExplorePointerIndex);
107147b9c1524fe20b9ae2b210acdcad64f764df68e0Svetoslav            outLocation.y = (int) lastExploreEvent.getY(lastExplorePointerIndex);
107247b9c1524fe20b9ae2b210acdcad64f764df68e0Svetoslav            if (!mAms.accessibilityFocusOnlyInActiveWindow()
107347b9c1524fe20b9ae2b210acdcad64f764df68e0Svetoslav                    || mLastTouchedWindowId == mAms.getActiveWindowId()) {
1074ded133c446fa9d0d23e6bde19a66fb2ce3980491Svetoslav                if (mAms.getAccessibilityFocusClickPointInScreen(outLocation)) {
1075ded133c446fa9d0d23e6bde19a66fb2ce3980491Svetoslav                    return CLICK_LOCATION_ACCESSIBILITY_FOCUS;
1076ded133c446fa9d0d23e6bde19a66fb2ce3980491Svetoslav                } else {
1077ded133c446fa9d0d23e6bde19a66fb2ce3980491Svetoslav                    return CLICK_LOCATION_LAST_TOUCH_EXPLORED;
1078ded133c446fa9d0d23e6bde19a66fb2ce3980491Svetoslav                }
107947b9c1524fe20b9ae2b210acdcad64f764df68e0Svetoslav            }
108047b9c1524fe20b9ae2b210acdcad64f764df68e0Svetoslav        }
108147b9c1524fe20b9ae2b210acdcad64f764df68e0Svetoslav        if (mAms.getAccessibilityFocusClickPointInScreen(outLocation)) {
1082ded133c446fa9d0d23e6bde19a66fb2ce3980491Svetoslav            return CLICK_LOCATION_ACCESSIBILITY_FOCUS;
108347b9c1524fe20b9ae2b210acdcad64f764df68e0Svetoslav        }
1084ded133c446fa9d0d23e6bde19a66fb2ce3980491Svetoslav        return CLICK_LOCATION_NONE;
108547b9c1524fe20b9ae2b210acdcad64f764df68e0Svetoslav    }
108647b9c1524fe20b9ae2b210acdcad64f764df68e0Svetoslav
1087736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov    /**
108851cccf0845b36539d42503495f0689d487712b3aSvetoslav Ganov     * Gets the symbolic name of a state.
108951cccf0845b36539d42503495f0689d487712b3aSvetoslav Ganov     *
109051cccf0845b36539d42503495f0689d487712b3aSvetoslav Ganov     * @param state A state.
109151cccf0845b36539d42503495f0689d487712b3aSvetoslav Ganov     * @return The state symbolic name.
109251cccf0845b36539d42503495f0689d487712b3aSvetoslav Ganov     */
109351cccf0845b36539d42503495f0689d487712b3aSvetoslav Ganov    private static String getStateSymbolicName(int state) {
109451cccf0845b36539d42503495f0689d487712b3aSvetoslav Ganov        switch (state) {
109551cccf0845b36539d42503495f0689d487712b3aSvetoslav Ganov            case STATE_TOUCH_EXPLORING:
109651cccf0845b36539d42503495f0689d487712b3aSvetoslav Ganov                return "STATE_TOUCH_EXPLORING";
109751cccf0845b36539d42503495f0689d487712b3aSvetoslav Ganov            case STATE_DRAGGING:
109851cccf0845b36539d42503495f0689d487712b3aSvetoslav Ganov                return "STATE_DRAGGING";
109951cccf0845b36539d42503495f0689d487712b3aSvetoslav Ganov            case STATE_DELEGATING:
110051cccf0845b36539d42503495f0689d487712b3aSvetoslav Ganov                return "STATE_DELEGATING";
11014213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov            case STATE_GESTURE_DETECTING:
11024213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov                return "STATE_GESTURE_DETECTING";
110351cccf0845b36539d42503495f0689d487712b3aSvetoslav Ganov            default:
110451cccf0845b36539d42503495f0689d487712b3aSvetoslav Ganov                throw new IllegalArgumentException("Unknown state: " + state);
110551cccf0845b36539d42503495f0689d487712b3aSvetoslav Ganov        }
110651cccf0845b36539d42503495f0689d487712b3aSvetoslav Ganov    }
110751cccf0845b36539d42503495f0689d487712b3aSvetoslav Ganov
110851cccf0845b36539d42503495f0689d487712b3aSvetoslav Ganov    /**
110995068e5d1bea47091e97955f271c789264994550Svetoslav Ganov     * Class for delayed exiting from gesture detecting mode.
111095068e5d1bea47091e97955f271c789264994550Svetoslav Ganov     */
111195068e5d1bea47091e97955f271c789264994550Svetoslav Ganov    private final class ExitGestureDetectionModeDelayed implements Runnable {
111295068e5d1bea47091e97955f271c789264994550Svetoslav Ganov
111395068e5d1bea47091e97955f271c789264994550Svetoslav Ganov        public void post() {
111495068e5d1bea47091e97955f271c789264994550Svetoslav Ganov            mHandler.postDelayed(this, EXIT_GESTURE_DETECTION_TIMEOUT);
111595068e5d1bea47091e97955f271c789264994550Svetoslav Ganov        }
111695068e5d1bea47091e97955f271c789264994550Svetoslav Ganov
111784044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav        public void cancel() {
111895068e5d1bea47091e97955f271c789264994550Svetoslav Ganov            mHandler.removeCallbacks(this);
111995068e5d1bea47091e97955f271c789264994550Svetoslav Ganov        }
112095068e5d1bea47091e97955f271c789264994550Svetoslav Ganov
112195068e5d1bea47091e97955f271c789264994550Svetoslav Ganov        @Override
112295068e5d1bea47091e97955f271c789264994550Svetoslav Ganov        public void run() {
1123aed4b6f812674bc60a04470013ca449e5c114fa5Svetoslav Ganov            // Announce the end of gesture recognition.
1124aed4b6f812674bc60a04470013ca449e5c114fa5Svetoslav Ganov            sendAccessibilityEvent(AccessibilityEvent.TYPE_GESTURE_DETECTION_END);
1125aed4b6f812674bc60a04470013ca449e5c114fa5Svetoslav Ganov            // Clearing puts is in touch exploration state with a finger already
1126aed4b6f812674bc60a04470013ca449e5c114fa5Svetoslav Ganov            // down, so announce the transition to exploration state.
1127aed4b6f812674bc60a04470013ca449e5c114fa5Svetoslav Ganov            sendAccessibilityEvent(AccessibilityEvent.TYPE_TOUCH_EXPLORATION_GESTURE_START);
112895068e5d1bea47091e97955f271c789264994550Svetoslav Ganov            clear();
112995068e5d1bea47091e97955f271c789264994550Svetoslav Ganov        }
113095068e5d1bea47091e97955f271c789264994550Svetoslav Ganov    }
113195068e5d1bea47091e97955f271c789264994550Svetoslav Ganov
113295068e5d1bea47091e97955f271c789264994550Svetoslav Ganov    /**
113384044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav     * Class for delayed sending of hover enter and move events.
11344213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov     */
113584044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav    class SendHoverEnterAndMoveDelayed implements Runnable {
113684044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav        private final String LOG_TAG_SEND_HOVER_DELAYED = "SendHoverEnterAndMoveDelayed";
1137e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov
113884044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav        private final List<MotionEvent> mEvents = new ArrayList<MotionEvent>();
1139e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov
11404213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov        private int mPointerIdBits;
11414213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov        private int mPolicyFlags;
11424213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov
114384044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav        public void post(MotionEvent event, boolean touchExplorationInProgress,
114477276b60851a158ad3e142cb3b091d57ae5ceffbSvetoslav Ganov                int pointerIdBits, int policyFlags) {
114584044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav            cancel();
114684044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav            addEvent(event);
11474213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov            mPointerIdBits = pointerIdBits;
11484213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov            mPolicyFlags = policyFlags;
1149e47957a0bbe2164467ff6e7a566b0c9e4689cdc9Svetoslav Ganov            mHandler.postDelayed(this, mDetermineUserIntentTimeout);
1150e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov        }
1151e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov
115284044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav        public void addEvent(MotionEvent event) {
115384044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav            mEvents.add(MotionEvent.obtain(event));
115484044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav        }
115584044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav
115684044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav        public void cancel() {
1157e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov            if (isPending()) {
115884044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav                mHandler.removeCallbacks(this);
115984044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav                clear();
116084044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav            }
116184044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav        }
116284044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav
116384044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav        private boolean isPending() {
116484044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav            return mHandler.hasCallbacks(this);
116584044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav        }
116684044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav
116784044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav        private void clear() {
116884044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav            mPointerIdBits = -1;
116984044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav            mPolicyFlags = 0;
117084044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav            final int eventCount = mEvents.size();
117184044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav            for (int i = eventCount - 1; i >= 0; i--) {
117284044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav                mEvents.remove(i).recycle();
1173e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov            }
1174e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov        }
1175e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov
117684044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav        public void forceSendAndRemove() {
1177e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov            if (isPending()) {
117884044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav                run();
117984044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav                cancel();
1180e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov            }
11814213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov        }
11824213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov
118384044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav        public void run() {
118484044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav            // Send an accessibility event to announce the touch exploration start.
118584044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav            sendAccessibilityEvent(AccessibilityEvent.TYPE_TOUCH_EXPLORATION_GESTURE_START);
118684044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav
118784044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav            if (!mEvents.isEmpty()) {
118884044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav                // Deliver a down event.
118984044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav                sendMotionEvent(mEvents.get(0), MotionEvent.ACTION_HOVER_ENTER,
119084044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav                        mPointerIdBits, mPolicyFlags);
119184044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav                if (DEBUG) {
119284044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav                    Slog.d(LOG_TAG_SEND_HOVER_DELAYED,
119384044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav                            "Injecting motion event: ACTION_HOVER_ENTER");
119484044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav                }
119584044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav
119684044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav                // Deliver move events.
119784044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav                final int eventCount = mEvents.size();
119884044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav                for (int i = 1; i < eventCount; i++) {
119984044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav                    sendMotionEvent(mEvents.get(i), MotionEvent.ACTION_HOVER_MOVE,
120084044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav                            mPointerIdBits, mPolicyFlags);
120184044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav                    if (DEBUG) {
120284044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav                        Slog.d(LOG_TAG_SEND_HOVER_DELAYED,
120384044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav                                "Injecting motion event: ACTION_HOVER_MOVE");
120484044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav                    }
120584044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav                }
120684044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav            }
12074213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov            clear();
12084213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov        }
120984044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav    }
121084044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav
121184044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav    /**
121284044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav     * Class for delayed sending of hover exit events.
121384044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav     */
121484044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav    class SendHoverExitDelayed implements Runnable {
121584044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav        private final String LOG_TAG_SEND_HOVER_DELAYED = "SendHoverExitDelayed";
121684044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav
121784044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav        private MotionEvent mPrototype;
121884044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav        private int mPointerIdBits;
121984044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav        private int mPolicyFlags;
122084044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav
122184044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav        public void post(MotionEvent prototype, int pointerIdBits, int policyFlags) {
122284044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav            cancel();
122384044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav            mPrototype = MotionEvent.obtain(prototype);
122484044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav            mPointerIdBits = pointerIdBits;
122584044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav            mPolicyFlags = policyFlags;
122684044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav            mHandler.postDelayed(this, mDetermineUserIntentTimeout);
122784044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav        }
122884044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav
122984044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav        public void cancel() {
123084044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav            if (isPending()) {
123184044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav                mHandler.removeCallbacks(this);
123284044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav                clear();
123384044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav            }
123484044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav        }
12354213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov
1236e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov        private boolean isPending() {
123784044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav            return mHandler.hasCallbacks(this);
12384213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov        }
12394213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov
12404213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov        private void clear() {
1241e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov            mPrototype.recycle();
1242e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov            mPrototype = null;
12434213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov            mPointerIdBits = -1;
12444213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov            mPolicyFlags = 0;
12454213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov        }
12464213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov
12474213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov        public void forceSendAndRemove() {
1248e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov            if (isPending()) {
12494213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov                run();
125084044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav                cancel();
12514213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov            }
12524213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov        }
12534213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov
12544213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov        public void run() {
12554213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov            if (DEBUG) {
125684044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav                Slog.d(LOG_TAG_SEND_HOVER_DELAYED, "Injecting motion event:"
125784044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav                        + " ACTION_HOVER_EXIT");
12584213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov            }
125984044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav            sendMotionEvent(mPrototype, MotionEvent.ACTION_HOVER_EXIT,
126084044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav                    mPointerIdBits, mPolicyFlags);
126184044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav            if (!mSendTouchExplorationEndDelayed.isPending()) {
126284044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav                mSendTouchExplorationEndDelayed.cancel();
126384044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav                mSendTouchExplorationEndDelayed.post();
126484044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav            }
126584044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav            if (mSendTouchInteractionEndDelayed.isPending()) {
126684044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav                  mSendTouchInteractionEndDelayed.cancel();
126784044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav                mSendTouchInteractionEndDelayed.post();
1268e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov            }
12694213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov            clear();
12704213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov        }
12714213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov    }
12724213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov
1273f068fed6c4c3fc2003aec19b6e7e892358179b02Svetoslav Ganov    private class SendAccessibilityEventDelayed implements Runnable {
1274f068fed6c4c3fc2003aec19b6e7e892358179b02Svetoslav Ganov        private final int mEventType;
1275f068fed6c4c3fc2003aec19b6e7e892358179b02Svetoslav Ganov        private final int mDelay;
1276f068fed6c4c3fc2003aec19b6e7e892358179b02Svetoslav Ganov
1277f068fed6c4c3fc2003aec19b6e7e892358179b02Svetoslav Ganov        public SendAccessibilityEventDelayed(int eventType, int delay) {
1278f068fed6c4c3fc2003aec19b6e7e892358179b02Svetoslav Ganov            mEventType = eventType;
1279f068fed6c4c3fc2003aec19b6e7e892358179b02Svetoslav Ganov            mDelay = delay;
1280f068fed6c4c3fc2003aec19b6e7e892358179b02Svetoslav Ganov        }
1281fe304b893968887323b93764caafa66ee8ad44deSvetoslav Ganov
128284044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav        public void cancel() {
1283fe304b893968887323b93764caafa66ee8ad44deSvetoslav Ganov            mHandler.removeCallbacks(this);
1284fe304b893968887323b93764caafa66ee8ad44deSvetoslav Ganov        }
1285fe304b893968887323b93764caafa66ee8ad44deSvetoslav Ganov
1286fe304b893968887323b93764caafa66ee8ad44deSvetoslav Ganov        public void post() {
1287f068fed6c4c3fc2003aec19b6e7e892358179b02Svetoslav Ganov            mHandler.postDelayed(this, mDelay);
1288fe304b893968887323b93764caafa66ee8ad44deSvetoslav Ganov        }
1289fe304b893968887323b93764caafa66ee8ad44deSvetoslav Ganov
1290fe304b893968887323b93764caafa66ee8ad44deSvetoslav Ganov        public boolean isPending() {
1291fe304b893968887323b93764caafa66ee8ad44deSvetoslav Ganov            return mHandler.hasCallbacks(this);
1292fe304b893968887323b93764caafa66ee8ad44deSvetoslav Ganov        }
1293fe304b893968887323b93764caafa66ee8ad44deSvetoslav Ganov
1294fe304b893968887323b93764caafa66ee8ad44deSvetoslav Ganov        public void forceSendAndRemove() {
1295fe304b893968887323b93764caafa66ee8ad44deSvetoslav Ganov            if (isPending()) {
1296fe304b893968887323b93764caafa66ee8ad44deSvetoslav Ganov                run();
129784044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav                cancel();
1298fe304b893968887323b93764caafa66ee8ad44deSvetoslav Ganov            }
1299fe304b893968887323b93764caafa66ee8ad44deSvetoslav Ganov        }
1300fe304b893968887323b93764caafa66ee8ad44deSvetoslav Ganov
1301fe304b893968887323b93764caafa66ee8ad44deSvetoslav Ganov        @Override
1302fe304b893968887323b93764caafa66ee8ad44deSvetoslav Ganov        public void run() {
1303f068fed6c4c3fc2003aec19b6e7e892358179b02Svetoslav Ganov            sendAccessibilityEvent(mEventType);
1304fe304b893968887323b93764caafa66ee8ad44deSvetoslav Ganov        }
1305fe304b893968887323b93764caafa66ee8ad44deSvetoslav Ganov    }
1306fe304b893968887323b93764caafa66ee8ad44deSvetoslav Ganov
13074213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov    @Override
13084213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov    public String toString() {
13094213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov        return LOG_TAG;
13104213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov    }
13114213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov
13124213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov    class InjectedPointerTracker {
13134213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov        private static final String LOG_TAG_INJECTED_POINTER_TRACKER = "InjectedPointerTracker";
13144213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov
13154213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov        // Keep track of which pointers sent to the system are down.
13164213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov        private int mInjectedPointersDown;
13174213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov
13184213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov        // The time of the last injected down.
13194213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov        private long mLastInjectedDownEventTime;
13204213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov
1321e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov        // The last injected hover event.
1322e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov        private MotionEvent mLastInjectedHoverEvent;
13234213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov
13245d043ce8cc2f588fdfb336cc843fb3b07b196f83Svetoslav Ganov        // The last injected hover event used for performing clicks.
13255d043ce8cc2f588fdfb336cc843fb3b07b196f83Svetoslav Ganov        private MotionEvent mLastInjectedHoverEventForClick;
13265d043ce8cc2f588fdfb336cc843fb3b07b196f83Svetoslav Ganov
13274213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov        /**
13284213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov         * Processes an injected {@link MotionEvent} event.
13294213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov         *
13304213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov         * @param event The event to process.
13314213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov         */
13324213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov        public void onMotionEvent(MotionEvent event) {
13334213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov            final int action = event.getActionMasked();
13344213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov            switch (action) {
13354213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov                case MotionEvent.ACTION_DOWN:
13364213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov                case MotionEvent.ACTION_POINTER_DOWN: {
13374213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov                    final int pointerId = event.getPointerId(event.getActionIndex());
13384213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov                    final int pointerFlag = (1 << pointerId);
13394213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov                    mInjectedPointersDown |= pointerFlag;
13404213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov                    mLastInjectedDownEventTime = event.getDownTime();
13414213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov                } break;
13424213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov                case MotionEvent.ACTION_UP:
13434213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov                case MotionEvent.ACTION_POINTER_UP: {
13444213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov                    final int pointerId = event.getPointerId(event.getActionIndex());
13454213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov                    final int pointerFlag = (1 << pointerId);
13464213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov                    mInjectedPointersDown &= ~pointerFlag;
13474213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov                    if (mInjectedPointersDown == 0) {
13484213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov                        mLastInjectedDownEventTime = 0;
13494213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov                    }
13504213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov                } break;
13514213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov                case MotionEvent.ACTION_HOVER_ENTER:
13524213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov                case MotionEvent.ACTION_HOVER_MOVE:
13534213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov                case MotionEvent.ACTION_HOVER_EXIT: {
1354e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov                    if (mLastInjectedHoverEvent != null) {
1355e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov                        mLastInjectedHoverEvent.recycle();
1356e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov                    }
1357e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov                    mLastInjectedHoverEvent = MotionEvent.obtain(event);
13585d043ce8cc2f588fdfb336cc843fb3b07b196f83Svetoslav Ganov                    if (mLastInjectedHoverEventForClick != null) {
13595d043ce8cc2f588fdfb336cc843fb3b07b196f83Svetoslav Ganov                        mLastInjectedHoverEventForClick.recycle();
13605d043ce8cc2f588fdfb336cc843fb3b07b196f83Svetoslav Ganov                    }
13615d043ce8cc2f588fdfb336cc843fb3b07b196f83Svetoslav Ganov                    mLastInjectedHoverEventForClick = MotionEvent.obtain(event);
13624213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov                } break;
13634213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov            }
13644213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov            if (DEBUG) {
1365e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov                Slog.i(LOG_TAG_INJECTED_POINTER_TRACKER, "Injected pointer:\n" + toString());
13664213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov            }
13674213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov        }
13684213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov
13694213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov        /**
13704213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov         * Clears the internals state.
13714213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov         */
13724213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov        public void clear() {
13734213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov            mInjectedPointersDown = 0;
13744213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov        }
13754213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov
13764213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov        /**
13774213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov         * @return The time of the last injected down event.
13784213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov         */
13794213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov        public long getLastInjectedDownEventTime() {
13804213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov            return mLastInjectedDownEventTime;
13814213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov        }
13824213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov
13834213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov        /**
13844213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov         * @return The number of down pointers injected to the view hierarchy.
13854213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov         */
13864213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov        public int getInjectedPointerDownCount() {
13874213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov            return Integer.bitCount(mInjectedPointersDown);
13884213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov        }
13894213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov
13904213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov        /**
13914213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov         * @return The bits of the injected pointers that are down.
13924213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov         */
13934213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov        public int getInjectedPointersDown() {
13944213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov            return mInjectedPointersDown;
13954213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov        }
13964213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov
13974213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov        /**
13984213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov         * Whether an injected pointer is down.
13994213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov         *
14004213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov         * @param pointerId The unique pointer id.
14014213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov         * @return True if the pointer is down.
14024213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov         */
14034213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov        public boolean isInjectedPointerDown(int pointerId) {
14044213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov            final int pointerFlag = (1 << pointerId);
14054213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov            return (mInjectedPointersDown & pointerFlag) != 0;
14064213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov        }
14074213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov
14084213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov        /**
1409e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov         * @return The the last injected hover event.
14104213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov         */
1411e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov        public MotionEvent getLastInjectedHoverEvent() {
1412e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov            return mLastInjectedHoverEvent;
14134213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov        }
14144213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov
14155d043ce8cc2f588fdfb336cc843fb3b07b196f83Svetoslav Ganov        /**
14165d043ce8cc2f588fdfb336cc843fb3b07b196f83Svetoslav Ganov         * @return The the last injected hover event.
14175d043ce8cc2f588fdfb336cc843fb3b07b196f83Svetoslav Ganov         */
14185d043ce8cc2f588fdfb336cc843fb3b07b196f83Svetoslav Ganov        public MotionEvent getLastInjectedHoverEventForClick() {
14195d043ce8cc2f588fdfb336cc843fb3b07b196f83Svetoslav Ganov            return mLastInjectedHoverEventForClick;
14205d043ce8cc2f588fdfb336cc843fb3b07b196f83Svetoslav Ganov        }
14215d043ce8cc2f588fdfb336cc843fb3b07b196f83Svetoslav Ganov
14224213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov        @Override
14234213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov        public String toString() {
14244213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov            StringBuilder builder = new StringBuilder();
14254213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov            builder.append("=========================");
14264213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov            builder.append("\nDown pointers #");
14274213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov            builder.append(Integer.bitCount(mInjectedPointersDown));
14284213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov            builder.append(" [ ");
14294213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov            for (int i = 0; i < MAX_POINTER_COUNT; i++) {
14304213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov                if ((mInjectedPointersDown & i) != 0) {
14314213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov                    builder.append(i);
14324213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov                    builder.append(" ");
14334213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov                }
14344213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov            }
14354213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov            builder.append("]");
14364213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov            builder.append("\n=========================");
14374213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov            return builder.toString();
14384213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov        }
14394213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov    }
14404213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov
14414213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov    class ReceivedPointerTracker {
14424213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov        private static final String LOG_TAG_RECEIVED_POINTER_TRACKER = "ReceivedPointerTracker";
1443736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov
1444736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov        // Keep track of where and when a pointer went down.
1445736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov        private final float[] mReceivedPointerDownX = new float[MAX_POINTER_COUNT];
1446736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov        private final float[] mReceivedPointerDownY = new float[MAX_POINTER_COUNT];
1447736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov        private final long[] mReceivedPointerDownTime = new long[MAX_POINTER_COUNT];
1448736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov
1449736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov        // Which pointers are down.
1450736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov        private int mReceivedPointersDown;
1451736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov
1452c4842c11932ea4f60fe7ae09b0a59660207e1587Svetoslav Ganov        // The edge flags of the last received down event.
1453c4842c11932ea4f60fe7ae09b0a59660207e1587Svetoslav Ganov        private int mLastReceivedDownEdgeFlags;
1454c4842c11932ea4f60fe7ae09b0a59660207e1587Svetoslav Ganov
145584044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav        // Primary pointer which is either the first that went down
145684044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav        // or if it goes up the next one that most recently went down.
145784044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav        private int mPrimaryPointerId;
1458736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov
1459736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov        // Keep track of the last up pointer data.
1460736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov        private long mLastReceivedUpPointerDownTime;
14614213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov        private float mLastReceivedUpPointerDownX;
14624213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov        private float mLastReceivedUpPointerDownY;
1463736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov
1464e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov        private MotionEvent mLastReceivedEvent;
1465e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov
1466736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov        /**
1467736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov         * Clears the internals state.
1468736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov         */
1469736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov        public void clear() {
1470736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov            Arrays.fill(mReceivedPointerDownX, 0);
1471736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov            Arrays.fill(mReceivedPointerDownY, 0);
1472736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov            Arrays.fill(mReceivedPointerDownTime, 0);
1473736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov            mReceivedPointersDown = 0;
147484044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav            mPrimaryPointerId = 0;
1475736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov            mLastReceivedUpPointerDownTime = 0;
14764213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov            mLastReceivedUpPointerDownX = 0;
14774213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov            mLastReceivedUpPointerDownY = 0;
1478736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov        }
1479736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov
1480736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov        /**
1481736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov         * Processes a received {@link MotionEvent} event.
1482736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov         *
1483736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov         * @param event The event to process.
1484736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov         */
14854213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov        public void onMotionEvent(MotionEvent event) {
1486e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov            if (mLastReceivedEvent != null) {
1487e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov                mLastReceivedEvent.recycle();
1488e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov            }
1489e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov            mLastReceivedEvent = MotionEvent.obtain(event);
1490e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov
1491736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov            final int action = event.getActionMasked();
1492736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov            switch (action) {
1493736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov                case MotionEvent.ACTION_DOWN: {
149400f7b3f76515d1c6fbe5cf9fee9d3760787c03cdSvetoslav Ganov                    handleReceivedPointerDown(event.getActionIndex(), event);
1495736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov                } break;
1496736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov                case MotionEvent.ACTION_POINTER_DOWN: {
1497736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov                    handleReceivedPointerDown(event.getActionIndex(), event);
1498736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov                } break;
1499736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov                case MotionEvent.ACTION_UP: {
150000f7b3f76515d1c6fbe5cf9fee9d3760787c03cdSvetoslav Ganov                    handleReceivedPointerUp(event.getActionIndex(), event);
1501736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov                } break;
1502736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov                case MotionEvent.ACTION_POINTER_UP: {
1503736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov                    handleReceivedPointerUp(event.getActionIndex(), event);
1504736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov                } break;
1505736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov            }
1506736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov            if (DEBUG) {
150738c992841b5f6bc80359dbf60d31aa7b994160fcSvetoslav Ganov                Slog.i(LOG_TAG_RECEIVED_POINTER_TRACKER, "Received pointer:\n" + toString());
1508736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov            }
1509736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov        }
1510736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov
1511736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov        /**
1512e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov         * @return The last received event.
1513e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov         */
1514e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov        public MotionEvent getLastReceivedEvent() {
1515e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov            return mLastReceivedEvent;
1516e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov        }
1517e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov
1518e15ccb93add99ebb9cd7aec03a04faa37f45b39dSvetoslav Ganov        /**
15194213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov         * @return The number of received pointers that are down.
1520736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov         */
15214213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov        public int getReceivedPointerDownCount() {
15224213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov            return Integer.bitCount(mReceivedPointersDown);
1523736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov        }
1524736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov
1525736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov        /**
1526736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov         * Whether an received pointer is down.
1527736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov         *
1528736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov         * @param pointerId The unique pointer id.
1529736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov         * @return True if the pointer is down.
1530736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov         */
1531736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov        public boolean isReceivedPointerDown(int pointerId) {
1532736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov            final int pointerFlag = (1 << pointerId);
1533736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov            return (mReceivedPointersDown & pointerFlag) != 0;
1534736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov        }
1535736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov
1536736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov        /**
1537736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov         * @param pointerId The unique pointer id.
1538736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov         * @return The X coordinate where the pointer went down.
1539736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov         */
1540736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov        public float getReceivedPointerDownX(int pointerId) {
1541736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov            return mReceivedPointerDownX[pointerId];
1542736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov        }
1543736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov
1544736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov        /**
1545736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov         * @param pointerId The unique pointer id.
1546736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov         * @return The Y coordinate where the pointer went down.
1547736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov         */
1548736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov        public float getReceivedPointerDownY(int pointerId) {
1549736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov            return mReceivedPointerDownY[pointerId];
1550736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov        }
1551736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov
1552736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov        /**
1553736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov         * @param pointerId The unique pointer id.
1554736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov         * @return The time when the pointer went down.
1555736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov         */
1556736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov        public long getReceivedPointerDownTime(int pointerId) {
1557736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov            return mReceivedPointerDownTime[pointerId];
1558736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov        }
1559736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov
1560736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov        /**
1561736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov         * @return The id of the primary pointer.
1562736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov         */
156384044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav        public int getPrimaryPointerId() {
156484044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav            if (mPrimaryPointerId == INVALID_POINTER_ID) {
156538c992841b5f6bc80359dbf60d31aa7b994160fcSvetoslav Ganov                mPrimaryPointerId = findPrimaryPointerId();
1566736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov            }
156784044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav            return mPrimaryPointerId;
1568736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov        }
1569736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov
1570736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov        /**
1571736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov         * @return The time when the last up received pointer went down.
1572736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov         */
1573736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov        public long getLastReceivedUpPointerDownTime() {
1574736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov            return mLastReceivedUpPointerDownTime;
1575736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov        }
1576736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov
1577736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov        /**
15784213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov         * @return The down X of the last received pointer that went up.
1579736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov         */
15804213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov        public float getLastReceivedUpPointerDownX() {
15814213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov            return mLastReceivedUpPointerDownX;
1582736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov        }
1583736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov
1584736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov        /**
15854213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov         * @return The down Y of the last received pointer that went up.
1586736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov         */
15874213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov        public float getLastReceivedUpPointerDownY() {
15884213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov            return mLastReceivedUpPointerDownY;
1589736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov        }
1590736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov
1591736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov        /**
1592c4842c11932ea4f60fe7ae09b0a59660207e1587Svetoslav Ganov         * @return The edge flags of the last received down event.
1593c4842c11932ea4f60fe7ae09b0a59660207e1587Svetoslav Ganov         */
1594c4842c11932ea4f60fe7ae09b0a59660207e1587Svetoslav Ganov        public int getLastReceivedDownEdgeFlags() {
1595c4842c11932ea4f60fe7ae09b0a59660207e1587Svetoslav Ganov            return mLastReceivedDownEdgeFlags;
1596c4842c11932ea4f60fe7ae09b0a59660207e1587Svetoslav Ganov        }
1597c4842c11932ea4f60fe7ae09b0a59660207e1587Svetoslav Ganov
1598c4842c11932ea4f60fe7ae09b0a59660207e1587Svetoslav Ganov        /**
1599736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov         * Handles a received pointer down event.
1600736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov         *
1601736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov         * @param pointerIndex The index of the pointer that has changed.
1602736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov         * @param event The event to be handled.
1603736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov         */
1604736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov        private void handleReceivedPointerDown(int pointerIndex, MotionEvent event) {
1605736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov            final int pointerId = event.getPointerId(pointerIndex);
1606736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov            final int pointerFlag = (1 << pointerId);
1607736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov
1608736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov            mLastReceivedUpPointerDownTime = 0;
16094213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov            mLastReceivedUpPointerDownX = 0;
16104213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov            mLastReceivedUpPointerDownX = 0;
1611736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov
1612c4842c11932ea4f60fe7ae09b0a59660207e1587Svetoslav Ganov            mLastReceivedDownEdgeFlags = event.getEdgeFlags();
1613c4842c11932ea4f60fe7ae09b0a59660207e1587Svetoslav Ganov
1614736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov            mReceivedPointersDown |= pointerFlag;
1615736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov            mReceivedPointerDownX[pointerId] = event.getX(pointerIndex);
1616736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov            mReceivedPointerDownY[pointerId] = event.getY(pointerIndex);
1617736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov            mReceivedPointerDownTime[pointerId] = event.getEventTime();
1618736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov
161984044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav            mPrimaryPointerId = pointerId;
1620736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov        }
1621736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov
1622736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov        /**
1623736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov         * Handles a received pointer up event.
1624736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov         *
1625736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov         * @param pointerIndex The index of the pointer that has changed.
1626736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov         * @param event The event to be handled.
1627736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov         */
1628736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov        private void handleReceivedPointerUp(int pointerIndex, MotionEvent event) {
1629736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov            final int pointerId = event.getPointerId(pointerIndex);
1630736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov            final int pointerFlag = (1 << pointerId);
1631736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov
1632736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov            mLastReceivedUpPointerDownTime = getReceivedPointerDownTime(pointerId);
16334213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov            mLastReceivedUpPointerDownX = mReceivedPointerDownX[pointerId];
16344213804541a8b05cd0587b138a2fd9a3b7fd9350Svetoslav Ganov            mLastReceivedUpPointerDownY = mReceivedPointerDownY[pointerId];
1635736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov
1636736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov            mReceivedPointersDown &= ~pointerFlag;
1637736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov            mReceivedPointerDownX[pointerId] = 0;
1638736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov            mReceivedPointerDownY[pointerId] = 0;
1639736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov            mReceivedPointerDownTime[pointerId] = 0;
1640736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov
164184044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav            if (mPrimaryPointerId == pointerId) {
164284044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav                mPrimaryPointerId = INVALID_POINTER_ID;
1643736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov            }
1644736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov        }
1645736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov
1646736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov        /**
164738c992841b5f6bc80359dbf60d31aa7b994160fcSvetoslav Ganov         * @return The primary pointer id.
1648736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov         */
164938c992841b5f6bc80359dbf60d31aa7b994160fcSvetoslav Ganov        private int findPrimaryPointerId() {
165084044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav            int primaryPointerId = INVALID_POINTER_ID;
1651736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov            long minDownTime = Long.MAX_VALUE;
165238c992841b5f6bc80359dbf60d31aa7b994160fcSvetoslav Ganov
165384044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav            // Find the pointer that went down first.
165438c992841b5f6bc80359dbf60d31aa7b994160fcSvetoslav Ganov            int pointerIdBits = mReceivedPointersDown;
165538c992841b5f6bc80359dbf60d31aa7b994160fcSvetoslav Ganov            while (pointerIdBits > 0) {
165638c992841b5f6bc80359dbf60d31aa7b994160fcSvetoslav Ganov                final int pointerId = Integer.numberOfTrailingZeros(pointerIdBits);
165738c992841b5f6bc80359dbf60d31aa7b994160fcSvetoslav Ganov                pointerIdBits &= ~(1 << pointerId);
165838c992841b5f6bc80359dbf60d31aa7b994160fcSvetoslav Ganov                final long downPointerTime = mReceivedPointerDownTime[pointerId];
165984044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav                if (downPointerTime < minDownTime) {
166084044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav                    minDownTime = downPointerTime;
166138c992841b5f6bc80359dbf60d31aa7b994160fcSvetoslav Ganov                    primaryPointerId = pointerId;
1662736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov                }
1663736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov            }
166484044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav            return primaryPointerId;
1665736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov        }
1666736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov
1667736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov        @Override
1668736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov        public String toString() {
1669736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov            StringBuilder builder = new StringBuilder();
1670736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov            builder.append("=========================");
1671736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov            builder.append("\nDown pointers #");
1672736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov            builder.append(getReceivedPointerDownCount());
1673736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov            builder.append(" [ ");
1674736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov            for (int i = 0; i < MAX_POINTER_COUNT; i++) {
1675736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov                if (isReceivedPointerDown(i)) {
1676736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov                    builder.append(i);
1677736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov                    builder.append(" ");
1678736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov                }
1679736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov            }
1680736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov            builder.append("]");
168184044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav            builder.append("\nPrimary pointer id [ ");
168284044b3ce737487b6e5bb1f6618d151c8659c2a1Svetoslav            builder.append(getPrimaryPointerId());
1683736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov            builder.append(" ]");
1684736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov            builder.append("\n=========================");
1685736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov            return builder.toString();
1686736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov        }
1687736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov    }
1688736c2756bf3c14ae9fef7255c119057f7a2be1edSvetoslav Ganov}
1689