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

Commit 4aca6fab authored by Haoyu Zhang's avatar Haoyu Zhang
Browse files

Fix: IMEs don't receive onUpdateSelection when performs insertion mode

TextView implements insertion mode by applying a new
TransformationMethod. However, in TextView#setTransformation it will
call TextView#setText. As a result, TextView will call
InputMethodManager#invalidateInput and make the following
updateSelection invalid until the InputConnection was reestablished.
This CL fix the issue by avoiding calling setText in
setTransformationMethod and instead, update the text layout only.
This also avoid triggering TextWatchers on the TextView.

Bug: 300850862
Test: mannually enable the flag and test with atest TextViewTest
Change-Id: I2298e523e688d48bc86f055a371cc6688e20c77e
parent cb1e8b12
Loading
Loading
Loading
Loading
+7 −0
Original line number Diff line number Diff line
@@ -96,3 +96,10 @@ flag {
  description: "A feature flag for fixing the crash while delete text in insert mode."
  bug: "314254153"
}

flag {
  name: "insert_mode_not_update_selection"
  namespace: "text"
  description: "Fix that InputService#onUpdateSelection is not called when insert mode gesture is performed."
  bug: "300850862"
}
+33 −2
Original line number Diff line number Diff line
@@ -243,6 +243,7 @@ import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
import com.android.internal.util.ArrayUtils;
import com.android.internal.util.FastMath;
import com.android.internal.util.Preconditions;
import com.android.text.flags.Flags;
import libcore.util.EmptyArray;
@@ -539,7 +540,6 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
    // System wide time for last cut, copy or text changed action.
    static long sLastCutCopyOrTextChangedTime;
    private ColorStateList mTextColor;
    private ColorStateList mHintTextColor;
    private ColorStateList mLinkTextColor;
@@ -2857,8 +2857,39 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
        }
        if (updateText) {
            if (Flags.insertModeNotUpdateSelection()) {
                // Update the transformation text.
                if (mTransformation == null) {
                    mTransformed = mText;
                } else {
                    mTransformed = mTransformation.getTransformation(mText, this);
                }
                if (mTransformed == null) {
                    // Should not happen if the transformation method follows the non-null
                    // postcondition.
                    mTransformed = "";
                }
                final boolean isOffsetMapping = mTransformed instanceof OffsetMapping;
                // If the mText is a Spannable and the new TransformationMethod needs to listen to
                // its updates, apply the watcher on it.
                if (mTransformation != null && mText instanceof Spannable
                        && (!mAllowTransformationLengthChange || isOffsetMapping)) {
                    Spannable sp = (Spannable) mText;
                    final int priority = isOffsetMapping ? OFFSET_MAPPING_SPAN_PRIORITY : 0;
                    sp.setSpan(mTransformation, 0, mText.length(),
                            Spanned.SPAN_INCLUSIVE_INCLUSIVE
                                    | (priority << Spanned.SPAN_PRIORITY_SHIFT));
                }
                if (mLayout != null) {
                    nullLayouts();
                    requestLayout();
                    invalidate();
                }
            } else {
                setText(mText);
            }
        }
        if (hasPasswordTransformationMethod()) {
            notifyViewAccessibilityStateChangedIfNeeded(