[IL26] Move restartSuggestionsOnWordTouchedByCursor
Bug: 8636060 Change-Id: I373874585f4fa663b4207b9a02d751805259eb36main
parent
ec4b6e7bb3
commit
221df9e59a
|
@ -43,7 +43,6 @@ import android.os.SystemClock;
|
|||
import android.preference.PreferenceManager;
|
||||
import android.text.InputType;
|
||||
import android.text.TextUtils;
|
||||
import android.text.style.SuggestionSpan;
|
||||
import android.util.Log;
|
||||
import android.util.Pair;
|
||||
import android.util.PrintWriterPrinter;
|
||||
|
@ -81,13 +80,11 @@ import com.android.inputmethod.latin.suggestions.SuggestionStripView;
|
|||
import com.android.inputmethod.latin.utils.ApplicationUtils;
|
||||
import com.android.inputmethod.latin.utils.AutoCorrectionUtils;
|
||||
import com.android.inputmethod.latin.utils.CapsModeUtils;
|
||||
import com.android.inputmethod.latin.utils.CollectionUtils;
|
||||
import com.android.inputmethod.latin.utils.CompletionInfoUtils;
|
||||
import com.android.inputmethod.latin.utils.IntentUtils;
|
||||
import com.android.inputmethod.latin.utils.JniUtils;
|
||||
import com.android.inputmethod.latin.utils.LatinImeLoggerUtils;
|
||||
import com.android.inputmethod.latin.utils.LeakGuardHandlerWrapper;
|
||||
import com.android.inputmethod.latin.utils.TextRange;
|
||||
import com.android.inputmethod.research.ResearchLogger;
|
||||
|
||||
import java.io.FileDescriptor;
|
||||
|
@ -212,7 +209,9 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
|
|||
}
|
||||
break;
|
||||
case MSG_RESUME_SUGGESTIONS:
|
||||
latinIme.restartSuggestionsOnWordTouchedByCursor();
|
||||
latinIme.mInputLogic.restartSuggestionsOnWordTouchedByCursor(
|
||||
latinIme.mSettings.getCurrent(), latinIme.mKeyboardSwitcher,
|
||||
latinIme.mInputUpdater);
|
||||
break;
|
||||
case MSG_REOPEN_DICTIONARIES:
|
||||
latinIme.initSuggest();
|
||||
|
@ -1367,7 +1366,9 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
|
|||
1 /* nthPreviousWord */));
|
||||
}
|
||||
|
||||
static final class InputUpdater implements Handler.Callback {
|
||||
// TODO[IL]: Make this a package-private standalone class in inputlogic/ and remove all
|
||||
// references to it in LatinIME
|
||||
public static final class InputUpdater implements Handler.Callback {
|
||||
private final Handler mHandler;
|
||||
private final LatinIME mLatinIme;
|
||||
private final Object mLock = new Object();
|
||||
|
@ -1617,7 +1618,8 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
|
|||
return mSettings.getCurrent().mSuggestPuncList == mInputLogic.mSuggestedWords;
|
||||
}
|
||||
|
||||
private boolean isSuggestionsStripVisible() {
|
||||
// TODO[IL]: Define a clear interface for this
|
||||
public boolean isSuggestionsStripVisible() {
|
||||
final SettingsValues currentSettings = mSettings.getCurrent();
|
||||
if (mSuggestionStripView == null)
|
||||
return false;
|
||||
|
@ -1903,111 +1905,6 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
|
|||
setSuggestionStripShown(isSuggestionsStripVisible());
|
||||
}
|
||||
|
||||
private boolean isResumableWord(final String word, final SettingsValues settings) {
|
||||
final int firstCodePoint = word.codePointAt(0);
|
||||
return settings.isWordCodePoint(firstCodePoint)
|
||||
&& Constants.CODE_SINGLE_QUOTE != firstCodePoint
|
||||
&& Constants.CODE_DASH != firstCodePoint;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the cursor is touching a word. If so, restart suggestions on this word, else
|
||||
* do nothing.
|
||||
*/
|
||||
private void restartSuggestionsOnWordTouchedByCursor() {
|
||||
// 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.
|
||||
final SettingsValues settingsValues = mSettings.getCurrent();
|
||||
if (settingsValues.isBrokenByRecorrection()) return;
|
||||
// A simple way to test for support from the TextView.
|
||||
if (!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 (mInputLogic.mLastSelectionStart != mInputLogic.mLastSelectionEnd) return;
|
||||
// If we don't know the cursor location, return.
|
||||
if (mInputLogic.mLastSelectionStart < 0) return;
|
||||
final SettingsValues currentSettings = mSettings.getCurrent();
|
||||
if (!mInputLogic.mConnection.isCursorTouchingWord(currentSettings)) return;
|
||||
final TextRange range = mInputLogic.mConnection.getWordRangeAtCursor(
|
||||
currentSettings.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 > mInputLogic.mLastSelectionStart) return;
|
||||
final ArrayList<SuggestedWordInfo> suggestions = CollectionUtils.newArrayList();
|
||||
final String typedWord = range.mWord.toString();
|
||||
if (!isResumableWord(typedWord, currentSettings)) 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 */));
|
||||
}
|
||||
}
|
||||
}
|
||||
mInputLogic.mWordComposer.setComposingWord(typedWord,
|
||||
mInputLogic.getNthPreviousWordForSuggestion(currentSettings,
|
||||
// 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),
|
||||
mKeyboardSwitcher.getKeyboard());
|
||||
mInputLogic.mWordComposer.setCursorPositionWithinWord(
|
||||
typedWord.codePointCount(0, numberOfCharsInWordBeforeCursor));
|
||||
mInputLogic.mConnection.setComposingRegion(
|
||||
mInputLogic.mLastSelectionStart - numberOfCharsInWordBeforeCursor,
|
||||
mInputLogic.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.
|
||||
mInputUpdater.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.
|
||||
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.
|
||||
unsetIsAutoCorrectionIndicatorOnAndCallShowSuggestionStrip(suggestedWords, typedWord);
|
||||
}
|
||||
}
|
||||
|
||||
public void unsetIsAutoCorrectionIndicatorOnAndCallShowSuggestionStrip(
|
||||
final SuggestedWords suggestedWords, final String typedWord) {
|
||||
// Note that it's very important here that suggestedWords.mWillAutoCorrect is false.
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
@ -953,6 +959,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.
|
||||
*
|
||||
|
@ -1100,6 +1207,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
|
||||
*/
|
||||
|
|
Loading…
Reference in New Issue