blob: 51c5c7b52b6b2d1dcd998efbb46857c57442e90c [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 Powell7232b0a2012-11-28 18:29:22 -0800262 mCurrTime = event.getEventTime();
263
Adam Powelle33cef82011-02-23 16:51:20 -0800264 final int action = event.getActionMasked();
Adam Powellae542ff2010-01-13 16:29:27 -0800265
Adam Powell618cbea2012-08-27 17:44:59 -0700266 final boolean streamComplete = action == MotionEvent.ACTION_UP ||
267 action == MotionEvent.ACTION_CANCEL;
268 if (action == MotionEvent.ACTION_DOWN || streamComplete) {
269 // Reset any scale in progress with the listener.
270 // If it's an ACTION_DOWN we're beginning a new event stream.
271 // This means the app probably didn't give us all the events. Shame on it.
272 if (mInProgress) {
Adam Powelld0197f32011-03-17 00:09:03 -0700273 mListener.onScaleEnd(this);
Adam Powell618cbea2012-08-27 17:44:59 -0700274 mInProgress = false;
Adam Powell47ec2fb2012-08-31 17:15:32 -0700275 mInitialSpan = 0;
Adam Powelld0197f32011-03-17 00:09:03 -0700276 }
Adam Powell618cbea2012-08-27 17:44:59 -0700277
278 if (streamComplete) {
Adam Powella4ce6ae2012-09-24 20:13:23 -0700279 clearTouchHistory();
Adam Powell618cbea2012-08-27 17:44:59 -0700280 return true;
281 }
Adam Powelld0197f32011-03-17 00:09:03 -0700282 }
283
Adam Powellabde0422012-09-26 17:12:50 -0700284 final boolean configChanged = action == MotionEvent.ACTION_DOWN ||
Adam Powell618cbea2012-08-27 17:44:59 -0700285 action == MotionEvent.ACTION_POINTER_UP ||
286 action == MotionEvent.ACTION_POINTER_DOWN;
287 final boolean pointerUp = action == MotionEvent.ACTION_POINTER_UP;
288 final int skipIndex = pointerUp ? event.getActionIndex() : -1;
Adam Powellae542ff2010-01-13 16:29:27 -0800289
Adam Powell618cbea2012-08-27 17:44:59 -0700290 // Determine focal point
291 float sumX = 0, sumY = 0;
292 final int count = event.getPointerCount();
293 for (int i = 0; i < count; i++) {
294 if (skipIndex == i) continue;
295 sumX += event.getX(i);
296 sumY += event.getY(i);
Adam Powellae542ff2010-01-13 16:29:27 -0800297 }
Adam Powell618cbea2012-08-27 17:44:59 -0700298 final int div = pointerUp ? count - 1 : count;
299 final float focusX = sumX / div;
300 final float focusY = sumY / div;
301
Adam Powell5b5c4142012-10-02 16:25:30 -0700302
303 addTouchHistory(event);
Adam Powella4ce6ae2012-09-24 20:13:23 -0700304
Adam Powell618cbea2012-08-27 17:44:59 -0700305 // Determine average deviation from focal point
306 float devSumX = 0, devSumY = 0;
307 for (int i = 0; i < count; i++) {
308 if (skipIndex == i) continue;
Adam Powell828e56e2012-09-14 18:56:16 -0700309
Adam Powell5b5c4142012-10-02 16:25:30 -0700310 // Convert the resulting diameter into a radius.
311 final float touchSize = mTouchHistoryLastAccepted / 2;
Adam Powell828e56e2012-09-14 18:56:16 -0700312 devSumX += Math.abs(event.getX(i) - focusX) + touchSize;
313 devSumY += Math.abs(event.getY(i) - focusY) + touchSize;
Adam Powellae542ff2010-01-13 16:29:27 -0800314 }
Adam Powell618cbea2012-08-27 17:44:59 -0700315 final float devX = devSumX / div;
316 final float devY = devSumY / div;
317
318 // Span is the average distance between touch points through the focal point;
319 // i.e. the diameter of the circle with a radius of the average deviation from
320 // the focal point.
321 final float spanX = devX * 2;
322 final float spanY = devY * 2;
323 final float span = FloatMath.sqrt(spanX * spanX + spanY * spanY);
324
325 // Dispatch begin/end events as needed.
326 // If the configuration changes, notify the app to reset its current state by beginning
327 // a fresh scale event stream.
Adam Powell47ec2fb2012-08-31 17:15:32 -0700328 final boolean wasInProgress = mInProgress;
329 mFocusX = focusX;
330 mFocusY = focusY;
Adam Powell828e56e2012-09-14 18:56:16 -0700331 if (mInProgress && (span < mMinSpan || configChanged)) {
Adam Powell618cbea2012-08-27 17:44:59 -0700332 mListener.onScaleEnd(this);
333 mInProgress = false;
Adam Powell47ec2fb2012-08-31 17:15:32 -0700334 mInitialSpan = span;
Adam Powell618cbea2012-08-27 17:44:59 -0700335 }
336 if (configChanged) {
337 mPrevSpanX = mCurrSpanX = spanX;
338 mPrevSpanY = mCurrSpanY = spanY;
Adam Powell47ec2fb2012-08-31 17:15:32 -0700339 mInitialSpan = mPrevSpan = mCurrSpan = span;
Adam Powell618cbea2012-08-27 17:44:59 -0700340 }
Adam Powellb1861c32012-09-16 12:04:39 -0700341 if (!mInProgress && span >= mMinSpan &&
Adam Powell47ec2fb2012-08-31 17:15:32 -0700342 (wasInProgress || Math.abs(span - mInitialSpan) > mSpanSlop)) {
343 mPrevSpanX = mCurrSpanX = spanX;
344 mPrevSpanY = mCurrSpanY = spanY;
345 mPrevSpan = mCurrSpan = span;
Adam Powell7232b0a2012-11-28 18:29:22 -0800346 mPrevTime = mCurrTime;
Adam Powell618cbea2012-08-27 17:44:59 -0700347 mInProgress = mListener.onScaleBegin(this);
348 }
349
350 // Handle motion; focal point and span/scale factor are changing.
351 if (action == MotionEvent.ACTION_MOVE) {
352 mCurrSpanX = spanX;
353 mCurrSpanY = spanY;
354 mCurrSpan = span;
Adam Powell618cbea2012-08-27 17:44:59 -0700355
356 boolean updatePrev = true;
357 if (mInProgress) {
358 updatePrev = mListener.onScale(this);
359 }
360
361 if (updatePrev) {
362 mPrevSpanX = mCurrSpanX;
363 mPrevSpanY = mCurrSpanY;
364 mPrevSpan = mCurrSpan;
Adam Powell7232b0a2012-11-28 18:29:22 -0800365 mPrevTime = mCurrTime;
Adam Powell618cbea2012-08-27 17:44:59 -0700366 }
367 }
368
369 return true;
Adam Powellae542ff2010-01-13 16:29:27 -0800370 }
371
372 /**
Adam Powell618cbea2012-08-27 17:44:59 -0700373 * Returns {@code true} if a scale gesture is in progress.
Adam Powellae542ff2010-01-13 16:29:27 -0800374 */
375 public boolean isInProgress() {
Adam Powell618cbea2012-08-27 17:44:59 -0700376 return mInProgress;
Adam Powellae542ff2010-01-13 16:29:27 -0800377 }
378
379 /**
380 * Get the X coordinate of the current gesture's focal point.
Adam Powell618cbea2012-08-27 17:44:59 -0700381 * If a gesture is in progress, the focal point is between
382 * each of the pointers forming the gesture.
383 *
Adam Powellab905c872010-02-03 11:01:58 -0800384 * If {@link #isInProgress()} would return false, the result of this
Adam Powellae542ff2010-01-13 16:29:27 -0800385 * function is undefined.
Erik47c41e82010-09-01 15:39:08 -0700386 *
Adam Powellae542ff2010-01-13 16:29:27 -0800387 * @return X coordinate of the focal point in pixels.
388 */
389 public float getFocusX() {
390 return mFocusX;
391 }
392
393 /**
394 * Get the Y coordinate of the current gesture's focal point.
Adam Powell618cbea2012-08-27 17:44:59 -0700395 * If a gesture is in progress, the focal point is between
396 * each of the pointers forming the gesture.
397 *
Adam Powellab905c872010-02-03 11:01:58 -0800398 * If {@link #isInProgress()} would return false, the result of this
Adam Powellae542ff2010-01-13 16:29:27 -0800399 * function is undefined.
Erik47c41e82010-09-01 15:39:08 -0700400 *
Adam Powellae542ff2010-01-13 16:29:27 -0800401 * @return Y coordinate of the focal point in pixels.
402 */
403 public float getFocusY() {
404 return mFocusY;
405 }
406
407 /**
Adam Powell618cbea2012-08-27 17:44:59 -0700408 * Return the average distance between each of the pointers forming the
409 * gesture in progress through the focal point.
Erik47c41e82010-09-01 15:39:08 -0700410 *
Adam Powellae542ff2010-01-13 16:29:27 -0800411 * @return Distance between pointers in pixels.
412 */
413 public float getCurrentSpan() {
Adam Powell618cbea2012-08-27 17:44:59 -0700414 return mCurrSpan;
Adam Powellae542ff2010-01-13 16:29:27 -0800415 }
416
417 /**
Adam Powell618cbea2012-08-27 17:44:59 -0700418 * Return the average X distance between each of the pointers forming the
419 * gesture in progress through the focal point.
Erik47c41e82010-09-01 15:39:08 -0700420 *
421 * @return Distance between pointers in pixels.
422 */
423 public float getCurrentSpanX() {
Adam Powell618cbea2012-08-27 17:44:59 -0700424 return mCurrSpanX;
Erik47c41e82010-09-01 15:39:08 -0700425 }
426
427 /**
Adam Powell618cbea2012-08-27 17:44:59 -0700428 * Return the average Y distance between each of the pointers forming the
429 * gesture in progress through the focal point.
Erik47c41e82010-09-01 15:39:08 -0700430 *
431 * @return Distance between pointers in pixels.
432 */
433 public float getCurrentSpanY() {
Adam Powell618cbea2012-08-27 17:44:59 -0700434 return mCurrSpanY;
Erik47c41e82010-09-01 15:39:08 -0700435 }
436
437 /**
Adam Powell618cbea2012-08-27 17:44:59 -0700438 * Return the previous average distance between each of the pointers forming the
439 * gesture in progress through the focal point.
Erik47c41e82010-09-01 15:39:08 -0700440 *
Adam Powellae542ff2010-01-13 16:29:27 -0800441 * @return Previous distance between pointers in pixels.
442 */
443 public float getPreviousSpan() {
Adam Powell618cbea2012-08-27 17:44:59 -0700444 return mPrevSpan;
Adam Powellae542ff2010-01-13 16:29:27 -0800445 }
446
447 /**
Adam Powell618cbea2012-08-27 17:44:59 -0700448 * Return the previous average X distance between each of the pointers forming the
449 * gesture in progress through the focal point.
Erik47c41e82010-09-01 15:39:08 -0700450 *
451 * @return Previous distance between pointers in pixels.
452 */
453 public float getPreviousSpanX() {
Adam Powell618cbea2012-08-27 17:44:59 -0700454 return mPrevSpanX;
Erik47c41e82010-09-01 15:39:08 -0700455 }
456
457 /**
Adam Powell618cbea2012-08-27 17:44:59 -0700458 * Return the previous average Y distance between each of the pointers forming the
459 * gesture in progress through the focal point.
Erik47c41e82010-09-01 15:39:08 -0700460 *
461 * @return Previous distance between pointers in pixels.
462 */
463 public float getPreviousSpanY() {
Adam Powell618cbea2012-08-27 17:44:59 -0700464 return mPrevSpanY;
Erik47c41e82010-09-01 15:39:08 -0700465 }
466
467 /**
Adam Powellae542ff2010-01-13 16:29:27 -0800468 * Return the scaling factor from the previous scale event to the current
469 * event. This value is defined as
Adam Powellab905c872010-02-03 11:01:58 -0800470 * ({@link #getCurrentSpan()} / {@link #getPreviousSpan()}).
Erik47c41e82010-09-01 15:39:08 -0700471 *
Adam Powellae542ff2010-01-13 16:29:27 -0800472 * @return The current scaling factor.
473 */
474 public float getScaleFactor() {
Adam Powell618cbea2012-08-27 17:44:59 -0700475 return mPrevSpan > 0 ? mCurrSpan / mPrevSpan : 1;
Adam Powellae542ff2010-01-13 16:29:27 -0800476 }
Erik47c41e82010-09-01 15:39:08 -0700477
Adam Powellae542ff2010-01-13 16:29:27 -0800478 /**
479 * Return the time difference in milliseconds between the previous
480 * accepted scaling event and the current scaling event.
Erik47c41e82010-09-01 15:39:08 -0700481 *
Adam Powellae542ff2010-01-13 16:29:27 -0800482 * @return Time difference since the last scaling event in milliseconds.
483 */
484 public long getTimeDelta() {
Adam Powell618cbea2012-08-27 17:44:59 -0700485 return mCurrTime - mPrevTime;
Adam Powellae542ff2010-01-13 16:29:27 -0800486 }
Erik47c41e82010-09-01 15:39:08 -0700487
Adam Powellae542ff2010-01-13 16:29:27 -0800488 /**
489 * Return the event time of the current event being processed.
Erik47c41e82010-09-01 15:39:08 -0700490 *
Adam Powellae542ff2010-01-13 16:29:27 -0800491 * @return Current event time in milliseconds.
492 */
493 public long getEventTime() {
Adam Powell618cbea2012-08-27 17:44:59 -0700494 return mCurrTime;
Adam Powellae542ff2010-01-13 16:29:27 -0800495 }
496}