Merge "Spell checher's language synced with keyboard."

This commit is contained in:
Gilles Debunne
2011-10-14 11:09:44 -07:00
committed by Android (Google) Code Review
3 changed files with 118 additions and 38 deletions

View File

@@ -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;
}

View File

@@ -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;
}

View File

@@ -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) {