ScaleGestureDetector.java revision abde042a824c3fc2f3537ef136017dfa006258b9
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; 20a4ce6ae0d379c8866697d064cfd1661ea156405eAdam Powellimport android.os.SystemClock; 21346c8fb036b99a33756c66dcebeb5d0f67a1df72Adam Powellimport android.util.FloatMath; 22ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell 23a4ce6ae0d379c8866697d064cfd1661ea156405eAdam Powellimport java.util.Arrays; 24a4ce6ae0d379c8866697d064cfd1661ea156405eAdam Powell 25ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell/** 26618cbea4e746196cbde43746706bec02e14b487bAdam Powell * Detects scaling transformation gestures using the supplied {@link MotionEvent}s. 27618cbea4e746196cbde43746706bec02e14b487bAdam Powell * The {@link OnScaleGestureListener} callback will notify users when a particular 28618cbea4e746196cbde43746706bec02e14b487bAdam Powell * gesture event has occurred. 29618cbea4e746196cbde43746706bec02e14b487bAdam Powell * 30ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell * This class should only be used with {@link MotionEvent}s reported via touch. 3147c41e807e36999e4d0d2072e41a82bc45655ff2Erik * 32ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell * To use this class: 33ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell * <ul> 34ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell * <li>Create an instance of the {@code ScaleGestureDetector} for your 35ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell * {@link View} 36ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell * <li>In the {@link View#onTouchEvent(MotionEvent)} method ensure you call 37ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell * {@link #onTouchEvent(MotionEvent)}. The methods defined in your 38ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell * callback will be executed when the events occur. 39ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell * </ul> 40ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell */ 41ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powellpublic class ScaleGestureDetector { 420818020d7cb04d83d51b71b8262d34bd79a76a95Adam Powell private static final String TAG = "ScaleGestureDetector"; 430818020d7cb04d83d51b71b8262d34bd79a76a95Adam Powell 44ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell /** 45ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell * The listener for receiving notifications when gestures occur. 46ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell * If you want to listen for all the different gestures then implement 47ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell * this interface. If you only want to listen for a subset it might 48ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell * be easier to extend {@link SimpleOnScaleGestureListener}. 4947c41e807e36999e4d0d2072e41a82bc45655ff2Erik * 50ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell * An application will receive events in the following order: 51ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell * <ul> 52ab905c87b7324d15715b78eaae7ef8558ad3bd10Adam Powell * <li>One {@link OnScaleGestureListener#onScaleBegin(ScaleGestureDetector)} 53ab905c87b7324d15715b78eaae7ef8558ad3bd10Adam Powell * <li>Zero or more {@link OnScaleGestureListener#onScale(ScaleGestureDetector)} 54ab905c87b7324d15715b78eaae7ef8558ad3bd10Adam Powell * <li>One {@link OnScaleGestureListener#onScaleEnd(ScaleGestureDetector)} 55ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell * </ul> 56ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell */ 57ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell public interface OnScaleGestureListener { 58ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell /** 59ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell * Responds to scaling events for a gesture in progress. 60ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell * Reported by pointer motion. 6147c41e807e36999e4d0d2072e41a82bc45655ff2Erik * 62ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell * @param detector The detector reporting the event - use this to 63ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell * retrieve extended info about event state. 64ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell * @return Whether or not the detector should consider this event 65ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell * as handled. If an event was not handled, the detector 66ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell * will continue to accumulate movement until an event is 67ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell * handled. This can be useful if an application, for example, 68ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell * only wants to update scaling factors if the change is 69ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell * greater than 0.01. 70ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell */ 71ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell public boolean onScale(ScaleGestureDetector detector); 72ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell 73ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell /** 74ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell * Responds to the beginning of a scaling gesture. Reported by 75ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell * new pointers going down. 7647c41e807e36999e4d0d2072e41a82bc45655ff2Erik * 77ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell * @param detector The detector reporting the event - use this to 78ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell * retrieve extended info about event state. 79ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell * @return Whether or not the detector should continue recognizing 80ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell * this gesture. For example, if a gesture is beginning 81ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell * with a focal point outside of a region where it makes 82ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell * sense, onScaleBegin() may return false to ignore the 83ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell * rest of the gesture. 84ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell */ 85ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell public boolean onScaleBegin(ScaleGestureDetector detector); 86ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell 87ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell /** 88ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell * Responds to the end of a scale gesture. Reported by existing 89216bccf804db9c972b317620a27de6a8adf7fbfeAdam Powell * pointers going up. 9047c41e807e36999e4d0d2072e41a82bc45655ff2Erik * 91ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell * Once a scale has ended, {@link ScaleGestureDetector#getFocusX()} 9247ec2fb37046797bebc82b505a13c552021b9ff3Adam Powell * and {@link ScaleGestureDetector#getFocusY()} will return focal point 9347ec2fb37046797bebc82b505a13c552021b9ff3Adam Powell * of the pointers remaining on the screen. 9447c41e807e36999e4d0d2072e41a82bc45655ff2Erik * 95ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell * @param detector The detector reporting the event - use this to 96ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell * retrieve extended info about event state. 97ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell */ 98ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell public void onScaleEnd(ScaleGestureDetector detector); 99ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell } 10047c41e807e36999e4d0d2072e41a82bc45655ff2Erik 101ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell /** 102ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell * A convenience class to extend when you only want to listen for a subset 103ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell * of scaling-related events. This implements all methods in 104ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell * {@link OnScaleGestureListener} but does nothing. 105346c8fb036b99a33756c66dcebeb5d0f67a1df72Adam Powell * {@link OnScaleGestureListener#onScale(ScaleGestureDetector)} returns 106346c8fb036b99a33756c66dcebeb5d0f67a1df72Adam Powell * {@code false} so that a subclass can retrieve the accumulated scale 107346c8fb036b99a33756c66dcebeb5d0f67a1df72Adam Powell * factor in an overridden onScaleEnd. 108346c8fb036b99a33756c66dcebeb5d0f67a1df72Adam Powell * {@link OnScaleGestureListener#onScaleBegin(ScaleGestureDetector)} returns 10947c41e807e36999e4d0d2072e41a82bc45655ff2Erik * {@code true}. 110ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell */ 111216bccf804db9c972b317620a27de6a8adf7fbfeAdam Powell public static class SimpleOnScaleGestureListener implements OnScaleGestureListener { 112ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell 113ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell public boolean onScale(ScaleGestureDetector detector) { 114346c8fb036b99a33756c66dcebeb5d0f67a1df72Adam Powell return false; 115ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell } 116ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell 117ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell public boolean onScaleBegin(ScaleGestureDetector detector) { 118ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell return true; 119ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell } 120ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell 121ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell public void onScaleEnd(ScaleGestureDetector detector) { 122ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell // Intentionally empty 123ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell } 124ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell } 125ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell 126346c8fb036b99a33756c66dcebeb5d0f67a1df72Adam Powell private final Context mContext; 127346c8fb036b99a33756c66dcebeb5d0f67a1df72Adam Powell private final OnScaleGestureListener mListener; 128ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell 129ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell private float mFocusX; 130ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell private float mFocusY; 131618cbea4e746196cbde43746706bec02e14b487bAdam Powell 132618cbea4e746196cbde43746706bec02e14b487bAdam Powell private float mCurrSpan; 133618cbea4e746196cbde43746706bec02e14b487bAdam Powell private float mPrevSpan; 13447ec2fb37046797bebc82b505a13c552021b9ff3Adam Powell private float mInitialSpan; 135618cbea4e746196cbde43746706bec02e14b487bAdam Powell private float mCurrSpanX; 136618cbea4e746196cbde43746706bec02e14b487bAdam Powell private float mCurrSpanY; 137618cbea4e746196cbde43746706bec02e14b487bAdam Powell private float mPrevSpanX; 138618cbea4e746196cbde43746706bec02e14b487bAdam Powell private float mPrevSpanY; 139618cbea4e746196cbde43746706bec02e14b487bAdam Powell private long mCurrTime; 140618cbea4e746196cbde43746706bec02e14b487bAdam Powell private long mPrevTime; 141618cbea4e746196cbde43746706bec02e14b487bAdam Powell private boolean mInProgress; 14247ec2fb37046797bebc82b505a13c552021b9ff3Adam Powell private int mSpanSlop; 143828e56ea6e8f4d5aa650052580f1f7873ad8296dAdam Powell private int mMinSpan; 144e33cef8037cb87386e17bcf8701a47452d262fa6Adam Powell 145a4ce6ae0d379c8866697d064cfd1661ea156405eAdam Powell private float[] mTouchHistoryLastAccepted; 146a4ce6ae0d379c8866697d064cfd1661ea156405eAdam Powell private int[] mTouchHistoryDirection; 147a4ce6ae0d379c8866697d064cfd1661ea156405eAdam Powell private long[] mTouchHistoryLastAcceptedTime; 148a4ce6ae0d379c8866697d064cfd1661ea156405eAdam Powell 149d736d2069b94348b519089348f4a85eb482db668Adam Powell private static final long TOUCH_STABILIZE_TIME = 128; // ms 150a4ce6ae0d379c8866697d064cfd1661ea156405eAdam Powell 15121bc5c917d4ee2a9b2b8173091e6bba85eaff899Jeff Brown /** 15221bc5c917d4ee2a9b2b8173091e6bba85eaff899Jeff Brown * Consistency verifier for debugging purposes. 15321bc5c917d4ee2a9b2b8173091e6bba85eaff899Jeff Brown */ 15421bc5c917d4ee2a9b2b8173091e6bba85eaff899Jeff Brown private final InputEventConsistencyVerifier mInputEventConsistencyVerifier = 15521bc5c917d4ee2a9b2b8173091e6bba85eaff899Jeff Brown InputEventConsistencyVerifier.isInstrumentationEnabled() ? 15621bc5c917d4ee2a9b2b8173091e6bba85eaff899Jeff Brown new InputEventConsistencyVerifier(this, 0) : null; 15721bc5c917d4ee2a9b2b8173091e6bba85eaff899Jeff Brown 158ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell public ScaleGestureDetector(Context context, OnScaleGestureListener listener) { 159ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell mContext = context; 160ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell mListener = listener; 16147ec2fb37046797bebc82b505a13c552021b9ff3Adam Powell mSpanSlop = ViewConfiguration.get(context).getScaledTouchSlop() * 2; 162828e56ea6e8f4d5aa650052580f1f7873ad8296dAdam Powell mMinSpan = context.getResources().getDimensionPixelSize( 163828e56ea6e8f4d5aa650052580f1f7873ad8296dAdam Powell com.android.internal.R.dimen.config_minScalingSpan); 164ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell } 165ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell 166618cbea4e746196cbde43746706bec02e14b487bAdam Powell /** 167a4ce6ae0d379c8866697d064cfd1661ea156405eAdam Powell * The touchMajor/touchMinor elements of a MotionEvent can flutter/jitter on 168a4ce6ae0d379c8866697d064cfd1661ea156405eAdam Powell * some hardware/driver combos. Smooth it out to get kinder, gentler behavior. 169a4ce6ae0d379c8866697d064cfd1661ea156405eAdam Powell * @param ev MotionEvent to add to the ongoing history 170a4ce6ae0d379c8866697d064cfd1661ea156405eAdam Powell */ 171a4ce6ae0d379c8866697d064cfd1661ea156405eAdam Powell private void addTouchHistory(MotionEvent ev) { 172a4ce6ae0d379c8866697d064cfd1661ea156405eAdam Powell final long currentTime = SystemClock.uptimeMillis(); 173a4ce6ae0d379c8866697d064cfd1661ea156405eAdam Powell final int count = ev.getPointerCount(); 174a4ce6ae0d379c8866697d064cfd1661ea156405eAdam Powell for (int i = 0; i < count; i++) { 175a4ce6ae0d379c8866697d064cfd1661ea156405eAdam Powell final int id = ev.getPointerId(i); 176a4ce6ae0d379c8866697d064cfd1661ea156405eAdam Powell ensureTouchHistorySize(id); 177a4ce6ae0d379c8866697d064cfd1661ea156405eAdam Powell 178a4ce6ae0d379c8866697d064cfd1661ea156405eAdam Powell final boolean hasLastAccepted = !Float.isNaN(mTouchHistoryLastAccepted[id]); 179a4ce6ae0d379c8866697d064cfd1661ea156405eAdam Powell boolean accept = true; 180a4ce6ae0d379c8866697d064cfd1661ea156405eAdam Powell final int historySize = ev.getHistorySize(); 181a4ce6ae0d379c8866697d064cfd1661ea156405eAdam Powell for (int h = 0; h < historySize + 1; h++) { 182a4ce6ae0d379c8866697d064cfd1661ea156405eAdam Powell final float major; 183a4ce6ae0d379c8866697d064cfd1661ea156405eAdam Powell final float minor; 184a4ce6ae0d379c8866697d064cfd1661ea156405eAdam Powell if (h < historySize) { 185a4ce6ae0d379c8866697d064cfd1661ea156405eAdam Powell major = ev.getHistoricalTouchMajor(i, h); 186a4ce6ae0d379c8866697d064cfd1661ea156405eAdam Powell minor = ev.getHistoricalTouchMinor(i, h); 187a4ce6ae0d379c8866697d064cfd1661ea156405eAdam Powell } else { 188a4ce6ae0d379c8866697d064cfd1661ea156405eAdam Powell major = ev.getTouchMajor(i); 189a4ce6ae0d379c8866697d064cfd1661ea156405eAdam Powell minor = ev.getTouchMinor(i); 190a4ce6ae0d379c8866697d064cfd1661ea156405eAdam Powell } 191a4ce6ae0d379c8866697d064cfd1661ea156405eAdam Powell final float avg = (major + minor) / 2; 192a4ce6ae0d379c8866697d064cfd1661ea156405eAdam Powell 193a4ce6ae0d379c8866697d064cfd1661ea156405eAdam Powell if (hasLastAccepted) { 194a4ce6ae0d379c8866697d064cfd1661ea156405eAdam Powell final int directionSig = (int) Math.signum(avg - mTouchHistoryLastAccepted[id]); 195d736d2069b94348b519089348f4a85eb482db668Adam Powell if (directionSig != mTouchHistoryDirection[id] || 196d736d2069b94348b519089348f4a85eb482db668Adam Powell (directionSig == 0 && mTouchHistoryDirection[id] == 0)) { 197a4ce6ae0d379c8866697d064cfd1661ea156405eAdam Powell mTouchHistoryDirection[id] = directionSig; 198a4ce6ae0d379c8866697d064cfd1661ea156405eAdam Powell final long time = h < historySize ? ev.getHistoricalEventTime(h) 199a4ce6ae0d379c8866697d064cfd1661ea156405eAdam Powell : ev.getEventTime(); 200a4ce6ae0d379c8866697d064cfd1661ea156405eAdam Powell mTouchHistoryLastAcceptedTime[id] = time; 201a4ce6ae0d379c8866697d064cfd1661ea156405eAdam Powell accept = false; 202a4ce6ae0d379c8866697d064cfd1661ea156405eAdam Powell } 203a4ce6ae0d379c8866697d064cfd1661ea156405eAdam Powell if (currentTime - mTouchHistoryLastAcceptedTime[id] < TOUCH_STABILIZE_TIME) { 204a4ce6ae0d379c8866697d064cfd1661ea156405eAdam Powell accept = false; 205a4ce6ae0d379c8866697d064cfd1661ea156405eAdam Powell } 206a4ce6ae0d379c8866697d064cfd1661ea156405eAdam Powell } 207a4ce6ae0d379c8866697d064cfd1661ea156405eAdam Powell } 208a4ce6ae0d379c8866697d064cfd1661ea156405eAdam Powell 209a4ce6ae0d379c8866697d064cfd1661ea156405eAdam Powell if (accept) { 210a4ce6ae0d379c8866697d064cfd1661ea156405eAdam Powell float newAccepted = (ev.getTouchMajor(i) + ev.getTouchMinor(i)) / 2; 211a4ce6ae0d379c8866697d064cfd1661ea156405eAdam Powell if (hasLastAccepted) { 212a4ce6ae0d379c8866697d064cfd1661ea156405eAdam Powell newAccepted = (mTouchHistoryLastAccepted[id] + newAccepted) / 2; 213a4ce6ae0d379c8866697d064cfd1661ea156405eAdam Powell } 214a4ce6ae0d379c8866697d064cfd1661ea156405eAdam Powell mTouchHistoryLastAccepted[id] = newAccepted; 215a4ce6ae0d379c8866697d064cfd1661ea156405eAdam Powell mTouchHistoryDirection[id] = 0; 216a4ce6ae0d379c8866697d064cfd1661ea156405eAdam Powell mTouchHistoryLastAcceptedTime[id] = ev.getEventTime(); 217a4ce6ae0d379c8866697d064cfd1661ea156405eAdam Powell } 218a4ce6ae0d379c8866697d064cfd1661ea156405eAdam Powell } 219a4ce6ae0d379c8866697d064cfd1661ea156405eAdam Powell } 220a4ce6ae0d379c8866697d064cfd1661ea156405eAdam Powell 221a4ce6ae0d379c8866697d064cfd1661ea156405eAdam Powell /** 222a4ce6ae0d379c8866697d064cfd1661ea156405eAdam Powell * Clear out the touch history for a given pointer id. 223a4ce6ae0d379c8866697d064cfd1661ea156405eAdam Powell * @param id pointer id to clear 224a4ce6ae0d379c8866697d064cfd1661ea156405eAdam Powell * @see #addTouchHistory(MotionEvent) 225a4ce6ae0d379c8866697d064cfd1661ea156405eAdam Powell */ 226a4ce6ae0d379c8866697d064cfd1661ea156405eAdam Powell private void removeTouchHistoryForId(int id) { 227a4ce6ae0d379c8866697d064cfd1661ea156405eAdam Powell mTouchHistoryLastAccepted[id] = Float.NaN; 228a4ce6ae0d379c8866697d064cfd1661ea156405eAdam Powell mTouchHistoryDirection[id] = 0; 229a4ce6ae0d379c8866697d064cfd1661ea156405eAdam Powell mTouchHistoryLastAcceptedTime[id] = 0; 230a4ce6ae0d379c8866697d064cfd1661ea156405eAdam Powell } 231a4ce6ae0d379c8866697d064cfd1661ea156405eAdam Powell 232a4ce6ae0d379c8866697d064cfd1661ea156405eAdam Powell /** 233a4ce6ae0d379c8866697d064cfd1661ea156405eAdam Powell * Get the adjusted combined touchMajor/touchMinor value for a given pointer id 234a4ce6ae0d379c8866697d064cfd1661ea156405eAdam Powell * @param id the pointer id of the data to obtain 235a4ce6ae0d379c8866697d064cfd1661ea156405eAdam Powell * @return the adjusted major/minor value for the point at id 236a4ce6ae0d379c8866697d064cfd1661ea156405eAdam Powell * @see #addTouchHistory(MotionEvent) 237a4ce6ae0d379c8866697d064cfd1661ea156405eAdam Powell */ 238a4ce6ae0d379c8866697d064cfd1661ea156405eAdam Powell private float getAdjustedTouchHistory(int id) { 239a4ce6ae0d379c8866697d064cfd1661ea156405eAdam Powell return mTouchHistoryLastAccepted[id]; 240a4ce6ae0d379c8866697d064cfd1661ea156405eAdam Powell } 241a4ce6ae0d379c8866697d064cfd1661ea156405eAdam Powell 242a4ce6ae0d379c8866697d064cfd1661ea156405eAdam Powell /** 243a4ce6ae0d379c8866697d064cfd1661ea156405eAdam Powell * Clear all touch history tracking. Useful in ACTION_CANCEL or ACTION_UP. 244a4ce6ae0d379c8866697d064cfd1661ea156405eAdam Powell * @see #addTouchHistory(MotionEvent) 245a4ce6ae0d379c8866697d064cfd1661ea156405eAdam Powell */ 246a4ce6ae0d379c8866697d064cfd1661ea156405eAdam Powell private void clearTouchHistory() { 247a4ce6ae0d379c8866697d064cfd1661ea156405eAdam Powell Arrays.fill(mTouchHistoryLastAccepted, Float.NaN); 248a4ce6ae0d379c8866697d064cfd1661ea156405eAdam Powell Arrays.fill(mTouchHistoryDirection, 0); 249a4ce6ae0d379c8866697d064cfd1661ea156405eAdam Powell Arrays.fill(mTouchHistoryLastAcceptedTime, 0); 250a4ce6ae0d379c8866697d064cfd1661ea156405eAdam Powell } 251a4ce6ae0d379c8866697d064cfd1661ea156405eAdam Powell 252a4ce6ae0d379c8866697d064cfd1661ea156405eAdam Powell private void ensureTouchHistorySize(int id) { 253a4ce6ae0d379c8866697d064cfd1661ea156405eAdam Powell final int requiredSize = id + 1; 254a4ce6ae0d379c8866697d064cfd1661ea156405eAdam Powell if (mTouchHistoryLastAccepted == null || mTouchHistoryLastAccepted.length < requiredSize) { 255a4ce6ae0d379c8866697d064cfd1661ea156405eAdam Powell final float[] newLastAccepted = new float[requiredSize]; 256a4ce6ae0d379c8866697d064cfd1661ea156405eAdam Powell final int[] newDirection = new int[requiredSize]; 257a4ce6ae0d379c8866697d064cfd1661ea156405eAdam Powell final long[] newLastAcceptedTime = new long[requiredSize]; 258a4ce6ae0d379c8866697d064cfd1661ea156405eAdam Powell 259a4ce6ae0d379c8866697d064cfd1661ea156405eAdam Powell int oldLength = 0; 260a4ce6ae0d379c8866697d064cfd1661ea156405eAdam Powell if (mTouchHistoryLastAccepted != null) { 261a4ce6ae0d379c8866697d064cfd1661ea156405eAdam Powell System.arraycopy(mTouchHistoryLastAccepted, 0, newLastAccepted, 0, 262a4ce6ae0d379c8866697d064cfd1661ea156405eAdam Powell mTouchHistoryLastAccepted.length); 263a4ce6ae0d379c8866697d064cfd1661ea156405eAdam Powell System.arraycopy(mTouchHistoryDirection, 0, newDirection, 0, 264a4ce6ae0d379c8866697d064cfd1661ea156405eAdam Powell mTouchHistoryDirection.length); 265a4ce6ae0d379c8866697d064cfd1661ea156405eAdam Powell System.arraycopy(mTouchHistoryLastAcceptedTime, 0, newLastAcceptedTime, 0, 266a4ce6ae0d379c8866697d064cfd1661ea156405eAdam Powell mTouchHistoryLastAcceptedTime.length); 267a4ce6ae0d379c8866697d064cfd1661ea156405eAdam Powell oldLength = mTouchHistoryLastAccepted.length; 268a4ce6ae0d379c8866697d064cfd1661ea156405eAdam Powell } 269a4ce6ae0d379c8866697d064cfd1661ea156405eAdam Powell 270a4ce6ae0d379c8866697d064cfd1661ea156405eAdam Powell Arrays.fill(newLastAccepted, oldLength, newLastAccepted.length, Float.NaN); 271a4ce6ae0d379c8866697d064cfd1661ea156405eAdam Powell Arrays.fill(newDirection, oldLength, newDirection.length, 0); 272a4ce6ae0d379c8866697d064cfd1661ea156405eAdam Powell Arrays.fill(newLastAcceptedTime, oldLength, newLastAcceptedTime.length, 0); 273a4ce6ae0d379c8866697d064cfd1661ea156405eAdam Powell 274a4ce6ae0d379c8866697d064cfd1661ea156405eAdam Powell mTouchHistoryLastAccepted = newLastAccepted; 275a4ce6ae0d379c8866697d064cfd1661ea156405eAdam Powell mTouchHistoryDirection = newDirection; 276a4ce6ae0d379c8866697d064cfd1661ea156405eAdam Powell mTouchHistoryLastAcceptedTime = newLastAcceptedTime; 277a4ce6ae0d379c8866697d064cfd1661ea156405eAdam Powell } 278a4ce6ae0d379c8866697d064cfd1661ea156405eAdam Powell } 279a4ce6ae0d379c8866697d064cfd1661ea156405eAdam Powell 280a4ce6ae0d379c8866697d064cfd1661ea156405eAdam Powell /** 281618cbea4e746196cbde43746706bec02e14b487bAdam Powell * Accepts MotionEvents and dispatches events to a {@link OnScaleGestureListener} 282618cbea4e746196cbde43746706bec02e14b487bAdam Powell * when appropriate. 283618cbea4e746196cbde43746706bec02e14b487bAdam Powell * 284618cbea4e746196cbde43746706bec02e14b487bAdam Powell * <p>Applications should pass a complete and consistent event stream to this method. 285618cbea4e746196cbde43746706bec02e14b487bAdam Powell * A complete and consistent event stream involves all MotionEvents from the initial 286618cbea4e746196cbde43746706bec02e14b487bAdam Powell * ACTION_DOWN to the final ACTION_UP or ACTION_CANCEL.</p> 287618cbea4e746196cbde43746706bec02e14b487bAdam Powell * 288618cbea4e746196cbde43746706bec02e14b487bAdam Powell * @param event The event to process 289618cbea4e746196cbde43746706bec02e14b487bAdam Powell * @return true if the event was processed and the detector wants to receive the 290618cbea4e746196cbde43746706bec02e14b487bAdam Powell * rest of the MotionEvents in this event stream. 291618cbea4e746196cbde43746706bec02e14b487bAdam Powell */ 292ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell public boolean onTouchEvent(MotionEvent event) { 29321bc5c917d4ee2a9b2b8173091e6bba85eaff899Jeff Brown if (mInputEventConsistencyVerifier != null) { 29421bc5c917d4ee2a9b2b8173091e6bba85eaff899Jeff Brown mInputEventConsistencyVerifier.onTouchEvent(event, 0); 29521bc5c917d4ee2a9b2b8173091e6bba85eaff899Jeff Brown } 29621bc5c917d4ee2a9b2b8173091e6bba85eaff899Jeff Brown 297e33cef8037cb87386e17bcf8701a47452d262fa6Adam Powell final int action = event.getActionMasked(); 298ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell 299618cbea4e746196cbde43746706bec02e14b487bAdam Powell final boolean streamComplete = action == MotionEvent.ACTION_UP || 300618cbea4e746196cbde43746706bec02e14b487bAdam Powell action == MotionEvent.ACTION_CANCEL; 301618cbea4e746196cbde43746706bec02e14b487bAdam Powell if (action == MotionEvent.ACTION_DOWN || streamComplete) { 302618cbea4e746196cbde43746706bec02e14b487bAdam Powell // Reset any scale in progress with the listener. 303618cbea4e746196cbde43746706bec02e14b487bAdam Powell // If it's an ACTION_DOWN we're beginning a new event stream. 304618cbea4e746196cbde43746706bec02e14b487bAdam Powell // This means the app probably didn't give us all the events. Shame on it. 305618cbea4e746196cbde43746706bec02e14b487bAdam Powell if (mInProgress) { 306618cbea4e746196cbde43746706bec02e14b487bAdam Powell mListener.onScaleEnd(this); 307618cbea4e746196cbde43746706bec02e14b487bAdam Powell mInProgress = false; 30847ec2fb37046797bebc82b505a13c552021b9ff3Adam Powell mInitialSpan = 0; 309ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell } 310618cbea4e746196cbde43746706bec02e14b487bAdam Powell 311618cbea4e746196cbde43746706bec02e14b487bAdam Powell if (streamComplete) { 312a4ce6ae0d379c8866697d064cfd1661ea156405eAdam Powell clearTouchHistory(); 313618cbea4e746196cbde43746706bec02e14b487bAdam Powell return true; 314ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell } 315ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell } 316bbdc50b102faf52768ac3028bc49e027ff140656Jeff Brown 317abde042a824c3fc2f3537ef136017dfa006258b9Adam Powell final boolean configChanged = action == MotionEvent.ACTION_DOWN || 318618cbea4e746196cbde43746706bec02e14b487bAdam Powell action == MotionEvent.ACTION_POINTER_UP || 319618cbea4e746196cbde43746706bec02e14b487bAdam Powell action == MotionEvent.ACTION_POINTER_DOWN; 320618cbea4e746196cbde43746706bec02e14b487bAdam Powell final boolean pointerUp = action == MotionEvent.ACTION_POINTER_UP; 321618cbea4e746196cbde43746706bec02e14b487bAdam Powell final int skipIndex = pointerUp ? event.getActionIndex() : -1; 322618cbea4e746196cbde43746706bec02e14b487bAdam Powell 323618cbea4e746196cbde43746706bec02e14b487bAdam Powell // Determine focal point 324618cbea4e746196cbde43746706bec02e14b487bAdam Powell float sumX = 0, sumY = 0; 325618cbea4e746196cbde43746706bec02e14b487bAdam Powell final int count = event.getPointerCount(); 326618cbea4e746196cbde43746706bec02e14b487bAdam Powell for (int i = 0; i < count; i++) { 327618cbea4e746196cbde43746706bec02e14b487bAdam Powell if (skipIndex == i) continue; 328618cbea4e746196cbde43746706bec02e14b487bAdam Powell sumX += event.getX(i); 329618cbea4e746196cbde43746706bec02e14b487bAdam Powell sumY += event.getY(i); 330bbdc50b102faf52768ac3028bc49e027ff140656Jeff Brown } 331618cbea4e746196cbde43746706bec02e14b487bAdam Powell final int div = pointerUp ? count - 1 : count; 332618cbea4e746196cbde43746706bec02e14b487bAdam Powell final float focusX = sumX / div; 333618cbea4e746196cbde43746706bec02e14b487bAdam Powell final float focusY = sumY / div; 334618cbea4e746196cbde43746706bec02e14b487bAdam Powell 335a4ce6ae0d379c8866697d064cfd1661ea156405eAdam Powell if (pointerUp) { 336a4ce6ae0d379c8866697d064cfd1661ea156405eAdam Powell removeTouchHistoryForId(event.getPointerId(event.getActionIndex())); 337a4ce6ae0d379c8866697d064cfd1661ea156405eAdam Powell } else { 338a4ce6ae0d379c8866697d064cfd1661ea156405eAdam Powell addTouchHistory(event); 339a4ce6ae0d379c8866697d064cfd1661ea156405eAdam Powell } 340a4ce6ae0d379c8866697d064cfd1661ea156405eAdam Powell 341618cbea4e746196cbde43746706bec02e14b487bAdam Powell // Determine average deviation from focal point 342618cbea4e746196cbde43746706bec02e14b487bAdam Powell float devSumX = 0, devSumY = 0; 343618cbea4e746196cbde43746706bec02e14b487bAdam Powell for (int i = 0; i < count; i++) { 344618cbea4e746196cbde43746706bec02e14b487bAdam Powell if (skipIndex == i) continue; 345828e56ea6e8f4d5aa650052580f1f7873ad8296dAdam Powell 346b1861c3e89c3e869c95c5c01b78320a1dcef26adAdam Powell // Average touch major and touch minor and convert the resulting diameter into a radius. 347abde042a824c3fc2f3537ef136017dfa006258b9Adam Powell final float touchSize = getAdjustedTouchHistory(event.getPointerId(i)) / 2; 348828e56ea6e8f4d5aa650052580f1f7873ad8296dAdam Powell devSumX += Math.abs(event.getX(i) - focusX) + touchSize; 349828e56ea6e8f4d5aa650052580f1f7873ad8296dAdam Powell devSumY += Math.abs(event.getY(i) - focusY) + touchSize; 350e33cef8037cb87386e17bcf8701a47452d262fa6Adam Powell } 351618cbea4e746196cbde43746706bec02e14b487bAdam Powell final float devX = devSumX / div; 352618cbea4e746196cbde43746706bec02e14b487bAdam Powell final float devY = devSumY / div; 353618cbea4e746196cbde43746706bec02e14b487bAdam Powell 354618cbea4e746196cbde43746706bec02e14b487bAdam Powell // Span is the average distance between touch points through the focal point; 355618cbea4e746196cbde43746706bec02e14b487bAdam Powell // i.e. the diameter of the circle with a radius of the average deviation from 356618cbea4e746196cbde43746706bec02e14b487bAdam Powell // the focal point. 357618cbea4e746196cbde43746706bec02e14b487bAdam Powell final float spanX = devX * 2; 358618cbea4e746196cbde43746706bec02e14b487bAdam Powell final float spanY = devY * 2; 359618cbea4e746196cbde43746706bec02e14b487bAdam Powell final float span = FloatMath.sqrt(spanX * spanX + spanY * spanY); 360618cbea4e746196cbde43746706bec02e14b487bAdam Powell 361618cbea4e746196cbde43746706bec02e14b487bAdam Powell // Dispatch begin/end events as needed. 362618cbea4e746196cbde43746706bec02e14b487bAdam Powell // If the configuration changes, notify the app to reset its current state by beginning 363618cbea4e746196cbde43746706bec02e14b487bAdam Powell // a fresh scale event stream. 36447ec2fb37046797bebc82b505a13c552021b9ff3Adam Powell final boolean wasInProgress = mInProgress; 36547ec2fb37046797bebc82b505a13c552021b9ff3Adam Powell mFocusX = focusX; 36647ec2fb37046797bebc82b505a13c552021b9ff3Adam Powell mFocusY = focusY; 367828e56ea6e8f4d5aa650052580f1f7873ad8296dAdam Powell if (mInProgress && (span < mMinSpan || configChanged)) { 368618cbea4e746196cbde43746706bec02e14b487bAdam Powell mListener.onScaleEnd(this); 369618cbea4e746196cbde43746706bec02e14b487bAdam Powell mInProgress = false; 37047ec2fb37046797bebc82b505a13c552021b9ff3Adam Powell mInitialSpan = span; 371618cbea4e746196cbde43746706bec02e14b487bAdam Powell } 372618cbea4e746196cbde43746706bec02e14b487bAdam Powell if (configChanged) { 373618cbea4e746196cbde43746706bec02e14b487bAdam Powell mPrevSpanX = mCurrSpanX = spanX; 374618cbea4e746196cbde43746706bec02e14b487bAdam Powell mPrevSpanY = mCurrSpanY = spanY; 37547ec2fb37046797bebc82b505a13c552021b9ff3Adam Powell mInitialSpan = mPrevSpan = mCurrSpan = span; 376618cbea4e746196cbde43746706bec02e14b487bAdam Powell } 377b1861c3e89c3e869c95c5c01b78320a1dcef26adAdam Powell if (!mInProgress && span >= mMinSpan && 37847ec2fb37046797bebc82b505a13c552021b9ff3Adam Powell (wasInProgress || Math.abs(span - mInitialSpan) > mSpanSlop)) { 37947ec2fb37046797bebc82b505a13c552021b9ff3Adam Powell mPrevSpanX = mCurrSpanX = spanX; 38047ec2fb37046797bebc82b505a13c552021b9ff3Adam Powell mPrevSpanY = mCurrSpanY = spanY; 38147ec2fb37046797bebc82b505a13c552021b9ff3Adam Powell mPrevSpan = mCurrSpan = span; 382618cbea4e746196cbde43746706bec02e14b487bAdam Powell mInProgress = mListener.onScaleBegin(this); 383ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell } 384ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell 385618cbea4e746196cbde43746706bec02e14b487bAdam Powell // Handle motion; focal point and span/scale factor are changing. 386618cbea4e746196cbde43746706bec02e14b487bAdam Powell if (action == MotionEvent.ACTION_MOVE) { 387618cbea4e746196cbde43746706bec02e14b487bAdam Powell mCurrSpanX = spanX; 388618cbea4e746196cbde43746706bec02e14b487bAdam Powell mCurrSpanY = spanY; 389618cbea4e746196cbde43746706bec02e14b487bAdam Powell mCurrSpan = span; 390618cbea4e746196cbde43746706bec02e14b487bAdam Powell 391618cbea4e746196cbde43746706bec02e14b487bAdam Powell boolean updatePrev = true; 392618cbea4e746196cbde43746706bec02e14b487bAdam Powell if (mInProgress) { 393618cbea4e746196cbde43746706bec02e14b487bAdam Powell updatePrev = mListener.onScale(this); 394618cbea4e746196cbde43746706bec02e14b487bAdam Powell } 395e33cef8037cb87386e17bcf8701a47452d262fa6Adam Powell 396618cbea4e746196cbde43746706bec02e14b487bAdam Powell if (updatePrev) { 397618cbea4e746196cbde43746706bec02e14b487bAdam Powell mPrevSpanX = mCurrSpanX; 398618cbea4e746196cbde43746706bec02e14b487bAdam Powell mPrevSpanY = mCurrSpanY; 399618cbea4e746196cbde43746706bec02e14b487bAdam Powell mPrevSpan = mCurrSpan; 400d0197f3669efda060c7ee2069ff41bd970fd6d9cAdam Powell } 401d0197f3669efda060c7ee2069ff41bd970fd6d9cAdam Powell } 402d0197f3669efda060c7ee2069ff41bd970fd6d9cAdam Powell 403618cbea4e746196cbde43746706bec02e14b487bAdam Powell return true; 404ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell } 405ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell 406ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell /** 407618cbea4e746196cbde43746706bec02e14b487bAdam Powell * Returns {@code true} if a scale gesture is in progress. 408ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell */ 409ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell public boolean isInProgress() { 410618cbea4e746196cbde43746706bec02e14b487bAdam Powell return mInProgress; 411ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell } 412ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell 413ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell /** 414ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell * Get the X coordinate of the current gesture's focal point. 415618cbea4e746196cbde43746706bec02e14b487bAdam Powell * If a gesture is in progress, the focal point is between 416618cbea4e746196cbde43746706bec02e14b487bAdam Powell * each of the pointers forming the gesture. 417618cbea4e746196cbde43746706bec02e14b487bAdam Powell * 418ab905c87b7324d15715b78eaae7ef8558ad3bd10Adam Powell * If {@link #isInProgress()} would return false, the result of this 419ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell * function is undefined. 42047c41e807e36999e4d0d2072e41a82bc45655ff2Erik * 421ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell * @return X coordinate of the focal point in pixels. 422ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell */ 423ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell public float getFocusX() { 424ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell return mFocusX; 425ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell } 426ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell 427ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell /** 428ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell * Get the Y coordinate of the current gesture's focal point. 429618cbea4e746196cbde43746706bec02e14b487bAdam Powell * If a gesture is in progress, the focal point is between 430618cbea4e746196cbde43746706bec02e14b487bAdam Powell * each of the pointers forming the gesture. 431618cbea4e746196cbde43746706bec02e14b487bAdam Powell * 432ab905c87b7324d15715b78eaae7ef8558ad3bd10Adam Powell * If {@link #isInProgress()} would return false, the result of this 433ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell * function is undefined. 43447c41e807e36999e4d0d2072e41a82bc45655ff2Erik * 435ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell * @return Y coordinate of the focal point in pixels. 436ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell */ 437ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell public float getFocusY() { 438ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell return mFocusY; 439ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell } 440ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell 441ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell /** 442618cbea4e746196cbde43746706bec02e14b487bAdam Powell * Return the average distance between each of the pointers forming the 443618cbea4e746196cbde43746706bec02e14b487bAdam Powell * gesture in progress through the focal point. 44447c41e807e36999e4d0d2072e41a82bc45655ff2Erik * 445ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell * @return Distance between pointers in pixels. 446ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell */ 447ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell public float getCurrentSpan() { 448618cbea4e746196cbde43746706bec02e14b487bAdam Powell return mCurrSpan; 449ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell } 450ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell 451ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell /** 452618cbea4e746196cbde43746706bec02e14b487bAdam Powell * Return the average X distance between each of the pointers forming the 453618cbea4e746196cbde43746706bec02e14b487bAdam Powell * gesture in progress through the focal point. 45447c41e807e36999e4d0d2072e41a82bc45655ff2Erik * 45547c41e807e36999e4d0d2072e41a82bc45655ff2Erik * @return Distance between pointers in pixels. 45647c41e807e36999e4d0d2072e41a82bc45655ff2Erik */ 45747c41e807e36999e4d0d2072e41a82bc45655ff2Erik public float getCurrentSpanX() { 458618cbea4e746196cbde43746706bec02e14b487bAdam Powell return mCurrSpanX; 45947c41e807e36999e4d0d2072e41a82bc45655ff2Erik } 46047c41e807e36999e4d0d2072e41a82bc45655ff2Erik 46147c41e807e36999e4d0d2072e41a82bc45655ff2Erik /** 462618cbea4e746196cbde43746706bec02e14b487bAdam Powell * Return the average Y distance between each of the pointers forming the 463618cbea4e746196cbde43746706bec02e14b487bAdam Powell * gesture in progress through the focal point. 46447c41e807e36999e4d0d2072e41a82bc45655ff2Erik * 46547c41e807e36999e4d0d2072e41a82bc45655ff2Erik * @return Distance between pointers in pixels. 46647c41e807e36999e4d0d2072e41a82bc45655ff2Erik */ 46747c41e807e36999e4d0d2072e41a82bc45655ff2Erik public float getCurrentSpanY() { 468618cbea4e746196cbde43746706bec02e14b487bAdam Powell return mCurrSpanY; 46947c41e807e36999e4d0d2072e41a82bc45655ff2Erik } 47047c41e807e36999e4d0d2072e41a82bc45655ff2Erik 47147c41e807e36999e4d0d2072e41a82bc45655ff2Erik /** 472618cbea4e746196cbde43746706bec02e14b487bAdam Powell * Return the previous average distance between each of the pointers forming the 473618cbea4e746196cbde43746706bec02e14b487bAdam Powell * gesture in progress through the focal point. 47447c41e807e36999e4d0d2072e41a82bc45655ff2Erik * 475ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell * @return Previous distance between pointers in pixels. 476ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell */ 477ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell public float getPreviousSpan() { 478618cbea4e746196cbde43746706bec02e14b487bAdam Powell return mPrevSpan; 479ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell } 480ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell 481ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell /** 482618cbea4e746196cbde43746706bec02e14b487bAdam Powell * Return the previous average X distance between each of the pointers forming the 483618cbea4e746196cbde43746706bec02e14b487bAdam Powell * gesture in progress through the focal point. 48447c41e807e36999e4d0d2072e41a82bc45655ff2Erik * 48547c41e807e36999e4d0d2072e41a82bc45655ff2Erik * @return Previous distance between pointers in pixels. 48647c41e807e36999e4d0d2072e41a82bc45655ff2Erik */ 48747c41e807e36999e4d0d2072e41a82bc45655ff2Erik public float getPreviousSpanX() { 488618cbea4e746196cbde43746706bec02e14b487bAdam Powell return mPrevSpanX; 48947c41e807e36999e4d0d2072e41a82bc45655ff2Erik } 49047c41e807e36999e4d0d2072e41a82bc45655ff2Erik 49147c41e807e36999e4d0d2072e41a82bc45655ff2Erik /** 492618cbea4e746196cbde43746706bec02e14b487bAdam Powell * Return the previous average Y distance between each of the pointers forming the 493618cbea4e746196cbde43746706bec02e14b487bAdam Powell * gesture in progress through the focal point. 49447c41e807e36999e4d0d2072e41a82bc45655ff2Erik * 49547c41e807e36999e4d0d2072e41a82bc45655ff2Erik * @return Previous distance between pointers in pixels. 49647c41e807e36999e4d0d2072e41a82bc45655ff2Erik */ 49747c41e807e36999e4d0d2072e41a82bc45655ff2Erik public float getPreviousSpanY() { 498618cbea4e746196cbde43746706bec02e14b487bAdam Powell return mPrevSpanY; 49947c41e807e36999e4d0d2072e41a82bc45655ff2Erik } 50047c41e807e36999e4d0d2072e41a82bc45655ff2Erik 50147c41e807e36999e4d0d2072e41a82bc45655ff2Erik /** 502ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell * Return the scaling factor from the previous scale event to the current 503ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell * event. This value is defined as 504ab905c87b7324d15715b78eaae7ef8558ad3bd10Adam Powell * ({@link #getCurrentSpan()} / {@link #getPreviousSpan()}). 50547c41e807e36999e4d0d2072e41a82bc45655ff2Erik * 506ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell * @return The current scaling factor. 507ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell */ 508ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell public float getScaleFactor() { 509618cbea4e746196cbde43746706bec02e14b487bAdam Powell return mPrevSpan > 0 ? mCurrSpan / mPrevSpan : 1; 510ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell } 51147c41e807e36999e4d0d2072e41a82bc45655ff2Erik 512ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell /** 513ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell * Return the time difference in milliseconds between the previous 514ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell * accepted scaling event and the current scaling event. 51547c41e807e36999e4d0d2072e41a82bc45655ff2Erik * 516ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell * @return Time difference since the last scaling event in milliseconds. 517ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell */ 518ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell public long getTimeDelta() { 519618cbea4e746196cbde43746706bec02e14b487bAdam Powell return mCurrTime - mPrevTime; 520ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell } 52147c41e807e36999e4d0d2072e41a82bc45655ff2Erik 522ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell /** 523ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell * Return the event time of the current event being processed. 52447c41e807e36999e4d0d2072e41a82bc45655ff2Erik * 525ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell * @return Current event time in milliseconds. 526ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell */ 527ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell public long getEventTime() { 528618cbea4e746196cbde43746706bec02e14b487bAdam Powell return mCurrTime; 529ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell } 530ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell} 531