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.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 long mPtr;
     38     private final String mStrategy;
     39 
     40     private static native long nativeInitialize(String strategy);
     41     private static native void nativeDispose(long ptr);
     42     private static native void nativeClear(long ptr);
     43     private static native void nativeAddMovement(long ptr, MotionEvent event);
     44     private static native void nativeComputeCurrentVelocity(long ptr, int units, float maxVelocity);
     45     private static native float nativeGetXVelocity(long ptr, int id);
     46     private static native float nativeGetYVelocity(long ptr, int id);
     47     private static native boolean nativeGetEstimator(long 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