am 7aedc8a0: Automaticaly snap back to the previous mode from sliding input (DO NOT MERGE)

* commit '7aedc8a054aca41e6c80d8faf0ed2dd47f9815c4':
  Automaticaly snap back to the previous mode from sliding input (DO NOT MERGE)
This commit is contained in:
Tadashi G. Takaoka 2011-01-05 23:00:54 -08:00 committed by Android Git Automerger
commit 0b157cc66f
4 changed files with 213 additions and 68 deletions

View file

@ -82,10 +82,6 @@ public class KeyboardSwitcher implements SharedPreferences.OnSharedPreferenceCha
R.xml.kbd_symbols_shift, R.xml.kbd_symbols_shift_black}; R.xml.kbd_symbols_shift, R.xml.kbd_symbols_shift_black};
private static final int[] KBD_QWERTY = new int[] {R.xml.kbd_qwerty, R.xml.kbd_qwerty_black}; private static final int[] KBD_QWERTY = new int[] {R.xml.kbd_qwerty, R.xml.kbd_qwerty_black};
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 LatinKeyboardView mInputView; private LatinKeyboardView mInputView;
private static final int[] ALPHABET_MODES = { private static final int[] ALPHABET_MODES = {
KEYBOARDMODE_NORMAL, KEYBOARDMODE_NORMAL,
@ -99,13 +95,14 @@ public class KeyboardSwitcher implements SharedPreferences.OnSharedPreferenceCha
KEYBOARDMODE_IM_WITH_SETTINGS_KEY, KEYBOARDMODE_IM_WITH_SETTINGS_KEY,
KEYBOARDMODE_WEB_WITH_SETTINGS_KEY }; KEYBOARDMODE_WEB_WITH_SETTINGS_KEY };
private final LatinIME mInputMethodService; private LatinIME mInputMethodService;
private KeyboardId mSymbolsId; private KeyboardId mSymbolsId;
private KeyboardId mSymbolsShiftedId; private KeyboardId mSymbolsShiftedId;
private KeyboardId mCurrentId; private KeyboardId mCurrentId;
private final HashMap<KeyboardId, SoftReference<LatinKeyboard>> mKeyboards; private final HashMap<KeyboardId, SoftReference<LatinKeyboard>> mKeyboards =
new HashMap<KeyboardId, SoftReference<LatinKeyboard>>();
private int mMode = MODE_NONE; /** One of the MODE_XXX values */ private int mMode = MODE_NONE; /** One of the MODE_XXX values */
private int mImeOptions; private int mImeOptions;
@ -116,7 +113,14 @@ public class KeyboardSwitcher implements SharedPreferences.OnSharedPreferenceCha
private boolean mHasVoice; private boolean mHasVoice;
private boolean mVoiceOnPrimary; private boolean mVoiceOnPrimary;
private boolean mPreferSymbols; private boolean mPreferSymbols;
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;
@ -133,17 +137,27 @@ public class KeyboardSwitcher implements SharedPreferences.OnSharedPreferenceCha
private int mLayoutId; private int mLayoutId;
public KeyboardSwitcher(LatinIME ims) { private static final KeyboardSwitcher sInstance = new KeyboardSwitcher();
mInputMethodService = ims;
public static KeyboardSwitcher getInstance() {
return sInstance;
}
private KeyboardSwitcher() {
// Intentional empty constructor for singleton.
}
public static void init(LatinIME ims) {
sInstance.mInputMethodService = ims;
final SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(ims); final SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(ims);
mLayoutId = Integer.valueOf(prefs.getString(PREF_KEYBOARD_LAYOUT, DEFAULT_LAYOUT_ID)); sInstance.mLayoutId = Integer.valueOf(
updateSettingsKeyState(prefs); prefs.getString(PREF_KEYBOARD_LAYOUT, DEFAULT_LAYOUT_ID));
prefs.registerOnSharedPreferenceChangeListener(this); sInstance.updateSettingsKeyState(prefs);
prefs.registerOnSharedPreferenceChangeListener(sInstance);
mKeyboards = new HashMap<KeyboardId, SoftReference<LatinKeyboard>>(); sInstance.mSymbolsId = sInstance.makeSymbolsId(false);
mSymbolsId = makeSymbolsId(false); sInstance.mSymbolsShiftedId = sInstance.makeSymbolsShiftedId(false);
mSymbolsShiftedId = makeSymbolsShiftedId(false);
} }
/** /**
@ -243,7 +257,7 @@ public class KeyboardSwitcher implements SharedPreferences.OnSharedPreferenceCha
} }
public void setKeyboardMode(int mode, int imeOptions, boolean enableVoice) { public void setKeyboardMode(int mode, int imeOptions, boolean enableVoice) {
mSymbolsModeState = SYMBOLS_MODE_STATE_NONE; mAutoModeSwitchState = AUTO_MODE_SWITCH_STATE_ALPHA;
mPreferSymbols = mode == MODE_SYMBOLS; mPreferSymbols = mode == MODE_SYMBOLS;
if (mode == MODE_SYMBOLS) { if (mode == MODE_SYMBOLS) {
mode = MODE_TEXT; mode = MODE_TEXT;
@ -410,12 +424,18 @@ public class KeyboardSwitcher implements SharedPreferences.OnSharedPreferenceCha
} }
} }
public void onCancelInput() {
// Snap back to the previous keyboard mode if the user cancels sliding input.
if (mAutoModeSwitchState == AUTO_MODE_SWITCH_STATE_MOMENTARY && getPointerCount() == 1)
mInputMethodService.changeKeyboardMode();
}
public void toggleSymbols() { public void toggleSymbols() {
setKeyboardMode(mMode, mImeOptions, mHasVoice, !mIsSymbols); setKeyboardMode(mMode, mImeOptions, mHasVoice, !mIsSymbols);
if (mIsSymbols && !mPreferSymbols) { if (mIsSymbols && !mPreferSymbols) {
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;
} }
} }
@ -423,24 +443,72 @@ public class KeyboardSwitcher implements SharedPreferences.OnSharedPreferenceCha
return mInputView != null && mInputView.hasDistinctMultitouch(); return mInputView != null && mInputView.hasDistinctMultitouch();
} }
public void setAutoModeSwitchStateMomentary() {
mAutoModeSwitchState = AUTO_MODE_SWITCH_STATE_MOMENTARY;
}
public boolean isInMomentaryAutoModeSwitchState() {
return mAutoModeSwitchState == AUTO_MODE_SWITCH_STATE_MOMENTARY;
}
public boolean isInChordingAutoModeSwitchState() {
return mAutoModeSwitchState == AUTO_MODE_SWITCH_STATE_CHORDING;
}
public boolean isVibrateAndSoundFeedbackRequired() {
return mInputView != null && !mInputView.isInSlidingKeyInput();
}
private int getPointerCount() {
return mInputView == null ? 0 : mInputView.getPointerCount();
}
/** /**
* 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.
* Returns true if the keyboard needs to switch back
*/ */
public boolean onKey(int key) { public void onKey(int key) {
// Switch back to alpha mode if user types one or more non-space/enter characters // Switch back to alpha mode if user types one or more non-space/enter characters
// followed by a space/enter // followed by a space/enter
switch (mSymbolsModeState) { switch (mAutoModeSwitchState) {
case SYMBOLS_MODE_STATE_BEGIN: case AUTO_MODE_SWITCH_STATE_MOMENTARY:
if (key != LatinIME.KEYCODE_SPACE && key != LatinIME.KEYCODE_ENTER && key > 0) { // Only distinct multi touch devices can be in this state.
mSymbolsModeState = SYMBOLS_MODE_STATE_SYMBOL; // 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 == LatinKeyboard.KEYCODE_MODE_CHANGE) {
// 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;
} }
break; } else if (getPointerCount() == 1) {
case SYMBOLS_MODE_STATE_SYMBOL: // Snap back to the previous keyboard mode if the user pressed the mode change key
if (key == LatinIME.KEYCODE_ENTER || key == LatinIME.KEYCODE_SPACE) return true; // and slid to other key, then released the finger.
break; // If the user cancels the sliding input, snapping back to the previous keyboard
// mode is handled by {@link #onCancelInput}.
mInputMethodService.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;
case AUTO_MODE_SWITCH_STATE_SYMBOL_BEGIN:
if (key != LatinIME.KEYCODE_SPACE && key != LatinIME.KEYCODE_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 == LatinIME.KEYCODE_ENTER || key == LatinIME.KEYCODE_SPACE) {
mInputMethodService.changeKeyboardMode();
}
break;
} }
return false;
} }
public LatinKeyboardView getInputView() { public LatinKeyboardView getInputView() {

View file

@ -342,6 +342,7 @@ public class LatinIME extends InputMethodService
@Override @Override
public void onCreate() { public void onCreate() {
LatinImeLogger.init(this); LatinImeLogger.init(this);
KeyboardSwitcher.init(this);
super.onCreate(); super.onCreate();
//setStatusIcon(R.drawable.ime_qwerty); //setStatusIcon(R.drawable.ime_qwerty);
mResources = getResources(); mResources = getResources();
@ -349,7 +350,7 @@ public class LatinIME extends InputMethodService
final SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(this); final SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(this);
mLanguageSwitcher = new LanguageSwitcher(this); mLanguageSwitcher = new LanguageSwitcher(this);
mLanguageSwitcher.loadLocales(prefs); mLanguageSwitcher.loadLocales(prefs);
mKeyboardSwitcher = new KeyboardSwitcher(this); mKeyboardSwitcher = KeyboardSwitcher.getInstance();
mKeyboardSwitcher.setLanguageSwitcher(mLanguageSwitcher); mKeyboardSwitcher.setLanguageSwitcher(mLanguageSwitcher);
mSystemLocale = conf.locale.toString(); mSystemLocale = conf.locale.toString();
mLanguageSwitcher.setSystemLocale(conf.locale); mLanguageSwitcher.setSystemLocale(conf.locale);
@ -1247,9 +1248,7 @@ public class LatinIME extends InputMethodService
// Cancel the just reverted state // Cancel the just reverted state
mJustRevertedSeparator = null; mJustRevertedSeparator = null;
} }
if (mKeyboardSwitcher.onKey(primaryCode)) { mKeyboardSwitcher.onKey(primaryCode);
changeKeyboardMode();
}
// Reset after any single keystroke // Reset after any single keystroke
mEnteredText = null; mEnteredText = null;
} }
@ -1269,6 +1268,7 @@ public class LatinIME extends InputMethodService
ic.commitText(text, 1); ic.commitText(text, 1);
ic.endBatchEdit(); ic.endBatchEdit();
updateShiftKeyState(getCurrentInputEditorInfo()); updateShiftKeyState(getCurrentInputEditorInfo());
mKeyboardSwitcher.onKey(0); // dummy key code.
mJustRevertedSeparator = null; mJustRevertedSeparator = null;
mJustAddedAutoSpace = false; mJustAddedAutoSpace = false;
mEnteredText = text; mEnteredText = text;
@ -1276,6 +1276,7 @@ public class LatinIME extends InputMethodService
public void onCancel() { public void onCancel() {
// User released a finger outside any key // User released a finger outside any key
mKeyboardSwitcher.onCancelInput();
} }
private void handleBackspace() { private void handleBackspace() {
@ -2283,15 +2284,18 @@ public class LatinIME extends InputMethodService
} }
public void onPress(int primaryCode) { public void onPress(int primaryCode) {
vibrate(); if (mKeyboardSwitcher.isVibrateAndSoundFeedbackRequired()) {
playKeyClick(primaryCode); vibrate();
playKeyClick(primaryCode);
}
final boolean distinctMultiTouch = mKeyboardSwitcher.hasDistinctMultitouch(); final boolean distinctMultiTouch = mKeyboardSwitcher.hasDistinctMultitouch();
if (distinctMultiTouch && primaryCode == Keyboard.KEYCODE_SHIFT) { if (distinctMultiTouch && primaryCode == Keyboard.KEYCODE_SHIFT) {
mShiftKeyState.onPress(); mShiftKeyState.onPress();
handleShift(); handleShift();
} else if (distinctMultiTouch && primaryCode == Keyboard.KEYCODE_MODE_CHANGE) { } else if (distinctMultiTouch && primaryCode == Keyboard.KEYCODE_MODE_CHANGE) {
mSymbolKeyState.onPress();
changeKeyboardMode(); changeKeyboardMode();
mSymbolKeyState.onPress();
mKeyboardSwitcher.setAutoModeSwitchStateMomentary();
} else { } else {
mShiftKeyState.onOtherKeyPressed(); mShiftKeyState.onOtherKeyPressed();
mSymbolKeyState.onOtherKeyPressed(); mSymbolKeyState.onOtherKeyPressed();
@ -2308,7 +2312,9 @@ public class LatinIME extends InputMethodService
resetShift(); resetShift();
mShiftKeyState.onRelease(); mShiftKeyState.onRelease();
} else if (distinctMultiTouch && primaryCode == Keyboard.KEYCODE_MODE_CHANGE) { } else if (distinctMultiTouch && primaryCode == Keyboard.KEYCODE_MODE_CHANGE) {
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 (mKeyboardSwitcher.isInChordingAutoModeSwitchState())
changeKeyboardMode(); changeKeyboardMode();
mSymbolKeyState.onRelease(); mSymbolKeyState.onRelease();
} }
@ -2562,7 +2568,7 @@ public class LatinIME extends InputMethodService
mOptionsDialog.show(); mOptionsDialog.show();
} }
private void changeKeyboardMode() { public void changeKeyboardMode() {
mKeyboardSwitcher.toggleSymbols(); mKeyboardSwitcher.toggleSymbols();
if (mCapsLock && mKeyboardSwitcher.isAlphabetMode()) { if (mCapsLock && mKeyboardSwitcher.isAlphabetMode()) {
mKeyboardSwitcher.setShiftLocked(mCapsLock); mKeyboardSwitcher.setShiftLocked(mCapsLock);

View file

@ -343,7 +343,7 @@ public class LatinKeyboardBaseView extends View implements PointerTracker.UIProx
cancelPopupPreview(); cancelPopupPreview();
cancelDismissPreview(); cancelDismissPreview();
} }
}; }
static class PointerQueue { static class PointerQueue {
private LinkedList<PointerTracker> mQueue = new LinkedList<PointerTracker>(); private LinkedList<PointerTracker> mQueue = new LinkedList<PointerTracker>();
@ -391,6 +391,14 @@ public class LatinKeyboardBaseView extends View implements PointerTracker.UIProx
public void remove(PointerTracker tracker) { public void remove(PointerTracker tracker) {
mQueue.remove(tracker); mQueue.remove(tracker);
} }
public boolean isInSlidingKeyInput() {
for (final PointerTracker tracker : mQueue) {
if (tracker.isInSlidingKeyInput())
return true;
}
return false;
}
} }
public LatinKeyboardBaseView(Context context, AttributeSet attrs) { public LatinKeyboardBaseView(Context context, AttributeSet attrs) {
@ -1086,6 +1094,7 @@ public class LatinKeyboardBaseView extends View implements PointerTracker.UIProx
} }
public void onCancel() { public void onCancel() {
mKeyboardActionListener.onCancel();
dismissPopupKeyboard(); dismissPopupKeyboard();
} }
@ -1294,15 +1303,29 @@ public class LatinKeyboardBaseView extends View implements PointerTracker.UIProx
return pointers.get(id); return pointers.get(id);
} }
public boolean isInSlidingKeyInput() {
if (mMiniKeyboard != null) {
return mMiniKeyboard.isInSlidingKeyInput();
} else {
return mPointerQueue.isInSlidingKeyInput();
}
}
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;
} }
@ -1358,7 +1381,6 @@ public class LatinKeyboardBaseView extends View implements PointerTracker.UIProx
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.
@ -1373,7 +1395,6 @@ public class LatinKeyboardBaseView extends View implements PointerTracker.UIProx
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

