summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--core/java/android/view/VelocityTracker.java33
-rw-r--r--core/java/com/android/internal/widget/PointerLocationView.java52
-rw-r--r--core/jni/android_view_VelocityTracker.cpp18
-rw-r--r--include/androidfw/VelocityTracker.h20
-rw-r--r--libs/androidfw/VelocityTracker.cpp91
5 files changed, 178 insertions, 36 deletions
diff --git a/core/java/android/view/VelocityTracker.java b/core/java/android/view/VelocityTracker.java
index f5870e1..82b3963 100644
--- a/core/java/android/view/VelocityTracker.java
+++ b/core/java/android/view/VelocityTracker.java
@@ -35,7 +35,7 @@ public final class VelocityTracker implements Poolable<VelocityTracker> {
private static final Pool<VelocityTracker> sPool = Pools.synchronizedPool(
Pools.finitePool(new PoolableManager<VelocityTracker>() {
public VelocityTracker newInstance() {
- return new VelocityTracker();
+ return new VelocityTracker(null);
}
public void onAcquired(VelocityTracker element) {
@@ -50,10 +50,12 @@ public final class VelocityTracker implements Poolable<VelocityTracker> {
private static final int ACTIVE_POINTER_ID = -1;
private int mPtr;
+ private final String mStrategy;
+
private VelocityTracker mNext;
private boolean mIsPooled;
- private static native int nativeInitialize();
+ private static native int nativeInitialize(String strategy);
private static native void nativeDispose(int ptr);
private static native void nativeClear(int ptr);
private static native void nativeAddMovement(int ptr, MotionEvent event);
@@ -75,11 +77,29 @@ public final class VelocityTracker implements Poolable<VelocityTracker> {
}
/**
+ * 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
+ */
+ 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() {
- sPool.release(this);
+ if (mStrategy == null) {
+ sPool.release(this);
+ }
}
/**
@@ -110,8 +130,9 @@ public final class VelocityTracker implements Poolable<VelocityTracker> {
mIsPooled = isPooled;
}
- private VelocityTracker() {
- mPtr = nativeInitialize();
+ private VelocityTracker(String strategy) {
+ mPtr = nativeInitialize(strategy);
+ mStrategy = strategy;
}
@Override
@@ -253,7 +274,7 @@ public final class VelocityTracker implements Poolable<VelocityTracker> {
*/
public static final class Estimator {
// Must match VelocityTracker::Estimator::MAX_DEGREE
- private static final int MAX_DEGREE = 2;
+ private static final int MAX_DEGREE = 4;
/**
* Polynomial coefficients describing motion in X.
diff --git a/core/java/com/android/internal/widget/PointerLocationView.java b/core/java/com/android/internal/widget/PointerLocationView.java
index 85e6c16..34cdd93 100644
--- a/core/java/com/android/internal/widget/PointerLocationView.java
+++ b/core/java/com/android/internal/widget/PointerLocationView.java
@@ -23,6 +23,7 @@ import android.graphics.RectF;
import android.graphics.Paint.FontMetricsInt;
import android.hardware.input.InputManager;
import android.hardware.input.InputManager.InputDeviceListener;
+import android.os.SystemProperties;
import android.util.Log;
import android.view.InputDevice;
import android.view.KeyEvent;
@@ -36,7 +37,11 @@ import java.util.ArrayList;
public class PointerLocationView extends View implements InputDeviceListener {
private static final String TAG = "Pointer";
-
+
+ // The system property key used to specify an alternate velocity tracker strategy
+ // to plot alongside the default one. Useful for testing and comparison purposes.
+ private static final String ALT_STRATEGY_PROPERY_KEY = "debug.velocitytracker.alt";
+
public static class PointerState {
// Trace of previous points.
private float[] mTraceX = new float[32];
@@ -53,9 +58,12 @@ public class PointerLocationView extends View implements InputDeviceListener {
// Most recent velocity.
private float mXVelocity;
private float mYVelocity;
+ private float mAltXVelocity;
+ private float mAltYVelocity;
// Position estimator.
private VelocityTracker.Estimator mEstimator = new VelocityTracker.Estimator();
+ private VelocityTracker.Estimator mAltEstimator = new VelocityTracker.Estimator();
public void clearTrace() {
mTraceCount = 0;
@@ -103,7 +111,8 @@ public class PointerLocationView extends View implements InputDeviceListener {
private final PointerCoords mTempCoords = new PointerCoords();
private final VelocityTracker mVelocity;
-
+ private final VelocityTracker mAltVelocity;
+
private final FasterStringBuilder mText = new FasterStringBuilder();
private boolean mPrintCoords = true;
@@ -145,6 +154,14 @@ public class PointerLocationView extends View implements InputDeviceListener {
mActivePointerId = 0;
mVelocity = VelocityTracker.obtain();
+
+ String altStrategy = SystemProperties.get(ALT_STRATEGY_PROPERY_KEY);
+ if (altStrategy.length() != 0) {
+ Log.d(TAG, "Comparing default velocity tracker strategy with " + altStrategy);
+ mAltVelocity = VelocityTracker.obtain(altStrategy);
+ } else {
+ mAltVelocity = null;
+ }
}
public void setPrintCoords(boolean state) {
@@ -296,6 +313,25 @@ public class PointerLocationView extends View implements InputDeviceListener {
float xVel = ps.mXVelocity * (1000 / 60);
float yVel = ps.mYVelocity * (1000 / 60);
canvas.drawLine(lastX, lastY, lastX + xVel, lastY + yVel, mPaint);
+
+ // Draw alternate estimate.
+ if (mAltVelocity != null) {
+ mPaint.setARGB(128, 0, 128, 128);
+ lx = ps.mAltEstimator.estimateX(-ESTIMATE_PAST_POINTS * ESTIMATE_INTERVAL);
+ ly = ps.mAltEstimator.estimateY(-ESTIMATE_PAST_POINTS * ESTIMATE_INTERVAL);
+ for (int i = -ESTIMATE_PAST_POINTS + 1; i <= ESTIMATE_FUTURE_POINTS; i++) {
+ float x = ps.mAltEstimator.estimateX(i * ESTIMATE_INTERVAL);
+ float y = ps.mAltEstimator.estimateY(i * ESTIMATE_INTERVAL);
+ canvas.drawLine(lx, ly, x, y, mPaint);
+ lx = x;
+ ly = y;
+ }
+
+ mPaint.setARGB(255, 64, 255, 128);
+ xVel = ps.mAltXVelocity * (1000 / 60);
+ yVel = ps.mAltYVelocity * (1000 / 60);
+ canvas.drawLine(lastX, lastY, lastX + xVel, lastY + yVel, mPaint);
+ }
}
if (mCurDown && ps.mCurDown) {
@@ -470,6 +506,9 @@ public class PointerLocationView extends View implements InputDeviceListener {
mCurNumPointers = 0;
mMaxNumPointers = 0;
mVelocity.clear();
+ if (mAltVelocity != null) {
+ mAltVelocity.clear();
+ }
}
mCurNumPointers += 1;
@@ -497,6 +536,10 @@ public class PointerLocationView extends View implements InputDeviceListener {
mVelocity.addMovement(event);
mVelocity.computeCurrentVelocity(1);
+ if (mAltVelocity != null) {
+ mAltVelocity.addMovement(event);
+ mAltVelocity.computeCurrentVelocity(1);
+ }
final int N = event.getHistorySize();
for (int historyPos = 0; historyPos < N; historyPos++) {
@@ -528,6 +571,11 @@ public class PointerLocationView extends View implements InputDeviceListener {
ps.mXVelocity = mVelocity.getXVelocity(id);
ps.mYVelocity = mVelocity.getYVelocity(id);
mVelocity.getEstimator(id, ps.mEstimator);
+ if (mAltVelocity != null) {
+ ps.mAltXVelocity = mAltVelocity.getXVelocity(id);
+ ps.mAltYVelocity = mAltVelocity.getYVelocity(id);
+ mAltVelocity.getEstimator(id, ps.mAltEstimator);
+ }
ps.mToolType = event.getToolType(i);
}
}
diff --git a/core/jni/android_view_VelocityTracker.cpp b/core/jni/android_view_VelocityTracker.cpp
index 0180e0a..c2fa3be 100644
--- a/core/jni/android_view_VelocityTracker.cpp
+++ b/core/jni/android_view_VelocityTracker.cpp
@@ -24,6 +24,8 @@
#include <androidfw/VelocityTracker.h>
#include "android_view_MotionEvent.h"
+#include <ScopedUtfChars.h>
+
namespace android {
@@ -42,7 +44,7 @@ static struct {
class VelocityTrackerState {
public:
- VelocityTrackerState();
+ VelocityTrackerState(const char* strategy);
void clear();
void addMovement(const MotionEvent* event);
@@ -61,7 +63,8 @@ private:
Velocity mCalculatedVelocity[MAX_POINTERS];
};
-VelocityTrackerState::VelocityTrackerState() : mActivePointerId(-1) {
+VelocityTrackerState::VelocityTrackerState(const char* strategy) :
+ mVelocityTracker(strategy), mActivePointerId(-1) {
}
void VelocityTrackerState::clear() {
@@ -135,8 +138,13 @@ bool VelocityTrackerState::getEstimator(int32_t id, VelocityTracker::Estimator*
// --- JNI Methods ---
-static jint android_view_VelocityTracker_nativeInitialize(JNIEnv* env, jclass clazz) {
- return reinterpret_cast<jint>(new VelocityTrackerState());
+static jint android_view_VelocityTracker_nativeInitialize(JNIEnv* env, jclass clazz,
+ jstring strategyStr) {
+ if (strategyStr) {
+ ScopedUtfChars strategy(env, strategyStr);
+ return reinterpret_cast<jint>(new VelocityTrackerState(strategy.c_str()));
+ }
+ return reinterpret_cast<jint>(new VelocityTrackerState(NULL));
}
static void android_view_VelocityTracker_nativeDispose(JNIEnv* env, jclass clazz, jint ptr) {
@@ -209,7 +217,7 @@ static jboolean android_view_VelocityTracker_nativeGetEstimator(JNIEnv* env, jcl
static JNINativeMethod gVelocityTrackerMethods[] = {
/* name, signature, funcPtr */
{ "nativeInitialize",
- "()I",
+ "(Ljava/lang/String;)I",
(void*)android_view_VelocityTracker_nativeInitialize },
{ "nativeDispose",
"(I)V",
diff --git a/include/androidfw/VelocityTracker.h b/include/androidfw/VelocityTracker.h
index cbb0782..1d44f13 100644
--- a/include/androidfw/VelocityTracker.h
+++ b/include/androidfw/VelocityTracker.h
@@ -35,7 +35,7 @@ public:
};
struct Estimator {
- static const size_t MAX_DEGREE = 2;
+ static const size_t MAX_DEGREE = 4;
// Estimator time base.
nsecs_t time;
@@ -61,7 +61,10 @@ public:
}
};
- VelocityTracker();
+ // Creates a velocity tracker using the specified strategy.
+ // If strategy is NULL, uses the default strategy for the platform.
+ VelocityTracker(const char* strategy = NULL);
+
~VelocityTracker();
// Resets the velocity tracker state.
@@ -99,10 +102,16 @@ public:
inline BitSet32 getCurrentPointerIdBits() const { return mCurrentPointerIdBits; }
private:
+ static const char* DEFAULT_STRATEGY;
+
nsecs_t mLastEventTime;
BitSet32 mCurrentPointerIdBits;
int32_t mActivePointerId;
VelocityTrackerStrategy* mStrategy;
+
+ bool configureStrategy(const char* strategy);
+
+ static VelocityTrackerStrategy* createStrategy(const char* strategy);
};
@@ -129,7 +138,8 @@ public:
*/
class LeastSquaresVelocityTrackerStrategy : public VelocityTrackerStrategy {
public:
- LeastSquaresVelocityTrackerStrategy();
+ // Degree must be no greater than Estimator::MAX_DEGREE.
+ LeastSquaresVelocityTrackerStrategy(uint32_t degree);
virtual ~LeastSquaresVelocityTrackerStrategy();
virtual void clear();
@@ -139,9 +149,6 @@ public:
virtual bool getEstimator(uint32_t id, VelocityTracker::Estimator* outEstimator) const;
private:
- // Polynomial degree. Must be less than or equal to Estimator::MAX_DEGREE.
- static const uint32_t DEGREE = 2;
-
// Sample horizon.
// We don't use too much history by default since we want to react to quick
// changes in direction.
@@ -160,6 +167,7 @@ private:
}
};
+ const uint32_t mDegree;
uint32_t mIndex;
Movement mMovements[HISTORY_SIZE];
};
diff --git a/libs/androidfw/VelocityTracker.cpp b/libs/androidfw/VelocityTracker.cpp
index 5dbafd8..408b240 100644
--- a/libs/androidfw/VelocityTracker.cpp
+++ b/libs/androidfw/VelocityTracker.cpp
@@ -20,8 +20,8 @@
// Log debug messages about velocity tracking.
#define DEBUG_VELOCITY 0
-// Log debug messages about least squares fitting.
-#define DEBUG_LEAST_SQUARES 0
+// Log debug messages about the progress of the algorithm itself.
+#define DEBUG_STRATEGY 0
#include <math.h>
#include <limits.h>
@@ -31,6 +31,8 @@
#include <utils/String8.h>
#include <utils/Timers.h>
+#include <cutils/properties.h>
+
namespace android {
// Nanoseconds per milliseconds.
@@ -60,7 +62,7 @@ static float vectorNorm(const float* a, uint32_t m) {
return sqrtf(r);
}
-#if DEBUG_LEAST_SQUARES || DEBUG_VELOCITY
+#if DEBUG_STRATEGY || DEBUG_VELOCITY
static String8 vectorToString(const float* a, uint32_t m) {
String8 str;
str.append("[");
@@ -98,15 +100,70 @@ static String8 matrixToString(const float* a, uint32_t m, uint32_t n, bool rowMa
// --- VelocityTracker ---
-VelocityTracker::VelocityTracker() :
- mLastEventTime(0), mCurrentPointerIdBits(0), mActivePointerId(-1),
- mStrategy(new LeastSquaresVelocityTrackerStrategy()) {
+// The default velocity tracker strategy.
+// Although other strategies are available for testing and comparison purposes,
+// this is the strategy that applications will actually use. Be very careful
+// when adjusting the default strategy because it can dramatically affect
+// (often in a bad way) the user experience.
+const char* VelocityTracker::DEFAULT_STRATEGY = "lsq2";
+
+VelocityTracker::VelocityTracker(const char* strategy) :
+ mLastEventTime(0), mCurrentPointerIdBits(0), mActivePointerId(-1) {
+ char value[PROPERTY_VALUE_MAX];
+
+ // Allow the default strategy to be overridden using a system property for debugging.
+ if (!strategy) {
+ int length = property_get("debug.velocitytracker.strategy", value, NULL);
+ if (length > 0) {
+ strategy = value;
+ } else {
+ strategy = DEFAULT_STRATEGY;
+ }
+ }
+
+ // Configure the strategy.
+ if (!configureStrategy(strategy)) {
+ ALOGD("Unrecognized velocity tracker strategy name '%s'.", strategy);
+ if (!configureStrategy(DEFAULT_STRATEGY)) {
+ LOG_ALWAYS_FATAL("Could not create the default velocity tracker strategy '%s'!",
+ strategy);
+ }
+ }
}
VelocityTracker::~VelocityTracker() {
delete mStrategy;
}
+bool VelocityTracker::configureStrategy(const char* strategy) {
+ mStrategy = createStrategy(strategy);
+ return mStrategy != NULL;
+}
+
+VelocityTrackerStrategy* VelocityTracker::createStrategy(const char* strategy) {
+ if (!strcmp("lsq1", strategy)) {
+ // 1st order least squares. Quality: POOR.
+ // Frequently underfits the touch data especially when the finger accelerates
+ // or changes direction. Often underestimates velocity. The direction
+ // is overly influenced by historical touch points.
+ return new LeastSquaresVelocityTrackerStrategy(1);
+ }
+ if (!strcmp("lsq2", strategy)) {
+ // 2nd order least squares. Quality: VERY GOOD.
+ // Pretty much ideal, but can be confused by certain kinds of touch data,
+ // particularly if the panel has a tendency to generate delayed,
+ // duplicate or jittery touch coordinates when the finger is released.
+ return new LeastSquaresVelocityTrackerStrategy(2);
+ }
+ if (!strcmp("lsq3", strategy)) {
+ // 3rd order least squares. Quality: UNUSABLE.
+ // Frequently overfits the touch data yielding wildly divergent estimates
+ // of the velocity when the finger is released.
+ return new LeastSquaresVelocityTrackerStrategy(3);
+ }
+ return NULL;
+}
+
void VelocityTracker::clear() {
mCurrentPointerIdBits.clear();
mActivePointerId = -1;
@@ -259,11 +316,11 @@ bool VelocityTracker::getEstimator(uint32_t id, Estimator* outEstimator) const {
// --- LeastSquaresVelocityTrackerStrategy ---
-const uint32_t LeastSquaresVelocityTrackerStrategy::DEGREE;
const nsecs_t LeastSquaresVelocityTrackerStrategy::HORIZON;
const uint32_t LeastSquaresVelocityTrackerStrategy::HISTORY_SIZE;
-LeastSquaresVelocityTrackerStrategy::LeastSquaresVelocityTrackerStrategy() {
+LeastSquaresVelocityTrackerStrategy::LeastSquaresVelocityTrackerStrategy(uint32_t degree) :
+ mDegree(degree) {
clear();
}
@@ -302,7 +359,7 @@ void LeastSquaresVelocityTrackerStrategy::addMovement(nsecs_t eventTime, BitSet3
* Returns true if a solution is found, false otherwise.
*
* The input consists of two vectors of data points X and Y with indices 0..m-1.
- * The output is a vector B with indices 0..n-1 that describes a polynomial
+ * The output is a vector B with indices 0..n that describes a polynomial
* that fits the data, such the sum of abs(Y[i] - (B[0] + B[1] X[i] + B[2] X[i]^2 ... B[n] X[i]^n))
* for all i between 0 and m-1 is minimized.
*
@@ -332,7 +389,7 @@ void LeastSquaresVelocityTrackerStrategy::addMovement(nsecs_t eventTime, BitSet3
*/
static bool solveLeastSquares(const float* x, const float* y, uint32_t m, uint32_t n,
float* outB, float* outDet) {
-#if DEBUG_LEAST_SQUARES
+#if DEBUG_STRATEGY
ALOGD("solveLeastSquares: m=%d, n=%d, x=%s, y=%s", int(m), int(n),
vectorToString(x, m).string(), vectorToString(y, m).string());
#endif
@@ -345,7 +402,7 @@ static bool solveLeastSquares(const float* x, const float* y, uint32_t m, uint32
a[i][h] = a[i - 1][h] * x[h];
}
}
-#if DEBUG_LEAST_SQUARES
+#if DEBUG_STRATEGY
ALOGD(" - a=%s", matrixToString(&a[0][0], m, n, false /*rowMajor*/).string());
#endif
@@ -366,7 +423,7 @@ static bool solveLeastSquares(const float* x, const float* y, uint32_t m, uint32
float norm = vectorNorm(&q[j][0], m);
if (norm < 0.000001f) {
// vectors are linearly dependent or zero so no solution
-#if DEBUG_LEAST_SQUARES
+#if DEBUG_STRATEGY
ALOGD(" - no solution, norm=%f", norm);
#endif
return false;
@@ -380,7 +437,7 @@ static bool solveLeastSquares(const float* x, const float* y, uint32_t m, uint32
r[j][i] = i < j ? 0 : vectorDot(&q[j][0], &a[i][0], m);
}
}
-#if DEBUG_LEAST_SQUARES
+#if DEBUG_STRATEGY
ALOGD(" - q=%s", matrixToString(&q[0][0], m, n, false /*rowMajor*/).string());
ALOGD(" - r=%s", matrixToString(&r[0][0], n, n, true /*rowMajor*/).string());
@@ -406,7 +463,7 @@ static bool solveLeastSquares(const float* x, const float* y, uint32_t m, uint32
}
outB[i] /= r[i][i];
}
-#if DEBUG_LEAST_SQUARES
+#if DEBUG_STRATEGY
ALOGD(" - b=%s", vectorToString(outB, n).string());
#endif
@@ -433,7 +490,7 @@ static bool solveLeastSquares(const float* x, const float* y, uint32_t m, uint32
sstot += var * var;
}
*outDet = sstot > 0.000001f ? 1.0f - (sserr / sstot) : 1;
-#if DEBUG_LEAST_SQUARES
+#if DEBUG_STRATEGY
ALOGD(" - sserr=%f", sserr);
ALOGD(" - sstot=%f", sstot);
ALOGD(" - det=%f", *outDet);
@@ -475,7 +532,7 @@ bool LeastSquaresVelocityTrackerStrategy::getEstimator(uint32_t id,
}
// Calculate a least squares polynomial fit.
- uint32_t degree = DEGREE;
+ uint32_t degree = mDegree;
if (degree > m - 1) {
degree = m - 1;
}
@@ -487,7 +544,7 @@ bool LeastSquaresVelocityTrackerStrategy::getEstimator(uint32_t id,
outEstimator->time = newestMovement.eventTime;
outEstimator->degree = degree;
outEstimator->confidence = xdet * ydet;
-#if DEBUG_LEAST_SQUARES
+#if DEBUG_STRATEGY
ALOGD("estimate: degree=%d, xCoeff=%s, yCoeff=%s, confidence=%f",
int(outEstimator->degree),
vectorToString(outEstimator->xCoeff, n).string(),