• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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