diff options
78 files changed, 937 insertions, 339 deletions
diff --git a/java/res/drawable-hdpi/sym_keyboard_smiley_lxx_light.png b/java/res/drawable-hdpi/sym_keyboard_smiley_lxx_light.png Binary files differindex 2cfe1d406..b1fe2c0b1 100644 --- a/java/res/drawable-hdpi/sym_keyboard_smiley_lxx_light.png +++ b/java/res/drawable-hdpi/sym_keyboard_smiley_lxx_light.png diff --git a/java/res/drawable-mdpi/sym_keyboard_smiley_lxx_light.png b/java/res/drawable-mdpi/sym_keyboard_smiley_lxx_light.png Binary files differindex ff49d58e1..e639416cd 100644 --- a/java/res/drawable-mdpi/sym_keyboard_smiley_lxx_light.png +++ b/java/res/drawable-mdpi/sym_keyboard_smiley_lxx_light.png diff --git a/java/res/drawable-xhdpi/sym_keyboard_smiley_lxx_light.png b/java/res/drawable-xhdpi/sym_keyboard_smiley_lxx_light.png Binary files differindex df3eba750..0d8c33bcd 100644 --- a/java/res/drawable-xhdpi/sym_keyboard_smiley_lxx_light.png +++ b/java/res/drawable-xhdpi/sym_keyboard_smiley_lxx_light.png diff --git a/java/res/drawable-xxhdpi/sym_keyboard_smiley_lxx_light.png b/java/res/drawable-xxhdpi/sym_keyboard_smiley_lxx_light.png Binary files differindex 08d4f8adc..ef5bf5a6f 100644 --- a/java/res/drawable-xxhdpi/sym_keyboard_smiley_lxx_light.png +++ b/java/res/drawable-xxhdpi/sym_keyboard_smiley_lxx_light.png diff --git a/java/res/values/attrs.xml b/java/res/values/attrs.xml index bfe385698..e89912a9d 100644 --- a/java/res/values/attrs.xml +++ b/java/res/values/attrs.xml @@ -242,7 +242,8 @@ <attr name="iconZwnjKey" format="reference" /> <attr name="iconZwjKey" format="reference" /> <attr name="iconImeKey" format="reference" /> - <attr name="iconEmojiKey" format="reference" /> + <attr name="iconEmojiActionKey" format="reference" /> + <attr name="iconEmojiNormalKey" format="reference" /> </declare-styleable> <declare-styleable name="Keyboard_GridRows"> diff --git a/java/res/values/donottranslate.xml b/java/res/values/donottranslate.xml index e3f0aeade..b25a20803 100644 --- a/java/res/values/donottranslate.xml +++ b/java/res/values/donottranslate.xml @@ -22,10 +22,12 @@ See {@link SettingsValues#needsToShowVoiceInputKey(SharedPreferences,Resources)} --> <string name="voice_mode_main">0</string> - <!-- Title for Latin keyboard debug settings activity / dialog --> - <string name="english_ime_debug_settings">Android keyboard Debug settings</string> + <!-- Title for Android keyboard debug settings activity / dialog --> + <string name="english_ime_debug_settings">Android Keyboard Debug settings</string> <string name="prefs_debug_mode">Debug Mode</string> <string name="prefs_force_non_distinct_multitouch">Force non-distinct multitouch</string> + <string name="prefs_force_physical_keyboard_special_key">Force physical keyboard special key</string> + <string name="prefs_show_ui_to_accept_typed_word">Show UI to accept typed word</string> <!-- Subtype locale display name exceptions. For each exception, there should be related string resources for display name that may have diff --git a/java/res/values/keyboard-icons-holo.xml b/java/res/values/keyboard-icons-holo.xml index d95ff8201..f5484bf4e 100644 --- a/java/res/values/keyboard-icons-holo.xml +++ b/java/res/values/keyboard-icons-holo.xml @@ -35,6 +35,7 @@ <item name="iconLanguageSwitchKey">@drawable/sym_keyboard_language_switch_dark</item> <item name="iconZwnjKey">@drawable/sym_keyboard_zwnj_holo_dark</item> <item name="iconZwjKey">@drawable/sym_keyboard_zwj_holo_dark</item> - <item name="iconEmojiKey">@drawable/sym_keyboard_smiley_holo_dark</item> + <item name="iconEmojiActionKey">@drawable/sym_keyboard_smiley_holo_dark</item> + <item name="iconEmojiNormalKey">@drawable/sym_keyboard_smiley_holo_dark</item> </style> </resources> diff --git a/java/res/values/keyboard-icons-lxx-dark.xml b/java/res/values/keyboard-icons-lxx-dark.xml index dfa585c16..305df02cd 100644 --- a/java/res/values/keyboard-icons-lxx-dark.xml +++ b/java/res/values/keyboard-icons-lxx-dark.xml @@ -40,6 +40,7 @@ <item name="iconLanguageSwitchKey">@drawable/sym_keyboard_language_switch_lxx_dark</item> <item name="iconZwnjKey">@drawable/sym_keyboard_zwnj_lxx_dark</item> <item name="iconZwjKey">@drawable/sym_keyboard_zwj_lxx_dark</item> - <item name="iconEmojiKey">@drawable/sym_keyboard_smiley_lxx_dark</item> + <item name="iconEmojiActionKey">@drawable/sym_keyboard_smiley_lxx_dark</item> + <item name="iconEmojiNormalKey">@drawable/sym_keyboard_smiley_lxx_dark</item> </style> </resources> diff --git a/java/res/values/keyboard-icons-lxx-light.xml b/java/res/values/keyboard-icons-lxx-light.xml index e8a3c56cd..866dc682f 100644 --- a/java/res/values/keyboard-icons-lxx-light.xml +++ b/java/res/values/keyboard-icons-lxx-light.xml @@ -40,6 +40,9 @@ <item name="iconLanguageSwitchKey">@drawable/sym_keyboard_language_switch_lxx_light</item> <item name="iconZwnjKey">@drawable/sym_keyboard_zwnj_lxx_light</item> <item name="iconZwjKey">@drawable/sym_keyboard_zwj_lxx_light</item> - <item name="iconEmojiKey">@drawable/sym_keyboard_smiley_lxx_light</item> + <!-- Use white emoji icon (for lxx_dark) because an action key has green/dark color background. --> + <item name="iconEmojiActionKey">@drawable/sym_keyboard_smiley_lxx_dark</item> + <!-- Use dark green emoji icon (for lxx_light) because a normal key has white color background. --> + <item name="iconEmojiNormalKey">@drawable/sym_keyboard_smiley_lxx_light</item> </style> </resources> diff --git a/java/res/xml-sw600dp/key_styles_common.xml b/java/res/xml-sw600dp/key_styles_common.xml index 877c796c7..1b52b1efd 100644 --- a/java/res/xml-sw600dp/key_styles_common.xml +++ b/java/res/xml-sw600dp/key_styles_common.xml @@ -105,7 +105,7 @@ latin:altCode="!code/key_space" /> <key-style latin:styleName="emojiKeyStyle" - latin:keySpec="!icon/emoji_key|!code/key_emoji" + latin:keySpec="!icon/emoji_normal_key|!code/key_emoji" latin:keyActionFlags="noKeyPreview" latin:backgroundType="functional" /> <key-style diff --git a/java/res/xml-sw600dp/key_styles_enter.xml b/java/res/xml-sw600dp/key_styles_enter.xml index d0167d336..c20523b2a 100644 --- a/java/res/xml-sw600dp/key_styles_enter.xml +++ b/java/res/xml-sw600dp/key_styles_enter.xml @@ -21,7 +21,6 @@ <merge xmlns:latin="http://schemas.android.com/apk/res/com.android.inputmethod.latin" > - <!-- TODO: Stop using many conditional cases for keyspec_emoji_key. There are way too many to maintain. --> <!-- Navigate more keys style --> <switch> <!-- latin:passwordInput="true" --> diff --git a/java/res/xml/key_styles_common.xml b/java/res/xml/key_styles_common.xml index bc739f7d0..2d006c992 100644 --- a/java/res/xml/key_styles_common.xml +++ b/java/res/xml/key_styles_common.xml @@ -80,7 +80,7 @@ <!-- emojiKeyStyle must be defined before including @xml/key_syles_enter. --> <key-style latin:styleName="emojiKeyStyle" - latin:keySpec="!icon/emoji_key|!code/key_emoji" + latin:keySpec="!icon/emoji_action_key|!code/key_emoji" latin:keyActionFlags="noKeyPreview" latin:backgroundType="action" /> <include diff --git a/java/res/xml/key_styles_enter.xml b/java/res/xml/key_styles_enter.xml index 960c79c18..55dab78c3 100644 --- a/java/res/xml/key_styles_enter.xml +++ b/java/res/xml/key_styles_enter.xml @@ -21,7 +21,7 @@ <merge xmlns:latin="http://schemas.android.com/apk/res/com.android.inputmethod.latin" > - <!-- TODO: Stop using many conditional cases for keyspec_emoji_key. There are way too many to maintain. --> + <!-- TODO: Stop using many conditional cases for keyspec_emoji_action_key. There are way too many to maintain. --> <!-- Navigate more keys style --> <switch> <!-- latin:passwordInput="true" --> @@ -182,7 +182,7 @@ <key-style latin:styleName="navigateMoreKeysStyle" latin:keyLabelFlags="hasPopupHint|preserveCase" - latin:moreKeys="!fixedColumnOrder!2,!needsDividers!,!text/keyspec_emoji_key,!text/keyspec_action_previous" /> + latin:moreKeys="!fixedColumnOrder!2,!needsDividers!,!text/keyspec_emoji_action_key,!text/keyspec_action_previous" /> </case> <case latin:imeAction="actionNext" @@ -191,7 +191,7 @@ <key-style latin:styleName="navigateMoreKeysStyle" latin:keyLabelFlags="hasPopupHint|preserveCase" - latin:moreKeys="!text/keyspec_emoji_key" /> + latin:moreKeys="!text/keyspec_emoji_action_key" /> </case> <case latin:imeAction="actionPrevious" @@ -200,7 +200,7 @@ <key-style latin:styleName="navigateMoreKeysStyle" latin:keyLabelFlags="hasPopupHint|preserveCase" - latin:moreKeys="!fixedColumnOrder!2,!needsDividers!,!text/keyspec_emoji_key,!text/keyspec_action_next" /> + latin:moreKeys="!fixedColumnOrder!2,!needsDividers!,!text/keyspec_emoji_action_key,!text/keyspec_action_next" /> </case> <case latin:imeAction="actionPrevious" @@ -209,7 +209,7 @@ <key-style latin:styleName="navigateMoreKeysStyle" latin:keyLabelFlags="hasPopupHint|preserveCase" - latin:moreKeys="!text/keyspec_emoji_key" /> + latin:moreKeys="!text/keyspec_emoji_action_key" /> </case> <case latin:navigateNext="true" @@ -218,7 +218,7 @@ <key-style latin:styleName="navigateMoreKeysStyle" latin:keyLabelFlags="hasPopupHint|preserveCase" - latin:moreKeys="!fixedColumnOrder!3,!needsDividers!,!text/keyspec_emoji_key,!text/keyspec_action_previous,!text/keyspec_action_next" /> + latin:moreKeys="!fixedColumnOrder!3,!needsDividers!,!text/keyspec_emoji_action_key,!text/keyspec_action_previous,!text/keyspec_action_next" /> </case> <case latin:navigateNext="true" @@ -227,7 +227,7 @@ <key-style latin:styleName="navigateMoreKeysStyle" latin:keyLabelFlags="hasPopupHint|preserveCase" - latin:moreKeys="!fixedColumnOrder!2,!needsDividers!,!text/keyspec_emoji_key,!text/keyspec_action_next" /> + latin:moreKeys="!fixedColumnOrder!2,!needsDividers!,!text/keyspec_emoji_action_key,!text/keyspec_action_next" /> </case> <case latin:navigateNext="false" @@ -236,7 +236,7 @@ <key-style latin:styleName="navigateMoreKeysStyle" latin:keyLabelFlags="hasPopupHint|preserveCase" - latin:moreKeys="!fixedColumnOrder!2,!needsDividers!,!text/keyspec_emoji_key,!text/keyspec_action_previous" /> + latin:moreKeys="!fixedColumnOrder!2,!needsDividers!,!text/keyspec_emoji_action_key,!text/keyspec_action_previous" /> </case> <case latin:navigateNext="false" @@ -245,7 +245,7 @@ <key-style latin:styleName="navigateMoreKeysStyle" latin:keyLabelFlags="hasPopupHint|preserveCase" - latin:moreKeys="!text/keyspec_emoji_key" /> + latin:moreKeys="!text/keyspec_emoji_action_key" /> </case> <default> <key-style diff --git a/java/res/xml/prefs_screen_debug.xml b/java/res/xml/prefs_screen_debug.xml index ae29a8a82..965369a90 100644 --- a/java/res/xml/prefs_screen_debug.xml +++ b/java/res/xml/prefs_screen_debug.xml @@ -31,6 +31,16 @@ android:defaultValue="false" android:persistent="true" /> <CheckBoxPreference + android:key="force_physical_keyboard_special_key" + android:title="@string/prefs_force_physical_keyboard_special_key" + android:defaultValue="false" + android:persistent="true" /> + <CheckBoxPreference + android:key="pref_show_ui_to_accept_typed_word" + android:title="@string/prefs_show_ui_to_accept_typed_word" + android:defaultValue="true" + android:persistent="true" /> + <CheckBoxPreference android:key="pref_sliding_key_input_preview" android:title="@string/sliding_key_input_preview" android:summary="@string/sliding_key_input_preview_summary" diff --git a/java/src/com/android/inputmethod/compat/BuildCompatUtils.java b/java/src/com/android/inputmethod/compat/BuildCompatUtils.java new file mode 100644 index 000000000..7d1717bd1 --- /dev/null +++ b/java/src/com/android/inputmethod/compat/BuildCompatUtils.java @@ -0,0 +1,43 @@ +/* + * Copyright (C) 2014 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.inputmethod.compat; + +import android.os.Build; + +public final class BuildCompatUtils { + private BuildCompatUtils() { + // This utility class is not publicly instantiable. + } + + private static final boolean IS_RELEASE_BUILD = Build.VERSION.CODENAME.equals("REL"); + + /** + * The "effective" API version. + * {@link android.os.Build.VERSION#SDK_INT} if the platform is a release build. + * {@link android.os.Build.VERSION#SDK_INT} plus 1 if the platform is a development build. + */ + public static final int EFFECTIVE_SDK_INT = IS_RELEASE_BUILD + ? Build.VERSION.SDK_INT + : Build.VERSION.SDK_INT + 1; + + /** + * API version for L-release. + */ + // TODO: Substitute this constant reference with Build.VERSION_CODES.L* once the *next* version + // becomes available. + public static final int VERSION_CODES_LXX = 21; +} diff --git a/java/src/com/android/inputmethod/compat/CursorAnchorInfoCompatWrapper.java b/java/src/com/android/inputmethod/compat/CursorAnchorInfoCompatWrapper.java new file mode 100644 index 000000000..2cec14240 --- /dev/null +++ b/java/src/com/android/inputmethod/compat/CursorAnchorInfoCompatWrapper.java @@ -0,0 +1,135 @@ +/* + * Copyright (C) 2014 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.inputmethod.compat; + +import android.graphics.Matrix; +import android.graphics.RectF; + +import com.android.inputmethod.annotations.UsedForTesting; + +import java.lang.reflect.Method; + +@UsedForTesting +public final class CursorAnchorInfoCompatWrapper { + // Note that CursorAnchorInfo has been introduced in API level XX (Build.VERSION_CODE.LXX). + private static Class<?> getCursorAnchorInfoClass() { + try { + return Class.forName("android.view.inputmethod.CursorAnchorInfo"); + } catch (ClassNotFoundException e) { + return null; + } + } + private static final Class<?> CLASS; + private static final Method METHOD_GET_CHARACTER_RECT; + private static final Method METHOD_GET_CHARACTER_RECT_FLAGS; + private static final Method METHOD_GET_COMPOSING_TEXT; + private static final Method METHOD_GET_COMPOSING_TEXT_START; + private static final Method METHOD_GET_MATRIX; + static { + CLASS = getCursorAnchorInfoClass(); + METHOD_GET_CHARACTER_RECT = CompatUtils.getMethod(CLASS, "getCharacterRect", int.class); + METHOD_GET_CHARACTER_RECT_FLAGS = CompatUtils.getMethod(CLASS, "getCharacterRectFlags", + int.class); + METHOD_GET_COMPOSING_TEXT = CompatUtils.getMethod(CLASS, "getComposingText"); + METHOD_GET_COMPOSING_TEXT_START = CompatUtils.getMethod(CLASS, "getComposingTextStart"); + METHOD_GET_MATRIX = CompatUtils.getMethod(CLASS, "getMatrix"); + } + + @UsedForTesting + public static boolean isAvailable() { + return CLASS != null; + } + + public static final int CHARACTER_RECT_TYPE_MASK = 0x0f; + + /** + * Type for {@link #CHARACTER_RECT_TYPE_MASK}: the editor did not specify any type of this + * character. Editor authors should not use this flag. + */ + public static final int CHARACTER_RECT_TYPE_UNSPECIFIED = 0; + + /** + * Type for {@link #CHARACTER_RECT_TYPE_MASK}: the character is entirely visible. + */ + public static final int CHARACTER_RECT_TYPE_FULLY_VISIBLE = 1; + + /** + * Type for {@link #CHARACTER_RECT_TYPE_MASK}: some area of the character is invisible. + */ + public static final int CHARACTER_RECT_TYPE_PARTIALLY_VISIBLE = 2; + + /** + * Type for {@link #CHARACTER_RECT_TYPE_MASK}: the character is entirely invisible. + */ + public static final int CHARACTER_RECT_TYPE_INVISIBLE = 3; + + /** + * Type for {@link #CHARACTER_RECT_TYPE_MASK}: the editor gave up to calculate the rectangle + * for this character. Input method authors should ignore the returned rectangle. + */ + public static final int CHARACTER_RECT_TYPE_NOT_FEASIBLE = 4; + + private Object mInstance; + + private CursorAnchorInfoCompatWrapper(final Object instance) { + mInstance = instance; + } + + @UsedForTesting + public static CursorAnchorInfoCompatWrapper fromObject(final Object instance) { + if (!isAvailable()) { + return new CursorAnchorInfoCompatWrapper(null); + } + return new CursorAnchorInfoCompatWrapper(instance); + } + + private static final class FakeHolder { + static CursorAnchorInfoCompatWrapper sInstance = new CursorAnchorInfoCompatWrapper(null); + } + + @UsedForTesting + public static CursorAnchorInfoCompatWrapper getFake() { + return FakeHolder.sInstance; + } + + public CharSequence getComposingText() { + return (CharSequence) CompatUtils.invoke(mInstance, null, METHOD_GET_COMPOSING_TEXT); + } + + private static int COMPOSING_TEXT_START_DEFAULT = -1; + public int getComposingTextStart() { + if (mInstance == null || METHOD_GET_COMPOSING_TEXT_START == null) { + return COMPOSING_TEXT_START_DEFAULT; + } + return (int) CompatUtils.invoke(mInstance, null, METHOD_GET_COMPOSING_TEXT_START); + } + + public Matrix getMatrix() { + return (Matrix) CompatUtils.invoke(mInstance, null, METHOD_GET_MATRIX); + } + + public RectF getCharacterRect(final int index) { + return (RectF) CompatUtils.invoke(mInstance, null, METHOD_GET_CHARACTER_RECT, index); + } + + public int getCharacterRectFlags(final int index) { + if (mInstance == null || METHOD_GET_CHARACTER_RECT_FLAGS == null) { + return CHARACTER_RECT_TYPE_UNSPECIFIED; + } + return (int) CompatUtils.invoke(mInstance, null, METHOD_GET_CHARACTER_RECT_FLAGS, index); + } +} diff --git a/java/src/com/android/inputmethod/event/Event.java b/java/src/com/android/inputmethod/event/Event.java index f02f7885a..ef5b04747 100644 --- a/java/src/com/android/inputmethod/event/Event.java +++ b/java/src/com/android/inputmethod/event/Event.java @@ -257,6 +257,8 @@ public class Event { public boolean isConsumed() { return 0 != (FLAG_CONSUMED & mFlags); } + public boolean isGesture() { return EVENT_TYPE_GESTURE == mEventType; } + // Returns whether this is a fake key press from the suggestion strip. This happens with // punctuation signs selected from the suggestion strip. public boolean isSuggestionStripPress() { diff --git a/java/src/com/android/inputmethod/event/InputTransaction.java b/java/src/com/android/inputmethod/event/InputTransaction.java index b18bf5638..5bc9111de 100644 --- a/java/src/com/android/inputmethod/event/InputTransaction.java +++ b/java/src/com/android/inputmethod/event/InputTransaction.java @@ -33,7 +33,7 @@ public class InputTransaction { // Initial conditions public final SettingsValues mSettingsValues; - private final Event mEvent; + public final Event mEvent; public final long mTimestamp; public final int mSpaceState; public final int mShiftState; diff --git a/java/src/com/android/inputmethod/keyboard/KeyboardSwitcher.java b/java/src/com/android/inputmethod/keyboard/KeyboardSwitcher.java index f35126750..140e76879 100644 --- a/java/src/com/android/inputmethod/keyboard/KeyboardSwitcher.java +++ b/java/src/com/android/inputmethod/keyboard/KeyboardSwitcher.java @@ -18,6 +18,7 @@ package com.android.inputmethod.keyboard; import android.content.Context; import android.content.SharedPreferences; +import android.content.res.Configuration; import android.content.res.Resources; import android.preference.PreferenceManager; import android.util.Log; @@ -233,11 +234,21 @@ public final class KeyboardSwitcher implements KeyboardState.SwitchActions { } private void setMainKeyboardFrame() { - mMainKeyboardFrame.setVisibility(View.VISIBLE); + mMainKeyboardFrame.setVisibility(hasHardwareKeyboard() ? View.GONE : View.VISIBLE); mEmojiPalettesView.setVisibility(View.GONE); mEmojiPalettesView.stopEmojiPalettes(); } + // TODO: Move this boolean to a member of {@link SettingsValues} and reset it + // at {@link LatinIME#onConfigurationChanged(Configuration)}. + public boolean hasHardwareKeyboard() { + // Copied from {@link InputMethodServce#onEvaluateInputViewShown()}. + final Configuration config = mLatinIME.getResources().getConfiguration(); + final boolean noHardwareKeyboard = config.keyboard == Configuration.KEYBOARD_NOKEYS + || config.hardKeyboardHidden == Configuration.HARDKEYBOARDHIDDEN_YES; + return !noHardwareKeyboard; + } + // Implements {@link KeyboardState.SwitchActions}. @Override public void setEmojiKeyboard() { @@ -249,6 +260,14 @@ public final class KeyboardSwitcher implements KeyboardState.SwitchActions { mEmojiPalettesView.setVisibility(View.VISIBLE); } + public void onToggleEmojiKeyboard() { + if (isShowingEmojiPalettes()) { + setAlphabetKeyboard(); + } else { + setEmojiKeyboard(); + } + } + // Implements {@link KeyboardState.SwitchActions}. @Override public void setSymbolsShiftedKeyboard() { diff --git a/java/src/com/android/inputmethod/keyboard/KeyboardTheme.java b/java/src/com/android/inputmethod/keyboard/KeyboardTheme.java index 4c2e0dd1d..0cd606d19 100644 --- a/java/src/com/android/inputmethod/keyboard/KeyboardTheme.java +++ b/java/src/com/android/inputmethod/keyboard/KeyboardTheme.java @@ -17,11 +17,11 @@ package com.android.inputmethod.keyboard; import android.content.SharedPreferences; -import android.os.Build; import android.os.Build.VERSION_CODES; import android.util.Log; import com.android.inputmethod.annotations.UsedForTesting; +import com.android.inputmethod.compat.BuildCompatUtils; import com.android.inputmethod.latin.R; import java.util.Arrays; @@ -47,8 +47,7 @@ public final class KeyboardTheme implements Comparable<KeyboardTheme> { VERSION_CODES.ICE_CREAM_SANDWICH), new KeyboardTheme(THEME_ID_LXX_LIGHT, R.style.KeyboardTheme_LXX_Light, // Default theme for LXX. - // TODO: Update this constant once the *next* version becomes available. - VERSION_CODES.CUR_DEVELOPMENT), + BuildCompatUtils.VERSION_CODES_LXX), new KeyboardTheme(THEME_ID_LXX_DARK, R.style.KeyboardTheme_LXX_Dark, VERSION_CODES.BASE), }; @@ -99,15 +98,6 @@ public final class KeyboardTheme implements Comparable<KeyboardTheme> { return null; } - private static int getSdkVersion() { - final int sdkVersion = Build.VERSION.SDK_INT; - // TODO: Consider to remove this check once the *next* version becomes available. - if (sdkVersion > VERSION_CODES.KITKAT) { - return VERSION_CODES.CUR_DEVELOPMENT; - } - return sdkVersion; - } - @UsedForTesting static KeyboardTheme getDefaultKeyboardTheme(final SharedPreferences prefs, final int sdkVersion) { @@ -140,7 +130,7 @@ public final class KeyboardTheme implements Comparable<KeyboardTheme> { public static void saveKeyboardThemeId(final String themeIdString, final SharedPreferences prefs) { - saveKeyboardThemeId(themeIdString, prefs, getSdkVersion()); + saveKeyboardThemeId(themeIdString, prefs, BuildCompatUtils.EFFECTIVE_SDK_INT); } @UsedForTesting @@ -159,7 +149,7 @@ public final class KeyboardTheme implements Comparable<KeyboardTheme> { } public static KeyboardTheme getKeyboardTheme(final SharedPreferences prefs) { - return getKeyboardTheme(prefs, getSdkVersion()); + return getKeyboardTheme(prefs, BuildCompatUtils.EFFECTIVE_SDK_INT); } @UsedForTesting diff --git a/java/src/com/android/inputmethod/keyboard/MainKeyboardView.java b/java/src/com/android/inputmethod/keyboard/MainKeyboardView.java index 702efb3d7..1ef53a65d 100644 --- a/java/src/com/android/inputmethod/keyboard/MainKeyboardView.java +++ b/java/src/com/android/inputmethod/keyboard/MainKeyboardView.java @@ -46,6 +46,7 @@ import com.android.inputmethod.keyboard.internal.GestureTrailsDrawingPreview; import com.android.inputmethod.keyboard.internal.KeyDrawParams; import com.android.inputmethod.keyboard.internal.KeyPreviewChoreographer; import com.android.inputmethod.keyboard.internal.KeyPreviewDrawParams; +import com.android.inputmethod.keyboard.internal.KeyPreviewView; import com.android.inputmethod.keyboard.internal.LanguageOnSpacebarHelper; import com.android.inputmethod.keyboard.internal.MoreKeySpec; import com.android.inputmethod.keyboard.internal.NonDistinctMultitouchHelper; @@ -764,6 +765,9 @@ public final class MainKeyboardView extends KeyboardView implements PointerTrack public void startDisplayLanguageOnSpacebar(final boolean subtypeChanged, final int languageOnSpacebarFormatType, final boolean hasMultipleEnabledIMEsOrSubtypes) { + if (subtypeChanged) { + KeyPreviewView.clearTextCache(); + } mLanguageOnSpacebarFormatType = languageOnSpacebarFormatType; mHasMultipleEnabledIMEsOrSubtypes = hasMultipleEnabledIMEsOrSubtypes; final ObjectAnimator animator = mLanguageOnSpacebarFadeoutAnimator; diff --git a/java/src/com/android/inputmethod/keyboard/emoji/EmojiCategory.java b/java/src/com/android/inputmethod/keyboard/emoji/EmojiCategory.java index 512d4615d..0f9dc855b 100644 --- a/java/src/com/android/inputmethod/keyboard/emoji/EmojiCategory.java +++ b/java/src/com/android/inputmethod/keyboard/emoji/EmojiCategory.java @@ -24,6 +24,7 @@ import android.os.Build; import android.util.Log; import android.util.Pair; +import com.android.inputmethod.compat.BuildCompatUtils; import com.android.inputmethod.keyboard.Key; import com.android.inputmethod.keyboard.Keyboard; import com.android.inputmethod.keyboard.KeyboardId; @@ -121,9 +122,7 @@ final class EmojiCategory { sCategoryTabIconAttr[i], 0); } addShownCategoryId(EmojiCategory.ID_RECENTS); - if (Build.VERSION.SDK_INT > Build.VERSION_CODES.JELLY_BEAN_MR2 - || android.os.Build.VERSION.CODENAME.equalsIgnoreCase("KeyLimePie") - || android.os.Build.VERSION.CODENAME.equalsIgnoreCase("KitKat")) { + if (BuildCompatUtils.EFFECTIVE_SDK_INT >= Build.VERSION_CODES.KITKAT) { addShownCategoryId(EmojiCategory.ID_PEOPLE); addShownCategoryId(EmojiCategory.ID_OBJECTS); addShownCategoryId(EmojiCategory.ID_NATURE); diff --git a/java/src/com/android/inputmethod/keyboard/internal/KeyPreviewView.java b/java/src/com/android/inputmethod/keyboard/internal/KeyPreviewView.java index 360faf829..24538605a 100644 --- a/java/src/com/android/inputmethod/keyboard/internal/KeyPreviewView.java +++ b/java/src/com/android/inputmethod/keyboard/internal/KeyPreviewView.java @@ -17,7 +17,10 @@ package com.android.inputmethod.keyboard.internal; import android.content.Context; +import android.graphics.Rect; import android.graphics.drawable.Drawable; +import android.text.TextPaint; +import android.text.TextUtils; import android.util.AttributeSet; import android.util.TypedValue; import android.view.Gravity; @@ -26,6 +29,8 @@ import android.widget.TextView; import com.android.inputmethod.keyboard.Key; import com.android.inputmethod.latin.R; +import java.util.HashSet; + /** * The pop up key preview view. */ @@ -34,6 +39,9 @@ public class KeyPreviewView extends TextView { public static final int POSITION_LEFT = 1; public static final int POSITION_RIGHT = 2; + private final Rect mBackgroundPadding = new Rect(); + private static final HashSet<String> sNoScaleXTextSet = new HashSet<>(); + public KeyPreviewView(final Context context, final AttributeSet attrs) { this(context, attrs, 0); } @@ -58,7 +66,48 @@ public class KeyPreviewView extends TextView { setTextSize(TypedValue.COMPLEX_UNIT_PX, key.selectPreviewTextSize(drawParams)); setTypeface(key.selectPreviewTypeface(drawParams)); // TODO Should take care of temporaryShiftLabel here. - setText(key.getPreviewLabel()); + setTextAndScaleX(key.getPreviewLabel()); + } + + private void setTextAndScaleX(final String text) { + setTextScaleX(1.0f); + setText(text); + if (sNoScaleXTextSet.contains(text)) { + return; + } + // TODO: Override {@link #setBackground(Drawable)} that is supported from API 16 and + // calculate maximum text width. + final Drawable background = getBackground(); + if (background == null) { + return; + } + background.getPadding(mBackgroundPadding); + final int maxWidth = background.getIntrinsicWidth() - mBackgroundPadding.left + - mBackgroundPadding.right; + final float width = getTextWidth(text, getPaint()); + if (width <= maxWidth) { + sNoScaleXTextSet.add(text); + return; + } + setTextScaleX(maxWidth / width); + } + + public static void clearTextCache() { + sNoScaleXTextSet.clear(); + } + + private static float getTextWidth(final String text, final TextPaint paint) { + if (TextUtils.isEmpty(text)) { + return 0.0f; + } + final int len = text.length(); + final float[] widths = new float[len]; + final int count = paint.getTextWidths(text, 0, len, widths); + float width = 0; + for (int i = 0; i < count; i++) { + width += widths[i]; + } + return width; } // Background state set diff --git a/java/src/com/android/inputmethod/keyboard/internal/KeyboardIconsSet.java b/java/src/com/android/inputmethod/keyboard/internal/KeyboardIconsSet.java index 09550c4cb..e1f302c1e 100644 --- a/java/src/com/android/inputmethod/keyboard/internal/KeyboardIconsSet.java +++ b/java/src/com/android/inputmethod/keyboard/internal/KeyboardIconsSet.java @@ -53,7 +53,8 @@ public final class KeyboardIconsSet { public static final String NAME_LANGUAGE_SWITCH_KEY = "language_switch_key"; public static final String NAME_ZWNJ_KEY = "zwnj_key"; public static final String NAME_ZWJ_KEY = "zwj_key"; - public static final String NAME_EMOJI_KEY = "emoji_key"; + public static final String NAME_EMOJI_ACTION_KEY = "emoji_action_key"; + public static final String NAME_EMOJI_NORMAL_KEY = "emoji_normal_key"; private static final SparseIntArray ATTR_ID_TO_ICON_ID = new SparseIntArray(); @@ -81,7 +82,8 @@ public final class KeyboardIconsSet { NAME_LANGUAGE_SWITCH_KEY, R.styleable.Keyboard_iconLanguageSwitchKey, NAME_ZWNJ_KEY, R.styleable.Keyboard_iconZwnjKey, NAME_ZWJ_KEY, R.styleable.Keyboard_iconZwjKey, - NAME_EMOJI_KEY, R.styleable.Keyboard_iconEmojiKey, + NAME_EMOJI_ACTION_KEY, R.styleable.Keyboard_iconEmojiActionKey, + NAME_EMOJI_NORMAL_KEY, R.styleable.Keyboard_iconEmojiNormalKey, }; private static int NUM_ICONS = NAMES_AND_ATTR_IDS.length / 2; diff --git a/java/src/com/android/inputmethod/keyboard/internal/KeyboardTextsTable.java b/java/src/com/android/inputmethod/keyboard/internal/KeyboardTextsTable.java index e6897bf1d..31bc549ca 100644 --- a/java/src/com/android/inputmethod/keyboard/internal/KeyboardTextsTable.java +++ b/java/src/com/android/inputmethod/keyboard/internal/KeyboardTextsTable.java @@ -250,7 +250,7 @@ public final class KeyboardTextsTable { /* 164: 0 */ "morekeys_single_quote", /* 165: 0 */ "morekeys_double_quote", /* 166: 0 */ "morekeys_tablet_double_quote", - /* 167: 0 */ "keyspec_emoji_key", + /* 167: 0 */ "keyspec_emoji_action_key", }; private static final String EMPTY = ""; @@ -464,7 +464,7 @@ public final class KeyboardTextsTable { /* morekeys_single_quote */ "!fixedColumnOrder!5,!text/single_quotes,!text/single_angle_quotes", /* morekeys_double_quote */ "!fixedColumnOrder!5,!text/double_quotes,!text/double_angle_quotes", /* morekeys_tablet_double_quote */ "!fixedColumnOrder!6,!text/double_quotes,!text/single_quotes,!text/double_angle_quotes,!text/single_angle_quotes", - /* keyspec_emoji_key */ "!icon/emoji_key|!code/key_emoji", + /* keyspec_emoji_action_key */ "!icon/emoji_action_key|!code/key_emoji", }; /* Locale af: Afrikaans */ diff --git a/java/src/com/android/inputmethod/latin/ExpandableBinaryDictionary.java b/java/src/com/android/inputmethod/latin/ExpandableBinaryDictionary.java index c11a220a4..22b91700c 100644 --- a/java/src/com/android/inputmethod/latin/ExpandableBinaryDictionary.java +++ b/java/src/com/android/inputmethod/latin/ExpandableBinaryDictionary.java @@ -647,7 +647,7 @@ abstract public class ExpandableBinaryDictionary extends Dictionary { @UsedForTesting public void waitAllTasksForTests() { final CountDownLatch countDownLatch = new CountDownLatch(1); - ExecutorUtils.getExecutor(mDictName).execute(new Runnable() { + asyncExecuteTaskWithWriteLock(new Runnable() { @Override public void run() { countDownLatch.countDown(); diff --git a/java/src/com/android/inputmethod/latin/LatinIME.java b/java/src/com/android/inputmethod/latin/LatinIME.java index 2e3cd6b6f..71fd10e83 100644 --- a/java/src/com/android/inputmethod/latin/LatinIME.java +++ b/java/src/com/android/inputmethod/latin/LatinIME.java @@ -49,11 +49,13 @@ import android.view.ViewGroup.LayoutParams; import android.view.Window; import android.view.WindowManager; import android.view.inputmethod.CompletionInfo; +import android.view.inputmethod.CursorAnchorInfo; import android.view.inputmethod.EditorInfo; import android.view.inputmethod.InputMethodSubtype; import com.android.inputmethod.accessibility.AccessibilityUtils; import com.android.inputmethod.annotations.UsedForTesting; +import com.android.inputmethod.compat.CursorAnchorInfoCompatWrapper; import com.android.inputmethod.compat.InputConnectionCompatUtils; import com.android.inputmethod.compat.InputMethodServiceCompatUtils; import com.android.inputmethod.dictionarypack.DictionaryPackConstants; @@ -136,7 +138,7 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen new Runnable() { @Override public void run() { - mHandler.postUpdateSuggestionStrip(); + mHandler.postUpdateSuggestionStrip(SuggestedWords.INPUT_STYLE_NONE); } }); private final InputLogic mInputLogic = new InputLogic(this /* LatinIME */, @@ -145,6 +147,8 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen // If it turns out we need several, it will get grown seamlessly. final SparseArray<HardwareEventDecoder> mHardwareEventDecoders = new SparseArray<>(1); + // TODO: Move these {@link View}s to {@link KeyboardSwitcher}. + private View mInputView; private View mExtractArea; private View mKeyPreviewBackingView; private SuggestionStripView mSuggestionStripView; @@ -153,6 +157,7 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen @UsedForTesting final KeyboardSwitcher mKeyboardSwitcher; private final SubtypeSwitcher mSubtypeSwitcher; private final SubtypeState mSubtypeState = new SubtypeState(); + private final SpecialKeyDetector mSpecialKeyDetector; // Object for reacting to adding/removing a dictionary pack. private final BroadcastReceiver mDictionaryPackInstallReceiver = @@ -215,7 +220,7 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen case MSG_UPDATE_SUGGESTION_STRIP: cancelUpdateSuggestionStrip(); latinIme.mInputLogic.performUpdateSuggestionStripSync( - latinIme.mSettings.getCurrent()); + latinIme.mSettings.getCurrent(), msg.arg1 /* inputStyle */); break; case MSG_UPDATE_SHIFT_STATE: switcher.requestUpdatingShiftState(latinIme.getCurrentAutoCapsState(), @@ -265,8 +270,9 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen } } - public void postUpdateSuggestionStrip() { - sendMessageDelayed(obtainMessage(MSG_UPDATE_SUGGESTION_STRIP), mDelayUpdateSuggestions); + public void postUpdateSuggestionStrip(final int inputStyle) { + sendMessageDelayed(obtainMessage(MSG_UPDATE_SUGGESTION_STRIP, inputStyle, + 0 /* ignored */), mDelayUpdateSuggestions); } public void postReopenDictionaries() { @@ -419,9 +425,12 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen latinIme.getCurrentInputConnection(), true /* enableMonitor */); } if (ProductionFlags.ENABLE_CURSOR_ANCHOR_INFO_CALLBACK) { - InputConnectionCompatUtils.requestCursorAnchorInfo( - latinIme.getCurrentInputConnection(), true /* enableMonitor */, - true /* requestImmediateCallback */); + // AcceptTypedWord feature relies on CursorAnchorInfo. + if (latinIme.mSettings.getCurrent().mShouldShowUiToAcceptTypedWord) { + InputConnectionCompatUtils.requestCursorAnchorInfo( + latinIme.getCurrentInputConnection(), true /* enableMonitor */, + true /* requestImmediateCallback */); + } } } } @@ -514,6 +523,7 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen mSettings = Settings.getInstance(); mSubtypeSwitcher = SubtypeSwitcher.getInstance(); mKeyboardSwitcher = KeyboardSwitcher.getInstance(); + mSpecialKeyDetector = new SpecialKeyDetector(this); mIsHardwareAcceleratedDrawingEnabled = InputMethodServiceCompatUtils.enableHardwareAcceleration(this); Log.i(TAG, "Hardware accelerated drawing: " + mIsHardwareAcceleratedDrawingEnabled); @@ -709,6 +719,7 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen @Override public void setInputView(final View view) { super.setInputView(view); + mInputView = view; mExtractArea = getWindow().getWindow().getDecorView() .findViewById(android.R.id.extractArea); mKeyPreviewBackingView = view.findViewById(R.id.key_preview_backing); @@ -967,6 +978,16 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen super.onUpdateCursor(rect); } + // We cannot mark this method as @Override until new SDK becomes publicly available. + // @Override + public void onUpdateCursorAnchorInfo(final CursorAnchorInfo info) { + if (ProductionFlags.ENABLE_CURSOR_ANCHOR_INFO_CALLBACK) { + final CursorAnchorInfoCompatWrapper wrapper = + CursorAnchorInfoCompatWrapper.fromObject(info); + // TODO: Implement here + } + } + /** * This is called when the user has clicked on the extracted text view, * when running in fullscreen mode. The default implementation hides @@ -1040,7 +1061,8 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen applicationSpecifiedCompletions); final SuggestedWords suggestedWords = new SuggestedWords(applicationSuggestedWords, null /* rawSuggestions */, false /* typedWordValid */, false /* willAutoCorrect */, - false /* isObsoleteSuggestions */, false /* isPrediction */); + false /* isObsoleteSuggestions */, false /* isPrediction */, + SuggestedWords.INPUT_STYLE_APPLICATION_SPECIFIED /* inputStyle */); // When in fullscreen mode, show completions generated by the application forcibly setSuggestedWords(suggestedWords); } @@ -1079,6 +1101,14 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen if (visibleKeyboardView == null || !hasSuggestionStripView()) { return; } + final boolean hasHardwareKeyboard = mKeyboardSwitcher.hasHardwareKeyboard(); + if (hasHardwareKeyboard && visibleKeyboardView.getVisibility() == View.GONE) { + // If there is a hardware keyboard and a visible software keyboard view has been hidden, + // no visual element will be shown on the screen. + outInsets.touchableInsets = mInputView.getHeight(); + outInsets.visibleTopInsets = mInputView.getHeight(); + return; + } final int adjustedBackingHeight = getAdjustedBackingViewHeight(); final boolean backingGone = (mKeyPreviewBackingView.getVisibility() == View.GONE); final int backingHeight = backingGone ? 0 : adjustedBackingHeight; @@ -1111,7 +1141,17 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen } @Override + public boolean onEvaluateInputViewShown() { + // Always show {@link InputView}. + return true; + } + + @Override public boolean onEvaluateFullscreenMode() { + if (mKeyboardSwitcher.hasHardwareKeyboard()) { + // If there is a hardware keyboard, disable full screen mode. + return false; + } // Reread resource value here, because this method is called by the framework as needed. final boolean isFullscreenModeAllowed = Settings.readUseFullscreenMode(getResources()); if (super.onEvaluateFullscreenMode() && isFullscreenModeAllowed) { @@ -1413,7 +1453,7 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen } // TODO[IL]: Move this out of LatinIME. - public void getSuggestedWords(final int sessionId, final int sequenceNumber, + public void getSuggestedWords(final int inputStyle, final int sequenceNumber, final OnGetSuggestedWordsCallback callback) { final Keyboard keyboard = mKeyboardSwitcher.getKeyboard(); if (keyboard == null) { @@ -1421,7 +1461,7 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen return; } mInputLogic.getSuggestedWords(mSettings.getCurrent(), keyboard.getProximityInfo(), - mKeyboardSwitcher.getKeyboardShiftMode(), sessionId, sequenceNumber, callback); + mKeyboardSwitcher.getKeyboardShiftMode(), inputStyle, sequenceNumber, callback); } @Override @@ -1505,7 +1545,16 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen default: // SHIFT_NO_UPDATE } if (inputTransaction.requiresUpdateSuggestions()) { - mHandler.postUpdateSuggestionStrip(); + final int inputStyle; + if (inputTransaction.mEvent.isSuggestionStripPress()) { + // Suggestion strip press: no input. + inputStyle = SuggestedWords.INPUT_STYLE_NONE; + } else if (inputTransaction.mEvent.isGesture()) { + inputStyle = SuggestedWords.INPUT_STYLE_TAIL_BATCH; + } else { + inputStyle = SuggestedWords.INPUT_STYLE_TYPING; + } + mHandler.postUpdateSuggestionStrip(inputStyle); } if (inputTransaction.didAffectContents()) { mSubtypeState.setCurrentSubtypeHasBeenUsed(); @@ -1568,6 +1617,7 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen // Hooks for hardware keyboard @Override public boolean onKeyDown(final int keyCode, final KeyEvent keyEvent) { + mSpecialKeyDetector.onKeyDown(keyEvent); if (!ProductionFlags.IS_HARDWARE_KEYBOARD_SUPPORTED) { return super.onKeyDown(keyCode, keyEvent); } @@ -1587,12 +1637,16 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen } @Override - public boolean onKeyUp(final int keyCode, final KeyEvent event) { - final long keyIdentifier = event.getDeviceId() << 32 + event.getKeyCode(); + public boolean onKeyUp(final int keyCode, final KeyEvent keyEvent) { + mSpecialKeyDetector.onKeyUp(keyEvent); + if (!ProductionFlags.IS_HARDWARE_KEYBOARD_SUPPORTED) { + return super.onKeyUp(keyCode, keyEvent); + } + final long keyIdentifier = keyEvent.getDeviceId() << 32 + keyEvent.getKeyCode(); if (mInputLogic.mCurrentlyPressedHardwareKeys.remove(keyIdentifier)) { return true; } - return super.onKeyUp(keyCode, event); + return super.onKeyUp(keyCode, keyEvent); } // onKeyDown and onKeyUp are the main events we are interested in. There are two more events diff --git a/java/src/com/android/inputmethod/latin/PunctuationSuggestions.java b/java/src/com/android/inputmethod/latin/PunctuationSuggestions.java index 0fba37c8a..6b0205c0f 100644 --- a/java/src/com/android/inputmethod/latin/PunctuationSuggestions.java +++ b/java/src/com/android/inputmethod/latin/PunctuationSuggestions.java @@ -35,7 +35,8 @@ public final class PunctuationSuggestions extends SuggestedWords { false /* typedWordValid */, false /* hasAutoCorrectionCandidate */, false /* isObsoleteSuggestions */, - false /* isPrediction */); + false /* isPrediction */, + INPUT_STYLE_NONE /* inputStyle */); } /** diff --git a/java/src/com/android/inputmethod/latin/SpecialKeyDetector.java b/java/src/com/android/inputmethod/latin/SpecialKeyDetector.java new file mode 100644 index 000000000..27b2f5012 --- /dev/null +++ b/java/src/com/android/inputmethod/latin/SpecialKeyDetector.java @@ -0,0 +1,43 @@ +/* + * Copyright (C) 2014, The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.inputmethod.latin; + +import android.content.Context; +import android.view.KeyEvent; + +final class SpecialKeyDetector { + /** + * Special physical key detector + * @param context a context of this detector. + */ + public SpecialKeyDetector(final Context context) { + } + + /** + * Record a down key event. + * @param keyEvent a down key event. + */ + public void onKeyDown(final KeyEvent keyEvent) { + } + + /** + * Record an up key event. + * @param keyEvent an up key event. + */ + public void onKeyUp(final KeyEvent keyEvent) { + } +} diff --git a/java/src/com/android/inputmethod/latin/Suggest.java b/java/src/com/android/inputmethod/latin/Suggest.java index b8b6d6471..ab852f8dd 100644 --- a/java/src/com/android/inputmethod/latin/Suggest.java +++ b/java/src/com/android/inputmethod/latin/Suggest.java @@ -40,13 +40,8 @@ public final class Suggest { // Session id for // {@link #getSuggestedWords(WordComposer,String,ProximityInfo,boolean,int)}. // We are sharing the same ID between typing and gesture to save RAM footprint. - public static final int SESSION_TYPING = 0; - public static final int SESSION_GESTURE = 0; - - // TODO: rename this to CORRECTION_OFF - public static final int CORRECTION_NONE = 0; - // TODO: rename this to CORRECTION_ON - public static final int CORRECTION_FULL = 1; + public static final int SESSION_ID_TYPING = 0; + public static final int SESSION_ID_GESTURE = 0; // Close to -2**31 private static final int SUPPRESS_SUGGEST_THRESHOLD = -2000000000; @@ -75,14 +70,15 @@ public final class Suggest { public void getSuggestedWords(final WordComposer wordComposer, final PrevWordsInfo prevWordsInfo, final ProximityInfo proximityInfo, final SettingsValuesForSuggestion settingsValuesForSuggestion, - final boolean isCorrectionEnabled, final int sessionId, final int sequenceNumber, + final boolean isCorrectionEnabled, final int inputStyle, final int sequenceNumber, final OnGetSuggestedWordsCallback callback) { if (wordComposer.isBatchMode()) { getSuggestedWordsForBatchInput(wordComposer, prevWordsInfo, proximityInfo, - settingsValuesForSuggestion, sessionId, sequenceNumber, callback); + settingsValuesForSuggestion, inputStyle, sequenceNumber, callback); } else { - getSuggestedWordsForTypingInput(wordComposer, prevWordsInfo, proximityInfo, - settingsValuesForSuggestion, isCorrectionEnabled, sequenceNumber, callback); + getSuggestedWordsForNonBatchInput(wordComposer, prevWordsInfo, proximityInfo, + settingsValuesForSuggestion, inputStyle, isCorrectionEnabled, + sequenceNumber, callback); } } @@ -120,11 +116,11 @@ public final class Suggest { return firstSuggestedWordInfo.mWord; } - // Retrieves suggestions for the typing input + // Retrieves suggestions for non-batch input (typing, recorrection, predictions...) // and calls the callback function with the suggestions. - private void getSuggestedWordsForTypingInput(final WordComposer wordComposer, + private void getSuggestedWordsForNonBatchInput(final WordComposer wordComposer, final PrevWordsInfo prevWordsInfo, final ProximityInfo proximityInfo, - final SettingsValuesForSuggestion settingsValuesForSuggestion, + final SettingsValuesForSuggestion settingsValuesForSuggestion, final int inputStyle, final boolean isCorrectionEnabled, final int sequenceNumber, final OnGetSuggestedWordsCallback callback) { final String typedWord = wordComposer.getTypedWord(); @@ -135,7 +131,7 @@ public final class Suggest { final SuggestionResults suggestionResults = mDictionaryFacilitator.getSuggestionResults( wordComposer, prevWordsInfo, proximityInfo, settingsValuesForSuggestion, - SESSION_TYPING); + SESSION_ID_TYPING); final ArrayList<SuggestedWordInfo> suggestionsContainer = getTransformedSuggestedWordInfoList(wordComposer, suggestionResults, trailingSingleQuotesCount); @@ -197,7 +193,8 @@ public final class Suggest { // rename the attribute or change the value. !resultsArePredictions && !allowsToBeAutoCorrected /* typedWordValid */, hasAutoCorrection /* willAutoCorrect */, - false /* isObsoleteSuggestions */, resultsArePredictions, sequenceNumber)); + false /* isObsoleteSuggestions */, resultsArePredictions, + inputStyle, sequenceNumber)); } // Retrieves suggestions for the batch input @@ -205,10 +202,11 @@ public final class Suggest { private void getSuggestedWordsForBatchInput(final WordComposer wordComposer, final PrevWordsInfo prevWordsInfo, final ProximityInfo proximityInfo, final SettingsValuesForSuggestion settingsValuesForSuggestion, - final int sessionId, final int sequenceNumber, + final int inputStyle, final int sequenceNumber, final OnGetSuggestedWordsCallback callback) { final SuggestionResults suggestionResults = mDictionaryFacilitator.getSuggestionResults( - wordComposer, prevWordsInfo, proximityInfo, settingsValuesForSuggestion, sessionId); + wordComposer, prevWordsInfo, proximityInfo, settingsValuesForSuggestion, + SESSION_ID_GESTURE); final ArrayList<SuggestedWordInfo> suggestionsContainer = new ArrayList<>(suggestionResults); final int suggestionsCount = suggestionsContainer.size(); @@ -246,7 +244,8 @@ public final class Suggest { true /* typedWordValid */, false /* willAutoCorrect */, false /* isObsoleteSuggestions */, - false /* isPrediction */, sequenceNumber)); + false /* isPrediction */, + inputStyle, sequenceNumber)); } private static ArrayList<SuggestedWordInfo> getSuggestionsInfoListWithDebugInfo( diff --git a/java/src/com/android/inputmethod/latin/SuggestedWords.java b/java/src/com/android/inputmethod/latin/SuggestedWords.java index 5231cc893..d7693af41 100644 --- a/java/src/com/android/inputmethod/latin/SuggestedWords.java +++ b/java/src/com/android/inputmethod/latin/SuggestedWords.java @@ -31,12 +31,20 @@ public class SuggestedWords { public static final int INDEX_OF_AUTO_CORRECTION = 1; public static final int NOT_A_SEQUENCE_NUMBER = -1; + public static final int INPUT_STYLE_NONE = 0; + public static final int INPUT_STYLE_TYPING = 1; + public static final int INPUT_STYLE_UPDATE_BATCH = 2; + public static final int INPUT_STYLE_TAIL_BATCH = 3; + public static final int INPUT_STYLE_APPLICATION_SPECIFIED = 4; + public static final int INPUT_STYLE_RECORRECTION = 5; + // The maximum number of suggestions available. public static final int MAX_SUGGESTIONS = 18; private static final ArrayList<SuggestedWordInfo> EMPTY_WORD_INFO_LIST = new ArrayList<>(0); public static final SuggestedWords EMPTY = new SuggestedWords( - EMPTY_WORD_INFO_LIST, null /* rawSuggestions */, false, false, false, false); + EMPTY_WORD_INFO_LIST, null /* rawSuggestions */, false, false, false, false, + INPUT_STYLE_NONE); public final String mTypedWord; public final boolean mTypedWordValid; @@ -46,6 +54,9 @@ public class SuggestedWords { public final boolean mWillAutoCorrect; public final boolean mIsObsoleteSuggestions; public final boolean mIsPrediction; + // How the input for these suggested words was done by the user. Must be one of the + // INPUT_STYLE_* constants above. + public final int mInputStyle; public final int mSequenceNumber; // Sequence number for auto-commit. protected final ArrayList<SuggestedWordInfo> mSuggestedWordInfoList; public final ArrayList<SuggestedWordInfo> mRawSuggestions; @@ -55,9 +66,10 @@ public class SuggestedWords { final boolean typedWordValid, final boolean willAutoCorrect, final boolean isObsoleteSuggestions, - final boolean isPrediction) { + final boolean isPrediction, + final int inputStyle) { this(suggestedWordInfoList, rawSuggestions, typedWordValid, willAutoCorrect, - isObsoleteSuggestions, isPrediction, NOT_A_SEQUENCE_NUMBER); + isObsoleteSuggestions, isPrediction, inputStyle, NOT_A_SEQUENCE_NUMBER); } public SuggestedWords(final ArrayList<SuggestedWordInfo> suggestedWordInfoList, @@ -66,11 +78,12 @@ public class SuggestedWords { final boolean willAutoCorrect, final boolean isObsoleteSuggestions, final boolean isPrediction, + final int inputStyle, final int sequenceNumber) { this(suggestedWordInfoList, rawSuggestions, (suggestedWordInfoList.isEmpty() || isPrediction) ? null : suggestedWordInfoList.get(INDEX_OF_TYPED_WORD).mWord, - typedWordValid, willAutoCorrect, isObsoleteSuggestions, isPrediction, + typedWordValid, willAutoCorrect, isObsoleteSuggestions, isPrediction, inputStyle, sequenceNumber); } @@ -81,6 +94,7 @@ public class SuggestedWords { final boolean willAutoCorrect, final boolean isObsoleteSuggestions, final boolean isPrediction, + final int inputStyle, final int sequenceNumber) { mSuggestedWordInfoList = suggestedWordInfoList; mRawSuggestions = rawSuggestions; @@ -88,6 +102,7 @@ public class SuggestedWords { mWillAutoCorrect = willAutoCorrect; mIsObsoleteSuggestions = isObsoleteSuggestions; mIsPrediction = isPrediction; + mInputStyle = inputStyle; mSequenceNumber = sequenceNumber; mTypedWord = typedWord; } @@ -367,7 +382,7 @@ public class SuggestedWords { // SuggestedWords is an immutable object, as much as possible. We must not just remove // words from the member ArrayList as some other parties may expect the object to never change. - public SuggestedWords getSuggestedWordsExcludingTypedWord() { + public SuggestedWords getSuggestedWordsExcludingTypedWord(final int inputStyle) { final ArrayList<SuggestedWordInfo> newSuggestions = new ArrayList<>(); String typedWord = null; for (int i = 0; i < mSuggestedWordInfoList.size(); ++i) { @@ -383,7 +398,7 @@ public class SuggestedWords { // no auto-correction should take place hence willAutoCorrect = false. return new SuggestedWords(newSuggestions, null /* rawSuggestions */, typedWord, true /* typedWordValid */, false /* willAutoCorrect */, mIsObsoleteSuggestions, - mIsPrediction, NOT_A_SEQUENCE_NUMBER); + mIsPrediction, inputStyle, NOT_A_SEQUENCE_NUMBER); } // Creates a new SuggestedWordInfo from the currently suggested words that removes all but the @@ -402,6 +417,7 @@ public class SuggestedWords { SuggestedWordInfo.NOT_A_CONFIDENCE)); } return new SuggestedWords(newSuggestions, null /* rawSuggestions */, mTypedWordValid, - mWillAutoCorrect, mIsObsoleteSuggestions, mIsPrediction); + mWillAutoCorrect, mIsObsoleteSuggestions, mIsPrediction, + INPUT_STYLE_TAIL_BATCH); } } diff --git a/java/src/com/android/inputmethod/latin/define/ProductionFlags.java b/java/src/com/android/inputmethod/latin/define/ProductionFlags.java index d385cf840..461c226a1 100644 --- a/java/src/com/android/inputmethod/latin/define/ProductionFlags.java +++ b/java/src/com/android/inputmethod/latin/define/ProductionFlags.java @@ -29,7 +29,7 @@ public final class ProductionFlags { * and prior. In general, this callback provides more detailed positional information, * even though an explicit support is required by the editor. */ - public static final boolean ENABLE_CURSOR_ANCHOR_INFO_CALLBACK = false; + public static final boolean ENABLE_CURSOR_ANCHOR_INFO_CALLBACK = true; /** * When true, enable {@link InputMethodService#onUpdateCursor} callback via diff --git a/java/src/com/android/inputmethod/latin/inputlogic/InputLogic.java b/java/src/com/android/inputmethod/latin/inputlogic/InputLogic.java index 418866ae1..348bae63a 100644 --- a/java/src/com/android/inputmethod/latin/inputlogic/InputLogic.java +++ b/java/src/com/android/inputmethod/latin/inputlogic/InputLogic.java @@ -216,7 +216,7 @@ public final class InputLogic { } else { resetComposingState(true /* alsoResetLastComposedWord */); } - handler.postUpdateSuggestionStrip(); + handler.postUpdateSuggestionStrip(SuggestedWords.INPUT_STYLE_TYPING); final String text = performSpecificTldProcessingOnTextInput(rawText); if (SpaceState.PHANTOM == mSpaceState) { promotePhantomSpace(settingsValues); @@ -288,9 +288,6 @@ public final class InputLogic { return inputTransaction; } - // We need to log before we commit, because the word composer will store away the user - // typed word. - final String replacedWord = mWordComposer.getTypedWord(); commitChosenWord(settingsValues, suggestion, LastComposedWord.COMMIT_TYPE_MANUAL_PICK, LastComposedWord.NOT_A_SEPARATOR); mConnection.endBatchEdit(); @@ -311,7 +308,8 @@ public final class InputLogic { mSuggestionStripViewAccessor.showAddToDictionaryHint(suggestion); } else { // If we're not showing the "Touch again to save", then update the suggestion strip. - handler.postUpdateSuggestionStrip(); + // That's going to be predictions (or punctuation suggestions), so INPUT_STYLE_NONE. + handler.postUpdateSuggestionStrip(SuggestedWords.INPUT_STYLE_NONE); } return inputTransaction; } @@ -1299,7 +1297,8 @@ public final class InputLogic { prevWordsInfo, timeStampInSeconds, settingsValues.mBlockPotentiallyOffensive); } - public void performUpdateSuggestionStripSync(final SettingsValues settingsValues) { + public void performUpdateSuggestionStripSync(final SettingsValues settingsValues, + final int inputStyle) { // Check if we have a suggestion engine attached. if (!settingsValues.needsToLookupSuggestions()) { if (mWordComposer.isComposingWord()) { @@ -1317,8 +1316,8 @@ public final class InputLogic { } final AsyncResultHolder<SuggestedWords> holder = new AsyncResultHolder<>(); - mInputLogicHandler.getSuggestedWords(Suggest.SESSION_TYPING, - SuggestedWords.NOT_A_SEQUENCE_NUMBER, new OnGetSuggestedWordsCallback() { + mInputLogicHandler.getSuggestedWords(inputStyle, SuggestedWords.NOT_A_SEQUENCE_NUMBER, + new OnGetSuggestedWordsCallback() { @Override public void onGetSuggestedWords(final SuggestedWords suggestedWords) { final String typedWord = mWordComposer.getTypedWord(); @@ -1379,7 +1378,7 @@ public final class InputLogic { if (!mConnection.isCursorTouchingWord(settingsValues.mSpacingAndPunctuations)) { // Show predictions. mWordComposer.setCapitalizedModeAtStartComposingTime(WordComposer.CAPS_MODE_OFF); - mLatinIME.mHandler.postUpdateSuggestionStrip(); + mLatinIME.mHandler.postUpdateSuggestionStrip(SuggestedWords.INPUT_STYLE_RECORRECTION); return; } final TextRange range = mConnection.getWordRangeAtCursor( @@ -1444,7 +1443,7 @@ public final class InputLogic { // If there weren't any suggestion spans on this word, suggestions#size() will be 1 // if shouldIncludeResumedWordInSuggestions is true, 0 otherwise. In this case, we // have no useful suggestions, so we will try to compute some for it instead. - mInputLogicHandler.getSuggestedWords(Suggest.SESSION_TYPING, + mInputLogicHandler.getSuggestedWords(Suggest.SESSION_ID_TYPING, SuggestedWords.NOT_A_SEQUENCE_NUMBER, new OnGetSuggestedWordsCallback() { @Override public void onGetSuggestedWords( @@ -1457,7 +1456,8 @@ public final class InputLogic { // case. The #getSuggestedWordsExcludingTypedWord() method sets // willAutoCorrect to false. suggestedWords = suggestedWordsIncludingTypedWord - .getSuggestedWordsExcludingTypedWord(); + .getSuggestedWordsExcludingTypedWord(SuggestedWords + .INPUT_STYLE_RECORRECTION); } else { // No saved suggestions, and we were unable to compute any good one // either. Rather than displaying an empty suggestion strip, we'll @@ -1477,6 +1477,7 @@ public final class InputLogic { null /* rawSuggestions */, typedWord, false /* typedWordValid */, false /* willAutoCorrect */, false /* isObsoleteSuggestions */, false /* isPrediction */, + SuggestedWords.INPUT_STYLE_RECORRECTION, SuggestedWords.NOT_A_SEQUENCE_NUMBER); mIsAutoCorrectionIndicatorOn = false; mLatinIME.mHandler.showSuggestionStrip(suggestedWords); @@ -1773,7 +1774,8 @@ public final class InputLogic { SuggestedWords.getTypedWordAndPreviousSuggestions(typedWord, oldSuggestedWords); return new SuggestedWords(typedWordAndPreviousSuggestions, null /* rawSuggestions */, false /* typedWordValid */, false /* hasAutoCorrectionCandidate */, - true /* isObsoleteSuggestions */, false /* isPrediction */); + true /* isObsoleteSuggestions */, false /* isPrediction */, + oldSuggestedWords.mInputStyle); } /** @@ -1956,7 +1958,15 @@ public final class InputLogic { // Complete any pending suggestions query first if (handler.hasPendingUpdateSuggestions()) { handler.cancelUpdateSuggestionStrip(); - performUpdateSuggestionStripSync(settingsValues); + // To know the input style here, we should retrieve the in-flight "update suggestions" + // message and read its arg1 member here. However, the Handler class does not let + // us retrieve this message, so we can't do that. But in fact, we notice that + // we only ever come here when the input style was typing. In the case of batch + // input, we update the suggestions synchronously when the tail batch comes. Likewise + // for application-specified completions. As for recorrections, we never auto-correct, + // so we don't come here either. Hence, the input style is necessarily + // INPUT_STYLE_TYPING. + performUpdateSuggestionStripSync(settingsValues, SuggestedWords.INPUT_STYLE_TYPING); } final String typedAutoCorrection = mWordComposer.getAutoCorrectionOrNull(); final String typedWord = mWordComposer.getTypedWord(); @@ -2052,7 +2062,7 @@ public final class InputLogic { } public void getSuggestedWords(final SettingsValues settingsValues, - final ProximityInfo proximityInfo, final int keyboardShiftMode, final int sessionId, + final ProximityInfo proximityInfo, final int keyboardShiftMode, final int inputStyle, final int sequenceNumber, final OnGetSuggestedWordsCallback callback) { mWordComposer.adviseCapitalizedModeBeforeFetchingSuggestions( getActualCapsMode(settingsValues, keyboardShiftMode)); @@ -2068,6 +2078,6 @@ public final class InputLogic { settingsValues.mPhraseGestureEnabled, settingsValues.mAdditionalFeaturesSettingValues), settingsValues.mAutoCorrectionEnabledPerUserSettings, - sessionId, sequenceNumber, callback); + inputStyle, sequenceNumber, callback); } } diff --git a/java/src/com/android/inputmethod/latin/inputlogic/InputLogicHandler.java b/java/src/com/android/inputmethod/latin/inputlogic/InputLogicHandler.java index 9dbe2c38b..c6f83d0b9 100644 --- a/java/src/com/android/inputmethod/latin/inputlogic/InputLogicHandler.java +++ b/java/src/com/android/inputmethod/latin/inputlogic/InputLogicHandler.java @@ -96,7 +96,7 @@ class InputLogicHandler implements Handler.Callback { public boolean handleMessage(final Message msg) { switch (msg.what) { case MSG_GET_SUGGESTED_WORDS: - mLatinIME.getSuggestedWords(msg.arg1 /* sessionId */, + mLatinIME.getSuggestedWords(msg.arg1 /* inputStyle */, msg.arg2 /* sequenceNumber */, (OnGetSuggestedWordsCallback) msg.obj); break; } @@ -134,7 +134,8 @@ class InputLogicHandler implements Handler.Callback { return; } mInputLogic.mWordComposer.setBatchInputPointers(batchPointers); - getSuggestedWords(Suggest.SESSION_GESTURE, sequenceNumber, + getSuggestedWords(isTailBatchInput ? SuggestedWords.INPUT_STYLE_TAIL_BATCH + : SuggestedWords.INPUT_STYLE_UPDATE_BATCH, sequenceNumber, new OnGetSuggestedWordsCallback() { @Override public void onGetSuggestedWords(SuggestedWords suggestedWords) { @@ -205,9 +206,9 @@ class InputLogicHandler implements Handler.Callback { updateBatchInput(batchPointers, sequenceNumber, true /* isTailBatchInput */); } - public void getSuggestedWords(final int sessionId, final int sequenceNumber, + public void getSuggestedWords(final int inputStyle, final int sequenceNumber, final OnGetSuggestedWordsCallback callback) { mNonUIThreadHandler.obtainMessage( - MSG_GET_SUGGESTED_WORDS, sessionId, sequenceNumber, callback).sendToTarget(); + MSG_GET_SUGGESTED_WORDS, inputStyle, sequenceNumber, callback).sendToTarget(); } } diff --git a/java/src/com/android/inputmethod/latin/settings/DebugSettings.java b/java/src/com/android/inputmethod/latin/settings/DebugSettings.java index dce11b434..63d848e2d 100644 --- a/java/src/com/android/inputmethod/latin/settings/DebugSettings.java +++ b/java/src/com/android/inputmethod/latin/settings/DebugSettings.java @@ -19,6 +19,10 @@ package com.android.inputmethod.latin.settings; public final class DebugSettings { public static final String PREF_DEBUG_MODE = "debug_mode"; public static final String PREF_FORCE_NON_DISTINCT_MULTITOUCH = "force_non_distinct_multitouch"; + public static final String PREF_FORCE_PHYSICAL_KEYBOARD_SPECIAL_KEY = + "force_physical_keyboard_special_key"; + public static final String PREF_SHOW_UI_TO_ACCEPT_TYPED_WORD = + "pref_show_ui_to_accept_typed_word"; public static final String PREF_KEY_PREVIEW_SHOW_UP_START_SCALE = "pref_key_preview_show_up_start_scale"; public static final String PREF_KEY_PREVIEW_DISMISS_END_SCALE = diff --git a/java/src/com/android/inputmethod/latin/settings/DebugSettingsFragment.java b/java/src/com/android/inputmethod/latin/settings/DebugSettingsFragment.java index 4e41d52a3..dc2f88aa8 100644 --- a/java/src/com/android/inputmethod/latin/settings/DebugSettingsFragment.java +++ b/java/src/com/android/inputmethod/latin/settings/DebugSettingsFragment.java @@ -56,6 +56,10 @@ public final class DebugSettingsFragment extends SubScreenFragment super.onCreate(icicle); addPreferencesFromResource(R.xml.prefs_screen_debug); + if (!Settings.HAS_UI_TO_ACCEPT_TYPED_WORD) { + removePreference(DebugSettings.PREF_SHOW_UI_TO_ACCEPT_TYPED_WORD); + } + mReadExternalDictionaryPref = findPreference(PREF_READ_EXTERNAL_DICTIONARY); if (mReadExternalDictionaryPref != null) { mReadExternalDictionaryPref.setOnPreferenceClickListener(this); @@ -133,7 +137,8 @@ public final class DebugSettingsFragment extends SubScreenFragment mServiceNeedsRestart = true; return; } - if (key.equals(DebugSettings.PREF_FORCE_NON_DISTINCT_MULTITOUCH)) { + if (key.equals(DebugSettings.PREF_FORCE_NON_DISTINCT_MULTITOUCH) + || key.equals(DebugSettings.PREF_FORCE_PHYSICAL_KEYBOARD_SPECIAL_KEY)) { mServiceNeedsRestart = true; return; } diff --git a/java/src/com/android/inputmethod/latin/settings/Settings.java b/java/src/com/android/inputmethod/latin/settings/Settings.java index 90174e490..9d3c27bbe 100644 --- a/java/src/com/android/inputmethod/latin/settings/Settings.java +++ b/java/src/com/android/inputmethod/latin/settings/Settings.java @@ -24,6 +24,7 @@ import android.os.Build; import android.preference.PreferenceManager; import android.util.Log; +import com.android.inputmethod.compat.BuildCompatUtils; import com.android.inputmethod.latin.AudioAndHapticFeedbackManager; import com.android.inputmethod.latin.InputAttributes; import com.android.inputmethod.latin.R; @@ -68,9 +69,9 @@ public final class Settings implements SharedPreferences.OnSharedPreferenceChang public static final String PREF_BLOCK_POTENTIALLY_OFFENSIVE = "pref_key_block_potentially_offensive"; public static final boolean ENABLE_SHOW_LANGUAGE_SWITCH_KEY_SETTINGS = - (Build.VERSION.SDK_INT < Build.VERSION_CODES.KITKAT) - || (Build.VERSION.SDK_INT == Build.VERSION_CODES.KITKAT - && Build.VERSION.CODENAME.equals("REL")); + BuildCompatUtils.EFFECTIVE_SDK_INT <= Build.VERSION_CODES.KITKAT; + public static final boolean HAS_UI_TO_ACCEPT_TYPED_WORD = + BuildCompatUtils.EFFECTIVE_SDK_INT >= BuildCompatUtils.VERSION_CODES_LXX; public static final String PREF_SHOW_LANGUAGE_SWITCH_KEY = "pref_show_language_switch_key"; public static final String PREF_INCLUDE_OTHER_IMES_IN_LANGUAGE_SWITCH_LIST = diff --git a/java/src/com/android/inputmethod/latin/settings/SettingsValues.java b/java/src/com/android/inputmethod/latin/settings/SettingsValues.java index 39e834f84..c12474b3d 100644 --- a/java/src/com/android/inputmethod/latin/settings/SettingsValues.java +++ b/java/src/com/android/inputmethod/latin/settings/SettingsValues.java @@ -40,7 +40,8 @@ import java.util.Locale; * When you call the constructor of this class, you may want to change the current system locale by * using {@link com.android.inputmethod.latin.utils.RunInLocale}. */ -public final class SettingsValues { +// Non-final for testing via mock library. +public class SettingsValues { private static final String TAG = SettingsValues.class.getSimpleName(); // "floatMaxValue" and "floatNegativeInfinity" are special marker strings for // Float.NEGATIVE_INFINITE and Float.MAX_VALUE. Currently used for auto-correction settings. @@ -75,6 +76,7 @@ public final class SettingsValues { public final int mKeyLongpressTimeout; public final Locale mLocale; public final boolean mEnableMetricsLogging; + public final boolean mShouldShowUiToAcceptTypedWord; // From the input box public final InputAttributes mInputAttributes; @@ -142,6 +144,8 @@ public final class SettingsValues { mBigramPredictionEnabled = readBigramPredictionEnabled(prefs, res); mDoubleSpacePeriodTimeout = res.getInteger(R.integer.config_double_space_period_timeout); mEnableMetricsLogging = prefs.getBoolean(Settings.PREF_ENABLE_METRICS_LOGGING, true); + mShouldShowUiToAcceptTypedWord = Settings.HAS_UI_TO_ACCEPT_TYPED_WORD + && prefs.getBoolean(DebugSettings.PREF_SHOW_UI_TO_ACCEPT_TYPED_WORD, true); // Compute other readable settings mKeyLongpressTimeout = Settings.readKeyLongpressTimeout(prefs, res); mKeypressVibrationDuration = Settings.readKeypressVibrationDuration(prefs, res); @@ -186,6 +190,10 @@ public final class SettingsValues { } } + public boolean isMetricsLoggingEnabled() { + return mEnableMetricsLogging; + } + public boolean isApplicationSpecifiedCompletionsOn() { return mInputAttributes.mApplicationSpecifiedCompletionOn; } diff --git a/native/jni/src/suggest/core/dictionary/error_type_utils.cpp b/native/jni/src/suggest/core/dictionary/error_type_utils.cpp index b6bf7a98c..1e2494e92 100644 --- a/native/jni/src/suggest/core/dictionary/error_type_utils.cpp +++ b/native/jni/src/suggest/core/dictionary/error_type_utils.cpp @@ -19,17 +19,18 @@ namespace latinime { const ErrorTypeUtils::ErrorType ErrorTypeUtils::NOT_AN_ERROR = 0x0; -const ErrorTypeUtils::ErrorType ErrorTypeUtils::MATCH_WITH_CASE_ERROR = 0x1; -const ErrorTypeUtils::ErrorType ErrorTypeUtils::MATCH_WITH_ACCENT_ERROR = 0x2; -const ErrorTypeUtils::ErrorType ErrorTypeUtils::MATCH_WITH_DIGRAPH = 0x4; -const ErrorTypeUtils::ErrorType ErrorTypeUtils::INTENTIONAL_OMISSION = 0x8; -const ErrorTypeUtils::ErrorType ErrorTypeUtils::EDIT_CORRECTION = 0x10; -const ErrorTypeUtils::ErrorType ErrorTypeUtils::PROXIMITY_CORRECTION = 0x20; -const ErrorTypeUtils::ErrorType ErrorTypeUtils::COMPLETION = 0x40; -const ErrorTypeUtils::ErrorType ErrorTypeUtils::NEW_WORD = 0x80; +const ErrorTypeUtils::ErrorType ErrorTypeUtils::MATCH_WITH_WRONG_CASE = 0x1; +const ErrorTypeUtils::ErrorType ErrorTypeUtils::MATCH_WITH_MISSING_ACCENT = 0x2; +const ErrorTypeUtils::ErrorType ErrorTypeUtils::MATCH_WITH_WRONG_ACCENT = 0x4; +const ErrorTypeUtils::ErrorType ErrorTypeUtils::MATCH_WITH_DIGRAPH = 0x8; +const ErrorTypeUtils::ErrorType ErrorTypeUtils::INTENTIONAL_OMISSION = 0x10; +const ErrorTypeUtils::ErrorType ErrorTypeUtils::EDIT_CORRECTION = 0x20; +const ErrorTypeUtils::ErrorType ErrorTypeUtils::PROXIMITY_CORRECTION = 0x40; +const ErrorTypeUtils::ErrorType ErrorTypeUtils::COMPLETION = 0x80; +const ErrorTypeUtils::ErrorType ErrorTypeUtils::NEW_WORD = 0x100; const ErrorTypeUtils::ErrorType ErrorTypeUtils::ERRORS_TREATED_AS_AN_EXACT_MATCH = - NOT_AN_ERROR | MATCH_WITH_CASE_ERROR | MATCH_WITH_ACCENT_ERROR | MATCH_WITH_DIGRAPH; + NOT_AN_ERROR | MATCH_WITH_WRONG_CASE | MATCH_WITH_MISSING_ACCENT | MATCH_WITH_DIGRAPH; const ErrorTypeUtils::ErrorType ErrorTypeUtils::ERRORS_TREATED_AS_AN_EXACT_MATCH_WITH_INTENTIONAL_OMISSION = diff --git a/native/jni/src/suggest/core/dictionary/error_type_utils.h b/native/jni/src/suggest/core/dictionary/error_type_utils.h index e3e76b238..fd1d5fcff 100644 --- a/native/jni/src/suggest/core/dictionary/error_type_utils.h +++ b/native/jni/src/suggest/core/dictionary/error_type_utils.h @@ -30,8 +30,9 @@ class ErrorTypeUtils { typedef uint32_t ErrorType; static const ErrorType NOT_AN_ERROR; - static const ErrorType MATCH_WITH_CASE_ERROR; - static const ErrorType MATCH_WITH_ACCENT_ERROR; + static const ErrorType MATCH_WITH_WRONG_CASE; + static const ErrorType MATCH_WITH_MISSING_ACCENT; + static const ErrorType MATCH_WITH_WRONG_ACCENT; static const ErrorType MATCH_WITH_DIGRAPH; // Treat error as an intentional omission when the CorrectionType is omission and the node can // be intentional omission. diff --git a/native/jni/src/suggest/policyimpl/dictionary/structure/backward/v402/ver4_patricia_trie_node_writer.cpp b/native/jni/src/suggest/policyimpl/dictionary/structure/backward/v402/ver4_patricia_trie_node_writer.cpp index 278f2b199..97a8bcc98 100644 --- a/native/jni/src/suggest/policyimpl/dictionary/structure/backward/v402/ver4_patricia_trie_node_writer.cpp +++ b/native/jni/src/suggest/policyimpl/dictionary/structure/backward/v402/ver4_patricia_trie_node_writer.cpp @@ -234,8 +234,8 @@ bool Ver4PatriciaTrieNodeWriter::writeNewTerminalPtNodeAndAdvancePosition( bool Ver4PatriciaTrieNodeWriter::addNgramEntry(const WordIdArrayView prevWordIds, const int wordId, const BigramProperty *const bigramProperty, bool *const outAddedNewEntry) { if (!mBigramPolicy->addNewEntry(prevWordIds[0], wordId, bigramProperty, outAddedNewEntry)) { - AKLOGE("Cannot add new bigram entry. terminalId: %d, targetTerminalId: %d", - sourcePtNodeParams->getTerminalId(), targetPtNodeParam->getTerminalId()); + AKLOGE("Cannot add new bigram entry. prevWordId: %d, wordId: %d", + prevWordIds[0], wordId); return false; } const int ptNodePos = @@ -425,6 +425,18 @@ bool Ver4PatriciaTrieNodeWriter::updatePtNodeFlags(const int ptNodePos, return true; } +bool Ver4PatriciaTrieNodeWriter::suppressUnigramEntry(const PtNodeParams *const ptNodeParams) { + if (!mHeaderPolicy->hasHistoricalInfoOfWords()) { + // Require historical info to suppress unigram entry. + return false; + } + const HistoricalInfo suppressedHistorycalInfo(0 /* timestamp */, 0 /* level */, 0 /* count */); + const ProbabilityEntry probabilityEntryToWrite = + ProbabilityEntry().createEntryWithUpdatedHistoricalInfo(&suppressedHistorycalInfo); + return mBuffers->getMutableProbabilityDictContent()->setProbabilityEntry( + ptNodeParams->getTerminalId(), &probabilityEntryToWrite); +} + } // namespace v402 } // namespace backward } // namespace latinime diff --git a/native/jni/src/suggest/policyimpl/dictionary/structure/backward/v402/ver4_patricia_trie_node_writer.h b/native/jni/src/suggest/policyimpl/dictionary/structure/backward/v402/ver4_patricia_trie_node_writer.h index d49d9a666..9d8a55bff 100644 --- a/native/jni/src/suggest/policyimpl/dictionary/structure/backward/v402/ver4_patricia_trie_node_writer.h +++ b/native/jni/src/suggest/policyimpl/dictionary/structure/backward/v402/ver4_patricia_trie_node_writer.h @@ -111,6 +111,11 @@ class Ver4PatriciaTrieNodeWriter : public PtNodeWriter { bool updatePtNodeHasBigramsAndShortcutTargetsFlags(const PtNodeParams *const ptNodeParams); + // Suppress unigram not to use the word for generating suggestions. So, this method can be used + // only for dictionaries with historical info. Also, suppressed entries are included in unigram + // count. They will be removed from the dictionary during GC. + bool suppressUnigramEntry(const PtNodeParams *const ptNodeParams); + private: DISALLOW_COPY_AND_ASSIGN(Ver4PatriciaTrieNodeWriter); diff --git a/native/jni/src/suggest/policyimpl/dictionary/structure/backward/v402/ver4_patricia_trie_policy.cpp b/native/jni/src/suggest/policyimpl/dictionary/structure/backward/v402/ver4_patricia_trie_policy.cpp index 1296b8acd..9c6452e40 100644 --- a/native/jni/src/suggest/policyimpl/dictionary/structure/backward/v402/ver4_patricia_trie_policy.cpp +++ b/native/jni/src/suggest/policyimpl/dictionary/structure/backward/v402/ver4_patricia_trie_policy.cpp @@ -210,7 +210,7 @@ bool Ver4PatriciaTriePolicy::addUnigramEntry(const int *const word, const int le } for (const auto &shortcut : unigramProperty->getShortcuts()) { if (shortcut.getTargetCodePoints()->size() > MAX_WORD_LENGTH) { - AKLOGE("One of shortcut targets is too long to insert to the dictionary, length: %d", + AKLOGE("One of shortcut targets is too long to insert to the dictionary, length: %zd", shortcut.getTargetCodePoints()->size()); return false; } @@ -245,7 +245,7 @@ bool Ver4PatriciaTriePolicy::addUnigramEntry(const int *const word, const int le if (!mUpdatingHelper.addShortcutTarget(wordPos, shortcut.getTargetCodePoints()->data(), shortcut.getTargetCodePoints()->size(), shortcut.getProbability())) { - AKLOGE("Cannot add new shortcut target. PtNodePos: %d, length: %d, " + AKLOGE("Cannot add new shortcut target. PtNodePos: %d, length: %zd, " "probability: %d", wordPos, shortcut.getTargetCodePoints()->size(), shortcut.getProbability()); return false; @@ -258,6 +258,20 @@ bool Ver4PatriciaTriePolicy::addUnigramEntry(const int *const word, const int le } } +bool Ver4PatriciaTriePolicy::removeUnigramEntry(const int *const word, const int length) { + if (!mBuffers->isUpdatable()) { + AKLOGI("Warning: removeUnigramEntry() is called for non-updatable dictionary."); + return false; + } + const int ptNodePos = getTerminalPtNodePositionOfWord(word, length, + false /* forceLowerCaseSearch */); + if (ptNodePos == NOT_A_DICT_POS) { + return false; + } + const PtNodeParams ptNodeParams = mNodeReader.fetchPtNodeParamsInBufferFromPtNodePos(ptNodePos); + return mNodeWriter.suppressUnigramEntry(&ptNodeParams); +} + bool Ver4PatriciaTriePolicy::addNgramEntry(const PrevWordsInfo *const prevWordsInfo, const BigramProperty *const bigramProperty) { if (!mBuffers->isUpdatable()) { @@ -275,7 +289,7 @@ bool Ver4PatriciaTriePolicy::addNgramEntry(const PrevWordsInfo *const prevWordsI } if (bigramProperty->getTargetCodePoints()->size() > MAX_WORD_LENGTH) { AKLOGE("The word is too long to insert the ngram to the dictionary. " - "length: %d", bigramProperty->getTargetCodePoints()->size()); + "length: %zd", bigramProperty->getTargetCodePoints()->size()); return false; } int prevWordsPtNodePos[MAX_PREV_WORD_COUNT_FOR_N_GRAM]; diff --git a/native/jni/src/suggest/policyimpl/dictionary/structure/backward/v402/ver4_patricia_trie_policy.h b/native/jni/src/suggest/policyimpl/dictionary/structure/backward/v402/ver4_patricia_trie_policy.h index 9e989b268..d77499636 100644 --- a/native/jni/src/suggest/policyimpl/dictionary/structure/backward/v402/ver4_patricia_trie_policy.h +++ b/native/jni/src/suggest/policyimpl/dictionary/structure/backward/v402/ver4_patricia_trie_policy.h @@ -108,10 +108,7 @@ class Ver4PatriciaTriePolicy : public DictionaryStructureWithBufferPolicy { bool addUnigramEntry(const int *const word, const int length, const UnigramProperty *const unigramProperty); - bool removeUnigramEntry(const int *const word, const int length) { - // Removing unigram entry is not supported. - return false; - } + bool removeUnigramEntry(const int *const word, const int length); bool addNgramEntry(const PrevWordsInfo *const prevWordsInfo, const BigramProperty *const bigramProperty); diff --git a/native/jni/src/suggest/policyimpl/dictionary/structure/dictionary_structure_with_buffer_policy_factory.cpp b/native/jni/src/suggest/policyimpl/dictionary/structure/dictionary_structure_with_buffer_policy_factory.cpp index e4ea3da16..9fa93efc9 100644 --- a/native/jni/src/suggest/policyimpl/dictionary/structure/dictionary_structure_with_buffer_policy_factory.cpp +++ b/native/jni/src/suggest/policyimpl/dictionary/structure/dictionary_structure_with_buffer_policy_factory.cpp @@ -111,8 +111,7 @@ template<class DictConstants, class DictBuffers, class DictBuffersPtr, class Str return nullptr; } const FormatUtils::FORMAT_VERSION formatVersion = FormatUtils::detectFormatVersion( - mmappedBuffer->getReadOnlyByteArrayView().data(), - mmappedBuffer->getReadOnlyByteArrayView().size()); + mmappedBuffer->getReadOnlyByteArrayView()); switch (formatVersion) { case FormatUtils::VERSION_2: AKLOGE("Given path is a directory but the format is version 2. path: %s", path); @@ -174,8 +173,7 @@ template<class DictConstants, class DictBuffers, class DictBuffersPtr, class Str if (!mmappedBuffer) { return nullptr; } - switch (FormatUtils::detectFormatVersion(mmappedBuffer->getReadOnlyByteArrayView().data(), - mmappedBuffer->getReadOnlyByteArrayView().size())) { + switch (FormatUtils::detectFormatVersion(mmappedBuffer->getReadOnlyByteArrayView())) { case FormatUtils::VERSION_2: return DictionaryStructureWithBufferPolicy::StructurePolicyPtr( new PatriciaTriePolicy(std::move(mmappedBuffer))); diff --git a/native/jni/src/suggest/policyimpl/dictionary/structure/v4/content/language_model_dict_content.cpp b/native/jni/src/suggest/policyimpl/dictionary/structure/v4/content/language_model_dict_content.cpp index 5dc91ba10..f3bc4a0cb 100644 --- a/native/jni/src/suggest/policyimpl/dictionary/structure/v4/content/language_model_dict_content.cpp +++ b/native/jni/src/suggest/policyimpl/dictionary/structure/v4/content/language_model_dict_content.cpp @@ -46,7 +46,7 @@ ProbabilityEntry LanguageModelDictContent::getNgramProbabilityEntry( bool LanguageModelDictContent::setNgramProbabilityEntry(const WordIdArrayView prevWordIds, const int terminalId, const ProbabilityEntry *const probabilityEntry) { - const int bitmapEntryIndex = getBitmapEntryIndex(prevWordIds); + const int bitmapEntryIndex = createAndGetBitmapEntryIndex(prevWordIds); if (bitmapEntryIndex == TrieMap::INVALID_INDEX) { return false; } @@ -80,6 +80,19 @@ bool LanguageModelDictContent::runGCInner( return true; } +int LanguageModelDictContent::createAndGetBitmapEntryIndex(const WordIdArrayView prevWordIds) { + if (prevWordIds.empty()) { + return mTrieMap.getRootBitmapEntryIndex(); + } + const int lastBitmapEntryIndex = + getBitmapEntryIndex(prevWordIds.limit(prevWordIds.size() - 1)); + if (lastBitmapEntryIndex == TrieMap::INVALID_INDEX) { + return TrieMap::INVALID_INDEX; + } + return mTrieMap.getNextLevelBitmapEntryIndex(prevWordIds[prevWordIds.size() - 1], + lastBitmapEntryIndex); +} + int LanguageModelDictContent::getBitmapEntryIndex(const WordIdArrayView prevWordIds) const { int bitmapEntryIndex = mTrieMap.getRootBitmapEntryIndex(); for (const int wordId : prevWordIds) { diff --git a/native/jni/src/suggest/policyimpl/dictionary/structure/v4/content/language_model_dict_content.h b/native/jni/src/suggest/policyimpl/dictionary/structure/v4/content/language_model_dict_content.h index 18f2e0170..104ee2520 100644 --- a/native/jni/src/suggest/policyimpl/dictionary/structure/v4/content/language_model_dict_content.h +++ b/native/jni/src/suggest/policyimpl/dictionary/structure/v4/content/language_model_dict_content.h @@ -76,7 +76,7 @@ class LanguageModelDictContent { bool runGCInner(const TerminalPositionLookupTable::TerminalIdMap *const terminalIdMap, const TrieMap::TrieMapRange trieMapRange, const int nextLevelBitmapEntryIndex, int *const outNgramCount); - + int createAndGetBitmapEntryIndex(const WordIdArrayView prevWordIds); int getBitmapEntryIndex(const WordIdArrayView prevWordIds) const; }; } // namespace latinime diff --git a/native/jni/src/suggest/policyimpl/dictionary/structure/v4/content/probability_entry.h b/native/jni/src/suggest/policyimpl/dictionary/structure/v4/content/probability_entry.h index feff6b57f..ed77bd20e 100644 --- a/native/jni/src/suggest/policyimpl/dictionary/structure/v4/content/probability_entry.h +++ b/native/jni/src/suggest/policyimpl/dictionary/structure/v4/content/probability_entry.h @@ -21,6 +21,8 @@ #include <cstdint> #include "defines.h" +#include "suggest/core/dictionary/property/bigram_property.h" +#include "suggest/core/dictionary/property/unigram_property.h" #include "suggest/policyimpl/dictionary/structure/v4/ver4_dict_constants.h" #include "suggest/policyimpl/dictionary/utils/historical_info.h" @@ -45,6 +47,20 @@ class ProbabilityEntry { const HistoricalInfo *const historicalInfo) : mFlags(flags), mProbability(probability), mHistoricalInfo(*historicalInfo) {} + // Create from unigram property. + // TODO: Set flags. + ProbabilityEntry(const UnigramProperty *const unigramProperty) + : mFlags(0), mProbability(unigramProperty->getProbability()), + mHistoricalInfo(unigramProperty->getTimestamp(), unigramProperty->getLevel(), + unigramProperty->getCount()) {} + + // Create from bigram property. + // TODO: Set flags. + ProbabilityEntry(const BigramProperty *const bigramProperty) + : mFlags(0), mProbability(bigramProperty->getProbability()), + mHistoricalInfo(bigramProperty->getTimestamp(), bigramProperty->getLevel(), + bigramProperty->getCount()) {} + const ProbabilityEntry createEntryWithUpdatedProbability(const int probability) const { return ProbabilityEntry(mFlags, probability, &mHistoricalInfo); } @@ -54,6 +70,10 @@ class ProbabilityEntry { return ProbabilityEntry(mFlags, mProbability, historicalInfo); } + bool isValid() const { + return (mProbability != NOT_A_PROBABILITY) || hasHistoricalInfo(); + } + bool hasHistoricalInfo() const { return mHistoricalInfo.isValid(); } @@ -89,7 +109,7 @@ class ProbabilityEntry { static ProbabilityEntry decode(const uint64_t encodedEntry, const bool hasHistoricalInfo) { if (hasHistoricalInfo) { const int flags = readFromEncodedEntry(encodedEntry, - Ver4DictConstants::FLAGS_IN_PROBABILITY_FILE_SIZE, + Ver4DictConstants::FLAGS_IN_LANGUAGE_MODEL_SIZE, Ver4DictConstants::TIME_STAMP_FIELD_SIZE + Ver4DictConstants::WORD_LEVEL_FIELD_SIZE + Ver4DictConstants::WORD_COUNT_FIELD_SIZE); @@ -106,7 +126,7 @@ class ProbabilityEntry { return ProbabilityEntry(flags, NOT_A_PROBABILITY, &historicalInfo); } else { const int flags = readFromEncodedEntry(encodedEntry, - Ver4DictConstants::FLAGS_IN_PROBABILITY_FILE_SIZE, + Ver4DictConstants::FLAGS_IN_LANGUAGE_MODEL_SIZE, Ver4DictConstants::PROBABILITY_SIZE); const int probability = readFromEncodedEntry(encodedEntry, Ver4DictConstants::PROBABILITY_SIZE, 0 /* pos */); diff --git a/native/jni/src/suggest/policyimpl/dictionary/structure/v4/ver4_dict_constants.cpp b/native/jni/src/suggest/policyimpl/dictionary/structure/v4/ver4_dict_constants.cpp index 93d4e562d..e622442ba 100644 --- a/native/jni/src/suggest/policyimpl/dictionary/structure/v4/ver4_dict_constants.cpp +++ b/native/jni/src/suggest/policyimpl/dictionary/structure/v4/ver4_dict_constants.cpp @@ -46,7 +46,7 @@ const int Ver4DictConstants::SHORTCUT_BUFFERS_INDEX = const int Ver4DictConstants::NOT_A_TERMINAL_ID = -1; const int Ver4DictConstants::PROBABILITY_SIZE = 1; -const int Ver4DictConstants::FLAGS_IN_PROBABILITY_FILE_SIZE = 1; +const int Ver4DictConstants::FLAGS_IN_LANGUAGE_MODEL_SIZE = 1; const int Ver4DictConstants::TERMINAL_ADDRESS_TABLE_ADDRESS_SIZE = 3; const int Ver4DictConstants::NOT_A_TERMINAL_ADDRESS = 0; const int Ver4DictConstants::TERMINAL_ID_FIELD_SIZE = 4; diff --git a/native/jni/src/suggest/policyimpl/dictionary/structure/v4/ver4_dict_constants.h b/native/jni/src/suggest/policyimpl/dictionary/structure/v4/ver4_dict_constants.h index 6950ca70f..8d29f60d4 100644 --- a/native/jni/src/suggest/policyimpl/dictionary/structure/v4/ver4_dict_constants.h +++ b/native/jni/src/suggest/policyimpl/dictionary/structure/v4/ver4_dict_constants.h @@ -41,7 +41,7 @@ class Ver4DictConstants { static const int NOT_A_TERMINAL_ID; static const int PROBABILITY_SIZE; - static const int FLAGS_IN_PROBABILITY_FILE_SIZE; + static const int FLAGS_IN_LANGUAGE_MODEL_SIZE; static const int TERMINAL_ADDRESS_TABLE_ADDRESS_SIZE; static const int NOT_A_TERMINAL_ADDRESS; static const int TERMINAL_ID_FIELD_SIZE; diff --git a/native/jni/src/suggest/policyimpl/dictionary/structure/v4/ver4_patricia_trie_node_writer.cpp b/native/jni/src/suggest/policyimpl/dictionary/structure/v4/ver4_patricia_trie_node_writer.cpp index 857222f5d..2c848cb29 100644 --- a/native/jni/src/suggest/policyimpl/dictionary/structure/v4/ver4_patricia_trie_node_writer.cpp +++ b/native/jni/src/suggest/policyimpl/dictionary/structure/v4/ver4_patricia_trie_node_writer.cpp @@ -145,10 +145,11 @@ bool Ver4PatriciaTrieNodeWriter::updatePtNodeUnigramProperty( const ProbabilityEntry originalProbabilityEntry = mBuffers->getLanguageModelDictContent()->getProbabilityEntry( toBeUpdatedPtNodeParams->getTerminalId()); - const ProbabilityEntry probabilityEntry = createUpdatedEntryFrom(&originalProbabilityEntry, - unigramProperty); + const ProbabilityEntry probabilityEntryOfUnigramProperty = ProbabilityEntry(unigramProperty); + const ProbabilityEntry updatedProbabilityEntry = + createUpdatedEntryFrom(&originalProbabilityEntry, &probabilityEntryOfUnigramProperty); return mBuffers->getMutableLanguageModelDictContent()->setProbabilityEntry( - toBeUpdatedPtNodeParams->getTerminalId(), &probabilityEntry); + toBeUpdatedPtNodeParams->getTerminalId(), &updatedProbabilityEntry); } bool Ver4PatriciaTrieNodeWriter::updatePtNodeProbabilityAndGetNeedsToKeepPtNodeAfterGC( @@ -216,16 +217,36 @@ bool Ver4PatriciaTrieNodeWriter::writeNewTerminalPtNodeAndAdvancePosition( } // Write probability. ProbabilityEntry newProbabilityEntry; + const ProbabilityEntry probabilityEntryOfUnigramProperty = ProbabilityEntry(unigramProperty); const ProbabilityEntry probabilityEntryToWrite = createUpdatedEntryFrom( - &newProbabilityEntry, unigramProperty); + &newProbabilityEntry, &probabilityEntryOfUnigramProperty); return mBuffers->getMutableLanguageModelDictContent()->setProbabilityEntry( terminalId, &probabilityEntryToWrite); } bool Ver4PatriciaTrieNodeWriter::addNgramEntry(const WordIdArrayView prevWordIds, const int wordId, const BigramProperty *const bigramProperty, bool *const outAddedNewBigram) { + // TODO: Support n-gram. + LanguageModelDictContent *const languageModelDictContent = + mBuffers->getMutableLanguageModelDictContent(); + const ProbabilityEntry probabilityEntry = + languageModelDictContent->getNgramProbabilityEntry( + prevWordIds.limit(1 /* maxSize */), wordId); + const ProbabilityEntry probabilityEntryOfBigramProperty(bigramProperty); + const ProbabilityEntry updatedProbabilityEntry = createUpdatedEntryFrom( + &probabilityEntry, &probabilityEntryOfBigramProperty); + if (!languageModelDictContent->setNgramProbabilityEntry( + prevWordIds.limit(1 /* maxSize */), wordId, &updatedProbabilityEntry)) { + AKLOGE("Cannot add new ngram entry. prevWordId: %d, wordId: %d", + prevWordIds[0], wordId); + return false; + } + if (!probabilityEntry.isValid() && outAddedNewBigram) { + *outAddedNewBigram = true; + } + // TODO: Remove. if (!mBigramPolicy->addNewEntry(prevWordIds[0], wordId, bigramProperty, outAddedNewBigram)) { - AKLOGE("Cannot add new bigram entry. terminalId: %d, targetTerminalId: %d", + AKLOGE("Cannot add new bigram entry. prevWordId: %d, wordId: %d", prevWordIds[0], wordId); return false; } @@ -234,6 +255,7 @@ bool Ver4PatriciaTrieNodeWriter::addNgramEntry(const WordIdArrayView prevWordIds bool Ver4PatriciaTrieNodeWriter::removeNgramEntry(const WordIdArrayView prevWordIds, const int wordId) { + // TODO: Remove. return mBigramPolicy->removeEntry(prevWordIds[0], wordId); } @@ -352,20 +374,19 @@ bool Ver4PatriciaTrieNodeWriter::writePtNodeAndGetTerminalIdAndAdvancePosition( const ProbabilityEntry Ver4PatriciaTrieNodeWriter::createUpdatedEntryFrom( const ProbabilityEntry *const originalProbabilityEntry, - const UnigramProperty *const unigramProperty) const { + const ProbabilityEntry *const probabilityEntry) const { // TODO: Consolidate historical info and probability. if (mHeaderPolicy->hasHistoricalInfoOfWords()) { - const HistoricalInfo historicalInfoForUpdate(unigramProperty->getTimestamp(), - unigramProperty->getLevel(), unigramProperty->getCount()); const HistoricalInfo updatedHistoricalInfo = ForgettingCurveUtils::createUpdatedHistoricalInfo( originalProbabilityEntry->getHistoricalInfo(), - unigramProperty->getProbability(), &historicalInfoForUpdate, mHeaderPolicy); + probabilityEntry->getProbability(), probabilityEntry->getHistoricalInfo(), + mHeaderPolicy); return originalProbabilityEntry->createEntryWithUpdatedHistoricalInfo( &updatedHistoricalInfo); } else { return originalProbabilityEntry->createEntryWithUpdatedProbability( - unigramProperty->getProbability()); + probabilityEntry->getProbability()); } } diff --git a/native/jni/src/suggest/policyimpl/dictionary/structure/v4/ver4_patricia_trie_node_writer.h b/native/jni/src/suggest/policyimpl/dictionary/structure/v4/ver4_patricia_trie_node_writer.h index 6703dba04..5d73b6ea3 100644 --- a/native/jni/src/suggest/policyimpl/dictionary/structure/v4/ver4_patricia_trie_node_writer.h +++ b/native/jni/src/suggest/policyimpl/dictionary/structure/v4/ver4_patricia_trie_node_writer.h @@ -98,12 +98,12 @@ class Ver4PatriciaTrieNodeWriter : public PtNodeWriter { const PtNodeParams *const ptNodeParams, int *const outTerminalId, int *const ptNodeWritingPos); - // Create updated probability entry using given unigram property. In addition to the + // Create updated probability entry using given probability property. In addition to the // probability, this method updates historical information if needed. - // TODO: Update flags belonging to the unigram property. + // TODO: Update flags. const ProbabilityEntry createUpdatedEntryFrom( const ProbabilityEntry *const originalProbabilityEntry, - const UnigramProperty *const unigramProperty) const; + const ProbabilityEntry *const probabilityEntry) const; bool updatePtNodeFlags(const int ptNodePos, const bool isBlacklisted, const bool isNotAWord, const bool isTerminal, const bool hasMultipleChars); diff --git a/native/jni/src/suggest/policyimpl/dictionary/structure/v4/ver4_patricia_trie_policy.cpp b/native/jni/src/suggest/policyimpl/dictionary/structure/v4/ver4_patricia_trie_policy.cpp index 723808399..d8f4595ff 100644 --- a/native/jni/src/suggest/policyimpl/dictionary/structure/v4/ver4_patricia_trie_policy.cpp +++ b/native/jni/src/suggest/policyimpl/dictionary/structure/v4/ver4_patricia_trie_policy.cpp @@ -200,7 +200,7 @@ bool Ver4PatriciaTriePolicy::addUnigramEntry(const int *const word, const int le } for (const auto &shortcut : unigramProperty->getShortcuts()) { if (shortcut.getTargetCodePoints()->size() > MAX_WORD_LENGTH) { - AKLOGE("One of shortcut targets is too long to insert to the dictionary, length: %d", + AKLOGE("One of shortcut targets is too long to insert to the dictionary, length: %zd", shortcut.getTargetCodePoints()->size()); return false; } @@ -235,7 +235,7 @@ bool Ver4PatriciaTriePolicy::addUnigramEntry(const int *const word, const int le if (!mUpdatingHelper.addShortcutTarget(wordPos, shortcut.getTargetCodePoints()->data(), shortcut.getTargetCodePoints()->size(), shortcut.getProbability())) { - AKLOGE("Cannot add new shortcut target. PtNodePos: %d, length: %d, " + AKLOGE("Cannot add new shortcut target. PtNodePos: %d, length: %zd, " "probability: %d", wordPos, shortcut.getTargetCodePoints()->size(), shortcut.getProbability()); return false; @@ -286,7 +286,7 @@ bool Ver4PatriciaTriePolicy::addNgramEntry(const PrevWordsInfo *const prevWordsI } if (bigramProperty->getTargetCodePoints()->size() > MAX_WORD_LENGTH) { AKLOGE("The word is too long to insert the ngram to the dictionary. " - "length: %d", bigramProperty->getTargetCodePoints()->size()); + "length: %zd", bigramProperty->getTargetCodePoints()->size()); return false; } int prevWordsPtNodePos[MAX_PREV_WORD_COUNT_FOR_N_GRAM]; diff --git a/native/jni/src/suggest/policyimpl/dictionary/utils/format_utils.cpp b/native/jni/src/suggest/policyimpl/dictionary/utils/format_utils.cpp index 1916ea560..e6e7167c2 100644 --- a/native/jni/src/suggest/policyimpl/dictionary/utils/format_utils.cpp +++ b/native/jni/src/suggest/policyimpl/dictionary/utils/format_utils.cpp @@ -23,7 +23,7 @@ namespace latinime { const uint32_t FormatUtils::MAGIC_NUMBER = 0x9BC13AFE; // Magic number (4 bytes), version (2 bytes), flags (2 bytes), header size (4 bytes) = 12 -const int FormatUtils::DICTIONARY_MINIMUM_SIZE = 12; +const size_t FormatUtils::DICTIONARY_MINIMUM_SIZE = 12; /* static */ FormatUtils::FORMAT_VERSION FormatUtils::getFormatVersion(const int formatVersion) { switch (formatVersion) { @@ -40,14 +40,14 @@ const int FormatUtils::DICTIONARY_MINIMUM_SIZE = 12; } } /* static */ FormatUtils::FORMAT_VERSION FormatUtils::detectFormatVersion( - const uint8_t *const dict, const int dictSize) { + const ReadOnlyByteArrayView dictBuffer) { // The magic number is stored big-endian. // If the dictionary is less than 4 bytes, we can't even read the magic number, so we don't // understand this format. - if (dictSize < DICTIONARY_MINIMUM_SIZE) { + if (dictBuffer.size() < DICTIONARY_MINIMUM_SIZE) { return UNKNOWN_VERSION; } - const uint32_t magicNumber = ByteArrayUtils::readUint32(dict, 0); + const uint32_t magicNumber = ByteArrayUtils::readUint32(dictBuffer.data(), 0); switch (magicNumber) { case MAGIC_NUMBER: // The layout of the header is as follows: @@ -58,7 +58,7 @@ const int FormatUtils::DICTIONARY_MINIMUM_SIZE = 12; // Conceptually this converts the hardcoded value of the bytes in the file into // the symbolic value we use in the code. But we want the constants to be the // same so we use them for both here. - return getFormatVersion(ByteArrayUtils::readUint16(dict, 4)); + return getFormatVersion(ByteArrayUtils::readUint16(dictBuffer.data(), 4)); default: return UNKNOWN_VERSION; } diff --git a/native/jni/src/suggest/policyimpl/dictionary/utils/format_utils.h b/native/jni/src/suggest/policyimpl/dictionary/utils/format_utils.h index 55ad5799f..51ad9877c 100644 --- a/native/jni/src/suggest/policyimpl/dictionary/utils/format_utils.h +++ b/native/jni/src/suggest/policyimpl/dictionary/utils/format_utils.h @@ -20,6 +20,7 @@ #include <cstdint> #include "defines.h" +#include "utils/byte_array_view.h" namespace latinime { @@ -42,12 +43,12 @@ class FormatUtils { static const uint32_t MAGIC_NUMBER; static FORMAT_VERSION getFormatVersion(const int formatVersion); - static FORMAT_VERSION detectFormatVersion(const uint8_t *const dict, const int dictSize); + static FORMAT_VERSION detectFormatVersion(const ReadOnlyByteArrayView dictBuffer); private: DISALLOW_IMPLICIT_CONSTRUCTORS(FormatUtils); - static const int DICTIONARY_MINIMUM_SIZE; + static const size_t DICTIONARY_MINIMUM_SIZE; }; } // namespace latinime #endif /* LATINIME_FORMAT_UTILS_H */ diff --git a/native/jni/src/suggest/policyimpl/dictionary/utils/trie_map.cpp b/native/jni/src/suggest/policyimpl/dictionary/utils/trie_map.cpp index 407b8efd0..e630aba9a 100644 --- a/native/jni/src/suggest/policyimpl/dictionary/utils/trie_map.cpp +++ b/native/jni/src/suggest/policyimpl/dictionary/utils/trie_map.cpp @@ -26,6 +26,7 @@ const int TrieMap::FIELD1_SIZE = 3; const int TrieMap::ENTRY_SIZE = FIELD0_SIZE + FIELD1_SIZE; const uint32_t TrieMap::VALUE_FLAG = 0x400000; const uint32_t TrieMap::VALUE_MASK = 0x3FFFFF; +const uint32_t TrieMap::INVALID_VALUE_IN_KEY_VALUE_ENTRY = VALUE_MASK; const uint32_t TrieMap::TERMINAL_LINK_FLAG = 0x800000; const uint32_t TrieMap::TERMINAL_LINK_MASK = 0x7FFFFF; const int TrieMap::NUM_OF_BITS_USED_FOR_ONE_LEVEL = 5; @@ -34,6 +35,7 @@ const int TrieMap::MAX_NUM_OF_ENTRIES_IN_ONE_LEVEL = 1 << NUM_OF_BITS_USED_FOR_O const int TrieMap::ROOT_BITMAP_ENTRY_INDEX = 0; const int TrieMap::ROOT_BITMAP_ENTRY_POS = MAX_NUM_OF_ENTRIES_IN_ONE_LEVEL * FIELD0_SIZE; const TrieMap::Entry TrieMap::EMPTY_BITMAP_ENTRY = TrieMap::Entry(0, 0); +const int TrieMap::TERMINAL_LINKED_ENTRY_COUNT = 2; // Value entry and bitmap entry. const uint64_t TrieMap::MAX_VALUE = (static_cast<uint64_t>(1) << ((FIELD0_SIZE + FIELD1_SIZE) * CHAR_BIT)) - 1; const int TrieMap::MAX_BUFFER_SIZE = TERMINAL_LINK_MASK * ENTRY_SIZE; @@ -76,7 +78,7 @@ int TrieMap::getNextLevelBitmapEntryIndex(const int key, const int bitmapEntryIn return terminalEntry.getValueEntryIndex() + 1; } // Create a value entry and a bitmap entry. - const int valueEntryIndex = allocateTable(2 /* entryCount */); + const int valueEntryIndex = allocateTable(TERMINAL_LINKED_ENTRY_COUNT); if (!writeEntry(Entry(0, terminalEntry.getValue()), valueEntryIndex)) { return INVALID_INDEX; } @@ -108,6 +110,31 @@ bool TrieMap::save(FILE *const file) const { return DictFileWritingUtils::writeBufferToFileTail(file, &mBuffer); } +bool TrieMap::remove(const int key, const int bitmapEntryIndex) { + const Entry bitmapEntry = readEntry(bitmapEntryIndex); + const uint32_t unsignedKey = static_cast<uint32_t>(key); + const int terminalEntryIndex = getTerminalEntryIndex( + unsignedKey, getBitShuffledKey(unsignedKey), bitmapEntry, 0 /* level */); + if (terminalEntryIndex == INVALID_INDEX) { + // Not found. + return false; + } + const Entry terminalEntry = readEntry(terminalEntryIndex); + if (!writeField1(VALUE_FLAG ^ INVALID_VALUE_IN_KEY_VALUE_ENTRY , terminalEntryIndex)) { + return false; + } + if (terminalEntry.hasTerminalLink()) { + const Entry nextLevelBitmapEntry = readEntry(terminalEntry.getValueEntryIndex() + 1); + if (!freeTable(terminalEntry.getValueEntryIndex(), TERMINAL_LINKED_ENTRY_COUNT)) { + return false; + } + if (!removeInner(nextLevelBitmapEntry)){ + return false; + } + } + return true; +} + /** * Iterate next entry in a certain level. * @@ -129,7 +156,7 @@ const TrieMap::Result TrieMap::iterateNext(std::vector<TableIterationState> *con if (entry.isBitmapEntry()) { // Move to child. iterationState->emplace_back(popCount(entry.getBitmap()), entry.getTableIndex()); - } else { + } else if (entry.isValidTerminalEntry()) { if (outKey) { *outKey = entry.getKey(); } @@ -162,12 +189,12 @@ uint32_t TrieMap::getBitShuffledKey(const uint32_t key) const { } bool TrieMap::writeValue(const uint64_t value, const int terminalEntryIndex) { - if (value <= VALUE_MASK) { + if (value < VALUE_MASK) { // Write value into the terminal entry. return writeField1(value | VALUE_FLAG, terminalEntryIndex); } // Create value entry and write value. - const int valueEntryIndex = allocateTable(2 /* entryCount */); + const int valueEntryIndex = allocateTable(TERMINAL_LINKED_ENTRY_COUNT); if (!writeEntry(Entry(value >> (FIELD1_SIZE * CHAR_BIT), value), valueEntryIndex)) { return false; } @@ -227,6 +254,9 @@ int TrieMap::getTerminalEntryIndex(const uint32_t key, const uint32_t hashedKey, // Move to the next level. return getTerminalEntryIndex(key, hashedKey, entry, level + 1); } + if (!entry.isValidTerminalEntry()) { + return INVALID_INDEX; + } if (entry.getKey() == key) { // Terminal entry is found. return entryIndex; @@ -287,6 +317,10 @@ bool TrieMap::putInternal(const uint32_t key, const uint64_t value, const uint32 // Bitmap entry is found. Go to the next level. return putInternal(key, value, hashedKey, entryIndex, entry, level + 1); } + if (!entry.isValidTerminalEntry()) { + // Overwrite invalid terminal entry. + return writeTerminalEntry(key, value, entryIndex); + } if (entry.getKey() == key) { // Terminal entry for the key is found. Update the value. return updateValue(entry, value, entryIndex); @@ -384,4 +418,33 @@ bool TrieMap::addNewEntryByExpandingTable(const uint32_t key, const uint64_t val return true; } +bool TrieMap::removeInner(const Entry &bitmapEntry) { + const int tableSize = popCount(bitmapEntry.getBitmap()); + for (int i = 0; i < tableSize; ++i) { + const int entryIndex = bitmapEntry.getTableIndex() + i; + const Entry entry = readEntry(entryIndex); + if (entry.isBitmapEntry()) { + // Delete next bitmap entry recursively. + if (!removeInner(entry)) { + return false; + } + } else { + // Invalidate terminal entry just in case. + if (!writeField1(VALUE_FLAG ^ INVALID_VALUE_IN_KEY_VALUE_ENTRY , entryIndex)) { + return false; + } + if (entry.hasTerminalLink()) { + const Entry nextLevelBitmapEntry = readEntry(entry.getValueEntryIndex() + 1); + if (!freeTable(entry.getValueEntryIndex(), TERMINAL_LINKED_ENTRY_COUNT)) { + return false; + } + if (!removeInner(nextLevelBitmapEntry)) { + return false; + } + } + } + } + return freeTable(bitmapEntry.getTableIndex(), tableSize); +} + } // namespace latinime diff --git a/native/jni/src/suggest/policyimpl/dictionary/utils/trie_map.h b/native/jni/src/suggest/policyimpl/dictionary/utils/trie_map.h index 3e5c4010c..6d91790b2 100644 --- a/native/jni/src/suggest/policyimpl/dictionary/utils/trie_map.h +++ b/native/jni/src/suggest/policyimpl/dictionary/utils/trie_map.h @@ -202,6 +202,8 @@ class TrieMap { bool save(FILE *const file) const; + bool remove(const int key, const int bitmapEntryIndex); + private: DISALLOW_COPY_AND_ASSIGN(TrieMap); @@ -245,6 +247,11 @@ class TrieMap { } // For terminal entry. + AK_FORCE_INLINE bool isValidTerminalEntry() const { + return hasTerminalLink() || ((mData1 & VALUE_MASK) != INVALID_VALUE_IN_KEY_VALUE_ENTRY); + } + + // For terminal entry. AK_FORCE_INLINE uint32_t getValueEntryIndex() const { return mData1 & TERMINAL_LINK_MASK; } @@ -272,6 +279,7 @@ class TrieMap { static const int ENTRY_SIZE; static const uint32_t VALUE_FLAG; static const uint32_t VALUE_MASK; + static const uint32_t INVALID_VALUE_IN_KEY_VALUE_ENTRY; static const uint32_t TERMINAL_LINK_FLAG; static const uint32_t TERMINAL_LINK_MASK; static const int NUM_OF_BITS_USED_FOR_ONE_LEVEL; @@ -280,6 +288,7 @@ class TrieMap { static const int ROOT_BITMAP_ENTRY_INDEX; static const int ROOT_BITMAP_ENTRY_POS; static const Entry EMPTY_BITMAP_ENTRY; + static const int TERMINAL_LINKED_ENTRY_COUNT; static const int MAX_BUFFER_SIZE; uint32_t getBitShuffledKey(const uint32_t key) const; @@ -378,6 +387,8 @@ class TrieMap { AK_FORCE_INLINE int getTailEntryIndex() const { return (mBuffer.getTailPosition() - ROOT_BITMAP_ENTRY_POS) / ENTRY_SIZE; } + + bool removeInner(const Entry &bitmapEntry); }; } // namespace latinime diff --git a/native/jni/src/suggest/policyimpl/typing/typing_scoring.h b/native/jni/src/suggest/policyimpl/typing/typing_scoring.h index 04cb6603a..52c4251f0 100644 --- a/native/jni/src/suggest/policyimpl/typing/typing_scoring.h +++ b/native/jni/src/suggest/policyimpl/typing/typing_scoring.h @@ -51,10 +51,10 @@ class TypingScoring : public Scoring { } if (boostExactMatches && ErrorTypeUtils::isExactMatch(containedErrorTypes)) { score += ScoringParams::EXACT_MATCH_PROMOTION; - if ((ErrorTypeUtils::MATCH_WITH_CASE_ERROR & containedErrorTypes) != 0) { + if ((ErrorTypeUtils::MATCH_WITH_WRONG_CASE & containedErrorTypes) != 0) { score -= ScoringParams::CASE_ERROR_PENALTY_FOR_EXACT_MATCH; } - if ((ErrorTypeUtils::MATCH_WITH_ACCENT_ERROR & containedErrorTypes) != 0) { + if ((ErrorTypeUtils::MATCH_WITH_MISSING_ACCENT & containedErrorTypes) != 0) { score -= ScoringParams::ACCENT_ERROR_PENALTY_FOR_EXACT_MATCH; } if ((ErrorTypeUtils::MATCH_WITH_DIGRAPH & containedErrorTypes) != 0) { diff --git a/native/jni/src/suggest/policyimpl/typing/typing_weighting.cpp b/native/jni/src/suggest/policyimpl/typing/typing_weighting.cpp index 54f65c786..1d590c353 100644 --- a/native/jni/src/suggest/policyimpl/typing/typing_weighting.cpp +++ b/native/jni/src/suggest/policyimpl/typing/typing_weighting.cpp @@ -36,25 +36,34 @@ ErrorTypeUtils::ErrorType TypingWeighting::getErrorType(const CorrectionType cor // Compare the node code point with original primary code point on the keyboard. const ProximityInfoState *const pInfoState = traverseSession->getProximityInfoState(0); - const int primaryOriginalCodePoint = pInfoState->getPrimaryOriginalCodePointAt( + const int primaryCodePoint = pInfoState->getPrimaryCodePointAt( dicNode->getInputIndex(0)); const int nodeCodePoint = dicNode->getNodeCodePoint(); - if (primaryOriginalCodePoint == nodeCodePoint) { + // TODO: Check whether the input code point is on the keyboard. + if (primaryCodePoint == nodeCodePoint) { // Node code point is same as original code point on the keyboard. return ErrorTypeUtils::NOT_AN_ERROR; - } else if (CharUtils::toLowerCase(primaryOriginalCodePoint) == + } else if (CharUtils::toLowerCase(primaryCodePoint) == CharUtils::toLowerCase(nodeCodePoint)) { // Only cases of the code points are different. - return ErrorTypeUtils::MATCH_WITH_CASE_ERROR; - } else if (CharUtils::toBaseCodePoint(primaryOriginalCodePoint) == - CharUtils::toBaseCodePoint(nodeCodePoint)) { + return ErrorTypeUtils::MATCH_WITH_WRONG_CASE; + } else if (primaryCodePoint == CharUtils::toBaseCodePoint(nodeCodePoint)) { // Node code point is a variant of original code point. - return ErrorTypeUtils::MATCH_WITH_ACCENT_ERROR; - } else { + return ErrorTypeUtils::MATCH_WITH_MISSING_ACCENT; + } else if (CharUtils::toBaseCodePoint(primaryCodePoint) + == CharUtils::toBaseCodePoint(nodeCodePoint)) { + // Base code points are the same but the code point is intentionally input. + return ErrorTypeUtils::MATCH_WITH_WRONG_ACCENT; + } else if (CharUtils::toLowerCase(primaryCodePoint) + == CharUtils::toBaseLowerCase(nodeCodePoint)) { // Node code point is a variant of original code point and the cases are also // different. - return ErrorTypeUtils::MATCH_WITH_ACCENT_ERROR - | ErrorTypeUtils::MATCH_WITH_CASE_ERROR; + return ErrorTypeUtils::MATCH_WITH_MISSING_ACCENT + | ErrorTypeUtils::MATCH_WITH_WRONG_CASE; + } else { + // Base code points are the same and the cases are different. + return ErrorTypeUtils::MATCH_WITH_WRONG_ACCENT + | ErrorTypeUtils::MATCH_WITH_WRONG_CASE; } } break; diff --git a/native/jni/src/utils/int_array_view.h b/native/jni/src/utils/int_array_view.h index c1ddc9812..53f2d2971 100644 --- a/native/jni/src/utils/int_array_view.h +++ b/native/jni/src/utils/int_array_view.h @@ -91,6 +91,11 @@ class IntArrayView { return mPtr + mSize; } + // Returns the view whose size is smaller than or equal to the given count. + const IntArrayView limit(const size_t maxSize) const { + return IntArrayView(mPtr, std::min(maxSize, mSize)); + } + private: DISALLOW_ASSIGNMENT_OPERATOR(IntArrayView); diff --git a/native/jni/tests/suggest/policyimpl/dictionary/utils/trie_map_test.cpp b/native/jni/tests/suggest/policyimpl/dictionary/utils/trie_map_test.cpp index df778b6cf..8c8e8838a 100644 --- a/native/jni/tests/suggest/policyimpl/dictionary/utils/trie_map_test.cpp +++ b/native/jni/tests/suggest/policyimpl/dictionary/utils/trie_map_test.cpp @@ -47,6 +47,31 @@ TEST(TrieMapTest, TestSetAndGet) { EXPECT_EQ(0xFFFFFFFFFull, trieMap.getRoot(0).mValue); } +TEST(TrieMapTest, TestRemove) { + TrieMap trieMap; + trieMap.putRoot(10, 10); + EXPECT_EQ(10ull, trieMap.getRoot(10).mValue); + EXPECT_TRUE(trieMap.remove(10, trieMap.getRootBitmapEntryIndex())); + EXPECT_FALSE(trieMap.getRoot(10).mIsValid); + for (const auto &element : trieMap.getEntriesInRootLevel()) { + EXPECT_TRUE(false); + } + EXPECT_TRUE(trieMap.putRoot(10, 0x3FFFFF)); + EXPECT_FALSE(trieMap.remove(11, trieMap.getRootBitmapEntryIndex())) + << "Should fail if the key does not exist."; + EXPECT_EQ(0x3FFFFFull, trieMap.getRoot(10).mValue); + trieMap.putRoot(12, 11); + const int nextLevel = trieMap.getNextLevelBitmapEntryIndex(10); + trieMap.put(10, 10, nextLevel); + EXPECT_EQ(0x3FFFFFull, trieMap.getRoot(10).mValue); + EXPECT_EQ(10ull, trieMap.get(10, nextLevel).mValue); + EXPECT_TRUE(trieMap.remove(10, trieMap.getRootBitmapEntryIndex())); + const TrieMap::Result result = trieMap.getRoot(10); + EXPECT_FALSE(result.mIsValid); + EXPECT_EQ(TrieMap::INVALID_INDEX, result.mNextLevelBitmapEntryIndex); + EXPECT_EQ(11ull, trieMap.getRoot(12).mValue); +} + TEST(TrieMapTest, TestSetAndGetLarge) { static const int ELEMENT_COUNT = 200000; TrieMap trieMap; diff --git a/native/jni/tests/utils/int_array_view_test.cpp b/native/jni/tests/utils/int_array_view_test.cpp index bd843ab02..ecc451af0 100644 --- a/native/jni/tests/utils/int_array_view_test.cpp +++ b/native/jni/tests/utils/int_array_view_test.cpp @@ -53,9 +53,24 @@ TEST(IntArrayViewTest, TestConstructFromArray) { TEST(IntArrayViewTest, TestConstructFromObject) { const int object = 10; const auto intArrayView = IntArrayView::fromObject(&object); - EXPECT_EQ(1, intArrayView.size()); + EXPECT_EQ(1u, intArrayView.size()); EXPECT_EQ(object, intArrayView[0]); } +TEST(IntArrayViewTest, TestLimit) { + const std::vector<int> intVector = {3, 2, 1, 0, -1, -2}; + IntArrayView intArrayView(intVector); + + EXPECT_TRUE(intArrayView.limit(0).empty()); + EXPECT_EQ(intArrayView.size(), intArrayView.limit(intArrayView.size()).size()); + EXPECT_EQ(intArrayView.size(), intArrayView.limit(1000).size()); + + IntArrayView subView = intArrayView.limit(4); + EXPECT_EQ(4u, subView.size()); + for (size_t i = 0; i < subView.size(); ++i) { + EXPECT_EQ(intVector[i], subView[i]); + } +} + } // namespace } // namespace latinime diff --git a/tests/src/com/android/inputmethod/keyboard/KeyboardThemeTests.java b/tests/src/com/android/inputmethod/keyboard/KeyboardThemeTests.java index 0c7e4000e..c20954f81 100644 --- a/tests/src/com/android/inputmethod/keyboard/KeyboardThemeTests.java +++ b/tests/src/com/android/inputmethod/keyboard/KeyboardThemeTests.java @@ -16,6 +16,7 @@ package com.android.inputmethod.keyboard; +import static com.android.inputmethod.compat.BuildCompatUtils.VERSION_CODES_LXX; import static com.android.inputmethod.keyboard.KeyboardTheme.THEME_ID_ICS; import static com.android.inputmethod.keyboard.KeyboardTheme.THEME_ID_KLP; import static com.android.inputmethod.keyboard.KeyboardTheme.THEME_ID_LXX_DARK; @@ -31,9 +32,6 @@ import android.test.suitebuilder.annotation.SmallTest; public class KeyboardThemeTests extends AndroidTestCase { private SharedPreferences mPrefs; - // TODO: Remove this constant once the *next* version becomes available. - private static final int VERSION_CODES_LXX = VERSION_CODES.CUR_DEVELOPMENT; - private static final int THEME_ID_NULL = -1; private static final int THEME_ID_UNKNOWN = -2; private static final int THEME_ID_ILLEGAL = -3; diff --git a/tests/src/com/android/inputmethod/keyboard/layout/ArmenianPhonetic.java b/tests/src/com/android/inputmethod/keyboard/layout/ArmenianPhonetic.java index 42ce0c1ea..dba91b4da 100644 --- a/tests/src/com/android/inputmethod/keyboard/layout/ArmenianPhonetic.java +++ b/tests/src/com/android/inputmethod/keyboard/layout/ArmenianPhonetic.java @@ -39,6 +39,9 @@ public final class ArmenianPhonetic extends LayoutBase { public ArmenianPhoneticCustomizer(final Locale locale) { super(locale); } @Override + public int getNumberOfRows() { return 5; } + + @Override public ExpectedKey getAlphabetKey() { return ARMENIAN_ALPHABET_KEY; } @Override @@ -112,29 +115,6 @@ public final class ArmenianPhonetic extends LayoutBase { return builder.build(); } - // Helper method to create alphabet layout by adding special function keys. - @Override - ExpectedKeyboardBuilder convertCommonLayoutToKeyboard(final ExpectedKeyboardBuilder builder, - final boolean isPhone) { - final LayoutCustomizer customizer = getCustomizer(); - builder.setKeysOfRow(5, (Object[])customizer.getSpaceKeys(isPhone)); - builder.addKeysOnTheLeftOfRow(5, (Object[])customizer.getKeysLeftToSpacebar(isPhone)); - builder.addKeysOnTheRightOfRow(5, (Object[])customizer.getKeysRightToSpacebar(isPhone)); - if (isPhone) { - builder.addKeysOnTheRightOfRow(4, DELETE_KEY) - .addKeysOnTheLeftOfRow(5, customizer.getSymbolsKey()) - .addKeysOnTheRightOfRow(5, key(ENTER_KEY, EMOJI_KEY)); - } else { - builder.addKeysOnTheRightOfRow(1, DELETE_KEY) - .addKeysOnTheRightOfRow(3, ENTER_KEY) - .addKeysOnTheLeftOfRow(5, customizer.getSymbolsKey()) - .addKeysOnTheRightOfRow(5, EMOJI_KEY); - } - builder.addKeysOnTheLeftOfRow(4, (Object[])customizer.getLeftShiftKeys(isPhone)) - .addKeysOnTheRightOfRow(4, (Object[])customizer.getRightShiftKeys(isPhone)); - return builder; - } - private static final ExpectedKey[][] ALPHABET_COMMON = new ExpectedKeyboardBuilder() .setKeysOfRow(1, // U+0567: "է" ARMENIAN SMALL LETTER EH diff --git a/tests/src/com/android/inputmethod/keyboard/layout/Khmer.java b/tests/src/com/android/inputmethod/keyboard/layout/Khmer.java index 143ccf6eb..7e4f159ab 100644 --- a/tests/src/com/android/inputmethod/keyboard/layout/Khmer.java +++ b/tests/src/com/android/inputmethod/keyboard/layout/Khmer.java @@ -40,6 +40,9 @@ public final class Khmer extends LayoutBase { public KhmerCustomizer(final Locale locale) { super(locale); } @Override + public int getNumberOfRows() { return 5; } + + @Override public ExpectedKey getAlphabetKey() { return KHMER_ALPHABET_KEY; } @Override @@ -79,29 +82,6 @@ public final class Khmer extends LayoutBase { return ALPHABET_SHIFTED_COMMON; } - // Helper method to create alphabet layout by adding special function keys. - @Override - ExpectedKeyboardBuilder convertCommonLayoutToKeyboard(final ExpectedKeyboardBuilder builder, - final boolean isPhone) { - final LayoutCustomizer customizer = getCustomizer(); - builder.setKeysOfRow(5, (Object[])customizer.getSpaceKeys(isPhone)); - builder.addKeysOnTheLeftOfRow(5, (Object[])customizer.getKeysLeftToSpacebar(isPhone)); - builder.addKeysOnTheRightOfRow(5, (Object[])customizer.getKeysRightToSpacebar(isPhone)); - if (isPhone) { - builder.addKeysOnTheRightOfRow(4, DELETE_KEY) - .addKeysOnTheLeftOfRow(5, customizer.getSymbolsKey()) - .addKeysOnTheRightOfRow(5, key(ENTER_KEY, EMOJI_KEY)); - } else { - builder.addKeysOnTheRightOfRow(1, DELETE_KEY) - .addKeysOnTheRightOfRow(3, ENTER_KEY) - .addKeysOnTheLeftOfRow(5, customizer.getSymbolsKey()) - .addKeysOnTheRightOfRow(5, EMOJI_KEY); - } - builder.addKeysOnTheLeftOfRow(4, (Object[])customizer.getLeftShiftKeys(isPhone)) - .addKeysOnTheRightOfRow(4, (Object[])customizer.getRightShiftKeys(isPhone)); - return builder; - } - private static final ExpectedKey[][] ALPHABET_COMMON = new ExpectedKeyboardBuilder() .setKeysOfRow(1, // U+17E1: "១" KHMER DIGIT ONE diff --git a/tests/src/com/android/inputmethod/keyboard/layout/Lao.java b/tests/src/com/android/inputmethod/keyboard/layout/Lao.java index e7be9982a..aaa1c8a8a 100644 --- a/tests/src/com/android/inputmethod/keyboard/layout/Lao.java +++ b/tests/src/com/android/inputmethod/keyboard/layout/Lao.java @@ -40,6 +40,9 @@ public final class Lao extends LayoutBase { public LaoCustomizer(final Locale locale) { super(locale); } @Override + public int getNumberOfRows() { return 5; } + + @Override public ExpectedKey getAlphabetKey() { return LAO_ALPHABET_KEY; } @Override @@ -83,29 +86,6 @@ public final class Lao extends LayoutBase { return ALPHABET_SHIFTED_COMMON; } - // Helper method to create alphabet layout by adding special function keys. - @Override - ExpectedKeyboardBuilder convertCommonLayoutToKeyboard(final ExpectedKeyboardBuilder builder, - final boolean isPhone) { - final LayoutCustomizer customizer = getCustomizer(); - builder.setKeysOfRow(5, (Object[])customizer.getSpaceKeys(isPhone)); - builder.addKeysOnTheLeftOfRow(5, (Object[])customizer.getKeysLeftToSpacebar(isPhone)); - builder.addKeysOnTheRightOfRow(5, (Object[])customizer.getKeysRightToSpacebar(isPhone)); - if (isPhone) { - builder.addKeysOnTheRightOfRow(4, DELETE_KEY) - .addKeysOnTheLeftOfRow(5, customizer.getSymbolsKey()) - .addKeysOnTheRightOfRow(5, key(ENTER_KEY, EMOJI_KEY)); - } else { - builder.addKeysOnTheRightOfRow(1, DELETE_KEY) - .addKeysOnTheRightOfRow(3, ENTER_KEY) - .addKeysOnTheLeftOfRow(5, customizer.getSymbolsKey()) - .addKeysOnTheRightOfRow(5, EMOJI_KEY); - } - builder.addKeysOnTheLeftOfRow(4, (Object[])customizer.getLeftShiftKeys(isPhone)) - .addKeysOnTheRightOfRow(4, (Object[])customizer.getRightShiftKeys(isPhone)); - return builder; - } - private static final ExpectedKey[][] ALPHABET_COMMON = new ExpectedKeyboardBuilder() .setKeysOfRow(1, // U+0EA2: "ຢ" LAO LETTER YO diff --git a/tests/src/com/android/inputmethod/keyboard/layout/LayoutBase.java b/tests/src/com/android/inputmethod/keyboard/layout/LayoutBase.java index c5223720c..0548a010d 100644 --- a/tests/src/com/android/inputmethod/keyboard/layout/LayoutBase.java +++ b/tests/src/com/android/inputmethod/keyboard/layout/LayoutBase.java @@ -46,6 +46,10 @@ public abstract class LayoutBase extends AbstractLayoutBase { return mLocale; } + public int getNumberOfRows() { + return 4; + } + /** * Set accented letters to common layout. * @param builder the {@link ExpectedKeyboardBuilder} object that contains common keyboard @@ -277,7 +281,7 @@ public abstract class LayoutBase extends AbstractLayoutBase { ",", "'", "#", ")", "(", "/", ";", "@", ":", "-", "\"", "+", "%", "&"); - /** + /** * Helper method to create alphabet layout adding special function keys. * @param builder the {@link ExpectedKeyboardBuilder} object that contains common keyboard * layout @@ -287,21 +291,26 @@ public abstract class LayoutBase extends AbstractLayoutBase { ExpectedKeyboardBuilder convertCommonLayoutToKeyboard(final ExpectedKeyboardBuilder builder, final boolean isPhone) { final LayoutCustomizer customizer = getCustomizer(); - builder.setKeysOfRow(4, (Object[])customizer.getSpaceKeys(isPhone)); - builder.addKeysOnTheLeftOfRow(4, (Object[])customizer.getKeysLeftToSpacebar(isPhone)); - builder.addKeysOnTheRightOfRow(4, (Object[])customizer.getKeysRightToSpacebar(isPhone)); + final int numberOfRows = customizer.getNumberOfRows(); + builder.setKeysOfRow(numberOfRows, (Object[])customizer.getSpaceKeys(isPhone)); + builder.addKeysOnTheLeftOfRow( + numberOfRows, (Object[])customizer.getKeysLeftToSpacebar(isPhone)); + builder.addKeysOnTheRightOfRow( + numberOfRows, (Object[])customizer.getKeysRightToSpacebar(isPhone)); if (isPhone) { - builder.addKeysOnTheRightOfRow(3, DELETE_KEY) - .addKeysOnTheLeftOfRow(4, customizer.getSymbolsKey()) - .addKeysOnTheRightOfRow(4, key(ENTER_KEY, EMOJI_KEY)); + builder.addKeysOnTheRightOfRow(numberOfRows - 1, DELETE_KEY) + .addKeysOnTheLeftOfRow(numberOfRows, customizer.getSymbolsKey()) + .addKeysOnTheRightOfRow(numberOfRows, key(ENTER_KEY, EMOJI_ACTION_KEY)); } else { builder.addKeysOnTheRightOfRow(1, DELETE_KEY) - .addKeysOnTheRightOfRow(2, ENTER_KEY) - .addKeysOnTheLeftOfRow(4, customizer.getSymbolsKey()) - .addKeysOnTheRightOfRow(4, EMOJI_KEY); + .addKeysOnTheRightOfRow(numberOfRows - 2, ENTER_KEY) + .addKeysOnTheLeftOfRow(numberOfRows, customizer.getSymbolsKey()) + .addKeysOnTheRightOfRow(numberOfRows, EMOJI_NORMAL_KEY); } - builder.addKeysOnTheLeftOfRow(3, (Object[])customizer.getLeftShiftKeys(isPhone)) - .addKeysOnTheRightOfRow(3, (Object[])customizer.getRightShiftKeys(isPhone)); + builder.addKeysOnTheLeftOfRow( + numberOfRows - 1, (Object[])customizer.getLeftShiftKeys(isPhone)); + builder.addKeysOnTheRightOfRow( + numberOfRows - 1, (Object[])customizer.getRightShiftKeys(isPhone)); return builder; } diff --git a/tests/src/com/android/inputmethod/keyboard/layout/Myanmar.java b/tests/src/com/android/inputmethod/keyboard/layout/Myanmar.java index f2a2dfdd8..15c74ed8c 100644 --- a/tests/src/com/android/inputmethod/keyboard/layout/Myanmar.java +++ b/tests/src/com/android/inputmethod/keyboard/layout/Myanmar.java @@ -40,6 +40,9 @@ public final class Myanmar extends LayoutBase { public MyanmarCustomizer(final Locale locale) { super(locale); } @Override + public int getNumberOfRows() { return 5; } + + @Override public ExpectedKey getAlphabetKey() { return MYANMAR_ALPHABET_KEY; } @Override @@ -97,29 +100,6 @@ public final class Myanmar extends LayoutBase { return ALPHABET_SHIFTED_COMMON; } - // Helper method to create alphabet layout by adding special function keys. - @Override - ExpectedKeyboardBuilder convertCommonLayoutToKeyboard(final ExpectedKeyboardBuilder builder, - final boolean isPhone) { - final LayoutCustomizer customizer = getCustomizer(); - builder.setKeysOfRow(5, (Object[])customizer.getSpaceKeys(isPhone)); - builder.addKeysOnTheLeftOfRow(5, (Object[])customizer.getKeysLeftToSpacebar(isPhone)); - builder.addKeysOnTheRightOfRow(5, (Object[])customizer.getKeysRightToSpacebar(isPhone)); - if (isPhone) { - builder.addKeysOnTheRightOfRow(4, DELETE_KEY) - .addKeysOnTheLeftOfRow(5, customizer.getSymbolsKey()) - .addKeysOnTheRightOfRow(5, key(ENTER_KEY, EMOJI_KEY)); - } else { - builder.addKeysOnTheRightOfRow(1, DELETE_KEY) - .addKeysOnTheRightOfRow(3, ENTER_KEY) - .addKeysOnTheLeftOfRow(5, customizer.getSymbolsKey()) - .addKeysOnTheRightOfRow(5, EMOJI_KEY); - } - builder.addKeysOnTheLeftOfRow(4, (Object[])customizer.getLeftShiftKeys(isPhone)) - .addKeysOnTheRightOfRow(4, (Object[])customizer.getRightShiftKeys(isPhone)); - return builder; - } - private static final ExpectedKey[][] ALPHABET_COMMON = new ExpectedKeyboardBuilder() .setKeysOfRow(1, // U+1041: "၁" MYANMAR DIGIT ONE diff --git a/tests/src/com/android/inputmethod/keyboard/layout/PcQwerty.java b/tests/src/com/android/inputmethod/keyboard/layout/PcQwerty.java index 9da6dcc44..3f7340fd0 100644 --- a/tests/src/com/android/inputmethod/keyboard/layout/PcQwerty.java +++ b/tests/src/com/android/inputmethod/keyboard/layout/PcQwerty.java @@ -39,6 +39,9 @@ public final class PcQwerty extends LayoutBase { public PcQwertyCustomizer(final Locale locale) { super(locale); } @Override + public int getNumberOfRows() { return 5; } + + @Override public ExpectedKey[] getLeftShiftKeys(final boolean isPhone) { return joinKeys(SHIFT_KEY); } @@ -55,7 +58,9 @@ public final class PcQwerty extends LayoutBase { @Override public ExpectedKey[] getKeysRightToSpacebar(final boolean isPhone) { - return isPhone ? joinKeys(key(ENTER_KEY, EMOJI_KEY)) : joinKeys(EMOJI_KEY); + return isPhone + ? joinKeys(key(ENTER_KEY, EMOJI_ACTION_KEY)) + : joinKeys(EMOJI_NORMAL_KEY); } } diff --git a/tests/src/com/android/inputmethod/keyboard/layout/Symbols.java b/tests/src/com/android/inputmethod/keyboard/layout/Symbols.java index 5f3e4b196..2cee2d909 100644 --- a/tests/src/com/android/inputmethod/keyboard/layout/Symbols.java +++ b/tests/src/com/android/inputmethod/keyboard/layout/Symbols.java @@ -43,7 +43,7 @@ public class Symbols extends AbstractLayoutBase { builder.addKeysOnTheLeftOfRow(3, customizer.getSymbolsShiftKey(isPhone)) .addKeysOnTheRightOfRow(3, DELETE_KEY) .addKeysOnTheLeftOfRow(4, customizer.getAlphabetKey()) - .addKeysOnTheRightOfRow(4, key(ENTER_KEY, EMOJI_KEY)); + .addKeysOnTheRightOfRow(4, key(ENTER_KEY, EMOJI_ACTION_KEY)); } else { // Tablet symbols keyboard has extra two keys at the left edge of the 3rd row. builder.addKeysOnTheLeftOfRow(3, (Object[])joinKeys("\\", "=")); @@ -52,7 +52,7 @@ public class Symbols extends AbstractLayoutBase { .addKeysOnTheLeftOfRow(3, customizer.getSymbolsShiftKey(isPhone)) .addKeysOnTheRightOfRow(3, customizer.getSymbolsShiftKey(isPhone)) .addKeysOnTheLeftOfRow(4, customizer.getAlphabetKey()) - .addKeysOnTheRightOfRow(4, EMOJI_KEY); + .addKeysOnTheRightOfRow(4, EMOJI_NORMAL_KEY); } return builder.build(); } diff --git a/tests/src/com/android/inputmethod/keyboard/layout/SymbolsShifted.java b/tests/src/com/android/inputmethod/keyboard/layout/SymbolsShifted.java index 3265e10e1..7ed103ba0 100644 --- a/tests/src/com/android/inputmethod/keyboard/layout/SymbolsShifted.java +++ b/tests/src/com/android/inputmethod/keyboard/layout/SymbolsShifted.java @@ -39,7 +39,7 @@ public class SymbolsShifted extends AbstractLayoutBase { builder.addKeysOnTheLeftOfRow(3, customizer.getBackToSymbolsKey()) .addKeysOnTheRightOfRow(3, DELETE_KEY) .addKeysOnTheLeftOfRow(4, customizer.getAlphabetKey()) - .addKeysOnTheRightOfRow(4, key(ENTER_KEY, EMOJI_KEY)); + .addKeysOnTheRightOfRow(4, key(ENTER_KEY, EMOJI_ACTION_KEY)); } else { // Tablet symbols shifted keyboard has extra two keys at the right edge of the 3rd row. // U+00BF: "¿" INVERTED QUESTION MARK @@ -50,7 +50,7 @@ public class SymbolsShifted extends AbstractLayoutBase { .addKeysOnTheLeftOfRow(3, customizer.getBackToSymbolsKey()) .addKeysOnTheRightOfRow(3, customizer.getBackToSymbolsKey()) .addKeysOnTheLeftOfRow(4, customizer.getAlphabetKey()) - .addKeysOnTheRightOfRow(4, EMOJI_KEY); + .addKeysOnTheRightOfRow(4, EMOJI_NORMAL_KEY); } return builder.build(); } diff --git a/tests/src/com/android/inputmethod/keyboard/layout/Thai.java b/tests/src/com/android/inputmethod/keyboard/layout/Thai.java index af4abea93..cfda2947c 100644 --- a/tests/src/com/android/inputmethod/keyboard/layout/Thai.java +++ b/tests/src/com/android/inputmethod/keyboard/layout/Thai.java @@ -40,6 +40,9 @@ public final class Thai extends LayoutBase { public ThaiCustomizer(final Locale locale) { super(locale); } @Override + public int getNumberOfRows() { return 5; } + + @Override public ExpectedKey getAlphabetKey() { return THAI_ALPHABET_KEY; } @Override @@ -96,29 +99,6 @@ public final class Thai extends LayoutBase { return builder.build(); } - // Helper method to create alphabet layout by adding special function keys. - @Override - ExpectedKeyboardBuilder convertCommonLayoutToKeyboard(final ExpectedKeyboardBuilder builder, - final boolean isPhone) { - final LayoutCustomizer customizer = getCustomizer(); - builder.setKeysOfRow(5, (Object[])customizer.getSpaceKeys(isPhone)); - builder.addKeysOnTheLeftOfRow(5, (Object[])customizer.getKeysLeftToSpacebar(isPhone)); - builder.addKeysOnTheRightOfRow(5, (Object[])customizer.getKeysRightToSpacebar(isPhone)); - if (isPhone) { - builder.addKeysOnTheRightOfRow(4, DELETE_KEY) - .addKeysOnTheLeftOfRow(5, customizer.getSymbolsKey()) - .addKeysOnTheRightOfRow(5, key(ENTER_KEY, EMOJI_KEY)); - } else { - builder.addKeysOnTheRightOfRow(1, DELETE_KEY) - .addKeysOnTheRightOfRow(3, ENTER_KEY) - .addKeysOnTheLeftOfRow(5, customizer.getSymbolsKey()) - .addKeysOnTheRightOfRow(5, EMOJI_KEY); - } - builder.addKeysOnTheLeftOfRow(4, (Object[])customizer.getLeftShiftKeys(isPhone)) - .addKeysOnTheRightOfRow(4, (Object[])customizer.getRightShiftKeys(isPhone)); - return builder; - } - private static final ExpectedKey[][] ALPHABET_COMMON = new ExpectedKeyboardBuilder() .setKeysOfRow(1, // U+0E45: "ๅ" THAI CHARACTER LAKKHANGYAO diff --git a/tests/src/com/android/inputmethod/keyboard/layout/expected/AbstractLayoutBase.java b/tests/src/com/android/inputmethod/keyboard/layout/expected/AbstractLayoutBase.java index 9e0039d84..0f7bef2a3 100644 --- a/tests/src/com/android/inputmethod/keyboard/layout/expected/AbstractLayoutBase.java +++ b/tests/src/com/android/inputmethod/keyboard/layout/expected/AbstractLayoutBase.java @@ -120,8 +120,10 @@ public abstract class AbstractLayoutBase { KeyboardIconsSet.NAME_LANGUAGE_SWITCH_KEY); private static final int ICON_ENTER = KeyboardIconsSet.getIconId( KeyboardIconsSet.NAME_ENTER_KEY); - private static final int ICON_EMOJI = KeyboardIconsSet.getIconId( - KeyboardIconsSet.NAME_EMOJI_KEY); + private static final int ICON_EMOJI_ACTION = KeyboardIconsSet.getIconId( + KeyboardIconsSet.NAME_EMOJI_ACTION_KEY); + private static final int ICON_EMOJI_NORMAL = KeyboardIconsSet.getIconId( + KeyboardIconsSet.NAME_EMOJI_NORMAL_KEY); // Functional keys. public static final ExpectedKey DELETE_KEY = key(ICON_DELETE, Constants.CODE_DELETE); @@ -131,6 +133,7 @@ public abstract class AbstractLayoutBase { public static final ExpectedKey LANGUAGE_SWITCH_KEY = key( ICON_LANGUAGE_SWITCH, Constants.CODE_LANGUAGE_SWITCH); public static final ExpectedKey ENTER_KEY = key(ICON_ENTER, Constants.CODE_ENTER); - public static final ExpectedKey EMOJI_KEY = key(ICON_EMOJI, Constants.CODE_EMOJI); + public static final ExpectedKey EMOJI_ACTION_KEY = key(ICON_EMOJI_ACTION, Constants.CODE_EMOJI); + public static final ExpectedKey EMOJI_NORMAL_KEY = key(ICON_EMOJI_NORMAL, Constants.CODE_EMOJI); public static final ExpectedKey SPACE_KEY = key(ICON_SPACE, Constants.CODE_SPACE); } diff --git a/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsMalayalamIN.java b/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsMalayalamIN.java index f937de89a..b494ad37b 100644 --- a/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsMalayalamIN.java +++ b/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsMalayalamIN.java @@ -25,7 +25,7 @@ import com.android.inputmethod.keyboard.layout.Malayalam.MalayalamCustomizer; import java.util.Locale; /** - * ta_IN: Malayalam (India)/malayalam + * ml_IN: Malayalam (India)/malayalam */ @SmallTest public final class TestsMalayalamIN extends LayoutTestsBase { diff --git a/tests/src/com/android/inputmethod/latin/BinaryDictionaryDecayingTests.java b/tests/src/com/android/inputmethod/latin/BinaryDictionaryDecayingTests.java index ae184268c..342eb2978 100644 --- a/tests/src/com/android/inputmethod/latin/BinaryDictionaryDecayingTests.java +++ b/tests/src/com/android/inputmethod/latin/BinaryDictionaryDecayingTests.java @@ -689,4 +689,36 @@ public class BinaryDictionaryDecayingTests extends AndroidTestCase { binaryDictionary.close(); dictFile.delete(); } + + public void testRemoveUnigrams() { + for (final int formatVersion : DICT_FORMAT_VERSIONS) { + testRemoveUnigrams(formatVersion); + } + } + + private void testRemoveUnigrams(final int formatVersion) { + final int unigramInputCount = 20; + setCurrentTimeForTestMode(mCurrentTime); + File dictFile = null; + try { + dictFile = createEmptyDictionaryAndGetFile("TestBinaryDictionary", formatVersion); + } catch (IOException e) { + fail("IOException while writing an initial dictionary : " + e); + } + final BinaryDictionary binaryDictionary = new BinaryDictionary(dictFile.getAbsolutePath(), + 0 /* offset */, dictFile.length(), true /* useFullEditDistance */, + Locale.getDefault(), TEST_LOCALE, true /* isUpdatable */); + + addUnigramWord(binaryDictionary, "aaa", Dictionary.NOT_A_PROBABILITY); + assertFalse(binaryDictionary.isValidWord("aaa")); + for (int i = 0; i < unigramInputCount; i++) { + addUnigramWord(binaryDictionary, "aaa", Dictionary.NOT_A_PROBABILITY); + } + assertTrue(binaryDictionary.isValidWord("aaa")); + assertTrue(binaryDictionary.removeUnigramEntry("aaa")); + assertFalse(binaryDictionary.isValidWord("aaa")); + + binaryDictionary.close(); + dictFile.delete(); + } } diff --git a/tests/src/com/android/inputmethod/latin/SuggestedWordsTests.java b/tests/src/com/android/inputmethod/latin/SuggestedWordsTests.java index 66b4a9c71..a5f20b565 100644 --- a/tests/src/com/android/inputmethod/latin/SuggestedWordsTests.java +++ b/tests/src/com/android/inputmethod/latin/SuggestedWordsTests.java @@ -48,7 +48,8 @@ public class SuggestedWordsTests extends AndroidTestCase { false /* typedWordValid */, false /* willAutoCorrect */, false /* isObsoleteSuggestions */, - false /* isPrediction*/); + false /* isPrediction*/, + SuggestedWords.INPUT_STYLE_NONE); assertEquals(NUMBER_OF_ADDED_SUGGESTIONS + 1, words.size()); assertEquals("typed", words.getWord(0)); assertTrue(words.getInfo(0).isKindOf(SuggestedWordInfo.KIND_TYPED)); @@ -57,7 +58,8 @@ public class SuggestedWordsTests extends AndroidTestCase { assertEquals("4", words.getWord(5)); assertTrue(words.getInfo(5).isKindOf(SuggestedWordInfo.KIND_CORRECTION)); - final SuggestedWords wordsWithoutTyped = words.getSuggestedWordsExcludingTypedWord(); + final SuggestedWords wordsWithoutTyped = words.getSuggestedWordsExcludingTypedWord( + SuggestedWords.INPUT_STYLE_NONE); assertEquals(words.size() - 1, wordsWithoutTyped.size()); assertEquals("0", wordsWithoutTyped.getWord(0)); assertTrue(wordsWithoutTyped.getInfo(0).isKindOf(SuggestedWordInfo.KIND_CORRECTION)); diff --git a/tests/src/com/android/inputmethod/latin/personalization/UserHistoryDictionaryTests.java b/tests/src/com/android/inputmethod/latin/personalization/UserHistoryDictionaryTests.java index f87f3b494..abb468fda 100644 --- a/tests/src/com/android/inputmethod/latin/personalization/UserHistoryDictionaryTests.java +++ b/tests/src/com/android/inputmethod/latin/personalization/UserHistoryDictionaryTests.java @@ -28,6 +28,7 @@ import com.android.inputmethod.latin.utils.DistracterFilter; import com.android.inputmethod.latin.utils.FileUtils; import java.io.File; +import java.io.FilenameFilter; import java.util.ArrayList; import java.util.HashSet; import java.util.List; @@ -41,6 +42,8 @@ import java.util.concurrent.TimeUnit; @LargeTest public class UserHistoryDictionaryTests extends AndroidTestCase { private static final String TAG = UserHistoryDictionaryTests.class.getSimpleName(); + private static final int WAIT_FOR_WRITING_FILE_IN_MILLISECONDS = 3000; + private static final String TEST_LOCALE_PREFIX = "test_"; private static final String[] CHARACTERS = { "a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l", "m", @@ -49,14 +52,55 @@ public class UserHistoryDictionaryTests extends AndroidTestCase { private int mCurrentTime = 0; + private void removeAllTestDictFiles() { + final Locale dummyLocale = new Locale(TEST_LOCALE_PREFIX); + final String dictName = ExpandableBinaryDictionary.getDictName( + UserHistoryDictionary.NAME, dummyLocale, null /* dictFile */); + final File dictFile = ExpandableBinaryDictionary.getDictFile( + mContext, dictName, null /* dictFile */); + final FilenameFilter filenameFilter = new FilenameFilter() { + @Override + public boolean accept(File dir, String filename) { + return filename.startsWith(UserHistoryDictionary.NAME + "." + TEST_LOCALE_PREFIX); + } + }; + FileUtils.deleteFilteredFiles(dictFile.getParentFile(), filenameFilter); + } + + private void printAllFiles(final File dir) { + Log.d(TAG, dir.getAbsolutePath()); + for (final File file : dir.listFiles()) { + Log.d(TAG, " " + file.getName()); + } + } + + private void checkExistenceAndRemoveDictFile(final Locale locale, final File dictFile) { + Log.d(TAG, "waiting for writing ..."); + waitForWriting(locale); + if (!dictFile.exists()) { + try { + Log.d(TAG, dictFile + " is not existing. Wait " + + WAIT_FOR_WRITING_FILE_IN_MILLISECONDS + " ms for writing."); + printAllFiles(dictFile.getParentFile()); + Thread.sleep(WAIT_FOR_WRITING_FILE_IN_MILLISECONDS); + } catch (final InterruptedException e) { + Log.e(TAG, "Interrupted during waiting for writing the dict file."); + } + } + assertTrue("check exisiting of " + dictFile, dictFile.exists()); + FileUtils.deleteRecursively(dictFile); + } + @Override protected void setUp() throws Exception { super.setUp(); resetCurrentTimeForTestMode(); + removeAllTestDictFiles(); } @Override protected void tearDown() throws Exception { + removeAllTestDictFiles(); stopTestModeInNativeCode(); super.tearDown(); } @@ -168,7 +212,8 @@ public class UserHistoryDictionaryTests extends AndroidTestCase { public void testRandomWords() { Log.d(TAG, "This test can be used for profiling."); Log.d(TAG, "Usage: please set UserHistoryDictionary.PROFILE_SAVE_RESTORE to true."); - final Locale dummyLocale = new Locale("test_random_words" + System.currentTimeMillis()); + final Locale dummyLocale = + new Locale(TEST_LOCALE_PREFIX + "random_words" + System.currentTimeMillis()); final String dictName = ExpandableBinaryDictionary.getDictName( UserHistoryDictionary.NAME, dummyLocale, null /* dictFile */); final File dictFile = ExpandableBinaryDictionary.getDictFile( @@ -182,10 +227,7 @@ public class UserHistoryDictionaryTests extends AndroidTestCase { addAndWriteRandomWords(dummyLocale, numberOfWords, random, true /* checksContents */); } finally { - Log.d(TAG, "waiting for writing ..."); - waitForWriting(dummyLocale); - assertTrue("check exisiting of " + dictFile, dictFile.exists()); - FileUtils.deleteRecursively(dictFile); + checkExistenceAndRemoveDictFile(dummyLocale, dictFile); } } @@ -201,7 +243,7 @@ public class UserHistoryDictionaryTests extends AndroidTestCase { // Create filename suffixes for this test. for (int i = 0; i < numberOfLanguages; i++) { - dummyLocales[i] = new Locale("test_switching_languages" + i); + dummyLocales[i] = new Locale(TEST_LOCALE_PREFIX + "switching_languages" + i); final String dictName = ExpandableBinaryDictionary.getDictName( UserHistoryDictionary.NAME, dummyLocales[i], null /* dictFile */); dictFiles[i] = ExpandableBinaryDictionary.getDictFile( @@ -223,19 +265,15 @@ public class UserHistoryDictionaryTests extends AndroidTestCase { Log.d(TAG, "testStressTestForSwitchingLanguageAndAddingWords took " + (end - start) + " ms"); } finally { - Log.d(TAG, "waiting for writing ..."); for (int i = 0; i < numberOfLanguages; i++) { - waitForWriting(dummyLocales[i]); - } - for (final File dictFile : dictFiles) { - assertTrue("check exisiting of " + dictFile, dictFile.exists()); - FileUtils.deleteRecursively(dictFile); + checkExistenceAndRemoveDictFile(dummyLocales[i], dictFiles[i]); } } } public void testAddManyWords() { - final Locale dummyLocale = new Locale("test_random_words" + System.currentTimeMillis()); + final Locale dummyLocale = + new Locale(TEST_LOCALE_PREFIX + "many_random_words" + System.currentTimeMillis()); final String dictName = ExpandableBinaryDictionary.getDictName( UserHistoryDictionary.NAME, dummyLocale, null /* dictFile */); final File dictFile = ExpandableBinaryDictionary.getDictFile( @@ -246,15 +284,13 @@ public class UserHistoryDictionaryTests extends AndroidTestCase { try { addAndWriteRandomWords(dummyLocale, numberOfWords, random, true /* checksContents */); } finally { - Log.d(TAG, "waiting for writing ..."); - waitForWriting(dummyLocale); - assertTrue("check exisiting of " + dictFile, dictFile.exists()); - FileUtils.deleteRecursively(dictFile); + checkExistenceAndRemoveDictFile(dummyLocale, dictFile); } } public void testDecaying() { - final Locale dummyLocale = new Locale("test_decaying" + System.currentTimeMillis()); + final Locale dummyLocale = + new Locale(TEST_LOCALE_PREFIX + "decaying" + System.currentTimeMillis()); final int numberOfWords = 5000; final Random random = new Random(123456); resetCurrentTimeForTestMode(); diff --git a/tools/make-keyboard-text/res/values/donottranslate-more-keys.xml b/tools/make-keyboard-text/res/values/donottranslate-more-keys.xml index bf97e73f4..c4a1b889e 100644 --- a/tools/make-keyboard-text/res/values/donottranslate-more-keys.xml +++ b/tools/make-keyboard-text/res/values/donottranslate-more-keys.xml @@ -258,5 +258,5 @@ <string name="morekeys_single_quote">!fixedColumnOrder!5,!text/single_quotes,!text/single_angle_quotes</string> <string name="morekeys_double_quote">!fixedColumnOrder!5,!text/double_quotes,!text/double_angle_quotes</string> <string name="morekeys_tablet_double_quote">!fixedColumnOrder!6,!text/double_quotes,!text/single_quotes,!text/double_angle_quotes,!text/single_angle_quotes</string> - <string name="keyspec_emoji_key">!icon/emoji_key|!code/key_emoji</string> + <string name="keyspec_emoji_action_key">!icon/emoji_action_key|!code/key_emoji</string> </resources> |