Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 3e66c635 authored by Dan Zivkovic's avatar Dan Zivkovic
Browse files

Skip decoding for in-vocabulary words.

The spell checker is decoding, and getting multiple sets of suggestions, for
every word it encounters. It even does that for in-vocabulary words, though
it will not underline or show suggestions for in-vocabulary words.

Bug 19987461.

Change-Id: Ie61101fa8ab8917f3f49c77768dbcffd96c1685e
parent ec2891b0
Loading
Loading
Loading
Loading
+30 −36
Original line number Original line Diff line number Diff line
@@ -35,6 +35,7 @@ import com.android.inputmethod.latin.WordComposer;
import com.android.inputmethod.latin.common.Constants;
import com.android.inputmethod.latin.common.Constants;
import com.android.inputmethod.latin.common.LocaleUtils;
import com.android.inputmethod.latin.common.LocaleUtils;
import com.android.inputmethod.latin.common.StringUtils;
import com.android.inputmethod.latin.common.StringUtils;
import com.android.inputmethod.latin.define.DebugFlags;
import com.android.inputmethod.latin.utils.BinaryDictionaryUtils;
import com.android.inputmethod.latin.utils.BinaryDictionaryUtils;
import com.android.inputmethod.latin.utils.ScriptUtils;
import com.android.inputmethod.latin.utils.ScriptUtils;
import com.android.inputmethod.latin.utils.StatsUtils;
import com.android.inputmethod.latin.utils.StatsUtils;
@@ -46,7 +47,6 @@ import java.util.Locale;


public abstract class AndroidWordLevelSpellCheckerSession extends Session {
public abstract class AndroidWordLevelSpellCheckerSession extends Session {
    private static final String TAG = AndroidWordLevelSpellCheckerSession.class.getSimpleName();
    private static final String TAG = AndroidWordLevelSpellCheckerSession.class.getSimpleName();
    private static final boolean DBG = false;


