diff options
| author | 2022-12-21 15:57:15 +0900 | |
|---|---|---|
| committer | 2023-01-19 02:43:37 +0000 | |
| commit | 7db1b9dc3fece621a23b7922c2b07698177df5e9 (patch) | |
| tree | 637a3a85bde94e68fd0aee7907bac148f0ab6193 | |
| parent | a43dbaae9b91f833ba7dead58109e055ce50f4f9 (diff) | |
[scribe] show hover icon on handwriting area
if user hover stylus pen on handwriting area,
show handwriting pointer icon.
so notify to user that can commit texts via handwriting
Test: Manual Test(hover pointer on handwriting area)
Bug: b/215436642
Change-Id: If18c2ca06436e88aaea4eb776a598aff0d7af851
| -rw-r--r-- | core/api/current.txt | 2 | ||||
| -rw-r--r-- | core/java/android/hardware/input/InputManager.java | 23 | ||||
| -rw-r--r-- | core/java/android/view/HandwritingInitiator.java | 54 | ||||
| -rw-r--r-- | core/java/android/view/MotionEvent.java | 23 | ||||
| -rw-r--r-- | core/java/android/view/PointerIcon.java | 5 | ||||
| -rw-r--r-- | core/java/android/view/ViewRootImpl.java | 20 | ||||
| -rw-r--r-- | core/res/res/drawable/pointer_handwriting_icon.xml | 5 | ||||
| -rw-r--r-- | core/res/res/values/attrs.xml | 4 | ||||
| -rw-r--r-- | core/res/res/values/config.xml | 4 | ||||
| -rw-r--r-- | core/res/res/values/styles.xml | 2 | ||||
| -rw-r--r-- | core/res/res/values/symbols.xml | 2 | ||||
| -rw-r--r-- | services/core/jni/com_android_server_input_InputManagerService.cpp | 2 |
12 files changed, 141 insertions, 5 deletions
diff --git a/core/api/current.txt b/core/api/current.txt index 0ad7d80ea508..e416e9156e81 100644 --- a/core/api/current.txt +++ b/core/api/current.txt @@ -19258,6 +19258,7 @@ package android.hardware.input { method @Nullable public android.view.InputDevice getInputDevice(int); method public int[] getInputDeviceIds(); 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); method public void unregisterInputDeviceListener(android.hardware.input.InputManager.InputDeviceListener); method @Nullable public android.view.VerifiedInputEvent verifyInputEvent(@NonNull android.view.InputEvent); @@ -50742,6 +50743,7 @@ package android.view { field public static final int TYPE_GRAB = 1020; // 0x3fc field public static final int TYPE_GRABBING = 1021; // 0x3fd field public static final int TYPE_HAND = 1002; // 0x3ea + field public static final int TYPE_HANDWRITING = 1022; // 0x3fe field public static final int TYPE_HELP = 1003; // 0x3eb field public static final int TYPE_HORIZONTAL_DOUBLE_ARROW = 1014; // 0x3f6 field public static final int TYPE_NO_DROP = 1012; // 0x3f4 diff --git a/core/java/android/hardware/input/InputManager.java b/core/java/android/hardware/input/InputManager.java index fb201cfbbe38..42b5a3408e88 100644 --- a/core/java/android/hardware/input/InputManager.java +++ b/core/java/android/hardware/input/InputManager.java @@ -99,6 +99,8 @@ public final class InputManager { @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023) private final IInputManager mIm; + private final boolean mIsStylusPointerIconEnabled; + // Guarded by mInputDevicesLock private final Object mInputDevicesLock = new Object(); private SparseArray<InputDevice> mInputDevices; @@ -321,6 +323,15 @@ public final class InputManager { } catch (RemoteException ex) { Log.w(TAG, "Could not get VelocityTracker strategy: " + ex); } + + // TODO(b/266013036): Pass a Context into InputManager constructor. + final Context context = ActivityThread.currentApplication(); + if (context != null) { + mIsStylusPointerIconEnabled = context.getResources() + .getBoolean(com.android.internal.R.bool.config_enableStylusPointerIcon); + } else { + mIsStylusPointerIconEnabled = false; + } } /** @@ -1379,7 +1390,7 @@ public final class InputManager { * Changes the mouse pointer's icon shape into the specified id. * * @param iconId The id of the pointer graphic, as a value between - * {@link PointerIcon.TYPE_ARROW} and {@link PointerIcon.TYPE_GRABBING}. + * {@link PointerIcon.TYPE_ARROW} and {@link PointerIcon.TYPE_HANDWRITING}. * * @hide */ @@ -1402,6 +1413,16 @@ public final class InputManager { } /** + * Check if showing a {@link android.view.PointerIcon} for styluses is enabled. + * + * @return true if a pointer icon will be shown over the location of a + * stylus pointer, false if there is no pointer icon shown for styluses. + */ + public boolean isStylusPointerIconEnabled() { + return mIsStylusPointerIconEnabled; + } + + /** * Request or release pointer capture. * <p> * When in capturing mode, the pointer icon disappears and all mouse events are dispatched to diff --git a/core/java/android/view/HandwritingInitiator.java b/core/java/android/view/HandwritingInitiator.java index 2e4073e9cfd0..8d221ab0b70a 100644 --- a/core/java/android/view/HandwritingInitiator.java +++ b/core/java/android/view/HandwritingInitiator.java @@ -19,6 +19,7 @@ package android.view; import android.annotation.IdRes; import android.annotation.NonNull; import android.annotation.Nullable; +import android.content.Context; import android.graphics.Rect; import android.view.inputmethod.InputMethodManager; @@ -77,6 +78,15 @@ public class HandwritingInitiator { private int mConnectionCount = 0; private final InputMethodManager mImm; + /** + * The handwrite-able View that is currently the target of a hovering stylus pointer. This is + * used to help determine whether the handwriting PointerIcon should be shown in + * {@link #onResolvePointerIcon(Context, MotionEvent)} so that we can reduce the number of calls + * to {@link #findBestCandidateView(float, float)}. + */ + @Nullable + private View mCachedHoverTarget = null; + @VisibleForTesting public HandwritingInitiator(@NonNull ViewConfiguration viewConfiguration, @NonNull InputMethodManager inputMethodManager) { @@ -308,6 +318,48 @@ public class HandwritingInitiator { } /** + * Returns the pointer icon for the motion event, or null if it doesn't specify the icon. + * This gives HandwritingInitiator a chance to show the stylus handwriting icon over a + * handwrite-able area. + */ + public PointerIcon onResolvePointerIcon(Context context, MotionEvent event) { + if (shouldShowHandwritingPointerIcon(event)) { + return PointerIcon.getSystemIcon(context, PointerIcon.TYPE_HANDWRITING); + } + return null; + } + + private boolean shouldShowHandwritingPointerIcon(MotionEvent event) { + if (!event.isStylusPointer() || !event.isHoverEvent()) { + return false; + } + + if (event.getActionMasked() == MotionEvent.ACTION_HOVER_ENTER + || event.getActionMasked() == MotionEvent.ACTION_HOVER_MOVE) { + final float hoverX = event.getX(event.getActionIndex()); + final float hoverY = event.getY(event.getActionIndex()); + + if (mCachedHoverTarget != null) { + final Rect handwritingArea = getViewHandwritingArea(mCachedHoverTarget); + if (isInHandwritingArea(handwritingArea, hoverX, hoverY, mCachedHoverTarget) + && shouldTriggerStylusHandwritingForView(mCachedHoverTarget)) { + return true; + } + } + + final View candidateView = findBestCandidateView(hoverX, hoverY); + + if (candidateView != null) { + mCachedHoverTarget = candidateView; + return true; + } + } + + mCachedHoverTarget = null; + return false; + } + + /** * Given the location of the stylus event, return the best candidate view to initialize * handwriting mode. * @@ -437,7 +489,7 @@ public class HandwritingInitiator { * Return true if the (x, y) is inside by the given {@link Rect} with the View's * handwriting bounds with offsets applied. */ - private boolean isInHandwritingArea(@Nullable Rect handwritingArea, + private static boolean isInHandwritingArea(@Nullable Rect handwritingArea, float x, float y, View view) { if (handwritingArea == null) return false; diff --git a/core/java/android/view/MotionEvent.java b/core/java/android/view/MotionEvent.java index b91019c01610..e92a3269d9f6 100644 --- a/core/java/android/view/MotionEvent.java +++ b/core/java/android/view/MotionEvent.java @@ -2293,6 +2293,29 @@ public final class MotionEvent extends InputEvent implements Parcelable { } /** + * Returns {@code true} if this motion event is from a stylus pointer. + * @hide + */ + public boolean isStylusPointer() { + final int actionIndex = getActionIndex(); + return isFromSource(InputDevice.SOURCE_STYLUS) + && (getToolType(actionIndex) == TOOL_TYPE_STYLUS + || getToolType(actionIndex) == TOOL_TYPE_ERASER); + } + + /** + * Returns {@code true} if this motion event is a hover event, identified by it having an action + * of either {@link #ACTION_HOVER_ENTER}, {@link #ACTION_HOVER_MOVE} or + * {@link #ACTION_HOVER_EXIT}. + * @hide + */ + public boolean isHoverEvent() { + return getActionMasked() == ACTION_HOVER_ENTER + || getActionMasked() == ACTION_HOVER_EXIT + || getActionMasked() == ACTION_HOVER_MOVE; + } + + /** * Gets the motion event flags. * * @see #FLAG_WINDOW_IS_OBSCURED diff --git a/core/java/android/view/PointerIcon.java b/core/java/android/view/PointerIcon.java index 080c0d81f95b..6a493e6c6ff1 100644 --- a/core/java/android/view/PointerIcon.java +++ b/core/java/android/view/PointerIcon.java @@ -138,6 +138,9 @@ public final class PointerIcon implements Parcelable { /** Type constant: grabbing. */ public static final int TYPE_GRABBING = 1021; + /** Type constant: handwriting. */ + public static final int TYPE_HANDWRITING = 1022; + // OEM private types should be defined starting at this range to avoid // conflicts with any system types that may be defined in the future. private static final int TYPE_OEM_FIRST = 10000; @@ -601,6 +604,8 @@ public final class PointerIcon implements Parcelable { return com.android.internal.R.styleable.Pointer_pointerIconGrab; case TYPE_GRABBING: return com.android.internal.R.styleable.Pointer_pointerIconGrabbing; + case TYPE_HANDWRITING: + return com.android.internal.R.styleable.Pointer_pointerIconHandwriting; default: return 0; } diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java index 2114ce7d7ffa..f26d8e211cf3 100644 --- a/core/java/android/view/ViewRootImpl.java +++ b/core/java/android/view/ViewRootImpl.java @@ -6835,7 +6835,13 @@ public final class ViewRootImpl implements ViewParent, } private void maybeUpdatePointerIcon(MotionEvent event) { - if (event.getPointerCount() == 1 && event.isFromSource(InputDevice.SOURCE_MOUSE)) { + if (event.getPointerCount() != 1) { + return; + } + final boolean needsStylusPointerIcon = + InputManager.getInstance().isStylusPointerIconEnabled() + && event.isStylusPointer(); + if (needsStylusPointerIcon || event.isFromSource(InputDevice.SOURCE_MOUSE)) { if (event.getActionMasked() == MotionEvent.ACTION_HOVER_ENTER || event.getActionMasked() == MotionEvent.ACTION_HOVER_EXIT) { // Other apps or the window manager may change the icon type outside of @@ -6903,7 +6909,17 @@ public final class ViewRootImpl implements ViewParent, Slog.d(mTag, "updatePointerIcon called with position out of bounds"); return false; } - final PointerIcon pointerIcon = mView.onResolvePointerIcon(event, pointerIndex); + + PointerIcon pointerIcon = null; + + if (event.isStylusPointer() && InputManager.getInstance().isStylusPointerIconEnabled()) { + pointerIcon = mHandwritingInitiator.onResolvePointerIcon(mContext, event); + } + + if (pointerIcon == null) { + pointerIcon = mView.onResolvePointerIcon(event, pointerIndex); + } + final int pointerType = (pointerIcon != null) ? pointerIcon.getType() : PointerIcon.TYPE_DEFAULT; diff --git a/core/res/res/drawable/pointer_handwriting_icon.xml b/core/res/res/drawable/pointer_handwriting_icon.xml new file mode 100644 index 000000000000..cdbf6938bd57 --- /dev/null +++ b/core/res/res/drawable/pointer_handwriting_icon.xml @@ -0,0 +1,5 @@ +<?xml version="1.0" encoding="utf-8"?> +<pointer-icon xmlns:android="http://schemas.android.com/apk/res/android" + android:bitmap="@drawable/pointer_crosshair" + android:hotSpotX="12dp" + android:hotSpotY="12dp" />
\ No newline at end of file diff --git a/core/res/res/values/attrs.xml b/core/res/res/values/attrs.xml index f4d563c71b20..a8c7ff8210a6 100644 --- a/core/res/res/values/attrs.xml +++ b/core/res/res/values/attrs.xml @@ -3299,6 +3299,8 @@ <enum name="grab" value="1020" /> <!-- Pointer icon of a hand sign while grabbing something. --> <enum name="grabbing" value="1021" /> + <!-- Pointer icon indicating handwriting. --> + <enum name="handwriting" value="1022"/> </attr> <!-- Whether this view has elements that may overlap when drawn. See @@ -9290,6 +9292,8 @@ <attr name="pointerIconGrab" format="reference"/> <!-- Reference to a pointer drawable with STYLE_GRABBING. --> <attr name="pointerIconGrabbing" format="reference"/> + <!-- Reference to a pointer drawable with HANDWRITING. --> + <attr name="pointerIconHandwriting" format="reference"/> </declare-styleable> <declare-styleable name="PointerIcon"> diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml index 6d21fb657a88..c75a5380d8ca 100644 --- a/core/res/res/values/config.xml +++ b/core/res/res/values/config.xml @@ -5935,6 +5935,10 @@ When this resource is empty, that button will not be shown. --> <string name="config_supervisedUserCreationPackage" translatable="false"></string> + <!-- Flag indicating whether the show Stylus pointer icon. + If set, a pointer icon will be shown over the location of a stylus pointer.--> + <bool name="config_enableStylusPointerIcon">false</bool> + <!-- Determines whether SafetyCenter feature is enabled. --> <bool name="config_enableSafetyCenter">true</bool> diff --git a/core/res/res/values/styles.xml b/core/res/res/values/styles.xml index 0a7ffca8c986..79964b3d91f7 100644 --- a/core/res/res/values/styles.xml +++ b/core/res/res/values/styles.xml @@ -1419,6 +1419,7 @@ please see styles_device_defaults.xml. <item name="pointerIconZoomOut">@drawable/pointer_zoom_out_icon</item> <item name="pointerIconGrab">@drawable/pointer_grab_icon</item> <item name="pointerIconGrabbing">@drawable/pointer_grabbing_icon</item> + <item name="pointerIconHandwriting">@drawable/pointer_handwriting_icon</item> </style> <style name="LargePointer"> @@ -1455,6 +1456,7 @@ please see styles_device_defaults.xml. <item name="pointerIconZoomOut">@drawable/pointer_zoom_out_large_icon</item> <item name="pointerIconGrab">@drawable/pointer_grab_large_icon</item> <item name="pointerIconGrabbing">@drawable/pointer_grabbing_large_icon</item> + <item name="pointerIconHandwriting">@drawable/pointer_handwriting_icon</item> </style> <!-- @hide --> diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml index 2abb0d89835b..fe12c385fc7b 100644 --- a/core/res/res/values/symbols.xml +++ b/core/res/res/values/symbols.xml @@ -4807,6 +4807,8 @@ <java-symbol type="string" name="config_supervisedUserCreationPackage"/> + <java-symbol type="bool" name="config_enableStylusPointerIcon" /> + <java-symbol type="bool" name="config_enableSafetyCenter" /> <java-symbol type="bool" name="config_safetyProtectionEnabled" /> diff --git a/services/core/jni/com_android_server_input_InputManagerService.cpp b/services/core/jni/com_android_server_input_InputManagerService.cpp index 40e74b1fe7d4..7c737680e226 100644 --- a/services/core/jni/com_android_server_input_InputManagerService.cpp +++ b/services/core/jni/com_android_server_input_InputManagerService.cpp @@ -1489,7 +1489,7 @@ void NativeInputManager::loadAdditionalMouseResources( mServiceObj, gServiceClassInfo.getContextForDisplay, displayId)); for (int32_t iconId = static_cast<int32_t>(PointerIconStyle::TYPE_CONTEXT_MENU); - iconId <= static_cast<int32_t>(PointerIconStyle::TYPE_GRABBING); ++iconId) { + iconId <= static_cast<int32_t>(PointerIconStyle::TYPE_HANDWRITING); ++iconId) { const PointerIconStyle pointerIconStyle = static_cast<PointerIconStyle>(iconId); PointerIcon pointerIcon; loadSystemIconAsSpriteWithPointerIcon(env, displayContext.get(), pointerIconStyle, |