Skip to content

Commit 93578af

Browse files
Jeff BrownAndroid (Google) Code Review
authored andcommitted
Merge "Velocity Tracker II: The Revenge of Velocity Tracker Bug: 5265529"
2 parents a0b1cc0 + b59ab9f commit 93578af

File tree

5 files changed

+496
-56
lines changed

5 files changed

+496
-56
lines changed

core/java/android/view/VelocityTracker.java

Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,8 @@ public void onReleased(VelocityTracker element) {
5959
private static native void nativeComputeCurrentVelocity(int ptr, int units, float maxVelocity);
6060
private static native float nativeGetXVelocity(int ptr, int id);
6161
private static native float nativeGetYVelocity(int ptr, int id);
62+
private static native boolean nativeGetEstimator(int ptr, int id,
63+
int degree, int horizonMillis, Estimator outEstimator);
6264

6365
/**
6466
* Retrieve a new VelocityTracker object to watch the velocity of a
@@ -215,4 +217,94 @@ public float getXVelocity(int id) {
215217
public float getYVelocity(int id) {
216218
return nativeGetYVelocity(mPtr, id);
217219
}
220+
221+
/**
222+
* Get an estimator for the movements of a pointer using past movements of the
223+
* pointer to predict future movements.
224+
*
225+
* It is not necessary to call {@link #computeCurrentVelocity(int)} before calling
226+
* this method.
227+
*
228+
* @param id Which pointer's velocity to return.
229+
* @param degree The desired polynomial degree. The actual estimator may have
230+
* a lower degree than what is requested here. If -1, uses the default degree.
231+
* @param horizonMillis The maximum age of the oldest sample to consider, in milliseconds.
232+
* If -1, uses the default horizon.
233+
* @param outEstimator The estimator to populate.
234+
* @return True if an estimator was obtained, false if there is no information
235+
* available about the pointer.
236+
*
237+
* @hide For internal use only. Not a final API.
238+
*/
239+
public boolean getEstimator(int id, int degree, int horizonMillis, Estimator outEstimator) {
240+
if (outEstimator == null) {
241+
throw new IllegalArgumentException("outEstimator must not be null");
242+
}
243+
return nativeGetEstimator(mPtr, id, degree, horizonMillis, outEstimator);
244+
}
245+
246+
/**
247+
* An estimator for the movements of a pointer based on a polynomial model.
248+
*
249+
* The last recorded position of the pointer is at time zero seconds.
250+
* Past estimated positions are at negative times and future estimated positions
251+
* are at positive times.
252+
*
253+
* First coefficient is position (in pixels), second is velocity (in pixels per second),
254+
* third is acceleration (in pixels per second squared).
255+
*
256+
* @hide For internal use only. Not a final API.
257+
*/
258+
public static final class Estimator {
259+
// Must match VelocityTracker::Estimator::MAX_DEGREE
260+
private static final int MAX_DEGREE = 2;
261+
262+
/**
263+
* Polynomial coefficients describing motion in X.
264+
*/
265+
public final float[] xCoeff = new float[MAX_DEGREE + 1];
266+
267+
/**
268+
* Polynomial coefficients describing motion in Y.
269+
*/
270+
public final float[] yCoeff = new float[MAX_DEGREE + 1];
271+
272+
/**
273+
* Polynomial degree, or zero if only position information is available.
274+
*/
275+
public int degree;
276+
277+
/**
278+
* Confidence (coefficient of determination), between 0 (no fit) and 1 (perfect fit).
279+
*/
280+
public float confidence;
281+
282+
/**
283+
* Gets an estimate of the X position of the pointer at the specified time point.
284+
* @param time The time point in seconds, 0 is the last recorded time.
285+
* @return The estimated X coordinate.
286+
*/
287+
public float estimateX(float time) {
288+
return estimate(time, xCoeff);
289+
}
290+
291+
/**
292+
* Gets an estimate of the Y position of the pointer at the specified time point.
293+
* @param time The time point in seconds, 0 is the last recorded time.
294+
* @return The estimated Y coordinate.
295+
*/
296+
public float estimateY(float time) {
297+
return estimate(time, yCoeff);
298+
}
299+
300+
private float estimate(float time, float[] c) {
301+
float a = 0;
302+
float scale = 1;
303+
for (int i = 0; i <= degree; i++) {
304+
a += c[i] * scale;
305+
scale *= time;
306+
}
307+
return a;
308+
}
309+
}
218310
}

