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