From e300be9c29b7915450ddb62f2957d312b52cfa32 Mon Sep 17 00:00:00 2001 From: Gilles Debunne Date: Tue, 6 Dec 2011 10:15:56 -0800 Subject: [PATCH] IOOB is Suggestions Bug 5555929 This problem only happens when in landscape extracted text mode. A suggestion pick from the popup window replaces the text in the ExtractedText, when it should do it in the underlying source EditText instead. When the replacement text is longer than the replaced text and is at the end of the text, an IOOB occurs because the ExtractedText was not modified (we now correctly change the source text using replaceText_internal). This is basically an implementation of the TODO comment next to setSpan in TextView. Change-Id: I6575137530e0bb5c9ac7e40cc2bba9c66dc254d2 --- .../inputmethodservice/ExtractEditText.java | 33 +++++++++++++++---- .../InputMethodService.java | 16 +++++++++ core/java/android/widget/TextView.java | 26 ++++++++++++--- 3 files changed, 64 insertions(+), 11 deletions(-) diff --git a/core/java/android/inputmethodservice/ExtractEditText.java b/core/java/android/inputmethodservice/ExtractEditText.java index 72431f3615b68..10c11958610b1 100644 --- a/core/java/android/inputmethodservice/ExtractEditText.java +++ b/core/java/android/inputmethodservice/ExtractEditText.java @@ -158,25 +158,46 @@ public class ExtractEditText extends EditText { } /** - * Delete the range of text, supposedly valid + * {@inheritDoc} * @hide */ @Override protected void deleteText_internal(int start, int end) { - // Do not call the super method. This will change the source TextView instead, which - // will update the ExtractTextView. + // Do not call the super method. + // This will change the source TextView instead, which will update the ExtractTextView. mIME.onExtractedDeleteText(start, end); } /** - * Replaces the range of text [start, end[ by replacement text + * {@inheritDoc} * @hide */ @Override protected void replaceText_internal(int start, int end, CharSequence text) { - // Do not call the super method. This will change the source TextView instead, which - // will update the ExtractTextView. + // Do not call the super method. + // This will change the source TextView instead, which will update the ExtractTextView. mIME.onExtractedReplaceText(start, end, text); } + /** + * {@inheritDoc} + * @hide + */ + @Override + protected void setSpan_internal(Object span, int start, int end, int flags) { + // Do not call the super method. + // This will change the source TextView instead, which will update the ExtractTextView. + mIME.onExtractedSetSpan(span, start, end, flags); + } + + /** + * {@inheritDoc} + * @hide + */ + @Override + protected void setCursorPosition_internal(int start, int end) { + // Do not call the super method. + // This will change the source TextView instead, which will update the ExtractTextView. + mIME.onExtractedSelectionChanged(start, end); + } } diff --git a/core/java/android/inputmethodservice/InputMethodService.java b/core/java/android/inputmethodservice/InputMethodService.java index 02839dbe67704..53cdf21460117 100644 --- a/core/java/android/inputmethodservice/InputMethodService.java +++ b/core/java/android/inputmethodservice/InputMethodService.java @@ -2005,6 +2005,22 @@ public class InputMethodService extends AbstractInputMethodService { } } + /** + * @hide + */ + public void onExtractedSetSpan(Object span, int start, int end, int flags) { + InputConnection conn = getCurrentInputConnection(); + if (conn != null) { + if (!conn.setSelection(start, end)) return; + CharSequence text = conn.getSelectedText(InputConnection.GET_TEXT_WITH_STYLES); + if (text instanceof Spannable) { + ((Spannable) text).setSpan(span, 0, text.length(), flags); + conn.setComposingRegion(start, end); + conn.commitText(text, 1); + } + } + } + /** * This is called when the user has clicked on the extracted text view, * when running in fullscreen mode. The default implementation hides diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java index 79949e5aaf07a..08ad5e133d836 100644 --- a/core/java/android/widget/TextView.java +++ b/core/java/android/widget/TextView.java @@ -9829,7 +9829,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener highlightTextDifferences(mSuggestionInfos[i], spanUnionStart, spanUnionEnd); } - // Add to dictionary item is there a span with the misspelled flag + // Add to dictionary item if there is a span with the misspelled flag if (misspelledSpan != null) { final int misspelledStart = spannable.getSpanStart(misspelledSpan); final int misspelledEnd = spannable.getSpanEnd(misspelledSpan); @@ -9915,7 +9915,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener final int spanStart = editable.getSpanStart(suggestionInfo.suggestionSpan); final int spanEnd = editable.getSpanEnd(suggestionInfo.suggestionSpan); - if (spanStart < 0 || spanEnd < 0) { + if (spanStart < 0 || spanEnd <= spanStart) { // Span has been removed hide(); return; @@ -9982,14 +9982,14 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener // way to assign them a valid range after replacement if (suggestionSpansStarts[i] <= spanStart && suggestionSpansEnds[i] >= spanEnd) { - // TODO The ExtractEditText should restore these spans in the original text - editable.setSpan(suggestionSpans[i], suggestionSpansStarts[i], + setSpan_internal(suggestionSpans[i], suggestionSpansStarts[i], suggestionSpansEnds[i] + lengthDifference, suggestionSpansFlags[i]); } } // Move cursor at the end of the replaced word - Selection.setSelection(editable, spanEnd + lengthDifference); + final int newCursorPosition = spanEnd + lengthDifference; + setCursorPosition_internal(newCursorPosition, newCursorPosition); } hide(); @@ -11462,6 +11462,22 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener ((Editable) mText).replace(start, end, text); } + /** + * Sets a span on the specified range of text + * @hide + */ + protected void setSpan_internal(Object span, int start, int end, int flags) { + ((Editable) mText).setSpan(span, start, end, flags); + } + + /** + * Moves the cursor to the specified offset position in text + * @hide + */ + protected void setCursorPosition_internal(int start, int end) { + Selection.setSelection(((Editable) mText), start, end); + } + @ViewDebug.ExportedProperty(category = "text") private CharSequence mText; private CharSequence mTransformed;