diff options
Diffstat (limited to 'java/src/com/android/inputmethod/latin/LatinIME.java')
-rw-r--r-- | java/src/com/android/inputmethod/latin/LatinIME.java | 261 |
1 files changed, 105 insertions, 156 deletions
diff --git a/java/src/com/android/inputmethod/latin/LatinIME.java b/java/src/com/android/inputmethod/latin/LatinIME.java index ce306eaad..47ec40f99 100644 --- a/java/src/com/android/inputmethod/latin/LatinIME.java +++ b/java/src/com/android/inputmethod/latin/LatinIME.java @@ -165,8 +165,8 @@ public class LatinIME extends InputMethodServiceCompatWrapper implements Keyboar // Double space: the state where the user pressed space twice quickly, which LatinIME // resolved as period-space. Undoing this converts the period to a space. private static final int SPACE_STATE_DOUBLE = 1; - // Swap punctuation: the state where a (weak or magic) space and a punctuation from the - // suggestion strip have just been swapped. Undoing this swaps them back. + // Swap punctuation: the state where a weak space and a punctuation from the suggestion strip + // have just been swapped. Undoing this swaps them back; the space is still considered weak. private static final int SPACE_STATE_SWAP_PUNCTUATION = 2; // Weak space: a space that should be swapped only by suggestion strip punctuation. Weak // spaces happen when the user presses space, accepting the current suggestion (whether @@ -658,7 +658,7 @@ public class LatinIME extends InputMethodServiceCompatWrapper implements Keyboar mDisplayOrientation = conf.orientation; mHandler.startOrientationChanging(); final InputConnection ic = getCurrentInputConnection(); - commitTyped(ic); + commitTyped(ic, LastComposedWord.NOT_A_SEPARATOR); if (ic != null) ic.finishComposingText(); // For voice input if (isShowingOptionDialog()) mOptionsDialog.dismiss(); @@ -887,26 +887,16 @@ public class LatinIME extends InputMethodServiceCompatWrapper implements Keyboar // 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. + // 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 + // the call to postUpdateShiftState. // We set this to NONE because after a cursor move, we don't want the space // state-related special processing to kick in. mSpaceState = SPACE_STATE_NONE; - if (((mWordComposer.isComposingWord()) - || mVoiceProxy.isVoiceInputHighlighted()) - && (selectionChanged || noComposingSpan)) { - resetComposingState(true /* alsoResetLastComposedWord */); - updateSuggestions(); - final InputConnection ic = getCurrentInputConnection(); - if (ic != null) { - ic.finishComposingText(); - } - mComposingStateManager.onFinishComposingText(); - mVoiceProxy.setVoiceInputHighlighted(false); - } else if (!mWordComposer.isComposingWord()) { - // TODO: is the following reset still needed, given that we are not composing - // a word? - resetComposingState(true /* alsoResetLastComposedWord */); - updateSuggestions(); + if ((!mWordComposer.isComposingWord()) || selectionChanged || noComposingSpan) { + resetEntireInputState(); } mHandler.postUpdateShiftState(); @@ -1116,17 +1106,33 @@ public class LatinIME extends InputMethodServiceCompatWrapper implements Keyboar return super.onKeyUp(keyCode, event); } + // 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 + // and the composingStateManager about it. + private void resetEntireInputState() { + resetComposingState(true /* alsoResetLastComposedWord */); + updateSuggestions(); + final InputConnection ic = getCurrentInputConnection(); + if (ic != null) { + ic.finishComposingText(); + } + mComposingStateManager.onFinishComposingText(); + mVoiceProxy.setVoiceInputHighlighted(false); + } + private void resetComposingState(final boolean alsoResetLastComposedWord) { mWordComposer.reset(); if (alsoResetLastComposedWord) mLastComposedWord = LastComposedWord.NOT_A_COMPOSED_WORD; } - public void commitTyped(final InputConnection ic) { + public void commitTyped(final InputConnection ic, final int separatorCode) { if (!mWordComposer.isComposingWord()) return; final CharSequence typedWord = mWordComposer.getTypedWord(); - mLastComposedWord = mWordComposer.commitWord(LastComposedWord.COMMIT_TYPE_USER_TYPED_WORD); if (typedWord.length() > 0) { + mLastComposedWord = mWordComposer.commitWord( + LastComposedWord.COMMIT_TYPE_USER_TYPED_WORD, typedWord.toString(), + separatorCode); if (ic != null) { ic.commitText(typedWord, 1); } @@ -1242,11 +1248,8 @@ public class LatinIME extends InputMethodServiceCompatWrapper implements Keyboar KeyboardActionListener.SUGGESTION_STRIP_COORDINATE); } - private static int getEditorActionId(EditorInfo editorInfo) { - if (editorInfo == null) return 0; - return (editorInfo.actionLabel != null) - ? editorInfo.actionId - : (editorInfo.imeOptions & EditorInfo.IME_MASK_ACTION); + private static int getActionId(Keyboard keyboard) { + return keyboard != null ? keyboard.mId.imeActionId() : EditorInfo.IME_ACTION_NONE; } private void performeEditorAction(int actionId) { @@ -1312,18 +1315,13 @@ public class LatinIME extends InputMethodServiceCompatWrapper implements Keyboar mSubtypeSwitcher.switchToShortcutIME(); break; case Keyboard.CODE_ACTION_ENTER: - performeEditorAction(getEditorActionId(getCurrentInputEditorInfo())); + performeEditorAction(getActionId(switcher.getKeyboard())); + break; + case Keyboard.CODE_ACTION_NEXT: + performeEditorAction(EditorInfo.IME_ACTION_NEXT); break; - case Keyboard.CODE_TAB: - handleTab(); - // There are two cases for tab. Either we send a "next" event, that may change the - // focus but will never move the cursor. Or, we send a real tab keycode, which some - // applications may accept or ignore, and we don't know whether this will move the - // cursor or not. So actually, we don't really know. - // So to go with the safer option, we'd rather behave as if the user moved the - // cursor when they didn't than the opposite. We also expect that most applications - // will actually use tab only for focus movement. - // To sum it up: do not update mExpectingUpdateSelection here. + case Keyboard.CODE_ACTION_PREVIOUS: + EditorInfoCompatUtils.performEditorActionPrevious(getCurrentInputConnection()); break; default: mSpaceState = SPACE_STATE_NONE; @@ -1348,7 +1346,7 @@ public class LatinIME extends InputMethodServiceCompatWrapper implements Keyboar final InputConnection ic = getCurrentInputConnection(); if (ic == null) return; ic.beginBatchEdit(); - commitTyped(ic); + commitTyped(ic, LastComposedWord.NOT_A_SEPARATOR); text = specificTldProcessingOnTextInput(ic, text); if (SPACE_STATE_PHANTOM == mSpaceState) { sendKeyCodePoint(Keyboard.CODE_SPACE); @@ -1433,16 +1431,11 @@ public class LatinIME extends InputMethodServiceCompatWrapper implements Keyboar ic.deleteSurroundingText(1, 0); } } else { - // We should be very careful about auto-correction cancellation and suggestion - // resuming here. The behavior needs to be different according to text field types, - // and it would be much clearer to test for them explicitly here rather than - // relying on implicit values like "whether the suggestion strip is displayed". - if (mLastComposedWord.canCancelAutoCorrect()) { + if (mLastComposedWord.canRevertCommit()) { Utils.Stats.onAutoCorrectionCancellation(); - cancelAutoCorrect(ic); + revertCommit(ic); return; } - if (SPACE_STATE_DOUBLE == spaceState) { if (revertDoubleSpaceWhileInBatchEdit(ic)) { // No need to reset mSpaceState, it has already be done (that's why we @@ -1456,61 +1449,27 @@ public class LatinIME extends InputMethodServiceCompatWrapper implements Keyboar } } - // See the comment above: must be careful about resuming auto-suggestion. - if (mSuggestionsView != null && mSuggestionsView.dismissAddToDictionaryHint()) { - // Go back to the suggestion mode if the user canceled the - // "Touch again to save". - // NOTE: In general, we don't revert the word when backspacing - // from a manual suggestion pick. We deliberately chose a - // different behavior only in the case of picking the first - // suggestion (typed word). It's intentional to have made this - // inconsistent with backspacing after selecting other suggestions. - restartSuggestionsOnManuallyPickedTypedWord(ic); + // No cancelling of commit/double space/swap: we have a regular backspace. + // We should backspace one char and restart suggestion if at the end of a word. + if (mLastSelectionStart != mLastSelectionEnd) { + // If there is a selection, remove it. + final int lengthToDelete = mLastSelectionEnd - mLastSelectionStart; + ic.setSelection(mLastSelectionEnd, mLastSelectionEnd); + ic.deleteSurroundingText(lengthToDelete, 0); } else { - // Here we must check whether there is a selection. If so we should remove the - // selected text, otherwise we should just delete the character before the cursor. - if (mLastSelectionStart != mLastSelectionEnd) { - final int lengthToDelete = mLastSelectionEnd - mLastSelectionStart; - ic.setSelection(mLastSelectionEnd, mLastSelectionEnd); - ic.deleteSurroundingText(lengthToDelete, 0); - } else { - if (NOT_A_CURSOR_POSITION == mLastSelectionEnd) { - // This should never happen. - Log.e(TAG, "Backspace when we don't know the selection position"); - } - ic.deleteSurroundingText(1, 0); - if (mDeleteCount > DELETE_ACCELERATE_AT) { - ic.deleteSurroundingText(1, 0); - } + // There is no selection, just delete one character. + if (NOT_A_CURSOR_POSITION == mLastSelectionEnd) { + // This should never happen. + Log.e(TAG, "Backspace when we don't know the selection position"); } - if (isSuggestionsRequested()) { - restartSuggestionsOnWordBeforeCursorIfAtEndOfWord(ic); + ic.deleteSurroundingText(1, 0); + if (mDeleteCount > DELETE_ACCELERATE_AT) { + ic.deleteSurroundingText(1, 0); } } - } - } - - // TODO: Implement next and previous actions using other key code than tab's code. - private void handleTab() { - final int imeOptions = getCurrentInputEditorInfo().imeOptions; - if (!EditorInfoCompatUtils.hasFlagNavigateNext(imeOptions) - && !EditorInfoCompatUtils.hasFlagNavigatePrevious(imeOptions)) { - // TODO: This should be {@link #sendKeyCodePoint(int)}. - sendDownUpKeyEvents(KeyEvent.KEYCODE_TAB); - return; - } - - final InputConnection ic = getCurrentInputConnection(); - if (ic == null) - return; - - final Keyboard keyboard = mKeyboardSwitcher.getKeyboard(); - // True if keyboard is in either shift chording or manual shifted state. - final boolean isManualShifted = (keyboard != null && keyboard.isManualShifted()); - if (EditorInfoCompatUtils.hasFlagNavigateNext(imeOptions) && !isManualShifted) { - EditorInfoCompatUtils.performEditorActionNext(ic); - } else if (EditorInfoCompatUtils.hasFlagNavigatePrevious(imeOptions) && isManualShifted) { - EditorInfoCompatUtils.performEditorActionPrevious(ic); + if (isSuggestionsRequested()) { + restartSuggestionsOnWordBeforeCursorIfAtEndOfWord(ic); + } } } @@ -1523,10 +1482,10 @@ public class LatinIME extends InputMethodServiceCompatWrapper implements Keyboar } else if ((SPACE_STATE_WEAK == spaceState || SPACE_STATE_SWAP_PUNCTUATION == spaceState) && isFromSuggestionStrip) { - if (mSettingsValues.isMagicSpaceSwapper(code)) { + if (mSettingsValues.isWeakSpaceSwapper(code)) { return true; } else { - if (mSettingsValues.isMagicSpaceStripper(code)) { + if (mSettingsValues.isWeakSpaceStripper(code)) { removeTrailingSpaceWhileInBatchEdit(ic); } return false; @@ -1599,6 +1558,15 @@ public class LatinIME extends InputMethodServiceCompatWrapper implements Keyboar swapSwapperAndSpaceWhileInBatchEdit(ic); mSpaceState = SPACE_STATE_WEAK; } + // Some characters are not word separators, yet they don't start a new + // composing span. For these, we haven't changed the suggestion strip, and + // if the "add to dictionary" hint is shown, we should do so now. Examples of + // such characters include single quote, dollar, and others; the exact list is + // the list of characters for which we enter handleCharacterWhileInBatchEdit + // that don't match the test if ((isAlphabet...)) at the top of this method. + if (null != mSuggestionsView && mSuggestionsView.dismissAddToDictionaryHint()) { + mHandler.postUpdateBigramPredictions(); + } } Utils.Stats.onNonSeparator((char)primaryCode, x, y); } @@ -1632,7 +1600,7 @@ public class LatinIME extends InputMethodServiceCompatWrapper implements Keyboar commitCurrentAutoCorrection(primaryCode, ic); didAutoCorrect = true; } else { - commitTyped(ic); + commitTyped(ic, primaryCode); } } @@ -1689,7 +1657,7 @@ public class LatinIME extends InputMethodServiceCompatWrapper implements Keyboar } private void handleClose() { - commitTyped(getCurrentInputConnection()); + commitTyped(getCurrentInputConnection(), LastComposedWord.NOT_A_SEPARATOR); mVoiceProxy.handleClose(); requestHideSelf(0); LatinKeyboardView inputView = mKeyboardSwitcher.getKeyboardView(); @@ -1900,7 +1868,8 @@ public class LatinIME extends InputMethodServiceCompatWrapper implements Keyboar } Utils.Stats.onAutoCorrection(typedWord, autoCorrection.toString(), separatorCodePoint); mExpectingUpdateSelection = true; - commitChosenWord(autoCorrection, LastComposedWord.COMMIT_TYPE_DECIDED_WORD); + commitChosenWord(autoCorrection, LastComposedWord.COMMIT_TYPE_DECIDED_WORD, + separatorCodePoint); // Add the word to the user unigram dictionary if it's not a known word addToUserUnigramAndBigramDictionaries(autoCorrection, UserUnigramDictionary.FREQUENCY_FOR_TYPED); @@ -1955,7 +1924,8 @@ public class LatinIME extends InputMethodServiceCompatWrapper implements Keyboar LatinImeLogger.logOnManualSuggestion(mWordComposer.getTypedWord().toString(), suggestion.toString(), index, suggestions.mWords); mExpectingUpdateSelection = true; - commitChosenWord(suggestion, LastComposedWord.COMMIT_TYPE_MANUAL_PICK); + commitChosenWord(suggestion, LastComposedWord.COMMIT_TYPE_MANUAL_PICK, + LastComposedWord.NOT_A_SEPARATOR); // Add the word to the auto dictionary if it's not a known word if (index == 0) { addToUserUnigramAndBigramDictionaries(suggestion, @@ -2005,7 +1975,8 @@ public class LatinIME extends InputMethodServiceCompatWrapper implements Keyboar /** * Commits the chosen word to the text field and saves it for later retrieval. */ - private void commitChosenWord(final CharSequence bestWord, final int commitType) { + private void commitChosenWord(final CharSequence bestWord, final int commitType, + final int separatorCode) { final InputConnection ic = getCurrentInputConnection(); if (ic != null) { mVoiceProxy.rememberReplacedWord(bestWord, mSettingsValues.mWordSeparators); @@ -2019,9 +1990,10 @@ public class LatinIME extends InputMethodServiceCompatWrapper implements Keyboar } // TODO: figure out here if this is an auto-correct or if the best word is actually // what user typed. Note: currently this is done much later in - // LastComposedWord#canCancelAutoCorrect by string equality of the remembered + // LastComposedWord#didCommitTypedWord by string equality of the remembered // strings. - mLastComposedWord = mWordComposer.commitWord(commitType); + mLastComposedWord = mWordComposer.commitWord(commitType, bestWord.toString(), + separatorCode); } private static final WordComposer sEmptyWordComposer = new WordComposer(); @@ -2183,66 +2155,43 @@ public class LatinIME extends InputMethodServiceCompatWrapper implements Keyboar } // "ic" must not be null - private void cancelAutoCorrect(final InputConnection ic) { + private void revertCommit(final InputConnection ic) { final String originallyTypedWord = mLastComposedWord.mTypedWord; - final CharSequence autoCorrectedTo = mLastComposedWord.mAutoCorrection; - final int cancelLength = autoCorrectedTo.length(); - final CharSequence separator = ic.getTextBeforeCursor(1, 0); + final CharSequence committedWord = mLastComposedWord.mCommittedWord; + final int cancelLength = committedWord.length(); + final int separatorLength = mLastComposedWord.getSeparatorLength( + mLastComposedWord.mSeparatorCode); + // TODO: should we check our saved separator against the actual contents of the text view? if (DEBUG) { if (mWordComposer.isComposingWord()) { - throw new RuntimeException("cancelAutoCorrect, but we are composing a word"); + throw new RuntimeException("revertCommit, but we are composing a word"); } final String wordBeforeCursor = - ic.getTextBeforeCursor(cancelLength + 1, 0).subSequence(0, cancelLength) - .toString(); - if (!TextUtils.equals(autoCorrectedTo, wordBeforeCursor)) { - throw new RuntimeException("cancelAutoCorrect check failed: we thought we were " - + "reverting \"" + autoCorrectedTo + ic.getTextBeforeCursor(cancelLength + separatorLength, 0) + .subSequence(0, cancelLength).toString(); + if (!TextUtils.equals(committedWord, wordBeforeCursor)) { + throw new RuntimeException("revertCommit check failed: we thought we were " + + "reverting \"" + committedWord + "\", but before the cursor we found \"" + wordBeforeCursor + "\""); } - if (TextUtils.equals(originallyTypedWord, wordBeforeCursor)) { - throw new RuntimeException("cancelAutoCorrect check failed: we wanted to cancel " - + "auto correction and revert to \"" + originallyTypedWord - + "\" but we found this very string before the cursor"); - } } - ic.deleteSurroundingText(cancelLength + 1, 0); - ic.commitText(originallyTypedWord, 1); - // Re-insert the separator - ic.commitText(separator, 1); - mLastComposedWord = LastComposedWord.NOT_A_COMPOSED_WORD; - Utils.Stats.onSeparator(separator.charAt(0), WordComposer.NOT_A_COORDINATE, - WordComposer.NOT_A_COORDINATE); - mHandler.cancelUpdateBigramPredictions(); - mHandler.postUpdateSuggestions(); - } - - // "ic" must not be null - private void restartSuggestionsOnManuallyPickedTypedWord(final InputConnection ic) { - // Note: this relies on the last word still being held in the WordComposer, in - // the field for suggestion resuming. - // Note: in the interest of code simplicity, we may want to just call - // restartSuggestionsOnWordBeforeCursorIfAtEndOfWord instead, but retrieving - // the old WordComposer allows to reuse the actual typed coordinates. - mWordComposer.resumeSuggestionOnLastComposedWord(mLastComposedWord); - // We resume suggestion, and then we want to set the composing text to the content - // of the word composer again. But since we just manually picked a word, there is - // no composing text at the moment, so we have to delete the word before we set a - // new composing text. - final int restartLength = mWordComposer.size(); - if (DEBUG) { - final String wordBeforeCursor = ic.getTextBeforeCursor(restartLength, 0).toString(); - if (!TextUtils.equals(mWordComposer.getTypedWord(), wordBeforeCursor)) { - throw new RuntimeException("restartSuggestionsOnManuallyPickedTypedWord " - + "check failed: we thought we were reverting \"" - + mWordComposer.getTypedWord() - + "\", but before the cursor we found \"" - + wordBeforeCursor + "\""); - } + ic.deleteSurroundingText(cancelLength + separatorLength, 0); + if (0 == separatorLength || mLastComposedWord.didCommitTypedWord()) { + // This is the case when we cancel a manual pick. + // We should restart suggestion on the word right away. + mWordComposer.resumeSuggestionOnLastComposedWord(mLastComposedWord); + mComposingStateManager.onStartComposingText(); + ic.setComposingText(originallyTypedWord, 1); + } else { + ic.commitText(originallyTypedWord, 1); + // Re-insert the separator + sendKeyCodePoint(mLastComposedWord.mSeparatorCode); + Utils.Stats.onSeparator(mLastComposedWord.mSeparatorCode, WordComposer.NOT_A_COORDINATE, + WordComposer.NOT_A_COORDINATE); + // Don't restart suggestion yet. We'll restart if the user deletes the + // separator. } - ic.deleteSurroundingText(restartLength, 0); - mComposingStateManager.onStartComposingText(); - ic.setComposingText(mWordComposer.getTypedWord(), 1); + mLastComposedWord = LastComposedWord.NOT_A_COMPOSED_WORD; mHandler.cancelUpdateBigramPredictions(); mHandler.postUpdateSuggestions(); } |