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

Commit f874c4f9 authored by Gilles Debunne's avatar Gilles Debunne Committed by Android (Google) Code Review
Browse files

Merge "Spell checking in TextViews"

parents 7d0dd6ad 6435a56a
Loading
Loading
Loading
Loading
+58 −59

File changed.

Preview size limit exceeded, changes collapsed.

+44 −81
Original line number Original line Diff line number Diff line
@@ -16,21 +16,18 @@


package android.text;
package android.text;


import com.android.internal.util.ArrayUtils;

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

import com.android.internal.util.ArrayUtils;


import java.lang.reflect.Array;
import java.lang.reflect.Array;


/**
/**
 * This is the class for text whose content and markup can both be changed.
 * This is the class for text whose content and markup can both be changed.
 */
 */
public class SpannableStringBuilder
public class SpannableStringBuilder implements CharSequence, GetChars, Spannable, Editable,
implements CharSequence, GetChars, Spannable, Editable, Appendable,
        Appendable, GraphicsOperations {
           GraphicsOperations
{
    /**
    /**
     * Create a new SpannableStringBuilder with empty contents
     * Create a new SpannableStringBuilder with empty contents
     */
     */
@@ -111,8 +108,7 @@ implements CharSequence, GetChars, Spannable, Editable, Appendable,
        if (where < 0) {
        if (where < 0) {
            throw new IndexOutOfBoundsException("charAt: " + where + " < 0");
            throw new IndexOutOfBoundsException("charAt: " + where + " < 0");
        } else if (where >= len) {
        } else if (where >= len) {
            throw new IndexOutOfBoundsException("charAt: " + where +
            throw new IndexOutOfBoundsException("charAt: " + where + " >= length " + len);
                                                " >= length " + len);
        }
        }


        if (where >= mGapStart)
        if (where >= mGapStart)
@@ -266,8 +262,7 @@ implements CharSequence, GetChars, Spannable, Editable, Appendable,
        return append(String.valueOf(text));
        return append(String.valueOf(text));
    }
    }


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


