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
This commit is contained in:
Gilles Debunne
2011-04-29 11:40:22 -07:00
parent efe2a0e6ac
commit 75beb336f4
2 changed files with 39 additions and 23 deletions

View File

@@ -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;

View File

@@ -8177,7 +8177,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
mInsertionControllerEnabled) {
final int offset = getOffset(mLastDownPositionX, mLastDownPositionY);
stopSelectionActionMode();
Selection.setSelection((Spannable)mText, offset);
Selection.setSelection((Spannable) mText, offset);
getInsertionController().showWithPaste();
handled = true;
}
@@ -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);