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.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) : <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)
|| 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|"<EOL> "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);
}
}
}

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