diff options
Diffstat (limited to 'java/src/com/android/inputmethod/latin/inputlogic/InputLogic.java')
-rw-r--r-- | java/src/com/android/inputmethod/latin/inputlogic/InputLogic.java | 124 |
1 files changed, 124 insertions, 0 deletions
diff --git a/java/src/com/android/inputmethod/latin/inputlogic/InputLogic.java b/java/src/com/android/inputmethod/latin/inputlogic/InputLogic.java index 27b7b98d8..5f3297794 100644 --- a/java/src/com/android/inputmethod/latin/inputlogic/InputLogic.java +++ b/java/src/com/android/inputmethod/latin/inputlogic/InputLogic.java @@ -18,6 +18,7 @@ package com.android.inputmethod.latin.inputlogic; import android.os.SystemClock; import android.text.TextUtils; +import android.text.style.SuggestionSpan; import android.util.Log; import android.view.KeyCharacterMap; import android.view.KeyEvent; @@ -30,6 +31,7 @@ import com.android.inputmethod.keyboard.Keyboard; import com.android.inputmethod.keyboard.KeyboardSwitcher; import com.android.inputmethod.keyboard.MainKeyboardView; import com.android.inputmethod.latin.Constants; +import com.android.inputmethod.latin.Dictionary; import com.android.inputmethod.latin.LastComposedWord; import com.android.inputmethod.latin.LatinIME; import com.android.inputmethod.latin.LatinImeLogger; @@ -38,11 +40,13 @@ import com.android.inputmethod.latin.SubtypeSwitcher; import com.android.inputmethod.latin.Suggest; import com.android.inputmethod.latin.Suggest.OnGetSuggestedWordsCallback; import com.android.inputmethod.latin.SuggestedWords; +import com.android.inputmethod.latin.SuggestedWords.SuggestedWordInfo; import com.android.inputmethod.latin.WordComposer; import com.android.inputmethod.latin.define.ProductionFlag; import com.android.inputmethod.latin.personalization.UserHistoryDictionary; import com.android.inputmethod.latin.settings.Settings; import com.android.inputmethod.latin.settings.SettingsValues; +import com.android.inputmethod.latin.suggestions.SuggestionStripView; import com.android.inputmethod.latin.utils.AsyncResultHolder; import com.android.inputmethod.latin.utils.AutoCorrectionUtils; import com.android.inputmethod.latin.utils.CollectionUtils; @@ -50,8 +54,10 @@ import com.android.inputmethod.latin.utils.InputTypeUtils; import com.android.inputmethod.latin.utils.LatinImeLoggerUtils; import com.android.inputmethod.latin.utils.RecapitalizeStatus; import com.android.inputmethod.latin.utils.StringUtils; +import com.android.inputmethod.latin.utils.TextRange; import com.android.inputmethod.research.ResearchLogger; +import java.util.ArrayList; import java.util.TreeSet; import java.util.concurrent.TimeUnit; @@ -954,6 +960,107 @@ public final class InputLogic { } /** + * Check if the cursor is touching a word. If so, restart suggestions on this word, else + * do nothing. + * + * @param settingsValues the current values of the settings. + */ + // TODO: make this private. + public void restartSuggestionsOnWordTouchedByCursor(final SettingsValues settingsValues, + // TODO: Remove these argument. + final KeyboardSwitcher keyboardSwitcher, final LatinIME.InputUpdater inputUpdater) { + // 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. + if (settingsValues.isBrokenByRecorrection()) return; + // A simple way to test for support from the TextView. + if (!mLatinIME.isSuggestionsStripVisible()) return; + // Recorrection is not supported in languages without spaces because we don't know + // how to segment them yet. + if (!settingsValues.mCurrentLanguageHasSpaces) return; + // If the cursor is not touching a word, or if there is a selection, return right away. + if (mLastSelectionStart != mLastSelectionEnd) return; + // If we don't know the cursor location, return. + if (mLastSelectionStart < 0) return; + if (!mConnection.isCursorTouchingWord(settingsValues)) return; + final TextRange range = mConnection.getWordRangeAtCursor( + settingsValues.mWordSeparators, 0 /* additionalPrecedingWordsCount */); + if (null == range) return; // Happens if we don't have an input connection at all + if (range.length() <= 0) return; // Race condition. No text to resume on, so bail out. + // If for some strange reason (editor bug or so) we measure the text before the cursor as + // longer than what the entire text is supposed to be, the safe thing to do is bail out. + final int numberOfCharsInWordBeforeCursor = range.getNumberOfCharsInWordBeforeCursor(); + if (numberOfCharsInWordBeforeCursor > mLastSelectionStart) return; + final ArrayList<SuggestedWordInfo> suggestions = CollectionUtils.newArrayList(); + final String typedWord = range.mWord.toString(); + if (!isResumableWord(settingsValues, typedWord)) return; + int i = 0; + for (final SuggestionSpan span : range.getSuggestionSpansAtWord()) { + for (final String s : span.getSuggestions()) { + ++i; + if (!TextUtils.equals(s, typedWord)) { + suggestions.add(new SuggestedWordInfo(s, + SuggestionStripView.MAX_SUGGESTIONS - i, + SuggestedWordInfo.KIND_RESUMED, Dictionary.DICTIONARY_RESUMED, + SuggestedWordInfo.NOT_AN_INDEX /* indexOfTouchPointOfSecondWord */, + SuggestedWordInfo.NOT_A_CONFIDENCE + /* autoCommitFirstWordConfidence */)); + } + } + } + mWordComposer.setComposingWord(typedWord, + getNthPreviousWordForSuggestion(settingsValues, + // We want the previous word for suggestion. If we have chars in the word + // before the cursor, then we want the word before that, hence 2; otherwise, + // we want the word immediately before the cursor, hence 1. + 0 == numberOfCharsInWordBeforeCursor ? 1 : 2), + keyboardSwitcher.getKeyboard()); + mWordComposer.setCursorPositionWithinWord( + typedWord.codePointCount(0, numberOfCharsInWordBeforeCursor)); + mConnection.setComposingRegion(mLastSelectionStart - numberOfCharsInWordBeforeCursor, + mLastSelectionEnd + 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. + inputUpdater.getSuggestedWords(Suggest.SESSION_TYPING, + SuggestedWords.NOT_A_SEQUENCE_NUMBER, new OnGetSuggestedWordsCallback() { + @Override + public void onGetSuggestedWords( + final SuggestedWords suggestedWordsIncludingTypedWord) { + final SuggestedWords suggestedWords; + if (suggestedWordsIncludingTypedWord.size() > 1) { + // 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 + // willAutoCorrect to false. + suggestedWords = suggestedWordsIncludingTypedWord + .getSuggestedWordsExcludingTypedWord(); + } else { + // No saved suggestions, and we were unable to compute any good one + // either. Rather than displaying an empty suggestion strip, we'll + // display the original word alone in the middle. + // Since there is only one word, willAutoCorrect is false. + suggestedWords = suggestedWordsIncludingTypedWord; + } + // We need to pass typedWord because mWordComposer.mTypedWord may + // differ from typedWord. + mLatinIME.unsetIsAutoCorrectionIndicatorOnAndCallShowSuggestionStrip( + suggestedWords, typedWord); + }}); + } else { + // We found suggestion spans in the word. We'll create the SuggestedWords out of + // them, and make willAutoCorrect false. + final SuggestedWords suggestedWords = new SuggestedWords(suggestions, + true /* typedWordValid */, false /* willAutoCorrect */, + false /* isPunctuationSuggestions */, false /* isObsoleteSuggestions */, + false /* isPrediction */); + // We need to pass typedWord because mWordComposer.mTypedWord may differ from typedWord. + mLatinIME.unsetIsAutoCorrectionIndicatorOnAndCallShowSuggestionStrip(suggestedWords, + typedWord); + } + } + + /** * Reverts a previous commit with auto-correction. * * This is triggered upon pressing backspace just after a commit with auto-correction. @@ -1101,6 +1208,23 @@ public final class InputLogic { } /** + * Tests the passed word for resumability. + * + * We can resume suggestions on words whose first code point is a word code point (with some + * nuances: check the code for details). + * + * @param settings the current values of the settings. + * @param word the word to evaluate. + * @return whether it's fine to resume suggestions on this word. + */ + private static boolean isResumableWord(final SettingsValues settings, final String word) { + final int firstCodePoint = word.codePointAt(0); + return settings.isWordCodePoint(firstCodePoint) + && Constants.CODE_SINGLE_QUOTE != firstCodePoint + && Constants.CODE_DASH != firstCodePoint; + } + + /** * @param actionId the action to perform */ private void performEditorAction(final int actionId) { |