diff options
26 files changed, 396 insertions, 316 deletions
diff --git a/java/res/values-fr/donottranslate.xml b/java/res/values-fr/donottranslate.xml index 09c37e31a..695750f32 100644 --- a/java/res/values-fr/donottranslate.xml +++ b/java/res/values-fr/donottranslate.xml @@ -25,5 +25,5 @@ <!-- Symbols that should promote magic spaces into real space --> <string name="magic_space_promoting_symbols">;:!?([*&@{<>+=|</string> <!-- Symbols that do NOT separate words --> - <string name="non_word_separator_symbols">\u0027</string> + <string name="symbols_excluded_from_word_separators">\u0027</string> </resources> diff --git a/java/res/values-it/donottranslate.xml b/java/res/values-it/donottranslate.xml index adb2a9a9d..58e94361b 100644 --- a/java/res/values-it/donottranslate.xml +++ b/java/res/values-it/donottranslate.xml @@ -19,5 +19,5 @@ --> <resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> <!-- Symbols that do NOT separate words --> - <string name="non_word_separator_symbols"></string> + <string name="symbols_excluded_from_word_separators"></string> </resources> diff --git a/java/res/values/donottranslate.xml b/java/res/values/donottranslate.xml index 0d18d907d..9727746e7 100644 --- a/java/res/values/donottranslate.xml +++ b/java/res/values/donottranslate.xml @@ -27,10 +27,10 @@ <!-- Symbols that should convert magic spaces into real space --> <string name="magic_space_promoting_symbols">([*&@{<>+=|</string> <!-- Symbols that do NOT separate words --> - <string name="non_word_separator_symbols">\u0027-</string> + <string name="symbols_excluded_from_word_separators">\u0027-</string> <!-- Word separator list is the union of all symbols except those that are not separators: magic_space_swapping_symbols | magic_space_stripping_symbols | - magic_space_neutral_symbols \ non_word_separator_symbols --> + magic_space_neutral_symbols \ symbols_excluded_from_word_separators --> <!-- Label for "switch to more symbol" modifier key. Must be short to fit on key! --> <string name="label_to_more_symbol_key">= \\ <</string> diff --git a/java/src/com/android/inputmethod/deprecated/recorrection/RecorrectionSuggestionEntries.java b/java/src/com/android/inputmethod/deprecated/recorrection/RecorrectionSuggestionEntries.java index 5e6c87044..f33a46277 100644 --- a/java/src/com/android/inputmethod/deprecated/recorrection/RecorrectionSuggestionEntries.java +++ b/java/src/com/android/inputmethod/deprecated/recorrection/RecorrectionSuggestionEntries.java @@ -57,6 +57,7 @@ public class RecorrectionSuggestionEntries { private static SuggestedWords.Builder getTypedSuggestions( Suggest suggest, KeyboardSwitcher keyboardSwitcher, WordComposer word) { - return suggest.getSuggestedWordBuilder(keyboardSwitcher.getKeyboardView(), word, null); + return suggest.getSuggestedWordBuilder(keyboardSwitcher.getKeyboardView(), word, null, + keyboardSwitcher.getLatinKeyboard().getProximityInfo()); } } diff --git a/java/src/com/android/inputmethod/keyboard/Keyboard.java b/java/src/com/android/inputmethod/keyboard/Keyboard.java index d5e75d01b..518bc8e9e 100644 --- a/java/src/com/android/inputmethod/keyboard/Keyboard.java +++ b/java/src/com/android/inputmethod/keyboard/Keyboard.java @@ -149,8 +149,8 @@ public class Keyboard { mMostCommonKeyWidth, mKeys); } - public int getProximityInfo() { - return mProximityInfo.getNativeProximityInfo(); + public ProximityInfo getProximityInfo() { + return mProximityInfo; } public boolean hasShiftLockKey() { diff --git a/java/src/com/android/inputmethod/keyboard/KeyboardSwitcher.java b/java/src/com/android/inputmethod/keyboard/KeyboardSwitcher.java index 2d08abc19..0779964af 100644 --- a/java/src/com/android/inputmethod/keyboard/KeyboardSwitcher.java +++ b/java/src/com/android/inputmethod/keyboard/KeyboardSwitcher.java @@ -188,8 +188,13 @@ public class KeyboardSwitcher implements SharedPreferences.OnSharedPreferenceCha public void save() { mIsAlphabetMode = isAlphabetMode(); - mIsShiftLocked = mIsAlphabetMode && isShiftLocked(); - mIsShifted = !mIsShiftLocked && isShiftedOrShiftLocked(); + if (mIsAlphabetMode) { + mIsShiftLocked = isShiftLocked(); + mIsShifted = !mIsShiftLocked && isShiftedOrShiftLocked(); + } else { + mIsShiftLocked = false; + mIsShifted = mSymbolsShiftedKeyboardId.equals(mCurrentId); + } mIsValid = true; } @@ -364,6 +369,7 @@ public class KeyboardSwitcher implements SharedPreferences.OnSharedPreferenceCha } keyboard.onAutoCorrectionStateChanged(mIsAutoCorrectionActive); + keyboard.setShiftLocked(false); keyboard.setShifted(false); // If the cached keyboard had been switched to another keyboard while the language was // displayed on its spacebar, it might have had arbitrary text fade factor. In such case, diff --git a/java/src/com/android/inputmethod/keyboard/ProximityInfo.java b/java/src/com/android/inputmethod/keyboard/ProximityInfo.java index aadedc69d..07dae168f 100644 --- a/java/src/com/android/inputmethod/keyboard/ProximityInfo.java +++ b/java/src/com/android/inputmethod/keyboard/ProximityInfo.java @@ -19,6 +19,7 @@ package com.android.inputmethod.keyboard; import com.android.inputmethod.latin.Utils; import java.util.Arrays; +import java.util.Collections; import java.util.List; public class ProximityInfo { @@ -54,6 +55,10 @@ public class ProximityInfo { computeNearestNeighbors(keyWidth, keys); } + public static ProximityInfo getDummyProximityInfo() { + return new ProximityInfo(1, 1, 1, 1, 1, Collections.<Key>emptyList()); + } + private int mNativeProximityInfo; static { Utils.loadNativeLibrary(); diff --git a/java/src/com/android/inputmethod/latin/BinaryDictionary.java b/java/src/com/android/inputmethod/latin/BinaryDictionary.java index 9748d6006..6a6a0a4ee 100644 --- a/java/src/com/android/inputmethod/latin/BinaryDictionary.java +++ b/java/src/com/android/inputmethod/latin/BinaryDictionary.java @@ -156,10 +156,11 @@ public class BinaryDictionary extends Dictionary { } } + // proximityInfo may not be null. @Override - public void getWords(final WordComposer codes, final WordCallback callback) { - final int count = getSuggestions(codes, mKeyboardSwitcher.getLatinKeyboard(), - mOutputChars, mScores); + public void getWords(final WordComposer codes, final WordCallback callback, + final ProximityInfo proximityInfo) { + final int count = getSuggestions(codes, proximityInfo, mOutputChars, mScores); for (int j = 0; j < count; ++j) { if (mScores[j] < 1) break; @@ -179,8 +180,9 @@ public class BinaryDictionary extends Dictionary { return mNativeDict != 0; } - /* package for test */ int getSuggestions(final WordComposer codes, final Keyboard keyboard, - char[] outputChars, int[] scores) { + // proximityInfo may not be null. + /* package for test */ int getSuggestions(final WordComposer codes, + final ProximityInfo proximityInfo, char[] outputChars, int[] scores) { if (!isValidDictionary()) return -1; final int codesSize = codes.size(); @@ -196,9 +198,8 @@ public class BinaryDictionary extends Dictionary { Arrays.fill(outputChars, (char) 0); Arrays.fill(scores, 0); - final int proximityInfo = keyboard == null ? 0 : keyboard.getProximityInfo(); return getSuggestionsNative( - mNativeDict, proximityInfo, + mNativeDict, proximityInfo.getNativeProximityInfo(), codes.getXCoordinates(), codes.getYCoordinates(), mInputCodes, codesSize, mFlags, outputChars, scores); } diff --git a/java/src/com/android/inputmethod/latin/CandidateView.java b/java/src/com/android/inputmethod/latin/CandidateView.java index 713f3abe2..9b39e36a0 100644 --- a/java/src/com/android/inputmethod/latin/CandidateView.java +++ b/java/src/com/android/inputmethod/latin/CandidateView.java @@ -57,8 +57,6 @@ public class CandidateView extends LinearLayout implements OnClickListener { public void pickSuggestionManually(int index, CharSequence word); } - private static final CharacterStyle BOLD_SPAN = new StyleSpan(Typeface.BOLD); - private static final CharacterStyle UNDERLINE_SPAN = new UnderlineSpan(); // The maximum number of suggestions available. See {@link Suggest#mPrefMaxSuggestions}. private static final int MAX_SUGGESTIONS = 18; private static final int WRAP_CONTENT = ViewGroup.LayoutParams.WRAP_CONTENT; @@ -67,8 +65,6 @@ public class CandidateView extends LinearLayout implements OnClickListener { private static final boolean DBG = LatinImeLogger.sDBG; private final ViewGroup mCandidatesStrip; - private final int mCandidateCountInStrip; - private static final int DEFAULT_CANDIDATE_COUNT_IN_STRIP = 3; private final ViewGroup mCandidatesPaneControl; private final TextView mExpandCandidatesPane; private final TextView mCloseCandidatesPane; @@ -81,15 +77,6 @@ public class CandidateView extends LinearLayout implements OnClickListener { private final ArrayList<View> mDividers = new ArrayList<View>(); private final int mCandidateStripHeight; - private final CharacterStyle mInvertedForegroundColorSpan; - private final CharacterStyle mInvertedBackgroundColorSpan; - private final int mAutoCorrectHighlight; - private static final int AUTO_CORRECT_BOLD = 0x01; - private static final int AUTO_CORRECT_UNDERLINE = 0x02; - private static final int AUTO_CORRECT_INVERT = 0x04; - private final int mColorTypedWord; - private final int mColorAutoCorrect; - private final int mColorSuggestedCandidate; private final PopupWindow mPreviewPopup; private final TextView mPreviewText; @@ -102,8 +89,8 @@ public class CandidateView extends LinearLayout implements OnClickListener { private boolean mShowingAutoCorrectionInverted; private boolean mShowingAddToDictionary; - private final CandidateViewLayoutParams mParams; - private static final int PUNCTUATIONS_IN_STRIP = 6; + private final SuggestionsStripParams mStripParams; + private final SuggestionsPaneParams mPaneParams; private static final float MIN_TEXT_XSCALE = 0.75f; private final UiHandler mHandler = new UiHandler(this); @@ -157,15 +144,49 @@ public class CandidateView extends LinearLayout implements OnClickListener { } } - private static class CandidateViewLayoutParams { - public final TextPaint mPaint; + private static class CandidateViewParams { public final int mPadding; public final int mDividerWidth; public final int mDividerHeight; public final int mControlWidth; + + protected CandidateViewParams(TextView word, View divider, View control) { + mPadding = word.getCompoundPaddingLeft() + word.getCompoundPaddingRight(); + divider.measure(WRAP_CONTENT, MATCH_PARENT); + mDividerWidth = divider.getMeasuredWidth(); + mDividerHeight = divider.getMeasuredHeight(); + mControlWidth = control.getMeasuredWidth(); + } + } + + private static class SuggestionsPaneParams extends CandidateViewParams { + public SuggestionsPaneParams(List<TextView> words, List<View> dividers, View control) { + super(words.get(0), dividers.get(0), control); + } + } + + private static class SuggestionsStripParams extends CandidateViewParams { + private static final int DEFAULT_CANDIDATE_COUNT_IN_STRIP = 3; + private static final int PUNCTUATIONS_IN_STRIP = 6; + + private final int mColorTypedWord; + private final int mColorAutoCorrect; + private final int mColorSuggestedCandidate; + private final int mCandidateCountInStrip; + + private static final CharacterStyle BOLD_SPAN = new StyleSpan(Typeface.BOLD); + private static final CharacterStyle UNDERLINE_SPAN = new UnderlineSpan(); + private final CharacterStyle mInvertedForegroundColorSpan; + private final CharacterStyle mInvertedBackgroundColorSpan; + private static final int AUTO_CORRECT_BOLD = 0x01; + private static final int AUTO_CORRECT_UNDERLINE = 0x02; + private static final int AUTO_CORRECT_INVERT = 0x04; + + public final TextPaint mPaint; private final int mAutoCorrectHighlight; - public final ArrayList<CharSequence> mTexts = new ArrayList<CharSequence>(); + private final ArrayList<CharSequence> mTexts = new ArrayList<CharSequence>(); + private SuggestedWords mSuggestedWords; public int mCountInStrip; // True if the mCountInStrip suggestions can fit in suggestion strip in equally divided @@ -177,23 +198,114 @@ public class CandidateView extends LinearLayout implements OnClickListener { public int mVariableWidthForWords; public float mScaleX; - public CandidateViewLayoutParams(Resources res, TextView word, View divider, View control, - int autoCorrectHighlight) { + public SuggestionsStripParams(Context context, AttributeSet attrs, int defStyle, + List<TextView> words, List<View> dividers, View control) { + super(words.get(0), dividers.get(0), control); + final TypedArray a = context.obtainStyledAttributes( + attrs, R.styleable.CandidateView, defStyle, R.style.CandidateViewStyle); + mAutoCorrectHighlight = a.getInt(R.styleable.CandidateView_autoCorrectHighlight, 0); + mColorTypedWord = a.getColor(R.styleable.CandidateView_colorTypedWord, 0); + mColorAutoCorrect = a.getColor(R.styleable.CandidateView_colorAutoCorrect, 0); + mColorSuggestedCandidate = a.getColor(R.styleable.CandidateView_colorSuggested, 0); + mCandidateCountInStrip = a.getInt( + R.styleable.CandidateView_candidateCountInStrip, + DEFAULT_CANDIDATE_COUNT_IN_STRIP); + a.recycle(); + + mInvertedForegroundColorSpan = new ForegroundColorSpan(mColorTypedWord ^ 0x00ffffff); + mInvertedBackgroundColorSpan = new BackgroundColorSpan(mColorTypedWord); + mPaint = new TextPaint(); - final float textSize = res.getDimension(R.dimen.candidate_text_size); + final float textSize = context.getResources().getDimension(R.dimen.candidate_text_size); mPaint.setTextSize(textSize); - mPadding = word.getCompoundPaddingLeft() + word.getCompoundPaddingRight(); - divider.measure(WRAP_CONTENT, MATCH_PARENT); - mDividerWidth = divider.getMeasuredWidth(); - mDividerHeight = divider.getMeasuredHeight(); - mControlWidth = control.getMeasuredWidth(); - mAutoCorrectHighlight = autoCorrectHighlight; } - public void layoutStrip(SuggestedWords suggestions, int maxWidth, int maxCount) { + public CharSequence getWord(int pos) { + return mTexts.get(pos); + } + + public CharSequence getDebugInfo(int pos) { + if (DBG) { + final SuggestedWordInfo wordInfo = mSuggestedWords.getInfo(pos); + if (wordInfo != null) { + final CharSequence debugInfo = wordInfo.getDebugString(); + if (!TextUtils.isEmpty(debugInfo)) { + return debugInfo; + } + } + } + return null; + } + + public CharSequence getStyledCandidateWord(CharSequence word, boolean isAutoCorrect) { + if (!isAutoCorrect) + return word; + final int len = word.length(); + final Spannable spannedWord = new SpannableString(word); + if ((mAutoCorrectHighlight & AUTO_CORRECT_BOLD) != 0) + spannedWord.setSpan(BOLD_SPAN, 0, len, Spanned.SPAN_INCLUSIVE_EXCLUSIVE); + if ((mAutoCorrectHighlight & AUTO_CORRECT_UNDERLINE) != 0) + spannedWord.setSpan(UNDERLINE_SPAN, 0, len, Spanned.SPAN_INCLUSIVE_EXCLUSIVE); + return spannedWord; + } + + public int getWordPosition(int index) { + if (index >= 2) { + return index; + } + final boolean willAutoCorrect = !mSuggestedWords.mTypedWordValid + && mSuggestedWords.mHasMinimalSuggestion; + return willAutoCorrect ? 1 - index : index; + } + + public int getCandidateTextColor(int pos) { + final SuggestedWords suggestions = mSuggestedWords; + final boolean isAutoCorrect = suggestions.mHasMinimalSuggestion + && ((pos == 1 && !suggestions.mTypedWordValid) + || (pos == 0 && suggestions.mTypedWordValid)); + // TODO: Need to revisit this logic with bigram suggestions + final boolean isSuggestedCandidate = (pos != 0); + final boolean isPunctuationSuggestions = suggestions.isPunctuationSuggestions(); + + final int color; + if (isPunctuationSuggestions) { + color = mColorTypedWord; + } else if (isAutoCorrect) { + color = mColorAutoCorrect; + } else if (isSuggestedCandidate) { + color = mColorSuggestedCandidate; + } else { + color = mColorTypedWord; + } + final SuggestedWordInfo info = suggestions.getInfo(pos); + if (info != null && info.isPreviousSuggestedWord()) { + return applyAlpha(color, 0.5f); + } else { + return color; + } + } + + private static int applyAlpha(final int color, final float alpha) { + final int newAlpha = (int)(Color.alpha(color) * alpha); + return Color.argb(newAlpha, Color.red(color), Color.green(color), Color.blue(color)); + } + + public CharSequence getInvertedText(CharSequence text) { + if ((mAutoCorrectHighlight & AUTO_CORRECT_INVERT) == 0) + return null; + final int len = text.length(); + final Spannable word = new SpannableString(text); + word.setSpan(mInvertedBackgroundColorSpan, 0, len, Spanned.SPAN_INCLUSIVE_EXCLUSIVE); + word.setSpan(mInvertedForegroundColorSpan, 0, len, Spanned.SPAN_INCLUSIVE_EXCLUSIVE); + return word; + } + + public void layoutStrip(SuggestedWords suggestions, int maxWidth) { + mSuggestedWords = suggestions; + final int maxCount = suggestions.isPunctuationSuggestions() + ? PUNCTUATIONS_IN_STRIP : mCandidateCountInStrip; final int size = suggestions.size(); - if (size == 0) return; - setupTexts(suggestions, size, mAutoCorrectHighlight); + setupTexts(suggestions, size); mCountInStrip = Math.min(maxCount, size); mScaleX = 1.0f; @@ -240,25 +352,14 @@ public class CandidateView extends LinearLayout implements OnClickListener { } } - private void setupTexts(SuggestedWords suggestions, int count, int autoCorrectHighlight) { + private void setupTexts(SuggestedWords suggestions, int count) { mTexts.clear(); for (int i = 0; i < count; i++) { - final CharSequence suggestion = suggestions.getWord(i); - if (suggestion == null) { - // Skip an empty suggestion, but we need to add a place-holder for it in order - // to avoid an exception in the loop in updateSuggestions(). - mTexts.add(""); - continue; - } - + final CharSequence word = suggestions.getWord(i); final boolean isAutoCorrect = suggestions.mHasMinimalSuggestion && ((i == 1 && !suggestions.mTypedWordValid) || (i == 0 && suggestions.mTypedWordValid)); - // HACK: even if i == 0, we use mColorOther when this suggestion's length is 1 - // and there are multiple suggestions, such as the default punctuation list. - // TODO: Need to revisit this logic with bigram suggestions - final CharSequence styled = getStyledCandidateWord(suggestion, isAutoCorrect, - autoCorrectHighlight); + final CharSequence styled = getStyledCandidateWord(word, isAutoCorrect); mTexts.add(styled); } } @@ -295,16 +396,6 @@ public class CandidateView extends LinearLayout implements OnClickListener { setBackgroundDrawable(LinearLayoutCompatUtils.getBackgroundDrawable( context, attrs, defStyle, R.style.CandidateViewStyle)); - final TypedArray a = context.obtainStyledAttributes( - attrs, R.styleable.CandidateView, defStyle, R.style.CandidateViewStyle); - mAutoCorrectHighlight = a.getInt(R.styleable.CandidateView_autoCorrectHighlight, 0); - mColorTypedWord = a.getColor(R.styleable.CandidateView_colorTypedWord, 0); - mColorAutoCorrect = a.getColor(R.styleable.CandidateView_colorAutoCorrect, 0); - mColorSuggestedCandidate = a.getColor(R.styleable.CandidateView_colorSuggested, 0); - mCandidateCountInStrip = a.getInt( - R.styleable.CandidateView_candidateCountInStrip, DEFAULT_CANDIDATE_COUNT_IN_STRIP); - a.recycle(); - Resources res = context.getResources(); LayoutInflater inflater = LayoutInflater.from(context); inflater.inflate(R.layout.candidates_strip, this); @@ -334,9 +425,6 @@ public class CandidateView extends LinearLayout implements OnClickListener { mWordToSave = (TextView)findViewById(R.id.word_to_save); mWordToSave.setOnClickListener(this); - mInvertedForegroundColorSpan = new ForegroundColorSpan(mColorTypedWord ^ 0x00ffffff); - mInvertedBackgroundColorSpan = new BackgroundColorSpan(mColorTypedWord); - final TypedArray keyboardViewAttr = context.obtainStyledAttributes( attrs, R.styleable.KeyboardView, R.attr.keyboardViewStyle, R.style.KeyboardView); final Drawable expandBackground = keyboardViewAttr.getDrawable( @@ -368,8 +456,9 @@ public class CandidateView extends LinearLayout implements OnClickListener { }); mCandidatesPaneControl.measure(WRAP_CONTENT, WRAP_CONTENT); - mParams = new CandidateViewLayoutParams(res, - mWords.get(0), mDividers.get(0), mCandidatesPaneControl, mAutoCorrectHighlight); + mStripParams = new SuggestionsStripParams(context, attrs, defStyle, + mWords, mDividers, mCandidatesPaneControl); + mPaneParams = new SuggestionsPaneParams(mWords, mDividers, mCandidatesPaneControl); } /** @@ -398,52 +487,20 @@ public class CandidateView extends LinearLayout implements OnClickListener { } } - private static CharSequence getStyledCandidateWord(CharSequence word, boolean isAutoCorrect, - int autoCorrectHighlight) { - if (!isAutoCorrect) - return word; - final Spannable spannedWord = new SpannableString(word); - if ((autoCorrectHighlight & AUTO_CORRECT_BOLD) != 0) - spannedWord.setSpan(BOLD_SPAN, 0, word.length(), Spanned.SPAN_INCLUSIVE_EXCLUSIVE); - if ((autoCorrectHighlight & AUTO_CORRECT_UNDERLINE) != 0) - spannedWord.setSpan(UNDERLINE_SPAN, 0, word.length(), Spanned.SPAN_INCLUSIVE_EXCLUSIVE); - return spannedWord; - } - - private int getCandidateTextColor(boolean isAutoCorrect, boolean isSuggestedCandidate, - SuggestedWordInfo info) { - final int color; - if (isAutoCorrect) { - color = mColorAutoCorrect; - } else if (isSuggestedCandidate) { - color = mColorSuggestedCandidate; - } else { - color = mColorTypedWord; - } - if (info != null && info.isPreviousSuggestedWord()) { - final int newAlpha = (int)(Color.alpha(color) * 0.5f); - return Color.argb(newAlpha, Color.red(color), Color.green(color), Color.blue(color)); - } else { - return color; - } - } - private void updateSuggestions() { - final SuggestedWords suggestions = mSuggestions; - final List<SuggestedWordInfo> suggestedWordInfoList = suggestions.mSuggestedWordInfoList; - final int paneWidth = getWidth(); - final CandidateViewLayoutParams params = mParams; - clear(); closeCandidatesPane(); + final SuggestedWords suggestions = mSuggestions; if (suggestions.size() == 0) return; - params.layoutStrip(suggestions, paneWidth, suggestions.isPunctuationSuggestions() - ? PUNCTUATIONS_IN_STRIP : mCandidateCountInStrip); + final int paneWidth = getWidth(); + final SuggestionsStripParams stripParams = mStripParams; + final SuggestionsPaneParams paneParams = mPaneParams; + stripParams.layoutStrip(suggestions, paneWidth); final int count = Math.min(mWords.size(), suggestions.size()); - if (count <= params.mCountInStrip && !DBG) { + if (count <= stripParams.mCountInStrip && !DBG) { mCandidatesPaneControl.setVisibility(GONE); } else { mCandidatesPaneControl.setVisibility(VISIBLE); @@ -451,71 +508,53 @@ public class CandidateView extends LinearLayout implements OnClickListener { mExpandCandidatesPane.setEnabled(true); } - final int countInStrip = params.mCountInStrip; + final int countInStrip = stripParams.mCountInStrip; View centeringFrom = null, lastView = null; int x = 0, y = 0, infoX = 0; - for (int i = 0; i < count; i++) { - final int pos; - if (i <= 1) { - final boolean willAutoCorrect = !suggestions.mTypedWordValid - && suggestions.mHasMinimalSuggestion; - pos = willAutoCorrect ? 1 - i : i; - } else { - pos = i; - } - final CharSequence suggestion = suggestions.getWord(pos); - if (suggestion == null) continue; - - final SuggestedWordInfo suggestionInfo = (suggestedWordInfoList != null) - ? suggestedWordInfoList.get(pos) : null; - final boolean isAutoCorrect = suggestions.mHasMinimalSuggestion - && ((pos == 1 && !suggestions.mTypedWordValid) - || (pos == 0 && suggestions.mTypedWordValid)); - // HACK: even if i == 0, we use mColorOther when this suggestion's length is 1 - // and there are multiple suggestions, such as the default punctuation list. - // TODO: Need to revisit this logic with bigram suggestions - final boolean isSuggestedCandidate = (pos != 0); - final boolean isPunctuationSuggestions = (suggestion.length() == 1 && count > 1); - + for (int index = 0; index < count; index++) { + final int pos = stripParams.getWordPosition(index); final TextView word = mWords.get(pos); - final TextPaint paint = word.getPaint(); final View divider = mDividers.get(pos); + final TextPaint paint = word.getPaint(); // TODO: Reorder candidates in strip as appropriate. The center candidate should hold // the word when space is typed (valid typed word or auto corrected word). - word.setTextColor(getCandidateTextColor(isAutoCorrect, - isSuggestedCandidate || isPunctuationSuggestions, suggestionInfo)); - final CharSequence styled = params.mTexts.get(pos); + word.setTextColor(stripParams.getCandidateTextColor(pos)); + final CharSequence styled = stripParams.getWord(pos); final TextView info; - if (DBG && suggestionInfo != null - && !TextUtils.isEmpty(suggestionInfo.getDebugString())) { - info = mInfos.get(i); - info.setText(suggestionInfo.getDebugString()); + if (DBG) { + final CharSequence debugInfo = stripParams.getDebugInfo(index); + if (debugInfo != null) { + info = mInfos.get(index); + info.setText(debugInfo); + } else { + info = null; + } } else { info = null; } final CharSequence text; final float scaleX; - if (i < countInStrip) { - if (i == 0 && params.mCountInStrip == 1) { - text = getEllipsizedText(styled, params.mMaxWidth, paint); + if (index < countInStrip) { + if (index == 0 && stripParams.mCountInStrip == 1) { + text = getEllipsizedText(styled, stripParams.mMaxWidth, paint); scaleX = paint.getTextScaleX(); } else { text = styled; - scaleX = params.mScaleX; + scaleX = stripParams.mScaleX; } word.setText(text); word.setTextScaleX(scaleX); - if (i != 0) { + if (index != 0) { // Add divider if this isn't the left most suggestion in candidate strip. mCandidatesStrip.addView(divider); } mCandidatesStrip.addView(word); - if (params.mCanUseFixedWidthColumns) { + if (stripParams.mCanUseFixedWidthColumns) { setLayoutWeight(word, 1.0f, mCandidateStripHeight); } else { - final int width = getTextWidth(text, paint) + params.mPadding; + final int width = getTextWidth(text, paint) + stripParams.mPadding; setLayoutWeight(word, width, mCandidateStripHeight); } if (info != null) { @@ -529,7 +568,7 @@ public class CandidateView extends LinearLayout implements OnClickListener { } else { paint.setTextScaleX(1.0f); final int textWidth = getTextWidth(styled, paint); - int available = paneWidth - x - params.mPadding; + int available = paneWidth - x - paneParams.mPadding; if (textWidth >= available) { // Needs new row, centering previous row. centeringCandidates(centeringFrom, lastView, x, paneWidth); @@ -540,11 +579,11 @@ public class CandidateView extends LinearLayout implements OnClickListener { // Add divider if this isn't the left most suggestion in current row. mCandidatesPane.addView(divider); FrameLayoutCompatUtils.placeViewAt( - divider, x, y + (mCandidateStripHeight - params.mDividerHeight) / 2, - params.mDividerWidth, params.mDividerHeight); - x += params.mDividerWidth; + divider, x, y + (mCandidateStripHeight - paneParams.mDividerHeight) / 2, + paneParams.mDividerWidth, paneParams.mDividerHeight); + x += paneParams.mDividerWidth; } - available = paneWidth - x - params.mPadding; + available = paneWidth - x - paneParams.mPadding; text = getEllipsizedText(styled, available, paint); scaleX = paint.getTextScaleX(); word.setText(text); @@ -670,16 +709,11 @@ public class CandidateView extends LinearLayout implements OnClickListener { } public void onAutoCorrectionInverted(CharSequence autoCorrectedWord) { - if ((mAutoCorrectHighlight & AUTO_CORRECT_INVERT) == 0) + final CharSequence inverted = mStripParams.getInvertedText(autoCorrectedWord); + if (inverted == null) return; final TextView tv = mWords.get(1); - final Spannable word = new SpannableString(autoCorrectedWord); - final int wordLength = word.length(); - word.setSpan(mInvertedBackgroundColorSpan, 0, wordLength, - Spanned.SPAN_INCLUSIVE_EXCLUSIVE); - word.setSpan(mInvertedForegroundColorSpan, 0, wordLength, - Spanned.SPAN_INCLUSIVE_EXCLUSIVE); - tv.setText(word); + tv.setText(inverted); mShowingAutoCorrectionInverted = true; } @@ -724,7 +758,7 @@ public class CandidateView extends LinearLayout implements OnClickListener { return; final TextView previewText = mPreviewText; - previewText.setTextColor(mColorTypedWord); + previewText.setTextColor(mStripParams.mColorTypedWord); previewText.setText(word); previewText.measure(MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED), MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED)); diff --git a/java/src/com/android/inputmethod/latin/Dictionary.java b/java/src/com/android/inputmethod/latin/Dictionary.java index c7737b9a2..c35b42877 100644 --- a/java/src/com/android/inputmethod/latin/Dictionary.java +++ b/java/src/com/android/inputmethod/latin/Dictionary.java @@ -1,12 +1,12 @@ /* * Copyright (C) 2008 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 @@ -16,6 +16,8 @@ package com.android.inputmethod.latin; +import com.android.inputmethod.keyboard.ProximityInfo; + /** * Abstract base class for a dictionary that can do a fuzzy search for words based on a set of key * strokes. @@ -25,7 +27,7 @@ public abstract class Dictionary { * Whether or not to replicate the typed word in the suggested list, even if it's valid. */ protected static final boolean INCLUDE_TYPED_WORD_IF_VALID = false; - + /** * The weight to give to a word if it's length is the same as the number of typed characters. */ @@ -57,13 +59,15 @@ public abstract class Dictionary { } /** - * Searches for words in the dictionary that match the characters in the composer. Matched + * Searches for words in the dictionary that match the characters in the composer. Matched * words are added through the callback object. * @param composer the key sequence to match * @param callback the callback object to send matched words to as possible candidates + * @param proximityInfo the object for key proximity. May be ignored by some implementations. * @see WordCallback#addWord(char[], int, int, int, int, DataType) */ - abstract public void getWords(final WordComposer composer, final WordCallback callback); + abstract public void getWords(final WordComposer composer, final WordCallback callback, + final ProximityInfo proximityInfo); /** * Searches for pairs in the bigram dictionary that matches the previous word and all the @@ -83,7 +87,7 @@ public abstract class Dictionary { * @return true if the word exists, false otherwise */ abstract public boolean isValidWord(CharSequence word); - + /** * Compares the contents of the character array with the typed word and returns true if they * are the same. diff --git a/java/src/com/android/inputmethod/latin/DictionaryCollection.java b/java/src/com/android/inputmethod/latin/DictionaryCollection.java index 107840331..739153044 100644 --- a/java/src/com/android/inputmethod/latin/DictionaryCollection.java +++ b/java/src/com/android/inputmethod/latin/DictionaryCollection.java @@ -16,6 +16,8 @@ package com.android.inputmethod.latin; +import com.android.inputmethod.keyboard.ProximityInfo; + import java.util.Collection; import java.util.Collections; import java.util.List; @@ -47,9 +49,10 @@ public class DictionaryCollection extends Dictionary { } @Override - public void getWords(final WordComposer composer, final WordCallback callback) { + public void getWords(final WordComposer composer, final WordCallback callback, + final ProximityInfo proximityInfo) { for (final Dictionary dict : mDictionaries) - dict.getWords(composer, callback); + dict.getWords(composer, callback, proximityInfo); } @Override diff --git a/java/src/com/android/inputmethod/latin/ExpandableDictionary.java b/java/src/com/android/inputmethod/latin/ExpandableDictionary.java index 97a4a1816..35d1541ff 100644 --- a/java/src/com/android/inputmethod/latin/ExpandableDictionary.java +++ b/java/src/com/android/inputmethod/latin/ExpandableDictionary.java @@ -20,6 +20,7 @@ import android.content.Context; import android.os.AsyncTask; import com.android.inputmethod.keyboard.Keyboard; +import com.android.inputmethod.keyboard.ProximityInfo; import java.util.LinkedList; @@ -193,7 +194,8 @@ public class ExpandableDictionary extends Dictionary { } @Override - public void getWords(final WordComposer codes, final WordCallback callback) { + public void getWords(final WordComposer codes, final WordCallback callback, + final ProximityInfo proximityInfo) { synchronized (mUpdatingLock) { // If we need to update, start off a background task if (mRequiresReload) startDictionaryLoadingTaskLocked(); diff --git a/java/src/com/android/inputmethod/latin/LatinIME.java b/java/src/com/android/inputmethod/latin/LatinIME.java index d00ce78e1..6c91c454c 100644 --- a/java/src/com/android/inputmethod/latin/LatinIME.java +++ b/java/src/com/android/inputmethod/latin/LatinIME.java @@ -115,7 +115,7 @@ public class LatinIME extends InputMethodServiceCompatWrapper implements Keyboar private static final int SCREEN_ORIENTATION_CHANGE_DETECTION_DELAY = 2; private static final int ACCUMULATE_START_INPUT_VIEW_DELAY = 20; - private static final int RESTORE_KEYBOARD_STATE_DELAY = 200; + private static final int RESTORE_KEYBOARD_STATE_DELAY = 500; /** * The name of the scheme used by the Package Manager to warn of a new package installation, @@ -1374,7 +1374,8 @@ public class LatinIME extends InputMethodServiceCompatWrapper implements Keyboar } int code = primaryCode; - if (isAlphabet(code) && isSuggestionsRequested() && !isCursorTouchingWord()) { + if ((isAlphabet(code) || mSettingsValues.isSymbolExcludedFromWordSeparators(code)) + && isSuggestionsRequested() && !isCursorTouchingWord()) { if (!mHasUncommittedTypedChars) { mHasUncommittedTypedChars = true; mComposingStringBuilder.setLength(0); @@ -1599,7 +1600,8 @@ public class LatinIME extends InputMethodServiceCompatWrapper implements Keyboar } // getSuggestedWordBuilder handles gracefully a null value of prevWord final SuggestedWords.Builder builder = mSuggest.getSuggestedWordBuilder( - mKeyboardSwitcher.getKeyboardView(), wordComposer, prevWord); + mKeyboardSwitcher.getKeyboardView(), wordComposer, prevWord, + mKeyboardSwitcher.getLatinKeyboard().getProximityInfo()); boolean autoCorrectionAvailable = !mInputTypeNoAutoCorrect && mSuggest.hasAutoCorrection(); final CharSequence typedWord = wordComposer.getTypedWord(); @@ -1824,7 +1826,8 @@ public class LatinIME extends InputMethodServiceCompatWrapper implements Keyboar final CharSequence prevWord = EditingUtils.getThisWord(getCurrentInputConnection(), mSettingsValues.mWordSeparators); SuggestedWords.Builder builder = mSuggest.getSuggestedWordBuilder( - mKeyboardSwitcher.getKeyboardView(), sEmptyWordComposer, prevWord); + mKeyboardSwitcher.getKeyboardView(), sEmptyWordComposer, prevWord, + mKeyboardSwitcher.getLatinKeyboard().getProximityInfo()); if (builder.size() > 0) { // Explicitly supply an empty typed word (the no-second-arg version of diff --git a/java/src/com/android/inputmethod/latin/PrivateBinaryDictionaryGetter.java b/java/src/com/android/inputmethod/latin/PrivateBinaryDictionaryGetter.java deleted file mode 100644 index eb740e111..000000000 --- a/java/src/com/android/inputmethod/latin/PrivateBinaryDictionaryGetter.java +++ /dev/null @@ -1,29 +0,0 @@ -/* - * 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.content.Context; - -import java.util.List; -import java.util.Locale; - -class PrivateBinaryDictionaryGetter { - private PrivateBinaryDictionaryGetter() {} - public static List<AssetFileAddress> getDictionaryFiles(Locale locale, Context context) { - return null; - } -} diff --git a/java/src/com/android/inputmethod/latin/Settings.java b/java/src/com/android/inputmethod/latin/Settings.java index 84751264a..4c2627be3 100644 --- a/java/src/com/android/inputmethod/latin/Settings.java +++ b/java/src/com/android/inputmethod/latin/Settings.java @@ -103,6 +103,7 @@ public class Settings extends InputMethodSettingsActivity public final String mMagicSpaceSwappers; public final String mSuggestPuncs; public final SuggestedWords mSuggestPuncList; + private final String mSymbolsExcludedFromWordSeparators; // From preferences: public final boolean mSoundOn; // Sound setting private to Latin IME (see mSilentModeOn) @@ -152,10 +153,13 @@ public class Settings extends InputMethodSettingsActivity mMagicSpaceSwappers = res.getString(R.string.magic_space_swapping_symbols); String wordSeparators = mMagicSpaceStrippers + mMagicSpaceSwappers + res.getString(R.string.magic_space_promoting_symbols); - final String notWordSeparators = res.getString(R.string.non_word_separator_symbols); - for (int i = notWordSeparators.length() - 1; i >= 0; --i) { - wordSeparators = wordSeparators.replace(notWordSeparators.substring(i, i + 1), ""); + final String symbolsExcludedFromWordSeparators = + res.getString(R.string.symbols_excluded_from_word_separators); + for (int i = symbolsExcludedFromWordSeparators.length() - 1; i >= 0; --i) { + wordSeparators = wordSeparators.replace( + symbolsExcludedFromWordSeparators.substring(i, i + 1), ""); } + mSymbolsExcludedFromWordSeparators = symbolsExcludedFromWordSeparators; mWordSeparators = wordSeparators; mSuggestPuncs = res.getString(R.string.suggested_punctuations); // TODO: it would be nice not to recreate this each time we change the configuration @@ -197,6 +201,10 @@ public class Settings extends InputMethodSettingsActivity return mWordSeparators.contains(String.valueOf((char)code)); } + public boolean isSymbolExcludedFromWordSeparators(int code) { + return mSymbolsExcludedFromWordSeparators.contains(String.valueOf((char)code)); + } + public boolean isMagicSpaceStripper(int code) { return mMagicSpaceStrippers.contains(String.valueOf((char)code)); } diff --git a/java/src/com/android/inputmethod/latin/Suggest.java b/java/src/com/android/inputmethod/latin/Suggest.java index c2452b947..933a94176 100644 --- a/java/src/com/android/inputmethod/latin/Suggest.java +++ b/java/src/com/android/inputmethod/latin/Suggest.java @@ -22,6 +22,8 @@ import android.text.TextUtils; import android.util.Log; import android.view.View; +import com.android.inputmethod.keyboard.ProximityInfo; + import java.io.File; import java.util.ArrayList; import java.util.Arrays; @@ -263,9 +265,10 @@ public class Suggest implements Dictionary.WordCallback { * @param prevWordForBigram previous word (used only for bigram) * @return suggested words object. */ - public SuggestedWords getSuggestions(View view, WordComposer wordComposer, - CharSequence prevWordForBigram) { - return getSuggestedWordBuilder(view, wordComposer, prevWordForBigram).build(); + public SuggestedWords getSuggestions(final View view, final WordComposer wordComposer, + final CharSequence prevWordForBigram, final ProximityInfo proximityInfo) { + return getSuggestedWordBuilder(view, wordComposer, prevWordForBigram, + proximityInfo).build(); } private CharSequence capitalizeWord(boolean all, boolean first, CharSequence word) { @@ -299,8 +302,9 @@ public class Suggest implements Dictionary.WordCallback { } // TODO: cleanup dictionaries looking up and suggestions building with SuggestedWords.Builder - public SuggestedWords.Builder getSuggestedWordBuilder(View view, WordComposer wordComposer, - CharSequence prevWordForBigram) { + public SuggestedWords.Builder getSuggestedWordBuilder(final View view, + final WordComposer wordComposer, CharSequence prevWordForBigram, + final ProximityInfo proximityInfo) { LatinImeLogger.onStartSuggestion(prevWordForBigram); mAutoCorrection.init(); mIsFirstCharCapitalized = wordComposer.isFirstCharCapitalized(); @@ -365,7 +369,7 @@ public class Suggest implements Dictionary.WordCallback { if (key.equals(DICT_KEY_USER_UNIGRAM) || key.equals(DICT_KEY_WHITELIST)) continue; final Dictionary dictionary = mUnigramDictionaries.get(key); - dictionary.getWords(wordComposer, this); + dictionary.getWords(wordComposer, this, proximityInfo); } } CharSequence autoText = null; diff --git a/java/src/com/android/inputmethod/latin/SuggestedWords.java b/java/src/com/android/inputmethod/latin/SuggestedWords.java index b77cbd199..c1c46fa47 100644 --- a/java/src/com/android/inputmethod/latin/SuggestedWords.java +++ b/java/src/com/android/inputmethod/latin/SuggestedWords.java @@ -31,7 +31,7 @@ public class SuggestedWords { public final boolean mTypedWordValid; public final boolean mHasMinimalSuggestion; public final boolean mIsPunctuationSuggestions; - public final List<SuggestedWordInfo> mSuggestedWordInfoList; + private final List<SuggestedWordInfo> mSuggestedWordInfoList; private SuggestedWords(List<CharSequence> words, boolean typedWordValid, boolean hasMinimalSuggestion, boolean isPunctuationSuggestions, @@ -55,6 +55,10 @@ public class SuggestedWords { return mWords.get(pos); } + public SuggestedWordInfo getInfo(int pos) { + return mSuggestedWordInfoList != null ? mSuggestedWordInfoList.get(pos) : null; + } + public boolean hasAutoCorrectionWord() { return mHasMinimalSuggestion && size() > 1 && !mTypedWordValid; } diff --git a/java/src/com/android/inputmethod/latin/UserDictionary.java b/java/src/com/android/inputmethod/latin/UserDictionary.java index f93d24fe6..6608d8268 100644 --- a/java/src/com/android/inputmethod/latin/UserDictionary.java +++ b/java/src/com/android/inputmethod/latin/UserDictionary.java @@ -26,6 +26,8 @@ import android.net.Uri; import android.os.RemoteException; import android.provider.UserDictionary.Words; +import com.android.inputmethod.keyboard.ProximityInfo; + public class UserDictionary extends ExpandableDictionary { private static final String[] PROJECTION_QUERY = { @@ -150,8 +152,9 @@ public class UserDictionary extends ExpandableDictionary { } @Override - public synchronized void getWords(final WordComposer codes, final WordCallback callback) { - super.getWords(codes, callback); + public synchronized void getWords(final WordComposer codes, final WordCallback callback, + final ProximityInfo proximityInfo) { + super.getWords(codes, callback, proximityInfo); } @Override diff --git a/java/src/com/android/inputmethod/latin/WhitelistDictionary.java b/java/src/com/android/inputmethod/latin/WhitelistDictionary.java index 4377373d2..639c96681 100644 --- a/java/src/com/android/inputmethod/latin/WhitelistDictionary.java +++ b/java/src/com/android/inputmethod/latin/WhitelistDictionary.java @@ -21,6 +21,8 @@ import android.text.TextUtils; import android.util.Log; import android.util.Pair; +import com.android.inputmethod.keyboard.ProximityInfo; + import java.util.HashMap; public class WhitelistDictionary extends Dictionary { @@ -89,7 +91,8 @@ public class WhitelistDictionary extends Dictionary { // Not used for WhitelistDictionary. We use getWhitelistedWord() in Suggest.java instead @Override - public void getWords(WordComposer composer, WordCallback callback) { + public void getWords(final WordComposer composer, final WordCallback callback, + final ProximityInfo proximityInfo) { } @Override diff --git a/java/src/com/android/inputmethod/latin/spellcheck/SpellChecker.java b/java/src/com/android/inputmethod/latin/spellcheck/SpellChecker.java index 63c6d69d7..d7283515b 100644 --- a/java/src/com/android/inputmethod/latin/spellcheck/SpellChecker.java +++ b/java/src/com/android/inputmethod/latin/spellcheck/SpellChecker.java @@ -20,6 +20,7 @@ import android.content.Context; import android.content.res.Resources; import com.android.inputmethod.compat.ArraysCompatUtils; +import com.android.inputmethod.keyboard.ProximityInfo; import com.android.inputmethod.latin.Dictionary; import com.android.inputmethod.latin.Dictionary.DataType; import com.android.inputmethod.latin.Dictionary.WordCallback; @@ -109,7 +110,7 @@ public class SpellChecker { composer.add(character, new int[] { character }, WordComposer.NOT_A_COORDINATE, WordComposer.NOT_A_COORDINATE); } - mDictionary.getWords(composer, suggestionsGatherer); + mDictionary.getWords(composer, suggestionsGatherer, ProximityInfo.getDummyProximityInfo()); return suggestionsGatherer.getGatheredSuggestions(); } } diff --git a/native/src/correction_state.cpp b/native/src/correction_state.cpp index add6cf673..b2c77b00d 100644 --- a/native/src/correction_state.cpp +++ b/native/src/correction_state.cpp @@ -58,32 +58,49 @@ int CorrectionState::getFreqForSplitTwoWords(const int firstFreq, const int seco return CorrectionState::RankingAlgorithm::calcFreqForSplitTwoWords(firstFreq, secondFreq, this); } -int CorrectionState::getFinalFreq(const int inputIndex, const int outputIndex, const int freq) { - const bool sameLength = (mExcessivePos == mInputLength - 1) ? (mInputLength == inputIndex + 2) - : (mInputLength == inputIndex + 1); - const int matchCount = mMatchedCharCount; +int CorrectionState::getFinalFreq(const unsigned short *word, const int freq) { + if (mProximityInfo->sameAsTyped(word, mOutputIndex + 1) || mOutputIndex < MIN_SUGGEST_DEPTH) { + return -1; + } + const bool sameLength = (mExcessivePos == mInputLength - 1) ? (mInputLength == mInputIndex + 2) + : (mInputLength == mInputIndex + 1); return CorrectionState::RankingAlgorithm::calculateFinalFreq( - inputIndex, outputIndex, matchCount, freq, sameLength, this); + mInputIndex, mOutputIndex, mMatchedCharCount, freq, sameLength, this); } -void CorrectionState::initDepth() { - mMatchedCharCount = 0; +void CorrectionState::initProcessState( + const int matchCount, const int inputIndex, const int outputIndex) { + mMatchedCharCount = matchCount; + mInputIndex = inputIndex; + mOutputIndex = outputIndex; +} + +void CorrectionState::getProcessState(int *matchedCount, int *inputIndex, int *outputIndex) { + *matchedCount = mMatchedCharCount; + *inputIndex = mInputIndex; + *outputIndex = mOutputIndex; } void CorrectionState::charMatched() { ++mMatchedCharCount; } -void CorrectionState::goUpTree(const int matchCount) { - mMatchedCharCount = matchCount; +// TODO: remove +int CorrectionState::getOutputIndex() { + return mOutputIndex; } -void CorrectionState::slideTree(const int matchCount) { - mMatchedCharCount = matchCount; +// TODO: remove +int CorrectionState::getInputIndex() { + return mInputIndex; } -void CorrectionState::goDownTree(int *matchedCount) { - *matchedCount = mMatchedCharCount; +void CorrectionState::incrementInputIndex() { + ++mInputIndex; +} + +void CorrectionState::incrementOutputIndex() { + ++mOutputIndex; } CorrectionState::~CorrectionState() { diff --git a/native/src/correction_state.h b/native/src/correction_state.h index 7bbad5f5b..cc3c3e669 100644 --- a/native/src/correction_state.h +++ b/native/src/correction_state.h @@ -28,16 +28,25 @@ class ProximityInfo; class CorrectionState { public: + typedef enum { + ALLOW_ALL, + UNRELATED, + RELATED + } CorrectionStateType; + CorrectionState(const int typedLetterMultiplier, const int fullWordMultiplier); void initCorrectionState(const ProximityInfo *pi, const int inputLength); void setCorrectionParams(const int skipPos, const int excessivePos, const int transposedPos, const int spaceProximityPos, const int missingSpacePos); - void initDepth(); void checkState(); - void goUpTree(const int matchCount); - void slideTree(const int matchCount); - void goDownTree(int *matchedCount); + void initProcessState(const int matchCount, const int inputIndex, const int outputIndex); + void getProcessState(int *matchedCount, int *inputIndex, int *outputIndex); void charMatched(); + void incrementInputIndex(); + void incrementOutputIndex(); + int getOutputIndex(); + int getInputIndex(); + virtual ~CorrectionState(); int getSkipPos() const { return mSkipPos; @@ -55,7 +64,7 @@ public: return mMissingSpacePos; } int getFreqForSplitTwoWords(const int firstFreq, const int secondFreq); - int getFinalFreq(const int inputIndex, const int outputIndex, const int freq); + int getFinalFreq(const unsigned short *word, const int freq); private: @@ -71,6 +80,8 @@ private: int mMissingSpacePos; int mMatchedCharCount; + int mInputIndex; + int mOutputIndex; class RankingAlgorithm { public: diff --git a/native/src/unigram_dictionary.cpp b/native/src/unigram_dictionary.cpp index f5648d3df..b95da99a3 100644 --- a/native/src/unigram_dictionary.cpp +++ b/native/src/unigram_dictionary.cpp @@ -363,27 +363,25 @@ void UnigramDictionary::getSuggestionCandidates(const int skipPos, mStackSiblingPos[0] = rootPosition; mStackOutputIndex[0] = 0; mStackMatchedCount[0] = 0; - mCorrectionState->initDepth(); // Depth first search while (depth >= 0) { if (mStackChildCount[depth] > 0) { --mStackChildCount[depth]; bool traverseAllNodes = mStackTraverseAll[depth]; - int inputIndex = mStackInputIndex[depth]; int diffs = mStackDiffs[depth]; int siblingPos = mStackSiblingPos[depth]; - int outputIndex = mStackOutputIndex[depth]; int firstChildPos; - mCorrectionState->slideTree(mStackMatchedCount[depth]); + mCorrectionState->initProcessState( + mStackMatchedCount[depth], mStackInputIndex[depth], mStackOutputIndex[depth]); // depth will never be greater than maxDepth because in that case, // needsToTraverseChildrenNodes should be false - const bool needsToTraverseChildrenNodes = processCurrentNode(siblingPos, outputIndex, - maxDepth, traverseAllNodes, inputIndex, diffs, + const bool needsToTraverseChildrenNodes = processCurrentNode(siblingPos, + maxDepth, traverseAllNodes, diffs, mCorrectionState, &childCount, - &firstChildPos, &traverseAllNodes, &inputIndex, &diffs, - &siblingPos, &outputIndex); + &firstChildPos, &traverseAllNodes, &diffs, + &siblingPos); // Update next sibling pos mStackSiblingPos[depth] = siblingPos; if (needsToTraverseChildrenNodes) { @@ -391,21 +389,15 @@ void UnigramDictionary::getSuggestionCandidates(const int skipPos, ++depth; mStackChildCount[depth] = childCount; mStackTraverseAll[depth] = traverseAllNodes; - mStackInputIndex[depth] = inputIndex; mStackDiffs[depth] = diffs; mStackSiblingPos[depth] = firstChildPos; - mStackOutputIndex[depth] = outputIndex; - int matchedCount; - mCorrectionState->goDownTree(&matchedCount); - mStackMatchedCount[depth] = matchedCount; - } else { - mCorrectionState->slideTree(mStackMatchedCount[depth]); + mCorrectionState->getProcessState(&mStackMatchedCount[depth], + &mStackInputIndex[depth], &mStackOutputIndex[depth]); } } else { // Goes to parent sibling node --depth; - mCorrectionState->goUpTree(mStackMatchedCount[depth]); } } } @@ -446,13 +438,11 @@ inline bool UnigramDictionary::needsToSkipCurrentNode(const unsigned short c, } -inline void UnigramDictionary::onTerminal(unsigned short int* word, const int outputIndex, - const int inputIndex, const int freq, CorrectionState *correctionState) { - if (!mProximityInfo->sameAsTyped(word, outputIndex + 1) && outputIndex >= MIN_SUGGEST_DEPTH) { - const int finalFreq = correctionState->getFinalFreq(inputIndex, outputIndex, freq); - if (finalFreq >= 0) { - addWord(word, outputIndex + 1, finalFreq); - } +inline void UnigramDictionary::onTerminal( + unsigned short int* word, const int freq, CorrectionState *correctionState) { + const int finalFreq = correctionState->getFinalFreq(word, freq); + if (finalFreq >= 0) { + addWord(word, correctionState->getOutputIndex() + 1, finalFreq); } } @@ -667,12 +657,10 @@ int UnigramDictionary::getBigramPosition(int pos, unsigned short *word, int offs // there aren't any more nodes at this level, it merely returns the address of the first byte after // the current node in nextSiblingPosition. Thus, the caller must keep count of the nodes at any // given level, as output into newCount when traversing this level's parent. -inline bool UnigramDictionary::processCurrentNode(const int initialPos, const int initialOutputPos, - const int maxDepth, const bool initialTraverseAllNodes, int inputIndex, - const int initialDiffs, +inline bool UnigramDictionary::processCurrentNode(const int initialPos, const int maxDepth, + const bool initialTraverseAllNodes, const int initialDiffs, CorrectionState *correctionState, int *newCount, int *newChildrenPosition, - bool *newTraverseAllNodes, int *newInputIndex, int *newDiffs, - int *nextSiblingPosition, int *newOutputIndex) { + bool *newTraverseAllNodes, int *newDiffs, int *nextSiblingPosition) { const int skipPos = correctionState->getSkipPos(); const int excessivePos = correctionState->getExcessivePos(); const int transposedPos = correctionState->getTransposedPos(); @@ -680,9 +668,9 @@ inline bool UnigramDictionary::processCurrentNode(const int initialPos, const in correctionState->checkState(); } int pos = initialPos; - int internalOutputPos = initialOutputPos; int traverseAllNodes = initialTraverseAllNodes; int diffs = initialDiffs; + const int initialInputIndex = correctionState->getInputIndex(); // Flags contain the following information: // - Address type (MASK_GROUP_ADDRESS_TYPE) on two bits: @@ -726,16 +714,18 @@ inline bool UnigramDictionary::processCurrentNode(const int initialPos, const in // This has to be done for each virtual char (this forwards the "inputIndex" which // is the index in the user-inputted chars, as read by proximity chars. - if (excessivePos == internalOutputPos && inputIndex < mInputLength - 1) { - ++inputIndex; + if (excessivePos == correctionState->getOutputIndex() + && correctionState->getInputIndex() < mInputLength - 1) { + correctionState->incrementInputIndex(); } - if (traverseAllNodes || needsToSkipCurrentNode(c, inputIndex, skipPos, internalOutputPos)) { - mWord[internalOutputPos] = c; + if (traverseAllNodes || needsToSkipCurrentNode( + c, correctionState->getInputIndex(), skipPos, correctionState->getOutputIndex())) { + mWord[correctionState->getOutputIndex()] = c; if (traverseAllNodes && isTerminal) { // The frequency should be here, because we come here only if this is actually // a terminal node, and we are on its last char. const int freq = BinaryFormat::readFrequencyWithoutMovingPointer(DICT_ROOT, pos); - onTerminal(mWord, internalOutputPos, inputIndex, freq, mCorrectionState); + onTerminal(mWord, freq, mCorrectionState); } if (!hasChildren) { // If we don't have children here, that means we finished processing all @@ -750,11 +740,15 @@ inline bool UnigramDictionary::processCurrentNode(const int initialPos, const in return false; } } else { - int inputIndexForProximity = inputIndex; + int inputIndexForProximity = correctionState->getInputIndex(); if (transposedPos >= 0) { - if (inputIndex == transposedPos) ++inputIndexForProximity; - if (inputIndex == (transposedPos + 1)) --inputIndexForProximity; + if (correctionState->getInputIndex() == transposedPos) { + ++inputIndexForProximity; + } + if (correctionState->getInputIndex() == (transposedPos + 1)) { + --inputIndexForProximity; + } } int matchedProximityCharId = mProximityInfo->getMatchedProximityId( @@ -775,18 +769,31 @@ inline bool UnigramDictionary::processCurrentNode(const int initialPos, const in BinaryFormat::skipChildrenPosAndAttributes(DICT_ROOT, flags, pos); return false; } - mWord[internalOutputPos] = c; + mWord[correctionState->getOutputIndex()] = c; // If inputIndex is greater than mInputLength, that means there is no // proximity chars. So, we don't need to check proximity. if (ProximityInfo::SAME_OR_ACCENTED_OR_CAPITALIZED_CHAR == matchedProximityCharId) { correctionState->charMatched(); } - const bool isSameAsUserTypedLength = mInputLength == inputIndex + 1 - || (excessivePos == mInputLength - 1 && inputIndex == mInputLength - 2); + const bool isSameAsUserTypedLength = mInputLength + == correctionState->getInputIndex() + 1 + || (excessivePos == mInputLength - 1 + && correctionState->getInputIndex() == mInputLength - 2); if (isSameAsUserTypedLength && isTerminal) { const int freq = BinaryFormat::readFrequencyWithoutMovingPointer(DICT_ROOT, pos); - onTerminal(mWord, internalOutputPos, inputIndex, freq, mCorrectionState); + onTerminal(mWord, freq, mCorrectionState); } + // Start traversing all nodes after the index exceeds the user typed length + traverseAllNodes = isSameAsUserTypedLength; + diffs = diffs + + ((ProximityInfo::NEAR_PROXIMITY_CHAR == matchedProximityCharId) ? 1 : 0); + // Finally, we are ready to go to the next character, the next "virtual node". + // We should advance the input index. + // We do this in this branch of the 'if traverseAllNodes' because we are still matching + // characters to input; the other branch is not matching them but searching for + // completions, this is why it does not have to do it. + correctionState->incrementInputIndex(); + // This character matched the typed character (enough to traverse the node at least) // so we just evaluated it. Now we should evaluate this virtual node's children - that // is, if it has any. If it has no children, we're done here - so we skip the end of @@ -799,19 +806,10 @@ inline bool UnigramDictionary::processCurrentNode(const int initialPos, const in BinaryFormat::skipChildrenPosAndAttributes(DICT_ROOT, flags, pos); return false; } - // Start traversing all nodes after the index exceeds the user typed length - traverseAllNodes = isSameAsUserTypedLength; - diffs = diffs - + ((ProximityInfo::NEAR_PROXIMITY_CHAR == matchedProximityCharId) ? 1 : 0); - // Finally, we are ready to go to the next character, the next "virtual node". - // We should advance the input index. - // We do this in this branch of the 'if traverseAllNodes' because we are still matching - // characters to input; the other branch is not matching them but searching for - // completions, this is why it does not have to do it. - ++inputIndex; } // Optimization: Prune out words that are too long compared to how much was typed. - if (internalOutputPos >= maxDepth || diffs > mMaxEditDistance) { + if (isTerminal + && (correctionState->getOutputIndex() >= maxDepth || diffs > mMaxEditDistance)) { // We are giving up parsing this node and its children. Skip the rest of the node, // output the sibling position, and return that we don't want to traverse children. if (!isLastChar) { @@ -822,18 +820,18 @@ inline bool UnigramDictionary::processCurrentNode(const int initialPos, const in BinaryFormat::skipChildrenPosAndAttributes(DICT_ROOT, flags, pos); return false; } + // Also, the next char is one "virtual node" depth more than this char. + correctionState->incrementOutputIndex(); // Prepare for the next character. Promote the prefetched char to current char - the loop // will take care of prefetching the next. If we finally found our last char, nextc will // contain NOT_A_CHARACTER. c = nextc; - // Also, the next char is one "virtual node" depth more than this char. - ++internalOutputPos; } while (NOT_A_CHARACTER != c); // If inputIndex is greater than mInputLength, that means there are no proximity chars. // Here, that's all we are interested in so we don't need to check for isSameAsUserTypedLength. - if (mInputLength <= *newInputIndex) { + if (mInputLength <= initialInputIndex) { traverseAllNodes = true; } @@ -841,8 +839,6 @@ inline bool UnigramDictionary::processCurrentNode(const int initialPos, const in // variables. Output them to the caller. *newTraverseAllNodes = traverseAllNodes; *newDiffs = diffs; - *newInputIndex = inputIndex; - *newOutputIndex = internalOutputPos; // Now we finished processing this node, and we want to traverse children. If there are no // children, we can't come here. diff --git a/native/src/unigram_dictionary.h b/native/src/unigram_dictionary.h index c67eaf6e0..cb86da41c 100644 --- a/native/src/unigram_dictionary.h +++ b/native/src/unigram_dictionary.h @@ -94,18 +94,14 @@ private: const int inputLength, const int missingSpacePos, CorrectionState *correctionState); void getMistypedSpaceWords( const int inputLength, const int spaceProximityPos, CorrectionState *correctionState); - void onTerminal(unsigned short int* word, const int depth, - const int inputIndex, const int freq, - CorrectionState *correctionState); + void onTerminal(unsigned short int* word, const int freq, CorrectionState *correctionState); bool needsToSkipCurrentNode(const unsigned short c, const int inputIndex, const int skipPos, const int depth); // Process a node by considering proximity, missing and excessive character - bool processCurrentNode(const int initialPos, const int initialDepth, - const int maxDepth, const bool initialTraverseAllNodes, int inputIndex, - const int initialDiffs, + bool processCurrentNode(const int initialPos, const int maxDepth, + const bool initialTraverseAllNodes, const int initialDiffs, CorrectionState *correctionState, int *newCount, int *newChildPosition, - bool *newTraverseAllNodes, int *newInputIndex, int *newDiffs, - int *nextSiblingPosition, int *nextOutputIndex); + bool *newTraverseAllNodes, int *newDiffs, int *nextSiblingPosition); int getMostFrequentWordLike(const int startInputIndex, const int inputLength, unsigned short *word); int getMostFrequentWordLikeInner(const uint16_t* const inWord, const int length, diff --git a/tests/src/com/android/inputmethod/latin/SuggestHelper.java b/tests/src/com/android/inputmethod/latin/SuggestHelper.java index 773674384..48fe81715 100644 --- a/tests/src/com/android/inputmethod/latin/SuggestHelper.java +++ b/tests/src/com/android/inputmethod/latin/SuggestHelper.java @@ -28,7 +28,7 @@ import java.io.File; public class SuggestHelper { protected final Suggest mSuggest; - private final LatinKeyboard mKeyboard; + protected final LatinKeyboard mKeyboard; private final KeyDetector mKeyDetector; public SuggestHelper(Context context, int dictionaryId, KeyboardId keyboardId) { @@ -94,19 +94,22 @@ public class SuggestHelper { // TODO: This may be slow, but is OK for test so far. public SuggestedWords getSuggestions(CharSequence typed) { - return mSuggest.getSuggestions(null, createWordComposer(typed), null); + return mSuggest.getSuggestions(null, createWordComposer(typed), null, + mKeyboard.getProximityInfo()); } public CharSequence getFirstSuggestion(CharSequence typed) { WordComposer word = createWordComposer(typed); - SuggestedWords suggestions = mSuggest.getSuggestions(null, word, null); + SuggestedWords suggestions = mSuggest.getSuggestions(null, word, null, + mKeyboard.getProximityInfo()); // Note that suggestions.getWord(0) is the word user typed. return suggestions.size() > 1 ? suggestions.getWord(1) : null; } public CharSequence getAutoCorrection(CharSequence typed) { WordComposer word = createWordComposer(typed); - SuggestedWords suggestions = mSuggest.getSuggestions(null, word, null); + SuggestedWords suggestions = mSuggest.getSuggestions(null, word, null, + mKeyboard.getProximityInfo()); // Note that suggestions.getWord(0) is the word user typed. return (suggestions.size() > 1 && mSuggest.hasAutoCorrection()) ? suggestions.getWord(1) : null; @@ -114,7 +117,8 @@ public class SuggestHelper { public int getSuggestIndex(CharSequence typed, CharSequence expected) { WordComposer word = createWordComposer(typed); - SuggestedWords suggestions = mSuggest.getSuggestions(null, word, null); + SuggestedWords suggestions = mSuggest.getSuggestions(null, word, null, + mKeyboard.getProximityInfo()); // Note that suggestions.getWord(0) is the word user typed. for (int i = 1; i < suggestions.size(); i++) { if (TextUtils.equals(suggestions.getWord(i), expected)) @@ -126,21 +130,23 @@ public class SuggestHelper { private void getBigramSuggestions(CharSequence previous, CharSequence typed) { if (!TextUtils.isEmpty(previous) && (typed.length() > 1)) { WordComposer firstChar = createWordComposer(Character.toString(typed.charAt(0))); - mSuggest.getSuggestions(null, firstChar, previous); + mSuggest.getSuggestions(null, firstChar, previous, mKeyboard.getProximityInfo()); } } public CharSequence getBigramFirstSuggestion(CharSequence previous, CharSequence typed) { WordComposer word = createWordComposer(typed); getBigramSuggestions(previous, typed); - SuggestedWords suggestions = mSuggest.getSuggestions(null, word, previous); + SuggestedWords suggestions = mSuggest.getSuggestions(null, word, previous, + mKeyboard.getProximityInfo()); return suggestions.size() > 1 ? suggestions.getWord(1) : null; } public CharSequence getBigramAutoCorrection(CharSequence previous, CharSequence typed) { WordComposer word = createWordComposer(typed); getBigramSuggestions(previous, typed); - SuggestedWords suggestions = mSuggest.getSuggestions(null, word, previous); + SuggestedWords suggestions = mSuggest.getSuggestions(null, word, previous, + mKeyboard.getProximityInfo()); return (suggestions.size() > 1 && mSuggest.hasAutoCorrection()) ? suggestions.getWord(1) : null; } @@ -149,7 +155,8 @@ public class SuggestHelper { CharSequence expected) { WordComposer word = createWordComposer(typed); getBigramSuggestions(previous, typed); - SuggestedWords suggestions = mSuggest.getSuggestions(null, word, previous); + SuggestedWords suggestions = mSuggest.getSuggestions(null, word, previous, + mKeyboard.getProximityInfo()); for (int i = 1; i < suggestions.size(); i++) { if (TextUtils.equals(suggestions.getWord(i), expected)) return i; diff --git a/tests/src/com/android/inputmethod/latin/UserBigramSuggestHelper.java b/tests/src/com/android/inputmethod/latin/UserBigramSuggestHelper.java index 46e5a2454..a7d532e7b 100644 --- a/tests/src/com/android/inputmethod/latin/UserBigramSuggestHelper.java +++ b/tests/src/com/android/inputmethod/latin/UserBigramSuggestHelper.java @@ -58,7 +58,7 @@ public class UserBigramSuggestHelper extends SuggestHelper { flushUserBigrams(); if (!TextUtils.isEmpty(previous) && !TextUtils.isEmpty(Character.toString(typed))) { WordComposer firstChar = createWordComposer(Character.toString(typed)); - mSuggest.getSuggestions(null, firstChar, previous); + mSuggest.getSuggestions(null, firstChar, previous, mKeyboard.getProximityInfo()); boolean reloading = mUserBigram.reloadDictionaryIfRequired(); if (reloading) mUserBigram.waitForDictionaryLoading(); mUserBigram.getBigrams(firstChar, previous, mSuggest); |