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