Disable lift prediction by default
Did a small study around pressure prediction in order to determine
if it would benefit from acceleration and jank; surprisingly, it
revealed that most lift predictions turn to be inaccurate, and
that adding acceleration makes things even worse. In the few
instances where it was detected properly (around 1%), the
prediction ratchet actually prevented it from being applied.
Because of this, this CL will make lift prediction configurable,
and disabled by default.
Bug: 232941452
Test: gradlew :input:input-motionprediction:test
Change-Id: If14f8c6c62e60c163477a347b3170f7515a623bc
diff --git a/input/input-motionprediction/src/main/java/androidx/input/motionprediction/common/Configuration.java b/input/input-motionprediction/src/main/java/androidx/input/motionprediction/common/Configuration.java
index c67cfcf..c0b64b5 100644
--- a/input/input-motionprediction/src/main/java/androidx/input/motionprediction/common/Configuration.java
+++ b/input/input-motionprediction/src/main/java/androidx/input/motionprediction/common/Configuration.java
@@ -28,8 +28,9 @@
private static volatile Configuration sInstance = null;
private static final Object sLock = new Object();
- private boolean mPreferSystemPrediction;
- private int mPredictionOffset;
+ private final boolean mPredictLift;
+ private final boolean mPreferSystemPrediction;
+ private final int mPredictionOffset;
/**
* Returns the configuration for prediction in this system.
@@ -52,6 +53,7 @@
mPreferSystemPrediction = SystemProperty
.getBoolean("debug.input.androidx_prefer_system_prediction");
mPredictionOffset = SystemProperty.getInt("debug.input.androidx_prediction_offset");
+ mPredictLift = SystemProperty.getBoolean("debug.input.androidx_predict_lift");
}
/**
@@ -71,4 +73,13 @@
public int predictionOffset() {
return mPredictionOffset;
}
+
+ /**
+ * Returns whether or not the pressure should be used to adjust the distance of the prediction
+ *
+ * @return true if the pressure should be used to determine the prediction length
+ */
+ public boolean predictLift() {
+ return mPredictLift;
+ }
}
diff --git a/input/input-motionprediction/src/main/java/androidx/input/motionprediction/kalman/SinglePointerPredictor.java b/input/input-motionprediction/src/main/java/androidx/input/motionprediction/kalman/SinglePointerPredictor.java
index fd64a94..f5d22c3 100644
--- a/input/input-motionprediction/src/main/java/androidx/input/motionprediction/kalman/SinglePointerPredictor.java
+++ b/input/input-motionprediction/src/main/java/androidx/input/motionprediction/kalman/SinglePointerPredictor.java
@@ -24,6 +24,7 @@
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.annotation.RestrictTo;
+import androidx.input.motionprediction.common.Configuration;
import androidx.input.motionprediction.kalman.matrix.DVector2;
import java.util.LinkedList;
@@ -92,6 +93,8 @@
private double mLastOrientation = 0;
private double mLastTilt = 0;
+ private final boolean mPredictLift;
+
/**
* Kalman based predictor, predicting the location of the pen `predictionTarget`
* milliseconds into the future.
@@ -107,6 +110,7 @@
mDownEventTime = 0;
mPointerId = pointerId;
mToolType = toolType;
+ mPredictLift = Configuration.getInstance().predictLift();
}
private void update(float x, float y, float pressure, float orientation,
@@ -277,7 +281,8 @@
long nextPredictedEventTime = predictedEventTime + Math.round(mReportRateMs);
// Abort prediction if the pen is to be lifted.
- if (mPressure < 0.1
+ if (mPredictLift
+ && mPressure < 0.1
&& nextPredictedEventTime > mLastPredictEventTime) {
//TODO: Should we generate ACTION_UP MotionEvent instead of ACTION_MOVE?
break;
diff --git a/input/input-motionprediction/src/test/kotlin/androidx/input/motionprediction/kalman/SinglePointerPredictorTest.kt b/input/input-motionprediction/src/test/kotlin/androidx/input/motionprediction/kalman/SinglePointerPredictorTest.kt
index 2f66349..d14ac06 100644
--- a/input/input-motionprediction/src/test/kotlin/androidx/input/motionprediction/kalman/SinglePointerPredictorTest.kt
+++ b/input/input-motionprediction/src/test/kotlin/androidx/input/motionprediction/kalman/SinglePointerPredictorTest.kt
@@ -92,23 +92,22 @@
}
@Test
- fun predictionNeverGoesBackwardsEvenWhenLifting() {
+ fun liftingDoesNotAffectPredictionDistance() {
val predictor = constructPredictor()
val coordGenerator = { delta: Long -> delta.toFloat() }
// Pressure will be 1 at the beginning and trend to zero while never getting there
val pressureGenerator = fun(delta: Long): Float {
- if (delta > 500) {
- return ((700 - delta) / 500).toFloat()
- }
- return 1f
+ if (delta > 500) {
+ return ((700 - delta) / 500).toFloat()
}
+ return 1f
+ }
val motionGenerator =
MotionEventGenerator(coordGenerator, coordGenerator, pressureGenerator)
var lastPredictedTime = 0L
var lastPredictedEvent: MotionEvent? = null
var predicted: MotionEvent?
- var iteration = 0
- do {
+ for (i in 1..MAX_ITERATIONS) {
predictor.onTouchEvent(motionGenerator.next())
predicted = predictor.predict(motionGenerator.getRateMs().toInt() * 10)
if (predicted != null) {
@@ -118,8 +117,10 @@
assertThat(lastPredictedEvent.getHistorySize()).isEqualTo(0);
}
lastPredictedEvent = predicted
- iteration++
- } while (predicted != null || iteration < INITIAL_FEED)
+ if (i > INITIAL_FEED) {
+ assertThat(predicted).isNotNull()
+ }
+ }
}
}
@@ -129,4 +130,5 @@
)
private const val INITIAL_FEED = 20
+private const val MAX_ITERATIONS = 10000
private const val PREDICT_LENGTH = 10