diff --git a/java/src/com/android/inputmethod/latin/spellcheck/AndroidSpellCheckerService.java b/java/src/com/android/inputmethod/latin/spellcheck/AndroidSpellCheckerService.java index eb6d7c106..503b18b1b 100644 --- a/java/src/com/android/inputmethod/latin/spellcheck/AndroidSpellCheckerService.java +++ b/java/src/com/android/inputmethod/latin/spellcheck/AndroidSpellCheckerService.java @@ -204,10 +204,20 @@ public final class AndroidSpellCheckerService extends SpellCheckerService return AndroidSpellCheckerSessionFactory.newInstance(this); } - public static SuggestionsInfo getNotInDictEmptySuggestions() { - return new SuggestionsInfo(0, EMPTY_STRING_ARRAY); + /** + * Returns an empty SuggestionsInfo with flags signaling the word is not in the dictionary. + * @param reportAsTypo whether this should include the flag LOOKS_LIKE_TYPO, for red underline. + * @return the empty SuggestionsInfo with the appropriate flags set. + */ + public static SuggestionsInfo getNotInDictEmptySuggestions(final boolean reportAsTypo) { + return new SuggestionsInfo(reportAsTypo ? SuggestionsInfo.RESULT_ATTR_LOOKS_LIKE_TYPO : 0, + EMPTY_STRING_ARRAY); } + /** + * Returns an empty suggestionInfo with flags signaling the word is in the dictionary. + * @return the empty SuggestionsInfo with the appropriate flags set. + */ public static SuggestionsInfo getInDictEmptySuggestions() { return new SuggestionsInfo(SuggestionsInfo.RESULT_ATTR_IN_THE_DICTIONARY, EMPTY_STRING_ARRAY); diff --git a/java/src/com/android/inputmethod/latin/spellcheck/AndroidWordLevelSpellCheckerSession.java b/java/src/com/android/inputmethod/latin/spellcheck/AndroidWordLevelSpellCheckerSession.java index 69f9a467f..d6e5b75ad 100644 --- a/java/src/com/android/inputmethod/latin/spellcheck/AndroidWordLevelSpellCheckerSession.java +++ b/java/src/com/android/inputmethod/latin/spellcheck/AndroidWordLevelSpellCheckerSession.java @@ -161,6 +161,12 @@ public abstract class AndroidWordLevelSpellCheckerSession extends Session { } } + private static final int CHECKABILITY_CHECKABLE = 0; + private static final int CHECKABILITY_TOO_MANY_NON_LETTERS = 1; + private static final int CHECKABILITY_CONTAINS_PERIOD = 2; + private static final int CHECKABILITY_EMAIL_OR_URL = 3; + private static final int CHECKABILITY_FIRST_LETTER_UNCHECKABLE = 4; + private static final int CHECKABILITY_TOO_SHORT = 5; /** * Finds out whether a particular string should be filtered out of spell checking. * @@ -171,10 +177,10 @@ public abstract class AndroidWordLevelSpellCheckerSession extends Session { * * @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 + * @return one of the FILTER_OUT_* constants above. */ - private static boolean shouldFilterOut(final String text, final int script) { - if (TextUtils.isEmpty(text) || text.length() <= 1) return true; + private static int getCheckabilityInScript(final String text, final int script) { + if (TextUtils.isEmpty(text) || text.length() <= 1) return CHECKABILITY_TOO_SHORT; // TODO: check if an equivalent processing can't be done more quickly with a // compiled regexp. @@ -182,7 +188,7 @@ public abstract class AndroidWordLevelSpellCheckerSession extends Session { final int firstCodePoint = text.codePointAt(0); // Filter out words that don't start with a letter or an apostrophe if (!isLetterCheckableByLanguage(firstCodePoint, script) - && '\'' != firstCodePoint) return true; + && '\'' != firstCodePoint) return CHECKABILITY_FIRST_LETTER_UNCHECKABLE; // Filter contents final int length = text.length(); @@ -193,13 +199,21 @@ public abstract class AndroidWordLevelSpellCheckerSession extends Session { // Any word containing a SLASH is probably either an ad-hoc combination of two // words or a URI - in either case we don't want to spell check that if (Constants.CODE_COMMERCIAL_AT == codePoint || Constants.CODE_SLASH == codePoint) { - return true; + return CHECKABILITY_EMAIL_OR_URL; + } + // If the string contains a period, native returns strange suggestions (it seems + // to return suggestions for everything up to the period only and to ignore the + // rest), so we suppress lookup if there is a period. + // TODO: investigate why native returns these suggestions and remove this code. + if (Constants.CODE_PERIOD == codePoint) { + return CHECKABILITY_CONTAINS_PERIOD; } if (isLetterCheckableByLanguage(codePoint, script)) ++letterCount; } // Guestimate heuristic: perform spell checking if at least 3/4 of the characters // in this word are letters - return (letterCount * 4 < length * 3); + return (letterCount * 4 < length * 3) + ? CHECKABILITY_TOO_MANY_NON_LETTERS : CHECKABILITY_CHECKABLE; } /** @@ -256,16 +270,20 @@ public abstract class AndroidWordLevelSpellCheckerSession extends Session { cachedSuggestionsParams.mFlags, cachedSuggestionsParams.mSuggestions); } - if (shouldFilterOut(inText, mScript)) { + final int checkability = getCheckabilityInScript(inText, mScript); + if (CHECKABILITY_CHECKABLE != checkability) { DictAndKeyboard dictInfo = null; try { dictInfo = mDictionaryPool.pollWithDefaultTimeout(); if (!DictionaryPool.isAValidDictionary(dictInfo)) { - return AndroidSpellCheckerService.getNotInDictEmptySuggestions(); + return AndroidSpellCheckerService.getNotInDictEmptySuggestions( + false /* reportAsTypo */); } return dictInfo.mDictionary.isValidWord(inText) ? AndroidSpellCheckerService.getInDictEmptySuggestions() - : AndroidSpellCheckerService.getNotInDictEmptySuggestions(); + : AndroidSpellCheckerService.getNotInDictEmptySuggestions( + CHECKABILITY_CONTAINS_PERIOD == checkability + /* reportAsTypo */); } finally { if (null != dictInfo) { if (!mDictionaryPool.offer(dictInfo)) { @@ -290,7 +308,8 @@ public abstract class AndroidWordLevelSpellCheckerSession extends Session { try { dictInfo = mDictionaryPool.pollWithDefaultTimeout(); if (!DictionaryPool.isAValidDictionary(dictInfo)) { - return AndroidSpellCheckerService.getNotInDictEmptySuggestions(); + return AndroidSpellCheckerService.getNotInDictEmptySuggestions( + false /* reportAsTypo */); } final WordComposer composer = new WordComposer(); final int length = text.length(); @@ -351,7 +370,8 @@ public abstract class AndroidWordLevelSpellCheckerSession extends Session { throw e; } else { Log.e(TAG, "Exception while spellcheking", e); - return AndroidSpellCheckerService.getNotInDictEmptySuggestions(); + return AndroidSpellCheckerService.getNotInDictEmptySuggestions( + false /* reportAsTypo */); } } }