diff options
Diffstat (limited to 'java')
39 files changed, 296 insertions, 351 deletions
diff --git a/java/res/drawable-hdpi/btn_keyboard_key_normal_off_lxx_dark.9.png b/java/res/drawable-hdpi/btn_keyboard_key_normal_off_lxx_dark.9.png Binary files differindex c0fee73bc..2e6489cf5 100644 --- a/java/res/drawable-hdpi/btn_keyboard_key_normal_off_lxx_dark.9.png +++ b/java/res/drawable-hdpi/btn_keyboard_key_normal_off_lxx_dark.9.png diff --git a/java/res/drawable-hdpi/btn_keyboard_key_normal_on_lxx_dark.9.png b/java/res/drawable-hdpi/btn_keyboard_key_normal_on_lxx_dark.9.png Binary files differindex 591292ced..0b1482281 100644 --- a/java/res/drawable-hdpi/btn_keyboard_key_normal_on_lxx_dark.9.png +++ b/java/res/drawable-hdpi/btn_keyboard_key_normal_on_lxx_dark.9.png diff --git a/java/res/drawable-hdpi/btn_keyboard_key_pressed_off_lxx_dark.9.png b/java/res/drawable-hdpi/btn_keyboard_key_pressed_off_lxx_dark.9.png Binary files differindex 10d91a2ea..4bf38fc35 100644 --- a/java/res/drawable-hdpi/btn_keyboard_key_pressed_off_lxx_dark.9.png +++ b/java/res/drawable-hdpi/btn_keyboard_key_pressed_off_lxx_dark.9.png diff --git a/java/res/drawable-hdpi/btn_keyboard_key_pressed_on_lxx_dark.9.png b/java/res/drawable-hdpi/btn_keyboard_key_pressed_on_lxx_dark.9.png Binary files differindex 6a8c62f12..ea12c7776 100644 --- a/java/res/drawable-hdpi/btn_keyboard_key_pressed_on_lxx_dark.9.png +++ b/java/res/drawable-hdpi/btn_keyboard_key_pressed_on_lxx_dark.9.png diff --git a/java/res/drawable-mdpi/btn_keyboard_key_normal_off_lxx_dark.9.png b/java/res/drawable-mdpi/btn_keyboard_key_normal_off_lxx_dark.9.png Binary files differindex 3ce13cc2c..9f244f2e9 100644 --- a/java/res/drawable-mdpi/btn_keyboard_key_normal_off_lxx_dark.9.png +++ b/java/res/drawable-mdpi/btn_keyboard_key_normal_off_lxx_dark.9.png diff --git a/java/res/drawable-mdpi/btn_keyboard_key_normal_on_lxx_dark.9.png b/java/res/drawable-mdpi/btn_keyboard_key_normal_on_lxx_dark.9.png Binary files differindex 65507eea5..c5b3fbbd7 100644 --- a/java/res/drawable-mdpi/btn_keyboard_key_normal_on_lxx_dark.9.png +++ b/java/res/drawable-mdpi/btn_keyboard_key_normal_on_lxx_dark.9.png diff --git a/java/res/drawable-mdpi/btn_keyboard_key_pressed_off_lxx_dark.9.png b/java/res/drawable-mdpi/btn_keyboard_key_pressed_off_lxx_dark.9.png Binary files differindex 724e14212..66824cf8e 100644 --- a/java/res/drawable-mdpi/btn_keyboard_key_pressed_off_lxx_dark.9.png +++ b/java/res/drawable-mdpi/btn_keyboard_key_pressed_off_lxx_dark.9.png diff --git a/java/res/drawable-mdpi/btn_keyboard_key_pressed_on_lxx_dark.9.png b/java/res/drawable-mdpi/btn_keyboard_key_pressed_on_lxx_dark.9.png Binary files differindex 264b65b52..527dfd014 100644 --- a/java/res/drawable-mdpi/btn_keyboard_key_pressed_on_lxx_dark.9.png +++ b/java/res/drawable-mdpi/btn_keyboard_key_pressed_on_lxx_dark.9.png diff --git a/java/res/drawable-xhdpi/btn_keyboard_key_normal_off_lxx_dark.9.png b/java/res/drawable-xhdpi/btn_keyboard_key_normal_off_lxx_dark.9.png Binary files differindex 70270e290..98c085b15 100644 --- a/java/res/drawable-xhdpi/btn_keyboard_key_normal_off_lxx_dark.9.png +++ b/java/res/drawable-xhdpi/btn_keyboard_key_normal_off_lxx_dark.9.png diff --git a/java/res/drawable-xhdpi/btn_keyboard_key_normal_on_lxx_dark.9.png b/java/res/drawable-xhdpi/btn_keyboard_key_normal_on_lxx_dark.9.png Binary files differindex 31f9e02ce..f0c132869 100644 --- a/java/res/drawable-xhdpi/btn_keyboard_key_normal_on_lxx_dark.9.png +++ b/java/res/drawable-xhdpi/btn_keyboard_key_normal_on_lxx_dark.9.png diff --git a/java/res/drawable-xhdpi/btn_keyboard_key_pressed_off_lxx_dark.9.png b/java/res/drawable-xhdpi/btn_keyboard_key_pressed_off_lxx_dark.9.png Binary files differindex 97ef98d4f..a2b17ba50 100644 --- a/java/res/drawable-xhdpi/btn_keyboard_key_pressed_off_lxx_dark.9.png +++ b/java/res/drawable-xhdpi/btn_keyboard_key_pressed_off_lxx_dark.9.png diff --git a/java/res/drawable-xhdpi/btn_keyboard_key_pressed_on_lxx_dark.9.png b/java/res/drawable-xhdpi/btn_keyboard_key_pressed_on_lxx_dark.9.png Binary files differindex a6e52ff82..99ff0affb 100644 --- a/java/res/drawable-xhdpi/btn_keyboard_key_pressed_on_lxx_dark.9.png +++ b/java/res/drawable-xhdpi/btn_keyboard_key_pressed_on_lxx_dark.9.png diff --git a/java/res/drawable-xxhdpi/btn_keyboard_key_normal_off_lxx_dark.9.png b/java/res/drawable-xxhdpi/btn_keyboard_key_normal_off_lxx_dark.9.png Binary files differindex 814758622..4ec5864c1 100644 --- a/java/res/drawable-xxhdpi/btn_keyboard_key_normal_off_lxx_dark.9.png +++ b/java/res/drawable-xxhdpi/btn_keyboard_key_normal_off_lxx_dark.9.png diff --git a/java/res/drawable-xxhdpi/btn_keyboard_key_normal_on_lxx_dark.9.png b/java/res/drawable-xxhdpi/btn_keyboard_key_normal_on_lxx_dark.9.png Binary files differindex 598bb68b7..6b5c0c0a4 100644 --- a/java/res/drawable-xxhdpi/btn_keyboard_key_normal_on_lxx_dark.9.png +++ b/java/res/drawable-xxhdpi/btn_keyboard_key_normal_on_lxx_dark.9.png diff --git a/java/res/drawable-xxhdpi/btn_keyboard_key_pressed_off_lxx_dark.9.png b/java/res/drawable-xxhdpi/btn_keyboard_key_pressed_off_lxx_dark.9.png Binary files differindex 963f34ab1..6fd8eedd3 100644 --- a/java/res/drawable-xxhdpi/btn_keyboard_key_pressed_off_lxx_dark.9.png +++ b/java/res/drawable-xxhdpi/btn_keyboard_key_pressed_off_lxx_dark.9.png diff --git a/java/res/drawable-xxhdpi/btn_keyboard_key_pressed_on_lxx_dark.9.png b/java/res/drawable-xxhdpi/btn_keyboard_key_pressed_on_lxx_dark.9.png Binary files differindex b40d6f484..91322882f 100644 --- a/java/res/drawable-xxhdpi/btn_keyboard_key_pressed_on_lxx_dark.9.png +++ b/java/res/drawable-xxhdpi/btn_keyboard_key_pressed_on_lxx_dark.9.png diff --git a/java/res/values-v20/platform-theme.xml b/java/res/values-v20/platform-theme.xml index 06062047b..52e7f3521 100644 --- a/java/res/values-v20/platform-theme.xml +++ b/java/res/values-v20/platform-theme.xml @@ -21,6 +21,6 @@ <!-- TODO: This file is temporarily placed under values-v20. --> <!-- TODO: It might be moved under values-v21. --> <resources xmlns:android="http://schemas.android.com/apk/res/android"> - <style name="platformActivityTheme" parent="@android:style/Theme.Quantum.Light" /> - <style name="platformDialogTheme" parent="@android:style/Theme.Quantum.Light.Dialog" /> + <style name="platformActivityTheme" parent="@android:style/Theme.Material.Light" /> + <style name="platformDialogTheme" parent="@android:style/Theme.Material.Light.Dialog" /> </resources> diff --git a/java/res/values/attrs.xml b/java/res/values/attrs.xml index 775a90c48..a1f478bd5 100644 --- a/java/res/values/attrs.xml +++ b/java/res/values/attrs.xml @@ -437,7 +437,6 @@ <attr name="navigatePrevious" format="boolean" /> <attr name="passwordInput" format="boolean" /> <attr name="clobberSettingsKey" format="boolean" /> - <attr name="supportsSwitchingToShortcutIme" format="boolean" /> <attr name="hasShortcutKey" format="boolean" /> <attr name="languageSwitchKeyEnabled" format="boolean" /> <attr name="isMultiLine" format="boolean" /> diff --git a/java/src/com/android/inputmethod/keyboard/KeyboardId.java b/java/src/com/android/inputmethod/keyboard/KeyboardId.java index 93a55fe6a..3c1167538 100644 --- a/java/src/com/android/inputmethod/keyboard/KeyboardId.java +++ b/java/src/com/android/inputmethod/keyboard/KeyboardId.java @@ -70,7 +70,6 @@ public final class KeyboardId { public final int mElementId; public final EditorInfo mEditorInfo; public final boolean mClobberSettingsKey; - public final boolean mSupportsSwitchingToShortcutIme; public final boolean mLanguageSwitchKeyEnabled; public final String mCustomActionLabel; public final boolean mHasShortcutKey; @@ -86,11 +85,10 @@ public final class KeyboardId { mElementId = elementId; mEditorInfo = params.mEditorInfo; mClobberSettingsKey = params.mNoSettingsKey; - mSupportsSwitchingToShortcutIme = params.mSupportsSwitchingToShortcutIme; mLanguageSwitchKeyEnabled = params.mLanguageSwitchKeyEnabled; mCustomActionLabel = (mEditorInfo.actionLabel != null) ? mEditorInfo.actionLabel.toString() : null; - mHasShortcutKey = mSupportsSwitchingToShortcutIme && params.mShowsVoiceInputKey; + mHasShortcutKey = params.mVoiceInputKeyEnabled; mHashCode = computeHashCode(this); } @@ -103,7 +101,6 @@ public final class KeyboardId { id.mHeight, id.passwordInput(), id.mClobberSettingsKey, - id.mSupportsSwitchingToShortcutIme, id.mHasShortcutKey, id.mLanguageSwitchKeyEnabled, id.isMultiLine(), @@ -124,7 +121,6 @@ public final class KeyboardId { && other.mHeight == mHeight && other.passwordInput() == passwordInput() && other.mClobberSettingsKey == mClobberSettingsKey - && other.mSupportsSwitchingToShortcutIme == mSupportsSwitchingToShortcutIme && other.mHasShortcutKey == mHasShortcutKey && other.mLanguageSwitchKeyEnabled == mLanguageSwitchKeyEnabled && other.isMultiLine() == isMultiLine() @@ -179,7 +175,7 @@ public final class KeyboardId { @Override public String toString() { - return String.format(Locale.ROOT, "[%s %s:%s %dx%d %s %s%s%s%s%s%s%s%s%s]", + return String.format(Locale.ROOT, "[%s %s:%s %dx%d %s %s%s%s%s%s%s%s%s]", elementIdToName(mElementId), mLocale, mSubtype.getExtraValueOf(KEYBOARD_LAYOUT_SET), mWidth, mHeight, @@ -189,7 +185,6 @@ public final class KeyboardId { (navigatePrevious() ? " navigatePrevious" : ""), (mClobberSettingsKey ? " clobberSettingsKey" : ""), (passwordInput() ? " passwordInput" : ""), - (mSupportsSwitchingToShortcutIme ? " supportsSwitchingToShortcutIme" : ""), (mHasShortcutKey ? " hasShortcutKey" : ""), (mLanguageSwitchKeyEnabled ? " languageSwitchKeyEnabled" : ""), (isMultiLine() ? " isMultiLine" : "") diff --git a/java/src/com/android/inputmethod/keyboard/KeyboardLayoutSet.java b/java/src/com/android/inputmethod/keyboard/KeyboardLayoutSet.java index aa646994c..3e5cfc11a 100644 --- a/java/src/com/android/inputmethod/keyboard/KeyboardLayoutSet.java +++ b/java/src/com/android/inputmethod/keyboard/KeyboardLayoutSet.java @@ -17,8 +17,6 @@ package com.android.inputmethod.keyboard; import static com.android.inputmethod.latin.Constants.ImeOption.FORCE_ASCII; -import static com.android.inputmethod.latin.Constants.ImeOption.NO_MICROPHONE; -import static com.android.inputmethod.latin.Constants.ImeOption.NO_MICROPHONE_COMPAT; import static com.android.inputmethod.latin.Constants.ImeOption.NO_SETTINGS_KEY; import android.content.Context; @@ -102,12 +100,11 @@ public final class KeyboardLayoutSet { public static final class Params { String mKeyboardLayoutSetName; int mMode; - EditorInfo mEditorInfo; boolean mDisableTouchPositionCorrectionDataForTest; + // TODO: Use {@link InputAttributes} instead of these variables. + EditorInfo mEditorInfo; boolean mIsPasswordField; - boolean mSupportsSwitchingToShortcutIme; - boolean mShowsVoiceInputKey; - boolean mNoMicrophoneKey; + boolean mVoiceInputKeyEnabled; boolean mNoSettingsKey; boolean mLanguageSwitchKeyEnabled; InputMethodSubtype mSubtype; @@ -228,14 +225,9 @@ public final class KeyboardLayoutSet { final EditorInfo editorInfo = (ei != null) ? ei : EMPTY_EDITOR_INFO; params.mMode = getKeyboardMode(editorInfo); + // TODO: Consolidate those with {@link InputAttributes}. params.mEditorInfo = editorInfo; params.mIsPasswordField = InputTypeUtils.isPasswordInputType(editorInfo.inputType); - @SuppressWarnings("deprecation") - final boolean deprecatedNoMicrophone = InputAttributes.inPrivateImeOptions( - null, NO_MICROPHONE_COMPAT, editorInfo); - params.mNoMicrophoneKey = InputAttributes.inPrivateImeOptions( - mPackageName, NO_MICROPHONE, editorInfo) - || deprecatedNoMicrophone; params.mNoSettingsKey = InputAttributes.inPrivateImeOptions( mPackageName, NO_SETTINGS_KEY, editorInfo); } @@ -248,6 +240,7 @@ public final class KeyboardLayoutSet { public Builder setSubtype(final InputMethodSubtype subtype) { final boolean asciiCapable = InputMethodSubtypeCompatUtils.isAsciiCapable(subtype); + // TODO: Consolidate with {@link InputAttributes}. @SuppressWarnings("deprecation") final boolean deprecatedForceAscii = InputAttributes.inPrivateImeOptions( mPackageName, FORCE_ASCII, mParams.mEditorInfo); @@ -268,12 +261,13 @@ public final class KeyboardLayoutSet { return this; } - public Builder setOptions(final boolean isShortcutImeEnabled, - final boolean showsVoiceInputKey, final boolean languageSwitchKeyEnabled) { - mParams.mSupportsSwitchingToShortcutIme = - isShortcutImeEnabled && !mParams.mNoMicrophoneKey && !mParams.mIsPasswordField; - mParams.mShowsVoiceInputKey = showsVoiceInputKey; - mParams.mLanguageSwitchKeyEnabled = languageSwitchKeyEnabled; + public Builder setVoiceInputKeyEnabled(final boolean enabled) { + mParams.mVoiceInputKeyEnabled = enabled; + return this; + } + + public Builder setLanguageSwitchKeyEnabled(final boolean enabled) { + mParams.mLanguageSwitchKeyEnabled = enabled; return this; } diff --git a/java/src/com/android/inputmethod/keyboard/KeyboardSwitcher.java b/java/src/com/android/inputmethod/keyboard/KeyboardSwitcher.java index ef5fabb41..6aeff189f 100644 --- a/java/src/com/android/inputmethod/keyboard/KeyboardSwitcher.java +++ b/java/src/com/android/inputmethod/keyboard/KeyboardSwitcher.java @@ -115,10 +115,8 @@ public final class KeyboardSwitcher implements KeyboardState.SwitchActions { final int keyboardHeight = ResourceUtils.getDefaultKeyboardHeight(res); builder.setKeyboardGeometry(keyboardWidth, keyboardHeight); builder.setSubtype(mSubtypeSwitcher.getCurrentSubtype()); - builder.setOptions( - mSubtypeSwitcher.isShortcutImeEnabled(), - settingsValues.mShowsVoiceInputKey, - mLatinIME.shouldShowLanguageSwitchKey()); + builder.setVoiceInputKeyEnabled(settingsValues.mShowsVoiceInputKey); + builder.setLanguageSwitchKeyEnabled(mLatinIME.shouldShowLanguageSwitchKey()); mKeyboardLayoutSet = builder.build(); mCurrentSettingsValues = settingsValues; try { diff --git a/java/src/com/android/inputmethod/keyboard/KeyboardTheme.java b/java/src/com/android/inputmethod/keyboard/KeyboardTheme.java index 60d9a054d..0ea9c742f 100644 --- a/java/src/com/android/inputmethod/keyboard/KeyboardTheme.java +++ b/java/src/com/android/inputmethod/keyboard/KeyboardTheme.java @@ -21,46 +21,43 @@ import android.os.Build; import android.os.Build.VERSION_CODES; import android.util.Log; +import com.android.inputmethod.annotations.UsedForTesting; import com.android.inputmethod.latin.R; import java.util.Arrays; -import java.util.Comparator; -public final class KeyboardTheme { +public final class KeyboardTheme implements Comparable<KeyboardTheme> { private static final String TAG = KeyboardTheme.class.getSimpleName(); static final String KLP_KEYBOARD_THEME_KEY = "pref_keyboard_layout_20110916"; static final String LXX_KEYBOARD_THEME_KEY = "pref_keyboard_theme_20140509"; - static final int THEME_ID_ICS = 0; - static final int THEME_ID_KLP = 2; - static final int THEME_ID_LXX_DARK = 3; - static final int DEFAULT_THEME_ID = THEME_ID_KLP; + public static final int THEME_ID_ICS = 0; + public static final int THEME_ID_KLP = 2; + public static final int THEME_ID_LXX_DARK = 3; + public static final int DEFAULT_THEME_ID = THEME_ID_KLP; private static final KeyboardTheme[] KEYBOARD_THEMES = { new KeyboardTheme(THEME_ID_ICS, R.style.KeyboardTheme_ICS, + // This has never been selected because we support ICS or later. VERSION_CODES.BASE), new KeyboardTheme(THEME_ID_KLP, R.style.KeyboardTheme_KLP, + // Default theme for ICS, JB, and KLP. VERSION_CODES.ICE_CREAM_SANDWICH), new KeyboardTheme(THEME_ID_LXX_DARK, R.style.KeyboardTheme_LXX_Dark, + // Default theme for LXX. // TODO: Update this constant once the *next* version becomes available. VERSION_CODES.CUR_DEVELOPMENT), }; + static { // Sort {@link #KEYBOARD_THEME} by descending order of {@link #mMinApiVersion}. - Arrays.sort(KEYBOARD_THEMES, new Comparator<KeyboardTheme>() { - @Override - public int compare(final KeyboardTheme lhs, final KeyboardTheme rhs) { - if (lhs.mMinApiVersion > rhs.mMinApiVersion) return -1; - if (lhs.mMinApiVersion < rhs.mMinApiVersion) return 1; - return 0; - } - }); + Arrays.sort(KEYBOARD_THEMES); } public final int mThemeId; public final int mStyleId; - final int mMinApiVersion; + private final int mMinApiVersion; // Note: The themeId should be aligned with "themeId" attribute of Keyboard style // in values/themes-<style>.xml. @@ -71,6 +68,13 @@ public final class KeyboardTheme { } @Override + public int compareTo(final KeyboardTheme rhs) { + if (mMinApiVersion > rhs.mMinApiVersion) return -1; + if (mMinApiVersion < rhs.mMinApiVersion) return 1; + return 0; + } + + @Override public boolean equals(final Object o) { if (o == this) return true; return (o instanceof KeyboardTheme) && ((KeyboardTheme)o).mThemeId == mThemeId; @@ -81,7 +85,8 @@ public final class KeyboardTheme { return mThemeId; } - private static KeyboardTheme searchKeyboardThemeById(final int themeId) { + @UsedForTesting + static KeyboardTheme searchKeyboardThemeById(final int themeId) { // TODO: This search algorithm isn't optimal if there are many themes. for (final KeyboardTheme theme : KEYBOARD_THEMES) { if (theme.mThemeId == themeId) { @@ -100,6 +105,7 @@ public final class KeyboardTheme { return sdkVersion; } + @UsedForTesting static KeyboardTheme getDefaultKeyboardTheme(final SharedPreferences prefs, final int sdkVersion) { final String klpThemeIdString = prefs.getString(KLP_KEYBOARD_THEME_KEY, null); @@ -134,6 +140,7 @@ public final class KeyboardTheme { saveKeyboardThemeId(themeIdString, prefs, getSdkVersion()); } + @UsedForTesting static String getPreferenceKey(final int sdkVersion) { if (sdkVersion <= VERSION_CODES.KITKAT) { return KLP_KEYBOARD_THEME_KEY; @@ -141,8 +148,9 @@ public final class KeyboardTheme { return LXX_KEYBOARD_THEME_KEY; } - static void saveKeyboardThemeId(final String themeIdString, final SharedPreferences prefs, - final int sdkVersion) { + @UsedForTesting + static void saveKeyboardThemeId(final String themeIdString, + final SharedPreferences prefs, final int sdkVersion) { final String prefKey = getPreferenceKey(sdkVersion); prefs.edit().putString(prefKey, themeIdString).apply(); } @@ -151,6 +159,7 @@ public final class KeyboardTheme { return getKeyboardTheme(prefs, getSdkVersion()); } + @UsedForTesting static KeyboardTheme getKeyboardTheme(final SharedPreferences prefs, final int sdkVersion) { final String lxxThemeIdString = prefs.getString(LXX_KEYBOARD_THEME_KEY, null); if (lxxThemeIdString == null) { diff --git a/java/src/com/android/inputmethod/keyboard/emoji/EmojiPalettesView.java b/java/src/com/android/inputmethod/keyboard/emoji/EmojiPalettesView.java index 81d8cda7b..abed5208b 100644 --- a/java/src/com/android/inputmethod/keyboard/emoji/EmojiPalettesView.java +++ b/java/src/com/android/inputmethod/keyboard/emoji/EmojiPalettesView.java @@ -115,8 +115,6 @@ public final class EmojiPalettesView extends LinearLayout implements OnTabChange builder.setSubtype(SubtypeSwitcher.getInstance().getEmojiSubtype()); builder.setKeyboardGeometry(ResourceUtils.getDefaultKeyboardWidth(res), mEmojiLayoutParams.mEmojiKeyboardHeight); - builder.setOptions(false /* shortcutImeEnabled */, false /* showsVoiceInputKey */, - false /* languageSwitchKeyEnabled */); final KeyboardLayoutSet layoutSet = builder.build(); final TypedArray emojiPalettesViewAttr = context.obtainStyledAttributes(attrs, R.styleable.EmojiPalettesView, defStyle, R.style.EmojiPalettesView); diff --git a/java/src/com/android/inputmethod/keyboard/internal/KeyboardBuilder.java b/java/src/com/android/inputmethod/keyboard/internal/KeyboardBuilder.java index 2aeeed87f..e69499829 100644 --- a/java/src/com/android/inputmethod/keyboard/internal/KeyboardBuilder.java +++ b/java/src/com/android/inputmethod/keyboard/internal/KeyboardBuilder.java @@ -652,9 +652,6 @@ public class KeyboardBuilder<KP extends KeyboardParams> { R.styleable.Keyboard_Case_passwordInput, id.passwordInput()); final boolean clobberSettingsKeyMatched = matchBoolean(caseAttr, R.styleable.Keyboard_Case_clobberSettingsKey, id.mClobberSettingsKey); - final boolean supportsSwitchingToShortcutImeMatched = matchBoolean(caseAttr, - R.styleable.Keyboard_Case_supportsSwitchingToShortcutIme, - id.mSupportsSwitchingToShortcutIme); final boolean hasShortcutKeyMatched = matchBoolean(caseAttr, R.styleable.Keyboard_Case_hasShortcutKey, id.mHasShortcutKey); final boolean languageSwitchKeyEnabledMatched = matchBoolean(caseAttr, @@ -674,14 +671,13 @@ public class KeyboardBuilder<KP extends KeyboardParams> { R.styleable.Keyboard_Case_countryCode, id.mLocale.getCountry()); final boolean selected = keyboardLayoutSetMatched && keyboardLayoutSetElementMatched && modeMatched && navigateNextMatched && navigatePreviousMatched - && passwordInputMatched && clobberSettingsKeyMatched - && supportsSwitchingToShortcutImeMatched && hasShortcutKeyMatched + && passwordInputMatched && clobberSettingsKeyMatched && hasShortcutKeyMatched && languageSwitchKeyEnabledMatched && isMultiLineMatched && imeActionMatched && isIconDefinedMatched && localeCodeMatched && languageCodeMatched && countryCodeMatched; if (DEBUG) { - startTag("<%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s>%s", TAG_CASE, + startTag("<%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s>%s", TAG_CASE, textAttr(caseAttr.getString( R.styleable.Keyboard_Case_keyboardLayoutSet), "keyboardLayoutSet"), textAttr(caseAttr.getString( @@ -698,9 +694,6 @@ public class KeyboardBuilder<KP extends KeyboardParams> { "clobberSettingsKey"), booleanAttr(caseAttr, R.styleable.Keyboard_Case_passwordInput, "passwordInput"), - booleanAttr( - caseAttr, R.styleable.Keyboard_Case_supportsSwitchingToShortcutIme, - "supportsSwitchingToShortcutIme"), booleanAttr(caseAttr, R.styleable.Keyboard_Case_hasShortcutKey, "hasShortcutKey"), booleanAttr(caseAttr, R.styleable.Keyboard_Case_languageSwitchKeyEnabled, diff --git a/java/src/com/android/inputmethod/latin/BinaryDictionary.java b/java/src/com/android/inputmethod/latin/BinaryDictionary.java index 096b946d2..7247a1f20 100644 --- a/java/src/com/android/inputmethod/latin/BinaryDictionary.java +++ b/java/src/com/android/inputmethod/latin/BinaryDictionary.java @@ -189,6 +189,7 @@ public final class BinaryDictionary extends Dictionary { private static native void closeNative(long dict); private static native int getFormatVersionNative(long dict); private static native int getProbabilityNative(long dict, int[] word); + private static native int getMaxProbabilityOfExactMatchesNative(long dict, int[] word); private static native int getBigramProbabilityNative(long dict, int[] word0, boolean isBeginningOfSentence, int[] word1); private static native void getWordPropertyNative(long dict, int[] word, @@ -350,11 +351,18 @@ public final class BinaryDictionary extends Dictionary { @Override public int getFrequency(final String word) { - if (word == null) return NOT_A_PROBABILITY; + if (TextUtils.isEmpty(word)) return NOT_A_PROBABILITY; int[] codePoints = StringUtils.toCodePointArray(word); return getProbabilityNative(mNativeDict, codePoints); } + @Override + public int getMaxFrequencyOfExactMatches(final String word) { + if (TextUtils.isEmpty(word)) return NOT_A_PROBABILITY; + int[] codePoints = StringUtils.toCodePointArray(word); + return getMaxProbabilityOfExactMatchesNative(mNativeDict, codePoints); + } + @UsedForTesting public boolean isValidNgram(final PrevWordsInfo prevWordsInfo, final String word) { return getNgramProbability(prevWordsInfo, word) != NOT_A_PROBABILITY; diff --git a/java/src/com/android/inputmethod/latin/Dictionary.java b/java/src/com/android/inputmethod/latin/Dictionary.java index aab16653e..bc7276b9a 100644 --- a/java/src/com/android/inputmethod/latin/Dictionary.java +++ b/java/src/com/android/inputmethod/latin/Dictionary.java @@ -95,6 +95,10 @@ public abstract class Dictionary { return NOT_A_PROBABILITY; } + public int getMaxFrequencyOfExactMatches(final String word) { + return NOT_A_PROBABILITY; + } + /** * Compares the contents of the character array with the typed word and returns true if they * are the same. diff --git a/java/src/com/android/inputmethod/latin/DictionaryCollection.java b/java/src/com/android/inputmethod/latin/DictionaryCollection.java index 53be28139..53c78fd00 100644 --- a/java/src/com/android/inputmethod/latin/DictionaryCollection.java +++ b/java/src/com/android/inputmethod/latin/DictionaryCollection.java @@ -89,9 +89,17 @@ public final class DictionaryCollection extends Dictionary { int maxFreq = -1; for (int i = mDictionaries.size() - 1; i >= 0; --i) { final int tempFreq = mDictionaries.get(i).getFrequency(word); - if (tempFreq >= maxFreq) { - maxFreq = tempFreq; - } + maxFreq = Math.max(tempFreq, maxFreq); + } + return maxFreq; + } + + @Override + public int getMaxFrequencyOfExactMatches(final String word) { + int maxFreq = -1; + for (int i = mDictionaries.size() - 1; i >= 0; --i) { + final int tempFreq = mDictionaries.get(i).getMaxFrequencyOfExactMatches(word); + maxFreq = Math.max(tempFreq, maxFreq); } return maxFreq; } diff --git a/java/src/com/android/inputmethod/latin/DictionaryFacilitator.java b/java/src/com/android/inputmethod/latin/DictionaryFacilitator.java index 7fa3d0479..e8b0be069 100644 --- a/java/src/com/android/inputmethod/latin/DictionaryFacilitator.java +++ b/java/src/com/android/inputmethod/latin/DictionaryFacilitator.java @@ -63,7 +63,7 @@ public class DictionaryFacilitator { private final Object mLock = new Object(); private final DistracterFilter mDistracterFilter; - private static final String[] DICT_TYPES_ORDERED_TO_GET_SUGGESTION = + private static final String[] DICT_TYPES_ORDERED_TO_GET_SUGGESTIONS = new String[] { Dictionary.TYPE_MAIN, Dictionary.TYPE_USER_HISTORY, @@ -89,8 +89,8 @@ public class DictionaryFacilitator { new Class[] { Context.class, Locale.class, File.class }; private static final String[] SUB_DICT_TYPES = - Arrays.copyOfRange(DICT_TYPES_ORDERED_TO_GET_SUGGESTION, 1 /* start */, - DICT_TYPES_ORDERED_TO_GET_SUGGESTION.length); + Arrays.copyOfRange(DICT_TYPES_ORDERED_TO_GET_SUGGESTIONS, 1 /* start */, + DICT_TYPES_ORDERED_TO_GET_SUGGESTIONS.length); /** * Class contains dictionaries for a locale. @@ -333,7 +333,7 @@ public class DictionaryFacilitator { dictionaries = mDictionaries; mDictionaries = new Dictionaries(); } - for (final String dictType : DICT_TYPES_ORDERED_TO_GET_SUGGESTION) { + for (final String dictType : DICT_TYPES_ORDERED_TO_GET_SUGGESTIONS) { dictionaries.closeDict(dictType); } mDistracterFilter.close(); @@ -469,7 +469,7 @@ public class DictionaryFacilitator { final SuggestionResults suggestionResults = new SuggestionResults(dictionaries.mLocale, SuggestedWords.MAX_SUGGESTIONS); final float[] languageWeight = new float[] { Dictionary.NOT_A_LANGUAGE_WEIGHT }; - for (final String dictType : DICT_TYPES_ORDERED_TO_GET_SUGGESTION) { + for (final String dictType : DICT_TYPES_ORDERED_TO_GET_SUGGESTIONS) { final Dictionary dictionary = dictionaries.getDict(dictType); if (null == dictionary) continue; final ArrayList<SuggestedWordInfo> dictionarySuggestions = @@ -502,7 +502,7 @@ public class DictionaryFacilitator { return false; } final String lowerCasedWord = word.toLowerCase(dictionaries.mLocale); - for (final String dictType : DICT_TYPES_ORDERED_TO_GET_SUGGESTION) { + for (final String dictType : DICT_TYPES_ORDERED_TO_GET_SUGGESTIONS) { final Dictionary dictionary = dictionaries.getDict(dictType); // Ideally the passed map would come out of a {@link java.util.concurrent.Future} and // would be immutable once it's finished initializing, but concretely a null test is @@ -516,16 +516,22 @@ public class DictionaryFacilitator { return false; } - public int getFrequency(final String word) { + private int getFrequencyInternal(final String word, + final boolean isGettingMaxFrequencyOfExactMatches) { if (TextUtils.isEmpty(word)) { return Dictionary.NOT_A_PROBABILITY; } - int maxFreq = -1; + int maxFreq = Dictionary.NOT_A_PROBABILITY; final Dictionaries dictionaries = mDictionaries; - for (final String dictType : DICT_TYPES_ORDERED_TO_GET_SUGGESTION) { + for (final String dictType : DICT_TYPES_ORDERED_TO_GET_SUGGESTIONS) { final Dictionary dictionary = dictionaries.getDict(dictType); if (dictionary == null) continue; - final int tempFreq = dictionary.getFrequency(word); + final int tempFreq; + if (isGettingMaxFrequencyOfExactMatches) { + tempFreq = dictionary.getMaxFrequencyOfExactMatches(word); + } else { + tempFreq = dictionary.getFrequency(word); + } if (tempFreq >= maxFreq) { maxFreq = tempFreq; } @@ -533,6 +539,14 @@ public class DictionaryFacilitator { return maxFreq; } + public int getFrequency(final String word) { + return getFrequencyInternal(word, false /* isGettingMaxFrequencyOfExactMatches */); + } + + public int getMaxFrequencyOfExactMatches(final String word) { + return getFrequencyInternal(word, true /* isGettingMaxFrequencyOfExactMatches */); + } + public void clearUserHistoryDictionary() { final ExpandableBinaryDictionary userHistoryDict = mDictionaries.getSubDict(Dictionary.TYPE_USER_HISTORY); diff --git a/java/src/com/android/inputmethod/latin/ExpandableBinaryDictionary.java b/java/src/com/android/inputmethod/latin/ExpandableBinaryDictionary.java index b10bae01a..8664c09e4 100644 --- a/java/src/com/android/inputmethod/latin/ExpandableBinaryDictionary.java +++ b/java/src/com/android/inputmethod/latin/ExpandableBinaryDictionary.java @@ -441,6 +441,30 @@ abstract public class ExpandableBinaryDictionary extends Dictionary { return mBinaryDictionary.isValidWord(word); } + @Override + public int getMaxFrequencyOfExactMatches(final String word) { + reloadDictionaryIfRequired(); + boolean lockAcquired = false; + try { + lockAcquired = mLock.readLock().tryLock( + TIMEOUT_FOR_READ_OPS_IN_MILLISECONDS, TimeUnit.MILLISECONDS); + if (lockAcquired) { + if (mBinaryDictionary == null) { + return NOT_A_PROBABILITY; + } + return mBinaryDictionary.getMaxFrequencyOfExactMatches(word); + } + } catch (final InterruptedException e) { + Log.e(TAG, "Interrupted tryLock() in getMaxFrequencyOfExactMatches().", e); + } finally { + if (lockAcquired) { + mLock.readLock().unlock(); + } + } + return NOT_A_PROBABILITY; + } + + protected boolean isValidNgramLocked(final PrevWordsInfo prevWordsInfo, final String word) { if (mBinaryDictionary == null) return false; return mBinaryDictionary.isValidNgram(prevWordsInfo, word); diff --git a/java/src/com/android/inputmethod/latin/InputAttributes.java b/java/src/com/android/inputmethod/latin/InputAttributes.java index e778a14f6..e1ae3dfe3 100644 --- a/java/src/com/android/inputmethod/latin/InputAttributes.java +++ b/java/src/com/android/inputmethod/latin/InputAttributes.java @@ -16,6 +16,9 @@ package com.android.inputmethod.latin; +import static com.android.inputmethod.latin.Constants.ImeOption.NO_MICROPHONE; +import static com.android.inputmethod.latin.Constants.ImeOption.NO_MICROPHONE_COMPAT; + import android.text.InputType; import android.util.Log; import android.view.inputmethod.EditorInfo; @@ -35,12 +38,17 @@ public final class InputAttributes { final public String mTargetApplicationPackageName; final public boolean mInputTypeNoAutoCorrect; final public boolean mIsPasswordField; - final public boolean mIsSettingsSuggestionStripOn; + final public boolean mShouldShowSuggestions; final public boolean mApplicationSpecifiedCompletionOn; final public boolean mShouldInsertSpacesAutomatically; final private int mInputType; + final private EditorInfo mEditorInfo; + final private String mPackageNameForPrivateImeOptions; - public InputAttributes(final EditorInfo editorInfo, final boolean isFullscreenMode) { + public InputAttributes(final EditorInfo editorInfo, final boolean isFullscreenMode, + final String packageNameForPrivateImeOptions) { + mEditorInfo = editorInfo; + mPackageNameForPrivateImeOptions = packageNameForPrivateImeOptions; mTargetApplicationPackageName = null != editorInfo ? editorInfo.packageName : null; final int inputType = null != editorInfo ? editorInfo.inputType : 0; final int inputClass = inputType & InputType.TYPE_MASK_CLASS; @@ -62,7 +70,7 @@ public final class InputAttributes { Log.w(TAG, String.format("Unexpected input class: inputType=0x%08x" + " imeOptions=0x%08x", inputType, editorInfo.imeOptions)); } - mIsSettingsSuggestionStripOn = false; + mShouldShowSuggestions = false; mInputTypeNoAutoCorrect = false; mApplicationSpecifiedCompletionOn = false; mShouldInsertSpacesAutomatically = false; @@ -81,13 +89,13 @@ public final class InputAttributes { // TODO: Have a helper method in InputTypeUtils // Make sure that passwords are not displayed in {@link SuggestionStripView}. - final boolean noSuggestionStrip = mIsPasswordField + final boolean shouldSuppressSuggestions = mIsPasswordField || InputTypeUtils.isEmailVariation(variation) || InputType.TYPE_TEXT_VARIATION_URI == variation || InputType.TYPE_TEXT_VARIATION_FILTER == variation || flagNoSuggestions || flagAutoComplete; - mIsSettingsSuggestionStripOn = !noSuggestionStrip; + mShouldShowSuggestions = !shouldSuppressSuggestions; mShouldInsertSpacesAutomatically = InputTypeUtils.isAutoSpaceFriendlyType(inputType); @@ -111,6 +119,15 @@ public final class InputAttributes { return editorInfo.inputType == mInputType; } + public boolean hasNoMicrophoneKeyOption() { + @SuppressWarnings("deprecation") + final boolean deprecatedNoMicrophone = InputAttributes.inPrivateImeOptions( + null, NO_MICROPHONE_COMPAT, mEditorInfo); + final boolean noMicrophone = InputAttributes.inPrivateImeOptions( + mPackageNameForPrivateImeOptions, NO_MICROPHONE, mEditorInfo); + return noMicrophone || deprecatedNoMicrophone; + } + @SuppressWarnings("unused") private void dumpFlags(final int inputType) { final int inputClass = inputType & InputType.TYPE_MASK_CLASS; @@ -241,7 +258,7 @@ public final class InputAttributes { mInputType, (mInputTypeNoAutoCorrect ? " noAutoCorrect" : ""), (mIsPasswordField ? " password" : ""), - (mIsSettingsSuggestionStripOn ? " suggestionStrip" : ""), + (mShouldShowSuggestions ? " shouldShowSuggestions" : ""), (mApplicationSpecifiedCompletionOn ? " appSpecified" : ""), (mShouldInsertSpacesAutomatically ? " insertSpaces" : ""), mTargetApplicationPackageName); diff --git a/java/src/com/android/inputmethod/latin/LatinIME.java b/java/src/com/android/inputmethod/latin/LatinIME.java index d329d2ce6..709f1334f 100644 --- a/java/src/com/android/inputmethod/latin/LatinIME.java +++ b/java/src/com/android/inputmethod/latin/LatinIME.java @@ -166,6 +166,8 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen private static final int ARG1_DISMISS_GESTURE_FLOATING_PREVIEW_TEXT = 1; private static final int ARG1_SHOW_GESTURE_FLOATING_PREVIEW_TEXT = 2; private static final int ARG2_UNUSED = 0; + private static final int ARG1_FALSE = 0; + private static final int ARG1_TRUE = 1; private int mDelayUpdateSuggestions; private int mDelayUpdateShiftState; @@ -213,7 +215,7 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen case MSG_RESUME_SUGGESTIONS: latinIme.mInputLogic.restartSuggestionsOnWordTouchedByCursor( latinIme.mSettings.getCurrent(), - false /* includeResumedWordInSuggestions */); + msg.arg1 == ARG1_TRUE /* shouldIncludeResumedWordInSuggestions */); break; case MSG_REOPEN_DICTIONARIES: latinIme.resetSuggest(); @@ -250,7 +252,7 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen sendMessage(obtainMessage(MSG_REOPEN_DICTIONARIES)); } - public void postResumeSuggestions() { + public void postResumeSuggestions(final boolean shouldIncludeResumedWordInSuggestions) { final LatinIME latinIme = getOwnerInstance(); if (latinIme == null) { return; @@ -260,7 +262,10 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen return; } removeMessages(MSG_RESUME_SUGGESTIONS); - sendMessageDelayed(obtainMessage(MSG_RESUME_SUGGESTIONS), mDelayUpdateSuggestions); + sendMessageDelayed(obtainMessage(MSG_RESUME_SUGGESTIONS, + shouldIncludeResumedWordInSuggestions ? ARG1_TRUE : ARG1_FALSE, + 0 /* ignored */), + mDelayUpdateSuggestions); } public void postResetCaches(final boolean tryResumeSuggestions, final int remainingTries) { @@ -522,7 +527,8 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen void loadSettings() { final Locale locale = mSubtypeSwitcher.getCurrentSubtypeLocale(); final EditorInfo editorInfo = getCurrentInputEditorInfo(); - final InputAttributes inputAttributes = new InputAttributes(editorInfo, isFullscreenMode()); + final InputAttributes inputAttributes = new InputAttributes( + editorInfo, isFullscreenMode(), getPackageName()); mSettings.loadSettings(this, locale, inputAttributes); final SettingsValues currentSettingsValues = mSettings.getCurrent(); AudioAndHapticFeedbackManager.getInstance().onSettingsChanged(currentSettingsValues); @@ -602,7 +608,7 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen mDictionaryFacilitator.resetDictionaries(this /* context */, locale, settingsValues.mUseContactsDict, settingsValues.mUsePersonalizedDicts, false /* forceReloadMainDictionary */, this); - if (settingsValues.mCorrectionEnabled) { + if (settingsValues.mAutoCorrectionEnabled) { mInputLogic.mSuggest.setAutoCorrectionThreshold( settingsValues.mAutoCorrectionThreshold); } @@ -640,14 +646,19 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen @Override public void onConfigurationChanged(final Configuration conf) { - // If orientation changed while predicting, commit the change final SettingsValues settingsValues = mSettings.getCurrent(); if (settingsValues.mDisplayOrientation != conf.orientation) { mHandler.startOrientationChanging(); - mInputLogic.mConnection.beginBatchEdit(); - mInputLogic.commitTyped(mSettings.getCurrent(), LastComposedWord.NOT_A_SEPARATOR); - mInputLogic.mConnection.finishComposingText(); - mInputLogic.mConnection.endBatchEdit(); + // If !isComposingWord, #commitTyped() is a no-op, but still, it's better to avoid + // the useless IPC of {begin,end}BatchEdit. + if (mInputLogic.mWordComposer.isComposingWord()) { + mInputLogic.mConnection.beginBatchEdit(); + // If we had a composition in progress, we need to commit the word so that the + // suggestionsSpan will be added. This will allow resuming on the same suggestions + // after rotation is finished. + mInputLogic.commitTyped(mSettings.getCurrent(), LastComposedWord.NOT_A_SEPARATOR); + mInputLogic.mConnection.endBatchEdit(); + } } PersonalizationDictionarySessionRegistrar.onConfigurationChanged(this, conf, mDictionaryFacilitator); @@ -741,6 +752,7 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen } Log.i(TAG, "Starting input. Cursor position = " + editorInfo.initialSelStart + "," + editorInfo.initialSelEnd); + // TODO: Consolidate these checks with {@link InputAttributes}. if (InputAttributes.inPrivateImeOptions(null, NO_MICROPHONE_COMPAT, editorInfo)) { Log.w(TAG, "Deprecated private IME option specified: " + editorInfo.privateImeOptions); Log.w(TAG, "Use " + getPackageName() + "." + NO_MICROPHONE + " instead"); @@ -802,7 +814,7 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen // When rotating, initialSelStart and initialSelEnd sometimes are lying. Make a best // effort to work around this bug. mInputLogic.mConnection.tryFixLyingCursorPosition(); - mHandler.postResumeSuggestions(); + mHandler.postResumeSuggestions(true /* shouldIncludeResumedWordInSuggestions */); canReachInputConnection = true; } @@ -814,7 +826,7 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen mainKeyboardView.closing(); currentSettingsValues = mSettings.getCurrent(); - if (currentSettingsValues.mCorrectionEnabled) { + if (currentSettingsValues.mAutoCorrectionEnabled) { suggest.setAutoCorrectionThreshold( currentSettingsValues.mAutoCorrectionThreshold); } @@ -991,7 +1003,7 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen null /* rawSuggestions */, false /* typedWordValid */, false /* willAutoCorrect */, false /* isObsoleteSuggestions */, false /* isPrediction */); // When in fullscreen mode, show completions generated by the application forcibly - setSuggestedWords(suggestedWords, true /* isSuggestionStripVisible */); + setSuggestedWords(suggestedWords); } private int getAdjustedBackingViewHeight() { @@ -1294,30 +1306,6 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen // Nothing to do so far. } - private boolean isSuggestionStripVisible() { - if (!hasSuggestionStripView()) { - return false; - } - if (mSuggestionStripView.isShowingAddToDictionaryHint()) { - return true; - } - final SettingsValues currentSettings = mSettings.getCurrent(); - if (null == currentSettings) { - return false; - } - if (ImportantNoticeUtils.shouldShowImportantNotice(this, - currentSettings.mInputAttributes)) { - return true; - } - if (!currentSettings.isCurrentOrientationAllowingSuggestionsPerUserSettings()) { - return false; - } - if (currentSettings.isApplicationSpecifiedCompletionsOn()) { - return true; - } - return currentSettings.isSuggestionsRequested(); - } - public boolean hasSuggestionStripView() { return null != mSuggestionStripView; } @@ -1335,9 +1323,7 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen mSuggestionStripView.dismissAddToDictionaryHint(); } - // TODO[IL]: Define a clear interface for this - public void setSuggestedWords(final SuggestedWords suggestedWords, - final boolean isSuggestionStripVisible) { + private void setSuggestedWords(final SuggestedWords suggestedWords) { mInputLogic.setSuggestedWords(suggestedWords); // TODO: Modify this when we support suggestions with hard keyboard if (!hasSuggestionStripView()) { @@ -1346,28 +1332,35 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen if (!onEvaluateInputViewShown()) { return; } - if (!isSuggestionStripVisible) { - mSuggestionStripView.setVisibility(isFullscreenMode() ? View.GONE : View.INVISIBLE); + + final SettingsValues currentSettingsValues = mSettings.getCurrent(); + final boolean shouldShowImportantNotice = + ImportantNoticeUtils.shouldShowImportantNotice(this); + final boolean shouldShowSuggestionsStripUnlessPassword = shouldShowImportantNotice + || currentSettingsValues.mShowsVoiceInputKey + || currentSettingsValues.isSuggestionsRequested() + || currentSettingsValues.isApplicationSpecifiedCompletionsOn(); + final boolean shouldShowSuggestionsStrip = shouldShowSuggestionsStripUnlessPassword + && !currentSettingsValues.mInputAttributes.mIsPasswordField; + mSuggestionStripView.updateVisibility(shouldShowSuggestionsStrip, isFullscreenMode()); + if (!shouldShowSuggestionsStrip) { return; } - mSuggestionStripView.setVisibility(View.VISIBLE); - final SettingsValues currentSettings = mSettings.getCurrent(); - final boolean showSuggestions; - // May show the important notice when there are no suggestions to show, - if (SuggestedWords.EMPTY == suggestedWords - // or the suggestion strip is expected to show punctuation suggestions, + final boolean isEmptyApplicationSpecifiedCompletions = + currentSettingsValues.isApplicationSpecifiedCompletionsOn() + && suggestedWords.isEmpty(); + final boolean noSuggestionsToShow = (SuggestedWords.EMPTY == suggestedWords) || suggestedWords.isPunctuationSuggestions() - // or it's not requested to show suggestions by the input field, - || !currentSettings.isSuggestionsRequested() - // or the "show correction suggestions" settings is off by users preference. - || !currentSettings.isCurrentOrientationAllowingSuggestionsPerUserSettings()) { - showSuggestions = !mSuggestionStripView.maybeShowImportantNoticeTitle( - currentSettings.mInputAttributes); + || isEmptyApplicationSpecifiedCompletions; + final boolean isShowingImportantNotice; + if (shouldShowImportantNotice && noSuggestionsToShow) { + isShowingImportantNotice = mSuggestionStripView.maybeShowImportantNoticeTitle(); } else { - showSuggestions = true; + isShowingImportantNotice = false; } - if (showSuggestions) { + + if (currentSettingsValues.isSuggestionsRequested() && !isShowingImportantNotice) { mSuggestionStripView.setSuggestions(suggestedWords, SubtypeLocaleUtils.isRtlLanguage(mSubtypeSwitcher.getCurrentSubtype())); } @@ -1410,7 +1403,7 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen mInputLogic.mSuggest.getSuggestedWords(mInputLogic.mWordComposer, mInputLogic.mWordComposer.getPrevWordsInfoForSuggestion(), keyboard.getProximityInfo(), currentSettings.mBlockPotentiallyOffensive, - currentSettings.mCorrectionEnabled, additionalFeaturesOptions, sessionId, + currentSettings.mAutoCorrectionEnabled, additionalFeaturesOptions, sessionId, sequenceNumber, callback); } @@ -1430,7 +1423,7 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen setNeutralSuggestionStrip(); } else { mInputLogic.mWordComposer.setAutoCorrection(autoCorrection); - setSuggestedWords(suggestedWords, isSuggestionStripVisible()); + setSuggestedWords(suggestedWords); } // Cache the auto-correction in accessibility code so we can speak it if the user // touches a key that will insert it. @@ -1463,7 +1456,7 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen final SettingsValues currentSettings = mSettings.getCurrent(); final SuggestedWords neutralSuggestions = currentSettings.mBigramPredictionEnabled ? SuggestedWords.EMPTY : currentSettings.mSpacingAndPunctuations.mSuggestPuncList; - setSuggestedWords(neutralSuggestions, isSuggestionStripVisible()); + setSuggestedWords(neutralSuggestions); } // TODO: Make this private diff --git a/java/src/com/android/inputmethod/latin/ReadOnlyBinaryDictionary.java b/java/src/com/android/inputmethod/latin/ReadOnlyBinaryDictionary.java index 8f744bef8..7989346f4 100644 --- a/java/src/com/android/inputmethod/latin/ReadOnlyBinaryDictionary.java +++ b/java/src/com/android/inputmethod/latin/ReadOnlyBinaryDictionary.java @@ -102,6 +102,18 @@ public final class ReadOnlyBinaryDictionary extends Dictionary { } @Override + public int getMaxFrequencyOfExactMatches(final String word) { + if (mLock.readLock().tryLock()) { + try { + return mBinaryDictionary.getMaxFrequencyOfExactMatches(word); + } finally { + mLock.readLock().unlock(); + } + } + return NOT_A_PROBABILITY; + } + + @Override public void close() { mLock.writeLock().lock(); try { diff --git a/java/src/com/android/inputmethod/latin/RichInputConnection.java b/java/src/com/android/inputmethod/latin/RichInputConnection.java index 9d72c64fc..96476b2ee 100644 --- a/java/src/com/android/inputmethod/latin/RichInputConnection.java +++ b/java/src/com/android/inputmethod/latin/RichInputConnection.java @@ -891,4 +891,8 @@ public final class RichInputConnection { public boolean hasSelection() { return mExpectedSelEnd != mExpectedSelStart; } + + public boolean isCursorPositionKnown() { + return INVALID_CURSOR_POSITION != mExpectedSelStart; + } } diff --git a/java/src/com/android/inputmethod/latin/inputlogic/InputLogic.java b/java/src/com/android/inputmethod/latin/inputlogic/InputLogic.java index dbbe1a0c5..c90dc90ce 100644 --- a/java/src/com/android/inputmethod/latin/inputlogic/InputLogic.java +++ b/java/src/com/android/inputmethod/latin/inputlogic/InputLogic.java @@ -319,8 +319,16 @@ public final class InputLogic { || !mWordComposer.isComposingWord(); // safe to reset final boolean hasOrHadSelection = (oldSelStart != oldSelEnd || newSelStart != newSelEnd); final int moveAmount = newSelStart - oldSelStart; - if (selectionChangedOrSafeToReset && (hasOrHadSelection - || !mWordComposer.moveCursorByAndReturnIfInsideComposingWord(moveAmount))) { + // As an added small gift from the framework, it happens upon rotation when there + // is a selection that we get a wrong cursor position delivered to startInput() that + // does not get reflected in the oldSel{Start,End} parameters to the next call to + // onUpdateSelection. In this case, we may have set a composition, and when we're here + // we realize we shouldn't have. In theory, in this case, selectionChangedOrSafeToReset + // should be true, but that is if the framework had taken that wrong cursor position + // into account, which means we have to reset the entire composing state whenever there + // is or was a selection regardless of whether it changed or not. + if (hasOrHadSelection || (selectionChangedOrSafeToReset + && !mWordComposer.moveCursorByAndReturnIfInsideComposingWord(moveAmount))) { // If we are composing a word and moving the cursor, we would want to set a // suggestion span for recorrection to work correctly. Unfortunately, that // would involve the keyboard committing some new text, which would move the @@ -348,7 +356,7 @@ public final class InputLogic { // The cursor has been moved : we now accept to perform recapitalization mRecapitalizeStatus.enable(); // We moved the cursor. If we are touching a word, we need to resume suggestion. - mLatinIME.mHandler.postResumeSuggestions(); + mLatinIME.mHandler.postResumeSuggestions(false /* shouldIncludeResumedWordInSuggestions */); // Stop the last recapitalization, if started. mRecapitalizeStatus.stop(); return true; @@ -770,7 +778,7 @@ public final class InputLogic { } // isComposingWord() may have changed since we stored wasComposing if (mWordComposer.isComposingWord()) { - if (settingsValues.mCorrectionEnabled) { + if (settingsValues.mAutoCorrectionEnabled) { final String separator = shouldAvoidSendingCode ? LastComposedWord.NOT_A_SEPARATOR : StringUtils.newSingleCodePointString(codePoint); commitCurrentAutoCorrection(settingsValues, separator, handler); @@ -990,7 +998,7 @@ public final class InputLogic { && !mConnection.isCursorFollowedByWordCharacter( inputTransaction.mSettingsValues.mSpacingAndPunctuations)) { restartSuggestionsOnWordTouchedByCursor(inputTransaction.mSettingsValues, - true /* includeResumedWordInSuggestions */); + true /* shouldIncludeResumedWordInSuggestions */); } } } @@ -1173,7 +1181,7 @@ public final class InputLogic { // If correction is not enabled, we don't add words to the user history dictionary. // That's to avoid unintended additions in some sensitive fields, or fields that // expect to receive non-words. - if (!settingsValues.mCorrectionEnabled) return; + if (!settingsValues.mAutoCorrectionEnabled) return; if (TextUtils.isEmpty(suggestion)) return; final boolean wasAutoCapitalized = @@ -1230,12 +1238,12 @@ public final class InputLogic { * do nothing. * * @param settingsValues the current values of the settings. - * @param includeResumedWordInSuggestions whether to include the word on which we resume + * @param shouldIncludeResumedWordInSuggestions whether to include the word on which we resume * suggestions in the suggestion list. */ // TODO: make this private. public void restartSuggestionsOnWordTouchedByCursor(final SettingsValues settingsValues, - final boolean includeResumedWordInSuggestions) { + final boolean shouldIncludeResumedWordInSuggestions) { // HACK: We may want to special-case some apps that exhibit bad behavior in case of // recorrection. This is a temporary, stopgap measure that will be removed later. // TODO: remove this. @@ -1281,7 +1289,7 @@ public final class InputLogic { if (numberOfCharsInWordBeforeCursor > expectedCursorPosition) return; final ArrayList<SuggestedWordInfo> suggestions = new ArrayList<>(); final String typedWord = range.mWord.toString(); - if (includeResumedWordInSuggestions) { + if (shouldIncludeResumedWordInSuggestions) { suggestions.add(new SuggestedWordInfo(typedWord, SuggestedWords.MAX_SUGGESTIONS + 1, SuggestedWordInfo.KIND_TYPED, Dictionary.DICTIONARY_USER_TYPED, @@ -1319,9 +1327,10 @@ public final class InputLogic { typedWord.codePointCount(0, numberOfCharsInWordBeforeCursor)); mConnection.setComposingRegion(expectedCursorPosition - numberOfCharsInWordBeforeCursor, expectedCursorPosition + range.getNumberOfCharsInWordAfterCursor()); - if (suggestions.isEmpty()) { - // We come here if there weren't any suggestion spans on this word. We will try to - // compute suggestions for it instead. + if (suggestions.size() <= (shouldIncludeResumedWordInSuggestions ? 1 : 0)) { + // 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, SuggestedWords.NOT_A_SEQUENCE_NUMBER, new OnGetSuggestedWordsCallback() { @Override @@ -1329,7 +1338,7 @@ public final class InputLogic { final SuggestedWords suggestedWordsIncludingTypedWord) { final SuggestedWords suggestedWords; if (suggestedWordsIncludingTypedWord.size() > 1 - && !includeResumedWordInSuggestions) { + && !shouldIncludeResumedWordInSuggestions) { // We were able to compute new suggestions for this word. // Remove the typed word, since we don't want to display it in this // case. The #getSuggestedWordsExcludingTypedWord() method sets @@ -1922,9 +1931,11 @@ public final class InputLogic { final boolean tryResumeSuggestions, final int remainingTries, // TODO: remove these arguments final LatinIME.UIHandler handler) { + final boolean shouldFinishComposition = mConnection.hasSelection() + || !mConnection.isCursorPositionKnown(); if (!mConnection.resetCachesUponCursorMoveAndReturnSuccess( mConnection.getExpectedSelectionStart(), mConnection.getExpectedSelectionEnd(), - false /* shouldFinishComposition */)) { + shouldFinishComposition)) { if (0 < remainingTries) { handler.postResetCaches(tryResumeSuggestions, remainingTries - 1); return false; @@ -1934,7 +1945,9 @@ public final class InputLogic { } mConnection.tryFixLyingCursorPosition(); if (tryResumeSuggestions) { - handler.postResumeSuggestions(); + // This is triggered when starting input anew, so we want to include the resumed + // word in suggestions. + handler.postResumeSuggestions(true /* shouldIncludeResumedWordInSuggestions */); } return true; } diff --git a/java/src/com/android/inputmethod/latin/settings/SettingsValues.java b/java/src/com/android/inputmethod/latin/settings/SettingsValues.java index 6c6e79e0f..389d9a869 100644 --- a/java/src/com/android/inputmethod/latin/settings/SettingsValues.java +++ b/java/src/com/android/inputmethod/latin/settings/SettingsValues.java @@ -28,6 +28,7 @@ import com.android.inputmethod.compat.AppWorkaroundsUtils; import com.android.inputmethod.latin.InputAttributes; import com.android.inputmethod.latin.R; import com.android.inputmethod.latin.RichInputMethodManager; +import com.android.inputmethod.latin.SubtypeSwitcher; import com.android.inputmethod.latin.utils.AsyncResultHolder; import com.android.inputmethod.latin.utils.ResourceUtils; import com.android.inputmethod.latin.utils.TargetPackageInfoGetterTask; @@ -84,7 +85,7 @@ public final class SettingsValues { public final int mKeyPreviewPopupDismissDelay; private final boolean mAutoCorrectEnabled; public final float mAutoCorrectionThreshold; - public final boolean mCorrectionEnabled; + public final boolean mAutoCorrectionEnabled; public final int mSuggestionVisibility; public final int mDisplayOrientation; private final AsyncResultHolder<AppWorkaroundsUtils> mAppWorkarounds; @@ -109,7 +110,8 @@ public final class SettingsValues { // Store the input attributes if (null == inputAttributes) { - mInputAttributes = new InputAttributes(null, false /* isFullscreenMode */); + mInputAttributes = new InputAttributes( + null, false /* isFullscreenMode */, context.getPackageName()); } else { mInputAttributes = inputAttributes; } @@ -121,7 +123,10 @@ public final class SettingsValues { mKeyPreviewPopupOn = Settings.readKeyPreviewPopupEnabled(prefs, res); mSlidingKeyInputPreviewEnabled = prefs.getBoolean( DebugSettings.PREF_SLIDING_KEY_INPUT_PREVIEW, true); - mShowsVoiceInputKey = needsToShowVoiceInputKey(prefs, res); + mShowsVoiceInputKey = needsToShowVoiceInputKey(prefs, res) + && !mInputAttributes.mIsPasswordField + && !mInputAttributes.hasNoMicrophoneKeyOption() + && SubtypeSwitcher.getInstance().isShortcutImeEnabled(); final String autoCorrectionThresholdRawValue = prefs.getString( Settings.PREF_AUTO_CORRECTION_THRESHOLD, res.getString(R.string.auto_correction_threshold_mode_index_modest)); @@ -150,7 +155,7 @@ public final class SettingsValues { mGestureFloatingPreviewTextEnabled = prefs.getBoolean( Settings.PREF_GESTURE_FLOATING_PREVIEW_TEXT, true); mPhraseGestureEnabled = Settings.readPhraseGestureEnabled(prefs, res); - mCorrectionEnabled = mAutoCorrectEnabled && !mInputAttributes.mInputTypeNoAutoCorrect; + mAutoCorrectionEnabled = mAutoCorrectEnabled && !mInputAttributes.mInputTypeNoAutoCorrect; final String showSuggestionsSetting = prefs.getString( Settings.PREF_SHOW_SUGGESTIONS_SETTING, res.getString(R.string.prefs_suggestion_visibility_default_value)); @@ -189,8 +194,8 @@ public final class SettingsValues { } public boolean isSuggestionsRequested() { - return mInputAttributes.mIsSettingsSuggestionStripOn - && (mCorrectionEnabled + return mInputAttributes.mShouldShowSuggestions + && (mAutoCorrectionEnabled || isCurrentOrientationAllowingSuggestionsPerUserSettings()); } @@ -318,18 +323,18 @@ public final class SettingsValues { private static boolean needsToShowVoiceInputKey(final SharedPreferences prefs, final Resources res) { - if (!prefs.contains(Settings.PREF_VOICE_INPUT_KEY)) { - // Migrate preference from {@link Settings#PREF_VOICE_MODE_OBSOLETE} to - // {@link Settings#PREF_VOICE_INPUT_KEY}. + // Migrate preference from {@link Settings#PREF_VOICE_MODE_OBSOLETE} to + // {@link Settings#PREF_VOICE_INPUT_KEY}. + if (prefs.contains(Settings.PREF_VOICE_MODE_OBSOLETE)) { final String voiceModeMain = res.getString(R.string.voice_mode_main); final String voiceMode = prefs.getString( Settings.PREF_VOICE_MODE_OBSOLETE, voiceModeMain); final boolean shouldShowVoiceInputKey = voiceModeMain.equals(voiceMode); - prefs.edit().putBoolean(Settings.PREF_VOICE_INPUT_KEY, shouldShowVoiceInputKey).apply(); - } - // Remove the obsolete preference if exists. - if (prefs.contains(Settings.PREF_VOICE_MODE_OBSOLETE)) { - prefs.edit().remove(Settings.PREF_VOICE_MODE_OBSOLETE).apply(); + prefs.edit() + .putBoolean(Settings.PREF_VOICE_INPUT_KEY, shouldShowVoiceInputKey) + // Remove the obsolete preference if exists. + .remove(Settings.PREF_VOICE_MODE_OBSOLETE) + .apply(); } return prefs.getBoolean(Settings.PREF_VOICE_INPUT_KEY, true); } @@ -390,8 +395,8 @@ public final class SettingsValues { sb.append("" + mAutoCorrectEnabled); sb.append("\n mAutoCorrectionThreshold = "); sb.append("" + mAutoCorrectionThreshold); - sb.append("\n mCorrectionEnabled = "); - sb.append("" + mCorrectionEnabled); + sb.append("\n mAutoCorrectionEnabled = "); + sb.append("" + mAutoCorrectionEnabled); sb.append("\n mSuggestionVisibility = "); sb.append("" + mSuggestionVisibility); sb.append("\n mDisplayOrientation = "); diff --git a/java/src/com/android/inputmethod/latin/suggestions/SuggestionStripView.java b/java/src/com/android/inputmethod/latin/suggestions/SuggestionStripView.java index 4a5a7f004..97241498a 100644 --- a/java/src/com/android/inputmethod/latin/suggestions/SuggestionStripView.java +++ b/java/src/com/android/inputmethod/latin/suggestions/SuggestionStripView.java @@ -42,12 +42,12 @@ import com.android.inputmethod.keyboard.MainKeyboardView; import com.android.inputmethod.keyboard.MoreKeysPanel; import com.android.inputmethod.latin.AudioAndHapticFeedbackManager; import com.android.inputmethod.latin.Constants; -import com.android.inputmethod.latin.InputAttributes; import com.android.inputmethod.latin.LatinImeLogger; import com.android.inputmethod.latin.R; import com.android.inputmethod.latin.SuggestedWords; import com.android.inputmethod.latin.SuggestedWords.SuggestedWordInfo; import com.android.inputmethod.latin.settings.Settings; +import com.android.inputmethod.latin.settings.SettingsValues; import com.android.inputmethod.latin.suggestions.MoreSuggestionsView.MoreSuggestionsListener; import com.android.inputmethod.latin.utils.ImportantNoticeUtils; @@ -89,19 +89,17 @@ public final class SuggestionStripView extends RelativeLayout implements OnClick private static class StripVisibilityGroup { private final View mSuggestionStripView; private final View mSuggestionsStrip; - private final View mVoiceKey; private final View mAddToDictionaryStrip; private final View mImportantNoticeStrip; public StripVisibilityGroup(final View suggestionStripView, - final ViewGroup suggestionsStrip, final ImageButton voiceKey, - final ViewGroup addToDictionaryStrip, final View importantNoticeStrip) { + final ViewGroup suggestionsStrip, final ViewGroup addToDictionaryStrip, + final View importantNoticeStrip) { mSuggestionStripView = suggestionStripView; mSuggestionsStrip = suggestionsStrip; - mVoiceKey = voiceKey; mAddToDictionaryStrip = addToDictionaryStrip; mImportantNoticeStrip = importantNoticeStrip; - showSuggestionsStrip(false /* voiceKeyEnabled */); + showSuggestionsStrip(); } public void setLayoutDirection(final boolean isRtlLanguage) { @@ -113,23 +111,20 @@ public final class SuggestionStripView extends RelativeLayout implements OnClick ViewCompat.setLayoutDirection(mImportantNoticeStrip, layoutDirection); } - public void showSuggestionsStrip(final boolean enableVoiceKey) { + public void showSuggestionsStrip() { mSuggestionsStrip.setVisibility(VISIBLE); - mVoiceKey.setVisibility(enableVoiceKey ? VISIBLE : INVISIBLE); mAddToDictionaryStrip.setVisibility(INVISIBLE); mImportantNoticeStrip.setVisibility(INVISIBLE); } public void showAddToDictionaryStrip() { mSuggestionsStrip.setVisibility(INVISIBLE); - mVoiceKey.setVisibility(INVISIBLE); mAddToDictionaryStrip.setVisibility(VISIBLE); mImportantNoticeStrip.setVisibility(INVISIBLE); } - public void showImportantNoticeStrip(final boolean enableVoiceKey) { + public void showImportantNoticeStrip() { mSuggestionsStrip.setVisibility(INVISIBLE); - mVoiceKey.setVisibility(enableVoiceKey ? VISIBLE : INVISIBLE); mAddToDictionaryStrip.setVisibility(INVISIBLE); mImportantNoticeStrip.setVisibility(VISIBLE); } @@ -159,7 +154,7 @@ public final class SuggestionStripView extends RelativeLayout implements OnClick mVoiceKey = (ImageButton)findViewById(R.id.suggestions_strip_voice_key); mAddToDictionaryStrip = (ViewGroup)findViewById(R.id.add_to_dictionary_strip); mImportantNoticeStrip = findViewById(R.id.important_notice_strip); - mStripVisibilityGroup = new StripVisibilityGroup(this, mSuggestionsStrip, mVoiceKey, + mStripVisibilityGroup = new StripVisibilityGroup(this, mSuggestionsStrip, mAddToDictionaryStrip, mImportantNoticeStrip); for (int pos = 0; pos < SuggestedWords.MAX_SUGGESTIONS; pos++) { @@ -207,15 +202,11 @@ public final class SuggestionStripView extends RelativeLayout implements OnClick mMainKeyboardView = (MainKeyboardView)inputView.findViewById(R.id.keyboard_view); } - private boolean isVoiceKeyEnabled() { - if (mMainKeyboardView == null) { - return false; - } - final Keyboard keyboard = mMainKeyboardView.getKeyboard(); - if (keyboard == null) { - return false; - } - return keyboard.mId.mHasShortcutKey; + public void updateVisibility(final boolean shouldBeVisible, final boolean isFullscreenMode) { + final int visibility = shouldBeVisible ? VISIBLE : (isFullscreenMode ? GONE : INVISIBLE); + setVisibility(visibility); + final SettingsValues currentSettingsValues = Settings.getInstance().getCurrent(); + mVoiceKey.setVisibility(currentSettingsValues.mShowsVoiceInputKey ? VISIBLE : INVISIBLE); } public void setSuggestions(final SuggestedWords suggestedWords, final boolean isRtlLanguage) { @@ -224,7 +215,7 @@ public final class SuggestionStripView extends RelativeLayout implements OnClick mSuggestedWords = suggestedWords; mSuggestionsCountInStrip = mLayoutHelper.layoutAndReturnSuggestionCountInStrip( mSuggestedWords, mSuggestionsStrip, this); - mStripVisibilityGroup.showSuggestionsStrip(isVoiceKeyEnabled()); + mStripVisibilityGroup.showSuggestionsStrip(); } public int setMoreSuggestionsHeight(final int remainingHeight) { @@ -255,8 +246,8 @@ public final class SuggestionStripView extends RelativeLayout implements OnClick // This method checks if we should show the important notice (checks on permanent storage if // it has been shown once already or not, and if in the setup wizard). If applicable, it shows // the notice. In all cases, it returns true if it was shown, false otherwise. - public boolean maybeShowImportantNoticeTitle(final InputAttributes inputAttributes) { - if (!ImportantNoticeUtils.shouldShowImportantNotice(getContext(), inputAttributes)) { + public boolean maybeShowImportantNoticeTitle() { + if (!ImportantNoticeUtils.shouldShowImportantNotice(getContext())) { return false; } if (getWidth() <= 0) { @@ -271,7 +262,7 @@ public final class SuggestionStripView extends RelativeLayout implements OnClick dismissMoreSuggestionsPanel(); } mLayoutHelper.layoutImportantNotice(mImportantNoticeStrip, importantNoticeTitle); - mStripVisibilityGroup.showImportantNoticeStrip(isVoiceKeyEnabled()); + mStripVisibilityGroup.showImportantNoticeStrip(); mImportantNoticeStrip.setOnClickListener(this); return true; } @@ -279,7 +270,7 @@ public final class SuggestionStripView extends RelativeLayout implements OnClick public void clear() { mSuggestionsStrip.removeAllViews(); removeAllDebugInfoViews(); - mStripVisibilityGroup.showSuggestionsStrip(false /* enableVoiceKey */); + mStripVisibilityGroup.showSuggestionsStrip(); dismissMoreSuggestionsPanel(); } @@ -481,7 +472,7 @@ public final class SuggestionStripView extends RelativeLayout implements OnClick // Called by the framework when the size is known. Show the important notice if applicable. // This may be overriden by showing suggestions later, if applicable. if (oldw <= 0 && w > 0) { - maybeShowImportantNoticeTitle(Settings.getInstance().getCurrent().mInputAttributes); + maybeShowImportantNoticeTitle(); } } } diff --git a/java/src/com/android/inputmethod/latin/userdictionary/UserDictionaryAddWordContents.java b/java/src/com/android/inputmethod/latin/userdictionary/UserDictionaryAddWordContents.java index 80d4cc44f..eda81940f 100644 --- a/java/src/com/android/inputmethod/latin/userdictionary/UserDictionaryAddWordContents.java +++ b/java/src/com/android/inputmethod/latin/userdictionary/UserDictionaryAddWordContents.java @@ -167,7 +167,9 @@ public class UserDictionaryAddWordContents { // should not insert, because either A. the word exists with no shortcut, in which // case the exact same thing we want to insert is already there, or B. the word // exists with at least one shortcut, in which case it has priority on our word. - if (hasWord(newWord, context)) return CODE_ALREADY_PRESENT; + if (TextUtils.isEmpty(newShortcut) && hasWord(newWord, context)) { + return CODE_ALREADY_PRESENT; + } // Disallow duplicates. If the same word with no shortcut is defined, remove it; if // the same word with the same shortcut is defined, remove it; but we don't mind if diff --git a/java/src/com/android/inputmethod/latin/utils/DistracterFilterUsingSuggestion.java b/java/src/com/android/inputmethod/latin/utils/DistracterFilterUsingSuggestion.java index b9c7f5671..8c3844ed8 100644 --- a/java/src/com/android/inputmethod/latin/utils/DistracterFilterUsingSuggestion.java +++ b/java/src/com/android/inputmethod/latin/utils/DistracterFilterUsingSuggestion.java @@ -16,33 +16,22 @@ package com.android.inputmethod.latin.utils; -import java.util.HashMap; import java.util.List; import java.util.Locale; -import java.util.Map; import java.util.concurrent.TimeUnit; import android.content.Context; -import android.content.res.Resources; -import android.text.InputType; import android.util.Log; -import android.view.inputmethod.EditorInfo; import android.view.inputmethod.InputMethodSubtype; -import com.android.inputmethod.keyboard.Keyboard; -import com.android.inputmethod.keyboard.KeyboardId; -import com.android.inputmethod.keyboard.KeyboardLayoutSet; -import com.android.inputmethod.latin.Constants; -import com.android.inputmethod.latin.Dictionary; import com.android.inputmethod.latin.DictionaryFacilitator; import com.android.inputmethod.latin.PrevWordsInfo; -import com.android.inputmethod.latin.SuggestedWords.SuggestedWordInfo; -import com.android.inputmethod.latin.WordComposer; /** * This class is used to prevent distracters being added to personalization * or user history dictionaries */ +// TODO: Rename. public class DistracterFilterUsingSuggestion implements DistracterFilter { private static final String TAG = DistracterFilterUsingSuggestion.class.getSimpleName(); private static final boolean DEBUG = false; @@ -50,10 +39,7 @@ public class DistracterFilterUsingSuggestion implements DistracterFilter { private static final long TIMEOUT_TO_WAIT_LOADING_DICTIONARIES_IN_SECONDS = 120; private final Context mContext; - private final Map<Locale, InputMethodSubtype> mLocaleToSubtypeMap; - private final Map<Locale, Keyboard> mLocaleToKeyboardMap; private final DictionaryFacilitator mDictionaryFacilitator; - private Keyboard mKeyboard; private final Object mLock = new Object(); /** @@ -63,10 +49,7 @@ public class DistracterFilterUsingSuggestion implements DistracterFilter { */ public DistracterFilterUsingSuggestion(final Context context) { mContext = context; - mLocaleToSubtypeMap = new HashMap<>(); - mLocaleToKeyboardMap = new HashMap<>(); mDictionaryFacilitator = new DictionaryFacilitator(); - mKeyboard = null; } @Override @@ -76,94 +59,6 @@ public class DistracterFilterUsingSuggestion implements DistracterFilter { @Override public void updateEnabledSubtypes(final List<InputMethodSubtype> enabledSubtypes) { - final Map<Locale, InputMethodSubtype> newLocaleToSubtypeMap = new HashMap<>(); - if (enabledSubtypes != null) { - for (final InputMethodSubtype subtype : enabledSubtypes) { - final Locale locale = SubtypeLocaleUtils.getSubtypeLocale(subtype); - if (newLocaleToSubtypeMap.containsKey(locale)) { - // Multiple subtypes are enabled for one locale. - // TODO: Investigate what we should do for this case. - continue; - } - newLocaleToSubtypeMap.put(locale, subtype); - } - } - if (mLocaleToSubtypeMap.equals(newLocaleToSubtypeMap)) { - // Enabled subtypes have not been changed. - return; - } - synchronized (mLock) { - mLocaleToSubtypeMap.clear(); - mLocaleToSubtypeMap.putAll(newLocaleToSubtypeMap); - mLocaleToKeyboardMap.clear(); - } - } - - private boolean isDistracter( - final SuggestionResults suggestionResults, final String consideredWord) { - int perfectMatchProbability = Dictionary.NOT_A_PROBABILITY; - for (final SuggestedWordInfo suggestedWordInfo : suggestionResults) { - if (suggestedWordInfo.mWord.equals(consideredWord)) { - perfectMatchProbability = mDictionaryFacilitator.getFrequency(consideredWord); - continue; - } - // Exact match can include case errors, accent errors, digraph conversions. - final boolean isExactMatch = suggestedWordInfo.isExactMatch(); - final boolean isExactMatchWithIntentionalOmission = - suggestedWordInfo.isExactMatchWithIntentionalOmission(); - - if (DEBUG) { - final float normalizedScore = BinaryDictionaryUtils.calcNormalizedScore( - consideredWord, suggestedWordInfo.mWord, suggestedWordInfo.mScore); - Log.d(TAG, "consideredWord: " + consideredWord); - Log.d(TAG, "top suggestion: " + suggestedWordInfo.mWord); - Log.d(TAG, "suggestionScore: " + suggestedWordInfo.mScore); - Log.d(TAG, "normalizedScore: " + normalizedScore); - Log.d(TAG, "isExactMatch: " + isExactMatch); - Log.d(TAG, "isExactMatchWithIntentionalOmission: " - + isExactMatchWithIntentionalOmission); - } - if (perfectMatchProbability != Dictionary.NOT_A_PROBABILITY) { - final int topNonPerfectProbability = mDictionaryFacilitator.getFrequency( - suggestedWordInfo.mWord); - if (DEBUG) { - Log.d(TAG, "perfectMatchProbability: " + perfectMatchProbability); - Log.d(TAG, "topNonPerfectProbability: " + topNonPerfectProbability); - } - if (perfectMatchProbability > topNonPerfectProbability) { - return false; - } - } - return isExactMatch || isExactMatchWithIntentionalOmission; - } - return false; - } - - private void loadKeyboardForLocale(final Locale newLocale) { - final Keyboard cachedKeyboard = mLocaleToKeyboardMap.get(newLocale); - if (cachedKeyboard != null) { - mKeyboard = cachedKeyboard; - return; - } - final InputMethodSubtype subtype; - synchronized (mLock) { - subtype = mLocaleToSubtypeMap.get(newLocale); - } - if (subtype == null) { - return; - } - final EditorInfo editorInfo = new EditorInfo(); - editorInfo.inputType = InputType.TYPE_CLASS_TEXT; - final KeyboardLayoutSet.Builder builder = new KeyboardLayoutSet.Builder( - mContext, editorInfo); - final Resources res = mContext.getResources(); - final int keyboardWidth = ResourceUtils.getDefaultKeyboardWidth(res); - final int keyboardHeight = ResourceUtils.getDefaultKeyboardHeight(res); - builder.setKeyboardGeometry(keyboardWidth, keyboardHeight); - builder.setSubtype(subtype); - builder.setIsSpellChecker(false /* isSpellChecker */); - final KeyboardLayoutSet layoutSet = builder.build(); - mKeyboard = layoutSet.getKeyboard(KeyboardId.ELEMENT_ALPHABET); } private void loadDictionariesForLocale(final Locale newlocale) throws InterruptedException { @@ -191,12 +86,6 @@ public class DistracterFilterUsingSuggestion implements DistracterFilter { } if (!locale.equals(mDictionaryFacilitator.getLocale())) { synchronized (mLock) { - if (!mLocaleToSubtypeMap.containsKey(locale)) { - Log.e(TAG, "Locale " + locale + " is not enabled."); - // TODO: Investigate what we should do for disabled locales. - return false; - } - loadKeyboardForLocale(locale); // Reset dictionaries for the locale. try { loadDictionariesForLocale(locale); @@ -207,37 +96,17 @@ public class DistracterFilterUsingSuggestion implements DistracterFilter { } } } - if (mKeyboard == null) { - return false; + // The tested word is a distracter when there is a word that is exact matched to the tested + // word and its probability is higher than the tested word's probability. + final int perfectMatchFreq = mDictionaryFacilitator.getFrequency(testedWord); + final int exactMatchFreq = mDictionaryFacilitator.getMaxFrequencyOfExactMatches(testedWord); + final boolean isDistracter = perfectMatchFreq < exactMatchFreq; + if (DEBUG) { + Log.d(TAG, "testedWord: " + testedWord); + Log.d(TAG, "perfectMatchFreq: " + perfectMatchFreq); + Log.d(TAG, "exactMatchFreq: " + exactMatchFreq); + Log.d(TAG, "isDistracter: " + isDistracter); } - final WordComposer composer = new WordComposer(); - final int[] codePoints = StringUtils.toCodePointArray(testedWord); - final int[] coordinates = mKeyboard.getCoordinates(codePoints); - composer.setComposingWord(codePoints, coordinates, PrevWordsInfo.EMPTY_PREV_WORDS_INFO); - - final int trailingSingleQuotesCount = StringUtils.getTrailingSingleQuotesCount(testedWord); - final String consideredWord = trailingSingleQuotesCount > 0 ? - testedWord.substring(0, testedWord.length() - trailingSingleQuotesCount) : - testedWord; - final AsyncResultHolder<Boolean> holder = new AsyncResultHolder<>(); - ExecutorUtils.getExecutor("check distracters").execute(new Runnable() { - @Override - public void run() { - final SuggestionResults suggestionResults = - mDictionaryFacilitator.getSuggestionResults( - composer, PrevWordsInfo.EMPTY_PREV_WORDS_INFO, - mKeyboard.getProximityInfo(), true /* blockOffensiveWords */, - null /* additionalFeaturesOptions */, 0 /* sessionId */, - null /* rawSuggestions */); - if (suggestionResults.isEmpty()) { - holder.set(false); - return; - } - holder.set(isDistracter(suggestionResults, consideredWord)); - } - }); - // It's OK to block the distracter filtering, but the dictionary lookup should be done - // sequentially using ExecutorUtils. - return holder.get(false /* defaultValue */, Constants.GET_SUGGESTED_WORDS_TIMEOUT); + return isDistracter; } } diff --git a/java/src/com/android/inputmethod/latin/utils/ImportantNoticeUtils.java b/java/src/com/android/inputmethod/latin/utils/ImportantNoticeUtils.java index 7d937a9d2..8b7077879 100644 --- a/java/src/com/android/inputmethod/latin/utils/ImportantNoticeUtils.java +++ b/java/src/com/android/inputmethod/latin/utils/ImportantNoticeUtils.java @@ -23,7 +23,6 @@ import android.provider.Settings.SettingNotFoundException; import android.text.TextUtils; import android.util.Log; -import com.android.inputmethod.latin.InputAttributes; import com.android.inputmethod.latin.R; public final class ImportantNoticeUtils { @@ -78,14 +77,7 @@ public final class ImportantNoticeUtils { return getCurrentImportantNoticeVersion(context) > lastVersion; } - public static boolean shouldShowImportantNotice(final Context context, - final InputAttributes inputAttributes) { - if (inputAttributes == null || inputAttributes.mIsPasswordField) { - return false; - } - if (isInSystemSetupWizard(context)) { - return false; - } + public static boolean shouldShowImportantNotice(final Context context) { if (!hasNewImportantNotice(context)) { return false; } @@ -93,6 +85,9 @@ public final class ImportantNoticeUtils { if (TextUtils.isEmpty(importantNoticeTitle)) { return false; } + if (isInSystemSetupWizard(context)) { + return false; + } return true; } |