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