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

Commit 75beb336 authored by Gilles Debunne's avatar Gilles Debunne
Browse files

SuggestionSpans are removed around edited text.

SuggestionSpans do not make sense anymore when the text they
refer to is modified. Removed these at the lowest possible common
level: In the SpannableStringBuilder that is used to back the Editable.

This way, IME do not have to care about removing these when they change
text. And they cannot forget to so either.

Also fixed a bug in TextView's paste with multi-item paste text (never
exercised, since we have no source for such a thing).

Change-Id: I08ed921f8c04ffb1a00936a3e554a85ee82f103c
parent efe2a0e6
Loading
Loading
Loading
Loading
+35 −20
Original line number Diff line number Diff line
@@ -20,6 +20,7 @@ import com.android.internal.util.ArrayUtils;

import android.graphics.Canvas;
import android.graphics.Paint;
import android.text.style.SuggestionSpan;

import java.lang.reflect.Array;

@@ -277,8 +278,7 @@ implements CharSequence, GetChars, Spannable, Editable, Appendable,
        TextWatcher[] recipients = null;

        if (notify)
            recipients = sendTextWillChange(start, end - start,
                                            tbend - tbstart);
            recipients = sendTextWillChange(start, end - start, tbend - tbstart);

        for (int i = mSpanCount - 1; i >= 0; i--) {
            if ((mSpanFlags[i] & SPAN_PARAGRAPH) == SPAN_PARAGRAPH) {
@@ -353,6 +353,7 @@ implements CharSequence, GetChars, Spannable, Editable, Appendable,
        // no need for span fixup on pure insertion
        if (tbend > tbstart && end - start == 0) {
            if (notify) {
                removeSuggestionSpans(start, end);
                sendTextChange(recipients, start, end - start, tbend - tbstart);
                sendTextHasChanged(recipients);
            }
@@ -384,20 +385,10 @@ implements CharSequence, GetChars, Spannable, Editable, Appendable,
            }

            // remove 0-length SPAN_EXCLUSIVE_EXCLUSIVE
            // XXX send notification on removal

            if (mSpanEnds[i] < mSpanStarts[i]) {
                System.arraycopy(mSpans, i + 1,
                                 mSpans, i, mSpanCount - (i + 1));
                System.arraycopy(mSpanStarts, i + 1,
                                 mSpanStarts, i, mSpanCount - (i + 1));
                System.arraycopy(mSpanEnds, i + 1,
                                 mSpanEnds, i, mSpanCount - (i + 1));
                System.arraycopy(mSpanFlags, i + 1,
                                 mSpanFlags, i, mSpanCount - (i + 1));

                mSpanCount--;
                removeSpan(i);
            }
            removeSuggestionSpans(start, end);
        }

        if (notify) {
@@ -408,6 +399,32 @@ implements CharSequence, GetChars, Spannable, Editable, Appendable,
        return ret;
    }

    /**
     * Removes the SuggestionSpan that overlap the [start, end] range, and that would
     * not make sense anymore after the change.
     */
    private void removeSuggestionSpans(int start, int end) {
        for (int i = mSpanCount - 1; i >= 0; i--) {
            final int spanEnd = mSpanEnds[i];
            final int spanSpart = mSpanStarts[i];
            if ((mSpans[i] instanceof SuggestionSpan) && (
                    (spanSpart < start && spanEnd > start) ||
                    (spanSpart < end && spanEnd > end))) {
                removeSpan(i);
            }
        }
    }

    private void removeSpan(int i) {
        // XXX send notification on removal
        System.arraycopy(mSpans, i + 1, mSpans, i, mSpanCount - (i + 1));
        System.arraycopy(mSpanStarts, i + 1, mSpanStarts, i, mSpanCount - (i + 1));
        System.arraycopy(mSpanEnds, i + 1, mSpanEnds, i, mSpanCount - (i + 1));
        System.arraycopy(mSpanFlags, i + 1, mSpanFlags, i, mSpanCount - (i + 1));

        mSpanCount--;
    }

    // Documentation from interface
    public SpannableStringBuilder replace(int start, int end, CharSequence tb) {
        return replace(start, end, tb, 0, tb.length());
@@ -465,16 +482,15 @@ implements CharSequence, GetChars, Spannable, Editable, Appendable,
            mGapStart++;
            mGapLength--;

            if (mGapLength < 1)
            if (mGapLength < 1) {
                new Exception("mGapLength < 1").printStackTrace();
            }

            int oldlen = (end + 1) - start;

            int inserted = change(false, start + 1, start + 1,
                                  tb, tbstart, tbend);
            int inserted = change(false, start + 1, start + 1, tb, tbstart, tbend);
            change(false, start, start + 1, "", 0, 0);
            change(false, start + inserted, start + inserted + oldlen - 1,
                   "", 0, 0);
            change(false, start + inserted, start + inserted + oldlen - 1, "", 0, 0);

            /*
             * Special case to keep the cursor in the same position
@@ -1274,7 +1290,6 @@ implements CharSequence, GetChars, Spannable, Editable, Appendable,
    private int[] mSpanFlags;
    private int mSpanCount;

    private static final int MARK = 1;
    private static final int POINT = 2;
    private static final int PARAGRAPH = 3;

+4 −3
Original line number Diff line number Diff line
@@ -8695,16 +8695,17 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
            (ClipboardManager) getContext().getSystemService(Context.CLIPBOARD_SERVICE);
        ClipData clip = clipboard.getPrimaryClip();
        if (clip != null) {
            boolean didfirst = false;
            boolean didFirst = false;
            for (int i=0; i<clip.getItemCount(); i++) {
                CharSequence paste = clip.getItemAt(i).coerceToText(getContext());
                if (paste != null) {
                    if (!didfirst) {
                    if (!didFirst) {
                        long minMax = prepareSpacesAroundPaste(min, max, paste);
                        min = extractRangeStartFromLong(minMax);
                        max = extractRangeEndFromLong(minMax);
                        Selection.setSelection((Spannable) mText, max);
                        ((Editable) mText).replace(min, max, paste);
                        didFirst = true;
                    } else {
                        ((Editable) mText).insert(getSelectionEnd(), "\n");
                        ((Editable) mText).insert(getSelectionEnd(), paste);