core/java/com/android/internal/widget/PointerLocationView.java

Lines changed: 22 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,10 @@ public static class PointerState {
5151
// Most recent velocity.
5252
private float mXVelocity;
5353
private float mYVelocity;
54-
54+
55+
// Position estimator.
56+
private VelocityTracker.Estimator mEstimator = new VelocityTracker.Estimator();
57+
5558
public void clearTrace() {
5659
mTraceCount = 0;
5760
}
@@ -75,6 +78,10 @@ public void addTrace(float x, float y) {
7578
}
7679
}
7780

81+
private final int ESTIMATE_PAST_POINTS = 4;
82+
private final int ESTIMATE_FUTURE_POINTS = 2;
83+
private final float ESTIMATE_INTERVAL = 0.02f;
84+
7885
private final ViewConfiguration mVC;
7986
private final Paint mTextPaint;
8087
private final Paint mTextBackgroundPaint;
@@ -278,8 +285,20 @@ protected void onDraw(Canvas canvas) {
278285
haveLast = true;
279286
}
280287

281-
// Draw velocity vector.
282288
if (drawn) {
289+
// Draw movement estimate curve.
290+
mPaint.setARGB(128, 128, 0, 128);
291+
float lx = ps.mEstimator.estimateX(-ESTIMATE_PAST_POINTS * ESTIMATE_INTERVAL);
292+
float ly = ps.mEstimator.estimateY(-ESTIMATE_PAST_POINTS * ESTIMATE_INTERVAL);
293+
for (int i = -ESTIMATE_PAST_POINTS + 1; i <= ESTIMATE_FUTURE_POINTS; i++) {
294+
float x = ps.mEstimator.estimateX(i * ESTIMATE_INTERVAL);
295+
float y = ps.mEstimator.estimateY(i * ESTIMATE_INTERVAL);
296+
canvas.drawLine(lx, ly, x, y, mPaint);
297+
lx = x;
298+
ly = y;
299+
}
300+
301+
// Draw velocity vector.
283302
mPaint.setARGB(255, 255, 64, 128);
284303
float xVel = ps.mXVelocity * (1000 / 60);
285304
float yVel = ps.mYVelocity * (1000 / 60);
@@ -517,6 +536,7 @@ public void addPointerEvent(MotionEvent event) {
517536
ps.addTrace(coords.x, coords.y);
518537
ps.mXVelocity = mVelocity.getXVelocity(id);
519538
ps.mYVelocity = mVelocity.getYVelocity(id);
539+
mVelocity.getEstimator(id, -1, -1, ps.mEstimator);
520540
ps.mToolType = event.getToolType(i);
521541
}
522542
}

