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

Commit b51036f7 authored by Gilles Debunne's avatar Gilles Debunne
Browse files

Refactoring SpannableStringBuilder

Made TextWatcher notification process clearer by moving it to
a single place, with methods renamed.

Also reverts CL 177544: we cannot broadcast span chages just yet,
the layout has not been reflown. A future CL will change this
behavior to make sure span changes are correctly broadcasted.

Change-Id: I9ef88dce91dff5f5f45e2845d5b3f18f1c853de3
parent 04247163
Loading
Loading
Loading
Loading
+52 −80
Original line number Diff line number Diff line
@@ -262,19 +262,8 @@ public class SpannableStringBuilder implements CharSequence, GetChars, Spannable
        return append(String.valueOf(text));
    }

    private int change(int start, int end, CharSequence tb, int tbstart, int tbend) {
        return change(true, start, end, tb, tbstart, tbend);
    }

    private int change(boolean notify, int start, int end,
                       CharSequence tb, int tbstart, int tbend) {
    private void change(int start, int end, CharSequence tb, int tbstart, int tbend) {
        checkRange("replace", start, end);
        int ret = tbend - tbstart;
        TextWatcher[] recipients = null;

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

        for (int i = mSpanCount - 1; i >= 0; i--) {
            if ((mSpanFlags[i] & SPAN_PARAGRAPH) == SPAN_PARAGRAPH) {
@@ -338,7 +327,7 @@ public class SpannableStringBuilder implements CharSequence, GetChars, Spannable
                    en = tbend;

                if (getSpanStart(spans[i]) < 0) {
                    setSpan(true, spans[i],
                    setSpan(false, spans[i],
                            st - tbstart + start,
                            en - tbstart + start,
                            sp.getSpanFlags(spans[i]));
@@ -346,24 +335,16 @@ public class SpannableStringBuilder implements CharSequence, GetChars, Spannable
            }
        }

        if (end > start) {
            // no need for span fixup on pure insertion
        if (tbend > tbstart && end - start == 0) {
            if (notify) {
                sendTextChange(recipients, start, end - start, tbend - tbstart);
                sendTextHasChanged(recipients);
            }

            return ret;
        }

        boolean atend = (mGapStart + mGapLength == mText.length);
            boolean atEnd = (mGapStart + mGapLength == mText.length);

            for (int i = mSpanCount - 1; i >= 0; i--) {
                if (mSpanStarts[i] >= start &&
                        mSpanStarts[i] < mGapStart + mGapLength) {
                    int flag = (mSpanFlags[i] & START_MASK) >> START_SHIFT;

                if (flag == POINT || (flag == PARAGRAPH && atend))
                if (flag == POINT || (flag == PARAGRAPH && atEnd))
                    mSpanStarts[i] = mGapStart + mGapLength;
                else
                    mSpanStarts[i] = start;
@@ -373,7 +354,7 @@ public class SpannableStringBuilder implements CharSequence, GetChars, Spannable
                        mSpanEnds[i] < mGapStart + mGapLength) {
                    int flag = (mSpanFlags[i] & END_MASK);

                if (flag == POINT || (flag == PARAGRAPH && atend))
                    if (flag == POINT || (flag == PARAGRAPH && atEnd))
                        mSpanEnds[i] = mGapStart + mGapLength;
                    else
                        mSpanEnds[i] = start;
@@ -384,13 +365,7 @@ public class SpannableStringBuilder implements CharSequence, GetChars, Spannable
                    removeSpan(i);
                }
            }

        if (notify) {
            sendTextChange(recipients, start, end - start, tbend - tbstart);
            sendTextHasChanged(recipients);
        }

        return ret;
    }

    private void removeSpan(int i) {
@@ -425,8 +400,7 @@ public class SpannableStringBuilder implements CharSequence, GetChars, Spannable
                        CharSequence tb, int tbstart, int tbend) {
        int filtercount = mFilters.length;
        for (int i = 0; i < filtercount; i++) {
            CharSequence repl = mFilters[i].filter(tb, tbstart, tbend,
                                                   this, start, end);
            CharSequence repl = mFilters[i].filter(tb, tbstart, tbend, this, start, end);

            if (repl != null) {
                tb = repl;
@@ -435,11 +409,17 @@ public class SpannableStringBuilder implements CharSequence, GetChars, Spannable
            }
        }

        if (end == start && tbstart == tbend) {
        final int origLen = end - start;
        final int newLen = tbend - tbstart;

        if (origLen == 0 && newLen == 0) {
            return this;
        }

        if (end == start || tbstart == tbend) {
        TextWatcher[] textWatchers = getSpans(start, start + origLen, TextWatcher.class);
        sendBeforeTextChanged(textWatchers, start, origLen, newLen);

        if (origLen == 0 || newLen == 0) {
            change(start, end, tb, tbstart, tbend);
        } else {
            int selstart = Selection.getSelectionStart(this);
@@ -450,11 +430,6 @@ public class SpannableStringBuilder implements CharSequence, GetChars, Spannable

            checkRange("replace", start, end);
            moveGapTo(end);
            TextWatcher[] recipients;

            int origlen = end - start;

            recipients = sendTextWillChange(start, origlen, tbend - tbstart);

            if (mGapLength < 2)
                resizeFor(length() + 1);
@@ -475,9 +450,9 @@ public class SpannableStringBuilder implements CharSequence, GetChars, Spannable
                new Exception("mGapLength < 1").printStackTrace();
            }

            int inserted = change(false, start + 1, start + 1, tb, tbstart, tbend);
            change(false, start, start + 1, "", 0, 0);
            change(false, start + inserted, start + inserted + origlen, "", 0, 0);
            change(start + 1, start + 1, tb, tbstart, tbend);
            change(start, start + 1, "", 0, 0);
            change(start + newLen, start + newLen + origLen, "", 0, 0);

            /*
             * Special case to keep the cursor in the same position
@@ -490,7 +465,7 @@ public class SpannableStringBuilder implements CharSequence, GetChars, Spannable
            if (selstart > start && selstart < end) {
                long off = selstart - start;

                off = off * inserted / (end - start);
                off = off * newLen / (end - start);
                selstart = (int) off + start;

                setSpan(false, Selection.SELECTION_START, selstart, selstart,
@@ -499,15 +474,16 @@ public class SpannableStringBuilder implements CharSequence, GetChars, Spannable
            if (selend > start && selend < end) {
                long off = selend - start;

                off = off * inserted / (end - start);
                off = off * newLen / (end - start);
                selend = (int) off + start;

                setSpan(false, Selection.SELECTION_END, selend, selend, Spanned.SPAN_POINT_POINT);
            }
            sendTextChange(recipients, start, origlen, inserted);
            sendTextHasChanged(recipients);
        }

        sendTextChanged(textWatchers, start, origLen, newLen);
        sendAfterTextChanged(textWatchers);

        return this; 
    }

@@ -872,30 +848,27 @@ public class SpannableStringBuilder implements CharSequence, GetChars, Spannable
        return new String(buf);
    }

    private TextWatcher[] sendTextWillChange(int start, int before, int after) {
        TextWatcher[] recip = getSpans(start, start + before, TextWatcher.class);
        int n = recip.length;
    private void sendBeforeTextChanged(TextWatcher[] watchers, int start, int before, int after) {
        int n = watchers.length;

        for (int i = 0; i < n; i++) {
            recip[i].beforeTextChanged(this, start, before, after);
            watchers[i].beforeTextChanged(this, start, before, after);
        }

        return recip;
    }

    private void sendTextChange(TextWatcher[] recip, int start, int before, int after) {
        int n = recip.length;
    private void sendTextChanged(TextWatcher[] watchers, int start, int before, int after) {
        int n = watchers.length;

        for (int i = 0; i < n; i++) {
            recip[i].onTextChanged(this, start, before, after);
            watchers[i].onTextChanged(this, start, before, after);
        }
    }

    private void sendTextHasChanged(TextWatcher[] recip) {
        int n = recip.length;
    private void sendAfterTextChanged(TextWatcher[] watchers) {
        int n = watchers.length;

        for (int i = 0; i < n; i++) {
            recip[i].afterTextChanged(this);
            watchers[i].afterTextChanged(this);
        }
    }

@@ -1037,8 +1010,7 @@ public class SpannableStringBuilder implements CharSequence, GetChars, Spannable
     * Don't call this yourself -- exists for Canvas to use internally.
     * {@hide}
     */
    public void drawText(Canvas c, int start, int end,
                         float x, float y, Paint p) {
    public void drawText(Canvas c, int start, int end, float x, float y, Paint p) {
        checkRange("drawText", start, end);

        if (end <= mGapStart) {
@@ -1059,8 +1031,7 @@ public class SpannableStringBuilder implements CharSequence, GetChars, Spannable
     * Don't call this yourself -- exists for Canvas to use internally.
     * {@hide}
     */
    public void drawTextRun(Canvas c, int start, int end,
            int contextStart, int contextEnd,
    public void drawTextRun(Canvas c, int start, int end, int contextStart, int contextEnd,
            float x, float y, int flags, Paint p) {
        checkRange("drawTextRun", start, end);

@@ -1262,6 +1233,7 @@ public class SpannableStringBuilder implements CharSequence, GetChars, Spannable
    private int[] mSpanFlags;
    private int mSpanCount;

    // TODO These value are tightly related to the public SPAN_MARK/POINT values in {@link Spanned}
    private static final int POINT = 2;
    private static final int PARAGRAPH = 3;