ScaleGestureDetector.java revision ae542ff055301a4c3c8a18e8da1739df3a771958
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;
20ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell
21ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell/**
22ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell * Detects transformation gestures involving more than one pointer ("multitouch")
23ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell * using the supplied {@link MotionEvent}s. The {@link OnScaleGestureListener}
24ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell * callback will notify users when a particular gesture event has occurred.
25ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell * This class should only be used with {@link MotionEvent}s reported via touch.
26ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell *
27ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell * To use this class:
28ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell * <ul>
29ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell *  <li>Create an instance of the {@code ScaleGestureDetector} for your
30ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell *      {@link View}
31ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell *  <li>In the {@link View#onTouchEvent(MotionEvent)} method ensure you call
32ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell *          {@link #onTouchEvent(MotionEvent)}. The methods defined in your
33ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell *          callback will be executed when the events occur.
34ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell * </ul>
35ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell * @hide Pending API approval
36ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell */
37ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powellpublic class ScaleGestureDetector {
38ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell    /**
39ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell     * The listener for receiving notifications when gestures occur.
40ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell     * If you want to listen for all the different gestures then implement
41ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell     * this interface. If you only want to listen for a subset it might
42ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell     * be easier to extend {@link SimpleOnScaleGestureListener}.
43ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell     *
44ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell     * An application will receive events in the following order:
45ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell     * <ul>
46ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell     *  <li>One {@link OnScaleGestureListener#onScaleBegin()}
47ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell     *  <li>Zero or more {@link OnScaleGestureListener#onScale()}
48ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell     *  <li>One {@link OnScaleGestureListener#onTransformEnd()}
49ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell     * </ul>
50ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell     */
51ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell    public interface OnScaleGestureListener {
52ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell        /**
53ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell         * Responds to scaling events for a gesture in progress.
54ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell         * Reported by pointer motion.
55ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell         *
56ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell         * @param detector The detector reporting the event - use this to
57ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell         *          retrieve extended info about event state.
58ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell         * @return Whether or not the detector should consider this event
59ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell         *          as handled. If an event was not handled, the detector
60ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell         *          will continue to accumulate movement until an event is
61ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell         *          handled. This can be useful if an application, for example,
62ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell         *          only wants to update scaling factors if the change is
63ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell         *          greater than 0.01.
64ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell         */
65ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell        public boolean onScale(ScaleGestureDetector detector);
66ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell
67ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell        /**
68ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell         * Responds to the beginning of a scaling gesture. Reported by
69ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell         * new pointers going down.
70ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell         *
71ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell         * @param detector The detector reporting the event - use this to
72ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell         *          retrieve extended info about event state.
73ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell         * @return Whether or not the detector should continue recognizing
74ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell         *          this gesture. For example, if a gesture is beginning
75ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell         *          with a focal point outside of a region where it makes
76ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell         *          sense, onScaleBegin() may return false to ignore the
77ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell         *          rest of the gesture.
78ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell         */
79ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell        public boolean onScaleBegin(ScaleGestureDetector detector);
80ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell
81ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell        /**
82ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell         * Responds to the end of a scale gesture. Reported by existing
83ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell         * pointers going up. If the end of a gesture would result in a fling,
84ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell         * {@link onTransformFling()} is called instead.
85ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell         *
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.
89ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell         *
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    }
95ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell
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.
100ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell     * {@link OnScaleGestureListener#onScale(ScaleGestureDetector)} and
101ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell     * {@link OnScaleGestureListener#onScaleBegin(ScaleGestureDetector)} return
102ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell     * {@code true}.
103ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell     */
104ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell    public class SimpleOnScaleGestureListener implements OnScaleGestureListener {
105ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell
106ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell        public boolean onScale(ScaleGestureDetector detector) {
107ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell            return true;
108ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell        }
109ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell
110ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell        public boolean onScaleBegin(ScaleGestureDetector detector) {
111ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell            return true;
112ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell        }
113ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell
114ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell        public void onScaleEnd(ScaleGestureDetector detector) {
115ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell            // Intentionally empty
116ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell        }
117ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell    }
118ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell
119ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell    private static final float PRESSURE_THRESHOLD = 0.67f;
120ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell
121ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell    private Context mContext;
122ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell    private OnScaleGestureListener mListener;
123ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell    private boolean mGestureInProgress;
124ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell
125ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell    private MotionEvent mPrevEvent;
126ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell    private MotionEvent mCurrEvent;
127ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell
128ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell    private float mFocusX;
129ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell    private float mFocusY;
130ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell    private float mPrevFingerDiffX;
131ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell    private float mPrevFingerDiffY;
132ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell    private float mCurrFingerDiffX;
133ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell    private float mCurrFingerDiffY;
134ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell    private float mCurrLen;
135ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell    private float mPrevLen;
136ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell    private float mScaleFactor;
137ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell    private float mCurrPressure;
138ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell    private float mPrevPressure;
139ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell    private long mTimeDelta;
140ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell
141ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell    public ScaleGestureDetector(Context context, OnScaleGestureListener listener) {
142ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell        mContext = context;
143ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell        mListener = listener;
144ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell    }
145ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell
146ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell    public boolean onTouchEvent(MotionEvent event) {
147ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell        final int action = event.getAction();
148ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell        boolean handled = true;
149ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell
150ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell        if (!mGestureInProgress) {
151ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell            if ((action == MotionEvent.ACTION_POINTER_1_DOWN ||
152ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell                    action == MotionEvent.ACTION_POINTER_2_DOWN) &&
153ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell                    event.getPointerCount() >= 2) {
154ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell                // We have a new multi-finger gesture
155ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell
156ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell                // Be paranoid in case we missed an event
157ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell                reset();
158ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell
159ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell                mPrevEvent = MotionEvent.obtain(event);
160ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell                mTimeDelta = 0;
161ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell
162ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell                setContext(event);
163ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell                mGestureInProgress = mListener.onScaleBegin(this);
164ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell            }
165ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell        } else {
166ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell            // Transform gesture in progress - attempt to handle it
167ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell            switch (action) {
168ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell                case MotionEvent.ACTION_POINTER_1_UP:
169ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell                case MotionEvent.ACTION_POINTER_2_UP:
170ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell                    // Gesture ended
171ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell                    setContext(event);
172ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell
173ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell                    // Set focus point to the remaining finger
174ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell                    int id = (((action & MotionEvent.ACTION_POINTER_ID_MASK)
175ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell                            >> MotionEvent.ACTION_POINTER_ID_SHIFT) == 0) ? 1 : 0;
176ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell                    mFocusX = event.getX(id);
177ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell                    mFocusY = event.getY(id);
178ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell
179ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell                    mListener.onScaleEnd(this);
180ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell                    mGestureInProgress = false;
181ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell
182ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell                    reset();
183ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell                    break;
184ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell
185ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell                case MotionEvent.ACTION_CANCEL:
186ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell                    mListener.onScaleEnd(this);
187ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell                    mGestureInProgress = false;
188ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell
189ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell                    reset();
190ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell                    break;
191ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell
192ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell                case MotionEvent.ACTION_MOVE:
193ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell                    setContext(event);
194ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell
195ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell                    // Only accept the event if our relative pressure is within
196ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell                    // a certain limit - this can help filter shaky data as a
197ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell                    // finger is lifted.
198ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell                    if (mCurrPressure / mPrevPressure > PRESSURE_THRESHOLD) {
199ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell                        final boolean updatePrevious = mListener.onScale(this);
200ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell
201ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell                        if (updatePrevious) {
202ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell                            mPrevEvent.recycle();
203ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell                            mPrevEvent = MotionEvent.obtain(event);
204ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell                        }
205ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell                    }
206ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell                    break;
207ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell            }
208ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell        }
209ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell        return handled;
210ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell    }
211ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell
212ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell    private void setContext(MotionEvent curr) {
213ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell        if (mCurrEvent != null) {
214ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell            mCurrEvent.recycle();
215ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell        }
216ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell        mCurrEvent = MotionEvent.obtain(curr);
217ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell
218ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell        mCurrLen = -1;
219ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell        mPrevLen = -1;
220ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell        mScaleFactor = -1;
221ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell
222ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell        final MotionEvent prev = mPrevEvent;
223ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell
224ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell        final float px0 = prev.getX(0);
225ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell        final float py0 = prev.getY(0);
226ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell        final float px1 = prev.getX(1);
227ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell        final float py1 = prev.getY(1);
228ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell        final float cx0 = curr.getX(0);
229ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell        final float cy0 = curr.getY(0);
230ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell        final float cx1 = curr.getX(1);
231ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell        final float cy1 = curr.getY(1);
232ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell
233ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell        final float pvx = px1 - px0;
234ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell        final float pvy = py1 - py0;
235ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell        final float cvx = cx1 - cx0;
236ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell        final float cvy = cy1 - cy0;
237ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell        mPrevFingerDiffX = pvx;
238ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell        mPrevFingerDiffY = pvy;
239ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell        mCurrFingerDiffX = cvx;
240ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell        mCurrFingerDiffY = cvy;
241ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell
242ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell        mFocusX = cx0 + cvx * 0.5f;
243ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell        mFocusY = cy0 + cvy * 0.5f;
244ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell        mTimeDelta = curr.getEventTime() - prev.getEventTime();
245ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell        mCurrPressure = curr.getPressure(0) + curr.getPressure(1);
246ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell        mPrevPressure = prev.getPressure(0) + prev.getPressure(1);
247ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell    }
248ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell
249ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell    private void reset() {
250ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell        if (mPrevEvent != null) {
251ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell            mPrevEvent.recycle();
252ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell            mPrevEvent = null;
253ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell        }
254ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell        if (mCurrEvent != null) {
255ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell            mCurrEvent.recycle();
256ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell            mCurrEvent = null;
257ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell        }
258ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell    }
259ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell
260ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell    /**
261ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell     * Returns {@code true} if a two-finger scale gesture is in progress.
262ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell     * @return {@code true} if a scale gesture is in progress, {@code false} otherwise.
263ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell     */
264ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell    public boolean isInProgress() {
265ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell        return mGestureInProgress;
266ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell    }
267ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell
268ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell    /**
269ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell     * Get the X coordinate of the current gesture's focal point.
270ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell     * If a gesture is in progress, the focal point is directly between
271ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell     * the two pointers forming the gesture.
272ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell     * If a gesture is ending, the focal point is the location of the
273ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell     * remaining pointer on the screen.
274ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell     * If {@link isInProgress()} would return false, the result of this
275ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell     * function is undefined.
276ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell     *
277ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell     * @return X coordinate of the focal point in pixels.
278ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell     */
279ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell    public float getFocusX() {
280ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell        return mFocusX;
281ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell    }
282ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell
283ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell    /**
284ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell     * Get the Y coordinate of the current gesture's focal point.
285ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell     * If a gesture is in progress, the focal point is directly between
286ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell     * the two pointers forming the gesture.
287ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell     * If a gesture is ending, the focal point is the location of the
288ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell     * remaining pointer on the screen.
289ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell     * If {@link isInProgress()} would return false, the result of this
290ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell     * function is undefined.
291ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell     *
292ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell     * @return Y coordinate of the focal point in pixels.
293ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell     */
294ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell    public float getFocusY() {
295ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell        return mFocusY;
296ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell    }
297ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell
298ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell    /**
299ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell     * Return the current distance between the two pointers forming the
300ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell     * gesture in progress.
301ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell     *
302ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell     * @return Distance between pointers in pixels.
303ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell     */
304ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell    public float getCurrentSpan() {
305ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell        if (mCurrLen == -1) {
306ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell            final float cvx = mCurrFingerDiffX;
307ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell            final float cvy = mCurrFingerDiffY;
308ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell            mCurrLen = (float)Math.sqrt(cvx*cvx + cvy*cvy);
309ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell        }
310ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell        return mCurrLen;
311ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell    }
312ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell
313ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell    /**
314ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell     * Return the previous distance between the two pointers forming the
315ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell     * gesture in progress.
316ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell     *
317ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell     * @return Previous distance between pointers in pixels.
318ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell     */
319ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell    public float getPreviousSpan() {
320ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell        if (mPrevLen == -1) {
321ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell            final float pvx = mPrevFingerDiffX;
322ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell            final float pvy = mPrevFingerDiffY;
323ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell            mPrevLen = (float)Math.sqrt(pvx*pvx + pvy*pvy);
324ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell        }
325ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell        return mPrevLen;
326ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell    }
327ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell
328ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell    /**
329ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell     * Return the scaling factor from the previous scale event to the current
330ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell     * event. This value is defined as
331ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell     * ({@link getCurrentSpan()} / {@link getPreviousSpan()}).
332ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell     *
333ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell     * @return The current scaling factor.
334ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell     */
335ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell    public float getScaleFactor() {
336ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell        if (mScaleFactor == -1) {
337ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell            mScaleFactor = getCurrentSpan() / getPreviousSpan();
338ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell        }
339ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell        return mScaleFactor;
340ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell    }
341ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell
342ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell    /**
343ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell     * Return the time difference in milliseconds between the previous
344ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell     * accepted scaling event and the current scaling event.
345ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell     *
346ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell     * @return Time difference since the last scaling event in milliseconds.
347ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell     */
348ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell    public long getTimeDelta() {
349ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell        return mTimeDelta;
350ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell    }
351ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell
352ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell    /**
353ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell     * Return the event time of the current event being processed.
354ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell     *
355ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell     * @return Current event time in milliseconds.
356ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell     */
357ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell    public long getEventTime() {
358ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell        return mCurrEvent.getEventTime();
359ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell    }
360ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell}
361