|
|
|
|
@@ -21,6 +21,7 @@ import android.content.ClipData;
|
|
|
|
|
import android.content.ClipData.Item;
|
|
|
|
|
import android.content.ClipboardManager;
|
|
|
|
|
import android.content.Context;
|
|
|
|
|
import android.content.Intent;
|
|
|
|
|
import android.content.pm.PackageManager;
|
|
|
|
|
import android.content.res.ColorStateList;
|
|
|
|
|
import android.content.res.Resources;
|
|
|
|
|
@@ -42,6 +43,7 @@ import android.os.Message;
|
|
|
|
|
import android.os.Parcel;
|
|
|
|
|
import android.os.Parcelable;
|
|
|
|
|
import android.os.SystemClock;
|
|
|
|
|
import android.provider.Settings;
|
|
|
|
|
import android.text.BoringLayout;
|
|
|
|
|
import android.text.DynamicLayout;
|
|
|
|
|
import android.text.Editable;
|
|
|
|
|
@@ -9417,7 +9419,6 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
|
|
|
|
|
|
|
|
|
|
private class SuggestionsPopupWindow extends PinnedPopupWindow implements OnItemClickListener {
|
|
|
|
|
private static final int MAX_NUMBER_SUGGESTIONS = SuggestionSpan.SUGGESTIONS_MAX_SIZE;
|
|
|
|
|
private static final int NO_SUGGESTIONS = -1;
|
|
|
|
|
private static final float AVERAGE_HIGHLIGHTS_PER_SUGGESTION = 1.4f;
|
|
|
|
|
private WordIterator mSuggestionWordIterator;
|
|
|
|
|
private TextAppearanceSpan[] mHighlightSpans = new TextAppearanceSpan
|
|
|
|
|
@@ -9472,9 +9473,9 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
|
|
|
|
|
listView.setOnItemClickListener(this);
|
|
|
|
|
mContentView = listView;
|
|
|
|
|
|
|
|
|
|
// Inflate the suggestion items once and for all.
|
|
|
|
|
mSuggestionInfos = new SuggestionInfo[MAX_NUMBER_SUGGESTIONS];
|
|
|
|
|
for (int i = 0; i < MAX_NUMBER_SUGGESTIONS; i++) {
|
|
|
|
|
// Inflate the suggestion items once and for all. +1 for add to dictionary
|
|
|
|
|
mSuggestionInfos = new SuggestionInfo[MAX_NUMBER_SUGGESTIONS + 1];
|
|
|
|
|
for (int i = 0; i < MAX_NUMBER_SUGGESTIONS + 1; i++) {
|
|
|
|
|
mSuggestionInfos[i] = new SuggestionInfo();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
@@ -9485,6 +9486,15 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
|
|
|
|
|
SuggestionSpan suggestionSpan; // the SuggestionSpan that this TextView represents
|
|
|
|
|
int suggestionIndex; // the index of the suggestion inside suggestionSpan
|
|
|
|
|
SpannableStringBuilder text = new SpannableStringBuilder();
|
|
|
|
|
|
|
|
|
|
void removeMisspelledFlag() {
|
|
|
|
|
int suggestionSpanFlags = suggestionSpan.getFlags();
|
|
|
|
|
if ((suggestionSpanFlags & SuggestionSpan.FLAG_MISSPELLED) > 0) {
|
|
|
|
|
suggestionSpanFlags &= ~(SuggestionSpan.FLAG_MISSPELLED);
|
|
|
|
|
suggestionSpanFlags &= ~(SuggestionSpan.FLAG_EASY_CORRECT);
|
|
|
|
|
suggestionSpan.setFlags(suggestionSpanFlags);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private class SuggestionAdapter extends BaseAdapter {
|
|
|
|
|
@@ -9623,6 +9633,8 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
|
|
|
|
|
int spanUnionStart = mText.length();
|
|
|
|
|
int spanUnionEnd = 0;
|
|
|
|
|
|
|
|
|
|
SuggestionSpan misspelledSpan = null;
|
|
|
|
|
|
|
|
|
|
for (int spanIndex = 0; spanIndex < nbSpans; spanIndex++) {
|
|
|
|
|
SuggestionSpan suggestionSpan = suggestionSpans[spanIndex];
|
|
|
|
|
final int spanStart = spannable.getSpanStart(suggestionSpan);
|
|
|
|
|
@@ -9630,6 +9642,10 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
|
|
|
|
|
spanUnionStart = Math.min(spanStart, spanUnionStart);
|
|
|
|
|
spanUnionEnd = Math.max(spanEnd, spanUnionEnd);
|
|
|
|
|
|
|
|
|
|
if ((suggestionSpan.getFlags() & SuggestionSpan.FLAG_MISSPELLED) != 0) {
|
|
|
|
|
misspelledSpan = suggestionSpan;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
String[] suggestions = suggestionSpan.getSuggestions();
|
|
|
|
|
int nbSuggestions = suggestions.length;
|
|
|
|
|
for (int suggestionIndex = 0; suggestionIndex < nbSuggestions; suggestionIndex++) {
|
|
|
|
|
@@ -9638,7 +9654,8 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
|
|
|
|
|
suggestionInfo.spanEnd = spanEnd;
|
|
|
|
|
suggestionInfo.suggestionSpan = suggestionSpan;
|
|
|
|
|
suggestionInfo.suggestionIndex = suggestionIndex;
|
|
|
|
|
suggestionInfo.text = new SpannableStringBuilder(suggestions[suggestionIndex]);
|
|
|
|
|
suggestionInfo.text.replace(0, suggestionInfo.text.length(),
|
|
|
|
|
suggestions[suggestionIndex]);
|
|
|
|
|
|
|
|
|
|
mNumberOfSuggestions++;
|
|
|
|
|
if (mNumberOfSuggestions == MAX_NUMBER_SUGGESTIONS) {
|
|
|
|
|
@@ -9649,41 +9666,38 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
for (int i = 0; i < mNumberOfSuggestions; i++) {
|
|
|
|
|
highlightTextDifferences(mSuggestionInfos[i], spanUnionStart, spanUnionEnd);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (misspelledSpan != null) {
|
|
|
|
|
final int misspelledStart = spannable.getSpanStart(misspelledSpan);
|
|
|
|
|
final int misspelledEnd = spannable.getSpanEnd(misspelledSpan);
|
|
|
|
|
if (misspelledStart >= 0 && misspelledEnd > misspelledStart) {
|
|
|
|
|
SuggestionInfo suggestionInfo = mSuggestionInfos[mNumberOfSuggestions];
|
|
|
|
|
suggestionInfo.spanStart = misspelledStart;
|
|
|
|
|
suggestionInfo.spanEnd = misspelledEnd;
|
|
|
|
|
suggestionInfo.suggestionSpan = misspelledSpan;
|
|
|
|
|
suggestionInfo.suggestionIndex = -1;
|
|
|
|
|
suggestionInfo.text.replace(0, suggestionInfo.text.length(),
|
|
|
|
|
getContext().getString(com.android.internal.R.string.addToDictionary));
|
|
|
|
|
|
|
|
|
|
mNumberOfSuggestions++;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (mNumberOfSuggestions == 0) return false;
|
|
|
|
|
|
|
|
|
|
if (mSuggestionRangeSpan == null) mSuggestionRangeSpan =
|
|
|
|
|
new SuggestionRangeSpan(mHighlightColor);
|
|
|
|
|
|
|
|
|
|
((Editable) mText).setSpan(mSuggestionRangeSpan, spanUnionStart, spanUnionEnd,
|
|
|
|
|
Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
|
|
|
|
|
|
|
|
|
|
for (int i = 0; i < mNumberOfSuggestions; i++) {
|
|
|
|
|
highlightTextDifferences(mSuggestionInfos[i], spanUnionStart, spanUnionEnd);
|
|
|
|
|
}
|
|
|
|
|
mSuggestionsAdapter.notifyDataSetChanged();
|
|
|
|
|
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private void onDictionarySuggestionsReceived(String[] suggestions) {
|
|
|
|
|
if (suggestions.length == 0) {
|
|
|
|
|
// TODO Actual implementation of this feature
|
|
|
|
|
suggestions = new String[] {"Add to dictionary"};
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
WordIterator wordIterator = getWordIterator();
|
|
|
|
|
wordIterator.setCharSequence(mText);
|
|
|
|
|
|
|
|
|
|
final int pos = getSelectionStart();
|
|
|
|
|
int wordStart = wordIterator.getBeginning(pos);
|
|
|
|
|
int wordEnd = wordIterator.getEnd(pos);
|
|
|
|
|
|
|
|
|
|
SuggestionSpan suggestionSpan = new SuggestionSpan(getContext(), suggestions, 0);
|
|
|
|
|
((Editable) mText).setSpan(suggestionSpan, wordStart, wordEnd,
|
|
|
|
|
Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
|
|
|
|
|
show();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private long[] getWordLimits(CharSequence text) {
|
|
|
|
|
// TODO locale for mSuggestionWordIterator
|
|
|
|
|
if (mSuggestionWordIterator == null) mSuggestionWordIterator = new WordIterator();
|
|
|
|
|
@@ -9836,10 +9850,19 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
|
|
|
|
|
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
|
|
|
|
|
if (view instanceof TextView) {
|
|
|
|
|
TextView textView = (TextView) view;
|
|
|
|
|
|
|
|
|
|
SuggestionInfo suggestionInfo = mSuggestionInfos[position];
|
|
|
|
|
final int spanStart = suggestionInfo.spanStart;
|
|
|
|
|
final int spanEnd = suggestionInfo.spanEnd;
|
|
|
|
|
if (spanStart != NO_SUGGESTIONS) {
|
|
|
|
|
final String originalText = mText.subSequence(spanStart, spanEnd).toString();
|
|
|
|
|
|
|
|
|
|
if (suggestionInfo.suggestionIndex < 0) {
|
|
|
|
|
Intent intent = new Intent(Settings.ACTION_USER_DICTIONARY_INSERT);
|
|
|
|
|
intent.putExtra("word", originalText);
|
|
|
|
|
intent.setFlags(intent.getFlags() | Intent.FLAG_ACTIVITY_NEW_TASK);
|
|
|
|
|
getContext().startActivity(intent);
|
|
|
|
|
suggestionInfo.removeMisspelledFlag();
|
|
|
|
|
} else {
|
|
|
|
|
// SuggestionSpans are removed by replace: save them before
|
|
|
|
|
Editable editable = (Editable) mText;
|
|
|
|
|
SuggestionSpan[] suggestionSpans = editable.getSpans(spanStart, spanEnd,
|
|
|
|
|
@@ -9859,17 +9882,9 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
|
|
|
|
|
final int suggestionEnd = suggestionInfo.suggestionEnd;
|
|
|
|
|
final String suggestion = textView.getText().subSequence(
|
|
|
|
|
suggestionStart, suggestionEnd).toString();
|
|
|
|
|
final String originalText = mText.subSequence(spanStart, spanEnd).toString();
|
|
|
|
|
editable.replace(spanStart, spanEnd, suggestion);
|
|
|
|
|
|
|
|
|
|
// A replacement on a misspelled text removes the misspelled flag.
|
|
|
|
|
// TODO restore the flag if the misspelled word is selected back?
|
|
|
|
|
int suggestionSpanFlags = suggestionInfo.suggestionSpan.getFlags();
|
|
|
|
|
if ((suggestionSpanFlags & SuggestionSpan.FLAG_MISSPELLED) > 0) {
|
|
|
|
|
suggestionSpanFlags &= ~(SuggestionSpan.FLAG_MISSPELLED);
|
|
|
|
|
suggestionSpanFlags &= ~(SuggestionSpan.FLAG_EASY_CORRECT);
|
|
|
|
|
suggestionInfo.suggestionSpan.setFlags(suggestionSpanFlags);
|
|
|
|
|
}
|
|
|
|
|
suggestionInfo.removeMisspelledFlag();
|
|
|
|
|
|
|
|
|
|
// Notify source IME of the suggestion pick. Do this before swaping texts.
|
|
|
|
|
if (!TextUtils.isEmpty(
|
|
|
|
|
@@ -9916,12 +9931,6 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
|
|
|
|
|
return mSuggestionsPopupWindow != null && mSuggestionsPopupWindow.isShowing();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void onDictionarySuggestionsReceived(String[] suggestions) {
|
|
|
|
|
if (mSuggestionsPopupWindow != null) {
|
|
|
|
|
mSuggestionsPopupWindow.onDictionarySuggestionsReceived(suggestions);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Return whether or not suggestions are enabled on this TextView. The suggestions are generated
|
|
|
|
|
* by the IME or by the spell checker as the user types. This is done by adding
|
|
|
|
|
|