1c18f2fdfcf8ec7a8534c032c4102739edb7f1c5eZachary Kuznia/* 2c18f2fdfcf8ec7a8534c032c4102739edb7f1c5eZachary Kuznia ** Copyright 2015, The Android Open Source Project 3c18f2fdfcf8ec7a8534c032c4102739edb7f1c5eZachary Kuznia ** 4c18f2fdfcf8ec7a8534c032c4102739edb7f1c5eZachary Kuznia ** Licensed under the Apache License, Version 2.0 (the "License"); 5c18f2fdfcf8ec7a8534c032c4102739edb7f1c5eZachary Kuznia ** you may not use this file except in compliance with the License. 6c18f2fdfcf8ec7a8534c032c4102739edb7f1c5eZachary Kuznia ** You may obtain a copy of the License at 7c18f2fdfcf8ec7a8534c032c4102739edb7f1c5eZachary Kuznia ** 8c18f2fdfcf8ec7a8534c032c4102739edb7f1c5eZachary Kuznia ** http://www.apache.org/licenses/LICENSE-2.0 9c18f2fdfcf8ec7a8534c032c4102739edb7f1c5eZachary Kuznia ** 10c18f2fdfcf8ec7a8534c032c4102739edb7f1c5eZachary Kuznia ** Unless required by applicable law or agreed to in writing, software 11c18f2fdfcf8ec7a8534c032c4102739edb7f1c5eZachary Kuznia ** distributed under the License is distributed on an "AS IS" BASIS, 12c18f2fdfcf8ec7a8534c032c4102739edb7f1c5eZachary Kuznia ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13c18f2fdfcf8ec7a8534c032c4102739edb7f1c5eZachary Kuznia ** See the License for the specific language governing permissions and 14c18f2fdfcf8ec7a8534c032c4102739edb7f1c5eZachary Kuznia ** limitations under the License. 15c18f2fdfcf8ec7a8534c032c4102739edb7f1c5eZachary Kuznia */ 16c18f2fdfcf8ec7a8534c032c4102739edb7f1c5eZachary Kuznia 17c18f2fdfcf8ec7a8534c032c4102739edb7f1c5eZachary Kuzniapackage com.android.server.accessibility; 18c18f2fdfcf8ec7a8534c032c4102739edb7f1c5eZachary Kuznia 19c18f2fdfcf8ec7a8534c032c4102739edb7f1c5eZachary Kuzniaimport android.content.Context; 20407df712e2a381e0227eb780786128a50bfcf5f5Zachary Kuzniaimport android.gesture.Gesture; 21407df712e2a381e0227eb780786128a50bfcf5f5Zachary Kuzniaimport android.gesture.GestureLibraries; 22407df712e2a381e0227eb780786128a50bfcf5f5Zachary Kuzniaimport android.gesture.GestureLibrary; 23407df712e2a381e0227eb780786128a50bfcf5f5Zachary Kuzniaimport android.gesture.GesturePoint; 24407df712e2a381e0227eb780786128a50bfcf5f5Zachary Kuzniaimport android.gesture.GestureStore; 25407df712e2a381e0227eb780786128a50bfcf5f5Zachary Kuzniaimport android.gesture.GestureStroke; 26407df712e2a381e0227eb780786128a50bfcf5f5Zachary Kuzniaimport android.gesture.Prediction; 27407df712e2a381e0227eb780786128a50bfcf5f5Zachary Kuzniaimport android.util.Slog; 2828239e8a619e6546580a0f3893cf55b0e97fc4e8Zachary Kuzniaimport android.util.TypedValue; 29c18f2fdfcf8ec7a8534c032c4102739edb7f1c5eZachary Kuzniaimport android.view.GestureDetector; 30c18f2fdfcf8ec7a8534c032c4102739edb7f1c5eZachary Kuzniaimport android.view.MotionEvent; 319254b3e336a01ff79742882bd98860b48ebae379Zachary Kuzniaimport android.view.VelocityTracker; 329254b3e336a01ff79742882bd98860b48ebae379Zachary Kuzniaimport android.view.ViewConfiguration; 33c18f2fdfcf8ec7a8534c032c4102739edb7f1c5eZachary Kuznia 34407df712e2a381e0227eb780786128a50bfcf5f5Zachary Kuzniaimport com.android.internal.R; 35407df712e2a381e0227eb780786128a50bfcf5f5Zachary Kuznia 36407df712e2a381e0227eb780786128a50bfcf5f5Zachary Kuzniaimport java.util.ArrayList; 37407df712e2a381e0227eb780786128a50bfcf5f5Zachary Kuznia 38c18f2fdfcf8ec7a8534c032c4102739edb7f1c5eZachary Kuznia/** 39c18f2fdfcf8ec7a8534c032c4102739edb7f1c5eZachary Kuznia * This class handles gesture detection for the Touch Explorer. It collects 40bf1cf52663414eec1d80d23edc029bf6223adb3bZachary Kuznia * touch events and determines when they match a gesture, as well as when they 41bf1cf52663414eec1d80d23edc029bf6223adb3bZachary Kuznia * won't match a gesture. These state changes are then surfaced to mListener. 42c18f2fdfcf8ec7a8534c032c4102739edb7f1c5eZachary Kuznia */ 43c18f2fdfcf8ec7a8534c032c4102739edb7f1c5eZachary Kuzniaclass AccessibilityGestureDetector extends GestureDetector.SimpleOnGestureListener { 44407df712e2a381e0227eb780786128a50bfcf5f5Zachary Kuznia 45407df712e2a381e0227eb780786128a50bfcf5f5Zachary Kuznia private static final boolean DEBUG = false; 46407df712e2a381e0227eb780786128a50bfcf5f5Zachary Kuznia 47407df712e2a381e0227eb780786128a50bfcf5f5Zachary Kuznia // Tag for logging received events. 48407df712e2a381e0227eb780786128a50bfcf5f5Zachary Kuznia private static final String LOG_TAG = "AccessibilityGestureDetector"; 49407df712e2a381e0227eb780786128a50bfcf5f5Zachary Kuznia 50bf1cf52663414eec1d80d23edc029bf6223adb3bZachary Kuznia /** 51bf1cf52663414eec1d80d23edc029bf6223adb3bZachary Kuznia * Listener functions are called as a result of onMoveEvent(). The current 52bf1cf52663414eec1d80d23edc029bf6223adb3bZachary Kuznia * MotionEvent in the context of these functions is the event passed into 53bf1cf52663414eec1d80d23edc029bf6223adb3bZachary Kuznia * onMotionEvent. 54bf1cf52663414eec1d80d23edc029bf6223adb3bZachary Kuznia */ 55407df712e2a381e0227eb780786128a50bfcf5f5Zachary Kuznia public interface Listener { 56bf1cf52663414eec1d80d23edc029bf6223adb3bZachary Kuznia /** 57bf1cf52663414eec1d80d23edc029bf6223adb3bZachary Kuznia * Called when the user has performed a double tap and then held down 58bf1cf52663414eec1d80d23edc029bf6223adb3bZachary Kuznia * the second tap. 59bf1cf52663414eec1d80d23edc029bf6223adb3bZachary Kuznia * 60bf1cf52663414eec1d80d23edc029bf6223adb3bZachary Kuznia * @param event The most recent MotionEvent received. 61bf1cf52663414eec1d80d23edc029bf6223adb3bZachary Kuznia * @param policyFlags The policy flags of the most recent event. 62bf1cf52663414eec1d80d23edc029bf6223adb3bZachary Kuznia */ 63bf1cf52663414eec1d80d23edc029bf6223adb3bZachary Kuznia void onDoubleTapAndHold(MotionEvent event, int policyFlags); 64bf1cf52663414eec1d80d23edc029bf6223adb3bZachary Kuznia 65bf1cf52663414eec1d80d23edc029bf6223adb3bZachary Kuznia /** 66bf1cf52663414eec1d80d23edc029bf6223adb3bZachary Kuznia * Called when the user lifts their finger on the second tap of a double 67bf1cf52663414eec1d80d23edc029bf6223adb3bZachary Kuznia * tap. 68bf1cf52663414eec1d80d23edc029bf6223adb3bZachary Kuznia * 69bf1cf52663414eec1d80d23edc029bf6223adb3bZachary Kuznia * @param event The most recent MotionEvent received. 70bf1cf52663414eec1d80d23edc029bf6223adb3bZachary Kuznia * @param policyFlags The policy flags of the most recent event. 71bf1cf52663414eec1d80d23edc029bf6223adb3bZachary Kuznia * 72bf1cf52663414eec1d80d23edc029bf6223adb3bZachary Kuznia * @return true if the event is consumed, else false 73bf1cf52663414eec1d80d23edc029bf6223adb3bZachary Kuznia */ 74bf1cf52663414eec1d80d23edc029bf6223adb3bZachary Kuznia boolean onDoubleTap(MotionEvent event, int policyFlags); 75bf1cf52663414eec1d80d23edc029bf6223adb3bZachary Kuznia 76bf1cf52663414eec1d80d23edc029bf6223adb3bZachary Kuznia /** 77bf1cf52663414eec1d80d23edc029bf6223adb3bZachary Kuznia * Called when the system has decided the event stream is a gesture. 78bf1cf52663414eec1d80d23edc029bf6223adb3bZachary Kuznia * 79bf1cf52663414eec1d80d23edc029bf6223adb3bZachary Kuznia * @return true if the event is consumed, else false 80bf1cf52663414eec1d80d23edc029bf6223adb3bZachary Kuznia */ 81bf1cf52663414eec1d80d23edc029bf6223adb3bZachary Kuznia boolean onGestureStarted(); 82bf1cf52663414eec1d80d23edc029bf6223adb3bZachary Kuznia 83bf1cf52663414eec1d80d23edc029bf6223adb3bZachary Kuznia /** 84bf1cf52663414eec1d80d23edc029bf6223adb3bZachary Kuznia * Called when an event stream is recognized as a gesture. 85bf1cf52663414eec1d80d23edc029bf6223adb3bZachary Kuznia * 86bf1cf52663414eec1d80d23edc029bf6223adb3bZachary Kuznia * @param gestureId ID of the gesture that was recognized. 87bf1cf52663414eec1d80d23edc029bf6223adb3bZachary Kuznia * 88bf1cf52663414eec1d80d23edc029bf6223adb3bZachary Kuznia * @return true if the event is consumed, else false 89bf1cf52663414eec1d80d23edc029bf6223adb3bZachary Kuznia */ 90bf1cf52663414eec1d80d23edc029bf6223adb3bZachary Kuznia boolean onGestureCompleted(int gestureId); 91bf1cf52663414eec1d80d23edc029bf6223adb3bZachary Kuznia 92bf1cf52663414eec1d80d23edc029bf6223adb3bZachary Kuznia /** 93bf1cf52663414eec1d80d23edc029bf6223adb3bZachary Kuznia * Called when the system has decided an event stream doesn't match any 94bf1cf52663414eec1d80d23edc029bf6223adb3bZachary Kuznia * known gesture. 95bf1cf52663414eec1d80d23edc029bf6223adb3bZachary Kuznia * 96bf1cf52663414eec1d80d23edc029bf6223adb3bZachary Kuznia * @param event The most recent MotionEvent received. 97bf1cf52663414eec1d80d23edc029bf6223adb3bZachary Kuznia * @param policyFlags The policy flags of the most recent event. 98bf1cf52663414eec1d80d23edc029bf6223adb3bZachary Kuznia * 99bf1cf52663414eec1d80d23edc029bf6223adb3bZachary Kuznia * @return true if the event is consumed, else false 100bf1cf52663414eec1d80d23edc029bf6223adb3bZachary Kuznia */ 101bf1cf52663414eec1d80d23edc029bf6223adb3bZachary Kuznia public boolean onGestureCancelled(MotionEvent event, int policyFlags); 102407df712e2a381e0227eb780786128a50bfcf5f5Zachary Kuznia } 103407df712e2a381e0227eb780786128a50bfcf5f5Zachary Kuznia 104c18f2fdfcf8ec7a8534c032c4102739edb7f1c5eZachary Kuznia private final Listener mListener; 105407df712e2a381e0227eb780786128a50bfcf5f5Zachary Kuznia private final GestureDetector mGestureDetector; 106407df712e2a381e0227eb780786128a50bfcf5f5Zachary Kuznia 107407df712e2a381e0227eb780786128a50bfcf5f5Zachary Kuznia // The library for gesture detection. 108407df712e2a381e0227eb780786128a50bfcf5f5Zachary Kuznia private final GestureLibrary mGestureLibrary; 109407df712e2a381e0227eb780786128a50bfcf5f5Zachary Kuznia 110407df712e2a381e0227eb780786128a50bfcf5f5Zachary Kuznia // Indicates that a single tap has occurred. 111c18f2fdfcf8ec7a8534c032c4102739edb7f1c5eZachary Kuznia private boolean mFirstTapDetected; 112407df712e2a381e0227eb780786128a50bfcf5f5Zachary Kuznia 113407df712e2a381e0227eb780786128a50bfcf5f5Zachary Kuznia // Indicates that the down event of a double tap has occured. 114c18f2fdfcf8ec7a8534c032c4102739edb7f1c5eZachary Kuznia private boolean mDoubleTapDetected; 115407df712e2a381e0227eb780786128a50bfcf5f5Zachary Kuznia 116407df712e2a381e0227eb780786128a50bfcf5f5Zachary Kuznia // Indicates that motion events are being collected to match a gesture. 117407df712e2a381e0227eb780786128a50bfcf5f5Zachary Kuznia private boolean mRecognizingGesture; 118407df712e2a381e0227eb780786128a50bfcf5f5Zachary Kuznia 1199254b3e336a01ff79742882bd98860b48ebae379Zachary Kuznia // Indicates that we've collected enough data to be sure it could be a 1209254b3e336a01ff79742882bd98860b48ebae379Zachary Kuznia // gesture. 12128239e8a619e6546580a0f3893cf55b0e97fc4e8Zachary Kuznia private boolean mGestureStarted; 1229254b3e336a01ff79742882bd98860b48ebae379Zachary Kuznia 123a70778f1c9c3ae946f890b0ac7116a5df844c39bZachary Kuznia // Indicates that motion events from the second pointer are being checked 124a70778f1c9c3ae946f890b0ac7116a5df844c39bZachary Kuznia // for a double tap. 125a70778f1c9c3ae946f890b0ac7116a5df844c39bZachary Kuznia private boolean mSecondFingerDoubleTap; 126a70778f1c9c3ae946f890b0ac7116a5df844c39bZachary Kuznia 127a70778f1c9c3ae946f890b0ac7116a5df844c39bZachary Kuznia // Tracks the most recent time where ACTION_POINTER_DOWN was sent for the 128a70778f1c9c3ae946f890b0ac7116a5df844c39bZachary Kuznia // second pointer. 129a70778f1c9c3ae946f890b0ac7116a5df844c39bZachary Kuznia private long mSecondPointerDownTime; 130a70778f1c9c3ae946f890b0ac7116a5df844c39bZachary Kuznia 131407df712e2a381e0227eb780786128a50bfcf5f5Zachary Kuznia // Policy flags of the previous event. 132c18f2fdfcf8ec7a8534c032c4102739edb7f1c5eZachary Kuznia private int mPolicyFlags; 133c18f2fdfcf8ec7a8534c032c4102739edb7f1c5eZachary Kuznia 13428239e8a619e6546580a0f3893cf55b0e97fc4e8Zachary Kuznia // These values track the previous point that was saved to use for gesture 13528239e8a619e6546580a0f3893cf55b0e97fc4e8Zachary Kuznia // detection. They are only updated when the user moves more than the 13628239e8a619e6546580a0f3893cf55b0e97fc4e8Zachary Kuznia // recognition threshold. 13728239e8a619e6546580a0f3893cf55b0e97fc4e8Zachary Kuznia private float mPreviousGestureX; 13828239e8a619e6546580a0f3893cf55b0e97fc4e8Zachary Kuznia private float mPreviousGestureY; 139407df712e2a381e0227eb780786128a50bfcf5f5Zachary Kuznia 14028239e8a619e6546580a0f3893cf55b0e97fc4e8Zachary Kuznia // These values track the previous point that was used to determine if there 14128239e8a619e6546580a0f3893cf55b0e97fc4e8Zachary Kuznia // was a transition into or out of gesture detection. They are updated when 14228239e8a619e6546580a0f3893cf55b0e97fc4e8Zachary Kuznia // the user moves more than the detection threshold. 1439254b3e336a01ff79742882bd98860b48ebae379Zachary Kuznia private float mBaseX; 1449254b3e336a01ff79742882bd98860b48ebae379Zachary Kuznia private float mBaseY; 14528239e8a619e6546580a0f3893cf55b0e97fc4e8Zachary Kuznia private long mBaseTime; 1469254b3e336a01ff79742882bd98860b48ebae379Zachary Kuznia 14728239e8a619e6546580a0f3893cf55b0e97fc4e8Zachary Kuznia // This is the calculated movement threshold used track if the user is still 14828239e8a619e6546580a0f3893cf55b0e97fc4e8Zachary Kuznia // moving their finger. 14928239e8a619e6546580a0f3893cf55b0e97fc4e8Zachary Kuznia private final float mGestureDetectionThreshold; 1509254b3e336a01ff79742882bd98860b48ebae379Zachary Kuznia 151407df712e2a381e0227eb780786128a50bfcf5f5Zachary Kuznia // Buffer for storing points for gesture detection. 152407df712e2a381e0227eb780786128a50bfcf5f5Zachary Kuznia private final ArrayList<GesturePoint> mStrokeBuffer = new ArrayList<GesturePoint>(100); 153407df712e2a381e0227eb780786128a50bfcf5f5Zachary Kuznia 154407df712e2a381e0227eb780786128a50bfcf5f5Zachary Kuznia // The minimal delta between moves to add a gesture point. 155407df712e2a381e0227eb780786128a50bfcf5f5Zachary Kuznia private static final int TOUCH_TOLERANCE = 3; 156407df712e2a381e0227eb780786128a50bfcf5f5Zachary Kuznia 157407df712e2a381e0227eb780786128a50bfcf5f5Zachary Kuznia // The minimal score for accepting a predicted gesture. 158407df712e2a381e0227eb780786128a50bfcf5f5Zachary Kuznia private static final float MIN_PREDICTION_SCORE = 2.0f; 159407df712e2a381e0227eb780786128a50bfcf5f5Zachary Kuznia 160c8e941f4ba5a7ce488deb7606606e2ff6c8f7cf8Zachary Kuznia // Distance a finger must travel before we decide if it is a gesture or not. 16128239e8a619e6546580a0f3893cf55b0e97fc4e8Zachary Kuznia private static final int GESTURE_CONFIRM_MM = 10; 162c8e941f4ba5a7ce488deb7606606e2ff6c8f7cf8Zachary Kuznia 163c8e941f4ba5a7ce488deb7606606e2ff6c8f7cf8Zachary Kuznia // Time threshold used to determine if an interaction is a gesture or not. 164c8e941f4ba5a7ce488deb7606606e2ff6c8f7cf8Zachary Kuznia // If the first movement of 1cm takes longer than this value, we assume it's 165c8e941f4ba5a7ce488deb7606606e2ff6c8f7cf8Zachary Kuznia // a slow movement, and therefore not a gesture. 166c8e941f4ba5a7ce488deb7606606e2ff6c8f7cf8Zachary Kuznia // 167c8e941f4ba5a7ce488deb7606606e2ff6c8f7cf8Zachary Kuznia // This value was determined by measuring the time for the first 1cm 168c8e941f4ba5a7ce488deb7606606e2ff6c8f7cf8Zachary Kuznia // movement when gesturing, and touch exploring. Based on user testing, 169c8e941f4ba5a7ce488deb7606606e2ff6c8f7cf8Zachary Kuznia // all gestures started with the initial movement taking less than 100ms. 170c8e941f4ba5a7ce488deb7606606e2ff6c8f7cf8Zachary Kuznia // When touch exploring, the first movement almost always takes longer than 1719002a152c4a504ea404fab528c9e7d4ef3cb3247Zachary Kuznia // 200ms. From this data, 200ms seems the best value to decide what 172c8e941f4ba5a7ce488deb7606606e2ff6c8f7cf8Zachary Kuznia // kind of interaction it is. 1739002a152c4a504ea404fab528c9e7d4ef3cb3247Zachary Kuznia private static final long CANCEL_ON_PAUSE_THRESHOLD_NOT_STARTED_MS = 200; 174c8e941f4ba5a7ce488deb7606606e2ff6c8f7cf8Zachary Kuznia 175c8e941f4ba5a7ce488deb7606606e2ff6c8f7cf8Zachary Kuznia // Time threshold used to determine if a gesture should be cancelled. If 176c8e941f4ba5a7ce488deb7606606e2ff6c8f7cf8Zachary Kuznia // the finger pauses for longer than this delay, the ongoing gesture is 177c8e941f4ba5a7ce488deb7606606e2ff6c8f7cf8Zachary Kuznia // cancelled. 178c8e941f4ba5a7ce488deb7606606e2ff6c8f7cf8Zachary Kuznia private static final long CANCEL_ON_PAUSE_THRESHOLD_STARTED_MS = 500; 1799254b3e336a01ff79742882bd98860b48ebae379Zachary Kuznia 180c18f2fdfcf8ec7a8534c032c4102739edb7f1c5eZachary Kuznia AccessibilityGestureDetector(Context context, Listener listener) { 181c18f2fdfcf8ec7a8534c032c4102739edb7f1c5eZachary Kuznia mListener = listener; 182c18f2fdfcf8ec7a8534c032c4102739edb7f1c5eZachary Kuznia 183c18f2fdfcf8ec7a8534c032c4102739edb7f1c5eZachary Kuznia mGestureDetector = new GestureDetector(context, this); 184c18f2fdfcf8ec7a8534c032c4102739edb7f1c5eZachary Kuznia mGestureDetector.setOnDoubleTapListener(this); 185407df712e2a381e0227eb780786128a50bfcf5f5Zachary Kuznia 186407df712e2a381e0227eb780786128a50bfcf5f5Zachary Kuznia mGestureLibrary = GestureLibraries.fromRawResource(context, R.raw.accessibility_gestures); 187407df712e2a381e0227eb780786128a50bfcf5f5Zachary Kuznia mGestureLibrary.setOrientationStyle(8 /* GestureStore.ORIENTATION_SENSITIVE_8 */); 188407df712e2a381e0227eb780786128a50bfcf5f5Zachary Kuznia mGestureLibrary.setSequenceType(GestureStore.SEQUENCE_SENSITIVE); 189407df712e2a381e0227eb780786128a50bfcf5f5Zachary Kuznia mGestureLibrary.load(); 1909254b3e336a01ff79742882bd98860b48ebae379Zachary Kuznia 19128239e8a619e6546580a0f3893cf55b0e97fc4e8Zachary Kuznia mGestureDetectionThreshold = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_MM, 1, 19228239e8a619e6546580a0f3893cf55b0e97fc4e8Zachary Kuznia context.getResources().getDisplayMetrics()) * GESTURE_CONFIRM_MM; 193c18f2fdfcf8ec7a8534c032c4102739edb7f1c5eZachary Kuznia } 194c18f2fdfcf8ec7a8534c032c4102739edb7f1c5eZachary Kuznia 195bf1cf52663414eec1d80d23edc029bf6223adb3bZachary Kuznia /** 196bf1cf52663414eec1d80d23edc029bf6223adb3bZachary Kuznia * Handle a motion event. If an action is completed, the appropriate 197bf1cf52663414eec1d80d23edc029bf6223adb3bZachary Kuznia * callback on mListener is called, and the return value of the callback is 198bf1cf52663414eec1d80d23edc029bf6223adb3bZachary Kuznia * passed to the caller. 199bf1cf52663414eec1d80d23edc029bf6223adb3bZachary Kuznia * 200bf1cf52663414eec1d80d23edc029bf6223adb3bZachary Kuznia * @param event The raw motion event. It's important that this be the raw 201bf1cf52663414eec1d80d23edc029bf6223adb3bZachary Kuznia * event, before any transformations have been applied, so that measurements 202bf1cf52663414eec1d80d23edc029bf6223adb3bZachary Kuznia * can be made in physical units. 203bf1cf52663414eec1d80d23edc029bf6223adb3bZachary Kuznia * @param policyFlags Policy flags for the event. 204bf1cf52663414eec1d80d23edc029bf6223adb3bZachary Kuznia * 205bf1cf52663414eec1d80d23edc029bf6223adb3bZachary Kuznia * @return true if the event is consumed, else false 206bf1cf52663414eec1d80d23edc029bf6223adb3bZachary Kuznia */ 207407df712e2a381e0227eb780786128a50bfcf5f5Zachary Kuznia public boolean onMotionEvent(MotionEvent event, int policyFlags) { 208407df712e2a381e0227eb780786128a50bfcf5f5Zachary Kuznia final float x = event.getX(); 209407df712e2a381e0227eb780786128a50bfcf5f5Zachary Kuznia final float y = event.getY(); 21028239e8a619e6546580a0f3893cf55b0e97fc4e8Zachary Kuznia final long time = event.getEventTime(); 211407df712e2a381e0227eb780786128a50bfcf5f5Zachary Kuznia 212c18f2fdfcf8ec7a8534c032c4102739edb7f1c5eZachary Kuznia mPolicyFlags = policyFlags; 213c18f2fdfcf8ec7a8534c032c4102739edb7f1c5eZachary Kuznia switch (event.getActionMasked()) { 214c18f2fdfcf8ec7a8534c032c4102739edb7f1c5eZachary Kuznia case MotionEvent.ACTION_DOWN: 215c18f2fdfcf8ec7a8534c032c4102739edb7f1c5eZachary Kuznia mDoubleTapDetected = false; 216a70778f1c9c3ae946f890b0ac7116a5df844c39bZachary Kuznia mSecondFingerDoubleTap = false; 217407df712e2a381e0227eb780786128a50bfcf5f5Zachary Kuznia mRecognizingGesture = true; 21828239e8a619e6546580a0f3893cf55b0e97fc4e8Zachary Kuznia mGestureStarted = false; 21928239e8a619e6546580a0f3893cf55b0e97fc4e8Zachary Kuznia mPreviousGestureX = x; 22028239e8a619e6546580a0f3893cf55b0e97fc4e8Zachary Kuznia mPreviousGestureY = y; 22128239e8a619e6546580a0f3893cf55b0e97fc4e8Zachary Kuznia mStrokeBuffer.clear(); 22228239e8a619e6546580a0f3893cf55b0e97fc4e8Zachary Kuznia mStrokeBuffer.add(new GesturePoint(x, y, time)); 22328239e8a619e6546580a0f3893cf55b0e97fc4e8Zachary Kuznia 2249254b3e336a01ff79742882bd98860b48ebae379Zachary Kuznia mBaseX = x; 2259254b3e336a01ff79742882bd98860b48ebae379Zachary Kuznia mBaseY = y; 22628239e8a619e6546580a0f3893cf55b0e97fc4e8Zachary Kuznia mBaseTime = time; 227407df712e2a381e0227eb780786128a50bfcf5f5Zachary Kuznia break; 228407df712e2a381e0227eb780786128a50bfcf5f5Zachary Kuznia 229407df712e2a381e0227eb780786128a50bfcf5f5Zachary Kuznia case MotionEvent.ACTION_MOVE: 230407df712e2a381e0227eb780786128a50bfcf5f5Zachary Kuznia if (mRecognizingGesture) { 23128239e8a619e6546580a0f3893cf55b0e97fc4e8Zachary Kuznia final float deltaX = mBaseX - x; 23228239e8a619e6546580a0f3893cf55b0e97fc4e8Zachary Kuznia final float deltaY = mBaseY - y; 23328239e8a619e6546580a0f3893cf55b0e97fc4e8Zachary Kuznia final double moveDelta = Math.hypot(deltaX, deltaY); 23428239e8a619e6546580a0f3893cf55b0e97fc4e8Zachary Kuznia if (moveDelta > mGestureDetectionThreshold) { 23528239e8a619e6546580a0f3893cf55b0e97fc4e8Zachary Kuznia // If the pointer has moved more than the threshold, 23628239e8a619e6546580a0f3893cf55b0e97fc4e8Zachary Kuznia // update the stored values. 23728239e8a619e6546580a0f3893cf55b0e97fc4e8Zachary Kuznia mBaseX = x; 23828239e8a619e6546580a0f3893cf55b0e97fc4e8Zachary Kuznia mBaseY = y; 23928239e8a619e6546580a0f3893cf55b0e97fc4e8Zachary Kuznia mBaseTime = time; 24028239e8a619e6546580a0f3893cf55b0e97fc4e8Zachary Kuznia 241fd6aa8a1fa6345030c382d7f2d9eac456b425f55Zachary Kuznia // Since the pointer has moved, this is not a double 242fd6aa8a1fa6345030c382d7f2d9eac456b425f55Zachary Kuznia // tap. 243fd6aa8a1fa6345030c382d7f2d9eac456b425f55Zachary Kuznia mFirstTapDetected = false; 244fd6aa8a1fa6345030c382d7f2d9eac456b425f55Zachary Kuznia mDoubleTapDetected = false; 245fd6aa8a1fa6345030c382d7f2d9eac456b425f55Zachary Kuznia 24628239e8a619e6546580a0f3893cf55b0e97fc4e8Zachary Kuznia // If this hasn't been confirmed as a gesture yet, send 24728239e8a619e6546580a0f3893cf55b0e97fc4e8Zachary Kuznia // the event. 24828239e8a619e6546580a0f3893cf55b0e97fc4e8Zachary Kuznia if (!mGestureStarted) { 24928239e8a619e6546580a0f3893cf55b0e97fc4e8Zachary Kuznia mGestureStarted = true; 250bf1cf52663414eec1d80d23edc029bf6223adb3bZachary Kuznia return mListener.onGestureStarted(); 25128239e8a619e6546580a0f3893cf55b0e97fc4e8Zachary Kuznia } 252fd6aa8a1fa6345030c382d7f2d9eac456b425f55Zachary Kuznia } else if (!mFirstTapDetected) { 253fd6aa8a1fa6345030c382d7f2d9eac456b425f55Zachary Kuznia // The finger may not move if they are double tapping. 254fd6aa8a1fa6345030c382d7f2d9eac456b425f55Zachary Kuznia // In that case, we shouldn't cancel the gesture. 25528239e8a619e6546580a0f3893cf55b0e97fc4e8Zachary Kuznia final long timeDelta = time - mBaseTime; 25628239e8a619e6546580a0f3893cf55b0e97fc4e8Zachary Kuznia final long threshold = mGestureStarted ? 25728239e8a619e6546580a0f3893cf55b0e97fc4e8Zachary Kuznia CANCEL_ON_PAUSE_THRESHOLD_STARTED_MS : 25828239e8a619e6546580a0f3893cf55b0e97fc4e8Zachary Kuznia CANCEL_ON_PAUSE_THRESHOLD_NOT_STARTED_MS; 25928239e8a619e6546580a0f3893cf55b0e97fc4e8Zachary Kuznia 26028239e8a619e6546580a0f3893cf55b0e97fc4e8Zachary Kuznia // If the pointer hasn't moved for longer than the 26128239e8a619e6546580a0f3893cf55b0e97fc4e8Zachary Kuznia // timeout, cancel gesture detection. 26228239e8a619e6546580a0f3893cf55b0e97fc4e8Zachary Kuznia if (timeDelta > threshold) { 26328239e8a619e6546580a0f3893cf55b0e97fc4e8Zachary Kuznia cancelGesture(); 264bf1cf52663414eec1d80d23edc029bf6223adb3bZachary Kuznia return mListener.onGestureCancelled(event, policyFlags); 2659254b3e336a01ff79742882bd98860b48ebae379Zachary Kuznia } 2669254b3e336a01ff79742882bd98860b48ebae379Zachary Kuznia } 26728239e8a619e6546580a0f3893cf55b0e97fc4e8Zachary Kuznia 26828239e8a619e6546580a0f3893cf55b0e97fc4e8Zachary Kuznia final float dX = Math.abs(x - mPreviousGestureX); 26928239e8a619e6546580a0f3893cf55b0e97fc4e8Zachary Kuznia final float dY = Math.abs(y - mPreviousGestureY); 270407df712e2a381e0227eb780786128a50bfcf5f5Zachary Kuznia if (dX >= TOUCH_TOLERANCE || dY >= TOUCH_TOLERANCE) { 27128239e8a619e6546580a0f3893cf55b0e97fc4e8Zachary Kuznia mPreviousGestureX = x; 27228239e8a619e6546580a0f3893cf55b0e97fc4e8Zachary Kuznia mPreviousGestureY = y; 27328239e8a619e6546580a0f3893cf55b0e97fc4e8Zachary Kuznia mStrokeBuffer.add(new GesturePoint(x, y, time)); 274407df712e2a381e0227eb780786128a50bfcf5f5Zachary Kuznia } 275407df712e2a381e0227eb780786128a50bfcf5f5Zachary Kuznia } 276c18f2fdfcf8ec7a8534c032c4102739edb7f1c5eZachary Kuznia break; 277c18f2fdfcf8ec7a8534c032c4102739edb7f1c5eZachary Kuznia 278c18f2fdfcf8ec7a8534c032c4102739edb7f1c5eZachary Kuznia case MotionEvent.ACTION_UP: 279bf1cf52663414eec1d80d23edc029bf6223adb3bZachary Kuznia if (mDoubleTapDetected) { 280bf1cf52663414eec1d80d23edc029bf6223adb3bZachary Kuznia return finishDoubleTap(event, policyFlags); 281407df712e2a381e0227eb780786128a50bfcf5f5Zachary Kuznia } 28228239e8a619e6546580a0f3893cf55b0e97fc4e8Zachary Kuznia if (mGestureStarted) { 28328239e8a619e6546580a0f3893cf55b0e97fc4e8Zachary Kuznia mStrokeBuffer.add(new GesturePoint(x, y, time)); 284407df712e2a381e0227eb780786128a50bfcf5f5Zachary Kuznia 285bf1cf52663414eec1d80d23edc029bf6223adb3bZachary Kuznia return recognizeGesture(event, policyFlags); 286407df712e2a381e0227eb780786128a50bfcf5f5Zachary Kuznia } 287c18f2fdfcf8ec7a8534c032c4102739edb7f1c5eZachary Kuznia break; 288a70778f1c9c3ae946f890b0ac7116a5df844c39bZachary Kuznia 289a70778f1c9c3ae946f890b0ac7116a5df844c39bZachary Kuznia case MotionEvent.ACTION_POINTER_DOWN: 290a70778f1c9c3ae946f890b0ac7116a5df844c39bZachary Kuznia // Once a second finger is used, we're definitely not 291a70778f1c9c3ae946f890b0ac7116a5df844c39bZachary Kuznia // recognizing a gesture. 292a70778f1c9c3ae946f890b0ac7116a5df844c39bZachary Kuznia cancelGesture(); 293a70778f1c9c3ae946f890b0ac7116a5df844c39bZachary Kuznia 294a70778f1c9c3ae946f890b0ac7116a5df844c39bZachary Kuznia if (event.getPointerCount() == 2) { 295a70778f1c9c3ae946f890b0ac7116a5df844c39bZachary Kuznia // If this was the second finger, attempt to recognize double 296a70778f1c9c3ae946f890b0ac7116a5df844c39bZachary Kuznia // taps on it. 297a70778f1c9c3ae946f890b0ac7116a5df844c39bZachary Kuznia mSecondFingerDoubleTap = true; 29828239e8a619e6546580a0f3893cf55b0e97fc4e8Zachary Kuznia mSecondPointerDownTime = time; 299a70778f1c9c3ae946f890b0ac7116a5df844c39bZachary Kuznia } else { 300a70778f1c9c3ae946f890b0ac7116a5df844c39bZachary Kuznia // If there are more than two fingers down, stop watching 301a70778f1c9c3ae946f890b0ac7116a5df844c39bZachary Kuznia // for a double tap. 302a70778f1c9c3ae946f890b0ac7116a5df844c39bZachary Kuznia mSecondFingerDoubleTap = false; 303a70778f1c9c3ae946f890b0ac7116a5df844c39bZachary Kuznia } 304a70778f1c9c3ae946f890b0ac7116a5df844c39bZachary Kuznia break; 305a70778f1c9c3ae946f890b0ac7116a5df844c39bZachary Kuznia 306a70778f1c9c3ae946f890b0ac7116a5df844c39bZachary Kuznia case MotionEvent.ACTION_POINTER_UP: 307a70778f1c9c3ae946f890b0ac7116a5df844c39bZachary Kuznia // If we're detecting taps on the second finger, see if we 308a70778f1c9c3ae946f890b0ac7116a5df844c39bZachary Kuznia // should finish the double tap. 309bf1cf52663414eec1d80d23edc029bf6223adb3bZachary Kuznia if (mSecondFingerDoubleTap && mDoubleTapDetected) { 310bf1cf52663414eec1d80d23edc029bf6223adb3bZachary Kuznia return finishDoubleTap(event, policyFlags); 311a70778f1c9c3ae946f890b0ac7116a5df844c39bZachary Kuznia } 312a70778f1c9c3ae946f890b0ac7116a5df844c39bZachary Kuznia break; 3139254b3e336a01ff79742882bd98860b48ebae379Zachary Kuznia 3149254b3e336a01ff79742882bd98860b48ebae379Zachary Kuznia case MotionEvent.ACTION_CANCEL: 3159254b3e336a01ff79742882bd98860b48ebae379Zachary Kuznia clear(); 3169254b3e336a01ff79742882bd98860b48ebae379Zachary Kuznia break; 317a70778f1c9c3ae946f890b0ac7116a5df844c39bZachary Kuznia } 318a70778f1c9c3ae946f890b0ac7116a5df844c39bZachary Kuznia 319a70778f1c9c3ae946f890b0ac7116a5df844c39bZachary Kuznia // If we're detecting taps on the second finger, map events from the 320a70778f1c9c3ae946f890b0ac7116a5df844c39bZachary Kuznia // finger to the first finger. 321a70778f1c9c3ae946f890b0ac7116a5df844c39bZachary Kuznia if (mSecondFingerDoubleTap) { 322a70778f1c9c3ae946f890b0ac7116a5df844c39bZachary Kuznia MotionEvent newEvent = mapSecondPointerToFirstPointer(event); 323a70778f1c9c3ae946f890b0ac7116a5df844c39bZachary Kuznia if (newEvent == null) { 324a70778f1c9c3ae946f890b0ac7116a5df844c39bZachary Kuznia return false; 325a70778f1c9c3ae946f890b0ac7116a5df844c39bZachary Kuznia } 326a70778f1c9c3ae946f890b0ac7116a5df844c39bZachary Kuznia boolean handled = mGestureDetector.onTouchEvent(newEvent); 327a70778f1c9c3ae946f890b0ac7116a5df844c39bZachary Kuznia newEvent.recycle(); 328a70778f1c9c3ae946f890b0ac7116a5df844c39bZachary Kuznia return handled; 329c18f2fdfcf8ec7a8534c032c4102739edb7f1c5eZachary Kuznia } 330407df712e2a381e0227eb780786128a50bfcf5f5Zachary Kuznia 331407df712e2a381e0227eb780786128a50bfcf5f5Zachary Kuznia if (!mRecognizingGesture) { 332407df712e2a381e0227eb780786128a50bfcf5f5Zachary Kuznia return false; 333407df712e2a381e0227eb780786128a50bfcf5f5Zachary Kuznia } 334407df712e2a381e0227eb780786128a50bfcf5f5Zachary Kuznia 335407df712e2a381e0227eb780786128a50bfcf5f5Zachary Kuznia // Pass the event on to the standard gesture detector. 336407df712e2a381e0227eb780786128a50bfcf5f5Zachary Kuznia return mGestureDetector.onTouchEvent(event); 337c18f2fdfcf8ec7a8534c032c4102739edb7f1c5eZachary Kuznia } 338c18f2fdfcf8ec7a8534c032c4102739edb7f1c5eZachary Kuznia 339c18f2fdfcf8ec7a8534c032c4102739edb7f1c5eZachary Kuznia public void clear() { 340c18f2fdfcf8ec7a8534c032c4102739edb7f1c5eZachary Kuznia mFirstTapDetected = false; 341c18f2fdfcf8ec7a8534c032c4102739edb7f1c5eZachary Kuznia mDoubleTapDetected = false; 342a70778f1c9c3ae946f890b0ac7116a5df844c39bZachary Kuznia mSecondFingerDoubleTap = false; 34328239e8a619e6546580a0f3893cf55b0e97fc4e8Zachary Kuznia mGestureStarted = false; 344407df712e2a381e0227eb780786128a50bfcf5f5Zachary Kuznia cancelGesture(); 345c18f2fdfcf8ec7a8534c032c4102739edb7f1c5eZachary Kuznia } 346c18f2fdfcf8ec7a8534c032c4102739edb7f1c5eZachary Kuznia 347c18f2fdfcf8ec7a8534c032c4102739edb7f1c5eZachary Kuznia public boolean firstTapDetected() { 348c18f2fdfcf8ec7a8534c032c4102739edb7f1c5eZachary Kuznia return mFirstTapDetected; 349c18f2fdfcf8ec7a8534c032c4102739edb7f1c5eZachary Kuznia } 350c18f2fdfcf8ec7a8534c032c4102739edb7f1c5eZachary Kuznia 351c18f2fdfcf8ec7a8534c032c4102739edb7f1c5eZachary Kuznia @Override 352c18f2fdfcf8ec7a8534c032c4102739edb7f1c5eZachary Kuznia public void onLongPress(MotionEvent e) { 353c18f2fdfcf8ec7a8534c032c4102739edb7f1c5eZachary Kuznia maybeSendLongPress(e, mPolicyFlags); 354c18f2fdfcf8ec7a8534c032c4102739edb7f1c5eZachary Kuznia } 355c18f2fdfcf8ec7a8534c032c4102739edb7f1c5eZachary Kuznia 356c18f2fdfcf8ec7a8534c032c4102739edb7f1c5eZachary Kuznia @Override 357c18f2fdfcf8ec7a8534c032c4102739edb7f1c5eZachary Kuznia public boolean onSingleTapUp(MotionEvent event) { 358c18f2fdfcf8ec7a8534c032c4102739edb7f1c5eZachary Kuznia mFirstTapDetected = true; 359c18f2fdfcf8ec7a8534c032c4102739edb7f1c5eZachary Kuznia return false; 360c18f2fdfcf8ec7a8534c032c4102739edb7f1c5eZachary Kuznia } 361c18f2fdfcf8ec7a8534c032c4102739edb7f1c5eZachary Kuznia 362c18f2fdfcf8ec7a8534c032c4102739edb7f1c5eZachary Kuznia @Override 363c18f2fdfcf8ec7a8534c032c4102739edb7f1c5eZachary Kuznia public boolean onSingleTapConfirmed(MotionEvent event) { 364c18f2fdfcf8ec7a8534c032c4102739edb7f1c5eZachary Kuznia clear(); 365c18f2fdfcf8ec7a8534c032c4102739edb7f1c5eZachary Kuznia return false; 366c18f2fdfcf8ec7a8534c032c4102739edb7f1c5eZachary Kuznia } 367c18f2fdfcf8ec7a8534c032c4102739edb7f1c5eZachary Kuznia 368c18f2fdfcf8ec7a8534c032c4102739edb7f1c5eZachary Kuznia @Override 369c18f2fdfcf8ec7a8534c032c4102739edb7f1c5eZachary Kuznia public boolean onDoubleTap(MotionEvent event) { 370c18f2fdfcf8ec7a8534c032c4102739edb7f1c5eZachary Kuznia // The processing of the double tap is deferred until the finger is 371c18f2fdfcf8ec7a8534c032c4102739edb7f1c5eZachary Kuznia // lifted, so that we can detect a long press on the second tap. 372c18f2fdfcf8ec7a8534c032c4102739edb7f1c5eZachary Kuznia mDoubleTapDetected = true; 373fd6aa8a1fa6345030c382d7f2d9eac456b425f55Zachary Kuznia return false; 374c18f2fdfcf8ec7a8534c032c4102739edb7f1c5eZachary Kuznia } 375c18f2fdfcf8ec7a8534c032c4102739edb7f1c5eZachary Kuznia 376c18f2fdfcf8ec7a8534c032c4102739edb7f1c5eZachary Kuznia private void maybeSendLongPress(MotionEvent event, int policyFlags) { 377c18f2fdfcf8ec7a8534c032c4102739edb7f1c5eZachary Kuznia if (!mDoubleTapDetected) { 378c18f2fdfcf8ec7a8534c032c4102739edb7f1c5eZachary Kuznia return; 379c18f2fdfcf8ec7a8534c032c4102739edb7f1c5eZachary Kuznia } 380c18f2fdfcf8ec7a8534c032c4102739edb7f1c5eZachary Kuznia 381c18f2fdfcf8ec7a8534c032c4102739edb7f1c5eZachary Kuznia clear(); 382c18f2fdfcf8ec7a8534c032c4102739edb7f1c5eZachary Kuznia 383c18f2fdfcf8ec7a8534c032c4102739edb7f1c5eZachary Kuznia mListener.onDoubleTapAndHold(event, policyFlags); 384c18f2fdfcf8ec7a8534c032c4102739edb7f1c5eZachary Kuznia } 385c18f2fdfcf8ec7a8534c032c4102739edb7f1c5eZachary Kuznia 386bf1cf52663414eec1d80d23edc029bf6223adb3bZachary Kuznia private boolean finishDoubleTap(MotionEvent event, int policyFlags) { 387c18f2fdfcf8ec7a8534c032c4102739edb7f1c5eZachary Kuznia clear(); 388c18f2fdfcf8ec7a8534c032c4102739edb7f1c5eZachary Kuznia 389407df712e2a381e0227eb780786128a50bfcf5f5Zachary Kuznia return mListener.onDoubleTap(event, policyFlags); 390c18f2fdfcf8ec7a8534c032c4102739edb7f1c5eZachary Kuznia } 391c18f2fdfcf8ec7a8534c032c4102739edb7f1c5eZachary Kuznia 3929254b3e336a01ff79742882bd98860b48ebae379Zachary Kuznia private void cancelGesture() { 3939254b3e336a01ff79742882bd98860b48ebae379Zachary Kuznia mRecognizingGesture = false; 39428239e8a619e6546580a0f3893cf55b0e97fc4e8Zachary Kuznia mGestureStarted = false; 3959254b3e336a01ff79742882bd98860b48ebae379Zachary Kuznia mStrokeBuffer.clear(); 3969254b3e336a01ff79742882bd98860b48ebae379Zachary Kuznia } 3979254b3e336a01ff79742882bd98860b48ebae379Zachary Kuznia 398bf1cf52663414eec1d80d23edc029bf6223adb3bZachary Kuznia private boolean recognizeGesture(MotionEvent event, int policyFlags) { 399407df712e2a381e0227eb780786128a50bfcf5f5Zachary Kuznia Gesture gesture = new Gesture(); 400407df712e2a381e0227eb780786128a50bfcf5f5Zachary Kuznia gesture.addStroke(new GestureStroke(mStrokeBuffer)); 401407df712e2a381e0227eb780786128a50bfcf5f5Zachary Kuznia 402407df712e2a381e0227eb780786128a50bfcf5f5Zachary Kuznia ArrayList<Prediction> predictions = mGestureLibrary.recognize(gesture); 403407df712e2a381e0227eb780786128a50bfcf5f5Zachary Kuznia if (!predictions.isEmpty()) { 404407df712e2a381e0227eb780786128a50bfcf5f5Zachary Kuznia Prediction bestPrediction = predictions.get(0); 405407df712e2a381e0227eb780786128a50bfcf5f5Zachary Kuznia if (bestPrediction.score >= MIN_PREDICTION_SCORE) { 406407df712e2a381e0227eb780786128a50bfcf5f5Zachary Kuznia if (DEBUG) { 407407df712e2a381e0227eb780786128a50bfcf5f5Zachary Kuznia Slog.i(LOG_TAG, "gesture: " + bestPrediction.name + " score: " 408407df712e2a381e0227eb780786128a50bfcf5f5Zachary Kuznia + bestPrediction.score); 409407df712e2a381e0227eb780786128a50bfcf5f5Zachary Kuznia } 410407df712e2a381e0227eb780786128a50bfcf5f5Zachary Kuznia try { 411407df712e2a381e0227eb780786128a50bfcf5f5Zachary Kuznia final int gestureId = Integer.parseInt(bestPrediction.name); 412bf1cf52663414eec1d80d23edc029bf6223adb3bZachary Kuznia return mListener.onGestureCompleted(gestureId); 413407df712e2a381e0227eb780786128a50bfcf5f5Zachary Kuznia } catch (NumberFormatException nfe) { 414407df712e2a381e0227eb780786128a50bfcf5f5Zachary Kuznia Slog.w(LOG_TAG, "Non numeric gesture id:" + bestPrediction.name); 415407df712e2a381e0227eb780786128a50bfcf5f5Zachary Kuznia } 416407df712e2a381e0227eb780786128a50bfcf5f5Zachary Kuznia } 417407df712e2a381e0227eb780786128a50bfcf5f5Zachary Kuznia } 418407df712e2a381e0227eb780786128a50bfcf5f5Zachary Kuznia 419bf1cf52663414eec1d80d23edc029bf6223adb3bZachary Kuznia return mListener.onGestureCancelled(event, policyFlags); 420c18f2fdfcf8ec7a8534c032c4102739edb7f1c5eZachary Kuznia } 421a70778f1c9c3ae946f890b0ac7116a5df844c39bZachary Kuznia 422a70778f1c9c3ae946f890b0ac7116a5df844c39bZachary Kuznia private MotionEvent mapSecondPointerToFirstPointer(MotionEvent event) { 423a70778f1c9c3ae946f890b0ac7116a5df844c39bZachary Kuznia // Only map basic events when two fingers are down. 424a70778f1c9c3ae946f890b0ac7116a5df844c39bZachary Kuznia if (event.getPointerCount() != 2 || 425a70778f1c9c3ae946f890b0ac7116a5df844c39bZachary Kuznia (event.getActionMasked() != MotionEvent.ACTION_POINTER_DOWN && 426a70778f1c9c3ae946f890b0ac7116a5df844c39bZachary Kuznia event.getActionMasked() != MotionEvent.ACTION_POINTER_UP && 427a70778f1c9c3ae946f890b0ac7116a5df844c39bZachary Kuznia event.getActionMasked() != MotionEvent.ACTION_MOVE)) { 428a70778f1c9c3ae946f890b0ac7116a5df844c39bZachary Kuznia return null; 429a70778f1c9c3ae946f890b0ac7116a5df844c39bZachary Kuznia } 430a70778f1c9c3ae946f890b0ac7116a5df844c39bZachary Kuznia 431a70778f1c9c3ae946f890b0ac7116a5df844c39bZachary Kuznia int action = event.getActionMasked(); 432a70778f1c9c3ae946f890b0ac7116a5df844c39bZachary Kuznia 433a70778f1c9c3ae946f890b0ac7116a5df844c39bZachary Kuznia if (action == MotionEvent.ACTION_POINTER_DOWN) { 434a70778f1c9c3ae946f890b0ac7116a5df844c39bZachary Kuznia action = MotionEvent.ACTION_DOWN; 435a70778f1c9c3ae946f890b0ac7116a5df844c39bZachary Kuznia } else if (action == MotionEvent.ACTION_POINTER_UP) { 436a70778f1c9c3ae946f890b0ac7116a5df844c39bZachary Kuznia action = MotionEvent.ACTION_UP; 437a70778f1c9c3ae946f890b0ac7116a5df844c39bZachary Kuznia } 438a70778f1c9c3ae946f890b0ac7116a5df844c39bZachary Kuznia 439a70778f1c9c3ae946f890b0ac7116a5df844c39bZachary Kuznia // Map the information from the second pointer to the first. 440a70778f1c9c3ae946f890b0ac7116a5df844c39bZachary Kuznia return MotionEvent.obtain(mSecondPointerDownTime, event.getEventTime(), action, 441a70778f1c9c3ae946f890b0ac7116a5df844c39bZachary Kuznia event.getX(1), event.getY(1), event.getPressure(1), event.getSize(1), 442a70778f1c9c3ae946f890b0ac7116a5df844c39bZachary Kuznia event.getMetaState(), event.getXPrecision(), event.getYPrecision(), 443a70778f1c9c3ae946f890b0ac7116a5df844c39bZachary Kuznia event.getDeviceId(), event.getEdgeFlags()); 444a70778f1c9c3ae946f890b0ac7116a5df844c39bZachary Kuznia } 445c18f2fdfcf8ec7a8534c032c4102739edb7f1c5eZachary Kuznia} 446