diff options
Diffstat (limited to 'java/src/com/android/inputmethod/latin/LatinIME.java')
-rw-r--r-- | java/src/com/android/inputmethod/latin/LatinIME.java | 477 |
1 files changed, 273 insertions, 204 deletions
diff --git a/java/src/com/android/inputmethod/latin/LatinIME.java b/java/src/com/android/inputmethod/latin/LatinIME.java index b668a7770..6a10131b0 100644 --- a/java/src/com/android/inputmethod/latin/LatinIME.java +++ b/java/src/com/android/inputmethod/latin/LatinIME.java @@ -68,7 +68,6 @@ import com.android.inputmethod.compat.InputMethodServiceCompatUtils; import com.android.inputmethod.compat.SuggestionSpanUtils; import com.android.inputmethod.dictionarypack.DictionaryPackConstants; import com.android.inputmethod.event.EventInterpreter; -import com.android.inputmethod.keyboard.KeyDetector; import com.android.inputmethod.keyboard.Keyboard; import com.android.inputmethod.keyboard.KeyboardActionListener; import com.android.inputmethod.keyboard.KeyboardId; @@ -81,7 +80,6 @@ import com.android.inputmethod.latin.personalization.DictionaryDecayBroadcastRec import com.android.inputmethod.latin.personalization.PersonalizationDictionary; import com.android.inputmethod.latin.personalization.PersonalizationDictionarySessionRegister; import com.android.inputmethod.latin.personalization.PersonalizationHelper; -import com.android.inputmethod.latin.personalization.PersonalizationPredictionDictionary; import com.android.inputmethod.latin.personalization.UserHistoryDictionary; import com.android.inputmethod.latin.settings.Settings; import com.android.inputmethod.latin.settings.SettingsActivity; @@ -97,8 +95,9 @@ import com.android.inputmethod.latin.utils.InputTypeUtils; 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.RecapitalizeStatus; -import com.android.inputmethod.latin.utils.StaticInnerHandlerWrapper; +import com.android.inputmethod.latin.utils.StringUtils; import com.android.inputmethod.latin.utils.TargetPackageInfoGetterTask; import com.android.inputmethod.latin.utils.TextRange; import com.android.inputmethod.latin.utils.UserHistoryForgettingCurveUtils; @@ -109,6 +108,7 @@ import java.io.PrintWriter; import java.util.ArrayList; import java.util.Locale; import java.util.TreeSet; +import java.util.concurrent.TimeUnit; /** * Input method implementation for Qwerty'ish keyboard. @@ -181,7 +181,6 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen private boolean mIsMainDictionaryAvailable; private UserBinaryDictionary mUserDictionary; private UserHistoryDictionary mUserHistoryDictionary; - private PersonalizationPredictionDictionary mPersonalizationPredictionDictionary; private PersonalizationDictionary mPersonalizationDictionary; private boolean mIsUserDictionaryAvailable; @@ -195,9 +194,6 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen private int mLastSelectionStart = NOT_A_CURSOR_POSITION; private int mLastSelectionEnd = NOT_A_CURSOR_POSITION; - // Whether we are expecting an onUpdateSelection event to fire. If it does when we don't - // "expect" it, it means the user actually moved the cursor. - private boolean mExpectingUpdateSelection; private int mDeleteCount; private long mLastKeyTime; private final TreeSet<Long> mCurrentlyPressedHardwareKeys = CollectionUtils.newTreeSet(); @@ -226,7 +222,7 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen public final UIHandler mHandler = new UIHandler(this); private InputUpdater mInputUpdater; - public static final class UIHandler extends StaticInnerHandlerWrapper<LatinIME> { + public static final class UIHandler extends LeakGuardHandlerWrapper<LatinIME> { private static final int MSG_UPDATE_SHIFT_STATE = 0; private static final int MSG_PENDING_IMS_CALLBACK = 1; private static final int MSG_UPDATE_SUGGESTION_STRIP = 2; @@ -235,6 +231,8 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen private static final int MSG_REOPEN_DICTIONARIES = 5; private static final int MSG_ON_END_BATCH_INPUT = 6; private static final int MSG_RESET_CACHES = 7; + // Update this when adding new messages + private static final int MSG_LAST = MSG_RESET_CACHES; private static final int ARG1_NOT_GESTURE_INPUT = 0; private static final int ARG1_DISMISS_GESTURE_FLOATING_PREVIEW_TEXT = 1; @@ -247,12 +245,12 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen private long mDoubleSpacePeriodTimeout; private long mDoubleSpacePeriodTimerStart; - public UIHandler(final LatinIME outerInstance) { - super(outerInstance); + public UIHandler(final LatinIME ownerInstance) { + super(ownerInstance); } public void onCreate() { - final Resources res = getOuterInstance().getResources(); + final Resources res = getOwnerInstance().getResources(); mDelayUpdateSuggestions = res.getInteger(R.integer.config_delay_update_suggestions); mDelayUpdateShiftState = @@ -263,7 +261,7 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen @Override public void handleMessage(final Message msg) { - final LatinIME latinIme = getOuterInstance(); + final LatinIME latinIme = getOwnerInstance(); final KeyboardSwitcher switcher = latinIme.mKeyboardSwitcher; switch (msg.what) { case MSG_UPDATE_SUGGESTION_STRIP: @@ -346,6 +344,13 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen removeMessages(MSG_UPDATE_SHIFT_STATE); } + @UsedForTesting + public void removeAllMessages() { + for (int i = 0; i <= MSG_LAST; ++i) { + removeMessages(i); + } + } + public void showGesturePreviewAndSuggestionStrip(final SuggestedWords suggestedWords, final boolean dismissGestureFloatingPreviewText) { removeMessages(MSG_SHOW_GESTURE_PREVIEW_AND_SUGGESTION_STRIP); @@ -400,7 +405,7 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen removeMessages(MSG_PENDING_IMS_CALLBACK); resetPendingImsCallback(); mIsOrientationChanging = true; - final LatinIME latinIme = getOuterInstance(); + final LatinIME latinIme = getOwnerInstance(); if (latinIme.isInputViewShown()) { latinIme.mKeyboardSwitcher.saveKeyboardState(); } @@ -433,7 +438,7 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen mIsOrientationChanging = false; mPendingSuccessiveImsCallback = true; } - final LatinIME latinIme = getOuterInstance(); + final LatinIME latinIme = getOwnerInstance(); executePendingImsCallback(latinIme, editorInfo, restarting); latinIme.onStartInputInternal(editorInfo, restarting); } @@ -452,7 +457,7 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen sendMessageDelayed(obtainMessage(MSG_PENDING_IMS_CALLBACK), PENDING_IMS_CALLBACK_DURATION); } - final LatinIME latinIme = getOuterInstance(); + final LatinIME latinIme = getOwnerInstance(); executePendingImsCallback(latinIme, editorInfo, restarting); latinIme.onStartInputViewInternal(editorInfo, restarting); mAppliedEditorInfo = editorInfo; @@ -464,7 +469,7 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen // Typically this is the first onFinishInputView after orientation changed. mHasPendingFinishInputView = true; } else { - final LatinIME latinIme = getOuterInstance(); + final LatinIME latinIme = getOwnerInstance(); latinIme.onFinishInputViewInternal(finishingInput); mAppliedEditorInfo = null; } @@ -475,7 +480,7 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen // Typically this is the first onFinishInput after orientation changed. mHasPendingFinishInput = true; } else { - final LatinIME latinIme = getOuterInstance(); + final LatinIME latinIme = getOwnerInstance(); executePendingImsCallback(latinIme, null, false); latinIme.onFinishInputInternal(); } @@ -608,7 +613,6 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen final Locale switcherSubtypeLocale = mSubtypeSwitcher.getCurrentSubtypeLocale(); final String switcherLocaleStr = switcherSubtypeLocale.toString(); final Locale subtypeLocale; - final String localeStr; if (TextUtils.isEmpty(switcherLocaleStr)) { // This happens in very rare corner cases - for example, immediately after a switch // to LatinIME has been requested, about a frame later another switch happens. In this @@ -618,10 +622,8 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen // of knowing anyway. Log.e(TAG, "System is reporting no current subtype."); subtypeLocale = getResources().getConfiguration().locale; - localeStr = subtypeLocale.toString(); } else { subtypeLocale = switcherSubtypeLocale; - localeStr = switcherLocaleStr; } final Suggest newSuggest = new Suggest(this /* Context */, subtypeLocale, @@ -636,21 +638,16 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen ResearchLogger.getInstance().initSuggest(newSuggest); } - mUserDictionary = new UserBinaryDictionary(this, localeStr); + mUserDictionary = new UserBinaryDictionary(this, subtypeLocale); mIsUserDictionaryAvailable = mUserDictionary.isEnabled(); newSuggest.setUserDictionary(mUserDictionary); - final SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(this); - mUserHistoryDictionary = PersonalizationHelper.getUserHistoryDictionary( - this, localeStr, prefs); + this, subtypeLocale); newSuggest.setUserHistoryDictionary(mUserHistoryDictionary); - mPersonalizationDictionary = PersonalizationHelper - .getPersonalizationDictionary(this, localeStr, prefs); + mPersonalizationDictionary = + PersonalizationHelper.getPersonalizationDictionary(this, subtypeLocale); newSuggest.setPersonalizationDictionary(mPersonalizationDictionary); - mPersonalizationPredictionDictionary = PersonalizationHelper - .getPersonalizationPredictionDictionary(this, localeStr, prefs); - newSuggest.setPersonalizationPredictionDictionary(mPersonalizationPredictionDictionary); final Suggest oldSuggest = mSuggest; resetContactsDictionary(null != oldSuggest ? oldSuggest.getContactsDictionary() : null); @@ -724,8 +721,7 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen LatinImeLogger.commit(); LatinImeLogger.onDestroy(); if (mInputUpdater != null) { - mInputUpdater.onDestroy(); - mInputUpdater = null; + mInputUpdater.quitLooper(); } super.onDestroy(); } @@ -760,8 +756,9 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen .findViewById(android.R.id.extractArea); mKeyPreviewBackingView = view.findViewById(R.id.key_preview_backing); mSuggestionStripView = (SuggestionStripView)view.findViewById(R.id.suggestion_strip_view); - if (mSuggestionStripView != null) + if (mSuggestionStripView != null) { mSuggestionStripView.setListener(this, view); + } if (LatinImeLogger.sVISUALDEBUG) { mKeyPreviewBackingView.setBackgroundColor(0x10FF0000); } @@ -810,6 +807,7 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen super.onStartInputView(editorInfo, restarting); mRichImm.clearSubtypeCaches(); final KeyboardSwitcher switcher = mKeyboardSwitcher; + switcher.updateKeyboardTheme(); final MainKeyboardView mainKeyboardView = switcher.getMainKeyboardView(); // If we are starting input in a different text field from before, we'll have to reload // settings, so currentSettingsValues can't be final. @@ -906,9 +904,10 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen // refresh later. final boolean canReachInputConnection; if (!mConnection.resetCachesUponCursorMoveAndReturnSuccess(editorInfo.initialSelStart, - false /* shouldFinishComposition */)) { + editorInfo.initialSelEnd, false /* shouldFinishComposition */)) { // We try resetting the caches up to 5 times before giving up. mHandler.postResetCaches(isDifferentTextField, 5 /* remainingTries */); + // mLastSelection{Start,End} are reset later in this method, don't need to do it here canReachInputConnection = false; } else { if (isDifferentTextField) { @@ -988,10 +987,16 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen if (textLength > mLastSelectionStart || (textLength < Constants.EDITOR_CONTENTS_CACHE_SIZE && mLastSelectionStart < Constants.EDITOR_CONTENTS_CACHE_SIZE)) { + // It should not be possible to have only one of those variables be + // NOT_A_CURSOR_POSITION, so if they are equal, either the selection is zero-sized + // (simple cursor, no selection) or there is no cursor/we don't know its pos + final boolean wasEqual = mLastSelectionStart == mLastSelectionEnd; mLastSelectionStart = textLength; // We can't figure out the value of mLastSelectionEnd :( - // But at least if it's smaller than mLastSelectionStart something is wrong - if (mLastSelectionStart > mLastSelectionEnd) { + // But at least if it's smaller than mLastSelectionStart something is wrong, + // and if they used to be equal we also don't want to make it look like there is a + // selection. + if (wasEqual || mLastSelectionStart > mLastSelectionEnd) { mLastSelectionEnd = mLastSelectionStart; } } @@ -1080,16 +1085,9 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen + ", ce=" + composingSpanEnd); } if (ProductionFlag.USES_DEVELOPMENT_ONLY_DIAGNOSTICS) { - final boolean expectingUpdateSelectionFromLogger = - ResearchLogger.getAndClearLatinIMEExpectingUpdateSelection(); ResearchLogger.latinIME_onUpdateSelection(mLastSelectionStart, mLastSelectionEnd, oldSelStart, oldSelEnd, newSelStart, newSelEnd, composingSpanStart, - composingSpanEnd, mExpectingUpdateSelection, - expectingUpdateSelectionFromLogger, mConnection); - if (expectingUpdateSelectionFromLogger) { - // TODO: Investigate. Quitting now sounds wrong - we won't do the resetting work - return; - } + composingSpanEnd, mConnection); } final boolean selectionChanged = mLastSelectionStart != newSelStart @@ -1108,14 +1106,8 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen // TODO: revisit this when LatinIME supports hardware keyboards. // NOTE: the test harness subclasses LatinIME and overrides isInputViewShown(). // TODO: find a better way to simulate actual execution. - if (isInputViewShown() && !mExpectingUpdateSelection - && !mConnection.isBelatedExpectedUpdate(oldSelStart, newSelStart)) { - // TAKE CARE: there is a race condition when we enter this test even when the user - // did not explicitly move the cursor. This happens when typing fast, where two keys - // turn this flag on in succession and both onUpdateSelection() calls arrive after - // the second one - the first call successfully avoids this test, but the second one - // enters. For the moment we rely on noComposingSpan to further reduce the impact. - + if (isInputViewShown() && !mConnection.isBelatedExpectedUpdate(oldSelStart, newSelStart, + oldSelEnd, newSelEnd)) { // TODO: the following is probably better done in resetEntireInputState(). // it should only happen when the cursor moved, and the very purpose of the // test below is to narrow down whether this happened or not. Likewise with @@ -1141,13 +1133,13 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen // Another option would be to send suggestions each time we set the composing // text, but that is probably too expensive to do, so we decided to leave things // as is. - resetEntireInputState(newSelStart); + resetEntireInputState(newSelStart, newSelEnd); } else { - // resetEntireInputState calls resetCachesUponCursorMove, but with the second - // argument as true. But in all cases where we don't reset the entire input state, - // we still want to tell the rich input connection about the new cursor position so - // that it can update its caches. - mConnection.resetCachesUponCursorMoveAndReturnSuccess(newSelStart, + // resetEntireInputState calls resetCachesUponCursorMove, but forcing the + // composition to end. But in all cases where we don't reset the entire input + // state, we still want to tell the rich input connection about the new cursor + // position so that it can update its caches. + mConnection.resetCachesUponCursorMoveAndReturnSuccess(newSelStart, newSelEnd, false /* shouldFinishComposition */); } @@ -1160,7 +1152,6 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen mRecapitalizeStatus.deactivate(); mKeyboardSwitcher.updateShiftState(); } - mExpectingUpdateSelection = false; // Make a note of the cursor position mLastSelectionStart = newSelStart; @@ -1261,9 +1252,7 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen final boolean needsInputViewShown) { // TODO: Modify this if we support suggestions with hard keyboard if (onEvaluateInputViewShown() && mSuggestionStripView != null) { - final MainKeyboardView mainKeyboardView = mKeyboardSwitcher.getMainKeyboardView(); - final boolean inputViewShown = (mainKeyboardView != null) - ? mainKeyboardView.isShown() : false; + final boolean inputViewShown = mKeyboardSwitcher.isShowingMainKeyboardOrEmojiPalettes(); final boolean shouldShowSuggestions = shown && (needsInputViewShown ? inputViewShown : true); if (isFullscreenMode()) { @@ -1329,7 +1318,7 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen if (visibleKeyboardView.isShown()) { // Note that the height of Emoji layout is the same as the height of the main keyboard // and the suggestion strip - if (mKeyboardSwitcher.isShowingEmojiKeyboard() + if (mKeyboardSwitcher.isShowingEmojiPalettes() || mSuggestionStripView.getVisibility() == View.VISIBLE) { visibleTopY -= suggestionsHeight; } @@ -1348,8 +1337,7 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen @Override public boolean onEvaluateFullscreenMode() { // Reread resource value here, because this method is called by framework anytime as needed. - final boolean isFullscreenModeAllowed = - Settings.readUseFullscreenMode(getResources()); + final boolean isFullscreenModeAllowed = Settings.readUseFullscreenMode(getResources()); if (super.onEvaluateFullscreenMode() && isFullscreenModeAllowed) { // TODO: Remove this hack. Actually we should not really assume NO_EXTRACT_UI // implies NO_FULLSCREEN. However, the framework mistakenly does. i.e. NO_EXTRACT_UI @@ -1374,7 +1362,7 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen // This will reset the whole input state to the starting state. It will clear // the composing word, reset the last composed word, tell the inputconnection about it. - private void resetEntireInputState(final int newCursorPosition) { + private void resetEntireInputState(final int newSelStart, final int newSelEnd) { final boolean shouldFinishComposition = mWordComposer.isComposingWord(); resetComposingState(true /* alsoResetLastComposedWord */); final SettingsValues settingsValues = mSettings.getCurrent(); @@ -1383,14 +1371,15 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen } else { setSuggestedWords(settingsValues.mSuggestPuncList, false); } - mConnection.resetCachesUponCursorMoveAndReturnSuccess(newCursorPosition, + mConnection.resetCachesUponCursorMoveAndReturnSuccess(newSelStart, newSelEnd, shouldFinishComposition); } private void resetComposingState(final boolean alsoResetLastComposedWord) { mWordComposer.reset(); - if (alsoResetLastComposedWord) + if (alsoResetLastComposedWord) { mLastComposedWord = LastComposedWord.NOT_A_COMPOSED_WORD; + } } private void commitTyped(final String separatorString) { @@ -1437,15 +1426,16 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen if (0 != (auto & TextUtils.CAP_MODE_CHARACTERS)) { return WordComposer.CAPS_MODE_AUTO_SHIFT_LOCKED; } - if (0 != auto) return WordComposer.CAPS_MODE_AUTO_SHIFTED; + if (0 != auto) { + return WordComposer.CAPS_MODE_AUTO_SHIFTED; + } return WordComposer.CAPS_MODE_OFF; } private void swapSwapperAndSpace() { final CharSequence lastTwo = mConnection.getTextBeforeCursor(2, 0); // It is guaranteed lastTwo.charAt(1) is a swapper - else this method is not called. - if (lastTwo != null && lastTwo.length() == 2 - && lastTwo.charAt(0) == Constants.CODE_SPACE) { + if (lastTwo != null && lastTwo.length() == 2 && lastTwo.charAt(0) == Constants.CODE_SPACE) { mConnection.deleteSurroundingText(2, 0); final String text = lastTwo.charAt(1) + " "; mConnection.commitText(text, 1); @@ -1458,7 +1448,6 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen private boolean maybeDoubleSpacePeriod() { final SettingsValues currentSettingsValues = mSettings.getCurrent(); - if (!currentSettingsValues.mCorrectionEnabled) return false; if (!currentSettingsValues.mUseDoubleSpacePeriod) return false; if (!mHandler.isAcceptingDoubleSpacePeriod()) return false; // We only do this when we see two spaces and an accepted code point before the cursor. @@ -1503,6 +1492,7 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen || codePoint == Constants.CODE_CLOSING_CURLY_BRACKET || codePoint == Constants.CODE_CLOSING_ANGLE_BRACKET || codePoint == Constants.CODE_PLUS + || codePoint == Constants.CODE_PERCENT || Character.getType(codePoint) == Character.OTHER_SYMBOL; } @@ -1588,8 +1578,7 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen // relying on this behavior so we continue to support it for older apps. sendDownUpKeyEvent(KeyEvent.KEYCODE_ENTER); } else { - final String text = new String(new int[] { code }, 0, 1); - mConnection.commitText(text, text.length()); + mConnection.commitText(StringUtils.newSingleCodePointString(code), 1); } } @@ -1624,8 +1613,6 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen case Constants.CODE_DELETE: mSpaceState = SPACE_STATE_NONE; handleBackspace(spaceState); - mDeleteCount++; - mExpectingUpdateSelection = true; LatinImeLogger.logOnDelete(x, y); break; case Constants.CODE_SHIFT: @@ -1712,7 +1699,8 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen mSpaceState = SPACE_STATE_NONE; final boolean didAutoCorrect; final SettingsValues settingsValues = mSettings.getCurrent(); - if (settingsValues.isWordSeparator(primaryCode)) { + if (settingsValues.isWordSeparator(primaryCode) + || Character.getType(primaryCode) == Character.OTHER_SYMBOL) { didAutoCorrect = handleSeparator(primaryCode, x, y, spaceState); } else { didAutoCorrect = false; @@ -1726,7 +1714,7 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen if (mWordComposer.isCursorFrontOrMiddleOfComposingWord()) { // If we are in the middle of a recorrection, we need to commit the recorrection // first so that we can insert the character at the current cursor position. - resetEntireInputState(mLastSelectionStart); + resetEntireInputState(mLastSelectionStart, mLastSelectionEnd); } else { commitTyped(LastComposedWord.NOT_A_SEPARATOR); } @@ -1742,7 +1730,6 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen } handleCharacter(primaryCode, keyX, keyY, spaceState); } - mExpectingUpdateSelection = true; return didAutoCorrect; } @@ -1782,9 +1769,9 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen mInputUpdater.onStartBatchInput(); mHandler.cancelUpdateSuggestionStrip(); mConnection.beginBatchEdit(); - final SettingsValues settingsValues = mSettings.getCurrent(); + final SettingsValues currentSettingsValues = mSettings.getCurrent(); if (mWordComposer.isComposingWord()) { - if (settingsValues.mIsInternal) { + if (currentSettingsValues.mIsInternal) { if (mWordComposer.isBatchMode()) { LatinImeLoggerUtils.onAutoCorrection( "", mWordComposer.getTypedWord(), " ", mWordComposer); @@ -1795,7 +1782,7 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen if (mWordComposer.isCursorFrontOrMiddleOfComposingWord()) { // If we are in the middle of a recorrection, we need to commit the recorrection // first so that we can insert the batch input at the current cursor position. - resetEntireInputState(mLastSelectionStart); + resetEntireInputState(mLastSelectionStart, mLastSelectionEnd); } else if (wordComposerSize <= 1) { // We auto-correct the previous (typed, not gestured) string iff it's one character // long. The reason for this is, even in the middle of gesture typing, you'll still @@ -1808,24 +1795,26 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen } else { commitTyped(LastComposedWord.NOT_A_SEPARATOR); } - mExpectingUpdateSelection = true; } final int codePointBeforeCursor = mConnection.getCodePointBeforeCursor(); if (Character.isLetterOrDigit(codePointBeforeCursor) - || settingsValues.isUsuallyFollowedBySpace(codePointBeforeCursor)) { + || currentSettingsValues.isUsuallyFollowedBySpace(codePointBeforeCursor)) { mSpaceState = SPACE_STATE_PHANTOM; } mConnection.endBatchEdit(); - mWordComposer.setCapitalizedModeAtStartComposingTime(getActualCapsMode()); + mKeyboardSwitcher.updateShiftState(); + mWordComposer.setCapitalizedModeAndPreviousWordAtStartComposingTime(getActualCapsMode(), + // Prev word is 1st word before cursor + getNthPreviousWordForSuggestion(currentSettingsValues, 1 /* nthPreviousWord */)); } - private static final class InputUpdater implements Handler.Callback { + static final class InputUpdater implements Handler.Callback { private final Handler mHandler; private final LatinIME mLatinIme; private final Object mLock = new Object(); private boolean mInBatchInput; // synchronized using {@link #mLock}. - private InputUpdater(final LatinIME latinIme) { + InputUpdater(final LatinIME latinIme) { final HandlerThread handlerThread = new HandlerThread( InputUpdater.class.getSimpleName()); handlerThread.start(); @@ -1838,12 +1827,15 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen @Override public boolean handleMessage(final Message msg) { + // TODO: straighten message passing - we don't need two kinds of messages calling + // each other. switch (msg.what) { case MSG_UPDATE_GESTURE_PREVIEW_AND_SUGGESTION_STRIP: - updateBatchInput((InputPointers)msg.obj); + updateBatchInput((InputPointers)msg.obj, msg.arg2 /* sequenceNumber */); break; case MSG_GET_SUGGESTED_WORDS: - mLatinIme.getSuggestedWords(msg.arg1, (OnGetSuggestedWordsCallback) msg.obj); + mLatinIme.getSuggestedWords(msg.arg1 /* sessionId */, + msg.arg2 /* sequenceNumber */, (OnGetSuggestedWordsCallback) msg.obj); break; } return true; @@ -1860,14 +1852,15 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen } // Run in the Handler thread. - private void updateBatchInput(final InputPointers batchPointers) { + private void updateBatchInput(final InputPointers batchPointers, final int sequenceNumber) { synchronized (mLock) { if (!mInBatchInput) { // Batch input has ended or canceled while the message was being delivered. return; } - getSuggestedWordsGestureLocked(batchPointers, new OnGetSuggestedWordsCallback() { + getSuggestedWordsGestureLocked(batchPointers, sequenceNumber, + new OnGetSuggestedWordsCallback() { @Override public void onGetSuggestedWords(final SuggestedWords suggestedWords) { mLatinIme.mHandler.showGesturePreviewAndSuggestionStrip( @@ -1878,13 +1871,13 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen } // Run in the UI thread. - public void onUpdateBatchInput(final InputPointers batchPointers) { + public void onUpdateBatchInput(final InputPointers batchPointers, + final int sequenceNumber) { if (mHandler.hasMessages(MSG_UPDATE_GESTURE_PREVIEW_AND_SUGGESTION_STRIP)) { return; } - mHandler.obtainMessage( - MSG_UPDATE_GESTURE_PREVIEW_AND_SUGGESTION_STRIP, batchPointers) - .sendToTarget(); + mHandler.obtainMessage(MSG_UPDATE_GESTURE_PREVIEW_AND_SUGGESTION_STRIP, 0 /* arg1 */, + sequenceNumber /* arg2 */, batchPointers /* obj */).sendToTarget(); } public void onCancelBatchInput() { @@ -1898,7 +1891,8 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen // Run in the UI thread. public void onEndBatchInput(final InputPointers batchPointers) { synchronized(mLock) { - getSuggestedWordsGestureLocked(batchPointers, new OnGetSuggestedWordsCallback() { + getSuggestedWordsGestureLocked(batchPointers, SuggestedWords.NOT_A_SEQUENCE_NUMBER, + new OnGetSuggestedWordsCallback() { @Override public void onGetSuggestedWords(final SuggestedWords suggestedWords) { mInBatchInput = false; @@ -1913,10 +1907,10 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen // {@link LatinIME#getSuggestedWords(int)} method calls with same session id have to // be synchronized. private void getSuggestedWordsGestureLocked(final InputPointers batchPointers, - final OnGetSuggestedWordsCallback callback) { + final int sequenceNumber, final OnGetSuggestedWordsCallback callback) { mLatinIme.mWordComposer.setBatchInputPointers(batchPointers); mLatinIme.getSuggestedWordsOrOlderSuggestionsAsync(Suggest.SESSION_GESTURE, - new OnGetSuggestedWordsCallback() { + sequenceNumber, new OnGetSuggestedWordsCallback() { @Override public void onGetSuggestedWords(SuggestedWords suggestedWords) { final int suggestionCount = suggestedWords.size(); @@ -1931,12 +1925,13 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen }); } - public void getSuggestedWords(final int sessionId, + public void getSuggestedWords(final int sessionId, final int sequenceNumber, final OnGetSuggestedWordsCallback callback) { - mHandler.obtainMessage(MSG_GET_SUGGESTED_WORDS, sessionId, 0, callback).sendToTarget(); + mHandler.obtainMessage(MSG_GET_SUGGESTED_WORDS, sessionId, sequenceNumber, callback) + .sendToTarget(); } - private void onDestroy() { + void quitLooper() { mHandler.removeMessages(MSG_GET_SUGGESTED_WORDS); mHandler.removeMessages(MSG_UPDATE_GESTURE_PREVIEW_AND_SUGGESTION_STRIP); mHandler.getLooper().quit(); @@ -1954,11 +1949,28 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen } } + /* The sequence number member is only used in onUpdateBatchInput. It is increased each time + * auto-commit happens. The reason we need this is, when auto-commit happens we trim the + * input pointers that are held in a singleton, and to know how much to trim we rely on the + * results of the suggestion process that is held in mSuggestedWords. + * However, the suggestion process is asynchronous, and sometimes we may enter the + * onUpdateBatchInput method twice without having recomputed suggestions yet, or having + * received new suggestions generated from not-yet-trimmed input pointers. In this case, the + * mIndexOfTouchPointOfSecondWords member will be out of date, and we must not use it lest we + * remove an unrelated number of pointers (possibly even more than are left in the input + * pointers, leading to a crash). + * To avoid that, we increase the sequence number each time we auto-commit and trim the + * input pointers, and we do not use any suggested words that have been generated with an + * earlier sequence number. + */ + private int mAutoCommitSequenceNumber = 1; @Override public void onUpdateBatchInput(final InputPointers batchPointers) { if (mSettings.getCurrent().mPhraseGestureEnabled) { final SuggestedWordInfo candidate = mSuggestedWords.getAutoCommitCandidate(); - if (null != candidate) { + // If these suggested words have been generated with out of date input pointers, then + // we skip auto-commit (see comments above on the mSequenceNumber member). + if (null != candidate && mSuggestedWords.mSequenceNumber >= mAutoCommitSequenceNumber) { if (candidate.mSourceDict.shouldAutoCommit(candidate)) { final String[] commitParts = candidate.mWord.split(" ", 2); batchPointers.shift(candidate.mIndexOfTouchPointOfSecondWord); @@ -1966,17 +1978,18 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen mConnection.commitText(commitParts[0], 0); mSpaceState = SPACE_STATE_PHANTOM; mKeyboardSwitcher.updateShiftState(); - mWordComposer.setCapitalizedModeAtStartComposingTime(getActualCapsMode()); + mWordComposer.setCapitalizedModeAndPreviousWordAtStartComposingTime( + getActualCapsMode(), commitParts[0]); + ++mAutoCommitSequenceNumber; } } } - mInputUpdater.onUpdateBatchInput(batchPointers); + mInputUpdater.onUpdateBatchInput(batchPointers, mAutoCommitSequenceNumber); } // This method must run in UI Thread. public void onEndBatchInputAsyncInternal(final SuggestedWords suggestedWords) { - final String batchInputText = suggestedWords.isEmpty() - ? null : suggestedWords.getWord(0); + final String batchInputText = suggestedWords.isEmpty() ? null : suggestedWords.getWord(0); if (TextUtils.isEmpty(batchInputText)) { return; } @@ -1998,7 +2011,6 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen mWordComposer.setBatchInputWord(batchInputText); mConnection.setComposingText(batchInputText, 1); } - mExpectingUpdateSelection = true; mConnection.endBatchEdit(); if (ProductionFlag.USES_DEVELOPMENT_ONLY_DIAGNOSTICS) { ResearchLogger.latinIME_onEndBatchInput(batchInputText, 0, suggestedWords); @@ -2052,6 +2064,8 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen } private void handleBackspace(final int spaceState) { + mDeleteCount++; + // In many cases, we may have to put the keyboard in auto-shift state again. However // we want to wait a few milliseconds before doing it to avoid the keyboard flashing // during key repeat. @@ -2060,7 +2074,7 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen if (mWordComposer.isCursorFrontOrMiddleOfComposingWord()) { // If we are in the middle of a recorrection, we need to commit the recorrection // first so that we can remove the character at the current cursor position. - resetEntireInputState(mLastSelectionStart); + resetEntireInputState(mLastSelectionStart, mLastSelectionEnd); // When we exit this if-clause, mWordComposer.isComposingWord() will return false. } if (mWordComposer.isComposingWord()) { @@ -2141,8 +2155,6 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen // This should never happen. Log.e(TAG, "Backspace when we don't know the selection position"); } - final int lengthToDelete = Character.isSupplementaryCodePoint( - mConnection.getCodePointBeforeCursor()) ? 2 : 1; if (mAppWorkAroundsUtils.isBeforeJellyBean() || currentSettings.mInputAttributes.isTypeNull()) { // There are two possible reasons to send a key event: either the field has @@ -2153,21 +2165,35 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen // applications are relying on this behavior so we continue to support it for // older apps, so we retain this behavior if the app has target SDK < JellyBean. sendDownUpKeyEvent(KeyEvent.KEYCODE_DEL); + if (mDeleteCount > DELETE_ACCELERATE_AT) { + sendDownUpKeyEvent(KeyEvent.KEYCODE_DEL); + } } else { + final int codePointBeforeCursor = mConnection.getCodePointBeforeCursor(); + if (codePointBeforeCursor == Constants.NOT_A_CODE) { + // Nothing to delete before the cursor. + return; + } + final int lengthToDelete = + Character.isSupplementaryCodePoint(codePointBeforeCursor) ? 2 : 1; mConnection.deleteSurroundingText(lengthToDelete, 0); - } - if (ProductionFlag.USES_DEVELOPMENT_ONLY_DIAGNOSTICS) { - ResearchLogger.latinIME_handleBackspace(lengthToDelete, - true /* shouldUncommitLogUnit */); - } - if (mDeleteCount > DELETE_ACCELERATE_AT) { - final int lengthToDeleteAgain = Character.isSupplementaryCodePoint( - mConnection.getCodePointBeforeCursor()) ? 2 : 1; - mConnection.deleteSurroundingText(lengthToDeleteAgain, 0); if (ProductionFlag.USES_DEVELOPMENT_ONLY_DIAGNOSTICS) { - ResearchLogger.latinIME_handleBackspace(lengthToDeleteAgain, + ResearchLogger.latinIME_handleBackspace(lengthToDelete, true /* shouldUncommitLogUnit */); } + if (mDeleteCount > DELETE_ACCELERATE_AT) { + final int codePointBeforeCursorToDeleteAgain = + mConnection.getCodePointBeforeCursor(); + if (codePointBeforeCursorToDeleteAgain != Constants.NOT_A_CODE) { + final int lengthToDeleteAgain = Character.isSupplementaryCodePoint( + codePointBeforeCursorToDeleteAgain) ? 2 : 1; + mConnection.deleteSurroundingText(lengthToDeleteAgain, 0); + if (ProductionFlag.USES_DEVELOPMENT_ONLY_DIAGNOSTICS) { + ResearchLogger.latinIME_handleBackspace(lengthToDeleteAgain, + true /* shouldUncommitLogUnit */); + } + } + } } } if (currentSettings.isSuggestionsRequested(mDisplayOrientation) @@ -2182,8 +2208,8 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen /* * Strip a trailing space if necessary and returns whether it's a swap weak space situation. */ - private boolean maybeStripSpace(final int code, - final int spaceState, final boolean isFromSuggestionStrip) { + private boolean maybeStripSpace(final int code, final int spaceState, + final boolean isFromSuggestionStrip) { if (Constants.CODE_ENTER == code && SPACE_STATE_SWAP_PUNCTUATION == spaceState) { mConnection.removeTrailingSpace(); return false; @@ -2198,8 +2224,8 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen return false; } - private void handleCharacter(final int primaryCode, final int x, - final int y, final int spaceState) { + private void handleCharacter(final int primaryCode, final int x, final int y, + final int spaceState) { // TODO: refactor this method to stop flipping isComposingWord around all the time, and // make it shorter (possibly cut into several pieces). Also factor handleNonSpecialCharacter // which has the same name as other handle* methods but is not the same. @@ -2219,7 +2245,7 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen if (mWordComposer.isCursorFrontOrMiddleOfComposingWord()) { // If we are in the middle of a recorrection, we need to commit the recorrection // first so that we can insert the character at the current cursor position. - resetEntireInputState(mLastSelectionStart); + resetEntireInputState(mLastSelectionStart, mLastSelectionEnd); isComposingWord = false; } // We want to find out whether to start composing a new word with this character. If so, @@ -2249,25 +2275,24 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen resetComposingState(false /* alsoResetLastComposedWord */); } if (isComposingWord) { - final int keyX, keyY; - if (Constants.isValidCoordinate(x) && Constants.isValidCoordinate(y)) { - final KeyDetector keyDetector = - mKeyboardSwitcher.getMainKeyboardView().getKeyDetector(); - keyX = keyDetector.getTouchX(x); - keyY = keyDetector.getTouchY(y); - } else { - keyX = x; - keyY = y; - } + final MainKeyboardView mainKeyboardView = mKeyboardSwitcher.getMainKeyboardView(); + // TODO: We should reconsider which coordinate system should be used to represent + // keyboard event. + final int keyX = mainKeyboardView.getKeyX(x); + final int keyY = mainKeyboardView.getKeyY(y); mWordComposer.add(primaryCode, keyX, keyY); // If it's the first letter, make note of auto-caps state if (mWordComposer.size() == 1) { - mWordComposer.setCapitalizedModeAtStartComposingTime(getActualCapsMode()); + // We pass 1 to getPreviousWordForSuggestion because we were not composing a word + // yet, so the word we want is the 1st word before the cursor. + mWordComposer.setCapitalizedModeAndPreviousWordAtStartComposingTime( + getActualCapsMode(), + getNthPreviousWordForSuggestion(currentSettings, 1 /* nthPreviousWord */)); } mConnection.setComposingText(getTextWithUnderline(mWordComposer.getTypedWord()), 1); } else { - final boolean swapWeakSpace = maybeStripSpace(primaryCode, - spaceState, Constants.SUGGESTION_STRIP_COORDINATE == x); + final boolean swapWeakSpace = maybeStripSpace(primaryCode, spaceState, + Constants.SUGGESTION_STRIP_COORDINATE == x); sendKeyCodePoint(primaryCode); @@ -2331,16 +2356,16 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen if (mWordComposer.isCursorFrontOrMiddleOfComposingWord()) { // If we are in the middle of a recorrection, we need to commit the recorrection // first so that we can insert the separator at the current cursor position. - resetEntireInputState(mLastSelectionStart); + resetEntireInputState(mLastSelectionStart, mLastSelectionEnd); } if (mWordComposer.isComposingWord()) { // May have changed since we stored wasComposing if (currentSettings.mCorrectionEnabled) { final String separator = shouldAvoidSendingCode ? LastComposedWord.NOT_A_SEPARATOR - : new String(new int[] { primaryCode }, 0, 1); + : StringUtils.newSingleCodePointString(primaryCode); commitCurrentAutoCorrection(separator); didAutoCorrect = true; } else { - commitTyped(new String(new int[]{primaryCode}, 0, 1)); + commitTyped(StringUtils.newSingleCodePointString(primaryCode)); } } @@ -2489,7 +2514,7 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen final AsyncResultHolder<SuggestedWords> holder = new AsyncResultHolder<SuggestedWords>(); getSuggestedWordsOrOlderSuggestionsAsync(Suggest.SESSION_TYPING, - new OnGetSuggestedWordsCallback() { + SuggestedWords.NOT_A_SEQUENCE_NUMBER, new OnGetSuggestedWordsCallback() { @Override public void onGetSuggestedWords(final SuggestedWords suggestedWords) { holder.set(suggestedWords); @@ -2504,7 +2529,25 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen } } - private void getSuggestedWords(final int sessionId, + /** + * Get the nth previous word before the cursor as context for the suggestion process. + * @param currentSettings the current settings values. + * @param nthPreviousWord reverse index of the word to get (1-indexed) + * @return the nth previous word before the cursor. + */ + private String getNthPreviousWordForSuggestion(final SettingsValues currentSettings, + final int nthPreviousWord) { + if (currentSettings.mCurrentLanguageHasSpaces) { + // If we are typing in a language with spaces we can just look up the previous + // word from textview. + return mConnection.getNthPreviousWord(currentSettings, nthPreviousWord); + } else { + return LastComposedWord.NOT_A_COMPOSED_WORD == mLastComposedWord ? null + : mLastComposedWord.mCommittedWord; + } + } + + private void getSuggestedWords(final int sessionId, final int sequenceNumber, final OnGetSuggestedWordsCallback callback) { final Keyboard keyboard = mKeyboardSwitcher.getKeyboard(); final Suggest suggest = mSuggest; @@ -2517,30 +2560,45 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen // should just skip whitespace if any, so 1. final SettingsValues currentSettings = mSettings.getCurrent(); final int[] additionalFeaturesOptions = currentSettings.mAdditionalFeaturesSettingValues; - final String prevWord; - if (currentSettings.mCurrentLanguageHasSpaces) { - // If we are typing in a language with spaces we can just look up the previous - // word from textview. - prevWord = mConnection.getNthPreviousWord(currentSettings.mWordSeparators, - mWordComposer.isComposingWord() ? 2 : 1); + + final String previousWord; + if (mWordComposer.isComposingWord() || mWordComposer.isBatchMode()) { + previousWord = mWordComposer.getPreviousWord(); } else { - prevWord = LastComposedWord.NOT_A_COMPOSED_WORD == mLastComposedWord ? null - : mLastComposedWord.mCommittedWord; + // Not composing: this is for prediction. + // TODO: read the previous word earlier for prediction, like we are doing for + // normal suggestions. + previousWord = getNthPreviousWordForSuggestion(currentSettings, 1 /* nthPreviousWord*/); + } + if (DEBUG) { + // TODO: this is for checking consistency with older versions. Remove this when + // we are confident this is stable. + // We're checking the previous word in the text field against the memorized previous + // word. If we are composing a word we should have the second word before the cursor + // memorized, otherwise we should have the first. + final String rereadPrevWord = getNthPreviousWordForSuggestion(currentSettings, + mWordComposer.isComposingWord() ? 2 : 1); + if (!TextUtils.equals(previousWord, rereadPrevWord)) { + throw new RuntimeException("Unexpected previous word: " + + previousWord + " <> " + rereadPrevWord); + } } - suggest.getSuggestedWords(mWordComposer, prevWord, keyboard.getProximityInfo(), + suggest.getSuggestedWords(mWordComposer, mWordComposer.getPreviousWord(), + keyboard.getProximityInfo(), currentSettings.mBlockPotentiallyOffensive, currentSettings.mCorrectionEnabled, - additionalFeaturesOptions, sessionId, callback); + additionalFeaturesOptions, sessionId, sequenceNumber, callback); } private void getSuggestedWordsOrOlderSuggestionsAsync(final int sessionId, - final OnGetSuggestedWordsCallback callback) { - mInputUpdater.getSuggestedWords(sessionId, new OnGetSuggestedWordsCallback() { - @Override - public void onGetSuggestedWords(SuggestedWords suggestedWords) { - callback.onGetSuggestedWords(maybeRetrieveOlderSuggestions( - mWordComposer.getTypedWord(), suggestedWords)); - } - }); + final int sequenceNumber, final OnGetSuggestedWordsCallback callback) { + mInputUpdater.getSuggestedWords(sessionId, sequenceNumber, + new OnGetSuggestedWordsCallback() { + @Override + public void onGetSuggestedWords(SuggestedWords suggestedWords) { + callback.onGetSuggestedWords(maybeRetrieveOlderSuggestions( + mWordComposer.getTypedWord(), suggestedWords)); + } + }); } private SuggestedWords maybeRetrieveOlderSuggestions(final String typedWord, @@ -2643,7 +2701,6 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen ResearchLogger.latinIme_commitCurrentAutoCorrection(typedWord, autoCorrection, separator, mWordComposer.isBatchMode(), suggestedWords); } - mExpectingUpdateSelection = true; commitChosenWord(autoCorrection, LastComposedWord.COMMIT_TYPE_DECIDED_WORD, separator); if (!typedWord.equals(autoCorrection)) { @@ -2714,7 +2771,6 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen // typed word. final String replacedWord = mWordComposer.getTypedWord(); LatinImeLogger.logOnManualSuggestion(replacedWord, suggestion, index, suggestedWords); - mExpectingUpdateSelection = true; commitChosenWord(suggestion, LastComposedWord.COMMIT_TYPE_MANUAL_PICK, LastComposedWord.NOT_A_SEPARATOR); if (ProductionFlag.USES_DEVELOPMENT_ONLY_DIAGNOSTICS) { @@ -2797,7 +2853,7 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen final UserHistoryDictionary userHistoryDictionary = mUserHistoryDictionary; if (userHistoryDictionary == null) return null; - final String prevWord = mConnection.getNthPreviousWord(currentSettings.mWordSeparators, 2); + final String prevWord = mConnection.getNthPreviousWord(currentSettings, 2); final String secondWord; if (mWordComposer.wasAutoCapitalized() && !mWordComposer.isMostlyCaps()) { secondWord = suggestion.toLowerCase(mSubtypeSwitcher.getCurrentSubtypeLocale()); @@ -2809,7 +2865,8 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen final int maxFreq = AutoCorrectionUtils.getMaxFrequency( suggest.getUnigramDictionaries(), suggestion); if (maxFreq == 0) return null; - userHistoryDictionary.addToDictionary(prevWord, secondWord, maxFreq > 0); + userHistoryDictionary.addToDictionary(prevWord, secondWord, maxFreq > 0, + (int)TimeUnit.MILLISECONDS.toSeconds((System.currentTimeMillis()))); return prevWord; } @@ -2865,7 +2922,13 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen } } } - mWordComposer.setComposingWord(typedWord, mKeyboardSwitcher.getKeyboard()); + mWordComposer.setComposingWord(typedWord, + 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()); mWordComposer.setCursorPositionWithinWord( typedWord.codePointCount(0, numberOfCharsInWordBeforeCursor)); mConnection.setComposingRegion( @@ -2875,30 +2938,30 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen // 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, - 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); - }}); + 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. @@ -2943,7 +3006,11 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen } private void restartSuggestionsOnWordBeforeCursor(final String word) { - mWordComposer.setComposingWord(word, mKeyboardSwitcher.getKeyboard()); + mWordComposer.setComposingWord(word, + // Previous word is the 2nd word before cursor because we are restarting on the + // 1st word before cursor. + getNthPreviousWordForSuggestion(mSettings.getCurrent(), 2 /* nthPreviousWord */), + mKeyboardSwitcher.getKeyboard()); final int length = word.length(); mConnection.deleteSurroundingText(length, 0); mConnection.setComposingText(word, 1); @@ -2961,7 +3028,8 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen * @param remainingTries How many times we may try again before giving up. */ private void retryResetCaches(final boolean tryResumeSuggestions, final int remainingTries) { - if (!mConnection.resetCachesUponCursorMoveAndReturnSuccess(mLastSelectionStart, false)) { + if (!mConnection.resetCachesUponCursorMoveAndReturnSuccess(mLastSelectionStart, + mLastSelectionEnd, false)) { if (0 < remainingTries) { mHandler.postResetCaches(tryResumeSuggestions, remainingTries - 1); return; @@ -2979,8 +3047,8 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen final String originallyTypedWord = mLastComposedWord.mTypedWord; final String committedWord = mLastComposedWord.mCommittedWord; final int cancelLength = committedWord.length(); - final int separatorLength = LastComposedWord.getSeparatorLength( - mLastComposedWord.mSeparatorString); + // We want java chars, not codepoints for the following. + final int separatorLength = mLastComposedWord.mSeparatorString.length(); // TODO: should we check our saved separator against the actual contents of the text view? final int deleteLength = cancelLength + separatorLength; if (DEBUG) { @@ -2988,8 +3056,7 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen throw new RuntimeException("revertCommit, but we are composing a word"); } final CharSequence wordBeforeCursor = - mConnection.getTextBeforeCursor(deleteLength, 0) - .subSequence(0, cancelLength); + mConnection.getTextBeforeCursor(deleteLength, 0).subSequence(0, cancelLength); if (!TextUtils.equals(committedWord, wordBeforeCursor)) { throw new RuntimeException("revertCommit check failed: we thought we were " + "reverting \"" + committedWord @@ -3009,7 +3076,8 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen } else { // For languages without spaces, we revert the typed string but the cursor is flush // with the typed word, so we need to resume suggestions right away. - mWordComposer.setComposingWord(stringToCommit, mKeyboardSwitcher.getKeyboard()); + mWordComposer.setComposingWord(stringToCommit, previousWord, + mKeyboardSwitcher.getKeyboard()); mConnection.setComposingText(stringToCommit, 1); } if (mSettings.isInternal()) { @@ -3059,8 +3127,8 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen private void hapticAndAudioFeedback(final int code, final int repeatCount) { final MainKeyboardView keyboardView = mKeyboardSwitcher.getMainKeyboardView(); - if (keyboardView != null && keyboardView.isInSlidingKeyInput()) { - // No need to feedback while sliding input. + if (keyboardView != null && keyboardView.isInDraggingFinger()) { + // No need to feedback while finger is dragging. return; } if (repeatCount > 0) { @@ -3190,8 +3258,8 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen final Intent intent = IntentUtils.getInputLanguageSelectionIntent( mRichImm.getInputMethodIdOfThisIme(), Intent.FLAG_ACTIVITY_NEW_TASK - | Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED - | Intent.FLAG_ACTIVITY_CLEAR_TOP); + | Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED + | Intent.FLAG_ACTIVITY_CLEAR_TOP); startActivity(intent); break; case 1: @@ -3200,9 +3268,8 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen } } }; - final AlertDialog.Builder builder = new AlertDialog.Builder(this) - .setItems(items, listener) - .setTitle(title); + final AlertDialog.Builder builder = + new AlertDialog.Builder(this).setItems(items, listener).setTitle(title); showOptionDialog(builder.create()); } @@ -3263,11 +3330,13 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen final Printer p = new PrintWriterPrinter(fout); p.println("LatinIME state :"); + p.println(" VersionCode = " + ApplicationUtils.getVersionCode(this)); + p.println(" VersionName = " + ApplicationUtils.getVersionName(this)); final Keyboard keyboard = mKeyboardSwitcher.getKeyboard(); final int keyboardMode = keyboard != null ? keyboard.mId.mMode : -1; p.println(" Keyboard mode = " + keyboardMode); final SettingsValues settingsValues = mSettings.getCurrent(); - p.println(" mIsSuggestionsSuggestionsRequested = " + p.println(" mIsSuggestionsRequested = " + settingsValues.isSuggestionsRequested(mDisplayOrientation)); p.println(" mCorrectionEnabled=" + settingsValues.mCorrectionEnabled); p.println(" isComposingWord=" + mWordComposer.isComposingWord()); |