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.annotation.IntDef; 20 import android.compat.annotation.UnsupportedAppUsage; 21 import android.hardware.input.InputManagerGlobal; 22 import android.os.IInputConstants; 23 import android.util.ArrayMap; 24 import android.util.Pools.SynchronizedPool; 25 26 import java.lang.annotation.Retention; 27 import java.lang.annotation.RetentionPolicy; 28 import java.util.Map; 29 30 /** 31 * Helper for tracking the velocity of motion events, for implementing 32 * flinging and other such gestures. 33 * 34 * Use {@link #obtain} to retrieve a new instance of the class when you are going 35 * to begin tracking. Put the motion events you receive into it with 36 * {@link #addMovement(MotionEvent)}. When you want to determine the velocity, call 37 * {@link #computeCurrentVelocity(int)} and then call the velocity-getter methods like 38 * {@link #getXVelocity(int)}, {@link #getYVelocity(int)}, or {@link #getAxisVelocity(int, int)} 39 * to retrieve velocity for different axes and/or pointer IDs. 40 */ 41 public final class VelocityTracker { 42 private static final SynchronizedPool<VelocityTracker> sPool = 43 new SynchronizedPool<VelocityTracker>(2); 44 45 private static final int ACTIVE_POINTER_ID = -1; 46 47 /** @hide */ 48 @IntDef(value = { 49 MotionEvent.AXIS_X, 50 MotionEvent.AXIS_Y, 51 MotionEvent.AXIS_SCROLL 52 }) 53 @Retention(RetentionPolicy.SOURCE) 54 public @interface VelocityTrackableMotionEventAxis {} 55 56 /** 57 * Use the default Velocity Tracker Strategy. Different axes may use different default 58 * strategies. 59 * 60 * @hide 61 */ 62 public static final int VELOCITY_TRACKER_STRATEGY_DEFAULT = 63 IInputConstants.VELOCITY_TRACKER_STRATEGY_DEFAULT; 64 65 /** 66 * Velocity Tracker Strategy: Impulse. 67 * Physical model of pushing an object. Quality: VERY GOOD. 68 * Works with duplicate coordinates, unclean finger liftoff. 69 * 70 * @hide 71 */ 72 public static final int VELOCITY_TRACKER_STRATEGY_IMPULSE = 73 IInputConstants.VELOCITY_TRACKER_STRATEGY_IMPULSE; 74 75 /** 76 * Velocity Tracker Strategy: LSQ1. 77 * 1st order least squares. Quality: POOR. 78 * Frequently underfits the touch data especially when the finger accelerates 79 * or changes direction. Often underestimates velocity. The direction 80 * is overly influenced by historical touch points. 81 * 82 * @hide 83 */ 84 public static final int VELOCITY_TRACKER_STRATEGY_LSQ1 = 85 IInputConstants.VELOCITY_TRACKER_STRATEGY_LSQ1; 86 87 /** 88 * Velocity Tracker Strategy: LSQ2. 89 * 2nd order least squares. Quality: VERY GOOD. 90 * Pretty much ideal, but can be confused by certain kinds of touch data, 91 * particularly if the panel has a tendency to generate delayed, 92 * duplicate or jittery touch coordinates when the finger is released. 93 * 94 * @hide 95 */ 96 public static final int VELOCITY_TRACKER_STRATEGY_LSQ2 = 97 IInputConstants.VELOCITY_TRACKER_STRATEGY_LSQ2; 98 99 /** 100 * Velocity Tracker Strategy: LSQ3. 101 * 3rd order least squares. Quality: UNUSABLE. 102 * Frequently overfits the touch data yielding wildly divergent estimates 103 * of the velocity when the finger is released. 104 * 105 * @hide 106 */ 107 public static final int VELOCITY_TRACKER_STRATEGY_LSQ3 = 108 IInputConstants.VELOCITY_TRACKER_STRATEGY_LSQ3; 109 110 /** 111 * Velocity Tracker Strategy: WLSQ2_DELTA. 112 * 2nd order weighted least squares, delta weighting. Quality: EXPERIMENTAL 113 * 114 * @hide 115 */ 116 public static final int VELOCITY_TRACKER_STRATEGY_WLSQ2_DELTA = 117 IInputConstants.VELOCITY_TRACKER_STRATEGY_WLSQ2_DELTA; 118 119 /** 120 * Velocity Tracker Strategy: WLSQ2_CENTRAL. 121 * 2nd order weighted least squares, central weighting. Quality: EXPERIMENTAL 122 * 123 * @hide 124 */ 125 public static final int VELOCITY_TRACKER_STRATEGY_WLSQ2_CENTRAL = 126 IInputConstants.VELOCITY_TRACKER_STRATEGY_WLSQ2_CENTRAL; 127 128 /** 129 * Velocity Tracker Strategy: WLSQ2_RECENT. 130 * 2nd order weighted least squares, recent weighting. Quality: EXPERIMENTAL 131 * 132 * @hide 133 */ 134 public static final int VELOCITY_TRACKER_STRATEGY_WLSQ2_RECENT = 135 IInputConstants.VELOCITY_TRACKER_STRATEGY_WLSQ2_RECENT; 136 137 /** 138 * Velocity Tracker Strategy: INT1. 139 * 1st order integrating filter. Quality: GOOD. 140 * Not as good as 'lsq2' because it cannot estimate acceleration but it is 141 * more tolerant of errors. Like 'lsq1', this strategy tends to underestimate 142 * the velocity of a fling but this strategy tends to respond to changes in 143 * direction more quickly and accurately. 144 * 145 * @hide 146 */ 147 public static final int VELOCITY_TRACKER_STRATEGY_INT1 = 148 IInputConstants.VELOCITY_TRACKER_STRATEGY_INT1; 149 150 /** 151 * Velocity Tracker Strategy: INT2. 152 * 2nd order integrating filter. Quality: EXPERIMENTAL. 153 * For comparison purposes only. Unlike 'int1' this strategy can compensate 154 * for acceleration but it typically overestimates the effect. 155 * 156 * @hide 157 */ 158 public static final int VELOCITY_TRACKER_STRATEGY_INT2 = 159 IInputConstants.VELOCITY_TRACKER_STRATEGY_INT2; 160 161 /** 162 * Velocity Tracker Strategy: Legacy. 163 * Legacy velocity tracker algorithm. Quality: POOR. 164 * For comparison purposes only. This algorithm is strongly influenced by 165 * old data points, consistently underestimates velocity and takes a very long 166 * time to adjust to changes in direction. 167 * 168 * @hide 169 */ 170 public static final int VELOCITY_TRACKER_STRATEGY_LEGACY = 171 IInputConstants.VELOCITY_TRACKER_STRATEGY_LEGACY; 172 173 174 /** 175 * Velocity Tracker Strategy look up table. 176 */ 177 private static final Map<String, Integer> STRATEGIES = new ArrayMap<>(); 178 179 /** @hide */ 180 @Retention(RetentionPolicy.SOURCE) 181 @IntDef(prefix = {"VELOCITY_TRACKER_STRATEGY_"}, value = { 182 VELOCITY_TRACKER_STRATEGY_DEFAULT, 183 VELOCITY_TRACKER_STRATEGY_IMPULSE, 184 VELOCITY_TRACKER_STRATEGY_LSQ1, 185 VELOCITY_TRACKER_STRATEGY_LSQ2, 186 VELOCITY_TRACKER_STRATEGY_LSQ3, 187 VELOCITY_TRACKER_STRATEGY_WLSQ2_DELTA, 188 VELOCITY_TRACKER_STRATEGY_WLSQ2_CENTRAL, 189 VELOCITY_TRACKER_STRATEGY_WLSQ2_RECENT, 190 VELOCITY_TRACKER_STRATEGY_INT1, 191 VELOCITY_TRACKER_STRATEGY_INT2, 192 VELOCITY_TRACKER_STRATEGY_LEGACY 193 }) 194 public @interface VelocityTrackerStrategy {} 195 196 private long mPtr; 197 @VelocityTrackerStrategy 198 private final int mStrategy; 199 nativeInitialize(int strategy)200 private static native long nativeInitialize(int strategy); nativeDispose(long ptr)201 private static native void nativeDispose(long ptr); nativeClear(long ptr)202 private static native void nativeClear(long ptr); nativeAddMovement(long ptr, MotionEvent event)203 private static native void nativeAddMovement(long ptr, MotionEvent event); nativeComputeCurrentVelocity(long ptr, int units, float maxVelocity)204 private static native void nativeComputeCurrentVelocity(long ptr, int units, float maxVelocity); nativeGetVelocity(long ptr, int axis, int id)205 private static native float nativeGetVelocity(long ptr, int axis, int id); nativeIsAxisSupported(int axis)206 private static native boolean nativeIsAxisSupported(int axis); 207 208 static { 209 // Strategy string and IDs mapping lookup. 210 STRATEGIES.put("impulse", VELOCITY_TRACKER_STRATEGY_IMPULSE); 211 STRATEGIES.put("lsq1", VELOCITY_TRACKER_STRATEGY_LSQ1); 212 STRATEGIES.put("lsq2", VELOCITY_TRACKER_STRATEGY_LSQ2); 213 STRATEGIES.put("lsq3", VELOCITY_TRACKER_STRATEGY_LSQ3); 214 STRATEGIES.put("wlsq2-delta", VELOCITY_TRACKER_STRATEGY_WLSQ2_DELTA); 215 STRATEGIES.put("wlsq2-central", VELOCITY_TRACKER_STRATEGY_WLSQ2_CENTRAL); 216 STRATEGIES.put("wlsq2-recent", VELOCITY_TRACKER_STRATEGY_WLSQ2_RECENT); 217 STRATEGIES.put("int1", VELOCITY_TRACKER_STRATEGY_INT1); 218 STRATEGIES.put("int2", VELOCITY_TRACKER_STRATEGY_INT2); 219 STRATEGIES.put("legacy", VELOCITY_TRACKER_STRATEGY_LEGACY); 220 } 221 222 /** 223 * Return a strategy ID from string. 224 */ toStrategyId(String strStrategy)225 private static int toStrategyId(String strStrategy) { 226 if (STRATEGIES.containsKey(strStrategy)) { 227 return STRATEGIES.get(strStrategy); 228 } 229 return VELOCITY_TRACKER_STRATEGY_DEFAULT; 230 } 231 232 /** 233 * Retrieve a new VelocityTracker object to watch the velocity of a 234 * motion. Be sure to call {@link #recycle} when done. You should 235 * generally only maintain an active object while tracking a movement, 236 * so that the VelocityTracker can be re-used elsewhere. 237 * 238 * @return Returns a new VelocityTracker. 239 */ obtain()240 static public VelocityTracker obtain() { 241 VelocityTracker instance = sPool.acquire(); 242 return (instance != null) ? instance 243 : new VelocityTracker(VELOCITY_TRACKER_STRATEGY_DEFAULT); 244 } 245 246 /** 247 * Obtains a velocity tracker with the specified strategy as string. 248 * For testing and comparison purposes only. 249 * @deprecated Use {@link obtain(int strategy)} instead. 250 * 251 * @param strategy The strategy, or null to use the default. 252 * @return The velocity tracker. 253 * 254 * @hide 255 */ 256 @UnsupportedAppUsage 257 @Deprecated obtain(String strategy)258 public static VelocityTracker obtain(String strategy) { 259 if (strategy == null) { 260 return obtain(); 261 } 262 return new VelocityTracker(toStrategyId(strategy)); 263 } 264 265 /** 266 * Obtains a velocity tracker with the specified strategy. 267 * 268 * @param strategy The strategy Id, VELOCITY_TRACKER_STRATEGY_DEFAULT to use the default. 269 * @return The velocity tracker. 270 * 271 * @hide 272 */ obtain(int strategy)273 public static VelocityTracker obtain(int strategy) { 274 if (strategy == VELOCITY_TRACKER_STRATEGY_DEFAULT) { 275 return obtain(); 276 } 277 return new VelocityTracker(strategy); 278 } 279 280 /** 281 * Return a VelocityTracker object back to be re-used by others. You must 282 * not touch the object after calling this function. 283 */ recycle()284 public void recycle() { 285 if (mStrategy == VELOCITY_TRACKER_STRATEGY_DEFAULT) { 286 clear(); 287 sPool.release(this); 288 } 289 } 290 291 /** 292 * Return strategy Id of VelocityTracker object. 293 * @return The velocity tracker strategy Id. 294 * 295 * @hide 296 */ getStrategyId()297 public int getStrategyId() { 298 return mStrategy; 299 } 300 VelocityTracker(@elocityTrackerStrategy int strategy)301 private VelocityTracker(@VelocityTrackerStrategy int strategy) { 302 // If user has not selected a specific strategy 303 if (strategy == VELOCITY_TRACKER_STRATEGY_DEFAULT) { 304 final String strategyProperty = InputManagerGlobal.getInstance() 305 .getVelocityTrackerStrategy(); 306 // Check if user specified strategy by overriding system property. 307 if (strategyProperty == null || strategyProperty.isEmpty()) { 308 mStrategy = strategy; 309 } else { 310 mStrategy = toStrategyId(strategyProperty); 311 } 312 } else { 313 // User specified strategy 314 mStrategy = strategy; 315 } 316 mPtr = nativeInitialize(mStrategy); 317 } 318 319 @Override finalize()320 protected void finalize() throws Throwable { 321 try { 322 if (mPtr != 0) { 323 nativeDispose(mPtr); 324 mPtr = 0; 325 } 326 } finally { 327 super.finalize(); 328 } 329 } 330 331 /** 332 * Checks whether a given velocity-trackable {@link MotionEvent} axis is supported for velocity 333 * tracking by this {@link VelocityTracker} instance (refer to 334 * {@link #getAxisVelocity(int, int)} for a list of potentially velocity-trackable axes). 335 * 336 * <p>Note that the value returned from this method will stay the same for a given instance, so 337 * a single check for axis support is enough per a {@link VelocityTracker} instance. 338 * 339 * @param axis The axis to check for velocity support. 340 * @return {@code true} if {@code axis} is supported for velocity tracking, or {@code false} 341 * otherwise. 342 * @see #getAxisVelocity(int, int) 343 * @see #getAxisVelocity(int) 344 */ isAxisSupported(@elocityTrackableMotionEventAxis int axis)345 public boolean isAxisSupported(@VelocityTrackableMotionEventAxis int axis) { 346 return nativeIsAxisSupported(axis); 347 } 348 349 /** 350 * Reset the velocity tracker back to its initial state. 351 */ clear()352 public void clear() { 353 nativeClear(mPtr); 354 } 355 356 /** 357 * Add a user's movement to the tracker. You should call this for the 358 * initial {@link MotionEvent#ACTION_DOWN}, the following 359 * {@link MotionEvent#ACTION_MOVE} events that you receive, and the 360 * final {@link MotionEvent#ACTION_UP}. You can, however, call this 361 * for whichever events you desire. 362 * 363 * @param event The MotionEvent you received and would like to track. 364 */ addMovement(MotionEvent event)365 public void addMovement(MotionEvent event) { 366 if (event == null) { 367 throw new IllegalArgumentException("event must not be null"); 368 } 369 nativeAddMovement(mPtr, event); 370 } 371 372 /** 373 * Equivalent to invoking {@link #computeCurrentVelocity(int, float)} with a maximum 374 * velocity of Float.MAX_VALUE. 375 * 376 * @see #computeCurrentVelocity(int, float) 377 */ computeCurrentVelocity(int units)378 public void computeCurrentVelocity(int units) { 379 nativeComputeCurrentVelocity(mPtr, units, Float.MAX_VALUE); 380 } 381 382 /** 383 * Compute the current velocity based on the points that have been 384 * collected. Only call this when you actually want to retrieve velocity 385 * information, as it is relatively expensive. You can then retrieve 386 * the velocity with {@link #getXVelocity()} and 387 * {@link #getYVelocity()}. 388 * 389 * @param units The units you would like the velocity in. A value of 1 390 * provides units per millisecond, 1000 provides units per second, etc. 391 * Note that the units referred to here are the same units with which motion is reported. For 392 * axes X and Y, the units are pixels. 393 * @param maxVelocity The maximum velocity that can be computed by this method. 394 * This value must be declared in the same unit as the units parameter. This value 395 * must be positive. 396 */ computeCurrentVelocity(int units, float maxVelocity)397 public void computeCurrentVelocity(int units, float maxVelocity) { 398 nativeComputeCurrentVelocity(mPtr, units, maxVelocity); 399 } 400 401 /** 402 * Retrieve the last computed X velocity. You must first call 403 * {@link #computeCurrentVelocity(int)} before calling this function. 404 * 405 * @return The previously computed X velocity. 406 */ getXVelocity()407 public float getXVelocity() { 408 return getXVelocity(ACTIVE_POINTER_ID); 409 } 410 411 /** 412 * Retrieve the last computed Y velocity. You must first call 413 * {@link #computeCurrentVelocity(int)} before calling this function. 414 * 415 * @return The previously computed Y velocity. 416 */ getYVelocity()417 public float getYVelocity() { 418 return getYVelocity(ACTIVE_POINTER_ID); 419 } 420 421 /** 422 * Retrieve the last computed X velocity. You must first call 423 * {@link #computeCurrentVelocity(int)} before calling this function. 424 * 425 * @param id Which pointer's velocity to return. 426 * @return The previously computed X velocity. 427 */ getXVelocity(int id)428 public float getXVelocity(int id) { 429 return nativeGetVelocity(mPtr, MotionEvent.AXIS_X, id); 430 } 431 432 /** 433 * Retrieve the last computed Y velocity. You must first call 434 * {@link #computeCurrentVelocity(int)} before calling this function. 435 * 436 * @param id Which pointer's velocity to return. 437 * @return The previously computed Y velocity. 438 */ getYVelocity(int id)439 public float getYVelocity(int id) { 440 return nativeGetVelocity(mPtr, MotionEvent.AXIS_Y, id); 441 } 442 443 /** 444 * Retrieve the last computed velocity for a given motion axis. You must first call 445 * {@link #computeCurrentVelocity(int)} or {@link #computeCurrentVelocity(int, float)} before 446 * calling this function. 447 * 448 * <p>In addition to {@link MotionEvent#AXIS_X} and {@link MotionEvent#AXIS_Y} which have been 449 * supported since the introduction of this class, the following axes can be candidates for this 450 * method: 451 * <ul> 452 * <li> {@link MotionEvent#AXIS_SCROLL}: supported starting 453 * {@link android.os.Build.VERSION_CODES#UPSIDE_DOWN_CAKE} 454 * </ul> 455 * 456 * <p>Before accessing velocities of an axis using this method, check that your 457 * {@link VelocityTracker} instance supports the axis by using {@link #isAxisSupported(int)}. 458 * 459 * @param axis Which axis' velocity to return. 460 * @param id Which pointer's velocity to return. 461 * @return The previously computed velocity for {@code axis} for pointer ID of {@code id} if 462 * {@code axis} is supported for velocity tracking, or 0 if velocity tracking is not supported 463 * for the axis. 464 * @see #isAxisSupported(int) 465 */ getAxisVelocity(@elocityTrackableMotionEventAxis int axis, int id)466 public float getAxisVelocity(@VelocityTrackableMotionEventAxis int axis, int id) { 467 return nativeGetVelocity(mPtr, axis, id); 468 } 469 470 /** 471 * Equivalent to calling {@link #getAxisVelocity(int, int)} for {@code axis} and the active 472 * pointer. 473 * 474 * @param axis Which axis' velocity to return. 475 * @return The previously computed velocity for {@code axis} for the active pointer if 476 * {@code axis} is supported for velocity tracking, or 0 if velocity tracking is not supported 477 * for the axis. 478 * @see #isAxisSupported(int) 479 * @see #getAxisVelocity(int, int) 480 */ getAxisVelocity(@elocityTrackableMotionEventAxis int axis)481 public float getAxisVelocity(@VelocityTrackableMotionEventAxis int axis) { 482 return nativeGetVelocity(mPtr, axis, ACTIVE_POINTER_ID); 483 } 484 } 485