ScaleGestureDetector.java revision e33cef8037cb87386e17bcf8701a47452d262fa6
1ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell/* 2ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell * Copyright (C) 2010 The Android Open Source Project 3ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell * 4ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell * Licensed under the Apache License, Version 2.0 (the "License"); 5ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell * you may not use this file except in compliance with the License. 6ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell * You may obtain a copy of the License at 7ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell * 8ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell * http://www.apache.org/licenses/LICENSE-2.0 9ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell * 10ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell * Unless required by applicable law or agreed to in writing, software 11ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell * distributed under the License is distributed on an "AS IS" BASIS, 12ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell * See the License for the specific language governing permissions and 14ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell * limitations under the License. 15ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell */ 16ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell 17ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powellpackage android.view; 18ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell 19ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powellimport android.content.Context; 20380b525220955ce4e4df8943b89082c7443ebfddAdam Powellimport android.util.DisplayMetrics; 21346c8fb036b99a33756c66dcebeb5d0f67a1df72Adam Powellimport android.util.FloatMath; 22ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell 23ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell/** 24ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell * Detects transformation gestures involving more than one pointer ("multitouch") 25ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell * using the supplied {@link MotionEvent}s. The {@link OnScaleGestureListener} 26ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell * callback will notify users when a particular gesture event has occurred. 27ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell * This class should only be used with {@link MotionEvent}s reported via touch. 2847c41e807e36999e4d0d2072e41a82bc45655ff2Erik * 29ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell * To use this class: 30ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell * <ul> 31ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell * <li>Create an instance of the {@code ScaleGestureDetector} for your 32ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell * {@link View} 33ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell * <li>In the {@link View#onTouchEvent(MotionEvent)} method ensure you call 34ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell * {@link #onTouchEvent(MotionEvent)}. The methods defined in your 35ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell * callback will be executed when the events occur. 36ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell * </ul> 37ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell */ 38ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powellpublic class ScaleGestureDetector { 39ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell /** 40ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell * The listener for receiving notifications when gestures occur. 41ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell * If you want to listen for all the different gestures then implement 42ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell * this interface. If you only want to listen for a subset it might 43ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell * be easier to extend {@link SimpleOnScaleGestureListener}. 4447c41e807e36999e4d0d2072e41a82bc45655ff2Erik * 45ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell * An application will receive events in the following order: 46ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell * <ul> 47ab905c87b7324d15715b78eaae7ef8558ad3bd10Adam Powell * <li>One {@link OnScaleGestureListener#onScaleBegin(ScaleGestureDetector)} 48ab905c87b7324d15715b78eaae7ef8558ad3bd10Adam Powell * <li>Zero or more {@link OnScaleGestureListener#onScale(ScaleGestureDetector)} 49ab905c87b7324d15715b78eaae7ef8558ad3bd10Adam Powell * <li>One {@link OnScaleGestureListener#onScaleEnd(ScaleGestureDetector)} 50ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell * </ul> 51ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell */ 52ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell public interface OnScaleGestureListener { 53ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell /** 54ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell * Responds to scaling events for a gesture in progress. 55ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell * Reported by pointer motion. 5647c41e807e36999e4d0d2072e41a82bc45655ff2Erik * 57ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell * @param detector The detector reporting the event - use this to 58ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell * retrieve extended info about event state. 59ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell * @return Whether or not the detector should consider this event 60ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell * as handled. If an event was not handled, the detector 61ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell * will continue to accumulate movement until an event is 62ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell * handled. This can be useful if an application, for example, 63ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell * only wants to update scaling factors if the change is 64ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell * greater than 0.01. 65ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell */ 66ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell public boolean onScale(ScaleGestureDetector detector); 67ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell 68ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell /** 69ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell * Responds to the beginning of a scaling gesture. Reported by 70ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell * new pointers going down. 7147c41e807e36999e4d0d2072e41a82bc45655ff2Erik * 72ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell * @param detector The detector reporting the event - use this to 73ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell * retrieve extended info about event state. 74ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell * @return Whether or not the detector should continue recognizing 75ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell * this gesture. For example, if a gesture is beginning 76ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell * with a focal point outside of a region where it makes 77ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell * sense, onScaleBegin() may return false to ignore the 78ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell * rest of the gesture. 79ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell */ 80ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell public boolean onScaleBegin(ScaleGestureDetector detector); 81ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell 82ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell /** 83ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell * Responds to the end of a scale gesture. Reported by existing 84216bccf804db9c972b317620a27de6a8adf7fbfeAdam Powell * pointers going up. 8547c41e807e36999e4d0d2072e41a82bc45655ff2Erik * 86ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell * Once a scale has ended, {@link ScaleGestureDetector#getFocusX()} 87ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell * and {@link ScaleGestureDetector#getFocusY()} will return the location 88ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell * of the pointer remaining on the screen. 8947c41e807e36999e4d0d2072e41a82bc45655ff2Erik * 90ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell * @param detector The detector reporting the event - use this to 91ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell * retrieve extended info about event state. 92ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell */ 93ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell public void onScaleEnd(ScaleGestureDetector detector); 94ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell } 9547c41e807e36999e4d0d2072e41a82bc45655ff2Erik 96ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell /** 97ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell * A convenience class to extend when you only want to listen for a subset 98ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell * of scaling-related events. This implements all methods in 99ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell * {@link OnScaleGestureListener} but does nothing. 100346c8fb036b99a33756c66dcebeb5d0f67a1df72Adam Powell * {@link OnScaleGestureListener#onScale(ScaleGestureDetector)} returns 101346c8fb036b99a33756c66dcebeb5d0f67a1df72Adam Powell * {@code false} so that a subclass can retrieve the accumulated scale 102346c8fb036b99a33756c66dcebeb5d0f67a1df72Adam Powell * factor in an overridden onScaleEnd. 103346c8fb036b99a33756c66dcebeb5d0f67a1df72Adam Powell * {@link OnScaleGestureListener#onScaleBegin(ScaleGestureDetector)} returns 10447c41e807e36999e4d0d2072e41a82bc45655ff2Erik * {@code true}. 105ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell */ 106216bccf804db9c972b317620a27de6a8adf7fbfeAdam Powell public static class SimpleOnScaleGestureListener implements OnScaleGestureListener { 107ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell 108ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell public boolean onScale(ScaleGestureDetector detector) { 109346c8fb036b99a33756c66dcebeb5d0f67a1df72Adam Powell return false; 110ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell } 111ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell 112ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell public boolean onScaleBegin(ScaleGestureDetector detector) { 113ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell return true; 114ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell } 115ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell 116ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell public void onScaleEnd(ScaleGestureDetector detector) { 117ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell // Intentionally empty 118ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell } 119ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell } 120ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell 121346c8fb036b99a33756c66dcebeb5d0f67a1df72Adam Powell /** 122346c8fb036b99a33756c66dcebeb5d0f67a1df72Adam Powell * This value is the threshold ratio between our previous combined pressure 123346c8fb036b99a33756c66dcebeb5d0f67a1df72Adam Powell * and the current combined pressure. We will only fire an onScale event if 124346c8fb036b99a33756c66dcebeb5d0f67a1df72Adam Powell * the computed ratio between the current and previous event pressures is 125346c8fb036b99a33756c66dcebeb5d0f67a1df72Adam Powell * greater than this value. When pressure decreases rapidly between events 126346c8fb036b99a33756c66dcebeb5d0f67a1df72Adam Powell * the position values can often be imprecise, as it usually indicates 127346c8fb036b99a33756c66dcebeb5d0f67a1df72Adam Powell * that the user is in the process of lifting a pointer off of the device. 128346c8fb036b99a33756c66dcebeb5d0f67a1df72Adam Powell * Its value was tuned experimentally. 129346c8fb036b99a33756c66dcebeb5d0f67a1df72Adam Powell */ 130ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell private static final float PRESSURE_THRESHOLD = 0.67f; 131ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell 132346c8fb036b99a33756c66dcebeb5d0f67a1df72Adam Powell private final Context mContext; 133346c8fb036b99a33756c66dcebeb5d0f67a1df72Adam Powell private final OnScaleGestureListener mListener; 134ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell private boolean mGestureInProgress; 135ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell 136ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell private MotionEvent mPrevEvent; 137ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell private MotionEvent mCurrEvent; 138ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell 139ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell private float mFocusX; 140ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell private float mFocusY; 141ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell private float mPrevFingerDiffX; 142ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell private float mPrevFingerDiffY; 143ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell private float mCurrFingerDiffX; 144ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell private float mCurrFingerDiffY; 145ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell private float mCurrLen; 146ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell private float mPrevLen; 147ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell private float mScaleFactor; 148ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell private float mCurrPressure; 149ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell private float mPrevPressure; 150ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell private long mTimeDelta; 15147c41e807e36999e4d0d2072e41a82bc45655ff2Erik 152346c8fb036b99a33756c66dcebeb5d0f67a1df72Adam Powell private final float mEdgeSlop; 153380b525220955ce4e4df8943b89082c7443ebfddAdam Powell private float mRightSlopEdge; 154380b525220955ce4e4df8943b89082c7443ebfddAdam Powell private float mBottomSlopEdge; 155380b525220955ce4e4df8943b89082c7443ebfddAdam Powell private boolean mSloppyGesture; 156ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell 157e33cef8037cb87386e17bcf8701a47452d262fa6Adam Powell // Pointer IDs currently responsible for the two fingers controlling the gesture 158e33cef8037cb87386e17bcf8701a47452d262fa6Adam Powell private int mActiveId0; 159e33cef8037cb87386e17bcf8701a47452d262fa6Adam Powell private int mActiveId1; 160e33cef8037cb87386e17bcf8701a47452d262fa6Adam Powell private boolean mActive0MostRecent; 161e33cef8037cb87386e17bcf8701a47452d262fa6Adam Powell 162ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell public ScaleGestureDetector(Context context, OnScaleGestureListener listener) { 163380b525220955ce4e4df8943b89082c7443ebfddAdam Powell ViewConfiguration config = ViewConfiguration.get(context); 164ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell mContext = context; 165ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell mListener = listener; 166380b525220955ce4e4df8943b89082c7443ebfddAdam Powell mEdgeSlop = config.getScaledEdgeSlop(); 167ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell } 168ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell 169ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell public boolean onTouchEvent(MotionEvent event) { 170e33cef8037cb87386e17bcf8701a47452d262fa6Adam Powell final int action = event.getActionMasked(); 171ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell boolean handled = true; 172ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell 173ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell if (!mGestureInProgress) { 174e33cef8037cb87386e17bcf8701a47452d262fa6Adam Powell switch (action) { 175e33cef8037cb87386e17bcf8701a47452d262fa6Adam Powell case MotionEvent.ACTION_DOWN: { 176e33cef8037cb87386e17bcf8701a47452d262fa6Adam Powell mActiveId0 = event.getPointerId(0); 177e33cef8037cb87386e17bcf8701a47452d262fa6Adam Powell mActive0MostRecent = true; 178e33cef8037cb87386e17bcf8701a47452d262fa6Adam Powell } 179e33cef8037cb87386e17bcf8701a47452d262fa6Adam Powell break; 180e33cef8037cb87386e17bcf8701a47452d262fa6Adam Powell 181e33cef8037cb87386e17bcf8701a47452d262fa6Adam Powell case MotionEvent.ACTION_UP: 182e33cef8037cb87386e17bcf8701a47452d262fa6Adam Powell reset(); 183e33cef8037cb87386e17bcf8701a47452d262fa6Adam Powell break; 184e33cef8037cb87386e17bcf8701a47452d262fa6Adam Powell 185f5bcc6addd9c1f8f6bb8b8626540d003b3f964faAdam Powell case MotionEvent.ACTION_POINTER_DOWN: { 186ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell // We have a new multi-finger gesture 187380b525220955ce4e4df8943b89082c7443ebfddAdam Powell 188d5ada83c719d366d3063572ca6ce5ab8918fd39bGrace Kloba // as orientation can change, query the metrics in touch down 189d5ada83c719d366d3063572ca6ce5ab8918fd39bGrace Kloba DisplayMetrics metrics = mContext.getResources().getDisplayMetrics(); 190d5ada83c719d366d3063572ca6ce5ab8918fd39bGrace Kloba mRightSlopEdge = metrics.widthPixels - mEdgeSlop; 191d5ada83c719d366d3063572ca6ce5ab8918fd39bGrace Kloba mBottomSlopEdge = metrics.heightPixels - mEdgeSlop; 192d5ada83c719d366d3063572ca6ce5ab8918fd39bGrace Kloba 193e33cef8037cb87386e17bcf8701a47452d262fa6Adam Powell if (mPrevEvent != null) mPrevEvent.recycle(); 194ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell mPrevEvent = MotionEvent.obtain(event); 195ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell mTimeDelta = 0; 196380b525220955ce4e4df8943b89082c7443ebfddAdam Powell 197e33cef8037cb87386e17bcf8701a47452d262fa6Adam Powell int index1 = event.getActionIndex(); 198e33cef8037cb87386e17bcf8701a47452d262fa6Adam Powell int index0 = event.findPointerIndex(mActiveId0); 199e33cef8037cb87386e17bcf8701a47452d262fa6Adam Powell mActiveId1 = event.getPointerId(index1); 200e33cef8037cb87386e17bcf8701a47452d262fa6Adam Powell mActive0MostRecent = false; 201e33cef8037cb87386e17bcf8701a47452d262fa6Adam Powell 202ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell setContext(event); 203380b525220955ce4e4df8943b89082c7443ebfddAdam Powell 204380b525220955ce4e4df8943b89082c7443ebfddAdam Powell // Check if we have a sloppy gesture. If so, delay 205380b525220955ce4e4df8943b89082c7443ebfddAdam Powell // the beginning of the gesture until we're sure that's 206380b525220955ce4e4df8943b89082c7443ebfddAdam Powell // what the user wanted. Sloppy gestures can happen if the 207380b525220955ce4e4df8943b89082c7443ebfddAdam Powell // edge of the user's hand is touching the screen, for example. 208380b525220955ce4e4df8943b89082c7443ebfddAdam Powell final float edgeSlop = mEdgeSlop; 209380b525220955ce4e4df8943b89082c7443ebfddAdam Powell final float rightSlop = mRightSlopEdge; 210380b525220955ce4e4df8943b89082c7443ebfddAdam Powell final float bottomSlop = mBottomSlopEdge; 211e33cef8037cb87386e17bcf8701a47452d262fa6Adam Powell float x0 = getRawX(event, index0); 212e33cef8037cb87386e17bcf8701a47452d262fa6Adam Powell float y0 = getRawY(event, index0); 213e33cef8037cb87386e17bcf8701a47452d262fa6Adam Powell float x1 = getRawX(event, index1); 214e33cef8037cb87386e17bcf8701a47452d262fa6Adam Powell float y1 = getRawY(event, index1); 215380b525220955ce4e4df8943b89082c7443ebfddAdam Powell 2168f9fbb0c45265a6f5da51cb3bc35362d8e5bc900Grace Kloba boolean p0sloppy = x0 < edgeSlop || y0 < edgeSlop 2178f9fbb0c45265a6f5da51cb3bc35362d8e5bc900Grace Kloba || x0 > rightSlop || y0 > bottomSlop; 2188f9fbb0c45265a6f5da51cb3bc35362d8e5bc900Grace Kloba boolean p1sloppy = x1 < edgeSlop || y1 < edgeSlop 2198f9fbb0c45265a6f5da51cb3bc35362d8e5bc900Grace Kloba || x1 > rightSlop || y1 > bottomSlop; 220380b525220955ce4e4df8943b89082c7443ebfddAdam Powell 221f5bcc6addd9c1f8f6bb8b8626540d003b3f964faAdam Powell if (p0sloppy && p1sloppy) { 2228f9fbb0c45265a6f5da51cb3bc35362d8e5bc900Grace Kloba mFocusX = -1; 2238f9fbb0c45265a6f5da51cb3bc35362d8e5bc900Grace Kloba mFocusY = -1; 2248f9fbb0c45265a6f5da51cb3bc35362d8e5bc900Grace Kloba mSloppyGesture = true; 2258f9fbb0c45265a6f5da51cb3bc35362d8e5bc900Grace Kloba } else if (p0sloppy) { 226e33cef8037cb87386e17bcf8701a47452d262fa6Adam Powell mFocusX = event.getX(index1); 227e33cef8037cb87386e17bcf8701a47452d262fa6Adam Powell mFocusY = event.getY(index1); 228380b525220955ce4e4df8943b89082c7443ebfddAdam Powell mSloppyGesture = true; 229380b525220955ce4e4df8943b89082c7443ebfddAdam Powell } else if (p1sloppy) { 230e33cef8037cb87386e17bcf8701a47452d262fa6Adam Powell mFocusX = event.getX(index0); 231e33cef8037cb87386e17bcf8701a47452d262fa6Adam Powell mFocusY = event.getY(index0); 232380b525220955ce4e4df8943b89082c7443ebfddAdam Powell mSloppyGesture = true; 233380b525220955ce4e4df8943b89082c7443ebfddAdam Powell } else { 234e33cef8037cb87386e17bcf8701a47452d262fa6Adam Powell mSloppyGesture = false; 235380b525220955ce4e4df8943b89082c7443ebfddAdam Powell mGestureInProgress = mListener.onScaleBegin(this); 236380b525220955ce4e4df8943b89082c7443ebfddAdam Powell } 237f5bcc6addd9c1f8f6bb8b8626540d003b3f964faAdam Powell } 238f5bcc6addd9c1f8f6bb8b8626540d003b3f964faAdam Powell break; 23947c41e807e36999e4d0d2072e41a82bc45655ff2Erik 240f5bcc6addd9c1f8f6bb8b8626540d003b3f964faAdam Powell case MotionEvent.ACTION_MOVE: 241f5bcc6addd9c1f8f6bb8b8626540d003b3f964faAdam Powell if (mSloppyGesture) { 242f5bcc6addd9c1f8f6bb8b8626540d003b3f964faAdam Powell // Initiate sloppy gestures if we've moved outside of the slop area. 243f5bcc6addd9c1f8f6bb8b8626540d003b3f964faAdam Powell final float edgeSlop = mEdgeSlop; 244f5bcc6addd9c1f8f6bb8b8626540d003b3f964faAdam Powell final float rightSlop = mRightSlopEdge; 245f5bcc6addd9c1f8f6bb8b8626540d003b3f964faAdam Powell final float bottomSlop = mBottomSlopEdge; 246e33cef8037cb87386e17bcf8701a47452d262fa6Adam Powell int index0 = event.findPointerIndex(mActiveId0); 247e33cef8037cb87386e17bcf8701a47452d262fa6Adam Powell int index1 = event.findPointerIndex(mActiveId1); 248e33cef8037cb87386e17bcf8701a47452d262fa6Adam Powell float x0 = getRawX(event, index0); 249e33cef8037cb87386e17bcf8701a47452d262fa6Adam Powell float y0 = getRawY(event, index0); 250e33cef8037cb87386e17bcf8701a47452d262fa6Adam Powell float x1 = getRawX(event, index1); 251e33cef8037cb87386e17bcf8701a47452d262fa6Adam Powell float y1 = getRawY(event, index1); 252380b525220955ce4e4df8943b89082c7443ebfddAdam Powell 253f5bcc6addd9c1f8f6bb8b8626540d003b3f964faAdam Powell boolean p0sloppy = x0 < edgeSlop || y0 < edgeSlop 254e33cef8037cb87386e17bcf8701a47452d262fa6Adam Powell || x0 > rightSlop || y0 > bottomSlop; 255f5bcc6addd9c1f8f6bb8b8626540d003b3f964faAdam Powell boolean p1sloppy = x1 < edgeSlop || y1 < edgeSlop 256e33cef8037cb87386e17bcf8701a47452d262fa6Adam Powell || x1 > rightSlop || y1 > bottomSlop; 257e33cef8037cb87386e17bcf8701a47452d262fa6Adam Powell 258e33cef8037cb87386e17bcf8701a47452d262fa6Adam Powell if (p0sloppy) { 259e33cef8037cb87386e17bcf8701a47452d262fa6Adam Powell // Do we have a different pointer that isn't sloppy? 260e33cef8037cb87386e17bcf8701a47452d262fa6Adam Powell int index = findNewActiveIndex(event, mActiveId1, index0); 261e33cef8037cb87386e17bcf8701a47452d262fa6Adam Powell if (index >= 0) { 262e33cef8037cb87386e17bcf8701a47452d262fa6Adam Powell index0 = index; 263e33cef8037cb87386e17bcf8701a47452d262fa6Adam Powell mActiveId0 = event.getPointerId(index); 264e33cef8037cb87386e17bcf8701a47452d262fa6Adam Powell x0 = getRawX(event, index); 265e33cef8037cb87386e17bcf8701a47452d262fa6Adam Powell y0 = getRawY(event, index); 266e33cef8037cb87386e17bcf8701a47452d262fa6Adam Powell p0sloppy = false; 267e33cef8037cb87386e17bcf8701a47452d262fa6Adam Powell } 268e33cef8037cb87386e17bcf8701a47452d262fa6Adam Powell } 269e33cef8037cb87386e17bcf8701a47452d262fa6Adam Powell 270e33cef8037cb87386e17bcf8701a47452d262fa6Adam Powell if (p1sloppy) { 271e33cef8037cb87386e17bcf8701a47452d262fa6Adam Powell // Do we have a different pointer that isn't sloppy? 272e33cef8037cb87386e17bcf8701a47452d262fa6Adam Powell int index = findNewActiveIndex(event, mActiveId0, index1); 273e33cef8037cb87386e17bcf8701a47452d262fa6Adam Powell if (index >= 0) { 274e33cef8037cb87386e17bcf8701a47452d262fa6Adam Powell index1 = index; 275e33cef8037cb87386e17bcf8701a47452d262fa6Adam Powell mActiveId1 = event.getPointerId(index); 276e33cef8037cb87386e17bcf8701a47452d262fa6Adam Powell x1 = getRawX(event, index); 277e33cef8037cb87386e17bcf8701a47452d262fa6Adam Powell y1 = getRawY(event, index); 278e33cef8037cb87386e17bcf8701a47452d262fa6Adam Powell p1sloppy = false; 279e33cef8037cb87386e17bcf8701a47452d262fa6Adam Powell } 280e33cef8037cb87386e17bcf8701a47452d262fa6Adam Powell } 281380b525220955ce4e4df8943b89082c7443ebfddAdam Powell 282f5bcc6addd9c1f8f6bb8b8626540d003b3f964faAdam Powell if(p0sloppy && p1sloppy) { 283f5bcc6addd9c1f8f6bb8b8626540d003b3f964faAdam Powell mFocusX = -1; 284f5bcc6addd9c1f8f6bb8b8626540d003b3f964faAdam Powell mFocusY = -1; 285f5bcc6addd9c1f8f6bb8b8626540d003b3f964faAdam Powell } else if (p0sloppy) { 286e33cef8037cb87386e17bcf8701a47452d262fa6Adam Powell mFocusX = event.getX(index1); 287e33cef8037cb87386e17bcf8701a47452d262fa6Adam Powell mFocusY = event.getY(index1); 288f5bcc6addd9c1f8f6bb8b8626540d003b3f964faAdam Powell } else if (p1sloppy) { 289e33cef8037cb87386e17bcf8701a47452d262fa6Adam Powell mFocusX = event.getX(index0); 290e33cef8037cb87386e17bcf8701a47452d262fa6Adam Powell mFocusY = event.getY(index0); 291f5bcc6addd9c1f8f6bb8b8626540d003b3f964faAdam Powell } else { 292f5bcc6addd9c1f8f6bb8b8626540d003b3f964faAdam Powell mSloppyGesture = false; 293f5bcc6addd9c1f8f6bb8b8626540d003b3f964faAdam Powell mGestureInProgress = mListener.onScaleBegin(this); 294f5bcc6addd9c1f8f6bb8b8626540d003b3f964faAdam Powell } 295f5bcc6addd9c1f8f6bb8b8626540d003b3f964faAdam Powell } 296f5bcc6addd9c1f8f6bb8b8626540d003b3f964faAdam Powell break; 29747c41e807e36999e4d0d2072e41a82bc45655ff2Erik 298f5bcc6addd9c1f8f6bb8b8626540d003b3f964faAdam Powell case MotionEvent.ACTION_POINTER_UP: 299f5bcc6addd9c1f8f6bb8b8626540d003b3f964faAdam Powell if (mSloppyGesture) { 300e33cef8037cb87386e17bcf8701a47452d262fa6Adam Powell final int pointerCount = event.getPointerCount(); 301e33cef8037cb87386e17bcf8701a47452d262fa6Adam Powell final int actionIndex = event.getActionIndex(); 302e33cef8037cb87386e17bcf8701a47452d262fa6Adam Powell final int actionId = event.getPointerId(actionIndex); 303e33cef8037cb87386e17bcf8701a47452d262fa6Adam Powell 304e33cef8037cb87386e17bcf8701a47452d262fa6Adam Powell if (pointerCount > 2) { 305e33cef8037cb87386e17bcf8701a47452d262fa6Adam Powell if (actionId == mActiveId0) { 306e33cef8037cb87386e17bcf8701a47452d262fa6Adam Powell final int newIndex = findNewActiveIndex(event, mActiveId1, actionIndex); 307e33cef8037cb87386e17bcf8701a47452d262fa6Adam Powell if (newIndex >= 0) mActiveId0 = event.getPointerId(newIndex); 308e33cef8037cb87386e17bcf8701a47452d262fa6Adam Powell } else if (actionId == mActiveId1) { 309e33cef8037cb87386e17bcf8701a47452d262fa6Adam Powell final int newIndex = findNewActiveIndex(event, mActiveId0, actionIndex); 310e33cef8037cb87386e17bcf8701a47452d262fa6Adam Powell if (newIndex >= 0) mActiveId1 = event.getPointerId(newIndex); 311e33cef8037cb87386e17bcf8701a47452d262fa6Adam Powell } 312e33cef8037cb87386e17bcf8701a47452d262fa6Adam Powell } else { 313e33cef8037cb87386e17bcf8701a47452d262fa6Adam Powell // Set focus point to the remaining finger 314e33cef8037cb87386e17bcf8701a47452d262fa6Adam Powell final int index = event.findPointerIndex(actionId == mActiveId0 ? 315e33cef8037cb87386e17bcf8701a47452d262fa6Adam Powell mActiveId1 : mActiveId0); 316e33cef8037cb87386e17bcf8701a47452d262fa6Adam Powell mActiveId0 = event.getPointerId(index); 317e33cef8037cb87386e17bcf8701a47452d262fa6Adam Powell mActive0MostRecent = true; 318e33cef8037cb87386e17bcf8701a47452d262fa6Adam Powell mActiveId1 = -1; 319e33cef8037cb87386e17bcf8701a47452d262fa6Adam Powell mFocusX = event.getX(index); 320e33cef8037cb87386e17bcf8701a47452d262fa6Adam Powell mFocusY = event.getY(index); 321e33cef8037cb87386e17bcf8701a47452d262fa6Adam Powell } 322380b525220955ce4e4df8943b89082c7443ebfddAdam Powell } 323f5bcc6addd9c1f8f6bb8b8626540d003b3f964faAdam Powell break; 324ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell } 325ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell } else { 326ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell // Transform gesture in progress - attempt to handle it 327e33cef8037cb87386e17bcf8701a47452d262fa6Adam Powell switch (action) { 328e33cef8037cb87386e17bcf8701a47452d262fa6Adam Powell case MotionEvent.ACTION_POINTER_DOWN: { 329e33cef8037cb87386e17bcf8701a47452d262fa6Adam Powell // End the old gesture and begin a new one with the most recent two fingers. 330e33cef8037cb87386e17bcf8701a47452d262fa6Adam Powell mListener.onScaleEnd(this); 331e33cef8037cb87386e17bcf8701a47452d262fa6Adam Powell final int oldActive0 = mActiveId0; 332e33cef8037cb87386e17bcf8701a47452d262fa6Adam Powell final int oldActive1 = mActiveId1; 333e33cef8037cb87386e17bcf8701a47452d262fa6Adam Powell reset(); 334e33cef8037cb87386e17bcf8701a47452d262fa6Adam Powell 335e33cef8037cb87386e17bcf8701a47452d262fa6Adam Powell mPrevEvent = MotionEvent.obtain(event); 336e33cef8037cb87386e17bcf8701a47452d262fa6Adam Powell mActiveId0 = mActive0MostRecent ? oldActive0 : oldActive1; 337e33cef8037cb87386e17bcf8701a47452d262fa6Adam Powell mActiveId1 = event.getPointerId(event.getActionIndex()); 338e33cef8037cb87386e17bcf8701a47452d262fa6Adam Powell mActive0MostRecent = false; 339e33cef8037cb87386e17bcf8701a47452d262fa6Adam Powell 340ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell setContext(event); 341380b525220955ce4e4df8943b89082c7443ebfddAdam Powell 342e33cef8037cb87386e17bcf8701a47452d262fa6Adam Powell mGestureInProgress = mListener.onScaleBegin(this); 343e33cef8037cb87386e17bcf8701a47452d262fa6Adam Powell } 344e33cef8037cb87386e17bcf8701a47452d262fa6Adam Powell break; 345380b525220955ce4e4df8943b89082c7443ebfddAdam Powell 346e33cef8037cb87386e17bcf8701a47452d262fa6Adam Powell case MotionEvent.ACTION_POINTER_UP: { 347e33cef8037cb87386e17bcf8701a47452d262fa6Adam Powell final int pointerCount = event.getPointerCount(); 348e33cef8037cb87386e17bcf8701a47452d262fa6Adam Powell final int actionIndex = event.getActionIndex(); 349e33cef8037cb87386e17bcf8701a47452d262fa6Adam Powell final int actionId = event.getPointerId(actionIndex); 350e33cef8037cb87386e17bcf8701a47452d262fa6Adam Powell 351e33cef8037cb87386e17bcf8701a47452d262fa6Adam Powell boolean gestureEnded = false; 352e33cef8037cb87386e17bcf8701a47452d262fa6Adam Powell if (pointerCount > 2) { 353e33cef8037cb87386e17bcf8701a47452d262fa6Adam Powell if (actionId == mActiveId0) { 354e33cef8037cb87386e17bcf8701a47452d262fa6Adam Powell final int newIndex = findNewActiveIndex(event, mActiveId1, actionIndex); 355e33cef8037cb87386e17bcf8701a47452d262fa6Adam Powell if (newIndex >= 0) { 356e33cef8037cb87386e17bcf8701a47452d262fa6Adam Powell mActiveId0 = event.getPointerId(newIndex); 357e33cef8037cb87386e17bcf8701a47452d262fa6Adam Powell } else { 358e33cef8037cb87386e17bcf8701a47452d262fa6Adam Powell gestureEnded = true; 359e33cef8037cb87386e17bcf8701a47452d262fa6Adam Powell } 360e33cef8037cb87386e17bcf8701a47452d262fa6Adam Powell } else if (actionId == mActiveId1) { 361e33cef8037cb87386e17bcf8701a47452d262fa6Adam Powell final int newIndex = findNewActiveIndex(event, mActiveId0, actionIndex); 362e33cef8037cb87386e17bcf8701a47452d262fa6Adam Powell if (newIndex >= 0) { 363e33cef8037cb87386e17bcf8701a47452d262fa6Adam Powell mActiveId1 = event.getPointerId(newIndex); 364e33cef8037cb87386e17bcf8701a47452d262fa6Adam Powell } else { 365e33cef8037cb87386e17bcf8701a47452d262fa6Adam Powell gestureEnded = true; 366e33cef8037cb87386e17bcf8701a47452d262fa6Adam Powell } 367e33cef8037cb87386e17bcf8701a47452d262fa6Adam Powell } 368e33cef8037cb87386e17bcf8701a47452d262fa6Adam Powell mPrevEvent.recycle(); 369e33cef8037cb87386e17bcf8701a47452d262fa6Adam Powell mPrevEvent = MotionEvent.obtain(event); 370e33cef8037cb87386e17bcf8701a47452d262fa6Adam Powell setContext(event); 371e33cef8037cb87386e17bcf8701a47452d262fa6Adam Powell } else { 372e33cef8037cb87386e17bcf8701a47452d262fa6Adam Powell gestureEnded = true; 373380b525220955ce4e4df8943b89082c7443ebfddAdam Powell } 374ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell 375e33cef8037cb87386e17bcf8701a47452d262fa6Adam Powell if (gestureEnded) { 376e33cef8037cb87386e17bcf8701a47452d262fa6Adam Powell // Gesture ended 377e33cef8037cb87386e17bcf8701a47452d262fa6Adam Powell setContext(event); 378e33cef8037cb87386e17bcf8701a47452d262fa6Adam Powell 379e33cef8037cb87386e17bcf8701a47452d262fa6Adam Powell // Set focus point to the remaining finger 380e33cef8037cb87386e17bcf8701a47452d262fa6Adam Powell final int activeId = actionId == mActiveId0 ? mActiveId1 : mActiveId0; 381e33cef8037cb87386e17bcf8701a47452d262fa6Adam Powell final int index = event.findPointerIndex(activeId); 382e33cef8037cb87386e17bcf8701a47452d262fa6Adam Powell mFocusX = event.getX(index); 383e33cef8037cb87386e17bcf8701a47452d262fa6Adam Powell mFocusY = event.getY(index); 384ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell 385380b525220955ce4e4df8943b89082c7443ebfddAdam Powell mListener.onScaleEnd(this); 386e33cef8037cb87386e17bcf8701a47452d262fa6Adam Powell reset(); 387e33cef8037cb87386e17bcf8701a47452d262fa6Adam Powell mActiveId0 = activeId; 388e33cef8037cb87386e17bcf8701a47452d262fa6Adam Powell mActive0MostRecent = true; 389380b525220955ce4e4df8943b89082c7443ebfddAdam Powell } 390e33cef8037cb87386e17bcf8701a47452d262fa6Adam Powell } 391e33cef8037cb87386e17bcf8701a47452d262fa6Adam Powell break; 392ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell 393e33cef8037cb87386e17bcf8701a47452d262fa6Adam Powell case MotionEvent.ACTION_CANCEL: 394e33cef8037cb87386e17bcf8701a47452d262fa6Adam Powell mListener.onScaleEnd(this); 395ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell reset(); 396ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell break; 397ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell 398e33cef8037cb87386e17bcf8701a47452d262fa6Adam Powell case MotionEvent.ACTION_UP: 399e33cef8037cb87386e17bcf8701a47452d262fa6Adam Powell reset(); 400e33cef8037cb87386e17bcf8701a47452d262fa6Adam Powell break; 401e33cef8037cb87386e17bcf8701a47452d262fa6Adam Powell 402e33cef8037cb87386e17bcf8701a47452d262fa6Adam Powell case MotionEvent.ACTION_MOVE: { 403ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell setContext(event); 404ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell 405ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell // Only accept the event if our relative pressure is within 406ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell // a certain limit - this can help filter shaky data as a 407ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell // finger is lifted. 408ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell if (mCurrPressure / mPrevPressure > PRESSURE_THRESHOLD) { 409ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell final boolean updatePrevious = mListener.onScale(this); 410ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell 411ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell if (updatePrevious) { 412ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell mPrevEvent.recycle(); 413ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell mPrevEvent = MotionEvent.obtain(event); 414ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell } 415ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell } 416e33cef8037cb87386e17bcf8701a47452d262fa6Adam Powell } 417e33cef8037cb87386e17bcf8701a47452d262fa6Adam Powell break; 418ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell } 419ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell } 420ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell return handled; 421ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell } 42247c41e807e36999e4d0d2072e41a82bc45655ff2Erik 423e33cef8037cb87386e17bcf8701a47452d262fa6Adam Powell private int findNewActiveIndex(MotionEvent ev, int otherActiveId, int oldIndex) { 424e33cef8037cb87386e17bcf8701a47452d262fa6Adam Powell final int pointerCount = ev.getPointerCount(); 425e33cef8037cb87386e17bcf8701a47452d262fa6Adam Powell 426e33cef8037cb87386e17bcf8701a47452d262fa6Adam Powell // It's ok if this isn't found and returns -1, it simply won't match. 427e33cef8037cb87386e17bcf8701a47452d262fa6Adam Powell final int otherActiveIndex = ev.findPointerIndex(otherActiveId); 428e33cef8037cb87386e17bcf8701a47452d262fa6Adam Powell int newActiveIndex = -1; 429e33cef8037cb87386e17bcf8701a47452d262fa6Adam Powell 430e33cef8037cb87386e17bcf8701a47452d262fa6Adam Powell // Pick a new id and update tracking state. Only pick pointers not on the slop edges. 431e33cef8037cb87386e17bcf8701a47452d262fa6Adam Powell for (int i = 0; i < pointerCount; i++) { 432e33cef8037cb87386e17bcf8701a47452d262fa6Adam Powell if (i != oldIndex && i != otherActiveIndex) { 433e33cef8037cb87386e17bcf8701a47452d262fa6Adam Powell final float edgeSlop = mEdgeSlop; 434e33cef8037cb87386e17bcf8701a47452d262fa6Adam Powell final float rightSlop = mRightSlopEdge; 435e33cef8037cb87386e17bcf8701a47452d262fa6Adam Powell final float bottomSlop = mBottomSlopEdge; 436e33cef8037cb87386e17bcf8701a47452d262fa6Adam Powell float x = getRawX(ev, i); 437e33cef8037cb87386e17bcf8701a47452d262fa6Adam Powell float y = getRawY(ev, i); 438e33cef8037cb87386e17bcf8701a47452d262fa6Adam Powell if (x >= edgeSlop && y >= edgeSlop && x <= rightSlop && y <= bottomSlop) { 439e33cef8037cb87386e17bcf8701a47452d262fa6Adam Powell newActiveIndex = i; 440e33cef8037cb87386e17bcf8701a47452d262fa6Adam Powell break; 441e33cef8037cb87386e17bcf8701a47452d262fa6Adam Powell } 442e33cef8037cb87386e17bcf8701a47452d262fa6Adam Powell } 443e33cef8037cb87386e17bcf8701a47452d262fa6Adam Powell } 444e33cef8037cb87386e17bcf8701a47452d262fa6Adam Powell 445e33cef8037cb87386e17bcf8701a47452d262fa6Adam Powell return newActiveIndex; 446e33cef8037cb87386e17bcf8701a47452d262fa6Adam Powell } 447e33cef8037cb87386e17bcf8701a47452d262fa6Adam Powell 448380b525220955ce4e4df8943b89082c7443ebfddAdam Powell /** 44947c41e807e36999e4d0d2072e41a82bc45655ff2Erik * MotionEvent has no getRawX(int) method; simulate it pending future API approval. 450380b525220955ce4e4df8943b89082c7443ebfddAdam Powell */ 451380b525220955ce4e4df8943b89082c7443ebfddAdam Powell private static float getRawX(MotionEvent event, int pointerIndex) { 452e33cef8037cb87386e17bcf8701a47452d262fa6Adam Powell if (pointerIndex == 0) return event.getRawX(); 4539bccdb7d5c93e350337e707bc6edf3cd017b8f96Adam Powell float offset = event.getRawX() - event.getX(); 454380b525220955ce4e4df8943b89082c7443ebfddAdam Powell return event.getX(pointerIndex) + offset; 455380b525220955ce4e4df8943b89082c7443ebfddAdam Powell } 45647c41e807e36999e4d0d2072e41a82bc45655ff2Erik 457380b525220955ce4e4df8943b89082c7443ebfddAdam Powell /** 45847c41e807e36999e4d0d2072e41a82bc45655ff2Erik * MotionEvent has no getRawY(int) method; simulate it pending future API approval. 459380b525220955ce4e4df8943b89082c7443ebfddAdam Powell */ 460380b525220955ce4e4df8943b89082c7443ebfddAdam Powell private static float getRawY(MotionEvent event, int pointerIndex) { 461e33cef8037cb87386e17bcf8701a47452d262fa6Adam Powell if (pointerIndex == 0) return event.getRawY(); 4629bccdb7d5c93e350337e707bc6edf3cd017b8f96Adam Powell float offset = event.getRawY() - event.getY(); 463380b525220955ce4e4df8943b89082c7443ebfddAdam Powell return event.getY(pointerIndex) + offset; 464380b525220955ce4e4df8943b89082c7443ebfddAdam Powell } 465ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell 466ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell private void setContext(MotionEvent curr) { 467ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell if (mCurrEvent != null) { 468ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell mCurrEvent.recycle(); 469ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell } 470ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell mCurrEvent = MotionEvent.obtain(curr); 471ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell 472ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell mCurrLen = -1; 473ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell mPrevLen = -1; 474ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell mScaleFactor = -1; 475ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell 476ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell final MotionEvent prev = mPrevEvent; 477ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell 478e33cef8037cb87386e17bcf8701a47452d262fa6Adam Powell final int prevIndex0 = prev.findPointerIndex(mActiveId0); 479e33cef8037cb87386e17bcf8701a47452d262fa6Adam Powell final int prevIndex1 = prev.findPointerIndex(mActiveId1); 480e33cef8037cb87386e17bcf8701a47452d262fa6Adam Powell final int currIndex0 = curr.findPointerIndex(mActiveId0); 481e33cef8037cb87386e17bcf8701a47452d262fa6Adam Powell final int currIndex1 = curr.findPointerIndex(mActiveId1); 482e33cef8037cb87386e17bcf8701a47452d262fa6Adam Powell 483e33cef8037cb87386e17bcf8701a47452d262fa6Adam Powell final float px0 = prev.getX(prevIndex0); 484e33cef8037cb87386e17bcf8701a47452d262fa6Adam Powell final float py0 = prev.getY(prevIndex0); 485e33cef8037cb87386e17bcf8701a47452d262fa6Adam Powell final float px1 = prev.getX(prevIndex1); 486e33cef8037cb87386e17bcf8701a47452d262fa6Adam Powell final float py1 = prev.getY(prevIndex1); 487e33cef8037cb87386e17bcf8701a47452d262fa6Adam Powell final float cx0 = curr.getX(currIndex0); 488e33cef8037cb87386e17bcf8701a47452d262fa6Adam Powell final float cy0 = curr.getY(currIndex0); 489e33cef8037cb87386e17bcf8701a47452d262fa6Adam Powell final float cx1 = curr.getX(currIndex1); 490e33cef8037cb87386e17bcf8701a47452d262fa6Adam Powell final float cy1 = curr.getY(currIndex1); 491ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell 492ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell final float pvx = px1 - px0; 493ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell final float pvy = py1 - py0; 494ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell final float cvx = cx1 - cx0; 495ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell final float cvy = cy1 - cy0; 496ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell mPrevFingerDiffX = pvx; 497ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell mPrevFingerDiffY = pvy; 498ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell mCurrFingerDiffX = cvx; 499ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell mCurrFingerDiffY = cvy; 500ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell 501ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell mFocusX = cx0 + cvx * 0.5f; 502ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell mFocusY = cy0 + cvy * 0.5f; 503ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell mTimeDelta = curr.getEventTime() - prev.getEventTime(); 504e33cef8037cb87386e17bcf8701a47452d262fa6Adam Powell mCurrPressure = curr.getPressure(currIndex0) + curr.getPressure(currIndex1); 505e33cef8037cb87386e17bcf8701a47452d262fa6Adam Powell mPrevPressure = prev.getPressure(prevIndex0) + prev.getPressure(prevIndex1); 506ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell } 507ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell 508ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell private void reset() { 509ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell if (mPrevEvent != null) { 510ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell mPrevEvent.recycle(); 511ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell mPrevEvent = null; 512ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell } 513ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell if (mCurrEvent != null) { 514ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell mCurrEvent.recycle(); 515ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell mCurrEvent = null; 516ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell } 517380b525220955ce4e4df8943b89082c7443ebfddAdam Powell mSloppyGesture = false; 518380b525220955ce4e4df8943b89082c7443ebfddAdam Powell mGestureInProgress = false; 519e33cef8037cb87386e17bcf8701a47452d262fa6Adam Powell mActiveId0 = -1; 520e33cef8037cb87386e17bcf8701a47452d262fa6Adam Powell mActiveId1 = -1; 521ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell } 522ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell 523ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell /** 524ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell * Returns {@code true} if a two-finger scale gesture is in progress. 525ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell * @return {@code true} if a scale gesture is in progress, {@code false} otherwise. 526ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell */ 527ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell public boolean isInProgress() { 528ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell return mGestureInProgress; 529ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell } 530ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell 531ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell /** 532ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell * Get the X coordinate of the current gesture's focal point. 533ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell * If a gesture is in progress, the focal point is directly between 534ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell * the two pointers forming the gesture. 535ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell * If a gesture is ending, the focal point is the location of the 536ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell * remaining pointer on the screen. 537ab905c87b7324d15715b78eaae7ef8558ad3bd10Adam Powell * If {@link #isInProgress()} would return false, the result of this 538ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell * function is undefined. 53947c41e807e36999e4d0d2072e41a82bc45655ff2Erik * 540ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell * @return X coordinate of the focal point in pixels. 541ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell */ 542ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell public float getFocusX() { 543ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell return mFocusX; 544ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell } 545ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell 546ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell /** 547ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell * Get the Y coordinate of the current gesture's focal point. 548ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell * If a gesture is in progress, the focal point is directly between 549ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell * the two pointers forming the gesture. 550ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell * If a gesture is ending, the focal point is the location of the 551ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell * remaining pointer on the screen. 552ab905c87b7324d15715b78eaae7ef8558ad3bd10Adam Powell * If {@link #isInProgress()} would return false, the result of this 553ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell * function is undefined. 55447c41e807e36999e4d0d2072e41a82bc45655ff2Erik * 555ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell * @return Y coordinate of the focal point in pixels. 556ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell */ 557ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell public float getFocusY() { 558ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell return mFocusY; 559ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell } 560ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell 561ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell /** 562ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell * Return the current distance between the two pointers forming the 563ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell * gesture in progress. 56447c41e807e36999e4d0d2072e41a82bc45655ff2Erik * 565ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell * @return Distance between pointers in pixels. 566ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell */ 567ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell public float getCurrentSpan() { 568ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell if (mCurrLen == -1) { 569ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell final float cvx = mCurrFingerDiffX; 570ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell final float cvy = mCurrFingerDiffY; 571346c8fb036b99a33756c66dcebeb5d0f67a1df72Adam Powell mCurrLen = FloatMath.sqrt(cvx*cvx + cvy*cvy); 572ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell } 573ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell return mCurrLen; 574ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell } 575ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell 576ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell /** 57747c41e807e36999e4d0d2072e41a82bc45655ff2Erik * Return the current x distance between the two pointers forming the 57847c41e807e36999e4d0d2072e41a82bc45655ff2Erik * gesture in progress. 57947c41e807e36999e4d0d2072e41a82bc45655ff2Erik * 58047c41e807e36999e4d0d2072e41a82bc45655ff2Erik * @return Distance between pointers in pixels. 58147c41e807e36999e4d0d2072e41a82bc45655ff2Erik */ 58247c41e807e36999e4d0d2072e41a82bc45655ff2Erik public float getCurrentSpanX() { 58347c41e807e36999e4d0d2072e41a82bc45655ff2Erik return mCurrFingerDiffX; 58447c41e807e36999e4d0d2072e41a82bc45655ff2Erik } 58547c41e807e36999e4d0d2072e41a82bc45655ff2Erik 58647c41e807e36999e4d0d2072e41a82bc45655ff2Erik /** 58747c41e807e36999e4d0d2072e41a82bc45655ff2Erik * Return the current y distance between the two pointers forming the 58847c41e807e36999e4d0d2072e41a82bc45655ff2Erik * gesture in progress. 58947c41e807e36999e4d0d2072e41a82bc45655ff2Erik * 59047c41e807e36999e4d0d2072e41a82bc45655ff2Erik * @return Distance between pointers in pixels. 59147c41e807e36999e4d0d2072e41a82bc45655ff2Erik */ 59247c41e807e36999e4d0d2072e41a82bc45655ff2Erik public float getCurrentSpanY() { 59347c41e807e36999e4d0d2072e41a82bc45655ff2Erik return mCurrFingerDiffY; 59447c41e807e36999e4d0d2072e41a82bc45655ff2Erik } 59547c41e807e36999e4d0d2072e41a82bc45655ff2Erik 59647c41e807e36999e4d0d2072e41a82bc45655ff2Erik /** 597ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell * Return the previous distance between the two pointers forming the 598ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell * gesture in progress. 59947c41e807e36999e4d0d2072e41a82bc45655ff2Erik * 600ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell * @return Previous distance between pointers in pixels. 601ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell */ 602ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell public float getPreviousSpan() { 603ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell if (mPrevLen == -1) { 604ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell final float pvx = mPrevFingerDiffX; 605ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell final float pvy = mPrevFingerDiffY; 606346c8fb036b99a33756c66dcebeb5d0f67a1df72Adam Powell mPrevLen = FloatMath.sqrt(pvx*pvx + pvy*pvy); 607ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell } 608ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell return mPrevLen; 609ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell } 610ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell 611ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell /** 61247c41e807e36999e4d0d2072e41a82bc45655ff2Erik * Return the previous x distance between the two pointers forming the 61347c41e807e36999e4d0d2072e41a82bc45655ff2Erik * gesture in progress. 61447c41e807e36999e4d0d2072e41a82bc45655ff2Erik * 61547c41e807e36999e4d0d2072e41a82bc45655ff2Erik * @return Previous distance between pointers in pixels. 61647c41e807e36999e4d0d2072e41a82bc45655ff2Erik */ 61747c41e807e36999e4d0d2072e41a82bc45655ff2Erik public float getPreviousSpanX() { 61847c41e807e36999e4d0d2072e41a82bc45655ff2Erik return mPrevFingerDiffX; 61947c41e807e36999e4d0d2072e41a82bc45655ff2Erik } 62047c41e807e36999e4d0d2072e41a82bc45655ff2Erik 62147c41e807e36999e4d0d2072e41a82bc45655ff2Erik /** 62247c41e807e36999e4d0d2072e41a82bc45655ff2Erik * Return the previous y distance between the two pointers forming the 62347c41e807e36999e4d0d2072e41a82bc45655ff2Erik * gesture in progress. 62447c41e807e36999e4d0d2072e41a82bc45655ff2Erik * 62547c41e807e36999e4d0d2072e41a82bc45655ff2Erik * @return Previous distance between pointers in pixels. 62647c41e807e36999e4d0d2072e41a82bc45655ff2Erik */ 62747c41e807e36999e4d0d2072e41a82bc45655ff2Erik public float getPreviousSpanY() { 62847c41e807e36999e4d0d2072e41a82bc45655ff2Erik return mPrevFingerDiffY; 62947c41e807e36999e4d0d2072e41a82bc45655ff2Erik } 63047c41e807e36999e4d0d2072e41a82bc45655ff2Erik 63147c41e807e36999e4d0d2072e41a82bc45655ff2Erik /** 632ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell * Return the scaling factor from the previous scale event to the current 633ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell * event. This value is defined as 634ab905c87b7324d15715b78eaae7ef8558ad3bd10Adam Powell * ({@link #getCurrentSpan()} / {@link #getPreviousSpan()}). 63547c41e807e36999e4d0d2072e41a82bc45655ff2Erik * 636ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell * @return The current scaling factor. 637ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell */ 638ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell public float getScaleFactor() { 639ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell if (mScaleFactor == -1) { 640ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell mScaleFactor = getCurrentSpan() / getPreviousSpan(); 641ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell } 642ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell return mScaleFactor; 643ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell } 64447c41e807e36999e4d0d2072e41a82bc45655ff2Erik 645ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell /** 646ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell * Return the time difference in milliseconds between the previous 647ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell * accepted scaling event and the current scaling event. 64847c41e807e36999e4d0d2072e41a82bc45655ff2Erik * 649ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell * @return Time difference since the last scaling event in milliseconds. 650ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell */ 651ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell public long getTimeDelta() { 652ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell return mTimeDelta; 653ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell } 65447c41e807e36999e4d0d2072e41a82bc45655ff2Erik 655ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell /** 656ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell * Return the event time of the current event being processed. 65747c41e807e36999e4d0d2072e41a82bc45655ff2Erik * 658ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell * @return Current event time in milliseconds. 659ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell */ 660ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell public long getEventTime() { 661ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell return mCurrEvent.getEventTime(); 662ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell } 663ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell} 664