Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit e76ea25c authored by Siarhei Vishniakou's avatar Siarhei Vishniakou Committed by Android (Google) Code Review
Browse files

Merge "Prediction for motion events"

parents 40f4255a a51cbe30
Loading
Loading
Loading
Loading
+116 −0
Original line number Diff line number Diff line
/*
 * 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.content.Context;

import libcore.util.NativeAllocationRegistry;

import java.util.Arrays;
import java.util.List;

/**
 * Calculates motion predictions.
 *
 * Add motions here to get predicted events!
 * @hide
 */
// Acts as a pass-through to the native MotionPredictor object.
// Do not store any state in this Java layer, or add any business logic here. All of the
// implementation details should go into the native MotionPredictor.
// The context / resource access must be here rather than in native layer due to the lack of the
// corresponding native API surface.
public final class MotionPredictor {

    private static class RegistryHolder {
        public static final NativeAllocationRegistry REGISTRY =
                NativeAllocationRegistry.createMalloced(
                        MotionPredictor.class.getClassLoader(),
                        nativeGetNativeMotionPredictorFinalizer());
    }

    // Pointer to the native object.
    private final long mPtr;
    private final Context mContext;

    /**
     * Create a new MotionPredictor for the provided {@link Context}.
     * @param context The context for the predictions
     */
    public MotionPredictor(@NonNull Context context) {
        mContext = context;
        final int offsetNanos = mContext.getResources().getInteger(
                com.android.internal.R.integer.config_motionPredictionOffsetNanos);
        mPtr = nativeInitialize(offsetNanos);
        RegistryHolder.REGISTRY.registerNativeAllocation(this, mPtr);
    }

    /**
     * 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 the correct
     * prediction.
     * @param event The received event
     */
    public void record(@NonNull MotionEvent event) {
        nativeRecord(mPtr, event);
    }

    /**
     * Get predicted events for all gestures that have been provided to the 'record' function.
     * If events from multiple devices were sent to 'record', this will produce a separate
     * {@link MotionEvent} for each device id. The returned list may be empty if no predictions for
     * any of the added events are available.
     * 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 list of predicted motion events, for each device id. Ensure to check the
     * historical data in addition to the latest ({@link MotionEvent#getX getX()},
     * {@link MotionEvent#getY getY()}) coordinates for smoothest prediction curves. Empty list is
     * returned if predictions are not supported, or not possible for the current set of gestures.
     */
    @NonNull
    public List<MotionEvent> predict(long predictionTimeNanos) {
        return Arrays.asList(nativePredict(mPtr, predictionTimeNanos));
    }

    /**
     * Check whether this device supports motion predictions for the 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.
     */
    public boolean isPredictionAvailable(int deviceId, int source) {
        // Device-specific override
        if (!mContext.getResources().getBoolean(
                com.android.internal.R.bool.config_enableMotionPrediction)) {
            return false;
        }
        return 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();
}
+2 −0
Original line number Diff line number Diff line
@@ -129,6 +129,7 @@ cc_library_shared {
                "android_view_KeyCharacterMap.cpp",
                "android_view_KeyEvent.cpp",
                "android_view_MotionEvent.cpp",
                "android_view_MotionPredictor.cpp",
                "android_view_PointerIcon.cpp",
                "android_view_Surface.cpp",
                "android_view_SurfaceControl.cpp",
@@ -283,6 +284,7 @@ cc_library_shared {
                "libhwui",
                "libmediandk",
                "libpermission",
                "libPlatformProperties",
                "libsensor",
                "libinput",
                "libcamera_client",
+2 −0
Original line number Diff line number Diff line
@@ -187,6 +187,7 @@ extern int register_android_view_InputQueue(JNIEnv* env);
extern int register_android_view_KeyCharacterMap(JNIEnv *env);
extern int register_android_view_KeyEvent(JNIEnv* env);
extern int register_android_view_MotionEvent(JNIEnv* env);
extern int register_android_view_MotionPredictor(JNIEnv* env);
extern int register_android_view_PointerIcon(JNIEnv* env);
extern int register_android_view_VelocityTracker(JNIEnv* env);
extern int register_android_view_VerifiedKeyEvent(JNIEnv* env);
@@ -1643,6 +1644,7 @@ static const RegJNIRec gRegJNI[] = {
        REG_JNI(register_android_view_InputQueue),
        REG_JNI(register_android_view_KeyEvent),
        REG_JNI(register_android_view_MotionEvent),
        REG_JNI(register_android_view_MotionPredictor),
        REG_JNI(register_android_view_PointerIcon),
        REG_JNI(register_android_view_VelocityTracker),
        REG_JNI(register_android_view_VerifiedKeyEvent),
+2 −4
Original line number Diff line number Diff line
@@ -97,7 +97,6 @@ jobject android_view_InputDevice_create(JNIEnv* env, const InputDeviceInfo& devi
    return env->NewLocalRef(inputDeviceObj.get());
}


int register_android_view_InputDevice(JNIEnv* env)
{
    gInputDeviceClassInfo.clazz = FindClassOrDie(env, "android/view/InputDevice");
@@ -108,9 +107,8 @@ int register_android_view_InputDevice(JNIEnv* env)
                                                  "String;ZIILandroid/view/KeyCharacterMap;Ljava/"
                                                  "lang/String;Ljava/lang/String;ZZZZZZ)V");

    gInputDeviceClassInfo.addMotionRange = GetMethodIDOrDie(env, gInputDeviceClassInfo.clazz,
            "addMotionRange", "(IIFFFFF)V");

    gInputDeviceClassInfo.addMotionRange =
            GetMethodIDOrDie(env, gInputDeviceClassInfo.clazz, "addMotionRange", "(IIFFFFF)V");
    return 0;
}

+14 −0
Original line number Diff line number Diff line
@@ -103,6 +103,20 @@ jobject android_view_MotionEvent_obtainAsCopy(JNIEnv* env, const MotionEvent& ev
    return eventObj;
}

jobject android_view_MotionEvent_obtainFromNative(JNIEnv* env, std::unique_ptr<MotionEvent> event) {
    if (event == nullptr) {
        return nullptr;
    }
    jobject eventObj =
            env->CallStaticObjectMethod(gMotionEventClassInfo.clazz, gMotionEventClassInfo.obtain);
    if (env->ExceptionCheck() || !eventObj) {
        LOGE_EX(env);
        LOG_ALWAYS_FATAL("An exception occurred while obtaining a Java motion event.");
    }
    android_view_MotionEvent_setNativePtr(env, eventObj, event.release());
    return eventObj;
}

status_t android_view_MotionEvent_recycle(JNIEnv* env, jobject eventObj) {
    env->CallVoidMethod(eventObj, gMotionEventClassInfo.recycle);
    if (env->ExceptionCheck()) {
Loading