Loading java/src/com/android/inputmethod/latin/Dictionary.java +2 −0 Original line number Diff line number Diff line Loading @@ -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) { Loading java/src/com/android/inputmethod/latin/LatinIME.java +67 −7 Original line number Diff line number Diff line Loading @@ -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; Loading Loading @@ -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; Loading Loading @@ -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; Loading Loading @@ -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; } } Loading @@ -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); } Loading Loading @@ -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; Loading Loading @@ -1728,6 +1738,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) { Loading Loading @@ -1859,6 +1872,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. Loading Loading @@ -2331,6 +2348,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. Loading @@ -2339,17 +2398,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); Loading java/src/com/android/inputmethod/latin/RichInputConnection.java +12 −7 Original line number Diff line number Diff line Loading @@ -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; Loading Loading @@ -444,9 +446,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(); } Loading Loading @@ -500,7 +502,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; Loading @@ -519,8 +521,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; } Loading Loading @@ -562,8 +566,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); } Loading java/src/com/android/inputmethod/latin/Settings.java +4 −0 Original line number Diff line number Diff line Loading @@ -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) { Loading java/src/com/android/inputmethod/latin/SuggestedWords.java +1 −0 Original line number Diff line number Diff line Loading @@ -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 Loading Loading
java/src/com/android/inputmethod/latin/Dictionary.java +2 −0 Original line number Diff line number Diff line Loading @@ -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) { Loading
java/src/com/android/inputmethod/latin/LatinIME.java +67 −7 Original line number Diff line number Diff line Loading @@ -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; Loading Loading @@ -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; Loading Loading @@ -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; Loading Loading @@ -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; } } Loading @@ -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); } Loading Loading @@ -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; Loading Loading @@ -1728,6 +1738,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) { Loading Loading @@ -1859,6 +1872,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. Loading Loading @@ -2331,6 +2348,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. Loading @@ -2339,17 +2398,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); Loading
java/src/com/android/inputmethod/latin/RichInputConnection.java +12 −7 Original line number Diff line number Diff line Loading @@ -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; Loading Loading @@ -444,9 +446,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(); } Loading Loading @@ -500,7 +502,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; Loading @@ -519,8 +521,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; } Loading Loading @@ -562,8 +566,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); } Loading
java/src/com/android/inputmethod/latin/Settings.java +4 −0 Original line number Diff line number Diff line Loading @@ -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) { Loading
java/src/com/android/inputmethod/latin/SuggestedWords.java +1 −0 Original line number Diff line number Diff line Loading @@ -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 Loading