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

Commit 4c38c543 authored by Yeabkal Wubshit's avatar Yeabkal Wubshit Committed by Android (Google) Code Review
Browse files

Merge "Create InputDevice ViewBehavior" into main

parents 81702c5e c5766da6
Loading
Loading
Loading
Loading
+5 −0
Original line number Diff line number Diff line
@@ -20268,6 +20268,7 @@ package android.hardware.input {
    method @Nullable public android.hardware.input.HostUsiVersion getHostUsiVersion(@NonNull android.view.Display);
    method @Nullable public android.view.InputDevice getInputDevice(int);
    method public int[] getInputDeviceIds();
    method @FlaggedApi("com.android.input.flags.input_device_view_behavior_api") @Nullable public android.view.InputDevice.ViewBehavior getInputDeviceViewBehavior(int);
    method @FloatRange(from=0, to=1) public float getMaximumObscuringOpacityForTouch();
    method public boolean isStylusPointerIconEnabled();
    method public void registerInputDeviceListener(android.hardware.input.InputManager.InputDeviceListener, android.os.Handler);
@@ -50448,6 +50449,10 @@ package android.view {
    method public boolean isFromSource(int);
  }
  @FlaggedApi("com.android.input.flags.input_device_view_behavior_api") public static final class InputDevice.ViewBehavior {
    method @FlaggedApi("com.android.input.flags.input_device_view_behavior_api") public boolean shouldSmoothScroll(int, int);
  }
  public abstract class InputEvent implements android.os.Parcelable {
    method public int describeContents();
    method public final android.view.InputDevice getDevice();
+19 −0
Original line number Diff line number Diff line
@@ -16,9 +16,11 @@

package android.hardware.input;

import static com.android.input.flags.Flags.FLAG_INPUT_DEVICE_VIEW_BEHAVIOR_API;
import static com.android.hardware.input.Flags.keyboardLayoutPreviewFlag;

import android.Manifest;
import android.annotation.FlaggedApi;
import android.annotation.FloatRange;
import android.annotation.IntDef;
import android.annotation.NonNull;
@@ -293,6 +295,23 @@ public final class InputManager {
        return mGlobal.getInputDevice(id);
    }

    /**
     * Gets the {@link InputDevice.ViewBehavior} of the input device with a given {@code id}.
     *
     * <p>Use this API to query a fresh view behavior instance whenever the input device
     * changes.
     *
     * @param deviceId the id of the input device whose view behavior is being requested.
     * @return the view behavior of the input device with the provided id, or {@code null} if there
     *      is not input device with the provided id.
     */
    @FlaggedApi(FLAG_INPUT_DEVICE_VIEW_BEHAVIOR_API)
    @Nullable
    public InputDevice.ViewBehavior getInputDeviceViewBehavior(int deviceId) {
        InputDevice device = getInputDevice(deviceId);
        return device == null ? null : device.getViewBehavior();
    }

    /**
     * Gets information about the input device with the specified descriptor.
     * @param descriptor The input device descriptor.
+120 −0
Original line number Diff line number Diff line
@@ -16,7 +16,10 @@

package android.view;

import static com.android.input.flags.Flags.FLAG_INPUT_DEVICE_VIEW_BEHAVIOR_API;

import android.Manifest;
import android.annotation.FlaggedApi;
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
@@ -28,6 +31,7 @@ import android.hardware.BatteryState;
import android.hardware.SensorManager;
import android.hardware.input.HostUsiVersion;
import android.hardware.input.InputDeviceIdentifier;
import android.hardware.input.InputManager;
import android.hardware.input.InputManagerGlobal;
import android.hardware.lights.LightsManager;
import android.icu.util.ULocale;
@@ -90,6 +94,8 @@ public final class InputDevice implements Parcelable {
    private final int mAssociatedDisplayId;
    private final ArrayList<MotionRange> mMotionRanges = new ArrayList<MotionRange>();

    private final ViewBehavior mViewBehavior = new ViewBehavior(this);

    @GuardedBy("mMotionRanges")
    private Vibrator mVibrator; // guarded by mMotionRanges during initialization

@@ -539,6 +545,8 @@ public final class InputDevice implements Parcelable {
            addMotionRange(in.readInt(), in.readInt(), in.readFloat(), in.readFloat(),
                    in.readFloat(), in.readFloat(), in.readFloat());
        }

        mViewBehavior.mShouldSmoothScroll = in.readBoolean();
    }

    /**
@@ -571,6 +579,7 @@ public final class InputDevice implements Parcelable {
        private int mUsiVersionMinor = -1;
        private int mAssociatedDisplayId = Display.INVALID_DISPLAY;
        private List<MotionRange> mMotionRanges = new ArrayList<>();
        private boolean mShouldSmoothScroll;

        /** @see InputDevice#getId() */
        public Builder setId(int id) {
@@ -706,6 +715,16 @@ public final class InputDevice implements Parcelable {
            return this;
        }

        /**
         * Sets the view behavior for smooth scrolling ({@code false} by default).
         *
         * @see ViewBehavior#shouldSmoothScroll(int, int)
         */
        public Builder setShouldSmoothScroll(boolean shouldSmoothScroll) {
            mShouldSmoothScroll = shouldSmoothScroll;
            return this;
        }

        /** Build {@link InputDevice}. */
        public InputDevice build() {
            InputDevice device = new InputDevice(
@@ -745,6 +764,8 @@ public final class InputDevice implements Parcelable {
                        range.getResolution());
            }

            device.setShouldSmoothScroll(mShouldSmoothScroll);

            return device;
        }
    }
@@ -1123,6 +1144,22 @@ public final class InputDevice implements Parcelable {
        return mMotionRanges;
    }

    /**
     * Provides the {@link ViewBehavior} for the device.
     *
     * <p>This behavior is designed to be obtained using the
     * {@link InputManager#getInputDeviceViewBehavior(int)} API, to allow associating the behavior
     * with a {@link Context} (since input device is not associated with a context).
     * The ability to associate the behavior with a context opens capabilities like linking the
     * behavior to user settings, for example.
     *
     * @hide
     */
    @NonNull
    public ViewBehavior getViewBehavior() {
        return mViewBehavior;
    }

    // Called from native code.
    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
    private void addMotionRange(int axis, int source,
@@ -1130,6 +1167,11 @@ public final class InputDevice implements Parcelable {
        mMotionRanges.add(new MotionRange(axis, source, min, max, flat, fuzz, resolution));
    }

    // Called from native code.
    private void setShouldSmoothScroll(boolean shouldSmoothScroll) {
        mViewBehavior.mShouldSmoothScroll = shouldSmoothScroll;
    }

    /**
     * Returns the Bluetooth address of this input device, if known.
     *
@@ -1447,6 +1489,82 @@ public final class InputDevice implements Parcelable {
        }
    }

    /**
     * Provides information on how views processing {@link MotionEvent}s generated by this input
     * device should respond to the events. Use {@link InputManager#getInputDeviceViewBehavior(int)}
     * to get an instance of the view behavior for an input device.
     *
     * <p>See an example below how a {@link View} can use this class to determine and apply the
     * scrolling behavior for a generic {@link MotionEvent}.
     *
     * <pre>{@code
     *     public boolean onGenericMotionEvent(MotionEvent event) {
     *         InputManager manager = context.getSystemService(InputManager.class);
     *         ViewBehavior viewBehavior = manager.getInputDeviceViewBehavior(event.getDeviceId());
     *         // Assume a helper function that tells us which axis to use for scrolling purpose.
     *         int axis = getScrollAxisForGenericMotionEvent(event);
     *         int source = event.getSource();
     *
     *         boolean shouldSmoothScroll =
     *                 viewBehavior != null && viewBehavior.shouldSmoothScroll(axis, source);
     *         // Proceed to running the scrolling logic...
     *     }
     * }</pre>
     *
     * @see InputManager#getInputDeviceViewBehavior(int)
     */
    @FlaggedApi(FLAG_INPUT_DEVICE_VIEW_BEHAVIOR_API)
    public static final class ViewBehavior {
        private static final boolean DEFAULT_SHOULD_SMOOTH_SCROLL = false;

        private final InputDevice mInputDevice;

        // TODO(b/246946631): implement support for InputDevices to adjust this configuration
        // by axis and source. When implemented, the axis/source specific config will take
        // precedence over this global config.
        /** A global smooth scroll configuration applying to all motion axis and input source. */
        private boolean mShouldSmoothScroll = DEFAULT_SHOULD_SMOOTH_SCROLL;

        /** @hide */
        public ViewBehavior(@NonNull InputDevice inputDevice) {
            mInputDevice = inputDevice;
        }

        /**
         * Returns whether a view should smooth scroll when scrolling due to a {@link MotionEvent}
         * generated by the input device.
         *
         * <p>Smooth scroll in this case refers to a scroll that animates the transition between
         * the starting and ending positions of the scroll. When this method returns {@code true},
         * views should try to animate a scroll generated by this device at the given axis and with
         * the given source to produce a good scroll user experience. If this method returns
         * {@code false}, animating scrolls is not necessary.
         *
         * <p>If the input device does not have a {@link MotionRange} with the provided axis and
         * source, this method returns {@code false}.
         *
         * @param axis the {@link MotionEvent} axis whose value is used to get the scroll extent.
         * @param source the {link InputDevice} source from which the {@link MotionEvent} that
         *      triggers the scroll came.
         * @return {@code true} if smooth scrolling should be used for the scroll, or {@code false}
         *      if smooth scrolling is not necessary, or if the provided axis and source combination
         *      is not available for the input device.
         */
        @FlaggedApi(FLAG_INPUT_DEVICE_VIEW_BEHAVIOR_API)
        public boolean shouldSmoothScroll(int axis, int source) {
            // Note: although we currently do not use axis and source in computing the return value,
            // we will keep the API params to avoid further public API changes when we start
            // supporting axis/source configuration. Also, having these params lets OEMs provide
            // their custom implementation of the API that depends on axis and source.

            // TODO(b/246946631): speed up computation using caching of results.
            if (mInputDevice.getMotionRange(axis, source) == null) {
                return false;
            }
            return mShouldSmoothScroll;
        }
    }

    @Override
    public void writeToParcel(Parcel out, int flags) {
        mKeyCharacterMap.writeToParcel(out, flags);
@@ -1484,6 +1602,8 @@ public final class InputDevice implements Parcelable {
            out.writeFloat(range.mFuzz);
            out.writeFloat(range.mResolution);
        }

        out.writeBoolean(mViewBehavior.mShouldSmoothScroll);
    }

    @Override
+18 −4
Original line number Diff line number Diff line
@@ -14,17 +14,16 @@
 * limitations under the License.
 */

#include <input/Input.h>
#include "android_view_InputDevice.h"

#include <android_runtime/AndroidRuntime.h>
#include <com_android_input_flags.h>
#include <input/Input.h>
#include <jni.h>
#include <nativehelper/JNIHelp.h>

#include <nativehelper/ScopedLocalRef.h>

#include "android_view_InputDevice.h"
#include "android_view_KeyCharacterMap.h"

#include "core_jni_helpers.h"

namespace android {
@@ -34,6 +33,7 @@ static struct {

    jmethodID ctor;
    jmethodID addMotionRange;
    jmethodID setShouldSmoothScroll;
} gInputDeviceClassInfo;

jobject android_view_InputDevice_create(JNIEnv* env, const InputDeviceInfo& deviceInfo) {
@@ -103,6 +103,18 @@ jobject android_view_InputDevice_create(JNIEnv* env, const InputDeviceInfo& devi
        }
    }

    if (com::android::input::flags::input_device_view_behavior_api()) {
        const InputDeviceViewBehavior& viewBehavior = deviceInfo.getViewBehavior();
        std::optional<bool> defaultSmoothScroll = viewBehavior.shouldSmoothScroll;
        if (defaultSmoothScroll.has_value()) {
            env->CallVoidMethod(inputDeviceObj.get(), gInputDeviceClassInfo.setShouldSmoothScroll,
                                *defaultSmoothScroll);
            if (env->ExceptionCheck()) {
                return NULL;
            }
        }
    }

    return env->NewLocalRef(inputDeviceObj.get());
}

@@ -118,6 +130,8 @@ int register_android_view_InputDevice(JNIEnv* env)

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

+10 −3
Original line number Diff line number Diff line
@@ -67,8 +67,14 @@ public class InputDeviceTest {
        assertEquals("keyCharacterMap not equal", keyCharacterMap, outKeyCharacterMap);

        for (int j = 0; j < device.getMotionRanges().size(); j++) {
            assertMotionRangeEquals(device.getMotionRanges().get(j),
                    outDevice.getMotionRanges().get(j));
            InputDevice.MotionRange motionRange = device.getMotionRanges().get(j);
            assertMotionRangeEquals(motionRange, outDevice.getMotionRanges().get(j));

            int axis = motionRange.getAxis();
            int source = motionRange.getSource();
            assertEquals(
                    device.getViewBehavior().shouldSmoothScroll(axis, source),
                    outDevice.getViewBehavior().shouldSmoothScroll(axis, source));
        }
    }

@@ -93,7 +99,8 @@ public class InputDeviceTest {
                .setHasBattery(true)
                .setKeyboardLanguageTag("en-US")
                .setKeyboardLayoutType("qwerty")
                .setUsiVersion(new HostUsiVersion(2, 0));
                .setUsiVersion(new HostUsiVersion(2, 0))
                .setShouldSmoothScroll(true);

        for (int i = 0; i < 30; i++) {
            deviceBuilder.addMotionRange(