Automaticaly snap back to the previous mode from sliding symbol input

Bug: 3280151

Change-Id: I48ea134639465d0cc178e524af8d7885d185957d
main
Tadashi G. Takaoka 2010-12-17 16:56:15 +09:00
parent 67a4ecacc7
commit 9e91472285
5 changed files with 94 additions and 25 deletions

View File

@ -89,6 +89,10 @@ public class KeyboardId {
return mXmlId == R.xml.kbd_qwerty; return mXmlId == R.xml.kbd_qwerty;
} }
public boolean isSymbolsKeyboard() {
return mXmlId == R.xml.kbd_symbols;
}
public boolean isPhoneKeyboard() { public boolean isPhoneKeyboard() {
return mMode == MODE_PHONE; return mMode == MODE_PHONE;
} }

View File

@ -51,10 +51,6 @@ public class KeyboardSwitcher implements SharedPreferences.OnSharedPreferenceCha
R.layout.input_honeycomb, // DEFAULT_LAYOUT_ID R.layout.input_honeycomb, // DEFAULT_LAYOUT_ID
}; };
private static final int SYMBOLS_MODE_STATE_NONE = 0;
private static final int SYMBOLS_MODE_STATE_BEGIN = 1;
private static final int SYMBOLS_MODE_STATE_SYMBOL = 2;
private SubtypeSwitcher mSubtypeSwitcher; private SubtypeSwitcher mSubtypeSwitcher;
private SharedPreferences mPrefs; private SharedPreferences mPrefs;
@ -79,7 +75,14 @@ public class KeyboardSwitcher implements SharedPreferences.OnSharedPreferenceCha
private boolean mIsAutoCorrectionActive; private boolean mIsAutoCorrectionActive;
private boolean mVoiceKeyEnabled; private boolean mVoiceKeyEnabled;
private boolean mVoiceButtonOnPrimary; private boolean mVoiceButtonOnPrimary;
private int mSymbolsModeState = SYMBOLS_MODE_STATE_NONE;
private static final int AUTO_MODE_SWITCH_STATE_ALPHA = 0;
private static final int AUTO_MODE_SWITCH_STATE_SYMBOL_BEGIN = 1;
private static final int AUTO_MODE_SWITCH_STATE_SYMBOL = 2;
// The following states are used only on the distinct multi-touch panel devices.
private static final int AUTO_MODE_SWITCH_STATE_MOMENTARY = 3;
private static final int AUTO_MODE_SWITCH_STATE_CHORDING = 4;
private int mAutoModeSwitchState = AUTO_MODE_SWITCH_STATE_ALPHA;
// Indicates whether or not we have the settings key // Indicates whether or not we have the settings key
private boolean mHasSettingsKey; private boolean mHasSettingsKey;
@ -146,10 +149,9 @@ public class KeyboardSwitcher implements SharedPreferences.OnSharedPreferenceCha
public void loadKeyboard(int mode, int imeOptions, boolean voiceKeyEnabled, public void loadKeyboard(int mode, int imeOptions, boolean voiceKeyEnabled,
boolean voiceButtonOnPrimary) { boolean voiceButtonOnPrimary) {
mSymbolsModeState = SYMBOLS_MODE_STATE_NONE; mAutoModeSwitchState = AUTO_MODE_SWITCH_STATE_ALPHA;
try { try {
loadKeyboardInternal(mode, imeOptions, voiceKeyEnabled, voiceButtonOnPrimary, loadKeyboardInternal(mode, imeOptions, voiceKeyEnabled, voiceButtonOnPrimary, false);
false);
} catch (RuntimeException e) { } catch (RuntimeException e) {
Log.w(TAG, e); Log.w(TAG, e);
LatinImeLogger.logOnException(mode + "," + imeOptions, e); LatinImeLogger.logOnException(mode + "," + imeOptions, e);
@ -467,19 +469,22 @@ public class KeyboardSwitcher implements SharedPreferences.OnSharedPreferenceCha
public void onPressSymbol() { public void onPressSymbol() {
if (DEBUG_STATE) if (DEBUG_STATE)
Log.d(TAG, "onReleaseShift:" Log.d(TAG, "onPressSymbol:"
+ " keyboard=" + getLatinKeyboard().getKeyboardShiftState() + " keyboard=" + getLatinKeyboard().getKeyboardShiftState()
+ " symbolKeyState=" + mSymbolKeyState); + " symbolKeyState=" + mSymbolKeyState);
changeKeyboardMode(); changeKeyboardMode();
mSymbolKeyState.onPress(); mSymbolKeyState.onPress();
mAutoModeSwitchState = AUTO_MODE_SWITCH_STATE_MOMENTARY;
} }
public void onReleaseSymbol() { public void onReleaseSymbol() {
if (DEBUG_STATE) if (DEBUG_STATE)
Log.d(TAG, "onReleaseShift:" Log.d(TAG, "onReleaseSymbol:"
+ " keyboard=" + getLatinKeyboard().getKeyboardShiftState() + " keyboard=" + getLatinKeyboard().getKeyboardShiftState()
+ " symbolKeyState=" + mSymbolKeyState); + " symbolKeyState=" + mSymbolKeyState);
if (mSymbolKeyState.isMomentary()) // Snap back to the previous keyboard mode if the user chords the mode change key and
// other key, then released the mode change key.
if (mAutoModeSwitchState == AUTO_MODE_SWITCH_STATE_CHORDING)
changeKeyboardMode(); changeKeyboardMode();
mSymbolKeyState.onRelease(); mSymbolKeyState.onRelease();
} }
@ -516,13 +521,21 @@ public class KeyboardSwitcher implements SharedPreferences.OnSharedPreferenceCha
mInputView.setKeyboard(keyboard); mInputView.setKeyboard(keyboard);
} }
public boolean isInMomentaryAutoModeSwitchState() {
return mAutoModeSwitchState == AUTO_MODE_SWITCH_STATE_MOMENTARY;
}
private int getPointerCount() {
return mInputView == null ? 0 : mInputView.getPointerCount();
}
private void toggleKeyboardMode() { private void toggleKeyboardMode() {
loadKeyboardInternal(mMode, mImeOptions, mVoiceKeyEnabled, mVoiceButtonOnPrimary, loadKeyboardInternal(mMode, mImeOptions, mVoiceKeyEnabled, mVoiceButtonOnPrimary,
!mIsSymbols); !mIsSymbols);
if (mIsSymbols) { if (mIsSymbols) {
mSymbolsModeState = SYMBOLS_MODE_STATE_BEGIN; mAutoModeSwitchState = AUTO_MODE_SWITCH_STATE_SYMBOL_BEGIN;
} else { } else {
mSymbolsModeState = SYMBOLS_MODE_STATE_NONE; mAutoModeSwitchState = AUTO_MODE_SWITCH_STATE_ALPHA;
} }
} }
@ -531,18 +544,45 @@ public class KeyboardSwitcher implements SharedPreferences.OnSharedPreferenceCha
} }
/** /**
* Updates state machine to figure out when to automatically switch back to alpha mode. * Updates state machine to figure out when to automatically snap back to the previous mode.
*/ */
public void onKey(int key) { public void onKey(int key) {
// Switch back to alpha mode if user types one or more non-space/enter if (DEBUG_STATE)
// characters followed by a space/enter Log.d(TAG, "onKey: code=" + key + " autoModeSwitchState=" + mAutoModeSwitchState
switch (mSymbolsModeState) { + " pointers=" + getPointerCount());
case SYMBOLS_MODE_STATE_BEGIN: switch (mAutoModeSwitchState) {
if (key != Keyboard.CODE_SPACE && key != Keyboard.CODE_ENTER && key > 0) { case AUTO_MODE_SWITCH_STATE_MOMENTARY:
mSymbolsModeState = SYMBOLS_MODE_STATE_SYMBOL; // Only distinct multi touch devices can be in this state.
// On non-distinct multi touch devices, mode change key is handled by {@link onKey},
// not by {@link onPress} and {@link onRelease}. So, on such devices,
// {@link mAutoModeSwitchState} starts from {@link AUTO_MODE_SWITCH_STATE_SYMBOL_BEGIN},
// or {@link AUTO_MODE_SWITCH_STATE_ALPHA}, not from
// {@link AUTO_MODE_SWITCH_STATE_MOMENTARY}.
if (key == Keyboard.CODE_SWITCH_ALPHA_SYMBOL) {
// Detected only the mode change key has been pressed, and then released.
if (mIsSymbols) {
mAutoModeSwitchState = AUTO_MODE_SWITCH_STATE_SYMBOL_BEGIN;
} else {
mAutoModeSwitchState = AUTO_MODE_SWITCH_STATE_ALPHA;
}
} else if (getPointerCount() == 1) {
// Snap back to the previous keyboard mode if the user pressed the mode change key
// and slid to other key, then released the finger.
changeKeyboardMode();
} 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.
mAutoModeSwitchState = AUTO_MODE_SWITCH_STATE_CHORDING;
} }
break; break;
case SYMBOLS_MODE_STATE_SYMBOL: case AUTO_MODE_SWITCH_STATE_SYMBOL_BEGIN:
if (key != Keyboard.CODE_SPACE && key != Keyboard.CODE_ENTER && key > 0) {
mAutoModeSwitchState = AUTO_MODE_SWITCH_STATE_SYMBOL;
}
break;
case AUTO_MODE_SWITCH_STATE_SYMBOL:
// Snap back to alpha keyboard mode if user types one or more non-space/enter
// characters followed by a space/enter.
if (key == Keyboard.CODE_ENTER || key == Keyboard.CODE_SPACE) { if (key == Keyboard.CODE_ENTER || key == Keyboard.CODE_SPACE) {
changeKeyboardMode(); changeKeyboardMode();
} }

View File

@ -1308,15 +1308,21 @@ public class KeyboardView extends View implements PointerTracker.UIProxy {
return pointers.get(id); return pointers.get(id);
} }
public int getPointerCount() {
return mOldPointerCount;
}
@Override @Override
public boolean onTouchEvent(MotionEvent me) { public boolean onTouchEvent(MotionEvent me) {
final int pointerCount = me.getPointerCount();
final int action = me.getActionMasked(); final int action = me.getActionMasked();
final int pointerCount = me.getPointerCount();
final int oldPointerCount = mOldPointerCount;
mOldPointerCount = pointerCount;
// TODO: cleanup this code into a multi-touch to single-touch event converter class? // TODO: cleanup this code into a multi-touch to single-touch event converter class?
// If the device does not have distinct multi-touch support panel, ignore all multi-touch // If the device does not have distinct multi-touch support panel, ignore all multi-touch
// events except a transition from/to single-touch. // events except a transition from/to single-touch.
if (!mHasDistinctMultitouch && pointerCount > 1 && mOldPointerCount > 1) { if (!mHasDistinctMultitouch && pointerCount > 1 && oldPointerCount > 1) {
return true; return true;
} }
@ -1372,7 +1378,6 @@ public class KeyboardView extends View implements PointerTracker.UIProxy {
if (!mHasDistinctMultitouch) { if (!mHasDistinctMultitouch) {
// Use only main (id=0) pointer tracker. // Use only main (id=0) pointer tracker.
PointerTracker tracker = getPointerTracker(0); PointerTracker tracker = getPointerTracker(0);
int oldPointerCount = mOldPointerCount;
if (pointerCount == 1 && oldPointerCount == 2) { if (pointerCount == 1 && oldPointerCount == 2) {
// Multi-touch to single touch transition. // Multi-touch to single touch transition.
// Send a down event for the latest pointer. // Send a down event for the latest pointer.
@ -1387,7 +1392,6 @@ public class KeyboardView extends View implements PointerTracker.UIProxy {
Log.w(TAG, "Unknown touch panel behavior: pointer count is " + pointerCount Log.w(TAG, "Unknown touch panel behavior: pointer count is " + pointerCount
+ " (old " + oldPointerCount + ")"); + " (old " + oldPointerCount + ")");
} }
mOldPointerCount = pointerCount;
return true; return true;
} }

View File

@ -55,6 +55,10 @@ public class ModifierKeyState {
Log.d(TAG, mName + ".onOtherKeyPressed: " + toString(oldState) + " > " + this); Log.d(TAG, mName + ".onOtherKeyPressed: " + toString(oldState) + " > " + this);
} }
public boolean isPressing() {
return mState == PRESSING;
}
public boolean isReleasing() { public boolean isReleasing() {
return mState == RELEASING; return mState == RELEASING;
} }

View File

@ -50,6 +50,7 @@ public class PointerTracker {
private final UIHandler mHandler; private final UIHandler mHandler;
private final KeyDetector mKeyDetector; private final KeyDetector mKeyDetector;
private KeyboardActionListener mListener; private KeyboardActionListener mListener;
private final KeyboardSwitcher mKeyboardSwitcher;
private final boolean mHasDistinctMultitouch; private final boolean mHasDistinctMultitouch;
private final boolean mConfigSlidingKeyInputEnabled; private final boolean mConfigSlidingKeyInputEnabled;
@ -175,6 +176,7 @@ public class PointerTracker {
mProxy = proxy; mProxy = proxy;
mHandler = handler; mHandler = handler;
mKeyDetector = keyDetector; mKeyDetector = keyDetector;
mKeyboardSwitcher = KeyboardSwitcher.getInstance();
mKeyState = new KeyState(keyDetector); mKeyState = new KeyState(keyDetector);
mHasDistinctMultitouch = proxy.hasDistinctMultitouch(); mHasDistinctMultitouch = proxy.hasDistinctMultitouch();
mConfigSlidingKeyInputEnabled = res.getBoolean(R.bool.config_sliding_key_input_enabled); mConfigSlidingKeyInputEnabled = res.getBoolean(R.bool.config_sliding_key_input_enabled);
@ -318,13 +320,22 @@ public class PointerTracker {
final Key oldKey = getKey(keyState.getKeyIndex()); final Key oldKey = getKey(keyState.getKeyIndex());
if (isValidKeyIndex(keyIndex)) { if (isValidKeyIndex(keyIndex)) {
if (oldKey == null) { if (oldKey == null) {
// The pointer has been slid in to the new key, but the finger was not on any keys.
// In this case, we must call onPress() to notify that the new key is being pressed.
if (mListener != null)
mListener.onPress(getKey(keyIndex).mCodes[0]);
keyState.onMoveToNewKey(keyIndex, x, y); keyState.onMoveToNewKey(keyIndex, x, y);
startLongPressTimer(keyIndex); startLongPressTimer(keyIndex);
} else if (!isMinorMoveBounce(x, y, keyIndex)) { } else if (!isMinorMoveBounce(x, y, keyIndex)) {
// The pointer has been slid in to the new key from the previous key, we must call
// onRelease() first to notify that the previous key has been released, then call
// onPress() to notify that the new key is being pressed.
if (mListener != null) if (mListener != null)
mListener.onRelease(oldKey.mCodes[0]); mListener.onRelease(oldKey.mCodes[0]);
if (mIsAllowedSlidingKeyInput) { if (mIsAllowedSlidingKeyInput) {
resetMultiTap(); resetMultiTap();
if (mListener != null)
mListener.onPress(getKey(keyIndex).mCodes[0]);
keyState.onMoveToNewKey(keyIndex, x, y); keyState.onMoveToNewKey(keyIndex, x, y);
startLongPressTimer(keyIndex); startLongPressTimer(keyIndex);
} else { } else {
@ -334,7 +345,10 @@ public class PointerTracker {
} }
} }
} else { } else {
// TODO: we should check isMinorMoveDebounce() first.
if (oldKey != null) { if (oldKey != null) {
// The pointer has been slid out from the previous key, we must call onRelease() to
// notify that the previous key has been released.
if (mListener != null) if (mListener != null)
mListener.onRelease(oldKey.mCodes[0]); mListener.onRelease(oldKey.mCodes[0]);
if (mIsAllowedSlidingKeyInput) { if (mIsAllowedSlidingKeyInput) {
@ -449,6 +463,9 @@ public class PointerTracker {
Key key = getKey(keyIndex); Key key = getKey(keyIndex);
if (key.mCodes[0] == Keyboard.CODE_SHIFT) { if (key.mCodes[0] == Keyboard.CODE_SHIFT) {
mHandler.startLongPressShiftTimer(mLongPressShiftKeyTimeout, keyIndex, this); mHandler.startLongPressShiftTimer(mLongPressShiftKeyTimeout, keyIndex, this);
} else if (mKeyboardSwitcher.isInMomentaryAutoModeSwitchState()) {
// We use longer timeout for sliding finger input started from the symbols mode key.
mHandler.startLongPressTimer(mLongPressKeyTimeout * 2, keyIndex, this);
} else { } else {
mHandler.startLongPressTimer(mLongPressKeyTimeout, keyIndex, this); mHandler.startLongPressTimer(mLongPressKeyTimeout, keyIndex, this);
} }