core/jni/android_view_VelocityTracker.cpp

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,14 @@ namespace android {
2929
// Special constant to request the velocity of the active pointer.
3030
static const int ACTIVE_POINTER_ID = -1;
3131

32+
static struct {
33+
jfieldID xCoeff;
34+
jfieldID yCoeff;
35+
jfieldID degree;
36+
jfieldID confidence;
37+
} gEstimatorClassInfo;
38+
39+
3240
// --- VelocityTrackerState ---
3341

3442
class VelocityTrackerState {
@@ -39,6 +47,8 @@ class VelocityTrackerState {
3947
void addMovement(const MotionEvent* event);
4048
void computeCurrentVelocity(int32_t units, float maxVelocity);
4149
void getVelocity(int32_t id, float* outVx, float* outVy);
50+
bool getEstimator(int32_t id, uint32_t degree, nsecs_t horizon,
51+
VelocityTracker::Estimator* outEstimator);
4252

4353
private:
4454
struct Velocity {
@@ -118,6 +128,11 @@ void VelocityTrackerState::getVelocity(int32_t id, float* outVx, float* outVy) {
118128
}
119129
}
120130

131+
bool VelocityTrackerState::getEstimator(int32_t id, uint32_t degree, nsecs_t horizon,
132+
VelocityTracker::Estimator* outEstimator) {
133+
return mVelocityTracker.getEstimator(id, degree, horizon, outEstimator);
134+
}
135+
121136

122137
// --- JNI Methods ---
123138

@@ -169,6 +184,30 @@ static jfloat android_view_VelocityTracker_nativeGetYVelocity(JNIEnv* env, jclas
169184
return vy;
170185
}
171186

187+
static jboolean android_view_VelocityTracker_nativeGetEstimator(JNIEnv* env, jclass clazz,
188+
jint ptr, jint id, jint degree, jint horizonMillis, jobject outEstimatorObj) {
189+
VelocityTrackerState* state = reinterpret_cast<VelocityTrackerState*>(ptr);
190+
VelocityTracker::Estimator estimator;
191+
bool result = state->getEstimator(id,
192+
degree < 0 ? VelocityTracker::DEFAULT_DEGREE : uint32_t(degree),
193+
horizonMillis < 0 ? VelocityTracker::DEFAULT_HORIZON :
194+
nsecs_t(horizonMillis) * 1000000L,
195+
&estimator);
196+
197+
jfloatArray xCoeffObj = jfloatArray(env->GetObjectField(outEstimatorObj,
198+
gEstimatorClassInfo.xCoeff));
199+
jfloatArray yCoeffObj = jfloatArray(env->GetObjectField(outEstimatorObj,
200+
gEstimatorClassInfo.yCoeff));
201+
202+
env->SetFloatArrayRegion(xCoeffObj, 0, VelocityTracker::Estimator::MAX_DEGREE + 1,
203+
estimator.xCoeff);
204+
env->SetFloatArrayRegion(yCoeffObj, 0, VelocityTracker::Estimator::MAX_DEGREE + 1,
205+
estimator.yCoeff);
206+
env->SetIntField(outEstimatorObj, gEstimatorClassInfo.degree, estimator.degree);
207+
env->SetFloatField(outEstimatorObj, gEstimatorClassInfo.confidence, estimator.confidence);
208+
return result;
209+
}
210+
172211

173212
// --- JNI Registration ---
174213

@@ -195,12 +234,35 @@ static JNINativeMethod gVelocityTrackerMethods[] = {
195234
{ "nativeGetYVelocity",
196235
"(II)F",
197236
(void*)android_view_VelocityTracker_nativeGetYVelocity },
237+
{ "nativeGetEstimator",
238+
"(IIIILandroid/view/VelocityTracker$Estimator;)Z",
239+
(void*)android_view_VelocityTracker_nativeGetEstimator },
198240
};
199241

242+
#define FIND_CLASS(var, className) \
243+
var = env->FindClass(className); \
244+
LOG_FATAL_IF(! var, "Unable to find class " className);
245+
246+
#define GET_FIELD_ID(var, clazz, fieldName, fieldDescriptor) \
247+
var = env->GetFieldID(clazz, fieldName, fieldDescriptor); \
248+
LOG_FATAL_IF(! var, "Unable to find field " fieldName);
249+
200250
int register_android_view_VelocityTracker(JNIEnv* env) {
201251
int res = jniRegisterNativeMethods(env, "android/view/VelocityTracker",
202252
gVelocityTrackerMethods, NELEM(gVelocityTrackerMethods));
203253
LOG_FATAL_IF(res < 0, "Unable to register native methods.");
254+
255+
jclass clazz;
256+
FIND_CLASS(clazz, "android/view/VelocityTracker$Estimator");
257+
258+
GET_FIELD_ID(gEstimatorClassInfo.xCoeff, clazz,
259+
"xCoeff", "[F");
260+
GET_FIELD_ID(gEstimatorClassInfo.yCoeff, clazz,
261+
"yCoeff", "[F");
262+
GET_FIELD_ID(gEstimatorClassInfo.degree, clazz,
263+
"degree", "I");
264+
GET_FIELD_ID(gEstimatorClassInfo.confidence, clazz,
265+
"confidence", "F");
204266
return 0;
205267
}
206268

include/ui/Input.h

Lines changed: 40 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -620,10 +620,41 @@ class PreallocatedInputEventFactory : public InputEventFactoryInterface {
620620
*/
621621
class VelocityTracker {
622622
public:
623+
// Default polynomial degree. (used by getVelocity)
624+
static const uint32_t DEFAULT_DEGREE = 2;
625+
626+
// Default sample horizon. (used by getVelocity)
627+
// We don't use too much history by default since we want to react to quick
628+
// changes in direction.
629+
static const nsecs_t DEFAULT_HORIZON = 100 * 1000000; // 100 ms
630+
623631
struct Position {
624632
float x, y;
625633
};
626634

635+
struct Estimator {
636+
static const size_t MAX_DEGREE = 2;
637+
638+
// Polynomial coefficients describing motion in X and Y.
639+
float xCoeff[MAX_DEGREE + 1], yCoeff[MAX_DEGREE + 1];
640+
641+
// Polynomial degree (number of coefficients), or zero if no information is
642+
// available.
643+
uint32_t degree;
644+
645+
// Confidence (coefficient of determination), between 0 (no fit) and 1 (perfect fit).
646+
float confidence;
647+
648+
inline void clear() {
649+
degree = 0;
650+
confidence = 0;
651+
for (size_t i = 0; i <= MAX_DEGREE; i++) {
652+
xCoeff[i] = 0;
653+
yCoeff[i] = 0;
654+
}
655+
}
656+
};
657+
627658
VelocityTracker();
628659

629660
// Resets the velocity tracker state.
@@ -645,10 +676,16 @@ class VelocityTracker {
645676
void addMovement(const MotionEvent* event);
646677

647678
// Gets the velocity of the specified pointer id in position units per second.
648-
// Returns false and sets the velocity components to zero if there is no movement
649-
// information for the pointer.
679+
// Returns false and sets the velocity components to zero if there is
680+
// insufficient movement information for the pointer.
650681
bool getVelocity(uint32_t id, float* outVx, float* outVy) const;
651682

683+
// Gets a quadratic estimator for the movements of the specified pointer id.
684+
// Returns false and clears the estimator if there is no information available
685+
// about the pointer.
686+
bool getEstimator(uint32_t id, uint32_t degree, nsecs_t horizon,
687+
Estimator* outEstimator) const;
688+
652689
// Gets the active pointer id, or -1 if none.
653690
inline int32_t getActivePointerId() const { return mActivePointerId; }
654691

@@ -657,13 +694,7 @@ class VelocityTracker {
657694

658695
private:
659696
// Number of samples to keep.
660-
static const uint32_t HISTORY_SIZE = 10;
661-
662-
// Oldest sample to consider when calculating the velocity.
663-
static const nsecs_t MAX_AGE = 100 * 1000000; // 100 ms
664-
665-
// The minimum duration between samples when estimating velocity.
666-
static const nsecs_t MIN_DURATION = 5 * 1000000; // 5 ms
697+
static const uint32_t HISTORY_SIZE = 20;
667698

668699
struct Movement {
669700
nsecs_t eventTime;

0 commit comments

Comments
 (0)