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