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