diff --git a/core/api/current.txt b/core/api/current.txt index 4d25ad7d8b37..23e0b1d65dfe 100644 --- a/core/api/current.txt +++ b/core/api/current.txt @@ -53061,6 +53061,24 @@ 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 { + method public int describeContents(); + method @NonNull public android.graphics.RectF getDeletionEndArea(); + method @NonNull public android.graphics.RectF getDeletionStartArea(); + method public int getGranularity(); + method public void writeToParcel(@NonNull android.os.Parcel, int); + field @NonNull public static final android.os.Parcelable.Creator CREATOR; + } + + public static final class DeleteRangeGesture.Builder { + ctor public DeleteRangeGesture.Builder(); + method @NonNull public android.view.inputmethod.DeleteRangeGesture build(); + method @NonNull public android.view.inputmethod.DeleteRangeGesture.Builder setDeletionEndArea(@NonNull android.graphics.RectF); + method @NonNull public android.view.inputmethod.DeleteRangeGesture.Builder setDeletionStartArea(@NonNull android.graphics.RectF); + method @NonNull public android.view.inputmethod.DeleteRangeGesture.Builder setFallbackText(@Nullable String); + method @NonNull public android.view.inputmethod.DeleteRangeGesture.Builder setGranularity(int); + } + public final class EditorBoundsInfo implements android.os.Parcelable { method public int describeContents(); method @Nullable public android.graphics.RectF getEditorBounds(); @@ -53160,11 +53178,13 @@ package android.view.inputmethod { public abstract class HandwritingGesture { method @Nullable public String getFallbackText(); 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_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 field public static final int GESTURE_TYPE_SELECT = 1; // 0x1 + field public static final int GESTURE_TYPE_SELECT_RANGE = 32; // 0x20 field public static final int GRANULARITY_CHARACTER = 2; // 0x2 field public static final int GRANULARITY_WORD = 1; // 0x1 } @@ -53549,6 +53569,24 @@ 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 { + method public int describeContents(); + method public int getGranularity(); + method @NonNull public android.graphics.RectF getSelectionEndArea(); + method @NonNull public android.graphics.RectF getSelectionStartArea(); + method public void writeToParcel(@NonNull android.os.Parcel, int); + field @NonNull public static final android.os.Parcelable.Creator CREATOR; + } + + public static final class SelectRangeGesture.Builder { + ctor public SelectRangeGesture.Builder(); + method @NonNull public android.view.inputmethod.SelectRangeGesture build(); + method @NonNull public android.view.inputmethod.SelectRangeGesture.Builder setFallbackText(@Nullable String); + method @NonNull public android.view.inputmethod.SelectRangeGesture.Builder setGranularity(int); + method @NonNull public android.view.inputmethod.SelectRangeGesture.Builder setSelectionEndArea(@NonNull android.graphics.RectF); + method @NonNull public android.view.inputmethod.SelectRangeGesture.Builder setSelectionStartArea(@NonNull android.graphics.RectF); + } + public final class SurroundingText implements android.os.Parcelable { ctor public SurroundingText(@NonNull CharSequence, @IntRange(from=0) int, @IntRange(from=0) int, @IntRange(from=0xffffffff) int); method public int describeContents(); diff --git a/core/java/android/inputmethodservice/IRemoteInputConnectionInvoker.java b/core/java/android/inputmethodservice/IRemoteInputConnectionInvoker.java index 5ce38e9a279a..6f758de63c95 100644 --- a/core/java/android/inputmethodservice/IRemoteInputConnectionInvoker.java +++ b/core/java/android/inputmethodservice/IRemoteInputConnectionInvoker.java @@ -27,6 +27,7 @@ import android.view.KeyEvent; import android.view.inputmethod.CompletionInfo; import android.view.inputmethod.CorrectionInfo; import android.view.inputmethod.DeleteGesture; +import android.view.inputmethod.DeleteRangeGesture; import android.view.inputmethod.ExtractedText; import android.view.inputmethod.ExtractedTextRequest; import android.view.inputmethod.HandwritingGesture; @@ -36,6 +37,7 @@ import android.view.inputmethod.InsertGesture; import android.view.inputmethod.JoinOrSplitGesture; import android.view.inputmethod.RemoveSpaceGesture; import android.view.inputmethod.SelectGesture; +import android.view.inputmethod.SelectRangeGesture; import android.view.inputmethod.SurroundingText; import android.view.inputmethod.TextAttribute; @@ -636,7 +638,9 @@ final class IRemoteInputConnectionInvoker { /** * Invokes one of {@link IRemoteInputConnection#performHandwritingSelectGesture}, + * {@link IRemoteInputConnection#performHandwritingSelectRangeGesture}, * {@link IRemoteInputConnection#performHandwritingDeleteGesture}, + * {@link IRemoteInputConnection#performHandwritingDeleteRangeGesture}, * {@link IRemoteInputConnection#performHandwritingInsertGesture}, * {@link IRemoteInputConnection#performHandwritingRemoveSpaceGesture}, * {@link IRemoteInputConnection#performHandwritingJoinOrSplitGesture}. @@ -655,12 +659,18 @@ final class IRemoteInputConnectionInvoker { if (gesture instanceof SelectGesture) { mConnection.performHandwritingSelectGesture( createHeader(), (SelectGesture) gesture, resultReceiver); + } else if (gesture instanceof SelectRangeGesture) { + mConnection.performHandwritingSelectRangeGesture( + createHeader(), (SelectRangeGesture) gesture, resultReceiver); } else if (gesture instanceof InsertGesture) { mConnection.performHandwritingInsertGesture( createHeader(), (InsertGesture) gesture, resultReceiver); } else if (gesture instanceof DeleteGesture) { mConnection.performHandwritingDeleteGesture( createHeader(), (DeleteGesture) gesture, resultReceiver); + } else if (gesture instanceof DeleteRangeGesture) { + mConnection.performHandwritingDeleteRangeGesture( + createHeader(), (DeleteRangeGesture) gesture, resultReceiver); } else if (gesture instanceof RemoveSpaceGesture) { mConnection.performHandwritingRemoveSpaceGesture( createHeader(), (RemoveSpaceGesture) gesture, resultReceiver); diff --git a/core/java/android/view/inputmethod/DeleteGesture.java b/core/java/android/view/inputmethod/DeleteGesture.java index 9fadabfee512..c88158f3b39a 100644 --- a/core/java/android/view/inputmethod/DeleteGesture.java +++ b/core/java/android/view/inputmethod/DeleteGesture.java @@ -27,9 +27,11 @@ import android.widget.TextView; import java.util.Objects; /** - * A sub-class of {@link HandwritingGesture} for deleting an area of text. + * A sub-class of {@link HandwritingGesture} for deleting an area of text using single rectangle. * This class holds the information required for deletion of text in * toolkit widgets like {@link TextView}. + *

Note: This deletes all text within the given area. To delete a range between + * two areas, use {@link DeleteRangeGesture}.

*/ public final class DeleteGesture extends HandwritingGesture implements Parcelable { diff --git a/core/java/android/view/inputmethod/DeleteRangeGesture.aidl b/core/java/android/view/inputmethod/DeleteRangeGesture.aidl new file mode 100644 index 000000000000..0ae889974fc7 --- /dev/null +++ b/core/java/android/view/inputmethod/DeleteRangeGesture.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 DeleteRangeGesture; \ No newline at end of file diff --git a/core/java/android/view/inputmethod/DeleteRangeGesture.java b/core/java/android/view/inputmethod/DeleteRangeGesture.java new file mode 100644 index 000000000000..53b42092c181 --- /dev/null +++ b/core/java/android/view/inputmethod/DeleteRangeGesture.java @@ -0,0 +1,243 @@ +/* + * 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.Nullable; +import android.annotation.SuppressLint; +import android.graphics.RectF; +import android.os.Parcel; +import android.os.Parcelable; +import android.widget.TextView; + +import java.util.Objects; + +/** + * A subclass of {@link HandwritingGesture} for deleting a range of text by defining start and end + * rectangles. This can be useful when the range cannot be defined with a single rectangle. + * This class holds the information required for deletion of text in + * toolkit widgets like {@link TextView}. + *

Note: this deletes text within a range between two given areas. To delete all text + * within a single area, use {@link DeleteGesture}.

+ */ +public final class DeleteRangeGesture extends HandwritingGesture implements Parcelable { + + private @Granularity int mGranularity; + private RectF mStartArea; + private RectF mEndArea; + + private DeleteRangeGesture( + int granularity, RectF startArea, RectF endArea, String fallbackText) { + mType = GESTURE_TYPE_DELETE_RANGE; + mStartArea = startArea; + mEndArea = endArea; + mGranularity = granularity; + mFallbackText = fallbackText; + } + + private DeleteRangeGesture(@NonNull Parcel source) { + mType = GESTURE_TYPE_DELETE_RANGE; + mFallbackText = source.readString8(); + mGranularity = source.readInt(); + mStartArea = source.readTypedObject(RectF.CREATOR); + mEndArea = source.readTypedObject(RectF.CREATOR); + } + + /** + * Returns Granular level on which text should be operated. + * @see #GRANULARITY_CHARACTER + * @see #GRANULARITY_WORD + */ + @Granularity + public int getGranularity() { + return mGranularity; + } + + /** + * Returns the Deletion start area {@link RectF} in screen coordinates. + * + * Getter for deletion area set with {@link Builder#setDeletionStartArea(RectF)}. + */ + @NonNull + public RectF getDeletionStartArea() { + return mStartArea; + } + + /** + * Returns the Deletion end area {@link RectF} in screen coordinates. + * + * Getter for deletion area set with {@link Builder#setDeletionEndArea(RectF)}. + */ + @NonNull + public RectF getDeletionEndArea() { + return mEndArea; + } + + /** + * Builder for {@link DeleteRangeGesture}. This class is not designed to be thread-safe. + */ + public static final class Builder { + private int mGranularity; + private RectF mStartArea; + private RectF mEndArea; + private String mFallbackText; + + /** + * Define text deletion granularity. Intersecting words/characters will be + * included in the operation. + * @param granularity {@link HandwritingGesture#GRANULARITY_WORD} or + * {@link HandwritingGesture#GRANULARITY_CHARACTER}. + * @return {@link Builder}. + */ + @NonNull + @SuppressLint("MissingGetterMatchingBuilder") + public Builder setGranularity(@Granularity int granularity) { + mGranularity = granularity; + return this; + } + + /** + * Set rectangular single/multiline start of text deletion area intersecting with text. + * + * The resulting deletion is performed from the start of first word/character in the start + * rectangle to the end of the last word/character in the end rectangle + * {@link #setDeletionEndArea(RectF)}. + *
+ * Deletion strategy using two rectangles + *
+ * + * Intersection is determined using + * {@link #setGranularity(int)}. e.g. {@link HandwritingGesture#GRANULARITY_WORD} includes + * all the words with their width/height center included in the deletion rectangle. + * @param startArea {@link RectF} (in screen coordinates) for start of deletion. This + * rectangle belongs to first line where deletion should start. + */ + @NonNull + @SuppressLint("MissingGetterMatchingBuilder") + public Builder setDeletionStartArea(@NonNull RectF startArea) { + mStartArea = startArea; + return this; + } + + /** + * Set rectangular single/multiline end of text deletion area intersecting with text. + * + * The resulting deletion is performed from the start of first word/character in the start + * rectangle {@link #setDeletionStartArea(RectF)} to the end of the last word/character in + * the end rectangle. + *
+ * Deletion strategy using two rectangles + * + * Intersection is determined using + * {@link #setGranularity(int)}. e.g. {@link HandwritingGesture#GRANULARITY_WORD} includes + * all the words with their width/height center included in the deletion rectangle. + * @param endArea {@link RectF} (in screen coordinates) for start of deletion. This + * rectangle belongs to the last line where deletion should end. + */ + @NonNull + @SuppressLint("MissingGetterMatchingBuilder") + public Builder setDeletionEndArea(@NonNull RectF endArea) { + mEndArea = endArea; + 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; + } + + /** + * @return {@link DeleteRangeGesture} using parameters in this + * {@link DeleteRangeGesture.Builder}. + * @throws IllegalArgumentException if one or more positional parameters are not specified. + */ + @NonNull + public DeleteRangeGesture build() { + if (mStartArea == null || mStartArea.isEmpty() || mEndArea == null + || mEndArea.isEmpty()) { + throw new IllegalArgumentException("Deletion area must be set."); + } + if (mGranularity <= GRANULARITY_UNDEFINED) { + throw new IllegalArgumentException("Deletion granularity must be set."); + } + return new DeleteRangeGesture(mGranularity, mStartArea, mEndArea, mFallbackText); + } + } + + /** + * Used to make this class parcelable. + */ + @NonNull + public static final Creator CREATOR = + new Creator() { + @Override + public DeleteRangeGesture createFromParcel(Parcel source) { + return new DeleteRangeGesture(source); + } + + @Override + public DeleteRangeGesture[] newArray(int size) { + return new DeleteRangeGesture[size]; + } + }; + + @Override + public int hashCode() { + return Objects.hash(mGranularity, mStartArea, mEndArea, mFallbackText); + } + + @Override + public boolean equals(Object o) { + if (!(o instanceof DeleteRangeGesture)) return false; + + DeleteRangeGesture that = (DeleteRangeGesture) o; + + if (mGranularity != that.mGranularity) return false; + if (!Objects.equals(mFallbackText, that.mFallbackText)) return false; + if (!Objects.equals(mStartArea, that.mStartArea)) return false; + return Objects.equals(mEndArea, that.mEndArea); + } + + @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.writeInt(mGranularity); + dest.writeTypedObject(mStartArea, flags); + dest.writeTypedObject(mEndArea, flags); + } +} diff --git a/core/java/android/view/inputmethod/EditorInfo.java b/core/java/android/view/inputmethod/EditorInfo.java index c3b32c9f1d54..4a79ba62de69 100644 --- a/core/java/android/view/inputmethod/EditorInfo.java +++ b/core/java/android/view/inputmethod/EditorInfo.java @@ -557,10 +557,14 @@ public class EditorInfo implements InputType, Parcelable { 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(InsertGesture.class)) { supportedTypes |= HandwritingGesture.GESTURE_TYPE_INSERT; } else if (gesture.equals(DeleteGesture.class)) { supportedTypes |= HandwritingGesture.GESTURE_TYPE_DELETE; + } else if (gesture.equals(DeleteRangeGesture.class)) { + supportedTypes |= HandwritingGesture.GESTURE_TYPE_DELETE_RANGE; } else if (gesture.equals(RemoveSpaceGesture.class)) { supportedTypes |= HandwritingGesture.GESTURE_TYPE_REMOVE_SPACE; } else if (gesture.equals(JoinOrSplitGesture.class)) { diff --git a/core/java/android/view/inputmethod/HandwritingGesture.java b/core/java/android/view/inputmethod/HandwritingGesture.java index 494aaaa32f57..07b1e1f0468e 100644 --- a/core/java/android/view/inputmethod/HandwritingGesture.java +++ b/core/java/android/view/inputmethod/HandwritingGesture.java @@ -25,6 +25,7 @@ import android.view.MotionEvent; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; +import java.util.List; import java.util.concurrent.Executor; import java.util.function.IntConsumer; @@ -105,6 +106,16 @@ public abstract class HandwritingGesture { /** Gesture of type {@link JoinOrSplitGesture} to join or split text. */ public static final int GESTURE_TYPE_JOIN_OR_SPLIT = 1 << 4; + /** + * Gesture of type {@link SelectRangeGesture} to select range of text. + */ + public static final int GESTURE_TYPE_SELECT_RANGE = 1 << 5; + + /** + * Gesture of type {@link DeleteRangeGesture} to delete range of text. + */ + public static final int GESTURE_TYPE_DELETE_RANGE = 1 << 6; + /** * Type of gesture like {@link #GESTURE_TYPE_SELECT}, {@link #GESTURE_TYPE_INSERT}, * or {@link #GESTURE_TYPE_DELETE}. @@ -112,8 +123,10 @@ public abstract class HandwritingGesture { @IntDef(prefix = {"GESTURE_TYPE_"}, value = { GESTURE_TYPE_NONE, GESTURE_TYPE_SELECT, + GESTURE_TYPE_SELECT_RANGE, GESTURE_TYPE_INSERT, GESTURE_TYPE_DELETE, + GESTURE_TYPE_DELETE_RANGE, GESTURE_TYPE_REMOVE_SPACE, GESTURE_TYPE_JOIN_OR_SPLIT}) @Retention(RetentionPolicy.SOURCE) @@ -123,13 +136,15 @@ public abstract class HandwritingGesture { * Flags which can be any combination of {@link #GESTURE_TYPE_SELECT}, * {@link #GESTURE_TYPE_INSERT}, or {@link #GESTURE_TYPE_DELETE}. * {@link GestureTypeFlags} can be used by editors to declare what gestures are supported - * and report them in {@link EditorInfo#setSupportedHandwritingGestureTypes(int)}. + * and report them in {@link EditorInfo#setSupportedHandwritingGestures(List)}. * @hide */ @IntDef(flag = true, prefix = {"GESTURE_TYPE_"}, value = { GESTURE_TYPE_SELECT, + GESTURE_TYPE_SELECT_RANGE, GESTURE_TYPE_INSERT, GESTURE_TYPE_DELETE, + GESTURE_TYPE_DELETE_RANGE, GESTURE_TYPE_REMOVE_SPACE, GESTURE_TYPE_JOIN_OR_SPLIT}) @Retention(RetentionPolicy.SOURCE) @@ -140,7 +155,7 @@ public abstract class HandwritingGesture { /** * Returns the gesture type {@link GestureType}. * {@link GestureType} can be used by editors to declare what gestures are supported and report - * them in {@link EditorInfo#setSupportedHandwritingGestureTypes(int)}. + * them in {@link EditorInfo#setSupportedHandwritingGestures(List)}. * @hide */ @TestApi diff --git a/core/java/android/view/inputmethod/SelectGesture.java b/core/java/android/view/inputmethod/SelectGesture.java index 2f02e66d4295..6dc4ed295669 100644 --- a/core/java/android/view/inputmethod/SelectGesture.java +++ b/core/java/android/view/inputmethod/SelectGesture.java @@ -27,9 +27,11 @@ import android.widget.TextView; import java.util.Objects; /** - * A sub-class of {@link HandwritingGesture} for selecting an area of text. + * A sub-class of {@link HandwritingGesture} for selecting an area of text using single rectangle. * This class holds the information required for selection of text in * toolkit widgets like {@link TextView}. + *

Note: This selects all text within the given area. To select a range between + * two areas, use {@link SelectRangeGesture}.

*/ public final class SelectGesture extends HandwritingGesture implements Parcelable { diff --git a/core/java/android/view/inputmethod/SelectRangeGesture.aidl b/core/java/android/view/inputmethod/SelectRangeGesture.aidl new file mode 100644 index 000000000000..a7bce0adddf0 --- /dev/null +++ b/core/java/android/view/inputmethod/SelectRangeGesture.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 SelectRangeGesture; \ No newline at end of file diff --git a/core/java/android/view/inputmethod/SelectRangeGesture.java b/core/java/android/view/inputmethod/SelectRangeGesture.java new file mode 100644 index 000000000000..7cb60023bd31 --- /dev/null +++ b/core/java/android/view/inputmethod/SelectRangeGesture.java @@ -0,0 +1,249 @@ +/* + * 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.Nullable; +import android.annotation.SuppressLint; +import android.graphics.RectF; +import android.os.Parcel; +import android.os.Parcelable; +import android.widget.TextView; + +import java.util.Objects; + +/** + * A subclass of {@link HandwritingGesture} for selecting a range of text by defining start and end + * rectangles. This can be useful when the range cannot be defined with a single rectangle. + * This class holds the information required for selection of text in + * toolkit widgets like {@link TextView}. + *

Note: this selects text within a range between two given areas. To select all text + * within a single area, use {@link SelectGesture}

+ */ +public final class SelectRangeGesture extends HandwritingGesture implements Parcelable { + + private @Granularity int mGranularity; + private RectF mStartArea; + private RectF mEndArea; + + private SelectRangeGesture( + int granularity, RectF startArea, RectF endArea, String fallbackText) { + mType = GESTURE_TYPE_SELECT_RANGE; + mStartArea = startArea; + mEndArea = endArea; + mGranularity = granularity; + mFallbackText = fallbackText; + } + + private SelectRangeGesture(@NonNull Parcel source) { + mType = GESTURE_TYPE_SELECT_RANGE; + mFallbackText = source.readString8(); + mGranularity = source.readInt(); + mStartArea = source.readTypedObject(RectF.CREATOR); + mEndArea = source.readTypedObject(RectF.CREATOR); + } + + /** + * Returns Granular level on which text should be operated. + * @see #GRANULARITY_CHARACTER + * @see #GRANULARITY_WORD + */ + @Granularity + public int getGranularity() { + return mGranularity; + } + + /** + * Returns the Selection start area {@link RectF} in screen coordinates. + * + * Getter for selection area set with {@link Builder#setSelectionStartArea(RectF)}. + */ + @NonNull + public RectF getSelectionStartArea() { + return mStartArea; + } + + /** + * Returns the Selection end area {@link RectF} in screen coordinates. + * + * Getter for selection area set with {@link Builder#setSelectionEndArea(RectF)}. + */ + @NonNull + public RectF getSelectionEndArea() { + return mEndArea; + } + + + /** + * Builder for {@link SelectRangeGesture}. This class is not designed to be thread-safe. + */ + public static final class Builder { + private int mGranularity; + private RectF mStartArea; + private RectF mEndArea; + private String mFallbackText; + + /** + * Define text selection granularity. Intersecting words/characters will be + * included in the operation. + * @param granularity {@link HandwritingGesture#GRANULARITY_WORD} or + * {@link HandwritingGesture#GRANULARITY_CHARACTER}. + * @return {@link Builder}. + */ + @NonNull + @SuppressLint("MissingGetterMatchingBuilder") + public Builder setGranularity(@HandwritingGesture.Granularity int granularity) { + mGranularity = granularity; + return this; + } + + /** + * Set rectangular single/multiline start of text selection area intersecting with text. + * + * The resulting selection is performed from the start of first word/character in the start + * rectangle to the end of the last word/character in the end rectangle + * {@link #setSelectionEndArea(RectF)}. + *
+ * Selection strategy using two rectangles + *
+ * + * Intersection is determined using + * {@link #setGranularity(int)}. e.g. {@link HandwritingGesture#GRANULARITY_WORD} includes + * all the words with their width/height center included in the selection rectangle. + * @param startArea {@link RectF} (in screen coordinates) for start of selection. This + * rectangle belongs to first line where selection should start. + */ + @NonNull + @SuppressLint("MissingGetterMatchingBuilder") + public Builder setSelectionStartArea(@NonNull RectF startArea) { + mStartArea = startArea; + return this; + } + + /** + * Set rectangular single/multiline end of text selection area intersecting with text. + * + * The resulting selection is performed from the start of first word/character in the start + * rectangle {@link #setSelectionStartArea(RectF)} to the end of the last word/character in + * the end rectangle. + *
+ * Selection strategy using two rectangles + *
+ * + * The selection includes the first word/character in the rectangle, the last + * word/character in the rectangle, and everything in between even if it's not in the + * rectangle. + * + * Intersection is determined using + * {@link #setGranularity(int)}. e.g. {@link HandwritingGesture#GRANULARITY_WORD} includes + * all the words with their width/height center included in the selection rectangle. + * @param endArea {@link RectF} (in screen coordinates) for start of selection. This + * rectangle belongs to the last line where selection should end. + */ + @NonNull + @SuppressLint("MissingGetterMatchingBuilder") + public Builder setSelectionEndArea(@NonNull RectF endArea) { + mEndArea = endArea; + 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; + } + + /** + * @return {@link SelectRangeGesture} using parameters in this + * {@link SelectRangeGesture.Builder}. + * @throws IllegalArgumentException if one or more positional parameters are not specified. + */ + @NonNull + public SelectRangeGesture build() { + if (mStartArea == null || mStartArea.isEmpty() || mEndArea == null + || mEndArea.isEmpty()) { + throw new IllegalArgumentException("Selection area must be set."); + } + if (mGranularity <= GRANULARITY_UNDEFINED) { + throw new IllegalArgumentException("Selection granularity must be set."); + } + return new SelectRangeGesture(mGranularity, mStartArea, mEndArea, mFallbackText); + } + } + + /** + * Used to make this class parcelable. + */ + @NonNull + public static final Parcelable.Creator CREATOR = + new Parcelable.Creator() { + @Override + public SelectRangeGesture createFromParcel(Parcel source) { + return new SelectRangeGesture(source); + } + + @Override + public SelectRangeGesture[] newArray(int size) { + return new SelectRangeGesture[size]; + } + }; + + @Override + public int hashCode() { + return Objects.hash(mGranularity, mStartArea, mEndArea, mFallbackText); + } + + @Override + public boolean equals(Object o) { + if (!(o instanceof SelectRangeGesture)) return false; + + SelectRangeGesture that = (SelectRangeGesture) o; + + if (mGranularity != that.mGranularity) return false; + if (!Objects.equals(mFallbackText, that.mFallbackText)) return false; + if (!Objects.equals(mStartArea, that.mStartArea)) return false; + return Objects.equals(mEndArea, that.mEndArea); + } + + @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.writeInt(mGranularity); + dest.writeTypedObject(mStartArea, flags); + dest.writeTypedObject(mEndArea, flags); + } +} diff --git a/core/java/com/android/internal/inputmethod/IRemoteInputConnection.aidl b/core/java/com/android/internal/inputmethod/IRemoteInputConnection.aidl index f456e85adff4..17f9b7dea045 100644 --- a/core/java/com/android/internal/inputmethod/IRemoteInputConnection.aidl +++ b/core/java/com/android/internal/inputmethod/IRemoteInputConnection.aidl @@ -22,12 +22,14 @@ import android.view.KeyEvent; import android.view.inputmethod.CompletionInfo; import android.view.inputmethod.CorrectionInfo; import android.view.inputmethod.DeleteGesture; +import android.view.inputmethod.DeleteRangeGesture; import android.view.inputmethod.ExtractedTextRequest; import android.view.inputmethod.InputContentInfo; import android.view.inputmethod.InsertGesture; import android.view.inputmethod.JoinOrSplitGesture; import android.view.inputmethod.RemoveSpaceGesture; import android.view.inputmethod.SelectGesture; +import android.view.inputmethod.SelectRangeGesture; import android.view.inputmethod.TextAttribute; import com.android.internal.infra.AndroidFuture; @@ -95,12 +97,18 @@ import com.android.internal.inputmethod.InputConnectionCommandHeader; void performHandwritingSelectGesture(in InputConnectionCommandHeader header, in SelectGesture gesture, in ResultReceiver resultReceiver); + void performHandwritingSelectRangeGesture(in InputConnectionCommandHeader header, + in SelectRangeGesture gesture, in ResultReceiver resultReceiver); + void performHandwritingInsertGesture(in InputConnectionCommandHeader header, in InsertGesture gesture, in ResultReceiver resultReceiver); void performHandwritingDeleteGesture(in InputConnectionCommandHeader header, in DeleteGesture gesture, in ResultReceiver resultReceiver); + void performHandwritingDeleteRangeGesture(in InputConnectionCommandHeader header, + in DeleteRangeGesture gesture, in ResultReceiver resultReceiver); + void performHandwritingRemoveSpaceGesture(in InputConnectionCommandHeader header, in RemoveSpaceGesture gesture, in ResultReceiver resultReceiver); diff --git a/core/java/com/android/internal/inputmethod/InputMethodDebug.java b/core/java/com/android/internal/inputmethod/InputMethodDebug.java index bbf0a764d9e4..7f3144bb5894 100644 --- a/core/java/com/android/internal/inputmethod/InputMethodDebug.java +++ b/core/java/com/android/internal/inputmethod/InputMethodDebug.java @@ -272,19 +272,24 @@ public final class InputMethodDebug { if ((gestureTypeFlags & HandwritingGesture.GESTURE_TYPE_SELECT) != 0) { joiner.add("SELECT"); } + if ((gestureTypeFlags & HandwritingGesture.GESTURE_TYPE_SELECT_RANGE) != 0) { + joiner.add("SELECT_RANGE"); + } if ((gestureTypeFlags & HandwritingGesture.GESTURE_TYPE_INSERT) != 0) { joiner.add("INSERT"); } if ((gestureTypeFlags & HandwritingGesture.GESTURE_TYPE_DELETE) != 0) { joiner.add("DELETE"); } + if ((gestureTypeFlags & HandwritingGesture.GESTURE_TYPE_DELETE_RANGE) != 0) { + joiner.add("DELETE_RANGE"); + } if ((gestureTypeFlags & HandwritingGesture.GESTURE_TYPE_REMOVE_SPACE) != 0) { joiner.add("REMOVE_SPACE"); } if ((gestureTypeFlags & HandwritingGesture.GESTURE_TYPE_JOIN_OR_SPLIT) != 0) { joiner.add("JOIN_OR_SPLIT"); } - return joiner.setEmptyValue("(none)").toString(); } diff --git a/core/java/com/android/internal/inputmethod/RemoteInputConnectionImpl.java b/core/java/com/android/internal/inputmethod/RemoteInputConnectionImpl.java index 6a7028d31edb..713e913abce1 100644 --- a/core/java/com/android/internal/inputmethod/RemoteInputConnectionImpl.java +++ b/core/java/com/android/internal/inputmethod/RemoteInputConnectionImpl.java @@ -41,6 +41,7 @@ import android.view.ViewRootImpl; import android.view.inputmethod.CompletionInfo; import android.view.inputmethod.CorrectionInfo; import android.view.inputmethod.DeleteGesture; +import android.view.inputmethod.DeleteRangeGesture; import android.view.inputmethod.DumpableInputConnection; import android.view.inputmethod.ExtractedTextRequest; import android.view.inputmethod.HandwritingGesture; @@ -51,6 +52,7 @@ import android.view.inputmethod.InsertGesture; import android.view.inputmethod.JoinOrSplitGesture; import android.view.inputmethod.RemoveSpaceGesture; import android.view.inputmethod.SelectGesture; +import android.view.inputmethod.SelectRangeGesture; import android.view.inputmethod.TextAttribute; import android.view.inputmethod.TextSnapshot; @@ -983,6 +985,14 @@ public final class RemoteInputConnectionImpl extends IRemoteInputConnection.Stub performHandwritingGestureInternal(header, gesture, resultReceiver); } + @Dispatching(cancellable = true) + @Override + public void performHandwritingSelectRangeGesture( + InputConnectionCommandHeader header, SelectRangeGesture gesture, + ResultReceiver resultReceiver) { + performHandwritingGestureInternal(header, gesture, resultReceiver); + } + @Dispatching(cancellable = true) @Override public void performHandwritingInsertGesture( @@ -999,6 +1009,14 @@ public final class RemoteInputConnectionImpl extends IRemoteInputConnection.Stub performHandwritingGestureInternal(header, gesture, resultReceiver); } + @Dispatching(cancellable = true) + @Override + public void performHandwritingDeleteRangeGesture( + InputConnectionCommandHeader header, DeleteRangeGesture gesture, + ResultReceiver resultReceiver) { + performHandwritingGestureInternal(header, gesture, resultReceiver); + } + @Dispatching(cancellable = true) @Override public void performHandwritingRemoveSpaceGesture( diff --git a/docs/html/reference/images/input_method/stylus_handwriting/delete_range_gesture_rects.png b/docs/html/reference/images/input_method/stylus_handwriting/delete_range_gesture_rects.png new file mode 100644 index 000000000000..185582017ed6 Binary files /dev/null and b/docs/html/reference/images/input_method/stylus_handwriting/delete_range_gesture_rects.png differ diff --git a/docs/html/reference/images/input_method/stylus_handwriting/select_range_gesture_rects.png b/docs/html/reference/images/input_method/stylus_handwriting/select_range_gesture_rects.png new file mode 100644 index 000000000000..e0801267fe6d Binary files /dev/null and b/docs/html/reference/images/input_method/stylus_handwriting/select_range_gesture_rects.png differ