diff options
63 files changed, 839 insertions, 297 deletions
diff --git a/java/res/drawable-hdpi/unbundled_check_01.png b/java/res/drawable-hdpi/unbundled_check_01.png Binary files differnew file mode 100644 index 000000000..8234399d5 --- /dev/null +++ b/java/res/drawable-hdpi/unbundled_check_01.png diff --git a/java/res/drawable-hdpi/unbundled_check_02.png b/java/res/drawable-hdpi/unbundled_check_02.png Binary files differnew file mode 100644 index 000000000..6ccd07b55 --- /dev/null +++ b/java/res/drawable-hdpi/unbundled_check_02.png diff --git a/java/res/drawable-hdpi/unbundled_earth_01.png b/java/res/drawable-hdpi/unbundled_earth_01.png Binary files differnew file mode 100644 index 000000000..3d22f3bfc --- /dev/null +++ b/java/res/drawable-hdpi/unbundled_earth_01.png diff --git a/java/res/drawable-hdpi/unbundled_earth_02.png b/java/res/drawable-hdpi/unbundled_earth_02.png Binary files differnew file mode 100644 index 000000000..1998aea56 --- /dev/null +++ b/java/res/drawable-hdpi/unbundled_earth_02.png diff --git a/java/res/drawable-hdpi/unbundled_key_01.png b/java/res/drawable-hdpi/unbundled_key_01.png Binary files differnew file mode 100644 index 000000000..84591ecb7 --- /dev/null +++ b/java/res/drawable-hdpi/unbundled_key_01.png diff --git a/java/res/drawable-hdpi/unbundled_key_02.png b/java/res/drawable-hdpi/unbundled_key_02.png Binary files differnew file mode 100644 index 000000000..f366e524c --- /dev/null +++ b/java/res/drawable-hdpi/unbundled_key_02.png diff --git a/java/res/drawable-hdpi/unbundled_select_01.png b/java/res/drawable-hdpi/unbundled_select_01.png Binary files differnew file mode 100644 index 000000000..3887fe491 --- /dev/null +++ b/java/res/drawable-hdpi/unbundled_select_01.png diff --git a/java/res/drawable-hdpi/unbundled_select_02.png b/java/res/drawable-hdpi/unbundled_select_02.png Binary files differnew file mode 100644 index 000000000..6a99b6b09 --- /dev/null +++ b/java/res/drawable-hdpi/unbundled_select_02.png diff --git a/java/res/drawable/ic_setup_step1.xml b/java/res/drawable/ic_setup_step1.xml new file mode 100644 index 000000000..e26afb3ca --- /dev/null +++ b/java/res/drawable/ic_setup_step1.xml @@ -0,0 +1,30 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- +/* +** +** Copyright 2013, 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. +*/ +--> + +<selector xmlns:android="http://schemas.android.com/apk/res/android"> + <item + android:state_focused="true" + android:drawable="@drawable/unbundled_key_01" /> + <item + android:state_pressed="true" + android:drawable="@drawable/unbundled_key_01" /> + <item + android:drawable="@drawable/unbundled_key_02" /> +</selector> diff --git a/java/res/drawable/ic_setup_step2.xml b/java/res/drawable/ic_setup_step2.xml new file mode 100644 index 000000000..46db29306 --- /dev/null +++ b/java/res/drawable/ic_setup_step2.xml @@ -0,0 +1,30 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- +/* +** +** Copyright 2013, 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. +*/ +--> + +<selector xmlns:android="http://schemas.android.com/apk/res/android"> + <item + android:state_focused="true" + android:drawable="@drawable/unbundled_select_01" /> + <item + android:state_pressed="true" + android:drawable="@drawable/unbundled_select_01" /> + <item + android:drawable="@drawable/unbundled_select_02" /> +</selector> diff --git a/java/res/drawable/ic_setup_step3.xml b/java/res/drawable/ic_setup_step3.xml new file mode 100644 index 000000000..4ff9fd933 --- /dev/null +++ b/java/res/drawable/ic_setup_step3.xml @@ -0,0 +1,30 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- +/* +** +** Copyright 2013, 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. +*/ +--> + +<selector xmlns:android="http://schemas.android.com/apk/res/android"> + <item + android:state_focused="true" + android:drawable="@drawable/unbundled_earth_01" /> + <item + android:state_pressed="true" + android:drawable="@drawable/unbundled_earth_01" /> + <item + android:drawable="@drawable/unbundled_earth_02" /> +</selector> diff --git a/java/res/drawable/ic_setup_step3_finish.xml b/java/res/drawable/ic_setup_step3_finish.xml new file mode 100644 index 000000000..8ac8a86d1 --- /dev/null +++ b/java/res/drawable/ic_setup_step3_finish.xml @@ -0,0 +1,30 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- +/* +** +** Copyright 2013, 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. +*/ +--> + +<selector xmlns:android="http://schemas.android.com/apk/res/android"> + <item + android:state_focused="true" + android:drawable="@drawable/unbundled_check_01" /> + <item + android:state_pressed="true" + android:drawable="@drawable/unbundled_check_01" /> + <item + android:drawable="@drawable/unbundled_check_02" /> +</selector> diff --git a/java/res/drawable/setup_step_action_background.xml b/java/res/drawable/setup_step_action_background.xml new file mode 100644 index 000000000..25738e3a4 --- /dev/null +++ b/java/res/drawable/setup_step_action_background.xml @@ -0,0 +1,30 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- +/* +** +** Copyright 2013, 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. +*/ +--> + +<selector xmlns:android="http://schemas.android.com/apk/res/android"> + <item + android:state_focused="true" + android:drawable="@color/setup_text_action" /> + <item + android:state_pressed="true" + android:drawable="@color/setup_text_action" /> + <item + android:drawable="@color/setup_step_background" /> +</selector> diff --git a/java/res/drawable/setup_step_action_color.xml b/java/res/drawable/setup_step_action_color.xml new file mode 100644 index 000000000..c53e026d5 --- /dev/null +++ b/java/res/drawable/setup_step_action_color.xml @@ -0,0 +1,30 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- +/* +** +** Copyright 2013, 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. +*/ +--> + +<selector xmlns:android="http://schemas.android.com/apk/res/android"> + <item + android:state_focused="true" + android:color="@color/setup_step_background" /> + <item + android:state_pressed="true" + android:color="@color/setup_step_background" /> + <item + android:color="@color/setup_text_action" /> +</selector> diff --git a/java/res/layout/setup_step.xml b/java/res/layout/setup_step.xml index 26d7fe799..c15d07bad 100644 --- a/java/res/layout/setup_step.xml +++ b/java/res/layout/setup_step.xml @@ -42,7 +42,7 @@ <View android:layout_width="match_parent" android:layout_height="2dp" /> - <TextView + <Button android:id="@+id/setup_step_action_label" style="@style/setupStepActionLabelStyle" android:gravity="center_vertical" diff --git a/java/res/values/attrs.xml b/java/res/values/attrs.xml index 3a7b39e3a..a71e7cc11 100644 --- a/java/res/values/attrs.xml +++ b/java/res/values/attrs.xml @@ -85,6 +85,8 @@ <attr name="slidingKeyInputEnable" format="boolean" /> <attr name="slidingKeyInputPreviewColor" format="color" /> <attr name="slidingKeyInputPreviewWidth" format="dimension" /> + <attr name="slidingKeyInputPreviewBodyRatio" format="integer" /> + <attr name="slidingKeyInputPreviewShadowRatio" format="integer" /> <!-- Key repeat start timeout --> <attr name="keyRepeatStartTimeout" format="integer" /> <!-- Key repeat interval in millisecond. --> @@ -115,6 +117,8 @@ <attr name="gesturePreviewTrailColor" format="color" /> <attr name="gesturePreviewTrailStartWidth" format="dimension" /> <attr name="gesturePreviewTrailEndWidth" format="dimension" /> + <attr name="gesturePreviewTrailBodyRatio" format="integer" /> + <attr name="gesturePreviewTrailShadowRatio" format="integer" /> <!-- Delay after gesture input and gesture floating preview text dismissing in millisecond --> <attr name="gestureFloatingPreviewTextLingerTimeout" format="integer" /> <!-- Attributes for GestureFloatingPreviewText --> diff --git a/java/res/values/config.xml b/java/res/values/config.xml index a90ba8014..d4fff620c 100644 --- a/java/res/values/config.xml +++ b/java/res/values/config.xml @@ -58,6 +58,9 @@ <bool name="config_sliding_key_input_enabled">true</bool> <!-- Sliding key input preview parameters --> <dimen name="config_sliding_key_input_preview_width">8.0dp</dimen> + <!-- Percentages of sliding key input preview body and shadow, in proportion to the width. --> + <integer name="config_sliding_key_input_preview_body_ratio">80</integer> + <integer name="config_sliding_key_input_preview_shadow_ratio">50</integer> <integer name="config_key_repeat_start_timeout">400</integer> <integer name="config_key_repeat_interval">50</integer> <integer name="config_default_longpress_key_timeout">300</integer> <!-- milliseconds --> diff --git a/java/res/values/dimens.xml b/java/res/values/dimens.xml index dd42acf3c..db33ad812 100644 --- a/java/res/values/dimens.xml +++ b/java/res/values/dimens.xml @@ -103,6 +103,9 @@ <!-- Gesture preview trail parameters --> <dimen name="gesture_preview_trail_start_width">10.0dp</dimen> <dimen name="gesture_preview_trail_end_width">2.5dp</dimen> + <!-- Percentages of gesture preview taril body and shadow, in proportion to the trail width. --> + <integer name="gesture_preview_trail_body_ratio">80</integer> + <integer name="gesture_preview_trail_shadow_ratio">50</integer> <!-- Gesture floating preview text parameters --> <dimen name="gesture_floating_preview_text_size">24dp</dimen> <dimen name="gesture_floating_preview_text_offset">73dp</dimen> diff --git a/java/res/values/setup-styles.xml b/java/res/values/setup-styles.xml index cfc689a78..420adcbb7 100644 --- a/java/res/values/setup-styles.xml +++ b/java/res/values/setup-styles.xml @@ -38,8 +38,8 @@ <item name="android:textSize">14sp</item> </style> <style name="setupStepActionLabelStyle"> - <item name="android:background">@color/setup_step_background</item> - <item name="android:textColor">@color/setup_text_action</item> + <item name="android:background">@drawable/setup_step_action_background</item> + <item name="android:textColor">@drawable/setup_step_action_color</item> <item name="android:textSize">18sp</item> </style> </resources> diff --git a/java/res/values/strings.xml b/java/res/values/strings.xml index dbadfabb8..3d283de23 100644 --- a/java/res/values/strings.xml +++ b/java/res/values/strings.xml @@ -442,31 +442,40 @@ <!-- Title of the button to revert to the default value of the device in the settings dialog [CHAR LIMIT=15] --> <string name="button_default">Default</string> - <!-- TODO: Remove translatable="false" once wordings are finalized. --> + <!-- Title of the setup wizard welcome screen. [CHAR LIMT=40] --> + <string name="setup_welcome_title">"Welcome to <xliff:g id="application_name">%s</xliff:g>"</string> + <!-- Additional title of the setup wizard welcome screen, just below the setup_welcome_title. [CHAR_LIMIT=64] --> + <string name="setup_welcome_additional_description">with Gesture Typing</string> + <!-- The label of the button that starts the setup wizard. [CHAR_LIMIT=64] --> + <string name="setup_start_action">Get started</string> <!-- Title of the setup wizard. [CHAR LIMT=40] --> - <string name="setup_title" translatable="false">"Installing <xliff:g id="application_name">%s</xliff:g>"</string> + <string name="setup_steps_title">"Setting up <xliff:g id="application_name">%s</xliff:g>"</string> <!-- Ordinal number of the 1st step in the setup wizard. [CHAR LIMIT=5] --> <string name="setup_step1_bullet" translatable="false">1</string> <!-- Title of the 1st step in the setup wizard. [CHAR LIMIT=64] --> - <string name="setup_step1_title" translatable="false">"Enable <xliff:g id="application_name">%s</xliff:g> in settings."</string> + <string name="setup_step1_title">"Enable <xliff:g id="application_name">%s</xliff:g>"</string> <!-- Detailed instruction of the 1st step in the setup wizard. [CHAR LIMIT=80] --> - <string name="setup_step1_instruction" translatable="false">"For security, please check \"<xliff:g id="application_name">%s</xliff:g>\""</string> + <string name="setup_step1_instruction">"Please check \"<xliff:g id="application_name">%s</xliff:g>\" in your Language & input settings. This will authorize it to run on your device."</string> + <!-- Title of the Language & input settings. This should be aligned with msgid="5292716747264442359" --> + <string name="setup_step1_action">Language & input</string> <!-- Ordinal number of the 2nd step in the setup wizard. [CHAR LIMIT=5] --> <string name="setup_step2_bullet" translatable="false">2</string> <!-- Title of the 2nd step in the setup wizard. [CHAR LIMIT=64] --> - <string name="setup_step2_title" translatable="false">"Switch to <xliff:g id="application_name">%s</xliff:g>."</string> + <string name="setup_step2_title">"Switch to <xliff:g id="application_name">%s</xliff:g>"</string> <!-- Detailed instruction of the 2nd step in the setup wizard. [CHAR LIMIT=80] --> - <string name="setup_step2_instruction" translatable="false">"Now that you've enabled <xliff:g id="application_name">%s</xliff:g>, you can switch to it."</string> + <string name="setup_step2_instruction">"Now that it's enabled, select \"<xliff:g id="application_name">%s</xliff:g>\", one more time to activate it."</string> + <!-- Title of the Input method picker. This should be aligned with msgid="4653387336791222978" --> + <string name="setup_step2_action">Choose input method</string> <!-- Ordinal number of the 3rd step in the setup wizard. [CHAR LIMIT=5] --> <string name="setup_step3_bullet" translatable="false">3</string> <!-- Title of the 3rd step in the setup wizard. [CHAR LIMIT=64] --> - <string name="setup_step3_title" translatable="false">"Congratulations, you're all set!"</string> + <string name="setup_step3_title">"Congratulations, you're all set!"</string> <!-- Detailed instruction of the 3rd step in the setup wizard. [CHAR LIMIT=80] --> - <string name="setup_step3_instruction" translatable="false">Configure additional languages</string> - <!-- Title of the Language & input settings. This should be aligned with msgid="5292716747264442359" --> - <string name="language_settings">Language & input</string> - <!-- Title of the Input method picker. This should be aligned with msgid="4653387336791222978" --> - <string name="select_input_method">Choose input method</string> + <string name="setup_step3_instruction">Now you can type in all your favorite apps with <xliff:g id="application_name">%s</xliff:g>.</string> + <!-- The label of the button that triggers the screen for configuaring additional languages of the keyboard. [CHAR_LIMIT=64] --> + <string name="setup_step3_action">Configure additional languages</string> + <!-- The label of the button that finishes the setup wizard. [CHAR_LIMIT=64] --> + <string name="setup_finish_action">Finished</string> <!-- Option to show setup wizard icon. [CHAR LIMIT=30]--> <string name="show_setup_wizard_icon" translatable="false">Show setup wizard icon</string> @@ -498,9 +507,9 @@ <!-- Message about some dictionary indicating the file is installed, but the dictionary is disabled --> <string name="dictionary_disabled">Installed, disabled</string> - <!-- Message to display in the dictionaries setting screen when some error prevented us to list installed dictionaries [CHAR LIMIT=50] --> + <!-- Message to display in the dictionaries setting screen when some error prevented us to list installed dictionaries [CHAR LIMIT=20] --> <string name="cannot_connect_to_dict_service">Problem connecting to dictionary service</string> - <!-- Message to display in the dictionaries setting screen when we found that no dictionaries are available [CHAR LIMIT=50]--> + <!-- Message to display in the dictionaries setting screen when we found that no dictionaries are available [CHAR LIMIT=20]--> <string name="no_dictionaries_available">No dictionaries available</string> <!-- Title of the options to press to refresh the list (as in, check for updates now) [CHAR_LIMIT=50] --> diff --git a/java/res/values/styles.xml b/java/res/values/styles.xml index fb59c745f..436e080f7 100644 --- a/java/res/values/styles.xml +++ b/java/res/values/styles.xml @@ -70,6 +70,8 @@ <item name="gesturePreviewTrailColor">@color/highlight_color_default</item> <item name="gesturePreviewTrailStartWidth">@dimen/gesture_preview_trail_start_width</item> <item name="gesturePreviewTrailEndWidth">@dimen/gesture_preview_trail_end_width</item> + <item name="gesturePreviewTrailBodyRatio">@integer/gesture_preview_trail_body_ratio</item> + <item name="gesturePreviewTrailShadowRatio">@integer/gesture_preview_trail_shadow_ratio</item> <!-- Common attributes of MainKeyboardView --> <item name="keyHysteresisDistance">@dimen/config_key_hysteresis_distance</item> <item name="keyHysteresisDistanceForSlidingModifier">@dimen/config_key_hysteresis_distance_for_sliding_modifier</item> @@ -78,6 +80,8 @@ <item name="slidingKeyInputEnable">@bool/config_sliding_key_input_enabled</item> <item name="slidingKeyInputPreviewColor">@color/highlight_translucent_color_default</item> <item name="slidingKeyInputPreviewWidth">@dimen/config_sliding_key_input_preview_width</item> + <item name="slidingKeyInputPreviewBodyRatio">@integer/config_sliding_key_input_preview_body_ratio</item> + <item name="slidingKeyInputPreviewShadowRatio">@integer/config_sliding_key_input_preview_shadow_ratio</item> <item name="keyRepeatStartTimeout">@integer/config_key_repeat_start_timeout</item> <item name="keyRepeatInterval">@integer/config_key_repeat_interval</item> <item name="longPressShiftLockTimeout">@integer/config_longpress_shift_lock_timeout</item> diff --git a/java/src/com/android/inputmethod/keyboard/KeyboardSwitcher.java b/java/src/com/android/inputmethod/keyboard/KeyboardSwitcher.java index d15f14f88..4e41b77ce 100644 --- a/java/src/com/android/inputmethod/keyboard/KeyboardSwitcher.java +++ b/java/src/com/android/inputmethod/keyboard/KeyboardSwitcher.java @@ -207,7 +207,8 @@ public final class KeyboardSwitcher implements KeyboardState.SwitchActions { * Update keyboard shift state triggered by connected EditText status change. */ public void updateShiftState() { - mState.onUpdateShiftState(mLatinIME.getCurrentAutoCapsState()); + mState.onUpdateShiftState(mLatinIME.getCurrentAutoCapsState(), + mLatinIME.getCurrentRecapitalizeState()); } // TODO: Remove this method. Come up with a more comprehensive way to reset the keyboard layout @@ -276,7 +277,8 @@ public final class KeyboardSwitcher implements KeyboardState.SwitchActions { // Implements {@link KeyboardState.SwitchActions}. @Override public void requestUpdatingShiftState() { - mState.onUpdateShiftState(mLatinIME.getCurrentAutoCapsState()); + mState.onUpdateShiftState(mLatinIME.getCurrentAutoCapsState(), + mLatinIME.getCurrentRecapitalizeState()); } // Implements {@link KeyboardState.SwitchActions}. diff --git a/java/src/com/android/inputmethod/keyboard/MainKeyboardView.java b/java/src/com/android/inputmethod/keyboard/MainKeyboardView.java index ba78d014a..d74644d9e 100644 --- a/java/src/com/android/inputmethod/keyboard/MainKeyboardView.java +++ b/java/src/com/android/inputmethod/keyboard/MainKeyboardView.java @@ -1189,10 +1189,12 @@ public final class MainKeyboardView extends KeyboardView implements PointerTrack if (ENABLE_USABILITY_STUDY_LOG) { writeUsabilityStudyLog(me, action, eventTime, i, pointerId, px, py); } - if (ProductionFlag.USES_DEVELOPMENT_ONLY_DIAGNOSTICS) { - ResearchLogger.mainKeyboardView_processMotionEvent( - me, action, eventTime, i, pointerId, px, py); - } + // TODO: This seems to be no longer necessary, and confusing because it leads to + // duplicate MotionEvents being recorded. + // if (ProductionFlag.USES_DEVELOPMENT_ONLY_DIAGNOSTICS) { + // ResearchLogger.mainKeyboardView_processMotionEvent( + // me, action, eventTime, i, pointerId, px, py); + // } } } else { final PointerTracker tracker = PointerTracker.getPointerTracker(id, this); diff --git a/java/src/com/android/inputmethod/keyboard/internal/GesturePreviewTrail.java b/java/src/com/android/inputmethod/keyboard/internal/GesturePreviewTrail.java index f682b518f..7fd1bedcb 100644 --- a/java/src/com/android/inputmethod/keyboard/internal/GesturePreviewTrail.java +++ b/java/src/com/android/inputmethod/keyboard/internal/GesturePreviewTrail.java @@ -51,6 +51,9 @@ final class GesturePreviewTrail { public final int mTrailColor; public final float mTrailStartWidth; public final float mTrailEndWidth; + public final float mTrailBodyRatio; + public boolean mTrailShadowEnabled; + public final float mTrailShadowRatio; public final int mFadeoutStartDelay; public final int mFadeoutDuration; public final int mUpdateInterval; @@ -64,6 +67,14 @@ final class GesturePreviewTrail { R.styleable.MainKeyboardView_gesturePreviewTrailStartWidth, 0.0f); mTrailEndWidth = mainKeyboardViewAttr.getDimension( R.styleable.MainKeyboardView_gesturePreviewTrailEndWidth, 0.0f); + final int PERCENTAGE_INT = 100; + mTrailBodyRatio = (float)mainKeyboardViewAttr.getInt( + R.styleable.MainKeyboardView_gesturePreviewTrailBodyRatio, PERCENTAGE_INT) + / (float)PERCENTAGE_INT; + final int trailShadowRatioInt = mainKeyboardViewAttr.getInt( + R.styleable.MainKeyboardView_gesturePreviewTrailShadowRatio, 0); + mTrailShadowEnabled = (trailShadowRatioInt > 0); + mTrailShadowRatio = (float)trailShadowRatioInt / (float)PERCENTAGE_INT; mFadeoutStartDelay = mainKeyboardViewAttr.getInt( R.styleable.MainKeyboardView_gesturePreviewTrailFadeoutStartDelay, 0); mFadeoutDuration = mainKeyboardViewAttr.getInt( @@ -97,7 +108,7 @@ final class GesturePreviewTrail { } private void addStrokeLocked(final GestureStrokeWithPreviewPoints stroke, final long downTime) { - final int trailSize = mEventTimes.getLength(); + final int trailSize = mEventTimes.getLength(); stroke.appendPreviewStroke(mEventTimes, mXCoordinates, mYCoordinates); if (mEventTimes.getLength() == trailSize) { return; @@ -219,14 +230,22 @@ final class GesturePreviewTrail { final float r2 = getWidth(elapsedTime, params) / 2.0f; // Draw trail line only when the current point isn't a down point. if (!isDownEventXCoord(xCoords[i])) { - final Path path = roundedLine.makePath(p1x, p1y, r1, p2x, p2y, r2); + final float body1 = r1 * params.mTrailBodyRatio; + final float body2 = r2 * params.mTrailBodyRatio; + final Path path = roundedLine.makePath(p1x, p1y, body1, p2x, p2y, body2); if (path != null) { + roundedLine.getBounds(mRoundedLineBounds); + if (params.mTrailShadowEnabled) { + final float shadow2 = r2 * params.mTrailShadowRatio; + paint.setShadowLayer(shadow2, 0.0f, 0.0f, params.mTrailColor); + final int shadowInset = -(int)Math.ceil(shadow2); + mRoundedLineBounds.inset(shadowInset, shadowInset); + } + // Take union for the bounds. + outBoundsRect.union(mRoundedLineBounds); final int alpha = getAlpha(elapsedTime, params); paint.setAlpha(alpha); canvas.drawPath(path, paint); - // Take union for the bounds. - roundedLine.getBounds(mRoundedLineBounds); - outBoundsRect.union(mRoundedLineBounds); } } p1x = p2x; @@ -242,14 +261,14 @@ final class GesturePreviewTrail { System.arraycopy(eventTimes, startIndex, eventTimes, 0, newSize); System.arraycopy(xCoords, startIndex, xCoords, 0, newSize); System.arraycopy(yCoords, startIndex, yCoords, 0, newSize); - // The start index of the last segment of the stroke - // {@link mLastInterpolatedDrawIndex} should also be updated because all array - // elements have just been shifted for compaction. - mLastInterpolatedDrawIndex = Math.max(mLastInterpolatedDrawIndex - startIndex, 0); } mEventTimes.setLength(newSize); mXCoordinates.setLength(newSize); mYCoordinates.setLength(newSize); + // The start index of the last segment of the stroke + // {@link mLastInterpolatedDrawIndex} should also be updated because all array + // elements have just been shifted for compaction or been zeroed. + mLastInterpolatedDrawIndex = Math.max(mLastInterpolatedDrawIndex - startIndex, 0); } return newSize > 0; } diff --git a/java/src/com/android/inputmethod/keyboard/internal/KeyboardState.java b/java/src/com/android/inputmethod/keyboard/internal/KeyboardState.java index 95d9ccb58..b1d499702 100644 --- a/java/src/com/android/inputmethod/keyboard/internal/KeyboardState.java +++ b/java/src/com/android/inputmethod/keyboard/internal/KeyboardState.java @@ -20,6 +20,7 @@ import android.text.TextUtils; import android.util.Log; import com.android.inputmethod.latin.Constants; +import com.android.inputmethod.latin.RecapitalizeStatus; /** * Keyboard state machine. @@ -29,7 +30,7 @@ import com.android.inputmethod.latin.Constants; * The input events are {@link #onLoadKeyboard()}, {@link #onSaveKeyboardState()}, * {@link #onPressKey(int, boolean, int)}, {@link #onReleaseKey(int, boolean)}, * {@link #onCodeInput(int, boolean, int)}, {@link #onCancelInput(boolean)}, - * {@link #onUpdateShiftState(int)}, {@link #onLongPressTimeout(int)}. + * {@link #onUpdateShiftState(int, int)}, {@link #onLongPressTimeout(int)}. * * The actions are {@link SwitchActions}'s methods. */ @@ -48,7 +49,7 @@ public final class KeyboardState { public void setSymbolsShiftedKeyboard(); /** - * Request to call back {@link KeyboardState#onUpdateShiftState(int)}. + * Request to call back {@link KeyboardState#onUpdateShiftState(int, int)}. */ public void requestUpdatingShiftState(); @@ -80,6 +81,7 @@ public final class KeyboardState { private boolean mIsSymbolShifted; private boolean mPrevMainKeyboardWasShiftLocked; private boolean mPrevSymbolsKeyboardWasShifted; + private int mRecapitalizeMode; // For handling long press. private boolean mLongPressShiftLockFired; @@ -110,6 +112,7 @@ public final class KeyboardState { public KeyboardState(final SwitchActions switchActions) { mSwitchActions = switchActions; + mRecapitalizeMode = RecapitalizeStatus.NOT_A_RECAPITALIZE_MODE; } public void onLoadKeyboard() { @@ -283,6 +286,7 @@ public final class KeyboardState { mSwitchActions.setAlphabetKeyboard(); mIsAlphabetMode = true; mIsSymbolShifted = false; + mRecapitalizeMode = RecapitalizeStatus.NOT_A_RECAPITALIZE_MODE; mSwitchState = SWITCH_STATE_ALPHA; mSwitchActions.requestUpdatingShiftState(); } @@ -386,11 +390,13 @@ public final class KeyboardState { } } - public void onUpdateShiftState(final int autoCaps) { + public void onUpdateShiftState(final int autoCaps, final int recapitalizeMode) { if (DEBUG_EVENT) { - Log.d(TAG, "onUpdateShiftState: autoCaps=" + autoCaps + " " + this); + Log.d(TAG, "onUpdateShiftState: autoCaps=" + autoCaps + ", recapitalizeMode=" + + recapitalizeMode + " " + this); } - updateAlphabetShiftState(autoCaps); + mRecapitalizeMode = recapitalizeMode; + updateAlphabetShiftState(autoCaps, recapitalizeMode); } // TODO: Remove this method. Come up with a more comprehensive way to reset the keyboard layout @@ -402,8 +408,28 @@ public final class KeyboardState { resetKeyboardStateToAlphabet(); } - private void updateAlphabetShiftState(final int autoCaps) { + private void updateShiftStateForRecapitalize(final int recapitalizeMode) { + switch (recapitalizeMode) { + case RecapitalizeStatus.CAPS_MODE_ALL_UPPER: + setShifted(SHIFT_LOCK_SHIFTED); + break; + case RecapitalizeStatus.CAPS_MODE_FIRST_WORD_UPPER: + setShifted(AUTOMATIC_SHIFT); + break; + case RecapitalizeStatus.CAPS_MODE_ALL_LOWER: + case RecapitalizeStatus.CAPS_MODE_ORIGINAL_MIXED_CASE: + default: + setShifted(UNSHIFT); + } + } + + private void updateAlphabetShiftState(final int autoCaps, final int recapitalizeMode) { if (!mIsAlphabetMode) return; + if (RecapitalizeStatus.NOT_A_RECAPITALIZE_MODE != recapitalizeMode) { + // We are recapitalizing. Match the keyboard to the current recapitalize state. + updateShiftStateForRecapitalize(recapitalizeMode); + return; + } if (!mShiftKeyState.isReleasing()) { // Ignore update shift state event while the shift key is being pressed (including // chording). @@ -421,6 +447,9 @@ public final class KeyboardState { private void onPressShift() { mLongPressShiftLockFired = false; + // If we are recapitalizing, we don't do any of the normal processing, including + // importantly the double tap timer. + if (RecapitalizeStatus.NOT_A_RECAPITALIZE_MODE != mRecapitalizeMode) return; if (mIsAlphabetMode) { mIsInDoubleTapShiftKey = mSwitchActions.isInDoubleTapTimeout(); if (!mIsInDoubleTapShiftKey) { @@ -467,7 +496,11 @@ public final class KeyboardState { } private void onReleaseShift(final boolean withSliding) { - if (mIsAlphabetMode) { + if (RecapitalizeStatus.NOT_A_RECAPITALIZE_MODE != mRecapitalizeMode) { + // We are recapitalizing. We should match the keyboard state to the recapitalize + // state in priority. + updateShiftStateForRecapitalize(mRecapitalizeMode); + } else if (mIsAlphabetMode) { final boolean isShiftLocked = mAlphabetShiftState.isShiftLocked(); mIsInAlphabetUnshiftedFromShifted = false; if (mIsInDoubleTapShiftKey) { @@ -597,7 +630,7 @@ public final class KeyboardState { // If the code is a letter, update keyboard shift state. if (Constants.isLetterCode(code)) { - updateAlphabetShiftState(autoCaps); + updateAlphabetShiftState(autoCaps, RecapitalizeStatus.NOT_A_RECAPITALIZE_MODE); } } diff --git a/java/src/com/android/inputmethod/keyboard/internal/SlidingKeyInputPreview.java b/java/src/com/android/inputmethod/keyboard/internal/SlidingKeyInputPreview.java index 37f4e3582..33dbbafa3 100644 --- a/java/src/com/android/inputmethod/keyboard/internal/SlidingKeyInputPreview.java +++ b/java/src/com/android/inputmethod/keyboard/internal/SlidingKeyInputPreview.java @@ -30,7 +30,7 @@ import com.android.inputmethod.latin.R; * Draw rubber band preview graphics during sliding key input. */ public final class SlidingKeyInputPreview extends AbstractDrawingPreview { - private final int mPreviewWidth; + private final float mPreviewBodyRadius; private boolean mShowSlidingKeyInputPreview; private final int[] mPreviewFrom = CoordinateUtils.newInstance(); @@ -44,8 +44,20 @@ public final class SlidingKeyInputPreview extends AbstractDrawingPreview { super(drawingView); final int previewColor = mainKeyboardViewAttr.getColor( R.styleable.MainKeyboardView_slidingKeyInputPreviewColor, 0); - mPreviewWidth = mainKeyboardViewAttr.getDimensionPixelSize( - R.styleable.MainKeyboardView_slidingKeyInputPreviewWidth, 0); + final float previewRadius = mainKeyboardViewAttr.getDimension( + R.styleable.MainKeyboardView_slidingKeyInputPreviewWidth, 0) / 2.0f; + final int PERCENTAGE_INT = 100; + final float previewBodyRatio = (float)mainKeyboardViewAttr.getInt( + R.styleable.MainKeyboardView_slidingKeyInputPreviewBodyRatio, PERCENTAGE_INT) + / (float)PERCENTAGE_INT; + mPreviewBodyRadius = previewRadius * previewBodyRatio; + final int previewShadowRatioInt = mainKeyboardViewAttr.getInt( + R.styleable.MainKeyboardView_slidingKeyInputPreviewShadowRatio, 0); + if (previewShadowRatioInt > 0) { + final float previewShadowRatio = (float)previewShadowRatioInt / (float)PERCENTAGE_INT; + final float shadowRadius = previewRadius * previewShadowRatio; + mPaint.setShadowLayer(shadowRadius, 0.0f, 0.0f, previewColor); + } mPaint.setColor(previewColor); } @@ -65,7 +77,7 @@ public final class SlidingKeyInputPreview extends AbstractDrawingPreview { } // TODO: Finalize the rubber band preview implementation. - final int radius = mPreviewWidth / 2; + final float radius = mPreviewBodyRadius; final Path path = mRoundedLine.makePath( CoordinateUtils.x(mPreviewFrom), CoordinateUtils.y(mPreviewFrom), radius, CoordinateUtils.x(mPreviewTo), CoordinateUtils.y(mPreviewTo), radius); diff --git a/java/src/com/android/inputmethod/latin/Constants.java b/java/src/com/android/inputmethod/latin/Constants.java index 50e50233e..86bb25562 100644 --- a/java/src/com/android/inputmethod/latin/Constants.java +++ b/java/src/com/android/inputmethod/latin/Constants.java @@ -160,6 +160,8 @@ public final class Constants { public static final int CODE_DOUBLE_QUOTE = '"'; public static final int CODE_QUESTION_MARK = '?'; public static final int CODE_EXCLAMATION_MARK = '!'; + public static final int CODE_SLASH = '/'; + public static final int CODE_COMMERCIAL_AT = '@'; // TODO: Check how this should work for right-to-left languages. It seems to stand // that for rtl languages, a closing parenthesis is a left parenthesis. Is this // managed by the font? Or is it a different char? diff --git a/java/src/com/android/inputmethod/latin/ContactsBinaryDictionary.java b/java/src/com/android/inputmethod/latin/ContactsBinaryDictionary.java index 22d189987..75c2cf2c8 100644 --- a/java/src/com/android/inputmethod/latin/ContactsBinaryDictionary.java +++ b/java/src/com/android/inputmethod/latin/ContactsBinaryDictionary.java @@ -252,7 +252,7 @@ public class ContactsBinaryDictionary extends ExpandableBinaryDictionary { } private static boolean isValidName(final String name) { - if (name != null && -1 == name.indexOf('@')) { + if (name != null && -1 == name.indexOf(Constants.CODE_COMMERCIAL_AT)) { return true; } return false; diff --git a/java/src/com/android/inputmethod/latin/InputTypeUtils.java b/java/src/com/android/inputmethod/latin/InputTypeUtils.java index ecb20144b..46194f6e4 100644 --- a/java/src/com/android/inputmethod/latin/InputTypeUtils.java +++ b/java/src/com/android/inputmethod/latin/InputTypeUtils.java @@ -33,7 +33,6 @@ public final class InputTypeUtils implements InputType { private static final int[] SUPPRESSING_AUTO_SPACES_FIELD_VARIATION = { InputType.TYPE_TEXT_VARIATION_EMAIL_ADDRESS, InputType.TYPE_TEXT_VARIATION_PASSWORD, - InputType.TYPE_TEXT_VARIATION_URI, InputType.TYPE_TEXT_VARIATION_VISIBLE_PASSWORD, InputType.TYPE_TEXT_VARIATION_WEB_PASSWORD }; public static final int IME_ACTION_CUSTOM_LABEL = EditorInfo.IME_MASK_ACTION + 1; diff --git a/java/src/com/android/inputmethod/latin/LatinIME.java b/java/src/com/android/inputmethod/latin/LatinIME.java index bf4c22d23..0e1c4dc31 100644 --- a/java/src/com/android/inputmethod/latin/LatinIME.java +++ b/java/src/com/android/inputmethod/latin/LatinIME.java @@ -161,7 +161,7 @@ public final class LatinIME extends InputMethodService implements KeyboardAction mPositionalInfoForUserDictPendingAddition = null; private final WordComposer mWordComposer = new WordComposer(); private final RichInputConnection mConnection = new RichInputConnection(this); - private RecapitalizeStatus mRecapitalizeStatus = null; + private final RecapitalizeStatus mRecapitalizeStatus = new RecapitalizeStatus(); // Keep track of the last selection range to decide if we need to show word alternatives private static final int NOT_A_CURSOR_POSITION = -1; @@ -742,6 +742,7 @@ public final class LatinIME extends InputMethodService implements KeyboardAction resetComposingState(true /* alsoResetLastComposedWord */); mDeleteCount = 0; mSpaceState = SPACE_STATE_NONE; + mRecapitalizeStatus.deactivate(); mCurrentlyPressedHardwareKeys.clear(); if (mSuggestionStripView != null) { @@ -925,7 +926,7 @@ public final class LatinIME extends InputMethodService implements KeyboardAction // We moved the cursor. If we are touching a word, we need to resume suggestion. mHandler.postResumeSuggestions(); // Reset the last recapitalization. - mRecapitalizeStatus = null; + mRecapitalizeStatus.deactivate(); mKeyboardSwitcher.updateShiftState(); } mExpectingUpdateSelection = false; @@ -995,8 +996,6 @@ public final class LatinIME extends InputMethodService implements KeyboardAction } } if (!mSettings.getCurrent().isApplicationSpecifiedCompletionsOn()) return; - mApplicationSpecifiedCompletions = - CompletionInfoUtils.removeNulls(applicationSpecifiedCompletions); if (applicationSpecifiedCompletions == null) { clearSuggestionStrip(); if (ProductionFlag.USES_DEVELOPMENT_ONLY_DIAGNOSTICS) { @@ -1004,6 +1003,8 @@ public final class LatinIME extends InputMethodService implements KeyboardAction } return; } + mApplicationSpecifiedCompletions = + CompletionInfoUtils.removeNulls(applicationSpecifiedCompletions); final ArrayList<SuggestedWords.SuggestedWordInfo> applicationSuggestedWords = SuggestedWords.getFromApplicationSpecifiedCompletions( @@ -1179,6 +1180,15 @@ public final class LatinIME extends InputMethodService implements KeyboardAction SPACE_STATE_PHANTOM == mSpaceState); } + public int getCurrentRecapitalizeState() { + if (!mRecapitalizeStatus.isActive() + || !mRecapitalizeStatus.isSetAt(mLastSelectionStart, mLastSelectionEnd)) { + // Not recapitalizing at the moment + return RecapitalizeStatus.NOT_A_RECAPITALIZE_MODE; + } + return mRecapitalizeStatus.getCurrentMode(); + } + // Factor in auto-caps and manual caps and compute the current caps mode. private int getActualCapsMode() { final int keyboardShiftMode = mKeyboardSwitcher.getKeyboardShiftMode(); @@ -1391,7 +1401,12 @@ public final class LatinIME extends InputMethodService implements KeyboardAction case Constants.CODE_SHIFT: // Note: calling back to the keyboard on Shift key is handled in onPressKey() // and onReleaseKey(). - handleRecapitalize(); + final Keyboard currentKeyboard = switcher.getKeyboard(); + if (null != currentKeyboard && currentKeyboard.mId.isAlphabetKeyboard()) { + // TODO: Instead of checking for alphabetic keyboard here, separate keycodes for + // alphabetic shift and shift while in symbol layout. + handleRecapitalize(); + } break; case Constants.CODE_SWITCH_ALPHA_SYMBOL: // Note: calling back to the keyboard on symbol key is handled in onPressKey() @@ -1953,10 +1968,9 @@ public final class LatinIME extends InputMethodService implements KeyboardAction private void handleRecapitalize() { if (mLastSelectionStart == mLastSelectionEnd) return; // No selection // If we have a recapitalize in progress, use it; otherwise, create a new one. - if (null == mRecapitalizeStatus + if (!mRecapitalizeStatus.isActive() || !mRecapitalizeStatus.isSetAt(mLastSelectionStart, mLastSelectionEnd)) { - mRecapitalizeStatus = - new RecapitalizeStatus(mLastSelectionStart, mLastSelectionEnd, + mRecapitalizeStatus.initialize(mLastSelectionStart, mLastSelectionEnd, mConnection.getSelectedText(0 /* flags, 0 for no styles */).toString(), mSettings.getCurrentLocale(), mSettings.getWordSeparators()); // We trim leading and trailing whitespace. @@ -1979,6 +1993,8 @@ public final class LatinIME extends InputMethodService implements KeyboardAction mLastSelectionStart = mRecapitalizeStatus.getNewCursorStart(); mLastSelectionEnd = mRecapitalizeStatus.getNewCursorEnd(); mConnection.setSelection(mLastSelectionStart, mLastSelectionEnd); + // Match the keyboard to the new state. + mKeyboardSwitcher.updateShiftState(); } // Returns true if we did an autocorrection, false otherwise. @@ -2413,6 +2429,7 @@ public final class LatinIME extends InputMethodService implements KeyboardAction if (!mConnection.isCursorTouchingWord(mSettings.getCurrent())) return; final Range range = mConnection.getWordRangeAtCursor(mSettings.getWordSeparators(), 0 /* additionalPrecedingWordsCount */); + if (null == range) return; // Happens if we don't have an input connection at all final ArrayList<SuggestedWordInfo> suggestions = CollectionUtils.newArrayList(); final String typedWord = range.mWord.toString(); if (range.mWord instanceof SpannableString) { @@ -2542,7 +2559,8 @@ public final class LatinIME extends InputMethodService implements KeyboardAction // This essentially inserts a space, and that's it. public void promotePhantomSpace() { - if (mSettings.getCurrent().shouldInsertSpacesAutomatically()) { + if (mSettings.getCurrent().shouldInsertSpacesAutomatically() + && !mConnection.textBeforeCursorLooksLikeURL()) { sendKeyCodePoint(Constants.CODE_SPACE); if (ProductionFlag.USES_DEVELOPMENT_ONLY_DIAGNOSTICS) { ResearchLogger.latinIME_promotePhantomSpace(); diff --git a/java/src/com/android/inputmethod/latin/RecapitalizeStatus.java b/java/src/com/android/inputmethod/latin/RecapitalizeStatus.java index 9edd3a160..8a704ab42 100644 --- a/java/src/com/android/inputmethod/latin/RecapitalizeStatus.java +++ b/java/src/com/android/inputmethod/latin/RecapitalizeStatus.java @@ -24,6 +24,7 @@ import java.util.Locale; * The status of the current recapitalize process. */ public class RecapitalizeStatus { + public static final int NOT_A_RECAPITALIZE_MODE = -1; public static final int CAPS_MODE_ORIGINAL_MIXED_CASE = 0; public static final int CAPS_MODE_ALL_LOWER = 1; public static final int CAPS_MODE_FIRST_WORD_UPPER = 2; @@ -37,6 +38,7 @@ public class RecapitalizeStatus { CAPS_MODE_FIRST_WORD_UPPER, CAPS_MODE_ALL_UPPER }; + private static final int getStringMode(final String string, final String separators) { if (StringUtils.isIdenticalAfterUpcase(string)) { return CAPS_MODE_ALL_UPPER; @@ -50,24 +52,29 @@ public class RecapitalizeStatus { } /** - * We store the location of the cursor and the string that was there before the undoable + * We store the location of the cursor and the string that was there before the recapitalize * action was done, and the location of the cursor and the string that was there after. */ private int mCursorStartBefore; - private int mCursorEndBefore; private String mStringBefore; private int mCursorStartAfter; private int mCursorEndAfter; private int mRotationStyleCurrentIndex; - private final boolean mSkipOriginalMixedCaseMode; - private final Locale mLocale; - private final String mSeparators; + private boolean mSkipOriginalMixedCaseMode; + private Locale mLocale; + private String mSeparators; private String mStringAfter; + private boolean mIsActive; + + public RecapitalizeStatus() { + // By default, initialize with dummy values that won't match any real recapitalize. + initialize(-1, -1, "", Locale.getDefault(), ""); + deactivate(); + } - public RecapitalizeStatus(final int cursorStart, final int cursorEnd, final String string, + public void initialize(final int cursorStart, final int cursorEnd, final String string, final Locale locale, final String separators) { mCursorStartBefore = cursorStart; - mCursorEndBefore = cursorEnd; mStringBefore = string; mCursorStartAfter = cursorStart; mCursorEndAfter = cursorEnd; @@ -89,6 +96,15 @@ public class RecapitalizeStatus { mRotationStyleCurrentIndex = currentMode; mSkipOriginalMixedCaseMode = true; } + mIsActive = true; + } + + public void deactivate() { + mIsActive = false; + } + + public boolean isActive() { + return mIsActive; } public boolean isSetAt(final int cursorStart, final int cursorEnd) { @@ -110,23 +126,23 @@ public class RecapitalizeStatus { } ++count; switch (ROTATION_STYLE[mRotationStyleCurrentIndex]) { - case CAPS_MODE_ORIGINAL_MIXED_CASE: - mStringAfter = mStringBefore; - break; - case CAPS_MODE_ALL_LOWER: - mStringAfter = mStringBefore.toLowerCase(mLocale); - break; - case CAPS_MODE_FIRST_WORD_UPPER: - mStringAfter = StringUtils.capitalizeEachWord(mStringBefore, mSeparators, - mLocale); - break; - case CAPS_MODE_ALL_UPPER: - mStringAfter = mStringBefore.toUpperCase(mLocale); - break; - default: - mStringAfter = mStringBefore; + case CAPS_MODE_ORIGINAL_MIXED_CASE: + mStringAfter = mStringBefore; + break; + case CAPS_MODE_ALL_LOWER: + mStringAfter = mStringBefore.toLowerCase(mLocale); + break; + case CAPS_MODE_FIRST_WORD_UPPER: + mStringAfter = StringUtils.capitalizeEachWord(mStringBefore, mSeparators, + mLocale); + break; + case CAPS_MODE_ALL_UPPER: + mStringAfter = mStringBefore.toUpperCase(mLocale); + break; + default: + mStringAfter = mStringBefore; } - } while (mStringAfter.equals(oldResult) && count < 5); + } while (mStringAfter.equals(oldResult) && count < ROTATION_STYLE.length + 1); mCursorEndAfter = mCursorStartAfter + mStringAfter.length(); } @@ -148,7 +164,7 @@ public class RecapitalizeStatus { if (!Character.isWhitespace(codePoint)) break; } if (0 != nonWhitespaceStart || len != nonWhitespaceEnd) { - mCursorEndBefore = mCursorEndAfter = mCursorStartBefore + nonWhitespaceEnd; + mCursorEndAfter = mCursorStartBefore + nonWhitespaceEnd; mCursorStartBefore = mCursorStartAfter = mCursorStartBefore + nonWhitespaceStart; mStringAfter = mStringBefore = mStringBefore.substring(nonWhitespaceStart, nonWhitespaceEnd); @@ -166,4 +182,8 @@ public class RecapitalizeStatus { public int getNewCursorEnd() { return mCursorEndAfter; } + + public int getCurrentMode() { + return ROTATION_STYLE[mRotationStyleCurrentIndex]; + } } diff --git a/java/src/com/android/inputmethod/latin/RichInputConnection.java b/java/src/com/android/inputmethod/latin/RichInputConnection.java index e17846618..8ed7ab264 100644 --- a/java/src/com/android/inputmethod/latin/RichInputConnection.java +++ b/java/src/com/android/inputmethod/latin/RichInputConnection.java @@ -19,7 +19,6 @@ package com.android.inputmethod.latin; import android.inputmethodservice.InputMethodService; import android.text.SpannableString; import android.text.TextUtils; -import android.text.style.SuggestionSpan; import android.util.Log; import android.view.KeyEvent; import android.view.inputmethod.CompletionInfo; @@ -721,4 +720,15 @@ public final class RichInputConnection { // position and the expected position, then it must be a belated update. return (newSelStart - oldSelStart) * (mCurrentCursorPosition - newSelStart) >= 0; } + + /** + * Looks at the text just before the cursor to find out if it looks like a URL. + * + * The weakest point here is, if we don't have enough text bufferized, we may fail to realize + * we are in URL situation, but other places in this class have the same limitation and it + * does not matter too much in the practice. + */ + public boolean textBeforeCursorLooksLikeURL() { + return StringUtils.lastPartLooksLikeURL(mCommittedTextBeforeComposingText); + } } diff --git a/java/src/com/android/inputmethod/latin/SettingsFragment.java b/java/src/com/android/inputmethod/latin/SettingsFragment.java index a96c997c8..79036c276 100644 --- a/java/src/com/android/inputmethod/latin/SettingsFragment.java +++ b/java/src/com/android/inputmethod/latin/SettingsFragment.java @@ -77,10 +77,13 @@ public final class SettingsFragment extends InputMethodSettingsFragment final Resources res = getResources(); final Context context = getActivity(); - // When we are called from the Settings application but we are not already running, the - // {@link SubtypeLocale} class may not have been initialized. It is safe to call - // {@link SubtypeLocale#init(Context)} multiple times. + // When we are called from the Settings application but we are not already running, some + // singleton and utility classes may not have been initialized. We have to call + // initialization method of these classes here. See {@link LatinIME#onCreate()}. + SubtypeSwitcher.init(context); SubtypeLocale.init(context); + AudioAndHapticFeedbackManager.init(context); + mVoicePreference = (ListPreference) findPreference(Settings.PREF_VOICE_MODE); mShowCorrectionSuggestionsPreference = (ListPreference) findPreference(Settings.PREF_SHOW_SUGGESTIONS_SETTING); diff --git a/java/src/com/android/inputmethod/latin/StringUtils.java b/java/src/com/android/inputmethod/latin/StringUtils.java index 7f1e7c619..d5ee58a63 100644 --- a/java/src/com/android/inputmethod/latin/StringUtils.java +++ b/java/src/com/android/inputmethod/latin/StringUtils.java @@ -282,4 +282,69 @@ public final class StringUtils { } return builder.toString(); } + + /** + * Approximates whether the text before the cursor looks like a URL. + * + * This is not foolproof, but it should work well in the practice. + * Essentially it walks backward from the cursor until it finds something that's not a letter, + * digit, or common URL symbol like underscore. If it hasn't found a period yet, then it + * does not look like a URL. + * If the text: + * - starts with www and contains a period + * - starts with a slash preceded by either a slash, whitespace, or start-of-string + * Then it looks like a URL and we return true. Otherwise, we return false. + * + * Note: this method is called quite often, and should be fast. + * + * TODO: This will return that "abc./def" and ".abc/def" look like URLs to keep down the + * code complexity, but ideally it should not. It's acceptable for now. + */ + public static boolean lastPartLooksLikeURL(final CharSequence text) { + int i = text.length(); + if (0 == i) return false; + int wCount = 0; + int slashCount = 0; + boolean hasSlash = false; + boolean hasPeriod = false; + int codePoint = 0; + while (i > 0) { + codePoint = Character.codePointBefore(text, i); + if (codePoint < Constants.CODE_PERIOD || codePoint > 'z') { + // Handwavy heuristic to see if that's a URL character. Anything between period + // and z. This includes all lower- and upper-case ascii letters, period, + // underscore, arrobase, question mark, equal sign. It excludes spaces, exclamation + // marks, double quotes... + // Anything that's not a URL-like character causes us to break from here and + // evaluate normally. + break; + } + if (Constants.CODE_PERIOD == codePoint) { + hasPeriod = true; + } + if (Constants.CODE_SLASH == codePoint) { + hasSlash = true; + if (2 == ++slashCount) { + return true; + } + } else { + slashCount = 0; + } + if ('w' == codePoint) { + ++wCount; + } else { + wCount = 0; + } + i = Character.offsetByCodePoints(text, i, -1); + } + // End of the text run. + // If it starts with www and includes a period, then it looks like a URL. + if (wCount >= 3 && hasPeriod) return true; + // If it starts with a slash, and the code point before is whitespace, it looks like an URL. + if (1 == slashCount && (0 == i || Character.isWhitespace(codePoint))) return true; + // If it has both a period and a slash, it looks like an URL. + if (hasPeriod && hasSlash) return true; + // Otherwise, it doesn't look like an URL. + return false; + } } diff --git a/java/src/com/android/inputmethod/latin/SubtypeSwitcher.java b/java/src/com/android/inputmethod/latin/SubtypeSwitcher.java index 2f9e34ff1..bef8a3cf1 100644 --- a/java/src/com/android/inputmethod/latin/SubtypeSwitcher.java +++ b/java/src/com/android/inputmethod/latin/SubtypeSwitcher.java @@ -80,6 +80,7 @@ public final class SubtypeSwitcher { public static void init(final Context context) { SubtypeLocale.init(context); + RichInputMethodManager.init(context); sInstance.initialize(context); } @@ -87,10 +88,13 @@ public final class SubtypeSwitcher { // Intentional empty constructor for singleton. } - private void initialize(final Context service) { - mResources = service.getResources(); + private void initialize(final Context context) { + if (mResources != null) { + return; + } + mResources = context.getResources(); mRichImm = RichInputMethodManager.getInstance(); - mConnectivityManager = (ConnectivityManager) service.getSystemService( + mConnectivityManager = (ConnectivityManager) context.getSystemService( Context.CONNECTIVITY_SERVICE); mNoLanguageSubtype = mRichImm.findSubtypeByLocaleAndKeyboardLayoutSet( SubtypeLocale.NO_LANGUAGE, SubtypeLocale.QWERTY); diff --git a/java/src/com/android/inputmethod/latin/setup/SetupActivity.java b/java/src/com/android/inputmethod/latin/setup/SetupActivity.java index 15d0bac37..099169aa9 100644 --- a/java/src/com/android/inputmethod/latin/setup/SetupActivity.java +++ b/java/src/com/android/inputmethod/latin/setup/SetupActivity.java @@ -20,7 +20,6 @@ import android.app.Activity; import android.content.Context; import android.content.Intent; import android.content.res.Resources; -import android.graphics.PorterDuff; import android.graphics.drawable.Drawable; import android.os.Bundle; import android.os.Message; @@ -113,13 +112,13 @@ public final class SetupActivity extends Activity { // the SDK version. final TextView titleView = (TextView)findViewById(R.id.setup_title); final int appName = getApplicationInfo().labelRes; - titleView.setText(getString(R.string.setup_title, getString(appName))); + titleView.setText(getString(R.string.setup_steps_title, getString(appName))); mStepIndicatorView = (SetupStepIndicatorView)findViewById(R.id.setup_step_indicator); final SetupStep step1 = new SetupStep(findViewById(R.id.setup_step1), appName, R.string.setup_step1_title, R.string.setup_step1_instruction, - R.drawable.ic_settings_language, R.string.language_settings); + R.drawable.ic_setup_step1, R.string.setup_step1_action); step1.setAction(new Runnable() { @Override public void run() { @@ -131,7 +130,7 @@ public final class SetupActivity extends Activity { final SetupStep step2 = new SetupStep(findViewById(R.id.setup_step2), appName, R.string.setup_step2_title, R.string.setup_step2_instruction, - 0 /* actionIcon */, R.string.select_input_method); + R.drawable.ic_setup_step2, R.string.setup_step2_action); step2.setAction(new Runnable() { @Override public void run() { @@ -143,8 +142,8 @@ public final class SetupActivity extends Activity { mSetupSteps.addStep(STEP_2, step2); final SetupStep step3 = new SetupStep(findViewById(R.id.setup_step3), - appName, R.string.setup_step3_title, 0 /* instruction */, - R.drawable.sym_keyboard_language_switch, R.string.setup_step3_instruction); + appName, R.string.setup_step3_title, R.string.setup_step3_instruction, + R.drawable.ic_setup_step3, R.string.setup_step3_action); step3.setAction(new Runnable() { @Override public void run() { @@ -314,9 +313,7 @@ public final class SetupActivity extends Activity { final int paddingEnd = ViewCompatUtils.getPaddingEnd(mActionLabel); ViewCompatUtils.setPaddingRelative(mActionLabel, paddingEnd, 0, paddingEnd, 0); } else { - final int overrideColor = res.getColor(R.color.setup_text_action); final Drawable icon = res.getDrawable(actionIcon); - icon.setColorFilter(overrideColor, PorterDuff.Mode.MULTIPLY); icon.setBounds(0, 0, icon.getIntrinsicWidth(), icon.getIntrinsicHeight()); TextViewCompatUtils.setCompoundDrawablesRelative( mActionLabel, icon, null, null, null); diff --git a/java/src/com/android/inputmethod/latin/spellcheck/AndroidWordLevelSpellCheckerSession.java b/java/src/com/android/inputmethod/latin/spellcheck/AndroidWordLevelSpellCheckerSession.java index 96b2c818d..da8657201 100644 --- a/java/src/com/android/inputmethod/latin/spellcheck/AndroidWordLevelSpellCheckerSession.java +++ b/java/src/com/android/inputmethod/latin/spellcheck/AndroidWordLevelSpellCheckerSession.java @@ -189,10 +189,12 @@ public abstract class AndroidWordLevelSpellCheckerSession extends Session { int letterCount = 0; for (int i = 0; i < length; i = text.offsetByCodePoints(i, 1)) { final int codePoint = text.codePointAt(i); - // Any word containing a '@' is probably an e-mail address - // Any word containing a '/' is probably either an ad-hoc combination of two + // Any word containing a COMMERCIAL_AT is probably an e-mail address + // Any word containing a SLASH is probably either an ad-hoc combination of two // words or a URI - in either case we don't want to spell check that - if ('@' == codePoint || '/' == codePoint) return true; + if (Constants.CODE_COMMERCIAL_AT == codePoint || Constants.CODE_SLASH == codePoint) { + return true; + } if (isLetterCheckableByLanguage(codePoint, script)) ++letterCount; } // Guestimate heuristic: perform spell checking if at least 3/4 of the characters diff --git a/java/src/com/android/inputmethod/research/FeedbackFragment.java b/java/src/com/android/inputmethod/research/FeedbackFragment.java index 39f9c87a0..a0738292e 100644 --- a/java/src/com/android/inputmethod/research/FeedbackFragment.java +++ b/java/src/com/android/inputmethod/research/FeedbackFragment.java @@ -65,12 +65,10 @@ public class FeedbackFragment extends Fragment implements OnClickListener { mCancelButton.setOnClickListener(this); if (savedInstanceState != null) { - Log.d(TAG, "restoring from savedInstanceState"); restoreState(savedInstanceState); } else { final Bundle bundle = getActivity().getIntent().getExtras(); if (bundle != null) { - Log.d(TAG, "restoring from getArguments()"); restoreState(bundle); } } @@ -81,10 +79,7 @@ public class FeedbackFragment extends Fragment implements OnClickListener { public void onClick(final View view) { final ResearchLogger researchLogger = ResearchLogger.getInstance(); if (view == mIncludingUserRecordingCheckBox) { - if (hasUserRecording()) { - // Remove the recording - setHasUserRecording(false); - } else { + if (mIncludingUserRecordingCheckBox.isChecked()) { final Bundle bundle = new Bundle(); onSaveInstanceState(bundle); @@ -103,9 +98,9 @@ public class FeedbackFragment extends Fragment implements OnClickListener { R.string.research_feedback_empty_feedback_error_message, Toast.LENGTH_LONG).show(); } else { - final boolean isIncludingAccountName = isIncludingAccountName(); - researchLogger.sendFeedback(feedbackContents, - false /* isIncludingHistory */, isIncludingAccountName, hasUserRecording()); + final boolean isIncludingAccountName = mIncludingAccountNameCheckBox.isChecked(); + researchLogger.sendFeedback(feedbackContents, false /* isIncludingHistory */, + isIncludingAccountName, mIncludingUserRecordingCheckBox.isChecked()); getActivity().finish(); researchLogger.setFeedbackDialogBundle(null); researchLogger.onLeavingSendFeedbackDialog(); @@ -125,29 +120,13 @@ public class FeedbackFragment extends Fragment implements OnClickListener { final String savedFeedbackString = mEditText.getText().toString(); bundle.putString(KEY_FEEDBACK_STRING, savedFeedbackString); - bundle.putBoolean(KEY_INCLUDE_ACCOUNT_NAME, isIncludingAccountName()); - bundle.putBoolean(KEY_HAS_USER_RECORDING, hasUserRecording()); + bundle.putBoolean(KEY_INCLUDE_ACCOUNT_NAME, mIncludingAccountNameCheckBox.isChecked()); + bundle.putBoolean(KEY_HAS_USER_RECORDING, mIncludingUserRecordingCheckBox.isChecked()); } - public void restoreState(final Bundle bundle) { + private void restoreState(final Bundle bundle) { mEditText.setText(bundle.getString(KEY_FEEDBACK_STRING)); - setIsIncludingAccountName(bundle.getBoolean(KEY_INCLUDE_ACCOUNT_NAME)); - setHasUserRecording(bundle.getBoolean(KEY_HAS_USER_RECORDING)); - } - - private boolean hasUserRecording() { - return mIncludingUserRecordingCheckBox.isChecked(); - } - - private void setHasUserRecording(final boolean hasRecording) { - mIncludingUserRecordingCheckBox.setChecked(hasRecording); - } - - private boolean isIncludingAccountName() { - return mIncludingAccountNameCheckBox.isChecked(); - } - - private void setIsIncludingAccountName(final boolean isIncludingAccountName) { - mIncludingAccountNameCheckBox.setChecked(isIncludingAccountName); + mIncludingAccountNameCheckBox.setChecked(bundle.getBoolean(KEY_INCLUDE_ACCOUNT_NAME)); + mIncludingUserRecordingCheckBox.setChecked(bundle.getBoolean(KEY_HAS_USER_RECORDING)); } } diff --git a/java/src/com/android/inputmethod/research/ResearchLog.java b/java/src/com/android/inputmethod/research/ResearchLog.java index 35a491f2c..18bf7ba54 100644 --- a/java/src/com/android/inputmethod/research/ResearchLog.java +++ b/java/src/com/android/inputmethod/research/ResearchLog.java @@ -108,10 +108,14 @@ public class ResearchLog { @Override public Object call() throws Exception { try { - if (mHasWrittenData) { - mJsonWriter.endArray(); - mHasWrittenData = false; + // TODO: This is necessary to avoid an exception. Better would be to not even + // open the JsonWriter if the file is not even opened unless there is valid data + // to write. + if (!mHasWrittenData) { + mJsonWriter.beginArray(); } + mJsonWriter.endArray(); + mHasWrittenData = false; mJsonWriter.flush(); mJsonWriter.close(); if (DEBUG) { @@ -159,6 +163,12 @@ public class ResearchLog { public Object call() throws Exception { try { if (mHasWrittenData) { + // TODO: This is necessary to avoid an exception. Better would be to not + // even open the JsonWriter if the file is not even opened unless there is + // valid data to write. + if (!mHasWrittenData) { + mJsonWriter.beginArray(); + } mJsonWriter.endArray(); mJsonWriter.close(); mHasWrittenData = false; diff --git a/java/src/com/android/inputmethod/research/ResearchLogDirectory.java b/java/src/com/android/inputmethod/research/ResearchLogDirectory.java index 291dea5d0..d156068d6 100644 --- a/java/src/com/android/inputmethod/research/ResearchLogDirectory.java +++ b/java/src/com/android/inputmethod/research/ResearchLogDirectory.java @@ -97,15 +97,17 @@ public class ResearchLogDirectory { } } - public File getLogFilePath(final long time) { - return new File(mFilesDir, getUniqueFilename(LOG_FILENAME_PREFIX, time)); + public File getLogFilePath(final long time, final long nanoTime) { + return new File(mFilesDir, getUniqueFilename(LOG_FILENAME_PREFIX, time, nanoTime)); } - public File getUserRecordingFilePath(final long time) { - return new File(mFilesDir, getUniqueFilename(USER_RECORDING_FILENAME_PREFIX, time)); + public File getUserRecordingFilePath(final long time, final long nanoTime) { + return new File(mFilesDir, getUniqueFilename(USER_RECORDING_FILENAME_PREFIX, time, + nanoTime)); } - private static String getUniqueFilename(final String prefix, final long time) { - return prefix + "-" + time + FILENAME_SUFFIX; + private static String getUniqueFilename(final String prefix, final long time, + final long nanoTime) { + return prefix + "-" + time + "-" + nanoTime + FILENAME_SUFFIX; } } diff --git a/java/src/com/android/inputmethod/research/ResearchLogger.java b/java/src/com/android/inputmethod/research/ResearchLogger.java index 7a23ddb05..cd18e3de6 100644 --- a/java/src/com/android/inputmethod/research/ResearchLogger.java +++ b/java/src/com/android/inputmethod/research/ResearchLogger.java @@ -389,7 +389,7 @@ public class ResearchLogger implements SharedPreferences.OnSharedPreferenceChang } if (mMainLogBuffer == null) { mMainResearchLog = new ResearchLog(mResearchLogDirectory.getLogFilePath( - System.currentTimeMillis()), mLatinIME); + System.currentTimeMillis(), System.nanoTime()), mLatinIME); final int numWordsToIgnore = new Random().nextInt(NUMBER_OF_WORDS_BETWEEN_SAMPLES + 1); mMainLogBuffer = new MainLogBuffer(NUMBER_OF_WORDS_BETWEEN_SAMPLES, numWordsToIgnore, mSuggest) { @@ -420,7 +420,7 @@ public class ResearchLogger implements SharedPreferences.OnSharedPreferenceChang private void resetFeedbackLogging() { mFeedbackLog = new ResearchLog(mResearchLogDirectory.getLogFilePath( - System.currentTimeMillis()), mLatinIME); + System.currentTimeMillis(), System.nanoTime()), mLatinIME); mFeedbackLogBuffer = new FixedLogBuffer(FEEDBACK_WORD_BUFFER_SIZE); } @@ -545,7 +545,7 @@ public class ResearchLogger implements SharedPreferences.OnSharedPreferenceChang mUserRecordingLog.blockingAbort(RESEARCHLOG_ABORT_TIMEOUT_IN_MS); } mUserRecordingFile = mResearchLogDirectory.getUserRecordingFilePath( - System.currentTimeMillis()); + System.currentTimeMillis(), System.nanoTime()); mUserRecordingLog = new ResearchLog(mUserRecordingFile, mLatinIME); mUserRecordingLogBuffer = new LogBuffer(); resetRecordingTimer(); @@ -813,7 +813,7 @@ public class ResearchLogger implements SharedPreferences.OnSharedPreferenceChang // enabled. The dot is actually a zero-width, zero-height rectangle, placed at the // lower-right corner of the canvas, painted with a non-zero border width. paint.setStrokeWidth(3); - canvas.drawRect(width, height, width, height, paint); + canvas.drawRect(width - 1, height - 1, width, height, paint); } paint.setColor(savedColor); paint.setStyle(savedStyle); @@ -894,7 +894,7 @@ public class ResearchLogger implements SharedPreferences.OnSharedPreferenceChang // Check that expected word matches. if (oldLogUnit != null) { final String oldLogUnitWord = oldLogUnit.getWord(); - if (!oldLogUnitWord.equals(expectedWord)) { + if (oldLogUnitWord != null && !oldLogUnitWord.equals(expectedWord)) { return; } } @@ -1107,7 +1107,7 @@ public class ResearchLogger implements SharedPreferences.OnSharedPreferenceChang packageInfo = mLatinIME.getPackageManager().getPackageInfo(mLatinIME.getPackageName(), 0); final String versionName = packageInfo.versionName; - return !(developerBuildRegex.matcher(versionName).find()); + return developerBuildRegex.matcher(versionName).find(); } catch (final NameNotFoundException e) { Log.e(TAG, "Could not determine package name", e); return false; @@ -1826,6 +1826,9 @@ public class ResearchLogger implements SharedPreferences.OnSharedPreferenceChang public static void latinIME_onEndBatchInput(final CharSequence enteredText, final int enteredWordPos, final SuggestedWords suggestedWords) { final ResearchLogger researchLogger = getInstance(); + if (!TextUtils.isEmpty(enteredText) && hasLetters(enteredText.toString())) { + researchLogger.mCurrentLogUnit.setWord(enteredText.toString()); + } researchLogger.enqueueEvent(LOGSTATEMENT_LATINIME_ONENDBATCHINPUT, enteredText, enteredWordPos); researchLogger.mCurrentLogUnit.initializeSuggestions(suggestedWords); diff --git a/java/src/com/android/inputmethod/research/UploaderService.java b/java/src/com/android/inputmethod/research/UploaderService.java index 6a9717b7c..d2db34927 100644 --- a/java/src/com/android/inputmethod/research/UploaderService.java +++ b/java/src/com/android/inputmethod/research/UploaderService.java @@ -22,6 +22,7 @@ import android.app.PendingIntent; import android.content.Context; import android.content.Intent; import android.os.Bundle; +import android.os.SystemClock; import com.android.inputmethod.latin.define.ProductionFlag; @@ -79,28 +80,14 @@ public final class UploaderService extends IntentService { */ public static void cancelAndRescheduleUploadingService(final Context context, final boolean needsRescheduling) { - final PendingIntent pendingIntent = getPendingIntentForService(context); + final Intent intent = new Intent(context, UploaderService.class); + final PendingIntent pendingIntent = PendingIntent.getService(context, 0, intent, 0); final AlarmManager alarmManager = (AlarmManager) context.getSystemService( Context.ALARM_SERVICE); - cancelAnyScheduledServiceAlarm(alarmManager, pendingIntent); + alarmManager.cancel(pendingIntent); if (needsRescheduling) { - scheduleServiceAlarm(alarmManager, pendingIntent); + alarmManager.set(AlarmManager.ELAPSED_REALTIME_WAKEUP, SystemClock.elapsedRealtime() + + UploaderService.RUN_INTERVAL, pendingIntent); } } - - private static PendingIntent getPendingIntentForService(final Context context) { - final Intent intent = new Intent(context, UploaderService.class); - return PendingIntent.getService(context, 0, intent, 0); - } - - private static void cancelAnyScheduledServiceAlarm(final AlarmManager alarmManager, - final PendingIntent pendingIntent) { - alarmManager.cancel(pendingIntent); - } - - private static void scheduleServiceAlarm(final AlarmManager alarmManager, - final PendingIntent pendingIntent) { - alarmManager.set(AlarmManager.ELAPSED_REALTIME_WAKEUP, UploaderService.RUN_INTERVAL, - pendingIntent); - } } diff --git a/native/jni/src/binary_format.h b/native/jni/src/binary_format.h index 1c4061fd8..2d2e19501 100644 --- a/native/jni/src/binary_format.h +++ b/native/jni/src/binary_format.h @@ -92,6 +92,7 @@ class BinaryFormat { const int unigramProbability, const int bigramProbability); static int getProbability(const int position, const std::map<int, int> *bigramMap, const uint8_t *bigramFilter, const int unigramProbability); + static float getMultiWordCostMultiplier(const uint8_t *const dict); // Flags for special processing // Those *must* match the flags in makedict (BinaryDictInputOutput#*_PROCESSING_FLAG) or @@ -241,6 +242,17 @@ AK_FORCE_INLINE int BinaryFormat::getGroupCountAndForwardPointer(const uint8_t * return ((msb & 0x7F) << 8) | dict[(*pos)++]; } +inline float BinaryFormat::getMultiWordCostMultiplier(const uint8_t *const dict) { + const int headerValue = readHeaderValueInt(dict, "MULTIPLE_WORDS_DEMOTION_RATE"); + if (headerValue == S_INT_MIN) { + return 1.0f; + } + if (headerValue <= 0) { + return static_cast<float>(MAX_VALUE_FOR_WEIGHTING); + } + return 100.0f / static_cast<float>(headerValue); +} + inline uint8_t BinaryFormat::getFlagsAndForwardPointer(const uint8_t *const dict, int *pos) { return dict[(*pos)++]; } diff --git a/native/jni/src/char_utils.cpp b/native/jni/src/char_utils.cpp index 8d917ea74..e219beb62 100644 --- a/native/jni/src/char_utils.cpp +++ b/native/jni/src/char_utils.cpp @@ -45,18 +45,16 @@ struct LatinCapitalSmallPair { extern "C" int main() { for (unsigned short c = 0; c < 0xFFFF; c++) { - const unsigned short baseC = c < NELEMS(BASE_CHARS) ? BASE_CHARS[c] : c; - if (baseC <= 0x7F) continue; - const unsigned short icu4cLowerBaseC = u_tolower(baseC); - const unsigned short myLowerBaseC = latin_tolower(baseC); - if (baseC != icu4cLowerBaseC) { + if (c <= 0x7F) continue; + const unsigned short icu4cLowerC = u_tolower(c); + const unsigned short myLowerC = latin_tolower(c); + if (c != icu4cLowerC) { #ifdef CONFIRMING_CHAR_UTILS - if (icu4cLowerBaseC != myLowerBaseC) { - fprintf(stderr, "icu4cLowerBaseC != myLowerBaseC, 0x%04X, 0x%04X\n", - icu4cLowerBaseC, myLowerBaseC); + if (icu4cLowerC != myLowerC) { + fprintf(stderr, "icu4cLowerC != myLowerC, 0x%04X, 0x%04X\n", icu4cLowerC, myLowerC); } #else // CONFIRMING_CHAR_UTILS - printf("0x%04X, 0x%04X\n", baseC, icu4cLowerBaseC); + printf("0x%04X, 0x%04X\n", c, icu4cLowerC); #endif // CONFIRMING_CHAR_UTILS } } @@ -77,14 +75,99 @@ extern "C" int main() { * $ */ static const struct LatinCapitalSmallPair SORTED_CHAR_MAP[] = { + { 0x00C0, 0x00E0 }, // LATIN CAPITAL LETTER A WITH GRAVE + { 0x00C1, 0x00E1 }, // LATIN CAPITAL LETTER A WITH ACUTE + { 0x00C2, 0x00E2 }, // LATIN CAPITAL LETTER A WITH CIRCUMFLEX + { 0x00C3, 0x00E3 }, // LATIN CAPITAL LETTER A WITH TILDE + { 0x00C4, 0x00E4 }, // LATIN CAPITAL LETTER A WITH DIAERESIS + { 0x00C5, 0x00E5 }, // LATIN CAPITAL LETTER A WITH RING ABOVE { 0x00C6, 0x00E6 }, // LATIN CAPITAL LETTER AE + { 0x00C7, 0x00E7 }, // LATIN CAPITAL LETTER C WITH CEDILLA + { 0x00C8, 0x00E8 }, // LATIN CAPITAL LETTER E WITH GRAVE + { 0x00C9, 0x00E9 }, // LATIN CAPITAL LETTER E WITH ACUTE + { 0x00CA, 0x00EA }, // LATIN CAPITAL LETTER E WITH CIRCUMFLEX + { 0x00CB, 0x00EB }, // LATIN CAPITAL LETTER E WITH DIAERESIS + { 0x00CC, 0x00EC }, // LATIN CAPITAL LETTER I WITH GRAVE + { 0x00CD, 0x00ED }, // LATIN CAPITAL LETTER I WITH ACUTE + { 0x00CE, 0x00EE }, // LATIN CAPITAL LETTER I WITH CIRCUMFLEX + { 0x00CF, 0x00EF }, // LATIN CAPITAL LETTER I WITH DIAERESIS { 0x00D0, 0x00F0 }, // LATIN CAPITAL LETTER ETH + { 0x00D1, 0x00F1 }, // LATIN CAPITAL LETTER N WITH TILDE + { 0x00D2, 0x00F2 }, // LATIN CAPITAL LETTER O WITH GRAVE + { 0x00D3, 0x00F3 }, // LATIN CAPITAL LETTER O WITH ACUTE + { 0x00D4, 0x00F4 }, // LATIN CAPITAL LETTER O WITH CIRCUMFLEX + { 0x00D5, 0x00F5 }, // LATIN CAPITAL LETTER O WITH TILDE + { 0x00D6, 0x00F6 }, // LATIN CAPITAL LETTER O WITH DIAERESIS + { 0x00D8, 0x00F8 }, // LATIN CAPITAL LETTER O WITH STROKE + { 0x00D9, 0x00F9 }, // LATIN CAPITAL LETTER U WITH GRAVE + { 0x00DA, 0x00FA }, // LATIN CAPITAL LETTER U WITH ACUTE + { 0x00DB, 0x00FB }, // LATIN CAPITAL LETTER U WITH CIRCUMFLEX + { 0x00DC, 0x00FC }, // LATIN CAPITAL LETTER U WITH DIAERESIS + { 0x00DD, 0x00FD }, // LATIN CAPITAL LETTER Y WITH ACUTE { 0x00DE, 0x00FE }, // LATIN CAPITAL LETTER THORN + { 0x0100, 0x0101 }, // LATIN CAPITAL LETTER A WITH MACRON + { 0x0102, 0x0103 }, // LATIN CAPITAL LETTER A WITH BREVE + { 0x0104, 0x0105 }, // LATIN CAPITAL LETTER A WITH OGONEK + { 0x0106, 0x0107 }, // LATIN CAPITAL LETTER C WITH ACUTE + { 0x0108, 0x0109 }, // LATIN CAPITAL LETTER C WITH CIRCUMFLEX + { 0x010A, 0x010B }, // LATIN CAPITAL LETTER C WITH DOT ABOVE + { 0x010C, 0x010D }, // LATIN CAPITAL LETTER C WITH CARON + { 0x010E, 0x010F }, // LATIN CAPITAL LETTER D WITH CARON { 0x0110, 0x0111 }, // LATIN CAPITAL LETTER D WITH STROKE + { 0x0112, 0x0113 }, // LATIN CAPITAL LETTER E WITH MACRON + { 0x0114, 0x0115 }, // LATIN CAPITAL LETTER E WITH BREVE + { 0x0116, 0x0117 }, // LATIN CAPITAL LETTER E WITH DOT ABOVE + { 0x0118, 0x0119 }, // LATIN CAPITAL LETTER E WITH OGONEK + { 0x011A, 0x011B }, // LATIN CAPITAL LETTER E WITH CARON + { 0x011C, 0x011D }, // LATIN CAPITAL LETTER G WITH CIRCUMFLEX + { 0x011E, 0x011F }, // LATIN CAPITAL LETTER G WITH BREVE + { 0x0120, 0x0121 }, // LATIN CAPITAL LETTER G WITH DOT ABOVE + { 0x0122, 0x0123 }, // LATIN CAPITAL LETTER G WITH CEDILLA + { 0x0124, 0x0125 }, // LATIN CAPITAL LETTER H WITH CIRCUMFLEX { 0x0126, 0x0127 }, // LATIN CAPITAL LETTER H WITH STROKE + { 0x0128, 0x0129 }, // LATIN CAPITAL LETTER I WITH TILDE + { 0x012A, 0x012B }, // LATIN CAPITAL LETTER I WITH MACRON + { 0x012C, 0x012D }, // LATIN CAPITAL LETTER I WITH BREVE + { 0x012E, 0x012F }, // LATIN CAPITAL LETTER I WITH OGONEK + { 0x0130, 0x0069 }, // LATIN CAPITAL LETTER I WITH DOT ABOVE + { 0x0132, 0x0133 }, // LATIN CAPITAL LIGATURE IJ + { 0x0134, 0x0135 }, // LATIN CAPITAL LETTER J WITH CIRCUMFLEX + { 0x0136, 0x0137 }, // LATIN CAPITAL LETTER K WITH CEDILLA + { 0x0139, 0x013A }, // LATIN CAPITAL LETTER L WITH ACUTE + { 0x013B, 0x013C }, // LATIN CAPITAL LETTER L WITH CEDILLA + { 0x013D, 0x013E }, // LATIN CAPITAL LETTER L WITH CARON + { 0x013F, 0x0140 }, // LATIN CAPITAL LETTER L WITH MIDDLE DOT + { 0x0141, 0x0142 }, // LATIN CAPITAL LETTER L WITH STROKE + { 0x0143, 0x0144 }, // LATIN CAPITAL LETTER N WITH ACUTE + { 0x0145, 0x0146 }, // LATIN CAPITAL LETTER N WITH CEDILLA + { 0x0147, 0x0148 }, // LATIN CAPITAL LETTER N WITH CARON { 0x014A, 0x014B }, // LATIN CAPITAL LETTER ENG + { 0x014C, 0x014D }, // LATIN CAPITAL LETTER O WITH MACRON + { 0x014E, 0x014F }, // LATIN CAPITAL LETTER O WITH BREVE + { 0x0150, 0x0151 }, // LATIN CAPITAL LETTER O WITH DOUBLE ACUTE { 0x0152, 0x0153 }, // LATIN CAPITAL LIGATURE OE + { 0x0154, 0x0155 }, // LATIN CAPITAL LETTER R WITH ACUTE + { 0x0156, 0x0157 }, // LATIN CAPITAL LETTER R WITH CEDILLA + { 0x0158, 0x0159 }, // LATIN CAPITAL LETTER R WITH CARON + { 0x015A, 0x015B }, // LATIN CAPITAL LETTER S WITH ACUTE + { 0x015C, 0x015D }, // LATIN CAPITAL LETTER S WITH CIRCUMFLEX + { 0x015E, 0x015F }, // LATIN CAPITAL LETTER S WITH CEDILLA + { 0x0160, 0x0161 }, // LATIN CAPITAL LETTER S WITH CARON + { 0x0162, 0x0163 }, // LATIN CAPITAL LETTER T WITH CEDILLA + { 0x0164, 0x0165 }, // LATIN CAPITAL LETTER T WITH CARON { 0x0166, 0x0167 }, // LATIN CAPITAL LETTER T WITH STROKE + { 0x0168, 0x0169 }, // LATIN CAPITAL LETTER U WITH TILDE + { 0x016A, 0x016B }, // LATIN CAPITAL LETTER U WITH MACRON + { 0x016C, 0x016D }, // LATIN CAPITAL LETTER U WITH BREVE + { 0x016E, 0x016F }, // LATIN CAPITAL LETTER U WITH RING ABOVE + { 0x0170, 0x0171 }, // LATIN CAPITAL LETTER U WITH DOUBLE ACUTE + { 0x0172, 0x0173 }, // LATIN CAPITAL LETTER U WITH OGONEK + { 0x0174, 0x0175 }, // LATIN CAPITAL LETTER W WITH CIRCUMFLEX + { 0x0176, 0x0177 }, // LATIN CAPITAL LETTER Y WITH CIRCUMFLEX + { 0x0178, 0x00FF }, // LATIN CAPITAL LETTER Y WITH DIAERESIS + { 0x0179, 0x017A }, // LATIN CAPITAL LETTER Z WITH ACUTE + { 0x017B, 0x017C }, // LATIN CAPITAL LETTER Z WITH DOT ABOVE + { 0x017D, 0x017E }, // LATIN CAPITAL LETTER Z WITH CARON { 0x0181, 0x0253 }, // LATIN CAPITAL LETTER B WITH HOOK { 0x0182, 0x0183 }, // LATIN CAPITAL LETTER B WITH TOPBAR { 0x0184, 0x0185 }, // LATIN CAPITAL LETTER TONE SIX @@ -105,6 +188,7 @@ static const struct LatinCapitalSmallPair SORTED_CHAR_MAP[] = { { 0x019C, 0x026F }, // LATIN CAPITAL LETTER TURNED M { 0x019D, 0x0272 }, // LATIN CAPITAL LETTER N WITH LEFT HOOK { 0x019F, 0x0275 }, // LATIN CAPITAL LETTER O WITH MIDDLE TILDE + { 0x01A0, 0x01A1 }, // LATIN CAPITAL LETTER O WITH HORN { 0x01A2, 0x01A3 }, // LATIN CAPITAL LETTER OI { 0x01A4, 0x01A5 }, // LATIN CAPITAL LETTER P WITH HOOK { 0x01A6, 0x0280 }, // LATIN LETTER YR @@ -112,6 +196,7 @@ static const struct LatinCapitalSmallPair SORTED_CHAR_MAP[] = { { 0x01A9, 0x0283 }, // LATIN CAPITAL LETTER ESH { 0x01AC, 0x01AD }, // LATIN CAPITAL LETTER T WITH HOOK { 0x01AE, 0x0288 }, // LATIN CAPITAL LETTER T WITH RETROFLEX HOOK + { 0x01AF, 0x01B0 }, // LATIN CAPITAL LETTER U WITH HORN { 0x01B1, 0x028A }, // LATIN CAPITAL LETTER UPSILON { 0x01B2, 0x028B }, // LATIN CAPITAL LETTER V WITH HOOK { 0x01B3, 0x01B4 }, // LATIN CAPITAL LETTER Y WITH HOOK @@ -119,13 +204,64 @@ static const struct LatinCapitalSmallPair SORTED_CHAR_MAP[] = { { 0x01B7, 0x0292 }, // LATIN CAPITAL LETTER EZH { 0x01B8, 0x01B9 }, // LATIN CAPITAL LETTER EZH REVERSED { 0x01BC, 0x01BD }, // LATIN CAPITAL LETTER TONE FIVE + { 0x01C4, 0x01C6 }, // LATIN CAPITAL LETTER DZ WITH CARON + { 0x01C5, 0x01C6 }, // LATIN CAPITAL LETTER D WITH SMALL LETTER Z WITH CARON + { 0x01C7, 0x01C9 }, // LATIN CAPITAL LETTER LJ + { 0x01C8, 0x01C9 }, // LATIN CAPITAL LETTER L WITH SMALL LETTER J + { 0x01CA, 0x01CC }, // LATIN CAPITAL LETTER NJ + { 0x01CB, 0x01CC }, // LATIN CAPITAL LETTER N WITH SMALL LETTER J + { 0x01CD, 0x01CE }, // LATIN CAPITAL LETTER A WITH CARON + { 0x01CF, 0x01D0 }, // LATIN CAPITAL LETTER I WITH CARON + { 0x01D1, 0x01D2 }, // LATIN CAPITAL LETTER O WITH CARON + { 0x01D3, 0x01D4 }, // LATIN CAPITAL LETTER U WITH CARON + { 0x01D5, 0x01D6 }, // LATIN CAPITAL LETTER U WITH DIAERESIS AND MACRON + { 0x01D7, 0x01D8 }, // LATIN CAPITAL LETTER U WITH DIAERESIS AND ACUTE + { 0x01D9, 0x01DA }, // LATIN CAPITAL LETTER U WITH DIAERESIS AND CARON + { 0x01DB, 0x01DC }, // LATIN CAPITAL LETTER U WITH DIAERESIS AND GRAVE + { 0x01DE, 0x01DF }, // LATIN CAPITAL LETTER A WITH DIAERESIS AND MACRON + { 0x01E0, 0x01E1 }, // LATIN CAPITAL LETTER A WITH DOT ABOVE AND MACRON + { 0x01E2, 0x01E3 }, // LATIN CAPITAL LETTER AE WITH MACRON { 0x01E4, 0x01E5 }, // LATIN CAPITAL LETTER G WITH STROKE + { 0x01E6, 0x01E7 }, // LATIN CAPITAL LETTER G WITH CARON + { 0x01E8, 0x01E9 }, // LATIN CAPITAL LETTER K WITH CARON + { 0x01EA, 0x01EB }, // LATIN CAPITAL LETTER O WITH OGONEK + { 0x01EC, 0x01ED }, // LATIN CAPITAL LETTER O WITH OGONEK AND MACRON + { 0x01EE, 0x01EF }, // LATIN CAPITAL LETTER EZH WITH CARON + { 0x01F1, 0x01F3 }, // LATIN CAPITAL LETTER DZ + { 0x01F2, 0x01F3 }, // LATIN CAPITAL LETTER D WITH SMALL LETTER Z + { 0x01F4, 0x01F5 }, // LATIN CAPITAL LETTER G WITH ACUTE { 0x01F6, 0x0195 }, // LATIN CAPITAL LETTER HWAIR { 0x01F7, 0x01BF }, // LATIN CAPITAL LETTER WYNN + { 0x01F8, 0x01F9 }, // LATIN CAPITAL LETTER N WITH GRAVE + { 0x01FA, 0x01FB }, // LATIN CAPITAL LETTER A WITH RING ABOVE AND ACUTE + { 0x01FC, 0x01FD }, // LATIN CAPITAL LETTER AE WITH ACUTE + { 0x01FE, 0x01FF }, // LATIN CAPITAL LETTER O WITH STROKE AND ACUTE + { 0x0200, 0x0201 }, // LATIN CAPITAL LETTER A WITH DOUBLE GRAVE + { 0x0202, 0x0203 }, // LATIN CAPITAL LETTER A WITH INVERTED BREVE + { 0x0204, 0x0205 }, // LATIN CAPITAL LETTER E WITH DOUBLE GRAVE + { 0x0206, 0x0207 }, // LATIN CAPITAL LETTER E WITH INVERTED BREVE + { 0x0208, 0x0209 }, // LATIN CAPITAL LETTER I WITH DOUBLE GRAVE + { 0x020A, 0x020B }, // LATIN CAPITAL LETTER I WITH INVERTED BREVE + { 0x020C, 0x020D }, // LATIN CAPITAL LETTER O WITH DOUBLE GRAVE + { 0x020E, 0x020F }, // LATIN CAPITAL LETTER O WITH INVERTED BREVE + { 0x0210, 0x0211 }, // LATIN CAPITAL LETTER R WITH DOUBLE GRAVE + { 0x0212, 0x0213 }, // LATIN CAPITAL LETTER R WITH INVERTED BREVE + { 0x0214, 0x0215 }, // LATIN CAPITAL LETTER U WITH DOUBLE GRAVE + { 0x0216, 0x0217 }, // LATIN CAPITAL LETTER U WITH INVERTED BREVE + { 0x0218, 0x0219 }, // LATIN CAPITAL LETTER S WITH COMMA BELOW + { 0x021A, 0x021B }, // LATIN CAPITAL LETTER T WITH COMMA BELOW { 0x021C, 0x021D }, // LATIN CAPITAL LETTER YOGH + { 0x021E, 0x021F }, // LATIN CAPITAL LETTER H WITH CARON { 0x0220, 0x019E }, // LATIN CAPITAL LETTER N WITH LONG RIGHT LEG { 0x0222, 0x0223 }, // LATIN CAPITAL LETTER OU { 0x0224, 0x0225 }, // LATIN CAPITAL LETTER Z WITH HOOK + { 0x0226, 0x0227 }, // LATIN CAPITAL LETTER A WITH DOT ABOVE + { 0x0228, 0x0229 }, // LATIN CAPITAL LETTER E WITH CEDILLA + { 0x022A, 0x022B }, // LATIN CAPITAL LETTER O WITH DIAERESIS AND MACRON + { 0x022C, 0x022D }, // LATIN CAPITAL LETTER O WITH TILDE AND MACRON + { 0x022E, 0x022F }, // LATIN CAPITAL LETTER O WITH DOT ABOVE + { 0x0230, 0x0231 }, // LATIN CAPITAL LETTER O WITH DOT ABOVE AND MACRON + { 0x0232, 0x0233 }, // LATIN CAPITAL LETTER Y WITH MACRON { 0x023A, 0x2C65 }, // LATIN CAPITAL LETTER A WITH STROKE { 0x023B, 0x023C }, // LATIN CAPITAL LETTER C WITH STROKE { 0x023D, 0x019A }, // LATIN CAPITAL LETTER L WITH BAR @@ -142,6 +278,13 @@ static const struct LatinCapitalSmallPair SORTED_CHAR_MAP[] = { { 0x0370, 0x0371 }, // GREEK CAPITAL LETTER HETA { 0x0372, 0x0373 }, // GREEK CAPITAL LETTER ARCHAIC SAMPI { 0x0376, 0x0377 }, // GREEK CAPITAL LETTER PAMPHYLIAN DIGAMMA + { 0x0386, 0x03AC }, // GREEK CAPITAL LETTER ALPHA WITH TONOS + { 0x0388, 0x03AD }, // GREEK CAPITAL LETTER EPSILON WITH TONOS + { 0x0389, 0x03AE }, // GREEK CAPITAL LETTER ETA WITH TONOS + { 0x038A, 0x03AF }, // GREEK CAPITAL LETTER IOTA WITH TONOS + { 0x038C, 0x03CC }, // GREEK CAPITAL LETTER OMICRON WITH TONOS + { 0x038E, 0x03CD }, // GREEK CAPITAL LETTER UPSILON WITH TONOS + { 0x038F, 0x03CE }, // GREEK CAPITAL LETTER OMEGA WITH TONOS { 0x0391, 0x03B1 }, // GREEK CAPITAL LETTER ALPHA { 0x0392, 0x03B2 }, // GREEK CAPITAL LETTER BETA { 0x0393, 0x03B3 }, // GREEK CAPITAL LETTER GAMMA @@ -166,6 +309,8 @@ static const struct LatinCapitalSmallPair SORTED_CHAR_MAP[] = { { 0x03A7, 0x03C7 }, // GREEK CAPITAL LETTER CHI { 0x03A8, 0x03C8 }, // GREEK CAPITAL LETTER PSI { 0x03A9, 0x03C9 }, // GREEK CAPITAL LETTER OMEGA + { 0x03AA, 0x03CA }, // GREEK CAPITAL LETTER IOTA WITH DIALYTIKA + { 0x03AB, 0x03CB }, // GREEK CAPITAL LETTER UPSILON WITH DIALYTIKA { 0x03CF, 0x03D7 }, // GREEK CAPITAL KAI SYMBOL { 0x03D8, 0x03D9 }, // GREEK LETTER ARCHAIC KOPPA { 0x03DA, 0x03DB }, // GREEK LETTER STIGMA @@ -179,19 +324,28 @@ static const struct LatinCapitalSmallPair SORTED_CHAR_MAP[] = { { 0x03EA, 0x03EB }, // COPTIC CAPITAL LETTER GANGIA { 0x03EC, 0x03ED }, // COPTIC CAPITAL LETTER SHIMA { 0x03EE, 0x03EF }, // COPTIC CAPITAL LETTER DEI + { 0x03F4, 0x03B8 }, // GREEK CAPITAL THETA SYMBOL { 0x03F7, 0x03F8 }, // GREEK CAPITAL LETTER SHO + { 0x03F9, 0x03F2 }, // GREEK CAPITAL LUNATE SIGMA SYMBOL { 0x03FA, 0x03FB }, // GREEK CAPITAL LETTER SAN { 0x03FD, 0x037B }, // GREEK CAPITAL REVERSED LUNATE SIGMA SYMBOL { 0x03FE, 0x037C }, // GREEK CAPITAL DOTTED LUNATE SIGMA SYMBOL { 0x03FF, 0x037D }, // GREEK CAPITAL REVERSED DOTTED LUNATE SIGMA SYMBOL + { 0x0400, 0x0450 }, // CYRILLIC CAPITAL LETTER IE WITH GRAVE + { 0x0401, 0x0451 }, // CYRILLIC CAPITAL LETTER IO { 0x0402, 0x0452 }, // CYRILLIC CAPITAL LETTER DJE + { 0x0403, 0x0453 }, // CYRILLIC CAPITAL LETTER GJE { 0x0404, 0x0454 }, // CYRILLIC CAPITAL LETTER UKRAINIAN IE { 0x0405, 0x0455 }, // CYRILLIC CAPITAL LETTER DZE { 0x0406, 0x0456 }, // CYRILLIC CAPITAL LETTER BYELORUSSIAN-UKRAINIAN I + { 0x0407, 0x0457 }, // CYRILLIC CAPITAL LETTER YI { 0x0408, 0x0458 }, // CYRILLIC CAPITAL LETTER JE { 0x0409, 0x0459 }, // CYRILLIC CAPITAL LETTER LJE { 0x040A, 0x045A }, // CYRILLIC CAPITAL LETTER NJE { 0x040B, 0x045B }, // CYRILLIC CAPITAL LETTER TSHE + { 0x040C, 0x045C }, // CYRILLIC CAPITAL LETTER KJE + { 0x040D, 0x045D }, // CYRILLIC CAPITAL LETTER I WITH GRAVE + { 0x040E, 0x045E }, // CYRILLIC CAPITAL LETTER SHORT U { 0x040F, 0x045F }, // CYRILLIC CAPITAL LETTER DZHE { 0x0410, 0x0430 }, // CYRILLIC CAPITAL LETTER A { 0x0411, 0x0431 }, // CYRILLIC CAPITAL LETTER BE @@ -236,6 +390,7 @@ static const struct LatinCapitalSmallPair SORTED_CHAR_MAP[] = { { 0x0470, 0x0471 }, // CYRILLIC CAPITAL LETTER PSI { 0x0472, 0x0473 }, // CYRILLIC CAPITAL LETTER FITA { 0x0474, 0x0475 }, // CYRILLIC CAPITAL LETTER IZHITSA + { 0x0476, 0x0477 }, // CYRILLIC CAPITAL LETTER IZHITSA WITH DOUBLE GRAVE ACCENT { 0x0478, 0x0479 }, // CYRILLIC CAPITAL LETTER UK { 0x047A, 0x047B }, // CYRILLIC CAPITAL LETTER ROUND OMEGA { 0x047C, 0x047D }, // CYRILLIC CAPITAL LETTER OMEGA WITH TITLO @@ -269,17 +424,34 @@ static const struct LatinCapitalSmallPair SORTED_CHAR_MAP[] = { { 0x04BC, 0x04BD }, // CYRILLIC CAPITAL LETTER ABKHASIAN CHE { 0x04BE, 0x04BF }, // CYRILLIC CAPITAL LETTER ABKHASIAN CHE WITH DESCENDER { 0x04C0, 0x04CF }, // CYRILLIC LETTER PALOCHKA + { 0x04C1, 0x04C2 }, // CYRILLIC CAPITAL LETTER ZHE WITH BREVE { 0x04C3, 0x04C4 }, // CYRILLIC CAPITAL LETTER KA WITH HOOK { 0x04C5, 0x04C6 }, // CYRILLIC CAPITAL LETTER EL WITH TAIL { 0x04C7, 0x04C8 }, // CYRILLIC CAPITAL LETTER EN WITH HOOK { 0x04C9, 0x04CA }, // CYRILLIC CAPITAL LETTER EN WITH TAIL { 0x04CB, 0x04CC }, // CYRILLIC CAPITAL LETTER KHAKASSIAN CHE { 0x04CD, 0x04CE }, // CYRILLIC CAPITAL LETTER EM WITH TAIL + { 0x04D0, 0x04D1 }, // CYRILLIC CAPITAL LETTER A WITH BREVE + { 0x04D2, 0x04D3 }, // CYRILLIC CAPITAL LETTER A WITH DIAERESIS { 0x04D4, 0x04D5 }, // CYRILLIC CAPITAL LIGATURE A IE + { 0x04D6, 0x04D7 }, // CYRILLIC CAPITAL LETTER IE WITH BREVE { 0x04D8, 0x04D9 }, // CYRILLIC CAPITAL LETTER SCHWA + { 0x04DA, 0x04DB }, // CYRILLIC CAPITAL LETTER SCHWA WITH DIAERESIS + { 0x04DC, 0x04DD }, // CYRILLIC CAPITAL LETTER ZHE WITH DIAERESIS + { 0x04DE, 0x04DF }, // CYRILLIC CAPITAL LETTER ZE WITH DIAERESIS { 0x04E0, 0x04E1 }, // CYRILLIC CAPITAL LETTER ABKHASIAN DZE + { 0x04E2, 0x04E3 }, // CYRILLIC CAPITAL LETTER I WITH MACRON + { 0x04E4, 0x04E5 }, // CYRILLIC CAPITAL LETTER I WITH DIAERESIS + { 0x04E6, 0x04E7 }, // CYRILLIC CAPITAL LETTER O WITH DIAERESIS { 0x04E8, 0x04E9 }, // CYRILLIC CAPITAL LETTER BARRED O + { 0x04EA, 0x04EB }, // CYRILLIC CAPITAL LETTER BARRED O WITH DIAERESIS + { 0x04EC, 0x04ED }, // CYRILLIC CAPITAL LETTER E WITH DIAERESIS + { 0x04EE, 0x04EF }, // CYRILLIC CAPITAL LETTER U WITH MACRON + { 0x04F0, 0x04F1 }, // CYRILLIC CAPITAL LETTER U WITH DIAERESIS + { 0x04F2, 0x04F3 }, // CYRILLIC CAPITAL LETTER U WITH DOUBLE ACUTE + { 0x04F4, 0x04F5 }, // CYRILLIC CAPITAL LETTER CHE WITH DIAERESIS { 0x04F6, 0x04F7 }, // CYRILLIC CAPITAL LETTER GHE WITH DESCENDER + { 0x04F8, 0x04F9 }, // CYRILLIC CAPITAL LETTER YERU WITH DIAERESIS { 0x04FA, 0x04FB }, // CYRILLIC CAPITAL LETTER GHE WITH STROKE AND HOOK { 0x04FC, 0x04FD }, // CYRILLIC CAPITAL LETTER HA WITH HOOK { 0x04FE, 0x04FF }, // CYRILLIC CAPITAL LETTER HA WITH STROKE diff --git a/native/jni/src/char_utils.h b/native/jni/src/char_utils.h index 58d388dbf..b429f40b2 100644 --- a/native/jni/src/char_utils.h +++ b/native/jni/src/char_utils.h @@ -58,7 +58,8 @@ inline static int toBaseCodePoint(int c) { AK_FORCE_INLINE static int toLowerCase(const int c) { if (isAsciiUpper(c)) { return toAsciiLower(c); - } else if (isAscii(c)) { + } + if (isAscii(c)) { return c; } return static_cast<int>(latin_tolower(static_cast<unsigned short>(c))); diff --git a/native/jni/src/defines.h b/native/jni/src/defines.h index a7b023a75..6ef9f414b 100644 --- a/native/jni/src/defines.h +++ b/native/jni/src/defines.h @@ -424,10 +424,9 @@ typedef enum { CT_OMISSION, CT_INSERTION, CT_TRANSPOSITION, - CT_SPACE_SUBSTITUTION, - CT_SPACE_OMISSION, CT_COMPLETION, CT_TERMINAL, - CT_NEW_WORD, + CT_NEW_WORD_SPACE_OMITTION, + CT_NEW_WORD_SPACE_SUBSTITUTION, } CorrectionType; #endif // LATINIME_DEFINES_H diff --git a/native/jni/src/digraph_utils.cpp b/native/jni/src/digraph_utils.cpp index 6a1ab0271..083442669 100644 --- a/native/jni/src/digraph_utils.cpp +++ b/native/jni/src/digraph_utils.cpp @@ -14,6 +14,7 @@ * limitations under the License. */ +#include "char_utils.h" #include "binary_format.h" #include "defines.h" #include "digraph_utils.h" @@ -120,10 +121,11 @@ const DigraphUtils::DigraphType DigraphUtils::USED_DIGRAPH_TYPES[] = /* static */ const DigraphUtils::digraph_t *DigraphUtils::getDigraphForDigraphTypeAndCodePoint( const DigraphUtils::DigraphType digraphType, const int compositeGlyphCodePoint) { const DigraphUtils::digraph_t *digraphs = 0; + const int compositeGlyphLowerCodePoint = toLowerCase(compositeGlyphCodePoint); const int digraphsSize = DigraphUtils::getAllDigraphsForDictionaryAndReturnSize(digraphType, &digraphs); for (int i = 0; i < digraphsSize; i++) { - if (digraphs[i].compositeGlyph == compositeGlyphCodePoint) { + if (digraphs[i].compositeGlyph == compositeGlyphLowerCodePoint) { return &digraphs[i]; } } diff --git a/native/jni/src/suggest/core/dicnode/dic_node.h b/native/jni/src/suggest/core/dicnode/dic_node.h index 32faae52c..f8d2df452 100644 --- a/native/jni/src/suggest/core/dicnode/dic_node.h +++ b/native/jni/src/suggest/core/dicnode/dic_node.h @@ -360,11 +360,6 @@ class DicNode { return mDicNodeState.mDicNodeStateScoring.getCompoundDistance(languageWeight); } - // Note that "cost" means delta for "distance" that is weighted. - float getTotalPrevWordsLanguageCost() const { - return mDicNodeState.mDicNodeStateScoring.getTotalPrevWordsLanguageCost(); - } - // Used to commit input partially int getPrevWordNodePos() const { return mDicNodeState.mDicNodeStatePrevWord.getPrevWordNodePos(); diff --git a/native/jni/src/suggest/core/dicnode/dic_node_state_scoring.h b/native/jni/src/suggest/core/dicnode/dic_node_state_scoring.h index 8902d3122..fd9d610e3 100644 --- a/native/jni/src/suggest/core/dicnode/dic_node_state_scoring.h +++ b/native/jni/src/suggest/core/dicnode/dic_node_state_scoring.h @@ -31,7 +31,7 @@ class DicNodeStateScoring { mDigraphIndex(DigraphUtils::NOT_A_DIGRAPH_INDEX), mEditCorrectionCount(0), mProximityCorrectionCount(0), mNormalizedCompoundDistance(0.0f), mSpatialDistance(0.0f), mLanguageDistance(0.0f), - mTotalPrevWordsLanguageCost(0.0f), mRawLength(0.0f) { + mRawLength(0.0f) { } virtual ~DicNodeStateScoring() {} @@ -42,7 +42,6 @@ class DicNodeStateScoring { mNormalizedCompoundDistance = 0.0f; mSpatialDistance = 0.0f; mLanguageDistance = 0.0f; - mTotalPrevWordsLanguageCost = 0.0f; mRawLength = 0.0f; mDoubleLetterLevel = NOT_A_DOUBLE_LETTER; mDigraphIndex = DigraphUtils::NOT_A_DIGRAPH_INDEX; @@ -54,7 +53,6 @@ class DicNodeStateScoring { mNormalizedCompoundDistance = scoring->mNormalizedCompoundDistance; mSpatialDistance = scoring->mSpatialDistance; mLanguageDistance = scoring->mLanguageDistance; - mTotalPrevWordsLanguageCost = scoring->mTotalPrevWordsLanguageCost; mRawLength = scoring->mRawLength; mDoubleLetterLevel = scoring->mDoubleLetterLevel; mDigraphIndex = scoring->mDigraphIndex; @@ -70,9 +68,6 @@ class DicNodeStateScoring { if (isProximityCorrection) { ++mProximityCorrectionCount; } - if (languageCost > 0.0f) { - setTotalPrevWordsLanguageCost(mTotalPrevWordsLanguageCost + languageCost); - } } void addRawLength(const float rawLength) { @@ -148,10 +143,6 @@ class DicNodeStateScoring { } } - float getTotalPrevWordsLanguageCost() const { - return mTotalPrevWordsLanguageCost; - } - private: // Caution!!! // Use a default copy constructor and an assign operator because shallow copies are ok @@ -165,7 +156,6 @@ class DicNodeStateScoring { float mNormalizedCompoundDistance; float mSpatialDistance; float mLanguageDistance; - float mTotalPrevWordsLanguageCost; float mRawLength; AK_FORCE_INLINE void addDistance(float spatialDistance, float languageDistance, @@ -179,11 +169,6 @@ class DicNodeStateScoring { / static_cast<float>(max(1, totalInputIndex)); } } - - //TODO: remove - AK_FORCE_INLINE void setTotalPrevWordsLanguageCost(float totalPrevWordsLanguageCost) { - mTotalPrevWordsLanguageCost = totalPrevWordsLanguageCost; - } }; } // namespace latinime #endif // LATINIME_DIC_NODE_STATE_SCORING_H diff --git a/native/jni/src/suggest/core/policy/weighting.cpp b/native/jni/src/suggest/core/policy/weighting.cpp index e62b70423..b9c0b8129 100644 --- a/native/jni/src/suggest/core/policy/weighting.cpp +++ b/native/jni/src/suggest/core/policy/weighting.cpp @@ -38,7 +38,7 @@ static inline void profile(const CorrectionType correctionType, DicNode *const n case CT_SUBSTITUTION: PROF_SUBSTITUTION(node->mProfiler); return; - case CT_NEW_WORD: + case CT_NEW_WORD_SPACE_OMITTION: PROF_NEW_WORD(node->mProfiler); return; case CT_MATCH: @@ -50,7 +50,7 @@ static inline void profile(const CorrectionType correctionType, DicNode *const n case CT_TERMINAL: PROF_TERMINAL(node->mProfiler); return; - case CT_SPACE_SUBSTITUTION: + case CT_NEW_WORD_SPACE_SUBSTITUTION: PROF_SPACE_SUBSTITUTION(node->mProfiler); return; case CT_INSERTION: @@ -107,16 +107,16 @@ static inline void profile(const CorrectionType correctionType, DicNode *const n case CT_SUBSTITUTION: // only used for typing return weighting->getSubstitutionCost(); - case CT_NEW_WORD: - return weighting->getNewWordCost(dicNode); + case CT_NEW_WORD_SPACE_OMITTION: + return weighting->getNewWordCost(traverseSession, dicNode); case CT_MATCH: return weighting->getMatchedCost(traverseSession, dicNode, inputStateG); case CT_COMPLETION: return weighting->getCompletionCost(traverseSession, dicNode); case CT_TERMINAL: return weighting->getTerminalSpatialCost(traverseSession, dicNode); - case CT_SPACE_SUBSTITUTION: - return weighting->getSpaceSubstitutionCost(); + case CT_NEW_WORD_SPACE_SUBSTITUTION: + return weighting->getSpaceSubstitutionCost(traverseSession, dicNode); case CT_INSERTION: return weighting->getInsertionCost(traverseSession, parentDicNode, dicNode); case CT_TRANSPOSITION: @@ -135,7 +135,7 @@ static inline void profile(const CorrectionType correctionType, DicNode *const n return 0.0f; case CT_SUBSTITUTION: return 0.0f; - case CT_NEW_WORD: + case CT_NEW_WORD_SPACE_OMITTION: return weighting->getNewWordBigramCost(traverseSession, parentDicNode, bigramCacheMap); case CT_MATCH: return 0.0f; @@ -147,8 +147,8 @@ static inline void profile(const CorrectionType correctionType, DicNode *const n traverseSession->getOffsetDict(), dicNode, bigramCacheMap); return weighting->getTerminalLanguageCost(traverseSession, dicNode, languageImprobability); } - case CT_SPACE_SUBSTITUTION: - return 0.0f; + case CT_NEW_WORD_SPACE_SUBSTITUTION: + return weighting->getNewWordBigramCost(traverseSession, parentDicNode, bigramCacheMap); case CT_INSERTION: return 0.0f; case CT_TRANSPOSITION: @@ -168,7 +168,7 @@ static inline void profile(const CorrectionType correctionType, DicNode *const n case CT_SUBSTITUTION: // Should return true? return false; - case CT_NEW_WORD: + case CT_NEW_WORD_SPACE_OMITTION: return false; case CT_MATCH: return false; @@ -176,7 +176,7 @@ static inline void profile(const CorrectionType correctionType, DicNode *const n return false; case CT_TERMINAL: return false; - case CT_SPACE_SUBSTITUTION: + case CT_NEW_WORD_SPACE_SUBSTITUTION: return false; case CT_INSERTION: return true; @@ -197,7 +197,7 @@ static inline void profile(const CorrectionType correctionType, DicNode *const n return false; case CT_SUBSTITUTION: return false; - case CT_NEW_WORD: + case CT_NEW_WORD_SPACE_OMITTION: return false; case CT_MATCH: return weighting->isProximityDicNode(traverseSession, dicNode); @@ -205,7 +205,7 @@ static inline void profile(const CorrectionType correctionType, DicNode *const n return false; case CT_TERMINAL: return false; - case CT_SPACE_SUBSTITUTION: + case CT_NEW_WORD_SPACE_SUBSTITUTION: return false; case CT_INSERTION: return false; @@ -224,7 +224,7 @@ static inline void profile(const CorrectionType correctionType, DicNode *const n return 0; case CT_SUBSTITUTION: return 0; - case CT_NEW_WORD: + case CT_NEW_WORD_SPACE_OMITTION: return 0; case CT_MATCH: return 1; @@ -232,7 +232,7 @@ static inline void profile(const CorrectionType correctionType, DicNode *const n return 0; case CT_TERMINAL: return 0; - case CT_SPACE_SUBSTITUTION: + case CT_NEW_WORD_SPACE_SUBSTITUTION: return 1; case CT_INSERTION: return 2; diff --git a/native/jni/src/suggest/core/policy/weighting.h b/native/jni/src/suggest/core/policy/weighting.h index b92dbe278..bce479c51 100644 --- a/native/jni/src/suggest/core/policy/weighting.h +++ b/native/jni/src/suggest/core/policy/weighting.h @@ -56,7 +56,8 @@ class Weighting { const DicTraverseSession *const traverseSession, const DicNode *const parentDicNode, const DicNode *const dicNode) const = 0; - virtual float getNewWordCost(const DicNode *const dicNode) const = 0; + virtual float getNewWordCost(const DicTraverseSession *const traverseSession, + const DicNode *const dicNode) const = 0; virtual float getNewWordBigramCost( const DicTraverseSession *const traverseSession, const DicNode *const dicNode, @@ -76,7 +77,8 @@ class Weighting { virtual float getSubstitutionCost() const = 0; - virtual float getSpaceSubstitutionCost() const = 0; + virtual float getSpaceSubstitutionCost(const DicTraverseSession *const traverseSession, + const DicNode *const dicNode) const = 0; Weighting() {} virtual ~Weighting() {} diff --git a/native/jni/src/suggest/core/session/dic_traverse_session.cpp b/native/jni/src/suggest/core/session/dic_traverse_session.cpp index 5b783a2ba..3c44db21c 100644 --- a/native/jni/src/suggest/core/session/dic_traverse_session.cpp +++ b/native/jni/src/suggest/core/session/dic_traverse_session.cpp @@ -16,6 +16,7 @@ #include "suggest/core/session/dic_traverse_session.h" +#include "binary_format.h" #include "defines.h" #include "dictionary.h" #include "dic_traverse_wrapper.h" @@ -63,6 +64,7 @@ static TraverseSessionFactoryRegisterer traverseSessionFactoryRegisterer; void DicTraverseSession::init(const Dictionary *const dictionary, const int *prevWord, int prevWordLength) { mDictionary = dictionary; + mMultiWordCostMultiplier = BinaryFormat::getMultiWordCostMultiplier(mDictionary->getDict()); if (!prevWord) { mPrevWordPos = NOT_VALID_WORD; return; diff --git a/native/jni/src/suggest/core/session/dic_traverse_session.h b/native/jni/src/suggest/core/session/dic_traverse_session.h index fe0527639..d9c2a51d0 100644 --- a/native/jni/src/suggest/core/session/dic_traverse_session.h +++ b/native/jni/src/suggest/core/session/dic_traverse_session.h @@ -36,7 +36,8 @@ class DicTraverseSession { AK_FORCE_INLINE DicTraverseSession(JNIEnv *env, jstring localeStr) : mPrevWordPos(NOT_VALID_WORD), mProximityInfo(0), mDictionary(0), mDicNodesCache(), mBigramCacheMap(), - mInputSize(0), mPartiallyCommited(false), mMaxPointerCount(1) { + mInputSize(0), mPartiallyCommited(false), mMaxPointerCount(1), + mMultiWordCostMultiplier(1.0f) { // NOTE: mProximityInfoStates is an array of instances. // No need to initialize it explicitly here. } @@ -52,6 +53,7 @@ class DicTraverseSession { const int maxPointerCount); void resetCache(const int nextActiveCacheSize, const int maxWords); + // TODO: Remove const uint8_t *getOffsetDict() const; int getDictFlags() const; @@ -150,6 +152,10 @@ class DicTraverseSession { return mProximityInfoStates[0].touchPositionCorrectionEnabled(); } + float getMultiWordCostMultiplier() const { + return mMultiWordCostMultiplier; + } + private: DISALLOW_IMPLICIT_CONSTRUCTORS(DicTraverseSession); // threshold to start caching @@ -170,6 +176,11 @@ class DicTraverseSession { int mInputSize; bool mPartiallyCommited; int mMaxPointerCount; + + ///////////////////////////////// + // Configuration per dictionary + float mMultiWordCostMultiplier; + }; } // namespace latinime #endif // LATINIME_DIC_TRAVERSE_SESSION_H diff --git a/native/jni/src/suggest/core/suggest.cpp b/native/jni/src/suggest/core/suggest.cpp index 67d351fa1..9de2cd2e2 100644 --- a/native/jni/src/suggest/core/suggest.cpp +++ b/native/jni/src/suggest/core/suggest.cpp @@ -33,16 +33,9 @@ namespace latinime { // Initialization of class constants. -const int Suggest::LOOKAHEAD_DIC_NODES_CACHE_SIZE = 25; const int Suggest::MIN_LEN_FOR_MULTI_WORD_AUTOCORRECT = 16; const int Suggest::MIN_CONTINUOUS_SUGGESTION_INPUT_SIZE = 2; const float Suggest::AUTOCORRECT_CLASSIFICATION_THRESHOLD = 0.33f; -const float Suggest::AUTOCORRECT_LANGUAGE_FEATURE_THRESHOLD = 0.6f; - -const bool Suggest::CORRECT_SPACE_OMISSION = true; -const bool Suggest::CORRECT_TRANSPOSITION = true; -const bool Suggest::CORRECT_INSERTION = true; -const bool Suggest::CORRECT_OMISSION_G = true; /** * Returns a set of suggestions for the given input touch points. The commitPoint argument indicates @@ -270,12 +263,8 @@ void Suggest::expandCurrentDicNodes(DicTraverseSession *traverseSession) const { // latest touch point yet. These are needed to apply look-ahead correction operations // that require special handling of the latest touch point. For example, with insertions // (e.g., "thiis" -> "this") the latest touch point should not be consumed at all. - if (CORRECT_TRANSPOSITION) { - processDicNodeAsTransposition(traverseSession, &dicNode); - } - if (CORRECT_INSERTION) { - processDicNodeAsInsertion(traverseSession, &dicNode); - } + processDicNodeAsTransposition(traverseSession, &dicNode); + processDicNodeAsInsertion(traverseSession, &dicNode); } else { // !isLookAheadCorrection // Only consider typing error corrections if the normalized compound distance is // below a spatial distance threshold. @@ -531,13 +520,10 @@ void Suggest::createNextWordDicNode(DicTraverseSession *traverseSession, DicNode DicNode newDicNode; DicNodeUtils::initAsRootWithPreviousWord(traverseSession->getDicRootPos(), traverseSession->getOffsetDict(), dicNode, &newDicNode); - Weighting::addCostAndForwardInputIndex(WEIGHTING, CT_NEW_WORD, traverseSession, dicNode, + const CorrectionType correctionType = spaceSubstitution ? + CT_NEW_WORD_SPACE_SUBSTITUTION : CT_NEW_WORD_SPACE_OMITTION; + Weighting::addCostAndForwardInputIndex(WEIGHTING, correctionType, traverseSession, dicNode, &newDicNode, traverseSession->getBigramCacheMap()); - if (spaceSubstitution) { - // Merge this with CT_NEW_WORD - Weighting::addCostAndForwardInputIndex(WEIGHTING, CT_SPACE_SUBSTITUTION, - traverseSession, 0, &newDicNode, 0 /* bigramCacheMap */); - } traverseSession->getDicTraverseCache()->copyPushNextActive(&newDicNode); } } // namespace latinime diff --git a/native/jni/src/suggest/core/suggest.h b/native/jni/src/suggest/core/suggest.h index becd6c1de..875cbe4e0 100644 --- a/native/jni/src/suggest/core/suggest.h +++ b/native/jni/src/suggest/core/suggest.h @@ -76,31 +76,16 @@ class Suggest : public SuggestInterface { void processDicNodeAsMatch(DicTraverseSession *traverseSession, DicNode *childDicNode) const; - // Dic nodes cache size for lookahead (autocompletion) - static const int LOOKAHEAD_DIC_NODES_CACHE_SIZE; - // Max characters to lookahead - static const int MAX_LOOKAHEAD; // Inputs longer than this will autocorrect if the suggestion is multi-word static const int MIN_LEN_FOR_MULTI_WORD_AUTOCORRECT; static const int MIN_CONTINUOUS_SUGGESTION_INPUT_SIZE; - // Base value for converting costs into scores (low so will not autocorrect without classifier) - static const float BASE_OUTPUT_SCORE; // Threshold for autocorrection classifier static const float AUTOCORRECT_CLASSIFICATION_THRESHOLD; - // Threshold for computing the language model feature for autocorrect classification - static const float AUTOCORRECT_LANGUAGE_FEATURE_THRESHOLD; - - // Typing error correction settings - static const bool CORRECT_SPACE_OMISSION; - static const bool CORRECT_TRANSPOSITION; - static const bool CORRECT_INSERTION; const Traversal *const TRAVERSAL; const Scoring *const SCORING; const Weighting *const WEIGHTING; - - static const bool CORRECT_OMISSION_G; }; } // namespace latinime #endif // LATINIME_SUGGEST_IMPL_H diff --git a/native/jni/src/suggest/policyimpl/typing/scoring_params.cpp b/native/jni/src/suggest/policyimpl/typing/scoring_params.cpp index 0fa684f01..11ccf1773 100644 --- a/native/jni/src/suggest/policyimpl/typing/scoring_params.cpp +++ b/native/jni/src/suggest/policyimpl/typing/scoring_params.cpp @@ -35,17 +35,17 @@ const float ScoringParams::INSERTION_COST = 0.670f; const float ScoringParams::INSERTION_COST_SAME_CHAR = 0.526f; const float ScoringParams::INSERTION_COST_FIRST_CHAR = 0.563f; const float ScoringParams::TRANSPOSITION_COST = 0.494f; -const float ScoringParams::SPACE_SUBSTITUTION_COST = 0.239f; +const float ScoringParams::SPACE_SUBSTITUTION_COST = 0.289f; const float ScoringParams::ADDITIONAL_PROXIMITY_COST = 0.380f; const float ScoringParams::SUBSTITUTION_COST = 0.363f; -const float ScoringParams::COST_NEW_WORD = 0.054f; +const float ScoringParams::COST_NEW_WORD = 0.024f; const float ScoringParams::COST_NEW_WORD_CAPITALIZED = 0.174f; const float ScoringParams::DISTANCE_WEIGHT_LANGUAGE = 1.123f; const float ScoringParams::COST_FIRST_LOOKAHEAD = 0.462f; const float ScoringParams::COST_LOOKAHEAD = 0.092f; const float ScoringParams::HAS_PROXIMITY_TERMINAL_COST = 0.126f; const float ScoringParams::HAS_EDIT_CORRECTION_TERMINAL_COST = 0.056f; -const float ScoringParams::HAS_MULTI_WORD_TERMINAL_COST = 0.136f; +const float ScoringParams::HAS_MULTI_WORD_TERMINAL_COST = 0.536f; const float ScoringParams::TYPING_BASE_OUTPUT_SCORE = 1.0f; const float ScoringParams::TYPING_MAX_OUTPUT_SCORE_PER_INPUT = 0.1f; const float ScoringParams::MAX_NORM_DISTANCE_FOR_EDIT = 0.1f; diff --git a/native/jni/src/suggest/policyimpl/typing/typing_traversal.cpp b/native/jni/src/suggest/policyimpl/typing/typing_traversal.cpp index 66f8ba9fa..e7e40e34d 100644 --- a/native/jni/src/suggest/policyimpl/typing/typing_traversal.cpp +++ b/native/jni/src/suggest/policyimpl/typing/typing_traversal.cpp @@ -18,7 +18,7 @@ namespace latinime { const bool TypingTraversal::CORRECT_OMISSION = true; -const bool TypingTraversal::CORRECT_SPACE_SUBSTITUTION = true; -const bool TypingTraversal::CORRECT_SPACE_OMISSION = true; +const bool TypingTraversal::CORRECT_NEW_WORD_SPACE_SUBSTITUTION = true; +const bool TypingTraversal::CORRECT_NEW_WORD_SPACE_OMISSION = true; const TypingTraversal TypingTraversal::sInstance; } // namespace latinime diff --git a/native/jni/src/suggest/policyimpl/typing/typing_traversal.h b/native/jni/src/suggest/policyimpl/typing/typing_traversal.h index f22029a2c..9f8347452 100644 --- a/native/jni/src/suggest/policyimpl/typing/typing_traversal.h +++ b/native/jni/src/suggest/policyimpl/typing/typing_traversal.h @@ -66,7 +66,7 @@ class TypingTraversal : public Traversal { AK_FORCE_INLINE bool isSpaceSubstitutionTerminal( const DicTraverseSession *const traverseSession, const DicNode *const dicNode) const { - if (!CORRECT_SPACE_SUBSTITUTION) { + if (!CORRECT_NEW_WORD_SPACE_SUBSTITUTION) { return false; } if (!canDoLookAheadCorrection(traverseSession, dicNode)) { @@ -80,7 +80,7 @@ class TypingTraversal : public Traversal { AK_FORCE_INLINE bool isSpaceOmissionTerminal( const DicTraverseSession *const traverseSession, const DicNode *const dicNode) const { - if (!CORRECT_SPACE_OMISSION) { + if (!CORRECT_NEW_WORD_SPACE_OMISSION) { return false; } const int inputSize = traverseSession->getInputSize(); @@ -173,8 +173,8 @@ class TypingTraversal : public Traversal { private: DISALLOW_COPY_AND_ASSIGN(TypingTraversal); static const bool CORRECT_OMISSION; - static const bool CORRECT_SPACE_SUBSTITUTION; - static const bool CORRECT_SPACE_OMISSION; + static const bool CORRECT_NEW_WORD_SPACE_SUBSTITUTION; + static const bool CORRECT_NEW_WORD_SPACE_OMISSION; static const TypingTraversal sInstance; TypingTraversal() {} diff --git a/native/jni/src/suggest/policyimpl/typing/typing_weighting.h b/native/jni/src/suggest/policyimpl/typing/typing_weighting.h index 2dcee343f..34d25ae1a 100644 --- a/native/jni/src/suggest/policyimpl/typing/typing_weighting.h +++ b/native/jni/src/suggest/policyimpl/typing/typing_weighting.h @@ -128,17 +128,19 @@ class TypingWeighting : public Weighting { return cost + weightedDistance; } - float getNewWordCost(const DicNode *const dicNode) const { + float getNewWordCost(const DicTraverseSession *const traverseSession, + const DicNode *const dicNode) const { const bool isCapitalized = dicNode->isCapitalized(); - return isCapitalized ? + const float cost = isCapitalized ? ScoringParams::COST_NEW_WORD_CAPITALIZED : ScoringParams::COST_NEW_WORD; + return cost * traverseSession->getMultiWordCostMultiplier(); } float getNewWordBigramCost( const DicTraverseSession *const traverseSession, const DicNode *const dicNode, hash_map_compat<int, int16_t> *const bigramCacheMap) const { return DicNodeUtils::getBigramNodeImprobability(traverseSession->getOffsetDict(), - dicNode, bigramCacheMap); + dicNode, bigramCacheMap) * ScoringParams::DISTANCE_WEIGHT_LANGUAGE; } float getCompletionCost(const DicTraverseSession *const traverseSession, @@ -162,13 +164,8 @@ class TypingWeighting : public Weighting { // because the input word shouldn't be treated as perfect const bool isExactMatch = !hasEditCount && !hasMultipleWords && !hasProximityErrors && isSameLength; - - const float totalPrevWordsLanguageCost = dicNode->getTotalPrevWordsLanguageCost(); const float languageImprobability = isExactMatch ? 0.0f : dicNodeLanguageImprobability; - const float languageWeight = ScoringParams::DISTANCE_WEIGHT_LANGUAGE; - // TODO: Caveat: The following equation should be: - // totalPrevWordsLanguageCost + (languageImprobability * languageWeight); - return (totalPrevWordsLanguageCost + languageImprobability) * languageWeight; + return languageImprobability * ScoringParams::DISTANCE_WEIGHT_LANGUAGE; } AK_FORCE_INLINE bool needsToNormalizeCompoundDistance() const { @@ -183,8 +180,13 @@ class TypingWeighting : public Weighting { return ScoringParams::SUBSTITUTION_COST; } - AK_FORCE_INLINE float getSpaceSubstitutionCost() const { - return ScoringParams::SPACE_SUBSTITUTION_COST; + AK_FORCE_INLINE float getSpaceSubstitutionCost( + const DicTraverseSession *const traverseSession, + const DicNode *const dicNode) const { + const bool isCapitalized = dicNode->isCapitalized(); + const float cost = ScoringParams::SPACE_SUBSTITUTION_COST + (isCapitalized ? + ScoringParams::COST_NEW_WORD_CAPITALIZED : ScoringParams::COST_NEW_WORD); + return cost * traverseSession->getMultiWordCostMultiplier(); } private: diff --git a/tests/src/com/android/inputmethod/keyboard/internal/MockKeyboardSwitcher.java b/tests/src/com/android/inputmethod/keyboard/internal/MockKeyboardSwitcher.java index eb484084e..74506d26a 100644 --- a/tests/src/com/android/inputmethod/keyboard/internal/MockKeyboardSwitcher.java +++ b/tests/src/com/android/inputmethod/keyboard/internal/MockKeyboardSwitcher.java @@ -19,6 +19,7 @@ package com.android.inputmethod.keyboard.internal; import android.text.TextUtils; import com.android.inputmethod.latin.Constants; +import com.android.inputmethod.latin.RecapitalizeStatus; public class MockKeyboardSwitcher implements KeyboardState.SwitchActions { public interface MockConstants { @@ -120,7 +121,7 @@ public class MockKeyboardSwitcher implements KeyboardState.SwitchActions { @Override public void requestUpdatingShiftState() { - mState.onUpdateShiftState(mAutoCapsState); + mState.onUpdateShiftState(mAutoCapsState, RecapitalizeStatus.NOT_A_RECAPITALIZE_MODE); } @Override @@ -162,7 +163,7 @@ public class MockKeyboardSwitcher implements KeyboardState.SwitchActions { } public void updateShiftState() { - mState.onUpdateShiftState(mAutoCapsState); + mState.onUpdateShiftState(mAutoCapsState, RecapitalizeStatus.NOT_A_RECAPITALIZE_MODE); } public void loadKeyboard() { diff --git a/tests/src/com/android/inputmethod/latin/RecapitalizeStatusTests.java b/tests/src/com/android/inputmethod/latin/RecapitalizeStatusTests.java index 4dfae4c94..9d7203e5a 100644 --- a/tests/src/com/android/inputmethod/latin/RecapitalizeStatusTests.java +++ b/tests/src/com/android/inputmethod/latin/RecapitalizeStatusTests.java @@ -24,29 +24,26 @@ import java.util.Locale; @SmallTest public class RecapitalizeStatusTests extends AndroidTestCase { public void testTrim() { - RecapitalizeStatus status = new RecapitalizeStatus(30, 40, "abcdefghij", - Locale.ENGLISH, " "); + final RecapitalizeStatus status = new RecapitalizeStatus(); + status.initialize(30, 40, "abcdefghij", Locale.ENGLISH, " "); status.trim(); assertEquals("abcdefghij", status.getRecapitalizedString()); assertEquals(30, status.getNewCursorStart()); assertEquals(40, status.getNewCursorEnd()); - status = new RecapitalizeStatus(30, 44, " abcdefghij", - Locale.ENGLISH, " "); + status.initialize(30, 44, " abcdefghij", Locale.ENGLISH, " "); status.trim(); assertEquals("abcdefghij", status.getRecapitalizedString()); assertEquals(34, status.getNewCursorStart()); assertEquals(44, status.getNewCursorEnd()); - status = new RecapitalizeStatus(30, 40, "abcdefgh ", - Locale.ENGLISH, " "); + status.initialize(30, 40, "abcdefgh ", Locale.ENGLISH, " "); status.trim(); assertEquals("abcdefgh", status.getRecapitalizedString()); assertEquals(30, status.getNewCursorStart()); assertEquals(38, status.getNewCursorEnd()); - status = new RecapitalizeStatus(30, 45, " abcdefghij ", - Locale.ENGLISH, " "); + status.initialize(30, 45, " abcdefghij ", Locale.ENGLISH, " "); status.trim(); assertEquals("abcdefghij", status.getRecapitalizedString()); assertEquals(33, status.getNewCursorStart()); @@ -54,8 +51,8 @@ public class RecapitalizeStatusTests extends AndroidTestCase { } public void testRotate() { - RecapitalizeStatus status = new RecapitalizeStatus(29, 40, "abcd efghij", - Locale.ENGLISH, " "); + final RecapitalizeStatus status = new RecapitalizeStatus(); + status.initialize(29, 40, "abcd efghij", Locale.ENGLISH, " "); status.rotate(); assertEquals("Abcd Efghij", status.getRecapitalizedString()); assertEquals(29, status.getNewCursorStart()); @@ -67,8 +64,7 @@ public class RecapitalizeStatusTests extends AndroidTestCase { status.rotate(); assertEquals("Abcd Efghij", status.getRecapitalizedString()); - status = new RecapitalizeStatus(29, 40, "Abcd Efghij", - Locale.ENGLISH, " "); + status.initialize(29, 40, "Abcd Efghij", Locale.ENGLISH, " "); status.rotate(); assertEquals("ABCD EFGHIJ", status.getRecapitalizedString()); assertEquals(29, status.getNewCursorStart()); @@ -80,8 +76,7 @@ public class RecapitalizeStatusTests extends AndroidTestCase { status.rotate(); assertEquals("ABCD EFGHIJ", status.getRecapitalizedString()); - status = new RecapitalizeStatus(29, 40, "ABCD EFGHIJ", - Locale.ENGLISH, " "); + status.initialize(29, 40, "ABCD EFGHIJ", Locale.ENGLISH, " "); status.rotate(); assertEquals("abcd efghij", status.getRecapitalizedString()); assertEquals(29, status.getNewCursorStart()); @@ -93,8 +88,7 @@ public class RecapitalizeStatusTests extends AndroidTestCase { status.rotate(); assertEquals("abcd efghij", status.getRecapitalizedString()); - status = new RecapitalizeStatus(29, 39, "AbCDefghij", - Locale.ENGLISH, " "); + status.initialize(29, 39, "AbCDefghij", Locale.ENGLISH, " "); status.rotate(); assertEquals("abcdefghij", status.getRecapitalizedString()); assertEquals(29, status.getNewCursorStart()); @@ -108,8 +102,7 @@ public class RecapitalizeStatusTests extends AndroidTestCase { status.rotate(); assertEquals("abcdefghij", status.getRecapitalizedString()); - status = new RecapitalizeStatus(29, 40, "Abcd efghij", - Locale.ENGLISH, " "); + status.initialize(29, 40, "Abcd efghij", Locale.ENGLISH, " "); status.rotate(); assertEquals("abcd efghij", status.getRecapitalizedString()); assertEquals(29, status.getNewCursorStart()); @@ -123,8 +116,7 @@ public class RecapitalizeStatusTests extends AndroidTestCase { status.rotate(); assertEquals("abcd efghij", status.getRecapitalizedString()); - status = new RecapitalizeStatus(30, 34, "grüß", Locale.GERMAN, " "); - status.rotate(); + status.initialize(30, 34, "grüß", Locale.GERMAN, " "); status.rotate(); assertEquals("Grüß", status.getRecapitalizedString()); assertEquals(30, status.getNewCursorStart()); assertEquals(34, status.getNewCursorEnd()); @@ -141,9 +133,7 @@ public class RecapitalizeStatusTests extends AndroidTestCase { assertEquals(30, status.getNewCursorStart()); assertEquals(34, status.getNewCursorEnd()); - - status = new RecapitalizeStatus(30, 33, "œuf", Locale.FRENCH, " "); - status.rotate(); + status.initialize(30, 33, "œuf", Locale.FRENCH, " "); status.rotate(); assertEquals("Œuf", status.getRecapitalizedString()); assertEquals(30, status.getNewCursorStart()); assertEquals(33, status.getNewCursorEnd()); @@ -160,8 +150,7 @@ public class RecapitalizeStatusTests extends AndroidTestCase { assertEquals(30, status.getNewCursorStart()); assertEquals(33, status.getNewCursorEnd()); - status = new RecapitalizeStatus(30, 33, "œUf", Locale.FRENCH, " "); - status.rotate(); + status.initialize(30, 33, "œUf", Locale.FRENCH, " "); status.rotate(); assertEquals("œuf", status.getRecapitalizedString()); assertEquals(30, status.getNewCursorStart()); assertEquals(33, status.getNewCursorEnd()); @@ -182,8 +171,7 @@ public class RecapitalizeStatusTests extends AndroidTestCase { assertEquals(30, status.getNewCursorStart()); assertEquals(33, status.getNewCursorEnd()); - status = new RecapitalizeStatus(30, 35, "école", Locale.FRENCH, " "); - status.rotate(); + status.initialize(30, 35, "école", Locale.FRENCH, " "); status.rotate(); assertEquals("École", status.getRecapitalizedString()); assertEquals(30, status.getNewCursorStart()); assertEquals(35, status.getNewCursorEnd()); diff --git a/tests/src/com/android/inputmethod/latin/RichInputConnectionTests.java b/tests/src/com/android/inputmethod/latin/RichInputConnectionTests.java index dc8837dab..aacd60f4d 100644 --- a/tests/src/com/android/inputmethod/latin/RichInputConnectionTests.java +++ b/tests/src/com/android/inputmethod/latin/RichInputConnectionTests.java @@ -85,6 +85,11 @@ public class RichInputConnectionTests extends AndroidTestCase { public boolean endBatchEdit() { return true; } + + @Override + public boolean finishComposingText() { + return true; + } } private class MockInputMethodService extends InputMethodService { diff --git a/tests/src/com/android/inputmethod/latin/StringUtilsTests.java b/tests/src/com/android/inputmethod/latin/StringUtilsTests.java index 98a50b730..1e3cc8ad4 100644 --- a/tests/src/com/android/inputmethod/latin/StringUtilsTests.java +++ b/tests/src/com/android/inputmethod/latin/StringUtilsTests.java @@ -215,4 +215,26 @@ public class StringUtilsTests extends AndroidTestCase { checkCapitalize("Lorem!Ipsum (dolor) Sit * Amet", "Lorem!Ipsum (Dolor) Sit * Amet", " \n,.;!?*()&", Locale.ENGLISH); } + + public void testLooksLikeURL() { + assertTrue(StringUtils.lastPartLooksLikeURL("http://www.google.")); + assertFalse(StringUtils.lastPartLooksLikeURL("word wo")); + assertTrue(StringUtils.lastPartLooksLikeURL("/etc/foo")); + assertFalse(StringUtils.lastPartLooksLikeURL("left/right")); + assertTrue(StringUtils.lastPartLooksLikeURL("www.goo")); + assertTrue(StringUtils.lastPartLooksLikeURL("www.")); + assertFalse(StringUtils.lastPartLooksLikeURL("U.S.A")); + assertFalse(StringUtils.lastPartLooksLikeURL("U.S.A.")); + assertTrue(StringUtils.lastPartLooksLikeURL("rtsp://foo.")); + assertTrue(StringUtils.lastPartLooksLikeURL("://")); + assertFalse(StringUtils.lastPartLooksLikeURL("abc/")); + assertTrue(StringUtils.lastPartLooksLikeURL("abc.def/ghi")); + assertFalse(StringUtils.lastPartLooksLikeURL("abc.def")); + // TODO: ideally this would not look like a URL, but to keep down the complexity of the + // code for now True is acceptable. + assertTrue(StringUtils.lastPartLooksLikeURL("abc./def")); + // TODO: ideally this would not look like a URL, but to keep down the complexity of the + // code for now True is acceptable. + assertTrue(StringUtils.lastPartLooksLikeURL(".abc/def")); + } } |