@ -51,6 +51,7 @@ public class PointerTracker {
private final UIHandler mHandler; private final UIHandler mHandler;
private final KeyDetector mKeyDetector; private final KeyDetector mKeyDetector;
private OnKeyboardActionListener mListener; private OnKeyboardActionListener mListener;
private final KeyboardSwitcher mKeyboardSwitcher;
private final boolean mHasDistinctMultitouch; private final boolean mHasDistinctMultitouch;
private Key[] mKeys; private Key[] mKeys;
@ -58,12 +59,18 @@ public class PointerTracker {
private final KeyState mKeyState; private final KeyState mKeyState;
// true if keyboard layout has been changed.
private boolean mKeyboardLayoutHasBeenChanged;
// true if event is already translated to a key action (long press or mini-keyboard) // true if event is already translated to a key action (long press or mini-keyboard)
private boolean mKeyAlreadyProcessed; private boolean mKeyAlreadyProcessed;
// true if this pointer is repeatable key // true if this pointer is repeatable key
private boolean mIsRepeatableKey; private boolean mIsRepeatableKey;
// true if this pointer is in sliding key input
private boolean mIsInSlidingKeyInput;
// For multi-tap // For multi-tap
private int mLastSentIndex; private int mLastSentIndex;
private int mTapCount; private int mTapCount;
@ -157,10 +164,6 @@ public class PointerTracker {
public int onUpKey(int x, int y) { public int onUpKey(int x, int y) {
return onMoveKeyInternal(x, y); return onMoveKeyInternal(x, y);
} }
public void onSetKeyboard() {
mKeyIndex = mKeyDetector.getKeyIndexAndNearbyCodes(mKeyX, mKeyY, null);
}
} }
public PointerTracker(int id, UIHandler handler, KeyDetector keyDetector, UIProxy proxy, public PointerTracker(int id, UIHandler handler, KeyDetector keyDetector, UIProxy proxy,
@ -171,6 +174,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();
mDelayBeforeKeyRepeatStart = res.getInteger(R.integer.config_delay_before_key_repeat_start); mDelayBeforeKeyRepeatStart = res.getInteger(R.integer.config_delay_before_key_repeat_start);
@ -188,8 +192,12 @@ public class PointerTracker {
throw new IllegalArgumentException(); throw new IllegalArgumentException();
mKeys = keys; mKeys = keys;
mKeyHysteresisDistanceSquared = (int)(keyHysteresisDistance * keyHysteresisDistance); mKeyHysteresisDistanceSquared = (int)(keyHysteresisDistance * keyHysteresisDistance);
// Update current key index because keyboard layout has been changed. // Mark that keyboard layout has been changed.
mKeyState.onSetKeyboard(); mKeyboardLayoutHasBeenChanged = true;
}
public boolean isInSlidingKeyInput() {
return mIsInSlidingKeyInput;
} }
private boolean isValidKeyIndex(int keyIndex) { private boolean isValidKeyIndex(int keyIndex) {
@ -268,15 +276,21 @@ public class PointerTracker {
if (DEBUG) if (DEBUG)
debugLog("onDownEvent:", x, y); debugLog("onDownEvent:", x, y);
int keyIndex = mKeyState.onDownKey(x, y, eventTime); int keyIndex = mKeyState.onDownKey(x, y, eventTime);
mKeyboardLayoutHasBeenChanged = false;
mKeyAlreadyProcessed = false; mKeyAlreadyProcessed = false;
mIsRepeatableKey = false; mIsRepeatableKey = false;
mIsInSlidingKeyInput = false;
checkMultiTap(eventTime, keyIndex); checkMultiTap(eventTime, keyIndex);
if (mListener != null) { if (mListener != null) {
if (isValidKeyIndex(keyIndex)) { if (isValidKeyIndex(keyIndex)) {
mListener.onPress(mKeys[keyIndex].codes[0]); mListener.onPress(mKeys[keyIndex].codes[0]);
// This onPress call may have changed keyboard layout and have updated mKeyIndex. // This onPress call may have changed keyboard layout. Those cases are detected at
// If that's the case, mKeyIndex has been updated in setKeyboard(). // {@link #setKeyboard}. In those cases, we should update keyIndex according to the
keyIndex = mKeyState.getKeyIndex(); // new keyboard layout.
if (mKeyboardLayoutHasBeenChanged) {
mKeyboardLayoutHasBeenChanged = false;
keyIndex = mKeyState.onDownKey(x, y, eventTime);
}
} }
} }
if (isValidKeyIndex(keyIndex)) { if (isValidKeyIndex(keyIndex)) {
@ -285,7 +299,7 @@ public class PointerTracker {
mHandler.startKeyRepeatTimer(mDelayBeforeKeyRepeatStart, keyIndex, this); mHandler.startKeyRepeatTimer(mDelayBeforeKeyRepeatStart, keyIndex, this);
mIsRepeatableKey = true; mIsRepeatableKey = true;
} }
mHandler.startLongPressTimer(mLongPressKeyTimeout, keyIndex, this); startLongPressTimer(keyIndex);
} }
showKeyPreviewAndUpdateKey(keyIndex); showKeyPreviewAndUpdateKey(keyIndex);
} }
@ -295,42 +309,70 @@ public class PointerTracker {
debugLog("onMoveEvent:", x, y); debugLog("onMoveEvent:", x, y);
if (mKeyAlreadyProcessed) if (mKeyAlreadyProcessed)
return; return;
KeyState keyState = mKeyState; final KeyState keyState = mKeyState;
final int keyIndex = keyState.onMoveKey(x, y); int keyIndex = keyState.onMoveKey(x, y);
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).codes[0]);
// This onPress call may have changed keyboard layout. Those cases are detected
// at {@link #setKeyboard}. In those cases, we should update keyIndex according
// to the new keyboard layout.
if (mKeyboardLayoutHasBeenChanged) {
mKeyboardLayoutHasBeenChanged = false;
keyIndex = keyState.onMoveKey(x, y);
}
}
keyState.onMoveToNewKey(keyIndex, x, y); keyState.onMoveToNewKey(keyIndex, x, y);
mHandler.startLongPressTimer(mLongPressKeyTimeout, keyIndex, this); 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.
mIsInSlidingKeyInput = true;
if (mListener != null) if (mListener != null)
mListener.onRelease(oldKey.codes[0]); mListener.onRelease(oldKey.codes[0]);
resetMultiTap(); resetMultiTap();
if (mListener != null) {
mListener.onPress(getKey(keyIndex).codes[0]);
// This onPress call may have changed keyboard layout. Those cases are detected
// at {@link #setKeyboard}. In those cases, we should update keyIndex according
// to the new keyboard layout.
if (mKeyboardLayoutHasBeenChanged) {
mKeyboardLayoutHasBeenChanged = false;
keyIndex = keyState.onMoveKey(x, y);
}
}
keyState.onMoveToNewKey(keyIndex, x, y); keyState.onMoveToNewKey(keyIndex, x, y);
mHandler.startLongPressTimer(mLongPressKeyTimeout, keyIndex, this); startLongPressTimer(keyIndex);
} }
} else { } else {
if (oldKey != null) { if (oldKey != null && !isMinorMoveBounce(x, y, keyIndex)) {
// The pointer has been slid out from the previous key, we must call onRelease() to
// notify that the previous key has been released.
mIsInSlidingKeyInput = true;
if (mListener != null) if (mListener != null)
mListener.onRelease(oldKey.codes[0]); mListener.onRelease(oldKey.codes[0]);
keyState.onMoveToNewKey(keyIndex, x ,y);
mHandler.cancelLongPressTimer();
} else if (!isMinorMoveBounce(x, y, keyIndex)) {
resetMultiTap(); resetMultiTap();
keyState.onMoveToNewKey(keyIndex, x ,y); keyState.onMoveToNewKey(keyIndex, x ,y);
mHandler.cancelLongPressTimer(); mHandler.cancelLongPressTimer();
} }
} }
showKeyPreviewAndUpdateKey(mKeyState.getKeyIndex()); showKeyPreviewAndUpdateKey(keyState.getKeyIndex());
} }
public void onUpEvent(int x, int y, long eventTime) { public void onUpEvent(int x, int y, long eventTime) {
if (DEBUG) if (DEBUG)
debugLog("onUpEvent :", x, y); debugLog("onUpEvent :", x, y);
if (mKeyAlreadyProcessed)
return;
mHandler.cancelKeyTimers(); mHandler.cancelKeyTimers();
mHandler.cancelPopupPreview(); mHandler.cancelPopupPreview();
showKeyPreviewAndUpdateKey(NOT_A_KEY);
mIsInSlidingKeyInput = false;
if (mKeyAlreadyProcessed)
return;
int keyIndex = mKeyState.onUpKey(x, y); int keyIndex = mKeyState.onUpKey(x, y);
if (isMinorMoveBounce(x, y, keyIndex)) { if (isMinorMoveBounce(x, y, keyIndex)) {
// Use previous fixed key index and coordinates. // Use previous fixed key index and coordinates.
@ -338,7 +380,6 @@ public class PointerTracker {
x = mKeyState.getKeyX(); x = mKeyState.getKeyX();
y = mKeyState.getKeyY(); y = mKeyState.getKeyY();
} }
showKeyPreviewAndUpdateKey(NOT_A_KEY);
if (!mIsRepeatableKey) { if (!mIsRepeatableKey) {
detectAndSendKey(keyIndex, x, y, eventTime); detectAndSendKey(keyIndex, x, y, eventTime);
} }
@ -353,6 +394,7 @@ public class PointerTracker {
mHandler.cancelKeyTimers(); mHandler.cancelKeyTimers();
mHandler.cancelPopupPreview(); mHandler.cancelPopupPreview();
showKeyPreviewAndUpdateKey(NOT_A_KEY); showKeyPreviewAndUpdateKey(NOT_A_KEY);
mIsInSlidingKeyInput = false;
int keyIndex = mKeyState.getKeyIndex(); int keyIndex = mKeyState.getKeyIndex();
if (isValidKeyIndex(keyIndex)) if (isValidKeyIndex(keyIndex))
mProxy.invalidateKey(mKeys[keyIndex]); mProxy.invalidateKey(mKeys[keyIndex]);
@ -425,6 +467,15 @@ public class PointerTracker {
} }
} }
private void startLongPressTimer(int keyIndex) {
if (mKeyboardSwitcher.isInMomentaryAutoModeSwitchState()) {
// We use longer timeout for sliding finger input started from the symbols mode key.
mHandler.startLongPressTimer(mLongPressKeyTimeout * 3, keyIndex, this);
} else {
mHandler.startLongPressTimer(mLongPressKeyTimeout, keyIndex, this);
}
}
private void detectAndSendKey(int index, int x, int y, long eventTime) { private void detectAndSendKey(int index, int x, int y, long eventTime) {
final OnKeyboardActionListener listener = mListener; final OnKeyboardActionListener listener = mListener;
final Key key = getKey(index); final Key key = getKey(index);
@ -436,11 +487,10 @@ public class PointerTracker {
if (key.text != null) { if (key.text != null) {
if (listener != null) { if (listener != null) {
listener.onText(key.text); listener.onText(key.text);
listener.onRelease(NOT_A_KEY); listener.onRelease(0); // dummy key code
} }
} else { } else {
int code = key.codes[0]; int code = key.codes[0];
//TextEntryState.keyPressedAt(key, x, y);
int[] codes = mKeyDetector.newCodeArray(); int[] codes = mKeyDetector.newCodeArray();
mKeyDetector.getKeyIndexAndNearbyCodes(x, y, codes); mKeyDetector.getKeyIndexAndNearbyCodes(x, y, codes);
// Multi-tap // Multi-tap