diff --git a/java/src/com/android/inputmethod/keyboard/KeyboardSwitcher.java b/java/src/com/android/inputmethod/keyboard/KeyboardSwitcher.java index 71a71d6fd..c02b37b82 100644 --- a/java/src/com/android/inputmethod/keyboard/KeyboardSwitcher.java +++ b/java/src/com/android/inputmethod/keyboard/KeyboardSwitcher.java @@ -20,7 +20,6 @@ import android.content.Context; import android.content.SharedPreferences; import android.content.res.Configuration; import android.content.res.Resources; -import android.text.TextUtils; import android.util.DisplayMetrics; import android.util.Log; import android.view.ContextThemeWrapper; @@ -44,7 +43,8 @@ import java.lang.ref.SoftReference; import java.util.HashMap; import java.util.Locale; -public class KeyboardSwitcher implements SharedPreferences.OnSharedPreferenceChangeListener { +public class KeyboardSwitcher implements KeyboardState.SwitchActions, + SharedPreferences.OnSharedPreferenceChangeListener { private static final String TAG = KeyboardSwitcher.class.getSimpleName(); private static final boolean DEBUG_CACHE = LatinImeLogger.sDBG; public static final boolean DEBUG_STATE = false; @@ -87,20 +87,6 @@ public class KeyboardSwitcher implements SharedPreferences.OnSharedPreferenceCha * what user actually typed. */ private boolean mIsAutoCorrectionActive; - // TODO: Encapsulate these state handling to separate class and combine with ShiftKeyState - // and ModifierKeyState into KeyboardState. - private static final int SWITCH_STATE_ALPHA = 0; - private static final int SWITCH_STATE_SYMBOL_BEGIN = 1; - private static final int SWITCH_STATE_SYMBOL = 2; - // The following states are used only on the distinct multi-touch panel devices. - private static final int SWITCH_STATE_MOMENTARY_ALPHA_AND_SYMBOL = 3; - private static final int SWITCH_STATE_MOMENTARY_SYMBOL_AND_MORE = 4; - private static final int SWITCH_STATE_CHORDING_ALPHA = 5; - private static final int SWITCH_STATE_CHORDING_SYMBOL = 6; - private int mSwitchState = SWITCH_STATE_ALPHA; - - private String mLayoutSwitchBackSymbols; - private int mThemeIndex = -1; private Context mThemeContext; @@ -123,10 +109,17 @@ public class KeyboardSwitcher implements SharedPreferences.OnSharedPreferenceCha mIsShifted = isSymbolShifted(); } mIsValid = true; + if (DEBUG_STATE) { + Log.d(TAG, "save: alphabet=" + mIsAlphabetMode + " shiftLocked=" + mIsShiftLocked + + " shift=" + mIsShifted); + } } public void restore() { - mPrevMainKeyboardWasShiftLocked = false; + if (DEBUG_STATE) { + Log.d(TAG, "restore: valid=" + mIsValid + " alphabet=" + mIsAlphabetMode + + " shiftLocked=" + mIsShiftLocked + " shift=" + mIsShifted); + } if (!mIsValid || mIsAlphabetMode) { setAlphabetKeyboard(); } else { @@ -167,7 +160,7 @@ public class KeyboardSwitcher implements SharedPreferences.OnSharedPreferenceCha mResources = ims.getResources(); mPrefs = prefs; mSubtypeSwitcher = SubtypeSwitcher.getInstance(); - mState = new KeyboardState(); + mState = new KeyboardState(this); setContextThemeWrapper(ims, getKeyboardThemeIndex(ims, prefs)); prefs.registerOnSharedPreferenceChangeListener(this); } @@ -199,8 +192,8 @@ public class KeyboardSwitcher implements SharedPreferences.OnSharedPreferenceCha mMainKeyboardId = getKeyboardId(editorInfo, false, false, settingsValues); mSymbolsKeyboardId = getKeyboardId(editorInfo, true, false, settingsValues); mSymbolsShiftedKeyboardId = getKeyboardId(editorInfo, true, true, settingsValues); - mState.onLoadKeyboard(); - mLayoutSwitchBackSymbols = mResources.getString(R.string.layout_switch_back_symbols); + mState.onLoadKeyboard(mResources.getString(R.string.layout_switch_back_symbols)); + mPrevMainKeyboardWasShiftLocked = false; mSavedKeyboardState.restore(); } catch (RuntimeException e) { Log.w(TAG, "loading keyboard failed: " + mMainKeyboardId, e); @@ -227,7 +220,7 @@ public class KeyboardSwitcher implements SharedPreferences.OnSharedPreferenceCha mKeyboardView.setKeyboard(keyboard); mCurrentInputView.setKeyboardGeometry(keyboard.mTopPadding); mCurrentId = keyboard.mId; - mSwitchState = getSwitchState(); + mState.onSetKeyboard(isAlphabetMode()); updateShiftLockState(keyboard); mKeyboardView.setKeyPreviewPopupEnabled( Settings.Values.isKeyPreviewPopupEnabled(mPrefs, mResources), @@ -238,10 +231,6 @@ public class KeyboardSwitcher implements SharedPreferences.OnSharedPreferenceCha updateShiftState(); } - private int getSwitchState() { - return isAlphabetMode() ? SWITCH_STATE_ALPHA : SWITCH_STATE_SYMBOL_BEGIN; - } - private void updateShiftLockState(Keyboard keyboard) { if (mCurrentId.equals(mSymbolsShiftedKeyboardId)) { // Symbol keyboard may have an ALT key that has a caps lock style indicator (a.k.a. @@ -377,7 +366,9 @@ public class KeyboardSwitcher implements SharedPreferences.OnSharedPreferenceCha return mState.isManualTemporaryUpperCase(); } - private void setShifted(int shiftMode) { + // Implements {@link KeyboardState.SwitchActions}. + @Override + public void setShifted(int shiftMode) { mInputMethodService.mHandler.cancelUpdateShiftState(); LatinKeyboard latinKeyboard = getLatinKeyboard(); if (latinKeyboard == null) @@ -401,7 +392,9 @@ public class KeyboardSwitcher implements SharedPreferences.OnSharedPreferenceCha mKeyboardView.invalidateAllKeys(); } - private void setShiftLocked(boolean shiftLocked) { + // Implements {@link KeyboardState.SwitchActions}. + @Override + public void setShiftLocked(boolean shiftLocked) { mInputMethodService.mHandler.cancelUpdateShiftState(); LatinKeyboard latinKeyboard = getLatinKeyboard(); if (latinKeyboard == null) @@ -463,19 +456,7 @@ public class KeyboardSwitcher implements SharedPreferences.OnSharedPreferenceCha Log.d(TAG, "updateShiftState: " + mState + " autoCaps=" + mInputMethodService.getCurrentAutoCapsState()); } - final boolean isAlphabetMode = isAlphabetMode(); - final boolean isShiftLocked = mState.isShiftLocked(); - if (isAlphabetMode) { - if (!isShiftLocked && !mState.isShiftKeyIgnoring()) { - if (mState.isShiftKeyReleasing() && mInputMethodService.getCurrentAutoCapsState()) { - // Only when shift key is releasing, automatic temporary upper case will be set. - setShifted(AUTOMATIC_SHIFT); - } else { - setShifted(mState.isShiftKeyMomentary() ? MANUAL_SHIFT : UNSHIFT); - } - } - } - mState.onUpdateShiftState(isAlphabetMode); + mState.onUpdateShiftState(isAlphabetMode(), mInputMethodService.getCurrentAutoCapsState()); } public void onPressShift(boolean withSliding) { @@ -484,33 +465,7 @@ public class KeyboardSwitcher implements SharedPreferences.OnSharedPreferenceCha if (DEBUG_STATE) { Log.d(TAG, "onPressShift: " + mState + " sliding=" + withSliding); } - final boolean isAlphabetMode = isAlphabetMode(); - final boolean isShiftLocked = mState.isShiftLocked(); - final boolean isAutomaticTemporaryUpperCase = mState.isAutomaticTemporaryUpperCase(); - final boolean isShiftedOrShiftLocked = mState.isShiftedOrShiftLocked(); - if (isAlphabetMode) { - if (isShiftLocked) { - // Shift key is pressed while caps lock state, we will treat this state as shifted - // caps lock state and mark as if shift key pressed while normal state. - setShifted(MANUAL_SHIFT); - } else if (isAutomaticTemporaryUpperCase) { - // Shift key is pressed while automatic temporary upper case, we have to move to - // manual temporary upper case. - setShifted(MANUAL_SHIFT); - } else if (isShiftedOrShiftLocked) { - // In manual upper case state, we just record shift key has been pressing while - // shifted state. - } else { - // In base layout, chording or manual temporary upper case mode is started. - setShifted(MANUAL_SHIFT); - } - } else { - // In symbol mode, just toggle symbol and symbol more keyboard. - toggleShiftInSymbols(); - mSwitchState = SWITCH_STATE_MOMENTARY_SYMBOL_AND_MORE; - } - mState.onPressShift(isAlphabetMode, isShiftLocked, isAutomaticTemporaryUpperCase, - isShiftedOrShiftLocked); + mState.onPressShift(isAlphabetMode(), isSymbolShifted()); } public void onReleaseShift(boolean withSliding) { @@ -519,61 +474,21 @@ public class KeyboardSwitcher implements SharedPreferences.OnSharedPreferenceCha if (DEBUG_STATE) { Log.d(TAG, "onReleaseShift: " + mState + " sliding=" + withSliding); } - final boolean isAlphabetMode = isAlphabetMode(); - final boolean isShiftLocked = mState.isShiftLocked(); - final boolean isShiftLockShifted = mState.isShiftLockShifted(); - final boolean isShiftedOrShiftLocked = mState.isShiftedOrShiftLocked(); - final boolean isManualTemporaryUpperCaseFromAuto = - mState.isManualTemporaryUpperCaseFromAuto(); - if (isAlphabetMode) { - if (mState.isShiftKeyMomentary()) { - // After chording input while normal state. - setShifted(UNSHIFT); - } else if (isShiftLocked && !isShiftLockShifted && (mState.isShiftKeyPressing() - || mState.isShiftKeyPressingOnShifted()) && !withSliding) { - // Shift has been long pressed, ignore this release. - } else if (isShiftLocked && !mState.isShiftKeyIgnoring() && !withSliding) { - // Shift has been pressed without chording while caps lock state. - setShiftLocked(false); - } else if (isShiftedOrShiftLocked && mState.isShiftKeyPressingOnShifted() - && !withSliding) { - // Shift has been pressed without chording while shifted state. - setShifted(UNSHIFT); - } else if (isManualTemporaryUpperCaseFromAuto && mState.isShiftKeyPressing() - && !withSliding) { - // Shift has been pressed without chording while manual temporary upper case - // transited from automatic temporary upper case. - setShifted(UNSHIFT); - } - } else { - // In symbol mode, snap back to the previous keyboard mode if the user chords the shift - // key and another key, then releases the shift key. - if (mSwitchState == SWITCH_STATE_CHORDING_SYMBOL) { - toggleShiftInSymbols(); - } - } - mState.onReleaseShift(); + mState.onReleaseShift(isAlphabetMode(), isSymbolShifted(), withSliding); } public void onPressSymbol() { if (DEBUG_STATE) { Log.d(TAG, "onPressSymbol: " + mState); } - toggleAlphabetAndSymbols(); - mState.onPressSymbol(); - mSwitchState = SWITCH_STATE_MOMENTARY_ALPHA_AND_SYMBOL; + mState.onPressSymbol(isAlphabetMode()); } public void onReleaseSymbol() { if (DEBUG_STATE) { Log.d(TAG, "onReleaseSymbol: " + mState); - } - // Snap back to the previous keyboard mode if the user chords the mode change key and - // another key, then releases the mode change key. - if (mSwitchState == SWITCH_STATE_CHORDING_ALPHA) { - toggleAlphabetAndSymbols(); } - mState.onReleaseSymbol(); + mState.onReleaseSymbol(isAlphabetMode()); } public void onOtherKeyPressed() { @@ -584,30 +499,28 @@ public class KeyboardSwitcher implements SharedPreferences.OnSharedPreferenceCha } public void onCancelInput() { - // Snap back to the previous keyboard mode if the user cancels sliding input. - if (isSinglePointer()) { - if (mSwitchState == SWITCH_STATE_MOMENTARY_ALPHA_AND_SYMBOL) { - toggleAlphabetAndSymbols(); - } else if (mSwitchState == SWITCH_STATE_MOMENTARY_SYMBOL_AND_MORE) { - toggleShiftInSymbols(); - } - } + mState.onCancelInput(isAlphabetMode(), isSymbolShifted(), isSinglePointer()); } // TODO: Move this variable to KeyboardState. private boolean mPrevMainKeyboardWasShiftLocked; - private void setSymbolsKeyboard() { + // Implements {@link KeyboardState.SwitchActions}. + @Override + public void setSymbolsKeyboard() { mPrevMainKeyboardWasShiftLocked = mState.isShiftLocked(); setKeyboard(getKeyboard(mSymbolsKeyboardId)); } - private void setAlphabetKeyboard() { + // Implements {@link KeyboardState.SwitchActions}. + @Override + public void setAlphabetKeyboard() { setKeyboard(getKeyboard(mMainKeyboardId)); setShiftLocked(mPrevMainKeyboardWasShiftLocked); mPrevMainKeyboardWasShiftLocked = false; } + // TODO: Remove this method and merge into toggleKeyboardMode(). private void toggleAlphabetAndSymbols() { if (isAlphabetMode()) { setSymbolsKeyboard(); @@ -620,10 +533,13 @@ public class KeyboardSwitcher implements SharedPreferences.OnSharedPreferenceCha return mCurrentId != null && mCurrentId.equals(mSymbolsShiftedKeyboardId); } - private void setSymbolsShiftedKeyboard() { + // Implements {@link KeyboardState.SwitchActions}. + @Override + public void setSymbolsShiftedKeyboard() { setKeyboard(getKeyboard(mSymbolsShiftedKeyboardId)); } + // TODO: Remove this method and merge into toggleShift(). private void toggleShiftInSymbols() { if (isSymbolShifted()) { setSymbolsKeyboard(); @@ -633,8 +549,7 @@ public class KeyboardSwitcher implements SharedPreferences.OnSharedPreferenceCha } public boolean isInMomentarySwitchState() { - return mSwitchState == SWITCH_STATE_MOMENTARY_ALPHA_AND_SYMBOL - || mSwitchState == SWITCH_STATE_MOMENTARY_SYMBOL_AND_MORE; + return mState.isInMomentarySwitchState(); } public boolean isVibrateAndSoundFeedbackRequired() { @@ -649,84 +564,15 @@ public class KeyboardSwitcher implements SharedPreferences.OnSharedPreferenceCha return mKeyboardView != null && mKeyboardView.hasDistinctMultitouch(); } - private static boolean isSpaceCharacter(int c) { - return c == Keyboard.CODE_SPACE || c == Keyboard.CODE_ENTER; - } - - private boolean isLayoutSwitchBackCharacter(int c) { - if (TextUtils.isEmpty(mLayoutSwitchBackSymbols)) return false; - if (mLayoutSwitchBackSymbols.indexOf(c) >= 0) return true; - return false; - } - /** * Updates state machine to figure out when to automatically snap back to the previous mode. */ - public void onKey(int code) { + public void onCodeInput(int code) { if (DEBUG_STATE) { - Log.d(TAG, "onKey: code=" + code + " switchState=" + mSwitchState - + " isSinglePointer=" + isSinglePointer()); - } - switch (mSwitchState) { - case SWITCH_STATE_MOMENTARY_ALPHA_AND_SYMBOL: - // Only distinct multi touch devices can be in this state. - // On non-distinct multi touch devices, mode change key is handled by - // {@link LatinIME#onCodeInput}, not by {@link LatinIME#onPress} and - // {@link LatinIME#onRelease}. So, on such devices, {@link #mSwitchState} starts - // from {@link #SWITCH_STATE_SYMBOL_BEGIN}, or {@link #SWITCH_STATE_ALPHA}, not from - // {@link #SWITCH_STATE_MOMENTARY}. - if (code == Keyboard.CODE_SWITCH_ALPHA_SYMBOL) { - // Detected only the mode change key has been pressed, and then released. - if (mCurrentId.equals(mMainKeyboardId)) { - mSwitchState = SWITCH_STATE_ALPHA; - } else { - mSwitchState = SWITCH_STATE_SYMBOL_BEGIN; - } - } else if (isSinglePointer()) { - // Snap back to the previous keyboard mode if the user pressed the mode change key - // and slid to other key, then released the finger. - // If the user cancels the sliding input, snapping back to the previous keyboard - // mode is handled by {@link #onCancelInput}. - toggleAlphabetAndSymbols(); - } else { - // Chording input is being started. The keyboard mode will be snapped back to the - // previous mode in {@link onReleaseSymbol} when the mode change key is released. - mSwitchState = SWITCH_STATE_CHORDING_ALPHA; - } - break; - case SWITCH_STATE_MOMENTARY_SYMBOL_AND_MORE: - if (code == Keyboard.CODE_SHIFT) { - // Detected only the shift key has been pressed on symbol layout, and then released. - mSwitchState = SWITCH_STATE_SYMBOL_BEGIN; - } else if (isSinglePointer()) { - // Snap back to the previous keyboard mode if the user pressed the shift key on - // symbol mode and slid to other key, then released the finger. - toggleShiftInSymbols(); - mSwitchState = SWITCH_STATE_SYMBOL; - } else { - // Chording input is being started. The keyboard mode will be snapped back to the - // previous mode in {@link onReleaseShift} when the shift key is released. - mSwitchState = SWITCH_STATE_CHORDING_SYMBOL; - } - break; - case SWITCH_STATE_SYMBOL_BEGIN: - if (!isSpaceCharacter(code) && code >= 0) { - mSwitchState = SWITCH_STATE_SYMBOL; - } - // Snap back to alpha keyboard mode immediately if user types a quote character. - if (isLayoutSwitchBackCharacter(code)) { - setAlphabetKeyboard(); - } - break; - case SWITCH_STATE_SYMBOL: - case SWITCH_STATE_CHORDING_SYMBOL: - // Snap back to alpha keyboard mode if user types one or more non-space/enter - // characters followed by a space/enter or a quote character. - if (isSpaceCharacter(code) || isLayoutSwitchBackCharacter(code)) { - setAlphabetKeyboard(); - } - break; + Log.d(TAG, "onCodeInput: code=" + code + " isSinglePointer=" + isSinglePointer() + + " " + mState); } + mState.onCodeInput(isAlphabetMode(), isSymbolShifted(), code, isSinglePointer()); } public LatinKeyboardView getKeyboardView() { diff --git a/java/src/com/android/inputmethod/keyboard/internal/KeyboardState.java b/java/src/com/android/inputmethod/keyboard/internal/KeyboardState.java index e56159405..a632a27b1 100644 --- a/java/src/com/android/inputmethod/keyboard/internal/KeyboardState.java +++ b/java/src/com/android/inputmethod/keyboard/internal/KeyboardState.java @@ -16,24 +16,59 @@ package com.android.inputmethod.keyboard.internal; +import android.text.TextUtils; + +import com.android.inputmethod.keyboard.Keyboard; + // TODO: Add unit tests public class KeyboardState { + public interface SwitchActions { + public void setAlphabetKeyboard(); + public static final int UNSHIFT = 0; + public static final int MANUAL_SHIFT = 1; + public static final int AUTOMATIC_SHIFT = 2; + public void setShifted(int shiftMode); + public void setShiftLocked(boolean shiftLocked); + public void setSymbolsKeyboard(); + public void setSymbolsShiftedKeyboard(); + } + private KeyboardShiftState mKeyboardShiftState = new KeyboardShiftState(); - // TODO: Combine these key state objects with auto mode switch state. private ShiftKeyState mShiftKeyState = new ShiftKeyState("Shift"); private ModifierKeyState mSymbolKeyState = new ModifierKeyState("Symbol"); - public KeyboardState() { + private static final int SWITCH_STATE_ALPHA = 0; + private static final int SWITCH_STATE_SYMBOL_BEGIN = 1; + private static final int SWITCH_STATE_SYMBOL = 2; + // The following states are used only on the distinct multi-touch panel devices. + private static final int SWITCH_STATE_MOMENTARY_ALPHA_AND_SYMBOL = 3; + private static final int SWITCH_STATE_MOMENTARY_SYMBOL_AND_MORE = 4; + private static final int SWITCH_STATE_CHORDING_ALPHA = 5; + private static final int SWITCH_STATE_CHORDING_SYMBOL = 6; + private int mSwitchState = SWITCH_STATE_ALPHA; + + private String mLayoutSwitchBackSymbols; + + private final SwitchActions mSwitchActions; + + public KeyboardState(SwitchActions switchActions) { + mSwitchActions = switchActions; } - public void onLoadKeyboard() { + public void onLoadKeyboard(String layoutSwitchBackSymbols) { + mLayoutSwitchBackSymbols = layoutSwitchBackSymbols; mKeyboardShiftState.setShifted(false); mKeyboardShiftState.setShiftLocked(false); mShiftKeyState.onRelease(); mSymbolKeyState.onRelease(); } + // TODO: Get rid of this method + public void onSetKeyboard(boolean isAlphabetMode) { + mSwitchState = isAlphabetMode ? SWITCH_STATE_ALPHA : SWITCH_STATE_SYMBOL_BEGIN; + } + public boolean isShiftLocked() { return mKeyboardShiftState.isShiftLocked(); } @@ -73,40 +108,40 @@ public class KeyboardState { mKeyboardShiftState.setAutomaticTemporaryUpperCase(); } - // TODO: Get rid of this method - public boolean isShiftKeyIgnoring() { - return mShiftKeyState.isIgnoring(); + private void toggleAlphabetAndSymbols(boolean isAlphabetMode) { + if (isAlphabetMode) { + mSwitchActions.setSymbolsKeyboard(); + } else { + mSwitchActions.setAlphabetKeyboard(); + } } - // TODO: Get rid of this method - public boolean isShiftKeyReleasing() { - return mShiftKeyState.isReleasing(); - } - - // TODO: Get rid of this method - public boolean isShiftKeyMomentary() { - return mShiftKeyState.isMomentary(); - } - - // TODO: Get rid of this method - public boolean isShiftKeyPressing() { - return mShiftKeyState.isPressing(); - } - - // TODO: Get rid of this method - public boolean isShiftKeyPressingOnShifted() { - return mShiftKeyState.isPressingOnShifted(); + private void toggleShiftInSymbols(boolean isSymbolShifted) { + if (isSymbolShifted) { + mSwitchActions.setSymbolsKeyboard(); + } else { + mSwitchActions.setSymbolsShiftedKeyboard(); + } } public void onReleaseCapsLock() { mShiftKeyState.onRelease(); } - public void onPressSymbol() { + // TODO: Get rid of isAlphabetMode argument. + public void onPressSymbol(boolean isAlphabetMode) { + toggleAlphabetAndSymbols(isAlphabetMode); mSymbolKeyState.onPress(); + mSwitchState = SWITCH_STATE_MOMENTARY_ALPHA_AND_SYMBOL; } - public void onReleaseSymbol() { + // TODO: Get rid of isAlphabetMode argument. + public void onReleaseSymbol(boolean isAlphabetMode) { + // Snap back to the previous keyboard mode if the user chords the mode change key and + // another key, then releases the mode change key. + if (mSwitchState == SWITCH_STATE_CHORDING_ALPHA) { + toggleAlphabetAndSymbols(isAlphabetMode); + } mSymbolKeyState.onRelease(); } @@ -115,48 +150,200 @@ public class KeyboardState { mSymbolKeyState.onOtherKeyPressed(); } - public void onUpdateShiftState(boolean isAlphabetMode) { - if (!isAlphabetMode) { + // TODO: Get rid of isAlphabetMode argument. + public void onUpdateShiftState(boolean isAlphabetMode, boolean autoCaps) { + if (isAlphabetMode) { + if (!isShiftLocked() && !mShiftKeyState.isIgnoring()) { + if (mShiftKeyState.isReleasing() && autoCaps) { + // Only when shift key is releasing, automatic temporary upper case will be set. + mSwitchActions.setShifted(SwitchActions.AUTOMATIC_SHIFT); + } else { + mSwitchActions.setShifted(mShiftKeyState.isMomentary() + ? SwitchActions.MANUAL_SHIFT : SwitchActions.UNSHIFT); + } + } + } else { // In symbol keyboard mode, we should clear shift key state because only alphabet // keyboard has shift key. mSymbolKeyState.onRelease(); } } - // TODO: Get rid of these boolean arguments. - public void onPressShift(boolean isAlphabetMode, boolean isShiftLocked, - boolean isAutomaticTemporaryUpperCase, boolean isShiftedOrShiftLocked) { + // TODO: Get rid of isAlphabetMode and isSymbolShifted arguments. + public void onPressShift(boolean isAlphabetMode, boolean isSymbolShifted) { if (isAlphabetMode) { - if (isShiftLocked) { + if (isShiftLocked()) { // Shift key is pressed while caps lock state, we will treat this state as shifted // caps lock state and mark as if shift key pressed while normal state. + mSwitchActions.setShifted(SwitchActions.MANUAL_SHIFT); mShiftKeyState.onPress(); - } else if (isAutomaticTemporaryUpperCase) { + } else if (isAutomaticTemporaryUpperCase()) { // Shift key is pressed while automatic temporary upper case, we have to move to // manual temporary upper case. + mSwitchActions.setShifted(SwitchActions.MANUAL_SHIFT); mShiftKeyState.onPress(); - } else if (isShiftedOrShiftLocked) { + } else if (isShiftedOrShiftLocked()) { // In manual upper case state, we just record shift key has been pressing while // shifted state. mShiftKeyState.onPressOnShifted(); } else { // In base layout, chording or manual temporary upper case mode is started. + mSwitchActions.setShifted(SwitchActions.MANUAL_SHIFT); mShiftKeyState.onPress(); } } else { // In symbol mode, just toggle symbol and symbol more keyboard. + toggleShiftInSymbols(isSymbolShifted); + mSwitchState = SWITCH_STATE_MOMENTARY_SYMBOL_AND_MORE; mShiftKeyState.onPress(); } } - public void onReleaseShift() { + // TODO: Get rid of isAlphabetMode and isSymbolShifted arguments. + public void onReleaseShift(boolean isAlphabetMode, boolean isSymbolShifted, + boolean withSliding) { + if (isAlphabetMode) { + final boolean isShiftLocked = isShiftLocked(); + if (mShiftKeyState.isMomentary()) { + // After chording input while normal state. + mSwitchActions.setShifted(SwitchActions.UNSHIFT); + } else if (isShiftLocked && !isShiftLockShifted() && (mShiftKeyState.isPressing() + || mShiftKeyState.isPressingOnShifted()) && !withSliding) { + // Shift has been long pressed, ignore this release. + } else if (isShiftLocked && !mShiftKeyState.isIgnoring() && !withSliding) { + // Shift has been pressed without chording while caps lock state. + mSwitchActions.setShiftLocked(false); + } else if (isShiftedOrShiftLocked() && mShiftKeyState.isPressingOnShifted() + && !withSliding) { + // Shift has been pressed without chording while shifted state. + mSwitchActions.setShifted(SwitchActions.UNSHIFT); + } else if (isManualTemporaryUpperCaseFromAuto() && mShiftKeyState.isPressing() + && !withSliding) { + // Shift has been pressed without chording while manual temporary upper case + // transited from automatic temporary upper case. + mSwitchActions.setShifted(SwitchActions.UNSHIFT); + } + } else { + // In symbol mode, snap back to the previous keyboard mode if the user chords the shift + // key and another key, then releases the shift key. + if (mSwitchState == SWITCH_STATE_CHORDING_SYMBOL) { + toggleShiftInSymbols(isSymbolShifted); + } + } mShiftKeyState.onRelease(); } + // TODO: Get rid of isAlphabetMode and isSymbolShifted arguments. + public void onCancelInput(boolean isAlphabetMode, boolean isSymbolShifted, + boolean isSinglePointer) { + // Snap back to the previous keyboard mode if the user cancels sliding input. + if (isSinglePointer) { + if (mSwitchState == SWITCH_STATE_MOMENTARY_ALPHA_AND_SYMBOL) { + toggleAlphabetAndSymbols(isAlphabetMode); + } else if (mSwitchState == SWITCH_STATE_MOMENTARY_SYMBOL_AND_MORE) { + toggleShiftInSymbols(isSymbolShifted); + } + } + } + + public boolean isInMomentarySwitchState() { + return mSwitchState == SWITCH_STATE_MOMENTARY_ALPHA_AND_SYMBOL + || mSwitchState == SWITCH_STATE_MOMENTARY_SYMBOL_AND_MORE; + } + + private static boolean isSpaceCharacter(int c) { + return c == Keyboard.CODE_SPACE || c == Keyboard.CODE_ENTER; + } + + private boolean isLayoutSwitchBackCharacter(int c) { + if (TextUtils.isEmpty(mLayoutSwitchBackSymbols)) return false; + if (mLayoutSwitchBackSymbols.indexOf(c) >= 0) return true; + return false; + } + + // TODO: Get rid of isAlphabetMode and isSymbolShifted arguments. + public void onCodeInput(boolean isAlphabetMode, boolean isSymbolShifted, int code, + boolean isSinglePointer) { + switch (mSwitchState) { + case SWITCH_STATE_MOMENTARY_ALPHA_AND_SYMBOL: + // Only distinct multi touch devices can be in this state. + // On non-distinct multi touch devices, mode change key is handled by + // {@link LatinIME#onCodeInput}, not by {@link LatinIME#onPress} and + // {@link LatinIME#onRelease}. So, on such devices, {@link #mSwitchState} starts + // from {@link #SWITCH_STATE_SYMBOL_BEGIN}, or {@link #SWITCH_STATE_ALPHA}, not from + // {@link #SWITCH_STATE_MOMENTARY}. + if (code == Keyboard.CODE_SWITCH_ALPHA_SYMBOL) { + // Detected only the mode change key has been pressed, and then released. + if (isAlphabetMode) { + mSwitchState = SWITCH_STATE_ALPHA; + } else { + mSwitchState = SWITCH_STATE_SYMBOL_BEGIN; + } + } else if (isSinglePointer) { + // Snap back to the previous keyboard mode if the user pressed the mode change key + // and slid to other key, then released the finger. + // If the user cancels the sliding input, snapping back to the previous keyboard + // mode is handled by {@link #onCancelInput}. + toggleAlphabetAndSymbols(isAlphabetMode); + } else { + // Chording input is being started. The keyboard mode will be snapped back to the + // previous mode in {@link onReleaseSymbol} when the mode change key is released. + mSwitchState = SWITCH_STATE_CHORDING_ALPHA; + } + break; + case SWITCH_STATE_MOMENTARY_SYMBOL_AND_MORE: + if (code == Keyboard.CODE_SHIFT) { + // Detected only the shift key has been pressed on symbol layout, and then released. + mSwitchState = SWITCH_STATE_SYMBOL_BEGIN; + } else if (isSinglePointer) { + // Snap back to the previous keyboard mode if the user pressed the shift key on + // symbol mode and slid to other key, then released the finger. + toggleShiftInSymbols(isSymbolShifted); + mSwitchState = SWITCH_STATE_SYMBOL; + } else { + // Chording input is being started. The keyboard mode will be snapped back to the + // previous mode in {@link onReleaseShift} when the shift key is released. + mSwitchState = SWITCH_STATE_CHORDING_SYMBOL; + } + break; + case SWITCH_STATE_SYMBOL_BEGIN: + if (!isSpaceCharacter(code) && code >= 0) { + mSwitchState = SWITCH_STATE_SYMBOL; + } + // Snap back to alpha keyboard mode immediately if user types a quote character. + if (isLayoutSwitchBackCharacter(code)) { + mSwitchActions.setAlphabetKeyboard(); + } + break; + case SWITCH_STATE_SYMBOL: + case SWITCH_STATE_CHORDING_SYMBOL: + // Snap back to alpha keyboard mode if user types one or more non-space/enter + // characters followed by a space/enter or a quote character. + if (isSpaceCharacter(code) || isLayoutSwitchBackCharacter(code)) { + mSwitchActions.setAlphabetKeyboard(); + } + break; + } + } + + private static String switchStateToString(int switchState) { + switch (switchState) { + case SWITCH_STATE_ALPHA: return "ALPHA"; + case SWITCH_STATE_SYMBOL_BEGIN: return "SYMBOL-BEGIN"; + case SWITCH_STATE_SYMBOL: return "SYMBOL"; + case SWITCH_STATE_MOMENTARY_ALPHA_AND_SYMBOL: return "MOMENTARY-ALPHA-SYMBOL"; + case SWITCH_STATE_MOMENTARY_SYMBOL_AND_MORE: return "MOMENTARY-SYMBOL-MORE"; + case SWITCH_STATE_CHORDING_ALPHA: return "CHORDING-ALPHA"; + case SWITCH_STATE_CHORDING_SYMBOL: return "CHORDING-SYMBOL"; + default: return null; + } + } + @Override public String toString() { return "[keyboard=" + mKeyboardShiftState + " shift=" + mShiftKeyState - + " symbol=" + mSymbolKeyState + "]"; + + " symbol=" + mSymbolKeyState + + " switch=" + switchStateToString(mSwitchState) + "]"; } } diff --git a/java/src/com/android/inputmethod/latin/LatinIME.java b/java/src/com/android/inputmethod/latin/LatinIME.java index c2656b891..ab41b1018 100644 --- a/java/src/com/android/inputmethod/latin/LatinIME.java +++ b/java/src/com/android/inputmethod/latin/LatinIME.java @@ -1374,7 +1374,7 @@ public class LatinIME extends InputMethodServiceCompatWrapper implements Keyboar mExpectingUpdateSelection = true; break; } - switcher.onKey(primaryCode); + switcher.onCodeInput(primaryCode); // Reset after any single keystroke mEnteredText = null; } @@ -1390,7 +1390,7 @@ public class LatinIME extends InputMethodServiceCompatWrapper implements Keyboar ic.commitText(text, 1); ic.endBatchEdit(); mKeyboardSwitcher.updateShiftState(); - mKeyboardSwitcher.onKey(Keyboard.CODE_DUMMY); + mKeyboardSwitcher.onCodeInput(Keyboard.CODE_DUMMY); mSpaceState = SPACE_STATE_NONE; mEnteredText = text; }