summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
author Taran Singh <tarandeep@google.com> 2022-11-04 18:12:07 +0000
committer Android (Google) Code Review <android-gerrit@google.com> 2022-11-04 18:12:07 +0000
commit48cba7c99a5eb2de425f4713e366e37bf0c8eee5 (patch)
tree1ac11c01e044749f339e611f5fae625d0c741cb4
parent5dedf4cdefd0d17039fbdf0a6937db6dffc755b7 (diff)
parent95baa907de566451093ea576a25b2ab963728416 (diff)
Merge "API for realtime Scribe gesture preview"
-rw-r--r--core/api/current.txt14
-rw-r--r--core/java/android/inputmethodservice/IRemoteInputConnectionInvoker.java22
-rw-r--r--core/java/android/inputmethodservice/RemoteInputConnection.java10
-rw-r--r--core/java/android/view/inputmethod/DeleteGesture.java2
-rw-r--r--core/java/android/view/inputmethod/DeleteRangeGesture.java2
-rw-r--r--core/java/android/view/inputmethod/EditorInfo.java97
-rw-r--r--core/java/android/view/inputmethod/InputConnection.java29
-rw-r--r--core/java/android/view/inputmethod/InputConnectionWrapper.java12
-rw-r--r--core/java/android/view/inputmethod/PreviewableHandwritingGesture.java38
-rw-r--r--core/java/android/view/inputmethod/RemoteInputConnectionImpl.java30
-rw-r--r--core/java/android/view/inputmethod/SelectGesture.java3
-rw-r--r--core/java/android/view/inputmethod/SelectRangeGesture.java3
-rw-r--r--core/java/com/android/internal/inputmethod/IRemoteInputConnection.aidl4
-rw-r--r--core/tests/coretests/src/android/view/inputmethod/EditorInfoTest.java7
14 files changed, 263 insertions, 10 deletions
diff --git a/core/api/current.txt b/core/api/current.txt
index 6e0e26f204fc..c8241066b258 100644
--- a/core/api/current.txt
+++ b/core/api/current.txt
@@ -53403,7 +53403,7 @@ package android.view.inputmethod {
method @NonNull public android.view.inputmethod.CursorAnchorInfo.Builder setTextAppearanceInfo(@Nullable android.view.inputmethod.TextAppearanceInfo);
}
- public final class DeleteGesture extends android.view.inputmethod.HandwritingGesture implements android.os.Parcelable {
+ public final class DeleteGesture extends android.view.inputmethod.PreviewableHandwritingGesture implements android.os.Parcelable {
method public int describeContents();
method @NonNull public android.graphics.RectF getDeletionArea();
method public int getGranularity();
@@ -53419,7 +53419,7 @@ package android.view.inputmethod {
method @NonNull public android.view.inputmethod.DeleteGesture.Builder setGranularity(int);
}
- public final class DeleteRangeGesture extends android.view.inputmethod.HandwritingGesture implements android.os.Parcelable {
+ public final class DeleteRangeGesture extends android.view.inputmethod.PreviewableHandwritingGesture implements android.os.Parcelable {
method public int describeContents();
method @NonNull public android.graphics.RectF getDeletionEndArea();
method @NonNull public android.graphics.RectF getDeletionStartArea();
@@ -53461,11 +53461,13 @@ package android.view.inputmethod {
method @Nullable public CharSequence getInitialTextAfterCursor(@IntRange(from=0) int, int);
method @Nullable public CharSequence getInitialTextBeforeCursor(@IntRange(from=0) int, int);
method public int getInitialToolType();
+ method @NonNull public java.util.Set<java.lang.Class<? extends android.view.inputmethod.PreviewableHandwritingGesture>> getSupportedHandwritingGesturePreviews();
method @NonNull public java.util.List<java.lang.Class<? extends android.view.inputmethod.HandwritingGesture>> getSupportedHandwritingGestures();
method public final void makeCompatible(int);
method public void setInitialSurroundingSubText(@NonNull CharSequence, int);
method public void setInitialSurroundingText(@NonNull CharSequence);
method public void setInitialToolType(int);
+ method public void setSupportedHandwritingGesturePreviews(@NonNull java.util.Set<java.lang.Class<? extends android.view.inputmethod.PreviewableHandwritingGesture>>);
method public void setSupportedHandwritingGestures(@NonNull java.util.List<java.lang.Class<? extends android.view.inputmethod.HandwritingGesture>>);
method public void writeToParcel(android.os.Parcel, int);
field @NonNull public static final android.os.Parcelable.Creator<android.view.inputmethod.EditorInfo> CREATOR;
@@ -53640,6 +53642,7 @@ package android.view.inputmethod {
method public default void performHandwritingGesture(@NonNull android.view.inputmethod.HandwritingGesture, @Nullable java.util.concurrent.Executor, @Nullable java.util.function.IntConsumer);
method public boolean performPrivateCommand(String, android.os.Bundle);
method public default boolean performSpellCheck();
+ method public default boolean previewHandwritingGesture(@NonNull android.view.inputmethod.PreviewableHandwritingGesture, @Nullable android.os.CancellationSignal);
method public default boolean replaceText(@IntRange(from=0) int, @IntRange(from=0) int, @NonNull CharSequence, int, @Nullable android.view.inputmethod.TextAttribute);
method public boolean reportFullscreenMode(boolean);
method public boolean requestCursorUpdates(int);
@@ -53900,6 +53903,9 @@ package android.view.inputmethod {
method @NonNull public android.view.inputmethod.JoinOrSplitGesture.Builder setJoinOrSplitPoint(@NonNull android.graphics.PointF);
}
+ public abstract class PreviewableHandwritingGesture extends android.view.inputmethod.HandwritingGesture {
+ }
+
public final class RemoveSpaceGesture extends android.view.inputmethod.HandwritingGesture implements android.os.Parcelable {
method public int describeContents();
method @NonNull public android.graphics.PointF getEndPoint();
@@ -53915,7 +53921,7 @@ package android.view.inputmethod {
method @NonNull public android.view.inputmethod.RemoveSpaceGesture.Builder setPoints(@NonNull android.graphics.PointF, @NonNull android.graphics.PointF);
}
- public final class SelectGesture extends android.view.inputmethod.HandwritingGesture implements android.os.Parcelable {
+ public final class SelectGesture extends android.view.inputmethod.PreviewableHandwritingGesture implements android.os.Parcelable {
method public int describeContents();
method public int getGranularity();
method @NonNull public android.graphics.RectF getSelectionArea();
@@ -53931,7 +53937,7 @@ package android.view.inputmethod {
method @NonNull public android.view.inputmethod.SelectGesture.Builder setSelectionArea(@NonNull android.graphics.RectF);
}
- public final class SelectRangeGesture extends android.view.inputmethod.HandwritingGesture implements android.os.Parcelable {
+ public final class SelectRangeGesture extends android.view.inputmethod.PreviewableHandwritingGesture implements android.os.Parcelable {
method public int describeContents();
method public int getGranularity();
method @NonNull public android.graphics.RectF getSelectionEndArea();
diff --git a/core/java/android/inputmethodservice/IRemoteInputConnectionInvoker.java b/core/java/android/inputmethodservice/IRemoteInputConnectionInvoker.java
index 95aecfe4c7af..c6a37259f992 100644
--- a/core/java/android/inputmethodservice/IRemoteInputConnectionInvoker.java
+++ b/core/java/android/inputmethodservice/IRemoteInputConnectionInvoker.java
@@ -24,6 +24,7 @@ import android.annotation.NonNull;
import android.annotation.Nullable;
import android.graphics.RectF;
import android.os.Bundle;
+import android.os.CancellationSignal;
import android.os.RemoteException;
import android.os.ResultReceiver;
import android.view.KeyEvent;
@@ -697,6 +698,27 @@ final class IRemoteInputConnectionInvoker {
}
/**
+ * Invokes one of {@link IRemoteInputConnection#previewHandwritingGesture(
+ * InputConnectionCommandHeader, ParcelableHandwritingGesture, CancellationSignal)}
+ */
+ @AnyThread
+ public boolean previewHandwritingGesture(
+ @NonNull ParcelableHandwritingGesture gesture,
+ @Nullable CancellationSignal cancellationSignal) {
+ if (cancellationSignal != null && cancellationSignal.isCanceled()) {
+ return false; // cancelled.
+ }
+
+ // TODO(b/254727073): Implement CancellationSignal
+ try {
+ mConnection.previewHandwritingGesture(createHeader(), gesture, null);
+ return true;
+ } catch (RemoteException e) {
+ return false;
+ }
+ }
+
+ /**
* Invokes {@link IRemoteInputConnection#requestCursorUpdates(InputConnectionCommandHeader, int,
* int, AndroidFuture)}.
*
diff --git a/core/java/android/inputmethodservice/RemoteInputConnection.java b/core/java/android/inputmethodservice/RemoteInputConnection.java
index 976e71f2b85a..f93f9abc3bb0 100644
--- a/core/java/android/inputmethodservice/RemoteInputConnection.java
+++ b/core/java/android/inputmethodservice/RemoteInputConnection.java
@@ -23,6 +23,7 @@ import android.annotation.NonNull;
import android.annotation.Nullable;
import android.graphics.RectF;
import android.os.Bundle;
+import android.os.CancellationSignal;
import android.os.Handler;
import android.util.Log;
import android.view.KeyEvent;
@@ -34,6 +35,7 @@ import android.view.inputmethod.HandwritingGesture;
import android.view.inputmethod.InputConnection;
import android.view.inputmethod.InputContentInfo;
import android.view.inputmethod.ParcelableHandwritingGesture;
+import android.view.inputmethod.PreviewableHandwritingGesture;
import android.view.inputmethod.SurroundingText;
import android.view.inputmethod.TextAttribute;
import android.view.inputmethod.TextBoundsInfoResult;
@@ -427,6 +429,14 @@ final class RemoteInputConnection implements InputConnection {
}
@AnyThread
+ public boolean previewHandwritingGesture(
+ @NonNull PreviewableHandwritingGesture gesture,
+ @Nullable CancellationSignal cancellationSignal) {
+ return mInvoker.previewHandwritingGesture(ParcelableHandwritingGesture.of(gesture),
+ cancellationSignal);
+ }
+
+ @AnyThread
public boolean requestCursorUpdates(int cursorUpdateMode) {
if (mCancellationGroup.isCanceled()) {
return false;
diff --git a/core/java/android/view/inputmethod/DeleteGesture.java b/core/java/android/view/inputmethod/DeleteGesture.java
index c88158f3b39a..b8435945a6c0 100644
--- a/core/java/android/view/inputmethod/DeleteGesture.java
+++ b/core/java/android/view/inputmethod/DeleteGesture.java
@@ -33,7 +33,7 @@ import java.util.Objects;
* <p>Note: This deletes all text <em>within</em> the given area. To delete a range <em>between</em>
* two areas, use {@link DeleteRangeGesture}.</p>
*/
-public final class DeleteGesture extends HandwritingGesture implements Parcelable {
+public final class DeleteGesture extends PreviewableHandwritingGesture implements Parcelable {
private @Granularity int mGranularity;
private RectF mArea;
diff --git a/core/java/android/view/inputmethod/DeleteRangeGesture.java b/core/java/android/view/inputmethod/DeleteRangeGesture.java
index 53b42092c181..0bce15e3f18b 100644
--- a/core/java/android/view/inputmethod/DeleteRangeGesture.java
+++ b/core/java/android/view/inputmethod/DeleteRangeGesture.java
@@ -34,7 +34,7 @@ import java.util.Objects;
* <p>Note: this deletes text within a range <em>between</em> two given areas. To delete all text
* <em>within</em> a single area, use {@link DeleteGesture}.</p>
*/
-public final class DeleteRangeGesture extends HandwritingGesture implements Parcelable {
+public final class DeleteRangeGesture extends PreviewableHandwritingGesture implements Parcelable {
private @Granularity int mGranularity;
private RectF mStartArea;
diff --git a/core/java/android/view/inputmethod/EditorInfo.java b/core/java/android/view/inputmethod/EditorInfo.java
index febdac26a0f2..9ebaa67b11f3 100644
--- a/core/java/android/view/inputmethod/EditorInfo.java
+++ b/core/java/android/view/inputmethod/EditorInfo.java
@@ -55,8 +55,10 @@ import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.util.ArrayList;
import java.util.Arrays;
+import java.util.HashSet;
import java.util.List;
import java.util.Objects;
+import java.util.Set;
/**
* An EditorInfo describes several attributes of a text editing object
@@ -534,6 +536,8 @@ public class EditorInfo implements InputType, Parcelable {
private @HandwritingGesture.GestureTypeFlags int mSupportedHandwritingGestureTypes;
+ private @HandwritingGesture.GestureTypeFlags int mSupportedHandwritingGesturePreviewTypes;
+
/**
* Set the Handwriting gestures supported by the current {@code Editor}.
* For an editor that supports Stylus Handwriting
@@ -541,8 +545,11 @@ public class EditorInfo implements InputType, Parcelable {
* supported gestures.
* <p> If editor doesn't support one of the declared types, IME will not send those Gestures
* to the editor. Instead they will fallback to using normal text input. </p>
+ * <p>Note: A supported gesture may not have preview supported
+ * {@link #getSupportedHandwritingGesturePreviews()}.</p>
* @param gestures List of supported gesture classes including any of {@link SelectGesture},
* {@link InsertGesture}, {@link DeleteGesture}.
+ * @see #setSupportedHandwritingGesturePreviews(Set)
*/
public void setSupportedHandwritingGestures(
@NonNull List<Class<? extends HandwritingGesture>> gestures) {
@@ -584,6 +591,7 @@ public class EditorInfo implements InputType, Parcelable {
* {@link InputMethodManager#startStylusHandwriting}, it also declares supported gestures.
* @return List of supported gesture classes including any of {@link SelectGesture},
* {@link InsertGesture}, {@link DeleteGesture}.
+ * @see #getSupportedHandwritingGesturePreviews()
*/
@NonNull
public List<Class<? extends HandwritingGesture>> getSupportedHandwritingGestures() {
@@ -623,6 +631,86 @@ public class EditorInfo implements InputType, Parcelable {
}
/**
+ * Set the Handwriting gesture previews supported by the current {@code Editor}.
+ * For an editor that supports Stylus Handwriting
+ * {@link InputMethodManager#startStylusHandwriting}, it is also recommended that it declares
+ * supported gesture previews.
+ * <p>Note: A supported gesture {@link EditorInfo#getSupportedHandwritingGestures()} may not
+ * have preview supported {@link EditorInfo#getSupportedHandwritingGesturePreviews()}.</p>
+ * <p> If editor doesn't support one of the declared types, gesture preview will be ignored.</p>
+ * @param gestures Set of supported gesture classes. One of {@link SelectGesture},
+ * {@link SelectRangeGesture}, {@link DeleteGesture}, {@link DeleteRangeGesture}.
+ * @see #setSupportedHandwritingGestures(List)
+ */
+ public void setSupportedHandwritingGesturePreviews(
+ @NonNull Set<Class<? extends PreviewableHandwritingGesture>> gestures) {
+ Objects.requireNonNull(gestures);
+ if (gestures.isEmpty()) {
+ mSupportedHandwritingGesturePreviewTypes = 0;
+ return;
+ }
+
+ int supportedTypes = 0;
+ for (Class<? extends PreviewableHandwritingGesture> gesture : gestures) {
+ Objects.requireNonNull(gesture);
+ if (gesture.equals(SelectGesture.class)) {
+ supportedTypes |= HandwritingGesture.GESTURE_TYPE_SELECT;
+ } else if (gesture.equals(SelectRangeGesture.class)) {
+ supportedTypes |= HandwritingGesture.GESTURE_TYPE_SELECT_RANGE;
+ } else if (gesture.equals(DeleteGesture.class)) {
+ supportedTypes |= HandwritingGesture.GESTURE_TYPE_DELETE;
+ } else if (gesture.equals(DeleteRangeGesture.class)) {
+ supportedTypes |= HandwritingGesture.GESTURE_TYPE_DELETE_RANGE;
+ } else {
+ throw new IllegalArgumentException(
+ "Unsupported gesture type for preview: " + gesture);
+ }
+ }
+
+ mSupportedHandwritingGesturePreviewTypes = supportedTypes;
+ }
+
+ /**
+ * Returns the combination of Stylus handwriting gesture preview types
+ * supported by the current {@code Editor}.
+ * For an editor that supports Stylus Handwriting.
+ * {@link InputMethodManager#startStylusHandwriting}, it also declares supported gesture
+ * previews.
+ * <p>Note: A supported gesture {@link EditorInfo#getSupportedHandwritingGestures()} may not
+ * have preview supported {@link EditorInfo#getSupportedHandwritingGesturePreviews()}.</p>
+ * @return Set of supported gesture preview classes. One of {@link SelectGesture},
+ * {@link SelectRangeGesture}, {@link DeleteGesture}, {@link DeleteRangeGesture}.
+ * @see #getSupportedHandwritingGestures()
+ */
+ @NonNull
+ public Set<Class<? extends PreviewableHandwritingGesture>>
+ getSupportedHandwritingGesturePreviews() {
+ Set<Class<? extends PreviewableHandwritingGesture>> set = new HashSet<>();
+ if (mSupportedHandwritingGesturePreviewTypes == 0) {
+ return set;
+ }
+ if ((mSupportedHandwritingGesturePreviewTypes & HandwritingGesture.GESTURE_TYPE_SELECT)
+ == HandwritingGesture.GESTURE_TYPE_SELECT) {
+ set.add(SelectGesture.class);
+ }
+ if ((mSupportedHandwritingGesturePreviewTypes
+ & HandwritingGesture.GESTURE_TYPE_SELECT_RANGE)
+ == HandwritingGesture.GESTURE_TYPE_SELECT_RANGE) {
+ set.add(SelectRangeGesture.class);
+ }
+ if ((mSupportedHandwritingGesturePreviewTypes & HandwritingGesture.GESTURE_TYPE_DELETE)
+ == HandwritingGesture.GESTURE_TYPE_DELETE) {
+ set.add(DeleteGesture.class);
+ }
+ if ((mSupportedHandwritingGesturePreviewTypes
+ & HandwritingGesture.GESTURE_TYPE_DELETE_RANGE)
+ == HandwritingGesture.GESTURE_TYPE_DELETE_RANGE) {
+ set.add(DeleteRangeGesture.class);
+ }
+ return set;
+ }
+
+ /**
* If not {@code null}, this editor needs to talk to IMEs that run for the specified user, no
* matter what user ID the calling process has.
*
@@ -1114,6 +1202,9 @@ public class EditorInfo implements InputType, Parcelable {
pw.println(prefix + "supportedHandwritingGestureTypes="
+ InputMethodDebug.handwritingGestureTypeFlagsToString(
mSupportedHandwritingGestureTypes));
+ pw.println(prefix + "supportedHandwritingGesturePreviewTypes="
+ + InputMethodDebug.handwritingGestureTypeFlagsToString(
+ mSupportedHandwritingGesturePreviewTypes));
pw.println(prefix + "contentMimeTypes=" + Arrays.toString(contentMimeTypes));
if (targetInputMethodUser != null) {
pw.println(prefix + "targetInputMethodUserId=" + targetInputMethodUser.getIdentifier());
@@ -1149,6 +1240,8 @@ public class EditorInfo implements InputType, Parcelable {
newEditorInfo.contentMimeTypes = ArrayUtils.cloneOrNull(contentMimeTypes);
newEditorInfo.targetInputMethodUser = targetInputMethodUser;
newEditorInfo.mSupportedHandwritingGestureTypes = mSupportedHandwritingGestureTypes;
+ newEditorInfo.mSupportedHandwritingGesturePreviewTypes =
+ mSupportedHandwritingGesturePreviewTypes;
return newEditorInfo;
}
@@ -1177,6 +1270,7 @@ public class EditorInfo implements InputType, Parcelable {
dest.writeString(fieldName);
dest.writeBundle(extras);
dest.writeInt(mSupportedHandwritingGestureTypes);
+ dest.writeInt(mSupportedHandwritingGesturePreviewTypes);
dest.writeBoolean(mInitialSurroundingText != null);
if (mInitialSurroundingText != null) {
mInitialSurroundingText.writeToParcel(dest, flags);
@@ -1215,6 +1309,7 @@ public class EditorInfo implements InputType, Parcelable {
res.fieldName = source.readString();
res.extras = source.readBundle();
res.mSupportedHandwritingGestureTypes = source.readInt();
+ res.mSupportedHandwritingGesturePreviewTypes = source.readInt();
boolean hasInitialSurroundingText = source.readBoolean();
if (hasInitialSurroundingText) {
res.mInitialSurroundingText =
@@ -1258,6 +1353,8 @@ public class EditorInfo implements InputType, Parcelable {
&& initialCapsMode == that.initialCapsMode
&& fieldId == that.fieldId
&& mSupportedHandwritingGestureTypes == that.mSupportedHandwritingGestureTypes
+ && mSupportedHandwritingGesturePreviewTypes
+ == that.mSupportedHandwritingGesturePreviewTypes
&& Objects.equals(autofillId, that.autofillId)
&& Objects.equals(privateImeOptions, that.privateImeOptions)
&& Objects.equals(packageName, that.packageName)
diff --git a/core/java/android/view/inputmethod/InputConnection.java b/core/java/android/view/inputmethod/InputConnection.java
index 2099a31c3e11..c94a3721df3c 100644
--- a/core/java/android/view/inputmethod/InputConnection.java
+++ b/core/java/android/view/inputmethod/InputConnection.java
@@ -26,6 +26,7 @@ import android.annotation.Nullable;
import android.graphics.RectF;
import android.inputmethodservice.InputMethodService;
import android.os.Bundle;
+import android.os.CancellationSignal;
import android.os.Handler;
import android.text.TextUtils;
import android.view.KeyCharacterMap;
@@ -1031,6 +1032,8 @@ public interface InputConnection {
/**
* Perform a handwriting gesture on text.
*
+ * <p>Note: A supported gesture {@link EditorInfo#getSupportedHandwritingGestures()} may not
+ * have preview supported {@link EditorInfo#getSupportedHandwritingGesturePreviews()}.</p>
* @param gesture the gesture to perform
* @param executor The executor to run the callback on.
* @param consumer if the caller passes a non-null consumer, the editor must invoke this
@@ -1041,6 +1044,7 @@ public interface InputConnection {
* completed. Will be invoked on the given {@link Executor}.
* Default implementation provides a callback to {@link IntConsumer} with
* {@link #HANDWRITING_GESTURE_RESULT_UNSUPPORTED}.
+ * @see #previewHandwritingGesture(PreviewableHandwritingGesture, CancellationSignal)
*/
default void performHandwritingGesture(
@NonNull HandwritingGesture gesture, @Nullable @CallbackExecutor Executor executor,
@@ -1051,6 +1055,31 @@ public interface InputConnection {
}
/**
+ * Preview a handwriting gesture on text.
+ * Provides a real-time preview for a gesture to user for an ongoing gesture. e.g. as user
+ * begins to draw a circle around text, resulting selection {@link SelectGesture} is previewed
+ * while stylus is moving over applicable text.
+ *
+ * <p>Note: A supported gesture {@link EditorInfo#getSupportedHandwritingGestures()} might not
+ * have preview supported {@link EditorInfo#getSupportedHandwritingGesturePreviews()}.</p>
+ * @param gesture the gesture to preview. Preview support for a gesture (regardless of whether
+ * implemented by editor) can be determined if gesture subclasses
+ * {@link PreviewableHandwritingGesture}. Supported previewable gestures include
+ * {@link SelectGesture}, {@link SelectRangeGesture}, {@link DeleteGesture} and
+ * {@link DeleteRangeGesture}.
+ * @param cancellationSignal signal to cancel an ongoing preview.
+ * @return true on successfully sending command to Editor, false if not implemented by editor or
+ * the input connection is no longer valid or preview was cancelled with
+ * {@link CancellationSignal}.
+ * @see #performHandwritingGesture(HandwritingGesture, Executor, IntConsumer)
+ */
+ default boolean previewHandwritingGesture(
+ @NonNull PreviewableHandwritingGesture gesture,
+ @Nullable CancellationSignal cancellationSignal) {
+ return false;
+ }
+
+ /**
* The editor is requested to call
* {@link InputMethodManager#updateCursorAnchorInfo(android.view.View, CursorAnchorInfo)} at
* once, as soon as possible, regardless of cursor/anchor position changes. This flag can be
diff --git a/core/java/android/view/inputmethod/InputConnectionWrapper.java b/core/java/android/view/inputmethod/InputConnectionWrapper.java
index 7af96b60938c..4befd6f026ca 100644
--- a/core/java/android/view/inputmethod/InputConnectionWrapper.java
+++ b/core/java/android/view/inputmethod/InputConnectionWrapper.java
@@ -22,6 +22,7 @@ import android.annotation.NonNull;
import android.annotation.Nullable;
import android.graphics.RectF;
import android.os.Bundle;
+import android.os.CancellationSignal;
import android.os.Handler;
import android.view.KeyEvent;
@@ -340,6 +341,17 @@ public class InputConnectionWrapper implements InputConnection {
* @throws NullPointerException if the target is {@code null}.
*/
@Override
+ public boolean previewHandwritingGesture(
+ @NonNull PreviewableHandwritingGesture gesture,
+ @Nullable CancellationSignal cancellationSignal) {
+ return mTarget.previewHandwritingGesture(gesture, cancellationSignal);
+ }
+
+ /**
+ * {@inheritDoc}
+ * @throws NullPointerException if the target is {@code null}.
+ */
+ @Override
public boolean requestCursorUpdates(int cursorUpdateMode) {
return mTarget.requestCursorUpdates(cursorUpdateMode);
}
diff --git a/core/java/android/view/inputmethod/PreviewableHandwritingGesture.java b/core/java/android/view/inputmethod/PreviewableHandwritingGesture.java
new file mode 100644
index 000000000000..7683ece69412
--- /dev/null
+++ b/core/java/android/view/inputmethod/PreviewableHandwritingGesture.java
@@ -0,0 +1,38 @@
+/*
+ * Copyright (C) 2022 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.inputmethod;
+
+import android.os.CancellationSignal;
+
+import java.util.Set;
+
+/**
+ * A {@link HandwritingGesture} that can be
+ * {@link InputConnection#previewHandwritingGesture(
+ * PreviewableHandwritingGesture, CancellationSignal) previewed}.
+ *
+ * Note: An editor might only implement a subset of gesture previews and declares the supported
+ * ones via {@link EditorInfo#getSupportedHandwritingGesturePreviews}.
+ *
+ * @see EditorInfo#setSupportedHandwritingGesturePreviews(Set)
+ * @see EditorInfo#getSupportedHandwritingGesturePreviews()
+ */
+public abstract class PreviewableHandwritingGesture extends HandwritingGesture {
+ PreviewableHandwritingGesture() {
+ // intentionally empty.
+ }
+}
diff --git a/core/java/android/view/inputmethod/RemoteInputConnectionImpl.java b/core/java/android/view/inputmethod/RemoteInputConnectionImpl.java
index c01c310dda1b..7525d723b802 100644
--- a/core/java/android/view/inputmethod/RemoteInputConnectionImpl.java
+++ b/core/java/android/view/inputmethod/RemoteInputConnectionImpl.java
@@ -30,7 +30,9 @@ import android.annotation.NonNull;
import android.annotation.Nullable;
import android.graphics.RectF;
import android.os.Bundle;
+import android.os.CancellationSignal;
import android.os.Handler;
+import android.os.ICancellationSignal;
import android.os.Looper;
import android.os.ResultReceiver;
import android.os.Trace;
@@ -1017,6 +1019,34 @@ final class RemoteInputConnectionImpl extends IRemoteInputConnection.Stub {
@Dispatching(cancellable = true)
@Override
+ public void previewHandwritingGesture(
+ InputConnectionCommandHeader header, ParcelableHandwritingGesture gestureContainer,
+ ICancellationSignal transport) {
+
+ // TODO(b/254727073): Implement CancellationSignal receiver
+ final CancellationSignal cancellationSignal = CancellationSignal.fromTransport(transport);
+ // Previews always use PreviewableHandwritingGesture but if incorrectly wrong class is
+ // passed, ClassCastException will be sent back to caller.
+ final PreviewableHandwritingGesture gesture =
+ (PreviewableHandwritingGesture) gestureContainer.get();
+
+ dispatchWithTracing("previewHandwritingGesture", () -> {
+ if (header.mSessionId != mCurrentSessionId.get()
+ || (cancellationSignal != null && cancellationSignal.isCanceled())) {
+ return; // cancelled
+ }
+ InputConnection ic = getInputConnection();
+ if (ic == null || !isActive()) {
+ Log.w(TAG, "previewHandwritingGesture on inactive InputConnection");
+ return; // cancelled
+ }
+
+ ic.previewHandwritingGesture(gesture, cancellationSignal);
+ });
+ }
+
+ @Dispatching(cancellable = true)
+ @Override
public void requestCursorUpdates(InputConnectionCommandHeader header, int cursorUpdateMode,
int imeDisplayId, AndroidFuture future /* T=Boolean */) {
dispatchWithTracing("requestCursorUpdates", future, () -> {
diff --git a/core/java/android/view/inputmethod/SelectGesture.java b/core/java/android/view/inputmethod/SelectGesture.java
index 6dc4ed295669..ba600df970bf 100644
--- a/core/java/android/view/inputmethod/SelectGesture.java
+++ b/core/java/android/view/inputmethod/SelectGesture.java
@@ -33,7 +33,7 @@ import java.util.Objects;
* <p>Note: This selects all text <em>within</em> the given area. To select a range <em>between</em>
* two areas, use {@link SelectRangeGesture}.</p>
*/
-public final class SelectGesture extends HandwritingGesture implements Parcelable {
+public final class SelectGesture extends PreviewableHandwritingGesture implements Parcelable {
private @Granularity int mGranularity;
private RectF mArea;
@@ -72,7 +72,6 @@ public final class SelectGesture extends HandwritingGesture implements Parcelabl
return mArea;
}
-
/**
* Builder for {@link SelectGesture}. This class is not designed to be thread-safe.
*/
diff --git a/core/java/android/view/inputmethod/SelectRangeGesture.java b/core/java/android/view/inputmethod/SelectRangeGesture.java
index 7cb60023bd31..c31bc275056e 100644
--- a/core/java/android/view/inputmethod/SelectRangeGesture.java
+++ b/core/java/android/view/inputmethod/SelectRangeGesture.java
@@ -34,7 +34,7 @@ import java.util.Objects;
* <p>Note: this selects text within a range <em>between</em> two given areas. To select all text
* <em>within</em> a single area, use {@link SelectGesture}</p>
*/
-public final class SelectRangeGesture extends HandwritingGesture implements Parcelable {
+public final class SelectRangeGesture extends PreviewableHandwritingGesture implements Parcelable {
private @Granularity int mGranularity;
private RectF mStartArea;
@@ -87,7 +87,6 @@ public final class SelectRangeGesture extends HandwritingGesture implements Parc
return mEndArea;
}
-
/**
* Builder for {@link SelectRangeGesture}. This class is not designed to be thread-safe.
*/
diff --git a/core/java/com/android/internal/inputmethod/IRemoteInputConnection.aidl b/core/java/com/android/internal/inputmethod/IRemoteInputConnection.aidl
index 81e060d8c648..65016c27575e 100644
--- a/core/java/com/android/internal/inputmethod/IRemoteInputConnection.aidl
+++ b/core/java/com/android/internal/inputmethod/IRemoteInputConnection.aidl
@@ -18,6 +18,7 @@ package com.android.internal.inputmethod;
import android.graphics.RectF;
import android.os.Bundle;
+import android.os.ICancellationSignal;
import android.os.ResultReceiver;
import android.view.KeyEvent;
import android.view.inputmethod.CompletionInfo;
@@ -92,6 +93,9 @@ import com.android.internal.inputmethod.InputConnectionCommandHeader;
void performHandwritingGesture(in InputConnectionCommandHeader header,
in ParcelableHandwritingGesture gesture, in ResultReceiver resultReceiver);
+ void previewHandwritingGesture(in InputConnectionCommandHeader header,
+ in ParcelableHandwritingGesture gesture, in ICancellationSignal transport);
+
void setComposingRegion(in InputConnectionCommandHeader header, int start, int end);
void setComposingRegionWithTextAttribute(in InputConnectionCommandHeader header, int start,
diff --git a/core/tests/coretests/src/android/view/inputmethod/EditorInfoTest.java b/core/tests/coretests/src/android/view/inputmethod/EditorInfoTest.java
index 599cf97cc89c..6e73b9fa35d5 100644
--- a/core/tests/coretests/src/android/view/inputmethod/EditorInfoTest.java
+++ b/core/tests/coretests/src/android/view/inputmethod/EditorInfoTest.java
@@ -52,6 +52,8 @@ import org.junit.Test;
import org.junit.runner.RunWith;
import java.util.Arrays;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
/**
* Supplemental tests that cannot be covered by CTS (e.g. due to hidden API dependencies).
@@ -500,6 +502,7 @@ public class EditorInfoTest {
+ "prefix: extras=null\n"
+ "prefix: hintLocales=null\n"
+ "prefix: supportedHandwritingGestureTypes=(none)\n"
+ + "prefix: supportedHandwritingGesturePreviewTypes=(none)\n"
+ "prefix: contentMimeTypes=null\n");
}
@@ -516,6 +519,8 @@ public class EditorInfoTest {
info.label = "testLabel";
info.setInitialToolType(MotionEvent.TOOL_TYPE_STYLUS);
info.setSupportedHandwritingGestures(Arrays.asList(SelectGesture.class));
+ info.setSupportedHandwritingGesturePreviews(
+ Stream.of(SelectGesture.class).collect(Collectors.toSet()));
info.packageName = "android.view.inputmethod";
info.autofillId = new AutofillId(123);
info.fieldId = 456;
@@ -538,6 +543,7 @@ public class EditorInfoTest {
+ "prefix2: extras=Bundle[{testKey=testValue}]\n"
+ "prefix2: hintLocales=[en,es,zh]\n"
+ "prefix2: supportedHandwritingGestureTypes=SELECT\n"
+ + "prefix2: supportedHandwritingGesturePreviewTypes=SELECT\n"
+ "prefix2: contentMimeTypes=[image/png]\n"
+ "prefix2: targetInputMethodUserId=10\n");
}
@@ -558,6 +564,7 @@ public class EditorInfoTest {
+ "prefix: packageName=null autofillId=null fieldId=0 fieldName=null\n"
+ "prefix: hintLocales=null\n"
+ "prefix: supportedHandwritingGestureTypes=(none)\n"
+ + "prefix: supportedHandwritingGesturePreviewTypes=(none)\n"
+ "prefix: contentMimeTypes=null\n");
}