| /* |
| * Copyright (C) 2023 The Android Open Source Project |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| package android.view; |
| |
| import android.annotation.NonNull; |
| import android.annotation.Nullable; |
| import android.content.Context; |
| |
| import com.android.internal.annotations.VisibleForTesting; |
| |
| import libcore.util.NativeAllocationRegistry; |
| |
| /** |
| * Calculate motion predictions. |
| * |
| * Feed motion events to this class in order to generate predicted future events. The prediction |
| * functionality may not be available on all devices: check if a specific source is supported on a |
| * given input device using {@link #isPredictionAvailable}. |
| * |
| * Send all of the events that were received from the system to {@link #record} to generate |
| * complete, accurate predictions from {@link #predict}. When processing the returned predictions, |
| * make sure to consider all of the {@link MotionEvent#getHistoricalAxisValue historical samples}. |
| */ |
| public final class MotionPredictor { |
| |
| // This is a pass-through to the native MotionPredictor object (mPtr). Do not store any state or |
| // add any business logic here -- all of the implementation details should go into the native |
| // MotionPredictor (except for accessing the context/resources, which have no corresponding |
| // native API). |
| |
| private static class RegistryHolder { |
| public static final NativeAllocationRegistry REGISTRY = |
| NativeAllocationRegistry.createMalloced( |
| MotionPredictor.class.getClassLoader(), |
| nativeGetNativeMotionPredictorFinalizer()); |
| } |
| |
| // Pointer to the native object. |
| private final long mPtr; |
| // Device-specific override to enable/disable motion prediction. |
| private final boolean mIsPredictionEnabled; |
| |
| /** |
| * Create a new MotionPredictor for the provided {@link Context}. |
| * @param context The context for the predictions |
| */ |
| public MotionPredictor(@NonNull Context context) { |
| this( |
| context.getResources().getBoolean( |
| com.android.internal.R.bool.config_enableMotionPrediction), |
| context.getResources().getInteger( |
| com.android.internal.R.integer.config_motionPredictionOffsetNanos)); |
| } |
| |
| /** |
| * Internal constructor for testing. |
| * @hide |
| */ |
| @VisibleForTesting |
| public MotionPredictor(boolean isPredictionEnabled, int motionPredictionOffsetNanos) { |
| mIsPredictionEnabled = isPredictionEnabled; |
| mPtr = nativeInitialize(motionPredictionOffsetNanos); |
| RegistryHolder.REGISTRY.registerNativeAllocation(this, mPtr); |
| } |
| |
| /** |
| * Record a movement so that in the future, a prediction for the current gesture can be |
| * 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 (!mIsPredictionEnabled) { |
| return; |
| } |
| nativeRecord(mPtr, event); |
| } |
| |
| /** |
| * 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 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. |
| */ |
| @Nullable |
| public MotionEvent predict(long predictionTimeNanos) { |
| if (!mIsPredictionEnabled) { |
| return null; |
| } |
| return nativePredict(mPtr, predictionTimeNanos); |
| } |
| |
| /** |
| * Check whether a device supports motion predictions for a given source type. |
| * |
| * @param deviceId The input device id. |
| * @param source The source of input events. |
| * @return True if the current device supports predictions, false otherwise. |
| * |
| * @see MotionEvent#getDeviceId |
| * @see MotionEvent#getSource |
| */ |
| public boolean isPredictionAvailable(int deviceId, int source) { |
| return mIsPredictionEnabled && nativeIsPredictionAvailable(mPtr, deviceId, source); |
| } |
| |
| 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 boolean nativeIsPredictionAvailable(long nativePtr, int deviceId, |
| int source); |
| private static native long nativeGetNativeMotionPredictorFinalizer(); |
| } |