diff --git a/java/src/com/android/inputmethod/latin/spellcheck/AndroidSpellCheckerService.java b/java/src/com/android/inputmethod/latin/spellcheck/AndroidSpellCheckerService.java index 26248db75..90398deb2 100644 --- a/java/src/com/android/inputmethod/latin/spellcheck/AndroidSpellCheckerService.java +++ b/java/src/com/android/inputmethod/latin/spellcheck/AndroidSpellCheckerService.java @@ -155,6 +155,10 @@ public final class AndroidSpellCheckerService extends SpellCheckerService onSharedPreferenceChanged(prefs, PREF_USE_CONTACTS_KEY); } + public float getRecommendedThreshold() { + return mRecommendedThreshold; + } + private static String getKeyboardLayoutNameForScript(final int script) { switch (script) { case ScriptUtils.SCRIPT_LATIN: @@ -214,95 +218,6 @@ public final class AndroidSpellCheckerService extends SpellCheckerService EMPTY_STRING_ARRAY); } - public SuggestionsGatherer newSuggestionsGatherer(final String text, int maxLength) { - return new SuggestionsGatherer(text, mRecommendedThreshold, maxLength); - } - - // TODO: remove this class and replace it by storage local to the session. - public static final class SuggestionsGatherer { - public static final class Result { - public final String[] mSuggestions; - public final boolean mHasRecommendedSuggestions; - public Result(final String[] gatheredSuggestions, - final boolean hasRecommendedSuggestions) { - mSuggestions = gatheredSuggestions; - mHasRecommendedSuggestions = hasRecommendedSuggestions; - } - } - - private final ArrayList mSuggestions; - private final ArrayList mScores; - private final String mOriginalText; - private final float mRecommendedThreshold; - private final int mMaxLength; - - SuggestionsGatherer(final String originalText, final float recommendedThreshold, - final int maxLength) { - mOriginalText = originalText; - mRecommendedThreshold = recommendedThreshold; - mMaxLength = maxLength; - mSuggestions = new ArrayList<>(); - mScores = new ArrayList<>(); - } - - public void addResults(final SuggestionResults suggestionResults) { - if (suggestionResults == null) { - return; - } - // suggestionResults is sorted. - for (final SuggestedWordInfo suggestedWordInfo : suggestionResults) { - mSuggestions.add(suggestedWordInfo.mWord); - mScores.add(suggestedWordInfo.mScore); - } - } - - public Result getResults(final int capitalizeType, final Locale locale) { - final String[] gatheredSuggestions; - final boolean hasRecommendedSuggestions; - if (mSuggestions.isEmpty()) { - gatheredSuggestions = null; - hasRecommendedSuggestions = false; - } else { - if (DBG) { - for (int i = 0; i < mSuggestions.size(); i++) { - Log.i(TAG, "" + mScores.get(i) + " " + mSuggestions.get(i)); - } - } - StringUtils.removeDupes(mSuggestions); - if (StringUtils.CAPITALIZE_ALL == capitalizeType) { - for (int i = 0; i < mSuggestions.size(); ++i) { - // get(i) returns a CharSequence which is actually a String so .toString() - // should return the same object. - mSuggestions.set(i, mSuggestions.get(i).toString().toUpperCase(locale)); - } - } else if (StringUtils.CAPITALIZE_FIRST == capitalizeType) { - for (int i = 0; i < mSuggestions.size(); ++i) { - // Likewise - mSuggestions.set(i, StringUtils.capitalizeFirstCodePoint( - mSuggestions.get(i).toString(), locale)); - } - } - // This returns a String[], while toArray() returns an Object[] which cannot be cast - // into a String[]. - gatheredSuggestions = mSuggestions.toArray(EMPTY_STRING_ARRAY); - - final int bestScore = mScores.get(0); - final String bestSuggestion = mSuggestions.get(0); - final float normalizedScore = - BinaryDictionaryUtils.calcNormalizedScore( - mOriginalText, bestSuggestion.toString(), bestScore); - hasRecommendedSuggestions = (normalizedScore > mRecommendedThreshold); - if (DBG) { - Log.i(TAG, "Best suggestion : " + bestSuggestion + ", score " + bestScore); - Log.i(TAG, "Normalized score = " + normalizedScore - + " (threshold " + mRecommendedThreshold - + ") => hasRecommendedSuggestions = " + hasRecommendedSuggestions); - } - } - return new Result(gatheredSuggestions, hasRecommendedSuggestions); - } - } - public boolean isValidWord(final Locale locale, final String word) { mSemaphore.acquireUninterruptibly(); try { diff --git a/java/src/com/android/inputmethod/latin/spellcheck/AndroidSpellCheckerSession.java b/java/src/com/android/inputmethod/latin/spellcheck/AndroidSpellCheckerSession.java index 6bfd354ea..38d720664 100644 --- a/java/src/com/android/inputmethod/latin/spellcheck/AndroidSpellCheckerSession.java +++ b/java/src/com/android/inputmethod/latin/spellcheck/AndroidSpellCheckerSession.java @@ -32,7 +32,6 @@ import java.util.Locale; public final class AndroidSpellCheckerSession extends AndroidWordLevelSpellCheckerSession { private static final String TAG = AndroidSpellCheckerSession.class.getSimpleName(); private static final boolean DBG = false; - private final static String[] EMPTY_STRING_ARRAY = new String[0]; private final Resources mResources; private SentenceLevelAdapter mSentenceLevelAdapter; diff --git a/java/src/com/android/inputmethod/latin/spellcheck/AndroidWordLevelSpellCheckerSession.java b/java/src/com/android/inputmethod/latin/spellcheck/AndroidWordLevelSpellCheckerSession.java index ca0725e28..d668672aa 100644 --- a/java/src/com/android/inputmethod/latin/spellcheck/AndroidWordLevelSpellCheckerSession.java +++ b/java/src/com/android/inputmethod/latin/spellcheck/AndroidWordLevelSpellCheckerSession.java @@ -34,20 +34,22 @@ import com.android.inputmethod.latin.Constants; import com.android.inputmethod.latin.PrevWordsInfo; import com.android.inputmethod.latin.SuggestedWords.SuggestedWordInfo; import com.android.inputmethod.latin.WordComposer; -import com.android.inputmethod.latin.settings.SettingsValuesForSuggestion; -import com.android.inputmethod.latin.spellcheck.AndroidSpellCheckerService.SuggestionsGatherer; +import com.android.inputmethod.latin.utils.BinaryDictionaryUtils; import com.android.inputmethod.latin.utils.CoordinateUtils; import com.android.inputmethod.latin.utils.LocaleUtils; import com.android.inputmethod.latin.utils.ScriptUtils; import com.android.inputmethod.latin.utils.StringUtils; import com.android.inputmethod.latin.utils.SuggestionResults; +import java.util.ArrayList; import java.util.Locale; public abstract class AndroidWordLevelSpellCheckerSession extends Session { private static final String TAG = AndroidWordLevelSpellCheckerSession.class.getSimpleName(); private static final boolean DBG = false; + public final static String[] EMPTY_STRING_ARRAY = new String[0]; + // Immutable, but not available in the constructor. private Locale mLocale; // Cache this for performance @@ -279,14 +281,11 @@ public abstract class AndroidWordLevelSpellCheckerSession extends Session { proximityInfo = keyboard.getProximityInfo(); } composer.setComposingWord(codePoints, coordinates); + // TODO: Don't gather suggestions if the limit is <= 0 unless necessary final SuggestionResults suggestionResults = mService.getSuggestionResults( mLocale, composer, prevWordsInfo, proximityInfo); - // TODO: Don't gather suggestions if the limit is <= 0 unless necessary - final SuggestionsGatherer suggestionsGatherer = mService.newSuggestionsGatherer( - text, suggestionsLimit); - suggestionsGatherer.addResults(suggestionResults); - final SuggestionsGatherer.Result result = suggestionsGatherer.getResults( - capitalizeType, mLocale); + final Result result = getResult(capitalizeType, mLocale, suggestionsLimit, + mService.getRecommendedThreshold(), text, suggestionResults); isInDict = isInDictForAnyCapitalization(text, capitalizeType); if (DBG) { Log.i(TAG, "Spell checking results for " + text + " with suggestion limit " @@ -324,6 +323,62 @@ public abstract class AndroidWordLevelSpellCheckerSession extends Session { } } + private static final class Result { + public final String[] mSuggestions; + public final boolean mHasRecommendedSuggestions; + public Result(final String[] gatheredSuggestions, + final boolean hasRecommendedSuggestions) { + mSuggestions = gatheredSuggestions; + mHasRecommendedSuggestions = hasRecommendedSuggestions; + } + } + + private static Result getResult(final int capitalizeType, final Locale locale, + final int suggestionsLimit, final float recommendedThreshold, final String originalText, + final SuggestionResults suggestionResults) { + if (suggestionResults.isEmpty() || suggestionsLimit <= 0) { + return new Result(null /* gatheredSuggestions */, + false /* hasRecommendedSuggestions */); + } + if (DBG) { + for (final SuggestedWordInfo suggestedWordInfo : suggestionResults) { + Log.i(TAG, "" + suggestedWordInfo.mScore + " " + suggestedWordInfo.mWord); + } + } + final ArrayList suggestions = new ArrayList<>(); + for (final SuggestedWordInfo suggestedWordInfo : suggestionResults) { + final String suggestion; + if (StringUtils.CAPITALIZE_ALL == capitalizeType) { + suggestion = suggestedWordInfo.mWord.toUpperCase(locale); + } else if (StringUtils.CAPITALIZE_FIRST == capitalizeType) { + suggestion = StringUtils.capitalizeFirstCodePoint( + suggestedWordInfo.mWord, locale); + } else { + suggestion = suggestedWordInfo.mWord; + } + suggestions.add(suggestion); + } + StringUtils.removeDupes(suggestions); + // This returns a String[], while toArray() returns an Object[] which cannot be cast + // into a String[]. + final String[] gatheredSuggestions = + suggestions.subList(0, Math.min(suggestions.size(), suggestionsLimit)) + .toArray(EMPTY_STRING_ARRAY); + + final int bestScore = suggestionResults.first().mScore; + final String bestSuggestion = suggestions.get(0); + final float normalizedScore = BinaryDictionaryUtils.calcNormalizedScore( + originalText, bestSuggestion.toString(), bestScore); + 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); + } + /* * The spell checker acts on its own behalf. That is needed, in particular, to be able to * access the dictionary files, which the provider restricts to the identity of Latin IME.