diff options
Diffstat (limited to 'java/src')
13 files changed, 208 insertions, 139 deletions
diff --git a/java/src/com/android/inputmethod/accessibility/AccessibilityUtils.java b/java/src/com/android/inputmethod/accessibility/AccessibilityUtils.java index 2762a9f25..b0072eebe 100644 --- a/java/src/com/android/inputmethod/accessibility/AccessibilityUtils.java +++ b/java/src/com/android/inputmethod/accessibility/AccessibilityUtils.java @@ -152,12 +152,16 @@ public final class AccessibilityUtils { * will occur when a key is typed. * * @param suggestedWords the list of suggested auto-correction words - * @param typedWord the currently typed word */ - public void setAutoCorrection(final SuggestedWords suggestedWords, final String typedWord) { + public void setAutoCorrection(final SuggestedWords suggestedWords) { if (suggestedWords.mWillAutoCorrect) { mAutoCorrectionWord = suggestedWords.getWord(SuggestedWords.INDEX_OF_AUTO_CORRECTION); - mTypedWord = typedWord; + final SuggestedWords.SuggestedWordInfo typedWordInfo = suggestedWords.mTypedWordInfo; + if (null == typedWordInfo) { + mTypedWord = null; + } else { + mTypedWord = typedWordInfo.mWord; + } } else { mAutoCorrectionWord = null; mTypedWord = null; diff --git a/java/src/com/android/inputmethod/keyboard/Key.java b/java/src/com/android/inputmethod/keyboard/Key.java index 6b2094b9e..06e552e3d 100644 --- a/java/src/com/android/inputmethod/keyboard/Key.java +++ b/java/src/com/android/inputmethod/keyboard/Key.java @@ -341,17 +341,24 @@ public class Key implements Comparable<Key> { // code point nor as a surrogate pair. mLabel = new StringBuilder().appendCodePoint(code).toString(); } else { - mLabel = StringUtils.toUpperCaseOfStringForLocale( - KeySpecParser.getLabel(keySpec), needsToUpcase, localeForUpcasing); + final String label = KeySpecParser.getLabel(keySpec); + mLabel = needsToUpcase + ? StringUtils.toTitleCaseOfKeyLabel(label, localeForUpcasing) + : label; } if ((mLabelFlags & LABEL_FLAGS_DISABLE_HINT_LABEL) != 0) { mHintLabel = null; } else { - mHintLabel = StringUtils.toUpperCaseOfStringForLocale(style.getString(keyAttr, - R.styleable.Keyboard_Key_keyHintLabel), needsToUpcase, localeForUpcasing); + final String hintLabel = style.getString( + keyAttr, R.styleable.Keyboard_Key_keyHintLabel); + mHintLabel = needsToUpcase + ? StringUtils.toTitleCaseOfKeyLabel(hintLabel, localeForUpcasing) + : hintLabel; + } + String outputText = KeySpecParser.getOutputText(keySpec); + if (needsToUpcase) { + outputText = StringUtils.toTitleCaseOfKeyLabel(outputText, localeForUpcasing); } - String outputText = StringUtils.toUpperCaseOfStringForLocale( - KeySpecParser.getOutputText(keySpec), needsToUpcase, localeForUpcasing); // Choose the first letter of the label as primary code if not specified. if (code == CODE_UNSPECIFIED && TextUtils.isEmpty(outputText) && !TextUtils.isEmpty(mLabel)) { @@ -377,12 +384,14 @@ public class Key implements Comparable<Key> { mCode = CODE_OUTPUT_TEXT; } } else { - mCode = StringUtils.toUpperCaseOfCodeForLocale(code, needsToUpcase, localeForUpcasing); + mCode = needsToUpcase ? StringUtils.toTitleCaseOfKeyCode(code, localeForUpcasing) + : code; } final int altCodeInAttr = KeySpecParser.parseCode( style.getString(keyAttr, R.styleable.Keyboard_Key_altCode), CODE_UNSPECIFIED); - final int altCode = StringUtils.toUpperCaseOfCodeForLocale( - altCodeInAttr, needsToUpcase, localeForUpcasing); + final int altCode = needsToUpcase + ? StringUtils.toTitleCaseOfKeyCode(altCodeInAttr, localeForUpcasing) + : altCodeInAttr; mOptionalAttributes = OptionalAttributes.newInstance(outputText, altCode, disabledIconId, visualInsetsLeft, visualInsetsRight); mKeyVisualAttributes = KeyVisualAttributes.newInstance(keyAttr); diff --git a/java/src/com/android/inputmethod/keyboard/MainKeyboardView.java b/java/src/com/android/inputmethod/keyboard/MainKeyboardView.java index cba7ff2a2..06b87bd9a 100644 --- a/java/src/com/android/inputmethod/keyboard/MainKeyboardView.java +++ b/java/src/com/android/inputmethod/keyboard/MainKeyboardView.java @@ -57,7 +57,6 @@ import com.android.inputmethod.latin.RichInputMethodSubtype; import com.android.inputmethod.latin.SuggestedWords; import com.android.inputmethod.latin.common.Constants; import com.android.inputmethod.latin.common.CoordinateUtils; -import com.android.inputmethod.latin.common.StringUtils; import com.android.inputmethod.latin.settings.DebugSettings; import com.android.inputmethod.latin.utils.TypefaceUtils; @@ -874,8 +873,7 @@ public final class MainKeyboardView extends KeyboardView implements DrawingProxy final Locale[] locales = subtype.getLocales(); final String[] languages = new String[locales.length]; for (int i = 0; i < locales.length; ++i) { - languages[i] = StringUtils.toUpperCaseOfStringForLocale( - locales[i].getLanguage(), true /* needsToUpperCase */, Locale.ROOT); + languages[i] = locales[i].getLanguage().toUpperCase(Locale.ROOT); } return TextUtils.join(" / ", languages); } diff --git a/java/src/com/android/inputmethod/keyboard/internal/MoreKeySpec.java b/java/src/com/android/inputmethod/keyboard/internal/MoreKeySpec.java index b1a3887d8..87c96cc0d 100644 --- a/java/src/com/android/inputmethod/keyboard/internal/MoreKeySpec.java +++ b/java/src/com/android/inputmethod/keyboard/internal/MoreKeySpec.java @@ -55,10 +55,11 @@ public final class MoreKeySpec { if (TextUtils.isEmpty(moreKeySpec)) { throw new KeySpecParser.KeySpecParserError("Empty more key spec"); } - mLabel = StringUtils.toUpperCaseOfStringForLocale( - KeySpecParser.getLabel(moreKeySpec), needsToUpperCase, locale); - final int code = StringUtils.toUpperCaseOfCodeForLocale( - KeySpecParser.getCode(moreKeySpec), needsToUpperCase, locale); + final String label = KeySpecParser.getLabel(moreKeySpec); + mLabel = needsToUpperCase ? StringUtils.toTitleCaseOfKeyLabel(label, locale) : label; + final int codeInSpec = KeySpecParser.getCode(moreKeySpec); + final int code = needsToUpperCase ? StringUtils.toTitleCaseOfKeyCode(codeInSpec, locale) + : codeInSpec; if (code == Constants.CODE_UNSPECIFIED) { // Some letter, for example German Eszett (U+00DF: "ß"), has multiple characters // upper case representation ("SS"). @@ -66,8 +67,9 @@ public final class MoreKeySpec { mOutputText = mLabel; } else { mCode = code; - mOutputText = StringUtils.toUpperCaseOfStringForLocale( - KeySpecParser.getOutputText(moreKeySpec), needsToUpperCase, locale); + final String outputText = KeySpecParser.getOutputText(moreKeySpec); + mOutputText = needsToUpperCase + ? StringUtils.toTitleCaseOfKeyLabel(outputText, locale) : outputText; } mIconId = KeySpecParser.getIconId(moreKeySpec); } diff --git a/java/src/com/android/inputmethod/latin/DictionaryFacilitator.java b/java/src/com/android/inputmethod/latin/DictionaryFacilitator.java index d23639a0d..b24fdea55 100644 --- a/java/src/com/android/inputmethod/latin/DictionaryFacilitator.java +++ b/java/src/com/android/inputmethod/latin/DictionaryFacilitator.java @@ -266,6 +266,12 @@ public class DictionaryFacilitator { } final DictionaryGroup newMostProbableDictionaryGroup = findDictionaryGroupWithLocale(mDictionaryGroups, locale); + if (null == newMostProbableDictionaryGroup) { + // It seems this may happen as a race condition; pressing the globe key and space + // in quick succession could commit a word out of a dictionary that's not in the + // facilitator any more. In this case, just not changing things is fine. + return; + } mMostProbableDictionaryGroup.mWeightForTypingInLocale = DictionaryGroup.WEIGHT_FOR_TYPING_IN_NOT_MOST_PROBABLE_LANGUAGE; mMostProbableDictionaryGroup.mWeightForGesturingInLocale = diff --git a/java/src/com/android/inputmethod/latin/LatinIME.java b/java/src/com/android/inputmethod/latin/LatinIME.java index 719656b07..7b7b6d35e 100644 --- a/java/src/com/android/inputmethod/latin/LatinIME.java +++ b/java/src/com/android/inputmethod/latin/LatinIME.java @@ -201,8 +201,9 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen private static final int MSG_RESET_CACHES = 7; private static final int MSG_WAIT_FOR_DICTIONARY_LOAD = 8; private static final int MSG_DEALLOCATE_MEMORY = 9; + private static final int MSG_RESUME_SUGGESTIONS_FOR_START_INPUT = 10; // Update this when adding new messages - private static final int MSG_LAST = MSG_DEALLOCATE_MEMORY; + private static final int MSG_LAST = MSG_RESUME_SUGGESTIONS_FOR_START_INPUT; private static final int ARG1_NOT_GESTURE_INPUT = 0; private static final int ARG1_DISMISS_GESTURE_FLOATING_PREVIEW_TEXT = 1; @@ -257,7 +258,12 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen break; case MSG_RESUME_SUGGESTIONS: latinIme.mInputLogic.restartSuggestionsOnWordTouchedByCursor( - latinIme.mSettings.getCurrent(), + latinIme.mSettings.getCurrent(), false /* forStartInput */, + latinIme.mKeyboardSwitcher.getCurrentKeyboardScriptId()); + break; + case MSG_RESUME_SUGGESTIONS_FOR_START_INPUT: + latinIme.mInputLogic.restartSuggestionsOnWordTouchedByCursor( + latinIme.mSettings.getCurrent(), true /* forStartInput */, latinIme.mKeyboardSwitcher.getCurrentKeyboardScriptId()); break; case MSG_REOPEN_DICTIONARIES: @@ -303,7 +309,8 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen sendMessage(obtainMessage(MSG_REOPEN_DICTIONARIES)); } - public void postResumeSuggestions(final boolean shouldDelay) { + private void postResumeSuggestionsInternal(final boolean shouldDelay, + final boolean forStartInput) { final LatinIME latinIme = getOwnerInstance(); if (latinIme == null) { return; @@ -312,14 +319,25 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen return; } removeMessages(MSG_RESUME_SUGGESTIONS); + removeMessages(MSG_RESUME_SUGGESTIONS_FOR_START_INPUT); + final int message = forStartInput ? MSG_RESUME_SUGGESTIONS_FOR_START_INPUT + : MSG_RESUME_SUGGESTIONS; if (shouldDelay) { - sendMessageDelayed(obtainMessage(MSG_RESUME_SUGGESTIONS), + sendMessageDelayed(obtainMessage(message), mDelayInMillisecondsToUpdateSuggestions); } else { - sendMessage(obtainMessage(MSG_RESUME_SUGGESTIONS)); + sendMessage(obtainMessage(message)); } } + public void postResumeSuggestions(final boolean shouldDelay) { + postResumeSuggestionsInternal(shouldDelay, false /* forStartInput */); + } + + public void postResumeSuggestionsForStartInput(final boolean shouldDelay) { + postResumeSuggestionsInternal(shouldDelay, true /* forStartInput */); + } + public void postResetCaches(final boolean tryResumeSuggestions, final int remainingTries) { removeMessages(MSG_RESET_CACHES); sendMessage(obtainMessage(MSG_RESET_CACHES, tryResumeSuggestions ? 1 : 0, @@ -969,7 +987,7 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen // initialSelStart and initialSelEnd sometimes are lying. Make a best effort to // work around this bug. mInputLogic.mConnection.tryFixLyingCursorPosition(); - mHandler.postResumeSuggestions(true /* shouldDelay */); + mHandler.postResumeSuggestionsForStartInput(true /* shouldDelay */); needToCallLoadKeyboardLater = false; } } else { @@ -1171,9 +1189,13 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen SuggestedWords.getFromApplicationSpecifiedCompletions( applicationSpecifiedCompletions); final SuggestedWords suggestedWords = new SuggestedWords(applicationSuggestedWords, - null /* rawSuggestions */, false /* typedWordValid */, false /* willAutoCorrect */, + null /* rawSuggestions */, + null /* typedWord */, + false /* typedWordValid */, + false /* willAutoCorrect */, false /* isObsoleteSuggestions */, - SuggestedWords.INPUT_STYLE_APPLICATION_SPECIFIED /* inputStyle */); + SuggestedWords.INPUT_STYLE_APPLICATION_SPECIFIED /* inputStyle */, + SuggestedWords.NOT_A_SEQUENCE_NUMBER); // When in fullscreen mode, show completions generated by the application forcibly setSuggestedWords(suggestedWords); } @@ -1615,8 +1637,7 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen } // Cache the auto-correction in accessibility code so we can speak it if the user // touches a key that will insert it. - AccessibilityUtils.getInstance().setAutoCorrection(suggestedWords, - suggestedWords.mTypedWord); + AccessibilityUtils.getInstance().setAutoCorrection(suggestedWords); } // Called from {@link SuggestionStripView} through the {@link SuggestionStripView#Listener} diff --git a/java/src/com/android/inputmethod/latin/PunctuationSuggestions.java b/java/src/com/android/inputmethod/latin/PunctuationSuggestions.java index a65304cd0..555bbc7d4 100644 --- a/java/src/com/android/inputmethod/latin/PunctuationSuggestions.java +++ b/java/src/com/android/inputmethod/latin/PunctuationSuggestions.java @@ -33,10 +33,12 @@ public final class PunctuationSuggestions extends SuggestedWords { private PunctuationSuggestions(final ArrayList<SuggestedWordInfo> punctuationsList) { super(punctuationsList, null /* rawSuggestions */, + null /* typedWord */, false /* typedWordValid */, false /* hasAutoCorrectionCandidate */, false /* isObsoleteSuggestions */, - INPUT_STYLE_NONE /* inputStyle */); + INPUT_STYLE_NONE /* inputStyle */, + SuggestedWords.NOT_A_SEQUENCE_NUMBER); } /** diff --git a/java/src/com/android/inputmethod/latin/RichInputMethodManager.java b/java/src/com/android/inputmethod/latin/RichInputMethodManager.java index a1ac55a20..686c3a4b2 100644 --- a/java/src/com/android/inputmethod/latin/RichInputMethodManager.java +++ b/java/src/com/android/inputmethod/latin/RichInputMethodManager.java @@ -110,7 +110,7 @@ public class RichInputMethodManager { // Initialize additional subtypes. SubtypeLocaleUtils.init(context); - final InputMethodSubtype[] additionalSubtypes = getAdditionalSubtypes(context); + final InputMethodSubtype[] additionalSubtypes = getAdditionalSubtypes(); setAdditionalInputMethodSubtypes(additionalSubtypes); final ConnectivityManager connectivityManager = @@ -119,11 +119,10 @@ public class RichInputMethodManager { mIsNetworkConnected = (info != null && info.isConnected()); } - public InputMethodSubtype[] getAdditionalSubtypes(final Context context) { - SubtypeLocaleUtils.init(context); - final SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context); + public InputMethodSubtype[] getAdditionalSubtypes() { + final SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(mContext); final String prefAdditionalSubtypes = Settings.readPrefAdditionalSubtypes( - prefs, context.getResources()); + prefs, mContext.getResources()); return AdditionalSubtypeUtils.createAdditionalSubtypesArray(prefAdditionalSubtypes); } diff --git a/java/src/com/android/inputmethod/latin/Suggest.java b/java/src/com/android/inputmethod/latin/Suggest.java index ee8d3f8cb..2d0ec42a6 100644 --- a/java/src/com/android/inputmethod/latin/Suggest.java +++ b/java/src/com/android/inputmethod/latin/Suggest.java @@ -32,6 +32,8 @@ import java.util.ArrayList; import java.util.HashMap; import java.util.Locale; +import javax.annotation.Nullable; + /** * This class loads a dictionary and provides a list of suggestions for a given sequence of * characters. This includes corrections and completions. @@ -133,23 +135,26 @@ public final class Suggest { final SettingsValuesForSuggestion settingsValuesForSuggestion, final int inputStyleIfNotPrediction, final boolean isCorrectionEnabled, final int sequenceNumber, final OnGetSuggestedWordsCallback callback) { - final String typedWord = wordComposer.getTypedWord(); - final int trailingSingleQuotesCount = StringUtils.getTrailingSingleQuotesCount(typedWord); + final String typedWordString = wordComposer.getTypedWord(); + final int trailingSingleQuotesCount = + StringUtils.getTrailingSingleQuotesCount(typedWordString); final String consideredWord = trailingSingleQuotesCount > 0 - ? typedWord.substring(0, typedWord.length() - trailingSingleQuotesCount) - : typedWord; + ? typedWordString.substring(0, typedWordString.length() - trailingSingleQuotesCount) + : typedWordString; final SuggestionResults suggestionResults = mDictionaryFacilitator.getSuggestionResults( wordComposer, ngramContext, proximityInfo.getNativeProximityInfo(), settingsValuesForSuggestion, SESSION_ID_TYPING); + final Locale mostProbableLocale = mDictionaryFacilitator.getMostProbableLocale(); final ArrayList<SuggestedWordInfo> suggestionsContainer = getTransformedSuggestedWordInfoList(wordComposer, suggestionResults, trailingSingleQuotesCount, // For transforming suggestions that don't come for any dictionary, we // use the currently most probable locale as it's our best bet. - mDictionaryFacilitator.getMostProbableLocale()); - final boolean didRemoveTypedWord = - SuggestedWordInfo.removeDups(wordComposer.getTypedWord(), suggestionsContainer); + mostProbableLocale); + @Nullable final Dictionary sourceDictionaryOfRemovedWord = + SuggestedWordInfo.removeDupsAndReturnSourceOfTypedWord(wordComposer.getTypedWord(), + mostProbableLocale /* preferredLocale */, suggestionsContainer); final String whitelistedWord = getWhitelistedWordOrNull(suggestionsContainer); final boolean resultsArePredictions = !wordComposer.isComposingWord(); @@ -157,7 +162,7 @@ public final class Suggest { // We allow auto-correction if we have a whitelisted word, or if the word had more than // one char and was not suggested. final boolean allowsToBeAutoCorrected = (null != whitelistedWord) - || (consideredWord.length() > 1 && !didRemoveTypedWord); + || (consideredWord.length() > 1 && (null == sourceDictionaryOfRemovedWord)); final boolean hasAutoCorrection; // If correction is not enabled, we never auto-correct. This is for example for when @@ -206,17 +211,20 @@ public final class Suggest { } } - if (!TextUtils.isEmpty(typedWord)) { - suggestionsContainer.add(0, new SuggestedWordInfo(typedWord, - SuggestedWordInfo.MAX_SCORE, SuggestedWordInfo.KIND_TYPED, - Dictionary.DICTIONARY_USER_TYPED, - SuggestedWordInfo.NOT_AN_INDEX /* indexOfTouchPointOfSecondWord */, - SuggestedWordInfo.NOT_A_CONFIDENCE /* autoCommitFirstWordConfidence */)); + final SuggestedWordInfo typedWordInfo = new SuggestedWordInfo(typedWordString, + SuggestedWordInfo.MAX_SCORE, SuggestedWordInfo.KIND_TYPED, + null == sourceDictionaryOfRemovedWord ? Dictionary.DICTIONARY_USER_TYPED + : sourceDictionaryOfRemovedWord, + SuggestedWordInfo.NOT_AN_INDEX /* indexOfTouchPointOfSecondWord */, + SuggestedWordInfo.NOT_A_CONFIDENCE /* autoCommitFirstWordConfidence */); + if (!TextUtils.isEmpty(typedWordString)) { + suggestionsContainer.add(0, typedWordInfo); } final ArrayList<SuggestedWordInfo> suggestionsList; if (DBG && !suggestionsContainer.isEmpty()) { - suggestionsList = getSuggestionsInfoListWithDebugInfo(typedWord, suggestionsContainer); + suggestionsList = getSuggestionsInfoListWithDebugInfo(typedWordString, + suggestionsContainer); } else { suggestionsList = suggestionsContainer; } @@ -230,7 +238,7 @@ public final class Suggest { inputStyle = inputStyleIfNotPrediction; } callback.onGetSuggestedWords(new SuggestedWords(suggestionsList, - suggestionResults.mRawSuggestions, typedWord, + suggestionResults.mRawSuggestions, typedWordInfo, // TODO: this first argument is lying. If this is a whitelisted word which is an // actual word, it says typedWordValid = false, which looks wrong. We should either // rename the attribute or change the value. @@ -272,7 +280,8 @@ public final class Suggest { final SuggestedWordInfo rejected = suggestionsContainer.remove(0); suggestionsContainer.add(1, rejected); } - SuggestedWordInfo.removeDups(null /* typedWord */, suggestionsContainer); + SuggestedWordInfo.removeDupsAndReturnSourceOfTypedWord(null /* typedWord */, + null /* preferredLocale */, suggestionsContainer); // For some reason some suggestions with MIN_VALUE are making their way here. // TODO: Find a more robust way to detect distracters. @@ -286,12 +295,12 @@ public final class Suggest { // (typedWordValid=true), not as an "auto correct word" (willAutoCorrect=false). // Note that because this method is never used to get predictions, there is no need to // modify inputType such in getSuggestedWordsForNonBatchInput. - final String pseudoTypedWord = suggestionsContainer.isEmpty() ? null - : suggestionsContainer.get(0).mWord; + final SuggestedWordInfo pseudoTypedWordInfo = suggestionsContainer.isEmpty() ? null + : suggestionsContainer.get(0); callback.onGetSuggestedWords(new SuggestedWords(suggestionsContainer, suggestionResults.mRawSuggestions, - pseudoTypedWord, + pseudoTypedWordInfo, true /* typedWordValid */, false /* willAutoCorrect */, false /* isObsoleteSuggestions */, diff --git a/java/src/com/android/inputmethod/latin/SuggestedWords.java b/java/src/com/android/inputmethod/latin/SuggestedWords.java index bddeac495..cbf48f0c0 100644 --- a/java/src/com/android/inputmethod/latin/SuggestedWords.java +++ b/java/src/com/android/inputmethod/latin/SuggestedWords.java @@ -27,8 +27,10 @@ import com.android.inputmethod.latin.define.DebugFlags; import java.util.ArrayList; import java.util.Arrays; import java.util.HashSet; +import java.util.Locale; import javax.annotation.Nonnull; +import javax.annotation.Nullable; public class SuggestedWords { public static final int INDEX_OF_TYPED_WORD = 0; @@ -50,10 +52,12 @@ public class SuggestedWords { private static final ArrayList<SuggestedWordInfo> EMPTY_WORD_INFO_LIST = new ArrayList<>(0); @Nonnull private static final SuggestedWords EMPTY = new SuggestedWords( - EMPTY_WORD_INFO_LIST, null /* rawSuggestions */, false /* typedWordValid */, - false /* willAutoCorrect */, false /* isObsoleteSuggestions */, INPUT_STYLE_NONE); + EMPTY_WORD_INFO_LIST, null /* rawSuggestions */, null /* typedWord */, + false /* typedWordValid */, false /* willAutoCorrect */, + false /* isObsoleteSuggestions */, INPUT_STYLE_NONE, NOT_A_SEQUENCE_NUMBER); - public final String mTypedWord; + @Nullable + public final SuggestedWordInfo mTypedWordInfo; public final boolean mTypedWordValid; // Note: this INCLUDES cases where the word will auto-correct to itself. A good definition // of what this flag means would be "the top suggestion is strong enough to auto-correct", @@ -64,25 +68,14 @@ public class SuggestedWords { // INPUT_STYLE_* constants above. public final int mInputStyle; public final int mSequenceNumber; // Sequence number for auto-commit. + @Nonnull protected final ArrayList<SuggestedWordInfo> mSuggestedWordInfoList; + @Nullable public final ArrayList<SuggestedWordInfo> mRawSuggestions; - public SuggestedWords(final ArrayList<SuggestedWordInfo> suggestedWordInfoList, - final ArrayList<SuggestedWordInfo> rawSuggestions, - final boolean typedWordValid, - final boolean willAutoCorrect, - final boolean isObsoleteSuggestions, - final int inputStyle) { - this(suggestedWordInfoList, rawSuggestions, - (suggestedWordInfoList.isEmpty() || isPrediction(inputStyle)) ? null - : suggestedWordInfoList.get(INDEX_OF_TYPED_WORD).mWord, - typedWordValid, willAutoCorrect, - isObsoleteSuggestions, inputStyle, NOT_A_SEQUENCE_NUMBER); - } - - public SuggestedWords(final ArrayList<SuggestedWordInfo> suggestedWordInfoList, - final ArrayList<SuggestedWordInfo> rawSuggestions, - final String typedWord, + public SuggestedWords(@Nonnull final ArrayList<SuggestedWordInfo> suggestedWordInfoList, + @Nullable final ArrayList<SuggestedWordInfo> rawSuggestions, + @Nullable final SuggestedWordInfo typedWordInfo, final boolean typedWordValid, final boolean willAutoCorrect, final boolean isObsoleteSuggestions, @@ -95,7 +88,7 @@ public class SuggestedWords { mIsObsoleteSuggestions = isObsoleteSuggestions; mInputStyle = inputStyle; mSequenceNumber = sequenceNumber; - mTypedWord = typedWord; + mTypedWordInfo = typedWordInfo; } public boolean isEmpty() { @@ -211,14 +204,12 @@ public class SuggestedWords { // Should get rid of the first one (what the user typed previously) from suggestions // and replace it with what the user currently typed. public static ArrayList<SuggestedWordInfo> getTypedWordAndPreviousSuggestions( - final String typedWord, final SuggestedWords previousSuggestions) { + @Nonnull final SuggestedWordInfo typedWordInfo, + @Nonnull final SuggestedWords previousSuggestions) { final ArrayList<SuggestedWordInfo> suggestionsList = new ArrayList<>(); final HashSet<String> alreadySeen = new HashSet<>(); - suggestionsList.add(new SuggestedWordInfo(typedWord, SuggestedWordInfo.MAX_SCORE, - SuggestedWordInfo.KIND_TYPED, Dictionary.DICTIONARY_USER_TYPED, - SuggestedWordInfo.NOT_AN_INDEX /* indexOfTouchPointOfSecondWord */, - SuggestedWordInfo.NOT_A_CONFIDENCE /* autoCommitFirstWordConfidence */)); - alreadySeen.add(typedWord.toString()); + suggestionsList.add(typedWordInfo); + alreadySeen.add(typedWordInfo.mWord); final int previousSize = previousSuggestions.size(); for (int index = 1; index < previousSize; index++) { final SuggestedWordInfo prevWordInfo = previousSuggestions.getInfo(index); @@ -365,37 +356,53 @@ public class SuggestedWords { } // This will always remove the higher index if a duplicate is found. - public static boolean removeDups(final String typedWord, - ArrayList<SuggestedWordInfo> candidates) { + // Returns null if the typed word is not found. Always return the dictionary for the + // highest suggestion matching the locale if found, otherwise return the dictionary for + // the highest suggestion. + @Nullable + public static Dictionary removeDupsAndReturnSourceOfTypedWord( + @Nullable final String typedWord, + @Nullable final Locale preferredLocale, + @Nonnull ArrayList<SuggestedWordInfo> candidates) { if (candidates.isEmpty()) { - return false; + return null; } - final boolean didRemoveTypedWord; + final Dictionary sourceDictionaryOfTypedWord; if (!TextUtils.isEmpty(typedWord)) { - didRemoveTypedWord = removeSuggestedWordInfoFrom(typedWord, candidates, - -1 /* startIndexExclusive */); + sourceDictionaryOfTypedWord = + removeSuggestedWordInfoFromListAndReturnSourceDictionary(typedWord, + preferredLocale, candidates, -1 /* startIndexExclusive */); } else { - didRemoveTypedWord = false; + sourceDictionaryOfTypedWord = null; } for (int i = 0; i < candidates.size(); ++i) { - removeSuggestedWordInfoFrom(candidates.get(i).mWord, candidates, - i /* startIndexExclusive */); + removeSuggestedWordInfoFromListAndReturnSourceDictionary(candidates.get(i).mWord, + null /* preferredLocale */, candidates, i /* startIndexExclusive */); } - return didRemoveTypedWord; + return sourceDictionaryOfTypedWord; } - private static boolean removeSuggestedWordInfoFrom(final String word, - final ArrayList<SuggestedWordInfo> candidates, final int startIndexExclusive) { - boolean didRemove = false; + @Nullable + private static Dictionary removeSuggestedWordInfoFromListAndReturnSourceDictionary( + @Nonnull final String word, @Nullable final Locale preferredLocale, + @Nonnull final ArrayList<SuggestedWordInfo> candidates, + final int startIndexExclusive) { + Dictionary sourceDictionaryOfTypedWord = null; for (int i = startIndexExclusive + 1; i < candidates.size(); ++i) { final SuggestedWordInfo previous = candidates.get(i); if (word.equals(previous.mWord)) { - didRemove = true; + if (null == sourceDictionaryOfTypedWord + || (null != preferredLocale + && preferredLocale.equals(previous.mSourceDict.mLocale))) { + if (Dictionary.TYPE_USER_HISTORY != previous.mSourceDict.mDictType) { + sourceDictionaryOfTypedWord = previous.mSourceDict; + } + } candidates.remove(i); --i; } } - return didRemove; + return sourceDictionaryOfTypedWord; } } @@ -423,8 +430,10 @@ public class SuggestedWords { info.mSourceDict, SuggestedWordInfo.NOT_AN_INDEX, SuggestedWordInfo.NOT_A_CONFIDENCE)); } - return new SuggestedWords(newSuggestions, null /* rawSuggestions */, mTypedWordValid, - mWillAutoCorrect, mIsObsoleteSuggestions, INPUT_STYLE_TAIL_BATCH); + return new SuggestedWords(newSuggestions, null /* rawSuggestions */, + newSuggestions.isEmpty() ? null : newSuggestions.get(0) /* typedWordInfo */, + mTypedWordValid, mWillAutoCorrect, mIsObsoleteSuggestions, INPUT_STYLE_TAIL_BATCH, + NOT_A_SEQUENCE_NUMBER); } /** diff --git a/java/src/com/android/inputmethod/latin/SystemBroadcastReceiver.java b/java/src/com/android/inputmethod/latin/SystemBroadcastReceiver.java index 123ab208c..982d4c690 100644 --- a/java/src/com/android/inputmethod/latin/SystemBroadcastReceiver.java +++ b/java/src/com/android/inputmethod/latin/SystemBroadcastReceiver.java @@ -69,7 +69,7 @@ public final class SystemBroadcastReceiver extends BroadcastReceiver { // subtypes when the package is replaced. RichInputMethodManager.init(context); final RichInputMethodManager richImm = RichInputMethodManager.getInstance(); - final InputMethodSubtype[] additionalSubtypes = richImm.getAdditionalSubtypes(context); + final InputMethodSubtype[] additionalSubtypes = richImm.getAdditionalSubtypes(); richImm.setAdditionalInputMethodSubtypes(additionalSubtypes); LauncherIconVisibilityManager.updateSetupWizardIconVisibility(context); } else if (Intent.ACTION_BOOT_COMPLETED.equals(intentAction)) { diff --git a/java/src/com/android/inputmethod/latin/inputlogic/InputLogic.java b/java/src/com/android/inputmethod/latin/inputlogic/InputLogic.java index bafea178e..0185a04ef 100644 --- a/java/src/com/android/inputmethod/latin/inputlogic/InputLogic.java +++ b/java/src/com/android/inputmethod/latin/inputlogic/InputLogic.java @@ -255,7 +255,7 @@ public final class InputLogic { handler.postUpdateSuggestionStrip(SuggestedWords.INPUT_STYLE_TYPING); final String text = performSpecificTldProcessingOnTextInput(rawText); if (SpaceState.PHANTOM == mSpaceState) { - promotePhantomSpace(settingsValues); + insertAutomaticSpaceIfOptionsAndTextAllow(settingsValues); } mConnection.commitText(text, 1); StatsUtils.onWordCommitUserTyped(mEnteredText, mWordComposer.isBatchMode()); @@ -322,7 +322,7 @@ public final class InputLogic { final int firstChar = Character.codePointAt(suggestion, 0); if (!settingsValues.isWordSeparator(firstChar) || settingsValues.isUsuallyPrecededBySpace(firstChar)) { - promotePhantomSpace(settingsValues); + insertAutomaticSpaceIfOptionsAndTextAllow(settingsValues); } } @@ -584,7 +584,9 @@ public final class InputLogic { if (candidate.mSourceDict.shouldAutoCommit(candidate)) { final String[] commitParts = candidate.mWord.split(Constants.WORD_SEPARATOR, 2); batchPointers.shift(candidate.mIndexOfTouchPointOfSecondWord); - promotePhantomSpace(settingsValues); + if (SpaceState.PHANTOM == mSpaceState) { + insertAutomaticSpaceIfOptionsAndTextAllow(settingsValues); + } mConnection.commitText(commitParts[0], 0); StatsUtils.onWordCommitUserTyped(commitParts[0], mWordComposer.isBatchMode()); mSpaceState = SpaceState.PHANTOM; @@ -621,11 +623,7 @@ public final class InputLogic { } else { // We can't use suggestedWords.getWord(SuggestedWords.INDEX_OF_TYPED_WORD) // because it may differ from mWordComposer.mTypedWord. - suggestedWordInfo = new SuggestedWordInfo(suggestedWords.mTypedWord, - SuggestedWordInfo.MAX_SCORE, - SuggestedWordInfo.KIND_TYPED, Dictionary.DICTIONARY_USER_TYPED, - SuggestedWordInfo.NOT_AN_INDEX /* indexOfTouchPointOfSecondWord */, - SuggestedWordInfo.NOT_A_CONFIDENCE /* autoCommitFirstWordConfidence */); + suggestedWordInfo = suggestedWords.mTypedWordInfo; } mWordComposer.setAutoCorrection(suggestedWordInfo); } @@ -861,7 +859,7 @@ public final class InputLogic { // Sanity check throw new RuntimeException("Should not be composing here"); } - promotePhantomSpace(settingsValues); + insertAutomaticSpaceIfOptionsAndTextAllow(settingsValues); } if (mWordComposer.isCursorFrontOrMiddleOfComposingWord()) { @@ -972,7 +970,7 @@ public final class InputLogic { } if (needsPrecedingSpace) { - promotePhantomSpace(settingsValues); + insertAutomaticSpaceIfOptionsAndTextAllow(settingsValues); } if (tryPerformDoubleSpacePeriod(event, inputTransaction)) { @@ -1176,14 +1174,13 @@ public final class InputLogic { StatsUtils.onBackspacePressed(totalDeletedLength); } } - if (inputTransaction.mSettingsValues - .isSuggestionsEnabledPerUserSettings() + if (inputTransaction.mSettingsValues.isSuggestionsEnabledPerUserSettings() && inputTransaction.mSettingsValues.mSpacingAndPunctuations .mCurrentLanguageHasSpaces && !mConnection.isCursorFollowedByWordCharacter( inputTransaction.mSettingsValues.mSpacingAndPunctuations)) { restartSuggestionsOnWordTouchedByCursor(inputTransaction.mSettingsValues, - currentKeyboardScriptId); + false /* forStartInput */, currentKeyboardScriptId); } } } @@ -1413,14 +1410,19 @@ public final class InputLogic { new OnGetSuggestedWordsCallback() { @Override public void onGetSuggestedWords(final SuggestedWords suggestedWords) { - final String typedWord = mWordComposer.getTypedWord(); + final String typedWordString = mWordComposer.getTypedWord(); + final SuggestedWordInfo typedWordInfo = new SuggestedWordInfo( + typedWordString, SuggestedWordInfo.MAX_SCORE, + SuggestedWordInfo.KIND_TYPED, Dictionary.DICTIONARY_USER_TYPED, + SuggestedWordInfo.NOT_AN_INDEX /* indexOfTouchPointOfSecondWord */, + SuggestedWordInfo.NOT_A_CONFIDENCE); // Show new suggestions if we have at least one. Otherwise keep the old // suggestions with the new typed word. Exception: if the length of the // typed word is <= 1 (after a deletion typically) we clear old suggestions. - if (suggestedWords.size() > 1 || typedWord.length() <= 1) { + if (suggestedWords.size() > 1 || typedWordString.length() <= 1) { holder.set(suggestedWords); } else { - holder.set(retrieveOlderSuggestions(typedWord, mSuggestedWords)); + holder.set(retrieveOlderSuggestions(typedWordInfo, mSuggestedWords)); } } } @@ -1439,10 +1441,13 @@ public final class InputLogic { * do nothing. * * @param settingsValues the current values of the settings. - * suggestions in the suggestion list. + * @param forStartInput whether we're doing this in answer to starting the input (as opposed + * to a cursor move, for example). In ICS, there is a platform bug that we need to work + * around only when we come here at input start time. */ // TODO: make this private. public void restartSuggestionsOnWordTouchedByCursor(final SettingsValues settingsValues, + final boolean forStartInput, // TODO: remove this argument, put it into settingsValues final int currentKeyboardScriptId) { // HACK: We may want to special-case some apps that exhibit bad behavior in case of @@ -1489,13 +1494,14 @@ public final class InputLogic { final int numberOfCharsInWordBeforeCursor = range.getNumberOfCharsInWordBeforeCursor(); if (numberOfCharsInWordBeforeCursor > expectedCursorPosition) return; final ArrayList<SuggestedWordInfo> suggestions = new ArrayList<>(); - final String typedWord = range.mWord.toString(); - suggestions.add(new SuggestedWordInfo(typedWord, + final String typedWordString = range.mWord.toString(); + final SuggestedWordInfo typedWordInfo = new SuggestedWordInfo(typedWordString, SuggestedWords.MAX_SUGGESTIONS + 1, SuggestedWordInfo.KIND_TYPED, Dictionary.DICTIONARY_USER_TYPED, SuggestedWordInfo.NOT_AN_INDEX /* indexOfTouchPointOfSecondWord */, - SuggestedWordInfo.NOT_A_CONFIDENCE /* autoCommitFirstWordConfidence */)); - if (!isResumableWord(settingsValues, typedWord)) { + SuggestedWordInfo.NOT_A_CONFIDENCE /* autoCommitFirstWordConfidence */); + suggestions.add(typedWordInfo); + if (!isResumableWord(settingsValues, typedWordString)) { mSuggestionStripViewAccessor.setNeutralSuggestionStrip(); return; } @@ -1503,7 +1509,7 @@ public final class InputLogic { for (final SuggestionSpan span : range.getSuggestionSpansAtWord()) { for (final String s : span.getSuggestions()) { ++i; - if (!TextUtils.equals(s, typedWord)) { + if (!TextUtils.equals(s, typedWordString)) { suggestions.add(new SuggestedWordInfo(s, SuggestedWords.MAX_SUGGESTIONS - i, SuggestedWordInfo.KIND_RESUMED, Dictionary.DICTIONARY_RESUMED, @@ -1513,12 +1519,14 @@ public final class InputLogic { } } } - final int[] codePoints = StringUtils.toCodePointArray(typedWord); + final int[] codePoints = StringUtils.toCodePointArray(typedWordString); mWordComposer.setComposingWord(codePoints, mLatinIME.getCoordinatesForCurrentKeyboard(codePoints)); mWordComposer.setCursorPositionWithinWord( - typedWord.codePointCount(0, numberOfCharsInWordBeforeCursor)); - mConnection.maybeMoveTheCursorAroundAndRestoreToWorkaroundABug(); + typedWordString.codePointCount(0, numberOfCharsInWordBeforeCursor)); + if (forStartInput) { + mConnection.maybeMoveTheCursorAroundAndRestoreToWorkaroundABug(); + } mConnection.setComposingRegion(expectedCursorPosition - numberOfCharsInWordBeforeCursor, expectedCursorPosition + range.getNumberOfCharsInWordAfterCursor()); if (suggestions.size() <= 1) { @@ -1537,7 +1545,7 @@ public final class InputLogic { // color of the word in the suggestion strip changes according to this parameter, // and false gives the correct color. final SuggestedWords suggestedWords = new SuggestedWords(suggestions, - null /* rawSuggestions */, typedWord, false /* typedWordValid */, + null /* rawSuggestions */, typedWordInfo, false /* typedWordValid */, false /* willAutoCorrect */, false /* isObsoleteSuggestions */, SuggestedWords.INPUT_STYLE_RECORRECTION, SuggestedWords.NOT_A_SEQUENCE_NUMBER); doShowSuggestionsAndClearAutoCorrectionIndicator(suggestedWords); @@ -1870,20 +1878,21 @@ public final class InputLogic { * Make a {@link com.android.inputmethod.latin.SuggestedWords} object containing a typed word * and obsolete suggestions. * See {@link com.android.inputmethod.latin.SuggestedWords#getTypedWordAndPreviousSuggestions( - * String, com.android.inputmethod.latin.SuggestedWords)}. - * @param typedWord The typed word as a string. + * SuggestedWordInfo, com.android.inputmethod.latin.SuggestedWords)}. + * @param typedWordInfo The typed word as a SuggestedWordInfo. * @param previousSuggestedWords The previously suggested words. * @return Obsolete suggestions with the newly typed word. */ - static SuggestedWords retrieveOlderSuggestions(final String typedWord, + static SuggestedWords retrieveOlderSuggestions(final SuggestedWordInfo typedWordInfo, final SuggestedWords previousSuggestedWords) { final SuggestedWords oldSuggestedWords = previousSuggestedWords.isPunctuationSuggestions() ? SuggestedWords.getEmptyInstance() : previousSuggestedWords; final ArrayList<SuggestedWords.SuggestedWordInfo> typedWordAndPreviousSuggestions = - SuggestedWords.getTypedWordAndPreviousSuggestions(typedWord, oldSuggestedWords); + SuggestedWords.getTypedWordAndPreviousSuggestions(typedWordInfo, oldSuggestedWords); return new SuggestedWords(typedWordAndPreviousSuggestions, null /* rawSuggestions */, - false /* typedWordValid */, false /* hasAutoCorrectionCandidate */, - true /* isObsoleteSuggestions */, oldSuggestedWords.mInputStyle); + typedWordInfo, false /* typedWordValid */, false /* hasAutoCorrectionCandidate */, + true /* isObsoleteSuggestions */, oldSuggestedWords.mInputStyle, + SuggestedWords.NOT_A_SEQUENCE_NUMBER); } /** @@ -1960,14 +1969,14 @@ public final class InputLogic { } /** - * Promote a phantom space to an actual space. + * Insert an automatic space, if the options allow it. * - * This essentially inserts a space, and that's it. It just checks the options and the text - * before the cursor are appropriate before doing it. + * This checks the options and the text before the cursor are appropriate before inserting + * an automatic space. * * @param settingsValues the current values of the settings. */ - private void promotePhantomSpace(final SettingsValues settingsValues) { + private void insertAutomaticSpaceIfOptionsAndTextAllow(final SettingsValues settingsValues) { if (settingsValues.shouldInsertSpacesAutomatically() && settingsValues.mSpacingAndPunctuations.mCurrentLanguageHasSpaces && !mConnection.textBeforeCursorLooksLikeURL()) { @@ -1990,7 +1999,7 @@ public final class InputLogic { } mConnection.beginBatchEdit(); if (SpaceState.PHANTOM == mSpaceState) { - promotePhantomSpace(settingsValues); + insertAutomaticSpaceIfOptionsAndTextAllow(settingsValues); } final SuggestedWordInfo autoCommitCandidate = mSuggestedWords.getAutoCommitCandidate(); // Commit except the last word for phrase gesture if the top suggestion is eligible for auto diff --git a/java/src/com/android/inputmethod/latin/utils/SubtypeLocaleUtils.java b/java/src/com/android/inputmethod/latin/utils/SubtypeLocaleUtils.java index 013f024c0..0e7f4717d 100644 --- a/java/src/com/android/inputmethod/latin/utils/SubtypeLocaleUtils.java +++ b/java/src/com/android/inputmethod/latin/utils/SubtypeLocaleUtils.java @@ -34,6 +34,7 @@ import java.util.HashMap; import java.util.Locale; import javax.annotation.Nonnull; +import javax.annotation.Nullable; /** * A helper class to deal with subtype locales. @@ -273,7 +274,7 @@ public final class SubtypeLocaleUtils { } @Nonnull - public static String getSubtypeNameForLogging(@Nonnull final InputMethodSubtype subtype) { + public static String getSubtypeNameForLogging(@Nullable final InputMethodSubtype subtype) { if (subtype == null) { return "<null subtype>"; } |