Restart suggestions when the cursor moves.
This uses the old suggestions. It does not try to recompute new suggestions if there are no old suggestions yet: this is coming in a later change. If there are no suggestions, this shows the word itself as a suggestion. Bug: 8084810 Change-Id: I4c2e25df0ff3673be1825f57a0c19a9d23d47a48main
parent
d24f939712
commit
6a114fa700
|
@ -37,6 +37,8 @@ public abstract class Dictionary {
|
|||
public static final String TYPE_USER = "user";
|
||||
// User history dictionary internal to LatinIME.
|
||||
public static final String TYPE_USER_HISTORY = "history";
|
||||
// Spawned by resuming suggestions. Comes from a span that was in the TextView.
|
||||
public static final String TYPE_RESUMED = "resumed";
|
||||
protected final String mDictType;
|
||||
|
||||
public Dictionary(final String dictType) {
|
||||
|
|
|
@ -44,7 +44,9 @@ import android.os.Message;
|
|||
import android.os.SystemClock;
|
||||
import android.preference.PreferenceManager;
|
||||
import android.text.InputType;
|
||||
import android.text.SpannableString;
|
||||
import android.text.TextUtils;
|
||||
import android.text.style.SuggestionSpan;
|
||||
import android.util.Log;
|
||||
import android.util.PrintWriterPrinter;
|
||||
import android.util.Printer;
|
||||
|
@ -72,6 +74,7 @@ import com.android.inputmethod.keyboard.KeyboardActionListener;
|
|||
import com.android.inputmethod.keyboard.KeyboardId;
|
||||
import com.android.inputmethod.keyboard.KeyboardSwitcher;
|
||||
import com.android.inputmethod.keyboard.MainKeyboardView;
|
||||
import com.android.inputmethod.latin.RichInputConnection.Range;
|
||||
import com.android.inputmethod.latin.SuggestedWords.SuggestedWordInfo;
|
||||
import com.android.inputmethod.latin.Utils.Stats;
|
||||
import com.android.inputmethod.latin.define.ProductionFlag;
|
||||
|
@ -197,6 +200,7 @@ public final class LatinIME extends InputMethodService implements KeyboardAction
|
|||
private static final int MSG_PENDING_IMS_CALLBACK = 1;
|
||||
private static final int MSG_UPDATE_SUGGESTION_STRIP = 2;
|
||||
private static final int MSG_SHOW_GESTURE_PREVIEW_AND_SUGGESTION_STRIP = 3;
|
||||
private static final int MSG_RESUME_SUGGESTIONS = 4;
|
||||
|
||||
private static final int ARG1_DISMISS_GESTURE_FLOATING_PREVIEW_TEXT = 1;
|
||||
|
||||
|
@ -234,6 +238,9 @@ public final class LatinIME extends InputMethodService implements KeyboardAction
|
|||
latinIme.showGesturePreviewAndSuggestionStrip((SuggestedWords)msg.obj,
|
||||
msg.arg1 == ARG1_DISMISS_GESTURE_FLOATING_PREVIEW_TEXT);
|
||||
break;
|
||||
case MSG_RESUME_SUGGESTIONS:
|
||||
latinIme.restartSuggestionsOnWordTouchedByCursor();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -241,6 +248,10 @@ public final class LatinIME extends InputMethodService implements KeyboardAction
|
|||
sendMessageDelayed(obtainMessage(MSG_UPDATE_SUGGESTION_STRIP), mDelayUpdateSuggestions);
|
||||
}
|
||||
|
||||
public void postResumeSuggestions() {
|
||||
sendMessageDelayed(obtainMessage(MSG_RESUME_SUGGESTIONS), mDelayUpdateSuggestions);
|
||||
}
|
||||
|
||||
public void cancelUpdateSuggestionStrip() {
|
||||
removeMessages(MSG_UPDATE_SUGGESTION_STRIP);
|
||||
}
|
||||
|
@ -910,13 +921,12 @@ public final class LatinIME extends InputMethodService implements KeyboardAction
|
|||
resetEntireInputState(newSelStart);
|
||||
}
|
||||
|
||||
// We moved the cursor. If we are touching a word, we need to resume suggestion.
|
||||
mHandler.postResumeSuggestions();
|
||||
|
||||
mKeyboardSwitcher.updateShiftState();
|
||||
}
|
||||
mExpectingUpdateSelection = false;
|
||||
// TODO: Decide to call restartSuggestionsOnWordBeforeCursorIfAtEndOfWord() or not
|
||||
// here. It would probably be too expensive to call directly here but we may want to post a
|
||||
// message to delay it. The point would be to unify behavior between backspace to the
|
||||
// end of a word and manually put the pointer at the end of the word.
|
||||
|
||||
// Make a note of the cursor position
|
||||
mLastSelectionStart = newSelStart;
|
||||
|
@ -1727,6 +1737,9 @@ public final class LatinIME extends InputMethodService implements KeyboardAction
|
|||
// during key repeat.
|
||||
mHandler.postUpdateShiftState();
|
||||
|
||||
if (mWordComposer.isComposingWord() && !mWordComposer.isCursorAtEndOfComposingWord()) {
|
||||
resetEntireInputState(mLastSelectionStart);
|
||||
}
|
||||
if (mWordComposer.isComposingWord()) {
|
||||
final int length = mWordComposer.size();
|
||||
if (length > 0) {
|
||||
|
@ -1858,6 +1871,10 @@ public final class LatinIME extends InputMethodService implements KeyboardAction
|
|||
promotePhantomSpace();
|
||||
}
|
||||
|
||||
if (mWordComposer.isComposingWord() && !mWordComposer.isCursorAtEndOfComposingWord()) {
|
||||
resetEntireInputState(mLastSelectionStart);
|
||||
isComposingWord = false;
|
||||
}
|
||||
// NOTE: isCursorTouchingWord() is a blocking IPC call, so it often takes several
|
||||
// dozen milliseconds. Avoid calling it as much as possible, since we are on the UI
|
||||
// thread here.
|
||||
|
@ -2330,6 +2347,48 @@ public final class LatinIME extends InputMethodService implements KeyboardAction
|
|||
return prevWord;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the cursor is touching a word. If so, restart suggestions on this word, else
|
||||
* do nothing.
|
||||
*/
|
||||
private void restartSuggestionsOnWordTouchedByCursor() {
|
||||
// If the cursor is not touching a word, or if there is a selection, return right away.
|
||||
if (mLastSelectionStart != mLastSelectionEnd) return;
|
||||
if (!mConnection.isCursorTouchingWord(mSettings.getCurrent())) return;
|
||||
final Range range = mConnection.getWordRangeAtCursor(mSettings.getWordSeparators(),
|
||||
0 /* additionalPrecedingWordsCount */);
|
||||
final ArrayList<SuggestedWordInfo> suggestions = CollectionUtils.newArrayList();
|
||||
if (range.mWord instanceof SpannableString) {
|
||||
final SpannableString spannableString = (SpannableString)range.mWord;
|
||||
final String typedWord = spannableString.toString();
|
||||
int i = 0;
|
||||
for (Object object : spannableString.getSpans(0, spannableString.length(),
|
||||
SuggestionSpan.class)) {
|
||||
SuggestionSpan span = (SuggestionSpan)object;
|
||||
for (String s : span.getSuggestions()) {
|
||||
++i;
|
||||
if (!TextUtils.equals(s, typedWord)) {
|
||||
suggestions.add(new SuggestedWordInfo(s,
|
||||
SuggestionStripView.MAX_SUGGESTIONS - i,
|
||||
SuggestedWordInfo.KIND_RESUMED, Dictionary.TYPE_RESUMED));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
mWordComposer.setComposingWord(range.mWord, mKeyboardSwitcher.getKeyboard());
|
||||
mWordComposer.setCursorPositionWithinWord(range.mCharsBefore);
|
||||
mConnection.setComposingRegion(mLastSelectionStart - range.mCharsBefore,
|
||||
mLastSelectionEnd + range.mCharsAfter);
|
||||
if (suggestions.isEmpty()) {
|
||||
suggestions.add(new SuggestedWordInfo(range.mWord.toString(), 1,
|
||||
SuggestedWordInfo.KIND_TYPED, Dictionary.TYPE_RESUMED));
|
||||
}
|
||||
showSuggestionStrip(new SuggestedWords(suggestions,
|
||||
true /* typedWordValid */, false /* willAutoCorrect */,
|
||||
false /* isPunctuationSuggestions */, false /* isObsoleteSuggestions */,
|
||||
false /* isPrediction */), range.mWord.toString());
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the cursor is actually at the end of a word. If so, restart suggestions on this
|
||||
* word, else do nothing.
|
||||
|
@ -2338,17 +2397,18 @@ public final class LatinIME extends InputMethodService implements KeyboardAction
|
|||
final CharSequence word =
|
||||
mConnection.getWordBeforeCursorIfAtEndOfWord(mSettings.getCurrent());
|
||||
if (null != word) {
|
||||
restartSuggestionsOnWordBeforeCursor(word);
|
||||
final String wordString = word.toString();
|
||||
restartSuggestionsOnWordBeforeCursor(wordString);
|
||||
// TODO: Handle the case where the user manually moves the cursor and then backs up over
|
||||
// a separator. In that case, the current log unit should not be uncommitted.
|
||||
if (ProductionFlag.USES_DEVELOPMENT_ONLY_DIAGNOSTICS) {
|
||||
ResearchLogger.getInstance().uncommitCurrentLogUnit(word.toString(),
|
||||
ResearchLogger.getInstance().uncommitCurrentLogUnit(wordString,
|
||||
true /* dumpCurrentLogUnit */);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void restartSuggestionsOnWordBeforeCursor(final CharSequence word) {
|
||||
private void restartSuggestionsOnWordBeforeCursor(final String word) {
|
||||
mWordComposer.setComposingWord(word, mKeyboardSwitcher.getKeyboard());
|
||||
final int length = word.length();
|
||||
mConnection.deleteSurroundingText(length, 0);
|
||||
|
|
|
@ -17,7 +17,9 @@
|
|||
package com.android.inputmethod.latin;
|
||||
|
||||
import android.inputmethodservice.InputMethodService;
|
||||
import android.text.SpannableString;
|
||||
import android.text.TextUtils;
|
||||
import android.text.style.SuggestionSpan;
|
||||
import android.util.Log;
|
||||
import android.view.KeyEvent;
|
||||
import android.view.inputmethod.CompletionInfo;
|
||||
|
@ -442,9 +444,9 @@ public final class RichInputConnection {
|
|||
public final int mCharsAfter;
|
||||
|
||||
/** The actual characters that make up a word */
|
||||
public final String mWord;
|
||||
public final CharSequence mWord;
|
||||
|
||||
public Range(int charsBefore, int charsAfter, String word) {
|
||||
public Range(int charsBefore, int charsAfter, CharSequence word) {
|
||||
if (charsBefore < 0 || charsAfter < 0) {
|
||||
throw new IndexOutOfBoundsException();
|
||||
}
|
||||
|
@ -498,7 +500,7 @@ public final class RichInputConnection {
|
|||
* separator. For example, if the field contains "he|llo world", where |
|
||||
* represents the cursor, then "hello " will be returned.
|
||||
*/
|
||||
public String getWordAtCursor(String separators) {
|
||||
public CharSequence getWordAtCursor(String separators) {
|
||||
// getWordRangeAtCursor returns null if the connection is null
|
||||
Range r = getWordRangeAtCursor(separators, 0);
|
||||
return (r == null) ? null : r.mWord;
|
||||
|
@ -517,8 +519,10 @@ public final class RichInputConnection {
|
|||
if (mIC == null || sep == null) {
|
||||
return null;
|
||||
}
|
||||
final CharSequence before = mIC.getTextBeforeCursor(1000, 0);
|
||||
final CharSequence after = mIC.getTextAfterCursor(1000, 0);
|
||||
final CharSequence before = mIC.getTextBeforeCursor(1000,
|
||||
InputConnection.GET_TEXT_WITH_STYLES);
|
||||
final CharSequence after = mIC.getTextAfterCursor(1000,
|
||||
InputConnection.GET_TEXT_WITH_STYLES);
|
||||
if (before == null || after == null) {
|
||||
return null;
|
||||
}
|
||||
|
@ -560,8 +564,9 @@ public final class RichInputConnection {
|
|||
}
|
||||
}
|
||||
|
||||
final String word = before.toString().substring(startIndexInBefore, before.length())
|
||||
+ after.toString().substring(0, endIndexInAfter);
|
||||
final SpannableString word = new SpannableString(TextUtils.concat(
|
||||
before.subSequence(startIndexInBefore, before.length()),
|
||||
after.subSequence(0, endIndexInAfter)));
|
||||
return new Range(before.length() - startIndexInBefore, endIndexInAfter, word);
|
||||
}
|
||||
|
||||
|
|
|
@ -134,6 +134,10 @@ public final class Settings implements SharedPreferences.OnSharedPreferenceChang
|
|||
return mSettingsValues.mIsInternal;
|
||||
}
|
||||
|
||||
public String getWordSeparators() {
|
||||
return mSettingsValues.mWordSeparators;
|
||||
}
|
||||
|
||||
// Accessed from the settings interface, hence public
|
||||
public static boolean readKeypressSoundEnabled(final SharedPreferences prefs,
|
||||
final Resources res) {
|
||||
|
|
|
@ -131,6 +131,7 @@ public final class SuggestedWords {
|
|||
public static final int KIND_APP_DEFINED = 6; // Suggested by the application
|
||||
public static final int KIND_SHORTCUT = 7; // A shortcut
|
||||
public static final int KIND_PREDICTION = 8; // A prediction (== a suggestion with no input)
|
||||
public static final int KIND_RESUMED = 9; // A resumed suggestion (comes from a span)
|
||||
public final String mWord;
|
||||
public final int mScore;
|
||||
public final int mKind; // one of the KIND_* constants above
|
||||
|
|
|
@ -49,6 +49,7 @@ public final class WordComposer {
|
|||
private int mCapitalizedMode;
|
||||
private int mTrailingSingleQuotesCount;
|
||||
private int mCodePointSize;
|
||||
private int mCursorPositionWithinWord;
|
||||
|
||||
/**
|
||||
* Whether the user chose to capitalize the first char of the word.
|
||||
|
@ -62,6 +63,7 @@ public final class WordComposer {
|
|||
mTrailingSingleQuotesCount = 0;
|
||||
mIsResumed = false;
|
||||
mIsBatchMode = false;
|
||||
mCursorPositionWithinWord = 0;
|
||||
refreshSize();
|
||||
}
|
||||
|
||||
|
@ -76,6 +78,7 @@ public final class WordComposer {
|
|||
mTrailingSingleQuotesCount = source.mTrailingSingleQuotesCount;
|
||||
mIsResumed = source.mIsResumed;
|
||||
mIsBatchMode = source.mIsBatchMode;
|
||||
mCursorPositionWithinWord = source.mCursorPositionWithinWord;
|
||||
refreshSize();
|
||||
}
|
||||
|
||||
|
@ -91,6 +94,7 @@ public final class WordComposer {
|
|||
mTrailingSingleQuotesCount = 0;
|
||||
mIsResumed = false;
|
||||
mIsBatchMode = false;
|
||||
mCursorPositionWithinWord = 0;
|
||||
refreshSize();
|
||||
}
|
||||
|
||||
|
@ -135,6 +139,7 @@ public final class WordComposer {
|
|||
final int newIndex = size();
|
||||
mTypedWord.appendCodePoint(primaryCode);
|
||||
refreshSize();
|
||||
mCursorPositionWithinWord = mCodePointSize;
|
||||
if (newIndex < MAX_WORD_LENGTH) {
|
||||
mPrimaryKeyCodes[newIndex] = primaryCode >= Constants.CODE_SPACE
|
||||
? Character.toLowerCase(primaryCode) : primaryCode;
|
||||
|
@ -158,6 +163,14 @@ public final class WordComposer {
|
|||
mAutoCorrection = null;
|
||||
}
|
||||
|
||||
public void setCursorPositionWithinWord(final int posWithinWord) {
|
||||
mCursorPositionWithinWord = posWithinWord;
|
||||
}
|
||||
|
||||
public boolean isCursorAtEndOfComposingWord() {
|
||||
return mCursorPositionWithinWord == mCodePointSize;
|
||||
}
|
||||
|
||||
public void setBatchInputPointers(final InputPointers batchPointers) {
|
||||
mInputPointers.set(batchPointers);
|
||||
mIsBatchMode = true;
|
||||
|
@ -242,6 +255,7 @@ public final class WordComposer {
|
|||
++mTrailingSingleQuotesCount;
|
||||
}
|
||||
}
|
||||
mCursorPositionWithinWord = mCodePointSize;
|
||||
mAutoCorrection = null;
|
||||
}
|
||||
|
||||
|
@ -368,6 +382,7 @@ public final class WordComposer {
|
|||
mCapitalizedMode = CAPS_MODE_OFF;
|
||||
refreshSize();
|
||||
mAutoCorrection = null;
|
||||
mCursorPositionWithinWord = 0;
|
||||
mIsResumed = false;
|
||||
return lastComposedWord;
|
||||
}
|
||||
|
@ -380,6 +395,7 @@ public final class WordComposer {
|
|||
refreshSize();
|
||||
mCapitalizedMode = lastComposedWord.mCapitalizedMode;
|
||||
mAutoCorrection = null; // This will be filled by the next call to updateSuggestion.
|
||||
mCursorPositionWithinWord = mCodePointSize;
|
||||
mIsResumed = true;
|
||||
}
|
||||
|
||||
|
|
|
@ -1283,7 +1283,7 @@ public class ResearchLogger implements SharedPreferences.OnSharedPreferenceChang
|
|||
if (connection != null) {
|
||||
Range range = connection.getWordRangeAtCursor(WHITESPACE_SEPARATORS, 1);
|
||||
if (range != null) {
|
||||
word = range.mWord;
|
||||
word = range.mWord.toString();
|
||||
}
|
||||
}
|
||||
final ResearchLogger researchLogger = getInstance();
|
||||
|
|
Loading…
Reference in New Issue