Merge "Spell checher's language synced with keyboard."
This commit is contained in:
committed by
Android (Google) Code Review
commit
b9e78d96b7
@@ -197,16 +197,18 @@ public class ArrowKeyMovementMethod extends BaseMovementMethod implements Moveme
|
||||
@Override
|
||||
protected boolean leftWord(TextView widget, Spannable buffer) {
|
||||
final int selectionEnd = widget.getSelectionEnd();
|
||||
mWordIterator.setCharSequence(buffer, selectionEnd, selectionEnd);
|
||||
return Selection.moveToPreceding(buffer, mWordIterator, isSelecting(buffer));
|
||||
final WordIterator wordIterator = widget.getWordIterator();
|
||||
wordIterator.setCharSequence(buffer, selectionEnd, selectionEnd);
|
||||
return Selection.moveToPreceding(buffer, wordIterator, isSelecting(buffer));
|
||||
}
|
||||
|
||||
/** {@hide} */
|
||||
@Override
|
||||
protected boolean rightWord(TextView widget, Spannable buffer) {
|
||||
final int selectionEnd = widget.getSelectionEnd();
|
||||
mWordIterator.setCharSequence(buffer, selectionEnd, selectionEnd);
|
||||
return Selection.moveToFollowing(buffer, mWordIterator, isSelecting(buffer));
|
||||
final WordIterator wordIterator = widget.getWordIterator();
|
||||
wordIterator.setCharSequence(buffer, selectionEnd, selectionEnd);
|
||||
return Selection.moveToFollowing(buffer, wordIterator, isSelecting(buffer));
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -322,8 +324,6 @@ public class ArrowKeyMovementMethod extends BaseMovementMethod implements Moveme
|
||||
return sInstance;
|
||||
}
|
||||
|
||||
private WordIterator mWordIterator = new WordIterator();
|
||||
|
||||
private static final Object LAST_TAP_DOWN = new Object();
|
||||
private static ArrowKeyMovementMethod sInstance;
|
||||
}
|
||||
|
||||
@@ -32,6 +32,7 @@ import android.view.textservice.TextServicesManager;
|
||||
import com.android.internal.util.ArrayUtils;
|
||||
|
||||
import java.text.BreakIterator;
|
||||
import java.util.Locale;
|
||||
|
||||
|
||||
/**
|
||||
@@ -45,7 +46,7 @@ public class SpellChecker implements SpellCheckerSessionListener {
|
||||
|
||||
private final TextView mTextView;
|
||||
|
||||
final SpellCheckerSession mSpellCheckerSession;
|
||||
SpellCheckerSession mSpellCheckerSession;
|
||||
final int mCookie;
|
||||
|
||||
// Paired arrays for the (id, spellCheckSpan) pair. A negative id means the associated
|
||||
@@ -61,23 +62,54 @@ public class SpellChecker implements SpellCheckerSessionListener {
|
||||
|
||||
private int mSpanSequenceCounter = 0;
|
||||
|
||||
private Locale mCurrentLocale;
|
||||
|
||||
// Shared by all SpellParsers. Cannot be shared with TextView since it may be used
|
||||
// concurrently due to the asynchronous nature of onGetSuggestions.
|
||||
private WordIterator mWordIterator;
|
||||
|
||||
public SpellChecker(TextView textView) {
|
||||
mTextView = textView;
|
||||
|
||||
final TextServicesManager textServicesManager = (TextServicesManager) textView.getContext().
|
||||
getSystemService(Context.TEXT_SERVICES_MANAGER_SERVICE);
|
||||
mSpellCheckerSession = textServicesManager.newSpellCheckerSession(
|
||||
null /* not currently used by the textServicesManager */,
|
||||
null /* null locale means use the languages defined in Settings
|
||||
if referToSpellCheckerLanguageSettings is true */,
|
||||
this, true /* means use the languages defined in Settings */);
|
||||
mCookie = hashCode();
|
||||
|
||||
// Arbitrary: 4 simultaneous spell check spans. Will automatically double size on demand
|
||||
// Arbitrary: these arrays will automatically double their sizes on demand
|
||||
final int size = ArrayUtils.idealObjectArraySize(1);
|
||||
mIds = new int[size];
|
||||
mSpellCheckSpans = new SpellCheckSpan[size];
|
||||
|
||||
setLocale(mTextView.getLocale());
|
||||
|
||||
mCookie = hashCode();
|
||||
}
|
||||
|
||||
private void setLocale(Locale locale) {
|
||||
final TextServicesManager textServicesManager = (TextServicesManager)
|
||||
mTextView.getContext().getSystemService(Context.TEXT_SERVICES_MANAGER_SERVICE);
|
||||
mSpellCheckerSession = textServicesManager.newSpellCheckerSession(
|
||||
null /* Bundle not currently used by the textServicesManager */,
|
||||
locale, this, false /* means any available languages from current spell checker */);
|
||||
mCurrentLocale = locale;
|
||||
|
||||
// Restore SpellCheckSpans in pool
|
||||
for (int i = 0; i < mLength; i++) {
|
||||
mSpellCheckSpans[i].setSpellCheckInProgress(false);
|
||||
mIds[i] = -1;
|
||||
}
|
||||
mLength = 0;
|
||||
|
||||
// Change SpellParsers' wordIterator locale
|
||||
mWordIterator = new WordIterator(locale);
|
||||
|
||||
// Stop all SpellParsers
|
||||
final int length = mSpellParsers.length;
|
||||
for (int i = 0; i < length; i++) {
|
||||
mSpellParsers[i].finish();
|
||||
}
|
||||
|
||||
// Remove existing misspelled SuggestionSpans
|
||||
mTextView.removeMisspelledSpans((Editable) mTextView.getText());
|
||||
|
||||
// This class is the listener for locale change: warn other locale-aware objects
|
||||
mTextView.onLocaleChanged();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -95,7 +127,7 @@ public class SpellChecker implements SpellCheckerSessionListener {
|
||||
|
||||
final int length = mSpellParsers.length;
|
||||
for (int i = 0; i < length; i++) {
|
||||
mSpellParsers[i].close();
|
||||
mSpellParsers[i].finish();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -140,12 +172,20 @@ public class SpellChecker implements SpellCheckerSessionListener {
|
||||
}
|
||||
|
||||
public void spellCheck(int start, int end) {
|
||||
final Locale locale = mTextView.getLocale();
|
||||
if (mCurrentLocale == null || (!(mCurrentLocale.equals(locale)))) {
|
||||
setLocale(locale);
|
||||
// Re-check the entire text
|
||||
start = 0;
|
||||
end = mTextView.getText().length();
|
||||
}
|
||||
|
||||
if (!isSessionActive()) return;
|
||||
|
||||
final int length = mSpellParsers.length;
|
||||
for (int i = 0; i < length; i++) {
|
||||
final SpellParser spellParser = mSpellParsers[i];
|
||||
if (spellParser.isDone()) {
|
||||
if (spellParser.isFinished()) {
|
||||
spellParser.init(start, end);
|
||||
spellParser.parse();
|
||||
return;
|
||||
@@ -229,7 +269,7 @@ public class SpellChecker implements SpellCheckerSessionListener {
|
||||
final int length = mSpellParsers.length;
|
||||
for (int i = 0; i < length; i++) {
|
||||
final SpellParser spellParser = mSpellParsers[i];
|
||||
if (!spellParser.isDone()) {
|
||||
if (!spellParser.isFinished()) {
|
||||
spellParser.parse();
|
||||
}
|
||||
}
|
||||
@@ -301,7 +341,6 @@ public class SpellChecker implements SpellCheckerSessionListener {
|
||||
}
|
||||
|
||||
private class SpellParser {
|
||||
private WordIterator mWordIterator = new WordIterator(/*TODO Locale*/);
|
||||
private Object mRange = new Object();
|
||||
|
||||
public void init(int start, int end) {
|
||||
@@ -309,11 +348,11 @@ public class SpellChecker implements SpellCheckerSessionListener {
|
||||
Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
|
||||
}
|
||||
|
||||
public void close() {
|
||||
public void finish() {
|
||||
((Editable) mTextView.getText()).removeSpan(mRange);
|
||||
}
|
||||
|
||||
public boolean isDone() {
|
||||
public boolean isFinished() {
|
||||
return ((Editable) mTextView.getText()).getSpanStart(mRange) < 0;
|
||||
}
|
||||
|
||||
|
||||
@@ -132,6 +132,7 @@ import android.view.inputmethod.ExtractedText;
|
||||
import android.view.inputmethod.ExtractedTextRequest;
|
||||
import android.view.inputmethod.InputConnection;
|
||||
import android.view.inputmethod.InputMethodManager;
|
||||
import android.view.inputmethod.InputMethodSubtype;
|
||||
import android.widget.AdapterView.OnItemClickListener;
|
||||
import android.widget.RemoteViews.RemoteView;
|
||||
|
||||
@@ -147,6 +148,7 @@ import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Comparator;
|
||||
import java.util.HashMap;
|
||||
import java.util.Locale;
|
||||
|
||||
/**
|
||||
* Displays text to the user and optionally allows them to edit it. A TextView
|
||||
@@ -2973,15 +2975,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
|
||||
sp.removeSpan(cw);
|
||||
}
|
||||
|
||||
SuggestionSpan[] suggestionSpans = sp.getSpans(0, sp.length(), SuggestionSpan.class);
|
||||
for (int i = 0; i < suggestionSpans.length; i++) {
|
||||
int flags = suggestionSpans[i].getFlags();
|
||||
if ((flags & SuggestionSpan.FLAG_EASY_CORRECT) != 0
|
||||
&& (flags & SuggestionSpan.FLAG_MISSPELLED) != 0) {
|
||||
sp.removeSpan(suggestionSpans[i]);
|
||||
}
|
||||
}
|
||||
|
||||
removeMisspelledSpans(sp);
|
||||
sp.removeSpan(mSuggestionRangeSpan);
|
||||
|
||||
ss.text = sp;
|
||||
@@ -3001,6 +2995,18 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
|
||||
return superState;
|
||||
}
|
||||
|
||||
void removeMisspelledSpans(Spannable spannable) {
|
||||
SuggestionSpan[] suggestionSpans = spannable.getSpans(0, spannable.length(),
|
||||
SuggestionSpan.class);
|
||||
for (int i = 0; i < suggestionSpans.length; i++) {
|
||||
int flags = suggestionSpans[i].getFlags();
|
||||
if ((flags & SuggestionSpan.FLAG_EASY_CORRECT) != 0
|
||||
&& (flags & SuggestionSpan.FLAG_MISSPELLED) != 0) {
|
||||
spannable.removeSpan(suggestionSpans[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onRestoreInstanceState(Parcelable state) {
|
||||
if (!(state instanceof SavedState)) {
|
||||
@@ -8870,15 +8876,13 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
|
||||
selectionStart = ((Spanned) mText).getSpanStart(urlSpan);
|
||||
selectionEnd = ((Spanned) mText).getSpanEnd(urlSpan);
|
||||
} else {
|
||||
if (mWordIterator == null) {
|
||||
mWordIterator = new WordIterator();
|
||||
}
|
||||
mWordIterator.setCharSequence(mText, minOffset, maxOffset);
|
||||
final WordIterator wordIterator = getWordIterator();
|
||||
wordIterator.setCharSequence(mText, minOffset, maxOffset);
|
||||
|
||||
selectionStart = mWordIterator.getBeginning(minOffset);
|
||||
selectionStart = wordIterator.getBeginning(minOffset);
|
||||
if (selectionStart == BreakIterator.DONE) return false;
|
||||
|
||||
selectionEnd = mWordIterator.getEnd(maxOffset);
|
||||
selectionEnd = wordIterator.getEnd(maxOffset);
|
||||
if (selectionEnd == BreakIterator.DONE) return false;
|
||||
|
||||
if (selectionStart == selectionEnd) {
|
||||
@@ -8893,6 +8897,43 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
|
||||
return selectionEnd > selectionStart;
|
||||
}
|
||||
|
||||
/**
|
||||
* This is a temporary method. Future versions may support multi-locale text.
|
||||
*
|
||||
* @return The current locale used in this TextView, based on the current IME's locale,
|
||||
* or the system default locale if this is not defined.
|
||||
* @hide
|
||||
*/
|
||||
public Locale getLocale() {
|
||||
Locale locale = Locale.getDefault();
|
||||
final InputMethodManager imm = InputMethodManager.peekInstance();
|
||||
if (imm != null) {
|
||||
final InputMethodSubtype currentInputMethodSubtype = imm.getCurrentInputMethodSubtype();
|
||||
if (currentInputMethodSubtype != null) {
|
||||
String localeString = currentInputMethodSubtype.getLocale();
|
||||
if (!TextUtils.isEmpty(localeString)) {
|
||||
locale = new Locale(localeString);
|
||||
}
|
||||
}
|
||||
}
|
||||
return locale;
|
||||
}
|
||||
|
||||
void onLocaleChanged() {
|
||||
// Will be re-created on demand in getWordIterator with the proper new locale
|
||||
mWordIterator = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @hide
|
||||
*/
|
||||
public WordIterator getWordIterator() {
|
||||
if (mWordIterator == null) {
|
||||
mWordIterator = new WordIterator(getLocale());
|
||||
}
|
||||
return mWordIterator;
|
||||
}
|
||||
|
||||
private long getCharRange(int offset) {
|
||||
final int textLength = mText.length();
|
||||
if (offset + 1 < textLength) {
|
||||
|
||||
Reference in New Issue
Block a user