summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
author Seunghwan Choi <sbee.choi@samsung.corp-partner.google.com> 2022-12-21 15:57:15 +0900
committer Prabir Pradhan <prabirmsp@google.com> 2023-01-19 02:43:37 +0000
commit7db1b9dc3fece621a23b7922c2b07698177df5e9 (patch)
tree637a3a85bde94e68fd0aee7907bac148f0ab6193
parenta43dbaae9b91f833ba7dead58109e055ce50f4f9 (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.txt2
-rw-r--r--core/java/android/hardware/input/InputManager.java23
-rw-r--r--core/java/android/view/HandwritingInitiator.java54
-rw-r--r--core/java/android/view/MotionEvent.java23
-rw-r--r--core/java/android/view/PointerIcon.java5
-rw-r--r--core/java/android/view/ViewRootImpl.java20
-rw-r--r--core/res/res/drawable/pointer_handwriting_icon.xml5
-rw-r--r--core/res/res/values/attrs.xml4
-rw-r--r--core/res/res/values/config.xml4
-rw-r--r--core/res/res/values/styles.xml2
-rw-r--r--core/res/res/values/symbols.xml2
-rw-r--r--services/core/jni/com_android_server_input_InputManagerService.cpp2
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,