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; 21a4ce6ae0d379c8866697d064cfd1661ea156405eAdam Powellimport android.os.SystemClock; 22346c8fb036b99a33756c66dcebeb5d0f67a1df72Adam Powellimport android.util.FloatMath; 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 131618cbea4e746196cbde43746706bec02e14b487bAdam Powell private float mCurrSpan; 132618cbea4e746196cbde43746706bec02e14b487bAdam Powell private float mPrevSpan; 13347ec2fb37046797bebc82b505a13c552021b9ff3Adam Powell private float mInitialSpan; 134618cbea4e746196cbde43746706bec02e14b487bAdam Powell private float mCurrSpanX; 135618cbea4e746196cbde43746706bec02e14b487bAdam Powell private float mCurrSpanY; 136618cbea4e746196cbde43746706bec02e14b487bAdam Powell private float mPrevSpanX; 137618cbea4e746196cbde43746706bec02e14b487bAdam Powell private float mPrevSpanY; 138618cbea4e746196cbde43746706bec02e14b487bAdam Powell private long mCurrTime; 139618cbea4e746196cbde43746706bec02e14b487bAdam Powell private long mPrevTime; 140618cbea4e746196cbde43746706bec02e14b487bAdam Powell private boolean mInProgress; 14147ec2fb37046797bebc82b505a13c552021b9ff3Adam Powell private int mSpanSlop; 142828e56ea6e8f4d5aa650052580f1f7873ad8296dAdam Powell private int mMinSpan; 143e33cef8037cb87386e17bcf8701a47452d262fa6Adam Powell 1445b5c414e31c4a8433a3290b931687a05dadc97b6Adam Powell // Bounds for recently seen values 1455b5c414e31c4a8433a3290b931687a05dadc97b6Adam Powell private float mTouchUpper; 1465b5c414e31c4a8433a3290b931687a05dadc97b6Adam Powell private float mTouchLower; 1475b5c414e31c4a8433a3290b931687a05dadc97b6Adam Powell private float mTouchHistoryLastAccepted; 1485b5c414e31c4a8433a3290b931687a05dadc97b6Adam Powell private int mTouchHistoryDirection; 1495b5c414e31c4a8433a3290b931687a05dadc97b6Adam Powell private long mTouchHistoryLastAcceptedTime; 1505b5c414e31c4a8433a3290b931687a05dadc97b6Adam Powell private int mTouchMinMajor; 151a4ce6ae0d379c8866697d064cfd1661ea156405eAdam Powell 152d736d2069b94348b519089348f4a85eb482db668Adam Powell private static final long TOUCH_STABILIZE_TIME = 128; // ms 1535b5c414e31c4a8433a3290b931687a05dadc97b6Adam Powell private static final int TOUCH_MIN_MAJOR = 48; // dp 154a4ce6ae0d379c8866697d064cfd1661ea156405eAdam Powell 15521bc5c917d4ee2a9b2b8173091e6bba85eaff899Jeff Brown /** 15621bc5c917d4ee2a9b2b8173091e6bba85eaff899Jeff Brown * Consistency verifier for debugging purposes. 15721bc5c917d4ee2a9b2b8173091e6bba85eaff899Jeff Brown */ 15821bc5c917d4ee2a9b2b8173091e6bba85eaff899Jeff Brown private final InputEventConsistencyVerifier mInputEventConsistencyVerifier = 15921bc5c917d4ee2a9b2b8173091e6bba85eaff899Jeff Brown InputEventConsistencyVerifier.isInstrumentationEnabled() ? 16021bc5c917d4ee2a9b2b8173091e6bba85eaff899Jeff Brown new InputEventConsistencyVerifier(this, 0) : null; 16121bc5c917d4ee2a9b2b8173091e6bba85eaff899Jeff Brown 162ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell public ScaleGestureDetector(Context context, OnScaleGestureListener listener) { 163ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell mContext = context; 164ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell mListener = listener; 16547ec2fb37046797bebc82b505a13c552021b9ff3Adam Powell mSpanSlop = ViewConfiguration.get(context).getScaledTouchSlop() * 2; 1663307958c6b4b3707c8861db829893b1f5820b677Adam Powell 1673307958c6b4b3707c8861db829893b1f5820b677Adam Powell final Resources res = context.getResources(); 1683307958c6b4b3707c8861db829893b1f5820b677Adam Powell mTouchMinMajor = res.getDimensionPixelSize( 1693307958c6b4b3707c8861db829893b1f5820b677Adam Powell com.android.internal.R.dimen.config_minScalingTouchMajor); 1703307958c6b4b3707c8861db829893b1f5820b677Adam Powell mMinSpan = res.getDimensionPixelSize( 171828e56ea6e8f4d5aa650052580f1f7873ad8296dAdam Powell com.android.internal.R.dimen.config_minScalingSpan); 172ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell } 173ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell 174618cbea4e746196cbde43746706bec02e14b487bAdam Powell /** 175a4ce6ae0d379c8866697d064cfd1661ea156405eAdam Powell * The touchMajor/touchMinor elements of a MotionEvent can flutter/jitter on 176a4ce6ae0d379c8866697d064cfd1661ea156405eAdam Powell * some hardware/driver combos. Smooth it out to get kinder, gentler behavior. 177a4ce6ae0d379c8866697d064cfd1661ea156405eAdam Powell * @param ev MotionEvent to add to the ongoing history 178a4ce6ae0d379c8866697d064cfd1661ea156405eAdam Powell */ 179a4ce6ae0d379c8866697d064cfd1661ea156405eAdam Powell private void addTouchHistory(MotionEvent ev) { 180a4ce6ae0d379c8866697d064cfd1661ea156405eAdam Powell final long currentTime = SystemClock.uptimeMillis(); 181a4ce6ae0d379c8866697d064cfd1661ea156405eAdam Powell final int count = ev.getPointerCount(); 1825b5c414e31c4a8433a3290b931687a05dadc97b6Adam Powell boolean accept = currentTime - mTouchHistoryLastAcceptedTime >= TOUCH_STABILIZE_TIME; 1835b5c414e31c4a8433a3290b931687a05dadc97b6Adam Powell float total = 0; 1845b5c414e31c4a8433a3290b931687a05dadc97b6Adam Powell int sampleCount = 0; 185a4ce6ae0d379c8866697d064cfd1661ea156405eAdam Powell for (int i = 0; i < count; i++) { 1865b5c414e31c4a8433a3290b931687a05dadc97b6Adam Powell final boolean hasLastAccepted = !Float.isNaN(mTouchHistoryLastAccepted); 187a4ce6ae0d379c8866697d064cfd1661ea156405eAdam Powell final int historySize = ev.getHistorySize(); 1885b5c414e31c4a8433a3290b931687a05dadc97b6Adam Powell final int pointerSampleCount = historySize + 1; 1895b5c414e31c4a8433a3290b931687a05dadc97b6Adam Powell for (int h = 0; h < pointerSampleCount; h++) { 1905b5c414e31c4a8433a3290b931687a05dadc97b6Adam Powell float major; 191a4ce6ae0d379c8866697d064cfd1661ea156405eAdam Powell if (h < historySize) { 192a4ce6ae0d379c8866697d064cfd1661ea156405eAdam Powell major = ev.getHistoricalTouchMajor(i, h); 193a4ce6ae0d379c8866697d064cfd1661ea156405eAdam Powell } else { 194a4ce6ae0d379c8866697d064cfd1661ea156405eAdam Powell major = ev.getTouchMajor(i); 195a4ce6ae0d379c8866697d064cfd1661ea156405eAdam Powell } 1965b5c414e31c4a8433a3290b931687a05dadc97b6Adam Powell if (major < mTouchMinMajor) major = mTouchMinMajor; 1975b5c414e31c4a8433a3290b931687a05dadc97b6Adam Powell total += major; 1985b5c414e31c4a8433a3290b931687a05dadc97b6Adam Powell 1995b5c414e31c4a8433a3290b931687a05dadc97b6Adam Powell if (Float.isNaN(mTouchUpper) || major > mTouchUpper) { 2005b5c414e31c4a8433a3290b931687a05dadc97b6Adam Powell mTouchUpper = major; 2015b5c414e31c4a8433a3290b931687a05dadc97b6Adam Powell } 2025b5c414e31c4a8433a3290b931687a05dadc97b6Adam Powell if (Float.isNaN(mTouchLower) || major < mTouchLower) { 2035b5c414e31c4a8433a3290b931687a05dadc97b6Adam Powell mTouchLower = major; 2045b5c414e31c4a8433a3290b931687a05dadc97b6Adam Powell } 205a4ce6ae0d379c8866697d064cfd1661ea156405eAdam Powell 206a4ce6ae0d379c8866697d064cfd1661ea156405eAdam Powell if (hasLastAccepted) { 2075b5c414e31c4a8433a3290b931687a05dadc97b6Adam Powell final int directionSig = (int) Math.signum(major - mTouchHistoryLastAccepted); 2085b5c414e31c4a8433a3290b931687a05dadc97b6Adam Powell if (directionSig != mTouchHistoryDirection || 2095b5c414e31c4a8433a3290b931687a05dadc97b6Adam Powell (directionSig == 0 && mTouchHistoryDirection == 0)) { 2105b5c414e31c4a8433a3290b931687a05dadc97b6Adam Powell mTouchHistoryDirection = directionSig; 211a4ce6ae0d379c8866697d064cfd1661ea156405eAdam Powell final long time = h < historySize ? ev.getHistoricalEventTime(h) 212a4ce6ae0d379c8866697d064cfd1661ea156405eAdam Powell : ev.getEventTime(); 2135b5c414e31c4a8433a3290b931687a05dadc97b6Adam Powell mTouchHistoryLastAcceptedTime = time; 214a4ce6ae0d379c8866697d064cfd1661ea156405eAdam Powell accept = false; 215a4ce6ae0d379c8866697d064cfd1661ea156405eAdam Powell } 216a4ce6ae0d379c8866697d064cfd1661ea156405eAdam Powell } 217a4ce6ae0d379c8866697d064cfd1661ea156405eAdam Powell } 2185b5c414e31c4a8433a3290b931687a05dadc97b6Adam Powell sampleCount += pointerSampleCount; 219a4ce6ae0d379c8866697d064cfd1661ea156405eAdam Powell } 220a4ce6ae0d379c8866697d064cfd1661ea156405eAdam Powell 2215b5c414e31c4a8433a3290b931687a05dadc97b6Adam Powell final float avg = total / sampleCount; 222a4ce6ae0d379c8866697d064cfd1661ea156405eAdam Powell 2235b5c414e31c4a8433a3290b931687a05dadc97b6Adam Powell if (accept) { 2245b5c414e31c4a8433a3290b931687a05dadc97b6Adam Powell float newAccepted = (mTouchUpper + mTouchLower + avg) / 3; 2255b5c414e31c4a8433a3290b931687a05dadc97b6Adam Powell mTouchUpper = (mTouchUpper + newAccepted) / 2; 2265b5c414e31c4a8433a3290b931687a05dadc97b6Adam Powell mTouchLower = (mTouchLower + newAccepted) / 2; 2275b5c414e31c4a8433a3290b931687a05dadc97b6Adam Powell mTouchHistoryLastAccepted = newAccepted; 2285b5c414e31c4a8433a3290b931687a05dadc97b6Adam Powell mTouchHistoryDirection = 0; 2295b5c414e31c4a8433a3290b931687a05dadc97b6Adam Powell mTouchHistoryLastAcceptedTime = ev.getEventTime(); 230f3a2bf8edd2e070a24f0d7c105d78b38576e14acAdam Powell } 231a4ce6ae0d379c8866697d064cfd1661ea156405eAdam Powell } 232a4ce6ae0d379c8866697d064cfd1661ea156405eAdam Powell 233a4ce6ae0d379c8866697d064cfd1661ea156405eAdam Powell /** 234a4ce6ae0d379c8866697d064cfd1661ea156405eAdam Powell * Clear all touch history tracking. Useful in ACTION_CANCEL or ACTION_UP. 235a4ce6ae0d379c8866697d064cfd1661ea156405eAdam Powell * @see #addTouchHistory(MotionEvent) 236a4ce6ae0d379c8866697d064cfd1661ea156405eAdam Powell */ 237a4ce6ae0d379c8866697d064cfd1661ea156405eAdam Powell private void clearTouchHistory() { 2385b5c414e31c4a8433a3290b931687a05dadc97b6Adam Powell mTouchUpper = Float.NaN; 2395b5c414e31c4a8433a3290b931687a05dadc97b6Adam Powell mTouchLower = Float.NaN; 2405b5c414e31c4a8433a3290b931687a05dadc97b6Adam Powell mTouchHistoryLastAccepted = Float.NaN; 2415b5c414e31c4a8433a3290b931687a05dadc97b6Adam Powell mTouchHistoryDirection = 0; 2425b5c414e31c4a8433a3290b931687a05dadc97b6Adam Powell mTouchHistoryLastAcceptedTime = 0; 243a4ce6ae0d379c8866697d064cfd1661ea156405eAdam Powell } 244a4ce6ae0d379c8866697d064cfd1661ea156405eAdam Powell 245a4ce6ae0d379c8866697d064cfd1661ea156405eAdam Powell /** 246618cbea4e746196cbde43746706bec02e14b487bAdam Powell * Accepts MotionEvents and dispatches events to a {@link OnScaleGestureListener} 247618cbea4e746196cbde43746706bec02e14b487bAdam Powell * when appropriate. 248618cbea4e746196cbde43746706bec02e14b487bAdam Powell * 249618cbea4e746196cbde43746706bec02e14b487bAdam Powell * <p>Applications should pass a complete and consistent event stream to this method. 250618cbea4e746196cbde43746706bec02e14b487bAdam Powell * A complete and consistent event stream involves all MotionEvents from the initial 251618cbea4e746196cbde43746706bec02e14b487bAdam Powell * ACTION_DOWN to the final ACTION_UP or ACTION_CANCEL.</p> 252618cbea4e746196cbde43746706bec02e14b487bAdam Powell * 253618cbea4e746196cbde43746706bec02e14b487bAdam Powell * @param event The event to process 254618cbea4e746196cbde43746706bec02e14b487bAdam Powell * @return true if the event was processed and the detector wants to receive the 255618cbea4e746196cbde43746706bec02e14b487bAdam Powell * rest of the MotionEvents in this event stream. 256618cbea4e746196cbde43746706bec02e14b487bAdam Powell */ 257ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell public boolean onTouchEvent(MotionEvent event) { 25821bc5c917d4ee2a9b2b8173091e6bba85eaff899Jeff Brown if (mInputEventConsistencyVerifier != null) { 25921bc5c917d4ee2a9b2b8173091e6bba85eaff899Jeff Brown mInputEventConsistencyVerifier.onTouchEvent(event, 0); 26021bc5c917d4ee2a9b2b8173091e6bba85eaff899Jeff Brown } 26121bc5c917d4ee2a9b2b8173091e6bba85eaff899Jeff Brown 2627232b0ad675402222bce3b478dbb682848d5e9e6Adam Powell mCurrTime = event.getEventTime(); 2637232b0ad675402222bce3b478dbb682848d5e9e6Adam Powell 264e33cef8037cb87386e17bcf8701a47452d262fa6Adam Powell final int action = event.getActionMasked(); 265ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell 266618cbea4e746196cbde43746706bec02e14b487bAdam Powell final boolean streamComplete = action == MotionEvent.ACTION_UP || 267618cbea4e746196cbde43746706bec02e14b487bAdam Powell action == MotionEvent.ACTION_CANCEL; 268618cbea4e746196cbde43746706bec02e14b487bAdam Powell if (action == MotionEvent.ACTION_DOWN || streamComplete) { 269618cbea4e746196cbde43746706bec02e14b487bAdam Powell // Reset any scale in progress with the listener. 270618cbea4e746196cbde43746706bec02e14b487bAdam Powell // If it's an ACTION_DOWN we're beginning a new event stream. 271618cbea4e746196cbde43746706bec02e14b487bAdam Powell // This means the app probably didn't give us all the events. Shame on it. 272618cbea4e746196cbde43746706bec02e14b487bAdam Powell if (mInProgress) { 273618cbea4e746196cbde43746706bec02e14b487bAdam Powell mListener.onScaleEnd(this); 274618cbea4e746196cbde43746706bec02e14b487bAdam Powell mInProgress = false; 27547ec2fb37046797bebc82b505a13c552021b9ff3Adam Powell mInitialSpan = 0; 276ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell } 277618cbea4e746196cbde43746706bec02e14b487bAdam Powell 278618cbea4e746196cbde43746706bec02e14b487bAdam Powell if (streamComplete) { 279a4ce6ae0d379c8866697d064cfd1661ea156405eAdam Powell clearTouchHistory(); 280618cbea4e746196cbde43746706bec02e14b487bAdam Powell return true; 281ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell } 282ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell } 283bbdc50b102faf52768ac3028bc49e027ff140656Jeff Brown 284abde042a824c3fc2f3537ef136017dfa006258b9Adam Powell final boolean configChanged = action == MotionEvent.ACTION_DOWN || 285618cbea4e746196cbde43746706bec02e14b487bAdam Powell action == MotionEvent.ACTION_POINTER_UP || 286618cbea4e746196cbde43746706bec02e14b487bAdam Powell action == MotionEvent.ACTION_POINTER_DOWN; 287618cbea4e746196cbde43746706bec02e14b487bAdam Powell final boolean pointerUp = action == MotionEvent.ACTION_POINTER_UP; 288618cbea4e746196cbde43746706bec02e14b487bAdam Powell final int skipIndex = pointerUp ? event.getActionIndex() : -1; 289618cbea4e746196cbde43746706bec02e14b487bAdam Powell 290618cbea4e746196cbde43746706bec02e14b487bAdam Powell // Determine focal point 291618cbea4e746196cbde43746706bec02e14b487bAdam Powell float sumX = 0, sumY = 0; 292618cbea4e746196cbde43746706bec02e14b487bAdam Powell final int count = event.getPointerCount(); 293618cbea4e746196cbde43746706bec02e14b487bAdam Powell for (int i = 0; i < count; i++) { 294618cbea4e746196cbde43746706bec02e14b487bAdam Powell if (skipIndex == i) continue; 295618cbea4e746196cbde43746706bec02e14b487bAdam Powell sumX += event.getX(i); 296618cbea4e746196cbde43746706bec02e14b487bAdam Powell sumY += event.getY(i); 297bbdc50b102faf52768ac3028bc49e027ff140656Jeff Brown } 298618cbea4e746196cbde43746706bec02e14b487bAdam Powell final int div = pointerUp ? count - 1 : count; 299618cbea4e746196cbde43746706bec02e14b487bAdam Powell final float focusX = sumX / div; 300618cbea4e746196cbde43746706bec02e14b487bAdam Powell final float focusY = sumY / div; 301618cbea4e746196cbde43746706bec02e14b487bAdam Powell 3025b5c414e31c4a8433a3290b931687a05dadc97b6Adam Powell 3035b5c414e31c4a8433a3290b931687a05dadc97b6Adam Powell addTouchHistory(event); 304a4ce6ae0d379c8866697d064cfd1661ea156405eAdam Powell 305618cbea4e746196cbde43746706bec02e14b487bAdam Powell // Determine average deviation from focal point 306618cbea4e746196cbde43746706bec02e14b487bAdam Powell float devSumX = 0, devSumY = 0; 307618cbea4e746196cbde43746706bec02e14b487bAdam Powell for (int i = 0; i < count; i++) { 308618cbea4e746196cbde43746706bec02e14b487bAdam Powell if (skipIndex == i) continue; 309828e56ea6e8f4d5aa650052580f1f7873ad8296dAdam Powell 3105b5c414e31c4a8433a3290b931687a05dadc97b6Adam Powell // Convert the resulting diameter into a radius. 3115b5c414e31c4a8433a3290b931687a05dadc97b6Adam Powell final float touchSize = mTouchHistoryLastAccepted / 2; 312828e56ea6e8f4d5aa650052580f1f7873ad8296dAdam Powell devSumX += Math.abs(event.getX(i) - focusX) + touchSize; 313828e56ea6e8f4d5aa650052580f1f7873ad8296dAdam Powell devSumY += Math.abs(event.getY(i) - focusY) + touchSize; 314e33cef8037cb87386e17bcf8701a47452d262fa6Adam Powell } 315618cbea4e746196cbde43746706bec02e14b487bAdam Powell final float devX = devSumX / div; 316618cbea4e746196cbde43746706bec02e14b487bAdam Powell final float devY = devSumY / div; 317618cbea4e746196cbde43746706bec02e14b487bAdam Powell 318618cbea4e746196cbde43746706bec02e14b487bAdam Powell // Span is the average distance between touch points through the focal point; 319618cbea4e746196cbde43746706bec02e14b487bAdam Powell // i.e. the diameter of the circle with a radius of the average deviation from 320618cbea4e746196cbde43746706bec02e14b487bAdam Powell // the focal point. 321618cbea4e746196cbde43746706bec02e14b487bAdam Powell final float spanX = devX * 2; 322618cbea4e746196cbde43746706bec02e14b487bAdam Powell final float spanY = devY * 2; 323618cbea4e746196cbde43746706bec02e14b487bAdam Powell final float span = FloatMath.sqrt(spanX * spanX + spanY * spanY); 324618cbea4e746196cbde43746706bec02e14b487bAdam Powell 325618cbea4e746196cbde43746706bec02e14b487bAdam Powell // Dispatch begin/end events as needed. 326618cbea4e746196cbde43746706bec02e14b487bAdam Powell // If the configuration changes, notify the app to reset its current state by beginning 327618cbea4e746196cbde43746706bec02e14b487bAdam Powell // a fresh scale event stream. 32847ec2fb37046797bebc82b505a13c552021b9ff3Adam Powell final boolean wasInProgress = mInProgress; 32947ec2fb37046797bebc82b505a13c552021b9ff3Adam Powell mFocusX = focusX; 33047ec2fb37046797bebc82b505a13c552021b9ff3Adam Powell mFocusY = focusY; 331828e56ea6e8f4d5aa650052580f1f7873ad8296dAdam Powell if (mInProgress && (span < mMinSpan || configChanged)) { 332618cbea4e746196cbde43746706bec02e14b487bAdam Powell mListener.onScaleEnd(this); 333618cbea4e746196cbde43746706bec02e14b487bAdam Powell mInProgress = false; 33447ec2fb37046797bebc82b505a13c552021b9ff3Adam Powell mInitialSpan = span; 335618cbea4e746196cbde43746706bec02e14b487bAdam Powell } 336618cbea4e746196cbde43746706bec02e14b487bAdam Powell if (configChanged) { 337618cbea4e746196cbde43746706bec02e14b487bAdam Powell mPrevSpanX = mCurrSpanX = spanX; 338618cbea4e746196cbde43746706bec02e14b487bAdam Powell mPrevSpanY = mCurrSpanY = spanY; 33947ec2fb37046797bebc82b505a13c552021b9ff3Adam Powell mInitialSpan = mPrevSpan = mCurrSpan = span; 340618cbea4e746196cbde43746706bec02e14b487bAdam Powell } 341b1861c3e89c3e869c95c5c01b78320a1dcef26adAdam Powell if (!mInProgress && span >= mMinSpan && 34247ec2fb37046797bebc82b505a13c552021b9ff3Adam Powell (wasInProgress || Math.abs(span - mInitialSpan) > mSpanSlop)) { 34347ec2fb37046797bebc82b505a13c552021b9ff3Adam Powell mPrevSpanX = mCurrSpanX = spanX; 34447ec2fb37046797bebc82b505a13c552021b9ff3Adam Powell mPrevSpanY = mCurrSpanY = spanY; 34547ec2fb37046797bebc82b505a13c552021b9ff3Adam Powell mPrevSpan = mCurrSpan = span; 3467232b0ad675402222bce3b478dbb682848d5e9e6Adam Powell mPrevTime = mCurrTime; 347618cbea4e746196cbde43746706bec02e14b487bAdam Powell mInProgress = mListener.onScaleBegin(this); 348ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell } 349ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell 350618cbea4e746196cbde43746706bec02e14b487bAdam Powell // Handle motion; focal point and span/scale factor are changing. 351618cbea4e746196cbde43746706bec02e14b487bAdam Powell if (action == MotionEvent.ACTION_MOVE) { 352618cbea4e746196cbde43746706bec02e14b487bAdam Powell mCurrSpanX = spanX; 353618cbea4e746196cbde43746706bec02e14b487bAdam Powell mCurrSpanY = spanY; 354618cbea4e746196cbde43746706bec02e14b487bAdam Powell mCurrSpan = span; 355618cbea4e746196cbde43746706bec02e14b487bAdam Powell 356618cbea4e746196cbde43746706bec02e14b487bAdam Powell boolean updatePrev = true; 357618cbea4e746196cbde43746706bec02e14b487bAdam Powell if (mInProgress) { 358618cbea4e746196cbde43746706bec02e14b487bAdam Powell updatePrev = mListener.onScale(this); 359618cbea4e746196cbde43746706bec02e14b487bAdam Powell } 360e33cef8037cb87386e17bcf8701a47452d262fa6Adam Powell 361618cbea4e746196cbde43746706bec02e14b487bAdam Powell if (updatePrev) { 362618cbea4e746196cbde43746706bec02e14b487bAdam Powell mPrevSpanX = mCurrSpanX; 363618cbea4e746196cbde43746706bec02e14b487bAdam Powell mPrevSpanY = mCurrSpanY; 364618cbea4e746196cbde43746706bec02e14b487bAdam Powell mPrevSpan = mCurrSpan; 3657232b0ad675402222bce3b478dbb682848d5e9e6Adam Powell mPrevTime = mCurrTime; 366d0197f3669efda060c7ee2069ff41bd970fd6d9cAdam Powell } 367d0197f3669efda060c7ee2069ff41bd970fd6d9cAdam Powell } 368d0197f3669efda060c7ee2069ff41bd970fd6d9cAdam Powell 369618cbea4e746196cbde43746706bec02e14b487bAdam Powell return true; 370ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell } 371ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell 372ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell /** 373618cbea4e746196cbde43746706bec02e14b487bAdam Powell * Returns {@code true} if a scale gesture is in progress. 374ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell */ 375ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell public boolean isInProgress() { 376618cbea4e746196cbde43746706bec02e14b487bAdam Powell return mInProgress; 377ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell } 378ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell 379ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell /** 380ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell * Get the X coordinate of the current gesture's focal point. 381618cbea4e746196cbde43746706bec02e14b487bAdam Powell * If a gesture is in progress, the focal point is between 382618cbea4e746196cbde43746706bec02e14b487bAdam Powell * each of the pointers forming the gesture. 383618cbea4e746196cbde43746706bec02e14b487bAdam Powell * 384ab905c87b7324d15715b78eaae7ef8558ad3bd10Adam Powell * If {@link #isInProgress()} would return false, the result of this 385ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell * function is undefined. 38647c41e807e36999e4d0d2072e41a82bc45655ff2Erik * 387ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell * @return X coordinate of the focal point in pixels. 388ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell */ 389ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell public float getFocusX() { 390ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell return mFocusX; 391ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell } 392ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell 393ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell /** 394ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell * Get the Y coordinate of the current gesture's focal point. 395618cbea4e746196cbde43746706bec02e14b487bAdam Powell * If a gesture is in progress, the focal point is between 396618cbea4e746196cbde43746706bec02e14b487bAdam Powell * each of the pointers forming the gesture. 397618cbea4e746196cbde43746706bec02e14b487bAdam Powell * 398ab905c87b7324d15715b78eaae7ef8558ad3bd10Adam Powell * If {@link #isInProgress()} would return false, the result of this 399ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell * function is undefined. 40047c41e807e36999e4d0d2072e41a82bc45655ff2Erik * 401ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell * @return Y coordinate of the focal point in pixels. 402ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell */ 403ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell public float getFocusY() { 404ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell return mFocusY; 405ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell } 406ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell 407ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell /** 408618cbea4e746196cbde43746706bec02e14b487bAdam Powell * Return the average distance between each of the pointers forming the 409618cbea4e746196cbde43746706bec02e14b487bAdam Powell * gesture in progress through the focal point. 41047c41e807e36999e4d0d2072e41a82bc45655ff2Erik * 411ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell * @return Distance between pointers in pixels. 412ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell */ 413ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell public float getCurrentSpan() { 414618cbea4e746196cbde43746706bec02e14b487bAdam Powell return mCurrSpan; 415ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell } 416ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell 417ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell /** 418618cbea4e746196cbde43746706bec02e14b487bAdam Powell * Return the average X distance between each of the pointers forming the 419618cbea4e746196cbde43746706bec02e14b487bAdam Powell * gesture in progress through the focal point. 42047c41e807e36999e4d0d2072e41a82bc45655ff2Erik * 42147c41e807e36999e4d0d2072e41a82bc45655ff2Erik * @return Distance between pointers in pixels. 42247c41e807e36999e4d0d2072e41a82bc45655ff2Erik */ 42347c41e807e36999e4d0d2072e41a82bc45655ff2Erik public float getCurrentSpanX() { 424618cbea4e746196cbde43746706bec02e14b487bAdam Powell return mCurrSpanX; 42547c41e807e36999e4d0d2072e41a82bc45655ff2Erik } 42647c41e807e36999e4d0d2072e41a82bc45655ff2Erik 42747c41e807e36999e4d0d2072e41a82bc45655ff2Erik /** 428618cbea4e746196cbde43746706bec02e14b487bAdam Powell * Return the average Y distance between each of the pointers forming the 429618cbea4e746196cbde43746706bec02e14b487bAdam Powell * gesture in progress through the focal point. 43047c41e807e36999e4d0d2072e41a82bc45655ff2Erik * 43147c41e807e36999e4d0d2072e41a82bc45655ff2Erik * @return Distance between pointers in pixels. 43247c41e807e36999e4d0d2072e41a82bc45655ff2Erik */ 43347c41e807e36999e4d0d2072e41a82bc45655ff2Erik public float getCurrentSpanY() { 434618cbea4e746196cbde43746706bec02e14b487bAdam Powell return mCurrSpanY; 43547c41e807e36999e4d0d2072e41a82bc45655ff2Erik } 43647c41e807e36999e4d0d2072e41a82bc45655ff2Erik 43747c41e807e36999e4d0d2072e41a82bc45655ff2Erik /** 438618cbea4e746196cbde43746706bec02e14b487bAdam Powell * Return the previous average distance between each of the pointers forming the 439618cbea4e746196cbde43746706bec02e14b487bAdam Powell * gesture in progress through the focal point. 44047c41e807e36999e4d0d2072e41a82bc45655ff2Erik * 441ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell * @return Previous distance between pointers in pixels. 442ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell */ 443ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell public float getPreviousSpan() { 444618cbea4e746196cbde43746706bec02e14b487bAdam Powell return mPrevSpan; 445ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell } 446ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell 447ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell /** 448618cbea4e746196cbde43746706bec02e14b487bAdam Powell * Return the previous average X distance between each of the pointers forming the 449618cbea4e746196cbde43746706bec02e14b487bAdam Powell * gesture in progress through the focal point. 45047c41e807e36999e4d0d2072e41a82bc45655ff2Erik * 45147c41e807e36999e4d0d2072e41a82bc45655ff2Erik * @return Previous distance between pointers in pixels. 45247c41e807e36999e4d0d2072e41a82bc45655ff2Erik */ 45347c41e807e36999e4d0d2072e41a82bc45655ff2Erik public float getPreviousSpanX() { 454618cbea4e746196cbde43746706bec02e14b487bAdam Powell return mPrevSpanX; 45547c41e807e36999e4d0d2072e41a82bc45655ff2Erik } 45647c41e807e36999e4d0d2072e41a82bc45655ff2Erik 45747c41e807e36999e4d0d2072e41a82bc45655ff2Erik /** 458618cbea4e746196cbde43746706bec02e14b487bAdam Powell * Return the previous average Y distance between each of the pointers forming the 459618cbea4e746196cbde43746706bec02e14b487bAdam Powell * gesture in progress through the focal point. 46047c41e807e36999e4d0d2072e41a82bc45655ff2Erik * 46147c41e807e36999e4d0d2072e41a82bc45655ff2Erik * @return Previous distance between pointers in pixels. 46247c41e807e36999e4d0d2072e41a82bc45655ff2Erik */ 46347c41e807e36999e4d0d2072e41a82bc45655ff2Erik public float getPreviousSpanY() { 464618cbea4e746196cbde43746706bec02e14b487bAdam Powell return mPrevSpanY; 46547c41e807e36999e4d0d2072e41a82bc45655ff2Erik } 46647c41e807e36999e4d0d2072e41a82bc45655ff2Erik 46747c41e807e36999e4d0d2072e41a82bc45655ff2Erik /** 468ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell * Return the scaling factor from the previous scale event to the current 469ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell * event. This value is defined as 470ab905c87b7324d15715b78eaae7ef8558ad3bd10Adam Powell * ({@link #getCurrentSpan()} / {@link #getPreviousSpan()}). 47147c41e807e36999e4d0d2072e41a82bc45655ff2Erik * 472ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell * @return The current scaling factor. 473ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell */ 474ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell public float getScaleFactor() { 475618cbea4e746196cbde43746706bec02e14b487bAdam Powell return mPrevSpan > 0 ? mCurrSpan / mPrevSpan : 1; 476ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell } 47747c41e807e36999e4d0d2072e41a82bc45655ff2Erik 478ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell /** 479ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell * Return the time difference in milliseconds between the previous 480ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell * accepted scaling event and the current scaling event. 48147c41e807e36999e4d0d2072e41a82bc45655ff2Erik * 482ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell * @return Time difference since the last scaling event in milliseconds. 483ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell */ 484ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell public long getTimeDelta() { 485618cbea4e746196cbde43746706bec02e14b487bAdam Powell return mCurrTime - mPrevTime; 486ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell } 48747c41e807e36999e4d0d2072e41a82bc45655ff2Erik 488ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell /** 489ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell * Return the event time of the current event being processed. 49047c41e807e36999e4d0d2072e41a82bc45655ff2Erik * 491ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell * @return Current event time in milliseconds. 492ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell */ 493ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell public long getEventTime() { 494618cbea4e746196cbde43746706bec02e14b487bAdam Powell return mCurrTime; 495ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell } 496ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell} 497