Loading core/api/current.txt +1 −0 Original line number Diff line number Diff line Loading @@ -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); core/java/android/view/inputmethod/EditorInfo.java +134 −142 Original line number Diff line number Diff line Loading @@ -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; Loading @@ -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; Loading @@ -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; Loading Loading @@ -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 Loading Loading @@ -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; Loading @@ -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); } /** Loading @@ -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) Loading Loading @@ -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. Loading @@ -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); } /** Loading @@ -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; Loading @@ -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); } /** Loading @@ -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, Loading Loading @@ -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 { Loading Loading @@ -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(); Loading @@ -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 core/java/android/view/inputmethod/SurroundingText.java +27 −1 Original line number Diff line number Diff line Loading @@ -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; Loading Loading @@ -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; Loading Loading @@ -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(); } } } } core/tests/coretests/src/android/view/inputmethod/EditorInfoTest.java +78 −11 File changed.Preview size limit exceeded, changes collapsed. Show changes Loading
core/api/current.txt +1 −0 Original line number Diff line number Diff line Loading @@ -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);
core/java/android/view/inputmethod/EditorInfo.java +134 −142 Original line number Diff line number Diff line Loading @@ -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; Loading @@ -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; Loading @@ -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; Loading Loading @@ -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 Loading Loading @@ -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; Loading @@ -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); } /** Loading @@ -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) Loading Loading @@ -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. Loading @@ -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); } /** Loading @@ -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; Loading @@ -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); } /** Loading @@ -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, Loading Loading @@ -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 { Loading Loading @@ -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(); Loading @@ -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
core/java/android/view/inputmethod/SurroundingText.java +27 −1 Original line number Diff line number Diff line Loading @@ -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; Loading Loading @@ -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; Loading Loading @@ -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(); } } } }
core/tests/coretests/src/android/view/inputmethod/EditorInfoTest.java +78 −11 File changed.Preview size limit exceeded, changes collapsed. Show changes