Merge "Move the auto correction functionalities to AutoCorrection.java"

This commit is contained in:
satok 2011-03-03 02:58:06 -08:00 committed by Android (Google) Code Review
commit dca305dd71
3 changed files with 154 additions and 66 deletions

View file

@ -0,0 +1,103 @@
/*
* Copyright (C) 2011 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.inputmethod.latin;
import android.util.Log;
import java.util.ArrayList;
public class AutoCorrection {
private static final boolean DBG = LatinImeLogger.sDBG;
private static final String TAG = AutoCorrection.class.getSimpleName();
private boolean mHasAutoCorrection;
private CharSequence mAutoCorrectionWord;
private double mNormalizedScore;
public void init() {
mHasAutoCorrection = false;
mAutoCorrectionWord = null;
mNormalizedScore = Integer.MIN_VALUE;
}
public boolean hasAutoCorrection() {
return mHasAutoCorrection;
}
public CharSequence getAutoCorrectionWord() {
return mAutoCorrectionWord;
}
public double getNormalizedScore() {
return mNormalizedScore;
}
public void updateAutoCorrectionStatus(Suggest suggest,
WordComposer wordComposer, ArrayList<CharSequence> suggestions, int[] priorities,
CharSequence typedWord, double autoCorrectionThreshold, int correctionMode,
CharSequence quickFixedWord) {
if (hasAutoCorrectionForTypedWord(
suggest, wordComposer, suggestions, typedWord, correctionMode)) {
mHasAutoCorrection = true;
mAutoCorrectionWord = typedWord;
} else if (hasAutoCorrectForBinaryDictionary(wordComposer, suggestions, correctionMode,
priorities, typedWord, autoCorrectionThreshold)) {
mHasAutoCorrection = true;
mAutoCorrectionWord = suggestions.get(0);
} else if (hasAutoCorrectionForQuickFix(quickFixedWord)) {
mHasAutoCorrection = true;
mAutoCorrectionWord = quickFixedWord;
}
}
private boolean hasAutoCorrectionForTypedWord(Suggest suggest, WordComposer wordComposer,
ArrayList<CharSequence> suggestions, CharSequence typedWord, int correctionMode) {
return wordComposer.size() > 1 && suggestions.size() > 0 && suggest.isValidWord(typedWord)
&& (correctionMode == Suggest.CORRECTION_FULL
|| correctionMode == Suggest.CORRECTION_FULL_BIGRAM);
}
private boolean hasAutoCorrectForBinaryDictionary(WordComposer wordComposer,
ArrayList<CharSequence> suggestions, int correctionMode, int[] priorities,
CharSequence typedWord, double autoCorrectionThreshold) {
if (wordComposer.size() > 1 && (correctionMode == Suggest.CORRECTION_FULL
|| correctionMode == Suggest.CORRECTION_FULL_BIGRAM)
&& typedWord != null && suggestions.size() > 0 && priorities.length > 0) {
final CharSequence autoCorrectionCandidate = suggestions.get(0);
final int autoCorrectionCandidateScore = priorities[0];
// TODO: when the normalized score of the first suggestion is nearly equals to
// the normalized score of the second suggestion, behave less aggressive.
mNormalizedScore = Utils.calcNormalizedScore(
typedWord,autoCorrectionCandidate, autoCorrectionCandidateScore);
if (DBG) {
Log.d(TAG, "Normalized " + typedWord + "," + autoCorrectionCandidate + ","
+ autoCorrectionCandidateScore + ", " + mNormalizedScore
+ "(" + autoCorrectionThreshold + ")");
}
if (mNormalizedScore >= autoCorrectionThreshold) {
if (DBG) {
Log.d(TAG, "Auto corrected by S-threshold.");
}
return true;
}
}
return false;
}
private boolean hasAutoCorrectionForQuickFix(CharSequence quickFixedWord) {
return quickFixedWord != null;
}
}

View file

@ -1530,7 +1530,7 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
} }
private void showSuggestions(WordComposer word) { private void showSuggestions(WordComposer word) {
// TODO Maybe need better way of retrieving previous word // TODO: May need a better way of retrieving previous word
CharSequence prevWord = EditingUtils.getPreviousWord(getCurrentInputConnection(), CharSequence prevWord = EditingUtils.getPreviousWord(getCurrentInputConnection(),
mWordSeparators); mWordSeparators);
SuggestedWords.Builder builder = mSuggest.getSuggestedWordBuilder( SuggestedWords.Builder builder = mSuggest.getSuggestedWordBuilder(
@ -2052,6 +2052,7 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
} }
private void updateCorrectionMode() { private void updateCorrectionMode() {
// TODO: cleanup messy flags
mHasDictionary = mSuggest != null ? mSuggest.hasMainDictionary() : false; mHasDictionary = mSuggest != null ? mSuggest.hasMainDictionary() : false;
mAutoCorrectOn = (mAutoCorrectEnabled || mQuickFixes) mAutoCorrectOn = (mAutoCorrectEnabled || mQuickFixes)
&& !mInputTypeNoAutoCorrect && mHasDictionary; && !mInputTypeNoAutoCorrect && mHasDictionary;

View file

@ -67,6 +67,8 @@ public class Suggest implements Dictionary.WordCallback {
private static final boolean DBG = LatinImeLogger.sDBG; private static final boolean DBG = LatinImeLogger.sDBG;
private AutoCorrection mAutoCorrection;
private BinaryDictionary mMainDict; private BinaryDictionary mMainDict;
private Dictionary mUserDictionary; private Dictionary mUserDictionary;
@ -90,7 +92,6 @@ 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 mHasAutoCorrection;
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
@ -101,12 +102,16 @@ public class Suggest implements Dictionary.WordCallback {
public Suggest(Context context, int dictionaryResId) { public Suggest(Context context, int dictionaryResId) {
mMainDict = BinaryDictionary.initDictionary(context, dictionaryResId, DIC_MAIN); mMainDict = BinaryDictionary.initDictionary(context, dictionaryResId, DIC_MAIN);
initPool(); init();
} }
// For unit test /* package for test */ Suggest(File dictionary, long startOffset, long length) {
/* package */ Suggest(File dictionary, long startOffset, long length) {
mMainDict = BinaryDictionary.initDictionary(dictionary, startOffset, length, DIC_MAIN); mMainDict = BinaryDictionary.initDictionary(dictionary, startOffset, length, DIC_MAIN);
init();
}
private void init() {
mAutoCorrection = new AutoCorrection();
initPool(); initPool();
} }
@ -205,7 +210,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);
mHasAutoCorrection = false; mAutoCorrection.init();
mIsFirstCharCapitalized = wordComposer.isFirstCharCapitalized(); mIsFirstCharCapitalized = wordComposer.isFirstCharCapitalized();
mIsAllUpperCase = wordComposer.isAllUpperCase(); mIsAllUpperCase = wordComposer.isAllUpperCase();
collectGarbage(mSuggestions, mPrefMaxSuggestions); collectGarbage(mSuggestions, mPrefMaxSuggestions);
@ -224,7 +229,6 @@ public class Suggest implements Dictionary.WordCallback {
mLowerOriginalWord = ""; mLowerOriginalWord = "";
} }
double normalizedScore = Integer.MIN_VALUE;
if (wordComposer.size() == 1 && (mCorrectionMode == CORRECTION_FULL_BIGRAM if (wordComposer.size() == 1 && (mCorrectionMode == CORRECTION_FULL_BIGRAM
|| mCorrectionMode == CORRECTION_BASIC)) { || mCorrectionMode == CORRECTION_BASIC)) {
// At first character typed, search only the bigrams // At first character typed, search only the bigrams
@ -273,86 +277,67 @@ public class Suggest implements Dictionary.WordCallback {
if (mContactsDictionary != null) { if (mContactsDictionary != null) {
mContactsDictionary.getWords(wordComposer, this); mContactsDictionary.getWords(wordComposer, this);
} }
if (mSuggestions.size() > 0 && isValidWord(typedWord)
&& (mCorrectionMode == CORRECTION_FULL
|| mCorrectionMode == CORRECTION_FULL_BIGRAM)) {
if (DBG) {
Log.d(TAG, "Auto corrected by CORRECTION_FULL.");
}
mHasAutoCorrection = true;
}
} }
if (mMainDict != null) mMainDict.getWords(wordComposer, this); if (mMainDict != null) mMainDict.getWords(wordComposer, this);
if ((mCorrectionMode == CORRECTION_FULL || mCorrectionMode == CORRECTION_FULL_BIGRAM)
&& mSuggestions.size() > 0 && mPriorities.length > 0) {
// TODO: when the normalized score of the first suggestion is nearly equals to
// the normalized score of the second suggestion, behave less aggressive.
normalizedScore = Utils.calcNormalizedScore(
typedWord, mSuggestions.get(0), mPriorities[0]);
if (DBG) {
Log.d(TAG, "Normalized " + typedWord + "," + mSuggestions.get(0) + ","
+ mPriorities[0] + ", " + normalizedScore
+ "(" + mAutoCorrectionThreshold + ")");
}
if (normalizedScore >= mAutoCorrectionThreshold) {
if (DBG) {
Log.d(TAG, "Auto corrected by S-threshold.");
}
mHasAutoCorrection = true;
}
}
} }
CharSequence autoText = null;
final String typedWordString = typedWord == null ? null : typedWord.toString();
if (typedWord != null) { if (typedWord != null) {
mSuggestions.add(0, typedWord.toString()); // Apply quick fix only for the typed word.
}
if (mQuickFixesEnabled) { if (mQuickFixesEnabled) {
int i = 0; final String lowerCaseTypedWord = typedWordString.toLowerCase();
int max = 6; CharSequence tempAutoText =
// Don't autotext the suggestions from the dictionaries AutoText.get(lowerCaseTypedWord, 0, lowerCaseTypedWord.length(), view);
if (mCorrectionMode == CORRECTION_BASIC) max = 1;
while (i < mSuggestions.size() && i < max) {
String suggestedWord = mSuggestions.get(i).toString().toLowerCase();
CharSequence autoText =
AutoText.get(suggestedWord, 0, suggestedWord.length(), view);
// Is there an AutoText (also known as Quick Fixes) correction? // Is there an AutoText (also known as Quick Fixes) correction?
boolean canAdd = autoText != null;
// Capitalize as needed // Capitalize as needed
final int autoTextLength = autoText != null ? autoText.length() : 0; if (!TextUtils.isEmpty(tempAutoText)
if (autoTextLength > 0 && (mIsAllUpperCase || mIsFirstCharCapitalized)) { && (mIsAllUpperCase || mIsFirstCharCapitalized)) {
int poolSize = mStringPool.size(); final int tempAutoTextLength = tempAutoText.length();
StringBuilder sb = poolSize > 0 ? (StringBuilder) mStringPool.remove( final int poolSize = mStringPool.size();
poolSize - 1) : new StringBuilder(getApproxMaxWordLength()); final StringBuilder sb =
poolSize > 0 ? (StringBuilder) mStringPool.remove(poolSize - 1)
: new StringBuilder(getApproxMaxWordLength());
sb.setLength(0); sb.setLength(0);
if (mIsAllUpperCase) { if (mIsAllUpperCase) {
sb.append(autoText.toString().toUpperCase()); sb.append(tempAutoText.toString().toUpperCase());
} else if (mIsFirstCharCapitalized) { } else if (mIsFirstCharCapitalized) {
sb.append(Character.toUpperCase(autoText.charAt(0))); sb.append(Character.toUpperCase(tempAutoText.charAt(0)));
if (autoTextLength > 1) { if (tempAutoTextLength > 1) {
sb.append(autoText.subSequence(1, autoTextLength)); sb.append(tempAutoText.subSequence(1, tempAutoTextLength));
} }
} }
autoText = sb.toString(); tempAutoText = sb.toString();
} }
boolean canAdd = tempAutoText != null;
// Is that correction already the current prediction (or original word)? // Is that correction already the current prediction (or original word)?
canAdd &= !TextUtils.equals(autoText, mSuggestions.get(i)); canAdd &= !TextUtils.equals(tempAutoText, typedWord);
// Is that correction already the next predicted word? // Is that correction already the next predicted word?
if (canAdd && i + 1 < mSuggestions.size() && mCorrectionMode != CORRECTION_BASIC) { if (canAdd && mSuggestions.size() > 0 && mCorrectionMode != CORRECTION_BASIC) {
canAdd &= !TextUtils.equals(autoText, mSuggestions.get(i + 1)); canAdd &= !TextUtils.equals(tempAutoText, mSuggestions.get(0));
} }
if (canAdd) { if (canAdd) {
if (DBG) { if (DBG) {
Log.d(TAG, "Auto corrected by AUTOTEXT."); Log.d(TAG, "Auto corrected by AUTOTEXT.");
} }
mHasAutoCorrection = true; autoText = tempAutoText;
mSuggestions.add(i + 1, autoText);
i++;
} }
i++;
} }
} }
mAutoCorrection.updateAutoCorrectionStatus(this, wordComposer, mSuggestions, mPriorities,
typedWord, mAutoCorrectionThreshold, mCorrectionMode, autoText);
if (autoText != null) {
mSuggestions.add(0, autoText);
}
if (typedWord != null) {
mSuggestions.add(0, typedWordString);
}
removeDupes(); removeDupes();
if (DBG) { if (DBG) {
double normalizedScore = mAutoCorrection.getNormalizedScore();
ArrayList<SuggestedWords.SuggestedWordInfo> frequencyInfoList = ArrayList<SuggestedWords.SuggestedWordInfo> frequencyInfoList =
new ArrayList<SuggestedWords.SuggestedWordInfo>(); new ArrayList<SuggestedWords.SuggestedWordInfo>();
frequencyInfoList.add(new SuggestedWords.SuggestedWordInfo("+", false)); frequencyInfoList.add(new SuggestedWords.SuggestedWordInfo("+", false));
@ -373,9 +358,8 @@ public class Suggest implements Dictionary.WordCallback {
frequencyInfoList.add(new SuggestedWords.SuggestedWordInfo("--", false)); frequencyInfoList.add(new SuggestedWords.SuggestedWordInfo("--", false));
} }
return new SuggestedWords.Builder().addWords(mSuggestions, frequencyInfoList); return new SuggestedWords.Builder().addWords(mSuggestions, frequencyInfoList);
} else {
return new SuggestedWords.Builder().addWords(mSuggestions, null);
} }
return new SuggestedWords.Builder().addWords(mSuggestions, null);
} }
private void removeDupes() { private void removeDupes() {
@ -406,7 +390,7 @@ public class Suggest implements Dictionary.WordCallback {
} }
public boolean hasAutoCorrection() { public boolean hasAutoCorrection() {
return mHasAutoCorrection; return mAutoCorrection.hasAutoCorrection();
} }
private static boolean compareCaseInsensitive(final String lowerOriginalWord, private static boolean compareCaseInsensitive(final String lowerOriginalWord,