diff --git a/core/api/current.txt b/core/api/current.txt index 84274cb507674..b807f1759538c 100644 --- a/core/api/current.txt +++ b/core/api/current.txt @@ -54958,6 +54958,22 @@ package android.view.inputmethod { method @NonNull public android.view.inputmethod.InsertGesture.Builder setTextToInsert(@NonNull String); } + public final class InsertModeGesture extends android.view.inputmethod.HandwritingGesture implements android.os.Parcelable { + method public int describeContents(); + method @NonNull public android.os.CancellationSignal getCancellationSignal(); + method @NonNull public android.graphics.PointF getInsertionPoint(); + method public void writeToParcel(@NonNull android.os.Parcel, int); + field @NonNull public static final android.os.Parcelable.Creator CREATOR; + } + + public static final class InsertModeGesture.Builder { + ctor public InsertModeGesture.Builder(); + method @NonNull public android.view.inputmethod.InsertModeGesture build(); + method @NonNull public android.view.inputmethod.InsertModeGesture.Builder setCancellationSignal(@NonNull android.os.CancellationSignal); + method @NonNull public android.view.inputmethod.InsertModeGesture.Builder setFallbackText(@Nullable String); + method @NonNull public android.view.inputmethod.InsertModeGesture.Builder setInsertionPoint(@NonNull android.graphics.PointF); + } + public final class JoinOrSplitGesture extends android.view.inputmethod.HandwritingGesture implements android.os.Parcelable { method public int describeContents(); method @NonNull public android.graphics.PointF getJoinOrSplitPoint(); diff --git a/core/api/test-current.txt b/core/api/test-current.txt index 0b4a70848b7ba..b1de8daa9bed4 100644 --- a/core/api/test-current.txt +++ b/core/api/test-current.txt @@ -3332,6 +3332,7 @@ package android.view.inputmethod { field public static final int GESTURE_TYPE_DELETE = 4; // 0x4 field public static final int GESTURE_TYPE_DELETE_RANGE = 64; // 0x40 field public static final int GESTURE_TYPE_INSERT = 2; // 0x2 + field public static final int GESTURE_TYPE_INSERT_MODE = 128; // 0x80 field public static final int GESTURE_TYPE_JOIN_OR_SPLIT = 16; // 0x10 field public static final int GESTURE_TYPE_NONE = 0; // 0x0 field public static final int GESTURE_TYPE_REMOVE_SPACE = 8; // 0x8 diff --git a/core/java/android/view/inputmethod/EditorInfo.java b/core/java/android/view/inputmethod/EditorInfo.java index 9ebaa67b11f38..4e5cec7b777d5 100644 --- a/core/java/android/view/inputmethod/EditorInfo.java +++ b/core/java/android/view/inputmethod/EditorInfo.java @@ -568,6 +568,8 @@ public class EditorInfo implements InputType, Parcelable { supportedTypes |= HandwritingGesture.GESTURE_TYPE_SELECT_RANGE; } else if (gesture.equals(InsertGesture.class)) { supportedTypes |= HandwritingGesture.GESTURE_TYPE_INSERT; + } else if (gesture.equals(InsertModeGesture.class)) { + supportedTypes |= HandwritingGesture.GESTURE_TYPE_INSERT_MODE; } else if (gesture.equals(DeleteGesture.class)) { supportedTypes |= HandwritingGesture.GESTURE_TYPE_DELETE; } else if (gesture.equals(DeleteRangeGesture.class)) { @@ -611,6 +613,10 @@ public class EditorInfo implements InputType, Parcelable { == HandwritingGesture.GESTURE_TYPE_INSERT) { list.add(InsertGesture.class); } + if ((mSupportedHandwritingGestureTypes & HandwritingGesture.GESTURE_TYPE_INSERT_MODE) + == HandwritingGesture.GESTURE_TYPE_INSERT_MODE) { + list.add(InsertModeGesture.class); + } if ((mSupportedHandwritingGestureTypes & HandwritingGesture.GESTURE_TYPE_DELETE) == HandwritingGesture.GESTURE_TYPE_DELETE) { list.add(DeleteGesture.class); diff --git a/core/java/android/view/inputmethod/HandwritingGesture.java b/core/java/android/view/inputmethod/HandwritingGesture.java index 1f4a7af433039..e7207fa0dfa05 100644 --- a/core/java/android/view/inputmethod/HandwritingGesture.java +++ b/core/java/android/view/inputmethod/HandwritingGesture.java @@ -141,6 +141,13 @@ public abstract class HandwritingGesture { @TestApi public static final int GESTURE_TYPE_DELETE_RANGE = 1 << 6; + /** + * Gesture of type {@link InsertModeGesture} to begin an insert mode at a designated point. + * @hide + */ + @TestApi + public static final int GESTURE_TYPE_INSERT_MODE = 1 << 7; + /** * Type of gesture like {@link #GESTURE_TYPE_SELECT}, {@link #GESTURE_TYPE_INSERT}, * or {@link #GESTURE_TYPE_DELETE}. @@ -150,6 +157,7 @@ public abstract class HandwritingGesture { GESTURE_TYPE_SELECT, GESTURE_TYPE_SELECT_RANGE, GESTURE_TYPE_INSERT, + GESTURE_TYPE_INSERT_MODE, GESTURE_TYPE_DELETE, GESTURE_TYPE_DELETE_RANGE, GESTURE_TYPE_REMOVE_SPACE, @@ -168,6 +176,7 @@ public abstract class HandwritingGesture { GESTURE_TYPE_SELECT, GESTURE_TYPE_SELECT_RANGE, GESTURE_TYPE_INSERT, + GESTURE_TYPE_INSERT_MODE, GESTURE_TYPE_DELETE, GESTURE_TYPE_DELETE_RANGE, GESTURE_TYPE_REMOVE_SPACE, diff --git a/core/java/android/view/inputmethod/InsertModeGesture.aidl b/core/java/android/view/inputmethod/InsertModeGesture.aidl new file mode 100644 index 0000000000000..a0790b88a8120 --- /dev/null +++ b/core/java/android/view/inputmethod/InsertModeGesture.aidl @@ -0,0 +1,19 @@ +/* + * 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; + +parcelable InsertModeGesture; \ No newline at end of file diff --git a/core/java/android/view/inputmethod/InsertModeGesture.java b/core/java/android/view/inputmethod/InsertModeGesture.java new file mode 100644 index 0000000000000..6b9d7fbbc65bc --- /dev/null +++ b/core/java/android/view/inputmethod/InsertModeGesture.java @@ -0,0 +1,187 @@ +/* + * 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.annotation.NonNull; +import android.annotation.SuppressLint; +import android.graphics.PointF; +import android.os.CancellationSignal; +import android.os.Parcel; +import android.os.Parcelable; +import android.widget.TextView; + +import androidx.annotation.Nullable; + +import java.util.Objects; + +/** + * A sub-class of {@link HandwritingGesture} for starting an insert mode which inserts a space in + * the editor to let users hand write freely at the designated insertion point. + * This class holds the information required for insertion of text in + * toolkit widgets like {@link TextView}. + * + * Once InsertMode gesture is started, it continues until IME calls + * {@link CancellationSignal#cancel()} and toolkit can receive cancel using + * {@link CancellationSignal#setOnCancelListener(CancellationSignal.OnCancelListener)} obtained from + * {@link #getCancellationSignal()}. + */ +public final class InsertModeGesture extends HandwritingGesture implements Parcelable { + + private PointF mPoint; + private CancellationSignal mCancellationSignal; + + private InsertModeGesture(PointF point, String fallbackText, + CancellationSignal cancellationSignal) { + mType = GESTURE_TYPE_INSERT_MODE; + mPoint = point; + mFallbackText = fallbackText; + mCancellationSignal = cancellationSignal; + } + + private InsertModeGesture(final Parcel source) { + mType = GESTURE_TYPE_INSERT_MODE; + mFallbackText = source.readString8(); + mPoint = source.readTypedObject(PointF.CREATOR); + } + + /** + * Returns the {@link CancellationSignal} associated with finishing this gesture. + * Once InsertMode gesture is started, it continues until IME calls + * {@link CancellationSignal#cancel()} and toolkit can receive cancel using + * {@link CancellationSignal#setOnCancelListener(CancellationSignal.OnCancelListener)}. + */ + @NonNull + public CancellationSignal getCancellationSignal() { + return mCancellationSignal; + } + + /** + * Returns the insertion point {@link PointF} (in screen coordinates) where space will be + * created for additional text to be inserted. + */ + @NonNull + public PointF getInsertionPoint() { + return mPoint; + } + + /** + * Builder for {@link InsertModeGesture}. This class is not designed to be thread-safe. + */ + public static final class Builder { + private PointF mPoint; + private String mFallbackText; + // TODO(b/254727073): implement CancellationSignal + private CancellationSignal mCancellationSignal; + + /** + * Sets the insertion point (in screen coordinates) where space will be created for + * additional text to be inserted. + */ + @NonNull + @SuppressLint("MissingGetterMatchingBuilder") + public Builder setInsertionPoint(@NonNull PointF point) { + mPoint = point; + return this; + } + + /** + * Sets the {@link CancellationSignal} used to cancel the ongoing gesture. + * @param cancellationSignal signal to cancel an ongoing gesture. + */ + @NonNull + @SuppressLint("MissingGetterMatchingBuilder") + public Builder setCancellationSignal(@NonNull CancellationSignal cancellationSignal) { + mCancellationSignal = cancellationSignal; + return this; + } + + /** + * Set fallback text that will be committed at current cursor position if there is no + * applicable text beneath the area of gesture. + * @param fallbackText text to set + */ + @NonNull + public Builder setFallbackText(@Nullable String fallbackText) { + mFallbackText = fallbackText; + return this; + } + + /** + * Returns {@link InsertModeGesture} using parameters in this + * {@link InsertModeGesture.Builder}. + * @throws IllegalArgumentException if one or more positional parameters are not specified. + */ + @NonNull + public InsertModeGesture build() { + if (mPoint == null) { + throw new IllegalArgumentException("Insertion point must be set."); + } else if (mCancellationSignal == null) { + throw new IllegalArgumentException("CancellationSignal must be set."); + } + return new InsertModeGesture(mPoint, mFallbackText, mCancellationSignal); + } + } + + /** + * Used to make this class parcelable. + */ + @NonNull + public static final Creator CREATOR = new Creator<>() { + @Override + public InsertModeGesture createFromParcel(Parcel source) { + return new InsertModeGesture(source); + } + + @Override + public InsertModeGesture[] newArray(int size) { + return new InsertModeGesture[size]; + } + }; + + @Override + public int hashCode() { + return Objects.hash(mPoint, mFallbackText); + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (!(o instanceof InsertModeGesture)) return false; + + InsertModeGesture that = (InsertModeGesture) o; + + if (!Objects.equals(mFallbackText, that.mFallbackText)) return false; + return Objects.equals(mPoint, that.mPoint); + } + + @Override + public int describeContents() { + return 0; + } + + /** + * Used to package this object into a {@link Parcel}. + * + * @param dest The {@link Parcel} to be written. + * @param flags The flags used for parceling. + */ + @Override + public void writeToParcel(@NonNull Parcel dest, int flags) { + dest.writeString8(mFallbackText); + dest.writeTypedObject(mPoint, flags); + } +} diff --git a/core/java/android/view/inputmethod/ParcelableHandwritingGesture.java b/core/java/android/view/inputmethod/ParcelableHandwritingGesture.java index e4066fcfd61df..ae7df0f91f2e0 100644 --- a/core/java/android/view/inputmethod/ParcelableHandwritingGesture.java +++ b/core/java/android/view/inputmethod/ParcelableHandwritingGesture.java @@ -70,6 +70,8 @@ public final class ParcelableHandwritingGesture implements Parcelable { return SelectRangeGesture.CREATOR.createFromParcel(parcel); case HandwritingGesture.GESTURE_TYPE_INSERT: return InsertGesture.CREATOR.createFromParcel(parcel); + case HandwritingGesture.GESTURE_TYPE_INSERT_MODE: + return InsertModeGesture.CREATOR.createFromParcel(parcel); case HandwritingGesture.GESTURE_TYPE_DELETE: return DeleteGesture.CREATOR.createFromParcel(parcel); case HandwritingGesture.GESTURE_TYPE_DELETE_RANGE: diff --git a/core/tests/coretests/src/android/view/inputmethod/ParcelableHandwritingGestureTest.java b/core/tests/coretests/src/android/view/inputmethod/ParcelableHandwritingGestureTest.java index 79aeaa39bc7c3..90f7d06857c75 100644 --- a/core/tests/coretests/src/android/view/inputmethod/ParcelableHandwritingGestureTest.java +++ b/core/tests/coretests/src/android/view/inputmethod/ParcelableHandwritingGestureTest.java @@ -22,6 +22,7 @@ import static org.junit.Assert.assertThrows; import android.annotation.NonNull; import android.graphics.PointF; import android.graphics.RectF; +import android.os.CancellationSignal; import android.os.Parcel; import android.platform.test.annotations.Presubmit; @@ -85,6 +86,14 @@ public class ParcelableHandwritingGestureTest { .build()); } + @Test + public void testInsertModeGesture() { + verifyEqualityAfterUnparcel(new InsertModeGesture.Builder() + .setInsertionPoint(new PointF(1, 1)).setFallbackText("") + .setCancellationSignal(new CancellationSignal()) + .build()); + } + @Test public void testDeleteGestureGesture() { verifyEqualityAfterUnparcel(new DeleteGesture.Builder()