[IL3] Move handleBackspace to InputLogic.

Also move resetEntireInputState, resetComposingState,
sendDownUpKeyEvent, sendKeyCodePoint and getTextWithUnderline

Bug: 8636060
Change-Id: Ic4c270ac49769ffba41dc3f12d16ac3aa938717f
This commit is contained in:
Jean Chalard 2013-12-19 16:07:24 +09:00
parent cddf4f9d78
commit 0e82fa273d
3 changed files with 257 additions and 241 deletions

View file

@ -156,6 +156,8 @@ public final class Constants {
// Key events coming any faster than this are long-presses.
public static final int LONG_PRESS_MILLISECONDS = 200;
// How many continuous deletes at which to start deleting at a higher speed.
public static final int DELETE_ACCELERATE_AT = 20;
public static boolean isValidCoordinate(final int coordinate) {
// Detect {@link NOT_A_COORDINATE}, {@link SUGGESTION_STRIP_COORDINATE},

View file

@ -121,9 +121,6 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
private static final int EXTENDED_TOUCHABLE_REGION_HEIGHT = 100;
// How many continuous deletes at which to start deleting at a higher speed.
private static final int DELETE_ACCELERATE_AT = 20;
private static final int PENDING_IMS_CALLBACK_DURATION = 800;
private static final int PERIOD_FOR_AUDIO_AND_HAPTIC_FEEDBACK_IN_KEY_REPEAT = 2;
@ -145,7 +142,8 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
private SuggestionStripView mSuggestionStripView;
private CompletionInfo[] mApplicationSpecifiedCompletions;
private AppWorkaroundsUtils mAppWorkAroundsUtils = new AppWorkaroundsUtils();
// TODO[IL]: Make this an AsyncResultHolder or a Future in SettingsValues
public AppWorkaroundsUtils mAppWorkAroundsUtils = new AppWorkaroundsUtils();
private RichInputMethodManager mRichImm;
@UsedForTesting final KeyboardSwitcher mKeyboardSwitcher;
@ -160,8 +158,9 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
private boolean mUseOnlyPersonalizationDictionaryForDebug = false;
private boolean mBoostPersonalizationDictionaryForDebug = false;
// Member variables for remembering the current device orientation.
private int mDisplayOrientation;
// Member variable for remembering the current device orientation.
// TODO[IL]: Move this to SettingsValues.
public int mDisplayOrientation;
// Object for reacting to adding/removing a dictionary pack.
private BroadcastReceiver mDictionaryPackInstallReceiver =
@ -784,7 +783,7 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
// The app calling setText() has the effect of clearing the composing
// span, so we should reset our state unconditionally, even if restarting is true.
mInputLogic.mEnteredText = null;
resetComposingState(true /* alsoResetLastComposedWord */);
mInputLogic.resetComposingState(true /* alsoResetLastComposedWord */);
mInputLogic.mDeleteCount = 0;
mInputLogic.mSpaceState = SpaceState.NONE;
mInputLogic.mRecapitalizeStatus.deactivate();
@ -965,7 +964,7 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
if (mInputLogic.mWordComposer.isComposingWord()) {
mInputLogic.mConnection.finishComposingText();
}
resetComposingState(true /* alsoResetLastComposedWord */);
mInputLogic.resetComposingState(true /* alsoResetLastComposedWord */);
// Notify ResearchLogger
if (ProductionFlag.USES_DEVELOPMENT_ONLY_DIAGNOSTICS) {
ResearchLogger.latinIME_onFinishInputViewInternal(finishingInput,
@ -1041,7 +1040,7 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
// Another option would be to send suggestions each time we set the composing
// text, but that is probably too expensive to do, so we decided to leave things
// as is.
resetEntireInputState(newSelStart, newSelEnd);
mInputLogic.resetEntireInputState(mSettings.getCurrent(), newSelStart, newSelEnd);
} else {
// resetEntireInputState calls resetCachesUponCursorMove, but forcing the
// composition to end. But in all cases where we don't reset the entire input
@ -1268,28 +1267,6 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
mKeyPreviewBackingView.setVisibility(isFullscreenMode() ? View.GONE : View.VISIBLE);
}
// This will reset the whole input state to the starting state. It will clear
// the composing word, reset the last composed word, tell the inputconnection about it.
private void resetEntireInputState(final int newSelStart, final int newSelEnd) {
final boolean shouldFinishComposition = mInputLogic.mWordComposer.isComposingWord();
resetComposingState(true /* alsoResetLastComposedWord */);
final SettingsValues settingsValues = mSettings.getCurrent();
if (settingsValues.mBigramPredictionEnabled) {
clearSuggestionStrip();
} else {
setSuggestedWords(settingsValues.mSuggestPuncList, false);
}
mInputLogic.mConnection.resetCachesUponCursorMoveAndReturnSuccess(newSelStart, newSelEnd,
shouldFinishComposition);
}
private void resetComposingState(final boolean alsoResetLastComposedWord) {
mInputLogic.mWordComposer.reset();
if (alsoResetLastComposedWord) {
mInputLogic.mLastComposedWord = LastComposedWord.NOT_A_COMPOSED_WORD;
}
}
private void commitTyped(final String separatorString) {
if (!mInputLogic.mWordComposer.isComposingWord()) return;
final String typedWord = mInputLogic.mWordComposer.getTypedWord();
@ -1459,38 +1436,6 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
mSubtypeState.switchSubtype(token, mRichImm);
}
private void sendDownUpKeyEvent(final int code) {
final long eventTime = SystemClock.uptimeMillis();
mInputLogic.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));
mInputLogic.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));
}
private void sendKeyCodePoint(final int code) {
if (ProductionFlag.USES_DEVELOPMENT_ONLY_DIAGNOSTICS) {
ResearchLogger.latinIME_sendKeyCodePoint(code);
}
// TODO: Remove this special handling of digit letters.
// For backward compatibility. See {@link InputMethodService#sendKeyChar(char)}.
if (code >= '0' && code <= '9') {
sendDownUpKeyEvent(code - '0' + KeyEvent.KEYCODE_0);
return;
}
if (Constants.CODE_ENTER == code && mAppWorkAroundsUtils.isBeforeJellyBean()) {
// 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.
sendDownUpKeyEvent(KeyEvent.KEYCODE_ENTER);
} else {
mInputLogic.mConnection.commitText(StringUtils.newSingleCodePointString(code), 1);
}
}
// Implementation of {@link KeyboardActionListener}.
@Override
public void onCodeInput(final int primaryCode, final int x, final int y) {
@ -1520,8 +1465,8 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
if (mInputLogic.mWordComposer.isCursorFrontOrMiddleOfComposingWord()) {
// If we are in the middle of a recorrection, we need to commit the recorrection
// first so that we can insert the character at the current cursor position.
resetEntireInputState(mInputLogic.mLastSelectionStart,
mInputLogic.mLastSelectionEnd);
mInputLogic.resetEntireInputState(settingsValues,
mInputLogic.mLastSelectionStart, mInputLogic.mLastSelectionEnd);
} else {
commitTyped(LastComposedWord.NOT_A_SEPARATOR);
}
@ -1547,7 +1492,7 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
if (mInputLogic.mWordComposer.isComposingWord()) {
commitCurrentAutoCorrection(rawText);
} else {
resetComposingState(true /* alsoResetLastComposedWord */);
mInputLogic.resetComposingState(true /* alsoResetLastComposedWord */);
}
mHandler.postUpdateSuggestionStrip();
if (ProductionFlag.USES_DEVELOPMENT_ONLY_DIAGNOSTICS
@ -1590,8 +1535,8 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
if (mInputLogic.mWordComposer.isCursorFrontOrMiddleOfComposingWord()) {
// If we are in the middle of a recorrection, we need to commit the recorrection
// first so that we can insert the batch input at the current cursor position.
resetEntireInputState(mInputLogic.mLastSelectionStart,
mInputLogic.mLastSelectionEnd);
mInputLogic.resetEntireInputState(currentSettingsValues,
mInputLogic.mLastSelectionStart, mInputLogic.mLastSelectionEnd);
} else if (wordComposerSize <= 1) {
// We auto-correct the previous (typed, not gestured) string iff it's one character
// long. The reason for this is, even in the middle of gesture typing, you'll still
@ -1886,156 +1831,6 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
mInputUpdater.onCancelBatchInput();
}
// TODO[IL]: Move this to InputLogic and make private again.
public void handleBackspace(final int spaceState) {
mInputLogic.mDeleteCount++;
// In many cases, we may have to put the keyboard in auto-shift state again. However
// we want to wait a few milliseconds before doing it to avoid the keyboard flashing
// during key repeat.
mHandler.postUpdateShiftState();
if (mInputLogic.mWordComposer.isCursorFrontOrMiddleOfComposingWord()) {
// If we are in the middle of a recorrection, we need to commit the recorrection
// first so that we can remove the character at the current cursor position.
resetEntireInputState(mInputLogic.mLastSelectionStart, mInputLogic.mLastSelectionEnd);
// When we exit this if-clause, mWordComposer.isComposingWord() will return false.
}
if (mInputLogic.mWordComposer.isComposingWord()) {
if (mInputLogic.mWordComposer.isBatchMode()) {
if (ProductionFlag.USES_DEVELOPMENT_ONLY_DIAGNOSTICS) {
final String word = mInputLogic.mWordComposer.getTypedWord();
ResearchLogger.latinIME_handleBackspace_batch(word, 1);
}
final String rejectedSuggestion = mInputLogic.mWordComposer.getTypedWord();
mInputLogic.mWordComposer.reset();
mInputLogic.mWordComposer.setRejectedBatchModeSuggestion(rejectedSuggestion);
} else {
mInputLogic.mWordComposer.deleteLast();
}
mInputLogic.mConnection.setComposingText(getTextWithUnderline(
mInputLogic.mWordComposer.getTypedWord()), 1);
mHandler.postUpdateSuggestionStrip();
if (!mInputLogic.mWordComposer.isComposingWord()) {
// If we just removed the last character, auto-caps mode may have changed so we
// need to re-evaluate.
mKeyboardSwitcher.updateShiftState();
}
} else {
final SettingsValues currentSettings = mSettings.getCurrent();
if (mInputLogic.mLastComposedWord.canRevertCommit()) {
if (currentSettings.mIsInternal) {
LatinImeLoggerUtils.onAutoCorrectionCancellation();
}
revertCommit();
return;
}
if (mInputLogic.mEnteredText != null
&& mInputLogic.mConnection.sameAsTextBeforeCursor(mInputLogic.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.
mInputLogic.mConnection.deleteSurroundingText(
mInputLogic.mEnteredText.length(), 0);
if (ProductionFlag.USES_DEVELOPMENT_ONLY_DIAGNOSTICS) {
ResearchLogger.latinIME_handleBackspace_cancelTextInput(
mInputLogic.mEnteredText);
}
mInputLogic.mEnteredText = null;
// If we have mEnteredText, then we know that mHasUncommittedTypedChars == false.
// In addition we know that spaceState is false, and that we should not be
// reverting any autocorrect at this point. So we can safely return.
return;
}
if (SpaceState.DOUBLE == spaceState) {
mHandler.cancelDoubleSpacePeriodTimer();
if (mInputLogic.mConnection.revertDoubleSpacePeriod()) {
// No need to reset mSpaceState, it has already be done (that's why we
// receive it as a parameter)
return;
}
} else if (SpaceState.SWAP_PUNCTUATION == spaceState) {
if (mInputLogic.mConnection.revertSwapPunctuation()) {
// Likewise
return;
}
}
// No cancelling of commit/double space/swap: we have a regular backspace.
// We should backspace one char and restart suggestion if at the end of a word.
if (mInputLogic.mLastSelectionStart != mInputLogic.mLastSelectionEnd) {
// If there is a selection, remove it.
final int numCharsDeleted =
mInputLogic.mLastSelectionEnd - mInputLogic.mLastSelectionStart;
mInputLogic.mConnection.setSelection(mInputLogic.mLastSelectionEnd,
mInputLogic.mLastSelectionEnd);
// Reset mLastSelectionEnd to mLastSelectionStart. This is what is supposed to
// happen, and if it's wrong, the next call to onUpdateSelection will correct it,
// but we want to set it right away to avoid it being used with the wrong values
// later (typically, in a subsequent press on backspace).
mInputLogic.mLastSelectionEnd = mInputLogic.mLastSelectionStart;
mInputLogic.mConnection.deleteSurroundingText(numCharsDeleted, 0);
if (ProductionFlag.USES_DEVELOPMENT_ONLY_DIAGNOSTICS) {
ResearchLogger.latinIME_handleBackspace(numCharsDeleted,
false /* shouldUncommitLogUnit */);
}
} else {
// There is no selection, just delete one character.
if (mInputLogic.NOT_A_CURSOR_POSITION == mInputLogic.mLastSelectionEnd) {
// This should never happen.
Log.e(TAG, "Backspace when we don't know the selection position");
}
if (mAppWorkAroundsUtils.isBeforeJellyBean() ||
currentSettings.mInputAttributes.isTypeNull()) {
// There are two possible reasons to send a key event: either the field has
// type TYPE_NULL, in which case the keyboard should send events, or we are
// running in 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, so we retain this behavior if the app has target SDK < JellyBean.
sendDownUpKeyEvent(KeyEvent.KEYCODE_DEL);
if (mInputLogic.mDeleteCount > DELETE_ACCELERATE_AT) {
sendDownUpKeyEvent(KeyEvent.KEYCODE_DEL);
}
} else {
final int codePointBeforeCursor =
mInputLogic.mConnection.getCodePointBeforeCursor();
if (codePointBeforeCursor == Constants.NOT_A_CODE) {
// Nothing to delete before the cursor.
return;
}
final int lengthToDelete =
Character.isSupplementaryCodePoint(codePointBeforeCursor) ? 2 : 1;
mInputLogic.mConnection.deleteSurroundingText(lengthToDelete, 0);
if (ProductionFlag.USES_DEVELOPMENT_ONLY_DIAGNOSTICS) {
ResearchLogger.latinIME_handleBackspace(lengthToDelete,
true /* shouldUncommitLogUnit */);
}
if (mInputLogic.mDeleteCount > DELETE_ACCELERATE_AT) {
final int codePointBeforeCursorToDeleteAgain =
mInputLogic.mConnection.getCodePointBeforeCursor();
if (codePointBeforeCursorToDeleteAgain != Constants.NOT_A_CODE) {
final int lengthToDeleteAgain = Character.isSupplementaryCodePoint(
codePointBeforeCursorToDeleteAgain) ? 2 : 1;
mInputLogic.mConnection.deleteSurroundingText(lengthToDeleteAgain, 0);
if (ProductionFlag.USES_DEVELOPMENT_ONLY_DIAGNOSTICS) {
ResearchLogger.latinIME_handleBackspace(lengthToDeleteAgain,
true /* shouldUncommitLogUnit */);
}
}
}
}
}
if (currentSettings.isSuggestionsRequested(mDisplayOrientation)
&& currentSettings.mCurrentLanguageHasSpaces) {
restartSuggestionsOnWordBeforeCursorIfAtEndOfWord();
}
// We just removed a character. We need to update the auto-caps state.
mKeyboardSwitcher.updateShiftState();
}
}
/*
* Strip a trailing space if necessary and returns whether it's a swap weak space situation.
*/
@ -2076,7 +1871,8 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
if (mInputLogic.mWordComposer.isCursorFrontOrMiddleOfComposingWord()) {
// If we are in the middle of a recorrection, we need to commit the recorrection
// first so that we can insert the character at the current cursor position.
resetEntireInputState(mInputLogic.mLastSelectionStart, mInputLogic.mLastSelectionEnd);
mInputLogic.resetEntireInputState(currentSettings, mInputLogic.mLastSelectionStart,
mInputLogic.mLastSelectionEnd);
isComposingWord = false;
}
// We want to find out whether to start composing a new word with this character. If so,
@ -2103,7 +1899,7 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
// when we commit this one, if we ever do; if on the other hand we backspace
// it entirely and resume suggestions on the previous word, we'd like to still
// have touch coordinates for it.
resetComposingState(false /* alsoResetLastComposedWord */);
mInputLogic.resetComposingState(false /* alsoResetLastComposedWord */);
}
if (isComposingWord) {
final MainKeyboardView mainKeyboardView = mKeyboardSwitcher.getMainKeyboardView();
@ -2120,13 +1916,13 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
getActualCapsMode(),
getNthPreviousWordForSuggestion(currentSettings, 1 /* nthPreviousWord */));
}
mInputLogic.mConnection.setComposingText(getTextWithUnderline(
mInputLogic.mConnection.setComposingText(mInputLogic.getTextWithUnderline(
mInputLogic.mWordComposer.getTypedWord()), 1);
} else {
final boolean swapWeakSpace = maybeStripSpace(primaryCode, spaceState,
Constants.SUGGESTION_STRIP_COORDINATE == x);
sendKeyCodePoint(primaryCode);
mInputLogic.sendKeyCodePoint(primaryCode);
if (swapWeakSpace) {
swapSwapperAndSpace();
@ -2200,7 +1996,8 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
if (mInputLogic.mWordComposer.isCursorFrontOrMiddleOfComposingWord()) {
// If we are in the middle of a recorrection, we need to commit the recorrection
// first so that we can insert the separator at the current cursor position.
resetEntireInputState(mInputLogic.mLastSelectionStart, mInputLogic.mLastSelectionEnd);
mInputLogic.resetEntireInputState(currentSettings, mInputLogic.mLastSelectionStart,
mInputLogic.mLastSelectionEnd);
}
// isComposingWord() may have changed since we stored wasComposing
if (mInputLogic.mWordComposer.isComposingWord()) {
@ -2227,7 +2024,7 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
}
if (!shouldAvoidSendingCode) {
sendKeyCodePoint(primaryCode);
mInputLogic.sendKeyCodePoint(primaryCode);
}
if (Constants.CODE_SPACE == primaryCode) {
@ -2272,12 +2069,6 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
return didAutoCorrect;
}
private CharSequence getTextWithUnderline(final String text) {
return mInputLogic.mIsAutoCorrectionIndicatorOn
? SuggestionSpanUtils.getTextWithAutoCorrectionIndicatorUnderline(this, text)
: text;
}
private void handleClose() {
// TODO: Verify that words are logged properly when IME is closed.
commitTyped(LastComposedWord.NOT_A_SEPARATOR);
@ -2311,12 +2102,14 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
return currentSettings.isSuggestionsRequested(mDisplayOrientation);
}
private void clearSuggestionStrip() {
// TODO[IL]: Define a clear interface for this
public void clearSuggestionStrip() {
setSuggestedWords(SuggestedWords.EMPTY, false);
setAutoCorrectionIndicator(false);
}
private void setSuggestedWords(final SuggestedWords words, final boolean isAutoCorrection) {
// TODO[IL]: Define a clear interface for this
public void setSuggestedWords(final SuggestedWords words, final boolean isAutoCorrection) {
mInputLogic.mSuggestedWords = words;
if (mSuggestionStripView != null) {
mSuggestionStripView.setSuggestions(words);
@ -2330,7 +2123,7 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
&& mInputLogic.mWordComposer.isComposingWord()) {
mInputLogic.mIsAutoCorrectionIndicatorOn = newAutoCorrectionIndicator;
final CharSequence textWithUnderline =
getTextWithUnderline(mInputLogic.mWordComposer.getTypedWord());
mInputLogic.getTextWithUnderline(mInputLogic.mWordComposer.getTypedWord());
// TODO: when called from an updateSuggestionStrip() call that results from a posted
// message, this is called outside any batch edit. Potentially, this may result in some
// janky flickering of the screen, although the display speed makes it unlikely in
@ -2604,7 +2397,7 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
mSuggestionStripView.clear();
}
mKeyboardSwitcher.updateShiftState();
resetComposingState(true /* alsoResetLastComposedWord */);
mInputLogic.resetComposingState(true /* alsoResetLastComposedWord */);
final CompletionInfo completionInfo = mApplicationSpecifiedCompletions[index];
mInputLogic.mConnection.commitCompletion(completionInfo);
mInputLogic.mConnection.endBatchEdit();
@ -2848,7 +2641,8 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
* 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() {
// TODO[IL]: Move this to InputLogic and make it private.
public void restartSuggestionsOnWordBeforeCursorIfAtEndOfWord() {
final CharSequence word =
mInputLogic.mConnection.getWordBeforeCursorIfAtEndOfWord(mSettings.getCurrent());
if (null != word) {
@ -2900,7 +2694,8 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
if (tryResumeSuggestions) mHandler.postResumeSuggestions();
}
private void revertCommit() {
// TODO[IL]: Move this to InputLogic and make it private again.
public void revertCommit() {
final String previousWord = mInputLogic.mLastComposedWord.mPrevWord;
final String originallyTypedWord = mInputLogic.mLastComposedWord.mTypedWord;
final String committedWord = mInputLogic.mLastComposedWord.mCommittedWord;
@ -2967,7 +2762,7 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
if (ProductionFlag.USES_DEVELOPMENT_ONLY_DIAGNOSTICS) {
ResearchLogger.latinIME_promotePhantomSpace();
}
sendKeyCodePoint(Constants.CODE_SPACE);
mInputLogic.sendKeyCodePoint(Constants.CODE_SPACE);
}
}

View file

@ -17,8 +17,12 @@
package com.android.inputmethod.latin.inputlogic;
import android.os.SystemClock;
import android.util.Log;
import android.view.KeyCharacterMap;
import android.view.KeyEvent;
import android.view.inputmethod.EditorInfo;
import com.android.inputmethod.compat.SuggestionSpanUtils;
import com.android.inputmethod.event.EventInterpreter;
import com.android.inputmethod.keyboard.Keyboard;
import com.android.inputmethod.keyboard.KeyboardSwitcher;
@ -32,9 +36,13 @@ import com.android.inputmethod.latin.Suggest;
import com.android.inputmethod.latin.SuggestedWords;
import com.android.inputmethod.latin.WordComposer;
import com.android.inputmethod.latin.define.ProductionFlag;
import com.android.inputmethod.latin.settings.Settings;
import com.android.inputmethod.latin.settings.SettingsValues;
import com.android.inputmethod.latin.utils.CollectionUtils;
import com.android.inputmethod.latin.utils.InputTypeUtils;
import com.android.inputmethod.latin.utils.LatinImeLoggerUtils;
import com.android.inputmethod.latin.utils.RecapitalizeStatus;
import com.android.inputmethod.latin.utils.StringUtils;
import com.android.inputmethod.research.ResearchLogger;
import java.util.TreeSet;
@ -43,6 +51,8 @@ import java.util.TreeSet;
* This class manages the input logic.
*/
public final class InputLogic {
private static final String TAG = InputLogic.class.getSimpleName();
// TODO : Remove this member when we can.
private final LatinIME mLatinIME;
@ -84,7 +94,9 @@ public final class InputLogic {
}
public void startInput(final boolean restarting) {
}
public void finishInput() {
}
public void onCodeInput(final int primaryCode, final int x, final int y,
@ -94,6 +106,7 @@ public final class InputLogic {
if (ProductionFlag.USES_DEVELOPMENT_ONLY_DIAGNOSTICS) {
ResearchLogger.latinIME_onCodeInput(primaryCode, x, y);
}
final SettingsValues settingsValues = Settings.getInstance().getCurrent();
final long when = SystemClock.uptimeMillis();
if (primaryCode != Constants.CODE_DELETE
|| when > mLastKeyTime + Constants.LONG_PRESS_MILLISECONDS) {
@ -121,7 +134,7 @@ public final class InputLogic {
switch (primaryCode) {
case Constants.CODE_DELETE:
mSpaceState = SpaceState.NONE;
handleBackspace(spaceState);
handleBackspace(settingsValues, spaceState, handler, keyboardSwitcher);
LatinImeLogger.logOnDelete(x, y);
break;
case Constants.CODE_SHIFT:
@ -224,10 +237,151 @@ public final class InputLogic {
/**
* Handle a press on the backspace key.
* @param settingsValues The current settings values.
* @param spaceState The space state at start of this batch edit.
*/
private void handleBackspace(final int spaceState) {
mLatinIME.handleBackspace(spaceState);
private void handleBackspace(final SettingsValues settingsValues, final int spaceState,
// TODO: remove these arguments
final LatinIME.UIHandler handler, final KeyboardSwitcher keyboardSwitcher) {
mDeleteCount++;
// In many cases, we may have to put the keyboard in auto-shift state again. However
// we want to wait a few milliseconds before doing it to avoid the keyboard flashing
// during key repeat.
handler.postUpdateShiftState();
if (mWordComposer.isCursorFrontOrMiddleOfComposingWord()) {
// If we are in the middle of a recorrection, we need to commit the recorrection
// first so that we can remove the character at the current cursor position.
resetEntireInputState(settingsValues, mLastSelectionStart, mLastSelectionEnd);
// When we exit this if-clause, mWordComposer.isComposingWord() will return false.
}
if (mWordComposer.isComposingWord()) {
if (mWordComposer.isBatchMode()) {
if (ProductionFlag.USES_DEVELOPMENT_ONLY_DIAGNOSTICS) {
final String word = mWordComposer.getTypedWord();
ResearchLogger.latinIME_handleBackspace_batch(word, 1);
}
final String rejectedSuggestion = mWordComposer.getTypedWord();
mWordComposer.reset();
mWordComposer.setRejectedBatchModeSuggestion(rejectedSuggestion);
} else {
mWordComposer.deleteLast();
}
mConnection.setComposingText(getTextWithUnderline(mWordComposer.getTypedWord()), 1);
handler.postUpdateSuggestionStrip();
if (!mWordComposer.isComposingWord()) {
// If we just removed the last character, auto-caps mode may have changed so we
// need to re-evaluate.
keyboardSwitcher.updateShiftState();
}
} else {
if (mLastComposedWord.canRevertCommit()) {
if (settingsValues.mIsInternal) {
LatinImeLoggerUtils.onAutoCorrectionCancellation();
}
mLatinIME.revertCommit();
return;
}
if (mEnteredText != null && mConnection.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.
mConnection.deleteSurroundingText(mEnteredText.length(), 0);
if (ProductionFlag.USES_DEVELOPMENT_ONLY_DIAGNOSTICS) {
ResearchLogger.latinIME_handleBackspace_cancelTextInput(mEnteredText);
}
mEnteredText = null;
// If we have mEnteredText, then we know that mHasUncommittedTypedChars == false.
// In addition we know that spaceState is false, and that we should not be
// reverting any autocorrect at this point. So we can safely return.
return;
}
if (SpaceState.DOUBLE == spaceState) {
handler.cancelDoubleSpacePeriodTimer();
if (mConnection.revertDoubleSpacePeriod()) {
// No need to reset mSpaceState, it has already be done (that's why we
// receive it as a parameter)
return;
}
} else if (SpaceState.SWAP_PUNCTUATION == spaceState) {
if (mConnection.revertSwapPunctuation()) {
// Likewise
return;
}
}
// No cancelling of commit/double space/swap: we have a regular backspace.
// We should backspace one char and restart suggestion if at the end of a word.
if (mLastSelectionStart != mLastSelectionEnd) {
// If there is a selection, remove it.
final int numCharsDeleted = mLastSelectionEnd - mLastSelectionStart;
mConnection.setSelection(mLastSelectionEnd, mLastSelectionEnd);
// Reset mLastSelectionEnd to mLastSelectionStart. This is what is supposed to
// happen, and if it's wrong, the next call to onUpdateSelection will correct it,
// but we want to set it right away to avoid it being used with the wrong values
// later (typically, in a subsequent press on backspace).
mLastSelectionEnd = mLastSelectionStart;
mConnection.deleteSurroundingText(numCharsDeleted, 0);
if (ProductionFlag.USES_DEVELOPMENT_ONLY_DIAGNOSTICS) {
ResearchLogger.latinIME_handleBackspace(numCharsDeleted,
false /* shouldUncommitLogUnit */);
}
} else {
// There is no selection, just delete one character.
if (NOT_A_CURSOR_POSITION == mLastSelectionEnd) {
// This should never happen.
Log.e(TAG, "Backspace when we don't know the selection position");
}
if (mLatinIME.mAppWorkAroundsUtils.isBeforeJellyBean() ||
settingsValues.mInputAttributes.isTypeNull()) {
// There are two possible reasons to send a key event: either the field has
// type TYPE_NULL, in which case the keyboard should send events, or we are
// running in 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, so we retain this behavior if the app has target SDK < JellyBean.
sendDownUpKeyEvent(KeyEvent.KEYCODE_DEL);
if (mDeleteCount > Constants.DELETE_ACCELERATE_AT) {
sendDownUpKeyEvent(KeyEvent.KEYCODE_DEL);
}
} else {
final int codePointBeforeCursor = mConnection.getCodePointBeforeCursor();
if (codePointBeforeCursor == Constants.NOT_A_CODE) {
// Nothing to delete before the cursor.
return;
}
final int lengthToDelete =
Character.isSupplementaryCodePoint(codePointBeforeCursor) ? 2 : 1;
mConnection.deleteSurroundingText(lengthToDelete, 0);
if (ProductionFlag.USES_DEVELOPMENT_ONLY_DIAGNOSTICS) {
ResearchLogger.latinIME_handleBackspace(lengthToDelete,
true /* shouldUncommitLogUnit */);
}
if (mDeleteCount > Constants.DELETE_ACCELERATE_AT) {
final int codePointBeforeCursorToDeleteAgain =
mConnection.getCodePointBeforeCursor();
if (codePointBeforeCursorToDeleteAgain != Constants.NOT_A_CODE) {
final int lengthToDeleteAgain = Character.isSupplementaryCodePoint(
codePointBeforeCursorToDeleteAgain) ? 2 : 1;
mConnection.deleteSurroundingText(lengthToDeleteAgain, 0);
if (ProductionFlag.USES_DEVELOPMENT_ONLY_DIAGNOSTICS) {
ResearchLogger.latinIME_handleBackspace(lengthToDeleteAgain,
true /* shouldUncommitLogUnit */);
}
}
}
}
}
// TODO: move mDisplayOrientation to CurrentSettings.
if (settingsValues.isSuggestionsRequested(mLatinIME.mDisplayOrientation)
&& settingsValues.mCurrentLanguageHasSpaces) {
mLatinIME.restartSuggestionsOnWordBeforeCursorIfAtEndOfWord();
}
// We just removed a character. We need to update the auto-caps state.
keyboardSwitcher.updateShiftState();
}
}
/**
@ -264,4 +418,69 @@ public final class InputLogic {
private void onSettingsKeyPressed() {
mLatinIME.onSettingsKeyPressed();
}
// This will reset the whole input state to the starting state. It will clear
// the composing word, reset the last composed word, tell the inputconnection about it.
// TODO: remove all references to this in LatinIME and make this private
public void resetEntireInputState(final SettingsValues settingsValues,
final int newSelStart, final int newSelEnd) {
final boolean shouldFinishComposition = mWordComposer.isComposingWord();
resetComposingState(true /* alsoResetLastComposedWord */);
if (settingsValues.mBigramPredictionEnabled) {
mLatinIME.clearSuggestionStrip();
} else {
mLatinIME.setSuggestedWords(settingsValues.mSuggestPuncList, false);
}
mConnection.resetCachesUponCursorMoveAndReturnSuccess(newSelStart, newSelEnd,
shouldFinishComposition);
}
// TODO: remove all references to this in LatinIME and make this private.
public void resetComposingState(final boolean alsoResetLastComposedWord) {
mWordComposer.reset();
if (alsoResetLastComposedWord) {
mLastComposedWord = LastComposedWord.NOT_A_COMPOSED_WORD;
}
}
// TODO: remove all references to this in LatinIME and make this private. Also, shouldn't
// this go in some *Utils class instead?
public CharSequence getTextWithUnderline(final String text) {
return mIsAutoCorrectionIndicatorOn
? SuggestionSpanUtils.getTextWithAutoCorrectionIndicatorUnderline(mLatinIME, text)
: text;
}
private void sendDownUpKeyEvent(final int code) {
final long eventTime = SystemClock.uptimeMillis();
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));
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));
}
// TODO: remove all references to this in LatinIME and make this private
public void sendKeyCodePoint(final int code) {
if (ProductionFlag.USES_DEVELOPMENT_ONLY_DIAGNOSTICS) {
ResearchLogger.latinIME_sendKeyCodePoint(code);
}
// TODO: Remove this special handling of digit letters.
// For backward compatibility. See {@link InputMethodService#sendKeyChar(char)}.
if (code >= '0' && code <= '9') {
sendDownUpKeyEvent(code - '0' + KeyEvent.KEYCODE_0);
return;
}
if (Constants.CODE_ENTER == code && mLatinIME.mAppWorkAroundsUtils.isBeforeJellyBean()) {
// 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.
sendDownUpKeyEvent(KeyEvent.KEYCODE_ENTER);
} else {
mConnection.commitText(StringUtils.newSingleCodePointString(code), 1);
}
}
}