aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--java/res/values-sw600dp/config.xml9
-rw-r--r--java/res/values-sw768dp/config.xml9
-rw-r--r--java/res/values/attrs.xml24
-rw-r--r--java/res/values/config.xml35
-rw-r--r--java/res/values/dimens.xml2
-rw-r--r--java/res/values/styles.xml13
-rw-r--r--java/src/com/android/inputmethod/keyboard/KeyboardSet.java10
-rw-r--r--java/src/com/android/inputmethod/keyboard/KeyboardSwitcher.java12
-rw-r--r--java/src/com/android/inputmethod/keyboard/KeyboardView.java49
-rw-r--r--java/src/com/android/inputmethod/keyboard/LatinKeyboardView.java81
-rw-r--r--java/src/com/android/inputmethod/keyboard/PointerTracker.java53
-rw-r--r--java/src/com/android/inputmethod/keyboard/internal/KeyboardState.java15
-rw-r--r--java/src/com/android/inputmethod/latin/BinaryDictionary.java4
-rw-r--r--java/src/com/android/inputmethod/latin/Dictionary.java11
-rw-r--r--java/src/com/android/inputmethod/latin/ExpandableDictionary.java6
-rw-r--r--java/src/com/android/inputmethod/latin/LastComposedWord.java72
-rw-r--r--java/src/com/android/inputmethod/latin/LatinIME.java87
-rw-r--r--java/src/com/android/inputmethod/latin/LatinImeLogger.java3
-rw-r--r--java/src/com/android/inputmethod/latin/Settings.java2
-rw-r--r--java/src/com/android/inputmethod/latin/SettingsValues.java5
-rw-r--r--java/src/com/android/inputmethod/latin/Suggest.java12
-rw-r--r--java/src/com/android/inputmethod/latin/WordComposer.java169
-rw-r--r--java/src/com/android/inputmethod/latin/spellcheck/AndroidSpellCheckerService.java3
-rw-r--r--native/src/correction.cpp94
-rw-r--r--native/src/correction.h2
-rw-r--r--native/src/defines.h1
-rw-r--r--native/src/unigram_dictionary.cpp269
-rw-r--r--native/src/unigram_dictionary.h11
-rw-r--r--native/src/words_priority_queue_pool.h37
-rw-r--r--tests/src/com/android/inputmethod/keyboard/internal/KeyboardStateMultiTouchTests.java4
-rw-r--r--tests/src/com/android/inputmethod/keyboard/internal/KeyboardStateSingleTouchTests.java12
-rw-r--r--tests/src/com/android/inputmethod/keyboard/internal/MockKeyboardSwitcher.java11
-rw-r--r--tests/src/com/android/inputmethod/latin/InputLogicTests.java41
33 files changed, 567 insertions, 601 deletions
diff --git a/java/res/values-sw600dp/config.xml b/java/res/values-sw600dp/config.xml
index 1dd93121d..35da3009f 100644
--- a/java/res/values-sw600dp/config.xml
+++ b/java/res/values-sw600dp/config.xml
@@ -24,14 +24,11 @@
<bool name="config_enable_show_voice_key_option">false</bool>
<bool name="config_enable_show_popup_on_keypress_option">false</bool>
<bool name="config_enable_bigram_suggestions_option">false</bool>
- <bool name="config_sliding_key_input_enabled">false</bool>
<bool name="config_digit_more_keys_enabled">false</bool>
<!-- Whether or not Popup on key press is enabled by default -->
<bool name="config_default_popup_preview">false</bool>
<bool name="config_default_sound_enabled">true</bool>
<bool name="config_auto_correction_spacebar_led_enabled">false</bool>
- <!-- Showing mini keyboard, just above the touched point if true, aligned to the key if false -->
- <bool name="config_show_mini_keyboard_at_touched_point">true</bool>
<!-- The language is never displayed if == 0, always displayed if < 0 -->
<integer name="config_delay_before_fadeout_language_on_spacebar">1200</integer>
<!-- Long pressing space will invoke IME switcher if > 0, never invoke IME switcher if == 0 -->
@@ -39,4 +36,10 @@
<!-- This configuration is the index of the array {@link KeyboardSwitcher.KEYBOARD_THEMES}. -->
<string name="config_default_keyboard_theme_id" translatable="false">5</string>
<integer name="config_max_more_keys_column">5</integer>
+ <!--
+ Configuration for LatinKeyboardView
+ -->
+ <bool name="config_sliding_key_input_enabled">false</bool>
+ <!-- Showing mini keyboard, just above the touched point if true, aligned to the key if false -->
+ <bool name="config_show_mini_keyboard_at_touched_point">true</bool>
</resources>
diff --git a/java/res/values-sw768dp/config.xml b/java/res/values-sw768dp/config.xml
index 06553a7c9..ddeadaf0f 100644
--- a/java/res/values-sw768dp/config.xml
+++ b/java/res/values-sw768dp/config.xml
@@ -24,19 +24,22 @@
<bool name="config_enable_show_voice_key_option">false</bool>
<bool name="config_enable_show_popup_on_keypress_option">false</bool>
<bool name="config_enable_bigram_suggestions_option">false</bool>
- <bool name="config_sliding_key_input_enabled">false</bool>
<bool name="config_digit_more_keys_enabled">false</bool>
<!-- Whether or not Popup on key press is enabled by default -->
<bool name="config_default_popup_preview">false</bool>
<bool name="config_default_sound_enabled">true</bool>
<bool name="config_auto_correction_spacebar_led_enabled">false</bool>
- <!-- Showing mini keyboard, just above the touched point if true, aligned to the key if false -->
- <bool name="config_show_mini_keyboard_at_touched_point">true</bool>
<!-- Long pressing space will invoke IME switcher if > 0, never invoke IME switcher if == 0 -->
<integer name="config_long_press_space_key_timeout">0</integer>
<!-- This configuration is the index of the array {@link KeyboardSwitcher.KEYBOARD_THEMES}. -->
<string name="config_default_keyboard_theme_id" translatable="false">5</string>
<integer name="config_max_more_keys_column">5</integer>
+ <!--
+ Configuration for LatinKeyboardView
+ -->
+ <bool name="config_sliding_key_input_enabled">false</bool>
+ <!-- Showing mini keyboard, just above the touched point if true, aligned to the key if false -->
+ <bool name="config_show_mini_keyboard_at_touched_point">true</bool>
<!-- Screen metrics for logging.
0 = "mdpi phone screen"
1 = "hdpi phone screen"
diff --git a/java/res/values/attrs.xml b/java/res/values/attrs.xml
index 70fc7f86e..b3bd0fe59 100644
--- a/java/res/values/attrs.xml
+++ b/java/res/values/attrs.xml
@@ -103,6 +103,8 @@
<attr name="keyPreviewHeight" format="dimension" />
<!-- Size of the text for key press feedback popup, int the proportion of key height -->
<attr name="keyPreviewTextRatio" format="float" />
+ <!-- Delay after key releasing and key press feedback dismissing in millisecond -->
+ <attr name="keyPreviewLingerTimeout" format="integer" />
<!-- Amount to offset the touch Y coordinate by, for bias correction. -->
<attr name="verticalCorrection" format="dimension" />
@@ -130,6 +132,28 @@
<attr name="spacebarTextRatio" format="fraction" />
<attr name="spacebarTextColor" format="color" />
<attr name="spacebarTextShadowColor" format="color" />
+ <!-- Key detection hysteresis distance. -->
+ <attr name="keyHysteresisDistance" format="dimension" />
+ <!-- Touch noise threshold time in millisecond -->
+ <attr name="touchNoiseThresholdTime" format="integer" />
+ <!-- Touch noise threshold distance in millimeter -->
+ <attr name="touchNoiseThresholdDistance" format="dimension" />
+ <!-- Sliding key input enable -->
+ <attr name="slidingKeyInputEnable" format="boolean" />
+ <!-- Key repeat start timeout -->
+ <attr name="keyRepeatStartTimeout" format="integer" />
+ <!-- Key repeat interval in millisecond. -->
+ <attr name="keyRepeatInterval" format="integer" />
+ <!-- Long press timeout of letter key in millisecond. -->
+ <attr name="longPressKeyTimeout" format="integer" />
+ <!-- Long press timeout of shift key in millisecond. -->
+ <attr name="longPressShiftKeyTimeout" format="integer" />
+ <!-- Long press timeout of space key in millisecond. -->
+ <attr name="longPressSpaceKeyTimeout" format="integer" />
+ <!-- Ignore special key timeout while typing in millisecond. -->
+ <attr name="ignoreSpecialKeyTimeout" format="integer" />
+ <!-- Mini-keyboard will shown at touched point. -->
+ <attr name="showMiniKeyboardAtTouchedPoint" format="boolean" />
</declare-styleable>
<declare-styleable name="SuggestionsView">
diff --git a/java/res/values/config.xml b/java/res/values/config.xml
index 5e6804364..32041df74 100644
--- a/java/res/values/config.xml
+++ b/java/res/values/config.xml
@@ -27,7 +27,6 @@
<bool name="config_enable_bigram_suggestions_option">true</bool>
<!-- TODO: Disable the following configuration for production. -->
<bool name="config_enable_usability_study_mode_option">true</bool>
- <bool name="config_sliding_key_input_enabled">true</bool>
<bool name="config_digit_more_keys_enabled">true</bool>
<!-- Whether or not Popup on key press is enabled by default -->
<bool name="config_default_popup_preview">true</bool>
@@ -39,8 +38,6 @@
<bool name="config_default_bigram_prediction">false</bool>
<bool name="config_default_sound_enabled">false</bool>
<bool name="config_default_vibration_enabled">true</bool>
- <!-- Showing mini keyboard, just above the touched point if true, aligned to the key if false -->
- <bool name="config_show_mini_keyboard_at_touched_point">false</bool>
<!-- The language is never displayed if == 0, always displayed if < 0 -->
<integer name="config_delay_before_fadeout_language_on_spacebar">1200</integer>
<integer name="config_delay_update_suggestions">100</integer>
@@ -48,26 +45,38 @@
<integer name="config_delay_update_shift_state">100</integer>
<integer name="config_duration_of_fadeout_language_on_spacebar">50</integer>
<integer name="config_final_fadeout_percentage_of_language_on_spacebar">50</integer>
- <integer name="config_delay_before_preview">0</integer>
- <integer name="config_delay_after_preview">70</integer>
<integer name="config_mini_keyboard_fadein_anim_time">0</integer>
<integer name="config_mini_keyboard_fadeout_anim_time">100</integer>
- <integer name="config_delay_before_key_repeat_start">400</integer>
- <integer name="config_key_repeat_interval">50</integer>
<integer name="config_keyboard_grid_width">32</integer>
<integer name="config_keyboard_grid_height">16</integer>
+ <integer name="config_double_spaces_turn_into_period_timeout">1100</integer>
+ <!-- This configuration is the index of the array {@link KeyboardSwitcher.KEYBOARD_THEMES}. -->
+ <string name="config_default_keyboard_theme_id" translatable="false">5</string>
+ <integer name="config_max_more_keys_column">5</integer>
+ <!--
+ Configuration for KeyboardView
+ -->
+ <integer name="config_key_preview_linger_timeout">70</integer>
+ <!--
+ Configuration for LatinKeyboardView
+ -->
+ <dimen name="config_key_hysteresis_distance">0.05in</dimen>
+ <integer name="config_touch_noise_threshold_time">40</integer>
+ <dimen name="config_touch_noise_threshold_distance">2.0mm</dimen>
+ <bool name="config_sliding_key_input_enabled">true</bool>
+ <integer name="config_key_repeat_start_timeout">400</integer>
+ <integer name="config_key_repeat_interval">50</integer>
<integer name="config_long_press_key_timeout">400</integer>
<!-- Long pressing shift will invoke caps-lock if > 0, never invoke caps-lock if == 0 -->
<integer name="config_long_press_shift_key_timeout">1200</integer>
<!-- Long pressing space will invoke IME switcher if > 0, never invoke IME switcher if == 0 -->
<integer name="config_long_press_space_key_timeout">@integer/config_long_press_key_timeout</integer>
- <integer name="config_touch_noise_threshold_millis">40</integer>
- <integer name="config_double_spaces_turn_into_period_timeout">1100</integer>
<integer name="config_ignore_special_key_timeout">700</integer>
- <dimen name="config_touch_noise_threshold_distance">2.0mm</dimen>
- <!-- This configuration is the index of the array {@link KeyboardSwitcher.KEYBOARD_THEMES}. -->
- <string name="config_default_keyboard_theme_id" translatable="false">5</string>
- <integer name="config_max_more_keys_column">5</integer>
+ <!-- Showing mini keyboard, just above the touched point if true, aligned to the key if false -->
+ <bool name="config_show_mini_keyboard_at_touched_point">false</bool>
+ <!--
+ Configuration for auto correction
+ -->
<string-array name="auto_correction_threshold_values" translatable="false">
<!-- Off, When auto correction setting is Off, this value is not used. -->
<item></item>
diff --git a/java/res/values/dimens.xml b/java/res/values/dimens.xml
index e46ff7718..95c4e5b8b 100644
--- a/java/res/values/dimens.xml
+++ b/java/res/values/dimens.xml
@@ -96,6 +96,4 @@
<dimen name="more_suggestions_hint_text_size">27dip</dimen>
<integer name="suggestions_count_in_strip">3</integer>
<integer name="center_suggestion_percentile">36</integer>
-
- <dimen name="key_hysteresis_distance">0.05in</dimen>
</resources>
diff --git a/java/res/values/styles.xml b/java/res/values/styles.xml
index c4e39e357..69637d75a 100644
--- a/java/res/values/styles.xml
+++ b/java/res/values/styles.xml
@@ -59,11 +59,24 @@
<item name="keyPreviewOffset">@dimen/key_preview_offset</item>
<item name="keyPreviewHeight">@dimen/key_preview_height</item>
<item name="keyPreviewTextRatio">@fraction/key_preview_text_ratio</item>
+ <item name="keyPreviewLingerTimeout">@integer/config_key_preview_linger_timeout</item>
<item name="moreKeysLayout">@layout/mini_keyboard</item>
<item name="verticalCorrection">@dimen/keyboard_vertical_correction</item>
<item name="shadowColor">#BB000000</item>
<item name="shadowRadius">2.75</item>
<item name="backgroundDimAmount">0.5</item>
+ <!-- Common attributes of LatinKeyboardView -->
+ <item name="keyHysteresisDistance">@dimen/config_key_hysteresis_distance</item>
+ <item name="touchNoiseThresholdTime">@integer/config_touch_noise_threshold_time</item>
+ <item name="touchNoiseThresholdDistance">@dimen/config_touch_noise_threshold_distance</item>
+ <item name="slidingKeyInputEnable">@bool/config_sliding_key_input_enabled</item>
+ <item name="keyRepeatStartTimeout">@integer/config_key_repeat_start_timeout</item>
+ <item name="keyRepeatInterval">@integer/config_key_repeat_interval</item>
+ <item name="longPressKeyTimeout">@integer/config_long_press_key_timeout</item>
+ <item name="longPressShiftKeyTimeout">@integer/config_long_press_shift_key_timeout</item>
+ <item name="longPressSpaceKeyTimeout">@integer/config_long_press_space_key_timeout</item>
+ <item name="ignoreSpecialKeyTimeout">@integer/config_ignore_special_key_timeout</item>
+ <item name="showMiniKeyboardAtTouchedPoint">@bool/config_show_mini_keyboard_at_touched_point</item>
</style>
<style
name="LatinKeyboardView"
diff --git a/java/src/com/android/inputmethod/keyboard/KeyboardSet.java b/java/src/com/android/inputmethod/keyboard/KeyboardSet.java
index cacb8a324..ae5e4e860 100644
--- a/java/src/com/android/inputmethod/keyboard/KeyboardSet.java
+++ b/java/src/com/android/inputmethod/keyboard/KeyboardSet.java
@@ -244,9 +244,10 @@ public class KeyboardSet {
// TODO: Use InputMethodSubtype object as argument.
public Builder setSubtype(Locale inputLocale, boolean asciiCapable,
boolean touchPositionCorrectionEnabled) {
+ final boolean deprecatedForceAscii = Utils.inPrivateImeOptions(
+ mPackageName, LatinIME.IME_OPTION_FORCE_ASCII, mEditorInfo);
final boolean forceAscii = EditorInfoCompatUtils.hasFlagForceAscii(mParams.mImeOptions)
- || Utils.inPrivateImeOptions(
- mPackageName, LatinIME.IME_OPTION_FORCE_ASCII, mEditorInfo);
+ || deprecatedForceAscii;
mParams.mLocale = (forceAscii && !asciiCapable) ? Locale.US : inputLocale;
mParams.mTouchPositionCorrectionEnabled = touchPositionCorrectionEnabled;
return this;
@@ -256,10 +257,11 @@ public class KeyboardSet {
boolean voiceKeyOnMain) {
mParams.mSettingsKeyEnabled = settingsKeyEnabled;
@SuppressWarnings("deprecation")
+ final boolean deprecatedNoMicrophone = Utils.inPrivateImeOptions(
+ null, LatinIME.IME_OPTION_NO_MICROPHONE_COMPAT, mEditorInfo);
final boolean noMicrophone = Utils.inPrivateImeOptions(
mPackageName, LatinIME.IME_OPTION_NO_MICROPHONE, mEditorInfo)
- || Utils.inPrivateImeOptions(
- null, LatinIME.IME_OPTION_NO_MICROPHONE_COMPAT, mEditorInfo);
+ || deprecatedNoMicrophone;
mParams.mVoiceKeyEnabled = voiceKeyEnabled && !noMicrophone;
mParams.mVoiceKeyOnMain = voiceKeyOnMain;
return this;
diff --git a/java/src/com/android/inputmethod/keyboard/KeyboardSwitcher.java b/java/src/com/android/inputmethod/keyboard/KeyboardSwitcher.java
index 1398bae2a..5a59cc1c7 100644
--- a/java/src/com/android/inputmethod/keyboard/KeyboardSwitcher.java
+++ b/java/src/com/android/inputmethod/keyboard/KeyboardSwitcher.java
@@ -240,6 +240,7 @@ public class KeyboardSwitcher implements KeyboardState.SwitchActions,
private static final int ALPHABET_MANUAL_SHIFTED = 1;
private static final int ALPHABET_AUTOMATIC_SHIFTED = 2;
private static final int ALPHABET_SHIFT_LOCKED = 3;
+ private static final int ALPHABET_SHIFT_LOCK_SHIFTED = 4;
// TODO: Remove this method.
private void updateAlphabetKeyboardShiftState(int shiftMode) {
@@ -260,6 +261,10 @@ public class KeyboardSwitcher implements KeyboardState.SwitchActions,
case ALPHABET_SHIFT_LOCKED:
keyboard.setShiftLocked(true);
break;
+ case ALPHABET_SHIFT_LOCK_SHIFTED:
+ keyboard.setShiftLocked(true);
+ keyboard.setShifted(true);
+ break;
}
mKeyboardView.invalidateAllKeys();
if (shiftMode != ALPHABET_SHIFT_LOCKED) {
@@ -300,6 +305,13 @@ public class KeyboardSwitcher implements KeyboardState.SwitchActions,
// Implements {@link KeyboardState.SwitchActions}.
@Override
+ public void setAlphabetShiftLockShiftedKeyboard() {
+ setKeyboard(mKeyboardSet.getMainKeyboard());
+ updateAlphabetKeyboardShiftState(ALPHABET_SHIFT_LOCK_SHIFTED);
+ }
+
+ // Implements {@link KeyboardState.SwitchActions}.
+ @Override
public void setSymbolsKeyboard() {
setKeyboard(mKeyboardSet.getSymbolsKeyboard());
}
diff --git a/java/src/com/android/inputmethod/keyboard/KeyboardView.java b/java/src/com/android/inputmethod/keyboard/KeyboardView.java
index afcf51059..6f4ef2580 100644
--- a/java/src/com/android/inputmethod/keyboard/KeyboardView.java
+++ b/java/src/com/android/inputmethod/keyboard/KeyboardView.java
@@ -17,7 +17,6 @@
package com.android.inputmethod.keyboard;
import android.content.Context;
-import android.content.res.Resources;
import android.content.res.TypedArray;
import android.graphics.Bitmap;
import android.graphics.Canvas;
@@ -39,7 +38,6 @@ import android.widget.RelativeLayout;
import android.widget.TextView;
import com.android.inputmethod.compat.FrameLayoutCompatUtils;
-import com.android.inputmethod.keyboard.internal.KeyboardIconsSet;
import com.android.inputmethod.latin.LatinImeLogger;
import com.android.inputmethod.latin.R;
import com.android.inputmethod.latin.StaticInnerHandlerWrapper;
@@ -103,7 +101,6 @@ public class KeyboardView extends View implements PointerTracker.DrawingProxy {
private final int mKeyPreviewLayoutId;
protected final KeyPreviewDrawParams mKeyPreviewDrawParams;
private boolean mShowKeyPreviewPopup = true;
- private final int mDelayBeforePreview;
private int mDelayAfterPreview;
private ViewGroup mPreviewPlacer;
@@ -135,8 +132,7 @@ public class KeyboardView extends View implements PointerTracker.DrawingProxy {
private final DrawingHandler mDrawingHandler = new DrawingHandler(this);
public static class DrawingHandler extends StaticInnerHandlerWrapper<KeyboardView> {
- private static final int MSG_SHOW_KEY_PREVIEW = 1;
- private static final int MSG_DISMISS_KEY_PREVIEW = 2;
+ private static final int MSG_DISMISS_KEY_PREVIEW = 1;
public DrawingHandler(KeyboardView outerInstance) {
super(outerInstance);
@@ -148,35 +144,12 @@ public class KeyboardView extends View implements PointerTracker.DrawingProxy {
if (keyboardView == null) return;
final PointerTracker tracker = (PointerTracker) msg.obj;
switch (msg.what) {
- case MSG_SHOW_KEY_PREVIEW:
- keyboardView.showKey(tracker);
- break;
case MSG_DISMISS_KEY_PREVIEW:
tracker.getKeyPreviewText().setVisibility(View.INVISIBLE);
break;
}
}
- public void showKeyPreview(long delay, PointerTracker tracker) {
- removeMessages(MSG_SHOW_KEY_PREVIEW);
- final KeyboardView keyboardView = getOuterInstance();
- if (keyboardView == null) return;
- if (tracker.getKeyPreviewText().getVisibility() == VISIBLE || delay == 0) {
- // Show right away, if it's already visible and finger is moving around
- keyboardView.showKey(tracker);
- } else {
- sendMessageDelayed(obtainMessage(MSG_SHOW_KEY_PREVIEW, tracker), delay);
- }
- }
-
- public void cancelShowKeyPreview(PointerTracker tracker) {
- removeMessages(MSG_SHOW_KEY_PREVIEW, tracker);
- }
-
- public void cancelAllShowKeyPreviews() {
- removeMessages(MSG_SHOW_KEY_PREVIEW);
- }
-
public void dismissKeyPreview(long delay, PointerTracker tracker) {
sendMessageDelayed(obtainMessage(MSG_DISMISS_KEY_PREVIEW, tracker), delay);
}
@@ -190,7 +163,6 @@ public class KeyboardView extends View implements PointerTracker.DrawingProxy {
}
public void cancelAllMessages() {
- cancelAllShowKeyPreviews();
cancelAllDismissKeyPreviews();
}
}
@@ -295,6 +267,7 @@ public class KeyboardView extends View implements PointerTracker.DrawingProxy {
public final int mPreviewOffset;
public final int mPreviewHeight;
public final Typeface mKeyTextStyle;
+ public final int mLingerTimeout;
private final float mPreviewTextRatio;
private final float mKeyLetterRatio;
@@ -324,6 +297,7 @@ public class KeyboardView extends View implements PointerTracker.DrawingProxy {
R.styleable.KeyboardView_keyPreviewHeight, 80);
mPreviewTextRatio = getRatio(a, R.styleable.KeyboardView_keyPreviewTextRatio);
mPreviewTextColor = a.getColor(R.styleable.KeyboardView_keyPreviewTextColor, 0);
+ mLingerTimeout = a.getInt(R.styleable.KeyboardView_keyPreviewLingerTimeout, 0);
mKeyLetterRatio = keyDrawParams.mKeyLetterRatio;
mKeyTextStyle = keyDrawParams.mKeyTextStyle;
@@ -363,10 +337,7 @@ public class KeyboardView extends View implements PointerTracker.DrawingProxy {
mBackgroundDimAmount = a.getFloat(R.styleable.KeyboardView_backgroundDimAmount, 0.5f);
a.recycle();
- final Resources res = getResources();
-
- mDelayBeforePreview = res.getInteger(R.integer.config_delay_before_preview);
- mDelayAfterPreview = res.getInteger(R.integer.config_delay_after_preview);
+ mDelayAfterPreview = mKeyPreviewDrawParams.mLingerTimeout;
mPaint.setAntiAlias(true);
mPaint.setTextAlign(Align.CENTER);
@@ -386,8 +357,8 @@ public class KeyboardView extends View implements PointerTracker.DrawingProxy {
* @param keyboard the keyboard to display in this view
*/
public void setKeyboard(Keyboard keyboard) {
- // Remove any pending dismissing preview
- mDrawingHandler.cancelAllShowKeyPreviews();
+ // Remove any pending messages.
+ mDrawingHandler.cancelAllMessages();
if (mKeyboard != null) {
PointerTracker.dismissAllKeyPreviews();
}
@@ -841,18 +812,12 @@ public class KeyboardView extends View implements PointerTracker.DrawingProxy {
@Override
public void showKeyPreview(PointerTracker tracker) {
if (mShowKeyPreviewPopup) {
- mDrawingHandler.showKeyPreview(mDelayBeforePreview, tracker);
+ showKey(tracker);
}
}
@Override
- public void cancelShowKeyPreview(PointerTracker tracker) {
- mDrawingHandler.cancelShowKeyPreview(tracker);
- }
-
- @Override
public void dismissKeyPreview(PointerTracker tracker) {
- mDrawingHandler.cancelShowKeyPreview(tracker);
mDrawingHandler.dismissKeyPreview(mDelayAfterPreview, tracker);
}
diff --git a/java/src/com/android/inputmethod/keyboard/LatinKeyboardView.java b/java/src/com/android/inputmethod/keyboard/LatinKeyboardView.java
index f5b282df3..aee356a0c 100644
--- a/java/src/com/android/inputmethod/keyboard/LatinKeyboardView.java
+++ b/java/src/com/android/inputmethod/keyboard/LatinKeyboardView.java
@@ -18,7 +18,6 @@ package com.android.inputmethod.keyboard;
import android.content.Context;
import android.content.pm.PackageManager;
-import android.content.res.Resources;
import android.content.res.TypedArray;
import android.graphics.Canvas;
import android.graphics.Color;
@@ -103,6 +102,7 @@ public class LatinKeyboardView extends KeyboardView implements PointerTracker.Ke
new WeakHashMap<Key, MoreKeysPanel>();
private final boolean mConfigShowMiniKeyboardAtTouchedPoint;
+ private final PointerTrackerParams mPointerTrackerParams;
private final boolean mIsSpacebarTriggeringPopupByLongPress;
private final SuddenJumpingTouchEventHandler mTouchScreenRegulator;
@@ -114,7 +114,7 @@ public class LatinKeyboardView extends KeyboardView implements PointerTracker.Ke
// To detect double tap.
protected GestureDetector mGestureDetector;
- private final KeyTimerHandler mKeyTimerHandler = new KeyTimerHandler(this);
+ private final KeyTimerHandler mKeyTimerHandler;
private static class KeyTimerHandler extends StaticInnerHandlerWrapper<LatinKeyboardView>
implements TimerProxy {
@@ -126,11 +126,9 @@ public class LatinKeyboardView extends KeyboardView implements PointerTracker.Ke
private final int mKeyRepeatInterval;
private boolean mInKeyRepeat;
- public KeyTimerHandler(LatinKeyboardView outerInstance) {
+ public KeyTimerHandler(LatinKeyboardView outerInstance, int keyRepeatInterval) {
super(outerInstance);
- // TODO: This should be the attribute of LatinKeyboardView.
- final Resources res = outerInstance.getContext().getResources();
- mKeyRepeatInterval = res.getInteger(R.integer.config_key_repeat_interval);
+ mKeyRepeatInterval = keyRepeatInterval;
}
@Override
@@ -253,6 +251,49 @@ public class LatinKeyboardView extends KeyboardView implements PointerTracker.Ke
}
}
+ public static class PointerTrackerParams {
+ public final boolean mSlidingKeyInputEnabled;
+ public final int mKeyRepeatStartTimeout;
+ public final int mLongPressKeyTimeout;
+ public final int mLongPressShiftKeyTimeout;
+ public final int mLongPressSpaceKeyTimeout;
+ public final int mIgnoreSpecialKeyTimeout;
+ public final int mTouchNoiseThresholdTime;
+ public final float mTouchNoiseThresholdDistance;
+
+ public static final PointerTrackerParams DEFAULT = new PointerTrackerParams();
+
+ private PointerTrackerParams() {
+ mSlidingKeyInputEnabled = false;
+ mKeyRepeatStartTimeout = 0;
+ mLongPressKeyTimeout = 0;
+ mLongPressShiftKeyTimeout = 0;
+ mLongPressSpaceKeyTimeout = 0;
+ mIgnoreSpecialKeyTimeout = 0;
+ mTouchNoiseThresholdTime =0;
+ mTouchNoiseThresholdDistance = 0;
+ }
+
+ public PointerTrackerParams(TypedArray latinKeyboardViewAttr) {
+ mSlidingKeyInputEnabled = latinKeyboardViewAttr.getBoolean(
+ R.styleable.LatinKeyboardView_slidingKeyInputEnable, false);
+ mKeyRepeatStartTimeout = latinKeyboardViewAttr.getInt(
+ R.styleable.LatinKeyboardView_keyRepeatStartTimeout, 0);
+ mLongPressKeyTimeout = latinKeyboardViewAttr.getInt(
+ R.styleable.LatinKeyboardView_longPressKeyTimeout, 0);
+ mLongPressShiftKeyTimeout = latinKeyboardViewAttr.getInt(
+ R.styleable.LatinKeyboardView_longPressShiftKeyTimeout, 0);
+ mLongPressSpaceKeyTimeout = latinKeyboardViewAttr.getInt(
+ R.styleable.LatinKeyboardView_longPressSpaceKeyTimeout, 0);
+ mIgnoreSpecialKeyTimeout = latinKeyboardViewAttr.getInt(
+ R.styleable.LatinKeyboardView_ignoreSpecialKeyTimeout, 0);
+ mTouchNoiseThresholdTime = latinKeyboardViewAttr.getInt(
+ R.styleable.LatinKeyboardView_touchNoiseThresholdTime, 0);
+ mTouchNoiseThresholdDistance = latinKeyboardViewAttr.getDimension(
+ R.styleable.LatinKeyboardView_touchNoiseThresholdDistance, 0);
+ }
+ }
+
public LatinKeyboardView(Context context, AttributeSet attrs) {
this(context, attrs, R.attr.latinKeyboardViewStyle);
}
@@ -262,14 +303,6 @@ public class LatinKeyboardView extends KeyboardView implements PointerTracker.Ke
mTouchScreenRegulator = new SuddenJumpingTouchEventHandler(getContext(), this);
- final Resources res = getResources();
- // TODO: This should be the attribute of LatinKeyboardView.
- mConfigShowMiniKeyboardAtTouchedPoint = res.getBoolean(
- R.bool.config_show_mini_keyboard_at_touched_point);
- // TODO: This should be the attribute of LatinKeyboardView.
- final float keyHysteresisDistance = res.getDimension(R.dimen.key_hysteresis_distance);
- mKeyDetector = new KeyDetector(keyHysteresisDistance);
-
final boolean ignoreMultitouch = true;
mGestureDetector = new GestureDetector(
getContext(), new DoubleTapListener(), null, ignoreMultitouch);
@@ -280,11 +313,6 @@ public class LatinKeyboardView extends KeyboardView implements PointerTracker.Ke
PointerTracker.init(mHasDistinctMultitouch, getContext());
- // TODO: This should be the attribute of LatinKeyboardView.
- final int longPressSpaceKeyTimeout =
- res.getInteger(R.integer.config_long_press_space_key_timeout);
- mIsSpacebarTriggeringPopupByLongPress = (longPressSpaceKeyTimeout > 0);
-
final TypedArray a = context.obtainStyledAttributes(
attrs, R.styleable.LatinKeyboardView, defStyle, R.style.LatinKeyboardView);
mAutoCorrectionSpacebarLedEnabled = a.getBoolean(
@@ -296,7 +324,22 @@ public class LatinKeyboardView extends KeyboardView implements PointerTracker.Ke
mSpacebarTextColor = a.getColor(R.styleable.LatinKeyboardView_spacebarTextColor, 0);
mSpacebarTextShadowColor = a.getColor(
R.styleable.LatinKeyboardView_spacebarTextShadowColor, 0);
+
+ mPointerTrackerParams = new PointerTrackerParams(a);
+ mIsSpacebarTriggeringPopupByLongPress = (
+ mPointerTrackerParams.mLongPressSpaceKeyTimeout > 0);
+
+ final float keyHysteresisDistance = a.getDimension(
+ R.styleable.LatinKeyboardView_keyHysteresisDistance, 0);
+ mKeyDetector = new KeyDetector(keyHysteresisDistance);
+ final int keyRepeatInterval = a.getInt(
+ R.styleable.LatinKeyboardView_keyRepeatInterval, 0);
+ mKeyTimerHandler = new KeyTimerHandler(this, keyRepeatInterval);
+ mConfigShowMiniKeyboardAtTouchedPoint = a.getBoolean(
+ R.styleable.LatinKeyboardView_showMiniKeyboardAtTouchedPoint, false);
a.recycle();
+
+ PointerTracker.setParameters(mPointerTrackerParams);
}
public void startIgnoringDoubleTap() {
diff --git a/java/src/com/android/inputmethod/keyboard/PointerTracker.java b/java/src/com/android/inputmethod/keyboard/PointerTracker.java
index 274bd0b31..a75b8f9e6 100644
--- a/java/src/com/android/inputmethod/keyboard/PointerTracker.java
+++ b/java/src/com/android/inputmethod/keyboard/PointerTracker.java
@@ -17,7 +17,6 @@
package com.android.inputmethod.keyboard;
import android.content.Context;
-import android.content.res.Resources;
import android.os.SystemClock;
import android.util.Log;
import android.view.MotionEvent;
@@ -25,7 +24,6 @@ import android.widget.TextView;
import com.android.inputmethod.keyboard.internal.PointerTrackerQueue;
import com.android.inputmethod.latin.LatinImeLogger;
-import com.android.inputmethod.latin.R;
import java.util.ArrayList;
import java.util.List;
@@ -69,7 +67,6 @@ public class PointerTracker {
public void invalidateKey(Key key);
public TextView inflateKeyPreviewText();
public void showKeyPreview(PointerTracker tracker);
- public void cancelShowKeyPreview(PointerTracker tracker);
public void dismissKeyPreview(PointerTracker tracker);
}
@@ -98,14 +95,8 @@ public class PointerTracker {
}
private static KeyboardSwitcher sKeyboardSwitcher;
- private static boolean sConfigSlidingKeyInputEnabled;
- // Timing constants
- private static int sDelayBeforeKeyRepeatStart;
- private static int sLongPressKeyTimeout;
- private static int sLongPressShiftKeyTimeout;
- private static int sLongPressSpaceKeyTimeout;
- private static int sIgnoreSpecialKeyTimeout;
- private static int sTouchNoiseThresholdMillis;
+ // Parameters for pointer handling.
+ private static LatinKeyboardView.PointerTrackerParams sParams;
private static int sTouchNoiseThresholdDistanceSquared;
private static final List<PointerTracker> sTrackers = new ArrayList<PointerTracker>();
@@ -169,20 +160,14 @@ public class PointerTracker {
sPointerTrackerQueue = null;
}
- final Resources res = context.getResources();
- sConfigSlidingKeyInputEnabled = res.getBoolean(R.bool.config_sliding_key_input_enabled);
- sDelayBeforeKeyRepeatStart = res.getInteger(R.integer.config_delay_before_key_repeat_start);
- sLongPressKeyTimeout = res.getInteger(R.integer.config_long_press_key_timeout);
- sLongPressShiftKeyTimeout = res.getInteger(R.integer.config_long_press_shift_key_timeout);
- sLongPressSpaceKeyTimeout = res.getInteger(R.integer.config_long_press_space_key_timeout);
- sIgnoreSpecialKeyTimeout = res.getInteger(R.integer.config_ignore_special_key_timeout);
- sTouchNoiseThresholdMillis = res.getInteger(R.integer.config_touch_noise_threshold_millis);
+ setParameters(LatinKeyboardView.PointerTrackerParams.DEFAULT);
+ sKeyboardSwitcher = KeyboardSwitcher.getInstance();
+ }
- final float touchNoiseThresholdDistance = res.getDimension(
- R.dimen.config_touch_noise_threshold_distance);
+ public static void setParameters(LatinKeyboardView.PointerTrackerParams params) {
+ sParams = params;
sTouchNoiseThresholdDistanceSquared = (int)(
- touchNoiseThresholdDistance * touchNoiseThresholdDistance);
- sKeyboardSwitcher = KeyboardSwitcher.getInstance();
+ params.mTouchNoiseThresholdDistance * params.mTouchNoiseThresholdDistance);
}
public static PointerTracker getPointerTracker(final int id, KeyEventHandler handler) {
@@ -278,7 +263,7 @@ public class PointerTracker {
mListener.onCodeInput(code, keyCodes, x, y);
}
if (!key.altCodeWhileTyping() && !key.isModifier()) {
- mTimerProxy.startKeyTypedTimer(sIgnoreSpecialKeyTimeout);
+ mTimerProxy.startKeyTypedTimer(sParams.mIgnoreSpecialKeyTimeout);
}
}
}
@@ -453,7 +438,7 @@ public class PointerTracker {
setKeyDetectorInner(handler.getKeyDetector());
// Naive up-to-down noise filter.
final long deltaT = eventTime - mUpTime;
- if (deltaT < sTouchNoiseThresholdMillis) {
+ if (deltaT < sParams.mTouchNoiseThresholdTime) {
final int dx = x - mLastX;
final int dy = y - mLastY;
final int distanceSquared = (dx * dx + dy * dy);
@@ -483,7 +468,7 @@ public class PointerTracker {
Key key = onDownKey(x, y, eventTime);
// Sliding key is allowed when 1) enabled by configuration, 2) this pointer starts sliding
// from modifier key, or 3) this pointer's KeyDetector always allows sliding input.
- mIsAllowedSlidingKeyInput = sConfigSlidingKeyInputEnabled
+ mIsAllowedSlidingKeyInput = sParams.mSlidingKeyInputEnabled
|| (key != null && key.isModifier())
|| mKeyDetector.alwaysAllowsSlidingInput();
mKeyboardLayoutHasBeenChanged = false;
@@ -620,7 +605,6 @@ public class PointerTracker {
private void onUpEventInternal(int x, int y, long eventTime) {
mTimerProxy.cancelKeyTimers();
- mDrawingProxy.cancelShowKeyPreview(this);
mIsInSlidingKeyInput = false;
final int keyX, keyY;
if (isMajorEnoughMoveToBeOnNewKey(x, y, onMoveKey(x, y))) {
@@ -673,7 +657,6 @@ public class PointerTracker {
private void onCancelEventInternal() {
mTimerProxy.cancelKeyTimers();
- mDrawingProxy.cancelShowKeyPreview(this);
setReleasedKeyGraphics(mCurrentKey);
mIsInSlidingKeyInput = false;
if (mIsShowingMoreKeysPanel) {
@@ -685,7 +668,7 @@ public class PointerTracker {
private void startRepeatKey(Key key) {
if (key != null && key.isRepeatable()) {
onRepeatKey(key);
- mTimerProxy.startKeyRepeatTimer(sDelayBeforeKeyRepeatStart, this);
+ mTimerProxy.startKeyRepeatTimer(sParams.mKeyRepeatStartTimeout, this);
mIsRepeatableKey = true;
} else {
mIsRepeatableKey = false;
@@ -715,12 +698,12 @@ public class PointerTracker {
private void startLongPressTimer(Key key) {
if (key == null) return;
if (key.mCode == Keyboard.CODE_SHIFT) {
- if (sLongPressShiftKeyTimeout > 0) {
- mTimerProxy.startLongPressTimer(sLongPressShiftKeyTimeout, this);
+ if (sParams.mLongPressShiftKeyTimeout > 0) {
+ mTimerProxy.startLongPressTimer(sParams.mLongPressShiftKeyTimeout, this);
}
} else if (key.mCode == Keyboard.CODE_SPACE) {
- if (sLongPressSpaceKeyTimeout > 0) {
- mTimerProxy.startLongPressTimer(sLongPressSpaceKeyTimeout, this);
+ if (sParams.mLongPressSpaceKeyTimeout > 0) {
+ mTimerProxy.startLongPressTimer(sParams.mLongPressSpaceKeyTimeout, this);
}
} else if (key.hasUppercaseLetter() && mKeyboard.isManualTemporaryUpperCase()) {
// We need not start long press timer on the key which has manual temporary upper case
@@ -728,9 +711,9 @@ public class PointerTracker {
return;
} else if (sKeyboardSwitcher.isInMomentarySwitchState()) {
// We use longer timeout for sliding finger input started from the symbols mode key.
- mTimerProxy.startLongPressTimer(sLongPressKeyTimeout * 3, this);
+ mTimerProxy.startLongPressTimer(sParams.mLongPressKeyTimeout * 3, this);
} else {
- mTimerProxy.startLongPressTimer(sLongPressKeyTimeout, this);
+ mTimerProxy.startLongPressTimer(sParams.mLongPressKeyTimeout, this);
}
}
diff --git a/java/src/com/android/inputmethod/keyboard/internal/KeyboardState.java b/java/src/com/android/inputmethod/keyboard/internal/KeyboardState.java
index 38c31adce..a89356c50 100644
--- a/java/src/com/android/inputmethod/keyboard/internal/KeyboardState.java
+++ b/java/src/com/android/inputmethod/keyboard/internal/KeyboardState.java
@@ -43,8 +43,7 @@ public class KeyboardState {
public void setAlphabetManualShiftedKeyboard();
public void setAlphabetAutomaticShiftedKeyboard();
public void setAlphabetShiftLockedKeyboard();
- // TODO: Add this.
- //public void setAlphabetShiftLockShiftedKeyboard();
+ public void setAlphabetShiftLockShiftedKeyboard();
public void setSymbolsKeyboard();
public void setSymbolsShiftedKeyboard();
@@ -157,14 +156,10 @@ public class KeyboardState {
}
}
- // TODO: Remove this method.
- public boolean isShiftLocked() {
- return mAlphabetShiftState.isShiftLocked();
- }
-
private static final int UNSHIFT = 0;
private static final int MANUAL_SHIFT = 1;
private static final int AUTOMATIC_SHIFT = 2;
+ private static final int SHIFT_LOCK_SHIFTED = 3;
private void setShifted(int shiftMode) {
if (DEBUG_ACTION) {
@@ -198,6 +193,10 @@ public class KeyboardState {
mSwitchActions.setAlphabetKeyboard();
}
break;
+ case SHIFT_LOCK_SHIFTED:
+ mAlphabetShiftState.setShifted(true);
+ mSwitchActions.setAlphabetShiftLockShiftedKeyboard();
+ break;
}
}
@@ -349,7 +348,7 @@ public class KeyboardState {
if (mAlphabetShiftState.isShiftLocked()) {
// Shift key is pressed while caps lock state, we will treat this state as shifted
// caps lock state and mark as if shift key pressed while normal state.
- setShifted(MANUAL_SHIFT);
+ setShifted(SHIFT_LOCK_SHIFTED);
mShiftKeyState.onPress();
} else if (mAlphabetShiftState.isAutomaticTemporaryUpperCase()) {
// Shift key is pressed while automatic temporary upper case, we have to move to
diff --git a/java/src/com/android/inputmethod/latin/BinaryDictionary.java b/java/src/com/android/inputmethod/latin/BinaryDictionary.java
index 3692ac179..b82400046 100644
--- a/java/src/com/android/inputmethod/latin/BinaryDictionary.java
+++ b/java/src/com/android/inputmethod/latin/BinaryDictionary.java
@@ -162,7 +162,7 @@ public class BinaryDictionary extends Dictionary {
}
if (len > 0) {
callback.addWord(mOutputChars_bigrams, start, len, mBigramScores[j],
- mDicTypeId, DataType.BIGRAM);
+ mDicTypeId, Dictionary.BIGRAM);
}
}
}
@@ -182,7 +182,7 @@ public class BinaryDictionary extends Dictionary {
}
if (len > 0) {
callback.addWord(mOutputChars, start, len, mScores[j], mDicTypeId,
- DataType.UNIGRAM);
+ Dictionary.UNIGRAM);
}
}
}
diff --git a/java/src/com/android/inputmethod/latin/Dictionary.java b/java/src/com/android/inputmethod/latin/Dictionary.java
index c35b42877..79bf33850 100644
--- a/java/src/com/android/inputmethod/latin/Dictionary.java
+++ b/java/src/com/android/inputmethod/latin/Dictionary.java
@@ -33,9 +33,8 @@ public abstract class Dictionary {
*/
protected static final int FULL_WORD_SCORE_MULTIPLIER = 2;
- public static enum DataType {
- UNIGRAM, BIGRAM
- }
+ public static final int UNIGRAM = 0;
+ public static final int BIGRAM = 1;
/**
* Interface to be implemented by classes requesting words to be fetched from the dictionary.
@@ -51,11 +50,11 @@ public abstract class Dictionary {
* @param score the score of occurrence. This is normalized between 1 and 255, but
* can exceed those limits
* @param dicTypeId of the dictionary where word was from
- * @param dataType tells type of this data
+ * @param dataType tells type of this data, either UNIGRAM or BIGRAM
* @return true if the word was added, false if no more words are required
*/
boolean addWord(char[] word, int wordOffset, int wordLength, int score, int dicTypeId,
- DataType dataType);
+ int dataType);
}
/**
@@ -64,7 +63,7 @@ public abstract class Dictionary {
* @param composer the key sequence to match
* @param callback the callback object to send matched words to as possible candidates
* @param proximityInfo the object for key proximity. May be ignored by some implementations.
- * @see WordCallback#addWord(char[], int, int, int, int, DataType)
+ * @see WordCallback#addWord(char[], int, int, int, int, int)
*/
abstract public void getWords(final WordComposer composer, final WordCallback callback,
final ProximityInfo proximityInfo);
diff --git a/java/src/com/android/inputmethod/latin/ExpandableDictionary.java b/java/src/com/android/inputmethod/latin/ExpandableDictionary.java
index 7eec8e2ca..8e8adc1c2 100644
--- a/java/src/com/android/inputmethod/latin/ExpandableDictionary.java
+++ b/java/src/com/android/inputmethod/latin/ExpandableDictionary.java
@@ -301,7 +301,7 @@ public class ExpandableDictionary extends Dictionary {
finalFreq = computeSkippedWordFinalFreq(freq, snr, mInputLength);
}
if (!callback.addWord(word, 0, depth + 1, finalFreq, mDicTypeId,
- DataType.UNIGRAM)) {
+ Dictionary.UNIGRAM)) {
return;
}
}
@@ -342,7 +342,7 @@ public class ExpandableDictionary extends Dictionary {
snr * addedAttenuation, mInputLength);
}
callback.addWord(word, 0, depth + 1, finalFreq, mDicTypeId,
- DataType.UNIGRAM);
+ Dictionary.UNIGRAM);
}
}
if (children != null) {
@@ -506,7 +506,7 @@ public class ExpandableDictionary extends Dictionary {
} while (node != null);
callback.addWord(mLookedUpString, index, MAX_WORD_LENGTH - index, freq, mDicTypeId,
- DataType.BIGRAM);
+ Dictionary.BIGRAM);
}
}
diff --git a/java/src/com/android/inputmethod/latin/LastComposedWord.java b/java/src/com/android/inputmethod/latin/LastComposedWord.java
new file mode 100644
index 000000000..0c8c88f50
--- /dev/null
+++ b/java/src/com/android/inputmethod/latin/LastComposedWord.java
@@ -0,0 +1,72 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+
+package com.android.inputmethod.latin;
+
+import android.text.TextUtils;
+
+import java.util.ArrayList;
+
+/**
+ * This class encapsulates data about a word previously composed, but that has been
+ * committed already. This is used for resuming suggestion, and cancel auto-correction.
+ */
+public class LastComposedWord {
+ // COMMIT_TYPE_USER_TYPED_WORD is used when the word committed is the exact typed word, with
+ // no hinting from the IME. It happens when some external event happens (rotating the device,
+ // for example) or when auto-correction is off by settings or editor attributes.
+ public static final int COMMIT_TYPE_USER_TYPED_WORD = 0;
+ // COMMIT_TYPE_MANUAL_PICK is used when the user pressed a field in the suggestion strip.
+ public static final int COMMIT_TYPE_MANUAL_PICK = 1;
+ // COMMIT_TYPE_DECIDED_WORD is used when the IME commits the word it decided was best
+ // for the current user input. It may be different from what the user typed (true auto-correct)
+ // or it may be exactly what the user typed if it's in the dictionary or the IME does not have
+ // enough confidence in any suggestion to auto-correct (auto-correct to typed word).
+ public static final int COMMIT_TYPE_DECIDED_WORD = 2;
+ // COMMIT_TYPE_CANCEL_AUTO_CORRECT is used upon committing back the old word upon cancelling
+ // an auto-correction.
+ public static final int COMMIT_TYPE_CANCEL_AUTO_CORRECT = 3;
+
+ public final ArrayList<int[]> mCodes;
+ public final int[] mXCoordinates;
+ public final int[] mYCoordinates;
+ public final String mTypedWord;
+ public final String mAutoCorrection;
+
+ private boolean mActive;
+
+ public static final LastComposedWord NOT_A_COMPOSED_WORD =
+ new LastComposedWord(null, null, null, "", "");
+
+ public LastComposedWord(final ArrayList<int[]> codes, final int[] xCoordinates,
+ final int[] yCoordinates, final String typedWord, final String autoCorrection) {
+ mCodes = codes;
+ mXCoordinates = xCoordinates;
+ mYCoordinates = yCoordinates;
+ mTypedWord = typedWord;
+ mAutoCorrection = autoCorrection;
+ mActive = true;
+ }
+
+ public void deactivate() {
+ mActive = false;
+ }
+
+ public boolean canCancelAutoCorrect() {
+ return mActive && !TextUtils.isEmpty(mAutoCorrection)
+ && !TextUtils.equals(mTypedWord, mAutoCorrection);
+ }
+}
diff --git a/java/src/com/android/inputmethod/latin/LatinIME.java b/java/src/com/android/inputmethod/latin/LatinIME.java
index d59497d6a..2e7e82637 100644
--- a/java/src/com/android/inputmethod/latin/LatinIME.java
+++ b/java/src/com/android/inputmethod/latin/LatinIME.java
@@ -104,7 +104,6 @@ public class LatinIME extends InputMethodServiceCompatWrapper implements Keyboar
*/
public static final String IME_OPTION_NO_SETTINGS_KEY = "noSettingsKey";
- // TODO: Remove this private option.
/**
* The private IME option used to indicate that the given text field needs
* ASCII code points input.
@@ -200,6 +199,7 @@ public class LatinIME extends InputMethodServiceCompatWrapper implements Keyboar
private UserUnigramDictionary mUserUnigramDictionary;
private boolean mIsUserDictionaryAvailable;
+ private LastComposedWord mLastComposedWord = LastComposedWord.NOT_A_COMPOSED_WORD;
private WordComposer mWordComposer = new WordComposer();
private int mCorrectionMode;
@@ -729,6 +729,17 @@ public class LatinIME extends InputMethodServiceCompatWrapper implements Keyboar
: String.format("inputType=0x%08x imeOptions=0x%08x",
editorInfo.inputType, editorInfo.imeOptions)));
}
+ if (Utils.inPrivateImeOptions(null, IME_OPTION_NO_MICROPHONE_COMPAT, editorInfo)) {
+ Log.w(TAG, "Deprecated private IME option specified: "
+ + editorInfo.privateImeOptions);
+ Log.w(TAG, "Use " + getPackageName() + "." + IME_OPTION_NO_MICROPHONE + " instead");
+ }
+ if (Utils.inPrivateImeOptions(getPackageName(), IME_OPTION_FORCE_ASCII, editorInfo)) {
+ Log.w(TAG, "Deprecated private IME option specified: "
+ + editorInfo.privateImeOptions);
+ Log.w(TAG, "Use EditorInfo.IME_FLAG_FORCE_ASCII flag instead");
+ }
+
LatinImeLogger.onStartInputView(editorInfo);
// In landscape mode, this method gets called without the input view being created.
if (inputView == null) {
@@ -759,7 +770,7 @@ public class LatinIME extends InputMethodServiceCompatWrapper implements Keyboar
inputView.closing();
mEnteredText = null;
- mWordComposer.reset();
+ resetComposingState(true /* alsoResetLastComposedWord */);
mDeleteCount = 0;
mSpaceState = SPACE_STATE_NONE;
@@ -871,7 +882,7 @@ public class LatinIME extends InputMethodServiceCompatWrapper implements Keyboar
if (((mWordComposer.isComposingWord())
|| mVoiceProxy.isVoiceInputHighlighted())
&& (selectionChanged || candidatesCleared)) {
- mWordComposer.reset();
+ resetComposingState(true /* alsoResetLastComposedWord */);
updateSuggestions();
final InputConnection ic = getCurrentInputConnection();
if (ic != null) {
@@ -880,7 +891,9 @@ public class LatinIME extends InputMethodServiceCompatWrapper implements Keyboar
mComposingStateManager.onFinishComposingText();
mVoiceProxy.setVoiceInputHighlighted(false);
} else if (!mWordComposer.isComposingWord()) {
- mWordComposer.reset();
+ // TODO: is the following reset still needed, given that we are not composing
+ // a word?
+ resetComposingState(true /* alsoResetLastComposedWord */);
updateSuggestions();
}
}
@@ -964,7 +977,9 @@ public class LatinIME extends InputMethodServiceCompatWrapper implements Keyboar
.setHasMinimalSuggestion(false);
// When in fullscreen mode, show completions generated by the application
setSuggestions(builder.build());
- mWordComposer.deleteAutoCorrection();
+ // TODO: is this the right thing to do? What should we auto-correct to in
+ // this case? This says to keep whatever the user typed.
+ mWordComposer.setAutoCorrection(mWordComposer.getTypedWord());
setSuggestionStripShown(true);
}
}
@@ -1083,10 +1098,16 @@ public class LatinIME extends InputMethodServiceCompatWrapper implements Keyboar
return super.onKeyUp(keyCode, event);
}
+ private void resetComposingState(final boolean alsoResetLastComposedWord) {
+ mWordComposer.reset();
+ if (alsoResetLastComposedWord)
+ mLastComposedWord = LastComposedWord.NOT_A_COMPOSED_WORD;
+ }
+
public void commitTyped(final InputConnection ic) {
if (!mWordComposer.isComposingWord()) return;
final CharSequence typedWord = mWordComposer.getTypedWord();
- mWordComposer.onCommitWord(WordComposer.COMMIT_TYPE_USER_TYPED_WORD);
+ mLastComposedWord = mWordComposer.commitWord(LastComposedWord.COMMIT_TYPE_USER_TYPED_WORD);
if (typedWord.length() > 0) {
if (ic != null) {
ic.commitText(typedWord, 1);
@@ -1253,6 +1274,7 @@ public class LatinIME extends InputMethodServiceCompatWrapper implements Keyboar
mHandler.cancelDoubleSpacesTimer();
}
+ boolean didAutoCorrect = false;
switch (primaryCode) {
case Keyboard.CODE_DELETE:
mSpaceState = SPACE_STATE_NONE;
@@ -1289,7 +1311,7 @@ public class LatinIME extends InputMethodServiceCompatWrapper implements Keyboar
default:
mSpaceState = SPACE_STATE_NONE;
if (mSettingsValues.isWordSeparator(primaryCode)) {
- handleSeparator(primaryCode, x, y, spaceState);
+ didAutoCorrect = handleSeparator(primaryCode, x, y, spaceState);
} else {
handleCharacter(primaryCode, keyCodes, x, y, spaceState);
}
@@ -1298,6 +1320,8 @@ public class LatinIME extends InputMethodServiceCompatWrapper implements Keyboar
}
switcher.onCodeInput(primaryCode);
// Reset after any single keystroke
+ if (!didAutoCorrect)
+ mLastComposedWord.deactivate();
mEnteredText = null;
}
@@ -1315,7 +1339,7 @@ public class LatinIME extends InputMethodServiceCompatWrapper implements Keyboar
mKeyboardSwitcher.onCodeInput(Keyboard.CODE_OUTPUT_TEXT);
mSpaceState = SPACE_STATE_NONE;
mEnteredText = text;
- mWordComposer.reset();
+ resetComposingState(true /* alsoResetLastComposedWord */);
}
@Override
@@ -1373,7 +1397,7 @@ public class LatinIME extends InputMethodServiceCompatWrapper implements Keyboar
// resuming here. The behavior needs to be different according to text field types,
// and it would be much clearer to test for them explicitly here rather than
// relying on implicit values like "whether the suggestion strip is displayed".
- if (mWordComposer.didAutoCorrectToAnotherWord()) {
+ if (mLastComposedWord.canCancelAutoCorrect()) {
Utils.Stats.onAutoCorrectionCancellation();
cancelAutoCorrect(ic);
return;
@@ -1485,7 +1509,11 @@ public class LatinIME extends InputMethodServiceCompatWrapper implements Keyboar
// separator and it should be treated as a normal character, except in the first
// position where it should not start composing a word.
isComposingWord = (Keyboard.CODE_SINGLE_QUOTE != code);
- mWordComposer.reset();
+ // Here we don't need to reset the last composed word. It will be reset
+ // when we commit this one, if we ever do; if on the other hand we backspace
+ // it entirely and resume suggestions on the previous word, we'd like to still
+ // have touch coordinates for it.
+ resetComposingState(false /* alsoResetLastComposedWord */);
clearSuggestions();
mComposingStateManager.onFinishComposingText();
}
@@ -1537,7 +1565,8 @@ public class LatinIME extends InputMethodServiceCompatWrapper implements Keyboar
}
}
- private void handleSeparator(final int primaryCode, final int x, final int y,
+ // Returns true if we did an autocorrection, false otherwise.
+ private boolean handleSeparator(final int primaryCode, final int x, final int y,
final int spaceState) {
mVoiceProxy.handleSeparator();
mComposingStateManager.onFinishComposingText();
@@ -1548,6 +1577,7 @@ public class LatinIME extends InputMethodServiceCompatWrapper implements Keyboar
mHandler.postUpdateSuggestions();
}
+ boolean didAutoCorrect = false;
// Handle separator
final InputConnection ic = getCurrentInputConnection();
if (ic != null) {
@@ -1562,6 +1592,7 @@ public class LatinIME extends InputMethodServiceCompatWrapper implements Keyboar
&& !mInputAttributes.mInputTypeNoAutoCorrect;
if (shouldAutoCorrect && primaryCode != Keyboard.CODE_SINGLE_QUOTE) {
commitCurrentAutoCorrection(primaryCode, ic);
+ didAutoCorrect = true;
} else {
commitTyped(ic);
}
@@ -1617,12 +1648,13 @@ public class LatinIME extends InputMethodServiceCompatWrapper implements Keyboar
if (ic != null) {
ic.endBatchEdit();
}
+ return didAutoCorrect;
}
private CharSequence getTextWithUnderline(final CharSequence text) {
return mComposingStateManager.isAutoCorrectionIndicatorOn()
? SuggestionSpanUtils.getTextWithAutoCorrectionIndicatorUnderline(this, text)
- : mWordComposer.getTypedWord();
+ : text;
}
private void handleClose() {
@@ -1837,7 +1869,7 @@ public class LatinIME extends InputMethodServiceCompatWrapper implements Keyboar
}
Utils.Stats.onAutoCorrection(typedWord, autoCorrection.toString(), separatorCodePoint);
mExpectingUpdateSelection = true;
- commitChosenWord(autoCorrection, WordComposer.COMMIT_TYPE_DECIDED_WORD);
+ commitChosenWord(autoCorrection, LastComposedWord.COMMIT_TYPE_DECIDED_WORD);
// Add the word to the user unigram dictionary if it's not a known word
addToUserUnigramAndBigramDictionaries(autoCorrection,
UserUnigramDictionary.FREQUENCY_FOR_TYPED);
@@ -1907,7 +1939,7 @@ public class LatinIME extends InputMethodServiceCompatWrapper implements Keyboar
LatinImeLogger.logOnManualSuggestion(mWordComposer.getTypedWord().toString(),
suggestion.toString(), index, suggestions.mWords);
mExpectingUpdateSelection = true;
- commitChosenWord(suggestion, WordComposer.COMMIT_TYPE_MANUAL_PICK);
+ commitChosenWord(suggestion, LastComposedWord.COMMIT_TYPE_MANUAL_PICK);
// Add the word to the auto dictionary if it's not a known word
if (index == 0) {
addToUserUnigramAndBigramDictionaries(suggestion,
@@ -1975,9 +2007,9 @@ public class LatinIME extends InputMethodServiceCompatWrapper implements Keyboar
}
// TODO: figure out here if this is an auto-correct or if the best word is actually
// what user typed. Note: currently this is done much later in
- // WordComposer#didAutoCorrectToAnotherWord by string equality of the remembered
+ // LastComposedWord#canCancelAutoCorrect by string equality of the remembered
// strings.
- mWordComposer.onCommitWord(commitType);
+ mLastComposedWord = mWordComposer.commitWord(commitType);
}
private static final WordComposer sEmptyWordComposer = new WordComposer();
@@ -2141,12 +2173,14 @@ public class LatinIME extends InputMethodServiceCompatWrapper implements Keyboar
// "ic" must not be null
private void cancelAutoCorrect(final InputConnection ic) {
- mWordComposer.resumeSuggestionOnKeptWord();
- final String originallyTypedWord = mWordComposer.getTypedWord();
- final CharSequence autoCorrectedTo = mWordComposer.getAutoCorrectionOrNull();
+ final String originallyTypedWord = mLastComposedWord.mTypedWord;
+ final CharSequence autoCorrectedTo = mLastComposedWord.mAutoCorrection;
final int cancelLength = autoCorrectedTo.length();
final CharSequence separator = ic.getTextBeforeCursor(1, 0);
if (DEBUG) {
+ if (mWordComposer.isComposingWord()) {
+ throw new RuntimeException("cancelAutoCorrect, but we are composing a word");
+ }
final String wordBeforeCursor =
ic.getTextBeforeCursor(cancelLength + 1, 0).subSequence(0, cancelLength)
.toString();
@@ -2165,8 +2199,7 @@ public class LatinIME extends InputMethodServiceCompatWrapper implements Keyboar
ic.commitText(originallyTypedWord, 1);
// Re-insert the separator
ic.commitText(separator, 1);
- mWordComposer.deleteAutoCorrection();
- mWordComposer.onCommitWord(WordComposer.COMMIT_TYPE_CANCEL_AUTO_CORRECT);
+ mLastComposedWord = LastComposedWord.NOT_A_COMPOSED_WORD;
Utils.Stats.onSeparator(separator.charAt(0), WordComposer.NOT_A_COORDINATE,
WordComposer.NOT_A_COORDINATE);
mHandler.cancelUpdateBigramPredictions();
@@ -2180,7 +2213,7 @@ public class LatinIME extends InputMethodServiceCompatWrapper implements Keyboar
// Note: in the interest of code simplicity, we may want to just call
// restartSuggestionsOnWordBeforeCursorIfAtEndOfWord instead, but retrieving
// the old WordComposer allows to reuse the actual typed coordinates.
- mWordComposer.resumeSuggestionOnKeptWord();
+ mWordComposer.resumeSuggestionOnLastComposedWord(mLastComposedWord);
// We resume suggestion, and then we want to set the composing text to the content
// of the word composer again. But since we just manually picked a word, there is
// no composing text at the moment, so we have to delete the word before we set a
@@ -2229,10 +2262,14 @@ public class LatinIME extends InputMethodServiceCompatWrapper implements Keyboar
final CharSequence textBeforeCursor = ic.getTextBeforeCursor(2, 0);
// NOTE: This does not work with surrogate pairs. Hopefully when the keyboard is able to
// enter surrogate pairs this code will have been removed.
- if (Keyboard.CODE_SPACE != textBeforeCursor.charAt(1)) {
- // We should not have come here if the text before the cursor is not a space.
- throw new RuntimeException("Tried to revert a swap of punctuation but we didn't "
+ if (TextUtils.isEmpty(textBeforeCursor)
+ || (Keyboard.CODE_SPACE != textBeforeCursor.charAt(1))) {
+ // We may only come here if the application is changing the text while we are typing.
+ // This is quite a broken case, but not logically impossible, so we shouldn't crash,
+ // but some debugging log may be in order.
+ Log.d(TAG, "Tried to revert a swap of punctuation but we didn't "
+ "find a space just before the cursor.");
+ return false;
}
ic.beginBatchEdit();
ic.deleteSurroundingText(2, 0);
diff --git a/java/src/com/android/inputmethod/latin/LatinImeLogger.java b/java/src/com/android/inputmethod/latin/LatinImeLogger.java
index 6f1adfe71..e3dadf250 100644
--- a/java/src/com/android/inputmethod/latin/LatinImeLogger.java
+++ b/java/src/com/android/inputmethod/latin/LatinImeLogger.java
@@ -21,7 +21,6 @@ import android.content.SharedPreferences;
import android.view.inputmethod.EditorInfo;
import com.android.inputmethod.keyboard.Keyboard;
-import com.android.inputmethod.latin.Dictionary.DataType;
import java.util.List;
@@ -75,7 +74,7 @@ public class LatinImeLogger implements SharedPreferences.OnSharedPreferenceChang
public static void onStartSuggestion(CharSequence previousWords) {
}
- public static void onAddSuggestedWord(String word, int typeId, DataType dataType) {
+ public static void onAddSuggestedWord(String word, int typeId, int dataType) {
}
public static void onSetKeyboard(Keyboard kb) {
diff --git a/java/src/com/android/inputmethod/latin/Settings.java b/java/src/com/android/inputmethod/latin/Settings.java
index 5af21452a..d32310096 100644
--- a/java/src/com/android/inputmethod/latin/Settings.java
+++ b/java/src/com/android/inputmethod/latin/Settings.java
@@ -218,7 +218,7 @@ public class Settings extends InputMethodSettingsActivity
res.getString(R.string.key_preview_popup_dismiss_default_delay),
};
final String popupDismissDelayDefaultValue = Integer.toString(res.getInteger(
- R.integer.config_delay_after_preview));
+ R.integer.config_key_preview_linger_timeout));
mKeyPreviewPopupDismissDelay.setEntries(entries);
mKeyPreviewPopupDismissDelay.setEntryValues(
new String[] { "0", popupDismissDelayDefaultValue });
diff --git a/java/src/com/android/inputmethod/latin/SettingsValues.java b/java/src/com/android/inputmethod/latin/SettingsValues.java
index 0ae28d3fc..e6ef3962e 100644
--- a/java/src/com/android/inputmethod/latin/SettingsValues.java
+++ b/java/src/com/android/inputmethod/latin/SettingsValues.java
@@ -124,7 +124,7 @@ public class SettingsValues {
mUsabilityStudyMode = getUsabilityStudyMode(prefs);
mKeyPreviewPopupDismissDelayRawValue = prefs.getString(
Settings.PREF_KEY_PREVIEW_POPUP_DISMISS_DELAY,
- Integer.toString(res.getInteger(R.integer.config_delay_after_preview)));
+ Integer.toString(res.getInteger(R.integer.config_key_preview_linger_timeout)));
mUseContactsDict = prefs.getBoolean(Settings.PREF_KEY_USE_CONTACTS_DICT, true);
mAutoCorrectEnabled = isAutoCorrectEnabled(res, mAutoCorrectionThresholdRawValue);
mBigramSuggestionEnabled = mAutoCorrectEnabled
@@ -234,7 +234,8 @@ public class SettingsValues {
Resources resources) {
// TODO: use mKeyPreviewPopupDismissDelayRawValue instead of reading it again here.
return Integer.parseInt(sp.getString(Settings.PREF_KEY_PREVIEW_POPUP_DISMISS_DELAY,
- Integer.toString(resources.getInteger(R.integer.config_delay_after_preview))));
+ Integer.toString(resources.getInteger(
+ R.integer.config_key_preview_linger_timeout))));
}
private static boolean isBigramSuggestionEnabled(final SharedPreferences sp,
diff --git a/java/src/com/android/inputmethod/latin/Suggest.java b/java/src/com/android/inputmethod/latin/Suggest.java
index 8e0d031f4..f6e177aaf 100644
--- a/java/src/com/android/inputmethod/latin/Suggest.java
+++ b/java/src/com/android/inputmethod/latin/Suggest.java
@@ -298,7 +298,7 @@ public class Suggest implements Dictionary.WordCallback {
if (typedWord != null) {
// Treating USER_TYPED as UNIGRAM suggestion for logging now.
LatinImeLogger.onAddSuggestedWord(typedWord, Suggest.DIC_USER_TYPED,
- Dictionary.DataType.UNIGRAM);
+ Dictionary.UNIGRAM);
}
mConsideredWord = consideredWord;
@@ -417,12 +417,12 @@ public class Suggest implements Dictionary.WordCallback {
@Override
public boolean addWord(final char[] word, final int offset, final int length, int score,
- final int dicTypeId, final Dictionary.DataType dataType) {
- Dictionary.DataType dataTypeForLog = dataType;
+ final int dicTypeId, final int dataType) {
+ int dataTypeForLog = dataType;
final ArrayList<CharSequence> suggestions;
final int[] sortedScores;
final int prefMaxSuggestions;
- if(dataType == Dictionary.DataType.BIGRAM) {
+ if (dataType == Dictionary.BIGRAM) {
suggestions = mBigramSuggestions;
sortedScores = mBigramScores;
prefMaxSuggestions = PREF_MAX_BIGRAMS;
@@ -450,11 +450,11 @@ public class Suggest implements Dictionary.WordCallback {
}
}
} else {
- if (dataType == Dictionary.DataType.UNIGRAM) {
+ if (dataType == Dictionary.UNIGRAM) {
// Check if the word was already added before (by bigram data)
int bigramSuggestion = searchBigramSuggestion(word,offset,length);
if(bigramSuggestion >= 0) {
- dataTypeForLog = Dictionary.DataType.BIGRAM;
+ dataTypeForLog = Dictionary.BIGRAM;
// turn freq from bigram into multiplier specified above
double multiplier = (((double) mBigramScores[bigramSuggestion])
/ MAXIMUM_BIGRAM_FREQUENCY)
diff --git a/java/src/com/android/inputmethod/latin/WordComposer.java b/java/src/com/android/inputmethod/latin/WordComposer.java
index e95dcfdc9..b96b0842a 100644
--- a/java/src/com/android/inputmethod/latin/WordComposer.java
+++ b/java/src/com/android/inputmethod/latin/WordComposer.java
@@ -33,64 +33,17 @@ public class WordComposer {
public static final int NOT_A_CODE = KeyDetector.NOT_A_CODE;
public static final int NOT_A_COORDINATE = -1;
- // TODO: Straighten out commit behavior so that the flags here are more understandable,
- // and possibly adjust their names.
- // COMMIT_TYPE_USER_TYPED_WORD is used when the word committed is the exact typed word, with
- // no hinting from the IME. It happens when some external event happens (rotating the device,
- // for example) or when auto-correction is off by settings or editor attributes.
- public static final int COMMIT_TYPE_USER_TYPED_WORD = 0;
- // COMMIT_TYPE_MANUAL_PICK is used when the user pressed a field in the suggestion strip.
- public static final int COMMIT_TYPE_MANUAL_PICK = 1;
- // COMMIT_TYPE_DECIDED_WORD is used when the IME commits the word it decided was best
- // for the current user input. It may be different from what the user typed (true auto-correct)
- // or it may be exactly what the user typed if it's in the dictionary or the IME does not have
- // enough confidence in any suggestion to auto-correct (auto-correct to typed word).
- public static final int COMMIT_TYPE_DECIDED_WORD = 2;
- // COMMIT_TYPE_CANCEL_AUTO_CORRECT is used upon committing back the old word upon cancelling
- // an auto-correction.
- public static final int COMMIT_TYPE_CANCEL_AUTO_CORRECT = 3;
-
- // Storage for all the info about the current input.
- private static class CharacterStore {
- /**
- * The list of unicode values for each keystroke (including surrounding keys)
- */
- ArrayList<int[]> mCodes;
- int[] mXCoordinates;
- int[] mYCoordinates;
- StringBuilder mTypedWord;
- CharSequence mAutoCorrection;
- CharacterStore() {
- final int N = BinaryDictionary.MAX_WORD_LENGTH;
- mCodes = new ArrayList<int[]>(N);
- mTypedWord = new StringBuilder(N);
- mXCoordinates = new int[N];
- mYCoordinates = new int[N];
- mAutoCorrection = null;
- }
- CharacterStore(final CharacterStore that) {
- mCodes = new ArrayList<int[]>(that.mCodes);
- mTypedWord = new StringBuilder(that.mTypedWord);
- mXCoordinates = Arrays.copyOf(that.mXCoordinates, that.mXCoordinates.length);
- mYCoordinates = Arrays.copyOf(that.mYCoordinates, that.mYCoordinates.length);
- }
- void reset() {
- // For better performance than creating a new character store.
- mCodes.clear();
- mTypedWord.setLength(0);
- mAutoCorrection = null;
- }
- }
+ final int N = BinaryDictionary.MAX_WORD_LENGTH;
- // The currently typing word. May not be null.
- private CharacterStore mCurrentWord;
- // The information being kept for resuming suggestion. May be null if wiped.
- private CharacterStore mCommittedWordSavedForSuggestionResuming;
+ private ArrayList<int[]> mCodes;
+ private int[] mXCoordinates;
+ private int[] mYCoordinates;
+ private StringBuilder mTypedWord;
+ private CharSequence mAutoCorrection;
+ // Cache these values for performance
private int mCapsCount;
-
private boolean mAutoCapitalized;
- // Cache this value for performance
private int mTrailingSingleQuotesCount;
/**
@@ -99,8 +52,11 @@ public class WordComposer {
private boolean mIsFirstCharCapitalized;
public WordComposer() {
- mCurrentWord = new CharacterStore();
- mCommittedWordSavedForSuggestionResuming = null;
+ mCodes = new ArrayList<int[]>(N);
+ mTypedWord = new StringBuilder(N);
+ mXCoordinates = new int[N];
+ mYCoordinates = new int[N];
+ mAutoCorrection = null;
mTrailingSingleQuotesCount = 0;
}
@@ -109,8 +65,10 @@ public class WordComposer {
}
public void init(WordComposer source) {
- mCurrentWord = new CharacterStore(source.mCurrentWord);
- mCommittedWordSavedForSuggestionResuming = source.mCommittedWordSavedForSuggestionResuming;
+ mCodes = new ArrayList<int[]>(source.mCodes);
+ mTypedWord = new StringBuilder(source.mTypedWord);
+ mXCoordinates = Arrays.copyOf(source.mXCoordinates, source.mXCoordinates.length);
+ mYCoordinates = Arrays.copyOf(source.mYCoordinates, source.mYCoordinates.length);
mCapsCount = source.mCapsCount;
mIsFirstCharCapitalized = source.mIsFirstCharCapitalized;
mAutoCapitalized = source.mAutoCapitalized;
@@ -121,8 +79,9 @@ public class WordComposer {
* Clear out the keys registered so far.
*/
public void reset() {
- mCurrentWord.reset();
- mCommittedWordSavedForSuggestionResuming = null;
+ mCodes.clear();
+ mTypedWord.setLength(0);
+ mAutoCorrection = null;
mCapsCount = 0;
mIsFirstCharCapitalized = false;
mTrailingSingleQuotesCount = 0;
@@ -133,7 +92,7 @@ public class WordComposer {
* @return the number of keystrokes
*/
public final int size() {
- return mCurrentWord.mTypedWord.length();
+ return mTypedWord.length();
}
public final boolean isComposingWord() {
@@ -146,15 +105,15 @@ public class WordComposer {
* @return the unicode for the pressed and surrounding keys
*/
public int[] getCodesAt(int index) {
- return mCurrentWord.mCodes.get(index);
+ return mCodes.get(index);
}
public int[] getXCoordinates() {
- return mCurrentWord.mXCoordinates;
+ return mXCoordinates;
}
public int[] getYCoordinates() {
- return mCurrentWord.mYCoordinates;
+ return mYCoordinates;
}
private static boolean isFirstCharCapitalized(int index, int codePoint, boolean previous) {
@@ -169,12 +128,12 @@ public class WordComposer {
*/
public void add(int primaryCode, int[] codes, int x, int y) {
final int newIndex = size();
- mCurrentWord.mTypedWord.append((char) primaryCode);
+ mTypedWord.append((char) primaryCode);
correctPrimaryJuxtapos(primaryCode, codes);
- mCurrentWord.mCodes.add(codes);
+ mCodes.add(codes);
if (newIndex < BinaryDictionary.MAX_WORD_LENGTH) {
- mCurrentWord.mXCoordinates[newIndex] = x;
- mCurrentWord.mYCoordinates[newIndex] = y;
+ mXCoordinates[newIndex] = x;
+ mYCoordinates[newIndex] = y;
}
mIsFirstCharCapitalized = isFirstCharCapitalized(
newIndex, primaryCode, mIsFirstCharCapitalized);
@@ -184,7 +143,7 @@ public class WordComposer {
} else {
mTrailingSingleQuotesCount = 0;
}
- mCurrentWord.mAutoCorrection = null;
+ mAutoCorrection = null;
}
/**
@@ -218,7 +177,6 @@ public class WordComposer {
int codePoint = word.charAt(i);
addKeyInfo(codePoint, keyboard, keyDetector);
}
- mCommittedWordSavedForSuggestionResuming = null;
}
/**
@@ -254,9 +212,9 @@ public class WordComposer {
final int size = size();
if (size > 0) {
final int lastPos = size - 1;
- char lastChar = mCurrentWord.mTypedWord.charAt(lastPos);
- mCurrentWord.mCodes.remove(lastPos);
- mCurrentWord.mTypedWord.deleteCharAt(lastPos);
+ char lastChar = mTypedWord.charAt(lastPos);
+ mCodes.remove(lastPos);
+ mTypedWord.deleteCharAt(lastPos);
if (Character.isUpperCase(lastChar)) mCapsCount--;
}
if (size() == 0) {
@@ -265,12 +223,12 @@ public class WordComposer {
if (mTrailingSingleQuotesCount > 0) {
--mTrailingSingleQuotesCount;
} else {
- for (int i = mCurrentWord.mTypedWord.length() - 1; i >= 0; --i) {
- if (Keyboard.CODE_SINGLE_QUOTE != mCurrentWord.mTypedWord.codePointAt(i)) break;
+ for (int i = mTypedWord.length() - 1; i >= 0; --i) {
+ if (Keyboard.CODE_SINGLE_QUOTE != mTypedWord.codePointAt(i)) break;
++mTrailingSingleQuotesCount;
}
}
- mCurrentWord.mAutoCorrection = null;
+ mAutoCorrection = null;
}
/**
@@ -278,7 +236,7 @@ public class WordComposer {
* @return the word that was typed so far. Never returns null.
*/
public String getTypedWord() {
- return mCurrentWord.mTypedWord.toString();
+ return mTypedWord.toString();
}
/**
@@ -329,26 +287,18 @@ public class WordComposer {
* Sets the auto-correction for this word.
*/
public void setAutoCorrection(final CharSequence correction) {
- mCurrentWord.mAutoCorrection = correction;
- }
-
- /**
- * Remove any auto-correction that may have been set.
- */
- public void deleteAutoCorrection() {
- mCurrentWord.mAutoCorrection = null;
+ mAutoCorrection = correction;
}
/**
* @return the auto-correction for this word, or null if none.
*/
public CharSequence getAutoCorrectionOrNull() {
- return mCurrentWord.mAutoCorrection;
+ return mAutoCorrection;
}
- // `type' should be one of the COMMIT_TYPE_* constants above.
- public void onCommitWord(final int type) {
- mCommittedWordSavedForSuggestionResuming = mCurrentWord;
+ // `type' should be one of the LastComposedWord.COMMIT_TYPE_* constants above.
+ public LastComposedWord commitWord(final int type) {
// Note: currently, we come here whenever we commit a word. If it's any *other* kind than
// DECIDED_WORD, we should reset mAutoCorrection so that we don't attempt to cancel later.
// If it's a DECIDED_WORD, it may be an actual auto-correction by the IME, or what the user
@@ -356,30 +306,25 @@ public class WordComposer {
// Ideally we would also null it when it was a DECIDED_WORD that was not an auto-correct.
// As it happens these two cases should behave differently, because the former can be
// canceled while the latter can't. Currently, we figure this out in
- // #didAutoCorrectToAnotherWord with #equals(). It would be marginally cleaner to do it
- // here, but it would be slower (since we would #equals() for each commit, instead of
- // only on cancel), and ultimately we want to figure it out even earlier anyway.
- if (type != COMMIT_TYPE_DECIDED_WORD) {
- // Only ever revert an auto-correct.
- mCommittedWordSavedForSuggestionResuming.mAutoCorrection = null;
- }
- // TODO: improve performance by swapping buffers instead of creating a new object.
- mCurrentWord = new CharacterStore();
- }
-
- public boolean hasWordKeptForSuggestionResuming() {
- return null != mCommittedWordSavedForSuggestionResuming;
- }
-
- public void resumeSuggestionOnKeptWord() {
- mCurrentWord = mCommittedWordSavedForSuggestionResuming;
- mCommittedWordSavedForSuggestionResuming = null;
+ // LastComposedWord#didAutoCorrectToAnotherWord with #equals(). It would be marginally
+ // cleaner to do it here, but it would be slower (since we would #equals() for each commit,
+ // instead of only on cancel), and ultimately we want to figure it out even earlier anyway.
+ final LastComposedWord lastComposedWord = new LastComposedWord(mCodes,
+ mXCoordinates, mYCoordinates, mTypedWord.toString(),
+ (type != LastComposedWord.COMMIT_TYPE_DECIDED_WORD) || (null == mAutoCorrection)
+ ? null : mAutoCorrection.toString());
+ mCodes.clear();
+ mTypedWord.setLength(0);
+ mAutoCorrection = null;
+ return lastComposedWord;
}
- public boolean didAutoCorrectToAnotherWord() {
- return null != mCommittedWordSavedForSuggestionResuming
- && !TextUtils.isEmpty(mCommittedWordSavedForSuggestionResuming.mAutoCorrection)
- && !TextUtils.equals(mCommittedWordSavedForSuggestionResuming.mTypedWord,
- mCommittedWordSavedForSuggestionResuming.mAutoCorrection);
+ public void resumeSuggestionOnLastComposedWord(final LastComposedWord lastComposedWord) {
+ mCodes = lastComposedWord.mCodes;
+ mXCoordinates = lastComposedWord.mXCoordinates;
+ mYCoordinates = lastComposedWord.mYCoordinates;
+ mTypedWord.setLength(0);
+ mTypedWord.append(lastComposedWord.mTypedWord);
+ mAutoCorrection = lastComposedWord.mAutoCorrection;
}
}
diff --git a/java/src/com/android/inputmethod/latin/spellcheck/AndroidSpellCheckerService.java b/java/src/com/android/inputmethod/latin/spellcheck/AndroidSpellCheckerService.java
index 39e47f661..88ac043ed 100644
--- a/java/src/com/android/inputmethod/latin/spellcheck/AndroidSpellCheckerService.java
+++ b/java/src/com/android/inputmethod/latin/spellcheck/AndroidSpellCheckerService.java
@@ -31,7 +31,6 @@ import com.android.inputmethod.compat.SuggestionsInfoCompatUtils;
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;
@@ -237,7 +236,7 @@ public class AndroidSpellCheckerService extends SpellCheckerService
@Override
synchronized public boolean addWord(char[] word, int wordOffset, int wordLength, int score,
- int dicTypeId, DataType dataType) {
+ int dicTypeId, int dataType) {
final int positionIndex = ArraysCompatUtils.binarySearch(mScores, 0, mLength, score);
// binarySearch returns the index if the element exists, and -<insertion index> - 1
// if it doesn't. See documentation for binarySearch.
diff --git a/native/src/correction.cpp b/native/src/correction.cpp
index d7d05edc2..2458bca86 100644
--- a/native/src/correction.cpp
+++ b/native/src/correction.cpp
@@ -903,100 +903,6 @@ int Correction::RankingAlgorithm::calcFreqForSplitTwoWords(
return totalFreq;
}
-/* static */
-int Correction::RankingAlgorithm::calcFreqForSplitTwoWordsOld(
- const int firstFreq, const int secondFreq, const Correction* correction,
- const unsigned short *word) {
- const int spaceProximityPos = correction->mSpaceProximityPos;
- const int missingSpacePos = correction->mMissingSpacePos;
- if (DEBUG_DICT) {
- int inputCount = 0;
- if (spaceProximityPos >= 0) ++inputCount;
- if (missingSpacePos >= 0) ++inputCount;
- assert(inputCount <= 1);
- }
- const bool isSpaceProximity = spaceProximityPos >= 0;
- const int inputLength = correction->mInputLength;
- const int firstWordLength = isSpaceProximity ? spaceProximityPos : missingSpacePos;
- const int secondWordLength = isSpaceProximity ? (inputLength - spaceProximityPos - 1)
- : (inputLength - missingSpacePos);
- const int typedLetterMultiplier = correction->TYPED_LETTER_MULTIPLIER;
-
- bool firstCapitalizedWordDemotion = false;
- if (firstWordLength >= 2) {
- firstCapitalizedWordDemotion = isUpperCase(word[0]);
- }
-
- bool secondCapitalizedWordDemotion = false;
- if (secondWordLength >= 2) {
- secondCapitalizedWordDemotion = isUpperCase(word[firstWordLength + 1]);
- }
-
- const bool capitalizedWordDemotion =
- firstCapitalizedWordDemotion ^ secondCapitalizedWordDemotion;
-
- if (DEBUG_DICT_FULL) {
- AKLOGI("Two words: %c, %c, %d",
- word[0], word[firstWordLength + 1], capitalizedWordDemotion);
- }
-
- if (firstWordLength == 0 || secondWordLength == 0) {
- return 0;
- }
- const int firstDemotionRate = 100 - 100 / (firstWordLength + 1);
- int tempFirstFreq = firstFreq;
- multiplyRate(firstDemotionRate, &tempFirstFreq);
-
- const int secondDemotionRate = 100 - 100 / (secondWordLength + 1);
- int tempSecondFreq = secondFreq;
- multiplyRate(secondDemotionRate, &tempSecondFreq);
-
- const int totalLength = firstWordLength + secondWordLength;
-
- // Promote pairFreq with multiplying by 2, because the word length is the same as the typed
- // length.
- int totalFreq = tempFirstFreq + tempSecondFreq;
-
- // This is a workaround to try offsetting the not-enough-demotion which will be done in
- // calcNormalizedScore in Utils.java.
- // In calcNormalizedScore the score will be demoted by (1 - 1 / length)
- // but we demoted only (1 - 1 / (length + 1)) so we will additionally adjust freq by
- // (1 - 1 / length) / (1 - 1 / (length + 1)) = (1 - 1 / (length * length))
- const int normalizedScoreNotEnoughDemotionAdjustment = 100 - 100 / (totalLength * totalLength);
- multiplyRate(normalizedScoreNotEnoughDemotionAdjustment, &totalFreq);
-
- // At this moment, totalFreq is calculated by the following formula:
- // (firstFreq * (1 - 1 / (firstWordLength + 1)) + secondFreq * (1 - 1 / (secondWordLength + 1)))
- // * (1 - 1 / totalLength) / (1 - 1 / (totalLength + 1))
-
- multiplyIntCapped(powerIntCapped(typedLetterMultiplier, totalLength), &totalFreq);
-
- // This is another workaround to offset the demotion which will be done in
- // calcNormalizedScore in Utils.java.
- // In calcNormalizedScore the score will be demoted by (1 - 1 / length) so we have to promote
- // the same amount because we already have adjusted the synthetic freq of this "missing or
- // mistyped space" suggestion candidate above in this method.
- const int normalizedScoreDemotionRateOffset = (100 + 100 / totalLength);
- multiplyRate(normalizedScoreDemotionRateOffset, &totalFreq);
-
- if (isSpaceProximity) {
- // A word pair with one space proximity correction
- if (DEBUG_DICT) {
- AKLOGI("Found a word pair with space proximity correction.");
- }
- multiplyIntCapped(typedLetterMultiplier, &totalFreq);
- multiplyRate(WORDS_WITH_PROXIMITY_CHARACTER_DEMOTION_RATE, &totalFreq);
- }
-
- multiplyRate(WORDS_WITH_MISSING_SPACE_CHARACTER_DEMOTION_RATE, &totalFreq);
-
- if (capitalizedWordDemotion) {
- multiplyRate(TWO_WORDS_CAPITALIZED_DEMOTION_RATE, &totalFreq);
- }
-
- return totalFreq;
-}
-
/* Damerau-Levenshtein distance */
inline static int editDistanceInternal(
int* editDistanceTable, const unsigned short* before,
diff --git a/native/src/correction.h b/native/src/correction.h
index b00c8e120..aec7bbd73 100644
--- a/native/src/correction.h
+++ b/native/src/correction.h
@@ -152,8 +152,6 @@ class Correction {
const int inputLength);
static int calcFreqForSplitTwoWords(const int firstFreq, const int secondFreq,
const Correction* correction, const unsigned short *word);
- static int calcFreqForSplitTwoWordsOld(const int firstFreq, const int secondFreq,
- const Correction* correction, const unsigned short *word);
static double calcNormalizedScore(const unsigned short* before, const int beforeLength,
const unsigned short* after, const int afterLength, const int score);
static int editDistance(const unsigned short* before,
diff --git a/native/src/defines.h b/native/src/defines.h
index 9c2d08777..7e171acfd 100644
--- a/native/src/defines.h
+++ b/native/src/defines.h
@@ -217,6 +217,7 @@ static void prof_out(void) {
#define SUB_QUEUE_MAX_WORDS 1
#define SUB_QUEUE_MAX_COUNT 10
#define SUB_QUEUE_MIN_WORD_LENGTH 4
+#define SUB_QUEUE_MAX_WORD_INDEX 2
#define TWO_WORDS_CORRECTION_WITH_OTHER_ERROR_THRESHOLD 0.39
#define START_TWO_WORDS_CORRECTION_THRESHOLD 0.22
diff --git a/native/src/unigram_dictionary.cpp b/native/src/unigram_dictionary.cpp
index a7eb4e10d..fd6f14af8 100644
--- a/native/src/unigram_dictionary.cpp
+++ b/native/src/unigram_dictionary.cpp
@@ -260,7 +260,7 @@ void UnigramDictionary::getWordSuggestions(ProximityInfo *proximityInfo,
if (DEBUG_DICT) {
queuePool->dumpSubQueue1TopSuggestions();
for (int i = 0; i < SUB_QUEUE_MAX_COUNT; ++i) {
- WordsPriorityQueue* queue = queuePool->getSubQueue1(i);
+ WordsPriorityQueue* queue = queuePool->getSubQueue(FIRST_WORD_INDEX, i);
if (queue->size() > 0) {
WordsPriorityQueue::SuggestedWord* sw = queue->top();
const int score = sw->mScore;
@@ -395,11 +395,8 @@ inline void UnigramDictionary::onTerminal(const int freq,
// or more length.
if (inputIndex >= SUB_QUEUE_MIN_WORD_LENGTH && addToSubQueue) {
WordsPriorityQueue *subQueue;
- if (currentWordIndex == 1) {
- subQueue = queuePool->getSubQueue1(inputIndex);
- } else if (currentWordIndex == 2) {
- subQueue = queuePool->getSubQueue2(inputIndex);
- } else {
+ subQueue = queuePool->getSubQueue(currentWordIndex, inputIndex);
+ if (!subQueue) {
return;
}
const int finalFreq = correction->getFinalFreqForSubQueue(freq, &wordPointer, &wordLength,
@@ -408,213 +405,123 @@ inline void UnigramDictionary::onTerminal(const int freq,
}
}
-void UnigramDictionary::getSplitTwoWordsSuggestions(ProximityInfo *proximityInfo,
- const int *xcoordinates, const int *ycoordinates, const int *codes,
- const bool useFullEditDistance, const int inputLength, const int missingSpacePos,
- const int spaceProximityPos, Correction *correction, WordsPriorityQueuePool* queuePool,
- const bool hasAutoCorrectionCandidate) {
- if (inputLength >= MAX_WORD_LENGTH) return;
- if (DEBUG_DICT) {
- int inputCount = 0;
- if (spaceProximityPos >= 0) ++inputCount;
- if (missingSpacePos >= 0) ++inputCount;
- assert(inputCount <= 1);
- // MAX_PROXIMITY_CHARS_SIZE in ProximityInfo.java should be 16
- assert(MAX_PROXIMITY_CHARS == 16);
- }
-
- initSuggestions(proximityInfo, xcoordinates, ycoordinates, codes,
- inputLength, correction);
- WordsPriorityQueue *masterQueue = queuePool->getMasterQueue();
- const bool isSpaceProximity = spaceProximityPos >= 0;
-
- // First word
- const int firstInputWordStartPos = 0;
- const int firstInputWordLength = isSpaceProximity ? spaceProximityPos : missingSpacePos;
- int firstFreq = getMostFrequentWordLike(
- firstInputWordStartPos, firstInputWordLength, proximityInfo, mWord);
- unsigned short* firstOutputWord = 0;
- int firstOutputWordLength = 0;
- if (firstFreq > 0) {
- firstOutputWordLength = firstInputWordLength;
- firstOutputWord = mWord;
+int UnigramDictionary::getSubStringSuggestion(
+ ProximityInfo *proximityInfo, const int *xcoordinates, const int *ycoordinates,
+ const int *codes, const bool useFullEditDistance, Correction *correction,
+ WordsPriorityQueuePool* queuePool, const int inputLength,
+ const bool hasAutoCorrectionCandidate, const int currentWordIndex,
+ const int inputWordStartPos, const int inputWordLength,
+ const int outputWordStartPos, unsigned short* outputWord, int *outputWordLength) {
+ unsigned short* tempOutputWord = 0;
+ int tempOutputWordLength = 0;
+ int freq = getMostFrequentWordLike(
+ inputWordStartPos, inputWordLength, proximityInfo, mWord);
+ if (freq > 0) {
+ tempOutputWordLength = inputWordLength;
+ tempOutputWord = mWord;
} else if (!hasAutoCorrectionCandidate) {
- WordsPriorityQueue* firstWordQueue = queuePool->getSubQueue1(firstInputWordLength);
- if (!firstWordQueue || firstWordQueue->size() < 1) {
- return;
- }
- int score = 0;
- const double ns = firstWordQueue->getHighestNormalizedScore(
- proximityInfo->getPrimaryInputWord(), firstInputWordLength,
- &firstOutputWord, &score, &firstOutputWordLength);
- if (DEBUG_DICT) {
- AKLOGI("NS1 = %f, Score = %d", ns, score);
- }
- // Two words correction won't be done if the score of the first word doesn't exceed the
- // threshold.
- if (ns < TWO_WORDS_CORRECTION_WITH_OTHER_ERROR_THRESHOLD
- || firstOutputWordLength < SUB_QUEUE_MIN_WORD_LENGTH) {
- return;
- }
- firstFreq = score >> (firstOutputWordLength
- + TWO_WORDS_PLUS_OTHER_ERROR_CORRECTION_DEMOTION_DIVIDER);
- }
-
- if (DEBUG_DICT) {
- AKLOGI("First freq: %d", firstFreq);
- }
-
- if (firstFreq <= 0 || firstOutputWordLength <= 0 || MAX_WORD_LENGTH <= firstOutputWordLength) {
- return;
- }
-
- // Allocating fixed length array on stack
- unsigned short outputWord[MAX_WORD_LENGTH];
- int outputWordLength = 0;
-
- for (int i = 0; i < firstOutputWordLength; ++i) {
- outputWord[i] = firstOutputWord[i];
- }
-
- outputWord[firstOutputWordLength] = SPACE;
- outputWordLength = firstOutputWordLength + 1;
-
- // Second word
- const int secondInputWordLength = isSpaceProximity
- ? (inputLength - spaceProximityPos - 1)
- : (inputLength - missingSpacePos);
- const int secondInputWordStartPos =
- isSpaceProximity ? (spaceProximityPos + 1) : missingSpacePos;
- int secondFreq = getMostFrequentWordLike(
- secondInputWordStartPos, secondInputWordLength, proximityInfo, mWord);
- unsigned short* secondOutputWord = 0;
- int secondOutputWordLength = 0;
-
- if (secondFreq > 0) {
- secondOutputWordLength = secondInputWordLength;
- secondOutputWord = mWord;
- } else if (!hasAutoCorrectionCandidate) {
- const int offset = secondInputWordStartPos;
- initSuggestions(proximityInfo, &xcoordinates[offset], &ycoordinates[offset],
- codes + offset * MAX_PROXIMITY_CHARS, secondInputWordLength, correction);
- queuePool->clearSubQueue2();
- getSuggestionCandidates(useFullEditDistance, secondInputWordLength, correction,
- queuePool, false, MAX_ERRORS_FOR_TWO_WORDS, SECOND_WORD_INDEX);
- if (DEBUG_DICT) {
- AKLOGI("Dump second word candidates %d", secondInputWordLength);
- for (int i = 0; i < SUB_QUEUE_MAX_COUNT; ++i) {
- queuePool->getSubQueue2(i)->dumpTopWord();
+ if (inputWordStartPos > 0) {
+ const int offset = inputWordStartPos;
+ initSuggestions(proximityInfo, &xcoordinates[offset], &ycoordinates[offset],
+ codes + offset * MAX_PROXIMITY_CHARS, inputWordLength, correction);
+ queuePool->clearSubQueue(currentWordIndex);
+ getSuggestionCandidates(useFullEditDistance, inputWordLength, correction,
+ queuePool, false, MAX_ERRORS_FOR_TWO_WORDS, currentWordIndex);
+ if (DEBUG_DICT) {
+ if (currentWordIndex <= SUB_QUEUE_MAX_WORD_INDEX) {
+ AKLOGI("Dump word candidates(%d) %d", currentWordIndex, inputWordLength);
+ for (int i = 0; i < SUB_QUEUE_MAX_COUNT; ++i) {
+ queuePool->getSubQueue(currentWordIndex, i)->dumpTopWord();
+ }
+ }
}
}
- WordsPriorityQueue* secondWordQueue = queuePool->getSubQueue2(secondInputWordLength);
- if (!secondWordQueue || secondWordQueue->size() < 1) {
- return;
+ WordsPriorityQueue* queue = queuePool->getSubQueue(currentWordIndex, inputWordLength);
+ if (!queue || queue->size() < 1) {
+ return 0;
}
int score = 0;
- const double ns = secondWordQueue->getHighestNormalizedScore(
- proximityInfo->getPrimaryInputWord(), secondInputWordLength,
- &secondOutputWord, &score, &secondOutputWordLength);
+ const double ns = queue->getHighestNormalizedScore(
+ proximityInfo->getPrimaryInputWord(), inputWordLength,
+ &tempOutputWord, &score, &tempOutputWordLength);
if (DEBUG_DICT) {
- AKLOGI("NS2 = %f, Score = %d", ns, score);
+ AKLOGI("NS(%d) = %f, Score = %d", currentWordIndex, ns, score);
}
// Two words correction won't be done if the score of the first word doesn't exceed the
// threshold.
if (ns < TWO_WORDS_CORRECTION_WITH_OTHER_ERROR_THRESHOLD
- || secondOutputWordLength < SUB_QUEUE_MIN_WORD_LENGTH) {
- return;
+ || tempOutputWordLength < SUB_QUEUE_MIN_WORD_LENGTH) {
+ return 0;
}
- secondFreq = score >> (secondOutputWordLength
+ freq = score >> (tempOutputWordLength
+ TWO_WORDS_PLUS_OTHER_ERROR_CORRECTION_DEMOTION_DIVIDER);
}
-
if (DEBUG_DICT) {
- DUMP_WORD(secondOutputWord, secondOutputWordLength);
- AKLOGI("Second freq: %d", secondFreq);
+ AKLOGI("Freq(%d): %d", currentWordIndex, freq);
}
-
- if (secondFreq <= 0 || secondOutputWordLength <= 0
- || MAX_WORD_LENGTH <= (firstOutputWordLength + 1 + secondOutputWordLength)) {
- return;
+ if (freq <= 0 || tempOutputWordLength <= 0
+ || MAX_WORD_LENGTH <= (outputWordStartPos + tempOutputWordLength)) {
+ return 0;
}
-
- for (int i = 0; i < secondOutputWordLength; ++i) {
- outputWord[firstOutputWordLength + 1 + i] = secondOutputWord[i];
+ for (int i = 0; i < tempOutputWordLength; ++i) {
+ outputWord[outputWordStartPos + i] = tempOutputWord[i];
}
-
- outputWordLength += secondOutputWordLength;
-
- // TODO: Remove initSuggestions and correction->setCorrectionParams
- initSuggestions(proximityInfo, xcoordinates, ycoordinates, codes, inputLength, correction);
-
- correction->setCorrectionParams(-1 /* skipPos */, -1 /* excessivePos */,
- -1 /* transposedPos */, spaceProximityPos, missingSpacePos,
- useFullEditDistance, false /* doAutoCompletion */, MAX_ERRORS_FOR_TWO_WORDS);
- const int pairFreq = correction->getFreqForSplitTwoWords(firstFreq, secondFreq, outputWord);
- if (DEBUG_DICT) {
- AKLOGI("Split two words: %d, %d, %d, %d", firstFreq, secondFreq, pairFreq, inputLength);
+ if ((inputWordStartPos + inputWordLength) < inputLength) {
+ if (outputWordStartPos + tempOutputWordLength >= MAX_WORD_LENGTH) {
+ return 0;
+ }
+ outputWord[outputWordStartPos + tempOutputWordLength] = SPACE;
+ ++tempOutputWordLength;
}
- addWord(outputWord, outputWordLength, pairFreq, masterQueue);
- return;
+ *outputWordLength = outputWordStartPos + tempOutputWordLength;
+ return freq;
}
-void UnigramDictionary::getSplitTwoWordsSuggestionsOld(ProximityInfo *proximityInfo,
+void UnigramDictionary::getSplitTwoWordsSuggestions(ProximityInfo *proximityInfo,
const int *xcoordinates, const int *ycoordinates, const int *codes,
const bool useFullEditDistance, const int inputLength, const int missingSpacePos,
- const int spaceProximityPos, Correction *correction, WordsPriorityQueuePool* queuePool) {
- WordsPriorityQueue *masterQueue = queuePool->getMasterQueue();
-
+ const int spaceProximityPos, Correction *correction, WordsPriorityQueuePool* queuePool,
+ const bool hasAutoCorrectionCandidate) {
+ if (inputLength >= MAX_WORD_LENGTH) return;
if (DEBUG_DICT) {
int inputCount = 0;
if (spaceProximityPos >= 0) ++inputCount;
if (missingSpacePos >= 0) ++inputCount;
assert(inputCount <= 1);
+ // MAX_PROXIMITY_CHARS_SIZE in ProximityInfo.java should be 16
+ assert(MAX_PROXIMITY_CHARS == 16);
}
- const bool isSpaceProximity = spaceProximityPos >= 0;
- const int firstWordStartPos = 0;
- const int secondWordStartPos = isSpaceProximity ? (spaceProximityPos + 1) : missingSpacePos;
- const int firstWordLength = isSpaceProximity ? spaceProximityPos : missingSpacePos;
- const int secondWordLength = isSpaceProximity
- ? (inputLength - spaceProximityPos - 1)
- : (inputLength - missingSpacePos);
-
- if (inputLength >= MAX_WORD_LENGTH) return;
- if (0 >= firstWordLength || 0 >= secondWordLength || firstWordStartPos >= secondWordStartPos
- || firstWordStartPos < 0 || secondWordStartPos + secondWordLength > inputLength)
- return;
-
- const int newWordLength = firstWordLength + secondWordLength + 1;
+ initSuggestions(proximityInfo, xcoordinates, ycoordinates, codes,
+ inputLength, correction);
- // Space proximity preparation
- //WordsPriorityQueue *subQueue = queuePool->getSubQueue1();
- //initSuggestions(proximityInfo, xcoordinates, ycoordinates, codes, firstWordLength, subQueue,
- //correction);
- //getSuggestionCandidates(useFullEditDistance, firstWordLength, correction, subQueue, false,
- //MAX_ERRORS_FOR_TWO_WORDS);
-
- // Allocating variable length array on stack
- unsigned short word[newWordLength];
- const int firstFreq = getMostFrequentWordLike(
- firstWordStartPos, firstWordLength, proximityInfo, mWord);
- if (DEBUG_DICT) {
- AKLOGI("First freq: %d", firstFreq);
- }
- if (firstFreq <= 0) return;
+ // Allocating fixed length array on stack
+ unsigned short outputWord[MAX_WORD_LENGTH];
+ int outputWordLength = 0;
- for (int i = 0; i < firstWordLength; ++i) {
- word[i] = mWord[i];
- }
+ WordsPriorityQueue *masterQueue = queuePool->getMasterQueue();
+ const bool isSpaceProximity = spaceProximityPos >= 0;
- const int secondFreq = getMostFrequentWordLike(
- secondWordStartPos, secondWordLength, proximityInfo, mWord);
- if (DEBUG_DICT) {
- AKLOGI("Second freq: %d", secondFreq);
+ // First word
+ int inputWordStartPos = 0;
+ int inputWordLength = isSpaceProximity ? spaceProximityPos : missingSpacePos;
+ const int firstFreq = getSubStringSuggestion(proximityInfo, xcoordinates, ycoordinates, codes,
+ useFullEditDistance, correction, queuePool, inputLength, hasAutoCorrectionCandidate,
+ FIRST_WORD_INDEX, inputWordStartPos, inputWordLength, 0, outputWord, &outputWordLength);
+ if (firstFreq <= 0) {
+ return;
}
- if (secondFreq <= 0) return;
- word[firstWordLength] = SPACE;
- for (int i = (firstWordLength + 1); i < newWordLength; ++i) {
- word[i] = mWord[i - firstWordLength - 1];
+ // Second word
+ inputWordStartPos = isSpaceProximity ? (spaceProximityPos + 1) : missingSpacePos;
+ inputWordLength = isSpaceProximity ? (inputLength - spaceProximityPos - 1)
+ : (inputLength - missingSpacePos);
+ const int secondFreq = getSubStringSuggestion(proximityInfo, xcoordinates, ycoordinates, codes,
+ useFullEditDistance, correction, queuePool, inputLength, hasAutoCorrectionCandidate,
+ SECOND_WORD_INDEX, inputWordStartPos, inputWordLength, outputWordLength, outputWord,
+ &outputWordLength);
+ if (secondFreq <= 0) {
+ return;
}
// TODO: Remove initSuggestions and correction->setCorrectionParams
@@ -623,11 +530,11 @@ void UnigramDictionary::getSplitTwoWordsSuggestionsOld(ProximityInfo *proximityI
correction->setCorrectionParams(-1 /* skipPos */, -1 /* excessivePos */,
-1 /* transposedPos */, spaceProximityPos, missingSpacePos,
useFullEditDistance, false /* doAutoCompletion */, MAX_ERRORS_FOR_TWO_WORDS);
- const int pairFreq = correction->getFreqForSplitTwoWords(firstFreq, secondFreq, word);
+ const int pairFreq = correction->getFreqForSplitTwoWords(firstFreq, secondFreq, outputWord);
if (DEBUG_DICT) {
AKLOGI("Split two words: %d, %d, %d, %d", firstFreq, secondFreq, pairFreq, inputLength);
}
- addWord(word, newWordLength, pairFreq, masterQueue);
+ addWord(outputWord, outputWordLength, pairFreq, masterQueue);
return;
}
diff --git a/native/src/unigram_dictionary.h b/native/src/unigram_dictionary.h
index 461e73586..0f50ccbd8 100644
--- a/native/src/unigram_dictionary.h
+++ b/native/src/unigram_dictionary.h
@@ -106,10 +106,6 @@ class UnigramDictionary {
const bool useFullEditDistance, const int inputLength, const int spaceProximityPos,
const int missingSpacePos, Correction *correction, WordsPriorityQueuePool* queuePool,
const bool hasAutoCorrectionCandidate);
- void getSplitTwoWordsSuggestionsOld(ProximityInfo *proximityInfo,
- const int *xcoordinates, const int *ycoordinates, const int *codes,
- const bool useFullEditDistance, const int inputLength, const int spaceProximityPos,
- const int missingSpacePos, Correction *correction, WordsPriorityQueuePool* queuePool);
void getMissingSpaceWords(ProximityInfo *proximityInfo, const int *xcoordinates,
const int *ycoordinates, const int *codes, const bool useFullEditDistance,
const int inputLength, const int missingSpacePos, Correction *correction,
@@ -131,6 +127,13 @@ class UnigramDictionary {
ProximityInfo *proximityInfo, unsigned short *word);
int getMostFrequentWordLikeInner(const uint16_t* const inWord, const int length,
short unsigned int *outWord);
+ int getSubStringSuggestion(
+ ProximityInfo *proximityInfo, const int *xcoordinates, const int *ycoordinates,
+ const int *codes, const bool useFullEditDistance, Correction *correction,
+ WordsPriorityQueuePool* queuePool, const int inputLength,
+ const bool hasAutoCorrectionCandidate, const int currentWordIndex,
+ const int inputWordStartPos, const int inputWordLength,
+ const int outputWordStartPos, unsigned short* outputWord, int *outputWordLength);
const uint8_t* const DICT_ROOT;
const int MAX_WORD_LENGTH;
diff --git a/native/src/words_priority_queue_pool.h b/native/src/words_priority_queue_pool.h
index 599b89711..a4aa8b6ca 100644
--- a/native/src/words_priority_queue_pool.h
+++ b/native/src/words_priority_queue_pool.h
@@ -43,25 +43,24 @@ class WordsPriorityQueuePool {
return mMasterQueue;
}
- // TODO: Come up with more generic pool
- WordsPriorityQueue* getSubQueue1(const int id) {
- if (id < 0 || id >= SUB_QUEUE_MAX_COUNT) {
- if (DEBUG_WORDS_PRIORITY_QUEUE) {
- assert(false);
- }
+ WordsPriorityQueue* getSubQueue(const int wordIndex, const int inputWordLength) {
+ if (wordIndex > SUB_QUEUE_MAX_WORD_INDEX) {
return 0;
}
- return mSubQueues1[id];
- }
-
- WordsPriorityQueue* getSubQueue2(const int id) {
- if (id < 0 || id >= SUB_QUEUE_MAX_COUNT) {
+ if (inputWordLength < 0 || inputWordLength >= SUB_QUEUE_MAX_COUNT) {
if (DEBUG_WORDS_PRIORITY_QUEUE) {
assert(false);
}
return 0;
}
- return mSubQueues2[id];
+ // TODO: Come up with more generic pool
+ if (wordIndex == 1) {
+ return mSubQueues1[inputWordLength];
+ } else if (wordIndex == 2) {
+ return mSubQueues2[inputWordLength];
+ } else {
+ return 0;
+ }
}
inline void clearAll() {
@@ -72,15 +71,13 @@ class WordsPriorityQueuePool {
}
}
- inline void clearSubQueue1() {
+ inline void clearSubQueue(const int wordIndex) {
for (int i = 0; i < SUB_QUEUE_MAX_COUNT; ++i) {
- mSubQueues1[i]->clear();
- }
- }
-
- inline void clearSubQueue2() {
- for (int i = 0; i < SUB_QUEUE_MAX_COUNT; ++i) {
- mSubQueues2[i]->clear();
+ if (wordIndex == 1) {
+ mSubQueues1[i]->clear();
+ } else if (wordIndex == 2) {
+ mSubQueues2[i]->clear();
+ }
}
}
diff --git a/tests/src/com/android/inputmethod/keyboard/internal/KeyboardStateMultiTouchTests.java b/tests/src/com/android/inputmethod/keyboard/internal/KeyboardStateMultiTouchTests.java
index bce63362a..d1887a1b9 100644
--- a/tests/src/com/android/inputmethod/keyboard/internal/KeyboardStateMultiTouchTests.java
+++ b/tests/src/com/android/inputmethod/keyboard/internal/KeyboardStateMultiTouchTests.java
@@ -40,9 +40,9 @@ public class KeyboardStateMultiTouchTests extends KeyboardStateTestsBase {
longPressShiftKey(ALPHABET_MANUAL_SHIFTED, ALPHABET_SHIFT_LOCKED);
// Press shift key and hold, enter into choring shift state.
- pressKey(CODE_SHIFT, ALPHABET_MANUAL_SHIFTED);
+ pressKey(CODE_SHIFT, ALPHABET_SHIFT_LOCK_SHIFTED);
// Press/release letter key.
- chordingPressAndReleaseKey('Z', ALPHABET_MANUAL_SHIFTED, ALPHABET_MANUAL_SHIFTED);
+ chordingPressAndReleaseKey('Z', ALPHABET_SHIFT_LOCK_SHIFTED, ALPHABET_SHIFT_LOCK_SHIFTED);
// Release shift key, switch back to alphabet shift locked.
releaseKey(CODE_SHIFT, ALPHABET_SHIFT_LOCKED);
diff --git a/tests/src/com/android/inputmethod/keyboard/internal/KeyboardStateSingleTouchTests.java b/tests/src/com/android/inputmethod/keyboard/internal/KeyboardStateSingleTouchTests.java
index 2204fca89..a1f28dce1 100644
--- a/tests/src/com/android/inputmethod/keyboard/internal/KeyboardStateSingleTouchTests.java
+++ b/tests/src/com/android/inputmethod/keyboard/internal/KeyboardStateSingleTouchTests.java
@@ -246,13 +246,13 @@ public class KeyboardStateSingleTouchTests extends KeyboardStateTestsBase {
pressAndReleaseKey(CODE_SPACE, ALPHABET_SHIFT_LOCKED, ALPHABET_SHIFT_LOCKED);
// Press/release shift key, back to alphabet.
- pressAndReleaseKey(CODE_SHIFT, ALPHABET_MANUAL_SHIFTED, ALPHABET_UNSHIFTED);
+ pressAndReleaseKey(CODE_SHIFT, ALPHABET_SHIFT_LOCK_SHIFTED, ALPHABET_UNSHIFTED);
// Long press shift key, enter alphabet shift locked.
longPressShiftKey(ALPHABET_MANUAL_SHIFTED, ALPHABET_SHIFT_LOCKED);
// Long press shift key, back to alphabet.
- longPressShiftKey(ALPHABET_MANUAL_SHIFTED, ALPHABET_UNSHIFTED);
+ longPressShiftKey(ALPHABET_SHIFT_LOCK_SHIFTED, ALPHABET_UNSHIFTED);
}
// Double tap shift key.
@@ -265,7 +265,7 @@ public class KeyboardStateSingleTouchTests extends KeyboardStateTestsBase {
secondTapShiftKey(ALPHABET_SHIFT_LOCKED);
// First shift key tap.
- pressAndReleaseKey(CODE_SHIFT, ALPHABET_MANUAL_SHIFTED, ALPHABET_UNSHIFTED);
+ pressAndReleaseKey(CODE_SHIFT, ALPHABET_SHIFT_LOCK_SHIFTED, ALPHABET_UNSHIFTED);
// Second shift key tap.
// Second tap is ignored in LatinKeyboardView.KeyTimerHandler.
}
@@ -289,7 +289,7 @@ public class KeyboardStateSingleTouchTests extends KeyboardStateTestsBase {
// Update shift state, remained in alphabet shift locked.
updateShiftState(ALPHABET_SHIFT_LOCKED);
// Long press shift key, back to alphabet.
- longPressShiftKey(ALPHABET_MANUAL_SHIFTED, ALPHABET_UNSHIFTED);
+ longPressShiftKey(ALPHABET_SHIFT_LOCK_SHIFTED, ALPHABET_UNSHIFTED);
// Press/release "?123" key, enter into symbols.
pressAndReleaseKey(CODE_SYMBOL, SYMBOLS_UNSHIFTED, SYMBOLS_UNSHIFTED);
@@ -320,7 +320,7 @@ public class KeyboardStateSingleTouchTests extends KeyboardStateTestsBase {
// Update shift state, remained in alphabet shift locked (not automatic shifted).
updateShiftState(ALPHABET_SHIFT_LOCKED);
// Long press shift key, back to alphabet.
- longPressShiftKey(ALPHABET_MANUAL_SHIFTED, ALPHABET_UNSHIFTED);
+ longPressShiftKey(ALPHABET_SHIFT_LOCK_SHIFTED, ALPHABET_UNSHIFTED);
// Load keyboard, should be in automatic shifted.
loadKeyboard(ALPHABET_AUTOMATIC_SHIFTED);
@@ -375,7 +375,7 @@ public class KeyboardStateSingleTouchTests extends KeyboardStateTestsBase {
// Alphabet shift locked -> "?123" key + letter -> alphabet shift locked.
// Press and slide from shift key, enter alphabet shifted.
- pressAndSlideFromKey(CODE_SHIFT, ALPHABET_MANUAL_SHIFTED, ALPHABET_SHIFT_LOCKED);
+ pressAndSlideFromKey(CODE_SHIFT, ALPHABET_SHIFT_LOCK_SHIFTED, ALPHABET_SHIFT_LOCKED);
// Enter/release letter key, switch back to shift locked.
pressAndReleaseKey('Z', ALPHABET_SHIFT_LOCKED, ALPHABET_SHIFT_LOCKED);
}
diff --git a/tests/src/com/android/inputmethod/keyboard/internal/MockKeyboardSwitcher.java b/tests/src/com/android/inputmethod/keyboard/internal/MockKeyboardSwitcher.java
index fce698a1e..f17b45235 100644
--- a/tests/src/com/android/inputmethod/keyboard/internal/MockKeyboardSwitcher.java
+++ b/tests/src/com/android/inputmethod/keyboard/internal/MockKeyboardSwitcher.java
@@ -40,8 +40,9 @@ public class MockKeyboardSwitcher implements KeyboardState.SwitchActions {
public static final int ALPHABET_MANUAL_SHIFTED = 1;
public static final int ALPHABET_AUTOMATIC_SHIFTED = 2;
public static final int ALPHABET_SHIFT_LOCKED = 3;
- public static final int SYMBOLS_UNSHIFTED = 4;
- public static final int SYMBOLS_SHIFTED = 5;
+ public static final int ALPHABET_SHIFT_LOCK_SHIFTED = 4;
+ public static final int SYMBOLS_UNSHIFTED = 5;
+ public static final int SYMBOLS_SHIFTED = 6;
}
private int mLayout = Constants.ALPHABET_UNSHIFTED;
@@ -62,6 +63,7 @@ public class MockKeyboardSwitcher implements KeyboardState.SwitchActions {
case Constants.ALPHABET_MANUAL_SHIFTED: return "ALPHABET_MANUAL_SHIFTED";
case Constants.ALPHABET_AUTOMATIC_SHIFTED: return "ALPHABET_AUTOMATIC_SHIFTED";
case Constants.ALPHABET_SHIFT_LOCKED: return "ALPHABET_SHIFT_LOCKED";
+ case Constants.ALPHABET_SHIFT_LOCK_SHIFTED: return "ALPHABET_SHIFT_LOCK_SHIFTED";
case Constants.SYMBOLS_UNSHIFTED: return "SYMBOLS_UNSHIFTED";
case Constants.SYMBOLS_SHIFTED: return "SYMBOLS_SHIFTED";
default: return "UNKNOWN<" + layoutId + ">";
@@ -93,6 +95,11 @@ public class MockKeyboardSwitcher implements KeyboardState.SwitchActions {
}
@Override
+ public void setAlphabetShiftLockShiftedKeyboard() {
+ mLayout = Constants.ALPHABET_SHIFT_LOCK_SHIFTED;
+ }
+
+ @Override
public void setSymbolsKeyboard() {
mLayout = Constants.SYMBOLS_UNSHIFTED;
}
diff --git a/tests/src/com/android/inputmethod/latin/InputLogicTests.java b/tests/src/com/android/inputmethod/latin/InputLogicTests.java
index 59ca22df4..6dfa80904 100644
--- a/tests/src/com/android/inputmethod/latin/InputLogicTests.java
+++ b/tests/src/com/android/inputmethod/latin/InputLogicTests.java
@@ -177,4 +177,45 @@ public class InputLogicTests extends ServiceTestCase<LatinIME> {
type(STRING_TO_TYPE);
assertEquals("simple auto-correct", EXPECTED_RESULT, mTextView.getText().toString());
}
+
+ public void testDoubleSpace() {
+ final String STRING_TO_TYPE = "this ";
+ final String EXPECTED_RESULT = "this. ";
+ type(STRING_TO_TYPE);
+ assertEquals("double space make a period", EXPECTED_RESULT, mTextView.getText().toString());
+ }
+
+ public void testCancelDoubleSpace() {
+ final String STRING_TO_TYPE = "tgis ";
+ final String EXPECTED_RESULT = "this ";
+ type(STRING_TO_TYPE);
+ type(Keyboard.CODE_DELETE);
+ assertEquals("double space make a period", EXPECTED_RESULT, mTextView.getText().toString());
+ }
+
+ public void testBackspaceAtStartAfterAutocorrect() {
+ final String STRING_TO_TYPE = "tgis ";
+ final String EXPECTED_RESULT = "this ";
+ final int NEW_CURSOR_POSITION = 0;
+ type(STRING_TO_TYPE);
+ mLatinIME.onUpdateSelection(0, 0, STRING_TO_TYPE.length(), STRING_TO_TYPE.length(), -1, -1);
+ mInputConnection.setSelection(NEW_CURSOR_POSITION, NEW_CURSOR_POSITION);
+ mLatinIME.onUpdateSelection(0, 0, NEW_CURSOR_POSITION, NEW_CURSOR_POSITION, -1, -1);
+ type(Keyboard.CODE_DELETE);
+ assertEquals("auto correct then move curor to start of line then backspace",
+ EXPECTED_RESULT, mTextView.getText().toString());
+ }
+
+ public void testAutoCorrectThenMoveCursorThenBackspace() {
+ final String STRING_TO_TYPE = "and tgis ";
+ final String EXPECTED_RESULT = "andthis ";
+ final int NEW_CURSOR_POSITION = STRING_TO_TYPE.indexOf('t');
+ type(STRING_TO_TYPE);
+ mLatinIME.onUpdateSelection(0, 0, STRING_TO_TYPE.length(), STRING_TO_TYPE.length(), -1, -1);
+ mInputConnection.setSelection(NEW_CURSOR_POSITION, NEW_CURSOR_POSITION);
+ mLatinIME.onUpdateSelection(0, 0, NEW_CURSOR_POSITION, NEW_CURSOR_POSITION, -1, -1);
+ type(Keyboard.CODE_DELETE);
+ assertEquals("auto correct then move curor then backspace",
+ EXPECTED_RESULT, mTextView.getText().toString());
+ }
}