blob: ee3f5d8e266b3ce36ecb576c0cb88879973aaedc [file] [log] [blame]
Adam Powellae542ff2010-01-13 16:29:27 -08001/*
2 * Copyright (C) 2010 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package android.view;
18
19import android.content.Context;
Adam Powell33079582012-10-09 11:20:39 -070020import android.content.res.Resources;
Adam Powella4ce6ae2012-09-24 20:13:23 -070021import android.os.SystemClock;
Adam Powell346c8fb2010-03-12 10:52:35 -080022import android.util.FloatMath;
Adam Powella4ce6ae2012-09-24 20:13:23 -070023
Adam Powellae542ff2010-01-13 16:29:27 -080024/**
Adam Powell618cbea2012-08-27 17:44:59 -070025 * Detects scaling transformation gestures using the supplied {@link MotionEvent}s.
26 * The {@link OnScaleGestureListener} callback will notify users when a particular
27 * gesture event has occurred.
28 *
Adam Powellae542ff2010-01-13 16:29:27 -080029 * This class should only be used with {@link MotionEvent}s reported via touch.
Erik47c41e82010-09-01 15:39:08 -070030 *
Adam Powellae542ff2010-01-13 16:29:27 -080031 * To use this class:
32 * <ul>
33 * <li>Create an instance of the {@code ScaleGestureDetector} for your
34 * {@link View}
35 * <li>In the {@link View#onTouchEvent(MotionEvent)} method ensure you call
36 * {@link #onTouchEvent(MotionEvent)}. The methods defined in your
37 * callback will be executed when the events occur.
38 * </ul>
Adam Powellae542ff2010-01-13 16:29:27 -080039 */
40public class ScaleGestureDetector {
Adam Powell08180202011-03-07 16:48:29 -080041 private static final String TAG = "ScaleGestureDetector";
42
Adam Powellae542ff2010-01-13 16:29:27 -080043 /**
44 * The listener for receiving notifications when gestures occur.
45 * If you want to listen for all the different gestures then implement
46 * this interface. If you only want to listen for a subset it might
47 * be easier to extend {@link SimpleOnScaleGestureListener}.
Erik47c41e82010-09-01 15:39:08 -070048 *
Adam Powellae542ff2010-01-13 16:29:27 -080049 * An application will receive events in the following order:
50 * <ul>
Adam Powellab905c872010-02-03 11:01:58 -080051 * <li>One {@link OnScaleGestureListener#onScaleBegin(ScaleGestureDetector)}
52 * <li>Zero or more {@link OnScaleGestureListener#onScale(ScaleGestureDetector)}
53 * <li>One {@link OnScaleGestureListener#onScaleEnd(ScaleGestureDetector)}
Adam Powellae542ff2010-01-13 16:29:27 -080054 * </ul>
55 */
56 public interface OnScaleGestureListener {
57 /**
58 * Responds to scaling events for a gesture in progress.
59 * Reported by pointer motion.
Erik47c41e82010-09-01 15:39:08 -070060 *
Adam Powellae542ff2010-01-13 16:29:27 -080061 * @param detector The detector reporting the event - use this to
62 * retrieve extended info about event state.
63 * @return Whether or not the detector should consider this event
64 * as handled. If an event was not handled, the detector
65 * will continue to accumulate movement until an event is
66 * handled. This can be useful if an application, for example,
67 * only wants to update scaling factors if the change is
68 * greater than 0.01.
69 */
70 public boolean onScale(ScaleGestureDetector detector);
71
72 /**
73 * Responds to the beginning of a scaling gesture. Reported by
74 * new pointers going down.
Erik47c41e82010-09-01 15:39:08 -070075 *
Adam Powellae542ff2010-01-13 16:29:27 -080076 * @param detector The detector reporting the event - use this to
77 * retrieve extended info about event state.
78 * @return Whether or not the detector should continue recognizing
79 * this gesture. For example, if a gesture is beginning
80 * with a focal point outside of a region where it makes
81 * sense, onScaleBegin() may return false to ignore the
82 * rest of the gesture.
83 */
84 public boolean onScaleBegin(ScaleGestureDetector detector);
85
86 /**
87 * Responds to the end of a scale gesture. Reported by existing
Adam Powell216bccf2010-02-01 15:03:17 -080088 * pointers going up.
Erik47c41e82010-09-01 15:39:08 -070089 *
Adam Powellae542ff2010-01-13 16:29:27 -080090 * Once a scale has ended, {@link ScaleGestureDetector#getFocusX()}
Adam Powell47ec2fb2012-08-31 17:15:32 -070091 * and {@link ScaleGestureDetector#getFocusY()} will return focal point
92 * of the pointers remaining on the screen.
Erik47c41e82010-09-01 15:39:08 -070093 *
Adam Powellae542ff2010-01-13 16:29:27 -080094 * @param detector The detector reporting the event - use this to
95 * retrieve extended info about event state.
96 */
97 public void onScaleEnd(ScaleGestureDetector detector);
98 }
Erik47c41e82010-09-01 15:39:08 -070099
Adam Powellae542ff2010-01-13 16:29:27 -0800100 /**
101 * A convenience class to extend when you only want to listen for a subset
102 * of scaling-related events. This implements all methods in
103 * {@link OnScaleGestureListener} but does nothing.
Adam Powell346c8fb2010-03-12 10:52:35 -0800104 * {@link OnScaleGestureListener#onScale(ScaleGestureDetector)} returns
105 * {@code false} so that a subclass can retrieve the accumulated scale
106 * factor in an overridden onScaleEnd.
107 * {@link OnScaleGestureListener#onScaleBegin(ScaleGestureDetector)} returns
Erik47c41e82010-09-01 15:39:08 -0700108 * {@code true}.
Adam Powellae542ff2010-01-13 16:29:27 -0800109 */
Adam Powell216bccf2010-02-01 15:03:17 -0800110 public static class SimpleOnScaleGestureListener implements OnScaleGestureListener {
Adam Powellae542ff2010-01-13 16:29:27 -0800111
112 public boolean onScale(ScaleGestureDetector detector) {
Adam Powell346c8fb2010-03-12 10:52:35 -0800113 return false;
Adam Powellae542ff2010-01-13 16:29:27 -0800114 }
115
116 public boolean onScaleBegin(ScaleGestureDetector detector) {
117 return true;
118 }
119
120 public void onScaleEnd(ScaleGestureDetector detector) {
121 // Intentionally empty
122 }
123 }
124
Adam Powell346c8fb2010-03-12 10:52:35 -0800125 private final Context mContext;
126 private final OnScaleGestureListener mListener;
Adam Powellae542ff2010-01-13 16:29:27 -0800127
128 private float mFocusX;
129 private float mFocusY;
Erik47c41e82010-09-01 15:39:08 -0700130
Adam Powell618cbea2012-08-27 17:44:59 -0700131 private float mCurrSpan;
132 private float mPrevSpan;
Adam Powell47ec2fb2012-08-31 17:15:32 -0700133 private float mInitialSpan;
Adam Powell618cbea2012-08-27 17:44:59 -0700134 private float mCurrSpanX;
135 private float mCurrSpanY;
136 private float mPrevSpanX;
137 private float mPrevSpanY;
138 private long mCurrTime;
139 private long mPrevTime;
140 private boolean mInProgress;
Adam Powell47ec2fb2012-08-31 17:15:32 -0700141 private int mSpanSlop;
Adam Powell828e56e2012-09-14 18:56:16 -0700142 private int mMinSpan;
Adam Powelle33cef82011-02-23 16:51:20 -0800143
Adam Powell5b5c4142012-10-02 16:25:30 -0700144 // Bounds for recently seen values
145 private float mTouchUpper;
146 private float mTouchLower;
147 private float mTouchHistoryLastAccepted;
148 private int mTouchHistoryDirection;
149 private long mTouchHistoryLastAcceptedTime;
150 private int mTouchMinMajor;
Adam Powella4ce6ae2012-09-24 20:13:23 -0700151
Adam Powelld736d202012-09-25 19:30:44 -0700152 private static final long TOUCH_STABILIZE_TIME = 128; // ms
Adam Powell5b5c4142012-10-02 16:25:30 -0700153 private static final int TOUCH_MIN_MAJOR = 48; // dp
Adam Powella4ce6ae2012-09-24 20:13:23 -0700154
Jeff Brown21bc5c92011-02-28 18:27:14 -0800155 /**
156 * Consistency verifier for debugging purposes.
157 */
158 private final InputEventConsistencyVerifier mInputEventConsistencyVerifier =
159 InputEventConsistencyVerifier.isInstrumentationEnabled() ?
160 new InputEventConsistencyVerifier(this, 0) : null;
161
Adam Powellae542ff2010-01-13 16:29:27 -0800162 public ScaleGestureDetector(Context context, OnScaleGestureListener listener) {
163 mContext = context;
164 mListener = listener;
Adam Powell47ec2fb2012-08-31 17:15:32 -0700165 mSpanSlop = ViewConfiguration.get(context).getScaledTouchSlop() * 2;
Adam Powell33079582012-10-09 11:20:39 -0700166
167 final Resources res = context.getResources();
168 mTouchMinMajor = res.getDimensionPixelSize(
169 com.android.internal.R.dimen.config_minScalingTouchMajor);
170 mMinSpan = res.getDimensionPixelSize(
Adam Powell828e56e2012-09-14 18:56:16 -0700171 com.android.internal.R.dimen.config_minScalingSpan);
Adam Powellae542ff2010-01-13 16:29:27 -0800172 }
173
Adam Powell618cbea2012-08-27 17:44:59 -0700174 /**
Adam Powella4ce6ae2012-09-24 20:13:23 -0700175 * The touchMajor/touchMinor elements of a MotionEvent can flutter/jitter on
176 * some hardware/driver combos. Smooth it out to get kinder, gentler behavior.
177 * @param ev MotionEvent to add to the ongoing history
178 */
179 private void addTouchHistory(MotionEvent ev) {
180 final long currentTime = SystemClock.uptimeMillis();
181 final int count = ev.getPointerCount();
Adam Powell5b5c4142012-10-02 16:25:30 -0700182 boolean accept = currentTime - mTouchHistoryLastAcceptedTime >= TOUCH_STABILIZE_TIME;
183 float total = 0;
184 int sampleCount = 0;
Adam Powella4ce6ae2012-09-24 20:13:23 -0700185 for (int i = 0; i < count; i++) {
Adam Powell5b5c4142012-10-02 16:25:30 -0700186 final boolean hasLastAccepted = !Float.isNaN(mTouchHistoryLastAccepted);
Adam Powella4ce6ae2012-09-24 20:13:23 -0700187 final int historySize = ev.getHistorySize();
Adam Powell5b5c4142012-10-02 16:25:30 -0700188 final int pointerSampleCount = historySize + 1;
189 for (int h = 0; h < pointerSampleCount; h++) {
190 float major;
Adam Powella4ce6ae2012-09-24 20:13:23 -0700191 if (h < historySize) {
192 major = ev.getHistoricalTouchMajor(i, h);
Adam Powella4ce6ae2012-09-24 20:13:23 -0700193 } else {
194 major = ev.getTouchMajor(i);
Adam Powella4ce6ae2012-09-24 20:13:23 -0700195 }
Adam Powell5b5c4142012-10-02 16:25:30 -0700196 if (major < mTouchMinMajor) major = mTouchMinMajor;
197 total += major;
198
199 if (Float.isNaN(mTouchUpper) || major > mTouchUpper) {
200 mTouchUpper = major;
201 }
202 if (Float.isNaN(mTouchLower) || major < mTouchLower) {
203 mTouchLower = major;
204 }
Adam Powella4ce6ae2012-09-24 20:13:23 -0700205
206 if (hasLastAccepted) {
Adam Powell5b5c4142012-10-02 16:25:30 -0700207 final int directionSig = (int) Math.signum(major - mTouchHistoryLastAccepted);
208 if (directionSig != mTouchHistoryDirection ||
209 (directionSig == 0 && mTouchHistoryDirection == 0)) {
210 mTouchHistoryDirection = directionSig;
Adam Powella4ce6ae2012-09-24 20:13:23 -0700211 final long time = h < historySize ? ev.getHistoricalEventTime(h)
212 : ev.getEventTime();
Adam Powell5b5c4142012-10-02 16:25:30 -0700213 mTouchHistoryLastAcceptedTime = time;
Adam Powella4ce6ae2012-09-24 20:13:23 -0700214 accept = false;
215 }
216 }
217 }
Adam Powell5b5c4142012-10-02 16:25:30 -0700218 sampleCount += pointerSampleCount;
Adam Powella4ce6ae2012-09-24 20:13:23 -0700219 }
Adam Powella4ce6ae2012-09-24 20:13:23 -0700220
Adam Powell5b5c4142012-10-02 16:25:30 -0700221 final float avg = total / sampleCount;
Adam Powella4ce6ae2012-09-24 20:13:23 -0700222
Adam Powell5b5c4142012-10-02 16:25:30 -0700223 if (accept) {
224 float newAccepted = (mTouchUpper + mTouchLower + avg) / 3;
225 mTouchUpper = (mTouchUpper + newAccepted) / 2;
226 mTouchLower = (mTouchLower + newAccepted) / 2;
227 mTouchHistoryLastAccepted = newAccepted;
228 mTouchHistoryDirection = 0;
229 mTouchHistoryLastAcceptedTime = ev.getEventTime();
Adam Powellf3a2bf82012-09-28 12:05:10 -0700230 }
Adam Powella4ce6ae2012-09-24 20:13:23 -0700231 }
232
233 /**
234 * Clear all touch history tracking. Useful in ACTION_CANCEL or ACTION_UP.
235 * @see #addTouchHistory(MotionEvent)
236 */
237 private void clearTouchHistory() {
Adam Powell5b5c4142012-10-02 16:25:30 -0700238 mTouchUpper = Float.NaN;
239 mTouchLower = Float.NaN;
240 mTouchHistoryLastAccepted = Float.NaN;
241 mTouchHistoryDirection = 0;
242 mTouchHistoryLastAcceptedTime = 0;
Adam Powella4ce6ae2012-09-24 20:13:23 -0700243 }
244
245 /**
Adam Powell618cbea2012-08-27 17:44:59 -0700246 * Accepts MotionEvents and dispatches events to a {@link OnScaleGestureListener}
247 * when appropriate.
248 *
249 * <p>Applications should pass a complete and consistent event stream to this method.
250 * A complete and consistent event stream involves all MotionEvents from the initial
251 * ACTION_DOWN to the final ACTION_UP or ACTION_CANCEL.</p>
252 *
253 * @param event The event to process
254 * @return true if the event was processed and the detector wants to receive the
255 * rest of the MotionEvents in this event stream.
256 */
Adam Powellae542ff2010-01-13 16:29:27 -0800257 public boolean onTouchEvent(MotionEvent event) {
Jeff Brown21bc5c92011-02-28 18:27:14 -0800258 if (mInputEventConsistencyVerifier != null) {
259 mInputEventConsistencyVerifier.onTouchEvent(event, 0);
260 }
261
Adam Powelle33cef82011-02-23 16:51:20 -0800262 final int action = event.getActionMasked();
Adam Powellae542ff2010-01-13 16:29:27 -0800263
Adam Powell618cbea2012-08-27 17:44:59 -0700264 final boolean streamComplete = action == MotionEvent.ACTION_UP ||
265 action == MotionEvent.ACTION_CANCEL;
266 if (action == MotionEvent.ACTION_DOWN || streamComplete) {
267 // Reset any scale in progress with the listener.
268 // If it's an ACTION_DOWN we're beginning a new event stream.
269 // This means the app probably didn't give us all the events. Shame on it.
270 if (mInProgress) {
Adam Powelld0197f32011-03-17 00:09:03 -0700271 mListener.onScaleEnd(this);
Adam Powell618cbea2012-08-27 17:44:59 -0700272 mInProgress = false;
Adam Powell47ec2fb2012-08-31 17:15:32 -0700273 mInitialSpan = 0;
Adam Powelld0197f32011-03-17 00:09:03 -0700274 }
Adam Powell618cbea2012-08-27 17:44:59 -0700275
276 if (streamComplete) {
Adam Powella4ce6ae2012-09-24 20:13:23 -0700277 clearTouchHistory();
Adam Powell618cbea2012-08-27 17:44:59 -0700278 return true;
279 }
Adam Powelld0197f32011-03-17 00:09:03 -0700280 }
281
Adam Powellabde0422012-09-26 17:12:50 -0700282 final boolean configChanged = action == MotionEvent.ACTION_DOWN ||
Adam Powell618cbea2012-08-27 17:44:59 -0700283 action == MotionEvent.ACTION_POINTER_UP ||
284 action == MotionEvent.ACTION_POINTER_DOWN;
285 final boolean pointerUp = action == MotionEvent.ACTION_POINTER_UP;
286 final int skipIndex = pointerUp ? event.getActionIndex() : -1;
Adam Powellae542ff2010-01-13 16:29:27 -0800287
Adam Powell618cbea2012-08-27 17:44:59 -0700288 // Determine focal point
289 float sumX = 0, sumY = 0;
290 final int count = event.getPointerCount();
291 for (int i = 0; i < count; i++) {
292 if (skipIndex == i) continue;
293 sumX += event.getX(i);
294 sumY += event.getY(i);
Adam Powellae542ff2010-01-13 16:29:27 -0800295 }
Adam Powell618cbea2012-08-27 17:44:59 -0700296 final int div = pointerUp ? count - 1 : count;
297 final float focusX = sumX / div;
298 final float focusY = sumY / div;
299
Adam Powell5b5c4142012-10-02 16:25:30 -0700300
301 addTouchHistory(event);
Adam Powella4ce6ae2012-09-24 20:13:23 -0700302
Adam Powell618cbea2012-08-27 17:44:59 -0700303 // Determine average deviation from focal point
304 float devSumX = 0, devSumY = 0;
305 for (int i = 0; i < count; i++) {
306 if (skipIndex == i) continue;
Adam Powell828e56e2012-09-14 18:56:16 -0700307
Adam Powell5b5c4142012-10-02 16:25:30 -0700308 // Convert the resulting diameter into a radius.
309 final float touchSize = mTouchHistoryLastAccepted / 2;
Adam Powell828e56e2012-09-14 18:56:16 -0700310 devSumX += Math.abs(event.getX(i) - focusX) + touchSize;
311 devSumY += Math.abs(event.getY(i) - focusY) + touchSize;
Adam Powellae542ff2010-01-13 16:29:27 -0800312 }
Adam Powell618cbea2012-08-27 17:44:59 -0700313 final float devX = devSumX / div;
314 final float devY = devSumY / div;
315
316 // Span is the average distance between touch points through the focal point;
317 // i.e. the diameter of the circle with a radius of the average deviation from
318 // the focal point.
319 final float spanX = devX * 2;
320 final float spanY = devY * 2;
321 final float span = FloatMath.sqrt(spanX * spanX + spanY * spanY);
322
323 // Dispatch begin/end events as needed.
324 // If the configuration changes, notify the app to reset its current state by beginning
325 // a fresh scale event stream.
Adam Powell47ec2fb2012-08-31 17:15:32 -0700326 final boolean wasInProgress = mInProgress;
327 mFocusX = focusX;
328 mFocusY = focusY;
Adam Powell828e56e2012-09-14 18:56:16 -0700329 if (mInProgress && (span < mMinSpan || configChanged)) {
Adam Powell618cbea2012-08-27 17:44:59 -0700330 mListener.onScaleEnd(this);
331 mInProgress = false;
Adam Powell47ec2fb2012-08-31 17:15:32 -0700332 mInitialSpan = span;
Adam Powell618cbea2012-08-27 17:44:59 -0700333 }
334 if (configChanged) {
335 mPrevSpanX = mCurrSpanX = spanX;
336 mPrevSpanY = mCurrSpanY = spanY;
Adam Powell47ec2fb2012-08-31 17:15:32 -0700337 mInitialSpan = mPrevSpan = mCurrSpan = span;
Adam Powell618cbea2012-08-27 17:44:59 -0700338 }
Adam Powellb1861c32012-09-16 12:04:39 -0700339 if (!mInProgress && span >= mMinSpan &&
Adam Powell47ec2fb2012-08-31 17:15:32 -0700340 (wasInProgress || Math.abs(span - mInitialSpan) > mSpanSlop)) {
341 mPrevSpanX = mCurrSpanX = spanX;
342 mPrevSpanY = mCurrSpanY = spanY;
343 mPrevSpan = mCurrSpan = span;
Adam Powell618cbea2012-08-27 17:44:59 -0700344 mInProgress = mListener.onScaleBegin(this);
345 }
346
347 // Handle motion; focal point and span/scale factor are changing.
348 if (action == MotionEvent.ACTION_MOVE) {
349 mCurrSpanX = spanX;
350 mCurrSpanY = spanY;
351 mCurrSpan = span;
Adam Powell618cbea2012-08-27 17:44:59 -0700352
353 boolean updatePrev = true;
354 if (mInProgress) {
355 updatePrev = mListener.onScale(this);
356 }
357
358 if (updatePrev) {
359 mPrevSpanX = mCurrSpanX;
360 mPrevSpanY = mCurrSpanY;
361 mPrevSpan = mCurrSpan;
362 }
363 }
364
365 return true;
Adam Powellae542ff2010-01-13 16:29:27 -0800366 }
367
368 /**
Adam Powell618cbea2012-08-27 17:44:59 -0700369 * Returns {@code true} if a scale gesture is in progress.
Adam Powellae542ff2010-01-13 16:29:27 -0800370 */
371 public boolean isInProgress() {
Adam Powell618cbea2012-08-27 17:44:59 -0700372 return mInProgress;
Adam Powellae542ff2010-01-13 16:29:27 -0800373 }
374
375 /**
376 * Get the X coordinate of the current gesture's focal point.
Adam Powell618cbea2012-08-27 17:44:59 -0700377 * If a gesture is in progress, the focal point is between
378 * each of the pointers forming the gesture.
379 *
Adam Powellab905c872010-02-03 11:01:58 -0800380 * If {@link #isInProgress()} would return false, the result of this
Adam Powellae542ff2010-01-13 16:29:27 -0800381 * function is undefined.
Erik47c41e82010-09-01 15:39:08 -0700382 *
Adam Powellae542ff2010-01-13 16:29:27 -0800383 * @return X coordinate of the focal point in pixels.
384 */
385 public float getFocusX() {
386 return mFocusX;
387 }
388
389 /**
390 * Get the Y coordinate of the current gesture's focal point.
Adam Powell618cbea2012-08-27 17:44:59 -0700391 * If a gesture is in progress, the focal point is between
392 * each of the pointers forming the gesture.
393 *
Adam Powellab905c872010-02-03 11:01:58 -0800394 * If {@link #isInProgress()} would return false, the result of this
Adam Powellae542ff2010-01-13 16:29:27 -0800395 * function is undefined.
Erik47c41e82010-09-01 15:39:08 -0700396 *
Adam Powellae542ff2010-01-13 16:29:27 -0800397 * @return Y coordinate of the focal point in pixels.
398 */
399 public float getFocusY() {
400 return mFocusY;
401 }
402
403 /**
Adam Powell618cbea2012-08-27 17:44:59 -0700404 * Return the average distance between each of the pointers forming the
405 * gesture in progress through the focal point.
Erik47c41e82010-09-01 15:39:08 -0700406 *
Adam Powellae542ff2010-01-13 16:29:27 -0800407 * @return Distance between pointers in pixels.
408 */
409 public float getCurrentSpan() {
Adam Powell618cbea2012-08-27 17:44:59 -0700410 return mCurrSpan;
Adam Powellae542ff2010-01-13 16:29:27 -0800411 }
412
413 /**
Adam Powell618cbea2012-08-27 17:44:59 -0700414 * Return the average X distance between each of the pointers forming the
415 * gesture in progress through the focal point.
Erik47c41e82010-09-01 15:39:08 -0700416 *
417 * @return Distance between pointers in pixels.
418 */
419 public float getCurrentSpanX() {
Adam Powell618cbea2012-08-27 17:44:59 -0700420 return mCurrSpanX;
Erik47c41e82010-09-01 15:39:08 -0700421 }
422
423 /**
Adam Powell618cbea2012-08-27 17:44:59 -0700424 * Return the average Y distance between each of the pointers forming the
425 * gesture in progress through the focal point.
Erik47c41e82010-09-01 15:39:08 -0700426 *
427 * @return Distance between pointers in pixels.
428 */
429 public float getCurrentSpanY() {
Adam Powell618cbea2012-08-27 17:44:59 -0700430 return mCurrSpanY;
Erik47c41e82010-09-01 15:39:08 -0700431 }
432
433 /**
Adam Powell618cbea2012-08-27 17:44:59 -0700434 * Return the previous average distance between each of the pointers forming the
435 * gesture in progress through the focal point.
Erik47c41e82010-09-01 15:39:08 -0700436 *
Adam Powellae542ff2010-01-13 16:29:27 -0800437 * @return Previous distance between pointers in pixels.
438 */
439 public float getPreviousSpan() {
Adam Powell618cbea2012-08-27 17:44:59 -0700440 return mPrevSpan;
Adam Powellae542ff2010-01-13 16:29:27 -0800441 }
442
443 /**
Adam Powell618cbea2012-08-27 17:44:59 -0700444 * Return the previous average X distance between each of the pointers forming the
445 * gesture in progress through the focal point.
Erik47c41e82010-09-01 15:39:08 -0700446 *
447 * @return Previous distance between pointers in pixels.
448 */
449 public float getPreviousSpanX() {
Adam Powell618cbea2012-08-27 17:44:59 -0700450 return mPrevSpanX;
Erik47c41e82010-09-01 15:39:08 -0700451 }
452
453 /**
Adam Powell618cbea2012-08-27 17:44:59 -0700454 * Return the previous average Y distance between each of the pointers forming the
455 * gesture in progress through the focal point.
Erik47c41e82010-09-01 15:39:08 -0700456 *
457 * @return Previous distance between pointers in pixels.
458 */
459 public float getPreviousSpanY() {
Adam Powell618cbea2012-08-27 17:44:59 -0700460 return mPrevSpanY;
Erik47c41e82010-09-01 15:39:08 -0700461 }
462
463 /**
Adam Powellae542ff2010-01-13 16:29:27 -0800464 * Return the scaling factor from the previous scale event to the current
465 * event. This value is defined as
Adam Powellab905c872010-02-03 11:01:58 -0800466 * ({@link #getCurrentSpan()} / {@link #getPreviousSpan()}).
Erik47c41e82010-09-01 15:39:08 -0700467 *
Adam Powellae542ff2010-01-13 16:29:27 -0800468 * @return The current scaling factor.
469 */
470 public float getScaleFactor() {
Adam Powell618cbea2012-08-27 17:44:59 -0700471 return mPrevSpan > 0 ? mCurrSpan / mPrevSpan : 1;
Adam Powellae542ff2010-01-13 16:29:27 -0800472 }
Erik47c41e82010-09-01 15:39:08 -0700473
Adam Powellae542ff2010-01-13 16:29:27 -0800474 /**
475 * Return the time difference in milliseconds between the previous
476 * accepted scaling event and the current scaling event.
Erik47c41e82010-09-01 15:39:08 -0700477 *
Adam Powellae542ff2010-01-13 16:29:27 -0800478 * @return Time difference since the last scaling event in milliseconds.
479 */
480 public long getTimeDelta() {
Adam Powell618cbea2012-08-27 17:44:59 -0700481 return mCurrTime - mPrevTime;
Adam Powellae542ff2010-01-13 16:29:27 -0800482 }
Erik47c41e82010-09-01 15:39:08 -0700483
Adam Powellae542ff2010-01-13 16:29:27 -0800484 /**
485 * Return the event time of the current event being processed.
Erik47c41e82010-09-01 15:39:08 -0700486 *
Adam Powellae542ff2010-01-13 16:29:27 -0800487 * @return Current event time in milliseconds.
488 */
489 public long getEventTime() {
Adam Powell618cbea2012-08-27 17:44:59 -0700490 return mCurrTime;
Adam Powellae542ff2010-01-13 16:29:27 -0800491 }
492}