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

Commit 81a8e6ec authored by Haoyu Zhang's avatar Haoyu Zhang Committed by Android (Google) Code Review
Browse files

Merge "Fix insert mode crash when text has UpdateLayout span" into main

parents 71724c7f 54716a54
Loading
Loading
Loading
Loading
+10 −2
Original line number Diff line number Diff line
@@ -1320,7 +1320,11 @@ public class DynamicLayout extends Layout {
                        // 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.
                        if (Flags.insertModeCrashUpdateLayoutSpan()) {
                            transformAndReflow(s, 0, s.length());
                        } else {
                            reflow(s, 0, 0, s.length());
                        }
                    } else {
                        reflow(s, start, end - start, end - start);
                    }
@@ -1343,7 +1347,11 @@ public class DynamicLayout extends Layout {
                        // 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.
                        if (Flags.insertModeCrashUpdateLayoutSpan()) {
                            transformAndReflow(s, 0, s.length());
                        } else {
                            reflow(s, 0, 0, s.length());
                        }
                    } else {
                        reflow(s, start, end - start, end - start);
                        reflow(s, nstart, nend - nstart, nend - nstart);
+10 −0
Original line number Diff line number Diff line
@@ -124,6 +124,16 @@ flag {
  bug: "300850862"
}

flag {
  name: "insert_mode_crash_update_layout_span"
  namespace: "text"
  description: "Fix insert mode crash when the text has UpdateLayout span attached."
  bug: "355137282"
  metadata {
    purpose: PURPOSE_BUGFIX
  }
}

flag {
  name: "icu_bidi_migration"
  namespace: "text"
+88 −0
Original line number Diff line number Diff line
@@ -21,11 +21,18 @@ import static android.text.Layout.Alignment.ALIGN_NORMAL;
import static com.google.common.truth.Truth.assertThat;

import android.platform.test.annotations.Presubmit;
import android.platform.test.annotations.RequiresFlagsEnabled;
import android.platform.test.flag.junit.CheckFlagsRule;
import android.platform.test.flag.junit.DeviceFlagsValueProvider;
import android.text.method.OffsetMapping;
import android.text.style.UpdateLayout;

import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.SmallTest;

import com.android.text.flags.Flags;

import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;

@@ -36,6 +43,9 @@ public class DynamicLayoutOffsetMappingTest {
    private static final int WIDTH = 10000;
    private static final TextPaint sTextPaint = new TextPaint();

    @Rule
    public CheckFlagsRule mCheckFlagsRule = DeviceFlagsValueProvider.createCheckFlagsRule();

    @Test
    public void textWithOffsetMapping() {
        final String text = "abcde";
@@ -119,6 +129,84 @@ public class DynamicLayoutOffsetMappingTest {
        assertLineRange(layout, /* lineBreaks */ 0, 5, 6, 8);
    }

    @Test
    @RequiresFlagsEnabled(Flags.FLAG_INSERT_MODE_CRASH_UPDATE_LAYOUT_SPAN)
    public void textWithOffsetMapping_deletion_withUpdateLayoutSpan() {
        final String text = "abcdef";
        final SpannableStringBuilder spannable = new SpannableStringBuilder(text);
        // UpdateLayout span covers the letter 'd'.
        spannable.setSpan(new UpdateLayout() {}, 3, 4, Spanned.SPAN_INCLUSIVE_EXCLUSIVE);

        final CharSequence transformedText =
                new TestOffsetMapping(spannable, 3, "\n\n");

        final DynamicLayout layout = DynamicLayout.Builder.obtain(spannable, sTextPaint, WIDTH)
                .setAlignment(ALIGN_NORMAL)
                .setIncludePad(false)
                .setDisplayText(transformedText)
                .build();

        // delete character 'c', original text becomes "abdef"
        spannable.delete(2, 3);
        assertThat(transformedText.toString()).isEqualTo("ab\n\ndef");
        assertLineRange(layout, /* lineBreaks */ 0, 3, 4, 7);

        // delete character 'd', original text becomes "abef"
        spannable.delete(2, 3);
        assertThat(transformedText.toString()).isEqualTo("ab\n\nef");
        assertLineRange(layout, /* lineBreaks */ 0, 3, 4, 6);

        // delete "be", original text becomes "af"
        spannable.delete(1, 3);
        assertThat(transformedText.toString()).isEqualTo("a\n\nf");
        assertLineRange(layout, /* lineBreaks */ 0, 2, 3, 4);
    }

    @Test
    @RequiresFlagsEnabled(Flags.FLAG_INSERT_MODE_CRASH_UPDATE_LAYOUT_SPAN)
    public void textWithOffsetMapping_insert_withUpdateLayoutSpan() {
        final String text = "abcdef";
        final SpannableStringBuilder spannable = new SpannableStringBuilder(text);
        final CharSequence transformedText = new TestOffsetMapping(spannable, 3, "\n\n");

        // UpdateLayout span covers the letter 'de'.
        spannable.setSpan(new UpdateLayout() {}, 3, 5, Spanned.SPAN_INCLUSIVE_EXCLUSIVE);

        final DynamicLayout layout = DynamicLayout.Builder.obtain(spannable, sTextPaint, WIDTH)
                .setAlignment(ALIGN_NORMAL)
                .setIncludePad(false)
                .setDisplayText(transformedText)
                .build();

        spannable.insert(3, "x");
        assertThat(transformedText.toString()).isEqualTo("abcx\n\ndef");
        assertLineRange(layout, /* lineBreaks */ 0, 5, 6, 9);

        spannable.insert(5, "x");
        assertThat(transformedText.toString()).isEqualTo("abcx\n\ndxef");
        assertLineRange(layout, /* lineBreaks */ 0, 5, 6, 10);
    }

    @Test
    @RequiresFlagsEnabled(Flags.FLAG_INSERT_MODE_CRASH_UPDATE_LAYOUT_SPAN)
    public void textWithOffsetMapping_replace_withUpdateLayoutSpan() {
        final String text = "abcdef";
        final SpannableStringBuilder spannable = new SpannableStringBuilder(text);
        final CharSequence transformedText = new TestOffsetMapping(spannable, 3, "\n\n");
        // UpdateLayout span covers the letter 'de'.
        spannable.setSpan(new UpdateLayout() {}, 3, 5, Spanned.SPAN_INCLUSIVE_EXCLUSIVE);

        final DynamicLayout layout = DynamicLayout.Builder.obtain(spannable, sTextPaint, WIDTH)
                .setAlignment(ALIGN_NORMAL)
                .setIncludePad(false)
                .setDisplayText(transformedText)
                .build();

        spannable.replace(2, 4, "xx");
        assertThat(transformedText.toString()).isEqualTo("abxx\n\nef");
        assertLineRange(layout, /* lineBreaks */ 0, 5, 6, 8);
    }

    @Test
    public void textWithOffsetMapping_blockBeforeTextChanged_deletion() {
        final String text = "abcdef";