aboutsummaryrefslogtreecommitdiffstats
path: root/java/src
diff options
context:
space:
mode:
Diffstat (limited to 'java/src')
-rw-r--r--java/src/com/android/inputmethod/latin/LatinIME.java68
-rw-r--r--java/src/com/android/inputmethod/latin/RichInputConnection.java10
-rw-r--r--java/src/com/android/inputmethod/latin/inputlogic/InputLogic.java15
-rw-r--r--java/src/com/android/inputmethod/latin/spellcheck/AndroidSpellCheckerService.java3
-rw-r--r--java/src/com/android/inputmethod/latin/spellcheck/AndroidWordLevelSpellCheckerSession.java6
-rw-r--r--java/src/com/android/inputmethod/latin/utils/ScriptUtils.java79
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?");