Move SWITCH_STATE related stuffs to KeyboardState

Bug: 5708602
Change-Id: I904ec370e3761c47af17a680c0932ec98cfd3c27
This commit is contained in:
Tadashi G. Takaoka 2011-12-07 19:31:11 +09:00
parent b300dbbd39
commit eef6238f94
3 changed files with 267 additions and 234 deletions

View file

@ -20,7 +20,6 @@ import android.content.Context;
import android.content.SharedPreferences; import android.content.SharedPreferences;
import android.content.res.Configuration; import android.content.res.Configuration;
import android.content.res.Resources; import android.content.res.Resources;
import android.text.TextUtils;
import android.util.DisplayMetrics; import android.util.DisplayMetrics;
import android.util.Log; import android.util.Log;
import android.view.ContextThemeWrapper; import android.view.ContextThemeWrapper;
@ -44,7 +43,8 @@ import java.lang.ref.SoftReference;
import java.util.HashMap; import java.util.HashMap;
import java.util.Locale; 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 String TAG = KeyboardSwitcher.class.getSimpleName();
private static final boolean DEBUG_CACHE = LatinImeLogger.sDBG; private static final boolean DEBUG_CACHE = LatinImeLogger.sDBG;
public static final boolean DEBUG_STATE = false; public static final boolean DEBUG_STATE = false;
@ -87,20 +87,6 @@ public class KeyboardSwitcher implements SharedPreferences.OnSharedPreferenceCha
* what user actually typed. */ * what user actually typed. */
private boolean mIsAutoCorrectionActive; 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 int mThemeIndex = -1;
private Context mThemeContext; private Context mThemeContext;
@ -123,10 +109,17 @@ public class KeyboardSwitcher implements SharedPreferences.OnSharedPreferenceCha
mIsShifted = isSymbolShifted(); mIsShifted = isSymbolShifted();
} }
mIsValid = true; mIsValid = true;
if (DEBUG_STATE) {
Log.d(TAG, "save: alphabet=" + mIsAlphabetMode + " shiftLocked=" + mIsShiftLocked
+ " shift=" + mIsShifted);
}
} }
public void restore() { public void restore() {
mPrevMainKeyboardWasShiftLocked = false; if (DEBUG_STATE) {
Log.d(TAG, "restore: valid=" + mIsValid + " alphabet=" + mIsAlphabetMode
+ " shiftLocked=" + mIsShiftLocked + " shift=" + mIsShifted);
}
if (!mIsValid || mIsAlphabetMode) { if (!mIsValid || mIsAlphabetMode) {
setAlphabetKeyboard(); setAlphabetKeyboard();
} else { } else {
@ -167,7 +160,7 @@ public class KeyboardSwitcher implements SharedPreferences.OnSharedPreferenceCha
mResources = ims.getResources(); mResources = ims.getResources();
mPrefs = prefs; mPrefs = prefs;
mSubtypeSwitcher = SubtypeSwitcher.getInstance(); mSubtypeSwitcher = SubtypeSwitcher.getInstance();
mState = new KeyboardState(); mState = new KeyboardState(this);
setContextThemeWrapper(ims, getKeyboardThemeIndex(ims, prefs)); setContextThemeWrapper(ims, getKeyboardThemeIndex(ims, prefs));
prefs.registerOnSharedPreferenceChangeListener(this); prefs.registerOnSharedPreferenceChangeListener(this);
} }
@ -199,8 +192,8 @@ public class KeyboardSwitcher implements SharedPreferences.OnSharedPreferenceCha
mMainKeyboardId = getKeyboardId(editorInfo, false, false, settingsValues); mMainKeyboardId = getKeyboardId(editorInfo, false, false, settingsValues);
mSymbolsKeyboardId = getKeyboardId(editorInfo, true, false, settingsValues); mSymbolsKeyboardId = getKeyboardId(editorInfo, true, false, settingsValues);
mSymbolsShiftedKeyboardId = getKeyboardId(editorInfo, true, true, settingsValues); mSymbolsShiftedKeyboardId = getKeyboardId(editorInfo, true, true, settingsValues);
mState.onLoadKeyboard(); mState.onLoadKeyboard(mResources.getString(R.string.layout_switch_back_symbols));
mLayoutSwitchBackSymbols = mResources.getString(R.string.layout_switch_back_symbols); mPrevMainKeyboardWasShiftLocked = false;
mSavedKeyboardState.restore(); mSavedKeyboardState.restore();
} catch (RuntimeException e) { } catch (RuntimeException e) {
Log.w(TAG, "loading keyboard failed: " + mMainKeyboardId, e); Log.w(TAG, "loading keyboard failed: " + mMainKeyboardId, e);
@ -227,7 +220,7 @@ public class KeyboardSwitcher implements SharedPreferences.OnSharedPreferenceCha
mKeyboardView.setKeyboard(keyboard); mKeyboardView.setKeyboard(keyboard);
mCurrentInputView.setKeyboardGeometry(keyboard.mTopPadding); mCurrentInputView.setKeyboardGeometry(keyboard.mTopPadding);
mCurrentId = keyboard.mId; mCurrentId = keyboard.mId;
mSwitchState = getSwitchState(); mState.onSetKeyboard(isAlphabetMode());
updateShiftLockState(keyboard); updateShiftLockState(keyboard);
mKeyboardView.setKeyPreviewPopupEnabled( mKeyboardView.setKeyPreviewPopupEnabled(
Settings.Values.isKeyPreviewPopupEnabled(mPrefs, mResources), Settings.Values.isKeyPreviewPopupEnabled(mPrefs, mResources),
@ -238,10 +231,6 @@ public class KeyboardSwitcher implements SharedPreferences.OnSharedPreferenceCha
updateShiftState(); updateShiftState();
} }
private int getSwitchState() {
return isAlphabetMode() ? SWITCH_STATE_ALPHA : SWITCH_STATE_SYMBOL_BEGIN;
}
private void updateShiftLockState(Keyboard keyboard) { private void updateShiftLockState(Keyboard keyboard) {
if (mCurrentId.equals(mSymbolsShiftedKeyboardId)) { if (mCurrentId.equals(mSymbolsShiftedKeyboardId)) {
// Symbol keyboard may have an ALT key that has a caps lock style indicator (a.k.a. // 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(); return mState.isManualTemporaryUpperCase();
} }
private void setShifted(int shiftMode) { // Implements {@link KeyboardState.SwitchActions}.
@Override
public void setShifted(int shiftMode) {
mInputMethodService.mHandler.cancelUpdateShiftState(); mInputMethodService.mHandler.cancelUpdateShiftState();
LatinKeyboard latinKeyboard = getLatinKeyboard(); LatinKeyboard latinKeyboard = getLatinKeyboard();
if (latinKeyboard == null) if (latinKeyboard == null)
@ -401,7 +392,9 @@ public class KeyboardSwitcher implements SharedPreferences.OnSharedPreferenceCha
mKeyboardView.invalidateAllKeys(); mKeyboardView.invalidateAllKeys();
} }
private void setShiftLocked(boolean shiftLocked) { // Implements {@link KeyboardState.SwitchActions}.
@Override
public void setShiftLocked(boolean shiftLocked) {
mInputMethodService.mHandler.cancelUpdateShiftState(); mInputMethodService.mHandler.cancelUpdateShiftState();
LatinKeyboard latinKeyboard = getLatinKeyboard(); LatinKeyboard latinKeyboard = getLatinKeyboard();
if (latinKeyboard == null) if (latinKeyboard == null)
@ -463,19 +456,7 @@ public class KeyboardSwitcher implements SharedPreferences.OnSharedPreferenceCha
Log.d(TAG, "updateShiftState: " + mState Log.d(TAG, "updateShiftState: " + mState
+ " autoCaps=" + mInputMethodService.getCurrentAutoCapsState()); + " autoCaps=" + mInputMethodService.getCurrentAutoCapsState());
} }
final boolean isAlphabetMode = isAlphabetMode(); mState.onUpdateShiftState(isAlphabetMode(), mInputMethodService.getCurrentAutoCapsState());
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);
} }
public void onPressShift(boolean withSliding) { public void onPressShift(boolean withSliding) {
@ -484,33 +465,7 @@ public class KeyboardSwitcher implements SharedPreferences.OnSharedPreferenceCha
if (DEBUG_STATE) { if (DEBUG_STATE) {
Log.d(TAG, "onPressShift: " + mState + " sliding=" + withSliding); Log.d(TAG, "onPressShift: " + mState + " sliding=" + withSliding);
} }
final boolean isAlphabetMode = isAlphabetMode(); mState.onPressShift(isAlphabetMode(), isSymbolShifted());
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);
} }
public void onReleaseShift(boolean withSliding) { public void onReleaseShift(boolean withSliding) {
@ -519,61 +474,21 @@ public class KeyboardSwitcher implements SharedPreferences.OnSharedPreferenceCha
if (DEBUG_STATE) { if (DEBUG_STATE) {
Log.d(TAG, "onReleaseShift: " + mState + " sliding=" + withSliding); Log.d(TAG, "onReleaseShift: " + mState + " sliding=" + withSliding);
} }
final boolean isAlphabetMode = isAlphabetMode(); mState.onReleaseShift(isAlphabetMode(), isSymbolShifted(), withSliding);
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();
} }
public void onPressSymbol() { public void onPressSymbol() {
if (DEBUG_STATE) { if (DEBUG_STATE) {
Log.d(TAG, "onPressSymbol: " + mState); Log.d(TAG, "onPressSymbol: " + mState);
} }
toggleAlphabetAndSymbols(); mState.onPressSymbol(isAlphabetMode());
mState.onPressSymbol();
mSwitchState = SWITCH_STATE_MOMENTARY_ALPHA_AND_SYMBOL;
} }
public void onReleaseSymbol() { public void onReleaseSymbol() {
if (DEBUG_STATE) { if (DEBUG_STATE) {
Log.d(TAG, "onReleaseSymbol: " + mState); 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() { public void onOtherKeyPressed() {
@ -584,30 +499,28 @@ public class KeyboardSwitcher implements SharedPreferences.OnSharedPreferenceCha
} }
public void onCancelInput() { public void onCancelInput() {
// Snap back to the previous keyboard mode if the user cancels sliding input. mState.onCancelInput(isAlphabetMode(), isSymbolShifted(), isSinglePointer());
if (isSinglePointer()) {
if (mSwitchState == SWITCH_STATE_MOMENTARY_ALPHA_AND_SYMBOL) {
toggleAlphabetAndSymbols();
} else if (mSwitchState == SWITCH_STATE_MOMENTARY_SYMBOL_AND_MORE) {
toggleShiftInSymbols();
}
}
} }
// TODO: Move this variable to KeyboardState. // TODO: Move this variable to KeyboardState.
private boolean mPrevMainKeyboardWasShiftLocked; private boolean mPrevMainKeyboardWasShiftLocked;
private void setSymbolsKeyboard() { // Implements {@link KeyboardState.SwitchActions}.
@Override
public void setSymbolsKeyboard() {
mPrevMainKeyboardWasShiftLocked = mState.isShiftLocked(); mPrevMainKeyboardWasShiftLocked = mState.isShiftLocked();
setKeyboard(getKeyboard(mSymbolsKeyboardId)); setKeyboard(getKeyboard(mSymbolsKeyboardId));
} }
private void setAlphabetKeyboard() { // Implements {@link KeyboardState.SwitchActions}.
@Override
public void setAlphabetKeyboard() {
setKeyboard(getKeyboard(mMainKeyboardId)); setKeyboard(getKeyboard(mMainKeyboardId));
setShiftLocked(mPrevMainKeyboardWasShiftLocked); setShiftLocked(mPrevMainKeyboardWasShiftLocked);
mPrevMainKeyboardWasShiftLocked = false; mPrevMainKeyboardWasShiftLocked = false;
} }
// TODO: Remove this method and merge into toggleKeyboardMode().
private void toggleAlphabetAndSymbols() { private void toggleAlphabetAndSymbols() {
if (isAlphabetMode()) { if (isAlphabetMode()) {
setSymbolsKeyboard(); setSymbolsKeyboard();
@ -620,10 +533,13 @@ public class KeyboardSwitcher implements SharedPreferences.OnSharedPreferenceCha
return mCurrentId != null && mCurrentId.equals(mSymbolsShiftedKeyboardId); return mCurrentId != null && mCurrentId.equals(mSymbolsShiftedKeyboardId);
} }
private void setSymbolsShiftedKeyboard() { // Implements {@link KeyboardState.SwitchActions}.
@Override
public void setSymbolsShiftedKeyboard() {
setKeyboard(getKeyboard(mSymbolsShiftedKeyboardId)); setKeyboard(getKeyboard(mSymbolsShiftedKeyboardId));
} }
// TODO: Remove this method and merge into toggleShift().
private void toggleShiftInSymbols() { private void toggleShiftInSymbols() {
if (isSymbolShifted()) { if (isSymbolShifted()) {
setSymbolsKeyboard(); setSymbolsKeyboard();
@ -633,8 +549,7 @@ public class KeyboardSwitcher implements SharedPreferences.OnSharedPreferenceCha
} }
public boolean isInMomentarySwitchState() { public boolean isInMomentarySwitchState() {
return mSwitchState == SWITCH_STATE_MOMENTARY_ALPHA_AND_SYMBOL return mState.isInMomentarySwitchState();
|| mSwitchState == SWITCH_STATE_MOMENTARY_SYMBOL_AND_MORE;
} }
public boolean isVibrateAndSoundFeedbackRequired() { public boolean isVibrateAndSoundFeedbackRequired() {
@ -649,84 +564,15 @@ public class KeyboardSwitcher implements SharedPreferences.OnSharedPreferenceCha
return mKeyboardView != null && mKeyboardView.hasDistinctMultitouch(); 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. * 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) { if (DEBUG_STATE) {
Log.d(TAG, "onKey: code=" + code + " switchState=" + mSwitchState Log.d(TAG, "onCodeInput: code=" + code + " isSinglePointer=" + isSinglePointer()
+ " isSinglePointer=" + isSinglePointer()); + " " + mState);
}
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;
} }
mState.onCodeInput(isAlphabetMode(), isSymbolShifted(), code, isSinglePointer());
} }
public LatinKeyboardView getKeyboardView() { public LatinKeyboardView getKeyboardView() {

View file

@ -16,24 +16,59 @@
package com.android.inputmethod.keyboard.internal; package com.android.inputmethod.keyboard.internal;
import android.text.TextUtils;
import com.android.inputmethod.keyboard.Keyboard;
// TODO: Add unit tests // TODO: Add unit tests
public class KeyboardState { 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(); private KeyboardShiftState mKeyboardShiftState = new KeyboardShiftState();
// TODO: Combine these key state objects with auto mode switch state.
private ShiftKeyState mShiftKeyState = new ShiftKeyState("Shift"); private ShiftKeyState mShiftKeyState = new ShiftKeyState("Shift");
private ModifierKeyState mSymbolKeyState = new ModifierKeyState("Symbol"); 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.setShifted(false);
mKeyboardShiftState.setShiftLocked(false); mKeyboardShiftState.setShiftLocked(false);
mShiftKeyState.onRelease(); mShiftKeyState.onRelease();
mSymbolKeyState.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() { public boolean isShiftLocked() {
return mKeyboardShiftState.isShiftLocked(); return mKeyboardShiftState.isShiftLocked();
} }
@ -73,40 +108,40 @@ public class KeyboardState {
mKeyboardShiftState.setAutomaticTemporaryUpperCase(); mKeyboardShiftState.setAutomaticTemporaryUpperCase();
} }
// TODO: Get rid of this method private void toggleAlphabetAndSymbols(boolean isAlphabetMode) {
public boolean isShiftKeyIgnoring() { if (isAlphabetMode) {
return mShiftKeyState.isIgnoring(); mSwitchActions.setSymbolsKeyboard();
} else {
mSwitchActions.setAlphabetKeyboard();
}
} }
// TODO: Get rid of this method private void toggleShiftInSymbols(boolean isSymbolShifted) {
public boolean isShiftKeyReleasing() { if (isSymbolShifted) {
return mShiftKeyState.isReleasing(); mSwitchActions.setSymbolsKeyboard();
} } else {
mSwitchActions.setSymbolsShiftedKeyboard();
// 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();
} }
public void onReleaseCapsLock() { public void onReleaseCapsLock() {
mShiftKeyState.onRelease(); mShiftKeyState.onRelease();
} }
public void onPressSymbol() { // TODO: Get rid of isAlphabetMode argument.
public void onPressSymbol(boolean isAlphabetMode) {
toggleAlphabetAndSymbols(isAlphabetMode);
mSymbolKeyState.onPress(); 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(); mSymbolKeyState.onRelease();
} }
@ -115,48 +150,200 @@ public class KeyboardState {
mSymbolKeyState.onOtherKeyPressed(); mSymbolKeyState.onOtherKeyPressed();
} }
public void onUpdateShiftState(boolean isAlphabetMode) { // TODO: Get rid of isAlphabetMode argument.
if (!isAlphabetMode) { 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 // In symbol keyboard mode, we should clear shift key state because only alphabet
// keyboard has shift key. // keyboard has shift key.
mSymbolKeyState.onRelease(); mSymbolKeyState.onRelease();
} }
} }
// TODO: Get rid of these boolean arguments. // TODO: Get rid of isAlphabetMode and isSymbolShifted arguments.
public void onPressShift(boolean isAlphabetMode, boolean isShiftLocked, public void onPressShift(boolean isAlphabetMode, boolean isSymbolShifted) {
boolean isAutomaticTemporaryUpperCase, boolean isShiftedOrShiftLocked) {
if (isAlphabetMode) { if (isAlphabetMode) {
if (isShiftLocked) { if (isShiftLocked()) {
// Shift key is pressed while caps lock state, we will treat this state as shifted // 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. // caps lock state and mark as if shift key pressed while normal state.
mSwitchActions.setShifted(SwitchActions.MANUAL_SHIFT);
mShiftKeyState.onPress(); mShiftKeyState.onPress();
} else if (isAutomaticTemporaryUpperCase) { } else if (isAutomaticTemporaryUpperCase()) {
// Shift key is pressed while automatic temporary upper case, we have to move to // Shift key is pressed while automatic temporary upper case, we have to move to
// manual temporary upper case. // manual temporary upper case.
mSwitchActions.setShifted(SwitchActions.MANUAL_SHIFT);
mShiftKeyState.onPress(); mShiftKeyState.onPress();
} else if (isShiftedOrShiftLocked) { } else if (isShiftedOrShiftLocked()) {
// In manual upper case state, we just record shift key has been pressing while // In manual upper case state, we just record shift key has been pressing while
// shifted state. // shifted state.
mShiftKeyState.onPressOnShifted(); mShiftKeyState.onPressOnShifted();
} else { } else {
// In base layout, chording or manual temporary upper case mode is started. // In base layout, chording or manual temporary upper case mode is started.
mSwitchActions.setShifted(SwitchActions.MANUAL_SHIFT);
mShiftKeyState.onPress(); mShiftKeyState.onPress();
} }
} else { } else {
// In symbol mode, just toggle symbol and symbol more keyboard. // In symbol mode, just toggle symbol and symbol more keyboard.
toggleShiftInSymbols(isSymbolShifted);
mSwitchState = SWITCH_STATE_MOMENTARY_SYMBOL_AND_MORE;
mShiftKeyState.onPress(); 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(); 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 @Override
public String toString() { public String toString() {
return "[keyboard=" + mKeyboardShiftState return "[keyboard=" + mKeyboardShiftState
+ " shift=" + mShiftKeyState + " shift=" + mShiftKeyState
+ " symbol=" + mSymbolKeyState + "]"; + " symbol=" + mSymbolKeyState
+ " switch=" + switchStateToString(mSwitchState) + "]";
} }
} }

View file

@ -1374,7 +1374,7 @@ public class LatinIME extends InputMethodServiceCompatWrapper implements Keyboar
mExpectingUpdateSelection = true; mExpectingUpdateSelection = true;
break; break;
} }
switcher.onKey(primaryCode); switcher.onCodeInput(primaryCode);
// Reset after any single keystroke // Reset after any single keystroke
mEnteredText = null; mEnteredText = null;
} }
@ -1390,7 +1390,7 @@ public class LatinIME extends InputMethodServiceCompatWrapper implements Keyboar
ic.commitText(text, 1); ic.commitText(text, 1);
ic.endBatchEdit(); ic.endBatchEdit();
mKeyboardSwitcher.updateShiftState(); mKeyboardSwitcher.updateShiftState();
mKeyboardSwitcher.onKey(Keyboard.CODE_DUMMY); mKeyboardSwitcher.onCodeInput(Keyboard.CODE_DUMMY);
mSpaceState = SPACE_STATE_NONE; mSpaceState = SPACE_STATE_NONE;
mEnteredText = text; mEnteredText = text;
} }