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