Merge "Too many SpellCheckSpans are created."
This commit is contained in:
committed by
Android (Google) Code Review
commit
f4314dffbd
@@ -710,18 +710,17 @@ public class SpannableStringBuilder implements CharSequence, GetChars, Spannable
|
|||||||
|
|
||||||
for (int i = 0; i < spanCount; i++) {
|
for (int i = 0; i < spanCount; i++) {
|
||||||
int spanStart = starts[i];
|
int spanStart = starts[i];
|
||||||
int spanEnd = ends[i];
|
|
||||||
|
|
||||||
if (spanStart > gapstart) {
|
if (spanStart > gapstart) {
|
||||||
spanStart -= gaplen;
|
spanStart -= gaplen;
|
||||||
}
|
}
|
||||||
if (spanEnd > gapstart) {
|
|
||||||
spanEnd -= gaplen;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (spanStart > queryEnd) {
|
if (spanStart > queryEnd) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int spanEnd = ends[i];
|
||||||
|
if (spanEnd > gapstart) {
|
||||||
|
spanEnd -= gaplen;
|
||||||
|
}
|
||||||
if (spanEnd < queryStart) {
|
if (spanEnd < queryStart) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -39,8 +39,8 @@ public class SpellCheckSpan implements ParcelableSpan {
|
|||||||
mSpellCheckInProgress = (src.readInt() != 0);
|
mSpellCheckInProgress = (src.readInt() != 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setSpellCheckInProgress() {
|
public void setSpellCheckInProgress(boolean inProgress) {
|
||||||
mSpellCheckInProgress = true;
|
mSpellCheckInProgress = inProgress;
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean isSpellCheckInProgress() {
|
public boolean isSpellCheckInProgress() {
|
||||||
|
|||||||
@@ -22,7 +22,6 @@ import android.text.Selection;
|
|||||||
import android.text.Spanned;
|
import android.text.Spanned;
|
||||||
import android.text.style.SpellCheckSpan;
|
import android.text.style.SpellCheckSpan;
|
||||||
import android.text.style.SuggestionSpan;
|
import android.text.style.SuggestionSpan;
|
||||||
import android.util.Log;
|
|
||||||
import android.view.textservice.SpellCheckerSession;
|
import android.view.textservice.SpellCheckerSession;
|
||||||
import android.view.textservice.SpellCheckerSession.SpellCheckerSessionListener;
|
import android.view.textservice.SpellCheckerSession.SpellCheckerSessionListener;
|
||||||
import android.view.textservice.SuggestionsInfo;
|
import android.view.textservice.SuggestionsInfo;
|
||||||
@@ -40,23 +39,21 @@ import java.util.Locale;
|
|||||||
* @hide
|
* @hide
|
||||||
*/
|
*/
|
||||||
public class SpellChecker implements SpellCheckerSessionListener {
|
public class SpellChecker implements SpellCheckerSessionListener {
|
||||||
private static final String LOG_TAG = "SpellChecker";
|
|
||||||
private static final boolean DEBUG_SPELL_CHECK = false;
|
|
||||||
private static final int DELAY_BEFORE_SPELL_CHECK = 400; // milliseconds
|
|
||||||
|
|
||||||
private final TextView mTextView;
|
private final TextView mTextView;
|
||||||
|
|
||||||
final SpellCheckerSession mSpellCheckerSession;
|
final SpellCheckerSession mSpellCheckerSession;
|
||||||
final int mCookie;
|
final int mCookie;
|
||||||
|
|
||||||
// Paired arrays for the (id, spellCheckSpan) pair. mIndex is the next available position
|
// Paired arrays for the (id, spellCheckSpan) pair. A negative id means the associated
|
||||||
|
// SpellCheckSpan has been recycled and can be-reused.
|
||||||
|
// May contain null SpellCheckSpans after a given index.
|
||||||
private int[] mIds;
|
private int[] mIds;
|
||||||
private SpellCheckSpan[] mSpellCheckSpans;
|
private SpellCheckSpan[] mSpellCheckSpans;
|
||||||
// The actual current number of used slots in the above arrays
|
// The mLength first elements of the above arrays have been initialized
|
||||||
private int mLength;
|
private int mLength;
|
||||||
|
|
||||||
private int mSpanSequenceCounter = 0;
|
private int mSpanSequenceCounter = 0;
|
||||||
private Runnable mChecker;
|
|
||||||
|
|
||||||
public SpellChecker(TextView textView) {
|
public SpellChecker(TextView textView) {
|
||||||
mTextView = textView;
|
mTextView = textView;
|
||||||
@@ -69,7 +66,7 @@ public class SpellChecker implements SpellCheckerSessionListener {
|
|||||||
mCookie = hashCode();
|
mCookie = hashCode();
|
||||||
|
|
||||||
// Arbitrary: 4 simultaneous spell check spans. Will automatically double size on demand
|
// Arbitrary: 4 simultaneous spell check spans. Will automatically double size on demand
|
||||||
final int size = ArrayUtils.idealObjectArraySize(4);
|
final int size = ArrayUtils.idealObjectArraySize(1);
|
||||||
mIds = new int[size];
|
mIds = new int[size];
|
||||||
mSpellCheckSpans = new SpellCheckSpan[size];
|
mSpellCheckSpans = new SpellCheckSpan[size];
|
||||||
mLength = 0;
|
mLength = 0;
|
||||||
@@ -89,73 +86,50 @@ public class SpellChecker implements SpellCheckerSessionListener {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void addSpellCheckSpan(SpellCheckSpan spellCheckSpan) {
|
private int nextSpellCheckSpanIndex() {
|
||||||
int length = mIds.length;
|
for (int i = 0; i < mLength; i++) {
|
||||||
if (mLength >= length) {
|
if (mIds[i] < 0) return i;
|
||||||
final int newSize = length * 2;
|
}
|
||||||
|
|
||||||
|
if (mLength == mSpellCheckSpans.length) {
|
||||||
|
final int newSize = mLength * 2;
|
||||||
int[] newIds = new int[newSize];
|
int[] newIds = new int[newSize];
|
||||||
SpellCheckSpan[] newSpellCheckSpans = new SpellCheckSpan[newSize];
|
SpellCheckSpan[] newSpellCheckSpans = new SpellCheckSpan[newSize];
|
||||||
System.arraycopy(mIds, 0, newIds, 0, length);
|
System.arraycopy(mIds, 0, newIds, 0, mLength);
|
||||||
System.arraycopy(mSpellCheckSpans, 0, newSpellCheckSpans, 0, length);
|
System.arraycopy(mSpellCheckSpans, 0, newSpellCheckSpans, 0, mLength);
|
||||||
mIds = newIds;
|
mIds = newIds;
|
||||||
mSpellCheckSpans = newSpellCheckSpans;
|
mSpellCheckSpans = newSpellCheckSpans;
|
||||||
}
|
}
|
||||||
|
|
||||||
mIds[mLength] = mSpanSequenceCounter++;
|
mSpellCheckSpans[mLength] = new SpellCheckSpan();
|
||||||
mSpellCheckSpans[mLength] = spellCheckSpan;
|
|
||||||
mLength++;
|
mLength++;
|
||||||
|
return mLength - 1;
|
||||||
|
}
|
||||||
|
|
||||||
if (DEBUG_SPELL_CHECK) {
|
public void addSpellCheckSpan(int wordStart, int wordEnd) {
|
||||||
final Editable mText = (Editable) mTextView.getText();
|
final int index = nextSpellCheckSpanIndex();
|
||||||
int start = mText.getSpanStart(spellCheckSpan);
|
((Editable) mTextView.getText()).setSpan(mSpellCheckSpans[index], wordStart, wordEnd,
|
||||||
int end = mText.getSpanEnd(spellCheckSpan);
|
Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
|
||||||
if (start >= 0 && end >= 0) {
|
mIds[index] = mSpanSequenceCounter++;
|
||||||
Log.d(LOG_TAG, "Schedule check " + mText.subSequence(start, end));
|
|
||||||
} else {
|
|
||||||
Log.d(LOG_TAG, "Schedule check EMPTY!");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
scheduleSpellCheck();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void removeSpellCheckSpan(SpellCheckSpan spellCheckSpan) {
|
public void removeSpellCheckSpan(SpellCheckSpan spellCheckSpan) {
|
||||||
for (int i = 0; i < mLength; i++) {
|
for (int i = 0; i < mLength; i++) {
|
||||||
if (mSpellCheckSpans[i] == spellCheckSpan) {
|
if (mSpellCheckSpans[i] == spellCheckSpan) {
|
||||||
removeAtIndex(i);
|
mSpellCheckSpans[i].setSpellCheckInProgress(false);
|
||||||
|
mIds[i] = -1;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void removeAtIndex(int i) {
|
|
||||||
System.arraycopy(mIds, i + 1, mIds, i, mLength - i - 1);
|
|
||||||
System.arraycopy(mSpellCheckSpans, i + 1, mSpellCheckSpans, i, mLength - i - 1);
|
|
||||||
mLength--;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void onSelectionChanged() {
|
public void onSelectionChanged() {
|
||||||
scheduleSpellCheck();
|
spellCheck();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void scheduleSpellCheck() {
|
public void spellCheck() {
|
||||||
if (mLength == 0) return;
|
|
||||||
if (mSpellCheckerSession == null) return;
|
if (mSpellCheckerSession == null) return;
|
||||||
|
|
||||||
if (mChecker != null) {
|
|
||||||
mTextView.removeCallbacks(mChecker);
|
|
||||||
}
|
|
||||||
if (mChecker == null) {
|
|
||||||
mChecker = new Runnable() {
|
|
||||||
public void run() {
|
|
||||||
spellCheck();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
mTextView.postDelayed(mChecker, DELAY_BEFORE_SPELL_CHECK);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void spellCheck() {
|
|
||||||
final Editable editable = (Editable) mTextView.getText();
|
final Editable editable = (Editable) mTextView.getText();
|
||||||
final int selectionStart = Selection.getSelectionStart(editable);
|
final int selectionStart = Selection.getSelectionStart(editable);
|
||||||
final int selectionEnd = Selection.getSelectionEnd(editable);
|
final int selectionEnd = Selection.getSelectionEnd(editable);
|
||||||
@@ -164,8 +138,7 @@ public class SpellChecker implements SpellCheckerSessionListener {
|
|||||||
int textInfosCount = 0;
|
int textInfosCount = 0;
|
||||||
|
|
||||||
for (int i = 0; i < mLength; i++) {
|
for (int i = 0; i < mLength; i++) {
|
||||||
SpellCheckSpan spellCheckSpan = mSpellCheckSpans[i];
|
final SpellCheckSpan spellCheckSpan = mSpellCheckSpans[i];
|
||||||
|
|
||||||
if (spellCheckSpan.isSpellCheckInProgress()) continue;
|
if (spellCheckSpan.isSpellCheckInProgress()) continue;
|
||||||
|
|
||||||
final int start = editable.getSpanStart(spellCheckSpan);
|
final int start = editable.getSpanStart(spellCheckSpan);
|
||||||
@@ -174,7 +147,7 @@ public class SpellChecker implements SpellCheckerSessionListener {
|
|||||||
// Do not check this word if the user is currently editing it
|
// Do not check this word if the user is currently editing it
|
||||||
if (start >= 0 && end > start && (selectionEnd < start || selectionStart > end)) {
|
if (start >= 0 && end > start && (selectionEnd < start || selectionStart > end)) {
|
||||||
final String word = editable.subSequence(start, end).toString();
|
final String word = editable.subSequence(start, end).toString();
|
||||||
spellCheckSpan.setSpellCheckInProgress();
|
spellCheckSpan.setSpellCheckInProgress(true);
|
||||||
textInfos[textInfosCount++] = new TextInfo(word, mCookie, mIds[i]);
|
textInfos[textInfosCount++] = new TextInfo(word, mCookie, mIds[i]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -196,27 +169,18 @@ public class SpellChecker implements SpellCheckerSessionListener {
|
|||||||
for (int i = 0; i < results.length; i++) {
|
for (int i = 0; i < results.length; i++) {
|
||||||
SuggestionsInfo suggestionsInfo = results[i];
|
SuggestionsInfo suggestionsInfo = results[i];
|
||||||
if (suggestionsInfo.getCookie() != mCookie) continue;
|
if (suggestionsInfo.getCookie() != mCookie) continue;
|
||||||
|
|
||||||
final int sequenceNumber = suggestionsInfo.getSequence();
|
final int sequenceNumber = suggestionsInfo.getSequence();
|
||||||
// Starting from the end, to limit the number of array copy while removing
|
|
||||||
for (int j = mLength - 1; j >= 0; j--) {
|
for (int j = 0; j < mLength; j++) {
|
||||||
|
final SpellCheckSpan spellCheckSpan = mSpellCheckSpans[j];
|
||||||
|
|
||||||
if (sequenceNumber == mIds[j]) {
|
if (sequenceNumber == mIds[j]) {
|
||||||
SpellCheckSpan spellCheckSpan = mSpellCheckSpans[j];
|
|
||||||
final int attributes = suggestionsInfo.getSuggestionsAttributes();
|
final int attributes = suggestionsInfo.getSuggestionsAttributes();
|
||||||
boolean isInDictionary =
|
boolean isInDictionary =
|
||||||
((attributes & SuggestionsInfo.RESULT_ATTR_IN_THE_DICTIONARY) > 0);
|
((attributes & SuggestionsInfo.RESULT_ATTR_IN_THE_DICTIONARY) > 0);
|
||||||
boolean looksLikeTypo =
|
boolean looksLikeTypo =
|
||||||
((attributes & SuggestionsInfo.RESULT_ATTR_LOOKS_LIKE_TYPO) > 0);
|
((attributes & SuggestionsInfo.RESULT_ATTR_LOOKS_LIKE_TYPO) > 0);
|
||||||
|
|
||||||
if (DEBUG_SPELL_CHECK) {
|
|
||||||
final int start = editable.getSpanStart(spellCheckSpan);
|
|
||||||
final int end = editable.getSpanEnd(spellCheckSpan);
|
|
||||||
Log.d(LOG_TAG, "Result sequence=" + suggestionsInfo.getSequence() + " " +
|
|
||||||
editable.subSequence(start, end) +
|
|
||||||
"\t" + (isInDictionary?"IN_DICT" : "NOT_DICT") +
|
|
||||||
"\t" + (looksLikeTypo?"TYPO" : "NOT_TYPO"));
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!isInDictionary && looksLikeTypo) {
|
if (!isInDictionary && looksLikeTypo) {
|
||||||
String[] suggestions = getSuggestions(suggestionsInfo);
|
String[] suggestions = getSuggestions(suggestionsInfo);
|
||||||
if (suggestions.length > 0) {
|
if (suggestions.length > 0) {
|
||||||
@@ -230,13 +194,6 @@ public class SpellChecker implements SpellCheckerSessionListener {
|
|||||||
Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
|
Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
|
||||||
// TODO limit to the word rectangle region
|
// TODO limit to the word rectangle region
|
||||||
mTextView.invalidate();
|
mTextView.invalidate();
|
||||||
|
|
||||||
if (DEBUG_SPELL_CHECK) {
|
|
||||||
String suggestionsString = "";
|
|
||||||
for (String s : suggestions) { suggestionsString += s + "|"; }
|
|
||||||
Log.d(LOG_TAG, " Suggestions for " + sequenceNumber + " " +
|
|
||||||
editable.subSequence(start, end)+ " " + suggestionsString);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
editable.removeSpan(spellCheckSpan);
|
editable.removeSpan(spellCheckSpan);
|
||||||
@@ -246,9 +203,10 @@ public class SpellChecker implements SpellCheckerSessionListener {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private static String[] getSuggestions(SuggestionsInfo suggestionsInfo) {
|
private static String[] getSuggestions(SuggestionsInfo suggestionsInfo) {
|
||||||
|
// A negative suggestion count is possible
|
||||||
final int len = Math.max(0, suggestionsInfo.getSuggestionsCount());
|
final int len = Math.max(0, suggestionsInfo.getSuggestionsCount());
|
||||||
String[] suggestions = new String[len];
|
String[] suggestions = new String[len];
|
||||||
for (int j = 0; j < len; ++j) {
|
for (int j = 0; j < len; j++) {
|
||||||
suggestions[j] = suggestionsInfo.getSuggestionAt(j);
|
suggestions[j] = suggestionsInfo.getSuggestionAt(j);
|
||||||
}
|
}
|
||||||
return suggestions;
|
return suggestions;
|
||||||
|
|||||||
@@ -7492,9 +7492,6 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
|
|||||||
*/
|
*/
|
||||||
protected void onSelectionChanged(int selStart, int selEnd) {
|
protected void onSelectionChanged(int selStart, int selEnd) {
|
||||||
sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_TEXT_SELECTION_CHANGED);
|
sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_TEXT_SELECTION_CHANGED);
|
||||||
if (mSpellChecker != null) {
|
|
||||||
mSpellChecker.onSelectionChanged();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -7553,6 +7550,8 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
|
|||||||
for (int i = 0; i < length; i++) {
|
for (int i = 0; i < length; i++) {
|
||||||
final int s = text.getSpanStart(spans[i]);
|
final int s = text.getSpanStart(spans[i]);
|
||||||
final int e = text.getSpanEnd(spans[i]);
|
final int e = text.getSpanEnd(spans[i]);
|
||||||
|
// Spans that are adjacent to the edited region will be handled in
|
||||||
|
// updateSpellCheckSpans. Result depends on what will be added (space or text)
|
||||||
if (e == start || s == end) break;
|
if (e == start || s == end) break;
|
||||||
text.removeSpan(spans[i]);
|
text.removeSpan(spans[i]);
|
||||||
}
|
}
|
||||||
@@ -7735,12 +7734,8 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (what instanceof SpellCheckSpan) {
|
if (newStart < 0 && what instanceof SpellCheckSpan) {
|
||||||
if (newStart < 0) {
|
getSpellChecker().removeSpellCheckSpan((SpellCheckSpan) what);
|
||||||
getSpellChecker().removeSpellCheckSpan((SpellCheckSpan) what);
|
|
||||||
} else if (oldStart < 0) {
|
|
||||||
getSpellChecker().addSpellCheckSpan((SpellCheckSpan) what);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -7750,8 +7745,8 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
|
|||||||
private void updateSpellCheckSpans(int start, int end) {
|
private void updateSpellCheckSpans(int start, int end) {
|
||||||
if (!isTextEditable() || !isSuggestionsEnabled() || !getSpellChecker().isSessionActive())
|
if (!isTextEditable() || !isSuggestionsEnabled() || !getSpellChecker().isSessionActive())
|
||||||
return;
|
return;
|
||||||
Editable text = (Editable) mText;
|
|
||||||
|
|
||||||
|
Editable text = (Editable) mText;
|
||||||
WordIterator wordIterator = getWordIterator();
|
WordIterator wordIterator = getWordIterator();
|
||||||
wordIterator.setCharSequence(text);
|
wordIterator.setCharSequence(text);
|
||||||
|
|
||||||
@@ -7770,57 +7765,75 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// We need to expand by one character because we want to include the spans that end/start
|
||||||
|
// at position start/end respectively.
|
||||||
|
SpellCheckSpan[] spellCheckSpans = text.getSpans(start - 1, end + 1, SpellCheckSpan.class);
|
||||||
|
SuggestionSpan[] suggestionSpans = text.getSpans(start - 1, end + 1, SuggestionSpan.class);
|
||||||
|
final int numberOfSpellCheckSpans = spellCheckSpans.length;
|
||||||
|
|
||||||
// Iterate over the newly added text and schedule new SpellCheckSpans
|
// Iterate over the newly added text and schedule new SpellCheckSpans
|
||||||
while (wordStart <= end) {
|
while (wordStart <= end) {
|
||||||
if (wordEnd >= start) {
|
if (wordEnd >= start) {
|
||||||
// A word across the interval boundaries must remove boundary edition spans
|
// A new word has been created across the interval boundaries. Remove previous spans
|
||||||
if (wordStart < start && wordEnd > start) {
|
if (wordStart < start && wordEnd > start) {
|
||||||
removeEditionSpansAt(start, text);
|
removeSpansAt(start, spellCheckSpans, text);
|
||||||
|
removeSpansAt(start, suggestionSpans, text);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (wordStart < end && wordEnd > end) {
|
if (wordStart < end && wordEnd > end) {
|
||||||
removeEditionSpansAt(end, text);
|
removeSpansAt(end, spellCheckSpans, text);
|
||||||
|
removeSpansAt(end, suggestionSpans, text);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Do not create new boundary spans if they already exist
|
// Do not create new boundary spans if they already exist
|
||||||
boolean createSpellCheckSpan = true;
|
boolean createSpellCheckSpan = true;
|
||||||
if (wordEnd == start) {
|
if (wordEnd == start) {
|
||||||
SpellCheckSpan[] spellCheckSpans = text.getSpans(start, start,
|
for (int i = 0; i < numberOfSpellCheckSpans; i++) {
|
||||||
SpellCheckSpan.class);
|
final int spanEnd = text.getSpanEnd(spellCheckSpans[i]);
|
||||||
if (spellCheckSpans.length > 0) createSpellCheckSpan = false;
|
if (spanEnd == start) {
|
||||||
|
createSpellCheckSpan = false;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (wordStart == end) {
|
if (wordStart == end) {
|
||||||
SpellCheckSpan[] spellCheckSpans = text.getSpans(end, end,
|
for (int i = 0; i < numberOfSpellCheckSpans; i++) {
|
||||||
SpellCheckSpan.class);
|
final int spanStart = text.getSpanEnd(spellCheckSpans[i]);
|
||||||
if (spellCheckSpans.length > 0) createSpellCheckSpan = false;
|
if (spanStart == end) {
|
||||||
|
createSpellCheckSpan = false;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (createSpellCheckSpan) {
|
if (createSpellCheckSpan) {
|
||||||
text.setSpan(new SpellCheckSpan(), wordStart, wordEnd,
|
mSpellChecker.addSpellCheckSpan(wordStart, wordEnd);
|
||||||
Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// iterate word by word
|
// iterate word by word
|
||||||
wordEnd = wordIterator.following(wordEnd);
|
wordEnd = wordIterator.following(wordEnd);
|
||||||
if (wordEnd == BreakIterator.DONE) return;
|
if (wordEnd == BreakIterator.DONE) break;
|
||||||
wordStart = wordIterator.getBeginning(wordEnd);
|
wordStart = wordIterator.getBeginning(wordEnd);
|
||||||
if (wordStart == BreakIterator.DONE) {
|
if (wordStart == BreakIterator.DONE) {
|
||||||
Log.e(LOG_TAG, "Unable to find word beginning from " + wordEnd + "in " + mText);
|
Log.e(LOG_TAG, "Unable to find word beginning from " + wordEnd + "in " + mText);
|
||||||
return;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
mSpellChecker.spellCheck();
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void removeEditionSpansAt(int offset, Editable text) {
|
private static <T> void removeSpansAt(int offset, T[] spans, Editable text) {
|
||||||
SuggestionSpan[] suggestionSpans = text.getSpans(offset, offset, SuggestionSpan.class);
|
final int length = spans.length;
|
||||||
for (int i = 0; i < suggestionSpans.length; i++) {
|
for (int i = 0; i < length; i++) {
|
||||||
text.removeSpan(suggestionSpans[i]);
|
final T span = spans[i];
|
||||||
}
|
final int start = text.getSpanStart(span);
|
||||||
SpellCheckSpan[] spellCheckSpans = text.getSpans(offset, offset, SpellCheckSpan.class);
|
if (start > offset) continue;
|
||||||
for (int i = 0; i < spellCheckSpans.length; i++) {
|
final int end = text.getSpanEnd(span);
|
||||||
text.removeSpan(spellCheckSpans[i]);
|
if (end < offset) continue;
|
||||||
|
text.removeSpan(span);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -8381,6 +8394,10 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
|
|||||||
boolean selectAllGotFocus = mSelectAllOnFocus && didTouchFocusSelect();
|
boolean selectAllGotFocus = mSelectAllOnFocus && didTouchFocusSelect();
|
||||||
hideControllers();
|
hideControllers();
|
||||||
if (!selectAllGotFocus && mText.length() > 0) {
|
if (!selectAllGotFocus && mText.length() > 0) {
|
||||||
|
if (mSpellChecker != null) {
|
||||||
|
// When the cursor moves, the word that was typed may need spell check
|
||||||
|
mSpellChecker.onSelectionChanged();
|
||||||
|
}
|
||||||
if (isCursorInsideEasyCorrectionSpan()) {
|
if (isCursorInsideEasyCorrectionSpan()) {
|
||||||
showSuggestions();
|
showSuggestions();
|
||||||
} else if (hasInsertionController()) {
|
} else if (hasInsertionController()) {
|
||||||
|
|||||||
Reference in New Issue
Block a user