[IL26] Move restartSuggestionsOnWordTouchedByCursor

Bug: 8636060
Change-Id: I373874585f4fa663b4207b9a02d751805259eb36
This commit is contained in:
Jean Chalard 2013-12-20 18:05:35 +09:00
parent ec4b6e7bb3
commit 221df9e59a
2 changed files with 132 additions and 111 deletions

View file

@ -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.

View file

@ -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
*/