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
This commit is contained in:
Jean Chalard 2012-06-08 19:04:09 +09:00
parent c33a5428be
commit 5475b38328
2 changed files with 275 additions and 213 deletions

View file

@ -56,7 +56,6 @@ import android.view.WindowManager;
import android.view.inputmethod.CompletionInfo; import android.view.inputmethod.CompletionInfo;
import android.view.inputmethod.CorrectionInfo; import android.view.inputmethod.CorrectionInfo;
import android.view.inputmethod.EditorInfo; import android.view.inputmethod.EditorInfo;
import android.view.inputmethod.InputConnection;
import android.view.inputmethod.InputMethodSubtype; import android.view.inputmethod.InputMethodSubtype;
import com.android.inputmethod.accessibility.AccessibilityUtils; 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 LastComposedWord mLastComposedWord = LastComposedWord.NOT_A_COMPOSED_WORD;
private WordComposer mWordComposer = new WordComposer(); private WordComposer mWordComposer = new WordComposer();
private RichInputConnection mConnection = new RichInputConnection();
private int mCorrectionMode; private int mCorrectionMode;
@ -558,9 +558,10 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
if (mDisplayOrientation != conf.orientation) { if (mDisplayOrientation != conf.orientation) {
mDisplayOrientation = conf.orientation; mDisplayOrientation = conf.orientation;
mHandler.startOrientationChanging(); mHandler.startOrientationChanging();
final InputConnection ic = getCurrentInputConnection(); mConnection.beginBatchEdit(getCurrentInputConnection());
commitTyped(ic, LastComposedWord.NOT_A_SEPARATOR); commitTyped(LastComposedWord.NOT_A_SEPARATOR);
if (ic != null) ic.finishComposingText(); // For voice input mConnection.finishComposingText();
mConnection.endBatchEdit();
if (isShowingOptionDialog()) if (isShowingOptionDialog())
mOptionsDialog.dismiss(); mOptionsDialog.dismiss();
} }
@ -1024,10 +1025,7 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
private void resetEntireInputState() { private void resetEntireInputState() {
resetComposingState(true /* alsoResetLastComposedWord */); resetComposingState(true /* alsoResetLastComposedWord */);
updateSuggestions(); updateSuggestions();
final InputConnection ic = getCurrentInputConnection(); mConnection.finishComposingText();
if (ic != null) {
ic.finishComposingText();
}
} }
private void resetComposingState(final boolean alsoResetLastComposedWord) { private void resetComposingState(final boolean alsoResetLastComposedWord) {
@ -1036,16 +1034,14 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
mLastComposedWord = LastComposedWord.NOT_A_COMPOSED_WORD; 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; if (!mWordComposer.isComposingWord()) return;
final CharSequence typedWord = mWordComposer.getTypedWord(); final CharSequence typedWord = mWordComposer.getTypedWord();
if (typedWord.length() > 0) { if (typedWord.length() > 0) {
if (ic != null) { mConnection.commitText(typedWord, 1);
ic.commitText(typedWord, 1);
if (ProductionFlag.IS_EXPERIMENTAL) { if (ProductionFlag.IS_EXPERIMENTAL) {
ResearchLogger.latinIME_commitText(typedWord); ResearchLogger.latinIME_commitText(typedWord);
} }
}
final CharSequence prevWord = addToUserHistoryDictionary(typedWord); final CharSequence prevWord = addToUserHistoryDictionary(typedWord);
mLastComposedWord = mWordComposer.commitWord( mLastComposedWord = mWordComposer.commitWord(
LastComposedWord.COMMIT_TYPE_USER_TYPED_WORD, typedWord.toString(), LastComposedWord.COMMIT_TYPE_USER_TYPED_WORD, typedWord.toString(),
@ -1073,27 +1069,23 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
// unless needed. // unless needed.
if (mWordComposer.isComposingWord()) return Constants.TextUtils.CAP_MODE_OFF; 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. // TODO: This blocking IPC call is heavy. Consider doing this without using IPC calls.
// Note: getCursorCapsMode() returns the current capitalization mode that is any // 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 // combination of CAP_MODE_CHARACTERS, CAP_MODE_WORDS, and CAP_MODE_SENTENCES. 0 means none
// of them. // of them.
return ic.getCursorCapsMode(inputType); return mConnection.getCursorCapsMode(inputType);
} }
// "ic" may be null private void swapSwapperAndSpaceWhileInBatchEdit() {
private void swapSwapperAndSpaceWhileInBatchEdit(final InputConnection ic) { CharSequence lastTwo = mConnection.getTextBeforeCursor(2, 0);
if (null == ic) return;
CharSequence lastTwo = ic.getTextBeforeCursor(2, 0);
// It is guaranteed lastTwo.charAt(1) is a swapper - else this method is not called. // It is guaranteed lastTwo.charAt(1) is a swapper - else this method is not called.
if (lastTwo != null && lastTwo.length() == 2 if (lastTwo != null && lastTwo.length() == 2
&& lastTwo.charAt(0) == Keyboard.CODE_SPACE) { && lastTwo.charAt(0) == Keyboard.CODE_SPACE) {
ic.deleteSurroundingText(2, 0); mConnection.deleteSurroundingText(2, 0);
if (ProductionFlag.IS_EXPERIMENTAL) { if (ProductionFlag.IS_EXPERIMENTAL) {
ResearchLogger.latinIME_deleteSurroundingText(2); ResearchLogger.latinIME_deleteSurroundingText(2);
} }
ic.commitText(lastTwo.charAt(1) + " ", 1); mConnection.commitText(lastTwo.charAt(1) + " ", 1);
if (ProductionFlag.IS_EXPERIMENTAL) { if (ProductionFlag.IS_EXPERIMENTAL) {
ResearchLogger.latinIME_swapSwapperAndSpaceWhileInBatchEdit(); 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 (mCorrectionMode == Suggest.CORRECTION_NONE) return false;
if (ic == null) return false; final CharSequence lastThree = mConnection.getTextBeforeCursor(3, 0);
final CharSequence lastThree = ic.getTextBeforeCursor(3, 0);
if (lastThree != null && lastThree.length() == 3 if (lastThree != null && lastThree.length() == 3
&& canBeFollowedByPeriod(lastThree.charAt(0)) && canBeFollowedByPeriod(lastThree.charAt(0))
&& lastThree.charAt(1) == Keyboard.CODE_SPACE && lastThree.charAt(1) == Keyboard.CODE_SPACE
&& lastThree.charAt(2) == Keyboard.CODE_SPACE && lastThree.charAt(2) == Keyboard.CODE_SPACE
&& mHandler.isAcceptingDoubleSpaces()) { && mHandler.isAcceptingDoubleSpaces()) {
mHandler.cancelDoubleSpacesTimer(); mHandler.cancelDoubleSpacesTimer();
ic.deleteSurroundingText(2, 0); mConnection.deleteSurroundingText(2, 0);
ic.commitText(". ", 1); mConnection.commitText(". ", 1);
if (ProductionFlag.IS_EXPERIMENTAL) { if (ProductionFlag.IS_EXPERIMENTAL) {
ResearchLogger.latinIME_doubleSpaceAutoPeriod(); ResearchLogger.latinIME_doubleSpaceAutoPeriod();
} }
@ -1134,13 +1125,11 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
|| codePoint == Keyboard.CODE_CLOSING_ANGLE_BRACKET; || codePoint == Keyboard.CODE_CLOSING_ANGLE_BRACKET;
} }
// "ic" may be null private void removeTrailingSpaceWhileInBatchEdit() {
private static void removeTrailingSpaceWhileInBatchEdit(final InputConnection ic) { final CharSequence lastOne = mConnection.getTextBeforeCursor(1, 0);
if (ic == null) return;
final CharSequence lastOne = ic.getTextBeforeCursor(1, 0);
if (lastOne != null && lastOne.length() == 1 if (lastOne != null && lastOne.length() == 1
&& lastOne.charAt(0) == Keyboard.CODE_SPACE) { && lastOne.charAt(0) == Keyboard.CODE_SPACE) {
ic.deleteSurroundingText(1, 0); mConnection.deleteSurroundingText(1, 0);
if (ProductionFlag.IS_EXPERIMENTAL) { if (ProductionFlag.IS_EXPERIMENTAL) {
ResearchLogger.latinIME_deleteSurroundingText(1); ResearchLogger.latinIME_deleteSurroundingText(1);
} }
@ -1192,14 +1181,11 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
} }
private void performEditorAction(int actionId) { private void performEditorAction(int actionId) {
final InputConnection ic = getCurrentInputConnection(); mConnection.performEditorAction(actionId);
if (ic != null) {
ic.performEditorAction(actionId);
if (ProductionFlag.IS_EXPERIMENTAL) { if (ProductionFlag.IS_EXPERIMENTAL) {
ResearchLogger.latinIME_performEditorAction(actionId); ResearchLogger.latinIME_performEditorAction(actionId);
} }
} }
}
private void handleLanguageSwitchKey() { private void handleLanguageSwitchKey() {
final boolean includesOtherImes = mSettingsValues.mIncludesOtherImesInLanguageSwitchList; final boolean includesOtherImes = mSettingsValues.mIncludesOtherImesInLanguageSwitchList;
@ -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(); 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.ACTION_DOWN, code, 0, 0, KeyCharacterMap.VIRTUAL_KEYBOARD, 0,
KeyEvent.FLAG_SOFT_KEYBOARD | KeyEvent.FLAG_KEEP_TOUCH_MODE)); 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.ACTION_UP, code, 0, 0, KeyCharacterMap.VIRTUAL_KEYBOARD, 0,
KeyEvent.FLAG_SOFT_KEYBOARD | KeyEvent.FLAG_KEEP_TOUCH_MODE)); KeyEvent.FLAG_SOFT_KEYBOARD | KeyEvent.FLAG_KEEP_TOUCH_MODE));
} }
@ -1238,8 +1224,6 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
return; return;
} }
final InputConnection ic = getCurrentInputConnection();
if (ic != null) {
// 16 is android.os.Build.VERSION_CODES.JELLY_BEAN but we can't write it because // 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. // we want to be able to compile against the Ice Cream Sandwich SDK.
if (Keyboard.CODE_ENTER == code && mTargetApplicationInfo != null if (Keyboard.CODE_ENTER == code && mTargetApplicationInfo != null
@ -1248,16 +1232,15 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
// a hardware keyboard event on pressing enter or delete. This is bad for many // 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 // reasons (there are race conditions with commits) but some applications are
// relying on this behavior so we continue to support it for older apps. // relying on this behavior so we continue to support it for older apps.
sendUpDownEnterOrBackspace(KeyEvent.KEYCODE_ENTER, ic); sendUpDownEnterOrBackspace(KeyEvent.KEYCODE_ENTER);
} else { } else {
final String text = new String(new int[] { code }, 0, 1); final String text = new String(new int[] { code }, 0, 1);
ic.commitText(text, text.length()); mConnection.commitText(text, text.length());
} }
if (ProductionFlag.IS_EXPERIMENTAL) { if (ProductionFlag.IS_EXPERIMENTAL) {
ResearchLogger.latinIME_sendKeyCodePoint(code); ResearchLogger.latinIME_sendKeyCodePoint(code);
} }
} }
}
// Implementation of {@link KeyboardActionListener}. // Implementation of {@link KeyboardActionListener}.
@Override @Override
@ -1355,19 +1338,17 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
@Override @Override
public void onTextInput(CharSequence text) { public void onTextInput(CharSequence text) {
final InputConnection ic = getCurrentInputConnection(); mConnection.beginBatchEdit(getCurrentInputConnection());
if (ic == null) return; commitTyped(LastComposedWord.NOT_A_SEPARATOR);
ic.beginBatchEdit(); text = specificTldProcessingOnTextInput(text);
commitTyped(ic, LastComposedWord.NOT_A_SEPARATOR);
text = specificTldProcessingOnTextInput(ic, text);
if (SPACE_STATE_PHANTOM == mSpaceState) { if (SPACE_STATE_PHANTOM == mSpaceState) {
sendKeyCodePoint(Keyboard.CODE_SPACE); sendKeyCodePoint(Keyboard.CODE_SPACE);
} }
ic.commitText(text, 1); mConnection.commitText(text, 1);
if (ProductionFlag.IS_EXPERIMENTAL) { if (ProductionFlag.IS_EXPERIMENTAL) {
ResearchLogger.latinIME_commitText(text); ResearchLogger.latinIME_commitText(text);
} }
ic.endBatchEdit(); mConnection.endBatchEdit();
mKeyboardSwitcher.updateShiftState(); mKeyboardSwitcher.updateShiftState();
mKeyboardSwitcher.onCodeInput(Keyboard.CODE_OUTPUT_TEXT); mKeyboardSwitcher.onCodeInput(Keyboard.CODE_OUTPUT_TEXT);
mSpaceState = SPACE_STATE_NONE; mSpaceState = SPACE_STATE_NONE;
@ -1375,9 +1356,7 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
resetComposingState(true /* alsoResetLastComposedWord */); resetComposingState(true /* alsoResetLastComposedWord */);
} }
// ic may not be null private CharSequence specificTldProcessingOnTextInput(final CharSequence text) {
private CharSequence specificTldProcessingOnTextInput(final InputConnection ic,
final CharSequence text) {
if (text.length() <= 1 || text.charAt(0) != Keyboard.CODE_PERIOD if (text.length() <= 1 || text.charAt(0) != Keyboard.CODE_PERIOD
|| !Character.isLetter(text.charAt(1))) { || !Character.isLetter(text.charAt(1))) {
// Not a tld: do nothing. // 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 // We have a TLD (or something that looks like this): make sure we don't add
// a space even if currently in phantom mode. // a space even if currently in phantom mode.
mSpaceState = SPACE_STATE_NONE; mSpaceState = SPACE_STATE_NONE;
final CharSequence lastOne = ic.getTextBeforeCursor(1, 0); final CharSequence lastOne = mConnection.getTextBeforeCursor(1, 0);
if (lastOne != null && lastOne.length() == 1 if (lastOne != null && lastOne.length() == 1
&& lastOne.charAt(0) == Keyboard.CODE_PERIOD) { && lastOne.charAt(0) == Keyboard.CODE_PERIOD) {
return text.subSequence(1, text.length()); return text.subSequence(1, text.length());
@ -1402,24 +1381,21 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
} }
private void handleBackspace(final int spaceState) { private void handleBackspace(final int spaceState) {
final InputConnection ic = getCurrentInputConnection(); mConnection.beginBatchEdit(getCurrentInputConnection());
if (ic == null) return; handleBackspaceWhileInBatchEdit(spaceState);
ic.beginBatchEdit(); mConnection.endBatchEdit();
handleBackspaceWhileInBatchEdit(spaceState, ic);
ic.endBatchEdit();
} }
// "ic" may not be null. private void handleBackspaceWhileInBatchEdit(final int spaceState) {
private void handleBackspaceWhileInBatchEdit(final int spaceState, final InputConnection ic) {
// In many cases, we may have to put the keyboard in auto-shift state again. // In many cases, we may have to put the keyboard in auto-shift state again.
mHandler.postUpdateShiftState(); mHandler.postUpdateShiftState();
if (mEnteredText != null && sameAsTextBeforeCursor(ic, mEnteredText)) { if (mEnteredText != null && sameAsTextBeforeCursor(mEnteredText)) {
// Cancel multi-character input: remove the text we just entered. // Cancel multi-character input: remove the text we just entered.
// This is triggered on backspace after a key that inputs multiple characters, // This is triggered on backspace after a key that inputs multiple characters,
// like the smiley key or the .com key. // like the smiley key or the .com key.
final int length = mEnteredText.length(); final int length = mEnteredText.length();
ic.deleteSurroundingText(length, 0); mConnection.deleteSurroundingText(length, 0);
if (ProductionFlag.IS_EXPERIMENTAL) { if (ProductionFlag.IS_EXPERIMENTAL) {
ResearchLogger.latinIME_deleteSurroundingText(length); ResearchLogger.latinIME_deleteSurroundingText(length);
} }
@ -1433,7 +1409,7 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
final int length = mWordComposer.size(); final int length = mWordComposer.size();
if (length > 0) { if (length > 0) {
mWordComposer.deleteLast(); 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 // If we have deleted the last remaining character of a word, then we are not
// isComposingWord() any more. // isComposingWord() any more.
if (!mWordComposer.isComposingWord()) { if (!mWordComposer.isComposingWord()) {
@ -1444,7 +1420,7 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
mHandler.postUpdateSuggestions(); mHandler.postUpdateSuggestions();
} }
} else { } else {
ic.deleteSurroundingText(1, 0); mConnection.deleteSurroundingText(1, 0);
if (ProductionFlag.IS_EXPERIMENTAL) { if (ProductionFlag.IS_EXPERIMENTAL) {
ResearchLogger.latinIME_deleteSurroundingText(1); ResearchLogger.latinIME_deleteSurroundingText(1);
} }
@ -1452,17 +1428,17 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
} else { } else {
if (mLastComposedWord.canRevertCommit()) { if (mLastComposedWord.canRevertCommit()) {
Utils.Stats.onAutoCorrectionCancellation(); Utils.Stats.onAutoCorrectionCancellation();
revertCommit(ic); revertCommit();
return; return;
} }
if (SPACE_STATE_DOUBLE == spaceState) { if (SPACE_STATE_DOUBLE == spaceState) {
if (revertDoubleSpaceWhileInBatchEdit(ic)) { if (revertDoubleSpaceWhileInBatchEdit()) {
// No need to reset mSpaceState, it has already be done (that's why we // No need to reset mSpaceState, it has already be done (that's why we
// receive it as a parameter) // receive it as a parameter)
return; return;
} }
} else if (SPACE_STATE_SWAP_PUNCTUATION == spaceState) { } else if (SPACE_STATE_SWAP_PUNCTUATION == spaceState) {
if (revertSwapPunctuation(ic)) { if (revertSwapPunctuation()) {
// Likewise // Likewise
return; return;
} }
@ -1473,8 +1449,8 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
if (mLastSelectionStart != mLastSelectionEnd) { if (mLastSelectionStart != mLastSelectionEnd) {
// If there is a selection, remove it. // If there is a selection, remove it.
final int lengthToDelete = mLastSelectionEnd - mLastSelectionStart; final int lengthToDelete = mLastSelectionEnd - mLastSelectionStart;
ic.setSelection(mLastSelectionEnd, mLastSelectionEnd); mConnection.setSelection(mLastSelectionEnd, mLastSelectionEnd);
ic.deleteSurroundingText(lengthToDelete, 0); mConnection.deleteSurroundingText(lengthToDelete, 0);
if (ProductionFlag.IS_EXPERIMENTAL) { if (ProductionFlag.IS_EXPERIMENTAL) {
ResearchLogger.latinIME_deleteSurroundingText(lengthToDelete); 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 // 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 // reasons (there are race conditions with commits) but some applications are
// relying on this behavior so we continue to support it for older apps. // relying on this behavior so we continue to support it for older apps.
sendUpDownEnterOrBackspace(KeyEvent.KEYCODE_DEL, ic); sendUpDownEnterOrBackspace(KeyEvent.KEYCODE_DEL);
} else { } else {
ic.deleteSurroundingText(1, 0); mConnection.deleteSurroundingText(1, 0);
} }
if (ProductionFlag.IS_EXPERIMENTAL) { if (ProductionFlag.IS_EXPERIMENTAL) {
ResearchLogger.latinIME_deleteSurroundingText(1); ResearchLogger.latinIME_deleteSurroundingText(1);
} }
if (mDeleteCount > DELETE_ACCELERATE_AT) { if (mDeleteCount > DELETE_ACCELERATE_AT) {
ic.deleteSurroundingText(1, 0); mConnection.deleteSurroundingText(1, 0);
if (ProductionFlag.IS_EXPERIMENTAL) { if (ProductionFlag.IS_EXPERIMENTAL) {
ResearchLogger.latinIME_deleteSurroundingText(1); ResearchLogger.latinIME_deleteSurroundingText(1);
} }
} }
} }
if (isSuggestionsRequested()) { if (isSuggestionsRequested()) {
restartSuggestionsOnWordBeforeCursorIfAtEndOfWord(ic); restartSuggestionsOnWordBeforeCursorIfAtEndOfWord();
} }
} }
} }
// ic may be null private boolean maybeStripSpaceWhileInBatchEdit(final int code,
private boolean maybeStripSpaceWhileInBatchEdit(final InputConnection ic, final int code,
final int spaceState, final boolean isFromSuggestionStrip) { final int spaceState, final boolean isFromSuggestionStrip) {
if (Keyboard.CODE_ENTER == code && SPACE_STATE_SWAP_PUNCTUATION == spaceState) { if (Keyboard.CODE_ENTER == code && SPACE_STATE_SWAP_PUNCTUATION == spaceState) {
removeTrailingSpaceWhileInBatchEdit(ic); removeTrailingSpaceWhileInBatchEdit();
return false; return false;
} else if ((SPACE_STATE_WEAK == spaceState } else if ((SPACE_STATE_WEAK == spaceState
|| SPACE_STATE_SWAP_PUNCTUATION == spaceState) || SPACE_STATE_SWAP_PUNCTUATION == spaceState)
@ -1525,7 +1500,7 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
return true; return true;
} else { } else {
if (mSettingsValues.isWeakSpaceStripper(code)) { if (mSettingsValues.isWeakSpaceStripper(code)) {
removeTrailingSpaceWhileInBatchEdit(ic); removeTrailingSpaceWhileInBatchEdit();
} }
return false; return false;
} }
@ -1536,16 +1511,13 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
private void handleCharacter(final int primaryCode, final int x, private void handleCharacter(final int primaryCode, final int x,
final int y, final int spaceState) { final int y, final int spaceState) {
final InputConnection ic = getCurrentInputConnection(); mConnection.beginBatchEdit(getCurrentInputConnection());
if (null != ic) ic.beginBatchEdit(); handleCharacterWhileInBatchEdit(primaryCode, x, y, spaceState);
// TODO: if ic is null, does it make any sense to call this? mConnection.endBatchEdit();
handleCharacterWhileInBatchEdit(primaryCode, x, y, spaceState, ic);
if (null != ic) ic.endBatchEdit();
} }
// "ic" may be null without this crashing, but the behavior will be really strange
private void handleCharacterWhileInBatchEdit(final int primaryCode, 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(); boolean isComposingWord = mWordComposer.isComposingWord();
if (SPACE_STATE_PHANTOM == spaceState && if (SPACE_STATE_PHANTOM == spaceState &&
@ -1578,23 +1550,21 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
if (isComposingWord) { if (isComposingWord) {
mWordComposer.add( mWordComposer.add(
primaryCode, x, y, mKeyboardSwitcher.getKeyboardView().getKeyDetector()); primaryCode, x, y, mKeyboardSwitcher.getKeyboardView().getKeyDetector());
if (ic != null) {
// If it's the first letter, make note of auto-caps state // If it's the first letter, make note of auto-caps state
if (mWordComposer.size() == 1) { if (mWordComposer.size() == 1) {
mWordComposer.setAutoCapitalized( mWordComposer.setAutoCapitalized(
getCurrentAutoCapsState() != Constants.TextUtils.CAP_MODE_OFF); getCurrentAutoCapsState() != Constants.TextUtils.CAP_MODE_OFF);
} }
ic.setComposingText(getTextWithUnderline(mWordComposer.getTypedWord()), 1); mConnection.setComposingText(getTextWithUnderline(mWordComposer.getTypedWord()), 1);
}
mHandler.postUpdateSuggestions(); mHandler.postUpdateSuggestions();
} else { } else {
final boolean swapWeakSpace = maybeStripSpaceWhileInBatchEdit(ic, primaryCode, final boolean swapWeakSpace = maybeStripSpaceWhileInBatchEdit(primaryCode,
spaceState, KeyboardActionListener.SUGGESTION_STRIP_COORDINATE == x); spaceState, KeyboardActionListener.SUGGESTION_STRIP_COORDINATE == x);
sendKeyCodePoint(primaryCode); sendKeyCodePoint(primaryCode);
if (swapWeakSpace) { if (swapWeakSpace) {
swapSwapperAndSpaceWhileInBatchEdit(ic); swapSwapperAndSpaceWhileInBatchEdit();
mSpaceState = SPACE_STATE_WEAK; mSpaceState = SPACE_STATE_WEAK;
} }
// Some characters are not word separators, yet they don't start a new // 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; boolean didAutoCorrect = false;
// Handle separator // Handle separator
final InputConnection ic = getCurrentInputConnection(); mConnection.beginBatchEdit(getCurrentInputConnection());
if (ic != null) {
ic.beginBatchEdit();
}
if (mWordComposer.isComposingWord()) { if (mWordComposer.isComposingWord()) {
// In certain languages where single quote is a separator, it's better // In certain languages where single quote is a separator, it's better
// not to auto correct, but accept the typed word. For instance, // 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 final boolean shouldAutoCorrect = mSettingsValues.mAutoCorrectEnabled
&& !mInputAttributes.mInputTypeNoAutoCorrect; && !mInputAttributes.mInputTypeNoAutoCorrect;
if (shouldAutoCorrect && primaryCode != Keyboard.CODE_SINGLE_QUOTE) { if (shouldAutoCorrect && primaryCode != Keyboard.CODE_SINGLE_QUOTE) {
commitCurrentAutoCorrection(primaryCode, ic); commitCurrentAutoCorrection(primaryCode);
didAutoCorrect = true; didAutoCorrect = true;
} else { } else {
commitTyped(ic, primaryCode); commitTyped(primaryCode);
} }
} }
final boolean swapWeakSpace = maybeStripSpaceWhileInBatchEdit(ic, primaryCode, spaceState, final boolean swapWeakSpace = maybeStripSpaceWhileInBatchEdit(primaryCode, spaceState,
KeyboardActionListener.SUGGESTION_STRIP_COORDINATE == x); KeyboardActionListener.SUGGESTION_STRIP_COORDINATE == x);
if (SPACE_STATE_PHANTOM == spaceState && if (SPACE_STATE_PHANTOM == spaceState &&
@ -1651,7 +1618,7 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
if (Keyboard.CODE_SPACE == primaryCode) { if (Keyboard.CODE_SPACE == primaryCode) {
if (isSuggestionsRequested()) { if (isSuggestionsRequested()) {
if (maybeDoubleSpaceWhileInBatchEdit(ic)) { if (maybeDoubleSpaceWhileInBatchEdit()) {
mSpaceState = SPACE_STATE_DOUBLE; mSpaceState = SPACE_STATE_DOUBLE;
} else if (!isShowingPunctuationList()) { } else if (!isShowingPunctuationList()) {
mSpaceState = SPACE_STATE_WEAK; mSpaceState = SPACE_STATE_WEAK;
@ -1665,7 +1632,7 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
} }
} else { } else {
if (swapWeakSpace) { if (swapWeakSpace) {
swapSwapperAndSpaceWhileInBatchEdit(ic); swapSwapperAndSpaceWhileInBatchEdit();
mSpaceState = SPACE_STATE_SWAP_PUNCTUATION; mSpaceState = SPACE_STATE_SWAP_PUNCTUATION;
} else if (SPACE_STATE_PHANTOM == spaceState) { } else if (SPACE_STATE_PHANTOM == spaceState) {
// If we are in phantom space state, and the user presses a separator, we want to // 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); Utils.Stats.onSeparator((char)primaryCode, x, y);
if (ic != null) { mConnection.endBatchEdit();
ic.endBatchEdit();
}
return didAutoCorrect; return didAutoCorrect;
} }
@ -1697,7 +1662,7 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
} }
private void handleClose() { private void handleClose() {
commitTyped(getCurrentInputConnection(), LastComposedWord.NOT_A_SEPARATOR); commitTyped(LastComposedWord.NOT_A_SEPARATOR);
requestHideSelf(0); requestHideSelf(0);
LatinKeyboardView inputView = mKeyboardSwitcher.getKeyboardView(); LatinKeyboardView inputView = mKeyboardSwitcher.getKeyboardView();
if (inputView != null) if (inputView != null)
@ -1767,14 +1732,12 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
private void setAutoCorrectionIndicator(final boolean newAutoCorrectionIndicator) { private void setAutoCorrectionIndicator(final boolean newAutoCorrectionIndicator) {
// Put a blue underline to a word in TextView which will be auto-corrected. // 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 if (mIsAutoCorrectionIndicatorOn != newAutoCorrectionIndicator
&& mWordComposer.isComposingWord()) { && mWordComposer.isComposingWord()) {
mIsAutoCorrectionIndicatorOn = newAutoCorrectionIndicator; mIsAutoCorrectionIndicatorOn = newAutoCorrectionIndicator;
final CharSequence textWithUnderline = final CharSequence textWithUnderline =
getTextWithUnderline(mWordComposer.getTypedWord()); 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 // TODO: May need a better way of retrieving previous word
final InputConnection ic = getCurrentInputConnection();
final CharSequence prevWord; final CharSequence prevWord;
if (null == ic) { // TODO: move getPreviousWord to AutoInputConnection
prevWord = null; prevWord = EditingUtils.getPreviousWord(mConnection.getInputConnection(),
} else { mSettingsValues.mWordSeparators);
prevWord = EditingUtils.getPreviousWord(ic, mSettingsValues.mWordSeparators);
}
final CharSequence typedWord = mWordComposer.getTypedWord(); final CharSequence typedWord = mWordComposer.getTypedWord();
// getSuggestedWords handles gracefully a null value of prevWord // getSuggestedWords handles gracefully a null value of prevWord
@ -1858,8 +1818,7 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
setSuggestionStripShown(isSuggestionsStripVisible()); setSuggestionStripShown(isSuggestionsStripVisible());
} }
private void commitCurrentAutoCorrection(final int separatorCodePoint, private void commitCurrentAutoCorrection(final int separatorCodePoint) {
final InputConnection ic) {
// Complete any pending suggestions query first // Complete any pending suggestions query first
if (mHandler.hasPendingUpdateSuggestions()) { if (mHandler.hasPendingUpdateSuggestions()) {
mHandler.cancelUpdateSuggestions(); mHandler.cancelUpdateSuggestions();
@ -1880,10 +1839,11 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
mExpectingUpdateSelection = true; mExpectingUpdateSelection = true;
commitChosenWord(autoCorrection, LastComposedWord.COMMIT_TYPE_DECIDED_WORD, commitChosenWord(autoCorrection, LastComposedWord.COMMIT_TYPE_DECIDED_WORD,
separatorCodePoint); 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 // This will make the correction flash for a short while as a visual clue
// to the user that auto-correction happened. // to the user that auto-correction happened.
ic.commitCorrection(new CorrectionInfo(mLastSelectionEnd - typedWord.length(), mConnection.commitCorrection(
new CorrectionInfo(mLastSelectionEnd - typedWord.length(),
typedWord, autoCorrection)); typedWord, autoCorrection));
} }
} }
@ -1892,14 +1852,13 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
@Override @Override
public void pickSuggestionManually(final int index, final CharSequence suggestion, public void pickSuggestionManually(final int index, final CharSequence suggestion,
int x, int y) { int x, int y) {
final InputConnection ic = getCurrentInputConnection(); mConnection.beginBatchEdit(getCurrentInputConnection());
if (null != ic) ic.beginBatchEdit(); pickSuggestionManuallyWhileInBatchEdit(index, suggestion, x, y);
pickSuggestionManuallyWhileInBatchEdit(index, suggestion, x, y, ic); mConnection.endBatchEdit();
if (null != ic) ic.endBatchEdit();
} }
public void pickSuggestionManuallyWhileInBatchEdit(final int index, 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(); final SuggestedWords suggestedWords = mSuggestionsView.getSuggestions();
// If this is a punctuation picked from the suggestion strip, pass it to onCodeInput // If this is a punctuation picked from the suggestion strip, pass it to onCodeInput
if (suggestion.length() == 1 && isShowingPunctuationList()) { if (suggestion.length() == 1 && isShowingPunctuationList()) {
@ -1933,14 +1892,12 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
} }
mKeyboardSwitcher.updateShiftState(); mKeyboardSwitcher.updateShiftState();
resetComposingState(true /* alsoResetLastComposedWord */); resetComposingState(true /* alsoResetLastComposedWord */);
if (ic != null) {
final CompletionInfo completionInfo = mApplicationSpecifiedCompletions[index]; final CompletionInfo completionInfo = mApplicationSpecifiedCompletions[index];
ic.commitCompletion(completionInfo); mConnection.commitCompletion(completionInfo);
if (ProductionFlag.IS_EXPERIMENTAL) { if (ProductionFlag.IS_EXPERIMENTAL) {
ResearchLogger.latinIME_pickApplicationSpecifiedCompletion(index, ResearchLogger.latinIME_pickApplicationSpecifiedCompletion(index,
completionInfo.getText(), x, y); completionInfo.getText(), x, y);
} }
}
return; return;
} }
@ -2001,23 +1958,20 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
*/ */
private void commitChosenWord(final CharSequence chosenWord, final int commitType, private void commitChosenWord(final CharSequence chosenWord, final int commitType,
final int separatorCode) { final int separatorCode) {
final InputConnection ic = getCurrentInputConnection();
if (ic != null) {
if (mSettingsValues.mEnableSuggestionSpanInsertion) { if (mSettingsValues.mEnableSuggestionSpanInsertion) {
final SuggestedWords suggestedWords = mSuggestionsView.getSuggestions(); final SuggestedWords suggestedWords = mSuggestionsView.getSuggestions();
ic.commitText(SuggestionSpanUtils.getTextWithSuggestionSpan( mConnection.commitText(SuggestionSpanUtils.getTextWithSuggestionSpan(
this, chosenWord, suggestedWords, mIsMainDictionaryAvailable), this, chosenWord, suggestedWords, mIsMainDictionaryAvailable),
1); 1);
if (ProductionFlag.IS_EXPERIMENTAL) { if (ProductionFlag.IS_EXPERIMENTAL) {
ResearchLogger.latinIME_commitText(chosenWord); ResearchLogger.latinIME_commitText(chosenWord);
} }
} else { } else {
ic.commitText(chosenWord, 1); mConnection.commitText(chosenWord, 1);
if (ProductionFlag.IS_EXPERIMENTAL) { if (ProductionFlag.IS_EXPERIMENTAL) {
ResearchLogger.latinIME_commitText(chosenWord); ResearchLogger.latinIME_commitText(chosenWord);
} }
} }
}
// Add the word to the user history dictionary // Add the word to the user history dictionary
final CharSequence prevWord = addToUserHistoryDictionary(chosenWord); final CharSequence prevWord = addToUserHistoryDictionary(chosenWord);
// TODO: figure out here if this is an auto-correct or if the best word is actually // TODO: figure out here if this is an auto-correct or if the best word is actually
@ -2077,13 +2031,10 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
} }
if (mUserHistoryDictionary != null) { if (mUserHistoryDictionary != null) {
final InputConnection ic = getCurrentInputConnection();
final CharSequence prevWord; final CharSequence prevWord;
if (null != ic) { // TODO: move getPreviousWord to AutoInputConnection
prevWord = EditingUtils.getPreviousWord(ic, mSettingsValues.mWordSeparators); prevWord = EditingUtils.getPreviousWord(mConnection.getInputConnection(),
} else { mSettingsValues.mWordSeparators);
prevWord = null;
}
final String secondWord; final String secondWord;
if (mWordComposer.isAutoCapitalized() && !mWordComposer.isMostlyCaps()) { if (mWordComposer.isAutoCapitalized() && !mWordComposer.isMostlyCaps()) {
secondWord = suggestion.toString().toLowerCase( secondWord = suggestion.toString().toLowerCase(
@ -2103,10 +2054,8 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
} }
public boolean isCursorTouchingWord() { public boolean isCursorTouchingWord() {
final InputConnection ic = getCurrentInputConnection(); CharSequence before = mConnection.getTextBeforeCursor(1, 0);
if (ic == null) return false; CharSequence after = mConnection.getTextAfterCursor(1, 0);
CharSequence before = ic.getTextBeforeCursor(1, 0);
CharSequence after = ic.getTextAfterCursor(1, 0);
if (!TextUtils.isEmpty(before) && !mSettingsValues.isWordSeparator(before.charAt(0)) if (!TextUtils.isEmpty(before) && !mSettingsValues.isWordSeparator(before.charAt(0))
&& !mSettingsValues.isSymbolExcludedFromWordSeparators(before.charAt(0))) { && !mSettingsValues.isSymbolExcludedFromWordSeparators(before.charAt(0))) {
return true; return true;
@ -2118,37 +2067,35 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
return false; return false;
} }
// "ic" must not be null private boolean sameAsTextBeforeCursor(final CharSequence text) {
private static boolean sameAsTextBeforeCursor(final InputConnection ic, final CharSequence beforeText = mConnection.getTextBeforeCursor(text.length(), 0);
final CharSequence text) {
final CharSequence beforeText = ic.getTextBeforeCursor(text.length(), 0);
return TextUtils.equals(text, beforeText); 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 * Check if the cursor is actually at the end of a word. If so, restart suggestions on this
* word, else do nothing. * word, else do nothing.
*/ */
private void restartSuggestionsOnWordBeforeCursorIfAtEndOfWord( private void restartSuggestionsOnWordBeforeCursorIfAtEndOfWord() {
final InputConnection ic) {
// Bail out if the cursor is not at the end of a word (cursor must be preceded by // 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) // non-whitespace, non-separator, non-start-of-text)
// Example ("|" is the cursor here) : <SOL>"|a" " |a" " | " all get rejected here. // Example ("|" is the cursor here) : <SOL>"|a" " |a" " | " all get rejected here.
final CharSequence textBeforeCursor = ic.getTextBeforeCursor(1, 0); final CharSequence textBeforeCursor = mConnection.getTextBeforeCursor(1, 0);
if (TextUtils.isEmpty(textBeforeCursor) if (TextUtils.isEmpty(textBeforeCursor)
|| mSettingsValues.isWordSeparator(textBeforeCursor.charAt(0))) return; || mSettingsValues.isWordSeparator(textBeforeCursor.charAt(0))) return;
// Bail out if the cursor is in the middle of a word (cursor must be followed by whitespace, // Bail out if the cursor is in the middle of a word (cursor must be followed by whitespace,
// separator or end of line/text) // separator or end of line/text)
// Example: "test|"<EOL> "te|st" get rejected here // Example: "test|"<EOL> "te|st" get rejected here
final CharSequence textAfterCursor = ic.getTextAfterCursor(1, 0); final CharSequence textAfterCursor = mConnection.getTextAfterCursor(1, 0);
if (!TextUtils.isEmpty(textAfterCursor) if (!TextUtils.isEmpty(textAfterCursor)
&& !mSettingsValues.isWordSeparator(textAfterCursor.charAt(0))) return; && !mSettingsValues.isWordSeparator(textAfterCursor.charAt(0))) return;
// Bail out if word before cursor is 0-length or a single non letter (like an apostrophe) // 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 // 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 // We don't suggest on leading single quotes, so we have to remove them from the word if
// it starts with single quotes. // it starts with single quotes.
while (!TextUtils.isEmpty(word) && Keyboard.CODE_SINGLE_QUOTE == word.charAt(0)) { 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. // 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 CharSequence word) {
private void restartSuggestionsOnWordBeforeCursor(final InputConnection ic,
final CharSequence word) {
mWordComposer.setComposingWord(word, mKeyboardSwitcher.getKeyboard()); mWordComposer.setComposingWord(word, mKeyboardSwitcher.getKeyboard());
final int length = word.length(); final int length = word.length();
ic.deleteSurroundingText(length, 0); mConnection.deleteSurroundingText(length, 0);
if (ProductionFlag.IS_EXPERIMENTAL) { if (ProductionFlag.IS_EXPERIMENTAL) {
ResearchLogger.latinIME_deleteSurroundingText(length); ResearchLogger.latinIME_deleteSurroundingText(length);
} }
ic.setComposingText(word, 1); mConnection.setComposingText(word, 1);
mHandler.postUpdateSuggestions(); mHandler.postUpdateSuggestions();
} }
// "ic" must not be null private void revertCommit() {
private void revertCommit(final InputConnection ic) {
final CharSequence previousWord = mLastComposedWord.mPrevWord; final CharSequence previousWord = mLastComposedWord.mPrevWord;
final String originallyTypedWord = mLastComposedWord.mTypedWord; final String originallyTypedWord = mLastComposedWord.mTypedWord;
final CharSequence committedWord = mLastComposedWord.mCommittedWord; 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"); throw new RuntimeException("revertCommit, but we are composing a word");
} }
final String wordBeforeCursor = final String wordBeforeCursor =
ic.getTextBeforeCursor(deleteLength, 0) mConnection.getTextBeforeCursor(deleteLength, 0)
.subSequence(0, cancelLength).toString(); .subSequence(0, cancelLength).toString();
if (!TextUtils.equals(committedWord, wordBeforeCursor)) { if (!TextUtils.equals(committedWord, wordBeforeCursor)) {
throw new RuntimeException("revertCommit check failed: we thought we were " 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 + "\""); + "\", but before the cursor we found \"" + wordBeforeCursor + "\"");
} }
} }
ic.deleteSurroundingText(deleteLength, 0); mConnection.deleteSurroundingText(deleteLength, 0);
if (ProductionFlag.IS_EXPERIMENTAL) { if (ProductionFlag.IS_EXPERIMENTAL) {
ResearchLogger.latinIME_deleteSurroundingText(deleteLength); 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. // This is the case when we cancel a manual pick.
// We should restart suggestion on the word right away. // We should restart suggestion on the word right away.
mWordComposer.resumeSuggestionOnLastComposedWord(mLastComposedWord); mWordComposer.resumeSuggestionOnLastComposedWord(mLastComposedWord);
ic.setComposingText(originallyTypedWord, 1); mConnection.setComposingText(originallyTypedWord, 1);
} else { } else {
ic.commitText(originallyTypedWord, 1); mConnection.commitText(originallyTypedWord, 1);
// Re-insert the separator // Re-insert the separator
sendKeyCodePoint(mLastComposedWord.mSeparatorCode); sendKeyCodePoint(mLastComposedWord.mSeparatorCode);
Utils.Stats.onSeparator(mLastComposedWord.mSeparatorCode, WordComposer.NOT_A_COORDINATE, Utils.Stats.onSeparator(mLastComposedWord.mSeparatorCode, WordComposer.NOT_A_COORDINATE,
@ -2235,12 +2179,11 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
mHandler.postUpdateSuggestions(); mHandler.postUpdateSuggestions();
} }
// "ic" must not be null private boolean revertDoubleSpaceWhileInBatchEdit() {
private boolean revertDoubleSpaceWhileInBatchEdit(final InputConnection ic) {
mHandler.cancelDoubleSpacesTimer(); mHandler.cancelDoubleSpacesTimer();
// Here we test whether we indeed have a period and a space before us. This should not // 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. // 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)) { if (!". ".equals(textBeforeCursor)) {
// Theoretically we should not be coming here if there isn't ". " before the // 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 // 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."); + "\". \" just before the cursor.");
return false; return false;
} }
ic.deleteSurroundingText(2, 0); mConnection.deleteSurroundingText(2, 0);
if (ProductionFlag.IS_EXPERIMENTAL) { if (ProductionFlag.IS_EXPERIMENTAL) {
ResearchLogger.latinIME_deleteSurroundingText(2); ResearchLogger.latinIME_deleteSurroundingText(2);
} }
ic.commitText(" ", 1); mConnection.commitText(" ", 1);
if (ProductionFlag.IS_EXPERIMENTAL) { if (ProductionFlag.IS_EXPERIMENTAL) {
ResearchLogger.latinIME_revertDoubleSpaceWhileInBatchEdit(); ResearchLogger.latinIME_revertDoubleSpaceWhileInBatchEdit();
} }
return true; 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 // 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. // 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 // NOTE: This does not work with surrogate pairs. Hopefully when the keyboard is able to
// enter surrogate pairs this code will have been removed. // enter surrogate pairs this code will have been removed.
if (TextUtils.isEmpty(textBeforeCursor) if (TextUtils.isEmpty(textBeforeCursor)
@ -2275,16 +2218,16 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
+ "find a space just before the cursor."); + "find a space just before the cursor.");
return false; return false;
} }
ic.beginBatchEdit(); mConnection.beginBatchEdit(getCurrentInputConnection());
ic.deleteSurroundingText(2, 0); mConnection.deleteSurroundingText(2, 0);
if (ProductionFlag.IS_EXPERIMENTAL) { if (ProductionFlag.IS_EXPERIMENTAL) {
ResearchLogger.latinIME_deleteSurroundingText(2); ResearchLogger.latinIME_deleteSurroundingText(2);
} }
ic.commitText(" " + textBeforeCursor.subSequence(0, 1), 1); mConnection.commitText(" " + textBeforeCursor.subSequence(0, 1), 1);
if (ProductionFlag.IS_EXPERIMENTAL) { if (ProductionFlag.IS_EXPERIMENTAL) {
ResearchLogger.latinIME_revertSwapPunctuation(); ResearchLogger.latinIME_revertSwapPunctuation();
} }
ic.endBatchEdit(); mConnection.endBatchEdit();
return true; 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. // 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 // In the future, we need to deprecate deteleSurroundingText() and have a surrogate
// pair-friendly way of deleting characters in InputConnection. // pair-friendly way of deleting characters in InputConnection.
final InputConnection ic = getCurrentInputConnection(); final CharSequence lastChar = mConnection.getTextBeforeCursor(1, 0);
if (null != ic) {
final CharSequence lastChar = ic.getTextBeforeCursor(1, 0);
if (!TextUtils.isEmpty(lastChar) && Character.isHighSurrogate(lastChar.charAt(0))) { if (!TextUtils.isEmpty(lastChar) && Character.isHighSurrogate(lastChar.charAt(0))) {
ic.deleteSurroundingText(1, 0); mConnection.deleteSurroundingText(1, 0);
}
} }
} }
} }

View file

@ -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);
}
}