diff options
Diffstat (limited to 'java/src/com/android/inputmethod/latin')
15 files changed, 249 insertions, 290 deletions
diff --git a/java/src/com/android/inputmethod/latin/BinaryDictionary.java b/java/src/com/android/inputmethod/latin/BinaryDictionary.java index 096b946d2..7247a1f20 100644 --- a/java/src/com/android/inputmethod/latin/BinaryDictionary.java +++ b/java/src/com/android/inputmethod/latin/BinaryDictionary.java @@ -189,6 +189,7 @@ public final class BinaryDictionary extends Dictionary { private static native void closeNative(long dict); private static native int getFormatVersionNative(long dict); private static native int getProbabilityNative(long dict, int[] word); + private static native int getMaxProbabilityOfExactMatchesNative(long dict, int[] word); private static native int getBigramProbabilityNative(long dict, int[] word0, boolean isBeginningOfSentence, int[] word1); private static native void getWordPropertyNative(long dict, int[] word, @@ -350,11 +351,18 @@ public final class BinaryDictionary extends Dictionary { @Override public int getFrequency(final String word) { - if (word == null) return NOT_A_PROBABILITY; + if (TextUtils.isEmpty(word)) return NOT_A_PROBABILITY; int[] codePoints = StringUtils.toCodePointArray(word); return getProbabilityNative(mNativeDict, codePoints); } + @Override + public int getMaxFrequencyOfExactMatches(final String word) { + if (TextUtils.isEmpty(word)) return NOT_A_PROBABILITY; + int[] codePoints = StringUtils.toCodePointArray(word); + return getMaxProbabilityOfExactMatchesNative(mNativeDict, codePoints); + } + @UsedForTesting public boolean isValidNgram(final PrevWordsInfo prevWordsInfo, final String word) { return getNgramProbability(prevWordsInfo, word) != NOT_A_PROBABILITY; diff --git a/java/src/com/android/inputmethod/latin/Dictionary.java b/java/src/com/android/inputmethod/latin/Dictionary.java index aab16653e..bc7276b9a 100644 --- a/java/src/com/android/inputmethod/latin/Dictionary.java +++ b/java/src/com/android/inputmethod/latin/Dictionary.java @@ -95,6 +95,10 @@ public abstract class Dictionary { return NOT_A_PROBABILITY; } + public int getMaxFrequencyOfExactMatches(final String word) { + return NOT_A_PROBABILITY; + } + /** * Compares the contents of the character array with the typed word and returns true if they * are the same. diff --git a/java/src/com/android/inputmethod/latin/DictionaryCollection.java b/java/src/com/android/inputmethod/latin/DictionaryCollection.java index 53be28139..53c78fd00 100644 --- a/java/src/com/android/inputmethod/latin/DictionaryCollection.java +++ b/java/src/com/android/inputmethod/latin/DictionaryCollection.java @@ -89,9 +89,17 @@ public final class DictionaryCollection extends Dictionary { int maxFreq = -1; for (int i = mDictionaries.size() - 1; i >= 0; --i) { final int tempFreq = mDictionaries.get(i).getFrequency(word); - if (tempFreq >= maxFreq) { - maxFreq = tempFreq; - } + maxFreq = Math.max(tempFreq, maxFreq); + } + return maxFreq; + } + + @Override + public int getMaxFrequencyOfExactMatches(final String word) { + int maxFreq = -1; + for (int i = mDictionaries.size() - 1; i >= 0; --i) { + final int tempFreq = mDictionaries.get(i).getMaxFrequencyOfExactMatches(word); + maxFreq = Math.max(tempFreq, maxFreq); } return maxFreq; } diff --git a/java/src/com/android/inputmethod/latin/DictionaryFacilitator.java b/java/src/com/android/inputmethod/latin/DictionaryFacilitator.java index 7fa3d0479..e8b0be069 100644 --- a/java/src/com/android/inputmethod/latin/DictionaryFacilitator.java +++ b/java/src/com/android/inputmethod/latin/DictionaryFacilitator.java @@ -63,7 +63,7 @@ public class DictionaryFacilitator { private final Object mLock = new Object(); private final DistracterFilter mDistracterFilter; - private static final String[] DICT_TYPES_ORDERED_TO_GET_SUGGESTION = + private static final String[] DICT_TYPES_ORDERED_TO_GET_SUGGESTIONS = new String[] { Dictionary.TYPE_MAIN, Dictionary.TYPE_USER_HISTORY, @@ -89,8 +89,8 @@ public class DictionaryFacilitator { new Class[] { Context.class, Locale.class, File.class }; private static final String[] SUB_DICT_TYPES = - Arrays.copyOfRange(DICT_TYPES_ORDERED_TO_GET_SUGGESTION, 1 /* start */, - DICT_TYPES_ORDERED_TO_GET_SUGGESTION.length); + Arrays.copyOfRange(DICT_TYPES_ORDERED_TO_GET_SUGGESTIONS, 1 /* start */, + DICT_TYPES_ORDERED_TO_GET_SUGGESTIONS.length); /** * Class contains dictionaries for a locale. @@ -333,7 +333,7 @@ public class DictionaryFacilitator { dictionaries = mDictionaries; mDictionaries = new Dictionaries(); } - for (final String dictType : DICT_TYPES_ORDERED_TO_GET_SUGGESTION) { + for (final String dictType : DICT_TYPES_ORDERED_TO_GET_SUGGESTIONS) { dictionaries.closeDict(dictType); } mDistracterFilter.close(); @@ -469,7 +469,7 @@ public class DictionaryFacilitator { final SuggestionResults suggestionResults = new SuggestionResults(dictionaries.mLocale, SuggestedWords.MAX_SUGGESTIONS); final float[] languageWeight = new float[] { Dictionary.NOT_A_LANGUAGE_WEIGHT }; - for (final String dictType : DICT_TYPES_ORDERED_TO_GET_SUGGESTION) { + for (final String dictType : DICT_TYPES_ORDERED_TO_GET_SUGGESTIONS) { final Dictionary dictionary = dictionaries.getDict(dictType); if (null == dictionary) continue; final ArrayList<SuggestedWordInfo> dictionarySuggestions = @@ -502,7 +502,7 @@ public class DictionaryFacilitator { return false; } final String lowerCasedWord = word.toLowerCase(dictionaries.mLocale); - for (final String dictType : DICT_TYPES_ORDERED_TO_GET_SUGGESTION) { + for (final String dictType : DICT_TYPES_ORDERED_TO_GET_SUGGESTIONS) { final Dictionary dictionary = dictionaries.getDict(dictType); // Ideally the passed map would come out of a {@link java.util.concurrent.Future} and // would be immutable once it's finished initializing, but concretely a null test is @@ -516,16 +516,22 @@ public class DictionaryFacilitator { return false; } - public int getFrequency(final String word) { + private int getFrequencyInternal(final String word, + final boolean isGettingMaxFrequencyOfExactMatches) { if (TextUtils.isEmpty(word)) { return Dictionary.NOT_A_PROBABILITY; } - int maxFreq = -1; + int maxFreq = Dictionary.NOT_A_PROBABILITY; final Dictionaries dictionaries = mDictionaries; - for (final String dictType : DICT_TYPES_ORDERED_TO_GET_SUGGESTION) { + for (final String dictType : DICT_TYPES_ORDERED_TO_GET_SUGGESTIONS) { final Dictionary dictionary = dictionaries.getDict(dictType); if (dictionary == null) continue; - final int tempFreq = dictionary.getFrequency(word); + final int tempFreq; + if (isGettingMaxFrequencyOfExactMatches) { + tempFreq = dictionary.getMaxFrequencyOfExactMatches(word); + } else { + tempFreq = dictionary.getFrequency(word); + } if (tempFreq >= maxFreq) { maxFreq = tempFreq; } @@ -533,6 +539,14 @@ public class DictionaryFacilitator { return maxFreq; } + public int getFrequency(final String word) { + return getFrequencyInternal(word, false /* isGettingMaxFrequencyOfExactMatches */); + } + + public int getMaxFrequencyOfExactMatches(final String word) { + return getFrequencyInternal(word, true /* isGettingMaxFrequencyOfExactMatches */); + } + public void clearUserHistoryDictionary() { final ExpandableBinaryDictionary userHistoryDict = mDictionaries.getSubDict(Dictionary.TYPE_USER_HISTORY); diff --git a/java/src/com/android/inputmethod/latin/ExpandableBinaryDictionary.java b/java/src/com/android/inputmethod/latin/ExpandableBinaryDictionary.java index b10bae01a..8664c09e4 100644 --- a/java/src/com/android/inputmethod/latin/ExpandableBinaryDictionary.java +++ b/java/src/com/android/inputmethod/latin/ExpandableBinaryDictionary.java @@ -441,6 +441,30 @@ abstract public class ExpandableBinaryDictionary extends Dictionary { return mBinaryDictionary.isValidWord(word); } + @Override + public int getMaxFrequencyOfExactMatches(final String word) { + reloadDictionaryIfRequired(); + boolean lockAcquired = false; + try { + lockAcquired = mLock.readLock().tryLock( + TIMEOUT_FOR_READ_OPS_IN_MILLISECONDS, TimeUnit.MILLISECONDS); + if (lockAcquired) { + if (mBinaryDictionary == null) { + return NOT_A_PROBABILITY; + } + return mBinaryDictionary.getMaxFrequencyOfExactMatches(word); + } + } catch (final InterruptedException e) { + Log.e(TAG, "Interrupted tryLock() in getMaxFrequencyOfExactMatches().", e); + } finally { + if (lockAcquired) { + mLock.readLock().unlock(); + } + } + return NOT_A_PROBABILITY; + } + + protected boolean isValidNgramLocked(final PrevWordsInfo prevWordsInfo, final String word) { if (mBinaryDictionary == null) return false; return mBinaryDictionary.isValidNgram(prevWordsInfo, word); diff --git a/java/src/com/android/inputmethod/latin/InputAttributes.java b/java/src/com/android/inputmethod/latin/InputAttributes.java index e778a14f6..e1ae3dfe3 100644 --- a/java/src/com/android/inputmethod/latin/InputAttributes.java +++ b/java/src/com/android/inputmethod/latin/InputAttributes.java @@ -16,6 +16,9 @@ package com.android.inputmethod.latin; +import static com.android.inputmethod.latin.Constants.ImeOption.NO_MICROPHONE; +import static com.android.inputmethod.latin.Constants.ImeOption.NO_MICROPHONE_COMPAT; + import android.text.InputType; import android.util.Log; import android.view.inputmethod.EditorInfo; @@ -35,12 +38,17 @@ public final class InputAttributes { final public String mTargetApplicationPackageName; final public boolean mInputTypeNoAutoCorrect; final public boolean mIsPasswordField; - final public boolean mIsSettingsSuggestionStripOn; + final public boolean mShouldShowSuggestions; final public boolean mApplicationSpecifiedCompletionOn; final public boolean mShouldInsertSpacesAutomatically; final private int mInputType; + final private EditorInfo mEditorInfo; + final private String mPackageNameForPrivateImeOptions; - public InputAttributes(final EditorInfo editorInfo, final boolean isFullscreenMode) { + public InputAttributes(final EditorInfo editorInfo, final boolean isFullscreenMode, + final String packageNameForPrivateImeOptions) { + mEditorInfo = editorInfo; + mPackageNameForPrivateImeOptions = packageNameForPrivateImeOptions; mTargetApplicationPackageName = null != editorInfo ? editorInfo.packageName : null; final int inputType = null != editorInfo ? editorInfo.inputType : 0; final int inputClass = inputType & InputType.TYPE_MASK_CLASS; @@ -62,7 +70,7 @@ public final class InputAttributes { Log.w(TAG, String.format("Unexpected input class: inputType=0x%08x" + " imeOptions=0x%08x", inputType, editorInfo.imeOptions)); } - mIsSettingsSuggestionStripOn = false; + mShouldShowSuggestions = false; mInputTypeNoAutoCorrect = false; mApplicationSpecifiedCompletionOn = false; mShouldInsertSpacesAutomatically = false; @@ -81,13 +89,13 @@ public final class InputAttributes { // TODO: Have a helper method in InputTypeUtils // Make sure that passwords are not displayed in {@link SuggestionStripView}. - final boolean noSuggestionStrip = mIsPasswordField + final boolean shouldSuppressSuggestions = mIsPasswordField || InputTypeUtils.isEmailVariation(variation) || InputType.TYPE_TEXT_VARIATION_URI == variation || InputType.TYPE_TEXT_VARIATION_FILTER == variation || flagNoSuggestions || flagAutoComplete; - mIsSettingsSuggestionStripOn = !noSuggestionStrip; + mShouldShowSuggestions = !shouldSuppressSuggestions; mShouldInsertSpacesAutomatically = InputTypeUtils.isAutoSpaceFriendlyType(inputType); @@ -111,6 +119,15 @@ public final class InputAttributes { return editorInfo.inputType == mInputType; } + public boolean hasNoMicrophoneKeyOption() { + @SuppressWarnings("deprecation") + final boolean deprecatedNoMicrophone = InputAttributes.inPrivateImeOptions( + null, NO_MICROPHONE_COMPAT, mEditorInfo); + final boolean noMicrophone = InputAttributes.inPrivateImeOptions( + mPackageNameForPrivateImeOptions, NO_MICROPHONE, mEditorInfo); + return noMicrophone || deprecatedNoMicrophone; + } + @SuppressWarnings("unused") private void dumpFlags(final int inputType) { final int inputClass = inputType & InputType.TYPE_MASK_CLASS; @@ -241,7 +258,7 @@ public final class InputAttributes { mInputType, (mInputTypeNoAutoCorrect ? " noAutoCorrect" : ""), (mIsPasswordField ? " password" : ""), - (mIsSettingsSuggestionStripOn ? " suggestionStrip" : ""), + (mShouldShowSuggestions ? " shouldShowSuggestions" : ""), (mApplicationSpecifiedCompletionOn ? " appSpecified" : ""), (mShouldInsertSpacesAutomatically ? " insertSpaces" : ""), mTargetApplicationPackageName); diff --git a/java/src/com/android/inputmethod/latin/LatinIME.java b/java/src/com/android/inputmethod/latin/LatinIME.java index d329d2ce6..709f1334f 100644 --- a/java/src/com/android/inputmethod/latin/LatinIME.java +++ b/java/src/com/android/inputmethod/latin/LatinIME.java @@ -166,6 +166,8 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen private static final int ARG1_DISMISS_GESTURE_FLOATING_PREVIEW_TEXT = 1; private static final int ARG1_SHOW_GESTURE_FLOATING_PREVIEW_TEXT = 2; private static final int ARG2_UNUSED = 0; + private static final int ARG1_FALSE = 0; + private static final int ARG1_TRUE = 1; private int mDelayUpdateSuggestions; private int mDelayUpdateShiftState; @@ -213,7 +215,7 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen case MSG_RESUME_SUGGESTIONS: latinIme.mInputLogic.restartSuggestionsOnWordTouchedByCursor( latinIme.mSettings.getCurrent(), - false /* includeResumedWordInSuggestions */); + msg.arg1 == ARG1_TRUE /* shouldIncludeResumedWordInSuggestions */); break; case MSG_REOPEN_DICTIONARIES: latinIme.resetSuggest(); @@ -250,7 +252,7 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen sendMessage(obtainMessage(MSG_REOPEN_DICTIONARIES)); } - public void postResumeSuggestions() { + public void postResumeSuggestions(final boolean shouldIncludeResumedWordInSuggestions) { final LatinIME latinIme = getOwnerInstance(); if (latinIme == null) { return; @@ -260,7 +262,10 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen return; } removeMessages(MSG_RESUME_SUGGESTIONS); - sendMessageDelayed(obtainMessage(MSG_RESUME_SUGGESTIONS), mDelayUpdateSuggestions); + sendMessageDelayed(obtainMessage(MSG_RESUME_SUGGESTIONS, + shouldIncludeResumedWordInSuggestions ? ARG1_TRUE : ARG1_FALSE, + 0 /* ignored */), + mDelayUpdateSuggestions); } public void postResetCaches(final boolean tryResumeSuggestions, final int remainingTries) { @@ -522,7 +527,8 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen void loadSettings() { final Locale locale = mSubtypeSwitcher.getCurrentSubtypeLocale(); final EditorInfo editorInfo = getCurrentInputEditorInfo(); - final InputAttributes inputAttributes = new InputAttributes(editorInfo, isFullscreenMode()); + final InputAttributes inputAttributes = new InputAttributes( + editorInfo, isFullscreenMode(), getPackageName()); mSettings.loadSettings(this, locale, inputAttributes); final SettingsValues currentSettingsValues = mSettings.getCurrent(); AudioAndHapticFeedbackManager.getInstance().onSettingsChanged(currentSettingsValues); @@ -602,7 +608,7 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen mDictionaryFacilitator.resetDictionaries(this /* context */, locale, settingsValues.mUseContactsDict, settingsValues.mUsePersonalizedDicts, false /* forceReloadMainDictionary */, this); - if (settingsValues.mCorrectionEnabled) { + if (settingsValues.mAutoCorrectionEnabled) { mInputLogic.mSuggest.setAutoCorrectionThreshold( settingsValues.mAutoCorrectionThreshold); } @@ -640,14 +646,19 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen @Override public void onConfigurationChanged(final Configuration conf) { - // If orientation changed while predicting, commit the change final SettingsValues settingsValues = mSettings.getCurrent(); if (settingsValues.mDisplayOrientation != conf.orientation) { mHandler.startOrientationChanging(); - mInputLogic.mConnection.beginBatchEdit(); - mInputLogic.commitTyped(mSettings.getCurrent(), LastComposedWord.NOT_A_SEPARATOR); - mInputLogic.mConnection.finishComposingText(); - mInputLogic.mConnection.endBatchEdit(); + // If !isComposingWord, #commitTyped() is a no-op, but still, it's better to avoid + // the useless IPC of {begin,end}BatchEdit. + if (mInputLogic.mWordComposer.isComposingWord()) { + mInputLogic.mConnection.beginBatchEdit(); + // If we had a composition in progress, we need to commit the word so that the + // suggestionsSpan will be added. This will allow resuming on the same suggestions + // after rotation is finished. + mInputLogic.commitTyped(mSettings.getCurrent(), LastComposedWord.NOT_A_SEPARATOR); + mInputLogic.mConnection.endBatchEdit(); + } } PersonalizationDictionarySessionRegistrar.onConfigurationChanged(this, conf, mDictionaryFacilitator); @@ -741,6 +752,7 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen } Log.i(TAG, "Starting input. Cursor position = " + editorInfo.initialSelStart + "," + editorInfo.initialSelEnd); + // TODO: Consolidate these checks with {@link InputAttributes}. if (InputAttributes.inPrivateImeOptions(null, NO_MICROPHONE_COMPAT, editorInfo)) { Log.w(TAG, "Deprecated private IME option specified: " + editorInfo.privateImeOptions); Log.w(TAG, "Use " + getPackageName() + "." + NO_MICROPHONE + " instead"); @@ -802,7 +814,7 @@ 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(); + mHandler.postResumeSuggestions(true /* shouldIncludeResumedWordInSuggestions */); canReachInputConnection = true; } @@ -814,7 +826,7 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen mainKeyboardView.closing(); currentSettingsValues = mSettings.getCurrent(); - if (currentSettingsValues.mCorrectionEnabled) { + if (currentSettingsValues.mAutoCorrectionEnabled) { suggest.setAutoCorrectionThreshold( currentSettingsValues.mAutoCorrectionThreshold); } @@ -991,7 +1003,7 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen null /* rawSuggestions */, false /* typedWordValid */, false /* willAutoCorrect */, false /* isObsoleteSuggestions */, false /* isPrediction */); // When in fullscreen mode, show completions generated by the application forcibly - setSuggestedWords(suggestedWords, true /* isSuggestionStripVisible */); + setSuggestedWords(suggestedWords); } private int getAdjustedBackingViewHeight() { @@ -1294,30 +1306,6 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen // Nothing to do so far. } - private boolean isSuggestionStripVisible() { - if (!hasSuggestionStripView()) { - return false; - } - if (mSuggestionStripView.isShowingAddToDictionaryHint()) { - return true; - } - final SettingsValues currentSettings = mSettings.getCurrent(); - if (null == currentSettings) { - return false; - } - if (ImportantNoticeUtils.shouldShowImportantNotice(this, - currentSettings.mInputAttributes)) { - return true; - } - if (!currentSettings.isCurrentOrientationAllowingSuggestionsPerUserSettings()) { - return false; - } - if (currentSettings.isApplicationSpecifiedCompletionsOn()) { - return true; - } - return currentSettings.isSuggestionsRequested(); - } - public boolean hasSuggestionStripView() { return null != mSuggestionStripView; } @@ -1335,9 +1323,7 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen mSuggestionStripView.dismissAddToDictionaryHint(); } - // TODO[IL]: Define a clear interface for this - public void setSuggestedWords(final SuggestedWords suggestedWords, - final boolean isSuggestionStripVisible) { + private void setSuggestedWords(final SuggestedWords suggestedWords) { mInputLogic.setSuggestedWords(suggestedWords); // TODO: Modify this when we support suggestions with hard keyboard if (!hasSuggestionStripView()) { @@ -1346,28 +1332,35 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen if (!onEvaluateInputViewShown()) { return; } - if (!isSuggestionStripVisible) { - mSuggestionStripView.setVisibility(isFullscreenMode() ? View.GONE : View.INVISIBLE); + + final SettingsValues currentSettingsValues = mSettings.getCurrent(); + final boolean shouldShowImportantNotice = + ImportantNoticeUtils.shouldShowImportantNotice(this); + final boolean shouldShowSuggestionsStripUnlessPassword = shouldShowImportantNotice + || currentSettingsValues.mShowsVoiceInputKey + || currentSettingsValues.isSuggestionsRequested() + || currentSettingsValues.isApplicationSpecifiedCompletionsOn(); + final boolean shouldShowSuggestionsStrip = shouldShowSuggestionsStripUnlessPassword + && !currentSettingsValues.mInputAttributes.mIsPasswordField; + mSuggestionStripView.updateVisibility(shouldShowSuggestionsStrip, isFullscreenMode()); + if (!shouldShowSuggestionsStrip) { return; } - mSuggestionStripView.setVisibility(View.VISIBLE); - final SettingsValues currentSettings = mSettings.getCurrent(); - final boolean showSuggestions; - // May show the important notice when there are no suggestions to show, - if (SuggestedWords.EMPTY == suggestedWords - // or the suggestion strip is expected to show punctuation suggestions, + final boolean isEmptyApplicationSpecifiedCompletions = + currentSettingsValues.isApplicationSpecifiedCompletionsOn() + && suggestedWords.isEmpty(); + final boolean noSuggestionsToShow = (SuggestedWords.EMPTY == suggestedWords) || suggestedWords.isPunctuationSuggestions() - // or it's not requested to show suggestions by the input field, - || !currentSettings.isSuggestionsRequested() - // or the "show correction suggestions" settings is off by users preference. - || !currentSettings.isCurrentOrientationAllowingSuggestionsPerUserSettings()) { - showSuggestions = !mSuggestionStripView.maybeShowImportantNoticeTitle( - currentSettings.mInputAttributes); + || isEmptyApplicationSpecifiedCompletions; + final boolean isShowingImportantNotice; + if (shouldShowImportantNotice && noSuggestionsToShow) { + isShowingImportantNotice = mSuggestionStripView.maybeShowImportantNoticeTitle(); } else { - showSuggestions = true; + isShowingImportantNotice = false; } - if (showSuggestions) { + + if (currentSettingsValues.isSuggestionsRequested() && !isShowingImportantNotice) { mSuggestionStripView.setSuggestions(suggestedWords, SubtypeLocaleUtils.isRtlLanguage(mSubtypeSwitcher.getCurrentSubtype())); } @@ -1410,7 +1403,7 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen mInputLogic.mSuggest.getSuggestedWords(mInputLogic.mWordComposer, mInputLogic.mWordComposer.getPrevWordsInfoForSuggestion(), keyboard.getProximityInfo(), currentSettings.mBlockPotentiallyOffensive, - currentSettings.mCorrectionEnabled, additionalFeaturesOptions, sessionId, + currentSettings.mAutoCorrectionEnabled, additionalFeaturesOptions, sessionId, sequenceNumber, callback); } @@ -1430,7 +1423,7 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen setNeutralSuggestionStrip(); } else { mInputLogic.mWordComposer.setAutoCorrection(autoCorrection); - setSuggestedWords(suggestedWords, isSuggestionStripVisible()); + setSuggestedWords(suggestedWords); } // Cache the auto-correction in accessibility code so we can speak it if the user // touches a key that will insert it. @@ -1463,7 +1456,7 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen final SettingsValues currentSettings = mSettings.getCurrent(); final SuggestedWords neutralSuggestions = currentSettings.mBigramPredictionEnabled ? SuggestedWords.EMPTY : currentSettings.mSpacingAndPunctuations.mSuggestPuncList; - setSuggestedWords(neutralSuggestions, isSuggestionStripVisible()); + setSuggestedWords(neutralSuggestions); } // TODO: Make this private diff --git a/java/src/com/android/inputmethod/latin/ReadOnlyBinaryDictionary.java b/java/src/com/android/inputmethod/latin/ReadOnlyBinaryDictionary.java index 8f744bef8..7989346f4 100644 --- a/java/src/com/android/inputmethod/latin/ReadOnlyBinaryDictionary.java +++ b/java/src/com/android/inputmethod/latin/ReadOnlyBinaryDictionary.java @@ -102,6 +102,18 @@ public final class ReadOnlyBinaryDictionary extends Dictionary { } @Override + public int getMaxFrequencyOfExactMatches(final String word) { + if (mLock.readLock().tryLock()) { + try { + return mBinaryDictionary.getMaxFrequencyOfExactMatches(word); + } finally { + mLock.readLock().unlock(); + } + } + return NOT_A_PROBABILITY; + } + + @Override public void close() { mLock.writeLock().lock(); try { diff --git a/java/src/com/android/inputmethod/latin/RichInputConnection.java b/java/src/com/android/inputmethod/latin/RichInputConnection.java index 9d72c64fc..96476b2ee 100644 --- a/java/src/com/android/inputmethod/latin/RichInputConnection.java +++ b/java/src/com/android/inputmethod/latin/RichInputConnection.java @@ -891,4 +891,8 @@ public final class RichInputConnection { public boolean hasSelection() { return mExpectedSelEnd != mExpectedSelStart; } + + public boolean isCursorPositionKnown() { + return INVALID_CURSOR_POSITION != mExpectedSelStart; + } } diff --git a/java/src/com/android/inputmethod/latin/inputlogic/InputLogic.java b/java/src/com/android/inputmethod/latin/inputlogic/InputLogic.java index dbbe1a0c5..c90dc90ce 100644 --- a/java/src/com/android/inputmethod/latin/inputlogic/InputLogic.java +++ b/java/src/com/android/inputmethod/latin/inputlogic/InputLogic.java @@ -319,8 +319,16 @@ public final class InputLogic { || !mWordComposer.isComposingWord(); // safe to reset final boolean hasOrHadSelection = (oldSelStart != oldSelEnd || newSelStart != newSelEnd); final int moveAmount = newSelStart - oldSelStart; - if (selectionChangedOrSafeToReset && (hasOrHadSelection - || !mWordComposer.moveCursorByAndReturnIfInsideComposingWord(moveAmount))) { + // As an added small gift from the framework, it happens upon rotation when there + // is a selection that we get a wrong cursor position delivered to startInput() that + // does not get reflected in the oldSel{Start,End} parameters to the next call to + // onUpdateSelection. In this case, we may have set a composition, and when we're here + // we realize we shouldn't have. In theory, in this case, selectionChangedOrSafeToReset + // should be true, but that is if the framework had taken that wrong cursor position + // into account, which means we have to reset the entire composing state whenever there + // is or was a selection regardless of whether it changed or not. + if (hasOrHadSelection || (selectionChangedOrSafeToReset + && !mWordComposer.moveCursorByAndReturnIfInsideComposingWord(moveAmount))) { // If we are composing a word and moving the cursor, we would want to set a // suggestion span for recorrection to work correctly. Unfortunately, that // would involve the keyboard committing some new text, which would move the @@ -348,7 +356,7 @@ 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(); + mLatinIME.mHandler.postResumeSuggestions(false /* shouldIncludeResumedWordInSuggestions */); // Stop the last recapitalization, if started. mRecapitalizeStatus.stop(); return true; @@ -770,7 +778,7 @@ public final class InputLogic { } // isComposingWord() may have changed since we stored wasComposing if (mWordComposer.isComposingWord()) { - if (settingsValues.mCorrectionEnabled) { + if (settingsValues.mAutoCorrectionEnabled) { final String separator = shouldAvoidSendingCode ? LastComposedWord.NOT_A_SEPARATOR : StringUtils.newSingleCodePointString(codePoint); commitCurrentAutoCorrection(settingsValues, separator, handler); @@ -990,7 +998,7 @@ public final class InputLogic { && !mConnection.isCursorFollowedByWordCharacter( inputTransaction.mSettingsValues.mSpacingAndPunctuations)) { restartSuggestionsOnWordTouchedByCursor(inputTransaction.mSettingsValues, - true /* includeResumedWordInSuggestions */); + true /* shouldIncludeResumedWordInSuggestions */); } } } @@ -1173,7 +1181,7 @@ public final class InputLogic { // If correction is not enabled, we don't add words to the user history dictionary. // That's to avoid unintended additions in some sensitive fields, or fields that // expect to receive non-words. - if (!settingsValues.mCorrectionEnabled) return; + if (!settingsValues.mAutoCorrectionEnabled) return; if (TextUtils.isEmpty(suggestion)) return; final boolean wasAutoCapitalized = @@ -1230,12 +1238,12 @@ public final class InputLogic { * do nothing. * * @param settingsValues the current values of the settings. - * @param includeResumedWordInSuggestions whether to include the word on which we resume + * @param shouldIncludeResumedWordInSuggestions whether to include the word on which we resume * suggestions in the suggestion list. */ // TODO: make this private. public void restartSuggestionsOnWordTouchedByCursor(final SettingsValues settingsValues, - final boolean includeResumedWordInSuggestions) { + final boolean shouldIncludeResumedWordInSuggestions) { // HACK: We may want to special-case some apps that exhibit bad behavior in case of // recorrection. This is a temporary, stopgap measure that will be removed later. // TODO: remove this. @@ -1281,7 +1289,7 @@ public final class InputLogic { if (numberOfCharsInWordBeforeCursor > expectedCursorPosition) return; final ArrayList<SuggestedWordInfo> suggestions = new ArrayList<>(); final String typedWord = range.mWord.toString(); - if (includeResumedWordInSuggestions) { + if (shouldIncludeResumedWordInSuggestions) { suggestions.add(new SuggestedWordInfo(typedWord, SuggestedWords.MAX_SUGGESTIONS + 1, SuggestedWordInfo.KIND_TYPED, Dictionary.DICTIONARY_USER_TYPED, @@ -1319,9 +1327,10 @@ public final class InputLogic { typedWord.codePointCount(0, numberOfCharsInWordBeforeCursor)); mConnection.setComposingRegion(expectedCursorPosition - numberOfCharsInWordBeforeCursor, expectedCursorPosition + range.getNumberOfCharsInWordAfterCursor()); - if (suggestions.isEmpty()) { - // We come here if there weren't any suggestion spans on this word. We will try to - // compute suggestions for it instead. + if (suggestions.size() <= (shouldIncludeResumedWordInSuggestions ? 1 : 0)) { + // If there weren't any suggestion spans on this word, suggestions#size() will be 1 + // if shouldIncludeResumedWordInSuggestions is true, 0 otherwise. In this case, we + // have no useful suggestions, so we will try to compute some for it instead. mInputLogicHandler.getSuggestedWords(Suggest.SESSION_TYPING, SuggestedWords.NOT_A_SEQUENCE_NUMBER, new OnGetSuggestedWordsCallback() { @Override @@ -1329,7 +1338,7 @@ public final class InputLogic { final SuggestedWords suggestedWordsIncludingTypedWord) { final SuggestedWords suggestedWords; if (suggestedWordsIncludingTypedWord.size() > 1 - && !includeResumedWordInSuggestions) { + && !shouldIncludeResumedWordInSuggestions) { // We were able to compute new suggestions for this word. // Remove the typed word, since we don't want to display it in this // case. The #getSuggestedWordsExcludingTypedWord() method sets @@ -1922,9 +1931,11 @@ public final class InputLogic { final boolean tryResumeSuggestions, final int remainingTries, // TODO: remove these arguments final LatinIME.UIHandler handler) { + final boolean shouldFinishComposition = mConnection.hasSelection() + || !mConnection.isCursorPositionKnown(); if (!mConnection.resetCachesUponCursorMoveAndReturnSuccess( mConnection.getExpectedSelectionStart(), mConnection.getExpectedSelectionEnd(), - false /* shouldFinishComposition */)) { + shouldFinishComposition)) { if (0 < remainingTries) { handler.postResetCaches(tryResumeSuggestions, remainingTries - 1); return false; @@ -1934,7 +1945,9 @@ public final class InputLogic { } mConnection.tryFixLyingCursorPosition(); if (tryResumeSuggestions) { - handler.postResumeSuggestions(); + // This is triggered when starting input anew, so we want to include the resumed + // word in suggestions. + handler.postResumeSuggestions(true /* shouldIncludeResumedWordInSuggestions */); } return true; } diff --git a/java/src/com/android/inputmethod/latin/settings/SettingsValues.java b/java/src/com/android/inputmethod/latin/settings/SettingsValues.java index 6c6e79e0f..389d9a869 100644 --- a/java/src/com/android/inputmethod/latin/settings/SettingsValues.java +++ b/java/src/com/android/inputmethod/latin/settings/SettingsValues.java @@ -28,6 +28,7 @@ import com.android.inputmethod.compat.AppWorkaroundsUtils; import com.android.inputmethod.latin.InputAttributes; import com.android.inputmethod.latin.R; import com.android.inputmethod.latin.RichInputMethodManager; +import com.android.inputmethod.latin.SubtypeSwitcher; import com.android.inputmethod.latin.utils.AsyncResultHolder; import com.android.inputmethod.latin.utils.ResourceUtils; import com.android.inputmethod.latin.utils.TargetPackageInfoGetterTask; @@ -84,7 +85,7 @@ public final class SettingsValues { public final int mKeyPreviewPopupDismissDelay; private final boolean mAutoCorrectEnabled; public final float mAutoCorrectionThreshold; - public final boolean mCorrectionEnabled; + public final boolean mAutoCorrectionEnabled; public final int mSuggestionVisibility; public final int mDisplayOrientation; private final AsyncResultHolder<AppWorkaroundsUtils> mAppWorkarounds; @@ -109,7 +110,8 @@ public final class SettingsValues { // Store the input attributes if (null == inputAttributes) { - mInputAttributes = new InputAttributes(null, false /* isFullscreenMode */); + mInputAttributes = new InputAttributes( + null, false /* isFullscreenMode */, context.getPackageName()); } else { mInputAttributes = inputAttributes; } @@ -121,7 +123,10 @@ public final class SettingsValues { mKeyPreviewPopupOn = Settings.readKeyPreviewPopupEnabled(prefs, res); mSlidingKeyInputPreviewEnabled = prefs.getBoolean( DebugSettings.PREF_SLIDING_KEY_INPUT_PREVIEW, true); - mShowsVoiceInputKey = needsToShowVoiceInputKey(prefs, res); + mShowsVoiceInputKey = needsToShowVoiceInputKey(prefs, res) + && !mInputAttributes.mIsPasswordField + && !mInputAttributes.hasNoMicrophoneKeyOption() + && SubtypeSwitcher.getInstance().isShortcutImeEnabled(); final String autoCorrectionThresholdRawValue = prefs.getString( Settings.PREF_AUTO_CORRECTION_THRESHOLD, res.getString(R.string.auto_correction_threshold_mode_index_modest)); @@ -150,7 +155,7 @@ public final class SettingsValues { mGestureFloatingPreviewTextEnabled = prefs.getBoolean( Settings.PREF_GESTURE_FLOATING_PREVIEW_TEXT, true); mPhraseGestureEnabled = Settings.readPhraseGestureEnabled(prefs, res); - mCorrectionEnabled = mAutoCorrectEnabled && !mInputAttributes.mInputTypeNoAutoCorrect; + mAutoCorrectionEnabled = mAutoCorrectEnabled && !mInputAttributes.mInputTypeNoAutoCorrect; final String showSuggestionsSetting = prefs.getString( Settings.PREF_SHOW_SUGGESTIONS_SETTING, res.getString(R.string.prefs_suggestion_visibility_default_value)); @@ -189,8 +194,8 @@ public final class SettingsValues { } public boolean isSuggestionsRequested() { - return mInputAttributes.mIsSettingsSuggestionStripOn - && (mCorrectionEnabled + return mInputAttributes.mShouldShowSuggestions + && (mAutoCorrectionEnabled || isCurrentOrientationAllowingSuggestionsPerUserSettings()); } @@ -318,18 +323,18 @@ public final class SettingsValues { private static boolean needsToShowVoiceInputKey(final SharedPreferences prefs, final Resources res) { - if (!prefs.contains(Settings.PREF_VOICE_INPUT_KEY)) { - // Migrate preference from {@link Settings#PREF_VOICE_MODE_OBSOLETE} to - // {@link Settings#PREF_VOICE_INPUT_KEY}. + // Migrate preference from {@link Settings#PREF_VOICE_MODE_OBSOLETE} to + // {@link Settings#PREF_VOICE_INPUT_KEY}. + if (prefs.contains(Settings.PREF_VOICE_MODE_OBSOLETE)) { final String voiceModeMain = res.getString(R.string.voice_mode_main); final String voiceMode = prefs.getString( Settings.PREF_VOICE_MODE_OBSOLETE, voiceModeMain); final boolean shouldShowVoiceInputKey = voiceModeMain.equals(voiceMode); - prefs.edit().putBoolean(Settings.PREF_VOICE_INPUT_KEY, shouldShowVoiceInputKey).apply(); - } - // Remove the obsolete preference if exists. - if (prefs.contains(Settings.PREF_VOICE_MODE_OBSOLETE)) { - prefs.edit().remove(Settings.PREF_VOICE_MODE_OBSOLETE).apply(); + prefs.edit() + .putBoolean(Settings.PREF_VOICE_INPUT_KEY, shouldShowVoiceInputKey) + // Remove the obsolete preference if exists. + .remove(Settings.PREF_VOICE_MODE_OBSOLETE) + .apply(); } return prefs.getBoolean(Settings.PREF_VOICE_INPUT_KEY, true); } @@ -390,8 +395,8 @@ public final class SettingsValues { sb.append("" + mAutoCorrectEnabled); sb.append("\n mAutoCorrectionThreshold = "); sb.append("" + mAutoCorrectionThreshold); - sb.append("\n mCorrectionEnabled = "); - sb.append("" + mCorrectionEnabled); + sb.append("\n mAutoCorrectionEnabled = "); + sb.append("" + mAutoCorrectionEnabled); sb.append("\n mSuggestionVisibility = "); sb.append("" + mSuggestionVisibility); sb.append("\n mDisplayOrientation = "); diff --git a/java/src/com/android/inputmethod/latin/suggestions/SuggestionStripView.java b/java/src/com/android/inputmethod/latin/suggestions/SuggestionStripView.java index 4a5a7f004..97241498a 100644 --- a/java/src/com/android/inputmethod/latin/suggestions/SuggestionStripView.java +++ b/java/src/com/android/inputmethod/latin/suggestions/SuggestionStripView.java @@ -42,12 +42,12 @@ import com.android.inputmethod.keyboard.MainKeyboardView; import com.android.inputmethod.keyboard.MoreKeysPanel; import com.android.inputmethod.latin.AudioAndHapticFeedbackManager; import com.android.inputmethod.latin.Constants; -import com.android.inputmethod.latin.InputAttributes; import com.android.inputmethod.latin.LatinImeLogger; import com.android.inputmethod.latin.R; import com.android.inputmethod.latin.SuggestedWords; import com.android.inputmethod.latin.SuggestedWords.SuggestedWordInfo; import com.android.inputmethod.latin.settings.Settings; +import com.android.inputmethod.latin.settings.SettingsValues; import com.android.inputmethod.latin.suggestions.MoreSuggestionsView.MoreSuggestionsListener; import com.android.inputmethod.latin.utils.ImportantNoticeUtils; @@ -89,19 +89,17 @@ public final class SuggestionStripView extends RelativeLayout implements OnClick private static class StripVisibilityGroup { private final View mSuggestionStripView; private final View mSuggestionsStrip; - private final View mVoiceKey; private final View mAddToDictionaryStrip; private final View mImportantNoticeStrip; public StripVisibilityGroup(final View suggestionStripView, - final ViewGroup suggestionsStrip, final ImageButton voiceKey, - final ViewGroup addToDictionaryStrip, final View importantNoticeStrip) { + final ViewGroup suggestionsStrip, final ViewGroup addToDictionaryStrip, + final View importantNoticeStrip) { mSuggestionStripView = suggestionStripView; mSuggestionsStrip = suggestionsStrip; - mVoiceKey = voiceKey; mAddToDictionaryStrip = addToDictionaryStrip; mImportantNoticeStrip = importantNoticeStrip; - showSuggestionsStrip(false /* voiceKeyEnabled */); + showSuggestionsStrip(); } public void setLayoutDirection(final boolean isRtlLanguage) { @@ -113,23 +111,20 @@ public final class SuggestionStripView extends RelativeLayout implements OnClick ViewCompat.setLayoutDirection(mImportantNoticeStrip, layoutDirection); } - public void showSuggestionsStrip(final boolean enableVoiceKey) { + public void showSuggestionsStrip() { mSuggestionsStrip.setVisibility(VISIBLE); - mVoiceKey.setVisibility(enableVoiceKey ? VISIBLE : INVISIBLE); mAddToDictionaryStrip.setVisibility(INVISIBLE); mImportantNoticeStrip.setVisibility(INVISIBLE); } public void showAddToDictionaryStrip() { mSuggestionsStrip.setVisibility(INVISIBLE); - mVoiceKey.setVisibility(INVISIBLE); mAddToDictionaryStrip.setVisibility(VISIBLE); mImportantNoticeStrip.setVisibility(INVISIBLE); } - public void showImportantNoticeStrip(final boolean enableVoiceKey) { + public void showImportantNoticeStrip() { mSuggestionsStrip.setVisibility(INVISIBLE); - mVoiceKey.setVisibility(enableVoiceKey ? VISIBLE : INVISIBLE); mAddToDictionaryStrip.setVisibility(INVISIBLE); mImportantNoticeStrip.setVisibility(VISIBLE); } @@ -159,7 +154,7 @@ public final class SuggestionStripView extends RelativeLayout implements OnClick mVoiceKey = (ImageButton)findViewById(R.id.suggestions_strip_voice_key); mAddToDictionaryStrip = (ViewGroup)findViewById(R.id.add_to_dictionary_strip); mImportantNoticeStrip = findViewById(R.id.important_notice_strip); - mStripVisibilityGroup = new StripVisibilityGroup(this, mSuggestionsStrip, mVoiceKey, + mStripVisibilityGroup = new StripVisibilityGroup(this, mSuggestionsStrip, mAddToDictionaryStrip, mImportantNoticeStrip); for (int pos = 0; pos < SuggestedWords.MAX_SUGGESTIONS; pos++) { @@ -207,15 +202,11 @@ public final class SuggestionStripView extends RelativeLayout implements OnClick mMainKeyboardView = (MainKeyboardView)inputView.findViewById(R.id.keyboard_view); } - private boolean isVoiceKeyEnabled() { - if (mMainKeyboardView == null) { - return false; - } - final Keyboard keyboard = mMainKeyboardView.getKeyboard(); - if (keyboard == null) { - return false; - } - return keyboard.mId.mHasShortcutKey; + public void updateVisibility(final boolean shouldBeVisible, final boolean isFullscreenMode) { + final int visibility = shouldBeVisible ? VISIBLE : (isFullscreenMode ? GONE : INVISIBLE); + setVisibility(visibility); + final SettingsValues currentSettingsValues = Settings.getInstance().getCurrent(); + mVoiceKey.setVisibility(currentSettingsValues.mShowsVoiceInputKey ? VISIBLE : INVISIBLE); } public void setSuggestions(final SuggestedWords suggestedWords, final boolean isRtlLanguage) { @@ -224,7 +215,7 @@ public final class SuggestionStripView extends RelativeLayout implements OnClick mSuggestedWords = suggestedWords; mSuggestionsCountInStrip = mLayoutHelper.layoutAndReturnSuggestionCountInStrip( mSuggestedWords, mSuggestionsStrip, this); - mStripVisibilityGroup.showSuggestionsStrip(isVoiceKeyEnabled()); + mStripVisibilityGroup.showSuggestionsStrip(); } public int setMoreSuggestionsHeight(final int remainingHeight) { @@ -255,8 +246,8 @@ public final class SuggestionStripView extends RelativeLayout implements OnClick // This method checks if we should show the important notice (checks on permanent storage if // it has been shown once already or not, and if in the setup wizard). If applicable, it shows // the notice. In all cases, it returns true if it was shown, false otherwise. - public boolean maybeShowImportantNoticeTitle(final InputAttributes inputAttributes) { - if (!ImportantNoticeUtils.shouldShowImportantNotice(getContext(), inputAttributes)) { + public boolean maybeShowImportantNoticeTitle() { + if (!ImportantNoticeUtils.shouldShowImportantNotice(getContext())) { return false; } if (getWidth() <= 0) { @@ -271,7 +262,7 @@ public final class SuggestionStripView extends RelativeLayout implements OnClick dismissMoreSuggestionsPanel(); } mLayoutHelper.layoutImportantNotice(mImportantNoticeStrip, importantNoticeTitle); - mStripVisibilityGroup.showImportantNoticeStrip(isVoiceKeyEnabled()); + mStripVisibilityGroup.showImportantNoticeStrip(); mImportantNoticeStrip.setOnClickListener(this); return true; } @@ -279,7 +270,7 @@ public final class SuggestionStripView extends RelativeLayout implements OnClick public void clear() { mSuggestionsStrip.removeAllViews(); removeAllDebugInfoViews(); - mStripVisibilityGroup.showSuggestionsStrip(false /* enableVoiceKey */); + mStripVisibilityGroup.showSuggestionsStrip(); dismissMoreSuggestionsPanel(); } @@ -481,7 +472,7 @@ public final class SuggestionStripView extends RelativeLayout implements OnClick // Called by the framework when the size is known. Show the important notice if applicable. // This may be overriden by showing suggestions later, if applicable. if (oldw <= 0 && w > 0) { - maybeShowImportantNoticeTitle(Settings.getInstance().getCurrent().mInputAttributes); + maybeShowImportantNoticeTitle(); } } } diff --git a/java/src/com/android/inputmethod/latin/userdictionary/UserDictionaryAddWordContents.java b/java/src/com/android/inputmethod/latin/userdictionary/UserDictionaryAddWordContents.java index 80d4cc44f..eda81940f 100644 --- a/java/src/com/android/inputmethod/latin/userdictionary/UserDictionaryAddWordContents.java +++ b/java/src/com/android/inputmethod/latin/userdictionary/UserDictionaryAddWordContents.java @@ -167,7 +167,9 @@ public class UserDictionaryAddWordContents { // should not insert, because either A. the word exists with no shortcut, in which // case the exact same thing we want to insert is already there, or B. the word // exists with at least one shortcut, in which case it has priority on our word. - if (hasWord(newWord, context)) return CODE_ALREADY_PRESENT; + if (TextUtils.isEmpty(newShortcut) && hasWord(newWord, context)) { + return CODE_ALREADY_PRESENT; + } // Disallow duplicates. If the same word with no shortcut is defined, remove it; if // the same word with the same shortcut is defined, remove it; but we don't mind if diff --git a/java/src/com/android/inputmethod/latin/utils/DistracterFilterUsingSuggestion.java b/java/src/com/android/inputmethod/latin/utils/DistracterFilterUsingSuggestion.java index b9c7f5671..8c3844ed8 100644 --- a/java/src/com/android/inputmethod/latin/utils/DistracterFilterUsingSuggestion.java +++ b/java/src/com/android/inputmethod/latin/utils/DistracterFilterUsingSuggestion.java @@ -16,33 +16,22 @@ package com.android.inputmethod.latin.utils; -import java.util.HashMap; import java.util.List; import java.util.Locale; -import java.util.Map; import java.util.concurrent.TimeUnit; import android.content.Context; -import android.content.res.Resources; -import android.text.InputType; import android.util.Log; -import android.view.inputmethod.EditorInfo; import android.view.inputmethod.InputMethodSubtype; -import com.android.inputmethod.keyboard.Keyboard; -import com.android.inputmethod.keyboard.KeyboardId; -import com.android.inputmethod.keyboard.KeyboardLayoutSet; -import com.android.inputmethod.latin.Constants; -import com.android.inputmethod.latin.Dictionary; import com.android.inputmethod.latin.DictionaryFacilitator; import com.android.inputmethod.latin.PrevWordsInfo; -import com.android.inputmethod.latin.SuggestedWords.SuggestedWordInfo; -import com.android.inputmethod.latin.WordComposer; /** * This class is used to prevent distracters being added to personalization * or user history dictionaries */ +// TODO: Rename. public class DistracterFilterUsingSuggestion implements DistracterFilter { private static final String TAG = DistracterFilterUsingSuggestion.class.getSimpleName(); private static final boolean DEBUG = false; @@ -50,10 +39,7 @@ public class DistracterFilterUsingSuggestion implements DistracterFilter { private static final long TIMEOUT_TO_WAIT_LOADING_DICTIONARIES_IN_SECONDS = 120; private final Context mContext; - private final Map<Locale, InputMethodSubtype> mLocaleToSubtypeMap; - private final Map<Locale, Keyboard> mLocaleToKeyboardMap; private final DictionaryFacilitator mDictionaryFacilitator; - private Keyboard mKeyboard; private final Object mLock = new Object(); /** @@ -63,10 +49,7 @@ public class DistracterFilterUsingSuggestion implements DistracterFilter { */ public DistracterFilterUsingSuggestion(final Context context) { mContext = context; - mLocaleToSubtypeMap = new HashMap<>(); - mLocaleToKeyboardMap = new HashMap<>(); mDictionaryFacilitator = new DictionaryFacilitator(); - mKeyboard = null; } @Override @@ -76,94 +59,6 @@ public class DistracterFilterUsingSuggestion implements DistracterFilter { @Override public void updateEnabledSubtypes(final List<InputMethodSubtype> enabledSubtypes) { - final Map<Locale, InputMethodSubtype> newLocaleToSubtypeMap = new HashMap<>(); - if (enabledSubtypes != null) { - for (final InputMethodSubtype subtype : enabledSubtypes) { - final Locale locale = SubtypeLocaleUtils.getSubtypeLocale(subtype); - if (newLocaleToSubtypeMap.containsKey(locale)) { - // Multiple subtypes are enabled for one locale. - // TODO: Investigate what we should do for this case. - continue; - } - newLocaleToSubtypeMap.put(locale, subtype); - } - } - if (mLocaleToSubtypeMap.equals(newLocaleToSubtypeMap)) { - // Enabled subtypes have not been changed. - return; - } - synchronized (mLock) { - mLocaleToSubtypeMap.clear(); - mLocaleToSubtypeMap.putAll(newLocaleToSubtypeMap); - mLocaleToKeyboardMap.clear(); - } - } - - private boolean isDistracter( - final SuggestionResults suggestionResults, final String consideredWord) { - int perfectMatchProbability = Dictionary.NOT_A_PROBABILITY; - for (final SuggestedWordInfo suggestedWordInfo : suggestionResults) { - if (suggestedWordInfo.mWord.equals(consideredWord)) { - perfectMatchProbability = mDictionaryFacilitator.getFrequency(consideredWord); - continue; - } - // Exact match can include case errors, accent errors, digraph conversions. - final boolean isExactMatch = suggestedWordInfo.isExactMatch(); - final boolean isExactMatchWithIntentionalOmission = - suggestedWordInfo.isExactMatchWithIntentionalOmission(); - - if (DEBUG) { - final float normalizedScore = BinaryDictionaryUtils.calcNormalizedScore( - consideredWord, suggestedWordInfo.mWord, suggestedWordInfo.mScore); - Log.d(TAG, "consideredWord: " + consideredWord); - Log.d(TAG, "top suggestion: " + suggestedWordInfo.mWord); - Log.d(TAG, "suggestionScore: " + suggestedWordInfo.mScore); - Log.d(TAG, "normalizedScore: " + normalizedScore); - Log.d(TAG, "isExactMatch: " + isExactMatch); - Log.d(TAG, "isExactMatchWithIntentionalOmission: " - + isExactMatchWithIntentionalOmission); - } - if (perfectMatchProbability != Dictionary.NOT_A_PROBABILITY) { - final int topNonPerfectProbability = mDictionaryFacilitator.getFrequency( - suggestedWordInfo.mWord); - if (DEBUG) { - Log.d(TAG, "perfectMatchProbability: " + perfectMatchProbability); - Log.d(TAG, "topNonPerfectProbability: " + topNonPerfectProbability); - } - if (perfectMatchProbability > topNonPerfectProbability) { - return false; - } - } - return isExactMatch || isExactMatchWithIntentionalOmission; - } - return false; - } - - private void loadKeyboardForLocale(final Locale newLocale) { - final Keyboard cachedKeyboard = mLocaleToKeyboardMap.get(newLocale); - if (cachedKeyboard != null) { - mKeyboard = cachedKeyboard; - return; - } - final InputMethodSubtype subtype; - synchronized (mLock) { - subtype = mLocaleToSubtypeMap.get(newLocale); - } - if (subtype == null) { - return; - } - final EditorInfo editorInfo = new EditorInfo(); - editorInfo.inputType = InputType.TYPE_CLASS_TEXT; - final KeyboardLayoutSet.Builder builder = new KeyboardLayoutSet.Builder( - mContext, editorInfo); - final Resources res = mContext.getResources(); - final int keyboardWidth = ResourceUtils.getDefaultKeyboardWidth(res); - final int keyboardHeight = ResourceUtils.getDefaultKeyboardHeight(res); - builder.setKeyboardGeometry(keyboardWidth, keyboardHeight); - builder.setSubtype(subtype); - builder.setIsSpellChecker(false /* isSpellChecker */); - final KeyboardLayoutSet layoutSet = builder.build(); - mKeyboard = layoutSet.getKeyboard(KeyboardId.ELEMENT_ALPHABET); } private void loadDictionariesForLocale(final Locale newlocale) throws InterruptedException { @@ -191,12 +86,6 @@ public class DistracterFilterUsingSuggestion implements DistracterFilter { } if (!locale.equals(mDictionaryFacilitator.getLocale())) { synchronized (mLock) { - if (!mLocaleToSubtypeMap.containsKey(locale)) { - Log.e(TAG, "Locale " + locale + " is not enabled."); - // TODO: Investigate what we should do for disabled locales. - return false; - } - loadKeyboardForLocale(locale); // Reset dictionaries for the locale. try { loadDictionariesForLocale(locale); @@ -207,37 +96,17 @@ public class DistracterFilterUsingSuggestion implements DistracterFilter { } } } - if (mKeyboard == null) { - return false; + // The tested word is a distracter when there is a word that is exact matched to the tested + // word and its probability is higher than the tested word's probability. + final int perfectMatchFreq = mDictionaryFacilitator.getFrequency(testedWord); + final int exactMatchFreq = mDictionaryFacilitator.getMaxFrequencyOfExactMatches(testedWord); + final boolean isDistracter = perfectMatchFreq < exactMatchFreq; + if (DEBUG) { + Log.d(TAG, "testedWord: " + testedWord); + Log.d(TAG, "perfectMatchFreq: " + perfectMatchFreq); + Log.d(TAG, "exactMatchFreq: " + exactMatchFreq); + Log.d(TAG, "isDistracter: " + isDistracter); } - final WordComposer composer = new WordComposer(); - final int[] codePoints = StringUtils.toCodePointArray(testedWord); - final int[] coordinates = mKeyboard.getCoordinates(codePoints); - composer.setComposingWord(codePoints, coordinates, PrevWordsInfo.EMPTY_PREV_WORDS_INFO); - - final int trailingSingleQuotesCount = StringUtils.getTrailingSingleQuotesCount(testedWord); - final String consideredWord = trailingSingleQuotesCount > 0 ? - testedWord.substring(0, testedWord.length() - trailingSingleQuotesCount) : - testedWord; - final AsyncResultHolder<Boolean> holder = new AsyncResultHolder<>(); - ExecutorUtils.getExecutor("check distracters").execute(new Runnable() { - @Override - public void run() { - final SuggestionResults suggestionResults = - mDictionaryFacilitator.getSuggestionResults( - composer, PrevWordsInfo.EMPTY_PREV_WORDS_INFO, - mKeyboard.getProximityInfo(), true /* blockOffensiveWords */, - null /* additionalFeaturesOptions */, 0 /* sessionId */, - null /* rawSuggestions */); - if (suggestionResults.isEmpty()) { - holder.set(false); - return; - } - holder.set(isDistracter(suggestionResults, consideredWord)); - } - }); - // It's OK to block the distracter filtering, but the dictionary lookup should be done - // sequentially using ExecutorUtils. - return holder.get(false /* defaultValue */, Constants.GET_SUGGESTED_WORDS_TIMEOUT); + return isDistracter; } } diff --git a/java/src/com/android/inputmethod/latin/utils/ImportantNoticeUtils.java b/java/src/com/android/inputmethod/latin/utils/ImportantNoticeUtils.java index 7d937a9d2..8b7077879 100644 --- a/java/src/com/android/inputmethod/latin/utils/ImportantNoticeUtils.java +++ b/java/src/com/android/inputmethod/latin/utils/ImportantNoticeUtils.java @@ -23,7 +23,6 @@ import android.provider.Settings.SettingNotFoundException; import android.text.TextUtils; import android.util.Log; -import com.android.inputmethod.latin.InputAttributes; import com.android.inputmethod.latin.R; public final class ImportantNoticeUtils { @@ -78,14 +77,7 @@ public final class ImportantNoticeUtils { return getCurrentImportantNoticeVersion(context) > lastVersion; } - public static boolean shouldShowImportantNotice(final Context context, - final InputAttributes inputAttributes) { - if (inputAttributes == null || inputAttributes.mIsPasswordField) { - return false; - } - if (isInSystemSetupWizard(context)) { - return false; - } + public static boolean shouldShowImportantNotice(final Context context) { if (!hasNewImportantNotice(context)) { return false; } @@ -93,6 +85,9 @@ public final class ImportantNoticeUtils { if (TextUtils.isEmpty(importantNoticeTitle)) { return false; } + if (isInSystemSetupWizard(context)) { + return false; + } return true; } |