Add a safety net for auto-correction.

Bug: 3353956

Change-Id: I6a32632b2f986f0d9a07aa72f256a2c41cc09873
This commit is contained in:
satok 2011-01-18 17:22:01 +09:00
parent 08ca36d038
commit 82411d47ba
4 changed files with 58 additions and 20 deletions

View file

@ -45,23 +45,22 @@ import android.widget.TextView;
import java.util.ArrayList; import java.util.ArrayList;
public class CandidateView extends LinearLayout implements OnClickListener, OnLongClickListener { public class CandidateView extends LinearLayout implements OnClickListener, OnLongClickListener {
private LatinIME mService;
private final ArrayList<View> mWords = new ArrayList<View>();
private final TextView mPreviewText;
private final PopupWindow mPreviewPopup;
private static final CharacterStyle BOLD_SPAN = new StyleSpan(Typeface.BOLD);
private static final CharacterStyle UNDERLINE_SPAN = new UnderlineSpan();
private static final int MAX_SUGGESTIONS = 16; private static final int MAX_SUGGESTIONS = 16;
private final ArrayList<View> mWords = new ArrayList<View>();
private final boolean mConfigCandidateHighlightFontColorEnabled; private final boolean mConfigCandidateHighlightFontColorEnabled;
private final CharacterStyle mInvertedForegroundColorSpan;
private final CharacterStyle mInvertedBackgroundColorSpan;
private final int mColorNormal; private final int mColorNormal;
private final int mColorRecommended; private final int mColorRecommended;
private final int mColorOther; private final int mColorOther;
private static final CharacterStyle BOLD_SPAN = new StyleSpan(Typeface.BOLD); private final PopupWindow mPreviewPopup;
private static final CharacterStyle UNDERLINE_SPAN = new UnderlineSpan(); private final TextView mPreviewText;
private final CharacterStyle mInvertedForegroundColorSpan;
private final CharacterStyle mInvertedBackgroundColorSpan;
private LatinIME mService;
private SuggestedWords mSuggestions = SuggestedWords.EMPTY; private SuggestedWords mSuggestions = SuggestedWords.EMPTY;
private boolean mShowingAutoCorrectionInverted; private boolean mShowingAutoCorrectionInverted;
private boolean mShowingAddToDictionary; private boolean mShowingAddToDictionary;
@ -186,9 +185,10 @@ public class CandidateView extends LinearLayout implements OnClickListener, OnLo
final TextView tv = (TextView)v.findViewById(R.id.candidate_word); final TextView tv = (TextView)v.findViewById(R.id.candidate_word);
final TextView dv = (TextView)v.findViewById(R.id.candidate_debug_info); final TextView dv = (TextView)v.findViewById(R.id.candidate_debug_info);
tv.setTextColor(mColorNormal); tv.setTextColor(mColorNormal);
// TODO: Needs safety net?
if (suggestions.mHasMinimalSuggestion if (suggestions.mHasMinimalSuggestion
&& ((i == 1 && !suggestions.mTypedWordValid) || && ((i == 1 && !suggestions.mTypedWordValid)
(i == 0 && suggestions.mTypedWordValid))) { || (i == 0 && suggestions.mTypedWordValid))) {
final CharacterStyle style; final CharacterStyle style;
if (mConfigCandidateHighlightFontColorEnabled) { if (mConfigCandidateHighlightFontColorEnabled) {
style = BOLD_SPAN; style = BOLD_SPAN;

View file

@ -1537,7 +1537,9 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
private void showSuggestions(SuggestedWords suggestedWords, CharSequence typedWord) { private void showSuggestions(SuggestedWords suggestedWords, CharSequence typedWord) {
setSuggestions(suggestedWords); setSuggestions(suggestedWords);
if (suggestedWords.size() > 0) { if (suggestedWords.size() > 0) {
if (suggestedWords.hasAutoCorrectionWord()) { if (Utils.shouldBlockedBySafetyNetForAutoCorrection(suggestedWords)) {
mBestWord = typedWord;
} else if (suggestedWords.hasAutoCorrectionWord()) {
mBestWord = suggestedWords.getWord(1); mBestWord = suggestedWords.getWord(1);
} else { } else {
mBestWord = typedWord; mBestWord = typedWord;

View file

@ -31,7 +31,7 @@ import java.util.Arrays;
*/ */
public class Suggest implements Dictionary.WordCallback { public class Suggest implements Dictionary.WordCallback {
public static final String TAG = "Suggest"; public static final String TAG = Suggest.class.getSimpleName();
public static final int APPROX_MAX_WORD_LENGTH = 32; public static final int APPROX_MAX_WORD_LENGTH = 32;
@ -64,6 +64,8 @@ public class Suggest implements Dictionary.WordCallback {
static final int LARGE_DICTIONARY_THRESHOLD = 200 * 1000; static final int LARGE_DICTIONARY_THRESHOLD = 200 * 1000;
private static boolean DBG = LatinImeLogger.sDBG;
private BinaryDictionary mMainDict; private BinaryDictionary mMainDict;
private Dictionary mUserDictionary; private Dictionary mUserDictionary;
@ -93,7 +95,7 @@ public class Suggest implements Dictionary.WordCallback {
private ArrayList<CharSequence> mSuggestions = new ArrayList<CharSequence>(); private ArrayList<CharSequence> mSuggestions = new ArrayList<CharSequence>();
ArrayList<CharSequence> mBigramSuggestions = new ArrayList<CharSequence>(); ArrayList<CharSequence> mBigramSuggestions = new ArrayList<CharSequence>();
private ArrayList<CharSequence> mStringPool = new ArrayList<CharSequence>(); private ArrayList<CharSequence> mStringPool = new ArrayList<CharSequence>();
private boolean mHaveCorrection; private boolean mHaveAutoCorrection;
private String mLowerOriginalWord; private String mLowerOriginalWord;
// TODO: Remove these member variables by passing more context to addWord() callback method // TODO: Remove these member variables by passing more context to addWord() callback method
@ -198,7 +200,7 @@ public class Suggest implements Dictionary.WordCallback {
public SuggestedWords.Builder getSuggestedWordBuilder(View view, WordComposer wordComposer, public SuggestedWords.Builder getSuggestedWordBuilder(View view, WordComposer wordComposer,
CharSequence prevWordForBigram) { CharSequence prevWordForBigram) {
LatinImeLogger.onStartSuggestion(prevWordForBigram); LatinImeLogger.onStartSuggestion(prevWordForBigram);
mHaveCorrection = false; mHaveAutoCorrection = false;
mIsFirstCharCapitalized = wordComposer.isFirstCharCapitalized(); mIsFirstCharCapitalized = wordComposer.isFirstCharCapitalized();
mIsAllUpperCase = wordComposer.isAllUpperCase(); mIsAllUpperCase = wordComposer.isAllUpperCase();
collectGarbage(mSuggestions, mPrefMaxSuggestions); collectGarbage(mSuggestions, mPrefMaxSuggestions);
@ -273,7 +275,10 @@ public class Suggest implements Dictionary.WordCallback {
if (mSuggestions.size() > 0 && isValidWord(typedWord) if (mSuggestions.size() > 0 && isValidWord(typedWord)
&& (mCorrectionMode == CORRECTION_FULL && (mCorrectionMode == CORRECTION_FULL
|| mCorrectionMode == CORRECTION_FULL_BIGRAM)) { || mCorrectionMode == CORRECTION_FULL_BIGRAM)) {
mHaveCorrection = true; if (DBG) {
Log.d(TAG, "Auto corrected by CORRECTION_FULL.");
}
mHaveAutoCorrection = true;
} }
} }
if (mMainDict != null) mMainDict.getWords(wordComposer, this, mNextLettersFrequencies); if (mMainDict != null) mMainDict.getWords(wordComposer, this, mNextLettersFrequencies);
@ -289,7 +294,10 @@ public class Suggest implements Dictionary.WordCallback {
+ "(" + mAutoCorrectionThreshold + ")"); + "(" + mAutoCorrectionThreshold + ")");
} }
if (normalizedScore >= mAutoCorrectionThreshold) { if (normalizedScore >= mAutoCorrectionThreshold) {
mHaveCorrection = true; if (DBG) {
Log.d(TAG, "Auto corrected by S-threthhold.");
}
mHaveAutoCorrection = true;
} }
} }
} }
@ -331,7 +339,10 @@ public class Suggest implements Dictionary.WordCallback {
canAdd &= !TextUtils.equals(autoText, mSuggestions.get(i + 1)); canAdd &= !TextUtils.equals(autoText, mSuggestions.get(i + 1));
} }
if (canAdd) { if (canAdd) {
mHaveCorrection = true; if (DBG) {
Log.d(TAG, "Auto corrected by AUTOTEXT.");
}
mHaveAutoCorrection = true;
mSuggestions.add(i + 1, autoText); mSuggestions.add(i + 1, autoText);
i++; i++;
} }
@ -374,7 +385,7 @@ public class Suggest implements Dictionary.WordCallback {
} }
public boolean hasMinimalCorrection() { public boolean hasMinimalCorrection() {
return mHaveCorrection; return mHaveAutoCorrection;
} }
private boolean compareCaseInsensitive(final String mLowerOriginalWord, private boolean compareCaseInsensitive(final String mLowerOriginalWord,

View file

@ -36,6 +36,8 @@ import java.text.SimpleDateFormat;
import java.util.Date; import java.util.Date;
public class Utils { public class Utils {
private static final String TAG = Utils.class.getSimpleName();
private static boolean DBG = LatinImeLogger.sDBG;
/** /**
* Cancel an {@link AsyncTask}. * Cancel an {@link AsyncTask}.
@ -95,6 +97,29 @@ public class Utils {
|| imm.getEnabledInputMethodSubtypeList(null, false).size() > 1; || imm.getEnabledInputMethodSubtypeList(null, false).size() > 1;
} }
public static boolean shouldBlockedBySafetyNetForAutoCorrection(SuggestedWords suggestions) {
// Safety net for auto correction.
// Actually if we hit this safety net, it's actually a bug.
if (suggestions.size() <= 1 || suggestions.mTypedWordValid) return false;
CharSequence typedWord = suggestions.getWord(0);
CharSequence candidateWord = suggestions.getWord(1);
final int typedWordLength = typedWord.length();
final int maxEditDistanceOfNativeDictionary = typedWordLength < 5 ? 2 : typedWordLength / 2;
final int distance = Utils.editDistance(typedWord, candidateWord);
if (DBG) {
Log.d(TAG, "Autocorrected edit distance = " + distance
+ ", " + maxEditDistanceOfNativeDictionary);
}
if (distance > maxEditDistanceOfNativeDictionary) {
Log.w(TAG, "(Error) The edit distance of this correction exceeds limit. "
+ "Turning off auto-correction.");
return true;
} else {
return false;
}
}
/* package */ static class RingCharBuffer { /* package */ static class RingCharBuffer {
private static RingCharBuffer sRingCharBuffer = new RingCharBuffer(); private static RingCharBuffer sRingCharBuffer = new RingCharBuffer();
private static final char PLACEHOLDER_DELIMITER_CHAR = '\uFFFC'; private static final char PLACEHOLDER_DELIMITER_CHAR = '\uFFFC';