aboutsummaryrefslogtreecommitdiffstats
path: root/java/src/com/android/inputmethod/latin/inputlogic/InputLogic.java
diff options
context:
space:
mode:
Diffstat (limited to 'java/src/com/android/inputmethod/latin/inputlogic/InputLogic.java')
-rw-r--r--java/src/com/android/inputmethod/latin/inputlogic/InputLogic.java124
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) {