summaryrefslogtreecommitdiffstats
path: root/core/java/android/text/SpannableStringBuilder.java
diff options
context:
space:
mode:
authorGilles Debunne <debunne@google.com>2011-08-04 21:22:30 -0700
committerGilles Debunne <debunne@google.com>2011-08-23 13:13:54 -0700
commit6435a56a8c02de98befcc8cd743b2b638cffb327 (patch)
tree62a5678b5531f53c7b25e920f8f9c6b65f8b510c /core/java/android/text/SpannableStringBuilder.java
parentdefa12e95b8d25db5f3e9a044e83d6fe680b67a3 (diff)
downloadframeworks_base-6435a56a8c02de98befcc8cd743b2b638cffb327.zip
frameworks_base-6435a56a8c02de98befcc8cd743b2b638cffb327.tar.gz
frameworks_base-6435a56a8c02de98befcc8cd743b2b638cffb327.tar.bz2
Spell checking in TextViews
New UX interactions (the Paste action is no longer displayed after a delay) suggestionEnabled flag replaced by existing input type flag. removeSpans fixed in SpannableStringBuilder to always send notifications SuggestionSpan handled by TextView instead of SpannableStringBuilder New span update algorithm to correctly handle edition around word boundaries. Change-Id: I52c01172f19e595fa512e285a565a3fd97c3c50e
Diffstat (limited to 'core/java/android/text/SpannableStringBuilder.java')
-rw-r--r--core/java/android/text/SpannableStringBuilder.java125
1 files changed, 44 insertions, 81 deletions
diff --git a/core/java/android/text/SpannableStringBuilder.java b/core/java/android/text/SpannableStringBuilder.java
index 6bde802..5fed775 100644
--- a/core/java/android/text/SpannableStringBuilder.java
+++ b/core/java/android/text/SpannableStringBuilder.java
@@ -16,21 +16,18 @@
package android.text;
-import com.android.internal.util.ArrayUtils;
-
import android.graphics.Canvas;
import android.graphics.Paint;
-import android.text.style.SuggestionSpan;
+
+import com.android.internal.util.ArrayUtils;
import java.lang.reflect.Array;
/**
* This is the class for text whose content and markup can both be changed.
*/
-public class SpannableStringBuilder
-implements CharSequence, GetChars, Spannable, Editable, Appendable,
- GraphicsOperations
-{
+public class SpannableStringBuilder implements CharSequence, GetChars, Spannable, Editable,
+ Appendable, GraphicsOperations {
/**
* Create a new SpannableStringBuilder with empty contents
*/
@@ -111,8 +108,7 @@ implements CharSequence, GetChars, Spannable, Editable, Appendable,
if (where < 0) {
throw new IndexOutOfBoundsException("charAt: " + where + " < 0");
} else if (where >= len) {
- throw new IndexOutOfBoundsException("charAt: " + where +
- " >= length " + len);
+ throw new IndexOutOfBoundsException("charAt: " + where + " >= length " + len);
}
if (where >= mGapStart)
@@ -266,8 +262,7 @@ implements CharSequence, GetChars, Spannable, Editable, Appendable,
return append(String.valueOf(text));
}
- private int change(int start, int end,
- CharSequence tb, int tbstart, int tbend) {
+ private int change(int start, int end, CharSequence tb, int tbstart, int tbend) {
return change(true, start, end, tb, tbstart, tbend);
}
@@ -277,8 +272,9 @@ implements CharSequence, GetChars, Spannable, Editable, Appendable,
int ret = tbend - tbstart;
TextWatcher[] recipients = null;
- if (notify)
+ if (notify) {
recipients = sendTextWillChange(start, end - start, tbend - tbstart);
+ }
for (int i = mSpanCount - 1; i >= 0; i--) {
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
if (tbend > tbstart && end - start == 0) {
if (notify) {
- removeSuggestionSpans(start, end);
sendTextChange(recipients, start, end - start, tbend - tbstart);
sendTextHasChanged(recipients);
}
@@ -388,7 +383,6 @@ implements CharSequence, GetChars, Spannable, Editable, Appendable,
if (mSpanEnds[i] < mSpanStarts[i]) {
removeSpan(i);
}
- removeSuggestionSpans(start, end);
}
if (notify) {
@@ -399,30 +393,26 @@ 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));
+ Object object = mSpans[i];
+
+ int start = mSpanStarts[i];
+ int end = mSpanEnds[i];
+
+ 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--;
+
+ mSpans[mSpanCount] = null;
+
+ sendSpanRemoved(object, start, end);
}
// Documentation from interface
@@ -462,11 +452,10 @@ implements CharSequence, GetChars, Spannable, Editable, Appendable,
moveGapTo(end);
TextWatcher[] recipients;
- recipients = sendTextWillChange(start, end - start,
- tbend - tbstart);
-
int origlen = end - start;
+ recipients = sendTextWillChange(start, origlen, tbend - tbstart);
+
if (mGapLength < 2)
resizeFor(length() + 1);
@@ -486,11 +475,9 @@ implements CharSequence, GetChars, Spannable, Editable, Appendable,
new Exception("mGapLength < 1").printStackTrace();
}
- int oldlen = (end + 1) - start;
-
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 + origlen, "", 0, 0);
/*
* 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);
selend = (int) off + start;
- setSpan(false, Selection.SELECTION_END, selend, selend,
- Spanned.SPAN_POINT_POINT);
+ setSpan(false, Selection.SELECTION_END, selend, selend, Spanned.SPAN_POINT_POINT);
}
-
sendTextChange(recipients, start, origlen, inserted);
sendTextHasChanged(recipients);
}
+
return this;
}
@@ -534,8 +520,7 @@ implements CharSequence, GetChars, Spannable, Editable, Appendable,
setSpan(true, what, start, end, flags);
}
- private void setSpan(boolean send,
- Object what, int start, int end, int flags) {
+ private void setSpan(boolean send, Object what, int start, int end, int flags) {
int nstart = start;
int nend = end;
@@ -546,8 +531,7 @@ implements CharSequence, GetChars, Spannable, Editable, Appendable,
char c = charAt(start - 1);
if (c != '\n')
- throw new RuntimeException(
- "PARAGRAPH span must start at paragraph boundary");
+ throw new RuntimeException("PARAGRAPH span must start at paragraph boundary");
}
}
@@ -556,23 +540,22 @@ implements CharSequence, GetChars, Spannable, Editable, Appendable,
char c = charAt(end - 1);
if (c != '\n')
- throw new RuntimeException(
- "PARAGRAPH span must end at paragraph boundary");
+ throw new RuntimeException("PARAGRAPH span must end at paragraph boundary");
}
}
- if (start > mGapStart)
+ if (start > mGapStart) {
start += mGapLength;
- else if (start == mGapStart) {
+ } else if (start == mGapStart) {
int flag = (flags & START_MASK) >> START_SHIFT;
if (flag == POINT || (flag == PARAGRAPH && start == length()))
start += mGapLength;
}
- if (end > mGapStart)
+ if (end > mGapStart) {
end += mGapLength;
- else if (end == mGapStart) {
+ } else if (end == mGapStart) {
int flag = (flags & END_MASK);
if (flag == POINT || (flag == PARAGRAPH && end == length()))
@@ -637,25 +620,7 @@ implements CharSequence, GetChars, Spannable, Editable, Appendable,
public void removeSpan(Object what) {
for (int i = mSpanCount - 1; i >= 0; i--) {
if (mSpans[i] == what) {
- int ostart = mSpanStarts[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);
+ removeSpan(i);
return;
}
}
@@ -729,6 +694,8 @@ implements CharSequence, GetChars, Spannable, Editable, Appendable,
*/
@SuppressWarnings("unchecked")
public <T> T[] getSpans(int queryStart, int queryEnd, Class<T> kind) {
+ if (kind == null) return ArrayUtils.emptyArray(kind);
+
int spanCount = mSpanCount;
Object[] spans = mSpans;
int[] starts = mSpanStarts;
@@ -742,6 +709,8 @@ implements CharSequence, GetChars, Spannable, Editable, Appendable,
T ret1 = null;
for (int i = 0; i < spanCount; i++) {
+ if (!kind.isInstance(spans[i])) continue;
+
int spanStart = starts[i];
int spanEnd = ends[i];
@@ -766,10 +735,6 @@ implements CharSequence, GetChars, Spannable, Editable, Appendable,
continue;
}
- if (kind != null && !kind.isInstance(spans[i])) {
- continue;
- }
-
if (count == 0) {
// Safe conversion thanks to the isInstance test above
ret1 = (T) spans[i];
@@ -909,8 +874,7 @@ implements CharSequence, GetChars, Spannable, Editable, Appendable,
return recip;
}
- private void sendTextChange(TextWatcher[] recip, int start, int before,
- int after) {
+ private void sendTextChange(TextWatcher[] recip, int start, int before, int after) {
int n = recip.length;
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) {
- SpanWatcher[] recip = getSpans(Math.min(s, st), Math.max(e, en),
- SpanWatcher.class);
+ SpanWatcher[] recip = getSpans(Math.min(s, st), Math.max(e, en), SpanWatcher.class);
int n = recip.length;
for (int i = 0; i < n; i++) {