From 5475b38328171a0841ae18074bd45380ec567e90 Mon Sep 17 00:00:00 2001 From: Jean Chalard Date: Fri, 8 Jun 2012 19:04:09 +0900 Subject: [PATCH] Make a wrapper for the input connection. The goal is to simplify the code in LatinIME.java as well as having a handy place to put debug calls to see interaction with TextView. Change-Id: I255227e7e7343e0c2f3dcd1f185e5020d6186732 --- .../android/inputmethod/latin/LatinIME.java | 366 ++++++++---------- .../latin/RichInputConnection.java | 122 ++++++ 2 files changed, 275 insertions(+), 213 deletions(-) create mode 100644 java/src/com/android/inputmethod/latin/RichInputConnection.java diff --git a/java/src/com/android/inputmethod/latin/LatinIME.java b/java/src/com/android/inputmethod/latin/LatinIME.java index c3db7a781..5b499e4be 100644 --- a/java/src/com/android/inputmethod/latin/LatinIME.java +++ b/java/src/com/android/inputmethod/latin/LatinIME.java @@ -56,7 +56,6 @@ import android.view.WindowManager; import android.view.inputmethod.CompletionInfo; import android.view.inputmethod.CorrectionInfo; import android.view.inputmethod.EditorInfo; -import android.view.inputmethod.InputConnection; import android.view.inputmethod.InputMethodSubtype; import com.android.inputmethod.accessibility.AccessibilityUtils; @@ -162,6 +161,7 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen private LastComposedWord mLastComposedWord = LastComposedWord.NOT_A_COMPOSED_WORD; private WordComposer mWordComposer = new WordComposer(); + private RichInputConnection mConnection = new RichInputConnection(); private int mCorrectionMode; @@ -558,9 +558,10 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen if (mDisplayOrientation != conf.orientation) { mDisplayOrientation = conf.orientation; mHandler.startOrientationChanging(); - final InputConnection ic = getCurrentInputConnection(); - commitTyped(ic, LastComposedWord.NOT_A_SEPARATOR); - if (ic != null) ic.finishComposingText(); // For voice input + mConnection.beginBatchEdit(getCurrentInputConnection()); + commitTyped(LastComposedWord.NOT_A_SEPARATOR); + mConnection.finishComposingText(); + mConnection.endBatchEdit(); if (isShowingOptionDialog()) mOptionsDialog.dismiss(); } @@ -1024,10 +1025,7 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen private void resetEntireInputState() { resetComposingState(true /* alsoResetLastComposedWord */); updateSuggestions(); - final InputConnection ic = getCurrentInputConnection(); - if (ic != null) { - ic.finishComposingText(); - } + mConnection.finishComposingText(); } private void resetComposingState(final boolean alsoResetLastComposedWord) { @@ -1036,15 +1034,13 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen mLastComposedWord = LastComposedWord.NOT_A_COMPOSED_WORD; } - public void commitTyped(final InputConnection ic, final int separatorCode) { + public void commitTyped(final int separatorCode) { if (!mWordComposer.isComposingWord()) return; final CharSequence typedWord = mWordComposer.getTypedWord(); if (typedWord.length() > 0) { - if (ic != null) { - ic.commitText(typedWord, 1); - if (ProductionFlag.IS_EXPERIMENTAL) { - ResearchLogger.latinIME_commitText(typedWord); - } + mConnection.commitText(typedWord, 1); + if (ProductionFlag.IS_EXPERIMENTAL) { + ResearchLogger.latinIME_commitText(typedWord); } final CharSequence prevWord = addToUserHistoryDictionary(typedWord); mLastComposedWord = mWordComposer.commitWord( @@ -1073,27 +1069,23 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen // unless needed. if (mWordComposer.isComposingWord()) return Constants.TextUtils.CAP_MODE_OFF; - final InputConnection ic = getCurrentInputConnection(); - if (ic == null) return Constants.TextUtils.CAP_MODE_OFF; // TODO: This blocking IPC call is heavy. Consider doing this without using IPC calls. // Note: getCursorCapsMode() returns the current capitalization mode that is any // combination of CAP_MODE_CHARACTERS, CAP_MODE_WORDS, and CAP_MODE_SENTENCES. 0 means none // of them. - return ic.getCursorCapsMode(inputType); + return mConnection.getCursorCapsMode(inputType); } - // "ic" may be null - private void swapSwapperAndSpaceWhileInBatchEdit(final InputConnection ic) { - if (null == ic) return; - CharSequence lastTwo = ic.getTextBeforeCursor(2, 0); + private void swapSwapperAndSpaceWhileInBatchEdit() { + 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) == Keyboard.CODE_SPACE) { - ic.deleteSurroundingText(2, 0); + mConnection.deleteSurroundingText(2, 0); if (ProductionFlag.IS_EXPERIMENTAL) { ResearchLogger.latinIME_deleteSurroundingText(2); } - ic.commitText(lastTwo.charAt(1) + " ", 1); + mConnection.commitText(lastTwo.charAt(1) + " ", 1); if (ProductionFlag.IS_EXPERIMENTAL) { ResearchLogger.latinIME_swapSwapperAndSpaceWhileInBatchEdit(); } @@ -1101,18 +1093,17 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen } } - private boolean maybeDoubleSpaceWhileInBatchEdit(final InputConnection ic) { + private boolean maybeDoubleSpaceWhileInBatchEdit() { if (mCorrectionMode == Suggest.CORRECTION_NONE) return false; - if (ic == null) return false; - final CharSequence lastThree = ic.getTextBeforeCursor(3, 0); + final CharSequence lastThree = mConnection.getTextBeforeCursor(3, 0); if (lastThree != null && lastThree.length() == 3 && canBeFollowedByPeriod(lastThree.charAt(0)) && lastThree.charAt(1) == Keyboard.CODE_SPACE && lastThree.charAt(2) == Keyboard.CODE_SPACE && mHandler.isAcceptingDoubleSpaces()) { mHandler.cancelDoubleSpacesTimer(); - ic.deleteSurroundingText(2, 0); - ic.commitText(". ", 1); + mConnection.deleteSurroundingText(2, 0); + mConnection.commitText(". ", 1); if (ProductionFlag.IS_EXPERIMENTAL) { ResearchLogger.latinIME_doubleSpaceAutoPeriod(); } @@ -1134,13 +1125,11 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen || codePoint == Keyboard.CODE_CLOSING_ANGLE_BRACKET; } - // "ic" may be null - private static void removeTrailingSpaceWhileInBatchEdit(final InputConnection ic) { - if (ic == null) return; - final CharSequence lastOne = ic.getTextBeforeCursor(1, 0); + private void removeTrailingSpaceWhileInBatchEdit() { + final CharSequence lastOne = mConnection.getTextBeforeCursor(1, 0); if (lastOne != null && lastOne.length() == 1 && lastOne.charAt(0) == Keyboard.CODE_SPACE) { - ic.deleteSurroundingText(1, 0); + mConnection.deleteSurroundingText(1, 0); if (ProductionFlag.IS_EXPERIMENTAL) { ResearchLogger.latinIME_deleteSurroundingText(1); } @@ -1192,12 +1181,9 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen } private void performEditorAction(int actionId) { - final InputConnection ic = getCurrentInputConnection(); - if (ic != null) { - ic.performEditorAction(actionId); - if (ProductionFlag.IS_EXPERIMENTAL) { - ResearchLogger.latinIME_performEditorAction(actionId); - } + mConnection.performEditorAction(actionId); + if (ProductionFlag.IS_EXPERIMENTAL) { + ResearchLogger.latinIME_performEditorAction(actionId); } } @@ -1220,12 +1206,12 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen } } - static private void sendUpDownEnterOrBackspace(final int code, final InputConnection ic) { + private void sendUpDownEnterOrBackspace(final int code) { final long eventTime = SystemClock.uptimeMillis(); - ic.sendKeyEvent(new KeyEvent(eventTime, eventTime, + mConnection.sendKeyEvent(new KeyEvent(eventTime, eventTime, KeyEvent.ACTION_DOWN, code, 0, 0, KeyCharacterMap.VIRTUAL_KEYBOARD, 0, KeyEvent.FLAG_SOFT_KEYBOARD | KeyEvent.FLAG_KEEP_TOUCH_MODE)); - ic.sendKeyEvent(new KeyEvent(SystemClock.uptimeMillis(), eventTime, + mConnection.sendKeyEvent(new KeyEvent(SystemClock.uptimeMillis(), eventTime, KeyEvent.ACTION_UP, code, 0, 0, KeyCharacterMap.VIRTUAL_KEYBOARD, 0, KeyEvent.FLAG_SOFT_KEYBOARD | KeyEvent.FLAG_KEEP_TOUCH_MODE)); } @@ -1238,24 +1224,21 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen return; } - final InputConnection ic = getCurrentInputConnection(); - if (ic != null) { - // 16 is android.os.Build.VERSION_CODES.JELLY_BEAN but we can't write it because - // we want to be able to compile against the Ice Cream Sandwich SDK. - if (Keyboard.CODE_ENTER == code && mTargetApplicationInfo != null - && mTargetApplicationInfo.targetSdkVersion < 16) { - // Backward compatibility mode. Before Jelly bean, the keyboard would simulate - // a hardware keyboard event on pressing enter or delete. This is bad for many - // reasons (there are race conditions with commits) but some applications are - // relying on this behavior so we continue to support it for older apps. - sendUpDownEnterOrBackspace(KeyEvent.KEYCODE_ENTER, ic); - } else { - final String text = new String(new int[] { code }, 0, 1); - ic.commitText(text, text.length()); - } - if (ProductionFlag.IS_EXPERIMENTAL) { - ResearchLogger.latinIME_sendKeyCodePoint(code); - } + // 16 is android.os.Build.VERSION_CODES.JELLY_BEAN but we can't write it because + // we want to be able to compile against the Ice Cream Sandwich SDK. + if (Keyboard.CODE_ENTER == code && mTargetApplicationInfo != null + && mTargetApplicationInfo.targetSdkVersion < 16) { + // Backward compatibility mode. Before Jelly bean, the keyboard would simulate + // a hardware keyboard event on pressing enter or delete. This is bad for many + // reasons (there are race conditions with commits) but some applications are + // relying on this behavior so we continue to support it for older apps. + sendUpDownEnterOrBackspace(KeyEvent.KEYCODE_ENTER); + } else { + final String text = new String(new int[] { code }, 0, 1); + mConnection.commitText(text, text.length()); + } + if (ProductionFlag.IS_EXPERIMENTAL) { + ResearchLogger.latinIME_sendKeyCodePoint(code); } } @@ -1355,19 +1338,17 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen @Override public void onTextInput(CharSequence text) { - final InputConnection ic = getCurrentInputConnection(); - if (ic == null) return; - ic.beginBatchEdit(); - commitTyped(ic, LastComposedWord.NOT_A_SEPARATOR); - text = specificTldProcessingOnTextInput(ic, text); + mConnection.beginBatchEdit(getCurrentInputConnection()); + commitTyped(LastComposedWord.NOT_A_SEPARATOR); + text = specificTldProcessingOnTextInput(text); if (SPACE_STATE_PHANTOM == mSpaceState) { sendKeyCodePoint(Keyboard.CODE_SPACE); } - ic.commitText(text, 1); + mConnection.commitText(text, 1); if (ProductionFlag.IS_EXPERIMENTAL) { ResearchLogger.latinIME_commitText(text); } - ic.endBatchEdit(); + mConnection.endBatchEdit(); mKeyboardSwitcher.updateShiftState(); mKeyboardSwitcher.onCodeInput(Keyboard.CODE_OUTPUT_TEXT); mSpaceState = SPACE_STATE_NONE; @@ -1375,9 +1356,7 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen resetComposingState(true /* alsoResetLastComposedWord */); } - // ic may not be null - private CharSequence specificTldProcessingOnTextInput(final InputConnection ic, - final CharSequence text) { + private CharSequence specificTldProcessingOnTextInput(final CharSequence text) { if (text.length() <= 1 || text.charAt(0) != Keyboard.CODE_PERIOD || !Character.isLetter(text.charAt(1))) { // Not a tld: do nothing. @@ -1386,7 +1365,7 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen // We have a TLD (or something that looks like this): make sure we don't add // a space even if currently in phantom mode. mSpaceState = SPACE_STATE_NONE; - final CharSequence lastOne = ic.getTextBeforeCursor(1, 0); + final CharSequence lastOne = mConnection.getTextBeforeCursor(1, 0); if (lastOne != null && lastOne.length() == 1 && lastOne.charAt(0) == Keyboard.CODE_PERIOD) { return text.subSequence(1, text.length()); @@ -1402,24 +1381,21 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen } private void handleBackspace(final int spaceState) { - final InputConnection ic = getCurrentInputConnection(); - if (ic == null) return; - ic.beginBatchEdit(); - handleBackspaceWhileInBatchEdit(spaceState, ic); - ic.endBatchEdit(); + mConnection.beginBatchEdit(getCurrentInputConnection()); + handleBackspaceWhileInBatchEdit(spaceState); + mConnection.endBatchEdit(); } - // "ic" may not be null. - private void handleBackspaceWhileInBatchEdit(final int spaceState, final InputConnection ic) { + private void handleBackspaceWhileInBatchEdit(final int spaceState) { // In many cases, we may have to put the keyboard in auto-shift state again. mHandler.postUpdateShiftState(); - if (mEnteredText != null && sameAsTextBeforeCursor(ic, mEnteredText)) { + if (mEnteredText != null && sameAsTextBeforeCursor(mEnteredText)) { // Cancel multi-character input: remove the text we just entered. // This is triggered on backspace after a key that inputs multiple characters, // like the smiley key or the .com key. final int length = mEnteredText.length(); - ic.deleteSurroundingText(length, 0); + mConnection.deleteSurroundingText(length, 0); if (ProductionFlag.IS_EXPERIMENTAL) { ResearchLogger.latinIME_deleteSurroundingText(length); } @@ -1433,7 +1409,7 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen final int length = mWordComposer.size(); if (length > 0) { mWordComposer.deleteLast(); - ic.setComposingText(getTextWithUnderline(mWordComposer.getTypedWord()), 1); + mConnection.setComposingText(getTextWithUnderline(mWordComposer.getTypedWord()), 1); // If we have deleted the last remaining character of a word, then we are not // isComposingWord() any more. if (!mWordComposer.isComposingWord()) { @@ -1444,7 +1420,7 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen mHandler.postUpdateSuggestions(); } } else { - ic.deleteSurroundingText(1, 0); + mConnection.deleteSurroundingText(1, 0); if (ProductionFlag.IS_EXPERIMENTAL) { ResearchLogger.latinIME_deleteSurroundingText(1); } @@ -1452,17 +1428,17 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen } else { if (mLastComposedWord.canRevertCommit()) { Utils.Stats.onAutoCorrectionCancellation(); - revertCommit(ic); + revertCommit(); return; } if (SPACE_STATE_DOUBLE == spaceState) { - if (revertDoubleSpaceWhileInBatchEdit(ic)) { + if (revertDoubleSpaceWhileInBatchEdit()) { // No need to reset mSpaceState, it has already be done (that's why we // receive it as a parameter) return; } } else if (SPACE_STATE_SWAP_PUNCTUATION == spaceState) { - if (revertSwapPunctuation(ic)) { + if (revertSwapPunctuation()) { // Likewise return; } @@ -1473,8 +1449,8 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen if (mLastSelectionStart != mLastSelectionEnd) { // If there is a selection, remove it. final int lengthToDelete = mLastSelectionEnd - mLastSelectionStart; - ic.setSelection(mLastSelectionEnd, mLastSelectionEnd); - ic.deleteSurroundingText(lengthToDelete, 0); + mConnection.setSelection(mLastSelectionEnd, mLastSelectionEnd); + mConnection.deleteSurroundingText(lengthToDelete, 0); if (ProductionFlag.IS_EXPERIMENTAL) { ResearchLogger.latinIME_deleteSurroundingText(lengthToDelete); } @@ -1492,31 +1468,30 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen // a hardware keyboard event on pressing enter or delete. This is bad for many // reasons (there are race conditions with commits) but some applications are // relying on this behavior so we continue to support it for older apps. - sendUpDownEnterOrBackspace(KeyEvent.KEYCODE_DEL, ic); + sendUpDownEnterOrBackspace(KeyEvent.KEYCODE_DEL); } else { - ic.deleteSurroundingText(1, 0); + mConnection.deleteSurroundingText(1, 0); } if (ProductionFlag.IS_EXPERIMENTAL) { ResearchLogger.latinIME_deleteSurroundingText(1); } if (mDeleteCount > DELETE_ACCELERATE_AT) { - ic.deleteSurroundingText(1, 0); + mConnection.deleteSurroundingText(1, 0); if (ProductionFlag.IS_EXPERIMENTAL) { ResearchLogger.latinIME_deleteSurroundingText(1); } } } if (isSuggestionsRequested()) { - restartSuggestionsOnWordBeforeCursorIfAtEndOfWord(ic); + restartSuggestionsOnWordBeforeCursorIfAtEndOfWord(); } } } - // ic may be null - private boolean maybeStripSpaceWhileInBatchEdit(final InputConnection ic, final int code, + private boolean maybeStripSpaceWhileInBatchEdit(final int code, final int spaceState, final boolean isFromSuggestionStrip) { if (Keyboard.CODE_ENTER == code && SPACE_STATE_SWAP_PUNCTUATION == spaceState) { - removeTrailingSpaceWhileInBatchEdit(ic); + removeTrailingSpaceWhileInBatchEdit(); return false; } else if ((SPACE_STATE_WEAK == spaceState || SPACE_STATE_SWAP_PUNCTUATION == spaceState) @@ -1525,7 +1500,7 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen return true; } else { if (mSettingsValues.isWeakSpaceStripper(code)) { - removeTrailingSpaceWhileInBatchEdit(ic); + removeTrailingSpaceWhileInBatchEdit(); } return false; } @@ -1536,16 +1511,13 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen private void handleCharacter(final int primaryCode, final int x, final int y, final int spaceState) { - final InputConnection ic = getCurrentInputConnection(); - if (null != ic) ic.beginBatchEdit(); - // TODO: if ic is null, does it make any sense to call this? - handleCharacterWhileInBatchEdit(primaryCode, x, y, spaceState, ic); - if (null != ic) ic.endBatchEdit(); + mConnection.beginBatchEdit(getCurrentInputConnection()); + handleCharacterWhileInBatchEdit(primaryCode, x, y, spaceState); + mConnection.endBatchEdit(); } - // "ic" may be null without this crashing, but the behavior will be really strange private void handleCharacterWhileInBatchEdit(final int primaryCode, - final int x, final int y, final int spaceState, final InputConnection ic) { + final int x, final int y, final int spaceState) { boolean isComposingWord = mWordComposer.isComposingWord(); if (SPACE_STATE_PHANTOM == spaceState && @@ -1578,23 +1550,21 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen if (isComposingWord) { mWordComposer.add( primaryCode, x, y, mKeyboardSwitcher.getKeyboardView().getKeyDetector()); - if (ic != null) { - // If it's the first letter, make note of auto-caps state - if (mWordComposer.size() == 1) { - mWordComposer.setAutoCapitalized( - getCurrentAutoCapsState() != Constants.TextUtils.CAP_MODE_OFF); - } - ic.setComposingText(getTextWithUnderline(mWordComposer.getTypedWord()), 1); + // If it's the first letter, make note of auto-caps state + if (mWordComposer.size() == 1) { + mWordComposer.setAutoCapitalized( + getCurrentAutoCapsState() != Constants.TextUtils.CAP_MODE_OFF); } + mConnection.setComposingText(getTextWithUnderline(mWordComposer.getTypedWord()), 1); mHandler.postUpdateSuggestions(); } else { - final boolean swapWeakSpace = maybeStripSpaceWhileInBatchEdit(ic, primaryCode, + final boolean swapWeakSpace = maybeStripSpaceWhileInBatchEdit(primaryCode, spaceState, KeyboardActionListener.SUGGESTION_STRIP_COORDINATE == x); sendKeyCodePoint(primaryCode); if (swapWeakSpace) { - swapSwapperAndSpaceWhileInBatchEdit(ic); + swapSwapperAndSpaceWhileInBatchEdit(); mSpaceState = SPACE_STATE_WEAK; } // Some characters are not word separators, yet they don't start a new @@ -1621,10 +1591,7 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen boolean didAutoCorrect = false; // Handle separator - final InputConnection ic = getCurrentInputConnection(); - if (ic != null) { - ic.beginBatchEdit(); - } + mConnection.beginBatchEdit(getCurrentInputConnection()); if (mWordComposer.isComposingWord()) { // In certain languages where single quote is a separator, it's better // not to auto correct, but accept the typed word. For instance, @@ -1633,14 +1600,14 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen final boolean shouldAutoCorrect = mSettingsValues.mAutoCorrectEnabled && !mInputAttributes.mInputTypeNoAutoCorrect; if (shouldAutoCorrect && primaryCode != Keyboard.CODE_SINGLE_QUOTE) { - commitCurrentAutoCorrection(primaryCode, ic); + commitCurrentAutoCorrection(primaryCode); didAutoCorrect = true; } else { - commitTyped(ic, primaryCode); + commitTyped(primaryCode); } } - final boolean swapWeakSpace = maybeStripSpaceWhileInBatchEdit(ic, primaryCode, spaceState, + final boolean swapWeakSpace = maybeStripSpaceWhileInBatchEdit(primaryCode, spaceState, KeyboardActionListener.SUGGESTION_STRIP_COORDINATE == x); if (SPACE_STATE_PHANTOM == spaceState && @@ -1651,7 +1618,7 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen if (Keyboard.CODE_SPACE == primaryCode) { if (isSuggestionsRequested()) { - if (maybeDoubleSpaceWhileInBatchEdit(ic)) { + if (maybeDoubleSpaceWhileInBatchEdit()) { mSpaceState = SPACE_STATE_DOUBLE; } else if (!isShowingPunctuationList()) { mSpaceState = SPACE_STATE_WEAK; @@ -1665,7 +1632,7 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen } } else { if (swapWeakSpace) { - swapSwapperAndSpaceWhileInBatchEdit(ic); + swapSwapperAndSpaceWhileInBatchEdit(); mSpaceState = SPACE_STATE_SWAP_PUNCTUATION; } else if (SPACE_STATE_PHANTOM == spaceState) { // If we are in phantom space state, and the user presses a separator, we want to @@ -1684,9 +1651,7 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen Utils.Stats.onSeparator((char)primaryCode, x, y); - if (ic != null) { - ic.endBatchEdit(); - } + mConnection.endBatchEdit(); return didAutoCorrect; } @@ -1697,7 +1662,7 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen } private void handleClose() { - commitTyped(getCurrentInputConnection(), LastComposedWord.NOT_A_SEPARATOR); + commitTyped(LastComposedWord.NOT_A_SEPARATOR); requestHideSelf(0); LatinKeyboardView inputView = mKeyboardSwitcher.getKeyboardView(); if (inputView != null) @@ -1767,14 +1732,12 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen private void setAutoCorrectionIndicator(final boolean newAutoCorrectionIndicator) { // Put a blue underline to a word in TextView which will be auto-corrected. - final InputConnection ic = getCurrentInputConnection(); - if (ic == null) return; if (mIsAutoCorrectionIndicatorOn != newAutoCorrectionIndicator && mWordComposer.isComposingWord()) { mIsAutoCorrectionIndicatorOn = newAutoCorrectionIndicator; final CharSequence textWithUnderline = getTextWithUnderline(mWordComposer.getTypedWord()); - ic.setComposingText(textWithUnderline, 1); + mConnection.setComposingText(textWithUnderline, 1); } } @@ -1797,13 +1760,10 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen } // TODO: May need a better way of retrieving previous word - final InputConnection ic = getCurrentInputConnection(); final CharSequence prevWord; - if (null == ic) { - prevWord = null; - } else { - prevWord = EditingUtils.getPreviousWord(ic, mSettingsValues.mWordSeparators); - } + // TODO: move getPreviousWord to AutoInputConnection + prevWord = EditingUtils.getPreviousWord(mConnection.getInputConnection(), + mSettingsValues.mWordSeparators); final CharSequence typedWord = mWordComposer.getTypedWord(); // getSuggestedWords handles gracefully a null value of prevWord @@ -1858,8 +1818,7 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen setSuggestionStripShown(isSuggestionsStripVisible()); } - private void commitCurrentAutoCorrection(final int separatorCodePoint, - final InputConnection ic) { + private void commitCurrentAutoCorrection(final int separatorCodePoint) { // Complete any pending suggestions query first if (mHandler.hasPendingUpdateSuggestions()) { mHandler.cancelUpdateSuggestions(); @@ -1880,10 +1839,11 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen mExpectingUpdateSelection = true; commitChosenWord(autoCorrection, LastComposedWord.COMMIT_TYPE_DECIDED_WORD, separatorCodePoint); - if (!typedWord.equals(autoCorrection) && null != ic) { + if (!typedWord.equals(autoCorrection)) { // This will make the correction flash for a short while as a visual clue // to the user that auto-correction happened. - ic.commitCorrection(new CorrectionInfo(mLastSelectionEnd - typedWord.length(), + mConnection.commitCorrection( + new CorrectionInfo(mLastSelectionEnd - typedWord.length(), typedWord, autoCorrection)); } } @@ -1892,14 +1852,13 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen @Override public void pickSuggestionManually(final int index, final CharSequence suggestion, int x, int y) { - final InputConnection ic = getCurrentInputConnection(); - if (null != ic) ic.beginBatchEdit(); - pickSuggestionManuallyWhileInBatchEdit(index, suggestion, x, y, ic); - if (null != ic) ic.endBatchEdit(); + mConnection.beginBatchEdit(getCurrentInputConnection()); + pickSuggestionManuallyWhileInBatchEdit(index, suggestion, x, y); + mConnection.endBatchEdit(); } public void pickSuggestionManuallyWhileInBatchEdit(final int index, - final CharSequence suggestion, final int x, final int y, final InputConnection ic) { + final CharSequence suggestion, final int x, final int y) { final SuggestedWords suggestedWords = mSuggestionsView.getSuggestions(); // If this is a punctuation picked from the suggestion strip, pass it to onCodeInput if (suggestion.length() == 1 && isShowingPunctuationList()) { @@ -1933,13 +1892,11 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen } mKeyboardSwitcher.updateShiftState(); resetComposingState(true /* alsoResetLastComposedWord */); - if (ic != null) { - final CompletionInfo completionInfo = mApplicationSpecifiedCompletions[index]; - ic.commitCompletion(completionInfo); - if (ProductionFlag.IS_EXPERIMENTAL) { - ResearchLogger.latinIME_pickApplicationSpecifiedCompletion(index, - completionInfo.getText(), x, y); - } + final CompletionInfo completionInfo = mApplicationSpecifiedCompletions[index]; + mConnection.commitCompletion(completionInfo); + if (ProductionFlag.IS_EXPERIMENTAL) { + ResearchLogger.latinIME_pickApplicationSpecifiedCompletion(index, + completionInfo.getText(), x, y); } return; } @@ -2001,21 +1958,18 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen */ private void commitChosenWord(final CharSequence chosenWord, final int commitType, final int separatorCode) { - final InputConnection ic = getCurrentInputConnection(); - if (ic != null) { - if (mSettingsValues.mEnableSuggestionSpanInsertion) { - final SuggestedWords suggestedWords = mSuggestionsView.getSuggestions(); - ic.commitText(SuggestionSpanUtils.getTextWithSuggestionSpan( - this, chosenWord, suggestedWords, mIsMainDictionaryAvailable), - 1); - if (ProductionFlag.IS_EXPERIMENTAL) { - ResearchLogger.latinIME_commitText(chosenWord); - } - } else { - ic.commitText(chosenWord, 1); - if (ProductionFlag.IS_EXPERIMENTAL) { - ResearchLogger.latinIME_commitText(chosenWord); - } + if (mSettingsValues.mEnableSuggestionSpanInsertion) { + final SuggestedWords suggestedWords = mSuggestionsView.getSuggestions(); + mConnection.commitText(SuggestionSpanUtils.getTextWithSuggestionSpan( + this, chosenWord, suggestedWords, mIsMainDictionaryAvailable), + 1); + if (ProductionFlag.IS_EXPERIMENTAL) { + ResearchLogger.latinIME_commitText(chosenWord); + } + } else { + mConnection.commitText(chosenWord, 1); + if (ProductionFlag.IS_EXPERIMENTAL) { + ResearchLogger.latinIME_commitText(chosenWord); } } // Add the word to the user history dictionary @@ -2077,13 +2031,10 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen } if (mUserHistoryDictionary != null) { - final InputConnection ic = getCurrentInputConnection(); final CharSequence prevWord; - if (null != ic) { - prevWord = EditingUtils.getPreviousWord(ic, mSettingsValues.mWordSeparators); - } else { - prevWord = null; - } + // TODO: move getPreviousWord to AutoInputConnection + prevWord = EditingUtils.getPreviousWord(mConnection.getInputConnection(), + mSettingsValues.mWordSeparators); final String secondWord; if (mWordComposer.isAutoCapitalized() && !mWordComposer.isMostlyCaps()) { secondWord = suggestion.toString().toLowerCase( @@ -2103,10 +2054,8 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen } public boolean isCursorTouchingWord() { - final InputConnection ic = getCurrentInputConnection(); - if (ic == null) return false; - CharSequence before = ic.getTextBeforeCursor(1, 0); - CharSequence after = ic.getTextAfterCursor(1, 0); + CharSequence before = mConnection.getTextBeforeCursor(1, 0); + CharSequence after = mConnection.getTextAfterCursor(1, 0); if (!TextUtils.isEmpty(before) && !mSettingsValues.isWordSeparator(before.charAt(0)) && !mSettingsValues.isSymbolExcludedFromWordSeparators(before.charAt(0))) { return true; @@ -2118,37 +2067,35 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen return false; } - // "ic" must not be null - private static boolean sameAsTextBeforeCursor(final InputConnection ic, - final CharSequence text) { - final CharSequence beforeText = ic.getTextBeforeCursor(text.length(), 0); + private boolean sameAsTextBeforeCursor(final CharSequence text) { + final CharSequence beforeText = mConnection.getTextBeforeCursor(text.length(), 0); return TextUtils.equals(text, beforeText); } - // "ic" must not be null /** * Check if the cursor is actually at the end of a word. If so, restart suggestions on this * word, else do nothing. */ - private void restartSuggestionsOnWordBeforeCursorIfAtEndOfWord( - final InputConnection ic) { + private void restartSuggestionsOnWordBeforeCursorIfAtEndOfWord() { // Bail out if the cursor is not at the end of a word (cursor must be preceded by // non-whitespace, non-separator, non-start-of-text) // Example ("|" is the cursor here) : "|a" " |a" " | " all get rejected here. - final CharSequence textBeforeCursor = ic.getTextBeforeCursor(1, 0); + final CharSequence textBeforeCursor = mConnection.getTextBeforeCursor(1, 0); if (TextUtils.isEmpty(textBeforeCursor) || mSettingsValues.isWordSeparator(textBeforeCursor.charAt(0))) return; // Bail out if the cursor is in the middle of a word (cursor must be followed by whitespace, // separator or end of line/text) // Example: "test|" "te|st" get rejected here - final CharSequence textAfterCursor = ic.getTextAfterCursor(1, 0); + final CharSequence textAfterCursor = mConnection.getTextAfterCursor(1, 0); if (!TextUtils.isEmpty(textAfterCursor) && !mSettingsValues.isWordSeparator(textAfterCursor.charAt(0))) return; // Bail out if word before cursor is 0-length or a single non letter (like an apostrophe) // Example: " -|" gets rejected here but "e-|" and "e|" are okay - CharSequence word = EditingUtils.getWordAtCursor(ic, mSettingsValues.mWordSeparators); + // TODO: move getWordAtCursor inside AutoInputConnection + CharSequence word = EditingUtils.getWordAtCursor(mConnection.getInputConnection(), + mSettingsValues.mWordSeparators); // We don't suggest on leading single quotes, so we have to remove them from the word if // it starts with single quotes. while (!TextUtils.isEmpty(word) && Keyboard.CODE_SINGLE_QUOTE == word.charAt(0)) { @@ -2166,24 +2113,21 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen } // Okay, we are at the end of a word. Restart suggestions. - restartSuggestionsOnWordBeforeCursor(ic, word); + restartSuggestionsOnWordBeforeCursor(word); } - // "ic" must not be null - private void restartSuggestionsOnWordBeforeCursor(final InputConnection ic, - final CharSequence word) { + private void restartSuggestionsOnWordBeforeCursor(final CharSequence word) { mWordComposer.setComposingWord(word, mKeyboardSwitcher.getKeyboard()); final int length = word.length(); - ic.deleteSurroundingText(length, 0); + mConnection.deleteSurroundingText(length, 0); if (ProductionFlag.IS_EXPERIMENTAL) { ResearchLogger.latinIME_deleteSurroundingText(length); } - ic.setComposingText(word, 1); + mConnection.setComposingText(word, 1); mHandler.postUpdateSuggestions(); } - // "ic" must not be null - private void revertCommit(final InputConnection ic) { + private void revertCommit() { final CharSequence previousWord = mLastComposedWord.mPrevWord; final String originallyTypedWord = mLastComposedWord.mTypedWord; final CharSequence committedWord = mLastComposedWord.mCommittedWord; @@ -2197,7 +2141,7 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen throw new RuntimeException("revertCommit, but we are composing a word"); } final String wordBeforeCursor = - ic.getTextBeforeCursor(deleteLength, 0) + mConnection.getTextBeforeCursor(deleteLength, 0) .subSequence(0, cancelLength).toString(); if (!TextUtils.equals(committedWord, wordBeforeCursor)) { throw new RuntimeException("revertCommit check failed: we thought we were " @@ -2205,7 +2149,7 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen + "\", but before the cursor we found \"" + wordBeforeCursor + "\""); } } - ic.deleteSurroundingText(deleteLength, 0); + mConnection.deleteSurroundingText(deleteLength, 0); if (ProductionFlag.IS_EXPERIMENTAL) { ResearchLogger.latinIME_deleteSurroundingText(deleteLength); } @@ -2217,9 +2161,9 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen // This is the case when we cancel a manual pick. // We should restart suggestion on the word right away. mWordComposer.resumeSuggestionOnLastComposedWord(mLastComposedWord); - ic.setComposingText(originallyTypedWord, 1); + mConnection.setComposingText(originallyTypedWord, 1); } else { - ic.commitText(originallyTypedWord, 1); + mConnection.commitText(originallyTypedWord, 1); // Re-insert the separator sendKeyCodePoint(mLastComposedWord.mSeparatorCode); Utils.Stats.onSeparator(mLastComposedWord.mSeparatorCode, WordComposer.NOT_A_COORDINATE, @@ -2235,12 +2179,11 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen mHandler.postUpdateSuggestions(); } - // "ic" must not be null - private boolean revertDoubleSpaceWhileInBatchEdit(final InputConnection ic) { + private boolean revertDoubleSpaceWhileInBatchEdit() { mHandler.cancelDoubleSpacesTimer(); // Here we test whether we indeed have a period and a space before us. This should not // be needed, but it's there just in case something went wrong. - final CharSequence textBeforeCursor = ic.getTextBeforeCursor(2, 0); + final CharSequence textBeforeCursor = mConnection.getTextBeforeCursor(2, 0); if (!". ".equals(textBeforeCursor)) { // Theoretically we should not be coming here if there isn't ". " before the // cursor, but the application may be changing the text while we are typing, so @@ -2249,21 +2192,21 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen + "\". \" just before the cursor."); return false; } - ic.deleteSurroundingText(2, 0); + mConnection.deleteSurroundingText(2, 0); if (ProductionFlag.IS_EXPERIMENTAL) { ResearchLogger.latinIME_deleteSurroundingText(2); } - ic.commitText(" ", 1); + mConnection.commitText(" ", 1); if (ProductionFlag.IS_EXPERIMENTAL) { ResearchLogger.latinIME_revertDoubleSpaceWhileInBatchEdit(); } return true; } - private static boolean revertSwapPunctuation(final InputConnection ic) { + private boolean revertSwapPunctuation() { // Here we test whether we indeed have a space and something else before us. This should not // be needed, but it's there just in case something went wrong. - final CharSequence textBeforeCursor = ic.getTextBeforeCursor(2, 0); + final CharSequence textBeforeCursor = mConnection.getTextBeforeCursor(2, 0); // NOTE: This does not work with surrogate pairs. Hopefully when the keyboard is able to // enter surrogate pairs this code will have been removed. if (TextUtils.isEmpty(textBeforeCursor) @@ -2275,16 +2218,16 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen + "find a space just before the cursor."); return false; } - ic.beginBatchEdit(); - ic.deleteSurroundingText(2, 0); + mConnection.beginBatchEdit(getCurrentInputConnection()); + mConnection.deleteSurroundingText(2, 0); if (ProductionFlag.IS_EXPERIMENTAL) { ResearchLogger.latinIME_deleteSurroundingText(2); } - ic.commitText(" " + textBeforeCursor.subSequence(0, 1), 1); + mConnection.commitText(" " + textBeforeCursor.subSequence(0, 1), 1); if (ProductionFlag.IS_EXPERIMENTAL) { ResearchLogger.latinIME_revertSwapPunctuation(); } - ic.endBatchEdit(); + mConnection.endBatchEdit(); return true; } @@ -2349,12 +2292,9 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen // This is a stopgap solution to avoid leaving a high surrogate alone in a text view. // In the future, we need to deprecate deteleSurroundingText() and have a surrogate // pair-friendly way of deleting characters in InputConnection. - final InputConnection ic = getCurrentInputConnection(); - if (null != ic) { - final CharSequence lastChar = ic.getTextBeforeCursor(1, 0); - if (!TextUtils.isEmpty(lastChar) && Character.isHighSurrogate(lastChar.charAt(0))) { - ic.deleteSurroundingText(1, 0); - } + final CharSequence lastChar = mConnection.getTextBeforeCursor(1, 0); + if (!TextUtils.isEmpty(lastChar) && Character.isHighSurrogate(lastChar.charAt(0))) { + mConnection.deleteSurroundingText(1, 0); } } } diff --git a/java/src/com/android/inputmethod/latin/RichInputConnection.java b/java/src/com/android/inputmethod/latin/RichInputConnection.java new file mode 100644 index 000000000..449d08e9b --- /dev/null +++ b/java/src/com/android/inputmethod/latin/RichInputConnection.java @@ -0,0 +1,122 @@ +/* + * Copyright (C) 2012 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ + +package com.android.inputmethod.latin; + +import android.util.Log; +import android.view.KeyEvent; +import android.view.inputmethod.CompletionInfo; +import android.view.inputmethod.CorrectionInfo; +import android.view.inputmethod.InputConnection; + +/** + * Wrapper for InputConnection to simplify interaction + */ +public class RichInputConnection { + private static final String TAG = RichInputConnection.class.getSimpleName(); + private static final boolean DBG = false; + InputConnection mIC; + int mNestLevel; + public RichInputConnection() { + mIC = null; + mNestLevel = 0; + } + + // TODO: remove this method - the whole point of this class is void if mIC is escaping + public InputConnection getInputConnection() { + return mIC; + } + + public void beginBatchEdit(final InputConnection newInputConnection) { + if (++mNestLevel == 1) { + mIC = newInputConnection; + if (null != mIC) mIC.beginBatchEdit(); + } else { + if (DBG) { + throw new RuntimeException("Nest level too deep"); + } else { + Log.e(TAG, "Nest level too deep : " + mNestLevel); + } + } + } + public void endBatchEdit() { + if (mNestLevel <= 0) Log.e(TAG, "Batch edit not in progress!"); // TODO: exception instead + if (--mNestLevel == 0 && null != mIC) mIC.endBatchEdit(); + } + + public void finishComposingText() { + if (mNestLevel <= 0) Log.e(TAG, "Batch edit not in progress!"); // TODO: exception instead + if (null != mIC) mIC.finishComposingText(); + } + + public void commitText(final CharSequence text, final int i) { + if (mNestLevel <= 0) Log.e(TAG, "Batch edit not in progress!"); // TODO: exception instead + if (null != mIC) mIC.commitText(text, i); + } + + public int getCursorCapsMode(final int inputType) { + if (mNestLevel <= 0) Log.e(TAG, "Batch edit not in progress!"); // TODO: exception instead + if (null == mIC) return Constants.TextUtils.CAP_MODE_OFF; + return mIC.getCursorCapsMode(inputType); + } + + public CharSequence getTextBeforeCursor(final int i, final int j) { + if (mNestLevel <= 0) Log.e(TAG, "Batch edit not in progress!"); // TODO: exception instead + if (null != mIC) return mIC.getTextBeforeCursor(i, j); + return null; + } + + public CharSequence getTextAfterCursor(final int i, final int j) { + if (mNestLevel <= 0) Log.e(TAG, "Batch edit not in progress!"); // TODO: exception instead + if (null != mIC) return mIC.getTextAfterCursor(i, j); + return null; + } + + public void deleteSurroundingText(final int i, final int j) { + if (mNestLevel <= 0) Log.e(TAG, "Batch edit not in progress!"); // TODO: exception instead + if (null != mIC) mIC.deleteSurroundingText(i, j); + } + + public void performEditorAction(final int actionId) { + if (mNestLevel <= 0) Log.e(TAG, "Batch edit not in progress!"); // TODO: exception instead + if (null != mIC) mIC.performEditorAction(actionId); + } + + public void sendKeyEvent(final KeyEvent keyEvent) { + if (mNestLevel <= 0) Log.e(TAG, "Batch edit not in progress!"); // TODO: exception instead + if (null != mIC) mIC.sendKeyEvent(keyEvent); + } + + public void setComposingText(final CharSequence text, final int i) { + if (mNestLevel <= 0) Log.e(TAG, "Batch edit not in progress!"); // TODO: exception instead + if (null != mIC) mIC.setComposingText(text, i); + } + + public void setSelection(final int from, final int to) { + if (mNestLevel <= 0) Log.e(TAG, "Batch edit not in progress!"); // TODO: exception instead + if (null != mIC) mIC.setSelection(from, to); + } + + public void commitCorrection(final CorrectionInfo correctionInfo) { + if (mNestLevel <= 0) Log.e(TAG, "Batch edit not in progress!"); // TODO: exception instead + if (null != mIC) mIC.commitCorrection(correctionInfo); + } + + public void commitCompletion(final CompletionInfo completionInfo) { + if (mNestLevel <= 0) Log.e(TAG, "Batch edit not in progress!"); // TODO: exception instead + if (null != mIC) mIC.commitCompletion(completionInfo); + } +}