diff options
38 files changed, 552 insertions, 220 deletions
diff --git a/CleanSpec.mk b/CleanSpec.mk index 505e41b49..c274a6c44 100644 --- a/CleanSpec.mk +++ b/CleanSpec.mk @@ -50,6 +50,7 @@ $(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/app/LatinIME.apk) $(call add-clean-step, rm -rf $(PRODUCT_OUT)/obj/SHARED_LIBRARIES/libjni_latinime_intermediates) $(call add-clean-step, rm -rf $(PRODUCT_OUT)/obj/SHARED_LIBRARIES/libjni_latinime_intermediates) $(call add-clean-step, rm -rf $(PRODUCT_OUT)/obj/SHARED_LIBRARIES/libjni_latinime_intermediates) +$(call add-clean-step, rm -rf $(PRODUCT_OUT)/obj/SHARED_LIBRARIES/libjni_latinime_intermediates) # ************************************************ # NEWER CLEAN STEPS MUST BE AT THE END OF THE LIST # ************************************************ diff --git a/java/AndroidManifest.xml b/java/AndroidManifest.xml index 2baae54fb..b0525326c 100644 --- a/java/AndroidManifest.xml +++ b/java/AndroidManifest.xml @@ -6,7 +6,6 @@ <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/> <uses-permission android:name="android.permission.READ_USER_DICTIONARY" /> <uses-permission android:name="android.permission.WRITE_USER_DICTIONARY" /> - <uses-permission android:name="android.permission.RECORD_AUDIO" /> <uses-permission android:name="android.permission.READ_CONTACTS" /> <application android:label="@string/english_ime_name" diff --git a/java/res/layout/vibration_settings_dialog.xml b/java/res/layout/vibration_settings_dialog.xml new file mode 100644 index 000000000..981ba9be0 --- /dev/null +++ b/java/res/layout/vibration_settings_dialog.xml @@ -0,0 +1,49 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- +/* +** +** Copyright 2011, The Android Open Source Project +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ +--> + +<LinearLayout + xmlns:android="http://schemas.android.com/apk/res/android" + android:orientation="vertical" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:layout_margin="10dip"> + <LinearLayout + android:orientation="horizontal" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:gravity="center_horizontal" + android:layout_margin="10dip"> + <TextView android:id="@+id/vibration_value" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:textSize="20dip"/> + <TextView + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:text="@string/settings_ms" + android:textSize="20dip"/> + </LinearLayout> + <SeekBar + android:id="@+id/vibration_settings" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:max="250" + android:layout_margin="10dip"/> +</LinearLayout> diff --git a/java/res/values/attrs.xml b/java/res/values/attrs.xml index 2a7632124..34ce527f1 100644 --- a/java/res/values/attrs.xml +++ b/java/res/values/attrs.xml @@ -34,7 +34,9 @@ <attr name="moreSuggestionsViewStyle" format="reference" /> <attr name="suggestionBackgroundStyle" format="reference" /> <attr name="suggestionPreviewBackgroundStyle" format="reference" /> - </declare-styleable> + <!-- Touch position correction --> + <attr name="touchPositionCorrectionData" format="reference" /> + </declare-styleable> <declare-styleable name="KeyboardView"> <!-- Image for the key. This image needs to be a StateListDrawable, with the following diff --git a/java/res/values/config.xml b/java/res/values/config.xml index 999247839..bcce58123 100644 --- a/java/res/values/config.xml +++ b/java/res/values/config.xml @@ -82,8 +82,10 @@ will be subject to auto-correction. --> <item>0</item> </string-array> - <!-- Threshold of the normalized score of the best suggestion for the spell checker to declare a word to be a typo --> - <string name="spellchecker_typo_threshold_value" translatable="false">0.11</string> + <!-- Threshold of the normalized score of the best suggestion for the spell checker to declare a word to be "likely" --> + <string name="spellchecker_likely_threshold_value" translatable="false">0.11</string> + <!-- Threshold of the normalized score of any dictionary lookup to be offered as a suggestion by the spell checker --> + <string name="spellchecker_suggestion_threshold_value" translatable="false">0.03</string> <!-- Screen metrics for logging. 0 = "mdpi phone screen" 1 = "hdpi phone screen" diff --git a/java/res/values/donottranslate.xml b/java/res/values/donottranslate.xml index 75e22ddd8..aefaec9ef 100644 --- a/java/res/values/donottranslate.xml +++ b/java/res/values/donottranslate.xml @@ -165,4 +165,5 @@ <!-- dictionary pack package name /settings activity (for shared prefs and settings) --> <string name="dictionary_pack_package_name">com.google.android.inputmethod.latin.dictionarypack</string> <string name="dictionary_pack_settings_activity">com.google.android.inputmethod.latin.dictionarypack.DictionarySettingsActivity</string> + <string name="settings_ms">ms</string> </resources> diff --git a/java/res/values/strings.xml b/java/res/values/strings.xml index 2f591fdf0..a1c1a9ffd 100644 --- a/java/res/values/strings.xml +++ b/java/res/values/strings.xml @@ -343,4 +343,6 @@ <!-- Title of an option for usability study mode --> <string name="prefs_usability_study_mode">Usability study mode</string> + <!-- Title of the settings for vibration duration --> + <string name="prefs_vibration_duration_settings">Vibration duration settings</string> </resources> diff --git a/java/res/values/themes-basic-highcontrast.xml b/java/res/values/themes-basic-highcontrast.xml index 91272356f..bc3c84706 100644 --- a/java/res/values/themes-basic-highcontrast.xml +++ b/java/res/values/themes-basic-highcontrast.xml @@ -28,5 +28,6 @@ <item name="moreSuggestionsViewStyle">@style/MoreSuggestionsViewStyle</item> <item name="suggestionBackgroundStyle">@style/SuggestionBackgroundStyle</item> <item name="suggestionPreviewBackgroundStyle">@style/SuggestionPreviewBackgroundStyle</item> + <item name="touchPositionCorrectionData">@array/touch_position_correction_data_empty</item> </style> </resources> diff --git a/java/res/values/themes-basic.xml b/java/res/values/themes-basic.xml index 6c0e16e7b..29cb9cc7d 100644 --- a/java/res/values/themes-basic.xml +++ b/java/res/values/themes-basic.xml @@ -28,5 +28,6 @@ <item name="moreSuggestionsViewStyle">@style/MoreSuggestionsViewStyle</item> <item name="suggestionBackgroundStyle">@style/SuggestionBackgroundStyle</item> <item name="suggestionPreviewBackgroundStyle">@style/SuggestionPreviewBackgroundStyle</item> + <item name="touchPositionCorrectionData">@array/touch_position_correction_data_empty</item> </style> </resources> diff --git a/java/res/values/themes-gingerbread.xml b/java/res/values/themes-gingerbread.xml index 43bff5082..c4a0f804a 100644 --- a/java/res/values/themes-gingerbread.xml +++ b/java/res/values/themes-gingerbread.xml @@ -28,5 +28,6 @@ <item name="moreSuggestionsViewStyle">@style/MoreSuggestionsViewStyle</item> <item name="suggestionBackgroundStyle">@style/SuggestionBackgroundStyle</item> <item name="suggestionPreviewBackgroundStyle">@style/SuggestionPreviewBackgroundStyle</item> + <item name="touchPositionCorrectionData">@array/touch_position_correction_data_gingerbread</item> </style> </resources> diff --git a/java/res/values/themes-ics.xml b/java/res/values/themes-ics.xml index 1235d4e88..dd2b6a334 100644 --- a/java/res/values/themes-ics.xml +++ b/java/res/values/themes-ics.xml @@ -28,5 +28,6 @@ <item name="moreSuggestionsViewStyle">@style/MoreSuggestionsViewStyle.IceCreamSandwich</item> <item name="suggestionBackgroundStyle">@style/SuggestionBackgroundStyle.IceCreamSandwich</item> <item name="suggestionPreviewBackgroundStyle">@style/SuggestionPreviewBackgroundStyle.IceCreamSandwich</item> + <item name="touchPositionCorrectionData">@array/touch_position_correction_data_ice_cream_sandwich</item> </style> </resources> diff --git a/java/res/values/themes-stone-bold.xml b/java/res/values/themes-stone-bold.xml index 6e25f41d7..6e864bed0 100644 --- a/java/res/values/themes-stone-bold.xml +++ b/java/res/values/themes-stone-bold.xml @@ -28,5 +28,6 @@ <item name="moreSuggestionsViewStyle">@style/MoreSuggestionsViewStyle</item> <item name="suggestionBackgroundStyle">@style/SuggestionBackgroundStyle</item> <item name="suggestionPreviewBackgroundStyle">@style/SuggestionPreviewBackgroundStyle</item> + <item name="touchPositionCorrectionData">@array/touch_position_correction_data_empty</item> </style> </resources> diff --git a/java/res/values/themes-stone.xml b/java/res/values/themes-stone.xml index 3cbda810e..64c557035 100644 --- a/java/res/values/themes-stone.xml +++ b/java/res/values/themes-stone.xml @@ -28,5 +28,6 @@ <item name="moreSuggestionsViewStyle">@style/MoreSuggestionsViewStyle</item> <item name="suggestionBackgroundStyle">@style/SuggestionBackgroundStyle</item> <item name="suggestionPreviewBackgroundStyle">@style/SuggestionPreviewBackgroundStyle</item> + <item name="touchPositionCorrectionData">@array/touch_position_correction_data_empty</item> </style> </resources> diff --git a/java/res/values/touch-position-correction.xml b/java/res/values/touch-position-correction.xml new file mode 100644 index 000000000..0a0e4e545 --- /dev/null +++ b/java/res/values/touch-position-correction.xml @@ -0,0 +1,74 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- +/* +** +** Copyright 2011, The Android Open Source Project +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ +--> +<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <!-- + An entry of the touch_position_correction word should be: + 1. (float) (touch_center_x - key_center_x) / key_width + 2. (float) (touch_center_y - key_center_y) / key_height + 3. (float) sweet_spot_radius / (key_width^2 + key_height^2) + --> + + <string-array + name="touch_position_correction_data_empty" + translatable="false" + > + <!-- empty --> + </string-array> + + <string-array + name="touch_position_correction_data_gingerbread" + translatable="false" + > + <!-- First row --> + <item>0.0091285</item> + <item>0.1193203</item> + <item>0.1622607</item> + + <!-- Second row --> + <item>-0.0233128</item> + <item>0.1379798</item> + <item>0.1585229</item> + + <!-- Third row --> + <item>-0.0080185</item> + <item>0.1911477</item> + <item>0.1570948</item> + </string-array> + + <string-array + name="touch_position_correction_data_ice_cream_sandwich" + translatable="false" + > + <!-- First row --> + <item>0.0038756</item> + <item>-0.0005677</item> + <item>0.1577026</item> + + <!-- Second row --> + <item>-0.0236678</item> + <item>0.0381731</item> + <item>0.1529972</item> + + <!-- Third row --> + <item>-0.0086827</item> + <item>0.0880847</item> + <item>0.1522819</item> + </string-array> +</resources>
\ No newline at end of file diff --git a/java/res/xml/kbd_rows_number.xml b/java/res/xml/kbd_rows_number.xml index 21d0656d1..90ac5686b 100644 --- a/java/res/xml/kbd_rows_number.xml +++ b/java/res/xml/kbd_rows_number.xml @@ -36,6 +36,7 @@ latin:keyStyle="num2KeyStyle" /> <Key latin:keyStyle="num3KeyStyle" /> + <Spacer /> </Row> <Row> <Key @@ -44,6 +45,7 @@ latin:keyStyle="num5KeyStyle" /> <Key latin:keyStyle="num6KeyStyle" /> + <Spacer /> </Row> <Row> <Key diff --git a/java/res/xml/prefs.xml b/java/res/xml/prefs.xml index 24de95f10..312af2888 100644 --- a/java/res/xml/prefs.xml +++ b/java/res/xml/prefs.xml @@ -125,6 +125,9 @@ android:summary="@string/enable_span_insert_summary" android:persistent="true" android:defaultValue="true" /> + <PreferenceScreen + android:key="pref_vibration_duration_settings" + android:title="@string/prefs_vibration_duration_settings"/> <!-- TODO: evaluate results and revive this option. The code already supports it. --> <!-- <CheckBoxPreference --> diff --git a/java/src/com/android/inputmethod/keyboard/Keyboard.java b/java/src/com/android/inputmethod/keyboard/Keyboard.java index 045f0d7b2..9e4c1ea79 100644 --- a/java/src/com/android/inputmethod/keyboard/Keyboard.java +++ b/java/src/com/android/inputmethod/keyboard/Keyboard.java @@ -138,7 +138,8 @@ public class Keyboard { mProximityInfo = new ProximityInfo( params.GRID_WIDTH, params.GRID_HEIGHT, mOccupiedWidth, mOccupiedHeight, - mMostCommonKeyWidth, mKeys); + mMostCommonKeyWidth, mMostCommonKeyHeight, mKeys, params.mTouchPositionCorrectionXs, + params.mTouchPositionCorrectionYs, params.mTouchPositionCorrectionRadii); } public ProximityInfo getProximityInfo() { diff --git a/java/src/com/android/inputmethod/keyboard/ProximityInfo.java b/java/src/com/android/inputmethod/keyboard/ProximityInfo.java index 71b46d646..d35b1a939 100644 --- a/java/src/com/android/inputmethod/keyboard/ProximityInfo.java +++ b/java/src/com/android/inputmethod/keyboard/ProximityInfo.java @@ -16,6 +16,9 @@ package com.android.inputmethod.keyboard; +import android.graphics.Rect; + +import com.android.inputmethod.keyboard.Key; import com.android.inputmethod.latin.Utils; import com.android.inputmethod.latin.spellcheck.SpellCheckerProximityInfo; @@ -27,9 +30,9 @@ public class ProximityInfo { public static final int MAX_PROXIMITY_CHARS_SIZE = 16; /** Number of key widths from current touch point to search for nearest keys. */ private static float SEARCH_DISTANCE = 1.2f; - private static final int UNKNOWN_THEME = -1; private static final int[] EMPTY_INT_ARRAY = new int[0]; + private final int mKeyHeight; private final int mGridWidth; private final int mGridHeight; private final int mGridSize; @@ -40,8 +43,13 @@ public class ProximityInfo { private final int mKeyboardHeight; private final int[][] mGridNeighbors; - ProximityInfo( - int gridWidth, int gridHeight, int minWidth, int height, int keyWidth, List<Key> keys) { + private final float[] mTouchPositionCorrectionXs; + private final float[] mTouchPositionCorrectionYs; + private final float[] mTouchPositionCorrectionRadii; + + ProximityInfo(int gridWidth, int gridHeight, int minWidth, int height, int keyWidth, + int keyHeight, List<Key> keys, float[] touchPositionCorrectionXs, + float[] touchPositionCorrectionYs, float[] touchPositionCorrectionRadii) { mGridWidth = gridWidth; mGridHeight = gridHeight; mGridSize = mGridWidth * mGridHeight; @@ -49,6 +57,10 @@ public class ProximityInfo { mCellHeight = (height + mGridHeight - 1) / mGridHeight; mKeyboardMinWidth = minWidth; mKeyboardHeight = height; + mKeyHeight = keyHeight; + mTouchPositionCorrectionXs = touchPositionCorrectionXs; + mTouchPositionCorrectionYs = touchPositionCorrectionYs; + mTouchPositionCorrectionRadii = touchPositionCorrectionRadii; mGridNeighbors = new int[mGridSize][]; if (minWidth == 0 || height == 0) { // No proximity required. Keyboard might be mini keyboard. @@ -58,7 +70,7 @@ public class ProximityInfo { } public static ProximityInfo createDummyProximityInfo() { - return new ProximityInfo(1, 1, 1, 1, 1, Collections.<Key>emptyList()); + return new ProximityInfo(1, 1, 1, 1, 1, 1, Collections.<Key>emptyList(), null, null, null); } public static ProximityInfo createSpellCheckerProximityInfo() { @@ -67,7 +79,7 @@ public class ProximityInfo { spellCheckerProximityInfo.setProximityInfoNative( SpellCheckerProximityInfo.ROW_SIZE, 480, 300, 10, 3, SpellCheckerProximityInfo.PROXIMITY, - 0, null, null, null, null, null, UNKNOWN_THEME); + 0, null, null, null, null, null, null, null, null); return spellCheckerProximityInfo; } @@ -78,7 +90,8 @@ public class ProximityInfo { private native int setProximityInfoNative(int maxProximityCharsSize, int displayWidth, int displayHeight, int gridWidth, int gridHeight, int[] proximityCharsArray, int keyCount, int[] keyXCoordinates, int[] keyYCoordinates, - int[] keyWidths, int[] keyHeights, int[] keyCharCodes, int themeId); + int[] keyWidths, int[] keyHeights, int[] keyCharCodes, + float[] sweetSpotCenterX, float[] sweetSpotCenterY, float[] sweetSpotRadii); private native void releaseProximityInfoNative(int nativeProximityInfo); private final void setProximityInfo(int[][] gridNeighborKeyIndexes, int keyboardWidth, @@ -93,12 +106,11 @@ public class ProximityInfo { } } final int keyCount = keys.size(); - int[] keyXCoordinates = new int[keyCount]; - int[] keyYCoordinates = new int[keyCount]; - int[] keyWidths = new int[keyCount]; - int[] keyHeights = new int[keyCount]; - int[] keyCharCodes = new int[keyCount]; - final int themeId = 5; // TODO: Use real theme id. + final int[] keyXCoordinates = new int[keyCount]; + final int[] keyYCoordinates = new int[keyCount]; + final int[] keyWidths = new int[keyCount]; + final int[] keyHeights = new int[keyCount]; + final int[] keyCharCodes = new int[keyCount]; for (int i = 0; i < keyCount; ++i) { final Key key = keys.get(i); keyXCoordinates[i] = key.mX; @@ -107,10 +119,51 @@ public class ProximityInfo { keyHeights[i] = key.mHeight; keyCharCodes[i] = key.mCode; } + + final boolean hasTouchPositionCorrectionData = + mTouchPositionCorrectionXs != null + && mTouchPositionCorrectionYs != null + && mTouchPositionCorrectionRadii != null + && mTouchPositionCorrectionXs.length > 0 + && mTouchPositionCorrectionYs.length > 0 + && mTouchPositionCorrectionRadii.length > 0; + final float[] sweetSpotCenterXs = + hasTouchPositionCorrectionData ? new float[keyCount] : null; + final float[] sweetSpotCenterYs = + hasTouchPositionCorrectionData ? new float[keyCount] : null; + final float[] sweetSpotRadii = + hasTouchPositionCorrectionData ? new float[keyCount] : null; + if (hasTouchPositionCorrectionData) { + calculateSweetSpot(keys, sweetSpotCenterXs, sweetSpotCenterYs, sweetSpotRadii); + } + mNativeProximityInfo = setProximityInfoNative(MAX_PROXIMITY_CHARS_SIZE, keyboardWidth, keyboardHeight, mGridWidth, mGridHeight, proximityCharsArray, keyCount, keyXCoordinates, keyYCoordinates, keyWidths, keyHeights, keyCharCodes, - themeId); + sweetSpotCenterXs, sweetSpotCenterYs, sweetSpotRadii); + } + + private void calculateSweetSpot(List<Key> keys, float[] sweetSpotCenterXs, + float[] sweetSpotCenterYs, float[] sweetSpotRadii) { + final int keyCount = keys.size(); + for (int i = 0; i < keyCount; ++i) { + final Key key = keys.get(i); + final Rect hitBox = key.mHitBox; + final int row = hitBox.top / mKeyHeight; + if (row < mTouchPositionCorrectionRadii.length) { + final float hitBoxCenterX = (hitBox.left + hitBox.right) * 0.5f; + final float hitBoxCenterY = (hitBox.top + hitBox.bottom) * 0.5f; + final float hitBoxWidth = hitBox.right - hitBox.left; + final float hitBoxHeight = hitBox.bottom - hitBox.top; + final float x = mTouchPositionCorrectionXs[row]; + final float y = mTouchPositionCorrectionYs[row]; + final float radius = mTouchPositionCorrectionRadii[row]; + sweetSpotCenterXs[i] = hitBoxCenterX + x * hitBoxWidth; + sweetSpotCenterYs[i] = hitBoxCenterY + y * hitBoxHeight; + sweetSpotRadii[i] = radius + * (float)Math.sqrt(hitBoxWidth * hitBoxWidth + hitBoxHeight * hitBoxHeight); + } + } } public int getNativeProximityInfo() { diff --git a/java/src/com/android/inputmethod/keyboard/internal/KeyboardBuilder.java b/java/src/com/android/inputmethod/keyboard/internal/KeyboardBuilder.java index 187a1adcb..46836da67 100644 --- a/java/src/com/android/inputmethod/keyboard/internal/KeyboardBuilder.java +++ b/java/src/com/android/inputmethod/keyboard/internal/KeyboardBuilder.java @@ -30,6 +30,7 @@ import com.android.inputmethod.compat.EditorInfoCompatUtils; import com.android.inputmethod.keyboard.Key; import com.android.inputmethod.keyboard.Keyboard; import com.android.inputmethod.keyboard.KeyboardId; +import com.android.inputmethod.latin.LatinImeLogger; import com.android.inputmethod.latin.R; import org.xmlpull.v1.XmlPullParser; @@ -126,6 +127,8 @@ public class KeyboardBuilder<KP extends KeyboardParams> { private static final int DEFAULT_KEYBOARD_COLUMNS = 10; private static final int DEFAULT_KEYBOARD_ROWS = 4; + private static final int TOUCH_POSITION_CORRECTION_RECORD_SIZE = 3; + protected final KP mParams; protected final Context mContext; protected final Resources mResources; @@ -248,10 +251,62 @@ public class KeyboardBuilder<KP extends KeyboardParams> { mParams.mThemeId = a.getInt(R.styleable.KeyboardTheme_themeId, 0); a.recycle(); + if (!setTouchPositionCorrectionData(context)) { + // In the regression test, setTouchPositionCorrectionData() fails + mParams.mTouchPositionCorrectionXs = null; + mParams.mTouchPositionCorrectionYs = null; + mParams.mTouchPositionCorrectionRadii = null; + } + mParams.GRID_WIDTH = res.getInteger(R.integer.config_keyboard_grid_width); mParams.GRID_HEIGHT = res.getInteger(R.integer.config_keyboard_grid_height); } + private boolean setTouchPositionCorrectionData(Context context) { + final TypedArray a = context.obtainStyledAttributes(R.styleable.KeyboardTheme); + final int resourceId = a.getResourceId( + R.styleable.KeyboardTheme_touchPositionCorrectionData, 0); + if (resourceId == 0) { + // In the regression test, we cannot use theme resources + // TODO: Fix this + return false; + } + final String[] data = context.getResources().getStringArray(resourceId); + a.recycle(); + final int dataLength = data.length; + if (dataLength % TOUCH_POSITION_CORRECTION_RECORD_SIZE != 0) { + if (LatinImeLogger.sDBG) { + throw new RuntimeException("the size of touch position correction data is invalid"); + } + return false; + } + final int length = dataLength / TOUCH_POSITION_CORRECTION_RECORD_SIZE; + mParams.mTouchPositionCorrectionXs = new float[length]; + mParams.mTouchPositionCorrectionYs = new float[length]; + mParams.mTouchPositionCorrectionRadii = new float[length]; + try { + for (int i = 0; i < dataLength; ++i) { + final int type = i % TOUCH_POSITION_CORRECTION_RECORD_SIZE; + final int index = i / TOUCH_POSITION_CORRECTION_RECORD_SIZE; + final float value = Float.parseFloat(data[i]); + if (type == 0) { + mParams.mTouchPositionCorrectionXs[index] = value; + } else if (type == 1) { + mParams.mTouchPositionCorrectionYs[index] = value; + } else { + mParams.mTouchPositionCorrectionRadii[index] = value; + } + } + } catch (NumberFormatException e) { + if (LatinImeLogger.sDBG) { + throw new RuntimeException( + "the number format for touch position correction data is invalid"); + } + return false; + } + return true; + } + public KeyboardBuilder<KP> load(KeyboardId id) { mParams.mId = id; try { diff --git a/java/src/com/android/inputmethod/keyboard/internal/KeyboardParams.java b/java/src/com/android/inputmethod/keyboard/internal/KeyboardParams.java index 97f58fad2..d1aea72a5 100644 --- a/java/src/com/android/inputmethod/keyboard/internal/KeyboardParams.java +++ b/java/src/com/android/inputmethod/keyboard/internal/KeyboardParams.java @@ -68,6 +68,10 @@ public class KeyboardParams { public int mMostCommonKeyHeight = 0; public int mMostCommonKeyWidth = 0; + public float[] mTouchPositionCorrectionXs; + public float[] mTouchPositionCorrectionYs; + public float[] mTouchPositionCorrectionRadii; + protected void clearKeys() { mKeys.clear(); mShiftKeys.clear(); diff --git a/java/src/com/android/inputmethod/latin/BinaryDictionary.java b/java/src/com/android/inputmethod/latin/BinaryDictionary.java index 6a6a0a4ee..ab9edb110 100644 --- a/java/src/com/android/inputmethod/latin/BinaryDictionary.java +++ b/java/src/com/android/inputmethod/latin/BinaryDictionary.java @@ -61,6 +61,8 @@ public class BinaryDictionary extends Dictionary { public static final Flag FLAG_REQUIRES_GERMAN_UMLAUT_PROCESSING = new Flag(R.bool.config_require_umlaut_processing, 0x1); + public static final Flag FLAG_USE_FULL_EDIT_DISTANCE = new Flag(0x2); + // Can create a new flag from extravalue : // public static final Flag FLAG_MYFLAG = // new Flag("my_flag", 0x02); diff --git a/java/src/com/android/inputmethod/latin/Flag.java b/java/src/com/android/inputmethod/latin/Flag.java index 3cb8f7e17..4ba6c80f5 100644 --- a/java/src/com/android/inputmethod/latin/Flag.java +++ b/java/src/com/android/inputmethod/latin/Flag.java @@ -25,8 +25,9 @@ public class Flag { public final int mMask; public final int mSource; - static private final int SOURCE_CONFIG = 1; - static private final int SOURCE_EXTRAVALUE = 2; + private static final int SOURCE_CONFIG = 1; + private static final int SOURCE_EXTRAVALUE = 2; + private static final int SOURCE_PARAM = 3; public Flag(int resourceId, int mask) { mName = null; @@ -42,6 +43,13 @@ public class Flag { mMask = mask; } + public Flag(int mask) { + mName = null; + mResource = 0; + mSource = SOURCE_PARAM; + mMask = mask; + } + // If context/switcher are null, set all related flags in flagArray to on. public static int initFlags(Flag[] flagArray, Context context, SubtypeSwitcher switcher) { int flags = 0; @@ -57,6 +65,9 @@ public class Flag { switcher.currentSubtypeContainsExtraValueKey(entry.mName)) flags |= entry.mMask; break; + case Flag.SOURCE_PARAM: + flags |= entry.mMask; + break; } } return flags; diff --git a/java/src/com/android/inputmethod/latin/LatinIME.java b/java/src/com/android/inputmethod/latin/LatinIME.java index 32649d5a1..cf1cb8f25 100644 --- a/java/src/com/android/inputmethod/latin/LatinIME.java +++ b/java/src/com/android/inputmethod/latin/LatinIME.java @@ -1661,9 +1661,13 @@ public class LatinIME extends InputMethodServiceCompatWrapper implements Keyboar builder.setTypedWordValid(typedWordValid).setHasMinimalSuggestion( autoCorrectionAvailable); } else { - final SuggestedWords previousSuggestions = mSuggestionsView.getSuggestions(); - if (previousSuggestions == mSettingsValues.mSuggestPuncList) - return; + SuggestedWords previousSuggestions = mSuggestionsView.getSuggestions(); + if (previousSuggestions == mSettingsValues.mSuggestPuncList) { + if (builder.size() == 0) { + return; + } + previousSuggestions = SuggestedWords.EMPTY; + } builder.addTypedWordAndPreviousSuggestions(typedWord, previousSuggestions); } } @@ -2099,16 +2103,7 @@ public class LatinIME extends InputMethodServiceCompatWrapper implements Keyboar } private void updateKeypressVibrationDuration() { - final String[] durationPerHardwareList = mResources.getStringArray( - R.array.keypress_vibration_durations); - final String hardwarePrefix = Build.HARDWARE + ","; - for (final String element : durationPerHardwareList) { - if (element.startsWith(hardwarePrefix)) { - mKeypressVibrationDuration = - Long.parseLong(element.substring(element.lastIndexOf(',') + 1)); - break; - } - } + mKeypressVibrationDuration = Utils.getCurrentVibrationDuration(mPrefs, mResources); } private void playKeyClick(int primaryCode) { diff --git a/java/src/com/android/inputmethod/latin/MoreSuggestionsView.java b/java/src/com/android/inputmethod/latin/MoreSuggestionsView.java index 5a2eb1632..51f6c040d 100644 --- a/java/src/com/android/inputmethod/latin/MoreSuggestionsView.java +++ b/java/src/com/android/inputmethod/latin/MoreSuggestionsView.java @@ -163,8 +163,6 @@ public class MoreSuggestionsView extends KeyboardView implements MoreKeysPanel { - (container.getMeasuredHeight() - container.getPaddingBottom()) + parentView.getPaddingTop() + mCoordinates[1]; - window.setInputMethodMode(PopupWindow.INPUT_METHOD_NOT_NEEDED); - window.setOutsideTouchable(true); window.setContentView(container); window.setWidth(container.getMeasuredWidth()); window.setHeight(container.getMeasuredHeight()); @@ -221,22 +219,6 @@ public class MoreSuggestionsView extends KeyboardView implements MoreKeysPanel { }; @Override - public boolean dispatchTouchEvent(MotionEvent me) { - final int index = me.getActionIndex(); - final int id = me.getPointerId(index); - final PointerTracker tracker = PointerTracker.getPointerTracker(id, this); - final int x = (int)me.getX(index); - final int y = (int)me.getY(index); - final boolean inside = (x >= 0 && x < getWidth() && y >= 0 && y < getHeight()); - if (inside || tracker.isInSlidingKeyInput()) { - return super.dispatchTouchEvent(me); - } else { - dismissMoreKeysPanel(); - return true; - } - } - - @Override public boolean onTouchEvent(MotionEvent me) { final int action = me.getAction(); final long eventTime = me.getEventTime(); diff --git a/java/src/com/android/inputmethod/latin/Settings.java b/java/src/com/android/inputmethod/latin/Settings.java index d706cd0a4..a2e896619 100644 --- a/java/src/com/android/inputmethod/latin/Settings.java +++ b/java/src/com/android/inputmethod/latin/Settings.java @@ -36,7 +36,10 @@ import android.preference.PreferenceScreen; import android.text.TextUtils; import android.text.method.LinkMovementMethod; import android.util.Log; +import android.view.View; import android.view.inputmethod.EditorInfo; +import android.widget.SeekBar; +import android.widget.SeekBar.OnSeekBarChangeListener; import android.widget.TextView; import com.android.inputmethod.compat.CompatUtils; @@ -89,6 +92,9 @@ public class Settings extends InputMethodSettingsActivity public static final String PREF_USABILITY_STUDY_MODE = "usability_study_mode"; + public static final String PREF_VIBRATION_DURATION_SETTINGS = + "pref_vibration_duration_settings"; + // Dialog ids private static final int VOICE_INPUT_CONFIRM_DIALOG = 0; @@ -335,6 +341,7 @@ public class Settings extends InputMethodSettingsActivity private boolean mVoiceOn; private AlertDialog mDialog; + private TextView mVibrationSettingsTextView; private boolean mOkClicked = false; private String mVoiceModeOff; @@ -475,6 +482,19 @@ public class Settings extends InputMethodSettingsActivity miscSettings.removePreference(pref); } } + + final PreferenceScreen vibrationSettingsPref = + (PreferenceScreen) findPreference(PREF_VIBRATION_DURATION_SETTINGS); + if (vibrationSettingsPref != null) { + vibrationSettingsPref.setOnPreferenceClickListener( + new OnPreferenceClickListener() { + @Override + public boolean onPreferenceClick(Preference arg0) { + showVibrationSettingsDialog(); + return true; + } + }); + } } @SuppressWarnings("unused") @@ -621,4 +641,51 @@ public class Settings extends InputMethodSettingsActivity mVoicePreference.setValue(mVoiceModeOff); } } -} + + private void showVibrationSettingsDialog() { + final SharedPreferences sp = getPreferenceManager().getSharedPreferences(); + final Activity context = getActivityInternal(); + final AlertDialog.Builder builder = new AlertDialog.Builder(context); + builder.setTitle(R.string.prefs_vibration_duration_settings); + builder.setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() { + @Override + public void onClick(DialogInterface dialog, int whichButton) { + final int ms = Integer.valueOf(mVibrationSettingsTextView.getText().toString()); + sp.edit().putInt(Settings.PREF_VIBRATION_DURATION_SETTINGS, ms).apply(); + } + }); + builder.setNegativeButton(android.R.string.cancel, new DialogInterface.OnClickListener() { + @Override + public void onClick(DialogInterface dialog, int whichButton) { + dialog.dismiss(); + } + }); + final View v = context.getLayoutInflater().inflate( + R.layout.vibration_settings_dialog, null); + final int currentMs = Utils.getCurrentVibrationDuration( + getPreferenceManager().getSharedPreferences(), getResources()); + mVibrationSettingsTextView = (TextView)v.findViewById(R.id.vibration_value); + final SeekBar sb = (SeekBar)v.findViewById(R.id.vibration_settings); + sb.setOnSeekBarChangeListener(new OnSeekBarChangeListener() { + @Override + public void onProgressChanged(SeekBar arg0, int arg1, boolean arg2) { + final int tempMs = arg1; + mVibrationSettingsTextView.setText(String.valueOf(tempMs)); + } + + @Override + public void onStartTrackingTouch(SeekBar arg0) { + } + + @Override + public void onStopTrackingTouch(SeekBar arg0) { + final int tempMs = arg0.getProgress(); + VibratorCompatWrapper.getInstance(context).vibrate(tempMs); + } + }); + sb.setProgress(currentMs); + mVibrationSettingsTextView.setText(String.valueOf(currentMs)); + builder.setView(v); + builder.create().show(); + } +}
\ No newline at end of file diff --git a/java/src/com/android/inputmethod/latin/SuggestionsView.java b/java/src/com/android/inputmethod/latin/SuggestionsView.java index 9d0e42a18..fe54f4ae1 100644 --- a/java/src/com/android/inputmethod/latin/SuggestionsView.java +++ b/java/src/com/android/inputmethod/latin/SuggestionsView.java @@ -27,6 +27,7 @@ import android.graphics.Paint.Align; import android.graphics.Rect; import android.graphics.Typeface; import android.graphics.drawable.BitmapDrawable; +import android.graphics.drawable.ColorDrawable; import android.graphics.drawable.Drawable; import android.os.Message; import android.os.SystemClock; @@ -506,10 +507,22 @@ public class SuggestionsView extends RelativeLayout implements OnClickListener, mMoreSuggestionsView = (MoreSuggestionsView)mMoreSuggestionsContainer .findViewById(R.id.more_suggestions_view); mMoreSuggestionsBuilder = new MoreSuggestions.Builder(mMoreSuggestionsView); - mMoreSuggestionsWindow = new PopupWindow(context); - mMoreSuggestionsWindow.setWindowLayoutMode( + + final PopupWindow moreWindow = new PopupWindow(context); + moreWindow.setWindowLayoutMode( ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT); - mMoreSuggestionsWindow.setBackgroundDrawable(null); + moreWindow.setBackgroundDrawable(new ColorDrawable(android.R.color.transparent)); + moreWindow.setInputMethodMode(PopupWindow.INPUT_METHOD_NOT_NEEDED); + moreWindow.setFocusable(true); + moreWindow.setOutsideTouchable(true); + moreWindow.setOnDismissListener(new PopupWindow.OnDismissListener() { + @Override + public void onDismiss() { + mKeyboardView.dimEntireKeyboard(false); + } + }); + mMoreSuggestionsWindow = moreWindow; + final Resources res = context.getResources(); mMoreSuggestionsModalTolerance = res.getDimensionPixelOffset( R.dimen.more_suggestions_modal_tolerance); @@ -517,21 +530,6 @@ public class SuggestionsView extends RelativeLayout implements OnClickListener, context, mMoreSuggestionsSlidingListener); } - private final View.OnTouchListener mMoreSuggestionsCanceller = new View.OnTouchListener() { - @Override - public boolean onTouch(View view, MotionEvent me) { - if (!mMoreSuggestionsWindow.isShowing()) return false; - - switch (me.getAction()) { - case MotionEvent.ACTION_UP: - case MotionEvent.ACTION_POINTER_UP: - return mMoreSuggestionsView.dismissMoreKeysPanel(); - default: - return true; - } - } - }; - /** * A connection back to the input method. * @param listener @@ -726,8 +724,6 @@ public class SuggestionsView extends RelativeLayout implements OnClickListener, private boolean dismissMoreSuggestions() { if (mMoreSuggestionsWindow.isShowing()) { mMoreSuggestionsWindow.dismiss(); - mKeyboardView.dimEntireKeyboard(false); - mKeyboardView.setOnTouchListener(null); return true; } return false; @@ -767,7 +763,6 @@ public class SuggestionsView extends RelativeLayout implements OnClickListener, mOriginX = mLastX; mOriginY = mLastY; mKeyboardView.dimEntireKeyboard(true); - mKeyboardView.setOnTouchListener(mMoreSuggestionsCanceller); for (int i = 0; i < params.mSuggestionsCountInStrip; i++) { mWords.get(i).setPressed(false); } diff --git a/java/src/com/android/inputmethod/latin/Utils.java b/java/src/com/android/inputmethod/latin/Utils.java index c35273edd..771276567 100644 --- a/java/src/com/android/inputmethod/latin/Utils.java +++ b/java/src/com/android/inputmethod/latin/Utils.java @@ -17,9 +17,11 @@ package com.android.inputmethod.latin; import android.content.Context; +import android.content.SharedPreferences; import android.content.res.Resources; import android.inputmethodservice.InputMethodService; import android.os.AsyncTask; +import android.os.Build; import android.os.Handler; import android.os.HandlerThread; import android.os.Process; @@ -772,4 +774,20 @@ public class Utils { // - It also does not work with unicode surrogate code points. return s.toUpperCase(locale).charAt(0) + s.substring(1); } + + public static int getCurrentVibrationDuration(SharedPreferences sp, Resources res) { + final int ms = sp.getInt(Settings.PREF_VIBRATION_DURATION_SETTINGS, -1); + if (ms >= 0) { + return ms; + } + final String[] durationPerHardwareList = res.getStringArray( + R.array.keypress_vibration_durations); + final String hardwarePrefix = Build.HARDWARE + ","; + for (final String element : durationPerHardwareList) { + if (element.startsWith(hardwarePrefix)) { + return (int)Long.parseLong(element.substring(element.lastIndexOf(',') + 1)); + } + } + return -1; + } } diff --git a/java/src/com/android/inputmethod/latin/spellcheck/AndroidSpellCheckerService.java b/java/src/com/android/inputmethod/latin/spellcheck/AndroidSpellCheckerService.java index 2546df0a2..1d5986ef9 100644 --- a/java/src/com/android/inputmethod/latin/spellcheck/AndroidSpellCheckerService.java +++ b/java/src/com/android/inputmethod/latin/spellcheck/AndroidSpellCheckerService.java @@ -70,11 +70,17 @@ public class AndroidSpellCheckerService extends SpellCheckerService { private Map<String, Dictionary> mUserDictionaries = Collections.synchronizedMap(new TreeMap<String, Dictionary>()); - private double mTypoThreshold; + // The threshold for a candidate to be offered as a suggestion. + private double mSuggestionThreshold; + // The threshold for a suggestion to be considered "likely". + private double mLikelyThreshold; @Override public void onCreate() { super.onCreate(); - mTypoThreshold = Double.parseDouble(getString(R.string.spellchecker_typo_threshold_value)); + mSuggestionThreshold = + Double.parseDouble(getString(R.string.spellchecker_suggestion_threshold_value)); + mLikelyThreshold = + Double.parseDouble(getString(R.string.spellchecker_likely_threshold_value)); } @Override @@ -95,6 +101,9 @@ public class AndroidSpellCheckerService extends SpellCheckerService { private final int DEFAULT_SUGGESTION_LENGTH = 16; private final ArrayList<CharSequence> mSuggestions; private final int[] mScores; + private final String mOriginalText; + private final double mSuggestionThreshold; + private final double mLikelyThreshold; private final int mMaxLength; private int mLength = 0; @@ -103,7 +112,11 @@ public class AndroidSpellCheckerService extends SpellCheckerService { private String mBestSuggestion = null; private int mBestScore = Integer.MIN_VALUE; // As small as possible - SuggestionsGatherer(final int maxLength) { + SuggestionsGatherer(final String originalText, final double suggestionThreshold, + final double likelyThreshold, final int maxLength) { + mOriginalText = originalText; + mSuggestionThreshold = suggestionThreshold; + mLikelyThreshold = likelyThreshold; mMaxLength = maxLength; mSuggestions = new ArrayList<CharSequence>(maxLength + 1); mScores = new int[mMaxLength]; @@ -117,28 +130,42 @@ public class AndroidSpellCheckerService extends SpellCheckerService { // if it doesn't. See documentation for binarySearch. final int insertIndex = positionIndex >= 0 ? positionIndex : -positionIndex - 1; + if (insertIndex == 0 && mLength >= mMaxLength) { + // In the future, we may want to keep track of the best suggestion score even if + // we are asked for 0 suggestions. In this case, we can use the following + // (tested) code to keep it: + // If the maxLength is 0 (should never be less, but if it is, it's treated as 0) + // then we need to keep track of the best suggestion in mBestScore and + // mBestSuggestion. This is so that we know whether the best suggestion makes + // the score cutoff, since we need to know that to return a meaningful + // looksLikeTypo. + // if (0 >= mMaxLength) { + // if (score > mBestScore) { + // mBestScore = score; + // mBestSuggestion = new String(word, wordOffset, wordLength); + // } + // } + return true; + } + + // Compute the normalized score and skip this word if it's normalized score does not + // make the threshold. + final String wordString = new String(word, wordOffset, wordLength); + final double normalizedScore = + Utils.calcNormalizedScore(mOriginalText, wordString, score); + if (normalizedScore < mSuggestionThreshold) { + if (DBG) Log.i(TAG, wordString + " does not make the score threshold"); + return true; + } + if (mLength < mMaxLength) { final int copyLen = mLength - insertIndex; ++mLength; System.arraycopy(mScores, insertIndex, mScores, insertIndex + 1, copyLen); - mSuggestions.add(insertIndex, new String(word, wordOffset, wordLength)); + mSuggestions.add(insertIndex, wordString); } else { - if (insertIndex == 0) { - // If the maxLength is 0 (should never be less, but if it is, it's treated as 0) - // then we need to keep track of the best suggestion in mBestScore and - // mBestSuggestion. This is so that we know whether the best suggestion makes - // the score cutoff, since we need to know that to return a meaningful - // looksLikeTypo. - if (0 >= mMaxLength) { - if (score > mBestScore) { - mBestScore = score; - mBestSuggestion = new String(word, wordOffset, wordLength); - } - } - return true; - } System.arraycopy(mScores, 1, mScores, 0, insertIndex); - mSuggestions.add(insertIndex, new String(word, wordOffset, wordLength)); + mSuggestions.add(insertIndex, wordString); mSuggestions.remove(0); } mScores[insertIndex] = score; @@ -146,8 +173,7 @@ public class AndroidSpellCheckerService extends SpellCheckerService { return true; } - public Result getResults(final CharSequence originalText, final double threshold, - final int capitalizeType, final Locale locale) { + public Result getResults(final int capitalizeType, final Locale locale) { final String[] gatheredSuggestions; final boolean hasLikelySuggestions; if (0 == mLength) { @@ -160,8 +186,8 @@ public class AndroidSpellCheckerService extends SpellCheckerService { } else { gatheredSuggestions = EMPTY_STRING_ARRAY; final double normalizedScore = - Utils.calcNormalizedScore(originalText, mBestSuggestion, mBestScore); - hasLikelySuggestions = (normalizedScore > threshold); + Utils.calcNormalizedScore(mOriginalText, mBestSuggestion, mBestScore); + hasLikelySuggestions = (normalizedScore > mLikelyThreshold); } } else { if (DBG) { @@ -194,11 +220,12 @@ public class AndroidSpellCheckerService extends SpellCheckerService { final int bestScore = mScores[mLength - 1]; final CharSequence bestSuggestion = mSuggestions.get(0); final double normalizedScore = - Utils.calcNormalizedScore(originalText, bestSuggestion, bestScore); - hasLikelySuggestions = (normalizedScore > threshold); + Utils.calcNormalizedScore(mOriginalText, bestSuggestion, bestScore); + hasLikelySuggestions = (normalizedScore > mLikelyThreshold); if (DBG) { Log.i(TAG, "Best suggestion : " + bestSuggestion + ", score " + bestScore); - Log.i(TAG, "Normalized score = " + normalizedScore + " (threshold " + threshold + Log.i(TAG, "Normalized score = " + normalizedScore + + " (threshold " + mLikelyThreshold + ") => hasLikelySuggestions = " + hasLikelySuggestions); } } @@ -350,8 +377,8 @@ public class AndroidSpellCheckerService extends SpellCheckerService { } // TODO: Don't gather suggestions if the limit is <= 0 unless necessary - final SuggestionsGatherer suggestionsGatherer = - new SuggestionsGatherer(suggestionsLimit); + final SuggestionsGatherer suggestionsGatherer = new SuggestionsGatherer(text, + mService.mSuggestionThreshold, mService.mLikelyThreshold, suggestionsLimit); final WordComposer composer = new WordComposer(); final int length = text.length(); for (int i = 0; i < length; ++i) { @@ -392,8 +419,8 @@ public class AndroidSpellCheckerService extends SpellCheckerService { } } - final SuggestionsGatherer.Result result = suggestionsGatherer.getResults(text, - mService.mTypoThreshold, capitalizeType, mLocale); + final SuggestionsGatherer.Result result = suggestionsGatherer.getResults( + capitalizeType, mLocale); if (DBG) { Log.i(TAG, "Spell checking results for " + text + " with suggestion limit " diff --git a/native/jni/com_android_inputmethod_keyboard_ProximityInfo.cpp b/native/jni/com_android_inputmethod_keyboard_ProximityInfo.cpp index 07cee4053..595ea2fdc 100644 --- a/native/jni/com_android_inputmethod_keyboard_ProximityInfo.cpp +++ b/native/jni/com_android_inputmethod_keyboard_ProximityInfo.cpp @@ -32,18 +32,27 @@ static jint latinime_Keyboard_setProximityInfo(JNIEnv *env, jobject object, jint maxProximityCharsSize, jint displayWidth, jint displayHeight, jint gridWidth, jint gridHeight, jintArray proximityCharsArray, jint keyCount, jintArray keyXCoordinateArray, jintArray keyYCoordinateArray, jintArray keyWidthArray, - jintArray keyHeightArray, jintArray keyCharCodeArray, jint themeId) { + jintArray keyHeightArray, jintArray keyCharCodeArray, + jfloatArray sweetSpotCenterXArray, jfloatArray sweetSpotCenterYArray, + jfloatArray sweetSpotRadiusArray) { jint *proximityChars = env->GetIntArrayElements(proximityCharsArray, NULL); jint *keyXCoordinates = safeGetIntArrayElements(env, keyXCoordinateArray); jint *keyYCoordinates = safeGetIntArrayElements(env, keyYCoordinateArray); jint *keyWidths = safeGetIntArrayElements(env, keyWidthArray); jint *keyHeights = safeGetIntArrayElements(env, keyHeightArray); jint *keyCharCodes = safeGetIntArrayElements(env, keyCharCodeArray); + jfloat *sweetSpotCenterXs = safeGetFloatArrayElements(env, sweetSpotCenterXArray); + jfloat *sweetSpotCenterYs = safeGetFloatArrayElements(env, sweetSpotCenterYArray); + jfloat *sweetSpotRadii = safeGetFloatArrayElements(env, sweetSpotRadiusArray); ProximityInfo *proximityInfo = new ProximityInfo(maxProximityCharsSize, displayWidth, displayHeight, gridWidth, gridHeight, (const uint32_t*)proximityChars, keyCount, (const int32_t*)keyXCoordinates, (const int32_t*)keyYCoordinates, (const int32_t*)keyWidths, (const int32_t*)keyHeights, (const int32_t*)keyCharCodes, - themeId); + (const float*)sweetSpotCenterXs, (const float*)sweetSpotCenterYs, + (const float*)sweetSpotRadii); + safeReleaseFloatArrayElements(env, sweetSpotRadiusArray, sweetSpotRadii); + safeReleaseFloatArrayElements(env, sweetSpotCenterYArray, sweetSpotCenterYs); + safeReleaseFloatArrayElements(env, sweetSpotCenterXArray, sweetSpotCenterXs); safeReleaseIntArrayElements(env, keyCharCodeArray, keyCharCodes); safeReleaseIntArrayElements(env, keyHeightArray, keyHeights); safeReleaseIntArrayElements(env, keyWidthArray, keyWidths); @@ -60,7 +69,8 @@ static void latinime_Keyboard_release(JNIEnv *env, jobject object, jint proximit } static JNINativeMethod sKeyboardMethods[] = { - {"setProximityInfoNative", "(IIIII[II[I[I[I[I[II)I", (void*)latinime_Keyboard_setProximityInfo}, + {"setProximityInfoNative", "(IIIII[II[I[I[I[I[I[F[F[F)I", + (void*)latinime_Keyboard_setProximityInfo}, {"releaseProximityInfoNative", "(I)V", (void*)latinime_Keyboard_release} }; diff --git a/native/jni/jni_common.h b/native/jni/jni_common.h index dbf6d3e19..9548e1b3f 100644 --- a/native/jni/jni_common.h +++ b/native/jni/jni_common.h @@ -35,12 +35,26 @@ inline jint *safeGetIntArrayElements(JNIEnv *env, jintArray jArray) { } } +inline jfloat *safeGetFloatArrayElements(JNIEnv *env, jfloatArray jArray) { + if (jArray) { + return env->GetFloatArrayElements(jArray, NULL); + } else { + return NULL; + } +} + inline void safeReleaseIntArrayElements(JNIEnv *env, jintArray jArray, jint *cArray) { if (jArray) { env->ReleaseIntArrayElements(jArray, cArray, 0); } } +inline void safeReleaseFloatArrayElements(JNIEnv *env, jfloatArray jArray, jfloat *cArray) { + if (jArray) { + env->ReleaseFloatArrayElements(jArray, cArray, 0); + } +} + } // namespace latinime #endif // LATINIME_JNI_COMMON_H diff --git a/native/src/correction.cpp b/native/src/correction.cpp index d5bfed017..9a7e5f35d 100644 --- a/native/src/correction.cpp +++ b/native/src/correction.cpp @@ -62,7 +62,8 @@ void Correction::initCorrectionState( } void Correction::setCorrectionParams(const int skipPos, const int excessivePos, - const int transposedPos, const int spaceProximityPos, const int missingSpacePos) { + const int transposedPos, const int spaceProximityPos, const int missingSpacePos, + const bool useFullEditDistance) { // TODO: remove mTransposedPos = transposedPos; mExcessivePos = excessivePos; @@ -74,6 +75,7 @@ void Correction::setCorrectionParams(const int skipPos, const int excessivePos, mSpaceProximityPos = spaceProximityPos; mMissingSpacePos = missingSpacePos; + mUseFullEditDistance = useFullEditDistance; } void Correction::checkState() { @@ -439,7 +441,7 @@ inline static void multiplyIntCapped(const int multiplier, int *base) { } inline static int powerIntCapped(const int base, const int n) { - if (n == 0) return 1; + if (n <= 0) return 1; if (base == 2) { return n < 31 ? 1 << n : S_INT_MAX; } else { @@ -529,6 +531,8 @@ int Correction::RankingAlgorithm::calculateFinalFreq(const int inputIndex, const const int excessiveCount = correction->mExcessiveCount + correction->mTransposedCount % 2; const int proximityMatchedCount = correction->mProximityCount; const bool lastCharExceeded = correction->mLastCharExceeded; + const bool useFullEditDistance = correction->mUseFullEditDistance; + const int outputLength = outputIndex + 1; if (skippedCount >= inputLength || inputLength == 0) { return -1; } @@ -682,6 +686,12 @@ int Correction::RankingAlgorithm::calculateFinalFreq(const int inputIndex, const multiplyIntCapped(fullWordMultiplier, &finalFreq); } + if (useFullEditDistance && outputLength > inputLength + 1) { + const int diff = outputLength - inputLength - 1; + const int divider = diff < 31 ? 1 << diff : S_INT_MAX; + finalFreq = divider > finalFreq ? 1 : finalFreq / divider; + } + if (DEBUG_DICT_FULL) { LOGI("calc: %d, %d", outputIndex, sameLength); } diff --git a/native/src/correction.h b/native/src/correction.h index 41130ad77..ddb98b9d8 100644 --- a/native/src/correction.h +++ b/native/src/correction.h @@ -44,7 +44,7 @@ public: // TODO: remove void setCorrectionParams(const int skipPos, const int excessivePos, const int transposedPos, - const int spaceProximityPos, const int missingSpacePos); + const int spaceProximityPos, const int missingSpacePos, const bool useFullEditDistance); void checkState(); bool initProcessState(const int index); @@ -111,6 +111,7 @@ private: const int FULL_WORD_MULTIPLIER; const ProximityInfo *mProximityInfo; + bool mUseFullEditDistance; int mMaxEditDistance; int mMaxDepth; int mInputLength; diff --git a/native/src/defines_touch_position_correction.h b/native/src/defines_touch_position_correction.h deleted file mode 100644 index 122906804..000000000 --- a/native/src/defines_touch_position_correction.h +++ /dev/null @@ -1,61 +0,0 @@ -/* -** -** Copyright 2011, The Android Open Source Project -** -** Licensed under the Apache License, Version 2.0 (the "License"); -** you may not use this file except in compliance with the License. -** You may obtain a copy of the License at -** -** http://www.apache.org/licenses/LICENSE-2.0 -** -** Unless required by applicable law or agreed to in writing, software -** distributed under the License is distributed on an "AS IS" BASIS, -** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -** See the License for the specific language governing permissions and -** limitations under the License. -*/ - -#ifndef LATINIME_DEFINES_TOUCH_POSITION_CORRECTION_H -#define LATINIME_DEFINES_TOUCH_POSITION_CORRECTION_H - -#define OUTER_SWEET_SPOT_RADIUS_RATIO 1.3 - -static const char* TOUCH_POSITION_CORRECTION_GROUPS[] = { - "qwertyuiop", - "a", - "sdfghjk", - "l", - "zxcvbnm", -}; - -// (center X) / (key width) -static const float RELATIVE_TOUCH_CENTER_X[] = { - 0, // qwertyuiop - -0.26871, // a - 0, // sdfghjk - 0.028050, // l - 0, // zxcvbnm -}; - -// (center Y) / (key height) -static const float RELATIVE_TOUCH_CENTER_Y[] = { - 0.192088, // qwertyuiop - 0.214100, // a - 0.216640, // sdfghjk - 0.233288, // l - 0.286598, // zxcvbnm -}; - -// (sweet spot radius) / ((key width) + (key height)) -static const float SWEET_SPOT_RADIUS[] = { - 0.148955, // qwertyuiop - 0.185437, // a - 0.145522, // sdfghjk - 0.156882, // l - 0.144211, // zxcvbnm -}; - -#define CORRECTION_GROUP_COUNT \ - ((int)(sizeof(TOUCH_POSITION_CORRECTION_GROUPS) / sizeof(TOUCH_POSITION_CORRECTION_GROUPS[0]))) - -#endif // LATINIME_DEFINES_TOUCH_POSITION_CORRECTION_H diff --git a/native/src/proximity_info.cpp b/native/src/proximity_info.cpp index 58842b92f..4ff6e0ac0 100644 --- a/native/src/proximity_info.cpp +++ b/native/src/proximity_info.cpp @@ -20,7 +20,6 @@ #define LOG_TAG "LatinIME: proximity_info.cpp" -#include "defines_touch_position_correction.h" #include "dictionary.h" #include "proximity_info.h" @@ -38,12 +37,13 @@ ProximityInfo::ProximityInfo(const int maxProximityCharsSize, const int keyboard const int keyboardHeight, const int gridWidth, const int gridHeight, const uint32_t *proximityCharsArray, const int keyCount, const int32_t *keyXCoordinates, const int32_t *keyYCoordinates, const int32_t *keyWidths, const int32_t *keyHeights, - const int32_t *keyCharCodes, int themeId) + const int32_t *keyCharCodes, const float *sweetSpotCenterXs, const float *sweetSpotCenterYs, + const float *sweetSpotRadii) : MAX_PROXIMITY_CHARS_SIZE(maxProximityCharsSize), KEYBOARD_WIDTH(keyboardWidth), KEYBOARD_HEIGHT(keyboardHeight), GRID_WIDTH(gridWidth), GRID_HEIGHT(gridHeight), CELL_WIDTH((keyboardWidth + gridWidth - 1) / gridWidth), CELL_HEIGHT((keyboardHeight + gridHeight - 1) / gridHeight), - KEY_COUNT(min(keyCount, MAX_KEY_COUNT_IN_A_KEYBOARD)), THEME_ID(themeId) { + KEY_COUNT(min(keyCount, MAX_KEY_COUNT_IN_A_KEYBOARD)) { const int len = GRID_WIDTH * GRID_HEIGHT * MAX_PROXIMITY_CHARS_SIZE; mProximityCharsArray = new uint32_t[len]; if (DEBUG_PROXIMITY_INFO) { @@ -56,33 +56,24 @@ ProximityInfo::ProximityInfo(const int maxProximityCharsSize, const int keyboard copyOrFillZero(mKeyWidths, keyWidths, KEY_COUNT * sizeof(mKeyWidths[0])); copyOrFillZero(mKeyHeights, keyHeights, KEY_COUNT * sizeof(mKeyHeights[0])); copyOrFillZero(mKeyCharCodes, keyCharCodes, KEY_COUNT * sizeof(mKeyCharCodes[0])); + copyOrFillZero(mSweetSpotCenterXs, sweetSpotCenterXs, + KEY_COUNT * sizeof(mSweetSpotCenterXs[0])); + copyOrFillZero(mSweetSpotCenterYs, sweetSpotCenterYs, + KEY_COUNT * sizeof(mSweetSpotCenterYs[0])); + copyOrFillZero(mSweetSpotRadii, sweetSpotRadii, KEY_COUNT * sizeof(mSweetSpotRadii[0])); - initializeCodeToGroup(); initializeCodeToKeyIndex(); } -// Build the reversed look up table from the char code to the index in its group. -// see TOUCH_POSITION_CORRECTION_GROUPS -void ProximityInfo::initializeCodeToGroup() { - memset(mCodeToGroup, -1, (MAX_GROUPED_CHAR_CODE + 1) * sizeof(mCodeToGroup[0])); - for (int i = 0; i < CORRECTION_GROUP_COUNT; ++i) { - const char *group = TOUCH_POSITION_CORRECTION_GROUPS[i]; - for (int j = 0; group[j]; ++j) { - const int code = group[j]; - if (0 <= code && code <= MAX_GROUPED_CHAR_CODE) - mCodeToGroup[code] = i; - } - } -} - // Build the reversed look up table from the char code to the index in mKeyXCoordinates, // mKeyYCoordinates, mKeyWidths, mKeyHeights, mKeyCharCodes. void ProximityInfo::initializeCodeToKeyIndex() { - memset(mCodeToKeyIndex, -1, (MAX_GROUPED_CHAR_CODE + 1) * sizeof(mCodeToKeyIndex[0])); + memset(mCodeToKeyIndex, -1, (MAX_CHAR_CODE + 1) * sizeof(mCodeToKeyIndex[0])); for (int i = 0; i < KEY_COUNT; ++i) { const int code = mKeyCharCodes[i]; - if (0 <= code && code <= MAX_GROUPED_CHAR_CODE) + if (0 <= code && code <= MAX_CHAR_CODE) { mCodeToKeyIndex[code] = i; + } } } @@ -210,6 +201,6 @@ bool ProximityInfo::sameAsTyped(const unsigned short *word, int length) const { } const int ProximityInfo::MAX_KEY_COUNT_IN_A_KEYBOARD; -const int ProximityInfo::MAX_GROUPED_CHAR_CODE; +const int ProximityInfo::MAX_CHAR_CODE; } // namespace latinime diff --git a/native/src/proximity_info.h b/native/src/proximity_info.h index 3190e73ef..b1e8236d3 100644 --- a/native/src/proximity_info.h +++ b/native/src/proximity_info.h @@ -37,7 +37,8 @@ public: const int keybaordHeight, const int gridWidth, const int gridHeight, const uint32_t *proximityCharsArray, const int keyCount, const int32_t *keyXCoordinates, const int32_t *keyYCoordinates, const int32_t *keyWidths, const int32_t *keyHeights, - const int32_t *keyCharCodes, int themeId); + const int32_t *keyCharCodes, const float *sweetSpotCenterXs, + const float *sweetSpotCenterYs, const float *sweetSpotRadii); ~ProximityInfo(); bool hasSpaceProximity(const int x, const int y) const; void setInputParams(const int* inputCodes, const int inputLength); @@ -55,11 +56,10 @@ public: private: // The max number of the keys in one keyboard layout static const int MAX_KEY_COUNT_IN_A_KEYBOARD = 64; - // The upper limit of the char code in TOUCH_POSITION_CORRECTION_GROUP - static const int MAX_GROUPED_CHAR_CODE = 127; + // The upper limit of the char code in mCodeToKeyIndex + static const int MAX_CHAR_CODE = 127; int getStartIndexFromCoordinates(const int x, const int y) const; - void initializeCodeToGroup(); void initializeCodeToKeyIndex(); const int MAX_PROXIMITY_CHARS_SIZE; const int KEYBOARD_WIDTH; @@ -69,7 +69,6 @@ private: const int CELL_WIDTH; const int CELL_HEIGHT; const int KEY_COUNT; - const int THEME_ID; const int *mInputCodes; uint32_t *mProximityCharsArray; int32_t mKeyXCoordinates[MAX_KEY_COUNT_IN_A_KEYBOARD]; @@ -77,10 +76,12 @@ private: int32_t mKeyWidths[MAX_KEY_COUNT_IN_A_KEYBOARD]; int32_t mKeyHeights[MAX_KEY_COUNT_IN_A_KEYBOARD]; int32_t mKeyCharCodes[MAX_KEY_COUNT_IN_A_KEYBOARD]; + float mSweetSpotCenterXs[MAX_KEY_COUNT_IN_A_KEYBOARD]; + float mSweetSpotCenterYs[MAX_KEY_COUNT_IN_A_KEYBOARD]; + float mSweetSpotRadii[MAX_KEY_COUNT_IN_A_KEYBOARD]; int mInputLength; unsigned short mPrimaryInputWord[MAX_WORD_LENGTH_INTERNAL]; - int mCodeToGroup[MAX_GROUPED_CHAR_CODE + 1]; - int mCodeToKeyIndex[MAX_GROUPED_CHAR_CODE + 1]; + int mCodeToKeyIndex[MAX_CHAR_CODE + 1]; }; } // namespace latinime diff --git a/native/src/unigram_dictionary.cpp b/native/src/unigram_dictionary.cpp index 517dc843e..1b798a8f1 100644 --- a/native/src/unigram_dictionary.cpp +++ b/native/src/unigram_dictionary.cpp @@ -132,7 +132,8 @@ void UnigramDictionary::getWordWithDigraphSuggestionsRec(ProximityInfo *proximit memcpy(codesDest, codesSrc, remainingBytes); getWordSuggestions(proximityInfo, xcoordinates, ycoordinates, codesBuffer, - (codesDest - codesBuffer) / MAX_PROXIMITY_CHARS + codesRemain, outWords, frequencies); + (codesDest - codesBuffer) / MAX_PROXIMITY_CHARS + codesRemain, outWords, frequencies, + flags); } int UnigramDictionary::getSuggestions(ProximityInfo *proximityInfo, const int *xcoordinates, @@ -146,7 +147,7 @@ int UnigramDictionary::getSuggestions(ProximityInfo *proximityInfo, const int *x codesSize, flags, codes, codesSize, 0, codesBuffer, outWords, frequencies); } else { // Normal processing getWordSuggestions(proximityInfo, xcoordinates, ycoordinates, codes, codesSize, - outWords, frequencies); + outWords, frequencies, flags); } PROF_START(20); @@ -175,7 +176,7 @@ int UnigramDictionary::getSuggestions(ProximityInfo *proximityInfo, const int *x void UnigramDictionary::getWordSuggestions(ProximityInfo *proximityInfo, const int *xcoordinates, const int *ycoordinates, const int *codes, const int codesSize, - unsigned short *outWords, int *frequencies) { + unsigned short *outWords, int *frequencies, const int flags) { PROF_OPEN; PROF_START(0); @@ -187,9 +188,10 @@ void UnigramDictionary::getWordSuggestions(ProximityInfo *proximityInfo, mCorrection->initCorrection(mProximityInfo, mInputLength, maxDepth); PROF_END(0); + const bool useFullEditDistance = USE_FULL_EDIT_DISTANCE & flags; // TODO: remove PROF_START(1); - getSuggestionCandidates(); + getSuggestionCandidates(useFullEditDistance); PROF_END(1); PROF_START(2); @@ -212,7 +214,7 @@ void UnigramDictionary::getWordSuggestions(ProximityInfo *proximityInfo, if (DEBUG_DICT) { LOGI("--- Suggest missing space characters %d", i); } - getMissingSpaceWords(mInputLength, i, mCorrection); + getMissingSpaceWords(mInputLength, i, mCorrection, useFullEditDistance); } } PROF_END(5); @@ -231,7 +233,7 @@ void UnigramDictionary::getWordSuggestions(ProximityInfo *proximityInfo, i, x, y, proximityInfo->hasSpaceProximity(x, y)); } if (proximityInfo->hasSpaceProximity(x, y)) { - getMistypedSpaceWords(mInputLength, i, mCorrection); + getMistypedSpaceWords(mInputLength, i, mCorrection, useFullEditDistance); } } } @@ -315,10 +317,10 @@ bool UnigramDictionary::addWord(unsigned short *word, int length, int frequency) static const char QUOTE = '\''; static const char SPACE = ' '; -void UnigramDictionary::getSuggestionCandidates() { +void UnigramDictionary::getSuggestionCandidates(const bool useFullEditDistance) { // TODO: Remove setCorrectionParams mCorrection->setCorrectionParams(0, 0, 0, - -1 /* spaceProximityPos */, -1 /* missingSpacePos */); + -1 /* spaceProximityPos */, -1 /* missingSpacePos */, useFullEditDistance); int rootPosition = ROOT_POS; // Get the number of children of root, then increment the position int childCount = Dictionary::getCount(DICT_ROOT, &rootPosition); @@ -349,16 +351,20 @@ void UnigramDictionary::getSuggestionCandidates() { } void UnigramDictionary::getMissingSpaceWords( - const int inputLength, const int missingSpacePos, Correction *correction) { + const int inputLength, const int missingSpacePos, Correction *correction, + const bool useFullEditDistance) { correction->setCorrectionParams(-1 /* skipPos */, -1 /* excessivePos */, - -1 /* transposedPos */, -1 /* spaceProximityPos */, missingSpacePos); + -1 /* transposedPos */, -1 /* spaceProximityPos */, missingSpacePos, + useFullEditDistance); getSplitTwoWordsSuggestion(inputLength, correction); } void UnigramDictionary::getMistypedSpaceWords( - const int inputLength, const int spaceProximityPos, Correction *correction) { + const int inputLength, const int spaceProximityPos, Correction *correction, + const bool useFullEditDistance) { correction->setCorrectionParams(-1 /* skipPos */, -1 /* excessivePos */, - -1 /* transposedPos */, spaceProximityPos, -1 /* missingSpacePos */); + -1 /* transposedPos */, spaceProximityPos, -1 /* missingSpacePos */, + useFullEditDistance); getSplitTwoWordsSuggestion(inputLength, correction); } diff --git a/native/src/unigram_dictionary.h b/native/src/unigram_dictionary.h index 65746db8d..ef9709a89 100644 --- a/native/src/unigram_dictionary.h +++ b/native/src/unigram_dictionary.h @@ -78,7 +78,7 @@ private: void getWordSuggestions(ProximityInfo *proximityInfo, const int *xcoordinates, const int *ycoordinates, const int *codes, const int codesSize, - unsigned short *outWords, int *frequencies); + unsigned short *outWords, int *frequencies, const int flags); bool isDigraph(const int* codes, const int i, const int codesSize) const; void getWordWithDigraphSuggestionsRec(ProximityInfo *proximityInfo, const int *xcoordinates, const int* ycoordinates, const int *codesBuffer, @@ -87,13 +87,13 @@ private: void initSuggestions(ProximityInfo *proximityInfo, const int *xcoordinates, const int *ycoordinates, const int *codes, const int codesSize, unsigned short *outWords, int *frequencies); - void getSuggestionCandidates(); + void getSuggestionCandidates(const bool useFullEditDistance); bool addWord(unsigned short *word, int length, int frequency); void getSplitTwoWordsSuggestion(const int inputLength, Correction *correction); - void getMissingSpaceWords( - const int inputLength, const int missingSpacePos, Correction *correction); - void getMistypedSpaceWords( - const int inputLength, const int spaceProximityPos, Correction *correction); + void getMissingSpaceWords(const int inputLength, const int missingSpacePos, + Correction *correction, const bool useFullEditDistance); + void getMistypedSpaceWords(const int inputLength, const int spaceProximityPos, + Correction *correction, const bool useFullEditDistance); void onTerminal(const int freq, Correction *correction); bool needsToSkipCurrentNode(const unsigned short c, const int inputIndex, const int skipPos, const int depth); @@ -122,7 +122,8 @@ private: // or something very bad (like, the apocalypse) will happen. // Please update both at the same time. enum { - REQUIRES_GERMAN_UMLAUT_PROCESSING = 0x1 + REQUIRES_GERMAN_UMLAUT_PROCESSING = 0x1, + USE_FULL_EDIT_DISTANCE = 0x2 }; static const struct digraph_t { int first; int second; } GERMAN_UMLAUT_DIGRAPHS[]; diff --git a/tools/makedict/src/com/android/inputmethod/latin/XmlDictInputOutput.java b/tools/makedict/src/com/android/inputmethod/latin/XmlDictInputOutput.java index 096bfd182..35a7b51d6 100644 --- a/tools/makedict/src/com/android/inputmethod/latin/XmlDictInputOutput.java +++ b/tools/makedict/src/com/android/inputmethod/latin/XmlDictInputOutput.java @@ -60,6 +60,7 @@ public class XmlDictInputOutput { final FusionDictionary mDictionary; int mState; // the state of the parser int mFreq; // the currently read freq + String mWord; // the current word final HashMap<String, ArrayList<WeightedString>> mBigramsMap; /** @@ -72,6 +73,7 @@ public class XmlDictInputOutput { HashMap<String, ArrayList<WeightedString>> bigrams) { mDictionary = dict; mBigramsMap = bigrams; + mWord = ""; mState = START; mFreq = 0; } @@ -80,6 +82,7 @@ public class XmlDictInputOutput { public void startElement(String uri, String localName, String qName, Attributes attrs) { if (WORD_TAG.equals(localName)) { mState = WORD; + mWord = ""; for (int attrIndex = 0; attrIndex < attrs.getLength(); ++attrIndex) { final String attrName = attrs.getLocalName(attrIndex); if (FREQUENCY_ATTR.equals(attrName)) { @@ -94,14 +97,19 @@ public class XmlDictInputOutput { @Override public void characters(char[] ch, int start, int length) { if (WORD == mState) { - final String word = String.copyValueOf(ch, start, length); - mDictionary.add(word, mFreq, mBigramsMap.get(word)); + // The XML parser is free to return text in arbitrary chunks one after the + // other. In particular, this happens in some implementations when it finds + // an escape code like "&". + mWord += String.copyValueOf(ch, start, length); } } @Override public void endElement(String uri, String localName, String qName) { - if (WORD == mState) mState = START; + if (WORD == mState) { + mDictionary.add(mWord, mFreq, mBigramsMap.get(mWord)); + mState = START; + } } } |