    public final static String[] EMPTY_STRING_ARRAY = new String[0];
    public final static String[] EMPTY_STRING_ARRAY = new String[0];


@@ -227,13 +227,20 @@ public abstract class AndroidWordLevelSpellCheckerSession extends Session {
            final String inText = textInfo.getText();
            final String inText = textInfo.getText();
            final SuggestionsParams cachedSuggestionsParams =
            final SuggestionsParams cachedSuggestionsParams =
                    mSuggestionsCache.getSuggestionsFromCache(inText, ngramContext);
                    mSuggestionsCache.getSuggestionsFromCache(inText, ngramContext);

            if (cachedSuggestionsParams != null) {
            if (cachedSuggestionsParams != null) {
                if (DBG) {
                Log.d(TAG, "onGetSuggestionsInternal() : Cache hit for [" + inText + "]");
                    Log.d(TAG, "Cache hit: " + inText + ", " + cachedSuggestionsParams.mFlags);
                }
                return new SuggestionsInfo(
                return new SuggestionsInfo(
                        cachedSuggestionsParams.mFlags, cachedSuggestionsParams.mSuggestions);
                        cachedSuggestionsParams.mFlags, cachedSuggestionsParams.mSuggestions);
            }
            }

            // If spell checking is impossible, return early.
            if (!mService.hasMainDictionaryForLocale(mLocale)) {
                return AndroidSpellCheckerService.getNotInDictEmptySuggestions(
                        false /* reportAsTypo */);
            }

            // Handle special patterns like email, URI, telephone number.
            final int checkability = getCheckabilityInScript(inText, mScript);
            final int checkability = getCheckabilityInScript(inText, mScript);
            if (CHECKABILITY_CHECKABLE != checkability) {
            if (CHECKABILITY_CHECKABLE != checkability) {
                if (CHECKABILITY_CONTAINS_PERIOD == checkability) {
                if (CHECKABILITY_CONTAINS_PERIOD == checkability) {
@@ -257,20 +264,26 @@ public abstract class AndroidWordLevelSpellCheckerSession extends Session {
                        AndroidSpellCheckerService.getNotInDictEmptySuggestions(
                        AndroidSpellCheckerService.getNotInDictEmptySuggestions(
                                CHECKABILITY_CONTAINS_PERIOD == checkability /* reportAsTypo */);
                                CHECKABILITY_CONTAINS_PERIOD == checkability /* reportAsTypo */);
            }
            }

            // Handle normal words.
            final String text = inText.replaceAll(
            final String text = inText.replaceAll(
                    AndroidSpellCheckerService.APOSTROPHE, AndroidSpellCheckerService.SINGLE_QUOTE);
                    AndroidSpellCheckerService.APOSTROPHE, AndroidSpellCheckerService.SINGLE_QUOTE);
            final int capitalizeType = StringUtils.getCapitalizationType(text);
            final int capitalizeType = StringUtils.getCapitalizationType(text);
            if (!mService.hasMainDictionaryForLocale(mLocale)) {

                return AndroidSpellCheckerService.getNotInDictEmptySuggestions(
            if (isInDictForAnyCapitalization(text, capitalizeType)) {
                        false /* reportAsTypo */);
                Log.i(TAG, "onGetSuggestionsInternal() : [" + text + "] is a valid word");
                return AndroidSpellCheckerService.getInDictEmptySuggestions();
            }
            }
            Log.i(TAG, "onGetSuggestionsInternal() : [" + text + "] is NOT a valid word");

            final Keyboard keyboard = mService.getKeyboardForLocale(mLocale);
            final Keyboard keyboard = mService.getKeyboardForLocale(mLocale);
            if (null == keyboard) {
            if (null == keyboard) {
                Log.d(TAG, "No keyboard for locale: " + mLocale);
                Log.w(TAG, "onGetSuggestionsInternal() : No keyboard for locale: " + mLocale);
                // If there is no keyboard for this locale, don't do any spell-checking.
                // If there is no keyboard for this locale, don't do any spell-checking.
                return AndroidSpellCheckerService.getNotInDictEmptySuggestions(
                return AndroidSpellCheckerService.getNotInDictEmptySuggestions(
                        false /* reportAsTypo */);
                        false /* reportAsTypo */);
            }
            }

            final WordComposer composer = new WordComposer();
            final WordComposer composer = new WordComposer();
            final int[] codePoints = StringUtils.toCodePointArray(text);
            final int[] codePoints = StringUtils.toCodePointArray(text);
            final int[] coordinates;
            final int[] coordinates;
@@ -281,17 +294,15 @@ public abstract class AndroidWordLevelSpellCheckerSession extends Session {
                    mLocale, composer.getComposedDataSnapshot(), ngramContext, keyboard);
                    mLocale, composer.getComposedDataSnapshot(), ngramContext, keyboard);
            final Result result = getResult(capitalizeType, mLocale, suggestionsLimit,
            final Result result = getResult(capitalizeType, mLocale, suggestionsLimit,
                    mService.getRecommendedThreshold(), text, suggestionResults);
                    mService.getRecommendedThreshold(), text, suggestionResults);
            final boolean isInDict = isInDictForAnyCapitalization(text, capitalizeType);
            if (DebugFlags.DEBUG_ENABLED) {
            if (DBG) {
                if (result.mSuggestions != null && result.mSuggestions.length > 0) {
                Log.i(TAG, "Spell checking results for " + text + " with suggestion limit "
                    final StringBuilder builder = new StringBuilder();
                        + suggestionsLimit);
                Log.i(TAG, "IsInDict = " + isInDict);
                Log.i(TAG, "LooksLikeTypo = " + (!isInDict));
                Log.i(TAG, "HasRecommendedSuggestions = " + result.mHasRecommendedSuggestions);
                if (null != result.mSuggestions) {
                    for (String suggestion : result.mSuggestions) {
                    for (String suggestion : result.mSuggestions) {
                        Log.i(TAG, suggestion);
                        builder.append(" [");
                        builder.append(suggestion);
                        builder.append("]");
                    }
                    }
                    Log.i(TAG, "onGetSuggestionsInternal() : Suggestions =" + builder);
                }
                }
            }
            }
            // Handle word not in dictionary.
            // Handle word not in dictionary.
@@ -300,13 +311,10 @@ public abstract class AndroidWordLevelSpellCheckerSession extends Session {
            // to this method.
            // to this method.
            // Also, upon changing the orientation of the device, this is called
            // Also, upon changing the orientation of the device, this is called
            // again for every unique invalid word in the text box.
            // again for every unique invalid word in the text box.
            if (!isInDict) {
            StatsUtils.onInvalidWordIdentification(text);
            StatsUtils.onInvalidWordIdentification(text);
            }


            final int flags =
            final int flags =
                    (isInDict ? SuggestionsInfo.RESULT_ATTR_IN_THE_DICTIONARY
                    SuggestionsInfo.RESULT_ATTR_LOOKS_LIKE_TYPO
                            : SuggestionsInfo.RESULT_ATTR_LOOKS_LIKE_TYPO)
                    | (result.mHasRecommendedSuggestions
                    | (result.mHasRecommendedSuggestions
                            ? SuggestionsInfoCompatUtils
                            ? SuggestionsInfoCompatUtils
                                    .getValueOf_RESULT_ATTR_HAS_RECOMMENDED_SUGGESTIONS()
                                    .getValueOf_RESULT_ATTR_HAS_RECOMMENDED_SUGGESTIONS()
@@ -317,9 +325,6 @@ public abstract class AndroidWordLevelSpellCheckerSession extends Session {
            return retval;
            return retval;
        } catch (RuntimeException e) {
        } catch (RuntimeException e) {
            // Don't kill the keyboard if there is a bug in the spell checker
            // Don't kill the keyboard if there is a bug in the spell checker
            if (DBG) {
                throw e;
            }
            Log.e(TAG, "Exception while spellchecking", e);
            Log.e(TAG, "Exception while spellchecking", e);
            return AndroidSpellCheckerService.getNotInDictEmptySuggestions(
            return AndroidSpellCheckerService.getNotInDictEmptySuggestions(
                    false /* reportAsTypo */);
                    false /* reportAsTypo */);
@@ -342,11 +347,6 @@ public abstract class AndroidWordLevelSpellCheckerSession extends Session {
            return new Result(null /* gatheredSuggestions */,
            return new Result(null /* gatheredSuggestions */,
                    false /* hasRecommendedSuggestions */);
                    false /* hasRecommendedSuggestions */);
        }
        }
        if (DBG) {
            for (final SuggestedWordInfo suggestedWordInfo : suggestionResults) {
                Log.i(TAG, "" + suggestedWordInfo.mScore + " " + suggestedWordInfo.mWord);
            }
        }
        final ArrayList<String> suggestions = new ArrayList<>();
        final ArrayList<String> suggestions = new ArrayList<>();
        for (final SuggestedWordInfo suggestedWordInfo : suggestionResults) {
        for (final SuggestedWordInfo suggestedWordInfo : suggestionResults) {
            final String suggestion;
            final String suggestion;
@@ -373,12 +373,6 @@ public abstract class AndroidWordLevelSpellCheckerSession extends Session {
        final float normalizedScore = BinaryDictionaryUtils.calcNormalizedScore(
        final float normalizedScore = BinaryDictionaryUtils.calcNormalizedScore(
                originalText, bestSuggestion, bestScore);
                originalText, bestSuggestion, bestScore);
        final boolean hasRecommendedSuggestions = (normalizedScore > recommendedThreshold);
        final boolean hasRecommendedSuggestions = (normalizedScore > recommendedThreshold);
        if (DBG) {
            Log.i(TAG, "Best suggestion : " + bestSuggestion + ", score " + bestScore);
            Log.i(TAG, "Normalized score = " + normalizedScore
                    + " (threshold " + recommendedThreshold
                    + ") => hasRecommendedSuggestions = " + hasRecommendedSuggestions);
        }
        return new Result(gatheredSuggestions, hasRecommendedSuggestions);
        return new Result(gatheredSuggestions, hasRecommendedSuggestions);
    }
    }