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) {
|
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;
|
||||||
|
|
|
@ -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,
|
||||||
|
|
Loading…
Reference in a new issue