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

Commit 11615dea authored by TreeHugger Robot's avatar TreeHugger Robot Committed by Android (Google) Code Review
Browse files

Merge "Add EditorInfo#getInitialSurroundingText API"

parents f2e177b9 10204965
Loading
Loading
Loading
Loading
+1 −0
Original line number Diff line number Diff line
@@ -50464,6 +50464,7 @@ package android.view.inputmethod {
    method public int describeContents();
    method public void dump(android.util.Printer, String);
    method @Nullable public CharSequence getInitialSelectedText(int);
    method @Nullable public android.view.inputmethod.SurroundingText getInitialSurroundingText(@IntRange(from=0) int, @IntRange(from=0) int, int);
    method @Nullable public CharSequence getInitialTextAfterCursor(int, int);
    method @Nullable public CharSequence getInitialTextBeforeCursor(int, int);
    method public final void makeCompatible(int);
+134 −142
Original line number Diff line number Diff line
@@ -25,6 +25,7 @@ import static android.view.inputmethod.EditorInfoProto.PRIVATE_IME_OPTIONS;
import static android.view.inputmethod.EditorInfoProto.TARGET_INPUT_METHOD_USER_ID;

import android.annotation.IntDef;
import android.annotation.IntRange;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.RequiresPermission;
@@ -36,7 +37,6 @@ import android.os.Parcel;
import android.os.Parcelable;
import android.os.UserHandle;
import android.text.InputType;
import android.text.ParcelableSpan;
import android.text.TextUtils;
import android.util.Printer;
import android.util.proto.ProtoOutputStream;
@@ -44,6 +44,7 @@ import android.view.View;
import android.view.autofill.AutofillId;

import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.util.Preconditions;

import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
@@ -563,8 +564,9 @@ public class EditorInfo implements InputType, Parcelable {
    @VisibleForTesting
    static final int MAX_INITIAL_SELECTION_LENGTH =  MEMORY_EFFICIENT_TEXT_LENGTH / 2;

    @NonNull
    private InitialSurroundingText mInitialSurroundingText = new InitialSurroundingText();
    @Nullable
    private SurroundingText mInitialSurroundingText = null;


    /**
     * Editors may use this method to provide initial input text to IMEs. As the surrounding text
@@ -607,6 +609,12 @@ public class EditorInfo implements InputType, Parcelable {
    public void setInitialSurroundingSubText(@NonNull CharSequence subText, int subTextStart) {
        Objects.requireNonNull(subText);

        // For privacy protection reason, we don't carry password inputs to IMEs.
        if (isPasswordInputType(inputType)) {
            mInitialSurroundingText = null;
            return;
        }

        // Swap selection start and end if necessary.
        final int subTextSelStart = initialSelStart > initialSelEnd
                ? initialSelEnd - subTextStart : initialSelStart - subTextStart;
@@ -616,23 +624,17 @@ public class EditorInfo implements InputType, Parcelable {
        final int subTextLength = subText.length();
        // Unknown or invalid selection.
        if (subTextStart < 0 || subTextSelStart < 0 || subTextSelEnd > subTextLength) {
            mInitialSurroundingText = new InitialSurroundingText();
            return;
        }

        // For privacy protection reason, we don't carry password inputs to IMEs.
        if (isPasswordInputType(inputType)) {
            mInitialSurroundingText = new InitialSurroundingText();
            mInitialSurroundingText = null;
            return;
        }

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

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

    /**
@@ -651,8 +653,10 @@ public class EditorInfo implements InputType, Parcelable {
     * @param subText The long text that needs to be trimmed.
     * @param selStart The text offset of the start of the selection.
     * @param selEnd The text offset of the end of the selection
     * @param subTextStart The position that the input text got trimmed.
     */
    private void trimLongSurroundingText(CharSequence subText, int selStart, int selEnd) {
    private void trimLongSurroundingText(CharSequence subText, int selStart, int selEnd,
            int subTextStart) {
        final int sourceSelLength = selEnd - selStart;
        // When the selected text is too long, drop it.
        final int newSelLength = (sourceSelLength > MAX_INITIAL_SELECTION_LENGTH)
@@ -702,10 +706,13 @@ public class EditorInfo implements InputType, Parcelable {
        // obj.
        newBeforeCursorHead = 0;
        final int newSelHead = newBeforeCursorHead + newBeforeCursorLength;
        mInitialSurroundingText = new InitialSurroundingText(
                newInitialSurroundingText, newSelHead, newSelHead + newSelLength);
        final int newOffset = subTextStart + selStart - newSelHead;
        mInitialSurroundingText = new SurroundingText(
                newInitialSurroundingText, newSelHead, newSelHead + newSelLength,
                newOffset);
    }


    /**
     * Get <var>length</var> characters of text before the current cursor position. May be
     * {@code null} when the protocol is not supported.
@@ -720,7 +727,17 @@ public class EditorInfo implements InputType, Parcelable {
     */
    @Nullable
    public CharSequence getInitialTextBeforeCursor(int length, int flags) {
        return mInitialSurroundingText.getInitialTextBeforeCursor(length, flags);
        if (mInitialSurroundingText == null) {
            return null;
        }

        int selStart = Math.min(mInitialSurroundingText.getSelectionStart(),
                mInitialSurroundingText.getSelectionEnd());
        int n = Math.min(length, selStart);
        return ((flags & InputConnection.GET_TEXT_WITH_STYLES) != 0)
                ? mInitialSurroundingText.getText().subSequence(selStart - n, selStart)
                : TextUtils.substring(mInitialSurroundingText.getText(), selStart - n,
                        selStart);
    }

    /**
@@ -735,6 +752,10 @@ public class EditorInfo implements InputType, Parcelable {
     */
    @Nullable
    public CharSequence getInitialSelectedText(int flags) {
        if (mInitialSurroundingText == null) {
            return null;
        }

        // Swap selection start and end if necessary.
        final int correctedTextSelStart = initialSelStart > initialSelEnd
                ? initialSelEnd : initialSelStart;
@@ -742,11 +763,21 @@ public class EditorInfo implements InputType, Parcelable {
                ? initialSelStart : initialSelEnd;

        final int sourceSelLength = correctedTextSelEnd - correctedTextSelStart;
        if (initialSelStart < 0 || initialSelEnd < 0
                || mInitialSurroundingText.getSelectionLength() != sourceSelLength) {
        int selStart = mInitialSurroundingText.getSelectionStart();
        int selEnd = mInitialSurroundingText.getSelectionEnd();
        if (selStart > selEnd) {
            int tmp = selStart;
            selStart = selEnd;
            selEnd = tmp;
        }
        final int selLength = selEnd - selStart;
        if (initialSelStart < 0 || initialSelEnd < 0 || selLength != sourceSelLength) {
            return null;
        }
        return mInitialSurroundingText.getInitialSelectedText(flags);

        return ((flags & InputConnection.GET_TEXT_WITH_STYLES) != 0)
                ? mInitialSurroundingText.getText().subSequence(selStart, selEnd)
                : TextUtils.substring(mInitialSurroundingText.getText(), selStart, selEnd);
    }

    /**
@@ -763,7 +794,79 @@ public class EditorInfo implements InputType, Parcelable {
     */
    @Nullable
    public CharSequence getInitialTextAfterCursor(int length, int flags) {
        return mInitialSurroundingText.getInitialTextAfterCursor(length, flags);
        if (mInitialSurroundingText == null) {
            return null;
        }

        int surroundingTextLength = mInitialSurroundingText.getText().length();
        int selEnd = Math.max(mInitialSurroundingText.getSelectionStart(),
                mInitialSurroundingText.getSelectionEnd());
        int n = Math.min(length, surroundingTextLength - selEnd);
        return ((flags & InputConnection.GET_TEXT_WITH_STYLES) != 0)
                ? mInitialSurroundingText.getText().subSequence(selEnd, selEnd + n)
                : TextUtils.substring(mInitialSurroundingText.getText(), selEnd, selEnd + n);
    }

    /**
     * Gets the surrounding text around the current cursor, with <var>beforeLength</var> characters
     * of text before the cursor (start of the selection), <var>afterLength</var> characters of text
     * after the cursor (end of the selection), and all of the selected text.
     *
     * <p>The initial surrounding text for return could be trimmed if oversize. Fundamental trimming
     * rules are:</p>
     * <ul>
     *     <li>The text before the cursor is the most important information to IMEs.</li>
     *     <li>The text after the cursor is the second important information to IMEs.</li>
     *     <li>The selected text is the least important information but it shall NEVER be truncated.
     *     When it is too long, just drop it.</li>
     * </ul>
     *
     * <p>For example, the subText can be viewed as TextBeforeCursor + Selection + TextAfterCursor.
     * The result could be:</p>
     * <ol>
     *     <li>(maybeTrimmedAtHead)TextBeforeCursor + Selection
     *     + TextAfterCursor(maybeTrimmedAtTail)</li>
     *     <li>(maybeTrimmedAtHead)TextBeforeCursor + TextAfterCursor(maybeTrimmedAtTail)</li>
     * </ol>
     *
     * @param beforeLength The expected length of the text before the cursor.
     * @param afterLength The expected length of the text after the cursor.
     * @param flags Supplies additional options controlling how the text is returned. May be either
     * {@code 0} or {@link InputConnection#GET_TEXT_WITH_STYLES}.
     * @return an {@link android.view.inputmethod.SurroundingText} object describing the surrounding
     * text and state of selection, or  {@code null} if the editor or system could not support this
     * protocol.
     * @throws IllegalArgumentException if {@code beforeLength} or {@code afterLength} is negative.
     */
    @Nullable
    public SurroundingText getInitialSurroundingText(
            @IntRange(from = 0) int beforeLength, @IntRange(from = 0)  int afterLength,
            @InputConnection.GetTextType int flags) {
        Preconditions.checkArgumentNonnegative(beforeLength);
        Preconditions.checkArgumentNonnegative(afterLength);

        if (mInitialSurroundingText == null) {
            return null;
        }

        int length = mInitialSurroundingText.getText().length();
        int selStart = mInitialSurroundingText.getSelectionStart();
        int selEnd = mInitialSurroundingText.getSelectionEnd();
        if (selStart > selEnd) {
            int tmp = selStart;
            selStart = selEnd;
            selEnd = tmp;
        }

        int before = Math.min(beforeLength, selStart);
        int after = Math.min(selEnd + afterLength, length);
        int offset = selStart - before;
        CharSequence newText = ((flags & InputConnection.GET_TEXT_WITH_STYLES) != 0)
                ? mInitialSurroundingText.getText().subSequence(offset, after)
                : TextUtils.substring(mInitialSurroundingText.getText(), offset, after);
        int newSelEnd = Math.min(selEnd - offset, length);
        return new SurroundingText(newText, before, newSelEnd,
                mInitialSurroundingText.getOffset() + offset);
    }

    private static boolean isCutOnSurrogate(CharSequence sourceText, int cutPosition,
@@ -893,7 +996,10 @@ public class EditorInfo implements InputType, Parcelable {
        dest.writeInt(fieldId);
        dest.writeString(fieldName);
        dest.writeBundle(extras);
        dest.writeBoolean(mInitialSurroundingText != null);
        if (mInitialSurroundingText != null) {
            mInitialSurroundingText.writeToParcel(dest, flags);
        }
        if (hintLocales != null) {
            hintLocales.writeToParcel(dest, flags);
        } else {
@@ -925,9 +1031,11 @@ public class EditorInfo implements InputType, Parcelable {
                    res.fieldId = source.readInt();
                    res.fieldName = source.readString();
                    res.extras = source.readBundle();
                    InitialSurroundingText initialSurroundingText =
                            InitialSurroundingText.CREATOR.createFromParcel(source);
                    res.mInitialSurroundingText = initialSurroundingText;
                    boolean hasInitialSurroundingText = source.readBoolean();
                    if (hasInitialSurroundingText) {
                        res.mInitialSurroundingText =
                                SurroundingText.CREATOR.createFromParcel(source);
                    }
                    LocaleList hintLocales = LocaleList.CREATOR.createFromParcel(source);
                    res.hintLocales = hintLocales.isEmpty() ? null : hintLocales;
                    res.contentMimeTypes = source.readStringArray();
@@ -943,120 +1051,4 @@ public class EditorInfo implements InputType, Parcelable {
    public int describeContents() {
        return 0;
    }

    static final class InitialSurroundingText implements Parcelable {
        @Nullable final CharSequence mSurroundingText;
        final int mSelectionHead;
        final int mSelectionEnd;

        InitialSurroundingText() {
            mSurroundingText = null;
            mSelectionHead = 0;
            mSelectionEnd = 0;
        }

        InitialSurroundingText(@Nullable CharSequence surroundingText, int selectionHead,
                int selectionEnd) {
            // Copy the original text (without NoCopySpan) in case the original text is updated
            // later.
            mSurroundingText = copyWithParcelableSpans(surroundingText);
            mSelectionHead = selectionHead;
            mSelectionEnd = selectionEnd;
        }

        @Nullable
        private CharSequence getInitialTextBeforeCursor(int n, int flags) {
            if (mSurroundingText == null) {
                return null;
            }

            final int length = Math.min(n, mSelectionHead);
            return ((flags & InputConnection.GET_TEXT_WITH_STYLES) != 0)
                    ? mSurroundingText.subSequence(mSelectionHead - length, mSelectionHead)
                    : TextUtils.substring(mSurroundingText, mSelectionHead - length,
                            mSelectionHead);
        }

        @Nullable
        private CharSequence getInitialSelectedText(int flags) {
            if (mSurroundingText == null) {
                return null;
            }

            return ((flags & InputConnection.GET_TEXT_WITH_STYLES) != 0)
                    ? mSurroundingText.subSequence(mSelectionHead, mSelectionEnd)
                    : TextUtils.substring(mSurroundingText, mSelectionHead, mSelectionEnd);
        }

        @Nullable
        private CharSequence getInitialTextAfterCursor(int n, int flags) {
            if (mSurroundingText == null) {
                return null;
            }

            final int length = Math.min(n, mSurroundingText.length() - mSelectionEnd);
            return ((flags & InputConnection.GET_TEXT_WITH_STYLES) != 0)
                    ? mSurroundingText.subSequence(mSelectionEnd, mSelectionEnd + length)
                    : TextUtils.substring(mSurroundingText, mSelectionEnd, mSelectionEnd + length);
        }

        private int getSelectionLength() {
            return mSelectionEnd - mSelectionHead;
        }

        @Override
        public int describeContents() {
            return 0;
        }

        @Override
        public void writeToParcel(Parcel dest, int flags) {
            TextUtils.writeToParcel(mSurroundingText, dest, flags);
            dest.writeInt(mSelectionHead);
            dest.writeInt(mSelectionEnd);
        }

        public static final @android.annotation.NonNull Parcelable.Creator<InitialSurroundingText>
                CREATOR = new Parcelable.Creator<InitialSurroundingText>() {
                    @Override
                    public InitialSurroundingText createFromParcel(Parcel source) {
                        final CharSequence initialText =
                                TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(source);
                        final int selectionHead = source.readInt();
                        final int selectionEnd = source.readInt();

                        return new InitialSurroundingText(initialText, selectionHead, selectionEnd);
                    }

                    @Override
                    public InitialSurroundingText[] newArray(int size) {
                        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();
                }
            }
        }
    }
}
 No newline at end of file
+27 −1
Original line number Diff line number Diff line
@@ -18,6 +18,7 @@ package android.view.inputmethod;

import android.annotation.IntRange;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.os.Parcel;
import android.os.Parcelable;
import android.text.TextUtils;
@@ -74,7 +75,7 @@ public final class SurroundingText implements Parcelable {
    public SurroundingText(@NonNull final CharSequence text,
            @IntRange(from = 0) int selectionStart, @IntRange(from = 0) int selectionEnd,
            @IntRange(from = -1) int offset) {
        mText = text;
        mText = copyWithParcelableSpans(text);
        mSelectionStart = selectionStart;
        mSelectionEnd = selectionEnd;
        mOffset = offset;
@@ -155,4 +156,29 @@ public final class SurroundingText implements Parcelable {
                    return new SurroundingText[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();
            }
        }
    }
}
+78 −11

File changed.

Preview size limit exceeded, changes collapsed.