diff options
Diffstat (limited to 'java/src/com/android/inputmethod/latin/LatinIME.java')
-rw-r--r-- | java/src/com/android/inputmethod/latin/LatinIME.java | 103 |
1 files changed, 59 insertions, 44 deletions
diff --git a/java/src/com/android/inputmethod/latin/LatinIME.java b/java/src/com/android/inputmethod/latin/LatinIME.java index aef248e19..a208a8748 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 @@ -761,14 +761,15 @@ public class LatinIME extends InputMethodServiceCompatWrapper implements Keyboar // Most such things we decide below in initializeInputAttributesAndGetMode, but we need to // know now whether this is a password text field, because we need to know now whether we // want to enable the voice button. - final VoiceProxy voiceIme = mVoiceProxy; final int inputType = (editorInfo != null) ? editorInfo.inputType : 0; - voiceIme.resetVoiceStates(InputTypeCompatUtils.isPasswordInputType(inputType) + mVoiceProxy.resetVoiceStates(InputTypeCompatUtils.isPasswordInputType(inputType) || InputTypeCompatUtils.isVisiblePasswordInputType(inputType)); // The EditorInfo might have a flag that affects fullscreen mode. // Note: This call should be done by InputMethodService? updateFullscreenMode(); + mLastSelectionStart = editorInfo.initialSelStart; + mLastSelectionEnd = editorInfo.initialSelEnd; mInputAttributes = new InputAttributes(editorInfo, isFullscreenMode()); mApplicationSpecifiedCompletions = null; @@ -805,7 +806,7 @@ public class LatinIME extends InputMethodServiceCompatWrapper implements Keyboar mSettingsValues.mKeyPreviewPopupDismissDelay); inputView.setProximityCorrectionEnabled(true); - voiceIme.onStartInputView(inputView.getWindowToken()); + mVoiceProxy.onStartInputView(inputView.getWindowToken()); if (TRACE) Debug.startMethodTracing("/data/trace/latinime"); } @@ -848,9 +849,9 @@ public class LatinIME extends InputMethodServiceCompatWrapper implements Keyboar @Override public void onUpdateSelection(int oldSelStart, int oldSelEnd, int newSelStart, int newSelEnd, - int candidatesStart, int candidatesEnd) { + int composingSpanStart, int composingSpanEnd) { super.onUpdateSelection(oldSelStart, oldSelEnd, newSelStart, newSelEnd, - candidatesStart, candidatesEnd); + composingSpanStart, composingSpanEnd); if (DEBUG) { Log.i(TAG, "onUpdateSelection: oss=" + oldSelStart @@ -859,44 +860,43 @@ public class LatinIME extends InputMethodServiceCompatWrapper implements Keyboar + ", lse=" + mLastSelectionEnd + ", nss=" + newSelStart + ", nse=" + newSelEnd - + ", cs=" + candidatesStart - + ", ce=" + candidatesEnd); + + ", cs=" + composingSpanStart + + ", ce=" + composingSpanEnd); } mVoiceProxy.setCursorAndSelection(newSelEnd, newSelStart); - // If the current selection in the text view changes, we should - // clear whatever candidate text we have. - final boolean selectionChanged = (newSelStart != candidatesEnd - || newSelEnd != candidatesEnd) && mLastSelectionStart != newSelStart; - final boolean candidatesCleared = candidatesStart == -1 && candidatesEnd == -1; + // TODO: refactor the following code to be less contrived. + // "newSelStart != composingSpanEnd" || "newSelEnd != composingSpanEnd" means + // that the cursor is not at the end of the composing span, or there is a selection. + // "mLastSelectionStart != newSelStart" means that the cursor is not in the same place + // as last time we were called (if there is a selection, it means the start hasn't + // changed, so it's the end that did). + final boolean selectionChanged = (newSelStart != composingSpanEnd + || newSelEnd != composingSpanEnd) && mLastSelectionStart != newSelStart; + // if composingSpanStart and composingSpanEnd are -1, it means there is no composing + // span in the view - we can use that to narrow down whether the cursor was moved + // by us or not. If we are composing a word but there is no composing span, then + // we know for sure the cursor moved while we were composing and we should reset + // the state. + final boolean noComposingSpan = composingSpanStart == -1 && composingSpanEnd == -1; if (!mExpectingUpdateSelection) { // 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 candidatesCleared to further reduce the impact. + // 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 || candidatesCleared)) { - 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(); @@ -1106,6 +1106,20 @@ 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) @@ -1457,18 +1471,10 @@ public class LatinIME extends InputMethodServiceCompatWrapper implements Keyboar ic.deleteSurroundingText(lengthToDelete, 0); } else { if (NOT_A_CURSOR_POSITION == mLastSelectionEnd) { - // We don't know whether there is a selection or not. We just send a false - // hardware key event and let TextView sort it out for us. The problem - // here is, this is asynchronous with respect to the input connection - // batch edit, so it may flicker. But this only ever happens if backspace - // is pressed just after the IME is invoked, and then again only once. - // TODO: add an API call that gets the selection indices. This is available - // to the IME in the general case via onUpdateSelection anyway, and would - // allow us to remove this race condition. - sendDownUpKeyEvents(KeyEvent.KEYCODE_DEL); - } else { - ic.deleteSurroundingText(1, 0); + // 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); } @@ -1489,10 +1495,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; @@ -1565,6 +1571,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); } |