Home | History | Annotate | Download | only in view
      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