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

Commit 97edc8f2 authored by Haoyu Zhang's avatar Haoyu Zhang
Browse files

Fix: insert mode crash when delete

When the editor has a Span that implement UpdateLayout, deleting
text in insert mode will crash the APP. This is due to the fact
that updating the text will also trigger span update. And then trigger
the DynamicLayout updates and display list update. Because the insert
mode applied a transformation on the text, it's hard to know the updated
text range on the transformed text.
This CL fixed the issue by always updating the entire when span is
updated.

Bug: 314254153
Test: mannually tested as the Editor.java is hidden
Change-Id: If4953de0ae37f92683e5c12c7a11b29a43260fa3
parent 64fccbba
Loading
Loading
Loading
Loading
+31 −4
Original line number Original line Diff line number Diff line
@@ -40,6 +40,7 @@ import android.util.Pools.SynchronizedPool;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.util.ArrayUtils;
import com.android.internal.util.ArrayUtils;
import com.android.internal.util.GrowingArrayUtils;
import com.android.internal.util.GrowingArrayUtils;
import com.android.text.flags.Flags;


import java.lang.ref.WeakReference;
import java.lang.ref.WeakReference;


@@ -1276,9 +1277,22 @@ public class DynamicLayout extends Layout {
        }
        }


        public void onSpanRemoved(Spannable s, Object o, int start, int end) {
        public void onSpanRemoved(Spannable s, Object o, int start, int end) {
            if (o instanceof UpdateLayout)
            if (o instanceof UpdateLayout) {
                if (Flags.insertModeCrashWhenDelete()) {
                    final DynamicLayout dynamicLayout = mLayout.get();
                    if (dynamicLayout != null && dynamicLayout.mDisplay instanceof OffsetMapping) {
                        // It's possible that a Span is removed when the text covering it is
                        // deleted, in this case, the original start and end of the span might be
                        // OOB. So it'll reflow the entire string instead.
                        reflow(s, 0, 0, s.length());
                    } else {
                        reflow(s, start, end - start, end - start);
                    }
                } else {
                    transformAndReflow(s, start, end);
                    transformAndReflow(s, start, end);
                }
                }
            }
        }


        public void onSpanChanged(Spannable s, Object o, int start, int end, int nstart, int nend) {
        public void onSpanChanged(Spannable s, Object o, int start, int end, int nstart, int nend) {
            if (o instanceof UpdateLayout) {
            if (o instanceof UpdateLayout) {
@@ -1287,10 +1301,23 @@ public class DynamicLayout extends Layout {
                    // instead of causing an exception
                    // instead of causing an exception
                    start = 0;
                    start = 0;
                }
                }
                if (Flags.insertModeCrashWhenDelete()) {
                    final DynamicLayout dynamicLayout = mLayout.get();
                    if (dynamicLayout != null && dynamicLayout.mDisplay instanceof OffsetMapping) {
                        // When text is changed, it'll also trigger onSpanChanged. In this case we
                        // can't determine the updated range in the transformed text. So it'll
                        // reflow the entire range instead.
                        reflow(s, 0, 0, s.length());
                    } else {
                        reflow(s, start, end - start, end - start);
                        reflow(s, nstart, nend - nstart, nend - nstart);
                    }
                } else {
                    transformAndReflow(s, start, end);
                    transformAndReflow(s, start, end);
                    transformAndReflow(s, nstart, nend);
                    transformAndReflow(s, nstart, nend);
                }
                }
            }
            }
        }


        private WeakReference<DynamicLayout> mLayout;
        private WeakReference<DynamicLayout> mLayout;
        private OffsetMapping.TextUpdate mTransformedTextUpdate;
        private OffsetMapping.TextUpdate mTransformedTextUpdate;
+7 −0
Original line number Original line Diff line number Diff line
@@ -89,3 +89,10 @@ flag {
  description: "Feature flag for clearing focus when the escape key is pressed."
  description: "Feature flag for clearing focus when the escape key is pressed."
  bug: "312921137"
  bug: "312921137"
}
}

flag {
  name: "insert_mode_crash_when_delete"
  namespace: "text"
  description: "A feature flag for fixing the crash while delete text in insert mode."
  bug: "314254153"
}
+8 −0
Original line number Original line Diff line number Diff line
@@ -148,6 +148,7 @@ import com.android.internal.util.ArrayUtils;
import com.android.internal.util.GrowingArrayUtils;
import com.android.internal.util.GrowingArrayUtils;
import com.android.internal.util.Preconditions;
import com.android.internal.util.Preconditions;
import com.android.internal.view.FloatingActionMode;
import com.android.internal.view.FloatingActionMode;
import com.android.text.flags.Flags;


import java.lang.annotation.Retention;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.RetentionPolicy;
@@ -2343,6 +2344,13 @@ public class Editor {
     */
     */
    void invalidateTextDisplayList(Layout layout, int start, int end) {
    void invalidateTextDisplayList(Layout layout, int start, int end) {
        if (mTextRenderNodes != null && layout instanceof DynamicLayout) {
        if (mTextRenderNodes != null && layout instanceof DynamicLayout) {
            if (Flags.insertModeCrashWhenDelete()
                    && mTextView.isOffsetMappingAvailable()) {
                // Text is transformed with an OffsetMapping, and we can't know the changed range
                // on the transformed text. Invalidate the all display lists instead.
                invalidateTextDisplayList();
                return;
            }
            final int startTransformed =
            final int startTransformed =
                    mTextView.originalToTransformed(start, OffsetMapping.MAP_STRATEGY_CHARACTER);
                    mTextView.originalToTransformed(start, OffsetMapping.MAP_STRATEGY_CHARACTER);
            final int endTransformed =
            final int endTransformed =