Loading apct-tests/perftests/core/src/android/input/MotionPredictorBenchmark.kt +1 −3 Original line number Diff line number Diff line Loading @@ -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 Loading Loading @@ -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()) } } Loading core/api/current.txt +1 −1 Original line number Diff line number Diff line Loading @@ -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); } core/java/android/view/MotionPredictor.java +16 −20 Original line number Diff line number Diff line Loading @@ -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. * Loading Loading @@ -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()) { Loading @@ -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() { Loading Loading @@ -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(); Loading core/jni/android_view_MotionPredictor.cpp +10 −15 Original line number Diff line number Diff line Loading @@ -20,7 +20,6 @@ #include <input/MotionPredictor.h> #include "android_view_MotionEvent.h" #include "core_jni_converters.h" #include "core_jni_helpers.h" /** Loading @@ -30,14 +29,6 @@ namespace android { // ---------------------------------------------------------------------------- static struct { jclass clazz; } gMotionEventClassInfo; // ---------------------------------------------------------------------------- static void release(void* ptr) { delete reinterpret_cast<MotionPredictor*>(ptr); } Loading @@ -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, Loading @@ -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()); } Loading tests/Input/src/com/android/test/input/MotionPredictorTest.kt +4 −6 Original line number Diff line number Diff line Loading @@ -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) } } Loading
apct-tests/perftests/core/src/android/input/MotionPredictorBenchmark.kt +1 −3 Original line number Diff line number Diff line Loading @@ -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 Loading Loading @@ -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()) } } Loading
core/api/current.txt +1 −1 Original line number Diff line number Diff line Loading @@ -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); }
core/java/android/view/MotionPredictor.java +16 −20 Original line number Diff line number Diff line Loading @@ -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. * Loading Loading @@ -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()) { Loading @@ -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() { Loading Loading @@ -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(); Loading
core/jni/android_view_MotionPredictor.cpp +10 −15 Original line number Diff line number Diff line Loading @@ -20,7 +20,6 @@ #include <input/MotionPredictor.h> #include "android_view_MotionEvent.h" #include "core_jni_converters.h" #include "core_jni_helpers.h" /** Loading @@ -30,14 +29,6 @@ namespace android { // ---------------------------------------------------------------------------- static struct { jclass clazz; } gMotionEventClassInfo; // ---------------------------------------------------------------------------- static void release(void* ptr) { delete reinterpret_cast<MotionPredictor*>(ptr); } Loading @@ -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, Loading @@ -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()); } Loading
tests/Input/src/com/android/test/input/MotionPredictorTest.kt +4 −6 Original line number Diff line number Diff line Loading @@ -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) } }