am cddf4f9d: [IL2] Move onCodeInput to InputLogic.

* commit 'cddf4f9d781850fb4ffcfd8dfc434aa44c5d9f88':
  [IL2] Move onCodeInput to InputLogic.
main
Jean Chalard 2013-12-18 23:49:22 -08:00 committed by Android Git Automerger
commit b2036c9910
4 changed files with 284 additions and 164 deletions

View File

@ -154,6 +154,9 @@ public final class Constants {
// Must be equal to MAX_WORD_LENGTH in native/jni/src/defines.h // Must be equal to MAX_WORD_LENGTH in native/jni/src/defines.h
public static final int DICTIONARY_MAX_WORD_LENGTH = 48; public static final int DICTIONARY_MAX_WORD_LENGTH = 48;
// Key events coming any faster than this are long-presses.
public static final int LONG_PRESS_MILLISECONDS = 200;
public static boolean isValidCoordinate(final int coordinate) { public static boolean isValidCoordinate(final int coordinate) {
// Detect {@link NOT_A_COORDINATE}, {@link SUGGESTION_STRIP_COORDINATE}, // Detect {@link NOT_A_COORDINATE}, {@link SUGGESTION_STRIP_COORDINATE},
// and {@link SPELL_CHECKER_COORDINATE}. // and {@link SPELL_CHECKER_COORDINATE}.
@ -242,5 +245,4 @@ public final class Constants {
private Constants() { private Constants() {
// This utility class is not publicly instantiable. // This utility class is not publicly instantiable.
} }
} }

View File

@ -77,6 +77,7 @@ import com.android.inputmethod.latin.Suggest.OnGetSuggestedWordsCallback;
import com.android.inputmethod.latin.SuggestedWords.SuggestedWordInfo; import com.android.inputmethod.latin.SuggestedWords.SuggestedWordInfo;
import com.android.inputmethod.latin.define.ProductionFlag; import com.android.inputmethod.latin.define.ProductionFlag;
import com.android.inputmethod.latin.inputlogic.InputLogic; import com.android.inputmethod.latin.inputlogic.InputLogic;
import com.android.inputmethod.latin.inputlogic.SpaceState;
import com.android.inputmethod.latin.personalization.DictionaryDecayBroadcastReciever; import com.android.inputmethod.latin.personalization.DictionaryDecayBroadcastReciever;
import com.android.inputmethod.latin.personalization.PersonalizationDictionarySessionRegister; import com.android.inputmethod.latin.personalization.PersonalizationDictionarySessionRegister;
import com.android.inputmethod.latin.personalization.UserHistoryDictionary; import com.android.inputmethod.latin.personalization.UserHistoryDictionary;
@ -122,8 +123,6 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
// How many continuous deletes at which to start deleting at a higher speed. // 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 DELETE_ACCELERATE_AT = 20;
// Key events coming any faster than this are long-presses.
private static final int QUICK_PRESS = 200;
private static final int PENDING_IMS_CALLBACK_DURATION = 800; private static final int PENDING_IMS_CALLBACK_DURATION = 800;
@ -138,22 +137,6 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
*/ */
private static final String SCHEME_PACKAGE = "package"; private static final String SCHEME_PACKAGE = "package";
private static final int SPACE_STATE_NONE = 0;
// Double space: the state where the user pressed space twice quickly, which LatinIME
// resolved as period-space. Undoing this converts the period to a space.
private static final int SPACE_STATE_DOUBLE = 1;
// Swap punctuation: the state where a weak space and a punctuation from the suggestion strip
// have just been swapped. Undoing this swaps them back; the space is still considered weak.
private static final int SPACE_STATE_SWAP_PUNCTUATION = 2;
// Weak space: a space that should be swapped only by suggestion strip punctuation. Weak
// spaces happen when the user presses space, accepting the current suggestion (whether
// it's an auto-correction or not).
private static final int SPACE_STATE_WEAK = 3;
// Phantom space: a not-yet-inserted space that should get inserted on the next input,
// character provided it's not a separator. If it's a separator, the phantom space is dropped.
// Phantom spaces happen when a user chooses a word from the suggestion strip.
private static final int SPACE_STATE_PHANTOM = 4;
private final Settings mSettings; private final Settings mSettings;
private final InputLogic mInputLogic = new InputLogic(this); private final InputLogic mInputLogic = new InputLogic(this);
@ -803,7 +786,7 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
mInputLogic.mEnteredText = null; mInputLogic.mEnteredText = null;
resetComposingState(true /* alsoResetLastComposedWord */); resetComposingState(true /* alsoResetLastComposedWord */);
mInputLogic.mDeleteCount = 0; mInputLogic.mDeleteCount = 0;
mInputLogic.mSpaceState = SPACE_STATE_NONE; mInputLogic.mSpaceState = SpaceState.NONE;
mInputLogic.mRecapitalizeStatus.deactivate(); mInputLogic.mRecapitalizeStatus.deactivate();
mInputLogic.mCurrentlyPressedHardwareKeys.clear(); mInputLogic.mCurrentlyPressedHardwareKeys.clear();
@ -1038,7 +1021,7 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
// the call to updateShiftState. // the call to updateShiftState.
// We set this to NONE because after a cursor move, we don't want the space // We set this to NONE because after a cursor move, we don't want the space
// state-related special processing to kick in. // state-related special processing to kick in.
mInputLogic.mSpaceState = SPACE_STATE_NONE; mInputLogic.mSpaceState = SpaceState.NONE;
// TODO: is it still necessary to test for composingSpan related stuff? // TODO: is it still necessary to test for composingSpan related stuff?
final boolean selectionChangedOrSafeToReset = selectionChanged final boolean selectionChangedOrSafeToReset = selectionChanged
@ -1332,7 +1315,7 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
// Warning: this depends on mSpaceState, which may not be the most current value. If // Warning: this depends on mSpaceState, which may not be the most current value. If
// mSpaceState gets updated later, whoever called this may need to be told about it. // mSpaceState gets updated later, whoever called this may need to be told about it.
return mInputLogic.mConnection.getCursorCapsMode(inputType, currentSettingsValues, return mInputLogic.mConnection.getCursorCapsMode(inputType, currentSettingsValues,
SPACE_STATE_PHANTOM == mInputLogic.mSpaceState); SpaceState.PHANTOM == mInputLogic.mSpaceState);
} }
public int getCurrentRecapitalizeState() { public int getCurrentRecapitalizeState() {
@ -1441,7 +1424,8 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
mUserDictionary.addWordToUserDictionary(wordToEdit); mUserDictionary.addWordToUserDictionary(wordToEdit);
} }
private void onSettingsKeyPressed() { // TODO[IL]: Rework the route through which this is called.
public void onSettingsKeyPressed() {
if (isShowingOptionDialog()) return; if (isShowingOptionDialog()) return;
showSubtypeSelectorAndSettings(); showSubtypeSelectorAndSettings();
} }
@ -1464,12 +1448,9 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
return mOptionsDialog != null && mOptionsDialog.isShowing(); return mOptionsDialog != null && mOptionsDialog.isShowing();
} }
private void performEditorAction(final int actionId) {
mInputLogic.mConnection.performEditorAction(actionId);
}
// TODO: Revise the language switch key behavior to make it much smarter and more reasonable. // TODO: Revise the language switch key behavior to make it much smarter and more reasonable.
private void handleLanguageSwitchKey() { // TODO[IL]: Move a part of this to InputLogic and straighten out the interface for this.
public void handleLanguageSwitchKey() {
final IBinder token = getWindow().getWindow().getAttributes().token; final IBinder token = getWindow().getWindow().getAttributes().token;
if (mSettings.getCurrent().mIncludesOtherImesInLanguageSwitchList) { if (mSettings.getCurrent().mIncludesOtherImesInLanguageSwitchList) {
mRichImm.switchToNextInputMethod(token, false /* onlyCurrentIme */); mRichImm.switchToNextInputMethod(token, false /* onlyCurrentIme */);
@ -1513,121 +1494,13 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
// Implementation of {@link KeyboardActionListener}. // Implementation of {@link KeyboardActionListener}.
@Override @Override
public void onCodeInput(final int primaryCode, final int x, final int y) { public void onCodeInput(final int primaryCode, final int x, final int y) {
if (ProductionFlag.USES_DEVELOPMENT_ONLY_DIAGNOSTICS) { mInputLogic.onCodeInput(primaryCode, x, y, mHandler, mKeyboardSwitcher, mSubtypeSwitcher);
ResearchLogger.latinIME_onCodeInput(primaryCode, x, y);
}
final long when = SystemClock.uptimeMillis();
if (primaryCode != Constants.CODE_DELETE
|| when > mInputLogic.mLastKeyTime + QUICK_PRESS) {
mInputLogic.mDeleteCount = 0;
}
mInputLogic.mLastKeyTime = when;
mInputLogic.mConnection.beginBatchEdit();
final KeyboardSwitcher switcher = mKeyboardSwitcher;
// The space state depends only on the last character pressed and its own previous
// state. Here, we revert the space state to neutral if the key is actually modifying
// the input contents (any non-shift key), which is what we should do for
// all inputs that do not result in a special state. Each character handling is then
// free to override the state as they see fit.
final int spaceState = mInputLogic.mSpaceState;
if (!mInputLogic.mWordComposer.isComposingWord()) {
mInputLogic.mIsAutoCorrectionIndicatorOn = false;
} }
// TODO: Consolidate the double-space period timer, mLastKeyTime, and the space state. // TODO[IL]: Move this to InputLogic and make it private again.
if (primaryCode != Constants.CODE_SPACE) { public boolean handleNonSpecialCharacter(final int primaryCode, final int x, final int y,
mHandler.cancelDoubleSpacePeriodTimer();
}
boolean didAutoCorrect = false;
switch (primaryCode) {
case Constants.CODE_DELETE:
mInputLogic.mSpaceState = SPACE_STATE_NONE;
handleBackspace(spaceState);
LatinImeLogger.logOnDelete(x, y);
break;
case Constants.CODE_SHIFT:
// Note: Calling back to the keyboard on Shift key is handled in
// {@link #onPressKey(int,int,boolean)} and {@link #onReleaseKey(int,boolean)}.
final Keyboard currentKeyboard = switcher.getKeyboard();
if (null != currentKeyboard && currentKeyboard.mId.isAlphabetKeyboard()) {
// TODO: Instead of checking for alphabetic keyboard here, separate keycodes for
// alphabetic shift and shift while in symbol layout.
handleRecapitalize();
}
break;
case Constants.CODE_CAPSLOCK:
// Note: Changing keyboard to shift lock state is handled in
// {@link KeyboardSwitcher#onCodeInput(int)}.
break;
case Constants.CODE_SWITCH_ALPHA_SYMBOL:
// Note: Calling back to the keyboard on symbol key is handled in
// {@link #onPressKey(int,int,boolean)} and {@link #onReleaseKey(int,boolean)}.
break;
case Constants.CODE_SETTINGS:
onSettingsKeyPressed();
break;
case Constants.CODE_SHORTCUT:
mSubtypeSwitcher.switchToShortcutIME(this);
break;
case Constants.CODE_ACTION_NEXT:
performEditorAction(EditorInfo.IME_ACTION_NEXT);
break;
case Constants.CODE_ACTION_PREVIOUS:
performEditorAction(EditorInfo.IME_ACTION_PREVIOUS);
break;
case Constants.CODE_LANGUAGE_SWITCH:
handleLanguageSwitchKey();
break;
case Constants.CODE_EMOJI:
// Note: Switching emoji keyboard is being handled in
// {@link KeyboardState#onCodeInput(int,int)}.
break;
case Constants.CODE_ENTER:
final EditorInfo editorInfo = getCurrentInputEditorInfo();
final int imeOptionsActionId =
InputTypeUtils.getImeOptionsActionIdFromEditorInfo(editorInfo);
if (InputTypeUtils.IME_ACTION_CUSTOM_LABEL == imeOptionsActionId) {
// Either we have an actionLabel and we should performEditorAction with actionId
// regardless of its value.
performEditorAction(editorInfo.actionId);
} else if (EditorInfo.IME_ACTION_NONE != imeOptionsActionId) {
// We didn't have an actionLabel, but we had another action to execute.
// EditorInfo.IME_ACTION_NONE explicitly means no action. In contrast,
// EditorInfo.IME_ACTION_UNSPECIFIED is the default value for an action, so it
// means there should be an action and the app didn't bother to set a specific
// code for it - presumably it only handles one. It does not have to be treated
// in any specific way: anything that is not IME_ACTION_NONE should be sent to
// performEditorAction.
performEditorAction(imeOptionsActionId);
} else {
// No action label, and the action from imeOptions is NONE: this is a regular
// enter key that should input a carriage return.
didAutoCorrect = handleNonSpecialCharacter(Constants.CODE_ENTER, x, y, spaceState);
}
break;
case Constants.CODE_SHIFT_ENTER:
didAutoCorrect = handleNonSpecialCharacter(Constants.CODE_ENTER, x, y, spaceState);
break;
default:
didAutoCorrect = handleNonSpecialCharacter(primaryCode, x, y, spaceState);
break;
}
switcher.onCodeInput(primaryCode);
// Reset after any single keystroke, except shift, capslock, and symbol-shift
if (!didAutoCorrect && primaryCode != Constants.CODE_SHIFT
&& primaryCode != Constants.CODE_CAPSLOCK
&& primaryCode != Constants.CODE_SWITCH_ALPHA_SYMBOL)
mInputLogic.mLastComposedWord.deactivate();
if (Constants.CODE_DELETE != primaryCode) {
mInputLogic.mEnteredText = null;
}
mInputLogic.mConnection.endBatchEdit();
}
private boolean handleNonSpecialCharacter(final int primaryCode, final int x, final int y,
final int spaceState) { final int spaceState) {
mInputLogic.mSpaceState = SPACE_STATE_NONE; mInputLogic.mSpaceState = SpaceState.NONE;
final boolean didAutoCorrect; final boolean didAutoCorrect;
final SettingsValues settingsValues = mSettings.getCurrent(); final SettingsValues settingsValues = mSettings.getCurrent();
if (settingsValues.isWordSeparator(primaryCode) if (settingsValues.isWordSeparator(primaryCode)
@ -1635,7 +1508,7 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
didAutoCorrect = handleSeparator(primaryCode, x, y, spaceState); didAutoCorrect = handleSeparator(primaryCode, x, y, spaceState);
} else { } else {
didAutoCorrect = false; didAutoCorrect = false;
if (SPACE_STATE_PHANTOM == spaceState) { if (SpaceState.PHANTOM == spaceState) {
if (settingsValues.mIsInternal) { if (settingsValues.mIsInternal) {
if (mInputLogic.mWordComposer.isComposingWord() if (mInputLogic.mWordComposer.isComposingWord()
&& mInputLogic.mWordComposer.isBatchMode()) { && mInputLogic.mWordComposer.isBatchMode()) {
@ -1683,7 +1556,7 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
return; return;
} }
final String text = specificTldProcessingOnTextInput(rawText); final String text = specificTldProcessingOnTextInput(rawText);
if (SPACE_STATE_PHANTOM == mInputLogic.mSpaceState) { if (SpaceState.PHANTOM == mInputLogic.mSpaceState) {
promotePhantomSpace(); promotePhantomSpace();
} }
mInputLogic.mConnection.commitText(text, 1); mInputLogic.mConnection.commitText(text, 1);
@ -1692,7 +1565,7 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
} }
mInputLogic.mConnection.endBatchEdit(); mInputLogic.mConnection.endBatchEdit();
// Space state must be updated before calling updateShiftState // Space state must be updated before calling updateShiftState
mInputLogic.mSpaceState = SPACE_STATE_NONE; mInputLogic.mSpaceState = SpaceState.NONE;
mKeyboardSwitcher.updateShiftState(); mKeyboardSwitcher.updateShiftState();
mKeyboardSwitcher.onCodeInput(Constants.CODE_OUTPUT_TEXT); mKeyboardSwitcher.onCodeInput(Constants.CODE_OUTPUT_TEXT);
mInputLogic.mEnteredText = text; mInputLogic.mEnteredText = text;
@ -1737,7 +1610,7 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
|| currentSettingsValues.isUsuallyFollowedBySpace(codePointBeforeCursor)) { || currentSettingsValues.isUsuallyFollowedBySpace(codePointBeforeCursor)) {
final boolean autoShiftHasBeenOverriden = mKeyboardSwitcher.getKeyboardShiftMode() != final boolean autoShiftHasBeenOverriden = mKeyboardSwitcher.getKeyboardShiftMode() !=
getCurrentAutoCapsState(); getCurrentAutoCapsState();
mInputLogic.mSpaceState = SPACE_STATE_PHANTOM; mInputLogic.mSpaceState = SpaceState.PHANTOM;
if (!autoShiftHasBeenOverriden) { if (!autoShiftHasBeenOverriden) {
// When we change the space state, we need to update the shift state of the // When we change the space state, we need to update the shift state of the
// keyboard unless it has been overridden manually. This is happening for example // keyboard unless it has been overridden manually. This is happening for example
@ -1924,7 +1797,7 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
batchPointers.shift(candidate.mIndexOfTouchPointOfSecondWord); batchPointers.shift(candidate.mIndexOfTouchPointOfSecondWord);
promotePhantomSpace(); promotePhantomSpace();
mInputLogic.mConnection.commitText(commitParts[0], 0); mInputLogic.mConnection.commitText(commitParts[0], 0);
mInputLogic.mSpaceState = SPACE_STATE_PHANTOM; mInputLogic.mSpaceState = SpaceState.PHANTOM;
mKeyboardSwitcher.updateShiftState(); mKeyboardSwitcher.updateShiftState();
mInputLogic.mWordComposer. mInputLogic.mWordComposer.
setCapitalizedModeAndPreviousWordAtStartComposingTime( setCapitalizedModeAndPreviousWordAtStartComposingTime(
@ -1943,7 +1816,7 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
return; return;
} }
mInputLogic.mConnection.beginBatchEdit(); mInputLogic.mConnection.beginBatchEdit();
if (SPACE_STATE_PHANTOM == mInputLogic.mSpaceState) { if (SpaceState.PHANTOM == mInputLogic.mSpaceState) {
promotePhantomSpace(); promotePhantomSpace();
} }
if (mSettings.getCurrent().mPhraseGestureEnabled) { if (mSettings.getCurrent().mPhraseGestureEnabled) {
@ -1966,7 +1839,7 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
ResearchLogger.latinIME_onEndBatchInput(batchInputText, 0, suggestedWords); ResearchLogger.latinIME_onEndBatchInput(batchInputText, 0, suggestedWords);
} }
// Space state must be updated before calling updateShiftState // Space state must be updated before calling updateShiftState
mInputLogic.mSpaceState = SPACE_STATE_PHANTOM; mInputLogic.mSpaceState = SpaceState.PHANTOM;
mKeyboardSwitcher.updateShiftState(); mKeyboardSwitcher.updateShiftState();
} }
@ -1983,7 +1856,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.
mInputLogic.mSpaceState = SPACE_STATE_NONE; mInputLogic.mSpaceState = SpaceState.NONE;
// TODO: use getCodePointBeforeCursor instead to improve performance and simplify the code // TODO: use getCodePointBeforeCursor instead to improve performance and simplify the code
final CharSequence lastOne = mInputLogic.mConnection.getTextBeforeCursor(1, 0); final CharSequence lastOne = mInputLogic.mConnection.getTextBeforeCursor(1, 0);
if (lastOne != null && lastOne.length() == 1 if (lastOne != null && lastOne.length() == 1
@ -2013,7 +1886,8 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
mInputUpdater.onCancelBatchInput(); mInputUpdater.onCancelBatchInput();
} }
private void handleBackspace(final int spaceState) { // TODO[IL]: Move this to InputLogic and make private again.
public void handleBackspace(final int spaceState) {
mInputLogic.mDeleteCount++; mInputLogic.mDeleteCount++;
// In many cases, we may have to put the keyboard in auto-shift state again. However // In many cases, we may have to put the keyboard in auto-shift state again. However
@ -2073,14 +1947,14 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
// reverting any autocorrect at this point. So we can safely return. // reverting any autocorrect at this point. So we can safely return.
return; return;
} }
if (SPACE_STATE_DOUBLE == spaceState) { if (SpaceState.DOUBLE == spaceState) {
mHandler.cancelDoubleSpacePeriodTimer(); mHandler.cancelDoubleSpacePeriodTimer();
if (mInputLogic.mConnection.revertDoubleSpacePeriod()) { if (mInputLogic.mConnection.revertDoubleSpacePeriod()) {
// 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 (SpaceState.SWAP_PUNCTUATION == spaceState) {
if (mInputLogic.mConnection.revertSwapPunctuation()) { if (mInputLogic.mConnection.revertSwapPunctuation()) {
// Likewise // Likewise
return; return;
@ -2167,11 +2041,11 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
*/ */
private boolean maybeStripSpace(final int code, final int spaceState, private boolean maybeStripSpace(final int code, final int spaceState,
final boolean isFromSuggestionStrip) { final boolean isFromSuggestionStrip) {
if (Constants.CODE_ENTER == code && SPACE_STATE_SWAP_PUNCTUATION == spaceState) { if (Constants.CODE_ENTER == code && SpaceState.SWAP_PUNCTUATION == spaceState) {
mInputLogic.mConnection.removeTrailingSpace(); mInputLogic.mConnection.removeTrailingSpace();
return false; return false;
} }
if ((SPACE_STATE_WEAK == spaceState || SPACE_STATE_SWAP_PUNCTUATION == spaceState) if ((SpaceState.WEAK == spaceState || SpaceState.SWAP_PUNCTUATION == spaceState)
&& isFromSuggestionStrip) { && isFromSuggestionStrip) {
final SettingsValues currentSettings = mSettings.getCurrent(); final SettingsValues currentSettings = mSettings.getCurrent();
if (currentSettings.isUsuallyPrecededBySpace(code)) return false; if (currentSettings.isUsuallyPrecededBySpace(code)) return false;
@ -2191,7 +2065,7 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
// TODO: remove isWordConnector() and use isUsuallyFollowedBySpace() instead. // TODO: remove isWordConnector() and use isUsuallyFollowedBySpace() instead.
// See onStartBatchInput() to see how to do it. // See onStartBatchInput() to see how to do it.
final SettingsValues currentSettings = mSettings.getCurrent(); final SettingsValues currentSettings = mSettings.getCurrent();
if (SPACE_STATE_PHANTOM == spaceState && !currentSettings.isWordConnector(primaryCode)) { if (SpaceState.PHANTOM == spaceState && !currentSettings.isWordConnector(primaryCode)) {
if (isComposingWord) { if (isComposingWord) {
// Sanity check // Sanity check
throw new RuntimeException("Should not be composing here"); throw new RuntimeException("Should not be composing here");
@ -2256,7 +2130,7 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
if (swapWeakSpace) { if (swapWeakSpace) {
swapSwapperAndSpace(); swapSwapperAndSpace();
mInputLogic.mSpaceState = SPACE_STATE_WEAK; mInputLogic.mSpaceState = SpaceState.WEAK;
} }
// In case the "add to dictionary" hint was still displayed. // In case the "add to dictionary" hint was still displayed.
if (null != mSuggestionStripView) mSuggestionStripView.dismissAddToDictionaryHint(); if (null != mSuggestionStripView) mSuggestionStripView.dismissAddToDictionaryHint();
@ -2267,7 +2141,8 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
} }
} }
private void handleRecapitalize() { // TODO[IL]: Move this to InputLogic and rename to processRecapitalize.
public void handleRecapitalize() {
if (mInputLogic.mLastSelectionStart == mInputLogic.mLastSelectionEnd) { if (mInputLogic.mLastSelectionStart == mInputLogic.mLastSelectionEnd) {
return; // No selection return; // No selection
} }
@ -2342,7 +2217,7 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
final boolean swapWeakSpace = maybeStripSpace(primaryCode, spaceState, final boolean swapWeakSpace = maybeStripSpace(primaryCode, spaceState,
Constants.SUGGESTION_STRIP_COORDINATE == x); Constants.SUGGESTION_STRIP_COORDINATE == x);
if (SPACE_STATE_PHANTOM == spaceState && if (SpaceState.PHANTOM == spaceState &&
currentSettings.isUsuallyPrecededBySpace(primaryCode)) { currentSettings.isUsuallyPrecededBySpace(primaryCode)) {
promotePhantomSpace(); promotePhantomSpace();
} }
@ -2358,9 +2233,9 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
if (Constants.CODE_SPACE == primaryCode) { if (Constants.CODE_SPACE == primaryCode) {
if (currentSettings.isSuggestionsRequested(mDisplayOrientation)) { if (currentSettings.isSuggestionsRequested(mDisplayOrientation)) {
if (maybeDoubleSpacePeriod()) { if (maybeDoubleSpacePeriod()) {
mInputLogic.mSpaceState = SPACE_STATE_DOUBLE; mInputLogic.mSpaceState = SpaceState.DOUBLE;
} else if (!isShowingPunctuationList()) { } else if (!isShowingPunctuationList()) {
mInputLogic.mSpaceState = SPACE_STATE_WEAK; mInputLogic.mSpaceState = SpaceState.WEAK;
} }
} }
@ -2369,8 +2244,8 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
} else { } else {
if (swapWeakSpace) { if (swapWeakSpace) {
swapSwapperAndSpace(); swapSwapperAndSpace();
mInputLogic.mSpaceState = SPACE_STATE_SWAP_PUNCTUATION; mInputLogic.mSpaceState = SpaceState.SWAP_PUNCTUATION;
} else if (SPACE_STATE_PHANTOM == spaceState } else if (SpaceState.PHANTOM == spaceState
&& currentSettings.isUsuallyFollowedBySpace(primaryCode)) { && currentSettings.isUsuallyFollowedBySpace(primaryCode)) {
// 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
// stay in phantom space state so that the next keypress has a chance to add the // stay in phantom space state so that the next keypress has a chance to add the
@ -2382,7 +2257,7 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
// separator does not normally need a space on the right (that's the difference // separator does not normally need a space on the right (that's the difference
// between swappers and strippers), so we should not stay in phantom space state if // between swappers and strippers), so we should not stay in phantom space state if
// the separator is a stripper. Hence the additional test above. // the separator is a stripper. Hence the additional test above.
mInputLogic.mSpaceState = SPACE_STATE_PHANTOM; mInputLogic.mSpaceState = SpaceState.PHANTOM;
} }
// Set punctuation right away. onUpdateSelection will fire but tests whether it is // Set punctuation right away. onUpdateSelection will fire but tests whether it is
@ -2710,7 +2585,7 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
mInputLogic.mConnection.beginBatchEdit(); mInputLogic.mConnection.beginBatchEdit();
final SettingsValues currentSettings = mSettings.getCurrent(); final SettingsValues currentSettings = mSettings.getCurrent();
if (SPACE_STATE_PHANTOM == mInputLogic.mSpaceState && suggestion.length() > 0 if (SpaceState.PHANTOM == mInputLogic.mSpaceState && suggestion.length() > 0
// In the batch input mode, a manually picked suggested word should just replace // In the batch input mode, a manually picked suggested word should just replace
// the current batch input text and there is no need for a phantom space. // the current batch input text and there is no need for a phantom space.
&& !mInputLogic.mWordComposer.isBatchMode()) { && !mInputLogic.mWordComposer.isBatchMode()) {
@ -2751,7 +2626,7 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
// Don't allow cancellation of manual pick // Don't allow cancellation of manual pick
mInputLogic.mLastComposedWord.deactivate(); mInputLogic.mLastComposedWord.deactivate();
// Space state must be updated before calling updateShiftState // Space state must be updated before calling updateShiftState
mInputLogic.mSpaceState = SPACE_STATE_PHANTOM; mInputLogic.mSpaceState = SpaceState.PHANTOM;
mKeyboardSwitcher.updateShiftState(); mKeyboardSwitcher.updateShiftState();
// We should show the "Touch again to save" hint if the user pressed the first entry // We should show the "Touch again to save" hint if the user pressed the first entry

View File

@ -16,15 +16,26 @@
package com.android.inputmethod.latin.inputlogic; package com.android.inputmethod.latin.inputlogic;
import android.os.SystemClock;
import android.view.inputmethod.EditorInfo;
import com.android.inputmethod.event.EventInterpreter; import com.android.inputmethod.event.EventInterpreter;
import com.android.inputmethod.keyboard.Keyboard;
import com.android.inputmethod.keyboard.KeyboardSwitcher;
import com.android.inputmethod.latin.Constants;
import com.android.inputmethod.latin.LastComposedWord; import com.android.inputmethod.latin.LastComposedWord;
import com.android.inputmethod.latin.LatinIME; import com.android.inputmethod.latin.LatinIME;
import com.android.inputmethod.latin.LatinImeLogger;
import com.android.inputmethod.latin.RichInputConnection; import com.android.inputmethod.latin.RichInputConnection;
import com.android.inputmethod.latin.SubtypeSwitcher;
import com.android.inputmethod.latin.Suggest; import com.android.inputmethod.latin.Suggest;
import com.android.inputmethod.latin.SuggestedWords; import com.android.inputmethod.latin.SuggestedWords;
import com.android.inputmethod.latin.WordComposer; import com.android.inputmethod.latin.WordComposer;
import com.android.inputmethod.latin.define.ProductionFlag;
import com.android.inputmethod.latin.utils.CollectionUtils; import com.android.inputmethod.latin.utils.CollectionUtils;
import com.android.inputmethod.latin.utils.InputTypeUtils;
import com.android.inputmethod.latin.utils.RecapitalizeStatus; import com.android.inputmethod.latin.utils.RecapitalizeStatus;
import com.android.inputmethod.research.ResearchLogger;
import java.util.TreeSet; import java.util.TreeSet;
@ -75,4 +86,182 @@ public final class InputLogic {
public void startInput(final boolean restarting) { public void startInput(final boolean restarting) {
} }
public void onCodeInput(final int primaryCode, final int x, final int y,
// TODO: remove these three arguments
final LatinIME.UIHandler handler, final KeyboardSwitcher keyboardSwitcher,
final SubtypeSwitcher subtypeSwitcher) {
if (ProductionFlag.USES_DEVELOPMENT_ONLY_DIAGNOSTICS) {
ResearchLogger.latinIME_onCodeInput(primaryCode, x, y);
}
final long when = SystemClock.uptimeMillis();
if (primaryCode != Constants.CODE_DELETE
|| when > mLastKeyTime + Constants.LONG_PRESS_MILLISECONDS) {
mDeleteCount = 0;
}
mLastKeyTime = when;
mConnection.beginBatchEdit();
final KeyboardSwitcher switcher = keyboardSwitcher;
// The space state depends only on the last character pressed and its own previous
// state. Here, we revert the space state to neutral if the key is actually modifying
// the input contents (any non-shift key), which is what we should do for
// all inputs that do not result in a special state. Each character handling is then
// free to override the state as they see fit.
final int spaceState = mSpaceState;
if (!mWordComposer.isComposingWord()) {
mIsAutoCorrectionIndicatorOn = false;
}
// TODO: Consolidate the double-space period timer, mLastKeyTime, and the space state.
if (primaryCode != Constants.CODE_SPACE) {
handler.cancelDoubleSpacePeriodTimer();
}
boolean didAutoCorrect = false;
switch (primaryCode) {
case Constants.CODE_DELETE:
mSpaceState = SpaceState.NONE;
handleBackspace(spaceState);
LatinImeLogger.logOnDelete(x, y);
break;
case Constants.CODE_SHIFT:
// Note: Calling back to the keyboard on Shift key is handled in
// {@link #onPressKey(int,int,boolean)} and {@link #onReleaseKey(int,boolean)}.
final Keyboard currentKeyboard = switcher.getKeyboard();
if (null != currentKeyboard && currentKeyboard.mId.isAlphabetKeyboard()) {
// TODO: Instead of checking for alphabetic keyboard here, separate keycodes for
// alphabetic shift and shift while in symbol layout.
handleRecapitalize();
}
break;
case Constants.CODE_CAPSLOCK:
// Note: Changing keyboard to shift lock state is handled in
// {@link KeyboardSwitcher#onCodeInput(int)}.
break;
case Constants.CODE_SWITCH_ALPHA_SYMBOL:
// Note: Calling back to the keyboard on symbol key is handled in
// {@link #onPressKey(int,int,boolean)} and {@link #onReleaseKey(int,boolean)}.
break;
case Constants.CODE_SETTINGS:
onSettingsKeyPressed();
break;
case Constants.CODE_SHORTCUT:
subtypeSwitcher.switchToShortcutIME(mLatinIME);
break;
case Constants.CODE_ACTION_NEXT:
performEditorAction(EditorInfo.IME_ACTION_NEXT);
break;
case Constants.CODE_ACTION_PREVIOUS:
performEditorAction(EditorInfo.IME_ACTION_PREVIOUS);
break;
case Constants.CODE_LANGUAGE_SWITCH:
handleLanguageSwitchKey();
break;
case Constants.CODE_EMOJI:
// Note: Switching emoji keyboard is being handled in
// {@link KeyboardState#onCodeInput(int,int)}.
break;
case Constants.CODE_ENTER:
final EditorInfo editorInfo = getCurrentInputEditorInfo();
final int imeOptionsActionId =
InputTypeUtils.getImeOptionsActionIdFromEditorInfo(editorInfo);
if (InputTypeUtils.IME_ACTION_CUSTOM_LABEL == imeOptionsActionId) {
// Either we have an actionLabel and we should performEditorAction with actionId
// regardless of its value.
performEditorAction(editorInfo.actionId);
} else if (EditorInfo.IME_ACTION_NONE != imeOptionsActionId) {
// We didn't have an actionLabel, but we had another action to execute.
// EditorInfo.IME_ACTION_NONE explicitly means no action. In contrast,
// EditorInfo.IME_ACTION_UNSPECIFIED is the default value for an action, so it
// means there should be an action and the app didn't bother to set a specific
// code for it - presumably it only handles one. It does not have to be treated
// in any specific way: anything that is not IME_ACTION_NONE should be sent to
// performEditorAction.
performEditorAction(imeOptionsActionId);
} else {
// No action label, and the action from imeOptions is NONE: this is a regular
// enter key that should input a carriage return.
didAutoCorrect = handleNonSpecialCharacter(Constants.CODE_ENTER, x, y, spaceState);
}
break;
case Constants.CODE_SHIFT_ENTER:
didAutoCorrect = handleNonSpecialCharacter(Constants.CODE_ENTER, x, y, spaceState);
break;
default:
didAutoCorrect = handleNonSpecialCharacter(primaryCode, x, y, spaceState);
break;
}
switcher.onCodeInput(primaryCode);
// Reset after any single keystroke, except shift, capslock, and symbol-shift
if (!didAutoCorrect && primaryCode != Constants.CODE_SHIFT
&& primaryCode != Constants.CODE_CAPSLOCK
&& primaryCode != Constants.CODE_SWITCH_ALPHA_SYMBOL)
mLastComposedWord.deactivate();
if (Constants.CODE_DELETE != primaryCode) {
mEnteredText = null;
}
mConnection.endBatchEdit();
}
/**
* Handle inputting a code point to the editor.
*
* Non-special keys are those that generate a single code point.
* This includes all letters, digits, punctuation, separators, emoji. It excludes keys that
* manage keyboard-related stuff like shift, language switch, settings, layout switch, or
* any key that results in multiple code points like the ".com" key.
*
* @param code the code point associated with the key.
* @param x the x-coordinate of the key press, or Contants.NOT_A_COORDINATE if not applicable.
* @param y the y-coordinate of the key press, or Contants.NOT_A_COORDINATE if not applicable.
* @param spaceState the space state at start of the batch input.
* @return whether this caused an auto-correction to happen.
*/
private boolean handleNonSpecialCharacter(final int code, final int x, final int y,
final int spaceState) {
return mLatinIME.handleNonSpecialCharacter(code, x, y, spaceState);
}
/**
* Handle a press on the backspace key.
* @param spaceState The space state at start of this batch edit.
*/
private void handleBackspace(final int spaceState) {
mLatinIME.handleBackspace(spaceState);
}
/**
* Handle a press on the language switch key (the "globe key")
*/
private void handleLanguageSwitchKey() {
mLatinIME.handleLanguageSwitchKey();
}
/**
* Processes a recapitalize event.
*/
private void handleRecapitalize() {
mLatinIME.handleRecapitalize();
}
/**
* @return the editor info for the current editor
*/
private EditorInfo getCurrentInputEditorInfo() {
return mLatinIME.getCurrentInputEditorInfo();
}
/**
* @param actionId the action to perform
*/
private void performEditorAction(final int actionId) {
mConnection.performEditorAction(actionId);
}
/**
* Handle a press on the settings key.
*/
private void onSettingsKeyPressed() {
mLatinIME.onSettingsKeyPressed();
}
} }

View File

@ -0,0 +1,54 @@
/*
* Copyright (C) 2013 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.inputlogic;
/**
* Class for managing space states.
*
* At any given time, the input logic is in one of five possible space states. Depending on the
* current space state, some behavior will change; the prime example of this is the PHANTOM state,
* in which any subsequent letter input will input a space before the letter. Read on the
* description inside this class for each of the space states.
*/
public class SpaceState {
// None: the state where all the keyboard behavior is the most "standard" and no automatic
// input is added or removed. In this state, all self-inserting keys only insert themselves,
// and backspace removes one character.
public static final int NONE = 0;
// Double space: the state where the user pressed space twice quickly, which LatinIME
// resolved as period-space. In this state, pressing backspace will undo the
// double-space-to-period insertion: it will replace ". " with " ".
public static final int DOUBLE = 1;
// Swap punctuation: the state where a weak space and a punctuation from the suggestion strip
// have just been swapped. In this state, pressing backspace will undo the swap: the
// characters will be swapped back back, and the space state will go to WEAK.
public static final int SWAP_PUNCTUATION = 2;
// Weak space: a space that should be swapped only by suggestion strip punctuation. Weak
// spaces happen when the user presses space, accepting the current suggestion (whether
// it's an auto-correction or not). In this state, pressing a punctuation from the suggestion
// strip inserts it before the space (while it inserts it after the space in the NONE state).
public static final int WEAK = 3;
// Phantom space: a not-yet-inserted space that should get inserted on the next input,
// character provided it's not a separator. If it's a separator, the phantom space is dropped.
// Phantom spaces happen when a user chooses a word from the suggestion strip. In this state,
// non-separators insert a space before they get inserted.
public static final int PHANTOM = 4;
private SpaceState() {
// This class is not publicly instantiable.
}
}