/*
|
* Copyright (C) 2006 The Android Open Source Project
|
*
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
* you may not use this file except in compliance with the License.
|
* You may obtain a copy of the License at
|
*
|
* http://www.apache.org/licenses/LICENSE-2.0
|
*
|
* Unless required by applicable law or agreed to in writing, software
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
* See the License for the specific language governing permissions and
|
* limitations under the License.
|
*/
|
|
package android.view;
|
|
import android.annotation.UnsupportedAppUsage;
|
import android.util.Pools.SynchronizedPool;
|
|
/**
|
* Helper for tracking the velocity of touch events, for implementing
|
* flinging and other such gestures.
|
*
|
* Use {@link #obtain} to retrieve a new instance of the class when you are going
|
* to begin tracking. Put the motion events you receive into it with
|
* {@link #addMovement(MotionEvent)}. When you want to determine the velocity call
|
* {@link #computeCurrentVelocity(int)} and then call {@link #getXVelocity(int)}
|
* and {@link #getYVelocity(int)} to retrieve the velocity for each pointer id.
|
*/
|
public final class VelocityTracker {
|
private static final SynchronizedPool<VelocityTracker> sPool =
|
new SynchronizedPool<VelocityTracker>(2);
|
|
private static final int ACTIVE_POINTER_ID = -1;
|
|
private long mPtr;
|
private final String mStrategy;
|
|
private static native long nativeInitialize(String strategy);
|
private static native void nativeDispose(long ptr);
|
private static native void nativeClear(long ptr);
|
private static native void nativeAddMovement(long ptr, MotionEvent event);
|
private static native void nativeComputeCurrentVelocity(long ptr, int units, float maxVelocity);
|
private static native float nativeGetXVelocity(long ptr, int id);
|
private static native float nativeGetYVelocity(long ptr, int id);
|
private static native boolean nativeGetEstimator(long ptr, int id, Estimator outEstimator);
|
|
/**
|
* Retrieve a new VelocityTracker object to watch the velocity of a
|
* motion. Be sure to call {@link #recycle} when done. You should
|
* generally only maintain an active object while tracking a movement,
|
* so that the VelocityTracker can be re-used elsewhere.
|
*
|
* @return Returns a new VelocityTracker.
|
*/
|
static public VelocityTracker obtain() {
|
VelocityTracker instance = sPool.acquire();
|
return (instance != null) ? instance : new VelocityTracker(null);
|
}
|
|
/**
|
* Obtains a velocity tracker with the specified strategy.
|
* For testing and comparison purposes only.
|
*
|
* @param strategy The strategy, or null to use the default.
|
* @return The velocity tracker.
|
*
|
* @hide
|
*/
|
@UnsupportedAppUsage
|
public static VelocityTracker obtain(String strategy) {
|
if (strategy == null) {
|
return obtain();
|
}
|
return new VelocityTracker(strategy);
|
}
|
|
/**
|
* Return a VelocityTracker object back to be re-used by others. You must
|
* not touch the object after calling this function.
|
*/
|
public void recycle() {
|
if (mStrategy == null) {
|
clear();
|
sPool.release(this);
|
}
|
}
|
|
private VelocityTracker(String strategy) {
|
mPtr = nativeInitialize(strategy);
|
mStrategy = strategy;
|
}
|
|
@Override
|
protected void finalize() throws Throwable {
|
try {
|
if (mPtr != 0) {
|
nativeDispose(mPtr);
|
mPtr = 0;
|
}
|
} finally {
|
super.finalize();
|
}
|
}
|
|
/**
|
* Reset the velocity tracker back to its initial state.
|
*/
|
public void clear() {
|
nativeClear(mPtr);
|
}
|
|
/**
|
* Add a user's movement to the tracker. You should call this for the
|
* initial {@link MotionEvent#ACTION_DOWN}, the following
|
* {@link MotionEvent#ACTION_MOVE} events that you receive, and the
|
* final {@link MotionEvent#ACTION_UP}. You can, however, call this
|
* for whichever events you desire.
|
*
|
* @param event The MotionEvent you received and would like to track.
|
*/
|
public void addMovement(MotionEvent event) {
|
if (event == null) {
|
throw new IllegalArgumentException("event must not be null");
|
}
|
nativeAddMovement(mPtr, event);
|
}
|
|
/**
|
* Equivalent to invoking {@link #computeCurrentVelocity(int, float)} with a maximum
|
* velocity of Float.MAX_VALUE.
|
*
|
* @see #computeCurrentVelocity(int, float)
|
*/
|
public void computeCurrentVelocity(int units) {
|
nativeComputeCurrentVelocity(mPtr, units, Float.MAX_VALUE);
|
}
|
|
/**
|
* Compute the current velocity based on the points that have been
|
* collected. Only call this when you actually want to retrieve velocity
|
* information, as it is relatively expensive. You can then retrieve
|
* the velocity with {@link #getXVelocity()} and
|
* {@link #getYVelocity()}.
|
*
|
* @param units The units you would like the velocity in. A value of 1
|
* provides pixels per millisecond, 1000 provides pixels per second, etc.
|
* @param maxVelocity The maximum velocity that can be computed by this method.
|
* This value must be declared in the same unit as the units parameter. This value
|
* must be positive.
|
*/
|
public void computeCurrentVelocity(int units, float maxVelocity) {
|
nativeComputeCurrentVelocity(mPtr, units, maxVelocity);
|
}
|
|
/**
|
* Retrieve the last computed X velocity. You must first call
|
* {@link #computeCurrentVelocity(int)} before calling this function.
|
*
|
* @return The previously computed X velocity.
|
*/
|
public float getXVelocity() {
|
return nativeGetXVelocity(mPtr, ACTIVE_POINTER_ID);
|
}
|
|
/**
|
* Retrieve the last computed Y velocity. You must first call
|
* {@link #computeCurrentVelocity(int)} before calling this function.
|
*
|
* @return The previously computed Y velocity.
|
*/
|
public float getYVelocity() {
|
return nativeGetYVelocity(mPtr, ACTIVE_POINTER_ID);
|
}
|
|
/**
|
* Retrieve the last computed X velocity. You must first call
|
* {@link #computeCurrentVelocity(int)} before calling this function.
|
*
|
* @param id Which pointer's velocity to return.
|
* @return The previously computed X velocity.
|
*/
|
public float getXVelocity(int id) {
|
return nativeGetXVelocity(mPtr, id);
|
}
|
|
/**
|
* Retrieve the last computed Y velocity. You must first call
|
* {@link #computeCurrentVelocity(int)} before calling this function.
|
*
|
* @param id Which pointer's velocity to return.
|
* @return The previously computed Y velocity.
|
*/
|
public float getYVelocity(int id) {
|
return nativeGetYVelocity(mPtr, id);
|
}
|
|
/**
|
* Get an estimator for the movements of a pointer using past movements of the
|
* pointer to predict future movements.
|
*
|
* It is not necessary to call {@link #computeCurrentVelocity(int)} before calling
|
* this method.
|
*
|
* @param id Which pointer's velocity to return.
|
* @param outEstimator The estimator to populate.
|
* @return True if an estimator was obtained, false if there is no information
|
* available about the pointer.
|
*
|
* @hide For internal use only. Not a final API.
|
*/
|
public boolean getEstimator(int id, Estimator outEstimator) {
|
if (outEstimator == null) {
|
throw new IllegalArgumentException("outEstimator must not be null");
|
}
|
return nativeGetEstimator(mPtr, id, outEstimator);
|
}
|
|
/**
|
* An estimator for the movements of a pointer based on a polynomial model.
|
*
|
* The last recorded position of the pointer is at time zero seconds.
|
* Past estimated positions are at negative times and future estimated positions
|
* are at positive times.
|
*
|
* First coefficient is position (in pixels), second is velocity (in pixels per second),
|
* third is acceleration (in pixels per second squared).
|
*
|
* @hide For internal use only. Not a final API.
|
*/
|
public static final class Estimator {
|
// Must match VelocityTracker::Estimator::MAX_DEGREE
|
private static final int MAX_DEGREE = 4;
|
|
/**
|
* Polynomial coefficients describing motion in X.
|
*/
|
@UnsupportedAppUsage
|
public final float[] xCoeff = new float[MAX_DEGREE + 1];
|
|
/**
|
* Polynomial coefficients describing motion in Y.
|
*/
|
@UnsupportedAppUsage
|
public final float[] yCoeff = new float[MAX_DEGREE + 1];
|
|
/**
|
* Polynomial degree, or zero if only position information is available.
|
*/
|
@UnsupportedAppUsage
|
public int degree;
|
|
/**
|
* Confidence (coefficient of determination), between 0 (no fit) and 1 (perfect fit).
|
*/
|
@UnsupportedAppUsage
|
public float confidence;
|
|
/**
|
* Gets an estimate of the X position of the pointer at the specified time point.
|
* @param time The time point in seconds, 0 is the last recorded time.
|
* @return The estimated X coordinate.
|
*/
|
public float estimateX(float time) {
|
return estimate(time, xCoeff);
|
}
|
|
/**
|
* Gets an estimate of the Y position of the pointer at the specified time point.
|
* @param time The time point in seconds, 0 is the last recorded time.
|
* @return The estimated Y coordinate.
|
*/
|
public float estimateY(float time) {
|
return estimate(time, yCoeff);
|
}
|
|
/**
|
* Gets the X coefficient with the specified index.
|
* @param index The index of the coefficient to return.
|
* @return The X coefficient, or 0 if the index is greater than the degree.
|
*/
|
public float getXCoeff(int index) {
|
return index <= degree ? xCoeff[index] : 0;
|
}
|
|
/**
|
* Gets the Y coefficient with the specified index.
|
* @param index The index of the coefficient to return.
|
* @return The Y coefficient, or 0 if the index is greater than the degree.
|
*/
|
public float getYCoeff(int index) {
|
return index <= degree ? yCoeff[index] : 0;
|
}
|
|
private float estimate(float time, float[] c) {
|
float a = 0;
|
float scale = 1;
|
for (int i = 0; i <= degree; i++) {
|
a += c[i] * scale;
|
scale *= time;
|
}
|
return a;
|
}
|
}
|
}
|