Merge "Move the auto correction functionalities to AutoCorrection.java"
This commit is contained in:
commit
dca305dd71
3 changed files with 154 additions and 66 deletions
103
java/src/com/android/inputmethod/latin/AutoCorrection.java
Normal file
103
java/src/com/android/inputmethod/latin/AutoCorrection.java
Normal 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;
|
||||
}
|
||||
}
|
|
@ -1530,7 +1530,7 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
|
|||
}
|
||||
|
||||
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(),
|
||||
mWordSeparators);
|
||||
SuggestedWords.Builder builder = mSuggest.getSuggestedWordBuilder(
|
||||
|
@ -2052,6 +2052,7 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
|
|||
}
|
||||
|
||||
private void updateCorrectionMode() {
|
||||
// TODO: cleanup messy flags
|
||||
mHasDictionary = mSuggest != null ? mSuggest.hasMainDictionary() : false;
|
||||
mAutoCorrectOn = (mAutoCorrectEnabled || mQuickFixes)
|
||||
&& !mInputTypeNoAutoCorrect && mHasDictionary;
|
||||
|
|
|
@ -67,6 +67,8 @@ public class Suggest implements Dictionary.WordCallback {
|
|||
|
||||
private static final boolean DBG = LatinImeLogger.sDBG;
|
||||
|
||||
private AutoCorrection mAutoCorrection;
|
||||
|
||||
private BinaryDictionary mMainDict;
|
||||
|
||||
private Dictionary mUserDictionary;
|
||||
|
@ -90,7 +92,6 @@ public class Suggest implements Dictionary.WordCallback {
|
|||
private ArrayList<CharSequence> mSuggestions = new ArrayList<CharSequence>();
|
||||
ArrayList<CharSequence> mBigramSuggestions = new ArrayList<CharSequence>();
|
||||
private ArrayList<CharSequence> mStringPool = new ArrayList<CharSequence>();
|
||||
private boolean mHasAutoCorrection;
|
||||
private String mLowerOriginalWord;
|
||||
|
||||
// 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) {
|
||||
mMainDict = BinaryDictionary.initDictionary(context, dictionaryResId, DIC_MAIN);
|
||||
initPool();
|
||||
init();
|
||||
}
|
||||
|
||||
// For unit test
|
||||
/* package */ Suggest(File dictionary, long startOffset, long length) {
|
||||
/* package for test */ Suggest(File dictionary, long startOffset, long length) {
|
||||
mMainDict = BinaryDictionary.initDictionary(dictionary, startOffset, length, DIC_MAIN);
|
||||
init();
|
||||
}
|
||||
|
||||
private void init() {
|
||||
mAutoCorrection = new AutoCorrection();
|
||||
initPool();
|
||||
}
|
||||
|
||||
|
@ -205,7 +210,7 @@ public class Suggest implements Dictionary.WordCallback {
|
|||
public SuggestedWords.Builder getSuggestedWordBuilder(View view, WordComposer wordComposer,
|
||||
CharSequence prevWordForBigram) {
|
||||
LatinImeLogger.onStartSuggestion(prevWordForBigram);
|
||||
mHasAutoCorrection = false;
|
||||
mAutoCorrection.init();
|
||||
mIsFirstCharCapitalized = wordComposer.isFirstCharCapitalized();
|
||||
mIsAllUpperCase = wordComposer.isAllUpperCase();
|
||||
collectGarbage(mSuggestions, mPrefMaxSuggestions);
|
||||
|
@ -224,7 +229,6 @@ public class Suggest implements Dictionary.WordCallback {
|
|||
mLowerOriginalWord = "";
|
||||
}
|
||||
|
||||
double normalizedScore = Integer.MIN_VALUE;
|
||||
if (wordComposer.size() == 1 && (mCorrectionMode == CORRECTION_FULL_BIGRAM
|
||||
|| mCorrectionMode == CORRECTION_BASIC)) {
|
||||
// At first character typed, search only the bigrams
|
||||
|
@ -273,86 +277,67 @@ public class Suggest implements Dictionary.WordCallback {
|
|||
if (mContactsDictionary != null) {
|
||||
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 ((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) {
|
||||
mSuggestions.add(0, typedWord.toString());
|
||||
}
|
||||
if (mQuickFixesEnabled) {
|
||||
int i = 0;
|
||||
int max = 6;
|
||||
// Don't autotext the suggestions from the dictionaries
|
||||
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);
|
||||
// Apply quick fix only for the typed word.
|
||||
if (mQuickFixesEnabled) {
|
||||
final String lowerCaseTypedWord = typedWordString.toLowerCase();
|
||||
CharSequence tempAutoText =
|
||||
AutoText.get(lowerCaseTypedWord, 0, lowerCaseTypedWord.length(), view);
|
||||
// Is there an AutoText (also known as Quick Fixes) correction?
|
||||
boolean canAdd = autoText != null;
|
||||
// Capitalize as needed
|
||||
final int autoTextLength = autoText != null ? autoText.length() : 0;
|
||||
if (autoTextLength > 0 && (mIsAllUpperCase || mIsFirstCharCapitalized)) {
|
||||
int poolSize = mStringPool.size();
|
||||
StringBuilder sb = poolSize > 0 ? (StringBuilder) mStringPool.remove(
|
||||
poolSize - 1) : new StringBuilder(getApproxMaxWordLength());
|
||||
if (!TextUtils.isEmpty(tempAutoText)
|
||||
&& (mIsAllUpperCase || mIsFirstCharCapitalized)) {
|
||||
final int tempAutoTextLength = tempAutoText.length();
|
||||
final int poolSize = mStringPool.size();
|
||||
final StringBuilder sb =
|
||||
poolSize > 0 ? (StringBuilder) mStringPool.remove(poolSize - 1)
|
||||
: new StringBuilder(getApproxMaxWordLength());
|
||||
sb.setLength(0);
|
||||
if (mIsAllUpperCase) {
|
||||
sb.append(autoText.toString().toUpperCase());
|
||||
sb.append(tempAutoText.toString().toUpperCase());
|
||||
} else if (mIsFirstCharCapitalized) {
|
||||
sb.append(Character.toUpperCase(autoText.charAt(0)));
|
||||
if (autoTextLength > 1) {
|
||||
sb.append(autoText.subSequence(1, autoTextLength));
|
||||
sb.append(Character.toUpperCase(tempAutoText.charAt(0)));
|
||||
if (tempAutoTextLength > 1) {
|
||||
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)?
|
||||
canAdd &= !TextUtils.equals(autoText, mSuggestions.get(i));
|
||||
canAdd &= !TextUtils.equals(tempAutoText, typedWord);
|
||||
// Is that correction already the next predicted word?
|
||||
if (canAdd && i + 1 < mSuggestions.size() && mCorrectionMode != CORRECTION_BASIC) {
|
||||
canAdd &= !TextUtils.equals(autoText, mSuggestions.get(i + 1));
|
||||
if (canAdd && mSuggestions.size() > 0 && mCorrectionMode != CORRECTION_BASIC) {
|
||||
canAdd &= !TextUtils.equals(tempAutoText, mSuggestions.get(0));
|
||||
}
|
||||
if (canAdd) {
|
||||
if (DBG) {
|
||||
Log.d(TAG, "Auto corrected by AUTOTEXT.");
|
||||
}
|
||||
mHasAutoCorrection = true;
|
||||
mSuggestions.add(i + 1, autoText);
|
||||
i++;
|
||||
autoText = tempAutoText;
|
||||
}
|
||||
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();
|
||||
|
||||
if (DBG) {
|
||||
double normalizedScore = mAutoCorrection.getNormalizedScore();
|
||||
ArrayList<SuggestedWords.SuggestedWordInfo> frequencyInfoList =
|
||||
new ArrayList<SuggestedWords.SuggestedWordInfo>();
|
||||
frequencyInfoList.add(new SuggestedWords.SuggestedWordInfo("+", false));
|
||||
|
@ -373,9 +358,8 @@ public class Suggest implements Dictionary.WordCallback {
|
|||
frequencyInfoList.add(new SuggestedWords.SuggestedWordInfo("--", false));
|
||||
}
|
||||
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() {
|
||||
|
@ -406,7 +390,7 @@ public class Suggest implements Dictionary.WordCallback {
|
|||
}
|
||||
|
||||
public boolean hasAutoCorrection() {
|
||||
return mHasAutoCorrection;
|
||||
return mAutoCorrection.hasAutoCorrection();
|
||||
}
|
||||
|
||||
private static boolean compareCaseInsensitive(final String lowerOriginalWord,
|
||||
|
|
Loading…
Reference in a new issue