Skip to content

Commit f656030

Browse files
author
Gilles Debunne
committed
Bug 5437846: Crash in SpellChecker
The cached mText field from TextView is not constant overtime. It is especially changed whne the first character of text is added. Retrieve the actual current value everytime we use it. Change-Id: Ie297a3292106879621e54a22e5d03444d442ec96
1 parent 9dcd5af commit f656030

File tree

1 file changed

+46
-41
lines changed

1 file changed

+46
-41
lines changed

core/java/android/widget/SpellChecker.java

Lines changed: 46 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,6 @@ public class SpellChecker implements SpellCheckerSessionListener {
4444
private final static int MAX_SPELL_BATCH_SIZE = 50;
4545

4646
private final TextView mTextView;
47-
private final Editable mText;
4847

4948
final SpellCheckerSession mSpellCheckerSession;
5049
final int mCookie;
@@ -64,7 +63,6 @@ public class SpellChecker implements SpellCheckerSessionListener {
6463

6564
public SpellChecker(TextView textView) {
6665
mTextView = textView;
67-
mText = (Editable) textView.getText();
6866

6967
final TextServicesManager textServicesManager = (TextServicesManager) textView.getContext().
7068
getSystemService(Context.TEXT_SERVICES_MANAGER_SERVICE);
@@ -121,9 +119,9 @@ private int nextSpellCheckSpanIndex() {
121119
return mLength - 1;
122120
}
123121

124-
private void addSpellCheckSpan(int start, int end) {
122+
private void addSpellCheckSpan(Editable editable, int start, int end) {
125123
final int index = nextSpellCheckSpanIndex();
126-
mText.setSpan(mSpellCheckSpans[index], start, end, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
124+
editable.setSpan(mSpellCheckSpans[index], start, end, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
127125
mIds[index] = mSpanSequenceCounter++;
128126
}
129127

@@ -168,8 +166,9 @@ public void spellCheck(int start, int end) {
168166
private void spellCheck() {
169167
if (mSpellCheckerSession == null) return;
170168

171-
final int selectionStart = Selection.getSelectionStart(mText);
172-
final int selectionEnd = Selection.getSelectionEnd(mText);
169+
Editable editable = (Editable) mTextView.getText();
170+
final int selectionStart = Selection.getSelectionStart(editable);
171+
final int selectionEnd = Selection.getSelectionEnd(editable);
173172

174173
TextInfo[] textInfos = new TextInfo[mLength];
175174
int textInfosCount = 0;
@@ -178,12 +177,12 @@ private void spellCheck() {
178177
final SpellCheckSpan spellCheckSpan = mSpellCheckSpans[i];
179178
if (spellCheckSpan.isSpellCheckInProgress()) continue;
180179

181-
final int start = mText.getSpanStart(spellCheckSpan);
182-
final int end = mText.getSpanEnd(spellCheckSpan);
180+
final int start = editable.getSpanStart(spellCheckSpan);
181+
final int end = editable.getSpanEnd(spellCheckSpan);
183182

184183
// Do not check this word if the user is currently editing it
185184
if (start >= 0 && end > start && (selectionEnd < start || selectionStart > end)) {
186-
final String word = mText.subSequence(start, end).toString();
185+
final String word = editable.subSequence(start, end).toString();
187186
spellCheckSpan.setSpellCheckInProgress(true);
188187
textInfos[textInfosCount++] = new TextInfo(word, mCookie, mIds[i]);
189188
}
@@ -202,6 +201,8 @@ private void spellCheck() {
202201

203202
@Override
204203
public void onGetSuggestions(SuggestionsInfo[] results) {
204+
Editable editable = (Editable) mTextView.getText();
205+
205206
for (int i = 0; i < results.length; i++) {
206207
SuggestionsInfo suggestionsInfo = results[i];
207208
if (suggestionsInfo.getCookie() != mCookie) continue;
@@ -217,9 +218,9 @@ public void onGetSuggestions(SuggestionsInfo[] results) {
217218

218219
SpellCheckSpan spellCheckSpan = mSpellCheckSpans[j];
219220
if (!isInDictionary && looksLikeTypo) {
220-
createMisspelledSuggestionSpan(suggestionsInfo, spellCheckSpan);
221+
createMisspelledSuggestionSpan(editable, suggestionsInfo, spellCheckSpan);
221222
}
222-
mText.removeSpan(spellCheckSpan);
223+
editable.removeSpan(spellCheckSpan);
223224
break;
224225
}
225226
}
@@ -234,18 +235,18 @@ public void onGetSuggestions(SuggestionsInfo[] results) {
234235
}
235236
}
236237

237-
private void createMisspelledSuggestionSpan(SuggestionsInfo suggestionsInfo,
238-
SpellCheckSpan spellCheckSpan) {
239-
final int start = mText.getSpanStart(spellCheckSpan);
240-
final int end = mText.getSpanEnd(spellCheckSpan);
238+
private void createMisspelledSuggestionSpan(Editable editable,
239+
SuggestionsInfo suggestionsInfo, SpellCheckSpan spellCheckSpan) {
240+
final int start = editable.getSpanStart(spellCheckSpan);
241+
final int end = editable.getSpanEnd(spellCheckSpan);
241242

242243
// Other suggestion spans may exist on that region, with identical suggestions, filter
243244
// them out to avoid duplicates. First, filter suggestion spans on that exact region.
244-
SuggestionSpan[] suggestionSpans = mText.getSpans(start, end, SuggestionSpan.class);
245+
SuggestionSpan[] suggestionSpans = editable.getSpans(start, end, SuggestionSpan.class);
245246
final int length = suggestionSpans.length;
246247
for (int i = 0; i < length; i++) {
247-
final int spanStart = mText.getSpanStart(suggestionSpans[i]);
248-
final int spanEnd = mText.getSpanEnd(suggestionSpans[i]);
248+
final int spanStart = editable.getSpanStart(suggestionSpans[i]);
249+
final int spanEnd = editable.getSpanEnd(suggestionSpans[i]);
249250
if (spanStart != start || spanEnd != end) {
250251
suggestionSpans[i] = null;
251252
break;
@@ -293,7 +294,7 @@ private void createMisspelledSuggestionSpan(SuggestionsInfo suggestionsInfo,
293294

294295
SuggestionSpan suggestionSpan = new SuggestionSpan(mTextView.getContext(), suggestions,
295296
SuggestionSpan.FLAG_EASY_CORRECT | SuggestionSpan.FLAG_MISSPELLED);
296-
mText.setSpan(suggestionSpan, start, end, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
297+
editable.setSpan(suggestionSpan, start, end, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
297298

298299
// TODO limit to the word rectangle region
299300
mTextView.invalidate();
@@ -304,22 +305,24 @@ private class SpellParser {
304305
private Object mRange = new Object();
305306

306307
public void init(int start, int end) {
307-
mText.setSpan(mRange, start, end, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
308+
((Editable) mTextView.getText()).setSpan(mRange, start, end,
309+
Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
308310
}
309311

310312
public void close() {
311-
mText.removeSpan(mRange);
313+
((Editable) mTextView.getText()).removeSpan(mRange);
312314
}
313315

314316
public boolean isDone() {
315-
return mText.getSpanStart(mRange) < 0;
317+
return ((Editable) mTextView.getText()).getSpanStart(mRange) < 0;
316318
}
317319

318320
public void parse() {
321+
Editable editable = (Editable) mTextView.getText();
319322
// Iterate over the newly added text and schedule new SpellCheckSpans
320-
final int start = mText.getSpanStart(mRange);
321-
final int end = mText.getSpanEnd(mRange);
322-
mWordIterator.setCharSequence(mText, start, end);
323+
final int start = editable.getSpanStart(mRange);
324+
final int end = editable.getSpanEnd(mRange);
325+
mWordIterator.setCharSequence(editable, start, end);
323326

324327
// Move back to the beginning of the current word, if any
325328
int wordStart = mWordIterator.preceding(start);
@@ -333,14 +336,16 @@ public void parse() {
333336
wordEnd = mWordIterator.getEnd(wordStart);
334337
}
335338
if (wordEnd == BreakIterator.DONE) {
336-
mText.removeSpan(mRange);
339+
editable.removeSpan(mRange);
337340
return;
338341
}
339342

340343
// We need to expand by one character because we want to include the spans that
341344
// end/start at position start/end respectively.
342-
SpellCheckSpan[] spellCheckSpans = mText.getSpans(start-1, end+1, SpellCheckSpan.class);
343-
SuggestionSpan[] suggestionSpans = mText.getSpans(start-1, end+1, SuggestionSpan.class);
345+
SpellCheckSpan[] spellCheckSpans = editable.getSpans(start - 1, end + 1,
346+
SpellCheckSpan.class);
347+
SuggestionSpan[] suggestionSpans = editable.getSpans(start - 1, end + 1,
348+
SuggestionSpan.class);
344349

345350
int nbWordsChecked = 0;
346351
boolean scheduleOtherSpellCheck = false;
@@ -350,20 +355,20 @@ public void parse() {
350355
// A new word has been created across the interval boundaries with this edit.
351356
// Previous spans (ended on start / started on end) removed, not valid anymore
352357
if (wordStart < start && wordEnd > start) {
353-
removeSpansAt(start, spellCheckSpans);
354-
removeSpansAt(start, suggestionSpans);
358+
removeSpansAt(editable, start, spellCheckSpans);
359+
removeSpansAt(editable, start, suggestionSpans);
355360
}
356361

357362
if (wordStart < end && wordEnd > end) {
358-
removeSpansAt(end, spellCheckSpans);
359-
removeSpansAt(end, suggestionSpans);
363+
removeSpansAt(editable, end, spellCheckSpans);
364+
removeSpansAt(editable, end, suggestionSpans);
360365
}
361366

362367
// Do not create new boundary spans if they already exist
363368
boolean createSpellCheckSpan = true;
364369
if (wordEnd == start) {
365370
for (int i = 0; i < spellCheckSpans.length; i++) {
366-
final int spanEnd = mText.getSpanEnd(spellCheckSpans[i]);
371+
final int spanEnd = editable.getSpanEnd(spellCheckSpans[i]);
367372
if (spanEnd == start) {
368373
createSpellCheckSpan = false;
369374
break;
@@ -373,7 +378,7 @@ public void parse() {
373378

374379
if (wordStart == end) {
375380
for (int i = 0; i < spellCheckSpans.length; i++) {
376-
final int spanStart = mText.getSpanStart(spellCheckSpans[i]);
381+
final int spanStart = editable.getSpanStart(spellCheckSpans[i]);
377382
if (spanStart == end) {
378383
createSpellCheckSpan = false;
379384
break;
@@ -386,7 +391,7 @@ public void parse() {
386391
scheduleOtherSpellCheck = true;
387392
break;
388393
}
389-
addSpellCheckSpan(wordStart, wordEnd);
394+
addSpellCheckSpan(editable, wordStart, wordEnd);
390395
nbWordsChecked++;
391396
}
392397
}
@@ -401,23 +406,23 @@ public void parse() {
401406
}
402407

403408
if (scheduleOtherSpellCheck) {
404-
mText.setSpan(mRange, wordStart, end, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
409+
editable.setSpan(mRange, wordStart, end, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
405410
} else {
406-
mText.removeSpan(mRange);
411+
editable.removeSpan(mRange);
407412
}
408413

409414
spellCheck();
410415
}
411416

412-
private <T> void removeSpansAt(int offset, T[] spans) {
417+
private <T> void removeSpansAt(Editable editable, int offset, T[] spans) {
413418
final int length = spans.length;
414419
for (int i = 0; i < length; i++) {
415420
final T span = spans[i];
416-
final int start = mText.getSpanStart(span);
421+
final int start = editable.getSpanStart(span);
417422
if (start > offset) continue;
418-
final int end = mText.getSpanEnd(span);
423+
final int end = editable.getSpanEnd(span);
419424
if (end < offset) continue;
420-
mText.removeSpan(span);
425+
editable.removeSpan(span);
421426
}
422427
}
423428
}

0 commit comments

Comments
 (0)