ScaleGestureDetector.java revision 8f9fbb0c45265a6f5da51cb3bc35362d8e5bc900
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);
150380b525220955ce4e4df8943b89082c7443ebfddAdam Powell        DisplayMetrics metrics = context.getResources().getDisplayMetrics();
151ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell        mContext = context;
152ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell        mListener = listener;
153380b525220955ce4e4df8943b89082c7443ebfddAdam Powell        mEdgeSlop = config.getScaledEdgeSlop();
154380b525220955ce4e4df8943b89082c7443ebfddAdam Powell        mRightSlopEdge = metrics.widthPixels - mEdgeSlop;
155380b525220955ce4e4df8943b89082c7443ebfddAdam Powell        mBottomSlopEdge = metrics.heightPixels - mEdgeSlop;
156ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell    }
157ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell
158ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell    public boolean onTouchEvent(MotionEvent event) {
159ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell        final int action = event.getAction();
160ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell        boolean handled = true;
161ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell
162ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell        if (!mGestureInProgress) {
163ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell            if ((action == MotionEvent.ACTION_POINTER_1_DOWN ||
164ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell                    action == MotionEvent.ACTION_POINTER_2_DOWN) &&
165ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell                    event.getPointerCount() >= 2) {
166ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell                // We have a new multi-finger gesture
167380b525220955ce4e4df8943b89082c7443ebfddAdam Powell
168ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell                // Be paranoid in case we missed an event
169ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell                reset();
170380b525220955ce4e4df8943b89082c7443ebfddAdam Powell
171ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell                mPrevEvent = MotionEvent.obtain(event);
172ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell                mTimeDelta = 0;
173380b525220955ce4e4df8943b89082c7443ebfddAdam Powell
174ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell                setContext(event);
175380b525220955ce4e4df8943b89082c7443ebfddAdam Powell
176380b525220955ce4e4df8943b89082c7443ebfddAdam Powell                // Check if we have a sloppy gesture. If so, delay
177380b525220955ce4e4df8943b89082c7443ebfddAdam Powell                // the beginning of the gesture until we're sure that's
178380b525220955ce4e4df8943b89082c7443ebfddAdam Powell                // what the user wanted. Sloppy gestures can happen if the
179380b525220955ce4e4df8943b89082c7443ebfddAdam Powell                // edge of the user's hand is touching the screen, for example.
180380b525220955ce4e4df8943b89082c7443ebfddAdam Powell                final float edgeSlop = mEdgeSlop;
181380b525220955ce4e4df8943b89082c7443ebfddAdam Powell                final float rightSlop = mRightSlopEdge;
182380b525220955ce4e4df8943b89082c7443ebfddAdam Powell                final float bottomSlop = mBottomSlopEdge;
183380b525220955ce4e4df8943b89082c7443ebfddAdam Powell                final float x0 = event.getRawX();
184380b525220955ce4e4df8943b89082c7443ebfddAdam Powell                final float y0 = event.getRawY();
185380b525220955ce4e4df8943b89082c7443ebfddAdam Powell                final float x1 = getRawX(event, 1);
186380b525220955ce4e4df8943b89082c7443ebfddAdam Powell                final float y1 = getRawY(event, 1);
187380b525220955ce4e4df8943b89082c7443ebfddAdam Powell
1888f9fbb0c45265a6f5da51cb3bc35362d8e5bc900Grace Kloba                boolean p0sloppy = x0 < edgeSlop || y0 < edgeSlop
1898f9fbb0c45265a6f5da51cb3bc35362d8e5bc900Grace Kloba                        || x0 > rightSlop || y0 > bottomSlop;
1908f9fbb0c45265a6f5da51cb3bc35362d8e5bc900Grace Kloba                boolean p1sloppy = x1 < edgeSlop || y1 < edgeSlop
1918f9fbb0c45265a6f5da51cb3bc35362d8e5bc900Grace Kloba                        || x1 > rightSlop || y1 > bottomSlop;
192380b525220955ce4e4df8943b89082c7443ebfddAdam Powell
1938f9fbb0c45265a6f5da51cb3bc35362d8e5bc900Grace Kloba                if(p0sloppy && p1sloppy) {
1948f9fbb0c45265a6f5da51cb3bc35362d8e5bc900Grace Kloba                    mFocusX = -1;
1958f9fbb0c45265a6f5da51cb3bc35362d8e5bc900Grace Kloba                    mFocusY = -1;
1968f9fbb0c45265a6f5da51cb3bc35362d8e5bc900Grace Kloba                    mSloppyGesture = true;
1978f9fbb0c45265a6f5da51cb3bc35362d8e5bc900Grace Kloba                } else if (p0sloppy) {
198380b525220955ce4e4df8943b89082c7443ebfddAdam Powell                    mFocusX = event.getX(1);
199380b525220955ce4e4df8943b89082c7443ebfddAdam Powell                    mFocusY = event.getY(1);
200380b525220955ce4e4df8943b89082c7443ebfddAdam Powell                    mSloppyGesture = true;
201380b525220955ce4e4df8943b89082c7443ebfddAdam Powell                } else if (p1sloppy) {
202380b525220955ce4e4df8943b89082c7443ebfddAdam Powell                    mFocusX = event.getX(0);
203380b525220955ce4e4df8943b89082c7443ebfddAdam Powell                    mFocusY = event.getY(0);
204380b525220955ce4e4df8943b89082c7443ebfddAdam Powell                    mSloppyGesture = true;
205380b525220955ce4e4df8943b89082c7443ebfddAdam Powell                } else {
206380b525220955ce4e4df8943b89082c7443ebfddAdam Powell                    mGestureInProgress = mListener.onScaleBegin(this);
207380b525220955ce4e4df8943b89082c7443ebfddAdam Powell                }
208380b525220955ce4e4df8943b89082c7443ebfddAdam Powell            } else if (action == MotionEvent.ACTION_MOVE && mSloppyGesture) {
209380b525220955ce4e4df8943b89082c7443ebfddAdam Powell                // Initiate sloppy gestures if we've moved outside of the slop area.
210380b525220955ce4e4df8943b89082c7443ebfddAdam Powell                final float edgeSlop = mEdgeSlop;
211380b525220955ce4e4df8943b89082c7443ebfddAdam Powell                final float rightSlop = mRightSlopEdge;
212380b525220955ce4e4df8943b89082c7443ebfddAdam Powell                final float bottomSlop = mBottomSlopEdge;
213380b525220955ce4e4df8943b89082c7443ebfddAdam Powell                final float x0 = event.getRawX();
214380b525220955ce4e4df8943b89082c7443ebfddAdam Powell                final float y0 = event.getRawY();
215380b525220955ce4e4df8943b89082c7443ebfddAdam Powell                final float x1 = getRawX(event, 1);
216380b525220955ce4e4df8943b89082c7443ebfddAdam Powell                final float y1 = getRawY(event, 1);
217380b525220955ce4e4df8943b89082c7443ebfddAdam Powell
2188f9fbb0c45265a6f5da51cb3bc35362d8e5bc900Grace Kloba                boolean p0sloppy = x0 < edgeSlop || y0 < edgeSlop
2198f9fbb0c45265a6f5da51cb3bc35362d8e5bc900Grace Kloba                        || x0 > rightSlop || y0 > bottomSlop;
2208f9fbb0c45265a6f5da51cb3bc35362d8e5bc900Grace Kloba                boolean p1sloppy = x1 < edgeSlop || y1 < edgeSlop
2218f9fbb0c45265a6f5da51cb3bc35362d8e5bc900Grace Kloba                        || x1 > rightSlop || y1 > bottomSlop;
222380b525220955ce4e4df8943b89082c7443ebfddAdam Powell
2238f9fbb0c45265a6f5da51cb3bc35362d8e5bc900Grace Kloba                if(p0sloppy && p1sloppy) {
2248f9fbb0c45265a6f5da51cb3bc35362d8e5bc900Grace Kloba                    mFocusX = -1;
2258f9fbb0c45265a6f5da51cb3bc35362d8e5bc900Grace Kloba                    mFocusY = -1;
2268f9fbb0c45265a6f5da51cb3bc35362d8e5bc900Grace Kloba                } else if (p0sloppy) {
227380b525220955ce4e4df8943b89082c7443ebfddAdam Powell                    mFocusX = event.getX(1);
228380b525220955ce4e4df8943b89082c7443ebfddAdam Powell                    mFocusY = event.getY(1);
229380b525220955ce4e4df8943b89082c7443ebfddAdam Powell                } else if (p1sloppy) {
230380b525220955ce4e4df8943b89082c7443ebfddAdam Powell                    mFocusX = event.getX(0);
231380b525220955ce4e4df8943b89082c7443ebfddAdam Powell                    mFocusY = event.getY(0);
232380b525220955ce4e4df8943b89082c7443ebfddAdam Powell                } else {
233380b525220955ce4e4df8943b89082c7443ebfddAdam Powell                    mSloppyGesture = false;
234380b525220955ce4e4df8943b89082c7443ebfddAdam Powell                    mGestureInProgress = mListener.onScaleBegin(this);
235380b525220955ce4e4df8943b89082c7443ebfddAdam Powell                }
2368f9fbb0c45265a6f5da51cb3bc35362d8e5bc900Grace Kloba            } else if ((action == MotionEvent.ACTION_POINTER_1_UP
2378f9fbb0c45265a6f5da51cb3bc35362d8e5bc900Grace Kloba                    || action == MotionEvent.ACTION_POINTER_2_UP)
2388f9fbb0c45265a6f5da51cb3bc35362d8e5bc900Grace Kloba                    && mSloppyGesture) {
2398f9fbb0c45265a6f5da51cb3bc35362d8e5bc900Grace Kloba                // Set focus point to the remaining finger
2408f9fbb0c45265a6f5da51cb3bc35362d8e5bc900Grace Kloba                int id = (((action & MotionEvent.ACTION_POINTER_ID_MASK)
2418f9fbb0c45265a6f5da51cb3bc35362d8e5bc900Grace Kloba                        >> MotionEvent.ACTION_POINTER_ID_SHIFT) == 0) ? 1 : 0;
2428f9fbb0c45265a6f5da51cb3bc35362d8e5bc900Grace Kloba                mFocusX = event.getX(id);
2438f9fbb0c45265a6f5da51cb3bc35362d8e5bc900Grace Kloba                mFocusY = event.getY(id);
244ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell            }
245ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell        } else {
246ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell            // Transform gesture in progress - attempt to handle it
247ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell            switch (action) {
248ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell                case MotionEvent.ACTION_POINTER_1_UP:
249ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell                case MotionEvent.ACTION_POINTER_2_UP:
250ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell                    // Gesture ended
251ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell                    setContext(event);
252380b525220955ce4e4df8943b89082c7443ebfddAdam Powell
253ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell                    // Set focus point to the remaining finger
254ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell                    int id = (((action & MotionEvent.ACTION_POINTER_ID_MASK)
255ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell                            >> MotionEvent.ACTION_POINTER_ID_SHIFT) == 0) ? 1 : 0;
256ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell                    mFocusX = event.getX(id);
257ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell                    mFocusY = event.getY(id);
258380b525220955ce4e4df8943b89082c7443ebfddAdam Powell
259380b525220955ce4e4df8943b89082c7443ebfddAdam Powell                    if (!mSloppyGesture) {
260380b525220955ce4e4df8943b89082c7443ebfddAdam Powell                        mListener.onScaleEnd(this);
261380b525220955ce4e4df8943b89082c7443ebfddAdam Powell                    }
262ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell
263ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell                    reset();
264ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell                    break;
265ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell
266ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell                case MotionEvent.ACTION_CANCEL:
267380b525220955ce4e4df8943b89082c7443ebfddAdam Powell                    if (!mSloppyGesture) {
268380b525220955ce4e4df8943b89082c7443ebfddAdam Powell                        mListener.onScaleEnd(this);
269380b525220955ce4e4df8943b89082c7443ebfddAdam Powell                    }
270ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell
271ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell                    reset();
272ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell                    break;
273ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell
274ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell                case MotionEvent.ACTION_MOVE:
275ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell                    setContext(event);
276ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell
277ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell                    // Only accept the event if our relative pressure is within
278ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell                    // a certain limit - this can help filter shaky data as a
279ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell                    // finger is lifted.
280ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell                    if (mCurrPressure / mPrevPressure > PRESSURE_THRESHOLD) {
281ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell                        final boolean updatePrevious = mListener.onScale(this);
282ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell
283ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell                        if (updatePrevious) {
284ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell                            mPrevEvent.recycle();
285ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell                            mPrevEvent = MotionEvent.obtain(event);
286ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell                        }
287ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell                    }
288ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell                    break;
289ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell            }
290ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell        }
291ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell        return handled;
292ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell    }
293380b525220955ce4e4df8943b89082c7443ebfddAdam Powell
294380b525220955ce4e4df8943b89082c7443ebfddAdam Powell    /**
295380b525220955ce4e4df8943b89082c7443ebfddAdam Powell     * MotionEvent has no getRawX(int) method; simulate it pending future API approval.
296380b525220955ce4e4df8943b89082c7443ebfddAdam Powell     */
297380b525220955ce4e4df8943b89082c7443ebfddAdam Powell    private static float getRawX(MotionEvent event, int pointerIndex) {
298380b525220955ce4e4df8943b89082c7443ebfddAdam Powell        float offset = event.getX() - event.getRawX();
299380b525220955ce4e4df8943b89082c7443ebfddAdam Powell        return event.getX(pointerIndex) + offset;
300380b525220955ce4e4df8943b89082c7443ebfddAdam Powell    }
301380b525220955ce4e4df8943b89082c7443ebfddAdam Powell
302380b525220955ce4e4df8943b89082c7443ebfddAdam Powell    /**
303380b525220955ce4e4df8943b89082c7443ebfddAdam Powell     * MotionEvent has no getRawY(int) method; simulate it pending future API approval.
304380b525220955ce4e4df8943b89082c7443ebfddAdam Powell     */
305380b525220955ce4e4df8943b89082c7443ebfddAdam Powell    private static float getRawY(MotionEvent event, int pointerIndex) {
306380b525220955ce4e4df8943b89082c7443ebfddAdam Powell        float offset = event.getY() - event.getRawY();
307380b525220955ce4e4df8943b89082c7443ebfddAdam Powell        return event.getY(pointerIndex) + offset;
308380b525220955ce4e4df8943b89082c7443ebfddAdam Powell    }
309ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell
310ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell    private void setContext(MotionEvent curr) {
311ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell        if (mCurrEvent != null) {
312ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell            mCurrEvent.recycle();
313ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell        }
314ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell        mCurrEvent = MotionEvent.obtain(curr);
315ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell
316ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell        mCurrLen = -1;
317ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell        mPrevLen = -1;
318ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell        mScaleFactor = -1;
319ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell
320ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell        final MotionEvent prev = mPrevEvent;
321ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell
322ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell        final float px0 = prev.getX(0);
323ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell        final float py0 = prev.getY(0);
324ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell        final float px1 = prev.getX(1);
325ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell        final float py1 = prev.getY(1);
326ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell        final float cx0 = curr.getX(0);
327ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell        final float cy0 = curr.getY(0);
328ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell        final float cx1 = curr.getX(1);
329ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell        final float cy1 = curr.getY(1);
330ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell
331ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell        final float pvx = px1 - px0;
332ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell        final float pvy = py1 - py0;
333ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell        final float cvx = cx1 - cx0;
334ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell        final float cvy = cy1 - cy0;
335ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell        mPrevFingerDiffX = pvx;
336ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell        mPrevFingerDiffY = pvy;
337ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell        mCurrFingerDiffX = cvx;
338ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell        mCurrFingerDiffY = cvy;
339ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell
340ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell        mFocusX = cx0 + cvx * 0.5f;
341ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell        mFocusY = cy0 + cvy * 0.5f;
342ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell        mTimeDelta = curr.getEventTime() - prev.getEventTime();
343ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell        mCurrPressure = curr.getPressure(0) + curr.getPressure(1);
344ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell        mPrevPressure = prev.getPressure(0) + prev.getPressure(1);
345ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell    }
346ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell
347ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell    private void reset() {
348ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell        if (mPrevEvent != null) {
349ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell            mPrevEvent.recycle();
350ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell            mPrevEvent = null;
351ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell        }
352ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell        if (mCurrEvent != null) {
353ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell            mCurrEvent.recycle();
354ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell            mCurrEvent = null;
355ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell        }
356380b525220955ce4e4df8943b89082c7443ebfddAdam Powell        mSloppyGesture = false;
357380b525220955ce4e4df8943b89082c7443ebfddAdam Powell        mGestureInProgress = false;
358ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell    }
359ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell
360ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell    /**
361ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell     * Returns {@code true} if a two-finger scale gesture is in progress.
362ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell     * @return {@code true} if a scale gesture is in progress, {@code false} otherwise.
363ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell     */
364ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell    public boolean isInProgress() {
365ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell        return mGestureInProgress;
366ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell    }
367ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell
368ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell    /**
369ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell     * Get the X coordinate of the current gesture's focal point.
370ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell     * If a gesture is in progress, the focal point is directly between
371ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell     * the two pointers forming the gesture.
372ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell     * If a gesture is ending, the focal point is the location of the
373ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell     * remaining pointer on the screen.
374ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell     * If {@link isInProgress()} would return false, the result of this
375ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell     * function is undefined.
376ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell     *
377ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell     * @return X coordinate of the focal point in pixels.
378ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell     */
379ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell    public float getFocusX() {
380ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell        return mFocusX;
381ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell    }
382ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell
383ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell    /**
384ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell     * Get the Y coordinate of the current gesture's focal point.
385ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell     * If a gesture is in progress, the focal point is directly between
386ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell     * the two pointers forming the gesture.
387ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell     * If a gesture is ending, the focal point is the location of the
388ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell     * remaining pointer on the screen.
389ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell     * If {@link isInProgress()} would return false, the result of this
390ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell     * function is undefined.
391ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell     *
392ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell     * @return Y coordinate of the focal point in pixels.
393ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell     */
394ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell    public float getFocusY() {
395ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell        return mFocusY;
396ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell    }
397ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell
398ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell    /**
399ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell     * Return the current distance between the two pointers forming the
400ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell     * gesture in progress.
401ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell     *
402ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell     * @return Distance between pointers in pixels.
403ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell     */
404ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell    public float getCurrentSpan() {
405ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell        if (mCurrLen == -1) {
406ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell            final float cvx = mCurrFingerDiffX;
407ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell            final float cvy = mCurrFingerDiffY;
408ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell            mCurrLen = (float)Math.sqrt(cvx*cvx + cvy*cvy);
409ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell        }
410ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell        return mCurrLen;
411ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell    }
412ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell
413ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell    /**
414ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell     * Return the previous distance between the two pointers forming the
415ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell     * gesture in progress.
416ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell     *
417ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell     * @return Previous distance between pointers in pixels.
418ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell     */
419ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell    public float getPreviousSpan() {
420ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell        if (mPrevLen == -1) {
421ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell            final float pvx = mPrevFingerDiffX;
422ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell            final float pvy = mPrevFingerDiffY;
423ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell            mPrevLen = (float)Math.sqrt(pvx*pvx + pvy*pvy);
424ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell        }
425ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell        return mPrevLen;
426ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell    }
427ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell
428ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell    /**
429ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell     * Return the scaling factor from the previous scale event to the current
430ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell     * event. This value is defined as
431ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell     * ({@link getCurrentSpan()} / {@link getPreviousSpan()}).
432ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell     *
433ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell     * @return The current scaling factor.
434ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell     */
435ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell    public float getScaleFactor() {
436ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell        if (mScaleFactor == -1) {
437ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell            mScaleFactor = getCurrentSpan() / getPreviousSpan();
438ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell        }
439ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell        return mScaleFactor;
440ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell    }
441ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell
442ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell    /**
443ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell     * Return the time difference in milliseconds between the previous
444ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell     * accepted scaling event and the current scaling event.
445ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell     *
446ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell     * @return Time difference since the last scaling event in milliseconds.
447ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell     */
448ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell    public long getTimeDelta() {
449ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell        return mTimeDelta;
450ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell    }
451ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell
452ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell    /**
453ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell     * Return the event time of the current event being processed.
454ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell     *
455ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell     * @return Current event time in milliseconds.
456ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell     */
457ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell    public long getEventTime() {
458ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell        return mCurrEvent.getEventTime();
459ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell    }
460ae542ff055301a4c3c8a18e8da1739df3a771958Adam Powell}
461