Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 85a57bc1 authored by Taran Singh's avatar Taran Singh
Browse files

Introduce Scribe Select/DeleteRangeGesture

Add new APIs for select and delete with two rectangles.

Test: atest InputConnectionEndToEndTest
Test: atest StylusHandwritingest
Bug: 239783077

Change-Id: Ia7240d3eaac8610c774c3a4aad074a4b84b71521
parent 07b5ffb2
Loading
Loading
Loading
Loading
+38 −0
Original line number Original line Diff line number Diff line
@@ -53061,6 +53061,24 @@ package android.view.inputmethod {
    method @NonNull public android.view.inputmethod.DeleteGesture.Builder setGranularity(int);
    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<android.view.inputmethod.DeleteRangeGesture> 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 {
  public final class EditorBoundsInfo implements android.os.Parcelable {
    method public int describeContents();
    method public int describeContents();
    method @Nullable public android.graphics.RectF getEditorBounds();
    method @Nullable public android.graphics.RectF getEditorBounds();
@@ -53160,11 +53178,13 @@ package android.view.inputmethod {
  public abstract class HandwritingGesture {
  public abstract class HandwritingGesture {
    method @Nullable public String getFallbackText();
    method @Nullable public String getFallbackText();
    field public static final int GESTURE_TYPE_DELETE = 4; // 0x4
    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 = 2; // 0x2
    field public static final int GESTURE_TYPE_JOIN_OR_SPLIT = 16; // 0x10
    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_NONE = 0; // 0x0
    field public static final int GESTURE_TYPE_REMOVE_SPACE = 8; // 0x8
    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 = 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_CHARACTER = 2; // 0x2
    field public static final int GRANULARITY_WORD = 1; // 0x1
    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);
    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<android.view.inputmethod.SelectRangeGesture> 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 {
  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);
    ctor public SurroundingText(@NonNull CharSequence, @IntRange(from=0) int, @IntRange(from=0) int, @IntRange(from=0xffffffff) int);
    method public int describeContents();
    method public int describeContents();
+10 −0
Original line number Original line Diff line number Diff line
@@ -27,6 +27,7 @@ import android.view.KeyEvent;
import android.view.inputmethod.CompletionInfo;
import android.view.inputmethod.CompletionInfo;
import android.view.inputmethod.CorrectionInfo;
import android.view.inputmethod.CorrectionInfo;
import android.view.inputmethod.DeleteGesture;
import android.view.inputmethod.DeleteGesture;
import android.view.inputmethod.DeleteRangeGesture;
import android.view.inputmethod.ExtractedText;
import android.view.inputmethod.ExtractedText;
import android.view.inputmethod.ExtractedTextRequest;
import android.view.inputmethod.ExtractedTextRequest;
import android.view.inputmethod.HandwritingGesture;
import android.view.inputmethod.HandwritingGesture;
@@ -36,6 +37,7 @@ import android.view.inputmethod.InsertGesture;
import android.view.inputmethod.JoinOrSplitGesture;
import android.view.inputmethod.JoinOrSplitGesture;
import android.view.inputmethod.RemoveSpaceGesture;
import android.view.inputmethod.RemoveSpaceGesture;
import android.view.inputmethod.SelectGesture;
import android.view.inputmethod.SelectGesture;
import android.view.inputmethod.SelectRangeGesture;
import android.view.inputmethod.SurroundingText;
import android.view.inputmethod.SurroundingText;
import android.view.inputmethod.TextAttribute;
import android.view.inputmethod.TextAttribute;


@@ -636,7 +638,9 @@ final class IRemoteInputConnectionInvoker {


    /**
    /**
     * Invokes one of {@link IRemoteInputConnection#performHandwritingSelectGesture},
     * Invokes one of {@link IRemoteInputConnection#performHandwritingSelectGesture},
     * {@link IRemoteInputConnection#performHandwritingSelectRangeGesture},
     * {@link IRemoteInputConnection#performHandwritingDeleteGesture},
     * {@link IRemoteInputConnection#performHandwritingDeleteGesture},
     * {@link IRemoteInputConnection#performHandwritingDeleteRangeGesture},
     * {@link IRemoteInputConnection#performHandwritingInsertGesture},
     * {@link IRemoteInputConnection#performHandwritingInsertGesture},
     * {@link IRemoteInputConnection#performHandwritingRemoveSpaceGesture},
     * {@link IRemoteInputConnection#performHandwritingRemoveSpaceGesture},
     * {@link IRemoteInputConnection#performHandwritingJoinOrSplitGesture}.
     * {@link IRemoteInputConnection#performHandwritingJoinOrSplitGesture}.
@@ -655,12 +659,18 @@ final class IRemoteInputConnectionInvoker {
            if (gesture instanceof SelectGesture) {
            if (gesture instanceof SelectGesture) {
                mConnection.performHandwritingSelectGesture(
                mConnection.performHandwritingSelectGesture(
                        createHeader(), (SelectGesture) gesture, resultReceiver);
                        createHeader(), (SelectGesture) gesture, resultReceiver);
            } else if (gesture instanceof SelectRangeGesture) {
                mConnection.performHandwritingSelectRangeGesture(
                        createHeader(), (SelectRangeGesture) gesture, resultReceiver);
            } else if (gesture instanceof InsertGesture) {
            } else if (gesture instanceof InsertGesture) {
                mConnection.performHandwritingInsertGesture(
                mConnection.performHandwritingInsertGesture(
                        createHeader(), (InsertGesture) gesture, resultReceiver);
                        createHeader(), (InsertGesture) gesture, resultReceiver);
            } else if (gesture instanceof DeleteGesture) {
            } else if (gesture instanceof DeleteGesture) {
                mConnection.performHandwritingDeleteGesture(
                mConnection.performHandwritingDeleteGesture(
                        createHeader(), (DeleteGesture) gesture, resultReceiver);
                        createHeader(), (DeleteGesture) gesture, resultReceiver);
            } else if (gesture instanceof DeleteRangeGesture) {
                mConnection.performHandwritingDeleteRangeGesture(
                        createHeader(), (DeleteRangeGesture) gesture, resultReceiver);
            } else if (gesture instanceof RemoveSpaceGesture) {
            } else if (gesture instanceof RemoveSpaceGesture) {
                mConnection.performHandwritingRemoveSpaceGesture(
                mConnection.performHandwritingRemoveSpaceGesture(
                        createHeader(), (RemoveSpaceGesture) gesture, resultReceiver);
                        createHeader(), (RemoveSpaceGesture) gesture, resultReceiver);
+3 −1
Original line number Original line Diff line number Diff line
@@ -27,9 +27,11 @@ import android.widget.TextView;
import java.util.Objects;
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
 * This class holds the information required for deletion of text in
 * toolkit widgets like {@link TextView}.
 * toolkit widgets like {@link TextView}.
 * <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 HandwritingGesture implements Parcelable {


+19 −0
Original line number Original line Diff line number Diff line
/*
 * 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
+243 −0
Original line number Original line Diff line number Diff line
/*
 * 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}.
 * <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 {

    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)}.
         * <br/>
         * <img src="{@docRoot}reference/android/images/input_method/stylus_handwriting
         * /delete_range_gesture_rects.png"
         * height="300" alt="Deletion strategy using two rectangles"/>
         *  <br/>
         *
         * 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.
         * <br/>
         * <img src="{@docRoot}reference/android/images/input_method/stylus_handwriting
         * /delete_range_gesture_rects.png"
         * height="300" alt="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<DeleteRangeGesture> CREATOR =
            new Creator<DeleteRangeGesture>() {
                @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);
    }
}
Loading