diff options
author | 2023-02-27 07:29:02 -0800 | |
---|---|---|
committer | 2023-03-03 21:38:58 +0000 | |
commit | c694cfe77b7aa433c6fd36a6e04c15be96c666a2 (patch) | |
tree | 60f670a0406f383c9efaf81d072502092ddbb988 | |
parent | 91bae30e7b2aa80236d4a372902bc6001a5d7b94 (diff) |
Change MotionPredictor API to only support a single device
Before this CL, the MotionPredictor API was inconvenient to use. The
MotionPredictor.predict returned a list of MotionEvents, with one
MotionEvent per-device.
The goal was to force applications to consider multi-device streams.
However, even if the apps use this API, there's no way they can
currently test this behaviour, since the multi-device feature is not yet
available.
For the multi-device streams feature, the current plan is to provide a
new View callback to get these raw streams. In those streams, the
per-device MotionEvents would continue to look the same. However, the
events may now be interleaved between different devices. For example, a
sequence like this would be possible: DOWN(deviceId=2) ->
DOWN(deviceId=3) -> MOVE(deviceId=2).
That means that the app will likely have to do per-device bookkeeping
anyways. So the app might as well just create a per-device
MotionPredictor object, as well.
Bug: 167946763
Test: (cd frameworks/native/services/inputflinger && atest)
Change-Id: I34b8dc02d4f995146cb3eed33888ae34abde29d6
6 files changed, 34 insertions, 47 deletions
diff --git a/apct-tests/perftests/core/src/android/input/MotionPredictorBenchmark.kt b/apct-tests/perftests/core/src/android/input/MotionPredictorBenchmark.kt index fabf8892b544..9482591c65b5 100644 --- a/apct-tests/perftests/core/src/android/input/MotionPredictorBenchmark.kt +++ b/apct-tests/perftests/core/src/android/input/MotionPredictorBenchmark.kt @@ -33,7 +33,6 @@ import androidx.test.filters.LargeTest import androidx.test.ext.junit.runners.AndroidJUnit4 import org.junit.After -import org.junit.Assert.assertEquals import org.junit.Assert.assertTrue import org.junit.Before import org.junit.Rule @@ -132,8 +131,7 @@ class MotionPredictorBenchmark { predictor.record(moveEvent) val predictionTime = eventTime + eventInterval val predicted = predictor.predict(predictionTime.toNanos()) - assertEquals(1, predicted.size) - assertTrue(predicted[0].eventTime <= (predictionTime + offset).toMillis()) + assertTrue(predicted.eventTime <= (predictionTime + offset).toMillis()) } } diff --git a/core/api/current.txt b/core/api/current.txt index 2b1cb0e4ab65..259f1e9d4c53 100644 --- a/core/api/current.txt +++ b/core/api/current.txt @@ -51443,7 +51443,7 @@ package android.view { public final class MotionPredictor { ctor public MotionPredictor(@NonNull android.content.Context); method public boolean isPredictionAvailable(int, int); - method @NonNull public java.util.List<android.view.MotionEvent> predict(long); + method @Nullable public android.view.MotionEvent predict(long); method public void record(@NonNull android.view.MotionEvent); } diff --git a/core/java/android/view/MotionPredictor.java b/core/java/android/view/MotionPredictor.java index 4d32efea9081..7d452f954fb0 100644 --- a/core/java/android/view/MotionPredictor.java +++ b/core/java/android/view/MotionPredictor.java @@ -17,14 +17,11 @@ package android.view; import android.annotation.NonNull; +import android.annotation.Nullable; import android.content.Context; import libcore.util.NativeAllocationRegistry; -import java.util.Arrays; -import java.util.Collections; -import java.util.List; - /** * Calculate motion predictions. * @@ -68,9 +65,12 @@ public final class MotionPredictor { /** * Record a movement so that in the future, a prediction for the current gesture can be - * generated. Ensure to add all motions from the gesture of interest to generate correct - * predictions. + * generated. Only gestures from one input device at a time should be provided to an instance of + * MotionPredictor. + * * @param event The received event + * + * @throws IllegalArgumentException if an inconsistent MotionEvent stream is sent. */ public void record(@NonNull MotionEvent event) { if (!isPredictionEnabled()) { @@ -80,28 +80,24 @@ public final class MotionPredictor { } /** - * Get predicted events for all gestures that have been provided to {@link #record}. - * If events from multiple devices were sent to 'record', this will produce a separate - * {@link MotionEvent} for each device. The returned list may be empty if no predictions for - * any of the added events/devices are available. + * Get a predicted event for the gesture that has been provided to {@link #record}. * Predictions may not reach the requested timestamp if the confidence in the prediction results * is low. * * @param predictionTimeNanos The time that the prediction should target, in the * {@link android.os.SystemClock#uptimeMillis} time base, but in nanoseconds. * - * @return A list of predicted motion events, with at most one for each device observed by - * {@link #record}. Be sure to check the historical data in addition to the latest - * ({@link MotionEvent#getX getX()}, {@link MotionEvent#getY getY()}) coordinates for smooth - * prediction curves. An empty list is returned if predictions are not supported, or not - * possible for the current set of gestures. + * @return The predicted motion event, or `null` if predictions are not supported, or not + * possible for the current gesture. Be sure to check the historical data in addition to the + * latest ({@link MotionEvent#getX getX()}, {@link MotionEvent#getY getY()}) coordinates for + * smooth prediction curves. */ - @NonNull - public List<MotionEvent> predict(long predictionTimeNanos) { + @Nullable + public MotionEvent predict(long predictionTimeNanos) { if (!isPredictionEnabled()) { - return Collections.emptyList(); + return null; } - return Arrays.asList(nativePredict(mPtr, predictionTimeNanos)); + return nativePredict(mPtr, predictionTimeNanos); } private boolean isPredictionEnabled() { @@ -129,7 +125,7 @@ public final class MotionPredictor { private static native long nativeInitialize(int offsetNanos); private static native void nativeRecord(long nativePtr, MotionEvent event); - private static native MotionEvent[] nativePredict(long nativePtr, long predictionTimeNanos); + private static native MotionEvent nativePredict(long nativePtr, long predictionTimeNanos); private static native boolean nativeIsPredictionAvailable(long nativePtr, int deviceId, int source); private static native long nativeGetNativeMotionPredictorFinalizer(); diff --git a/core/jni/android_view_MotionPredictor.cpp b/core/jni/android_view_MotionPredictor.cpp index 2c232fadbbc5..de3e81c7088b 100644 --- a/core/jni/android_view_MotionPredictor.cpp +++ b/core/jni/android_view_MotionPredictor.cpp @@ -20,7 +20,6 @@ #include <input/MotionPredictor.h> #include "android_view_MotionEvent.h" -#include "core_jni_converters.h" #include "core_jni_helpers.h" /** @@ -30,14 +29,6 @@ namespace android { -// ---------------------------------------------------------------------------- - -static struct { - jclass clazz; -} gMotionEventClassInfo; - -// ---------------------------------------------------------------------------- - static void release(void* ptr) { delete reinterpret_cast<MotionPredictor*>(ptr); } @@ -57,14 +48,20 @@ static void android_view_MotionPredictor_nativeRecord(JNIEnv* env, jclass clazz, jobject event) { MotionPredictor* predictor = reinterpret_cast<MotionPredictor*>(ptr); MotionEvent* motionEvent = android_view_MotionEvent_getNativePtr(env, event); - predictor->record(*motionEvent); + + android::base::Result<void> result = predictor->record(*motionEvent); + if (!result.ok()) { + jniThrowException(env, "java/lang/IllegalArgumentException", + result.error().message().c_str()); + } } static jobject android_view_MotionPredictor_nativePredict(JNIEnv* env, jclass clazz, jlong ptr, jlong predictionTimeNanos) { MotionPredictor* predictor = reinterpret_cast<MotionPredictor*>(ptr); - return toJavaArray(env, predictor->predict(static_cast<nsecs_t>(predictionTimeNanos)), - gMotionEventClassInfo.clazz, &android_view_MotionEvent_obtainFromNative); + return android_view_MotionEvent_obtainFromNative(env, + predictor->predict(static_cast<nsecs_t>( + predictionTimeNanos))); } static jboolean android_view_MotionPredictor_nativeIsPredictionAvailable(JNIEnv* env, jclass clazz, @@ -84,15 +81,13 @@ static const std::array<JNINativeMethod, 5> gMotionPredictorMethods{{ (void*)android_view_MotionPredictor_nativeGetNativeMotionPredictorFinalizer}, {"nativeRecord", "(JLandroid/view/MotionEvent;)V", (void*)android_view_MotionPredictor_nativeRecord}, - {"nativePredict", "(JJ)[Landroid/view/MotionEvent;", + {"nativePredict", "(JJ)Landroid/view/MotionEvent;", (void*)android_view_MotionPredictor_nativePredict}, {"nativeIsPredictionAvailable", "(JII)Z", (void*)android_view_MotionPredictor_nativeIsPredictionAvailable}, }}; int register_android_view_MotionPredictor(JNIEnv* env) { - jclass motionEventClazz = FindClassOrDie(env, "android/view/MotionEvent"); - gMotionEventClassInfo.clazz = MakeGlobalRefOrDie(env, motionEventClazz); return RegisterMethodsOrDie(env, "android/view/MotionPredictor", gMotionPredictorMethods.data(), gMotionPredictorMethods.size()); } diff --git a/tests/Input/src/com/android/test/input/MotionPredictorTest.kt b/tests/Input/src/com/android/test/input/MotionPredictorTest.kt index 8b1b06fe40e8..24a567130ff0 100644 --- a/tests/Input/src/com/android/test/input/MotionPredictorTest.kt +++ b/tests/Input/src/com/android/test/input/MotionPredictorTest.kt @@ -124,14 +124,12 @@ class MotionPredictorTest { predictor.record(moveEvent) val predicted = predictor.predict(Duration.ofMillis(8).toNanos()) - assertEquals(1, predicted.size) - val event = predicted[0] - assertNotNull(event) + assertNotNull(predicted) // Prediction will happen for t=12 (since it is the next input interval after the requested // time, 8, plus the model offset, 1). - assertEquals(12, event.eventTime) - assertEquals(30f, event.x, /*delta=*/5f) - assertEquals(60f, event.y, /*delta=*/15f) + assertEquals(12, predicted!!.eventTime) + assertEquals(30f, predicted.x, /*delta=*/5f) + assertEquals(60f, predicted.y, /*delta=*/15f) } } diff --git a/tests/MotionPrediction/src/test/motionprediction/DrawingView.kt b/tests/MotionPrediction/src/test/motionprediction/DrawingView.kt index f529bf77f32a..229d0c8da6e9 100644 --- a/tests/MotionPrediction/src/test/motionprediction/DrawingView.kt +++ b/tests/MotionPrediction/src/test/motionprediction/DrawingView.kt @@ -97,8 +97,8 @@ class DrawingView(context: Context, attrs: AttributeSet) : View(context, attrs) } // Draw predictions. Convert to nanos and hardcode to +20ms into the future - val predictionList = predictor.predict(eventTime * 1000000 + 20000000) - for (prediction in predictionList) { + val prediction = predictor.predict(eventTime * 1000000 + 20000000) + if (prediction != null) { val realEvents = events.get(prediction.deviceId)!! drawLine(canvas, realEvents[realEvents.size - 1], prediction, predictionPaint) } |