diff options
Diffstat (limited to 'java/src')
4 files changed, 353 insertions, 301 deletions
diff --git a/java/src/com/android/inputmethod/keyboard/KeyboardId.java b/java/src/com/android/inputmethod/keyboard/KeyboardId.java index 840857778..ecc821a4b 100644 --- a/java/src/com/android/inputmethod/keyboard/KeyboardId.java +++ b/java/src/com/android/inputmethod/keyboard/KeyboardId.java @@ -16,6 +16,7 @@ package com.android.inputmethod.keyboard; +import android.text.TextUtils; import android.view.inputmethod.EditorInfo; import com.android.inputmethod.compat.EditorInfoCompatUtils; @@ -177,6 +178,14 @@ public class KeyboardId { ); } + public static boolean equivalentEditorInfoForKeyboard(EditorInfo a, EditorInfo b) { + if (a == null && b == null) return true; + if (a == null || b == null) return false; + return a.inputType == b.inputType + && a.imeOptions == b.imeOptions + && TextUtils.equals(a.privateImeOptions, b.privateImeOptions); + } + public static String modeName(int mode) { switch (mode) { case MODE_TEXT: return "text"; diff --git a/java/src/com/android/inputmethod/keyboard/KeyboardSwitcher.java b/java/src/com/android/inputmethod/keyboard/KeyboardSwitcher.java index 32aabf928..d38f25496 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; @@ -81,80 +81,15 @@ public class KeyboardSwitcher implements SharedPreferences.OnSharedPreferenceCha private final HashMap<KeyboardId, SoftReference<LatinKeyboard>> mKeyboardCache = new HashMap<KeyboardId, SoftReference<LatinKeyboard>>(); - private KeyboardLayoutState mSavedKeyboardState = new KeyboardLayoutState(); - /** mIsAutoCorrectionActive indicates that auto corrected word will be input instead of * 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; private static final KeyboardSwitcher sInstance = new KeyboardSwitcher(); - private class KeyboardLayoutState { - private boolean mIsValid; - private boolean mIsAlphabetMode; - private boolean mIsShiftLocked; - private boolean mIsShifted; - - public void save() { - if (mCurrentId == null) { - return; - } - mIsAlphabetMode = isAlphabetMode(); - if (mIsAlphabetMode) { - mIsShiftLocked = mState.isShiftLocked(); - mIsShifted = !mIsShiftLocked && mState.isShiftedOrShiftLocked(); - } else { - mIsShiftLocked = false; - mIsShifted = mCurrentId.equals(mSymbolsShiftedKeyboardId); - } - mIsValid = true; - } - - public KeyboardId getKeyboardId() { - if (!mIsValid) return mMainKeyboardId; - - if (mIsAlphabetMode) { - return mMainKeyboardId; - } else { - return mIsShifted ? mSymbolsShiftedKeyboardId : mSymbolsKeyboardId; - } - } - - public void restore() { - if (!mIsValid) return; - mIsValid = false; - - if (mIsAlphabetMode) { - final boolean isAlphabetMode = isAlphabetMode(); - final boolean isShiftLocked = isAlphabetMode && mState.isShiftLocked(); - final boolean isShifted = !isShiftLocked && mState.isShiftedOrShiftLocked(); - if (mIsShiftLocked != isShiftLocked) { - toggleCapsLock(); - } else if (mIsShifted != isShifted) { - onPressShift(false); - onReleaseShift(false); - } - } - } - } - public static KeyboardSwitcher getInstance() { return sInstance; } @@ -173,7 +108,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); } @@ -205,9 +140,9 @@ public class KeyboardSwitcher implements SharedPreferences.OnSharedPreferenceCha mMainKeyboardId = getKeyboardId(editorInfo, false, false, settingsValues); mSymbolsKeyboardId = getKeyboardId(editorInfo, true, false, settingsValues); mSymbolsShiftedKeyboardId = getKeyboardId(editorInfo, true, true, settingsValues); - mLayoutSwitchBackSymbols = mResources.getString(R.string.layout_switch_back_symbols); - setKeyboard(getKeyboard(mSavedKeyboardState.getKeyboardId())); - mSavedKeyboardState.restore(); + mState.onLoadKeyboard(mResources.getString(R.string.layout_switch_back_symbols)); + mPrevMainKeyboardWasShiftLocked = false; + mState.onRestoreKeyboardState(); } catch (RuntimeException e) { Log.w(TAG, "loading keyboard failed: " + mMainKeyboardId, e); LatinImeLogger.logOnException(mMainKeyboardId.toString(), e); @@ -215,7 +150,9 @@ public class KeyboardSwitcher implements SharedPreferences.OnSharedPreferenceCha } public void saveKeyboardState() { - mSavedKeyboardState.save(); + if (mCurrentId != null) { + mState.onSaveKeyboardState(isAlphabetMode(), isSymbolShifted()); + } } public void onFinishInputView() { @@ -231,7 +168,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), @@ -242,10 +179,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. @@ -381,7 +314,10 @@ 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) return; @@ -404,20 +340,28 @@ 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) return; mState.setShiftLocked(shiftLocked); latinKeyboard.setShiftLocked(shiftLocked); mKeyboardView.invalidateAllKeys(); + if (!shiftLocked) { + // To be able to turn off caps lock by "double tap" on shift key, we should ignore + // the second tap of the "double tap" from now for a while because we just have + // already turned off caps lock above. + mKeyboardView.startIgnoringDoubleTap(); + } } /** * Toggle keyboard shift state triggered by user touch event. */ public void toggleShift() { - mInputMethodService.mHandler.cancelUpdateShiftState(); if (DEBUG_STATE) { Log.d(TAG, "toggleShift: " + mState); } @@ -429,16 +373,16 @@ public class KeyboardSwitcher implements SharedPreferences.OnSharedPreferenceCha } public void toggleCapsLock() { - mInputMethodService.mHandler.cancelUpdateShiftState(); if (DEBUG_STATE) { Log.d(TAG, "toggleCapsLock: " + mState); } if (isAlphabetMode()) { if (mState.isShiftLocked()) { + setShiftLocked(false); + // TODO: Remove this. // Shift key is long pressed while caps lock state, we will toggle back to normal // state. And mark as if shift key is released. - setShiftLocked(false); - mState.onToggleCapsLock(); + mState.onReleaseCapsLock(); } else { setShiftLocked(true); } @@ -452,12 +396,6 @@ public class KeyboardSwitcher implements SharedPreferences.OnSharedPreferenceCha toggleAlphabetAndSymbols(); } - private void startIgnoringDoubleTap() { - if (mKeyboardView != null) { - mKeyboardView.startIgnoringDoubleTap(); - } - } - /** * Update keyboard shift state triggered by connected EditText status change. */ @@ -466,19 +404,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) { @@ -487,33 +413,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. - toggleShift(); - } - } 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) { @@ -522,65 +422,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. - toggleShift(); - } 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. - toggleCapsLock(); - // To be able to turn off caps lock by "double tap" on shift key, we should ignore - // the second tap of the "double tap" from now for a while because we just have - // already turned off caps lock above. - startIgnoringDoubleTap(); - } else if (isShiftedOrShiftLocked && mState.isShiftKeyPressingOnShifted() - && !withSliding) { - // Shift has been pressed without chording while shifted state. - toggleShift(); - } else if (isManualTemporaryUpperCaseFromAuto && mState.isShiftKeyPressing() - && !withSliding) { - // Shift has been pressed without chording while manual temporary upper case - // transited from automatic temporary upper case. - toggleShift(); - } - } 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() { @@ -591,29 +447,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(); @@ -626,10 +481,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(); @@ -639,8 +497,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() { @@ -655,84 +512,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 fd7e77863..ee5ef9164 100644 --- a/java/src/com/android/inputmethod/keyboard/internal/KeyboardState.java +++ b/java/src/com/android/inputmethod/keyboard/internal/KeyboardState.java @@ -16,15 +16,116 @@ package com.android.inputmethod.keyboard.internal; +import android.text.TextUtils; +import android.util.Log; + +import com.android.inputmethod.keyboard.Keyboard; + // TODO: Add unit tests public class KeyboardState { + private static final String TAG = KeyboardState.class.getSimpleName(); + private static final boolean DEBUG_STATE = false; + + 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; + + private final SavedKeyboardState mSavedKeyboardState = new SavedKeyboardState(); + + private static class SavedKeyboardState { + public boolean mIsValid; + public boolean mIsAlphabetMode; + public boolean mIsShiftLocked; + public boolean mIsShifted; + } + + public KeyboardState(SwitchActions switchActions) { + mSwitchActions = switchActions; + } + + public void onLoadKeyboard(String layoutSwitchBackSymbols) { + mLayoutSwitchBackSymbols = layoutSwitchBackSymbols; + mKeyboardShiftState.setShifted(false); + mKeyboardShiftState.setShiftLocked(false); + mShiftKeyState.onRelease(); + mSymbolKeyState.onRelease(); + } + + // TODO: Get rid of isAlphabetMode and isSymbolShifted arguments. + public void onSaveKeyboardState(boolean isAlphabetMode, boolean isSymbolShifted) { + final SavedKeyboardState state = mSavedKeyboardState; + state.mIsAlphabetMode = isAlphabetMode; + if (isAlphabetMode) { + state.mIsShiftLocked = isShiftLocked(); + state.mIsShifted = !state.mIsShiftLocked && isShiftedOrShiftLocked(); + } else { + state.mIsShiftLocked = false; + state.mIsShifted = isSymbolShifted; + } + state.mIsValid = true; + if (DEBUG_STATE) { + Log.d(TAG, "save: alphabet=" + state.mIsAlphabetMode + + " shiftLocked=" + state.mIsShiftLocked + " shift=" + state.mIsShifted); + } + } + + public void onRestoreKeyboardState() { + final SavedKeyboardState state = mSavedKeyboardState; + if (DEBUG_STATE) { + Log.d(TAG, "restore: valid=" + state.mIsValid + " alphabet=" + state.mIsAlphabetMode + + " shiftLocked=" + state.mIsShiftLocked + " shift=" + state.mIsShifted); + } + if (!state.mIsValid || state.mIsAlphabetMode) { + mSwitchActions.setAlphabetKeyboard(); + } else { + if (state.mIsShifted) { + mSwitchActions.setSymbolsShiftedKeyboard(); + } else { + mSwitchActions.setSymbolsKeyboard(); + } + } + + if (!state.mIsValid) return; + state.mIsValid = false; + + if (state.mIsAlphabetMode) { + mSwitchActions.setShiftLocked(state.mIsShiftLocked); + if (!state.mIsShiftLocked) { + mSwitchActions.setShifted( + state.mIsShifted ? SwitchActions.MANUAL_SHIFT : SwitchActions.UNSHIFT); + } + } + } + + // TODO: Get rid of this method + public void onSetKeyboard(boolean isAlphabetMode) { + mSwitchState = isAlphabetMode ? SWITCH_STATE_ALPHA : SWITCH_STATE_SYMBOL_BEGIN; } public boolean isShiftLocked() { @@ -66,40 +167,40 @@ public class KeyboardState { mKeyboardShiftState.setAutomaticTemporaryUpperCase(); } - // TODO: Get rid of this method - public boolean isShiftKeyIgnoring() { - return mShiftKeyState.isIgnoring(); - } - - // 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(); + private void toggleAlphabetAndSymbols(boolean isAlphabetMode) { + if (isAlphabetMode) { + mSwitchActions.setSymbolsKeyboard(); + } else { + mSwitchActions.setAlphabetKeyboard(); + } } - // 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 onToggleCapsLock() { + 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(); } @@ -108,48 +209,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 5f446a5c4..ab41b1018 100644 --- a/java/src/com/android/inputmethod/latin/LatinIME.java +++ b/java/src/com/android/inputmethod/latin/LatinIME.java @@ -62,6 +62,7 @@ import com.android.inputmethod.deprecated.VoiceProxy; import com.android.inputmethod.keyboard.Key; import com.android.inputmethod.keyboard.Keyboard; import com.android.inputmethod.keyboard.KeyboardActionListener; +import com.android.inputmethod.keyboard.KeyboardId; import com.android.inputmethod.keyboard.KeyboardSwitcher; import com.android.inputmethod.keyboard.KeyboardView; import com.android.inputmethod.keyboard.LatinKeyboard; @@ -442,7 +443,8 @@ public class LatinIME extends InputMethodServiceCompatWrapper implements Keyboar } public void onStartInputView(EditorInfo editorInfo, boolean restarting) { - if (hasMessages(MSG_PENDING_IMS_CALLBACK) && editorInfo == mAppliedEditorInfo) { + if (hasMessages(MSG_PENDING_IMS_CALLBACK) + && KeyboardId.equivalentEditorInfoForKeyboard(editorInfo, mAppliedEditorInfo)) { // Typically this is the second onStartInputView after orientation changed. resetPendingImsCallback(); } else { @@ -1372,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; } @@ -1388,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; } |