diff --git a/core/java/android/text/method/InsertModeTransformationMethod.java b/core/java/android/text/method/InsertModeTransformationMethod.java index fbdaa3d735f4b..0c933d996ff30 100644 --- a/core/java/android/text/method/InsertModeTransformationMethod.java +++ b/core/java/android/text/method/InsertModeTransformationMethod.java @@ -80,12 +80,34 @@ public class InsertModeTransformationMethod implements TransformationMethod, Tex */ public InsertModeTransformationMethod(@IntRange(from = 0) int offset, boolean singleLine, @NonNull TransformationMethod oldTransformationMethod) { - mStart = offset; - mEnd = offset; + this(offset, offset, singleLine, oldTransformationMethod); + } + + private InsertModeTransformationMethod(int start, int end, boolean singleLine, + @NonNull TransformationMethod oldTransformationMethod) { + mStart = start; + mEnd = end; mSingleLine = singleLine; mOldTransformationMethod = oldTransformationMethod; } + /** + * Create a new {@code InsertModeTransformation} with the given new inner + * {@code oldTransformationMethod} and the {@code singleLine} value. The returned + * {@link InsertModeTransformationMethod} will keep the highlight range. + * + * @param oldTransformationMethod the updated inner transformation method at the + * {@link android.widget.TextView}. + * @param singleLine the updated singleLine value. + * @return the new {@link InsertModeTransformationMethod} with the updated + * {@code oldTransformationMethod} and {@code singleLine} value. + */ + public InsertModeTransformationMethod update(TransformationMethod oldTransformationMethod, + boolean singleLine) { + return new InsertModeTransformationMethod(mStart, mEnd, singleLine, + oldTransformationMethod); + } + public TransformationMethod getOldTransformationMethod() { return mOldTransformationMethod; } diff --git a/core/java/android/widget/Editor.java b/core/java/android/widget/Editor.java index 616957a0bdd50..b89dd31725b7b 100644 --- a/core/java/android/widget/Editor.java +++ b/core/java/android/widget/Editor.java @@ -8072,7 +8072,7 @@ public class Editor { final boolean isSingleLine = mTextView.isSingleLine(); mInsertModeTransformationMethod = new InsertModeTransformationMethod(offset, isSingleLine, oldTransformationMethod); - mTextView.setTransformationMethod(mInsertModeTransformationMethod); + mTextView.setTransformationMethodInternal(mInsertModeTransformationMethod); Selection.setSelection((Spannable) mTextView.getText(), offset); mIsInsertModeActive = true; @@ -8083,11 +8083,6 @@ public class Editor { if (!mIsInsertModeActive) return; if (mInsertModeTransformationMethod == null || mInsertModeTransformationMethod != mTextView.getTransformationMethod()) { - // If mInsertionModeTransformationMethod doesn't match the one on TextView, - // something else have changed the TextView's TransformationMethod while the - // insertion mode is active. We don't need to restore the oldTransformationMethod. - // TODO(265871733): support the case where setTransformationMethod is called in - // the insert mode. mIsInsertModeActive = false; return; } @@ -8097,7 +8092,7 @@ public class Editor { final int selectionEnd = mTextView.getSelectionEnd(); final TransformationMethod oldTransformationMethod = mInsertModeTransformationMethod.getOldTransformationMethod(); - mTextView.setTransformationMethod(oldTransformationMethod); + mTextView.setTransformationMethodInternal(oldTransformationMethod); Selection.setSelection((Spannable) mTextView.getText(), selectionStart, selectionEnd); mIsInsertModeActive = false; } @@ -8118,6 +8113,25 @@ public class Editor { layout.getSelection(highlightStart, highlightEnd, consumer); } } + + /** + * Notify the {@link InsertModeController} before the TextView's + * {@link TransformationMethod} is updated. If it's not in the insert mode, + * the given method is directly returned. Otherwise, it will wrap the given transformation + * method with an {@link InsertModeTransformationMethod} and then return. + * + * @param oldTransformationMethod the new {@link TransformationMethod} to be set on the + * TextView. + * @return the updated {@link TransformationMethod} to be set on the Textview. + */ + TransformationMethod updateTransformationMethod( + TransformationMethod oldTransformationMethod) { + if (!mIsInsertModeActive) return oldTransformationMethod; + + mInsertModeTransformationMethod = mInsertModeTransformationMethod.update( + oldTransformationMethod, mTextView.isSingleLine()); + return mInsertModeTransformationMethod; + } } boolean enterInsertMode(int offset) { @@ -8133,6 +8147,26 @@ public class Editor { mInsertModeController.exitInsertMode(); } + /** + * Called by the {@link TextView} when the {@link TransformationMethod} is updated. + * + * @param method the {@link TransformationMethod} to be set on the TextView. + */ + void setTransformationMethod(TransformationMethod method) { + if (mInsertModeController == null || !mInsertModeController.mIsInsertModeActive) { + mTextView.setTransformationMethodInternal(method); + return; + } + + // Changing TransformationMethod will reset selection range to [0, 0), we need to + // manually restore the old selection range. + final int selectionStart = mTextView.getSelectionStart(); + final int selectionEnd = mTextView.getSelectionEnd(); + method = mInsertModeController.updateTransformationMethod(method); + mTextView.setTransformationMethodInternal(method); + Selection.setSelection((Spannable) mTextView.getText(), selectionStart, selectionEnd); + } + /** * Initializes the nodeInfo with smart actions. */ diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java index 56bdea3d0eed0..5de807936c8ac 100644 --- a/core/java/android/widget/TextView.java +++ b/core/java/android/widget/TextView.java @@ -2769,6 +2769,14 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener * @attr ref android.R.styleable#TextView_singleLine */ public final void setTransformationMethod(TransformationMethod method) { + if (mEditor != null) { + mEditor.setTransformationMethod(method); + } else { + setTransformationMethodInternal(method); + } + } + + void setTransformationMethodInternal(@Nullable TransformationMethod method) { if (method == mTransformation) { // Avoid the setText() below if the transformation is // the same.