1 /* 2 * Copyright (C) 2006 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package android.view; 18 19 import android.util.Pools.SynchronizedPool; 20 21 /** 22 * Helper for tracking the velocity of touch events, for implementing 23 * flinging and other such gestures. 24 * 25 * Use {@link #obtain} to retrieve a new instance of the class when you are going 26 * to begin tracking. Put the motion events you receive into it with 27 * {@link #addMovement(MotionEvent)}. When you want to determine the velocity call 28 * {@link #computeCurrentVelocity(int)} and then call {@link #getXVelocity(int)} 29 * and {@link #getYVelocity(int)} to retrieve the velocity for each pointer id. 30 */ 31 public final class VelocityTracker { 32 private static final SynchronizedPool<VelocityTracker> sPool = 33 new SynchronizedPool<VelocityTracker>(2); 34 35 private static final int ACTIVE_POINTER_ID = -1; 36 37 private int mPtr; 38 private final String mStrategy; 39 40 private static native int nativeInitialize(String strategy); 41 private static native void nativeDispose(int ptr); 42 private static native void nativeClear(int ptr); 43 private static native void nativeAddMovement(int ptr, MotionEvent event); 44 private static native void nativeComputeCurrentVelocity(int ptr, int units, float maxVelocity); 45 private static native float nativeGetXVelocity(int ptr, int id); 46 private static native float nativeGetYVelocity(int ptr, int id); 47 private static native boolean nativeGetEstimator(int ptr, int id, Estimator outEstimator); 48 49 /** 50 * Retrieve a new VelocityTracker object to watch the velocity of a 51 * motion. Be sure to call {@link #recycle} when done. You should 52 * generally only maintain an active object while tracking a movement, 53 * so that the VelocityTracker can be re-used elsewhere. 54 * 55 * @return Returns a new VelocityTracker. 56 */ 57 static public VelocityTracker obtain() { 58 VelocityTracker instance = sPool.acquire(); 59 return (instance != null) ? instance : new VelocityTracker(null); 60 } 61 62 /** 63 * Obtains a velocity tracker with the specified strategy. 64 * For testing and comparison purposes only. 65 * 66 * @param strategy The strategy, or null to use the default. 67 * @return The velocity tracker. 68 * 69 * @hide 70 */ 71 public static VelocityTracker obtain(String strategy) { 72 if (strategy == null) { 73 return obtain(); 74 } 75 return new VelocityTracker(strategy); 76 } 77 78 /** 79 * Return a VelocityTracker object back to be re-used by others. You must 80 * not touch the object after calling this function. 81 */ 82 public void recycle() { 83 if (mStrategy == null) { 84 clear(); 85 sPool.release(this); 86 } 87 } 88 89 private VelocityTracker(String strategy) { 90 mPtr = nativeInitialize(strategy); 91 mStrategy = strategy; 92 } 93 94 @Override 95 protected void finalize() throws Throwable { 96 try { 97 if (mPtr != 0) { 98 nativeDispose(mPtr); 99 mPtr = 0; 100 } 101 } finally { 102 super.finalize(); 103 } 104 } 105 106 /** 107 * Reset the velocity tracker back to its initial state. 108 */ 109 public void clear() { 110 nativeClear(mPtr); 111 } 112 113 /** 114 * Add a user's movement to the tracker. You should call this for the 115 * initial {@link MotionEvent#ACTION_DOWN}, the following 116 * {@link MotionEvent#ACTION_MOVE} events that you receive, and the 117 * final {@link MotionEvent#ACTION_UP}. You can, however, call this 118 * for whichever events you desire. 119 * 120 * @param event The MotionEvent you received and would like to track. 121 */ 122 public void addMovement(MotionEvent event) { 123 if (event == null) { 124 throw new IllegalArgumentException("event must not be null"); 125 } 126 nativeAddMovement(mPtr, event); 127 } 128 129 /** 130 * Equivalent to invoking {@link #computeCurrentVelocity(int, float)} with a maximum 131 * velocity of Float.MAX_VALUE. 132 * 133 * @see #computeCurrentVelocity(int, float) 134 */ 135 public void computeCurrentVelocity(int units) { 136 nativeComputeCurrentVelocity(mPtr, units, Float.MAX_VALUE); 137 } 138 139 /** 140 * Compute the current velocity based on the points that have been 141 * collected. Only call this when you actually want to retrieve velocity 142 * information, as it is relatively expensive. You can then retrieve 143 * the velocity with {@link #getXVelocity()} and 144 * {@link #getYVelocity()}. 145 * 146 * @param units The units you would like the velocity in. A value of 1 147 * provides pixels per millisecond, 1000 provides pixels per second, etc. 148 * @param maxVelocity The maximum velocity that can be computed by this method. 149 * This value must be declared in the same unit as the units parameter. This value 150 * must be positive. 151 */ 152 public void computeCurrentVelocity(int units, float maxVelocity) { 153 nativeComputeCurrentVelocity(mPtr, units, maxVelocity); 154 } 155 156 /** 157 * Retrieve the last computed X velocity. You must first call 158 * {@link #computeCurrentVelocity(int)} before calling this function. 159 * 160 * @return The previously computed X velocity. 161 */ 162 public float getXVelocity() { 163 return nativeGetXVelocity(mPtr, ACTIVE_POINTER_ID); 164 } 165 166 /** 167 * Retrieve the last computed Y velocity. You must first call 168 * {@link #computeCurrentVelocity(int)} before calling this function. 169 * 170 * @return The previously computed Y velocity. 171 */ 172 public float getYVelocity() { 173 return nativeGetYVelocity(mPtr, ACTIVE_POINTER_ID); 174 } 175 176 /** 177 * Retrieve the last computed X velocity. You must first call 178 * {@link #computeCurrentVelocity(int)} before calling this function. 179 * 180 * @param id Which pointer's velocity to return. 181 * @return The previously computed X velocity. 182 */ 183 public float getXVelocity(int id) { 184 return nativeGetXVelocity(mPtr, id); 185 } 186 187 /** 188 * Retrieve the last computed Y velocity. You must first call 189 * {@link #computeCurrentVelocity(int)} before calling this function. 190 * 191 * @param id Which pointer's velocity to return. 192 * @return The previously computed Y velocity. 193 */ 194 public float getYVelocity(int id) { 195 return nativeGetYVelocity(mPtr, id); 196 } 197 198 /** 199 * Get an estimator for the movements of a pointer using past movements of the 200 * pointer to predict future movements. 201 * 202 * It is not necessary to call {@link #computeCurrentVelocity(int)} before calling 203 * this method. 204 * 205 * @param id Which pointer's velocity to return. 206 * @param outEstimator The estimator to populate. 207 * @return True if an estimator was obtained, false if there is no information 208 * available about the pointer. 209 * 210 * @hide For internal use only. Not a final API. 211 */ 212 public boolean getEstimator(int id, Estimator outEstimator) { 213 if (outEstimator == null) { 214 throw new IllegalArgumentException("outEstimator must not be null"); 215 } 216 return nativeGetEstimator(mPtr, id, outEstimator); 217 } 218 219 /** 220 * An estimator for the movements of a pointer based on a polynomial model. 221 * 222 * The last recorded position of the pointer is at time zero seconds. 223 * Past estimated positions are at negative times and future estimated positions 224 * are at positive times. 225 * 226 * First coefficient is position (in pixels), second is velocity (in pixels per second), 227 * third is acceleration (in pixels per second squared). 228 * 229 * @hide For internal use only. Not a final API. 230 */ 231 public static final class Estimator { 232 // Must match VelocityTracker::Estimator::MAX_DEGREE 233 private static final int MAX_DEGREE = 4; 234 235 /** 236 * Polynomial coefficients describing motion in X. 237 */ 238 public final float[] xCoeff = new float[MAX_DEGREE + 1]; 239 240 /** 241 * Polynomial coefficients describing motion in Y. 242 */ 243 public final float[] yCoeff = new float[MAX_DEGREE + 1]; 244 245 /** 246 * Polynomial degree, or zero if only position information is available. 247 */ 248 public int degree; 249 250 /** 251 * Confidence (coefficient of determination), between 0 (no fit) and 1 (perfect fit). 252 */ 253 public float confidence; 254 255 /** 256 * Gets an estimate of the X position of the pointer at the specified time point. 257 * @param time The time point in seconds, 0 is the last recorded time. 258 * @return The estimated X coordinate. 259 */ 260 public float estimateX(float time) { 261 return estimate(time, xCoeff); 262 } 263 264 /** 265 * Gets an estimate of the Y position of the pointer at the specified time point. 266 * @param time The time point in seconds, 0 is the last recorded time. 267 * @return The estimated Y coordinate. 268 */ 269 public float estimateY(float time) { 270 return estimate(time, yCoeff); 271 } 272 273 /** 274 * Gets the X coefficient with the specified index. 275 * @param index The index of the coefficient to return. 276 * @return The X coefficient, or 0 if the index is greater than the degree. 277 */ 278 public float getXCoeff(int index) { 279 return index <= degree ? xCoeff[index] : 0; 280 } 281 282 /** 283 * Gets the Y coefficient with the specified index. 284 * @param index The index of the coefficient to return. 285 * @return The Y coefficient, or 0 if the index is greater than the degree. 286 */ 287 public float getYCoeff(int index) { 288 return index <= degree ? yCoeff[index] : 0; 289 } 290 291 private float estimate(float time, float[] c) { 292 float a = 0; 293 float scale = 1; 294 for (int i = 0; i <= degree; i++) { 295 a += c[i] * scale; 296 scale *= time; 297 } 298 return a; 299 } 300 } 301 } 302