Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 7d3fa093 authored by Jeff Brown's avatar Jeff Brown Committed by Android Git Automerger
Browse files

am f47e76e2: Merge "Make velocity tracker strategy configurable." into jb-dev

* commit 'f47e76e2':
  Make velocity tracker strategy configurable.
parents 66a10964 f47e76e2
Loading
Loading
Loading
Loading
+27 −6
Original line number Diff line number Diff line
@@ -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);
@@ -74,13 +76,31 @@ public final class VelocityTracker implements Poolable<VelocityTracker> {
        return sPool.acquire();
    }

    /**
     * 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() {
        if (mStrategy == null) {
            sPool.release(this);
        }
    }

    /**
     * @hide
@@ -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.
+50 −2
Original line number Diff line number Diff line
@@ -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;
@@ -37,6 +38,10 @@ 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,6 +111,7 @@ 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();
    
@@ -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);
            }
        }
+13 −5
Original line number Diff line number Diff line
@@ -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",
+14 −6
Original line number Diff line number Diff line
@@ -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];
};
+74 −17
Original line number Diff line number Diff line
@@ -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(),