Merge "Fix "out of bound exception" when the span has prefix."
This commit is contained in:
committed by
Android (Google) Code Review
commit
0046199dab
@@ -9541,10 +9541,6 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
|
|||||||
|
|
||||||
private class SuggestionsPopupWindow extends PinnedPopupWindow implements OnItemClickListener {
|
private class SuggestionsPopupWindow extends PinnedPopupWindow implements OnItemClickListener {
|
||||||
private static final int MAX_NUMBER_SUGGESTIONS = SuggestionSpan.SUGGESTIONS_MAX_SIZE;
|
private static final int MAX_NUMBER_SUGGESTIONS = SuggestionSpan.SUGGESTIONS_MAX_SIZE;
|
||||||
private static final float AVERAGE_HIGHLIGHTS_PER_SUGGESTION = 1.4f;
|
|
||||||
private WordIterator mSuggestionWordIterator;
|
|
||||||
private TextAppearanceSpan[] mHighlightSpans = new TextAppearanceSpan
|
|
||||||
[(int) (AVERAGE_HIGHLIGHTS_PER_SUGGESTION * MAX_NUMBER_SUGGESTIONS)];
|
|
||||||
private SuggestionInfo[] mSuggestionInfos;
|
private SuggestionInfo[] mSuggestionInfos;
|
||||||
private int mNumberOfSuggestions;
|
private int mNumberOfSuggestions;
|
||||||
private boolean mCursorWasVisibleBeforeSuggestions;
|
private boolean mCursorWasVisibleBeforeSuggestions;
|
||||||
@@ -9571,10 +9567,6 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
|
|||||||
}
|
}
|
||||||
|
|
||||||
public SuggestionsPopupWindow() {
|
public SuggestionsPopupWindow() {
|
||||||
for (int i = 0; i < mHighlightSpans.length; i++) {
|
|
||||||
mHighlightSpans[i] = new TextAppearanceSpan(mContext,
|
|
||||||
android.R.style.TextAppearance_SuggestionHighlight);
|
|
||||||
}
|
|
||||||
mCursorWasVisibleBeforeSuggestions = mCursorVisible;
|
mCursorWasVisibleBeforeSuggestions = mCursorVisible;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -9608,6 +9600,8 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
|
|||||||
SuggestionSpan suggestionSpan; // the SuggestionSpan that this TextView represents
|
SuggestionSpan suggestionSpan; // the SuggestionSpan that this TextView represents
|
||||||
int suggestionIndex; // the index of the suggestion inside suggestionSpan
|
int suggestionIndex; // the index of the suggestion inside suggestionSpan
|
||||||
SpannableStringBuilder text = new SpannableStringBuilder();
|
SpannableStringBuilder text = new SpannableStringBuilder();
|
||||||
|
TextAppearanceSpan highlightSpan = new TextAppearanceSpan(mContext,
|
||||||
|
android.R.style.TextAppearance_SuggestionHighlight);
|
||||||
|
|
||||||
void removeMisspelledFlag() {
|
void removeMisspelledFlag() {
|
||||||
int suggestionSpanFlags = suggestionSpan.getFlags();
|
int suggestionSpanFlags = suggestionSpan.getFlags();
|
||||||
@@ -9820,152 +9814,23 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
private long[] getWordLimits(CharSequence text) {
|
private void highlightTextDifferences(SuggestionInfo suggestionInfo, int unionStart,
|
||||||
// TODO locale for mSuggestionWordIterator
|
int unionEnd) {
|
||||||
if (mSuggestionWordIterator == null) mSuggestionWordIterator = new WordIterator();
|
|
||||||
mSuggestionWordIterator.setCharSequence(text);
|
|
||||||
|
|
||||||
// First pass will simply count the number of words to be able to create an array
|
|
||||||
// Not too expensive since previous break positions are cached by the BreakIterator
|
|
||||||
int nbWords = 0;
|
|
||||||
int position = mSuggestionWordIterator.following(0);
|
|
||||||
while (position != BreakIterator.DONE) {
|
|
||||||
nbWords++;
|
|
||||||
position = mSuggestionWordIterator.following(position);
|
|
||||||
}
|
|
||||||
|
|
||||||
int index = 0;
|
|
||||||
long[] result = new long[nbWords];
|
|
||||||
|
|
||||||
position = mSuggestionWordIterator.following(0);
|
|
||||||
while (position != BreakIterator.DONE) {
|
|
||||||
int wordStart = mSuggestionWordIterator.getBeginning(position);
|
|
||||||
result[index++] = packRangeInLong(wordStart, position);
|
|
||||||
position = mSuggestionWordIterator.following(position);
|
|
||||||
}
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
private TextAppearanceSpan highlightSpan(int index) {
|
|
||||||
final int length = mHighlightSpans.length;
|
|
||||||
if (index < length) {
|
|
||||||
return mHighlightSpans[index];
|
|
||||||
}
|
|
||||||
|
|
||||||
// Assumes indexes are requested in sequence: simply append one more item
|
|
||||||
TextAppearanceSpan[] newArray = new TextAppearanceSpan[length + 1];
|
|
||||||
System.arraycopy(mHighlightSpans, 0, newArray, 0, length);
|
|
||||||
TextAppearanceSpan highlightSpan = new TextAppearanceSpan(mContext,
|
|
||||||
android.R.style.TextAppearance_SuggestionHighlight);
|
|
||||||
newArray[length] = highlightSpan;
|
|
||||||
mHighlightSpans = newArray;
|
|
||||||
return highlightSpan;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void highlightTextDifferences(SuggestionInfo suggestionInfo,
|
|
||||||
int unionStart, int unionEnd) {
|
|
||||||
final int spanStart = suggestionInfo.spanStart;
|
final int spanStart = suggestionInfo.spanStart;
|
||||||
final int spanEnd = suggestionInfo.spanEnd;
|
final int spanEnd = suggestionInfo.spanEnd;
|
||||||
|
|
||||||
// Remove all text formating by converting to Strings
|
// Adjust the start/end of the suggestion span
|
||||||
final String text = suggestionInfo.text.toString();
|
suggestionInfo.suggestionStart = spanStart - unionStart;
|
||||||
final String sourceText = mText.subSequence(spanStart, spanEnd).toString();
|
suggestionInfo.suggestionEnd = suggestionInfo.suggestionStart
|
||||||
|
+ suggestionInfo.text.length();
|
||||||
|
|
||||||
|
suggestionInfo.text.clearSpans();
|
||||||
|
suggestionInfo.text.setSpan(suggestionInfo.highlightSpan, 0,
|
||||||
|
suggestionInfo.text.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
|
||||||
|
|
||||||
long[] sourceWordLimits = getWordLimits(sourceText);
|
// Add the text before and after the span.
|
||||||
long[] wordLimits = getWordLimits(text);
|
suggestionInfo.text.insert(0, mText.subSequence(unionStart, spanStart).toString());
|
||||||
|
suggestionInfo.text.append(mText.subSequence(spanEnd, unionEnd).toString());
|
||||||
SpannableStringBuilder ssb = new SpannableStringBuilder();
|
|
||||||
// span [spanStart, spanEnd] is included in union [spanUnionStart, int spanUnionEnd]
|
|
||||||
// The final result is made of 3 parts: the text before, between and after the span
|
|
||||||
// This is the text before, provided for context
|
|
||||||
ssb.append(mText.subSequence(unionStart, spanStart).toString());
|
|
||||||
|
|
||||||
// shift is used to offset spans positions wrt span's beginning
|
|
||||||
final int shift = spanStart - unionStart;
|
|
||||||
suggestionInfo.suggestionStart = shift;
|
|
||||||
suggestionInfo.suggestionEnd = shift + text.length();
|
|
||||||
|
|
||||||
// This is the actual suggestion text, which will be highlighted by the following code
|
|
||||||
ssb.append(text);
|
|
||||||
|
|
||||||
String[] words = new String[wordLimits.length];
|
|
||||||
for (int i = 0; i < wordLimits.length; i++) {
|
|
||||||
int wordStart = extractRangeStartFromLong(wordLimits[i]);
|
|
||||||
int wordEnd = extractRangeEndFromLong(wordLimits[i]);
|
|
||||||
words[i] = text.substring(wordStart, wordEnd);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Highlighted word algorithm is based on word matching between source and text
|
|
||||||
// Matching words are found from left to right. TODO: change for RTL languages
|
|
||||||
// Characters between matching words are highlighted
|
|
||||||
int previousCommonWordIndex = -1;
|
|
||||||
int nbHighlightSpans = 0;
|
|
||||||
for (int i = 0; i < sourceWordLimits.length; i++) {
|
|
||||||
int wordStart = extractRangeStartFromLong(sourceWordLimits[i]);
|
|
||||||
int wordEnd = extractRangeEndFromLong(sourceWordLimits[i]);
|
|
||||||
String sourceWord = sourceText.substring(wordStart, wordEnd);
|
|
||||||
|
|
||||||
for (int j = previousCommonWordIndex + 1; j < words.length; j++) {
|
|
||||||
if (sourceWord.equals(words[j])) {
|
|
||||||
if (j != previousCommonWordIndex + 1) {
|
|
||||||
int firstDifferentPosition = previousCommonWordIndex < 0 ? 0 :
|
|
||||||
extractRangeEndFromLong(wordLimits[previousCommonWordIndex]);
|
|
||||||
int lastDifferentPosition = extractRangeStartFromLong(wordLimits[j]);
|
|
||||||
ssb.setSpan(highlightSpan(nbHighlightSpans++),
|
|
||||||
shift + firstDifferentPosition, shift + lastDifferentPosition,
|
|
||||||
Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
|
|
||||||
} else {
|
|
||||||
// Compare characters between words
|
|
||||||
int previousSourceWordEnd = i == 0 ? 0 :
|
|
||||||
extractRangeEndFromLong(sourceWordLimits[i - 1]);
|
|
||||||
int sourceWordStart = extractRangeStartFromLong(sourceWordLimits[i]);
|
|
||||||
String sourceSpaces = sourceText.substring(previousSourceWordEnd,
|
|
||||||
sourceWordStart);
|
|
||||||
|
|
||||||
int previousWordEnd = j == 0 ? 0 :
|
|
||||||
extractRangeEndFromLong(wordLimits[j - 1]);
|
|
||||||
int currentWordStart = extractRangeStartFromLong(wordLimits[j]);
|
|
||||||
String textSpaces = text.substring(previousWordEnd, currentWordStart);
|
|
||||||
|
|
||||||
if (!sourceSpaces.equals(textSpaces)) {
|
|
||||||
ssb.setSpan(highlightSpan(nbHighlightSpans++),
|
|
||||||
shift + previousWordEnd, shift + currentWordStart,
|
|
||||||
Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
previousCommonWordIndex = j;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Finally, compare ends of Strings
|
|
||||||
if (previousCommonWordIndex < words.length - 1) {
|
|
||||||
int firstDifferentPosition = previousCommonWordIndex < 0 ? 0 :
|
|
||||||
extractRangeEndFromLong(wordLimits[previousCommonWordIndex]);
|
|
||||||
int lastDifferentPosition = text.length();
|
|
||||||
ssb.setSpan(highlightSpan(nbHighlightSpans++),
|
|
||||||
shift + firstDifferentPosition, shift + lastDifferentPosition,
|
|
||||||
Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
|
|
||||||
} else {
|
|
||||||
int lastSourceWordEnd = sourceWordLimits.length == 0 ? 0 :
|
|
||||||
extractRangeEndFromLong(sourceWordLimits[sourceWordLimits.length - 1]);
|
|
||||||
String sourceSpaces = sourceText.substring(lastSourceWordEnd, sourceText.length());
|
|
||||||
|
|
||||||
int lastCommonTextWordEnd = previousCommonWordIndex < 0 ? 0 :
|
|
||||||
extractRangeEndFromLong(wordLimits[previousCommonWordIndex]);
|
|
||||||
String textSpaces = text.substring(lastCommonTextWordEnd, text.length());
|
|
||||||
|
|
||||||
if (!sourceSpaces.equals(textSpaces) && textSpaces.length() > 0) {
|
|
||||||
ssb.setSpan(highlightSpan(nbHighlightSpans++),
|
|
||||||
shift + lastCommonTextWordEnd, shift + text.length(),
|
|
||||||
Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Final part, text after the current suggestion range.
|
|
||||||
ssb.append(mText.subSequence(spanEnd, unionEnd).toString());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|||||||
Reference in New Issue
Block a user