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

Commit 93cdaafc authored by Wilson Wu's avatar Wilson Wu
Browse files

Refine the surrounding text copy logic

Our CL [1] used to copy the original surrounding text
in case the text is updated unexpectedly before the
surrounding text retrieval.

Refine the copy logic by:
-. Move copy action to the InitialSurroundingText constructor
-. Apply a deep copy with parcelable, it also avoid the text
   span is updated later

[1] I6082a4cae2fcdc4c529dc14e2e5e7a45ab1aae4d

Bug: 160390184
Fixes: 163176179
Test: atest FrameworksCoreTests:EditorInfoTest
Test: atest CtsInputMethodTestCases:EditorInfoTest
Change-Id: I75edcf6046467f2776d62bb38137f9d15dbfab52
parent 214c89d2
Loading
Loading
Loading
Loading
+40 −7
Original line number Diff line number Diff line
@@ -22,13 +22,14 @@ import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.RequiresPermission;
import android.os.Build.VERSION_CODES;
import android.os.Bundle;
import android.os.LocaleList;
import android.os.Parcel;
import android.os.Parcelable;
import android.os.UserHandle;
import android.text.Editable;
import android.text.InputType;
import android.text.ParcelableSpan;
import android.text.TextUtils;
import android.util.Printer;
import android.view.View;
@@ -543,6 +544,9 @@ public class EditorInfo implements InputType, Parcelable {
     * {@code #getInitialSelectedText}, and {@code #getInitialTextBeforeCursor}. System is allowed
     * to trim {@code sourceText} for various reasons while keeping the most valuable data to IMEs.
     *
     * Starting from {@link VERSION_CODES#S}, spans that do not implement {@link Parcelable} will
     * be automatically dropped.
     *
     * <p><strong>Editor authors: </strong>Providing the initial input text helps reducing IPC calls
     * for IMEs to provide many modern features right after the connection setup. We recommend
     * calling this method in your implementation.
@@ -562,14 +566,16 @@ public class EditorInfo implements InputType, Parcelable {
     * try to include the selected text within {@code subText} to give the system best flexibility
     * to choose where and how to trim {@code subText} when necessary.
     *
     * Starting from {@link VERSION_CODES#S}, spans that do not implement {@link Parcelable} will
     * be automatically dropped.
     *
     * @param subText The input text. When it was trimmed, {@code subTextStart} must be provided
     *                correctly.
     * @param subTextStart  The position that the input text got trimmed. For example, when the
     *                      editor wants to trim out the first 10 chars, subTextStart should be 10.
     */
    public void setInitialSurroundingSubText(@NonNull CharSequence subText, int subTextStart) {
        CharSequence newSubText = Editable.Factory.getInstance().newEditable(subText);
        Objects.requireNonNull(newSubText);
        Objects.requireNonNull(subText);

        // Swap selection start and end if necessary.
        final int subTextSelStart = initialSelStart > initialSelEnd
@@ -577,7 +583,7 @@ public class EditorInfo implements InputType, Parcelable {
        final int subTextSelEnd = initialSelStart > initialSelEnd
                ? initialSelStart - subTextStart : initialSelEnd - subTextStart;

        final int subTextLength = newSubText.length();
        final int subTextLength = subText.length();
        // Unknown or invalid selection.
        if (subTextStart < 0 || subTextSelStart < 0 || subTextSelEnd > subTextLength) {
            mInitialSurroundingText = new InitialSurroundingText();
@@ -591,12 +597,12 @@ public class EditorInfo implements InputType, Parcelable {
        }

        if (subTextLength <= MEMORY_EFFICIENT_TEXT_LENGTH) {
            mInitialSurroundingText = new InitialSurroundingText(newSubText, subTextSelStart,
            mInitialSurroundingText = new InitialSurroundingText(subText, subTextSelStart,
                    subTextSelEnd);
            return;
        }

        trimLongSurroundingText(newSubText, subTextSelStart, subTextSelEnd);
        trimLongSurroundingText(subText, subTextSelStart, subTextSelEnd);
    }

    /**
@@ -901,7 +907,9 @@ public class EditorInfo implements InputType, Parcelable {

        InitialSurroundingText(@Nullable CharSequence surroundingText, int selectionHead,
                int selectionEnd) {
            mSurroundingText = surroundingText;
            // Copy the original text (without NoCopySpan) in case the original text is updated
            // later.
            mSurroundingText = copyWithParcelableSpans(surroundingText);
            mSelectionHead = selectionHead;
            mSelectionEnd = selectionEnd;
        }
@@ -975,5 +983,30 @@ public class EditorInfo implements InputType, Parcelable {
                        return new InitialSurroundingText[size];
                    }
                };

        /**
         * Create a copy of the given {@link CharSequence} object, with completely copy
         * {@link ParcelableSpan} instances.
         *
         * @param source the original {@link CharSequence} to be copied.
         * @return the copied {@link CharSequence}. {@code null} if {@code source} is {@code null}.
         */
        @Nullable
        private static CharSequence copyWithParcelableSpans(@Nullable CharSequence source) {
            if (source == null) {
                return null;
            }
            Parcel parcel = null;
            try {
                parcel = Parcel.obtain();
                TextUtils.writeToParcel(source, parcel, /* parcelableFlags= */ 0);
                parcel.setDataPosition(0);
                return TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(parcel);
            } finally {
                if (parcel != null) {
                    parcel.recycle();
                }
            }
        }
    }
}
+3 −6
Original line number Diff line number Diff line
@@ -274,13 +274,10 @@ public class EditorInfoTest {
        editorInfo.inputType = EditorInfo.TYPE_CLASS_TEXT;

        editorInfo.setInitialSurroundingText(sb);
        sb.setLength(0);
        editorInfo.writeToParcel(parcel, 0);
        sb.setLength(/* newLength= */ 0);
        editorInfo.writeToParcel(parcel, /* flags= */ 0);

        try {
            editorInfo.getInitialTextBeforeCursor(60, 1);
            fail("Test shouldn't have exception");
        } catch (AssertionError e) { }
        editorInfo.getInitialTextBeforeCursor(/* length= */ 60, /* flags= */ 1);
    }

    private static void assertExpectedTextLength(EditorInfo editorInfo,