ScaleGestureDetector.java revision 47c41e807e36999e4d0d2072e41a82bc45655ff2
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;
20380b525220955ce4e4df8943b89082c7443ebfddAdam Powellimport android.util.DisplayMetrics;
21346c8fb036b99a33756c66dcebeb5d0f67a1df72Adam Powellimport android.util.FloatMath;
22ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell
23ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell/**
24ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell * Detects transformation gestures involving more than one pointer ("multitouch")
25ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell * using the supplied {@link MotionEvent}s. The {@link OnScaleGestureListener}
26ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell * callback will notify users when a particular gesture event has occurred.
27ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell * This class should only be used with {@link MotionEvent}s reported via touch.
2847c41e807e36999e4d0d2072e41a82bc45655ff2Erik *
29ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell * To use this class:
30ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell * <ul>
31ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell *  <li>Create an instance of the {@code ScaleGestureDetector} for your
32ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell *      {@link View}
33ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell *  <li>In the {@link View#onTouchEvent(MotionEvent)} method ensure you call
34ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell *          {@link #onTouchEvent(MotionEvent)}. The methods defined in your
35ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell *          callback will be executed when the events occur.
36ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell * </ul>
37ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell */
38ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powellpublic class ScaleGestureDetector {
39ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell    /**
40ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell     * The listener for receiving notifications when gestures occur.
41ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell     * If you want to listen for all the different gestures then implement
42ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell     * this interface. If you only want to listen for a subset it might
43ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell     * be easier to extend {@link SimpleOnScaleGestureListener}.
4447c41e807e36999e4d0d2072e41a82bc45655ff2Erik     *
45ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell     * An application will receive events in the following order:
46ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell     * <ul>
47ab905c87b7324d15715b78eaae7ef8558ad3bd10Adam Powell     *  <li>One {@link OnScaleGestureListener#onScaleBegin(ScaleGestureDetector)}
48ab905c87b7324d15715b78eaae7ef8558ad3bd10Adam Powell     *  <li>Zero or more {@link OnScaleGestureListener#onScale(ScaleGestureDetector)}
49ab905c87b7324d15715b78eaae7ef8558ad3bd10Adam Powell     *  <li>One {@link OnScaleGestureListener#onScaleEnd(ScaleGestureDetector)}
50ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell     * </ul>
51ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell     */
52ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell    public interface OnScaleGestureListener {
53ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell        /**
54ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell         * Responds to scaling events for a gesture in progress.
55ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell         * Reported by pointer motion.
5647c41e807e36999e4d0d2072e41a82bc45655ff2Erik         *
57ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell         * @param detector The detector reporting the event - use this to
58ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell         *          retrieve extended info about event state.
59ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell         * @return Whether or not the detector should consider this event
60ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell         *          as handled. If an event was not handled, the detector
61ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell         *          will continue to accumulate movement until an event is
62ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell         *          handled. This can be useful if an application, for example,
63ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell         *          only wants to update scaling factors if the change is
64ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell         *          greater than 0.01.
65ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell         */
66ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell        public boolean onScale(ScaleGestureDetector detector);
67ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell
68ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell        /**
69ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell         * Responds to the beginning of a scaling gesture. Reported by
70ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell         * new pointers going down.
7147c41e807e36999e4d0d2072e41a82bc45655ff2Erik         *
72ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell         * @param detector The detector reporting the event - use this to
73ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell         *          retrieve extended info about event state.
74ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell         * @return Whether or not the detector should continue recognizing
75ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell         *          this gesture. For example, if a gesture is beginning
76ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell         *          with a focal point outside of a region where it makes
77ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell         *          sense, onScaleBegin() may return false to ignore the
78ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell         *          rest of the gesture.
79ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell         */
80ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell        public boolean onScaleBegin(ScaleGestureDetector detector);
81ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell
82ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell        /**
83ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell         * Responds to the end of a scale gesture. Reported by existing
84216bccf804db9c972b317620a27de6a8adf7fbfeAdam Powell         * pointers going up.
8547c41e807e36999e4d0d2072e41a82bc45655ff2Erik         *
86ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell         * Once a scale has ended, {@link ScaleGestureDetector#getFocusX()}
87ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell         * and {@link ScaleGestureDetector#getFocusY()} will return the location
88ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell         * of the pointer remaining on the screen.
8947c41e807e36999e4d0d2072e41a82bc45655ff2Erik         *
90ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell         * @param detector The detector reporting the event - use this to
91ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell         *          retrieve extended info about event state.
92ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell         */
93ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell        public void onScaleEnd(ScaleGestureDetector detector);
94ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell    }
9547c41e807e36999e4d0d2072e41a82bc45655ff2Erik
96ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell    /**
97ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell     * A convenience class to extend when you only want to listen for a subset
98ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell     * of scaling-related events. This implements all methods in
99ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell     * {@link OnScaleGestureListener} but does nothing.
100346c8fb036b99a33756c66dcebeb5d0f67a1df72Adam Powell     * {@link OnScaleGestureListener#onScale(ScaleGestureDetector)} returns
101346c8fb036b99a33756c66dcebeb5d0f67a1df72Adam Powell     * {@code false} so that a subclass can retrieve the accumulated scale
102346c8fb036b99a33756c66dcebeb5d0f67a1df72Adam Powell     * factor in an overridden onScaleEnd.
103346c8fb036b99a33756c66dcebeb5d0f67a1df72Adam Powell     * {@link OnScaleGestureListener#onScaleBegin(ScaleGestureDetector)} returns
10447c41e807e36999e4d0d2072e41a82bc45655ff2Erik     * {@code true}.
105ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell     */
106216bccf804db9c972b317620a27de6a8adf7fbfeAdam Powell    public static class SimpleOnScaleGestureListener implements OnScaleGestureListener {
107ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell
108ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell        public boolean onScale(ScaleGestureDetector detector) {
109346c8fb036b99a33756c66dcebeb5d0f67a1df72Adam Powell            return false;
110ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell        }
111ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell
112ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell        public boolean onScaleBegin(ScaleGestureDetector detector) {
113ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell            return true;
114ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell        }
115ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell
116ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell        public void onScaleEnd(ScaleGestureDetector detector) {
117ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell            // Intentionally empty
118ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell        }
119ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell    }
120ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell
121346c8fb036b99a33756c66dcebeb5d0f67a1df72Adam Powell    /**
122346c8fb036b99a33756c66dcebeb5d0f67a1df72Adam Powell     * This value is the threshold ratio between our previous combined pressure
123346c8fb036b99a33756c66dcebeb5d0f67a1df72Adam Powell     * and the current combined pressure. We will only fire an onScale event if
124346c8fb036b99a33756c66dcebeb5d0f67a1df72Adam Powell     * the computed ratio between the current and previous event pressures is
125346c8fb036b99a33756c66dcebeb5d0f67a1df72Adam Powell     * greater than this value. When pressure decreases rapidly between events
126346c8fb036b99a33756c66dcebeb5d0f67a1df72Adam Powell     * the position values can often be imprecise, as it usually indicates
127346c8fb036b99a33756c66dcebeb5d0f67a1df72Adam Powell     * that the user is in the process of lifting a pointer off of the device.
128346c8fb036b99a33756c66dcebeb5d0f67a1df72Adam Powell     * Its value was tuned experimentally.
129346c8fb036b99a33756c66dcebeb5d0f67a1df72Adam Powell     */
130ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell    private static final float PRESSURE_THRESHOLD = 0.67f;
131ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell
132346c8fb036b99a33756c66dcebeb5d0f67a1df72Adam Powell    private final Context mContext;
133346c8fb036b99a33756c66dcebeb5d0f67a1df72Adam Powell    private final OnScaleGestureListener mListener;
134ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell    private boolean mGestureInProgress;
135ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell
136ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell    private MotionEvent mPrevEvent;
137ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell    private MotionEvent mCurrEvent;
138ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell
139ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell    private float mFocusX;
140ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell    private float mFocusY;
141ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell    private float mPrevFingerDiffX;
142ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell    private float mPrevFingerDiffY;
143ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell    private float mCurrFingerDiffX;
144ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell    private float mCurrFingerDiffY;
145ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell    private float mCurrLen;
146ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell    private float mPrevLen;
147ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell    private float mScaleFactor;
148ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell    private float mCurrPressure;
149ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell    private float mPrevPressure;
150ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell    private long mTimeDelta;
15147c41e807e36999e4d0d2072e41a82bc45655ff2Erik
152346c8fb036b99a33756c66dcebeb5d0f67a1df72Adam Powell    private final float mEdgeSlop;
153380b525220955ce4e4df8943b89082c7443ebfddAdam Powell    private float mRightSlopEdge;
154380b525220955ce4e4df8943b89082c7443ebfddAdam Powell    private float mBottomSlopEdge;
155380b525220955ce4e4df8943b89082c7443ebfddAdam Powell    private boolean mSloppyGesture;
156ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell
157ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell    public ScaleGestureDetector(Context context, OnScaleGestureListener listener) {
158380b525220955ce4e4df8943b89082c7443ebfddAdam Powell        ViewConfiguration config = ViewConfiguration.get(context);
159ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell        mContext = context;
160ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell        mListener = listener;
161380b525220955ce4e4df8943b89082c7443ebfddAdam Powell        mEdgeSlop = config.getScaledEdgeSlop();
162ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell    }
163ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell
164ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell    public boolean onTouchEvent(MotionEvent event) {
165ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell        final int action = event.getAction();
166ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell        boolean handled = true;
167ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell
168ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell        if (!mGestureInProgress) {
169f5bcc6addd9c1f8f6bb8b8626540d003b3f964faAdam Powell            switch (action & MotionEvent.ACTION_MASK) {
170f5bcc6addd9c1f8f6bb8b8626540d003b3f964faAdam Powell            case MotionEvent.ACTION_POINTER_DOWN: {
171ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell                // We have a new multi-finger gesture
172380b525220955ce4e4df8943b89082c7443ebfddAdam Powell
173d5ada83c719d366d3063572ca6ce5ab8918fd39bGrace Kloba                // as orientation can change, query the metrics in touch down
174d5ada83c719d366d3063572ca6ce5ab8918fd39bGrace Kloba                DisplayMetrics metrics = mContext.getResources().getDisplayMetrics();
175d5ada83c719d366d3063572ca6ce5ab8918fd39bGrace Kloba                mRightSlopEdge = metrics.widthPixels - mEdgeSlop;
176d5ada83c719d366d3063572ca6ce5ab8918fd39bGrace Kloba                mBottomSlopEdge = metrics.heightPixels - mEdgeSlop;
177d5ada83c719d366d3063572ca6ce5ab8918fd39bGrace Kloba
178ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell                // Be paranoid in case we missed an event
179ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell                reset();
180380b525220955ce4e4df8943b89082c7443ebfddAdam Powell
181ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell                mPrevEvent = MotionEvent.obtain(event);
182ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell                mTimeDelta = 0;
183380b525220955ce4e4df8943b89082c7443ebfddAdam Powell
184ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell                setContext(event);
185380b525220955ce4e4df8943b89082c7443ebfddAdam Powell
186380b525220955ce4e4df8943b89082c7443ebfddAdam Powell                // Check if we have a sloppy gesture. If so, delay
187380b525220955ce4e4df8943b89082c7443ebfddAdam Powell                // the beginning of the gesture until we're sure that's
188380b525220955ce4e4df8943b89082c7443ebfddAdam Powell                // what the user wanted. Sloppy gestures can happen if the
189380b525220955ce4e4df8943b89082c7443ebfddAdam Powell                // edge of the user's hand is touching the screen, for example.
190380b525220955ce4e4df8943b89082c7443ebfddAdam Powell                final float edgeSlop = mEdgeSlop;
191380b525220955ce4e4df8943b89082c7443ebfddAdam Powell                final float rightSlop = mRightSlopEdge;
192380b525220955ce4e4df8943b89082c7443ebfddAdam Powell                final float bottomSlop = mBottomSlopEdge;
193380b525220955ce4e4df8943b89082c7443ebfddAdam Powell                final float x0 = event.getRawX();
194380b525220955ce4e4df8943b89082c7443ebfddAdam Powell                final float y0 = event.getRawY();
195380b525220955ce4e4df8943b89082c7443ebfddAdam Powell                final float x1 = getRawX(event, 1);
196380b525220955ce4e4df8943b89082c7443ebfddAdam Powell                final float y1 = getRawY(event, 1);
197380b525220955ce4e4df8943b89082c7443ebfddAdam Powell
1988f9fbb0c45265a6f5da51cb3bc35362d8e5bc900Grace Kloba                boolean p0sloppy = x0 < edgeSlop || y0 < edgeSlop
1998f9fbb0c45265a6f5da51cb3bc35362d8e5bc900Grace Kloba                        || x0 > rightSlop || y0 > bottomSlop;
2008f9fbb0c45265a6f5da51cb3bc35362d8e5bc900Grace Kloba                boolean p1sloppy = x1 < edgeSlop || y1 < edgeSlop
2018f9fbb0c45265a6f5da51cb3bc35362d8e5bc900Grace Kloba                        || x1 > rightSlop || y1 > bottomSlop;
202380b525220955ce4e4df8943b89082c7443ebfddAdam Powell
203f5bcc6addd9c1f8f6bb8b8626540d003b3f964faAdam Powell                if (p0sloppy && p1sloppy) {
2048f9fbb0c45265a6f5da51cb3bc35362d8e5bc900Grace Kloba                    mFocusX = -1;
2058f9fbb0c45265a6f5da51cb3bc35362d8e5bc900Grace Kloba                    mFocusY = -1;
2068f9fbb0c45265a6f5da51cb3bc35362d8e5bc900Grace Kloba                    mSloppyGesture = true;
2078f9fbb0c45265a6f5da51cb3bc35362d8e5bc900Grace Kloba                } else if (p0sloppy) {
208380b525220955ce4e4df8943b89082c7443ebfddAdam Powell                    mFocusX = event.getX(1);
209380b525220955ce4e4df8943b89082c7443ebfddAdam Powell                    mFocusY = event.getY(1);
210380b525220955ce4e4df8943b89082c7443ebfddAdam Powell                    mSloppyGesture = true;
211380b525220955ce4e4df8943b89082c7443ebfddAdam Powell                } else if (p1sloppy) {
212380b525220955ce4e4df8943b89082c7443ebfddAdam Powell                    mFocusX = event.getX(0);
213380b525220955ce4e4df8943b89082c7443ebfddAdam Powell                    mFocusY = event.getY(0);
214380b525220955ce4e4df8943b89082c7443ebfddAdam Powell                    mSloppyGesture = true;
215380b525220955ce4e4df8943b89082c7443ebfddAdam Powell                } else {
216380b525220955ce4e4df8943b89082c7443ebfddAdam Powell                    mGestureInProgress = mListener.onScaleBegin(this);
217380b525220955ce4e4df8943b89082c7443ebfddAdam Powell                }
218f5bcc6addd9c1f8f6bb8b8626540d003b3f964faAdam Powell            }
219f5bcc6addd9c1f8f6bb8b8626540d003b3f964faAdam Powell            break;
22047c41e807e36999e4d0d2072e41a82bc45655ff2Erik
221f5bcc6addd9c1f8f6bb8b8626540d003b3f964faAdam Powell            case MotionEvent.ACTION_MOVE:
222f5bcc6addd9c1f8f6bb8b8626540d003b3f964faAdam Powell                if (mSloppyGesture) {
223f5bcc6addd9c1f8f6bb8b8626540d003b3f964faAdam Powell                    // Initiate sloppy gestures if we've moved outside of the slop area.
224f5bcc6addd9c1f8f6bb8b8626540d003b3f964faAdam Powell                    final float edgeSlop = mEdgeSlop;
225f5bcc6addd9c1f8f6bb8b8626540d003b3f964faAdam Powell                    final float rightSlop = mRightSlopEdge;
226f5bcc6addd9c1f8f6bb8b8626540d003b3f964faAdam Powell                    final float bottomSlop = mBottomSlopEdge;
227f5bcc6addd9c1f8f6bb8b8626540d003b3f964faAdam Powell                    final float x0 = event.getRawX();
228f5bcc6addd9c1f8f6bb8b8626540d003b3f964faAdam Powell                    final float y0 = event.getRawY();
229f5bcc6addd9c1f8f6bb8b8626540d003b3f964faAdam Powell                    final float x1 = getRawX(event, 1);
230f5bcc6addd9c1f8f6bb8b8626540d003b3f964faAdam Powell                    final float y1 = getRawY(event, 1);
231380b525220955ce4e4df8943b89082c7443ebfddAdam Powell
232f5bcc6addd9c1f8f6bb8b8626540d003b3f964faAdam Powell                    boolean p0sloppy = x0 < edgeSlop || y0 < edgeSlop
233f5bcc6addd9c1f8f6bb8b8626540d003b3f964faAdam Powell                    || x0 > rightSlop || y0 > bottomSlop;
234f5bcc6addd9c1f8f6bb8b8626540d003b3f964faAdam Powell                    boolean p1sloppy = x1 < edgeSlop || y1 < edgeSlop
235f5bcc6addd9c1f8f6bb8b8626540d003b3f964faAdam Powell                    || x1 > rightSlop || y1 > bottomSlop;
236380b525220955ce4e4df8943b89082c7443ebfddAdam Powell
237f5bcc6addd9c1f8f6bb8b8626540d003b3f964faAdam Powell                    if(p0sloppy && p1sloppy) {
238f5bcc6addd9c1f8f6bb8b8626540d003b3f964faAdam Powell                        mFocusX = -1;
239f5bcc6addd9c1f8f6bb8b8626540d003b3f964faAdam Powell                        mFocusY = -1;
240f5bcc6addd9c1f8f6bb8b8626540d003b3f964faAdam Powell                    } else if (p0sloppy) {
241f5bcc6addd9c1f8f6bb8b8626540d003b3f964faAdam Powell                        mFocusX = event.getX(1);
242f5bcc6addd9c1f8f6bb8b8626540d003b3f964faAdam Powell                        mFocusY = event.getY(1);
243f5bcc6addd9c1f8f6bb8b8626540d003b3f964faAdam Powell                    } else if (p1sloppy) {
244f5bcc6addd9c1f8f6bb8b8626540d003b3f964faAdam Powell                        mFocusX = event.getX(0);
245f5bcc6addd9c1f8f6bb8b8626540d003b3f964faAdam Powell                        mFocusY = event.getY(0);
246f5bcc6addd9c1f8f6bb8b8626540d003b3f964faAdam Powell                    } else {
247f5bcc6addd9c1f8f6bb8b8626540d003b3f964faAdam Powell                        mSloppyGesture = false;
248f5bcc6addd9c1f8f6bb8b8626540d003b3f964faAdam Powell                        mGestureInProgress = mListener.onScaleBegin(this);
249f5bcc6addd9c1f8f6bb8b8626540d003b3f964faAdam Powell                    }
250f5bcc6addd9c1f8f6bb8b8626540d003b3f964faAdam Powell                }
251f5bcc6addd9c1f8f6bb8b8626540d003b3f964faAdam Powell                break;
25247c41e807e36999e4d0d2072e41a82bc45655ff2Erik
253f5bcc6addd9c1f8f6bb8b8626540d003b3f964faAdam Powell            case MotionEvent.ACTION_POINTER_UP:
254f5bcc6addd9c1f8f6bb8b8626540d003b3f964faAdam Powell                if (mSloppyGesture) {
255f5bcc6addd9c1f8f6bb8b8626540d003b3f964faAdam Powell                    // Set focus point to the remaining finger
256f5bcc6addd9c1f8f6bb8b8626540d003b3f964faAdam Powell                    int id = (((action & MotionEvent.ACTION_POINTER_INDEX_MASK)
257f5bcc6addd9c1f8f6bb8b8626540d003b3f964faAdam Powell                            >> MotionEvent.ACTION_POINTER_INDEX_SHIFT) == 0) ? 1 : 0;
258f5bcc6addd9c1f8f6bb8b8626540d003b3f964faAdam Powell                    mFocusX = event.getX(id);
259f5bcc6addd9c1f8f6bb8b8626540d003b3f964faAdam Powell                    mFocusY = event.getY(id);
260380b525220955ce4e4df8943b89082c7443ebfddAdam Powell                }
261f5bcc6addd9c1f8f6bb8b8626540d003b3f964faAdam Powell                break;
262ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell            }
263ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell        } else {
264ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell            // Transform gesture in progress - attempt to handle it
265f5bcc6addd9c1f8f6bb8b8626540d003b3f964faAdam Powell            switch (action & MotionEvent.ACTION_MASK) {
266f5bcc6addd9c1f8f6bb8b8626540d003b3f964faAdam Powell                case MotionEvent.ACTION_POINTER_UP:
267ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell                    // Gesture ended
268ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell                    setContext(event);
269380b525220955ce4e4df8943b89082c7443ebfddAdam Powell
270ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell                    // Set focus point to the remaining finger
271f5bcc6addd9c1f8f6bb8b8626540d003b3f964faAdam Powell                    int id = (((action & MotionEvent.ACTION_POINTER_INDEX_MASK)
272f5bcc6addd9c1f8f6bb8b8626540d003b3f964faAdam Powell                            >> MotionEvent.ACTION_POINTER_INDEX_SHIFT) == 0) ? 1 : 0;
273ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell                    mFocusX = event.getX(id);
274ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell                    mFocusY = event.getY(id);
275380b525220955ce4e4df8943b89082c7443ebfddAdam Powell
276380b525220955ce4e4df8943b89082c7443ebfddAdam Powell                    if (!mSloppyGesture) {
277380b525220955ce4e4df8943b89082c7443ebfddAdam Powell                        mListener.onScaleEnd(this);
278380b525220955ce4e4df8943b89082c7443ebfddAdam Powell                    }
279ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell
280ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell                    reset();
281ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell                    break;
282ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell
283ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell                case MotionEvent.ACTION_CANCEL:
284380b525220955ce4e4df8943b89082c7443ebfddAdam Powell                    if (!mSloppyGesture) {
285380b525220955ce4e4df8943b89082c7443ebfddAdam Powell                        mListener.onScaleEnd(this);
286380b525220955ce4e4df8943b89082c7443ebfddAdam Powell                    }
287ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell
288ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell                    reset();
289ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell                    break;
290ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell
291ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell                case MotionEvent.ACTION_MOVE:
292ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell                    setContext(event);
293ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell
294ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell                    // Only accept the event if our relative pressure is within
295ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell                    // a certain limit - this can help filter shaky data as a
296ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell                    // finger is lifted.
297ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell                    if (mCurrPressure / mPrevPressure > PRESSURE_THRESHOLD) {
298ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell                        final boolean updatePrevious = mListener.onScale(this);
299ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell
300ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell                        if (updatePrevious) {
301ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell                            mPrevEvent.recycle();
302ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell                            mPrevEvent = MotionEvent.obtain(event);
303ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell                        }
304ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell                    }
305ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell                    break;
306ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell            }
307ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell        }
308ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell        return handled;
309ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell    }
31047c41e807e36999e4d0d2072e41a82bc45655ff2Erik
311380b525220955ce4e4df8943b89082c7443ebfddAdam Powell    /**
31247c41e807e36999e4d0d2072e41a82bc45655ff2Erik     * MotionEvent has no getRawX(int) method; simulate it pending future API approval.
313380b525220955ce4e4df8943b89082c7443ebfddAdam Powell     */
314380b525220955ce4e4df8943b89082c7443ebfddAdam Powell    private static float getRawX(MotionEvent event, int pointerIndex) {
3159bccdb7d5c93e350337e707bc6edf3cd017b8f96Adam Powell        float offset = event.getRawX() - event.getX();
316380b525220955ce4e4df8943b89082c7443ebfddAdam Powell        return event.getX(pointerIndex) + offset;
317380b525220955ce4e4df8943b89082c7443ebfddAdam Powell    }
31847c41e807e36999e4d0d2072e41a82bc45655ff2Erik
319380b525220955ce4e4df8943b89082c7443ebfddAdam Powell    /**
32047c41e807e36999e4d0d2072e41a82bc45655ff2Erik     * MotionEvent has no getRawY(int) method; simulate it pending future API approval.
321380b525220955ce4e4df8943b89082c7443ebfddAdam Powell     */
322380b525220955ce4e4df8943b89082c7443ebfddAdam Powell    private static float getRawY(MotionEvent event, int pointerIndex) {
3239bccdb7d5c93e350337e707bc6edf3cd017b8f96Adam Powell        float offset = event.getRawY() - event.getY();
324380b525220955ce4e4df8943b89082c7443ebfddAdam Powell        return event.getY(pointerIndex) + offset;
325380b525220955ce4e4df8943b89082c7443ebfddAdam Powell    }
326ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell
327ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell    private void setContext(MotionEvent curr) {
328ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell        if (mCurrEvent != null) {
329ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell            mCurrEvent.recycle();
330ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell        }
331ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell        mCurrEvent = MotionEvent.obtain(curr);
332ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell
333ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell        mCurrLen = -1;
334ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell        mPrevLen = -1;
335ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell        mScaleFactor = -1;
336ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell
337ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell        final MotionEvent prev = mPrevEvent;
338ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell
339ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell        final float px0 = prev.getX(0);
340ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell        final float py0 = prev.getY(0);
341ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell        final float px1 = prev.getX(1);
342ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell        final float py1 = prev.getY(1);
343ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell        final float cx0 = curr.getX(0);
344ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell        final float cy0 = curr.getY(0);
345ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell        final float cx1 = curr.getX(1);
346ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell        final float cy1 = curr.getY(1);
347ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell
348ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell        final float pvx = px1 - px0;
349ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell        final float pvy = py1 - py0;
350ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell        final float cvx = cx1 - cx0;
351ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell        final float cvy = cy1 - cy0;
352ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell        mPrevFingerDiffX = pvx;
353ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell        mPrevFingerDiffY = pvy;
354ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell        mCurrFingerDiffX = cvx;
355ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell        mCurrFingerDiffY = cvy;
356ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell
357ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell        mFocusX = cx0 + cvx * 0.5f;
358ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell        mFocusY = cy0 + cvy * 0.5f;
359ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell        mTimeDelta = curr.getEventTime() - prev.getEventTime();
360ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell        mCurrPressure = curr.getPressure(0) + curr.getPressure(1);
361ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell        mPrevPressure = prev.getPressure(0) + prev.getPressure(1);
362ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell    }
363ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell
364ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell    private void reset() {
365ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell        if (mPrevEvent != null) {
366ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell            mPrevEvent.recycle();
367ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell            mPrevEvent = null;
368ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell        }
369ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell        if (mCurrEvent != null) {
370ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell            mCurrEvent.recycle();
371ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell            mCurrEvent = null;
372ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell        }
373380b525220955ce4e4df8943b89082c7443ebfddAdam Powell        mSloppyGesture = false;
374380b525220955ce4e4df8943b89082c7443ebfddAdam Powell        mGestureInProgress = false;
375ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell    }
376ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell
377ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell    /**
378ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell     * Returns {@code true} if a two-finger scale gesture is in progress.
379ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell     * @return {@code true} if a scale gesture is in progress, {@code false} otherwise.
380ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell     */
381ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell    public boolean isInProgress() {
382ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell        return mGestureInProgress;
383ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell    }
384ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell
385ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell    /**
386ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell     * Get the X coordinate of the current gesture's focal point.
387ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell     * If a gesture is in progress, the focal point is directly between
388ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell     * the two pointers forming the gesture.
389ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell     * If a gesture is ending, the focal point is the location of the
390ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell     * remaining pointer on the screen.
391ab905c87b7324d15715b78eaae7ef8558ad3bd10Adam Powell     * If {@link #isInProgress()} would return false, the result of this
392ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell     * function is undefined.
39347c41e807e36999e4d0d2072e41a82bc45655ff2Erik     *
394ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell     * @return X coordinate of the focal point in pixels.
395ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell     */
396ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell    public float getFocusX() {
397ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell        return mFocusX;
398ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell    }
399ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell
400ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell    /**
401ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell     * Get the Y coordinate of the current gesture's focal point.
402ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell     * If a gesture is in progress, the focal point is directly between
403ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell     * the two pointers forming the gesture.
404ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell     * If a gesture is ending, the focal point is the location of the
405ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell     * remaining pointer on the screen.
406ab905c87b7324d15715b78eaae7ef8558ad3bd10Adam Powell     * If {@link #isInProgress()} would return false, the result of this
407ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell     * function is undefined.
40847c41e807e36999e4d0d2072e41a82bc45655ff2Erik     *
409ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell     * @return Y coordinate of the focal point in pixels.
410ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell     */
411ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell    public float getFocusY() {
412ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell        return mFocusY;
413ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell    }
414ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell
415ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell    /**
416ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell     * Return the current distance between the two pointers forming the
417ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell     * gesture in progress.
41847c41e807e36999e4d0d2072e41a82bc45655ff2Erik     *
419ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell     * @return Distance between pointers in pixels.
420ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell     */
421ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell    public float getCurrentSpan() {
422ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell        if (mCurrLen == -1) {
423ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell            final float cvx = mCurrFingerDiffX;
424ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell            final float cvy = mCurrFingerDiffY;
425346c8fb036b99a33756c66dcebeb5d0f67a1df72Adam Powell            mCurrLen = FloatMath.sqrt(cvx*cvx + cvy*cvy);
426ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell        }
427ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell        return mCurrLen;
428ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell    }
429ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell
430ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell    /**
43147c41e807e36999e4d0d2072e41a82bc45655ff2Erik     * Return the current x distance between the two pointers forming the
43247c41e807e36999e4d0d2072e41a82bc45655ff2Erik     * gesture in progress.
43347c41e807e36999e4d0d2072e41a82bc45655ff2Erik     *
43447c41e807e36999e4d0d2072e41a82bc45655ff2Erik     * @return Distance between pointers in pixels.
43547c41e807e36999e4d0d2072e41a82bc45655ff2Erik     */
43647c41e807e36999e4d0d2072e41a82bc45655ff2Erik    public float getCurrentSpanX() {
43747c41e807e36999e4d0d2072e41a82bc45655ff2Erik        return mCurrFingerDiffX;
43847c41e807e36999e4d0d2072e41a82bc45655ff2Erik    }
43947c41e807e36999e4d0d2072e41a82bc45655ff2Erik
44047c41e807e36999e4d0d2072e41a82bc45655ff2Erik    /**
44147c41e807e36999e4d0d2072e41a82bc45655ff2Erik     * Return the current y distance between the two pointers forming the
44247c41e807e36999e4d0d2072e41a82bc45655ff2Erik     * gesture in progress.
44347c41e807e36999e4d0d2072e41a82bc45655ff2Erik     *
44447c41e807e36999e4d0d2072e41a82bc45655ff2Erik     * @return Distance between pointers in pixels.
44547c41e807e36999e4d0d2072e41a82bc45655ff2Erik     */
44647c41e807e36999e4d0d2072e41a82bc45655ff2Erik    public float getCurrentSpanY() {
44747c41e807e36999e4d0d2072e41a82bc45655ff2Erik        return mCurrFingerDiffY;
44847c41e807e36999e4d0d2072e41a82bc45655ff2Erik    }
44947c41e807e36999e4d0d2072e41a82bc45655ff2Erik
45047c41e807e36999e4d0d2072e41a82bc45655ff2Erik    /**
451ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell     * Return the previous distance between the two pointers forming the
452ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell     * gesture in progress.
45347c41e807e36999e4d0d2072e41a82bc45655ff2Erik     *
454ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell     * @return Previous distance between pointers in pixels.
455ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell     */
456ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell    public float getPreviousSpan() {
457ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell        if (mPrevLen == -1) {
458ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell            final float pvx = mPrevFingerDiffX;
459ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell            final float pvy = mPrevFingerDiffY;
460346c8fb036b99a33756c66dcebeb5d0f67a1df72Adam Powell            mPrevLen = FloatMath.sqrt(pvx*pvx + pvy*pvy);
461ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell        }
462ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell        return mPrevLen;
463ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell    }
464ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell
465ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell    /**
46647c41e807e36999e4d0d2072e41a82bc45655ff2Erik     * Return the previous x distance between the two pointers forming the
46747c41e807e36999e4d0d2072e41a82bc45655ff2Erik     * gesture in progress.
46847c41e807e36999e4d0d2072e41a82bc45655ff2Erik     *
46947c41e807e36999e4d0d2072e41a82bc45655ff2Erik     * @return Previous distance between pointers in pixels.
47047c41e807e36999e4d0d2072e41a82bc45655ff2Erik     */
47147c41e807e36999e4d0d2072e41a82bc45655ff2Erik    public float getPreviousSpanX() {
47247c41e807e36999e4d0d2072e41a82bc45655ff2Erik        return mPrevFingerDiffX;
47347c41e807e36999e4d0d2072e41a82bc45655ff2Erik    }
47447c41e807e36999e4d0d2072e41a82bc45655ff2Erik
47547c41e807e36999e4d0d2072e41a82bc45655ff2Erik    /**
47647c41e807e36999e4d0d2072e41a82bc45655ff2Erik     * Return the previous y distance between the two pointers forming the
47747c41e807e36999e4d0d2072e41a82bc45655ff2Erik     * gesture in progress.
47847c41e807e36999e4d0d2072e41a82bc45655ff2Erik     *
47947c41e807e36999e4d0d2072e41a82bc45655ff2Erik     * @return Previous distance between pointers in pixels.
48047c41e807e36999e4d0d2072e41a82bc45655ff2Erik     */
48147c41e807e36999e4d0d2072e41a82bc45655ff2Erik    public float getPreviousSpanY() {
48247c41e807e36999e4d0d2072e41a82bc45655ff2Erik        return mPrevFingerDiffY;
48347c41e807e36999e4d0d2072e41a82bc45655ff2Erik    }
48447c41e807e36999e4d0d2072e41a82bc45655ff2Erik
48547c41e807e36999e4d0d2072e41a82bc45655ff2Erik    /**
486ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell     * Return the scaling factor from the previous scale event to the current
487ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell     * event. This value is defined as
488ab905c87b7324d15715b78eaae7ef8558ad3bd10Adam Powell     * ({@link #getCurrentSpan()} / {@link #getPreviousSpan()}).
48947c41e807e36999e4d0d2072e41a82bc45655ff2Erik     *
490ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell     * @return The current scaling factor.
491ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell     */
492ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell    public float getScaleFactor() {
493ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell        if (mScaleFactor == -1) {
494ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell            mScaleFactor = getCurrentSpan() / getPreviousSpan();
495ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell        }
496ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell        return mScaleFactor;
497ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell    }
49847c41e807e36999e4d0d2072e41a82bc45655ff2Erik
499ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell    /**
500ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell     * Return the time difference in milliseconds between the previous
501ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell     * accepted scaling event and the current scaling event.
50247c41e807e36999e4d0d2072e41a82bc45655ff2Erik     *
503ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell     * @return Time difference since the last scaling event in milliseconds.
504ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell     */
505ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell    public long getTimeDelta() {
506ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell        return mTimeDelta;
507ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell    }
50847c41e807e36999e4d0d2072e41a82bc45655ff2Erik
509ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell    /**
510ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell     * Return the event time of the current event being processed.
51147c41e807e36999e4d0d2072e41a82bc45655ff2Erik     *
512ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell     * @return Current event time in milliseconds.
513ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell     */
514ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell    public long getEventTime() {
515ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell        return mCurrEvent.getEventTime();
516ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell    }
517ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell}
518