diff options
21 files changed, 373 insertions, 146 deletions
diff --git a/java/res/values/strings.xml b/java/res/values/strings.xml index a1c1a9ffd..9b3829f74 100644 --- a/java/res/values/strings.xml +++ b/java/res/values/strings.xml @@ -99,7 +99,7 @@ <!-- Option to decide the auto correction threshold score --> <!-- Option to enable auto correction [CHAR LIMIT=20]--> <string name="auto_correction">Auto correction</string> - <!-- Description for auto correction [CHAR LIMIT=35] --> + <!-- Description for auto correction [CHAR LIMIT=65 (two lines) or 30 (fits on one line, preferable)] --> <string name="auto_correction_summary">Spacebar and punctuation automatically correct mistyped words</string> <!-- Option to disable auto correction. [CHAR LIMIT=20] --> <string name="auto_correction_threshold_mode_off">Off</string> diff --git a/java/res/xml/method.xml b/java/res/xml/method.xml index f2f3178de..6184add4d 100644 --- a/java/res/xml/method.xml +++ b/java/res/xml/method.xml @@ -31,141 +31,145 @@ android:label="@string/subtype_en_US" android:imeSubtypeLocale="en_US" android:imeSubtypeMode="keyboard" - android:imeSubtypeExtraValue="TrySuppressingImeSwitcher,AsciiCapable" + android:imeSubtypeExtraValue="TrySuppressingImeSwitcher,AsciiCapable,SupportTouchPositionCorrection" /> <subtype android:icon="@drawable/ic_subtype_keyboard" android:label="@string/subtype_en_GB" android:imeSubtypeLocale="en_GB" android:imeSubtypeMode="keyboard" - android:imeSubtypeExtraValue="TrySuppressingImeSwitcher,AsciiCapable" + android:imeSubtypeExtraValue="TrySuppressingImeSwitcher,AsciiCapable,SupportTouchPositionCorrection" /> <subtype android:icon="@drawable/ic_subtype_keyboard" android:label="@string/subtype_generic" android:imeSubtypeLocale="ar" android:imeSubtypeMode="keyboard" + android:imeSubtypeExtraValue="SupportTouchPositionCorrection" /> <subtype android:icon="@drawable/ic_subtype_keyboard" android:label="@string/subtype_generic" android:imeSubtypeLocale="cs" android:imeSubtypeMode="keyboard" - android:imeSubtypeExtraValue="AsciiCapable" + android:imeSubtypeExtraValue="AsciiCapable,SupportTouchPositionCorrection" /> <subtype android:icon="@drawable/ic_subtype_keyboard" android:label="@string/subtype_generic" android:imeSubtypeLocale="da" android:imeSubtypeMode="keyboard" - android:imeSubtypeExtraValue="AsciiCapable" + android:imeSubtypeExtraValue="AsciiCapable,SupportTouchPositionCorrection" /> <subtype android:icon="@drawable/ic_subtype_keyboard" android:label="@string/subtype_generic" android:imeSubtypeLocale="de" android:imeSubtypeMode="keyboard" - android:imeSubtypeExtraValue="AsciiCapable" + android:imeSubtypeExtraValue="AsciiCapable,SupportTouchPositionCorrection" /> <subtype android:icon="@drawable/ic_subtype_keyboard" android:label="@string/subtype_de_qwerty" android:imeSubtypeLocale="de" android:imeSubtypeMode="keyboard" - android:imeSubtypeExtraValue="AsciiCapable,KeyboardLocale=de_ZZ" + android:imeSubtypeExtraValue="AsciiCapable,KeyboardLocale=de_ZZ,SupportTouchPositionCorrection" /> <subtype android:icon="@drawable/ic_subtype_keyboard" android:label="@string/subtype_generic" android:imeSubtypeLocale="es" android:imeSubtypeMode="keyboard" - android:imeSubtypeExtraValue="AsciiCapable" + android:imeSubtypeExtraValue="AsciiCapable,SupportTouchPositionCorrection" /> <subtype android:icon="@drawable/ic_subtype_keyboard" android:label="@string/subtype_generic" android:imeSubtypeLocale="fi" android:imeSubtypeMode="keyboard" - android:imeSubtypeExtraValue="AsciiCapable" + android:imeSubtypeExtraValue="AsciiCapable,SupportTouchPositionCorrection" /> <subtype android:icon="@drawable/ic_subtype_keyboard" android:label="@string/subtype_generic" android:imeSubtypeLocale="fr" android:imeSubtypeMode="keyboard" - android:imeSubtypeExtraValue="AsciiCapable" + android:imeSubtypeExtraValue="AsciiCapable,SupportTouchPositionCorrection" /> <subtype android:icon="@drawable/ic_subtype_keyboard" android:label="@string/subtype_generic" android:imeSubtypeLocale="fr_CA" android:imeSubtypeMode="keyboard" - android:imeSubtypeExtraValue="AsciiCapable" + android:imeSubtypeExtraValue="AsciiCapable,SupportTouchPositionCorrection" /> <subtype android:icon="@drawable/ic_subtype_keyboard" android:label="@string/subtype_generic" android:imeSubtypeLocale="fr_CH" android:imeSubtypeMode="keyboard" - android:imeSubtypeExtraValue="AsciiCapable" + android:imeSubtypeExtraValue="AsciiCapable,SupportTouchPositionCorrection" /> <subtype android:icon="@drawable/ic_subtype_keyboard" android:label="@string/subtype_generic" android:imeSubtypeLocale="hr" android:imeSubtypeMode="keyboard" - android:imeSubtypeExtraValue="AsciiCapable" + android:imeSubtypeExtraValue="AsciiCapable,SupportTouchPositionCorrection" /> <subtype android:icon="@drawable/ic_subtype_keyboard" android:label="@string/subtype_generic" android:imeSubtypeLocale="hu" android:imeSubtypeMode="keyboard" - android:imeSubtypeExtraValue="AsciiCapable" + android:imeSubtypeExtraValue="AsciiCapable,SupportTouchPositionCorrection" /> <subtype android:icon="@drawable/ic_subtype_keyboard" android:label="@string/subtype_generic" android:imeSubtypeLocale="it" android:imeSubtypeMode="keyboard" - android:imeSubtypeExtraValue="AsciiCapable" + android:imeSubtypeExtraValue="AsciiCapable,SupportTouchPositionCorrection" /> <!-- Java uses the deprecated "iw" code instead of the standard "he" code for Hebrew. --> <subtype android:icon="@drawable/ic_subtype_keyboard" android:label="@string/subtype_generic" android:imeSubtypeLocale="iw" android:imeSubtypeMode="keyboard" + android:imeSubtypeExtraValue="SupportTouchPositionCorrection" /> <subtype android:icon="@drawable/ic_subtype_keyboard" android:label="@string/subtype_generic" android:imeSubtypeLocale="nb" android:imeSubtypeMode="keyboard" - android:imeSubtypeExtraValue="AsciiCapable" + android:imeSubtypeExtraValue="AsciiCapable,SupportTouchPositionCorrection" /> <subtype android:icon="@drawable/ic_subtype_keyboard" android:label="@string/subtype_generic" android:imeSubtypeLocale="nl" android:imeSubtypeMode="keyboard" - android:imeSubtypeExtraValue="AsciiCapable" + android:imeSubtypeExtraValue="AsciiCapable,SupportTouchPositionCorrection" /> <subtype android:icon="@drawable/ic_subtype_keyboard" android:label="@string/subtype_generic" android:imeSubtypeLocale="pl" android:imeSubtypeMode="keyboard" - android:imeSubtypeExtraValue="AsciiCapable" + android:imeSubtypeExtraValue="AsciiCapable,SupportTouchPositionCorrection" /> <subtype android:icon="@drawable/ic_subtype_keyboard" android:label="@string/subtype_generic" android:imeSubtypeLocale="pt" android:imeSubtypeMode="keyboard" - android:imeSubtypeExtraValue="AsciiCapable" + android:imeSubtypeExtraValue="AsciiCapable,SupportTouchPositionCorrection" /> <subtype android:icon="@drawable/ic_subtype_keyboard" android:label="@string/subtype_generic" android:imeSubtypeLocale="ru" android:imeSubtypeMode="keyboard" + android:imeSubtypeExtraValue="SupportTouchPositionCorrection" /> <subtype android:icon="@drawable/ic_subtype_keyboard" android:label="@string/subtype_generic" android:imeSubtypeLocale="sr" android:imeSubtypeMode="keyboard" + android:imeSubtypeExtraValue="SupportTouchPositionCorrection" /> <subtype android:icon="@drawable/ic_subtype_keyboard" android:label="@string/subtype_generic" android:imeSubtypeLocale="sv" android:imeSubtypeMode="keyboard" - android:imeSubtypeExtraValue="AsciiCapable" + android:imeSubtypeExtraValue="AsciiCapable,SupportTouchPositionCorrection" /> <subtype android:icon="@drawable/ic_subtype_keyboard" android:label="@string/subtype_generic" android:imeSubtypeLocale="tr" android:imeSubtypeMode="keyboard" - android:imeSubtypeExtraValue="AsciiCapable" + android:imeSubtypeExtraValue="AsciiCapable,SupportTouchPositionCorrection" /> </input-method> diff --git a/java/src/com/android/inputmethod/latin/SharedPreferencesCompat.java b/java/src/com/android/inputmethod/compat/SharedPreferencesCompat.java index 1d36c0b98..38736f3a1 100644 --- a/java/src/com/android/inputmethod/latin/SharedPreferencesCompat.java +++ b/java/src/com/android/inputmethod/compat/SharedPreferencesCompat.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.android.inputmethod.latin; +package com.android.inputmethod.compat; import android.content.SharedPreferences; diff --git a/java/src/com/android/inputmethod/deprecated/VoiceProxy.java b/java/src/com/android/inputmethod/deprecated/VoiceProxy.java index a4dfa10d2..3f8c2ef8f 100644 --- a/java/src/com/android/inputmethod/deprecated/VoiceProxy.java +++ b/java/src/com/android/inputmethod/deprecated/VoiceProxy.java @@ -18,6 +18,7 @@ package com.android.inputmethod.deprecated; import com.android.inputmethod.compat.InputMethodManagerCompatWrapper; import com.android.inputmethod.compat.InputMethodServiceCompatWrapper; +import com.android.inputmethod.compat.SharedPreferencesCompat; import com.android.inputmethod.deprecated.voice.FieldContext; import com.android.inputmethod.deprecated.voice.Hints; import com.android.inputmethod.deprecated.voice.SettingsUtil; @@ -28,7 +29,6 @@ import com.android.inputmethod.latin.LatinIME; import com.android.inputmethod.latin.LatinIME.UIHandler; import com.android.inputmethod.latin.LatinImeLogger; import com.android.inputmethod.latin.R; -import com.android.inputmethod.latin.SharedPreferencesCompat; import com.android.inputmethod.latin.SubtypeSwitcher; import com.android.inputmethod.latin.SuggestedWords; import com.android.inputmethod.latin.Utils; diff --git a/java/src/com/android/inputmethod/deprecated/languageswitcher/InputLanguageSelection.java b/java/src/com/android/inputmethod/deprecated/languageswitcher/InputLanguageSelection.java index b6e0ec9cf..dbe7aec6a 100644 --- a/java/src/com/android/inputmethod/deprecated/languageswitcher/InputLanguageSelection.java +++ b/java/src/com/android/inputmethod/deprecated/languageswitcher/InputLanguageSelection.java @@ -16,12 +16,12 @@ package com.android.inputmethod.deprecated.languageswitcher; +import com.android.inputmethod.compat.SharedPreferencesCompat; import com.android.inputmethod.keyboard.internal.KeyboardBuilder; import com.android.inputmethod.latin.DictionaryFactory; import com.android.inputmethod.latin.LocaleUtils; import com.android.inputmethod.latin.R; import com.android.inputmethod.latin.Settings; -import com.android.inputmethod.latin.SharedPreferencesCompat; import com.android.inputmethod.latin.Utils; import org.xmlpull.v1.XmlPullParserException; diff --git a/java/src/com/android/inputmethod/deprecated/languageswitcher/LanguageSwitcher.java b/java/src/com/android/inputmethod/deprecated/languageswitcher/LanguageSwitcher.java index 8070942d0..7e2627c81 100644 --- a/java/src/com/android/inputmethod/deprecated/languageswitcher/LanguageSwitcher.java +++ b/java/src/com/android/inputmethod/deprecated/languageswitcher/LanguageSwitcher.java @@ -16,11 +16,11 @@ package com.android.inputmethod.deprecated.languageswitcher; +import com.android.inputmethod.compat.SharedPreferencesCompat; import com.android.inputmethod.latin.LatinIME; import com.android.inputmethod.latin.LatinImeLogger; import com.android.inputmethod.latin.LocaleUtils; import com.android.inputmethod.latin.Settings; -import com.android.inputmethod.latin.SharedPreferencesCompat; import android.content.SharedPreferences; import android.content.SharedPreferences.Editor; diff --git a/java/src/com/android/inputmethod/deprecated/voice/Hints.java b/java/src/com/android/inputmethod/deprecated/voice/Hints.java index 06b234381..17a19bf23 100644 --- a/java/src/com/android/inputmethod/deprecated/voice/Hints.java +++ b/java/src/com/android/inputmethod/deprecated/voice/Hints.java @@ -16,8 +16,8 @@ package com.android.inputmethod.deprecated.voice; +import com.android.inputmethod.compat.SharedPreferencesCompat; import com.android.inputmethod.latin.R; -import com.android.inputmethod.latin.SharedPreferencesCompat; import android.content.ContentResolver; import android.content.Context; diff --git a/java/src/com/android/inputmethod/keyboard/KeyboardSwitcher.java b/java/src/com/android/inputmethod/keyboard/KeyboardSwitcher.java index 13e8ba13c..837a53391 100644 --- a/java/src/com/android/inputmethod/keyboard/KeyboardSwitcher.java +++ b/java/src/com/android/inputmethod/keyboard/KeyboardSwitcher.java @@ -106,16 +106,12 @@ public class KeyboardSwitcher implements SharedPreferences.OnSharedPreferenceCha private static final KeyboardSwitcher sInstance = new KeyboardSwitcher(); - public class KeyboardLayoutState { + private class KeyboardLayoutState { private boolean mIsValid; private boolean mIsAlphabetMode; private boolean mIsShiftLocked; private boolean mIsShifted; - public boolean isValid() { - return mIsValid; - } - public void save() { if (mCurrentId == null) { return; @@ -210,14 +206,15 @@ public class KeyboardSwitcher implements SharedPreferences.OnSharedPreferenceCha mSymbolsShiftedKeyboardId = getKeyboardId(editorInfo, true, true, settingsValues); mLayoutSwitchBackSymbols = mResources.getString(R.string.layout_switch_back_symbols); setKeyboard(getKeyboard(mSavedKeyboardState.getKeyboardId())); + mSavedKeyboardState.restore(); } catch (RuntimeException e) { Log.w(TAG, "loading keyboard failed: " + mMainKeyboardId, e); LatinImeLogger.logOnException(mMainKeyboardId.toString(), e); } } - public KeyboardLayoutState getKeyboardState() { - return mSavedKeyboardState; + public void saveKeyboardState() { + mSavedKeyboardState.save(); } public void onFinishInputView() { diff --git a/java/src/com/android/inputmethod/keyboard/ProximityInfo.java b/java/src/com/android/inputmethod/keyboard/ProximityInfo.java index d35b1a939..cc6feeb4a 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 android.graphics.Rect; import com.android.inputmethod.keyboard.Key; +import com.android.inputmethod.latin.SubtypeSwitcher; import com.android.inputmethod.latin.Utils; import com.android.inputmethod.latin.spellcheck.SpellCheckerProximityInfo; @@ -31,6 +32,8 @@ public class ProximityInfo { /** Number of key widths from current touch point to search for nearest keys. */ private static float SEARCH_DISTANCE = 1.2f; private static final int[] EMPTY_INT_ARRAY = new int[0]; + private static final String SUPPORT_TOUCH_POSITION_CORRECTION = + "SupportTouchPositionCorrection"; private final int mKeyHeight; private final int mGridWidth; @@ -120,8 +123,10 @@ public class ProximityInfo { keyCharCodes[i] = key.mCode; } + final SubtypeSwitcher switcher = SubtypeSwitcher.getInstance(); final boolean hasTouchPositionCorrectionData = - mTouchPositionCorrectionXs != null + switcher.currentSubtypeContainsExtraValueKey(SUPPORT_TOUCH_POSITION_CORRECTION) + && mTouchPositionCorrectionXs != null && mTouchPositionCorrectionYs != null && mTouchPositionCorrectionRadii != null && mTouchPositionCorrectionXs.length > 0 diff --git a/java/src/com/android/inputmethod/latin/BinaryDictionary.java b/java/src/com/android/inputmethod/latin/BinaryDictionary.java index ab9edb110..18a9e3ab1 100644 --- a/java/src/com/android/inputmethod/latin/BinaryDictionary.java +++ b/java/src/com/android/inputmethod/latin/BinaryDictionary.java @@ -61,16 +61,26 @@ public class BinaryDictionary extends Dictionary { public static final Flag FLAG_REQUIRES_GERMAN_UMLAUT_PROCESSING = new Flag(R.bool.config_require_umlaut_processing, 0x1); + // FULL_EDIT_DISTANCE is a flag that forces the dictionary to use full words + // when computing edit distance, instead of the default behavior of stopping + // the evaluation at the size the user typed. public static final Flag FLAG_USE_FULL_EDIT_DISTANCE = new Flag(0x2); // Can create a new flag from extravalue : // public static final Flag FLAG_MYFLAG = // new Flag("my_flag", 0x02); - private static final Flag[] ALL_FLAGS = { + // ALL_CONFIG_FLAGS is a collection of flags that enable reading all flags from configuration. + // This is but a mask - it does not mean the flags will be on, only that the configuration + // will be read for this particular flag. + public static final Flag[] ALL_CONFIG_FLAGS = { // Here should reside all flags that trigger some special processing // These *must* match the definition in UnigramDictionary enum in // unigram_dictionary.h so please update both at the same time. + // Please note that flags created with a resource are of type CONFIG while flags + // created with a string are of type EXTRAVALUE. These behave like masks, and the + // actual value will be read from the configuration/extra value at run time for + // the configuration at dictionary creation time. FLAG_REQUIRES_GERMAN_UMLAUT_PROCESSING, }; @@ -93,7 +103,7 @@ public class BinaryDictionary extends Dictionary { // the Suggest class knows everything about every single dictionary. mDicTypeId = Suggest.DIC_MAIN; // TODO: Stop relying on the state of SubtypeSwitcher, get it as a parameter - mFlags = Flag.initFlags(null == flagArray ? ALL_FLAGS : flagArray, context, + mFlags = Flag.initFlags(null == flagArray ? ALL_CONFIG_FLAGS : flagArray, context, SubtypeSwitcher.getInstance()); loadDictionary(filename, offset, length); } diff --git a/java/src/com/android/inputmethod/latin/DictionaryFactory.java b/java/src/com/android/inputmethod/latin/DictionaryFactory.java index dfaad26bc..1607f86a8 100644 --- a/java/src/com/android/inputmethod/latin/DictionaryFactory.java +++ b/java/src/com/android/inputmethod/latin/DictionaryFactory.java @@ -34,7 +34,7 @@ public class DictionaryFactory { private static String TAG = DictionaryFactory.class.getSimpleName(); /** - * Initializes a dictionary from a dictionary pack. + * Initializes a dictionary from a dictionary pack, with explicit flags. * * This searches for a content provider providing a dictionary pack for the specified * locale. If none is found, it falls back to using the resource passed as fallBackResId @@ -42,10 +42,11 @@ public class DictionaryFactory { * @param context application context for reading resources * @param locale the locale for which to create the dictionary * @param fallbackResId the id of the resource to use as a fallback if no pack is found + * @param flagArray an array of flags to use * @return an initialized instance of DictionaryCollection */ - public static DictionaryCollection createDictionaryFromManager(Context context, Locale locale, - int fallbackResId) { + public static DictionaryCollection createDictionaryFromManager(final Context context, + final Locale locale, final int fallbackResId, final Flag[] flagArray) { if (null == locale) { Log.e(TAG, "No locale defined for dictionary"); return new DictionaryCollection(createBinaryDictionary(context, fallbackResId, locale)); @@ -57,7 +58,7 @@ public class DictionaryFactory { if (null != assetFileList) { for (final AssetFileAddress f : assetFileList) { final BinaryDictionary binaryDictionary = - new BinaryDictionary(context, f.mFilename, f.mOffset, f.mLength, null); + new BinaryDictionary(context, f.mFilename, f.mOffset, f.mLength, flagArray); if (binaryDictionary.isValidDictionary()) { dictList.add(binaryDictionary); } @@ -71,6 +72,22 @@ public class DictionaryFactory { } /** + * Initializes a dictionary from a dictionary pack, with default flags. + * + * This searches for a content provider providing a dictionary pack for the specified + * locale. If none is found, it falls back to using the resource passed as fallBackResId + * as a dictionary. + * @param context application context for reading resources + * @param locale the locale for which to create the dictionary + * @param fallbackResId the id of the resource to use as a fallback if no pack is found + * @return an initialized instance of DictionaryCollection + */ + public static DictionaryCollection createDictionaryFromManager(final Context context, + final Locale locale, final int fallbackResId) { + return createDictionaryFromManager(context, locale, fallbackResId, null); + } + + /** * Initializes a dictionary from a raw resource file * @param context application context for reading resources * @param resId the resource containing the raw binary dictionary diff --git a/java/src/com/android/inputmethod/latin/LatinIME.java b/java/src/com/android/inputmethod/latin/LatinIME.java index cf1cb8f25..dfb4d0622 100644 --- a/java/src/com/android/inputmethod/latin/LatinIME.java +++ b/java/src/com/android/inputmethod/latin/LatinIME.java @@ -65,7 +65,6 @@ import com.android.inputmethod.keyboard.Key; import com.android.inputmethod.keyboard.Keyboard; import com.android.inputmethod.keyboard.KeyboardActionListener; import com.android.inputmethod.keyboard.KeyboardSwitcher; -import com.android.inputmethod.keyboard.KeyboardSwitcher.KeyboardLayoutState; import com.android.inputmethod.keyboard.KeyboardView; import com.android.inputmethod.keyboard.LatinKeyboard; import com.android.inputmethod.keyboard.LatinKeyboardView; @@ -132,9 +131,7 @@ public class LatinIME extends InputMethodServiceCompatWrapper implements Keyboar // Key events coming any faster than this are long-presses. private static final int QUICK_PRESS = 200; - private static final int START_INPUT_VIEW_DELAY_WHEN_SCREEN_ORIENTATION_STARTED = 10; - private static final int ACCUMULATE_START_INPUT_VIEW_DELAY = 20; - private static final int RESTORE_KEYBOARD_STATE_DELAY = 500; + private static final int PENDING_IMS_CALLBACK_DURATION = 800; /** * The name of the scheme used by the Package Manager to warn of a new package installation, @@ -239,10 +236,7 @@ public class LatinIME extends InputMethodServiceCompatWrapper implements Keyboar private static final int MSG_DISMISS_LANGUAGE_ON_SPACEBAR = 5; private static final int MSG_SPACE_TYPED = 6; private static final int MSG_SET_BIGRAM_PREDICTIONS = 7; - private static final int MSG_START_ORIENTATION_CHANGE = 8; - private static final int MSG_START_INPUT_VIEW = 9; - private static final int MSG_DISPLAY_COMPLETIONS = 10; - private static final int MSG_RESTORE_KEYBOARD_LAYOUT = 11; + private static final int MSG_PENDING_IMS_CALLBACK = 8; public UIHandler(LatinIME outerInstance) { super(outerInstance); @@ -291,16 +285,6 @@ public class LatinIME extends InputMethodServiceCompatWrapper implements Keyboar (LatinKeyboard)msg.obj); } break; - case MSG_START_INPUT_VIEW: - latinIme.onStartInputView((EditorInfo)msg.obj, false); - break; - case MSG_DISPLAY_COMPLETIONS: - latinIme.onDisplayCompletions((CompletionInfo[])msg.obj); - break; - case MSG_RESTORE_KEYBOARD_LAYOUT: - removeMessages(MSG_UPDATE_SHIFT_STATE); - ((KeyboardLayoutState)msg.obj).restore(); - break; } } @@ -391,47 +375,89 @@ public class LatinIME extends InputMethodServiceCompatWrapper implements Keyboar return hasMessages(MSG_SPACE_TYPED); } - public void postRestoreKeyboardLayout() { - final LatinIME latinIme = getOuterInstance(); - final KeyboardLayoutState state = latinIme.mKeyboardSwitcher.getKeyboardState(); - if (state.isValid()) { - removeMessages(MSG_RESTORE_KEYBOARD_LAYOUT); - sendMessageDelayed( - obtainMessage(MSG_RESTORE_KEYBOARD_LAYOUT, state), - RESTORE_KEYBOARD_STATE_DELAY); - } - } + // Working variables for the following methods. + private boolean mIsOrientationChanging; + private boolean mPendingSuccesiveImsCallback; + private boolean mHasPendingStartInput; + private boolean mHasPendingFinishInputView; + private boolean mHasPendingFinishInput; public void startOrientationChanging() { - sendMessageDelayed(obtainMessage(MSG_START_ORIENTATION_CHANGE), - START_INPUT_VIEW_DELAY_WHEN_SCREEN_ORIENTATION_STARTED); + mIsOrientationChanging = true; final LatinIME latinIme = getOuterInstance(); - latinIme.mKeyboardSwitcher.getKeyboardState().save(); - postRestoreKeyboardLayout(); + latinIme.mKeyboardSwitcher.saveKeyboardState(); } - public boolean postStartInputView(EditorInfo attribute) { - if (hasMessages(MSG_START_ORIENTATION_CHANGE) || hasMessages(MSG_START_INPUT_VIEW)) { - removeMessages(MSG_START_INPUT_VIEW); - // Postpone onStartInputView by ACCUMULATE_START_INPUT_VIEW_DELAY and see if - // orientation change has finished. - sendMessageDelayed(obtainMessage(MSG_START_INPUT_VIEW, attribute), - ACCUMULATE_START_INPUT_VIEW_DELAY); - return true; + private void resetPendingImsCallback() { + mHasPendingFinishInputView = false; + mHasPendingFinishInput = false; + mHasPendingStartInput = false; + } + + private void executePendingImsCallback(LatinIME latinIme, EditorInfo attribute, + boolean restarting) { + if (mHasPendingFinishInputView) + latinIme.onFinishInputViewInternal(mHasPendingFinishInput); + if (mHasPendingFinishInput) + latinIme.onFinishInputInternal(); + if (mHasPendingStartInput) + latinIme.onStartInputInternal(attribute, restarting); + resetPendingImsCallback(); + } + + public void onStartInput(EditorInfo attribute, boolean restarting) { + if (hasMessages(MSG_PENDING_IMS_CALLBACK)) { + // Typically this is the second onStartInput after orientation changed. + mHasPendingStartInput = true; + } else { + if (mIsOrientationChanging && restarting) { + // This is the first onStartInput after orientation changed. + mIsOrientationChanging = false; + mPendingSuccesiveImsCallback = true; + } + final LatinIME latinIme = getOuterInstance(); + executePendingImsCallback(latinIme, attribute, restarting); + latinIme.onStartInputInternal(attribute, restarting); } - return false; } - public boolean postDisplayCompletions(CompletionInfo[] applicationSpecifiedCompletions) { - if (hasMessages(MSG_START_INPUT_VIEW) || hasMessages(MSG_DISPLAY_COMPLETIONS)) { - removeMessages(MSG_DISPLAY_COMPLETIONS); - // Postpone onDisplayCompletions by ACCUMULATE_START_INPUT_VIEW_DELAY. - sendMessageDelayed( - obtainMessage(MSG_DISPLAY_COMPLETIONS, applicationSpecifiedCompletions), - ACCUMULATE_START_INPUT_VIEW_DELAY); - return true; + public void onStartInputView(EditorInfo attribute, boolean restarting) { + if (hasMessages(MSG_PENDING_IMS_CALLBACK)) { + // Typically this is the second onStartInputView after orientation changed. + resetPendingImsCallback(); + } else { + if (mPendingSuccesiveImsCallback) { + // This is the first onStartInputView after orientation changed. + mPendingSuccesiveImsCallback = false; + resetPendingImsCallback(); + sendMessageDelayed(obtainMessage(MSG_PENDING_IMS_CALLBACK), + PENDING_IMS_CALLBACK_DURATION); + } + final LatinIME latinIme = getOuterInstance(); + executePendingImsCallback(latinIme, attribute, restarting); + latinIme.onStartInputViewInternal(attribute, restarting); + } + } + + public void onFinishInputView(boolean finishingInput) { + if (hasMessages(MSG_PENDING_IMS_CALLBACK)) { + // Typically this is the first onFinishInputView after orientation changed. + mHasPendingFinishInputView = true; + } else { + final LatinIME latinIme = getOuterInstance(); + latinIme.onFinishInputViewInternal(finishingInput); + } + } + + public void onFinishInput() { + if (hasMessages(MSG_PENDING_IMS_CALLBACK)) { + // Typically this is the first onFinishInput after orientation changed. + mHasPendingFinishInput = true; + } else { + final LatinIME latinIme = getOuterInstance(); + executePendingImsCallback(latinIme, null, false); + latinIme.onFinishInputInternal(); } - return false; } } @@ -646,12 +672,31 @@ public class LatinIME extends InputMethodServiceCompatWrapper implements Keyboar } @Override + public void onStartInput(EditorInfo attribute, boolean restarting) { + mHandler.onStartInput(attribute, restarting); + } + + @Override public void onStartInputView(EditorInfo attribute, boolean restarting) { - mHandler.postRestoreKeyboardLayout(); - if (mHandler.postStartInputView(attribute)) { - return; - } + mHandler.onStartInputView(attribute, restarting); + } + @Override + public void onFinishInputView(boolean finishingInput) { + mHandler.onFinishInputView(finishingInput); + } + + @Override + public void onFinishInput() { + mHandler.onFinishInput(); + } + + private void onStartInputInternal(EditorInfo attribute, boolean restarting) { + super.onStartInput(attribute, restarting); + } + + private void onStartInputViewInternal(EditorInfo attribute, boolean restarting) { + super.onStartInputView(attribute, restarting); final KeyboardSwitcher switcher = mKeyboardSwitcher; LatinKeyboardView inputView = switcher.getKeyboardView(); @@ -785,8 +830,7 @@ public class LatinIME extends InputMethodServiceCompatWrapper implements Keyboar if (inputView != null) inputView.closing(); } - @Override - public void onFinishInput() { + private void onFinishInputInternal() { super.onFinishInput(); LatinImeLogger.commit(); @@ -799,8 +843,7 @@ public class LatinIME extends InputMethodServiceCompatWrapper implements Keyboar if (mUserBigramDictionary != null) mUserBigramDictionary.flushPendingWrites(); } - @Override - public void onFinishInputView(boolean finishingInput) { + private void onFinishInputViewInternal(boolean finishingInput) { super.onFinishInputView(finishingInput); mKeyboardSwitcher.onFinishInputView(); KeyboardView inputView = mKeyboardSwitcher.getKeyboardView(); @@ -939,9 +982,6 @@ public class LatinIME extends InputMethodServiceCompatWrapper implements Keyboar @Override public void onDisplayCompletions(CompletionInfo[] applicationSpecifiedCompletions) { - if (mHandler.postDisplayCompletions(applicationSpecifiedCompletions)) { - return; - } if (DEBUG) { Log.i(TAG, "Received completions:"); if (applicationSpecifiedCompletions != null) { diff --git a/java/src/com/android/inputmethod/latin/Settings.java b/java/src/com/android/inputmethod/latin/Settings.java index a2e896619..bd94bab34 100644 --- a/java/src/com/android/inputmethod/latin/Settings.java +++ b/java/src/com/android/inputmethod/latin/Settings.java @@ -328,6 +328,7 @@ public class Settings extends InputMethodSettingsActivity } private PreferenceScreen mInputLanguageSelection; + private PreferenceScreen mVibrationDurationSettingsPref; private ListPreference mVoicePreference; private CheckBoxPreference mShowSettingsKeyPreference; private ListPreference mShowCorrectionSuggestionsPreference; @@ -483,10 +484,10 @@ public class Settings extends InputMethodSettingsActivity } } - final PreferenceScreen vibrationSettingsPref = + mVibrationDurationSettingsPref = (PreferenceScreen) findPreference(PREF_VIBRATION_DURATION_SETTINGS); - if (vibrationSettingsPref != null) { - vibrationSettingsPref.setOnPreferenceClickListener( + if (mVibrationDurationSettingsPref != null) { + mVibrationDurationSettingsPref.setOnPreferenceClickListener( new OnPreferenceClickListener() { @Override public boolean onPreferenceClick(Preference arg0) { @@ -494,6 +495,7 @@ public class Settings extends InputMethodSettingsActivity return true; } }); + updateVibrationDurationSettingsSummary(prefs, res); } } @@ -642,9 +644,18 @@ public class Settings extends InputMethodSettingsActivity } } + private void updateVibrationDurationSettingsSummary(SharedPreferences sp, Resources res) { + if (mVibrationDurationSettingsPref != null) { + mVibrationDurationSettingsPref.setSummary( + Utils.getCurrentVibrationDuration(sp, res) + + res.getString(R.string.settings_ms)); + } + } + private void showVibrationSettingsDialog() { final SharedPreferences sp = getPreferenceManager().getSharedPreferences(); final Activity context = getActivityInternal(); + final Resources res = context.getResources(); final AlertDialog.Builder builder = new AlertDialog.Builder(context); builder.setTitle(R.string.prefs_vibration_duration_settings); builder.setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() { @@ -652,6 +663,7 @@ public class Settings extends InputMethodSettingsActivity public void onClick(DialogInterface dialog, int whichButton) { final int ms = Integer.valueOf(mVibrationSettingsTextView.getText().toString()); sp.edit().putInt(Settings.PREF_VIBRATION_DURATION_SETTINGS, ms).apply(); + updateVibrationDurationSettingsSummary(sp, res); } }); builder.setNegativeButton(android.R.string.cancel, new DialogInterface.OnClickListener() { diff --git a/java/src/com/android/inputmethod/latin/spellcheck/AndroidSpellCheckerService.java b/java/src/com/android/inputmethod/latin/spellcheck/AndroidSpellCheckerService.java index 1d5986ef9..9e030eb90 100644 --- a/java/src/com/android/inputmethod/latin/spellcheck/AndroidSpellCheckerService.java +++ b/java/src/com/android/inputmethod/latin/spellcheck/AndroidSpellCheckerService.java @@ -28,11 +28,13 @@ import android.text.TextUtils; import com.android.inputmethod.compat.ArraysCompatUtils; import com.android.inputmethod.keyboard.Key; import com.android.inputmethod.keyboard.ProximityInfo; +import com.android.inputmethod.latin.BinaryDictionary; import com.android.inputmethod.latin.Dictionary; import com.android.inputmethod.latin.Dictionary.DataType; import com.android.inputmethod.latin.Dictionary.WordCallback; import com.android.inputmethod.latin.DictionaryCollection; import com.android.inputmethod.latin.DictionaryFactory; +import com.android.inputmethod.latin.Flag; import com.android.inputmethod.latin.LocaleUtils; import com.android.inputmethod.latin.R; import com.android.inputmethod.latin.SynchronouslyLoadedUserDictionary; @@ -65,6 +67,17 @@ public class AndroidSpellCheckerService extends SpellCheckerService { private final static SuggestionsInfo IN_DICT_EMPTY_SUGGESTIONS = new SuggestionsInfo(SuggestionsInfo.RESULT_ATTR_IN_THE_DICTIONARY, EMPTY_STRING_ARRAY); + private final static Flag[] USE_FULL_EDIT_DISTANCE_FLAG_ARRAY; + static { + // See BinaryDictionary.java for an explanation of these flags + // Specifially, ALL_CONFIG_FLAGS means that we want to consider all flags with the + // current dictionary configuration - for example, consider the UMLAUT flag + // so that it will be turned on for German dictionaries and off for others. + USE_FULL_EDIT_DISTANCE_FLAG_ARRAY = Arrays.copyOf(BinaryDictionary.ALL_CONFIG_FLAGS, + BinaryDictionary.ALL_CONFIG_FLAGS.length + 1); + USE_FULL_EDIT_DISTANCE_FLAG_ARRAY[BinaryDictionary.ALL_CONFIG_FLAGS.length] = + BinaryDictionary.FLAG_USE_FULL_EDIT_DISTANCE; + } private Map<String, DictionaryPool> mDictionaryPools = Collections.synchronizedMap(new TreeMap<String, DictionaryPool>()); private Map<String, Dictionary> mUserDictionaries = @@ -263,7 +276,8 @@ public class AndroidSpellCheckerService extends SpellCheckerService { final Resources resources = getResources(); final int fallbackResourceId = Utils.getMainDictionaryResourceId(resources); final DictionaryCollection dictionaryCollection = - DictionaryFactory.createDictionaryFromManager(this, locale, fallbackResourceId); + DictionaryFactory.createDictionaryFromManager(this, locale, fallbackResourceId, + USE_FULL_EDIT_DISTANCE_FLAG_ARRAY); final String localeStr = locale.toString(); Dictionary userDict = mUserDictionaries.get(localeStr); if (null == userDict) { diff --git a/native/src/correction.cpp b/native/src/correction.cpp index 9a7e5f35d..5128c2e5c 100644 --- a/native/src/correction.cpp +++ b/native/src/correction.cpp @@ -115,6 +115,9 @@ bool Correction::initProcessState(const int outputIndex) { mInputIndex = mCorrectionStates[outputIndex].mInputIndex; mNeedsToTraverseAllNodes = mCorrectionStates[outputIndex].mNeedsToTraverseAllNodes; + mEquivalentCharStrongCount = mCorrectionStates[outputIndex].mEquivalentCharStrongCount; + mEquivalentCharNormalCount = mCorrectionStates[outputIndex].mEquivalentCharNormalCount; + mEquivalentCharWeakCount = mCorrectionStates[outputIndex].mEquivalentCharWeakCount; mProximityCount = mCorrectionStates[outputIndex].mProximityCount; mTransposedCount = mCorrectionStates[outputIndex].mTransposedCount; mExcessiveCount = mCorrectionStates[outputIndex].mExcessiveCount; @@ -169,6 +172,9 @@ void Correction::incrementOutputIndex() { mCorrectionStates[mOutputIndex].mInputIndex = mInputIndex; mCorrectionStates[mOutputIndex].mNeedsToTraverseAllNodes = mNeedsToTraverseAllNodes; + mCorrectionStates[mOutputIndex].mEquivalentCharStrongCount = mEquivalentCharStrongCount; + mCorrectionStates[mOutputIndex].mEquivalentCharNormalCount = mEquivalentCharNormalCount; + mCorrectionStates[mOutputIndex].mEquivalentCharWeakCount = mEquivalentCharWeakCount; mCorrectionStates[mOutputIndex].mProximityCount = mProximityCount; mCorrectionStates[mOutputIndex].mTransposedCount = mTransposedCount; mCorrectionStates[mOutputIndex].mExcessiveCount = mExcessiveCount; @@ -210,6 +216,12 @@ Correction::CorrectionType Correction::processSkipChar( } } +inline bool isEquivalentChar(ProximityInfo::ProximityType type) { + // 'type ProximityInfo::EQUIVALENT_CHAR_WEAK' means that + // type == ..._WEAK or type == ..._NORMAL or type == ..._STRONG. + return type <= ProximityInfo::EQUIVALENT_CHAR_WEAK; +} + Correction::CorrectionType Correction::processCharAndCalcState( const int32_t c, const bool isTerminal) { const int correctionCount = (mSkippedCount + mExcessiveCount + mTransposedCount); @@ -221,8 +233,9 @@ Correction::CorrectionType Correction::processCharAndCalcState( bool incremented = false; if (mLastCharExceeded && mInputIndex == mInputLength - 1) { // TODO: Do not check the proximity if EditDistance exceeds the threshold - const int matchId = mProximityInfo->getMatchedProximityId(mInputIndex, c, true); - if (matchId == ProximityInfo::SAME_OR_ACCENTED_OR_CAPITALIZED_CHAR) { + const ProximityInfo::ProximityType matchId = + mProximityInfo->getMatchedProximityId(mInputIndex, c, true); + if (isEquivalentChar(matchId)) { mLastCharExceeded = false; --mExcessiveCount; } else if (matchId == ProximityInfo::NEAR_PROXIMITY_CHAR) { @@ -266,8 +279,7 @@ Correction::CorrectionType Correction::processCharAndCalcState( bool secondTransposing = false; if (mTransposedCount % 2 == 1) { - if (mProximityInfo->getMatchedProximityId(mInputIndex - 1, c, false) - == ProximityInfo::SAME_OR_ACCENTED_OR_CAPITALIZED_CHAR) { + if (isEquivalentChar(mProximityInfo->getMatchedProximityId(mInputIndex - 1, c, false))) { ++mTransposedCount; secondTransposing = true; } else if (mCorrectionStates[mOutputIndex].mExceeding) { @@ -288,8 +300,8 @@ Correction::CorrectionType Correction::processCharAndCalcState( // TODO: Change the limit if we'll allow two or more proximity chars with corrections const bool checkProximityChars = noCorrectionsHappenedSoFar || mProximityCount == 0; - const int matchedProximityCharId = secondTransposing - ? ProximityInfo::SAME_OR_ACCENTED_OR_CAPITALIZED_CHAR + const ProximityInfo::ProximityType matchedProximityCharId = secondTransposing + ? ProximityInfo::EQUIVALENT_CHAR_NORMAL : mProximityInfo->getMatchedProximityId(mInputIndex, c, checkProximityChars); if (ProximityInfo::UNRELATED_CHAR == matchedProximityCharId) { @@ -299,19 +311,18 @@ Correction::CorrectionType Correction::processCharAndCalcState( // here refers to the previous state. if (canTryCorrection && mCorrectionStates[mOutputIndex].mProximityMatching && mCorrectionStates[mOutputIndex].mExceeding - && mProximityInfo->getMatchedProximityId(mInputIndex, mWord[mOutputIndex], false) - == ProximityInfo::SAME_OR_ACCENTED_OR_CAPITALIZED_CHAR) { + && isEquivalentChar(mProximityInfo->getMatchedProximityId( + mInputIndex, mWord[mOutputIndex], false))) { // Conversion p->e ++mExcessiveCount; --mProximityCount; } else if (mInputIndex < mInputLength - 1 && mOutputIndex > 0 && mTransposedCount > 0 && !mCorrectionStates[mOutputIndex].mTransposing && mCorrectionStates[mOutputIndex - 1].mTransposing - && mProximityInfo->getMatchedProximityId( - mInputIndex, mWord[mOutputIndex - 1], false) - == ProximityInfo::SAME_OR_ACCENTED_OR_CAPITALIZED_CHAR - && mProximityInfo->getMatchedProximityId(mInputIndex + 1, c, false) - == ProximityInfo::SAME_OR_ACCENTED_OR_CAPITALIZED_CHAR) { + && isEquivalentChar(mProximityInfo->getMatchedProximityId( + mInputIndex, mWord[mOutputIndex - 1], false)) + && isEquivalentChar( + mProximityInfo->getMatchedProximityId(mInputIndex + 1, c, false))) { // Conversion t->e // Example: // occaisional -> occa sional @@ -322,8 +333,8 @@ Correction::CorrectionType Correction::processCharAndCalcState( } else if (mOutputIndex > 0 && mInputIndex > 0 && mTransposedCount > 0 && !mCorrectionStates[mOutputIndex].mTransposing && mCorrectionStates[mOutputIndex - 1].mTransposing - && mProximityInfo->getMatchedProximityId(mInputIndex - 1, c, false) - == ProximityInfo::SAME_OR_ACCENTED_OR_CAPITALIZED_CHAR) { + && isEquivalentChar( + mProximityInfo->getMatchedProximityId(mInputIndex - 1, c, false))) { // Conversion t->s // Example: // chcolate -> chocolate @@ -334,8 +345,8 @@ Correction::CorrectionType Correction::processCharAndCalcState( } else if (canTryCorrection && mInputIndex > 0 && mCorrectionStates[mOutputIndex].mProximityMatching && mCorrectionStates[mOutputIndex].mSkipping - && mProximityInfo->getMatchedProximityId(mInputIndex - 1, c, false) - == ProximityInfo::SAME_OR_ACCENTED_OR_CAPITALIZED_CHAR) { + && isEquivalentChar( + mProximityInfo->getMatchedProximityId(mInputIndex - 1, c, false))) { // Conversion p->s // Note: This logic tries saving cases like contrst --> contrast -- "a" is one of // proximity chars of "s", but it should rather be handled as a skipped char. @@ -343,8 +354,8 @@ Correction::CorrectionType Correction::processCharAndCalcState( --mProximityCount; return processSkipChar(c, isTerminal, false); } else if ((mExceeding || mTransposing) && mInputIndex - 1 < mInputLength - && mProximityInfo->getMatchedProximityId(mInputIndex + 1, c, false) - == ProximityInfo::SAME_OR_ACCENTED_OR_CAPITALIZED_CHAR) { + && isEquivalentChar( + mProximityInfo->getMatchedProximityId(mInputIndex + 1, c, false))) { // 1.2. Excessive or transpose correction if (mTransposing) { ++mTransposedCount; @@ -364,14 +375,28 @@ Correction::CorrectionType Correction::processCharAndCalcState( } return UNRELATED; } - } else if (secondTransposing - || ProximityInfo::SAME_OR_ACCENTED_OR_CAPITALIZED_CHAR == matchedProximityCharId) { + } else if (secondTransposing) { // If inputIndex is greater than mInputLength, that means there is no // proximity chars. So, we don't need to check proximity. mMatching = true; + } else if (isEquivalentChar(matchedProximityCharId)) { + mMatching = true; + switch (matchedProximityCharId) { + case ProximityInfo::EQUIVALENT_CHAR_STRONG: + ++mEquivalentCharStrongCount; + break; + case ProximityInfo::EQUIVALENT_CHAR_NORMAL: + ++mEquivalentCharNormalCount; + break; + case ProximityInfo::EQUIVALENT_CHAR_WEAK: + ++mEquivalentCharWeakCount; + break; + default: + assert(false); + } } else if (ProximityInfo::NEAR_PROXIMITY_CHAR == matchedProximityCharId) { mProximityMatching = true; - incrementProximityCount(); + ++mProximityCount; } mWord[mOutputIndex] = c; @@ -530,6 +555,8 @@ int Correction::RankingAlgorithm::calculateFinalFreq(const int inputIndex, const const int transposedCount = correction->mTransposedCount / 2; const int excessiveCount = correction->mExcessiveCount + correction->mTransposedCount % 2; const int proximityMatchedCount = correction->mProximityCount; + const int equivalentCharStrongCount = correction->mEquivalentCharStrongCount; + const int equivalentCharWeakCount = correction->mEquivalentCharWeakCount; const bool lastCharExceeded = correction->mLastCharExceeded; const bool useFullEditDistance = correction->mUseFullEditDistance; const int outputLength = outputIndex + 1; @@ -639,6 +666,20 @@ int Correction::RankingAlgorithm::calculateFinalFreq(const int inputIndex, const multiplyRate(WORDS_WITH_PROXIMITY_CHARACTER_DEMOTION_RATE, &finalFreq); } + for (int i = 0; i < equivalentCharStrongCount; ++i) { + if (DEBUG_DICT_FULL) { + LOGI("equivalent char strong"); + } + multiplyRate(WORDS_WITH_EQUIVALENT_CHAR_STRONG_PROMOTION_RATE, &finalFreq); + } + + for (int i = 0; i < equivalentCharWeakCount; ++i) { + if (DEBUG_DICT_FULL) { + LOGI("equivalent char weak"); + } + multiplyRate(WORDS_WITH_EQUIVALENT_CHAR_WEAK_DEMOTION_RATE, &finalFreq); + } + const int errorCount = adjustedProximityMatchedCount > 0 ? adjustedProximityMatchedCount : (proximityMatchedCount + transposedCount); diff --git a/native/src/correction.h b/native/src/correction.h index ddb98b9d8..84e075266 100644 --- a/native/src/correction.h +++ b/native/src/correction.h @@ -102,11 +102,6 @@ private: inline CorrectionType processSkipChar( const int32_t c, const bool isTerminal, const bool inputIndexIncremented); - // TODO: remove - inline void incrementProximityCount() { - ++mProximityCount; - } - const int TYPED_LETTER_MULTIPLIER; const int FULL_WORD_MULTIPLIER; const ProximityInfo *mProximityInfo; @@ -131,6 +126,9 @@ private: int mOutputIndex; int mInputIndex; + int mEquivalentCharStrongCount; + int mEquivalentCharNormalCount; + int mEquivalentCharWeakCount; int mProximityCount; int mExcessiveCount; int mTransposedCount; diff --git a/native/src/correction_state.h b/native/src/correction_state.h index 93f8a8aab..a8ee82acd 100644 --- a/native/src/correction_state.h +++ b/native/src/correction_state.h @@ -29,6 +29,9 @@ struct CorrectionState { uint16_t mChildCount; uint8_t mInputIndex; + uint8_t mEquivalentCharStrongCount; + uint8_t mEquivalentCharNormalCount; + uint8_t mEquivalentCharWeakCount; uint8_t mProximityCount; uint8_t mTransposedCount; uint8_t mExcessiveCount; @@ -63,7 +66,9 @@ inline static void initCorrectionState(CorrectionState *state, const int rootPos state->mExcessivePos = -1; state->mSkipPos = -1; - + state->mEquivalentCharStrongCount = 0; + state->mEquivalentCharNormalCount = 0; + state->mEquivalentCharWeakCount = 0; state->mProximityCount = 0; state->mTransposedCount = 0; state->mExcessiveCount = 0; diff --git a/native/src/defines.h b/native/src/defines.h index 55469a788..dab862924 100644 --- a/native/src/defines.h +++ b/native/src/defines.h @@ -165,6 +165,8 @@ static void dumpWord(const unsigned short* word, const int length) { #define KEYCODE_SPACE ' ' +#define CALIBRATE_SCORE_BY_TOUCH_COORDINATES false + #define SUGGEST_WORDS_WITH_MISSING_CHARACTER true #define SUGGEST_WORDS_WITH_MISSING_SPACE_CHARACTER true #define SUGGEST_WORDS_WITH_EXCESSIVE_CHARACTER true @@ -178,6 +180,8 @@ static void dumpWord(const unsigned short* word, const int length) { #define WORDS_WITH_EXCESSIVE_CHARACTER_DEMOTION_RATE 75 #define WORDS_WITH_EXCESSIVE_CHARACTER_OUT_OF_PROXIMITY_DEMOTION_RATE 75 #define WORDS_WITH_TRANSPOSED_CHARACTERS_DEMOTION_RATE 60 +#define WORDS_WITH_EQUIVALENT_CHAR_STRONG_PROMOTION_RATE 105 +#define WORDS_WITH_EQUIVALENT_CHAR_WEAK_DEMOTION_RATE 95 #define FULL_MATCHED_WORDS_PROMOTION_RATE 120 #define WORDS_WITH_PROXIMITY_CHARACTER_DEMOTION_RATE 90 #define WORDS_WITH_MATCH_SKIP_PROMOTION_RATE 105 @@ -204,4 +208,7 @@ static void dumpWord(const unsigned short* word, const int length) { #define min(a,b) ((a)<(b)?(a):(b)) #define max(a,b) ((a)>(b)?(a):(b)) +// The ratio of neutral area radius to sweet spot radius. +#define NEUTRAL_AREA_RADIUS_RATIO 1.3f + #endif // LATINIME_DEFINES_H diff --git a/native/src/proximity_info.cpp b/native/src/proximity_info.cpp index 4ff6e0ac0..081cb61c3 100644 --- a/native/src/proximity_info.cpp +++ b/native/src/proximity_info.cpp @@ -43,7 +43,8 @@ ProximityInfo::ProximityInfo(const int maxProximityCharsSize, const int keyboard KEYBOARD_HEIGHT(keyboardHeight), GRID_WIDTH(gridWidth), GRID_HEIGHT(gridHeight), CELL_WIDTH((keyboardWidth + gridWidth - 1) / gridWidth), CELL_HEIGHT((keyboardHeight + gridHeight - 1) / gridHeight), - KEY_COUNT(min(keyCount, MAX_KEY_COUNT_IN_A_KEYBOARD)) { + KEY_COUNT(min(keyCount, MAX_KEY_COUNT_IN_A_KEYBOARD)), + mInputXCoordinates(NULL), mInputYCoordinates(NULL) { const int len = GRID_WIDTH * GRID_HEIGHT * MAX_PROXIMITY_CHARS_SIZE; mProximityCharsArray = new uint32_t[len]; if (DEBUG_PROXIMITY_INFO) { @@ -103,8 +104,11 @@ bool ProximityInfo::hasSpaceProximity(const int x, const int y) const { } // TODO: Calculate nearby codes here. -void ProximityInfo::setInputParams(const int* inputCodes, const int inputLength) { +void ProximityInfo::setInputParams(const int* inputCodes, const int inputLength, + const int* xCoordinates, const int* yCoordinates) { mInputCodes = inputCodes; + mInputXCoordinates = xCoordinates; + mInputYCoordinates = yCoordinates; mInputLength = inputLength; for (int i = 0; i < inputLength; ++i) { mPrimaryInputWord[i] = getPrimaryCharAt(i); @@ -158,19 +162,37 @@ bool ProximityInfo::existsAdjacentProximityChars(const int index) const { ProximityInfo::ProximityType ProximityInfo::getMatchedProximityId( const int index, const unsigned short c, const bool checkProximityChars) const { const int *currentChars = getProximityCharsAt(index); + const int firstChar = currentChars[0]; const unsigned short baseLowerC = Dictionary::toBaseLowerCase(c); // The first char in the array is what user typed. If it matches right away, // that means the user typed that same char for this pos. - if (currentChars[0] == baseLowerC || currentChars[0] == c) - return SAME_OR_ACCENTED_OR_CAPITALIZED_CHAR; + if (firstChar == baseLowerC || firstChar == c) { + if (CALIBRATE_SCORE_BY_TOUCH_COORDINATES) { + const SweetSpotType result = calculateSweetSpotType(index, baseLowerC); + switch (result) { + case UNKNOWN: + return EQUIVALENT_CHAR_NORMAL; + case IN_SWEET_SPOT: + return EQUIVALENT_CHAR_STRONG; + case IN_NEUTRAL_AREA: + return EQUIVALENT_CHAR_NORMAL; + case OUT_OF_NEUTRAL_AREA: + return EQUIVALENT_CHAR_WEAK; + default: + assert(false); + } + } else { + return EQUIVALENT_CHAR_NORMAL; + } + } if (!checkProximityChars) return UNRELATED_CHAR; // If the non-accented, lowercased version of that first character matches c, // then we have a non-accented version of the accented character the user // typed. Treat it as a close char. - if (Dictionary::toBaseLowerCase(currentChars[0]) == baseLowerC) + if (Dictionary::toBaseLowerCase(firstChar) == baseLowerC) return NEAR_PROXIMITY_CHAR; // Not an exact nor an accent-alike match: search the list of close keys @@ -185,6 +207,38 @@ ProximityInfo::ProximityType ProximityInfo::getMatchedProximityId( return UNRELATED_CHAR; } +inline float square(const float x) { return x * x; } + +ProximityInfo::SweetSpotType ProximityInfo::calculateSweetSpotType( + int index, unsigned short baseLowerC) const { + if (KEY_COUNT == 0 || !mInputXCoordinates || !mInputYCoordinates + || baseLowerC > MAX_CHAR_CODE) { + return UNKNOWN; + } + const int keyIndex = mCodeToKeyIndex[baseLowerC]; + if (keyIndex < 0) { + return UNKNOWN; + } + const float sweetSpotRadius = mSweetSpotRadii[keyIndex]; + if (sweetSpotRadius <= 0.0) { + return UNKNOWN; + } + const float sweetSpotCenterX = mSweetSpotCenterXs[keyIndex]; + const float sweetSpotCenterY = mSweetSpotCenterXs[keyIndex]; + const float inputX = (float)mInputXCoordinates[index]; + const float inputY = (float)mInputYCoordinates[index]; + const float squaredDistance = + square(inputX - sweetSpotCenterX) + square(inputY - sweetSpotCenterY); + const float squaredSweetSpotRadius = square(sweetSpotRadius); + if (squaredDistance <= squaredSweetSpotRadius) { + return IN_SWEET_SPOT; + } + if (squaredDistance <= square(NEUTRAL_AREA_RADIUS_RATIO) * squaredSweetSpotRadius) { + return IN_NEUTRAL_AREA; + } + return OUT_OF_NEUTRAL_AREA; +} + bool ProximityInfo::sameAsTyped(const unsigned short *word, int length) const { if (length != mInputLength) { return false; diff --git a/native/src/proximity_info.h b/native/src/proximity_info.h index b1e8236d3..a705d0cf6 100644 --- a/native/src/proximity_info.h +++ b/native/src/proximity_info.h @@ -27,10 +27,18 @@ class Correction; class ProximityInfo { public: - typedef enum { // Used as a return value for character comparison - SAME_OR_ACCENTED_OR_CAPITALIZED_CHAR, // Same char, possibly with different case or accent - NEAR_PROXIMITY_CHAR, // It is a char located nearby on the keyboard - UNRELATED_CHAR // It is an unrelated char + // Used as a return value for character comparison + typedef enum { + // Same char, possibly with different case or accent, and in the sweet spot of the char + EQUIVALENT_CHAR_STRONG, + // Same char, possibly with different case or accent, and in the outer sweet spot + EQUIVALENT_CHAR_NORMAL, + // Same char, possibly with different case or accent, and in the hit box of the char + EQUIVALENT_CHAR_WEAK, + // It is a char located nearby on the keyboard + NEAR_PROXIMITY_CHAR, + // It is an unrelated char + UNRELATED_CHAR } ProximityType; ProximityInfo(const int maxProximityCharsSize, const int keyboardWidth, @@ -41,7 +49,8 @@ public: const float *sweetSpotCenterYs, const float *sweetSpotRadii); ~ProximityInfo(); bool hasSpaceProximity(const int x, const int y) const; - void setInputParams(const int* inputCodes, const int inputLength); + void setInputParams(const int* inputCodes, const int inputLength, + const int *xCoordinates, const int *yCoordinates); const int* getProximityCharsAt(const int index) const; unsigned short getPrimaryCharAt(const int index) const; bool existsCharInProximityAt(const int index, const int c) const; @@ -59,8 +68,20 @@ private: // The upper limit of the char code in mCodeToKeyIndex static const int MAX_CHAR_CODE = 127; + typedef enum { + // cannot figure out the sweet spot type + UNKNOWN, + // touch position is out of neutral area of the given char + OUT_OF_NEUTRAL_AREA, + // touch position is in the neutral area of the given char + IN_NEUTRAL_AREA, + // touch position is in the sweet spot of the given char + IN_SWEET_SPOT + } SweetSpotType; + int getStartIndexFromCoordinates(const int x, const int y) const; void initializeCodeToKeyIndex(); + SweetSpotType calculateSweetSpotType(int index, unsigned short baseLowerC) const; const int MAX_PROXIMITY_CHARS_SIZE; const int KEYBOARD_WIDTH; const int KEYBOARD_HEIGHT; @@ -70,6 +91,8 @@ private: const int CELL_HEIGHT; const int KEY_COUNT; const int *mInputCodes; + const int *mInputXCoordinates; + const int *mInputYCoordinates; uint32_t *mProximityCharsArray; int32_t mKeyXCoordinates[MAX_KEY_COUNT_IN_A_KEYBOARD]; int32_t mKeyYCoordinates[MAX_KEY_COUNT_IN_A_KEYBOARD]; diff --git a/native/src/unigram_dictionary.cpp b/native/src/unigram_dictionary.cpp index 1b798a8f1..f23bd3208 100644 --- a/native/src/unigram_dictionary.cpp +++ b/native/src/unigram_dictionary.cpp @@ -240,8 +240,8 @@ void UnigramDictionary::getWordSuggestions(ProximityInfo *proximityInfo, PROF_END(6); } -void UnigramDictionary::initSuggestions(ProximityInfo *proximityInfo, const int *xcoordinates, - const int *ycoordinates, const int *codes, const int codesSize, +void UnigramDictionary::initSuggestions(ProximityInfo *proximityInfo, const int *xCoordinates, + const int *yCoordinates, const int *codes, const int codesSize, unsigned short *outWords, int *frequencies) { if (DEBUG_DICT) { LOGI("initSuggest"); @@ -249,7 +249,7 @@ void UnigramDictionary::initSuggestions(ProximityInfo *proximityInfo, const int mFrequencies = frequencies; mOutputChars = outWords; mInputLength = codesSize; - proximityInfo->setInputParams(codes, codesSize); + proximityInfo->setInputParams(codes, codesSize, xCoordinates, yCoordinates); mProximityInfo = proximityInfo; } |