ScaleGestureDetector.java revision d5ada83c719d366d3063572ca6ce5ab8918fd39b
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;
21380b525220955ce4e4df8943b89082c7443ebfddAdam Powellimport android.util.Log;
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.
28ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell *
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 * @hide Pending API approval
38ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell */
39ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powellpublic class ScaleGestureDetector {
40ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell    /**
41ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell     * The listener for receiving notifications when gestures occur.
42ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell     * If you want to listen for all the different gestures then implement
43ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell     * this interface. If you only want to listen for a subset it might
44ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell     * be easier to extend {@link SimpleOnScaleGestureListener}.
45ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell     *
46ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell     * An application will receive events in the following order:
47ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell     * <ul>
48ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell     *  <li>One {@link OnScaleGestureListener#onScaleBegin()}
49ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell     *  <li>Zero or more {@link OnScaleGestureListener#onScale()}
50ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell     *  <li>One {@link OnScaleGestureListener#onTransformEnd()}
51ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell     * </ul>
52ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell     */
53ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell    public interface OnScaleGestureListener {
54ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell        /**
55ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell         * Responds to scaling events for a gesture in progress.
56ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell         * Reported by pointer motion.
57ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell         *
58ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell         * @param detector The detector reporting the event - use this to
59ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell         *          retrieve extended info about event state.
60ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell         * @return Whether or not the detector should consider this event
61ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell         *          as handled. If an event was not handled, the detector
62ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell         *          will continue to accumulate movement until an event is
63ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell         *          handled. This can be useful if an application, for example,
64ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell         *          only wants to update scaling factors if the change is
65ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell         *          greater than 0.01.
66ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell         */
67ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell        public boolean onScale(ScaleGestureDetector detector);
68ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell
69ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell        /**
70ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell         * Responds to the beginning of a scaling gesture. Reported by
71ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell         * new pointers going down.
72ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell         *
73ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell         * @param detector The detector reporting the event - use this to
74ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell         *          retrieve extended info about event state.
75ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell         * @return Whether or not the detector should continue recognizing
76ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell         *          this gesture. For example, if a gesture is beginning
77ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell         *          with a focal point outside of a region where it makes
78ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell         *          sense, onScaleBegin() may return false to ignore the
79ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell         *          rest of the gesture.
80ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell         */
81ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell        public boolean onScaleBegin(ScaleGestureDetector detector);
82ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell
83ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell        /**
84ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell         * Responds to the end of a scale gesture. Reported by existing
85ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell         * pointers going up. If the end of a gesture would result in a fling,
86ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell         * {@link onTransformFling()} is called instead.
87ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell         *
88ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell         * Once a scale has ended, {@link ScaleGestureDetector#getFocusX()}
89ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell         * and {@link ScaleGestureDetector#getFocusY()} will return the location
90ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell         * of the pointer remaining on the screen.
91ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell         *
92ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell         * @param detector The detector reporting the event - use this to
93ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell         *          retrieve extended info about event state.
94ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell         */
95ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell        public void onScaleEnd(ScaleGestureDetector detector);
96ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell    }
97ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell
98ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell    /**
99ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell     * A convenience class to extend when you only want to listen for a subset
100ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell     * of scaling-related events. This implements all methods in
101ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell     * {@link OnScaleGestureListener} but does nothing.
102ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell     * {@link OnScaleGestureListener#onScale(ScaleGestureDetector)} and
103ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell     * {@link OnScaleGestureListener#onScaleBegin(ScaleGestureDetector)} return
104ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell     * {@code true}.
105ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell     */
106ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell    public class SimpleOnScaleGestureListener implements OnScaleGestureListener {
107ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell
108ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell        public boolean onScale(ScaleGestureDetector detector) {
109ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell            return true;
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
121ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell    private static final float PRESSURE_THRESHOLD = 0.67f;
122ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell
123ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell    private Context mContext;
124ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell    private OnScaleGestureListener mListener;
125ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell    private boolean mGestureInProgress;
126ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell
127ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell    private MotionEvent mPrevEvent;
128ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell    private MotionEvent mCurrEvent;
129ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell
130ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell    private float mFocusX;
131ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell    private float mFocusY;
132ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell    private float mPrevFingerDiffX;
133ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell    private float mPrevFingerDiffY;
134ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell    private float mCurrFingerDiffX;
135ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell    private float mCurrFingerDiffY;
136ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell    private float mCurrLen;
137ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell    private float mPrevLen;
138ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell    private float mScaleFactor;
139ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell    private float mCurrPressure;
140ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell    private float mPrevPressure;
141ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell    private long mTimeDelta;
142380b525220955ce4e4df8943b89082c7443ebfddAdam Powell
143380b525220955ce4e4df8943b89082c7443ebfddAdam Powell    private float mEdgeSlop;
144380b525220955ce4e4df8943b89082c7443ebfddAdam Powell    private float mRightSlopEdge;
145380b525220955ce4e4df8943b89082c7443ebfddAdam Powell    private float mBottomSlopEdge;
146380b525220955ce4e4df8943b89082c7443ebfddAdam Powell    private boolean mSloppyGesture;
147ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell
148ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell    public ScaleGestureDetector(Context context, OnScaleGestureListener listener) {
149380b525220955ce4e4df8943b89082c7443ebfddAdam Powell        ViewConfiguration config = ViewConfiguration.get(context);
150ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell        mContext = context;
151ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell        mListener = listener;
152380b525220955ce4e4df8943b89082c7443ebfddAdam Powell        mEdgeSlop = config.getScaledEdgeSlop();
153ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell    }
154ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell
155ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell    public boolean onTouchEvent(MotionEvent event) {
156ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell        final int action = event.getAction();
157ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell        boolean handled = true;
158ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell
159ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell        if (!mGestureInProgress) {
160ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell            if ((action == MotionEvent.ACTION_POINTER_1_DOWN ||
161ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell                    action == MotionEvent.ACTION_POINTER_2_DOWN) &&
162ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell                    event.getPointerCount() >= 2) {
163ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell                // We have a new multi-finger gesture
164380b525220955ce4e4df8943b89082c7443ebfddAdam Powell
165d5ada83c719d366d3063572ca6ce5ab8918fd39bGrace Kloba                // as orientation can change, query the metrics in touch down
166d5ada83c719d366d3063572ca6ce5ab8918fd39bGrace Kloba                DisplayMetrics metrics = mContext.getResources().getDisplayMetrics();
167d5ada83c719d366d3063572ca6ce5ab8918fd39bGrace Kloba                mRightSlopEdge = metrics.widthPixels - mEdgeSlop;
168d5ada83c719d366d3063572ca6ce5ab8918fd39bGrace Kloba                mBottomSlopEdge = metrics.heightPixels - mEdgeSlop;
169d5ada83c719d366d3063572ca6ce5ab8918fd39bGrace Kloba
170ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell                // Be paranoid in case we missed an event
171ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell                reset();
172380b525220955ce4e4df8943b89082c7443ebfddAdam Powell
173ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell                mPrevEvent = MotionEvent.obtain(event);
174ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell                mTimeDelta = 0;
175380b525220955ce4e4df8943b89082c7443ebfddAdam Powell
176ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell                setContext(event);
177380b525220955ce4e4df8943b89082c7443ebfddAdam Powell
178380b525220955ce4e4df8943b89082c7443ebfddAdam Powell                // Check if we have a sloppy gesture. If so, delay
179380b525220955ce4e4df8943b89082c7443ebfddAdam Powell                // the beginning of the gesture until we're sure that's
180380b525220955ce4e4df8943b89082c7443ebfddAdam Powell                // what the user wanted. Sloppy gestures can happen if the
181380b525220955ce4e4df8943b89082c7443ebfddAdam Powell                // edge of the user's hand is touching the screen, for example.
182380b525220955ce4e4df8943b89082c7443ebfddAdam Powell                final float edgeSlop = mEdgeSlop;
183380b525220955ce4e4df8943b89082c7443ebfddAdam Powell                final float rightSlop = mRightSlopEdge;
184380b525220955ce4e4df8943b89082c7443ebfddAdam Powell                final float bottomSlop = mBottomSlopEdge;
185380b525220955ce4e4df8943b89082c7443ebfddAdam Powell                final float x0 = event.getRawX();
186380b525220955ce4e4df8943b89082c7443ebfddAdam Powell                final float y0 = event.getRawY();
187380b525220955ce4e4df8943b89082c7443ebfddAdam Powell                final float x1 = getRawX(event, 1);
188380b525220955ce4e4df8943b89082c7443ebfddAdam Powell                final float y1 = getRawY(event, 1);
189380b525220955ce4e4df8943b89082c7443ebfddAdam Powell
1908f9fbb0c45265a6f5da51cb3bc35362d8e5bc900Grace Kloba                boolean p0sloppy = x0 < edgeSlop || y0 < edgeSlop
1918f9fbb0c45265a6f5da51cb3bc35362d8e5bc900Grace Kloba                        || x0 > rightSlop || y0 > bottomSlop;
1928f9fbb0c45265a6f5da51cb3bc35362d8e5bc900Grace Kloba                boolean p1sloppy = x1 < edgeSlop || y1 < edgeSlop
1938f9fbb0c45265a6f5da51cb3bc35362d8e5bc900Grace Kloba                        || x1 > rightSlop || y1 > bottomSlop;
194380b525220955ce4e4df8943b89082c7443ebfddAdam Powell
1958f9fbb0c45265a6f5da51cb3bc35362d8e5bc900Grace Kloba                if(p0sloppy && p1sloppy) {
1968f9fbb0c45265a6f5da51cb3bc35362d8e5bc900Grace Kloba                    mFocusX = -1;
1978f9fbb0c45265a6f5da51cb3bc35362d8e5bc900Grace Kloba                    mFocusY = -1;
1988f9fbb0c45265a6f5da51cb3bc35362d8e5bc900Grace Kloba                    mSloppyGesture = true;
1998f9fbb0c45265a6f5da51cb3bc35362d8e5bc900Grace Kloba                } else if (p0sloppy) {
200380b525220955ce4e4df8943b89082c7443ebfddAdam Powell                    mFocusX = event.getX(1);
201380b525220955ce4e4df8943b89082c7443ebfddAdam Powell                    mFocusY = event.getY(1);
202380b525220955ce4e4df8943b89082c7443ebfddAdam Powell                    mSloppyGesture = true;
203380b525220955ce4e4df8943b89082c7443ebfddAdam Powell                } else if (p1sloppy) {
204380b525220955ce4e4df8943b89082c7443ebfddAdam Powell                    mFocusX = event.getX(0);
205380b525220955ce4e4df8943b89082c7443ebfddAdam Powell                    mFocusY = event.getY(0);
206380b525220955ce4e4df8943b89082c7443ebfddAdam Powell                    mSloppyGesture = true;
207380b525220955ce4e4df8943b89082c7443ebfddAdam Powell                } else {
208380b525220955ce4e4df8943b89082c7443ebfddAdam Powell                    mGestureInProgress = mListener.onScaleBegin(this);
209380b525220955ce4e4df8943b89082c7443ebfddAdam Powell                }
210380b525220955ce4e4df8943b89082c7443ebfddAdam Powell            } else if (action == MotionEvent.ACTION_MOVE && mSloppyGesture) {
211380b525220955ce4e4df8943b89082c7443ebfddAdam Powell                // Initiate sloppy gestures if we've moved outside of the slop area.
212380b525220955ce4e4df8943b89082c7443ebfddAdam Powell                final float edgeSlop = mEdgeSlop;
213380b525220955ce4e4df8943b89082c7443ebfddAdam Powell                final float rightSlop = mRightSlopEdge;
214380b525220955ce4e4df8943b89082c7443ebfddAdam Powell                final float bottomSlop = mBottomSlopEdge;
215380b525220955ce4e4df8943b89082c7443ebfddAdam Powell                final float x0 = event.getRawX();
216380b525220955ce4e4df8943b89082c7443ebfddAdam Powell                final float y0 = event.getRawY();
217380b525220955ce4e4df8943b89082c7443ebfddAdam Powell                final float x1 = getRawX(event, 1);
218380b525220955ce4e4df8943b89082c7443ebfddAdam Powell                final float y1 = getRawY(event, 1);
219380b525220955ce4e4df8943b89082c7443ebfddAdam Powell
2208f9fbb0c45265a6f5da51cb3bc35362d8e5bc900Grace Kloba                boolean p0sloppy = x0 < edgeSlop || y0 < edgeSlop
2218f9fbb0c45265a6f5da51cb3bc35362d8e5bc900Grace Kloba                        || x0 > rightSlop || y0 > bottomSlop;
2228f9fbb0c45265a6f5da51cb3bc35362d8e5bc900Grace Kloba                boolean p1sloppy = x1 < edgeSlop || y1 < edgeSlop
2238f9fbb0c45265a6f5da51cb3bc35362d8e5bc900Grace Kloba                        || x1 > rightSlop || y1 > bottomSlop;
224380b525220955ce4e4df8943b89082c7443ebfddAdam Powell
2258f9fbb0c45265a6f5da51cb3bc35362d8e5bc900Grace Kloba                if(p0sloppy && p1sloppy) {
2268f9fbb0c45265a6f5da51cb3bc35362d8e5bc900Grace Kloba                    mFocusX = -1;
2278f9fbb0c45265a6f5da51cb3bc35362d8e5bc900Grace Kloba                    mFocusY = -1;
2288f9fbb0c45265a6f5da51cb3bc35362d8e5bc900Grace Kloba                } else if (p0sloppy) {
229380b525220955ce4e4df8943b89082c7443ebfddAdam Powell                    mFocusX = event.getX(1);
230380b525220955ce4e4df8943b89082c7443ebfddAdam Powell                    mFocusY = event.getY(1);
231380b525220955ce4e4df8943b89082c7443ebfddAdam Powell                } else if (p1sloppy) {
232380b525220955ce4e4df8943b89082c7443ebfddAdam Powell                    mFocusX = event.getX(0);
233380b525220955ce4e4df8943b89082c7443ebfddAdam Powell                    mFocusY = event.getY(0);
234380b525220955ce4e4df8943b89082c7443ebfddAdam Powell                } else {
235380b525220955ce4e4df8943b89082c7443ebfddAdam Powell                    mSloppyGesture = false;
236380b525220955ce4e4df8943b89082c7443ebfddAdam Powell                    mGestureInProgress = mListener.onScaleBegin(this);
237380b525220955ce4e4df8943b89082c7443ebfddAdam Powell                }
2388f9fbb0c45265a6f5da51cb3bc35362d8e5bc900Grace Kloba            } else if ((action == MotionEvent.ACTION_POINTER_1_UP
2398f9fbb0c45265a6f5da51cb3bc35362d8e5bc900Grace Kloba                    || action == MotionEvent.ACTION_POINTER_2_UP)
2408f9fbb0c45265a6f5da51cb3bc35362d8e5bc900Grace Kloba                    && mSloppyGesture) {
2418f9fbb0c45265a6f5da51cb3bc35362d8e5bc900Grace Kloba                // Set focus point to the remaining finger
2428f9fbb0c45265a6f5da51cb3bc35362d8e5bc900Grace Kloba                int id = (((action & MotionEvent.ACTION_POINTER_ID_MASK)
2438f9fbb0c45265a6f5da51cb3bc35362d8e5bc900Grace Kloba                        >> MotionEvent.ACTION_POINTER_ID_SHIFT) == 0) ? 1 : 0;
2448f9fbb0c45265a6f5da51cb3bc35362d8e5bc900Grace Kloba                mFocusX = event.getX(id);
2458f9fbb0c45265a6f5da51cb3bc35362d8e5bc900Grace Kloba                mFocusY = event.getY(id);
246ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell            }
247ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell        } else {
248ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell            // Transform gesture in progress - attempt to handle it
249ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell            switch (action) {
250ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell                case MotionEvent.ACTION_POINTER_1_UP:
251ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell                case MotionEvent.ACTION_POINTER_2_UP:
252ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell                    // Gesture ended
253ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell                    setContext(event);
254380b525220955ce4e4df8943b89082c7443ebfddAdam Powell
255ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell                    // Set focus point to the remaining finger
256ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell                    int id = (((action & MotionEvent.ACTION_POINTER_ID_MASK)
257ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell                            >> MotionEvent.ACTION_POINTER_ID_SHIFT) == 0) ? 1 : 0;
258ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell                    mFocusX = event.getX(id);
259ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell                    mFocusY = event.getY(id);
260380b525220955ce4e4df8943b89082c7443ebfddAdam Powell
261380b525220955ce4e4df8943b89082c7443ebfddAdam Powell                    if (!mSloppyGesture) {
262380b525220955ce4e4df8943b89082c7443ebfddAdam Powell                        mListener.onScaleEnd(this);
263380b525220955ce4e4df8943b89082c7443ebfddAdam Powell                    }
264ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell
265ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell                    reset();
266ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell                    break;
267ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell
268ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell                case MotionEvent.ACTION_CANCEL:
269380b525220955ce4e4df8943b89082c7443ebfddAdam Powell                    if (!mSloppyGesture) {
270380b525220955ce4e4df8943b89082c7443ebfddAdam Powell                        mListener.onScaleEnd(this);
271380b525220955ce4e4df8943b89082c7443ebfddAdam Powell                    }
272ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell
273ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell                    reset();
274ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell                    break;
275ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell
276ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell                case MotionEvent.ACTION_MOVE:
277ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell                    setContext(event);
278ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell
279ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell                    // Only accept the event if our relative pressure is within
280ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell                    // a certain limit - this can help filter shaky data as a
281ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell                    // finger is lifted.
282ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell                    if (mCurrPressure / mPrevPressure > PRESSURE_THRESHOLD) {
283ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell                        final boolean updatePrevious = mListener.onScale(this);
284ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell
285ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell                        if (updatePrevious) {
286ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell                            mPrevEvent.recycle();
287ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell                            mPrevEvent = MotionEvent.obtain(event);
288ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell                        }
289ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell                    }
290ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell                    break;
291ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell            }
292ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell        }
293ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell        return handled;
294ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell    }
295380b525220955ce4e4df8943b89082c7443ebfddAdam Powell
296380b525220955ce4e4df8943b89082c7443ebfddAdam Powell    /**
297380b525220955ce4e4df8943b89082c7443ebfddAdam Powell     * MotionEvent has no getRawX(int) method; simulate it pending future API approval.
298380b525220955ce4e4df8943b89082c7443ebfddAdam Powell     */
299380b525220955ce4e4df8943b89082c7443ebfddAdam Powell    private static float getRawX(MotionEvent event, int pointerIndex) {
300380b525220955ce4e4df8943b89082c7443ebfddAdam Powell        float offset = event.getX() - event.getRawX();
301380b525220955ce4e4df8943b89082c7443ebfddAdam Powell        return event.getX(pointerIndex) + offset;
302380b525220955ce4e4df8943b89082c7443ebfddAdam Powell    }
303380b525220955ce4e4df8943b89082c7443ebfddAdam Powell
304380b525220955ce4e4df8943b89082c7443ebfddAdam Powell    /**
305380b525220955ce4e4df8943b89082c7443ebfddAdam Powell     * MotionEvent has no getRawY(int) method; simulate it pending future API approval.
306380b525220955ce4e4df8943b89082c7443ebfddAdam Powell     */
307380b525220955ce4e4df8943b89082c7443ebfddAdam Powell    private static float getRawY(MotionEvent event, int pointerIndex) {
308380b525220955ce4e4df8943b89082c7443ebfddAdam Powell        float offset = event.getY() - event.getRawY();
309380b525220955ce4e4df8943b89082c7443ebfddAdam Powell        return event.getY(pointerIndex) + offset;
310380b525220955ce4e4df8943b89082c7443ebfddAdam Powell    }
311ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell
312ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell    private void setContext(MotionEvent curr) {
313ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell        if (mCurrEvent != null) {
314ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell            mCurrEvent.recycle();
315ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell        }
316ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell        mCurrEvent = MotionEvent.obtain(curr);
317ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell
318ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell        mCurrLen = -1;
319ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell        mPrevLen = -1;
320ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell        mScaleFactor = -1;
321ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell
322ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell        final MotionEvent prev = mPrevEvent;
323ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell
324ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell        final float px0 = prev.getX(0);
325ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell        final float py0 = prev.getY(0);
326ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell        final float px1 = prev.getX(1);
327ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell        final float py1 = prev.getY(1);
328ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell        final float cx0 = curr.getX(0);
329ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell        final float cy0 = curr.getY(0);
330ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell        final float cx1 = curr.getX(1);
331ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell        final float cy1 = curr.getY(1);
332ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell
333ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell        final float pvx = px1 - px0;
334ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell        final float pvy = py1 - py0;
335ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell        final float cvx = cx1 - cx0;
336ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell        final float cvy = cy1 - cy0;
337ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell        mPrevFingerDiffX = pvx;
338ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell        mPrevFingerDiffY = pvy;
339ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell        mCurrFingerDiffX = cvx;
340ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell        mCurrFingerDiffY = cvy;
341ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell
342ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell        mFocusX = cx0 + cvx * 0.5f;
343ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell        mFocusY = cy0 + cvy * 0.5f;
344ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell        mTimeDelta = curr.getEventTime() - prev.getEventTime();
345ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell        mCurrPressure = curr.getPressure(0) + curr.getPressure(1);
346ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell        mPrevPressure = prev.getPressure(0) + prev.getPressure(1);
347ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell    }
348ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell
349ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell    private void reset() {
350ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell        if (mPrevEvent != null) {
351ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell            mPrevEvent.recycle();
352ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell            mPrevEvent = null;
353ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell        }
354ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell        if (mCurrEvent != null) {
355ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell            mCurrEvent.recycle();
356ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell            mCurrEvent = null;
357ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell        }
358380b525220955ce4e4df8943b89082c7443ebfddAdam Powell        mSloppyGesture = false;
359380b525220955ce4e4df8943b89082c7443ebfddAdam Powell        mGestureInProgress = false;
360ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell    }
361ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell
362ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell    /**
363ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell     * Returns {@code true} if a two-finger scale gesture is in progress.
364ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell     * @return {@code true} if a scale gesture is in progress, {@code false} otherwise.
365ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell     */
366ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell    public boolean isInProgress() {
367ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell        return mGestureInProgress;
368ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell    }
369ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell
370ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell    /**
371ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell     * Get the X coordinate of the current gesture's focal point.
372ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell     * If a gesture is in progress, the focal point is directly between
373ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell     * the two pointers forming the gesture.
374ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell     * If a gesture is ending, the focal point is the location of the
375ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell     * remaining pointer on the screen.
376ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell     * If {@link isInProgress()} would return false, the result of this
377ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell     * function is undefined.
378ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell     *
379ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell     * @return X coordinate of the focal point in pixels.
380ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell     */
381ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell    public float getFocusX() {
382ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell        return mFocusX;
383ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell    }
384ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell
385ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell    /**
386ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell     * Get the Y 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.
391ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell     * If {@link isInProgress()} would return false, the result of this
392ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell     * function is undefined.
393ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell     *
394ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell     * @return Y coordinate of the focal point in pixels.
395ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell     */
396ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell    public float getFocusY() {
397ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell        return mFocusY;
398ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell    }
399ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell
400ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell    /**
401ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell     * Return the current distance between the two pointers forming the
402ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell     * gesture in progress.
403ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell     *
404ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell     * @return Distance between pointers in pixels.
405ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell     */
406ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell    public float getCurrentSpan() {
407ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell        if (mCurrLen == -1) {
408ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell            final float cvx = mCurrFingerDiffX;
409ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell            final float cvy = mCurrFingerDiffY;
410ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell            mCurrLen = (float)Math.sqrt(cvx*cvx + cvy*cvy);
411ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell        }
412ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell        return mCurrLen;
413ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell    }
414ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell
415ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell    /**
416ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell     * Return the previous distance between the two pointers forming the
417ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell     * gesture in progress.
418ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell     *
419ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell     * @return Previous distance between pointers in pixels.
420ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell     */
421ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell    public float getPreviousSpan() {
422ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell        if (mPrevLen == -1) {
423ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell            final float pvx = mPrevFingerDiffX;
424ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell            final float pvy = mPrevFingerDiffY;
425ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell            mPrevLen = (float)Math.sqrt(pvx*pvx + pvy*pvy);
426ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell        }
427ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell        return mPrevLen;
428ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell    }
429ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell
430ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell    /**
431ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell     * Return the scaling factor from the previous scale event to the current
432ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell     * event. This value is defined as
433ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell     * ({@link getCurrentSpan()} / {@link getPreviousSpan()}).
434ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell     *
435ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell     * @return The current scaling factor.
436ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell     */
437ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell    public float getScaleFactor() {
438ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell        if (mScaleFactor == -1) {
439ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell            mScaleFactor = getCurrentSpan() / getPreviousSpan();
440ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell        }
441ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell        return mScaleFactor;
442ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell    }
443ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell
444ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell    /**
445ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell     * Return the time difference in milliseconds between the previous
446ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell     * accepted scaling event and the current scaling event.
447ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell     *
448ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell     * @return Time difference since the last scaling event in milliseconds.
449ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell     */
450ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell    public long getTimeDelta() {
451ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell        return mTimeDelta;
452ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell    }
453ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell
454ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell    /**
455ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell     * Return the event time of the current event being processed.
456ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell     *
457ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell     * @return Current event time in milliseconds.
458ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell     */
459ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell    public long getEventTime() {
460ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell        return mCurrEvent.getEventTime();
461ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell    }
462ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell}
463