diff options
Diffstat (limited to 'java/src')
6 files changed, 128 insertions, 53 deletions
diff --git a/java/src/com/android/inputmethod/latin/LatinIME.java b/java/src/com/android/inputmethod/latin/LatinIME.java index 865ff07d6..1a76f3b7e 100644 --- a/java/src/com/android/inputmethod/latin/LatinIME.java +++ b/java/src/com/android/inputmethod/latin/LatinIME.java @@ -112,6 +112,8 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen private static final int PENDING_IMS_CALLBACK_DURATION = 800; + private static final int DELAY_WAIT_FOR_DICTIONARY_LOAD = 2000; // 2s + private static final int PERIOD_FOR_AUDIO_AND_HAPTIC_FEEDBACK_IN_KEY_REPEAT = 2; /** @@ -171,8 +173,9 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen private static final int MSG_REOPEN_DICTIONARIES = 5; private static final int MSG_UPDATE_TAIL_BATCH_INPUT_COMPLETED = 6; private static final int MSG_RESET_CACHES = 7; + private static final int MSG_WAIT_FOR_DICTIONARY_LOAD = 8; // Update this when adding new messages - private static final int MSG_LAST = MSG_RESET_CACHES; + private static final int MSG_LAST = MSG_WAIT_FOR_DICTIONARY_LOAD; private static final int ARG1_NOT_GESTURE_INPUT = 0; private static final int ARG1_DISMISS_GESTURE_FLOATING_PREVIEW_TEXT = 1; @@ -234,7 +237,7 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen latinIme.resetSuggest(); // We need to re-evaluate the currently composing word in case the script has // changed. - postResumeSuggestions(true /* shouldIncludeResumedWordInSuggestions */); + postWaitForDictionaryLoad(); break; case MSG_UPDATE_TAIL_BATCH_INPUT_COMPLETED: latinIme.mInputLogic.onUpdateTailBatchInputCompleted( @@ -253,6 +256,9 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen latinIme.getCurrentRecapitalizeState()); } break; + case MSG_WAIT_FOR_DICTIONARY_LOAD: + Log.i(TAG, "Timeout waiting for dictionary load"); + break; } } @@ -264,7 +270,8 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen sendMessage(obtainMessage(MSG_REOPEN_DICTIONARIES)); } - public void postResumeSuggestions(final boolean shouldIncludeResumedWordInSuggestions) { + public void postResumeSuggestions(final boolean shouldIncludeResumedWordInSuggestions, + final boolean shouldDelay) { final LatinIME latinIme = getOwnerInstance(); if (latinIme == null) { return; @@ -274,10 +281,16 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen return; } removeMessages(MSG_RESUME_SUGGESTIONS); - sendMessageDelayed(obtainMessage(MSG_RESUME_SUGGESTIONS, - shouldIncludeResumedWordInSuggestions ? ARG1_TRUE : ARG1_FALSE, - 0 /* ignored */), - mDelayUpdateSuggestions); + if (shouldDelay) { + sendMessageDelayed(obtainMessage(MSG_RESUME_SUGGESTIONS, + shouldIncludeResumedWordInSuggestions ? ARG1_TRUE : ARG1_FALSE, + 0 /* ignored */), + mDelayUpdateSuggestions); + } else { + sendMessage(obtainMessage(MSG_RESUME_SUGGESTIONS, + shouldIncludeResumedWordInSuggestions ? ARG1_TRUE : ARG1_FALSE, + 0 /* ignored */)); + } } public void postResetCaches(final boolean tryResumeSuggestions, final int remainingTries) { @@ -286,6 +299,19 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen remainingTries, null)); } + public void postWaitForDictionaryLoad() { + sendMessageDelayed(obtainMessage(MSG_WAIT_FOR_DICTIONARY_LOAD), + DELAY_WAIT_FOR_DICTIONARY_LOAD); + } + + public void cancelWaitForDictionaryLoad() { + removeMessages(MSG_WAIT_FOR_DICTIONARY_LOAD); + } + + public boolean hasPendingWaitForDictionaryLoad() { + return hasMessages(MSG_WAIT_FOR_DICTIONARY_LOAD); + } + public void cancelUpdateSuggestionStrip() { removeMessages(MSG_UPDATE_SUGGESTION_STRIP); } @@ -582,6 +608,11 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen if (mainKeyboardView != null) { mainKeyboardView.setMainDictionaryAvailability(isMainDictionaryAvailable); } + if (mHandler.hasPendingWaitForDictionaryLoad()) { + mHandler.cancelWaitForDictionaryLoad(); + mHandler.postResumeSuggestions(true /* shouldIncludeResumedWordInSuggestions */, + false /* shouldDelay */); + } } private void resetSuggest() { @@ -821,7 +852,8 @@ 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(true /* shouldIncludeResumedWordInSuggestions */); + mHandler.postResumeSuggestions(true /* shouldIncludeResumedWordInSuggestions */, + true /* shouldDelay */); canReachInputConnection = true; } @@ -1336,6 +1368,17 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen } private void setSuggestedWords(final SuggestedWords suggestedWords) { + if (SuggestedWords.EMPTY != suggestedWords) { + final String autoCorrection; + if (suggestedWords.mWillAutoCorrect) { + autoCorrection = suggestedWords.getWord(SuggestedWords.INDEX_OF_AUTO_CORRECTION); + } else { + // We can't use suggestedWords.getWord(SuggestedWords.INDEX_OF_TYPED_WORD) + // because it may differ from mWordComposer.mTypedWord. + autoCorrection = suggestedWords.mTypedWord; + } + mInputLogic.mWordComposer.setAutoCorrection(autoCorrection); + } mInputLogic.setSuggestedWords(suggestedWords); // TODO: Modify this when we support suggestions with hard keyboard if (!hasSuggestionStripView()) { @@ -1399,18 +1442,9 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen public void showSuggestionStrip(final SuggestedWords sourceSuggestedWords) { final SuggestedWords suggestedWords = sourceSuggestedWords.isEmpty() ? SuggestedWords.EMPTY : sourceSuggestedWords; - final String autoCorrection; - if (suggestedWords.mWillAutoCorrect) { - autoCorrection = suggestedWords.getWord(SuggestedWords.INDEX_OF_AUTO_CORRECTION); - } else { - // We can't use suggestedWords.getWord(SuggestedWords.INDEX_OF_TYPED_WORD) - // because it may differ from mWordComposer.mTypedWord. - autoCorrection = sourceSuggestedWords.mTypedWord; - } if (SuggestedWords.EMPTY == suggestedWords) { setNeutralSuggestionStrip(); } else { - mInputLogic.mWordComposer.setAutoCorrection(autoCorrection); setSuggestedWords(suggestedWords); } // Cache the auto-correction in accessibility code so we can speak it if the user diff --git a/java/src/com/android/inputmethod/latin/RichInputConnection.java b/java/src/com/android/inputmethod/latin/RichInputConnection.java index 5e0dafa57..fdd47a40f 100644 --- a/java/src/com/android/inputmethod/latin/RichInputConnection.java +++ b/java/src/com/android/inputmethod/latin/RichInputConnection.java @@ -30,6 +30,7 @@ import com.android.inputmethod.latin.PrevWordsInfo.WordInfo; import com.android.inputmethod.latin.settings.SpacingAndPunctuations; import com.android.inputmethod.latin.utils.CapsModeUtils; import com.android.inputmethod.latin.utils.DebugLogUtils; +import com.android.inputmethod.latin.utils.ScriptUtils; import com.android.inputmethod.latin.utils.SpannableStringUtils; import com.android.inputmethod.latin.utils.StringUtils; import com.android.inputmethod.latin.utils.TextRange; @@ -623,9 +624,10 @@ public final class RichInputConnection { * Returns the text surrounding the cursor. * * @param sortedSeparators a sorted array of code points that split words. + * @param scriptId the script we consider to be writing words, as one of ScriptUtils.SCRIPT_* * @return a range containing the text surrounding the cursor */ - public TextRange getWordRangeAtCursor(final int[] sortedSeparators) { + public TextRange getWordRangeAtCursor(final int[] sortedSeparators, final int scriptId) { mIC = mParent.getCurrentInputConnection(); if (mIC == null) { return null; @@ -642,7 +644,8 @@ public final class RichInputConnection { int startIndexInBefore = before.length(); while (startIndexInBefore > 0) { final int codePoint = Character.codePointBefore(before, startIndexInBefore); - if (isSeparator(codePoint, sortedSeparators)) { + if (isSeparator(codePoint, sortedSeparators) + || !ScriptUtils.isLetterPartOfScript(codePoint, scriptId)) { break; } --startIndexInBefore; @@ -655,7 +658,8 @@ public final class RichInputConnection { int endIndexInAfter = -1; while (++endIndexInAfter < after.length()) { final int codePoint = Character.codePointAt(after, endIndexInAfter); - if (isSeparator(codePoint, sortedSeparators)) { + if (isSeparator(codePoint, sortedSeparators) + || !ScriptUtils.isLetterPartOfScript(codePoint, scriptId)) { break; } if (Character.isSupplementaryCodePoint(codePoint)) { diff --git a/java/src/com/android/inputmethod/latin/inputlogic/InputLogic.java b/java/src/com/android/inputmethod/latin/inputlogic/InputLogic.java index b9a87c921..7a4b726e8 100644 --- a/java/src/com/android/inputmethod/latin/inputlogic/InputLogic.java +++ b/java/src/com/android/inputmethod/latin/inputlogic/InputLogic.java @@ -362,7 +362,8 @@ 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(false /* shouldIncludeResumedWordInSuggestions */); + mLatinIME.mHandler.postResumeSuggestions(false /* shouldIncludeResumedWordInSuggestions */, + true /* shouldDelay */); // Stop the last recapitalization, if started. mRecapitalizeStatus.stop(); return true; @@ -1288,9 +1289,14 @@ public final class InputLogic { return; } final TextRange range = mConnection.getWordRangeAtCursor( - settingsValues.mSpacingAndPunctuations.mSortedWordSeparators); + settingsValues.mSpacingAndPunctuations.mSortedWordSeparators, + currentKeyboardScriptId); if (null == range) return; // Happens if we don't have an input connection at all - if (range.length() <= 0) return; // Race condition. No text to resume on, so bail out. + if (range.length() <= 0) { + // Race condition, or touching a word in a non-supported script. + mLatinIME.setNeutralSuggestionStrip(); + return; + } // If for some strange reason (editor bug or so) we measure the text before the cursor as // longer than what the entire text is supposed to be, the safe thing to do is bail out. if (range.mHasUrlSpans) return; // If there are links, we don't resume suggestions. Making @@ -1945,7 +1951,8 @@ public final class InputLogic { if (tryResumeSuggestions) { // This is triggered when starting input anew, so we want to include the resumed // word in suggestions. - handler.postResumeSuggestions(true /* shouldIncludeResumedWordInSuggestions */); + handler.postResumeSuggestions(true /* shouldIncludeResumedWordInSuggestions */, + true /* shouldDelay */); } return true; } diff --git a/java/src/com/android/inputmethod/latin/spellcheck/AndroidSpellCheckerService.java b/java/src/com/android/inputmethod/latin/spellcheck/AndroidSpellCheckerService.java index d0316242b..b57eab31b 100644 --- a/java/src/com/android/inputmethod/latin/spellcheck/AndroidSpellCheckerService.java +++ b/java/src/com/android/inputmethod/latin/spellcheck/AndroidSpellCheckerService.java @@ -48,7 +48,6 @@ import java.util.HashSet; import java.util.Iterator; import java.util.Locale; import java.util.Map; -import java.util.TreeMap; /** * Service for spell checking, using LatinIME's dictionaries and mechanisms. @@ -373,7 +372,7 @@ public final class AndroidSpellCheckerService extends SpellCheckerService } public DictAndKeyboard createDictAndKeyboard(final Locale locale) { - final int script = ScriptUtils.getScriptFromLocale(locale); + final int script = ScriptUtils.getScriptFromSpellCheckerLocale(locale); final String keyboardLayoutName = getKeyboardLayoutNameForScript(script); final InputMethodSubtype subtype = AdditionalSubtypeUtils.createAdditionalSubtype( locale.toString(), keyboardLayoutName, null); diff --git a/java/src/com/android/inputmethod/latin/spellcheck/AndroidWordLevelSpellCheckerSession.java b/java/src/com/android/inputmethod/latin/spellcheck/AndroidWordLevelSpellCheckerSession.java index be33f339d..4825b9e2c 100644 --- a/java/src/com/android/inputmethod/latin/spellcheck/AndroidWordLevelSpellCheckerSession.java +++ b/java/src/com/android/inputmethod/latin/spellcheck/AndroidWordLevelSpellCheckerSession.java @@ -117,7 +117,7 @@ public abstract class AndroidWordLevelSpellCheckerSession extends Session { final String localeString = getLocale(); mDictionaryPool = mService.getDictionaryPool(localeString); mLocale = LocaleUtils.constructLocaleFromString(localeString); - mScript = ScriptUtils.getScriptFromLocale(mLocale); + mScript = ScriptUtils.getScriptFromSpellCheckerLocale(mLocale); } @Override @@ -152,7 +152,7 @@ public abstract class AndroidWordLevelSpellCheckerSession extends Session { // Filter by first letter final int firstCodePoint = text.codePointAt(0); // Filter out words that don't start with a letter or an apostrophe - if (!ScriptUtils.isLetterCheckableByScript(firstCodePoint, script) + if (!ScriptUtils.isLetterPartOfScript(firstCodePoint, script) && '\'' != firstCodePoint) return CHECKABILITY_FIRST_LETTER_UNCHECKABLE; // Filter contents @@ -173,7 +173,7 @@ public abstract class AndroidWordLevelSpellCheckerSession extends Session { if (Constants.CODE_PERIOD == codePoint) { return CHECKABILITY_CONTAINS_PERIOD; } - if (ScriptUtils.isLetterCheckableByScript(codePoint, script)) ++letterCount; + if (ScriptUtils.isLetterPartOfScript(codePoint, script)) ++letterCount; } // Guestimate heuristic: perform spell checking if at least 3/4 of the characters // in this word are letters diff --git a/java/src/com/android/inputmethod/latin/utils/ScriptUtils.java b/java/src/com/android/inputmethod/latin/utils/ScriptUtils.java index 9ab7c7778..a76a6dfd7 100644 --- a/java/src/com/android/inputmethod/latin/utils/ScriptUtils.java +++ b/java/src/com/android/inputmethod/latin/utils/ScriptUtils.java @@ -31,7 +31,9 @@ public class ScriptUtils { public static final int SCRIPT_GREEK = 2; public static final int SCRIPT_ARABIC = 3; public static final int SCRIPT_HEBREW = 4; - public static final TreeMap<String, Integer> mLanguageToScript; + public static final int SCRIPT_ARMENIAN = 5; + public static final int SCRIPT_GEORGIAN = 6; + public static final TreeMap<String, Integer> mSpellCheckerLanguageToScript; 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 @@ -41,24 +43,24 @@ public class ScriptUtils { // proximity to pass to the dictionary descent algorithm. // IMPORTANT: this only contains languages - do not write countries in there. // Only the language is searched from the map. - mLanguageToScript = new TreeMap<>(); - mLanguageToScript.put("cs", SCRIPT_LATIN); - mLanguageToScript.put("da", SCRIPT_LATIN); - mLanguageToScript.put("de", SCRIPT_LATIN); - mLanguageToScript.put("el", SCRIPT_GREEK); - mLanguageToScript.put("en", SCRIPT_LATIN); - mLanguageToScript.put("es", SCRIPT_LATIN); - mLanguageToScript.put("fi", SCRIPT_LATIN); - mLanguageToScript.put("fr", SCRIPT_LATIN); - mLanguageToScript.put("hr", SCRIPT_LATIN); - mLanguageToScript.put("it", SCRIPT_LATIN); - mLanguageToScript.put("lt", SCRIPT_LATIN); - mLanguageToScript.put("lv", SCRIPT_LATIN); - mLanguageToScript.put("nb", SCRIPT_LATIN); - mLanguageToScript.put("nl", SCRIPT_LATIN); - mLanguageToScript.put("pt", SCRIPT_LATIN); - mLanguageToScript.put("sl", SCRIPT_LATIN); - mLanguageToScript.put("ru", SCRIPT_CYRILLIC); + mSpellCheckerLanguageToScript = new TreeMap<>(); + mSpellCheckerLanguageToScript.put("cs", SCRIPT_LATIN); + mSpellCheckerLanguageToScript.put("da", SCRIPT_LATIN); + mSpellCheckerLanguageToScript.put("de", SCRIPT_LATIN); + mSpellCheckerLanguageToScript.put("el", SCRIPT_GREEK); + mSpellCheckerLanguageToScript.put("en", SCRIPT_LATIN); + mSpellCheckerLanguageToScript.put("es", SCRIPT_LATIN); + mSpellCheckerLanguageToScript.put("fi", SCRIPT_LATIN); + mSpellCheckerLanguageToScript.put("fr", SCRIPT_LATIN); + mSpellCheckerLanguageToScript.put("hr", SCRIPT_LATIN); + mSpellCheckerLanguageToScript.put("it", SCRIPT_LATIN); + mSpellCheckerLanguageToScript.put("lt", SCRIPT_LATIN); + mSpellCheckerLanguageToScript.put("lv", SCRIPT_LATIN); + mSpellCheckerLanguageToScript.put("nb", SCRIPT_LATIN); + mSpellCheckerLanguageToScript.put("nl", SCRIPT_LATIN); + mSpellCheckerLanguageToScript.put("pt", SCRIPT_LATIN); + mSpellCheckerLanguageToScript.put("sl", SCRIPT_LATIN); + mSpellCheckerLanguageToScript.put("ru", SCRIPT_CYRILLIC); } /* * Returns whether the code point is a letter that makes sense for the specified @@ -68,8 +70,8 @@ public class ScriptUtils { * Hence at the moment this explicitly tests for Cyrillic characters or Latin characters * as appropriate, and explicitly excludes CJK, Arabic and Hebrew characters. */ - public static boolean isLetterCheckableByScript(final int codePoint, final int script) { - switch (script) { + public static boolean isLetterPartOfScript(final int codePoint, final int scriptId) { + switch (scriptId) { 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 @@ -91,16 +93,45 @@ public class ScriptUtils { return (codePoint >= 0x370 && codePoint <= 0x3FF) || (codePoint >= 0x1F00 && codePoint <= 0x1FFF) || codePoint == 0xF2; + case SCRIPT_ARABIC: + // Arabic letters can be in any of the following blocks: + // Arabic U+0600..U+06FF + // Arabic Supplement U+0750..U+077F + // Arabic Extended-A U+08A0..U+08FF + // Arabic Presentation Forms-A U+FB50..U+FDFF + // Arabic Presentation Forms-B U+FE70..U+FEFF + return (codePoint >= 0x600 && codePoint <= 0x6FF) + || (codePoint >= 0x750 && codePoint <= 0x77F) + || (codePoint >= 0x8A0 && codePoint <= 0x8FF) + || (codePoint >= 0xFB50 && codePoint <= 0xFDFF) + || (codePoint >= 0xFE70 && codePoint <= 0xFEFF); + case SCRIPT_HEBREW: + // Hebrew letters are in the Hebrew unicode block, which spans from U+0590 to U+05FF, + // or in the Alphabetic Presentation Forms block, U+FB00..U+FB4F, but only in the + // Hebrew part of that block, which is U+FB1D..U+FB4F. + return (codePoint >= 0x590 && codePoint <= 0x5FF + || codePoint >= 0xFB1D && codePoint <= 0xFB4F); + case SCRIPT_ARMENIAN: + // Armenian letters are in the Armenian unicode block, U+0530..U+058F and + // Alphabetic Presentation Forms block, U+FB00..U+FB4F, but only in the Armenian part + // of that block, which is U+FB13..U+FB17. + return (codePoint >= 0x530 && codePoint <= 0x58F + || codePoint >= 0xFB13 && codePoint <= 0xFB17); + case SCRIPT_GEORGIAN: + // Georgian letters are in the Georgian unicode block, U+10A0..U+10FF, + // or Georgian supplement block, U+2D00..U+2D2F + return (codePoint >= 0x10A0 && codePoint <= 0x10FF + || codePoint >= 0x2D00 && codePoint <= 0x2D2F); case SCRIPT_UNKNOWN: return true; default: // Should never come here - throw new RuntimeException("Impossible value of script: " + script); + throw new RuntimeException("Impossible value of script: " + scriptId); } } - public static int getScriptFromLocale(final Locale locale) { - final Integer script = mLanguageToScript.get(locale.getLanguage()); + public static int getScriptFromSpellCheckerLocale(final Locale locale) { + final Integer script = mSpellCheckerLanguageToScript.get(locale.getLanguage()); if (null == script) { throw new RuntimeException("We have been called with an unsupported language: \"" + locale.getLanguage() + "\". Framework bug?"); |