Add a safety net for auto-correction.
Bug: 3353956 Change-Id: I6a32632b2f986f0d9a07aa72f256a2c41cc09873
This commit is contained in:
parent
08ca36d038
commit
82411d47ba
4 changed files with 58 additions and 20 deletions
|
@ -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 static final CharacterStyle BOLD_SPAN = new StyleSpan(Typeface.BOLD);
|
||||||
private final PopupWindow mPreviewPopup;
|
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;
|
||||||
|
@ -329,7 +329,7 @@ public class CandidateView extends LinearLayout implements OnClickListener, OnLo
|
||||||
mService.pickSuggestionManually(index, word);
|
mService.pickSuggestionManually(index, word);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onDetachedFromWindow() {
|
public void onDetachedFromWindow() {
|
||||||
super.onDetachedFromWindow();
|
super.onDetachedFromWindow();
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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,
|
||||||
|
|
|
@ -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';
|
||||||
|
|
Loading…
Reference in a new issue