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; 203307958c6b4b3707c8861db829893b1f5820b677Adam Powellimport android.content.res.Resources; 21e8ce8ba2b5633e479ccaa82d8e3147ccbba62961Mindy Pereiraimport android.os.Build; 22e8ce8ba2b5633e479ccaa82d8e3147ccbba62961Mindy Pereiraimport android.os.Handler; 23a4ce6ae0d379c8866697d064cfd1661ea156405eAdam Powell 24ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell/** 25618cbea4e746196cbde43746706bec02e14b487bAdam Powell * Detects scaling transformation gestures using the supplied {@link MotionEvent}s. 26618cbea4e746196cbde43746706bec02e14b487bAdam Powell * The {@link OnScaleGestureListener} callback will notify users when a particular 27618cbea4e746196cbde43746706bec02e14b487bAdam Powell * gesture event has occurred. 28618cbea4e746196cbde43746706bec02e14b487bAdam Powell * 29ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell * This class should only be used with {@link MotionEvent}s reported via touch. 3047c41e807e36999e4d0d2072e41a82bc45655ff2Erik * 31ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell * To use this class: 32ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell * <ul> 33ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell * <li>Create an instance of the {@code ScaleGestureDetector} for your 34ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell * {@link View} 35ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell * <li>In the {@link View#onTouchEvent(MotionEvent)} method ensure you call 36ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell * {@link #onTouchEvent(MotionEvent)}. The methods defined in your 37ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell * callback will be executed when the events occur. 38ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell * </ul> 39ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell */ 40ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powellpublic class ScaleGestureDetector { 410818020d7cb04d83d51b71b8262d34bd79a76a95Adam Powell private static final String TAG = "ScaleGestureDetector"; 420818020d7cb04d83d51b71b8262d34bd79a76a95Adam Powell 43ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell /** 44ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell * The listener for receiving notifications when gestures occur. 45ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell * If you want to listen for all the different gestures then implement 46ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell * this interface. If you only want to listen for a subset it might 47ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell * be easier to extend {@link SimpleOnScaleGestureListener}. 4847c41e807e36999e4d0d2072e41a82bc45655ff2Erik * 49ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell * An application will receive events in the following order: 50ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell * <ul> 51ab905c87b7324d15715b78eaae7ef8558ad3bd10Adam Powell * <li>One {@link OnScaleGestureListener#onScaleBegin(ScaleGestureDetector)} 52ab905c87b7324d15715b78eaae7ef8558ad3bd10Adam Powell * <li>Zero or more {@link OnScaleGestureListener#onScale(ScaleGestureDetector)} 53ab905c87b7324d15715b78eaae7ef8558ad3bd10Adam Powell * <li>One {@link OnScaleGestureListener#onScaleEnd(ScaleGestureDetector)} 54ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell * </ul> 55ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell */ 56ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell public interface OnScaleGestureListener { 57ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell /** 58ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell * Responds to scaling events for a gesture in progress. 59ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell * Reported by pointer motion. 6047c41e807e36999e4d0d2072e41a82bc45655ff2Erik * 61ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell * @param detector The detector reporting the event - use this to 62ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell * retrieve extended info about event state. 63ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell * @return Whether or not the detector should consider this event 64ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell * as handled. If an event was not handled, the detector 65ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell * will continue to accumulate movement until an event is 66ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell * handled. This can be useful if an application, for example, 67ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell * only wants to update scaling factors if the change is 68ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell * greater than 0.01. 69ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell */ 70ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell public boolean onScale(ScaleGestureDetector detector); 71ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell 72ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell /** 73ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell * Responds to the beginning of a scaling gesture. Reported by 74ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell * new pointers going down. 7547c41e807e36999e4d0d2072e41a82bc45655ff2Erik * 76ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell * @param detector The detector reporting the event - use this to 77ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell * retrieve extended info about event state. 78ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell * @return Whether or not the detector should continue recognizing 79ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell * this gesture. For example, if a gesture is beginning 80ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell * with a focal point outside of a region where it makes 81ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell * sense, onScaleBegin() may return false to ignore the 82ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell * rest of the gesture. 83ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell */ 84ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell public boolean onScaleBegin(ScaleGestureDetector detector); 85ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell 86ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell /** 87ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell * Responds to the end of a scale gesture. Reported by existing 88216bccf804db9c972b317620a27de6a8adf7fbfeAdam Powell * pointers going up. 8947c41e807e36999e4d0d2072e41a82bc45655ff2Erik * 90ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell * Once a scale has ended, {@link ScaleGestureDetector#getFocusX()} 9147ec2fb37046797bebc82b505a13c552021b9ff3Adam Powell * and {@link ScaleGestureDetector#getFocusY()} will return focal point 9247ec2fb37046797bebc82b505a13c552021b9ff3Adam Powell * of the pointers remaining on the screen. 9347c41e807e36999e4d0d2072e41a82bc45655ff2Erik * 94ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell * @param detector The detector reporting the event - use this to 95ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell * retrieve extended info about event state. 96ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell */ 97ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell public void onScaleEnd(ScaleGestureDetector detector); 98ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell } 9947c41e807e36999e4d0d2072e41a82bc45655ff2Erik 100ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell /** 101ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell * A convenience class to extend when you only want to listen for a subset 102ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell * of scaling-related events. This implements all methods in 103ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell * {@link OnScaleGestureListener} but does nothing. 104346c8fb036b99a33756c66dcebeb5d0f67a1df72Adam Powell * {@link OnScaleGestureListener#onScale(ScaleGestureDetector)} returns 105346c8fb036b99a33756c66dcebeb5d0f67a1df72Adam Powell * {@code false} so that a subclass can retrieve the accumulated scale 106346c8fb036b99a33756c66dcebeb5d0f67a1df72Adam Powell * factor in an overridden onScaleEnd. 107346c8fb036b99a33756c66dcebeb5d0f67a1df72Adam Powell * {@link OnScaleGestureListener#onScaleBegin(ScaleGestureDetector)} returns 10847c41e807e36999e4d0d2072e41a82bc45655ff2Erik * {@code true}. 109ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell */ 110216bccf804db9c972b317620a27de6a8adf7fbfeAdam Powell public static class SimpleOnScaleGestureListener implements OnScaleGestureListener { 111ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell 112ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell public boolean onScale(ScaleGestureDetector detector) { 113346c8fb036b99a33756c66dcebeb5d0f67a1df72Adam Powell return false; 114ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell } 115ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell 116ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell public boolean onScaleBegin(ScaleGestureDetector detector) { 117ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell return true; 118ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell } 119ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell 120ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell public void onScaleEnd(ScaleGestureDetector detector) { 121ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell // Intentionally empty 122ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell } 123ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell } 124ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell 125346c8fb036b99a33756c66dcebeb5d0f67a1df72Adam Powell private final Context mContext; 126346c8fb036b99a33756c66dcebeb5d0f67a1df72Adam Powell private final OnScaleGestureListener mListener; 127ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell 128ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell private float mFocusX; 129ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell private float mFocusY; 130618cbea4e746196cbde43746706bec02e14b487bAdam Powell 1319f1221f87e4762457c8fa0b4c0e5a291d9aef5c9Mindy Pereira private boolean mQuickScaleEnabled; 1326895518d48f265371c26ab4c0d300c3313c4c210Mady Mellor private boolean mStylusScaleEnabled; 133e8ce8ba2b5633e479ccaa82d8e3147ccbba62961Mindy Pereira 134618cbea4e746196cbde43746706bec02e14b487bAdam Powell private float mCurrSpan; 135618cbea4e746196cbde43746706bec02e14b487bAdam Powell private float mPrevSpan; 13647ec2fb37046797bebc82b505a13c552021b9ff3Adam Powell private float mInitialSpan; 137618cbea4e746196cbde43746706bec02e14b487bAdam Powell private float mCurrSpanX; 138618cbea4e746196cbde43746706bec02e14b487bAdam Powell private float mCurrSpanY; 139618cbea4e746196cbde43746706bec02e14b487bAdam Powell private float mPrevSpanX; 140618cbea4e746196cbde43746706bec02e14b487bAdam Powell private float mPrevSpanY; 141618cbea4e746196cbde43746706bec02e14b487bAdam Powell private long mCurrTime; 142618cbea4e746196cbde43746706bec02e14b487bAdam Powell private long mPrevTime; 143618cbea4e746196cbde43746706bec02e14b487bAdam Powell private boolean mInProgress; 14447ec2fb37046797bebc82b505a13c552021b9ff3Adam Powell private int mSpanSlop; 145828e56ea6e8f4d5aa650052580f1f7873ad8296dAdam Powell private int mMinSpan; 146e33cef8037cb87386e17bcf8701a47452d262fa6Adam Powell 147e8ce8ba2b5633e479ccaa82d8e3147ccbba62961Mindy Pereira private final Handler mHandler; 148a4ce6ae0d379c8866697d064cfd1661ea156405eAdam Powell 149847d17fcba7e0fab3093d05b5405554df91c08e2Mady Mellor private float mAnchoredScaleStartX; 150847d17fcba7e0fab3093d05b5405554df91c08e2Mady Mellor private float mAnchoredScaleStartY; 151847d17fcba7e0fab3093d05b5405554df91c08e2Mady Mellor private int mAnchoredScaleMode = ANCHORED_SCALE_MODE_NONE; 152847d17fcba7e0fab3093d05b5405554df91c08e2Mady Mellor 153d736d2069b94348b519089348f4a85eb482db668Adam Powell private static final long TOUCH_STABILIZE_TIME = 128; // ms 15424870ce4f222fec664a24e47cf6b12db36dbdff2Mindy Pereira private static final float SCALE_FACTOR = .5f; 155847d17fcba7e0fab3093d05b5405554df91c08e2Mady Mellor private static final int ANCHORED_SCALE_MODE_NONE = 0; 156847d17fcba7e0fab3093d05b5405554df91c08e2Mady Mellor private static final int ANCHORED_SCALE_MODE_DOUBLE_TAP = 1; 1576895518d48f265371c26ab4c0d300c3313c4c210Mady Mellor private static final int ANCHORED_SCALE_MODE_STYLUS = 2; 158e8ce8ba2b5633e479ccaa82d8e3147ccbba62961Mindy Pereira 159a4ce6ae0d379c8866697d064cfd1661ea156405eAdam Powell 16021bc5c917d4ee2a9b2b8173091e6bba85eaff899Jeff Brown /** 16121bc5c917d4ee2a9b2b8173091e6bba85eaff899Jeff Brown * Consistency verifier for debugging purposes. 16221bc5c917d4ee2a9b2b8173091e6bba85eaff899Jeff Brown */ 16321bc5c917d4ee2a9b2b8173091e6bba85eaff899Jeff Brown private final InputEventConsistencyVerifier mInputEventConsistencyVerifier = 16421bc5c917d4ee2a9b2b8173091e6bba85eaff899Jeff Brown InputEventConsistencyVerifier.isInstrumentationEnabled() ? 16521bc5c917d4ee2a9b2b8173091e6bba85eaff899Jeff Brown new InputEventConsistencyVerifier(this, 0) : null; 166e8ce8ba2b5633e479ccaa82d8e3147ccbba62961Mindy Pereira private GestureDetector mGestureDetector; 167e8ce8ba2b5633e479ccaa82d8e3147ccbba62961Mindy Pereira 168e8ce8ba2b5633e479ccaa82d8e3147ccbba62961Mindy Pereira private boolean mEventBeforeOrAboveStartingGestureEvent; 16921bc5c917d4ee2a9b2b8173091e6bba85eaff899Jeff Brown 170e8ce8ba2b5633e479ccaa82d8e3147ccbba62961Mindy Pereira /** 171e8ce8ba2b5633e479ccaa82d8e3147ccbba62961Mindy Pereira * Creates a ScaleGestureDetector with the supplied listener. 172e8ce8ba2b5633e479ccaa82d8e3147ccbba62961Mindy Pereira * You may only use this constructor from a {@link android.os.Looper Looper} thread. 173e8ce8ba2b5633e479ccaa82d8e3147ccbba62961Mindy Pereira * 174e8ce8ba2b5633e479ccaa82d8e3147ccbba62961Mindy Pereira * @param context the application's context 175e8ce8ba2b5633e479ccaa82d8e3147ccbba62961Mindy Pereira * @param listener the listener invoked for all the callbacks, this must 176e8ce8ba2b5633e479ccaa82d8e3147ccbba62961Mindy Pereira * not be null. 177e8ce8ba2b5633e479ccaa82d8e3147ccbba62961Mindy Pereira * 178e8ce8ba2b5633e479ccaa82d8e3147ccbba62961Mindy Pereira * @throws NullPointerException if {@code listener} is null. 179e8ce8ba2b5633e479ccaa82d8e3147ccbba62961Mindy Pereira */ 180ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell public ScaleGestureDetector(Context context, OnScaleGestureListener listener) { 181e8ce8ba2b5633e479ccaa82d8e3147ccbba62961Mindy Pereira this(context, listener, null); 182e8ce8ba2b5633e479ccaa82d8e3147ccbba62961Mindy Pereira } 183e8ce8ba2b5633e479ccaa82d8e3147ccbba62961Mindy Pereira 184e8ce8ba2b5633e479ccaa82d8e3147ccbba62961Mindy Pereira /** 185e8ce8ba2b5633e479ccaa82d8e3147ccbba62961Mindy Pereira * Creates a ScaleGestureDetector with the supplied listener. 186e8ce8ba2b5633e479ccaa82d8e3147ccbba62961Mindy Pereira * @see android.os.Handler#Handler() 187e8ce8ba2b5633e479ccaa82d8e3147ccbba62961Mindy Pereira * 188e8ce8ba2b5633e479ccaa82d8e3147ccbba62961Mindy Pereira * @param context the application's context 189e8ce8ba2b5633e479ccaa82d8e3147ccbba62961Mindy Pereira * @param listener the listener invoked for all the callbacks, this must 190e8ce8ba2b5633e479ccaa82d8e3147ccbba62961Mindy Pereira * not be null. 191e8ce8ba2b5633e479ccaa82d8e3147ccbba62961Mindy Pereira * @param handler the handler to use for running deferred listener events. 192e8ce8ba2b5633e479ccaa82d8e3147ccbba62961Mindy Pereira * 193e8ce8ba2b5633e479ccaa82d8e3147ccbba62961Mindy Pereira * @throws NullPointerException if {@code listener} is null. 194e8ce8ba2b5633e479ccaa82d8e3147ccbba62961Mindy Pereira */ 195e8ce8ba2b5633e479ccaa82d8e3147ccbba62961Mindy Pereira public ScaleGestureDetector(Context context, OnScaleGestureListener listener, 19624870ce4f222fec664a24e47cf6b12db36dbdff2Mindy Pereira Handler handler) { 197ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell mContext = context; 198ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell mListener = listener; 19947ec2fb37046797bebc82b505a13c552021b9ff3Adam Powell mSpanSlop = ViewConfiguration.get(context).getScaledTouchSlop() * 2; 2003307958c6b4b3707c8861db829893b1f5820b677Adam Powell 2013307958c6b4b3707c8861db829893b1f5820b677Adam Powell final Resources res = context.getResources(); 202e8ce8ba2b5633e479ccaa82d8e3147ccbba62961Mindy Pereira mMinSpan = res.getDimensionPixelSize(com.android.internal.R.dimen.config_minScalingSpan); 203e8ce8ba2b5633e479ccaa82d8e3147ccbba62961Mindy Pereira mHandler = handler; 204e8ce8ba2b5633e479ccaa82d8e3147ccbba62961Mindy Pereira // Quick scale is enabled by default after JB_MR2 2057c36a685fea4ee2c93d1dad941061ed3b7c0c566Mady Mellor final int targetSdkVersion = context.getApplicationInfo().targetSdkVersion; 2067c36a685fea4ee2c93d1dad941061ed3b7c0c566Mady Mellor if (targetSdkVersion > Build.VERSION_CODES.JELLY_BEAN_MR2) { 207e8ce8ba2b5633e479ccaa82d8e3147ccbba62961Mindy Pereira setQuickScaleEnabled(true); 208e8ce8ba2b5633e479ccaa82d8e3147ccbba62961Mindy Pereira } 2097c36a685fea4ee2c93d1dad941061ed3b7c0c566Mady Mellor // Stylus scale is enabled by default after LOLLIPOP_MR1 2107c36a685fea4ee2c93d1dad941061ed3b7c0c566Mady Mellor if (targetSdkVersion > Build.VERSION_CODES.LOLLIPOP_MR1) { 2117c36a685fea4ee2c93d1dad941061ed3b7c0c566Mady Mellor setStylusScaleEnabled(true); 2127c36a685fea4ee2c93d1dad941061ed3b7c0c566Mady Mellor } 213ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell } 214ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell 215618cbea4e746196cbde43746706bec02e14b487bAdam Powell /** 216618cbea4e746196cbde43746706bec02e14b487bAdam Powell * Accepts MotionEvents and dispatches events to a {@link OnScaleGestureListener} 217618cbea4e746196cbde43746706bec02e14b487bAdam Powell * when appropriate. 218618cbea4e746196cbde43746706bec02e14b487bAdam Powell * 219618cbea4e746196cbde43746706bec02e14b487bAdam Powell * <p>Applications should pass a complete and consistent event stream to this method. 220618cbea4e746196cbde43746706bec02e14b487bAdam Powell * A complete and consistent event stream involves all MotionEvents from the initial 221618cbea4e746196cbde43746706bec02e14b487bAdam Powell * ACTION_DOWN to the final ACTION_UP or ACTION_CANCEL.</p> 222618cbea4e746196cbde43746706bec02e14b487bAdam Powell * 223618cbea4e746196cbde43746706bec02e14b487bAdam Powell * @param event The event to process 224618cbea4e746196cbde43746706bec02e14b487bAdam Powell * @return true if the event was processed and the detector wants to receive the 225618cbea4e746196cbde43746706bec02e14b487bAdam Powell * rest of the MotionEvents in this event stream. 226618cbea4e746196cbde43746706bec02e14b487bAdam Powell */ 227ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell public boolean onTouchEvent(MotionEvent event) { 22821bc5c917d4ee2a9b2b8173091e6bba85eaff899Jeff Brown if (mInputEventConsistencyVerifier != null) { 22921bc5c917d4ee2a9b2b8173091e6bba85eaff899Jeff Brown mInputEventConsistencyVerifier.onTouchEvent(event, 0); 23021bc5c917d4ee2a9b2b8173091e6bba85eaff899Jeff Brown } 23121bc5c917d4ee2a9b2b8173091e6bba85eaff899Jeff Brown 2327232b0ad675402222bce3b478dbb682848d5e9e6Adam Powell mCurrTime = event.getEventTime(); 2337232b0ad675402222bce3b478dbb682848d5e9e6Adam Powell 234e33cef8037cb87386e17bcf8701a47452d262fa6Adam Powell final int action = event.getActionMasked(); 235ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell 236e8ce8ba2b5633e479ccaa82d8e3147ccbba62961Mindy Pereira // Forward the event to check for double tap gesture 2379f1221f87e4762457c8fa0b4c0e5a291d9aef5c9Mindy Pereira if (mQuickScaleEnabled) { 238e8ce8ba2b5633e479ccaa82d8e3147ccbba62961Mindy Pereira mGestureDetector.onTouchEvent(event); 239e8ce8ba2b5633e479ccaa82d8e3147ccbba62961Mindy Pereira } 240e8ce8ba2b5633e479ccaa82d8e3147ccbba62961Mindy Pereira 241847d17fcba7e0fab3093d05b5405554df91c08e2Mady Mellor final int count = event.getPointerCount(); 242772fcb96f19fbd6b86ac562863714019909634bfMady Mellor final boolean isStylusButtonDown = 243772fcb96f19fbd6b86ac562863714019909634bfMady Mellor (event.getButtonState() & MotionEvent.BUTTON_STYLUS_PRIMARY) != 0; 244847d17fcba7e0fab3093d05b5405554df91c08e2Mady Mellor 245847d17fcba7e0fab3093d05b5405554df91c08e2Mady Mellor final boolean anchoredScaleCancelled = 2466895518d48f265371c26ab4c0d300c3313c4c210Mady Mellor mAnchoredScaleMode == ANCHORED_SCALE_MODE_STYLUS && !isStylusButtonDown; 247618cbea4e746196cbde43746706bec02e14b487bAdam Powell final boolean streamComplete = action == MotionEvent.ACTION_UP || 248847d17fcba7e0fab3093d05b5405554df91c08e2Mady Mellor action == MotionEvent.ACTION_CANCEL || anchoredScaleCancelled; 249e8ce8ba2b5633e479ccaa82d8e3147ccbba62961Mindy Pereira 250618cbea4e746196cbde43746706bec02e14b487bAdam Powell if (action == MotionEvent.ACTION_DOWN || streamComplete) { 251618cbea4e746196cbde43746706bec02e14b487bAdam Powell // Reset any scale in progress with the listener. 252618cbea4e746196cbde43746706bec02e14b487bAdam Powell // If it's an ACTION_DOWN we're beginning a new event stream. 253618cbea4e746196cbde43746706bec02e14b487bAdam Powell // This means the app probably didn't give us all the events. Shame on it. 254618cbea4e746196cbde43746706bec02e14b487bAdam Powell if (mInProgress) { 255618cbea4e746196cbde43746706bec02e14b487bAdam Powell mListener.onScaleEnd(this); 256618cbea4e746196cbde43746706bec02e14b487bAdam Powell mInProgress = false; 25747ec2fb37046797bebc82b505a13c552021b9ff3Adam Powell mInitialSpan = 0; 258847d17fcba7e0fab3093d05b5405554df91c08e2Mady Mellor mAnchoredScaleMode = ANCHORED_SCALE_MODE_NONE; 259847d17fcba7e0fab3093d05b5405554df91c08e2Mady Mellor } else if (inAnchoredScaleMode() && streamComplete) { 2605823352c6c5bfa3824afacd023b01af537b5dfa0Mindy Pereira mInProgress = false; 2615823352c6c5bfa3824afacd023b01af537b5dfa0Mindy Pereira mInitialSpan = 0; 262847d17fcba7e0fab3093d05b5405554df91c08e2Mady Mellor mAnchoredScaleMode = ANCHORED_SCALE_MODE_NONE; 263ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell } 264618cbea4e746196cbde43746706bec02e14b487bAdam Powell 265618cbea4e746196cbde43746706bec02e14b487bAdam Powell if (streamComplete) { 266618cbea4e746196cbde43746706bec02e14b487bAdam Powell return true; 267ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell } 268ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell } 269bbdc50b102faf52768ac3028bc49e027ff140656Jeff Brown 2706895518d48f265371c26ab4c0d300c3313c4c210Mady Mellor if (!mInProgress && mStylusScaleEnabled && !inAnchoredScaleMode() 2716895518d48f265371c26ab4c0d300c3313c4c210Mady Mellor && !streamComplete && isStylusButtonDown) { 272847d17fcba7e0fab3093d05b5405554df91c08e2Mady Mellor // Start of a button scale gesture 273847d17fcba7e0fab3093d05b5405554df91c08e2Mady Mellor mAnchoredScaleStartX = event.getX(); 274847d17fcba7e0fab3093d05b5405554df91c08e2Mady Mellor mAnchoredScaleStartY = event.getY(); 2756895518d48f265371c26ab4c0d300c3313c4c210Mady Mellor mAnchoredScaleMode = ANCHORED_SCALE_MODE_STYLUS; 276847d17fcba7e0fab3093d05b5405554df91c08e2Mady Mellor mInitialSpan = 0; 277847d17fcba7e0fab3093d05b5405554df91c08e2Mady Mellor } 278847d17fcba7e0fab3093d05b5405554df91c08e2Mady Mellor 279abde042a824c3fc2f3537ef136017dfa006258b9Adam Powell final boolean configChanged = action == MotionEvent.ACTION_DOWN || 280618cbea4e746196cbde43746706bec02e14b487bAdam Powell action == MotionEvent.ACTION_POINTER_UP || 281847d17fcba7e0fab3093d05b5405554df91c08e2Mady Mellor action == MotionEvent.ACTION_POINTER_DOWN || anchoredScaleCancelled; 282e8ce8ba2b5633e479ccaa82d8e3147ccbba62961Mindy Pereira 283618cbea4e746196cbde43746706bec02e14b487bAdam Powell final boolean pointerUp = action == MotionEvent.ACTION_POINTER_UP; 284618cbea4e746196cbde43746706bec02e14b487bAdam Powell final int skipIndex = pointerUp ? event.getActionIndex() : -1; 285618cbea4e746196cbde43746706bec02e14b487bAdam Powell 286618cbea4e746196cbde43746706bec02e14b487bAdam Powell // Determine focal point 287618cbea4e746196cbde43746706bec02e14b487bAdam Powell float sumX = 0, sumY = 0; 288618cbea4e746196cbde43746706bec02e14b487bAdam Powell final int div = pointerUp ? count - 1 : count; 289e8ce8ba2b5633e479ccaa82d8e3147ccbba62961Mindy Pereira final float focusX; 290e8ce8ba2b5633e479ccaa82d8e3147ccbba62961Mindy Pereira final float focusY; 291847d17fcba7e0fab3093d05b5405554df91c08e2Mady Mellor if (inAnchoredScaleMode()) { 292847d17fcba7e0fab3093d05b5405554df91c08e2Mady Mellor // In anchored scale mode, the focal pt is always where the double tap 293847d17fcba7e0fab3093d05b5405554df91c08e2Mady Mellor // or button down gesture started 294847d17fcba7e0fab3093d05b5405554df91c08e2Mady Mellor focusX = mAnchoredScaleStartX; 295847d17fcba7e0fab3093d05b5405554df91c08e2Mady Mellor focusY = mAnchoredScaleStartY; 296e8ce8ba2b5633e479ccaa82d8e3147ccbba62961Mindy Pereira if (event.getY() < focusY) { 297e8ce8ba2b5633e479ccaa82d8e3147ccbba62961Mindy Pereira mEventBeforeOrAboveStartingGestureEvent = true; 298e8ce8ba2b5633e479ccaa82d8e3147ccbba62961Mindy Pereira } else { 299e8ce8ba2b5633e479ccaa82d8e3147ccbba62961Mindy Pereira mEventBeforeOrAboveStartingGestureEvent = false; 300e8ce8ba2b5633e479ccaa82d8e3147ccbba62961Mindy Pereira } 301e8ce8ba2b5633e479ccaa82d8e3147ccbba62961Mindy Pereira } else { 302e8ce8ba2b5633e479ccaa82d8e3147ccbba62961Mindy Pereira for (int i = 0; i < count; i++) { 303e8ce8ba2b5633e479ccaa82d8e3147ccbba62961Mindy Pereira if (skipIndex == i) continue; 304e8ce8ba2b5633e479ccaa82d8e3147ccbba62961Mindy Pereira sumX += event.getX(i); 305e8ce8ba2b5633e479ccaa82d8e3147ccbba62961Mindy Pereira sumY += event.getY(i); 306e8ce8ba2b5633e479ccaa82d8e3147ccbba62961Mindy Pereira } 307618cbea4e746196cbde43746706bec02e14b487bAdam Powell 308e8ce8ba2b5633e479ccaa82d8e3147ccbba62961Mindy Pereira focusX = sumX / div; 309e8ce8ba2b5633e479ccaa82d8e3147ccbba62961Mindy Pereira focusY = sumY / div; 310e8ce8ba2b5633e479ccaa82d8e3147ccbba62961Mindy Pereira } 3115b5c414e31c4a8433a3290b931687a05dadc97b6Adam Powell 312618cbea4e746196cbde43746706bec02e14b487bAdam Powell // Determine average deviation from focal point 313618cbea4e746196cbde43746706bec02e14b487bAdam Powell float devSumX = 0, devSumY = 0; 314618cbea4e746196cbde43746706bec02e14b487bAdam Powell for (int i = 0; i < count; i++) { 315618cbea4e746196cbde43746706bec02e14b487bAdam Powell if (skipIndex == i) continue; 316828e56ea6e8f4d5aa650052580f1f7873ad8296dAdam Powell 3175b5c414e31c4a8433a3290b931687a05dadc97b6Adam Powell // Convert the resulting diameter into a radius. 318c6df18f5fa9f08183af96de7433fd80d74325318Adam Powell devSumX += Math.abs(event.getX(i) - focusX); 319c6df18f5fa9f08183af96de7433fd80d74325318Adam Powell devSumY += Math.abs(event.getY(i) - focusY); 320e33cef8037cb87386e17bcf8701a47452d262fa6Adam Powell } 321618cbea4e746196cbde43746706bec02e14b487bAdam Powell final float devX = devSumX / div; 322618cbea4e746196cbde43746706bec02e14b487bAdam Powell final float devY = devSumY / div; 323618cbea4e746196cbde43746706bec02e14b487bAdam Powell 324618cbea4e746196cbde43746706bec02e14b487bAdam Powell // Span is the average distance between touch points through the focal point; 325618cbea4e746196cbde43746706bec02e14b487bAdam Powell // i.e. the diameter of the circle with a radius of the average deviation from 326618cbea4e746196cbde43746706bec02e14b487bAdam Powell // the focal point. 327618cbea4e746196cbde43746706bec02e14b487bAdam Powell final float spanX = devX * 2; 328618cbea4e746196cbde43746706bec02e14b487bAdam Powell final float spanY = devY * 2; 329e8ce8ba2b5633e479ccaa82d8e3147ccbba62961Mindy Pereira final float span; 330847d17fcba7e0fab3093d05b5405554df91c08e2Mady Mellor if (inAnchoredScaleMode()) { 331e8ce8ba2b5633e479ccaa82d8e3147ccbba62961Mindy Pereira span = spanY; 332e8ce8ba2b5633e479ccaa82d8e3147ccbba62961Mindy Pereira } else { 33333253a4baa6279f81a73425b49dfb6abe5f5416eNeil Fuller span = (float) Math.hypot(spanX, spanY); 334e8ce8ba2b5633e479ccaa82d8e3147ccbba62961Mindy Pereira } 335618cbea4e746196cbde43746706bec02e14b487bAdam Powell 336618cbea4e746196cbde43746706bec02e14b487bAdam Powell // Dispatch begin/end events as needed. 337618cbea4e746196cbde43746706bec02e14b487bAdam Powell // If the configuration changes, notify the app to reset its current state by beginning 338618cbea4e746196cbde43746706bec02e14b487bAdam Powell // a fresh scale event stream. 33947ec2fb37046797bebc82b505a13c552021b9ff3Adam Powell final boolean wasInProgress = mInProgress; 34047ec2fb37046797bebc82b505a13c552021b9ff3Adam Powell mFocusX = focusX; 34147ec2fb37046797bebc82b505a13c552021b9ff3Adam Powell mFocusY = focusY; 342847d17fcba7e0fab3093d05b5405554df91c08e2Mady Mellor if (!inAnchoredScaleMode() && mInProgress && (span < mMinSpan || configChanged)) { 343618cbea4e746196cbde43746706bec02e14b487bAdam Powell mListener.onScaleEnd(this); 344618cbea4e746196cbde43746706bec02e14b487bAdam Powell mInProgress = false; 34547ec2fb37046797bebc82b505a13c552021b9ff3Adam Powell mInitialSpan = span; 346618cbea4e746196cbde43746706bec02e14b487bAdam Powell } 347618cbea4e746196cbde43746706bec02e14b487bAdam Powell if (configChanged) { 348618cbea4e746196cbde43746706bec02e14b487bAdam Powell mPrevSpanX = mCurrSpanX = spanX; 349618cbea4e746196cbde43746706bec02e14b487bAdam Powell mPrevSpanY = mCurrSpanY = spanY; 35047ec2fb37046797bebc82b505a13c552021b9ff3Adam Powell mInitialSpan = mPrevSpan = mCurrSpan = span; 351618cbea4e746196cbde43746706bec02e14b487bAdam Powell } 35224870ce4f222fec664a24e47cf6b12db36dbdff2Mindy Pereira 353847d17fcba7e0fab3093d05b5405554df91c08e2Mady Mellor final int minSpan = inAnchoredScaleMode() ? mSpanSlop : mMinSpan; 35424870ce4f222fec664a24e47cf6b12db36dbdff2Mindy Pereira if (!mInProgress && span >= minSpan && 35547ec2fb37046797bebc82b505a13c552021b9ff3Adam Powell (wasInProgress || Math.abs(span - mInitialSpan) > mSpanSlop)) { 35647ec2fb37046797bebc82b505a13c552021b9ff3Adam Powell mPrevSpanX = mCurrSpanX = spanX; 35747ec2fb37046797bebc82b505a13c552021b9ff3Adam Powell mPrevSpanY = mCurrSpanY = spanY; 35847ec2fb37046797bebc82b505a13c552021b9ff3Adam Powell mPrevSpan = mCurrSpan = span; 3597232b0ad675402222bce3b478dbb682848d5e9e6Adam Powell mPrevTime = mCurrTime; 360618cbea4e746196cbde43746706bec02e14b487bAdam Powell mInProgress = mListener.onScaleBegin(this); 361ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell } 362ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell 363618cbea4e746196cbde43746706bec02e14b487bAdam Powell // Handle motion; focal point and span/scale factor are changing. 364618cbea4e746196cbde43746706bec02e14b487bAdam Powell if (action == MotionEvent.ACTION_MOVE) { 365618cbea4e746196cbde43746706bec02e14b487bAdam Powell mCurrSpanX = spanX; 366618cbea4e746196cbde43746706bec02e14b487bAdam Powell mCurrSpanY = spanY; 367618cbea4e746196cbde43746706bec02e14b487bAdam Powell mCurrSpan = span; 368618cbea4e746196cbde43746706bec02e14b487bAdam Powell 369618cbea4e746196cbde43746706bec02e14b487bAdam Powell boolean updatePrev = true; 370e8ce8ba2b5633e479ccaa82d8e3147ccbba62961Mindy Pereira 371618cbea4e746196cbde43746706bec02e14b487bAdam Powell if (mInProgress) { 372618cbea4e746196cbde43746706bec02e14b487bAdam Powell updatePrev = mListener.onScale(this); 373618cbea4e746196cbde43746706bec02e14b487bAdam Powell } 374e33cef8037cb87386e17bcf8701a47452d262fa6Adam Powell 375618cbea4e746196cbde43746706bec02e14b487bAdam Powell if (updatePrev) { 376618cbea4e746196cbde43746706bec02e14b487bAdam Powell mPrevSpanX = mCurrSpanX; 377618cbea4e746196cbde43746706bec02e14b487bAdam Powell mPrevSpanY = mCurrSpanY; 378618cbea4e746196cbde43746706bec02e14b487bAdam Powell mPrevSpan = mCurrSpan; 3797232b0ad675402222bce3b478dbb682848d5e9e6Adam Powell mPrevTime = mCurrTime; 380d0197f3669efda060c7ee2069ff41bd970fd6d9cAdam Powell } 381d0197f3669efda060c7ee2069ff41bd970fd6d9cAdam Powell } 382d0197f3669efda060c7ee2069ff41bd970fd6d9cAdam Powell 383618cbea4e746196cbde43746706bec02e14b487bAdam Powell return true; 384ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell } 385ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell 386847d17fcba7e0fab3093d05b5405554df91c08e2Mady Mellor private boolean inAnchoredScaleMode() { 387847d17fcba7e0fab3093d05b5405554df91c08e2Mady Mellor return mAnchoredScaleMode != ANCHORED_SCALE_MODE_NONE; 388e8ce8ba2b5633e479ccaa82d8e3147ccbba62961Mindy Pereira } 389e8ce8ba2b5633e479ccaa82d8e3147ccbba62961Mindy Pereira 390e8ce8ba2b5633e479ccaa82d8e3147ccbba62961Mindy Pereira /** 391e8ce8ba2b5633e479ccaa82d8e3147ccbba62961Mindy Pereira * Set whether the associated {@link OnScaleGestureListener} should receive onScale callbacks 392e8ce8ba2b5633e479ccaa82d8e3147ccbba62961Mindy Pereira * when the user performs a doubleTap followed by a swipe. Note that this is enabled by default 393e8ce8ba2b5633e479ccaa82d8e3147ccbba62961Mindy Pereira * if the app targets API 19 and newer. 394e8ce8ba2b5633e479ccaa82d8e3147ccbba62961Mindy Pereira * @param scales true to enable quick scaling, false to disable 395e8ce8ba2b5633e479ccaa82d8e3147ccbba62961Mindy Pereira */ 396e8ce8ba2b5633e479ccaa82d8e3147ccbba62961Mindy Pereira public void setQuickScaleEnabled(boolean scales) { 3979f1221f87e4762457c8fa0b4c0e5a291d9aef5c9Mindy Pereira mQuickScaleEnabled = scales; 3989f1221f87e4762457c8fa0b4c0e5a291d9aef5c9Mindy Pereira if (mQuickScaleEnabled && mGestureDetector == null) { 399e8ce8ba2b5633e479ccaa82d8e3147ccbba62961Mindy Pereira GestureDetector.SimpleOnGestureListener gestureListener = 400e8ce8ba2b5633e479ccaa82d8e3147ccbba62961Mindy Pereira new GestureDetector.SimpleOnGestureListener() { 401e8ce8ba2b5633e479ccaa82d8e3147ccbba62961Mindy Pereira @Override 402e8ce8ba2b5633e479ccaa82d8e3147ccbba62961Mindy Pereira public boolean onDoubleTap(MotionEvent e) { 403e8ce8ba2b5633e479ccaa82d8e3147ccbba62961Mindy Pereira // Double tap: start watching for a swipe 404847d17fcba7e0fab3093d05b5405554df91c08e2Mady Mellor mAnchoredScaleStartX = e.getX(); 405847d17fcba7e0fab3093d05b5405554df91c08e2Mady Mellor mAnchoredScaleStartY = e.getY(); 406847d17fcba7e0fab3093d05b5405554df91c08e2Mady Mellor mAnchoredScaleMode = ANCHORED_SCALE_MODE_DOUBLE_TAP; 407e8ce8ba2b5633e479ccaa82d8e3147ccbba62961Mindy Pereira return true; 408e8ce8ba2b5633e479ccaa82d8e3147ccbba62961Mindy Pereira } 40924870ce4f222fec664a24e47cf6b12db36dbdff2Mindy Pereira }; 410e8ce8ba2b5633e479ccaa82d8e3147ccbba62961Mindy Pereira mGestureDetector = new GestureDetector(mContext, gestureListener, mHandler); 411e8ce8ba2b5633e479ccaa82d8e3147ccbba62961Mindy Pereira } 412e8ce8ba2b5633e479ccaa82d8e3147ccbba62961Mindy Pereira } 413e8ce8ba2b5633e479ccaa82d8e3147ccbba62961Mindy Pereira 4149f1221f87e4762457c8fa0b4c0e5a291d9aef5c9Mindy Pereira /** 4159f1221f87e4762457c8fa0b4c0e5a291d9aef5c9Mindy Pereira * Return whether the quick scale gesture, in which the user performs a double tap followed by a 4169f1221f87e4762457c8fa0b4c0e5a291d9aef5c9Mindy Pereira * swipe, should perform scaling. {@see #setQuickScaleEnabled(boolean)}. 4179f1221f87e4762457c8fa0b4c0e5a291d9aef5c9Mindy Pereira */ 4189f1221f87e4762457c8fa0b4c0e5a291d9aef5c9Mindy Pereira public boolean isQuickScaleEnabled() { 4199f1221f87e4762457c8fa0b4c0e5a291d9aef5c9Mindy Pereira return mQuickScaleEnabled; 4209f1221f87e4762457c8fa0b4c0e5a291d9aef5c9Mindy Pereira } 4219f1221f87e4762457c8fa0b4c0e5a291d9aef5c9Mindy Pereira 422ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell /** 4236895518d48f265371c26ab4c0d300c3313c4c210Mady Mellor * Sets whether the associates {@link OnScaleGestureListener} should receive 4246895518d48f265371c26ab4c0d300c3313c4c210Mady Mellor * onScale callbacks when the user uses a stylus and presses the button. 4256895518d48f265371c26ab4c0d300c3313c4c210Mady Mellor * Note that this is enabled by default if the app targets API 23 and newer. 426847d17fcba7e0fab3093d05b5405554df91c08e2Mady Mellor * 4276895518d48f265371c26ab4c0d300c3313c4c210Mady Mellor * @param scales true to enable stylus scaling, false to disable. 428847d17fcba7e0fab3093d05b5405554df91c08e2Mady Mellor */ 4296895518d48f265371c26ab4c0d300c3313c4c210Mady Mellor public void setStylusScaleEnabled(boolean scales) { 4306895518d48f265371c26ab4c0d300c3313c4c210Mady Mellor mStylusScaleEnabled = scales; 431847d17fcba7e0fab3093d05b5405554df91c08e2Mady Mellor } 432847d17fcba7e0fab3093d05b5405554df91c08e2Mady Mellor 433847d17fcba7e0fab3093d05b5405554df91c08e2Mady Mellor /** 434d9ff4df850e9e22915d09047c953765c84dcfb84Mady Mellor * Return whether the stylus scale gesture, in which the user uses a stylus and presses the 435d9ff4df850e9e22915d09047c953765c84dcfb84Mady Mellor * button, should perform scaling. {@see #setStylusScaleEnabled(boolean)} 436847d17fcba7e0fab3093d05b5405554df91c08e2Mady Mellor */ 4376895518d48f265371c26ab4c0d300c3313c4c210Mady Mellor public boolean isStylusScaleEnabled() { 4386895518d48f265371c26ab4c0d300c3313c4c210Mady Mellor return mStylusScaleEnabled; 439847d17fcba7e0fab3093d05b5405554df91c08e2Mady Mellor } 440847d17fcba7e0fab3093d05b5405554df91c08e2Mady Mellor 441847d17fcba7e0fab3093d05b5405554df91c08e2Mady Mellor /** 442618cbea4e746196cbde43746706bec02e14b487bAdam Powell * Returns {@code true} if a scale gesture is in progress. 443ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell */ 444ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell public boolean isInProgress() { 445618cbea4e746196cbde43746706bec02e14b487bAdam Powell return mInProgress; 446ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell } 447ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell 448ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell /** 449ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell * Get the X coordinate of the current gesture's focal point. 450618cbea4e746196cbde43746706bec02e14b487bAdam Powell * If a gesture is in progress, the focal point is between 451618cbea4e746196cbde43746706bec02e14b487bAdam Powell * each of the pointers forming the gesture. 452618cbea4e746196cbde43746706bec02e14b487bAdam Powell * 453ab905c87b7324d15715b78eaae7ef8558ad3bd10Adam Powell * If {@link #isInProgress()} would return false, the result of this 454ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell * function is undefined. 45547c41e807e36999e4d0d2072e41a82bc45655ff2Erik * 456ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell * @return X coordinate of the focal point in pixels. 457ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell */ 458ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell public float getFocusX() { 459ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell return mFocusX; 460ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell } 461ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell 462ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell /** 463ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell * Get the Y coordinate of the current gesture's focal point. 464618cbea4e746196cbde43746706bec02e14b487bAdam Powell * If a gesture is in progress, the focal point is between 465618cbea4e746196cbde43746706bec02e14b487bAdam Powell * each of the pointers forming the gesture. 466618cbea4e746196cbde43746706bec02e14b487bAdam Powell * 467ab905c87b7324d15715b78eaae7ef8558ad3bd10Adam Powell * If {@link #isInProgress()} would return false, the result of this 468ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell * function is undefined. 46947c41e807e36999e4d0d2072e41a82bc45655ff2Erik * 470ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell * @return Y coordinate of the focal point in pixels. 471ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell */ 472ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell public float getFocusY() { 473ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell return mFocusY; 474ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell } 475ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell 476ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell /** 477618cbea4e746196cbde43746706bec02e14b487bAdam Powell * Return the average distance between each of the pointers forming the 478618cbea4e746196cbde43746706bec02e14b487bAdam Powell * gesture in progress through the focal point. 47947c41e807e36999e4d0d2072e41a82bc45655ff2Erik * 480ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell * @return Distance between pointers in pixels. 481ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell */ 482ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell public float getCurrentSpan() { 483618cbea4e746196cbde43746706bec02e14b487bAdam Powell return mCurrSpan; 484ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell } 485ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell 486ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell /** 487618cbea4e746196cbde43746706bec02e14b487bAdam Powell * Return the average X distance between each of the pointers forming the 488618cbea4e746196cbde43746706bec02e14b487bAdam Powell * gesture in progress through the focal point. 48947c41e807e36999e4d0d2072e41a82bc45655ff2Erik * 49047c41e807e36999e4d0d2072e41a82bc45655ff2Erik * @return Distance between pointers in pixels. 49147c41e807e36999e4d0d2072e41a82bc45655ff2Erik */ 49247c41e807e36999e4d0d2072e41a82bc45655ff2Erik public float getCurrentSpanX() { 493618cbea4e746196cbde43746706bec02e14b487bAdam Powell return mCurrSpanX; 49447c41e807e36999e4d0d2072e41a82bc45655ff2Erik } 49547c41e807e36999e4d0d2072e41a82bc45655ff2Erik 49647c41e807e36999e4d0d2072e41a82bc45655ff2Erik /** 497618cbea4e746196cbde43746706bec02e14b487bAdam Powell * Return the average Y distance between each of the pointers forming the 498618cbea4e746196cbde43746706bec02e14b487bAdam Powell * gesture in progress through the focal point. 49947c41e807e36999e4d0d2072e41a82bc45655ff2Erik * 50047c41e807e36999e4d0d2072e41a82bc45655ff2Erik * @return Distance between pointers in pixels. 50147c41e807e36999e4d0d2072e41a82bc45655ff2Erik */ 50247c41e807e36999e4d0d2072e41a82bc45655ff2Erik public float getCurrentSpanY() { 503618cbea4e746196cbde43746706bec02e14b487bAdam Powell return mCurrSpanY; 50447c41e807e36999e4d0d2072e41a82bc45655ff2Erik } 50547c41e807e36999e4d0d2072e41a82bc45655ff2Erik 50647c41e807e36999e4d0d2072e41a82bc45655ff2Erik /** 507618cbea4e746196cbde43746706bec02e14b487bAdam Powell * Return the previous average distance between each of the pointers forming the 508618cbea4e746196cbde43746706bec02e14b487bAdam Powell * gesture in progress through the focal point. 50947c41e807e36999e4d0d2072e41a82bc45655ff2Erik * 510ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell * @return Previous distance between pointers in pixels. 511ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell */ 512ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell public float getPreviousSpan() { 513618cbea4e746196cbde43746706bec02e14b487bAdam Powell return mPrevSpan; 514ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell } 515ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell 516ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell /** 517618cbea4e746196cbde43746706bec02e14b487bAdam Powell * Return the previous average X distance between each of the pointers forming the 518618cbea4e746196cbde43746706bec02e14b487bAdam Powell * gesture in progress through the focal point. 51947c41e807e36999e4d0d2072e41a82bc45655ff2Erik * 52047c41e807e36999e4d0d2072e41a82bc45655ff2Erik * @return Previous distance between pointers in pixels. 52147c41e807e36999e4d0d2072e41a82bc45655ff2Erik */ 52247c41e807e36999e4d0d2072e41a82bc45655ff2Erik public float getPreviousSpanX() { 523618cbea4e746196cbde43746706bec02e14b487bAdam Powell return mPrevSpanX; 52447c41e807e36999e4d0d2072e41a82bc45655ff2Erik } 52547c41e807e36999e4d0d2072e41a82bc45655ff2Erik 52647c41e807e36999e4d0d2072e41a82bc45655ff2Erik /** 527618cbea4e746196cbde43746706bec02e14b487bAdam Powell * Return the previous average Y distance between each of the pointers forming the 528618cbea4e746196cbde43746706bec02e14b487bAdam Powell * gesture in progress through the focal point. 52947c41e807e36999e4d0d2072e41a82bc45655ff2Erik * 53047c41e807e36999e4d0d2072e41a82bc45655ff2Erik * @return Previous distance between pointers in pixels. 53147c41e807e36999e4d0d2072e41a82bc45655ff2Erik */ 53247c41e807e36999e4d0d2072e41a82bc45655ff2Erik public float getPreviousSpanY() { 533618cbea4e746196cbde43746706bec02e14b487bAdam Powell return mPrevSpanY; 53447c41e807e36999e4d0d2072e41a82bc45655ff2Erik } 53547c41e807e36999e4d0d2072e41a82bc45655ff2Erik 53647c41e807e36999e4d0d2072e41a82bc45655ff2Erik /** 537ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell * Return the scaling factor from the previous scale event to the current 538ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell * event. This value is defined as 539ab905c87b7324d15715b78eaae7ef8558ad3bd10Adam Powell * ({@link #getCurrentSpan()} / {@link #getPreviousSpan()}). 54047c41e807e36999e4d0d2072e41a82bc45655ff2Erik * 541ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell * @return The current scaling factor. 542ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell */ 543ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell public float getScaleFactor() { 544847d17fcba7e0fab3093d05b5405554df91c08e2Mady Mellor if (inAnchoredScaleMode()) { 545e8ce8ba2b5633e479ccaa82d8e3147ccbba62961Mindy Pereira // Drag is moving up; the further away from the gesture 546e8ce8ba2b5633e479ccaa82d8e3147ccbba62961Mindy Pereira // start, the smaller the span should be, the closer, 547e8ce8ba2b5633e479ccaa82d8e3147ccbba62961Mindy Pereira // the larger the span, and therefore the larger the scale 54824870ce4f222fec664a24e47cf6b12db36dbdff2Mindy Pereira final boolean scaleUp = 54924870ce4f222fec664a24e47cf6b12db36dbdff2Mindy Pereira (mEventBeforeOrAboveStartingGestureEvent && (mCurrSpan < mPrevSpan)) || 55024870ce4f222fec664a24e47cf6b12db36dbdff2Mindy Pereira (!mEventBeforeOrAboveStartingGestureEvent && (mCurrSpan > mPrevSpan)); 55124870ce4f222fec664a24e47cf6b12db36dbdff2Mindy Pereira final float spanDiff = (Math.abs(1 - (mCurrSpan / mPrevSpan)) * SCALE_FACTOR); 55224870ce4f222fec664a24e47cf6b12db36dbdff2Mindy Pereira return mPrevSpan <= 0 ? 1 : scaleUp ? (1 + spanDiff) : (1 - spanDiff); 553e8ce8ba2b5633e479ccaa82d8e3147ccbba62961Mindy Pereira } 554618cbea4e746196cbde43746706bec02e14b487bAdam Powell return mPrevSpan > 0 ? mCurrSpan / mPrevSpan : 1; 555ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell } 55647c41e807e36999e4d0d2072e41a82bc45655ff2Erik 557ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell /** 558ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell * Return the time difference in milliseconds between the previous 559ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell * accepted scaling event and the current scaling event. 56047c41e807e36999e4d0d2072e41a82bc45655ff2Erik * 561ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell * @return Time difference since the last scaling event in milliseconds. 562ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell */ 563ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell public long getTimeDelta() { 564618cbea4e746196cbde43746706bec02e14b487bAdam Powell return mCurrTime - mPrevTime; 565ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell } 56647c41e807e36999e4d0d2072e41a82bc45655ff2Erik 567ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell /** 568ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell * Return the event time of the current event being processed. 56947c41e807e36999e4d0d2072e41a82bc45655ff2Erik * 570ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell * @return Current event time in milliseconds. 571ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell */ 572ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell public long getEventTime() { 573618cbea4e746196cbde43746706bec02e14b487bAdam Powell return mCurrTime; 574ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell } 575e8ce8ba2b5633e479ccaa82d8e3147ccbba62961Mindy Pereira}