diff options
Diffstat (limited to 'java/src')
3 files changed, 79 insertions, 39 deletions
diff --git a/java/src/com/android/inputmethod/compat/SuggestionSpanUtils.java b/java/src/com/android/inputmethod/compat/SuggestionSpanUtils.java index 164d2b748..161ef09b8 100644 --- a/java/src/com/android/inputmethod/compat/SuggestionSpanUtils.java +++ b/java/src/com/android/inputmethod/compat/SuggestionSpanUtils.java @@ -51,7 +51,7 @@ public class SuggestionSpanUtils { public static final Field FIELD_FLAG_AUTO_CORRECTION = CompatUtils.getField(CLASS_SuggestionSpan, "FLAG_AUTO_CORRECTION"); public static final Field FIELD_SUGGESTION_MAX_SIZE - = CompatUtils.getField(CLASS_SuggestionSpan, "SUGGESTION_MAX_SIZE"); + = CompatUtils.getField(CLASS_SuggestionSpan, "SUGGESTIONS_MAX_SIZE"); public static final Integer OBJ_FLAG_AUTO_CORRECTION = (Integer) CompatUtils .getFieldValue(null, null, FIELD_FLAG_AUTO_CORRECTION);; public static final Integer OBJ_SUGGESTION_MAX_SIZE = (Integer) CompatUtils @@ -63,7 +63,7 @@ public class SuggestionSpanUtils { if (LatinImeLogger.sDBG) { if (SUGGESTION_SPAN_IS_SUPPORTED && (OBJ_FLAG_AUTO_CORRECTION == null || OBJ_SUGGESTION_MAX_SIZE == null)) { - Log.e(TAG, "Field is accidentially null."); + throw new RuntimeException("Field is accidentially null."); } } } @@ -71,7 +71,7 @@ public class SuggestionSpanUtils { public static CharSequence getTextWithAutoCorrectionIndicatorUnderline( Context context, CharSequence text) { if (TextUtils.isEmpty(text) || CONSTRUCTOR_SuggestionSpan == null - || OBJ_FLAG_AUTO_CORRECTION == null) { + || OBJ_FLAG_AUTO_CORRECTION == null || OBJ_SUGGESTION_MAX_SIZE == null) { return text; } final Spannable spannable = text instanceof Spannable diff --git a/java/src/com/android/inputmethod/keyboard/KeyboardSwitcher.java b/java/src/com/android/inputmethod/keyboard/KeyboardSwitcher.java index bf1e22095..65f8a79df 100644 --- a/java/src/com/android/inputmethod/keyboard/KeyboardSwitcher.java +++ b/java/src/com/android/inputmethod/keyboard/KeyboardSwitcher.java @@ -69,6 +69,9 @@ public class KeyboardSwitcher implements SharedPreferences.OnSharedPreferenceCha private Resources mResources; private KeyboardState mState; + private static final int UNSHIFT = 0; + private static final int MANUAL_SHIFT = 1; + private static final int AUTOMATIC_SHIFT = 2; private KeyboardId mMainKeyboardId; private KeyboardId mSymbolsKeyboardId; @@ -391,7 +394,7 @@ public class KeyboardSwitcher implements SharedPreferences.OnSharedPreferenceCha return false; } - public boolean isAutomaticTemporaryUpperCase() { + private boolean isAutomaticTemporaryUpperCase() { LatinKeyboard latinKeyboard = getLatinKeyboard(); if (latinKeyboard != null) return latinKeyboard.isAutomaticTemporaryUpperCase(); @@ -412,13 +415,19 @@ public class KeyboardSwitcher implements SharedPreferences.OnSharedPreferenceCha return false; } - private void setManualTemporaryUpperCase(boolean shifted) { + private void setShift(int shiftMode) { LatinKeyboard latinKeyboard = getLatinKeyboard(); - if (latinKeyboard != null) { + if (latinKeyboard == null) + return; + if (shiftMode == AUTOMATIC_SHIFT) { + latinKeyboard.setAutomaticTemporaryUpperCase(); + mKeyboardView.invalidateAllKeys(); + } else { + final boolean shifted = (shiftMode == MANUAL_SHIFT); // On non-distinct multi touch panel device, we should also turn off the shift locked // state when shift key is pressed to go to normal mode. - // On the other hand, on distinct multi touch panel device, turning off the shift locked - // state with shift key pressing is handled by onReleaseShift(). + // On the other hand, on distinct multi touch panel device, turning off the shift + // locked state with shift key pressing is handled by onReleaseShift(). if (!hasDistinctMultitouch() && !shifted && latinKeyboard.isShiftLocked()) { latinKeyboard.setShiftLocked(false); } @@ -428,14 +437,6 @@ public class KeyboardSwitcher implements SharedPreferences.OnSharedPreferenceCha } } - private void setAutomaticTemporaryUpperCase() { - LatinKeyboard latinKeyboard = getLatinKeyboard(); - if (latinKeyboard != null) { - latinKeyboard.setAutomaticTemporaryUpperCase(); - mKeyboardView.invalidateAllKeys(); - } - } - private void setShiftLocked(boolean shiftLocked) { LatinKeyboard latinKeyboard = getLatinKeyboard(); if (latinKeyboard != null && latinKeyboard.setShiftLocked(shiftLocked)) { @@ -454,7 +455,7 @@ public class KeyboardSwitcher implements SharedPreferences.OnSharedPreferenceCha + " state=" + mState); } if (isAlphabetMode()) { - setManualTemporaryUpperCase(!isShiftedOrShiftLocked()); + setShift(isShiftedOrShiftLocked() ? UNSHIFT : MANUAL_SHIFT); } else { toggleShiftInSymbol(); } @@ -516,9 +517,9 @@ public class KeyboardSwitcher implements SharedPreferences.OnSharedPreferenceCha if (!isShiftLocked && !mState.isShiftKeyIgnoring()) { if (mState.isShiftKeyReleasing() && mInputMethodService.getCurrentAutoCapsState()) { // Only when shift key is releasing, automatic temporary upper case will be set. - setAutomaticTemporaryUpperCase(); + setShift(AUTOMATIC_SHIFT); } else { - setManualTemporaryUpperCase(mState.isShiftKeyMomentary()); + setShift(mState.isShiftKeyMomentary() ? MANUAL_SHIFT : UNSHIFT); } } } @@ -541,11 +542,11 @@ public class KeyboardSwitcher implements SharedPreferences.OnSharedPreferenceCha if (isShiftLocked) { // Shift key is pressed while caps lock state, we will treat this state as shifted // caps lock state and mark as if shift key pressed while normal state. - setManualTemporaryUpperCase(true); + setShift(MANUAL_SHIFT); } else if (isAutomaticTemporaryUpperCase) { // Shift key is pressed while automatic temporary upper case, we have to move to // manual temporary upper case. - setManualTemporaryUpperCase(true); + setShift(MANUAL_SHIFT); } else if (isShiftedOrShiftLocked) { // In manual upper case state, we just record shift key has been pressing while // shifted state. diff --git a/java/src/com/android/inputmethod/latin/spellcheck/AndroidSpellCheckerService.java b/java/src/com/android/inputmethod/latin/spellcheck/AndroidSpellCheckerService.java index 9dfbe7a54..a89ef001e 100644 --- a/java/src/com/android/inputmethod/latin/spellcheck/AndroidSpellCheckerService.java +++ b/java/src/com/android/inputmethod/latin/spellcheck/AndroidSpellCheckerService.java @@ -415,10 +415,31 @@ public class AndroidSpellCheckerService extends SpellCheckerService } private static class AndroidSpellCheckerSession extends Session { + private static final int SCRIPT_LATIN = 0; + private static final int SCRIPT_CYRILLIC = 1; + private static final TreeMap<String, Integer> mLanguageToScript; + static { + // List of the supported languages and their associated script. We won't check + // words written in another script than the selected script, because we know we + // don't have those in our dictionary so we will underline everything and we + // will never have any suggestions, so it makes no sense checking them. + mLanguageToScript = new TreeMap<String, Integer>(); + mLanguageToScript.put("en", SCRIPT_LATIN); + mLanguageToScript.put("fr", SCRIPT_LATIN); + mLanguageToScript.put("de", SCRIPT_LATIN); + mLanguageToScript.put("nl", SCRIPT_LATIN); + mLanguageToScript.put("cs", SCRIPT_LATIN); + mLanguageToScript.put("es", SCRIPT_LATIN); + mLanguageToScript.put("it", SCRIPT_LATIN); + mLanguageToScript.put("ru", SCRIPT_CYRILLIC); + } + // Immutable, but need the locale which is not available in the constructor yet private DictionaryPool mDictionaryPool; // Likewise private Locale mLocale; + // Cache this for performance + private int mScript; // One of SCRIPT_LATIN or SCRIPT_CYRILLIC for now. private final AndroidSpellCheckerService mService; @@ -431,38 +452,56 @@ public class AndroidSpellCheckerService extends SpellCheckerService final String localeString = getLocale(); mDictionaryPool = mService.getDictionaryPool(localeString); mLocale = LocaleUtils.constructLocaleFromString(localeString); + final Integer script = mLanguageToScript.get(mLocale.getLanguage()); + if (null == script) { + throw new RuntimeException("We have been called with an unsupported language: \"" + + mLocale.getLanguage() + "\". Framework bug?"); + } + mScript = script; } /* * Returns whether the code point is a letter that makes sense for the specified * locale for this spell checker. * The dictionaries supported by Latin IME are described in res/xml/spellchecker.xml - * and is limited to EFIGS language. - * Hence at the moment this explicitly excludes non-Latin scripts, including CJK - * characters, but also Cyrillic, Arabic or Hebrew characters. - * The locale should be used to rule out inappropriate characters when we support - * spellchecking other languages like Russian. + * and is limited to EFIGS languages and Russian. + * Hence at the moment this explicitly tests for Cyrillic characters or Latin characters + * as appropriate, and explicitly excludes CJK, Arabic and Hebrew characters. */ private static boolean isLetterCheckableByLanguage(final int codePoint, - final Locale locale) { - // Our supported dictionaries (EFIGS) at the moment only includes characters - // in the C0, C1, Latin Extended A and B, IPA extensions unicode blocks. - // As it happens, those are back-to-back in the code range 0x40 to 0x2AF, so - // the below is a very efficient way to test for it. As for the 0-0x3F, it's - // excluded from isLetter anyway. - // TODO: change this to use locale when we support other scripts - return codePoint <= 0x2AF && Character.isLetter(codePoint); + final int script) { + switch (script) { + case SCRIPT_LATIN: + // Our supported latin script dictionaries (EFIGS) at the moment only include + // characters in the C0, C1, Latin Extended A and B, IPA extensions unicode + // blocks. As it happens, those are back-to-back in the code range 0x40 to 0x2AF, + // so the below is a very efficient way to test for it. As for the 0-0x3F, it's + // excluded from isLetter anyway. + return codePoint <= 0x2AF && Character.isLetter(codePoint); + case SCRIPT_CYRILLIC: + // All Cyrillic characters are in the 400~52F block. There are some in the upper + // Unicode range, but they are archaic characters that are not used in modern + // russian and are not used by our dictionary. + return codePoint >= 0x400 && codePoint <= 0x52F && Character.isLetter(codePoint); + default: + // Should never come here + throw new RuntimeException("Impossible value of script: " + script); + } } /** * Finds out whether a particular string should be filtered out of spell checking. * - * This will loosely match URLs, numbers, symbols. + * This will loosely match URLs, numbers, symbols. To avoid always underlining words that + * we know we will never recognize, this accepts a script identifier that should be one + * of the SCRIPT_* constants defined above, to rule out quickly characters from very + * different languages. * * @param text the string to evaluate. + * @param script the identifier for the script this spell checker recognizes * @return true if we should filter this text out, false otherwise */ - private static boolean shouldFilterOut(final String text, final Locale locale) { + private static boolean shouldFilterOut(final String text, final int script) { if (TextUtils.isEmpty(text) || text.length() <= 1) return true; // TODO: check if an equivalent processing can't be done more quickly with a @@ -470,7 +509,7 @@ public class AndroidSpellCheckerService extends SpellCheckerService // Filter by first letter final int firstCodePoint = text.codePointAt(0); // Filter out words that don't start with a letter or an apostrophe - if (!isLetterCheckableByLanguage(firstCodePoint, locale) + if (!isLetterCheckableByLanguage(firstCodePoint, script) && '\'' != firstCodePoint) return true; // Filter contents @@ -483,7 +522,7 @@ public class AndroidSpellCheckerService extends SpellCheckerService // words or a URI - in either case we don't want to spell check that if ('@' == codePoint || '/' == codePoint) return true; - if (isLetterCheckableByLanguage(codePoint, locale)) ++letterCount; + if (isLetterCheckableByLanguage(codePoint, script)) ++letterCount; } // Guestimate heuristic: perform spell checking if at least 3/4 of the characters // in this word are letters @@ -502,7 +541,7 @@ public class AndroidSpellCheckerService extends SpellCheckerService try { final String text = textInfo.getText(); - if (shouldFilterOut(text, mLocale)) { + if (shouldFilterOut(text, mScript)) { DictAndProximity dictInfo = null; try { dictInfo = mDictionaryPool.takeOrGetNull(); |