diff options
17 files changed, 201 insertions, 136 deletions
diff --git a/java/src/com/android/inputmethod/keyboard/KeyboardSwitcher.java b/java/src/com/android/inputmethod/keyboard/KeyboardSwitcher.java index d15f14f88..4e41b77ce 100644 --- a/java/src/com/android/inputmethod/keyboard/KeyboardSwitcher.java +++ b/java/src/com/android/inputmethod/keyboard/KeyboardSwitcher.java @@ -207,7 +207,8 @@ public final class KeyboardSwitcher implements KeyboardState.SwitchActions { * Update keyboard shift state triggered by connected EditText status change. */ public void updateShiftState() { - mState.onUpdateShiftState(mLatinIME.getCurrentAutoCapsState()); + mState.onUpdateShiftState(mLatinIME.getCurrentAutoCapsState(), + mLatinIME.getCurrentRecapitalizeState()); } // TODO: Remove this method. Come up with a more comprehensive way to reset the keyboard layout @@ -276,7 +277,8 @@ public final class KeyboardSwitcher implements KeyboardState.SwitchActions { // Implements {@link KeyboardState.SwitchActions}. @Override public void requestUpdatingShiftState() { - mState.onUpdateShiftState(mLatinIME.getCurrentAutoCapsState()); + mState.onUpdateShiftState(mLatinIME.getCurrentAutoCapsState(), + mLatinIME.getCurrentRecapitalizeState()); } // Implements {@link KeyboardState.SwitchActions}. diff --git a/java/src/com/android/inputmethod/keyboard/internal/KeyboardState.java b/java/src/com/android/inputmethod/keyboard/internal/KeyboardState.java index 95d9ccb58..b1d499702 100644 --- a/java/src/com/android/inputmethod/keyboard/internal/KeyboardState.java +++ b/java/src/com/android/inputmethod/keyboard/internal/KeyboardState.java @@ -20,6 +20,7 @@ import android.text.TextUtils; import android.util.Log; import com.android.inputmethod.latin.Constants; +import com.android.inputmethod.latin.RecapitalizeStatus; /** * Keyboard state machine. @@ -29,7 +30,7 @@ import com.android.inputmethod.latin.Constants; * The input events are {@link #onLoadKeyboard()}, {@link #onSaveKeyboardState()}, * {@link #onPressKey(int, boolean, int)}, {@link #onReleaseKey(int, boolean)}, * {@link #onCodeInput(int, boolean, int)}, {@link #onCancelInput(boolean)}, - * {@link #onUpdateShiftState(int)}, {@link #onLongPressTimeout(int)}. + * {@link #onUpdateShiftState(int, int)}, {@link #onLongPressTimeout(int)}. * * The actions are {@link SwitchActions}'s methods. */ @@ -48,7 +49,7 @@ public final class KeyboardState { public void setSymbolsShiftedKeyboard(); /** - * Request to call back {@link KeyboardState#onUpdateShiftState(int)}. + * Request to call back {@link KeyboardState#onUpdateShiftState(int, int)}. */ public void requestUpdatingShiftState(); @@ -80,6 +81,7 @@ public final class KeyboardState { private boolean mIsSymbolShifted; private boolean mPrevMainKeyboardWasShiftLocked; private boolean mPrevSymbolsKeyboardWasShifted; + private int mRecapitalizeMode; // For handling long press. private boolean mLongPressShiftLockFired; @@ -110,6 +112,7 @@ public final class KeyboardState { public KeyboardState(final SwitchActions switchActions) { mSwitchActions = switchActions; + mRecapitalizeMode = RecapitalizeStatus.NOT_A_RECAPITALIZE_MODE; } public void onLoadKeyboard() { @@ -283,6 +286,7 @@ public final class KeyboardState { mSwitchActions.setAlphabetKeyboard(); mIsAlphabetMode = true; mIsSymbolShifted = false; + mRecapitalizeMode = RecapitalizeStatus.NOT_A_RECAPITALIZE_MODE; mSwitchState = SWITCH_STATE_ALPHA; mSwitchActions.requestUpdatingShiftState(); } @@ -386,11 +390,13 @@ public final class KeyboardState { } } - public void onUpdateShiftState(final int autoCaps) { + public void onUpdateShiftState(final int autoCaps, final int recapitalizeMode) { if (DEBUG_EVENT) { - Log.d(TAG, "onUpdateShiftState: autoCaps=" + autoCaps + " " + this); + Log.d(TAG, "onUpdateShiftState: autoCaps=" + autoCaps + ", recapitalizeMode=" + + recapitalizeMode + " " + this); } - updateAlphabetShiftState(autoCaps); + mRecapitalizeMode = recapitalizeMode; + updateAlphabetShiftState(autoCaps, recapitalizeMode); } // TODO: Remove this method. Come up with a more comprehensive way to reset the keyboard layout @@ -402,8 +408,28 @@ public final class KeyboardState { resetKeyboardStateToAlphabet(); } - private void updateAlphabetShiftState(final int autoCaps) { + private void updateShiftStateForRecapitalize(final int recapitalizeMode) { + switch (recapitalizeMode) { + case RecapitalizeStatus.CAPS_MODE_ALL_UPPER: + setShifted(SHIFT_LOCK_SHIFTED); + break; + case RecapitalizeStatus.CAPS_MODE_FIRST_WORD_UPPER: + setShifted(AUTOMATIC_SHIFT); + break; + case RecapitalizeStatus.CAPS_MODE_ALL_LOWER: + case RecapitalizeStatus.CAPS_MODE_ORIGINAL_MIXED_CASE: + default: + setShifted(UNSHIFT); + } + } + + private void updateAlphabetShiftState(final int autoCaps, final int recapitalizeMode) { if (!mIsAlphabetMode) return; + if (RecapitalizeStatus.NOT_A_RECAPITALIZE_MODE != recapitalizeMode) { + // We are recapitalizing. Match the keyboard to the current recapitalize state. + updateShiftStateForRecapitalize(recapitalizeMode); + return; + } if (!mShiftKeyState.isReleasing()) { // Ignore update shift state event while the shift key is being pressed (including // chording). @@ -421,6 +447,9 @@ public final class KeyboardState { private void onPressShift() { mLongPressShiftLockFired = false; + // If we are recapitalizing, we don't do any of the normal processing, including + // importantly the double tap timer. + if (RecapitalizeStatus.NOT_A_RECAPITALIZE_MODE != mRecapitalizeMode) return; if (mIsAlphabetMode) { mIsInDoubleTapShiftKey = mSwitchActions.isInDoubleTapTimeout(); if (!mIsInDoubleTapShiftKey) { @@ -467,7 +496,11 @@ public final class KeyboardState { } private void onReleaseShift(final boolean withSliding) { - if (mIsAlphabetMode) { + if (RecapitalizeStatus.NOT_A_RECAPITALIZE_MODE != mRecapitalizeMode) { + // We are recapitalizing. We should match the keyboard state to the recapitalize + // state in priority. + updateShiftStateForRecapitalize(mRecapitalizeMode); + } else if (mIsAlphabetMode) { final boolean isShiftLocked = mAlphabetShiftState.isShiftLocked(); mIsInAlphabetUnshiftedFromShifted = false; if (mIsInDoubleTapShiftKey) { @@ -597,7 +630,7 @@ public final class KeyboardState { // If the code is a letter, update keyboard shift state. if (Constants.isLetterCode(code)) { - updateAlphabetShiftState(autoCaps); + updateAlphabetShiftState(autoCaps, RecapitalizeStatus.NOT_A_RECAPITALIZE_MODE); } } diff --git a/java/src/com/android/inputmethod/latin/LatinIME.java b/java/src/com/android/inputmethod/latin/LatinIME.java index bf4c22d23..0a6f2ab00 100644 --- a/java/src/com/android/inputmethod/latin/LatinIME.java +++ b/java/src/com/android/inputmethod/latin/LatinIME.java @@ -161,7 +161,7 @@ public final class LatinIME extends InputMethodService implements KeyboardAction mPositionalInfoForUserDictPendingAddition = null; private final WordComposer mWordComposer = new WordComposer(); private final RichInputConnection mConnection = new RichInputConnection(this); - private RecapitalizeStatus mRecapitalizeStatus = null; + private final RecapitalizeStatus mRecapitalizeStatus = new RecapitalizeStatus(); // Keep track of the last selection range to decide if we need to show word alternatives private static final int NOT_A_CURSOR_POSITION = -1; @@ -742,6 +742,7 @@ public final class LatinIME extends InputMethodService implements KeyboardAction resetComposingState(true /* alsoResetLastComposedWord */); mDeleteCount = 0; mSpaceState = SPACE_STATE_NONE; + mRecapitalizeStatus.deactivate(); mCurrentlyPressedHardwareKeys.clear(); if (mSuggestionStripView != null) { @@ -925,7 +926,7 @@ public final class LatinIME extends InputMethodService implements KeyboardAction // We moved the cursor. If we are touching a word, we need to resume suggestion. mHandler.postResumeSuggestions(); // Reset the last recapitalization. - mRecapitalizeStatus = null; + mRecapitalizeStatus.deactivate(); mKeyboardSwitcher.updateShiftState(); } mExpectingUpdateSelection = false; @@ -995,8 +996,6 @@ public final class LatinIME extends InputMethodService implements KeyboardAction } } if (!mSettings.getCurrent().isApplicationSpecifiedCompletionsOn()) return; - mApplicationSpecifiedCompletions = - CompletionInfoUtils.removeNulls(applicationSpecifiedCompletions); if (applicationSpecifiedCompletions == null) { clearSuggestionStrip(); if (ProductionFlag.USES_DEVELOPMENT_ONLY_DIAGNOSTICS) { @@ -1004,6 +1003,8 @@ public final class LatinIME extends InputMethodService implements KeyboardAction } return; } + mApplicationSpecifiedCompletions = + CompletionInfoUtils.removeNulls(applicationSpecifiedCompletions); final ArrayList<SuggestedWords.SuggestedWordInfo> applicationSuggestedWords = SuggestedWords.getFromApplicationSpecifiedCompletions( @@ -1179,6 +1180,15 @@ public final class LatinIME extends InputMethodService implements KeyboardAction SPACE_STATE_PHANTOM == mSpaceState); } + public int getCurrentRecapitalizeState() { + if (!mRecapitalizeStatus.isActive() + || !mRecapitalizeStatus.isSetAt(mLastSelectionStart, mLastSelectionEnd)) { + // Not recapitalizing at the moment + return RecapitalizeStatus.NOT_A_RECAPITALIZE_MODE; + } + return mRecapitalizeStatus.getCurrentMode(); + } + // Factor in auto-caps and manual caps and compute the current caps mode. private int getActualCapsMode() { final int keyboardShiftMode = mKeyboardSwitcher.getKeyboardShiftMode(); @@ -1391,7 +1401,12 @@ public final class LatinIME extends InputMethodService implements KeyboardAction case Constants.CODE_SHIFT: // Note: calling back to the keyboard on Shift key is handled in onPressKey() // and onReleaseKey(). - handleRecapitalize(); + final Keyboard currentKeyboard = switcher.getKeyboard(); + if (null != currentKeyboard && currentKeyboard.mId.isAlphabetKeyboard()) { + // TODO: Instead of checking for alphabetic keyboard here, separate keycodes for + // alphabetic shift and shift while in symbol layout. + handleRecapitalize(); + } break; case Constants.CODE_SWITCH_ALPHA_SYMBOL: // Note: calling back to the keyboard on symbol key is handled in onPressKey() @@ -1953,10 +1968,9 @@ public final class LatinIME extends InputMethodService implements KeyboardAction private void handleRecapitalize() { if (mLastSelectionStart == mLastSelectionEnd) return; // No selection // If we have a recapitalize in progress, use it; otherwise, create a new one. - if (null == mRecapitalizeStatus + if (!mRecapitalizeStatus.isActive() || !mRecapitalizeStatus.isSetAt(mLastSelectionStart, mLastSelectionEnd)) { - mRecapitalizeStatus = - new RecapitalizeStatus(mLastSelectionStart, mLastSelectionEnd, + mRecapitalizeStatus.initialize(mLastSelectionStart, mLastSelectionEnd, mConnection.getSelectedText(0 /* flags, 0 for no styles */).toString(), mSettings.getCurrentLocale(), mSettings.getWordSeparators()); // We trim leading and trailing whitespace. @@ -1979,6 +1993,8 @@ public final class LatinIME extends InputMethodService implements KeyboardAction mLastSelectionStart = mRecapitalizeStatus.getNewCursorStart(); mLastSelectionEnd = mRecapitalizeStatus.getNewCursorEnd(); mConnection.setSelection(mLastSelectionStart, mLastSelectionEnd); + // Match the keyboard to the new state. + mKeyboardSwitcher.updateShiftState(); } // Returns true if we did an autocorrection, false otherwise. @@ -2413,6 +2429,7 @@ public final class LatinIME extends InputMethodService implements KeyboardAction if (!mConnection.isCursorTouchingWord(mSettings.getCurrent())) return; final Range range = mConnection.getWordRangeAtCursor(mSettings.getWordSeparators(), 0 /* additionalPrecedingWordsCount */); + if (null == range) return; // Happens if we don't have an input connection at all final ArrayList<SuggestedWordInfo> suggestions = CollectionUtils.newArrayList(); final String typedWord = range.mWord.toString(); if (range.mWord instanceof SpannableString) { diff --git a/java/src/com/android/inputmethod/latin/RecapitalizeStatus.java b/java/src/com/android/inputmethod/latin/RecapitalizeStatus.java index 9edd3a160..8a704ab42 100644 --- a/java/src/com/android/inputmethod/latin/RecapitalizeStatus.java +++ b/java/src/com/android/inputmethod/latin/RecapitalizeStatus.java @@ -24,6 +24,7 @@ import java.util.Locale; * The status of the current recapitalize process. */ public class RecapitalizeStatus { + public static final int NOT_A_RECAPITALIZE_MODE = -1; public static final int CAPS_MODE_ORIGINAL_MIXED_CASE = 0; public static final int CAPS_MODE_ALL_LOWER = 1; public static final int CAPS_MODE_FIRST_WORD_UPPER = 2; @@ -37,6 +38,7 @@ public class RecapitalizeStatus { CAPS_MODE_FIRST_WORD_UPPER, CAPS_MODE_ALL_UPPER }; + private static final int getStringMode(final String string, final String separators) { if (StringUtils.isIdenticalAfterUpcase(string)) { return CAPS_MODE_ALL_UPPER; @@ -50,24 +52,29 @@ public class RecapitalizeStatus { } /** - * We store the location of the cursor and the string that was there before the undoable + * We store the location of the cursor and the string that was there before the recapitalize * action was done, and the location of the cursor and the string that was there after. */ private int mCursorStartBefore; - private int mCursorEndBefore; private String mStringBefore; private int mCursorStartAfter; private int mCursorEndAfter; private int mRotationStyleCurrentIndex; - private final boolean mSkipOriginalMixedCaseMode; - private final Locale mLocale; - private final String mSeparators; + private boolean mSkipOriginalMixedCaseMode; + private Locale mLocale; + private String mSeparators; private String mStringAfter; + private boolean mIsActive; + + public RecapitalizeStatus() { + // By default, initialize with dummy values that won't match any real recapitalize. + initialize(-1, -1, "", Locale.getDefault(), ""); + deactivate(); + } - public RecapitalizeStatus(final int cursorStart, final int cursorEnd, final String string, + public void initialize(final int cursorStart, final int cursorEnd, final String string, final Locale locale, final String separators) { mCursorStartBefore = cursorStart; - mCursorEndBefore = cursorEnd; mStringBefore = string; mCursorStartAfter = cursorStart; mCursorEndAfter = cursorEnd; @@ -89,6 +96,15 @@ public class RecapitalizeStatus { mRotationStyleCurrentIndex = currentMode; mSkipOriginalMixedCaseMode = true; } + mIsActive = true; + } + + public void deactivate() { + mIsActive = false; + } + + public boolean isActive() { + return mIsActive; } public boolean isSetAt(final int cursorStart, final int cursorEnd) { @@ -110,23 +126,23 @@ public class RecapitalizeStatus { } ++count; switch (ROTATION_STYLE[mRotationStyleCurrentIndex]) { - case CAPS_MODE_ORIGINAL_MIXED_CASE: - mStringAfter = mStringBefore; - break; - case CAPS_MODE_ALL_LOWER: - mStringAfter = mStringBefore.toLowerCase(mLocale); - break; - case CAPS_MODE_FIRST_WORD_UPPER: - mStringAfter = StringUtils.capitalizeEachWord(mStringBefore, mSeparators, - mLocale); - break; - case CAPS_MODE_ALL_UPPER: - mStringAfter = mStringBefore.toUpperCase(mLocale); - break; - default: - mStringAfter = mStringBefore; + case CAPS_MODE_ORIGINAL_MIXED_CASE: + mStringAfter = mStringBefore; + break; + case CAPS_MODE_ALL_LOWER: + mStringAfter = mStringBefore.toLowerCase(mLocale); + break; + case CAPS_MODE_FIRST_WORD_UPPER: + mStringAfter = StringUtils.capitalizeEachWord(mStringBefore, mSeparators, + mLocale); + break; + case CAPS_MODE_ALL_UPPER: + mStringAfter = mStringBefore.toUpperCase(mLocale); + break; + default: + mStringAfter = mStringBefore; } - } while (mStringAfter.equals(oldResult) && count < 5); + } while (mStringAfter.equals(oldResult) && count < ROTATION_STYLE.length + 1); mCursorEndAfter = mCursorStartAfter + mStringAfter.length(); } @@ -148,7 +164,7 @@ public class RecapitalizeStatus { if (!Character.isWhitespace(codePoint)) break; } if (0 != nonWhitespaceStart || len != nonWhitespaceEnd) { - mCursorEndBefore = mCursorEndAfter = mCursorStartBefore + nonWhitespaceEnd; + mCursorEndAfter = mCursorStartBefore + nonWhitespaceEnd; mCursorStartBefore = mCursorStartAfter = mCursorStartBefore + nonWhitespaceStart; mStringAfter = mStringBefore = mStringBefore.substring(nonWhitespaceStart, nonWhitespaceEnd); @@ -166,4 +182,8 @@ public class RecapitalizeStatus { public int getNewCursorEnd() { return mCursorEndAfter; } + + public int getCurrentMode() { + return ROTATION_STYLE[mRotationStyleCurrentIndex]; + } } diff --git a/native/jni/src/binary_format.h b/native/jni/src/binary_format.h index 1c4061fd8..2d2e19501 100644 --- a/native/jni/src/binary_format.h +++ b/native/jni/src/binary_format.h @@ -92,6 +92,7 @@ class BinaryFormat { const int unigramProbability, const int bigramProbability); static int getProbability(const int position, const std::map<int, int> *bigramMap, const uint8_t *bigramFilter, const int unigramProbability); + static float getMultiWordCostMultiplier(const uint8_t *const dict); // Flags for special processing // Those *must* match the flags in makedict (BinaryDictInputOutput#*_PROCESSING_FLAG) or @@ -241,6 +242,17 @@ AK_FORCE_INLINE int BinaryFormat::getGroupCountAndForwardPointer(const uint8_t * return ((msb & 0x7F) << 8) | dict[(*pos)++]; } +inline float BinaryFormat::getMultiWordCostMultiplier(const uint8_t *const dict) { + const int headerValue = readHeaderValueInt(dict, "MULTIPLE_WORDS_DEMOTION_RATE"); + if (headerValue == S_INT_MIN) { + return 1.0f; + } + if (headerValue <= 0) { + return static_cast<float>(MAX_VALUE_FOR_WEIGHTING); + } + return 100.0f / static_cast<float>(headerValue); +} + inline uint8_t BinaryFormat::getFlagsAndForwardPointer(const uint8_t *const dict, int *pos) { return dict[(*pos)++]; } diff --git a/native/jni/src/defines.h b/native/jni/src/defines.h index a7b023a75..6ef9f414b 100644 --- a/native/jni/src/defines.h +++ b/native/jni/src/defines.h @@ -424,10 +424,9 @@ typedef enum { CT_OMISSION, CT_INSERTION, CT_TRANSPOSITION, - CT_SPACE_SUBSTITUTION, - CT_SPACE_OMISSION, CT_COMPLETION, CT_TERMINAL, - CT_NEW_WORD, + CT_NEW_WORD_SPACE_OMITTION, + CT_NEW_WORD_SPACE_SUBSTITUTION, } CorrectionType; #endif // LATINIME_DEFINES_H diff --git a/native/jni/src/suggest/core/policy/weighting.cpp b/native/jni/src/suggest/core/policy/weighting.cpp index e62b70423..b9c0b8129 100644 --- a/native/jni/src/suggest/core/policy/weighting.cpp +++ b/native/jni/src/suggest/core/policy/weighting.cpp @@ -38,7 +38,7 @@ static inline void profile(const CorrectionType correctionType, DicNode *const n case CT_SUBSTITUTION: PROF_SUBSTITUTION(node->mProfiler); return; - case CT_NEW_WORD: + case CT_NEW_WORD_SPACE_OMITTION: PROF_NEW_WORD(node->mProfiler); return; case CT_MATCH: @@ -50,7 +50,7 @@ static inline void profile(const CorrectionType correctionType, DicNode *const n case CT_TERMINAL: PROF_TERMINAL(node->mProfiler); return; - case CT_SPACE_SUBSTITUTION: + case CT_NEW_WORD_SPACE_SUBSTITUTION: PROF_SPACE_SUBSTITUTION(node->mProfiler); return; case CT_INSERTION: @@ -107,16 +107,16 @@ static inline void profile(const CorrectionType correctionType, DicNode *const n case CT_SUBSTITUTION: // only used for typing return weighting->getSubstitutionCost(); - case CT_NEW_WORD: - return weighting->getNewWordCost(dicNode); + case CT_NEW_WORD_SPACE_OMITTION: + return weighting->getNewWordCost(traverseSession, dicNode); case CT_MATCH: return weighting->getMatchedCost(traverseSession, dicNode, inputStateG); case CT_COMPLETION: return weighting->getCompletionCost(traverseSession, dicNode); case CT_TERMINAL: return weighting->getTerminalSpatialCost(traverseSession, dicNode); - case CT_SPACE_SUBSTITUTION: - return weighting->getSpaceSubstitutionCost(); + case CT_NEW_WORD_SPACE_SUBSTITUTION: + return weighting->getSpaceSubstitutionCost(traverseSession, dicNode); case CT_INSERTION: return weighting->getInsertionCost(traverseSession, parentDicNode, dicNode); case CT_TRANSPOSITION: @@ -135,7 +135,7 @@ static inline void profile(const CorrectionType correctionType, DicNode *const n return 0.0f; case CT_SUBSTITUTION: return 0.0f; - case CT_NEW_WORD: + case CT_NEW_WORD_SPACE_OMITTION: return weighting->getNewWordBigramCost(traverseSession, parentDicNode, bigramCacheMap); case CT_MATCH: return 0.0f; @@ -147,8 +147,8 @@ static inline void profile(const CorrectionType correctionType, DicNode *const n traverseSession->getOffsetDict(), dicNode, bigramCacheMap); return weighting->getTerminalLanguageCost(traverseSession, dicNode, languageImprobability); } - case CT_SPACE_SUBSTITUTION: - return 0.0f; + case CT_NEW_WORD_SPACE_SUBSTITUTION: + return weighting->getNewWordBigramCost(traverseSession, parentDicNode, bigramCacheMap); case CT_INSERTION: return 0.0f; case CT_TRANSPOSITION: @@ -168,7 +168,7 @@ static inline void profile(const CorrectionType correctionType, DicNode *const n case CT_SUBSTITUTION: // Should return true? return false; - case CT_NEW_WORD: + case CT_NEW_WORD_SPACE_OMITTION: return false; case CT_MATCH: return false; @@ -176,7 +176,7 @@ static inline void profile(const CorrectionType correctionType, DicNode *const n return false; case CT_TERMINAL: return false; - case CT_SPACE_SUBSTITUTION: + case CT_NEW_WORD_SPACE_SUBSTITUTION: return false; case CT_INSERTION: return true; @@ -197,7 +197,7 @@ static inline void profile(const CorrectionType correctionType, DicNode *const n return false; case CT_SUBSTITUTION: return false; - case CT_NEW_WORD: + case CT_NEW_WORD_SPACE_OMITTION: return false; case CT_MATCH: return weighting->isProximityDicNode(traverseSession, dicNode); @@ -205,7 +205,7 @@ static inline void profile(const CorrectionType correctionType, DicNode *const n return false; case CT_TERMINAL: return false; - case CT_SPACE_SUBSTITUTION: + case CT_NEW_WORD_SPACE_SUBSTITUTION: return false; case CT_INSERTION: return false; @@ -224,7 +224,7 @@ static inline void profile(const CorrectionType correctionType, DicNode *const n return 0; case CT_SUBSTITUTION: return 0; - case CT_NEW_WORD: + case CT_NEW_WORD_SPACE_OMITTION: return 0; case CT_MATCH: return 1; @@ -232,7 +232,7 @@ static inline void profile(const CorrectionType correctionType, DicNode *const n return 0; case CT_TERMINAL: return 0; - case CT_SPACE_SUBSTITUTION: + case CT_NEW_WORD_SPACE_SUBSTITUTION: return 1; case CT_INSERTION: return 2; diff --git a/native/jni/src/suggest/core/policy/weighting.h b/native/jni/src/suggest/core/policy/weighting.h index b92dbe278..bce479c51 100644 --- a/native/jni/src/suggest/core/policy/weighting.h +++ b/native/jni/src/suggest/core/policy/weighting.h @@ -56,7 +56,8 @@ class Weighting { const DicTraverseSession *const traverseSession, const DicNode *const parentDicNode, const DicNode *const dicNode) const = 0; - virtual float getNewWordCost(const DicNode *const dicNode) const = 0; + virtual float getNewWordCost(const DicTraverseSession *const traverseSession, + const DicNode *const dicNode) const = 0; virtual float getNewWordBigramCost( const DicTraverseSession *const traverseSession, const DicNode *const dicNode, @@ -76,7 +77,8 @@ class Weighting { virtual float getSubstitutionCost() const = 0; - virtual float getSpaceSubstitutionCost() const = 0; + virtual float getSpaceSubstitutionCost(const DicTraverseSession *const traverseSession, + const DicNode *const dicNode) const = 0; Weighting() {} virtual ~Weighting() {} diff --git a/native/jni/src/suggest/core/session/dic_traverse_session.cpp b/native/jni/src/suggest/core/session/dic_traverse_session.cpp index 5b783a2ba..3c44db21c 100644 --- a/native/jni/src/suggest/core/session/dic_traverse_session.cpp +++ b/native/jni/src/suggest/core/session/dic_traverse_session.cpp @@ -16,6 +16,7 @@ #include "suggest/core/session/dic_traverse_session.h" +#include "binary_format.h" #include "defines.h" #include "dictionary.h" #include "dic_traverse_wrapper.h" @@ -63,6 +64,7 @@ static TraverseSessionFactoryRegisterer traverseSessionFactoryRegisterer; void DicTraverseSession::init(const Dictionary *const dictionary, const int *prevWord, int prevWordLength) { mDictionary = dictionary; + mMultiWordCostMultiplier = BinaryFormat::getMultiWordCostMultiplier(mDictionary->getDict()); if (!prevWord) { mPrevWordPos = NOT_VALID_WORD; return; diff --git a/native/jni/src/suggest/core/session/dic_traverse_session.h b/native/jni/src/suggest/core/session/dic_traverse_session.h index fe0527639..d9c2a51d0 100644 --- a/native/jni/src/suggest/core/session/dic_traverse_session.h +++ b/native/jni/src/suggest/core/session/dic_traverse_session.h @@ -36,7 +36,8 @@ class DicTraverseSession { AK_FORCE_INLINE DicTraverseSession(JNIEnv *env, jstring localeStr) : mPrevWordPos(NOT_VALID_WORD), mProximityInfo(0), mDictionary(0), mDicNodesCache(), mBigramCacheMap(), - mInputSize(0), mPartiallyCommited(false), mMaxPointerCount(1) { + mInputSize(0), mPartiallyCommited(false), mMaxPointerCount(1), + mMultiWordCostMultiplier(1.0f) { // NOTE: mProximityInfoStates is an array of instances. // No need to initialize it explicitly here. } @@ -52,6 +53,7 @@ class DicTraverseSession { const int maxPointerCount); void resetCache(const int nextActiveCacheSize, const int maxWords); + // TODO: Remove const uint8_t *getOffsetDict() const; int getDictFlags() const; @@ -150,6 +152,10 @@ class DicTraverseSession { return mProximityInfoStates[0].touchPositionCorrectionEnabled(); } + float getMultiWordCostMultiplier() const { + return mMultiWordCostMultiplier; + } + private: DISALLOW_IMPLICIT_CONSTRUCTORS(DicTraverseSession); // threshold to start caching @@ -170,6 +176,11 @@ class DicTraverseSession { int mInputSize; bool mPartiallyCommited; int mMaxPointerCount; + + ///////////////////////////////// + // Configuration per dictionary + float mMultiWordCostMultiplier; + }; } // namespace latinime #endif // LATINIME_DIC_TRAVERSE_SESSION_H diff --git a/native/jni/src/suggest/core/suggest.cpp b/native/jni/src/suggest/core/suggest.cpp index 67d351fa1..9de2cd2e2 100644 --- a/native/jni/src/suggest/core/suggest.cpp +++ b/native/jni/src/suggest/core/suggest.cpp @@ -33,16 +33,9 @@ namespace latinime { // Initialization of class constants. -const int Suggest::LOOKAHEAD_DIC_NODES_CACHE_SIZE = 25; const int Suggest::MIN_LEN_FOR_MULTI_WORD_AUTOCORRECT = 16; const int Suggest::MIN_CONTINUOUS_SUGGESTION_INPUT_SIZE = 2; const float Suggest::AUTOCORRECT_CLASSIFICATION_THRESHOLD = 0.33f; -const float Suggest::AUTOCORRECT_LANGUAGE_FEATURE_THRESHOLD = 0.6f; - -const bool Suggest::CORRECT_SPACE_OMISSION = true; -const bool Suggest::CORRECT_TRANSPOSITION = true; -const bool Suggest::CORRECT_INSERTION = true; -const bool Suggest::CORRECT_OMISSION_G = true; /** * Returns a set of suggestions for the given input touch points. The commitPoint argument indicates @@ -270,12 +263,8 @@ void Suggest::expandCurrentDicNodes(DicTraverseSession *traverseSession) const { // latest touch point yet. These are needed to apply look-ahead correction operations // that require special handling of the latest touch point. For example, with insertions // (e.g., "thiis" -> "this") the latest touch point should not be consumed at all. - if (CORRECT_TRANSPOSITION) { - processDicNodeAsTransposition(traverseSession, &dicNode); - } - if (CORRECT_INSERTION) { - processDicNodeAsInsertion(traverseSession, &dicNode); - } + processDicNodeAsTransposition(traverseSession, &dicNode); + processDicNodeAsInsertion(traverseSession, &dicNode); } else { // !isLookAheadCorrection // Only consider typing error corrections if the normalized compound distance is // below a spatial distance threshold. @@ -531,13 +520,10 @@ void Suggest::createNextWordDicNode(DicTraverseSession *traverseSession, DicNode DicNode newDicNode; DicNodeUtils::initAsRootWithPreviousWord(traverseSession->getDicRootPos(), traverseSession->getOffsetDict(), dicNode, &newDicNode); - Weighting::addCostAndForwardInputIndex(WEIGHTING, CT_NEW_WORD, traverseSession, dicNode, + const CorrectionType correctionType = spaceSubstitution ? + CT_NEW_WORD_SPACE_SUBSTITUTION : CT_NEW_WORD_SPACE_OMITTION; + Weighting::addCostAndForwardInputIndex(WEIGHTING, correctionType, traverseSession, dicNode, &newDicNode, traverseSession->getBigramCacheMap()); - if (spaceSubstitution) { - // Merge this with CT_NEW_WORD - Weighting::addCostAndForwardInputIndex(WEIGHTING, CT_SPACE_SUBSTITUTION, - traverseSession, 0, &newDicNode, 0 /* bigramCacheMap */); - } traverseSession->getDicTraverseCache()->copyPushNextActive(&newDicNode); } } // namespace latinime diff --git a/native/jni/src/suggest/core/suggest.h b/native/jni/src/suggest/core/suggest.h index becd6c1de..875cbe4e0 100644 --- a/native/jni/src/suggest/core/suggest.h +++ b/native/jni/src/suggest/core/suggest.h @@ -76,31 +76,16 @@ class Suggest : public SuggestInterface { void processDicNodeAsMatch(DicTraverseSession *traverseSession, DicNode *childDicNode) const; - // Dic nodes cache size for lookahead (autocompletion) - static const int LOOKAHEAD_DIC_NODES_CACHE_SIZE; - // Max characters to lookahead - static const int MAX_LOOKAHEAD; // Inputs longer than this will autocorrect if the suggestion is multi-word static const int MIN_LEN_FOR_MULTI_WORD_AUTOCORRECT; static const int MIN_CONTINUOUS_SUGGESTION_INPUT_SIZE; - // Base value for converting costs into scores (low so will not autocorrect without classifier) - static const float BASE_OUTPUT_SCORE; // Threshold for autocorrection classifier static const float AUTOCORRECT_CLASSIFICATION_THRESHOLD; - // Threshold for computing the language model feature for autocorrect classification - static const float AUTOCORRECT_LANGUAGE_FEATURE_THRESHOLD; - - // Typing error correction settings - static const bool CORRECT_SPACE_OMISSION; - static const bool CORRECT_TRANSPOSITION; - static const bool CORRECT_INSERTION; const Traversal *const TRAVERSAL; const Scoring *const SCORING; const Weighting *const WEIGHTING; - - static const bool CORRECT_OMISSION_G; }; } // namespace latinime #endif // LATINIME_SUGGEST_IMPL_H diff --git a/native/jni/src/suggest/policyimpl/typing/typing_traversal.cpp b/native/jni/src/suggest/policyimpl/typing/typing_traversal.cpp index 66f8ba9fa..e7e40e34d 100644 --- a/native/jni/src/suggest/policyimpl/typing/typing_traversal.cpp +++ b/native/jni/src/suggest/policyimpl/typing/typing_traversal.cpp @@ -18,7 +18,7 @@ namespace latinime { const bool TypingTraversal::CORRECT_OMISSION = true; -const bool TypingTraversal::CORRECT_SPACE_SUBSTITUTION = true; -const bool TypingTraversal::CORRECT_SPACE_OMISSION = true; +const bool TypingTraversal::CORRECT_NEW_WORD_SPACE_SUBSTITUTION = true; +const bool TypingTraversal::CORRECT_NEW_WORD_SPACE_OMISSION = true; const TypingTraversal TypingTraversal::sInstance; } // namespace latinime diff --git a/native/jni/src/suggest/policyimpl/typing/typing_traversal.h b/native/jni/src/suggest/policyimpl/typing/typing_traversal.h index f22029a2c..9f8347452 100644 --- a/native/jni/src/suggest/policyimpl/typing/typing_traversal.h +++ b/native/jni/src/suggest/policyimpl/typing/typing_traversal.h @@ -66,7 +66,7 @@ class TypingTraversal : public Traversal { AK_FORCE_INLINE bool isSpaceSubstitutionTerminal( const DicTraverseSession *const traverseSession, const DicNode *const dicNode) const { - if (!CORRECT_SPACE_SUBSTITUTION) { + if (!CORRECT_NEW_WORD_SPACE_SUBSTITUTION) { return false; } if (!canDoLookAheadCorrection(traverseSession, dicNode)) { @@ -80,7 +80,7 @@ class TypingTraversal : public Traversal { AK_FORCE_INLINE bool isSpaceOmissionTerminal( const DicTraverseSession *const traverseSession, const DicNode *const dicNode) const { - if (!CORRECT_SPACE_OMISSION) { + if (!CORRECT_NEW_WORD_SPACE_OMISSION) { return false; } const int inputSize = traverseSession->getInputSize(); @@ -173,8 +173,8 @@ class TypingTraversal : public Traversal { private: DISALLOW_COPY_AND_ASSIGN(TypingTraversal); static const bool CORRECT_OMISSION; - static const bool CORRECT_SPACE_SUBSTITUTION; - static const bool CORRECT_SPACE_OMISSION; + static const bool CORRECT_NEW_WORD_SPACE_SUBSTITUTION; + static const bool CORRECT_NEW_WORD_SPACE_OMISSION; static const TypingTraversal sInstance; TypingTraversal() {} diff --git a/native/jni/src/suggest/policyimpl/typing/typing_weighting.h b/native/jni/src/suggest/policyimpl/typing/typing_weighting.h index 2dcee343f..74e4e34e4 100644 --- a/native/jni/src/suggest/policyimpl/typing/typing_weighting.h +++ b/native/jni/src/suggest/policyimpl/typing/typing_weighting.h @@ -128,10 +128,12 @@ class TypingWeighting : public Weighting { return cost + weightedDistance; } - float getNewWordCost(const DicNode *const dicNode) const { + float getNewWordCost(const DicTraverseSession *const traverseSession, + const DicNode *const dicNode) const { const bool isCapitalized = dicNode->isCapitalized(); - return isCapitalized ? + const float cost = isCapitalized ? ScoringParams::COST_NEW_WORD_CAPITALIZED : ScoringParams::COST_NEW_WORD; + return cost * traverseSession->getMultiWordCostMultiplier(); } float getNewWordBigramCost( @@ -183,8 +185,13 @@ class TypingWeighting : public Weighting { return ScoringParams::SUBSTITUTION_COST; } - AK_FORCE_INLINE float getSpaceSubstitutionCost() const { - return ScoringParams::SPACE_SUBSTITUTION_COST; + AK_FORCE_INLINE float getSpaceSubstitutionCost( + const DicTraverseSession *const traverseSession, + const DicNode *const dicNode) const { + const bool isCapitalized = dicNode->isCapitalized(); + const float cost = ScoringParams::SPACE_SUBSTITUTION_COST + (isCapitalized ? + ScoringParams::COST_NEW_WORD_CAPITALIZED : ScoringParams::COST_NEW_WORD); + return cost * traverseSession->getMultiWordCostMultiplier(); } private: diff --git a/tests/src/com/android/inputmethod/keyboard/internal/MockKeyboardSwitcher.java b/tests/src/com/android/inputmethod/keyboard/internal/MockKeyboardSwitcher.java index eb484084e..74506d26a 100644 --- a/tests/src/com/android/inputmethod/keyboard/internal/MockKeyboardSwitcher.java +++ b/tests/src/com/android/inputmethod/keyboard/internal/MockKeyboardSwitcher.java @@ -19,6 +19,7 @@ package com.android.inputmethod.keyboard.internal; import android.text.TextUtils; import com.android.inputmethod.latin.Constants; +import com.android.inputmethod.latin.RecapitalizeStatus; public class MockKeyboardSwitcher implements KeyboardState.SwitchActions { public interface MockConstants { @@ -120,7 +121,7 @@ public class MockKeyboardSwitcher implements KeyboardState.SwitchActions { @Override public void requestUpdatingShiftState() { - mState.onUpdateShiftState(mAutoCapsState); + mState.onUpdateShiftState(mAutoCapsState, RecapitalizeStatus.NOT_A_RECAPITALIZE_MODE); } @Override @@ -162,7 +163,7 @@ public class MockKeyboardSwitcher implements KeyboardState.SwitchActions { } public void updateShiftState() { - mState.onUpdateShiftState(mAutoCapsState); + mState.onUpdateShiftState(mAutoCapsState, RecapitalizeStatus.NOT_A_RECAPITALIZE_MODE); } public void loadKeyboard() { diff --git a/tests/src/com/android/inputmethod/latin/RecapitalizeStatusTests.java b/tests/src/com/android/inputmethod/latin/RecapitalizeStatusTests.java index 4dfae4c94..9d7203e5a 100644 --- a/tests/src/com/android/inputmethod/latin/RecapitalizeStatusTests.java +++ b/tests/src/com/android/inputmethod/latin/RecapitalizeStatusTests.java @@ -24,29 +24,26 @@ import java.util.Locale; @SmallTest public class RecapitalizeStatusTests extends AndroidTestCase { public void testTrim() { - RecapitalizeStatus status = new RecapitalizeStatus(30, 40, "abcdefghij", - Locale.ENGLISH, " "); + final RecapitalizeStatus status = new RecapitalizeStatus(); + status.initialize(30, 40, "abcdefghij", Locale.ENGLISH, " "); status.trim(); assertEquals("abcdefghij", status.getRecapitalizedString()); assertEquals(30, status.getNewCursorStart()); assertEquals(40, status.getNewCursorEnd()); - status = new RecapitalizeStatus(30, 44, " abcdefghij", - Locale.ENGLISH, " "); + status.initialize(30, 44, " abcdefghij", Locale.ENGLISH, " "); status.trim(); assertEquals("abcdefghij", status.getRecapitalizedString()); assertEquals(34, status.getNewCursorStart()); assertEquals(44, status.getNewCursorEnd()); - status = new RecapitalizeStatus(30, 40, "abcdefgh ", - Locale.ENGLISH, " "); + status.initialize(30, 40, "abcdefgh ", Locale.ENGLISH, " "); status.trim(); assertEquals("abcdefgh", status.getRecapitalizedString()); assertEquals(30, status.getNewCursorStart()); assertEquals(38, status.getNewCursorEnd()); - status = new RecapitalizeStatus(30, 45, " abcdefghij ", - Locale.ENGLISH, " "); + status.initialize(30, 45, " abcdefghij ", Locale.ENGLISH, " "); status.trim(); assertEquals("abcdefghij", status.getRecapitalizedString()); assertEquals(33, status.getNewCursorStart()); @@ -54,8 +51,8 @@ public class RecapitalizeStatusTests extends AndroidTestCase { } public void testRotate() { - RecapitalizeStatus status = new RecapitalizeStatus(29, 40, "abcd efghij", - Locale.ENGLISH, " "); + final RecapitalizeStatus status = new RecapitalizeStatus(); + status.initialize(29, 40, "abcd efghij", Locale.ENGLISH, " "); status.rotate(); assertEquals("Abcd Efghij", status.getRecapitalizedString()); assertEquals(29, status.getNewCursorStart()); @@ -67,8 +64,7 @@ public class RecapitalizeStatusTests extends AndroidTestCase { status.rotate(); assertEquals("Abcd Efghij", status.getRecapitalizedString()); - status = new RecapitalizeStatus(29, 40, "Abcd Efghij", - Locale.ENGLISH, " "); + status.initialize(29, 40, "Abcd Efghij", Locale.ENGLISH, " "); status.rotate(); assertEquals("ABCD EFGHIJ", status.getRecapitalizedString()); assertEquals(29, status.getNewCursorStart()); @@ -80,8 +76,7 @@ public class RecapitalizeStatusTests extends AndroidTestCase { status.rotate(); assertEquals("ABCD EFGHIJ", status.getRecapitalizedString()); - status = new RecapitalizeStatus(29, 40, "ABCD EFGHIJ", - Locale.ENGLISH, " "); + status.initialize(29, 40, "ABCD EFGHIJ", Locale.ENGLISH, " "); status.rotate(); assertEquals("abcd efghij", status.getRecapitalizedString()); assertEquals(29, status.getNewCursorStart()); @@ -93,8 +88,7 @@ public class RecapitalizeStatusTests extends AndroidTestCase { status.rotate(); assertEquals("abcd efghij", status.getRecapitalizedString()); - status = new RecapitalizeStatus(29, 39, "AbCDefghij", - Locale.ENGLISH, " "); + status.initialize(29, 39, "AbCDefghij", Locale.ENGLISH, " "); status.rotate(); assertEquals("abcdefghij", status.getRecapitalizedString()); assertEquals(29, status.getNewCursorStart()); @@ -108,8 +102,7 @@ public class RecapitalizeStatusTests extends AndroidTestCase { status.rotate(); assertEquals("abcdefghij", status.getRecapitalizedString()); - status = new RecapitalizeStatus(29, 40, "Abcd efghij", - Locale.ENGLISH, " "); + status.initialize(29, 40, "Abcd efghij", Locale.ENGLISH, " "); status.rotate(); assertEquals("abcd efghij", status.getRecapitalizedString()); assertEquals(29, status.getNewCursorStart()); @@ -123,8 +116,7 @@ public class RecapitalizeStatusTests extends AndroidTestCase { status.rotate(); assertEquals("abcd efghij", status.getRecapitalizedString()); - status = new RecapitalizeStatus(30, 34, "grüß", Locale.GERMAN, " "); - status.rotate(); + status.initialize(30, 34, "grüß", Locale.GERMAN, " "); status.rotate(); assertEquals("Grüß", status.getRecapitalizedString()); assertEquals(30, status.getNewCursorStart()); assertEquals(34, status.getNewCursorEnd()); @@ -141,9 +133,7 @@ public class RecapitalizeStatusTests extends AndroidTestCase { assertEquals(30, status.getNewCursorStart()); assertEquals(34, status.getNewCursorEnd()); - - status = new RecapitalizeStatus(30, 33, "œuf", Locale.FRENCH, " "); - status.rotate(); + status.initialize(30, 33, "œuf", Locale.FRENCH, " "); status.rotate(); assertEquals("Œuf", status.getRecapitalizedString()); assertEquals(30, status.getNewCursorStart()); assertEquals(33, status.getNewCursorEnd()); @@ -160,8 +150,7 @@ public class RecapitalizeStatusTests extends AndroidTestCase { assertEquals(30, status.getNewCursorStart()); assertEquals(33, status.getNewCursorEnd()); - status = new RecapitalizeStatus(30, 33, "œUf", Locale.FRENCH, " "); - status.rotate(); + status.initialize(30, 33, "œUf", Locale.FRENCH, " "); status.rotate(); assertEquals("œuf", status.getRecapitalizedString()); assertEquals(30, status.getNewCursorStart()); assertEquals(33, status.getNewCursorEnd()); @@ -182,8 +171,7 @@ public class RecapitalizeStatusTests extends AndroidTestCase { assertEquals(30, status.getNewCursorStart()); assertEquals(33, status.getNewCursorEnd()); - status = new RecapitalizeStatus(30, 35, "école", Locale.FRENCH, " "); - status.rotate(); + status.initialize(30, 35, "école", Locale.FRENCH, " "); status.rotate(); assertEquals("École", status.getRecapitalizedString()); assertEquals(30, status.getNewCursorStart()); assertEquals(35, status.getNewCursorEnd()); |