1 /*
2  * Copyright (C) 2023 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package android.view;
18 
19 import android.annotation.NonNull;
20 import android.annotation.Nullable;
21 import android.content.Context;
22 
23 import libcore.util.NativeAllocationRegistry;
24 
25 /**
26  * Calculate motion predictions.
27  *
28  * Feed motion events to this class in order to generate predicted future events. The prediction
29  * functionality may not be available on all devices: check if a specific source is supported on a
30  * given input device using {@link #isPredictionAvailable}.
31  *
32  * Send all of the events that were received from the system to {@link #record} to generate
33  * complete, accurate predictions from {@link #predict}. When processing the returned predictions,
34  * make sure to consider all of the {@link MotionEvent#getHistoricalAxisValue historical samples}.
35  */
36 public final class MotionPredictor {
37 
38     // This is a pass-through to the native MotionPredictor object (mPtr). Do not store any state or
39     // add any business logic here -- all of the implementation details should go into the native
40     // MotionPredictor (except for accessing the context/resources, which have no corresponding
41     // native API).
42 
43     private static class RegistryHolder {
44         public static final NativeAllocationRegistry REGISTRY =
45                 NativeAllocationRegistry.createMalloced(
46                         MotionPredictor.class.getClassLoader(),
47                         nativeGetNativeMotionPredictorFinalizer());
48     }
49 
50     // Pointer to the native object.
51     private final long mPtr;
52     // Device-specific override to enable/disable motion prediction.
53     private final boolean mIsPredictionEnabled;
54 
55     /**
56      * Create a new MotionPredictor for the provided {@link Context}.
57      * @param context The context for the predictions
58      */
MotionPredictor(@onNull Context context)59     public MotionPredictor(@NonNull Context context) {
60         mIsPredictionEnabled = context.getResources().getBoolean(
61                 com.android.internal.R.bool.config_enableMotionPrediction);
62         final int offsetNanos = context.getResources().getInteger(
63                 com.android.internal.R.integer.config_motionPredictionOffsetNanos);
64         mPtr = nativeInitialize(offsetNanos);
65         RegistryHolder.REGISTRY.registerNativeAllocation(this, mPtr);
66     }
67 
68     /**
69      * Record a movement so that in the future, a prediction for the current gesture can be
70      * generated. Only gestures from one input device at a time should be provided to an instance of
71      * MotionPredictor.
72      *
73      * @param event The received event
74      *
75      * @throws IllegalArgumentException if an inconsistent MotionEvent stream is sent.
76      */
record(@onNull MotionEvent event)77     public void record(@NonNull MotionEvent event) {
78         if (!mIsPredictionEnabled) {
79             return;
80         }
81         nativeRecord(mPtr, event);
82     }
83 
84     /**
85      * Get a predicted event for the gesture that has been provided to {@link #record}.
86      * Predictions may not reach the requested timestamp if the confidence in the prediction results
87      * is low.
88      *
89      * @param predictionTimeNanos The time that the prediction should target, in the
90      * {@link android.os.SystemClock#uptimeMillis} time base, but in nanoseconds.
91      *
92      * @return The predicted motion event, or `null` if predictions are not supported, or not
93      * possible for the current gesture. Be sure to check the historical data in addition to the
94      * latest ({@link MotionEvent#getX getX()}, {@link MotionEvent#getY getY()}) coordinates for
95      * smooth prediction curves.
96      */
97     @Nullable
predict(long predictionTimeNanos)98     public MotionEvent predict(long predictionTimeNanos) {
99         if (!mIsPredictionEnabled) {
100             return null;
101         }
102         return nativePredict(mPtr, predictionTimeNanos);
103     }
104 
105     /**
106      * Check whether a device supports motion predictions for a given source type.
107      *
108      * @param deviceId The input device id.
109      * @param source The source of input events.
110      * @return True if the current device supports predictions, false otherwise.
111      *
112      * @see MotionEvent#getDeviceId
113      * @see MotionEvent#getSource
114      */
isPredictionAvailable(int deviceId, int source)115     public boolean isPredictionAvailable(int deviceId, int source) {
116         return mIsPredictionEnabled && nativeIsPredictionAvailable(mPtr, deviceId, source);
117     }
118 
nativeInitialize(int offsetNanos)119     private static native long nativeInitialize(int offsetNanos);
nativeRecord(long nativePtr, MotionEvent event)120     private static native void nativeRecord(long nativePtr, MotionEvent event);
nativePredict(long nativePtr, long predictionTimeNanos)121     private static native MotionEvent nativePredict(long nativePtr, long predictionTimeNanos);
nativeIsPredictionAvailable(long nativePtr, int deviceId, int source)122     private static native boolean nativeIsPredictionAvailable(long nativePtr, int deviceId,
123             int source);
nativeGetNativeMotionPredictorFinalizer()124     private static native long nativeGetNativeMotionPredictorFinalizer();
125 }
126