@@ -277,8 +272,9 @@ implements CharSequence, GetChars, Spannable, Editable, Appendable,
        int ret = tbend - tbstart;
        int ret = tbend - tbstart;
        TextWatcher[] recipients = null;
        TextWatcher[] recipients = null;


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


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


        if (notify) {
        if (notify) {
@@ -399,30 +393,26 @@ implements CharSequence, GetChars, Spannable, Editable, Appendable,
        return ret;
        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) {
    private void removeSpan(int i) {
        // XXX send notification on removal
        Object object = mSpans[i];
        System.arraycopy(mSpans, i + 1, mSpans, i, mSpanCount - (i + 1));

        System.arraycopy(mSpanStarts, i + 1, mSpanStarts, i, mSpanCount - (i + 1));
        int start = mSpanStarts[i];
        System.arraycopy(mSpanEnds, i + 1, mSpanEnds, i, mSpanCount - (i + 1));
        int end = mSpanEnds[i];
        System.arraycopy(mSpanFlags, i + 1, mSpanFlags, i, mSpanCount - (i + 1));

        if (start > mGapStart) start -= mGapLength;
        if (end > mGapStart) end -= mGapLength;

        int count = mSpanCount - (i + 1);
        System.arraycopy(mSpans, i + 1, mSpans, i, count);
        System.arraycopy(mSpanStarts, i + 1, mSpanStarts, i, count);
        System.arraycopy(mSpanEnds, i + 1, mSpanEnds, i, count);
        System.arraycopy(mSpanFlags, i + 1, mSpanFlags, i, count);


        mSpanCount--;
        mSpanCount--;

        mSpans[mSpanCount] = null;

        sendSpanRemoved(object, start, end);
    }
    }


    // Documentation from interface
    // Documentation from interface
@@ -462,11 +452,10 @@ implements CharSequence, GetChars, Spannable, Editable, Appendable,
            moveGapTo(end);
            moveGapTo(end);
            TextWatcher[] recipients;
            TextWatcher[] recipients;


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

            int origlen = end - start;
            int origlen = end - start;


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

            if (mGapLength < 2)
            if (mGapLength < 2)
                resizeFor(length() + 1);
                resizeFor(length() + 1);


@@ -486,11 +475,9 @@ implements CharSequence, GetChars, Spannable, Editable, Appendable,
                new Exception("mGapLength < 1").printStackTrace();
                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, start + 1, "", 0, 0);
            change(false, start + inserted, start + inserted + oldlen - 1, "", 0, 0);
            change(false, start + inserted, start + inserted + origlen, "", 0, 0);


            /*
            /*
             * Special case to keep the cursor in the same position
             * Special case to keep the cursor in the same position
@@ -515,13 +502,12 @@ implements CharSequence, GetChars, Spannable, Editable, Appendable,
                off = off * inserted / (end - start);
                off = off * inserted / (end - start);
                selend = (int) off + start;
                selend = (int) off + start;


                setSpan(false, Selection.SELECTION_END, selend, selend,
                setSpan(false, Selection.SELECTION_END, selend, selend, Spanned.SPAN_POINT_POINT);
                        Spanned.SPAN_POINT_POINT);
            }
            }

            sendTextChange(recipients, start, origlen, inserted);
            sendTextChange(recipients, start, origlen, inserted);
            sendTextHasChanged(recipients);
            sendTextHasChanged(recipients);
        }
        }

        return this; 
        return this; 
    }
    }


@@ -534,8 +520,7 @@ implements CharSequence, GetChars, Spannable, Editable, Appendable,
        setSpan(true, what, start, end, flags);
        setSpan(true, what, start, end, flags);
    }
    }


    private void setSpan(boolean send,
    private void setSpan(boolean send, Object what, int start, int end, int flags) {
                         Object what, int start, int end, int flags) {
        int nstart = start;
        int nstart = start;
        int nend = end;
        int nend = end;


@@ -546,8 +531,7 @@ implements CharSequence, GetChars, Spannable, Editable, Appendable,
                char c = charAt(start - 1);
                char c = charAt(start - 1);


                if (c != '\n')
                if (c != '\n')
                    throw new RuntimeException(
                    throw new RuntimeException("PARAGRAPH span must start at paragraph boundary");
                            "PARAGRAPH span must start at paragraph boundary");
            }
            }
        }
        }


@@ -556,23 +540,22 @@ implements CharSequence, GetChars, Spannable, Editable, Appendable,
                char c = charAt(end - 1);
                char c = charAt(end - 1);


                if (c != '\n')
                if (c != '\n')
                    throw new RuntimeException(
                    throw new RuntimeException("PARAGRAPH span must end at paragraph boundary");
                            "PARAGRAPH span must end at paragraph boundary");
            }
            }
        }
        }


        if (start > mGapStart)
        if (start > mGapStart) {
            start += mGapLength;
            start += mGapLength;
        else if (start == mGapStart) {
        } else if (start == mGapStart) {
            int flag = (flags & START_MASK) >> START_SHIFT;
            int flag = (flags & START_MASK) >> START_SHIFT;


            if (flag == POINT || (flag == PARAGRAPH && start == length()))
            if (flag == POINT || (flag == PARAGRAPH && start == length()))
                start += mGapLength;
                start += mGapLength;
        }
        }


        if (end > mGapStart)
        if (end > mGapStart) {
            end += mGapLength;
            end += mGapLength;
        else if (end == mGapStart) {
        } else if (end == mGapStart) {
            int flag = (flags & END_MASK);
            int flag = (flags & END_MASK);


            if (flag == POINT || (flag == PARAGRAPH && end == length()))
            if (flag == POINT || (flag == PARAGRAPH && end == length()))
@@ -637,25 +620,7 @@ implements CharSequence, GetChars, Spannable, Editable, Appendable,
    public void removeSpan(Object what) {
    public void removeSpan(Object what) {
        for (int i = mSpanCount - 1; i >= 0; i--) {
        for (int i = mSpanCount - 1; i >= 0; i--) {
            if (mSpans[i] == what) {
            if (mSpans[i] == what) {
                int ostart = mSpanStarts[i];
                removeSpan(i);
                int oend = mSpanEnds[i];

                if (ostart > mGapStart)
                    ostart -= mGapLength;
                if (oend > mGapStart)
                    oend -= mGapLength;

                int count = mSpanCount - (i + 1);

                System.arraycopy(mSpans, i + 1, mSpans, i, count);
                System.arraycopy(mSpanStarts, i + 1, mSpanStarts, i, count);
                System.arraycopy(mSpanEnds, i + 1, mSpanEnds, i, count);
                System.arraycopy(mSpanFlags, i + 1, mSpanFlags, i, count);

                mSpanCount--;
                mSpans[mSpanCount] = null;

                sendSpanRemoved(what, ostart, oend);
                return;
                return;
            }
            }
        }
        }
@@ -729,6 +694,8 @@ implements CharSequence, GetChars, Spannable, Editable, Appendable,
     */
     */
    @SuppressWarnings("unchecked")
    @SuppressWarnings("unchecked")
    public <T> T[] getSpans(int queryStart, int queryEnd, Class<T> kind) {
    public <T> T[] getSpans(int queryStart, int queryEnd, Class<T> kind) {
        if (kind == null) return ArrayUtils.emptyArray(kind);

        int spanCount = mSpanCount;
        int spanCount = mSpanCount;
        Object[] spans = mSpans;
        Object[] spans = mSpans;
        int[] starts = mSpanStarts;
        int[] starts = mSpanStarts;
@@ -742,6 +709,8 @@ implements CharSequence, GetChars, Spannable, Editable, Appendable,
        T ret1 = null;
        T ret1 = null;


        for (int i = 0; i < spanCount; i++) {
        for (int i = 0; i < spanCount; i++) {
            if (!kind.isInstance(spans[i])) continue;

            int spanStart = starts[i];
            int spanStart = starts[i];
            int spanEnd = ends[i];
            int spanEnd = ends[i];


@@ -766,10 +735,6 @@ implements CharSequence, GetChars, Spannable, Editable, Appendable,
                    continue;
                    continue;
            }
            }


            if (kind != null && !kind.isInstance(spans[i])) {
                continue;
            }

            if (count == 0) {
            if (count == 0) {
                // Safe conversion thanks to the isInstance test above
                // Safe conversion thanks to the isInstance test above
                ret1 = (T) spans[i];
                ret1 = (T) spans[i];
@@ -909,8 +874,7 @@ implements CharSequence, GetChars, Spannable, Editable, Appendable,
        return recip;
        return recip;
    }
    }


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


        for (int i = 0; i < n; i++) {
        for (int i = 0; i < n; i++) {
@@ -945,8 +909,7 @@ implements CharSequence, GetChars, Spannable, Editable, Appendable,
    }
    }


    private void sendSpanChanged(Object what, int s, int e, int st, int en) {
    private void sendSpanChanged(Object what, int s, int e, int st, int en) {
        SpanWatcher[] recip = getSpans(Math.min(s, st), Math.max(e, en),
        SpanWatcher[] recip = getSpans(Math.min(s, st), Math.max(e, en), SpanWatcher.class);
                                  SpanWatcher.class);
        int n = recip.length;
        int n = recip.length;


        for (int i = 0; i < n; i++) {
        for (int i = 0; i < n; i++) {
+4 −0
Original line number Original line Diff line number Diff line
@@ -73,6 +73,10 @@ public class WordIterator implements Selection.PositionIterator {
        }
        }
    };
    };


    public void forceUpdate() {
        mCurrentDirty = true;
    }

    public void setCharSequence(CharSequence incoming) {
    public void setCharSequence(CharSequence incoming) {
        // When incoming is different object, move listeners to new sequence
        // When incoming is different object, move listeners to new sequence
        // and mark as dirty so we reload contents.
        // and mark as dirty so we reload contents.
+41 −0
Original line number Original line Diff line number Diff line
/*
 * Copyright (C) 2011 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package android.text.style;

/**
 * A SpellCheckSpan is an internal data structure created by the TextView's SpellChecker to
 * annotate portions of the text that are about to or currently being spell checked. They are
 * automatically removed once the spell check is completed.
 *
 * @hide
 */
public class SpellCheckSpan {

    private boolean mSpellCheckInProgress;

    public SpellCheckSpan() {
        mSpellCheckInProgress = false;
    }

    public void setSpellCheckInProgress() {
        mSpellCheckInProgress = true;
    }

    public boolean isSpellCheckInProgress() {
        return mSpellCheckInProgress;
    }
}
+10 −7
Original line number Original line Diff line number Diff line
@@ -40,7 +40,7 @@ import java.util.Locale;
 * These spans should typically be created by the input method to provide correction and alternates
 * These spans should typically be created by the input method to provide correction and alternates
 * for the text.
 * for the text.
 *
 *
 * @see TextView#setSuggestionsEnabled(boolean)
 * @see TextView#isSuggestionsEnabled()
 */
 */
public class SuggestionSpan extends CharacterStyle implements ParcelableSpan {
public class SuggestionSpan extends CharacterStyle implements ParcelableSpan {


@@ -76,7 +76,7 @@ public class SuggestionSpan extends CharacterStyle implements ParcelableSpan {
     * And the current IME might want to specify any IME as the target IME including other IMEs.
     * And the current IME might want to specify any IME as the target IME including other IMEs.
     */
     */


    private final int mFlags;
    private int mFlags;
    private final String[] mSuggestions;
    private final String[] mSuggestions;
    private final String mLocaleString;
    private final String mLocaleString;
    private final String mNotificationTargetClassName;
    private final String mNotificationTargetClassName;
@@ -134,8 +134,7 @@ public class SuggestionSpan extends CharacterStyle implements ParcelableSpan {
        } else {
        } else {
            mNotificationTargetClassName = "";
            mNotificationTargetClassName = "";
        }
        }
        mHashCode = hashCodeInternal(
        mHashCode = hashCodeInternal(mSuggestions, mLocaleString, mNotificationTargetClassName);
                mFlags, mSuggestions, mLocaleString, mNotificationTargetClassName);


        initStyle(context);
        initStyle(context);
    }
    }
@@ -211,6 +210,10 @@ public class SuggestionSpan extends CharacterStyle implements ParcelableSpan {
        return mFlags;
        return mFlags;
    }
    }


    public void setFlags(int flags) {
        mFlags = flags;
    }

    @Override
    @Override
    public int describeContents() {
    public int describeContents() {
        return 0;
        return 0;
@@ -247,10 +250,10 @@ public class SuggestionSpan extends CharacterStyle implements ParcelableSpan {
        return mHashCode;
        return mHashCode;
    }
    }


    private static int hashCodeInternal(int flags, String[] suggestions,String locale,
    private static int hashCodeInternal(String[] suggestions, String locale,
            String notificationTargetClassName) {
            String notificationTargetClassName) {
        return Arrays.hashCode(new Object[] {SystemClock.uptimeMillis(), flags, suggestions, locale,
        return Arrays.hashCode(new Object[] {Long.valueOf(SystemClock.uptimeMillis()), suggestions,
                notificationTargetClassName});
                locale, notificationTargetClassName});
    }
    }


    public static final Parcelable.Creator<SuggestionSpan> CREATOR =
    public static final Parcelable.Creator<SuggestionSpan> CREATOR =
Loading