138 lines
5.5 KiB
Java
138 lines
5.5 KiB
Java
/*
|
|
* 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();
|
|
}
|