diff --git a/java/src/com/android/inputmethod/latin/LastComposedWord.java b/java/src/com/android/inputmethod/latin/LastComposedWord.java index 2e9280c77..8546cebd5 100644 --- a/java/src/com/android/inputmethod/latin/LastComposedWord.java +++ b/java/src/com/android/inputmethod/latin/LastComposedWord.java @@ -42,7 +42,7 @@ public final class LastComposedWord { public final int[] mPrimaryKeyCodes; public final String mTypedWord; - public final String mCommittedWord; + public final CharSequence mCommittedWord; public final String mSeparatorString; public final String mPrevWord; public final int mCapitalizedMode; @@ -58,7 +58,7 @@ public final class LastComposedWord { // Warning: this is using the passed objects as is and fully expects them to be // immutable. Do not fiddle with their contents after you passed them to this constructor. public LastComposedWord(final int[] primaryKeyCodes, final InputPointers inputPointers, - final String typedWord, final String committedWord, final String separatorString, + final String typedWord, final CharSequence committedWord, final String separatorString, final String prevWord, final int capitalizedMode) { mPrimaryKeyCodes = primaryKeyCodes; if (inputPointers != null) { diff --git a/java/src/com/android/inputmethod/latin/LatinIME.java b/java/src/com/android/inputmethod/latin/LatinIME.java index 40391694c..fd2cd30b6 100644 --- a/java/src/com/android/inputmethod/latin/LatinIME.java +++ b/java/src/com/android/inputmethod/latin/LatinIME.java @@ -1389,7 +1389,7 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen // 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 = mInputLogic.getNthPreviousWordForSuggestion( + final CharSequence rereadPrevWord = mInputLogic.getNthPreviousWordForSuggestion( currentSettings.mSpacingAndPunctuations, mInputLogic.mWordComposer.isComposingWord() ? 2 : 1); if (!TextUtils.equals(previousWord, rereadPrevWord)) { diff --git a/java/src/com/android/inputmethod/latin/WordComposer.java b/java/src/com/android/inputmethod/latin/WordComposer.java index b4f2d1a58..ebf65630e 100644 --- a/java/src/com/android/inputmethod/latin/WordComposer.java +++ b/java/src/com/android/inputmethod/latin/WordComposer.java @@ -297,7 +297,7 @@ public final class WordComposer { * the context is nil (typically, at start of text). * @param keyboard the keyboard this is typed on, for coordinate info/proximity. */ - public void setComposingWord(final CharSequence word, final String previousWord, + public void setComposingWord(final CharSequence word, final CharSequence previousWord, final Keyboard keyboard) { reset(); final int length = word.length(); @@ -306,7 +306,7 @@ public final class WordComposer { addKeyInfo(codePoint, keyboard); } mIsResumed = true; - mPreviousWordForSuggestion = previousWord; + mPreviousWordForSuggestion = null == previousWord ? null : previousWord.toString(); } /** @@ -418,9 +418,9 @@ public final class WordComposer { * @param previousWord the previous word as context for suggestions. May be null if none. */ public void setCapitalizedModeAndPreviousWordAtStartComposingTime(final int mode, - final String previousWord) { + final CharSequence previousWord) { mCapitalizedMode = mode; - mPreviousWordForSuggestion = previousWord; + mPreviousWordForSuggestion = null == previousWord ? null : previousWord.toString(); } /** @@ -454,7 +454,8 @@ public final class WordComposer { } // `type' should be one of the LastComposedWord.COMMIT_TYPE_* constants above. - public LastComposedWord commitWord(final int type, final String committedWord, + // committedWord should contain suggestion spans if applicable. + public LastComposedWord commitWord(final int type, final CharSequence committedWord, final String separatorString, final String prevWord) { // Note: currently, we come here whenever we commit a word. If it's a MANUAL_PICK // or a DECIDED_WORD we may cancel the commit later; otherwise, we should deactivate @@ -472,7 +473,7 @@ public final class WordComposer { mCapsCount = 0; mDigitsCount = 0; mIsBatchMode = false; - mPreviousWordForSuggestion = committedWord; + mPreviousWordForSuggestion = committedWord.toString(); mTypedWord.setLength(0); mCodePointSize = 0; mTrailingSingleQuotesCount = 0; diff --git a/java/src/com/android/inputmethod/latin/inputlogic/InputLogic.java b/java/src/com/android/inputmethod/latin/inputlogic/InputLogic.java index 71b88703a..beef7612b 100644 --- a/java/src/com/android/inputmethod/latin/inputlogic/InputLogic.java +++ b/java/src/com/android/inputmethod/latin/inputlogic/InputLogic.java @@ -17,6 +17,7 @@ package com.android.inputmethod.latin.inputlogic; import android.os.SystemClock; +import android.text.SpannableString; import android.text.TextUtils; import android.text.style.SuggestionSpan; import android.util.Log; @@ -1168,8 +1169,8 @@ public final class InputLogic { // TODO: remove these arguments final KeyboardSwitcher keyboardSwitcher, final LatinIME.UIHandler handler) { final String previousWord = mLastComposedWord.mPrevWord; - final String originallyTypedWord = mLastComposedWord.mTypedWord; - final String committedWord = mLastComposedWord.mCommittedWord; + final CharSequence originallyTypedWord = mLastComposedWord.mTypedWord; + final CharSequence committedWord = mLastComposedWord.mCommittedWord; final int cancelLength = committedWord.length(); // We want java chars, not codepoints for the following. final int separatorLength = mLastComposedWord.mSeparatorString.length(); @@ -1191,28 +1192,53 @@ public final class InputLogic { if (!TextUtils.isEmpty(previousWord) && !TextUtils.isEmpty(committedWord)) { if (mSuggest != null) { mSuggest.mDictionaryFacilitator.cancelAddingUserHistory( - previousWord, committedWord); + previousWord, committedWord.toString()); + } + } + final SpannableString textToCommit = + new SpannableString(originallyTypedWord + mLastComposedWord.mSeparatorString); + if (committedWord instanceof SpannableString) { + final int lastCharIndex = textToCommit.length() - 1; + // Add the auto-correction to the list of suggestions. + textToCommit.setSpan(new SuggestionSpan(settingsValues.mLocale, + new String[] { committedWord.toString() }, 0 /* flags */), + 0 /* start */, lastCharIndex /* end */, 0 /* flags */); + final SpannableString committedWordWithSuggestionSpans = (SpannableString)committedWord; + final Object[] spans = committedWordWithSuggestionSpans.getSpans(0, + committedWord.length(), Object.class); + for (final Object span : spans) { + // Put all the spans in the original text on this new text. We could remove the + // typed word from the suggestions, but we'd have to make more dynamic instanceof + // checks, to copy the span, copy all suggestions and attributes... And there is + // the risk to drop the originally typed string if there is a subtle bug. There is + // still the committed auto-correction that we reverted from, which is not included + // in the suggestions, that's why we added it with another call to setSpan a few + // lines above. + // The code that re-reads these spans already knows to do the right thing whether + // the typed word is included or not. That should be enough. + textToCommit.setSpan(span, 0 /* start */, lastCharIndex /* end */, + committedWordWithSuggestionSpans.getSpanFlags(span)); } } - final String stringToCommit = originallyTypedWord + mLastComposedWord.mSeparatorString; if (settingsValues.mSpacingAndPunctuations.mCurrentLanguageHasSpaces) { // For languages with spaces, we revert to the typed string, but the cursor is still // after the separator so we don't resume suggestions. If the user wants to correct // the word, they have to press backspace again. - mConnection.commitText(stringToCommit, 1); + mConnection.commitText(textToCommit, 1); } 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, previousWord, + mWordComposer.setComposingWord(textToCommit, previousWord, keyboardSwitcher.getKeyboard()); - mConnection.setComposingText(stringToCommit, 1); + mConnection.setComposingText(textToCommit, 1); } if (settingsValues.mIsInternal) { LatinImeLoggerUtils.onSeparator(mLastComposedWord.mSeparatorString, Constants.NOT_A_COORDINATE, Constants.NOT_A_COORDINATE); } if (ProductionFlag.USES_DEVELOPMENT_ONLY_DIAGNOSTICS) { - ResearchLogger.latinIME_revertCommit(committedWord, originallyTypedWord, + ResearchLogger.latinIME_revertCommit(committedWord.toString(), + originallyTypedWord.toString(), mWordComposer.isBatchMode(), mLastComposedWord.mSeparatorString); } // Don't restart suggestion yet. We'll restart if the user deletes the @@ -1294,7 +1320,7 @@ public final class InputLogic { * @return the nth previous word before the cursor. */ // TODO: Make this private - public String getNthPreviousWordForSuggestion( + public CharSequence getNthPreviousWordForSuggestion( final SpacingAndPunctuations spacingAndPunctuations, final int nthPreviousWord) { if (spacingAndPunctuations.mCurrentLanguageHasSpaces) { // If we are typing in a language with spaces we can just look up the previous @@ -1645,8 +1671,10 @@ public final class InputLogic { public void commitChosenWord(final SettingsValues settingsValues, final String chosenWord, final int commitType, final String separatorString) { final SuggestedWords suggestedWords = mSuggestedWords; - mConnection.commitText(SuggestionSpanUtils.getTextWithSuggestionSpan(mLatinIME, chosenWord, - suggestedWords), 1); + final CharSequence chosenWordWithSuggestions = + SuggestionSpanUtils.getTextWithSuggestionSpan(mLatinIME, chosenWord, + suggestedWords); + mConnection.commitText(chosenWordWithSuggestions, 1); // TODO: we pass 2 here, but would it be better to move this above and pass 1 instead? final String prevWord = mConnection.getNthPreviousWord( settingsValues.mSpacingAndPunctuations, 2); @@ -1657,7 +1685,7 @@ public final class InputLogic { // LastComposedWord#didCommitTypedWord by string equality of the remembered // strings. mLastComposedWord = mWordComposer.commitWord(commitType, - chosenWord, separatorString, prevWord); + chosenWordWithSuggestions, separatorString, prevWord); final boolean shouldDiscardPreviousWordForSuggestion; if (0 == StringUtils.codePointCount(separatorString)) { // Separator is 0-length, we can keep the previous word for suggestion. Either this