Loading java/src/com/android/inputmethod/latin/KeyboardSwitcher.java +98 −30 Original line number Diff line number Diff line Loading @@ -82,10 +82,6 @@ public class KeyboardSwitcher implements SharedPreferences.OnSharedPreferenceCha 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 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 static final int[] ALPHABET_MODES = { KEYBOARDMODE_NORMAL, Loading @@ -99,13 +95,14 @@ public class KeyboardSwitcher implements SharedPreferences.OnSharedPreferenceCha KEYBOARDMODE_IM_WITH_SETTINGS_KEY, KEYBOARDMODE_WEB_WITH_SETTINGS_KEY }; private final LatinIME mInputMethodService; private LatinIME mInputMethodService; private KeyboardId mSymbolsId; private KeyboardId mSymbolsShiftedId; 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 mImeOptions; Loading @@ -116,7 +113,14 @@ public class KeyboardSwitcher implements SharedPreferences.OnSharedPreferenceCha private boolean mHasVoice; private boolean mVoiceOnPrimary; 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 private boolean mHasSettingsKey; Loading @@ -133,17 +137,27 @@ public class KeyboardSwitcher implements SharedPreferences.OnSharedPreferenceCha private int mLayoutId; public KeyboardSwitcher(LatinIME ims) { mInputMethodService = ims; private static final KeyboardSwitcher sInstance = new KeyboardSwitcher(); 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); mLayoutId = Integer.valueOf(prefs.getString(PREF_KEYBOARD_LAYOUT, DEFAULT_LAYOUT_ID)); updateSettingsKeyState(prefs); prefs.registerOnSharedPreferenceChangeListener(this); sInstance.mLayoutId = Integer.valueOf( prefs.getString(PREF_KEYBOARD_LAYOUT, DEFAULT_LAYOUT_ID)); sInstance.updateSettingsKeyState(prefs); prefs.registerOnSharedPreferenceChangeListener(sInstance); mKeyboards = new HashMap<KeyboardId, SoftReference<LatinKeyboard>>(); mSymbolsId = makeSymbolsId(false); mSymbolsShiftedId = makeSymbolsShiftedId(false); sInstance.mSymbolsId = sInstance.makeSymbolsId(false); sInstance.mSymbolsShiftedId = sInstance.makeSymbolsShiftedId(false); } /** Loading Loading @@ -243,7 +257,7 @@ public class KeyboardSwitcher implements SharedPreferences.OnSharedPreferenceCha } public void setKeyboardMode(int mode, int imeOptions, boolean enableVoice) { mSymbolsModeState = SYMBOLS_MODE_STATE_NONE; mAutoModeSwitchState = AUTO_MODE_SWITCH_STATE_ALPHA; mPreferSymbols = mode == MODE_SYMBOLS; if (mode == MODE_SYMBOLS) { mode = MODE_TEXT; Loading Loading @@ -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() { setKeyboardMode(mMode, mImeOptions, mHasVoice, !mIsSymbols); if (mIsSymbols && !mPreferSymbols) { mSymbolsModeState = SYMBOLS_MODE_STATE_BEGIN; mAutoModeSwitchState = AUTO_MODE_SWITCH_STATE_SYMBOL_BEGIN; } else { mSymbolsModeState = SYMBOLS_MODE_STATE_NONE; mAutoModeSwitchState = AUTO_MODE_SWITCH_STATE_ALPHA; } } Loading @@ -423,24 +443,72 @@ public class KeyboardSwitcher implements SharedPreferences.OnSharedPreferenceCha 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. * Returns true if the keyboard needs to switch back * Updates state machine to figure out when to automatically snap back to the previous mode. */ 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 // followed by a space/enter switch (mSymbolsModeState) { case SYMBOLS_MODE_STATE_BEGIN: if (key != LatinIME.KEYCODE_SPACE && key != LatinIME.KEYCODE_ENTER && key > 0) { mSymbolsModeState = SYMBOLS_MODE_STATE_SYMBOL; switch (mAutoModeSwitchState) { case AUTO_MODE_SWITCH_STATE_MOMENTARY: // 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 == 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; } } 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. // 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 SYMBOLS_MODE_STATE_SYMBOL: if (key == LatinIME.KEYCODE_ENTER || key == LatinIME.KEYCODE_SPACE) return true; 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() { Loading java/src/com/android/inputmethod/latin/LatinIME.java +15 −9 Original line number Diff line number Diff line Loading @@ -342,6 +342,7 @@ public class LatinIME extends InputMethodService @Override public void onCreate() { LatinImeLogger.init(this); KeyboardSwitcher.init(this); super.onCreate(); //setStatusIcon(R.drawable.ime_qwerty); mResources = getResources(); Loading @@ -349,7 +350,7 @@ public class LatinIME extends InputMethodService final SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(this); mLanguageSwitcher = new LanguageSwitcher(this); mLanguageSwitcher.loadLocales(prefs); mKeyboardSwitcher = new KeyboardSwitcher(this); mKeyboardSwitcher = KeyboardSwitcher.getInstance(); mKeyboardSwitcher.setLanguageSwitcher(mLanguageSwitcher); mSystemLocale = conf.locale.toString(); mLanguageSwitcher.setSystemLocale(conf.locale); Loading Loading @@ -1247,9 +1248,7 @@ public class LatinIME extends InputMethodService // Cancel the just reverted state mJustRevertedSeparator = null; } if (mKeyboardSwitcher.onKey(primaryCode)) { changeKeyboardMode(); } mKeyboardSwitcher.onKey(primaryCode); // Reset after any single keystroke mEnteredText = null; } Loading @@ -1269,6 +1268,7 @@ public class LatinIME extends InputMethodService ic.commitText(text, 1); ic.endBatchEdit(); updateShiftKeyState(getCurrentInputEditorInfo()); mKeyboardSwitcher.onKey(0); // dummy key code. mJustRevertedSeparator = null; mJustAddedAutoSpace = false; mEnteredText = text; Loading @@ -1276,6 +1276,7 @@ public class LatinIME extends InputMethodService public void onCancel() { // User released a finger outside any key mKeyboardSwitcher.onCancelInput(); } private void handleBackspace() { Loading Loading @@ -2283,15 +2284,18 @@ public class LatinIME extends InputMethodService } public void onPress(int primaryCode) { if (mKeyboardSwitcher.isVibrateAndSoundFeedbackRequired()) { vibrate(); playKeyClick(primaryCode); } final boolean distinctMultiTouch = mKeyboardSwitcher.hasDistinctMultitouch(); if (distinctMultiTouch && primaryCode == Keyboard.KEYCODE_SHIFT) { mShiftKeyState.onPress(); handleShift(); } else if (distinctMultiTouch && primaryCode == Keyboard.KEYCODE_MODE_CHANGE) { mSymbolKeyState.onPress(); changeKeyboardMode(); mSymbolKeyState.onPress(); mKeyboardSwitcher.setAutoModeSwitchStateMomentary(); } else { mShiftKeyState.onOtherKeyPressed(); mSymbolKeyState.onOtherKeyPressed(); Loading @@ -2308,7 +2312,9 @@ public class LatinIME extends InputMethodService resetShift(); mShiftKeyState.onRelease(); } 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(); mSymbolKeyState.onRelease(); } Loading Loading @@ -2562,7 +2568,7 @@ public class LatinIME extends InputMethodService mOptionsDialog.show(); } private void changeKeyboardMode() { public void changeKeyboardMode() { mKeyboardSwitcher.toggleSymbols(); if (mCapsLock && mKeyboardSwitcher.isAlphabetMode()) { mKeyboardSwitcher.setShiftLocked(mCapsLock); Loading java/src/com/android/inputmethod/latin/LatinKeyboardBaseView.java +26 −5 Original line number Diff line number Diff line Loading @@ -343,7 +343,7 @@ public class LatinKeyboardBaseView extends View implements PointerTracker.UIProx cancelPopupPreview(); cancelDismissPreview(); } }; } static class PointerQueue { private LinkedList<PointerTracker> mQueue = new LinkedList<PointerTracker>(); Loading Loading @@ -391,6 +391,14 @@ public class LatinKeyboardBaseView extends View implements PointerTracker.UIProx public void remove(PointerTracker 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) { Loading Loading @@ -1086,6 +1094,7 @@ public class LatinKeyboardBaseView extends View implements PointerTracker.UIProx } public void onCancel() { mKeyboardActionListener.onCancel(); dismissPopupKeyboard(); } Loading Loading @@ -1294,15 +1303,29 @@ public class LatinKeyboardBaseView extends View implements PointerTracker.UIProx return pointers.get(id); } public boolean isInSlidingKeyInput() { if (mMiniKeyboard != null) { return mMiniKeyboard.isInSlidingKeyInput(); } else { return mPointerQueue.isInSlidingKeyInput(); } } public int getPointerCount() { return mOldPointerCount; } @Override public boolean onTouchEvent(MotionEvent me) { final int pointerCount = me.getPointerCount(); 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? // If the device does not have distinct multi-touch support panel, ignore all multi-touch // events except a transition from/to single-touch. if (!mHasDistinctMultitouch && pointerCount > 1 && mOldPointerCount > 1) { if (!mHasDistinctMultitouch && pointerCount > 1 && oldPointerCount > 1) { return true; } Loading Loading @@ -1358,7 +1381,6 @@ public class LatinKeyboardBaseView extends View implements PointerTracker.UIProx if (!mHasDistinctMultitouch) { // Use only main (id=0) pointer tracker. PointerTracker tracker = getPointerTracker(0); int oldPointerCount = mOldPointerCount; if (pointerCount == 1 && oldPointerCount == 2) { // Multi-touch to single touch transition. // Send a down event for the latest pointer. Loading @@ -1373,7 +1395,6 @@ public class LatinKeyboardBaseView extends View implements PointerTracker.UIProx Log.w(TAG, "Unknown touch panel behavior: pointer count is " + pointerCount + " (old " + oldPointerCount + ")"); } mOldPointerCount = pointerCount; return true; } Loading java/src/com/android/inputmethod/latin/PointerTracker.java +74 −24 Original line number Diff line number Diff line Loading @@ -51,6 +51,7 @@ public class PointerTracker { private final UIHandler mHandler; private final KeyDetector mKeyDetector; private OnKeyboardActionListener mListener; private final KeyboardSwitcher mKeyboardSwitcher; private final boolean mHasDistinctMultitouch; private Key[] mKeys; Loading @@ -58,12 +59,18 @@ public class PointerTracker { 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) private boolean mKeyAlreadyProcessed; // true if this pointer is repeatable key private boolean mIsRepeatableKey; // true if this pointer is in sliding key input private boolean mIsInSlidingKeyInput; // For multi-tap private int mLastSentIndex; private int mTapCount; Loading Loading @@ -157,10 +164,6 @@ public class PointerTracker { public int onUpKey(int x, int y) { return onMoveKeyInternal(x, y); } public void onSetKeyboard() { mKeyIndex = mKeyDetector.getKeyIndexAndNearbyCodes(mKeyX, mKeyY, null); } } public PointerTracker(int id, UIHandler handler, KeyDetector keyDetector, UIProxy proxy, Loading @@ -171,6 +174,7 @@ public class PointerTracker { mProxy = proxy; mHandler = handler; mKeyDetector = keyDetector; mKeyboardSwitcher = KeyboardSwitcher.getInstance(); mKeyState = new KeyState(keyDetector); mHasDistinctMultitouch = proxy.hasDistinctMultitouch(); mDelayBeforeKeyRepeatStart = res.getInteger(R.integer.config_delay_before_key_repeat_start); Loading @@ -188,8 +192,12 @@ public class PointerTracker { throw new IllegalArgumentException(); mKeys = keys; mKeyHysteresisDistanceSquared = (int)(keyHysteresisDistance * keyHysteresisDistance); // Update current key index because keyboard layout has been changed. mKeyState.onSetKeyboard(); // Mark that keyboard layout has been changed. mKeyboardLayoutHasBeenChanged = true; } public boolean isInSlidingKeyInput() { return mIsInSlidingKeyInput; } private boolean isValidKeyIndex(int keyIndex) { Loading Loading @@ -268,15 +276,21 @@ public class PointerTracker { if (DEBUG) debugLog("onDownEvent:", x, y); int keyIndex = mKeyState.onDownKey(x, y, eventTime); mKeyboardLayoutHasBeenChanged = false; mKeyAlreadyProcessed = false; mIsRepeatableKey = false; mIsInSlidingKeyInput = false; checkMultiTap(eventTime, keyIndex); if (mListener != null) { if (isValidKeyIndex(keyIndex)) { mListener.onPress(mKeys[keyIndex].codes[0]); // This onPress call may have changed keyboard layout and have updated mKeyIndex. // If that's the case, mKeyIndex has been updated in setKeyboard(). keyIndex = mKeyState.getKeyIndex(); // 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 = mKeyState.onDownKey(x, y, eventTime); } } } if (isValidKeyIndex(keyIndex)) { Loading @@ -285,7 +299,7 @@ public class PointerTracker { mHandler.startKeyRepeatTimer(mDelayBeforeKeyRepeatStart, keyIndex, this); mIsRepeatableKey = true; } mHandler.startLongPressTimer(mLongPressKeyTimeout, keyIndex, this); startLongPressTimer(keyIndex); } showKeyPreviewAndUpdateKey(keyIndex); } Loading @@ -295,42 +309,70 @@ public class PointerTracker { debugLog("onMoveEvent:", x, y); if (mKeyAlreadyProcessed) return; KeyState keyState = mKeyState; final int keyIndex = keyState.onMoveKey(x, y); final KeyState keyState = mKeyState; int keyIndex = keyState.onMoveKey(x, y); final Key oldKey = getKey(keyState.getKeyIndex()); if (isValidKeyIndex(keyIndex)) { 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); mHandler.startLongPressTimer(mLongPressKeyTimeout, keyIndex, this); startLongPressTimer(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) mListener.onRelease(oldKey.codes[0]); 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); mHandler.startLongPressTimer(mLongPressKeyTimeout, keyIndex, this); startLongPressTimer(keyIndex); } } 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) mListener.onRelease(oldKey.codes[0]); keyState.onMoveToNewKey(keyIndex, x ,y); mHandler.cancelLongPressTimer(); } else if (!isMinorMoveBounce(x, y, keyIndex)) { resetMultiTap(); keyState.onMoveToNewKey(keyIndex, x ,y); mHandler.cancelLongPressTimer(); } } showKeyPreviewAndUpdateKey(mKeyState.getKeyIndex()); showKeyPreviewAndUpdateKey(keyState.getKeyIndex()); } public void onUpEvent(int x, int y, long eventTime) { if (DEBUG) debugLog("onUpEvent :", x, y); if (mKeyAlreadyProcessed) return; mHandler.cancelKeyTimers(); mHandler.cancelPopupPreview(); showKeyPreviewAndUpdateKey(NOT_A_KEY); mIsInSlidingKeyInput = false; if (mKeyAlreadyProcessed) return; int keyIndex = mKeyState.onUpKey(x, y); if (isMinorMoveBounce(x, y, keyIndex)) { // Use previous fixed key index and coordinates. Loading @@ -338,7 +380,6 @@ public class PointerTracker { x = mKeyState.getKeyX(); y = mKeyState.getKeyY(); } showKeyPreviewAndUpdateKey(NOT_A_KEY); if (!mIsRepeatableKey) { detectAndSendKey(keyIndex, x, y, eventTime); } Loading @@ -353,6 +394,7 @@ public class PointerTracker { mHandler.cancelKeyTimers(); mHandler.cancelPopupPreview(); showKeyPreviewAndUpdateKey(NOT_A_KEY); mIsInSlidingKeyInput = false; int keyIndex = mKeyState.getKeyIndex(); if (isValidKeyIndex(keyIndex)) mProxy.invalidateKey(mKeys[keyIndex]); Loading Loading @@ -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) { final OnKeyboardActionListener listener = mListener; final Key key = getKey(index); Loading @@ -436,11 +487,10 @@ public class PointerTracker { if (key.text != null) { if (listener != null) { listener.onText(key.text); listener.onRelease(NOT_A_KEY); listener.onRelease(0); // dummy key code } } else { int code = key.codes[0]; //TextEntryState.keyPressedAt(key, x, y); int[] codes = mKeyDetector.newCodeArray(); mKeyDetector.getKeyIndexAndNearbyCodes(x, y, codes); // Multi-tap Loading Loading
java/src/com/android/inputmethod/latin/KeyboardSwitcher.java +98 −30 Original line number Diff line number Diff line Loading @@ -82,10 +82,6 @@ public class KeyboardSwitcher implements SharedPreferences.OnSharedPreferenceCha 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 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 static final int[] ALPHABET_MODES = { KEYBOARDMODE_NORMAL, Loading @@ -99,13 +95,14 @@ public class KeyboardSwitcher implements SharedPreferences.OnSharedPreferenceCha KEYBOARDMODE_IM_WITH_SETTINGS_KEY, KEYBOARDMODE_WEB_WITH_SETTINGS_KEY }; private final LatinIME mInputMethodService; private LatinIME mInputMethodService; private KeyboardId mSymbolsId; private KeyboardId mSymbolsShiftedId; 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 mImeOptions; Loading @@ -116,7 +113,14 @@ public class KeyboardSwitcher implements SharedPreferences.OnSharedPreferenceCha private boolean mHasVoice; private boolean mVoiceOnPrimary; 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 private boolean mHasSettingsKey; Loading @@ -133,17 +137,27 @@ public class KeyboardSwitcher implements SharedPreferences.OnSharedPreferenceCha private int mLayoutId; public KeyboardSwitcher(LatinIME ims) { mInputMethodService = ims; private static final KeyboardSwitcher sInstance = new KeyboardSwitcher(); 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); mLayoutId = Integer.valueOf(prefs.getString(PREF_KEYBOARD_LAYOUT, DEFAULT_LAYOUT_ID)); updateSettingsKeyState(prefs); prefs.registerOnSharedPreferenceChangeListener(this); sInstance.mLayoutId = Integer.valueOf( prefs.getString(PREF_KEYBOARD_LAYOUT, DEFAULT_LAYOUT_ID)); sInstance.updateSettingsKeyState(prefs); prefs.registerOnSharedPreferenceChangeListener(sInstance); mKeyboards = new HashMap<KeyboardId, SoftReference<LatinKeyboard>>(); mSymbolsId = makeSymbolsId(false); mSymbolsShiftedId = makeSymbolsShiftedId(false); sInstance.mSymbolsId = sInstance.makeSymbolsId(false); sInstance.mSymbolsShiftedId = sInstance.makeSymbolsShiftedId(false); } /** Loading Loading @@ -243,7 +257,7 @@ public class KeyboardSwitcher implements SharedPreferences.OnSharedPreferenceCha } public void setKeyboardMode(int mode, int imeOptions, boolean enableVoice) { mSymbolsModeState = SYMBOLS_MODE_STATE_NONE; mAutoModeSwitchState = AUTO_MODE_SWITCH_STATE_ALPHA; mPreferSymbols = mode == MODE_SYMBOLS; if (mode == MODE_SYMBOLS) { mode = MODE_TEXT; Loading Loading @@ -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() { setKeyboardMode(mMode, mImeOptions, mHasVoice, !mIsSymbols); if (mIsSymbols && !mPreferSymbols) { mSymbolsModeState = SYMBOLS_MODE_STATE_BEGIN; mAutoModeSwitchState = AUTO_MODE_SWITCH_STATE_SYMBOL_BEGIN; } else { mSymbolsModeState = SYMBOLS_MODE_STATE_NONE; mAutoModeSwitchState = AUTO_MODE_SWITCH_STATE_ALPHA; } } Loading @@ -423,24 +443,72 @@ public class KeyboardSwitcher implements SharedPreferences.OnSharedPreferenceCha 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. * Returns true if the keyboard needs to switch back * Updates state machine to figure out when to automatically snap back to the previous mode. */ 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 // followed by a space/enter switch (mSymbolsModeState) { case SYMBOLS_MODE_STATE_BEGIN: if (key != LatinIME.KEYCODE_SPACE && key != LatinIME.KEYCODE_ENTER && key > 0) { mSymbolsModeState = SYMBOLS_MODE_STATE_SYMBOL; switch (mAutoModeSwitchState) { case AUTO_MODE_SWITCH_STATE_MOMENTARY: // 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 == 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; } } 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. // 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 SYMBOLS_MODE_STATE_SYMBOL: if (key == LatinIME.KEYCODE_ENTER || key == LatinIME.KEYCODE_SPACE) return true; 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() { Loading
java/src/com/android/inputmethod/latin/LatinIME.java +15 −9 Original line number Diff line number Diff line Loading @@ -342,6 +342,7 @@ public class LatinIME extends InputMethodService @Override public void onCreate() { LatinImeLogger.init(this); KeyboardSwitcher.init(this); super.onCreate(); //setStatusIcon(R.drawable.ime_qwerty); mResources = getResources(); Loading @@ -349,7 +350,7 @@ public class LatinIME extends InputMethodService final SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(this); mLanguageSwitcher = new LanguageSwitcher(this); mLanguageSwitcher.loadLocales(prefs); mKeyboardSwitcher = new KeyboardSwitcher(this); mKeyboardSwitcher = KeyboardSwitcher.getInstance(); mKeyboardSwitcher.setLanguageSwitcher(mLanguageSwitcher); mSystemLocale = conf.locale.toString(); mLanguageSwitcher.setSystemLocale(conf.locale); Loading Loading @@ -1247,9 +1248,7 @@ public class LatinIME extends InputMethodService // Cancel the just reverted state mJustRevertedSeparator = null; } if (mKeyboardSwitcher.onKey(primaryCode)) { changeKeyboardMode(); } mKeyboardSwitcher.onKey(primaryCode); // Reset after any single keystroke mEnteredText = null; } Loading @@ -1269,6 +1268,7 @@ public class LatinIME extends InputMethodService ic.commitText(text, 1); ic.endBatchEdit(); updateShiftKeyState(getCurrentInputEditorInfo()); mKeyboardSwitcher.onKey(0); // dummy key code. mJustRevertedSeparator = null; mJustAddedAutoSpace = false; mEnteredText = text; Loading @@ -1276,6 +1276,7 @@ public class LatinIME extends InputMethodService public void onCancel() { // User released a finger outside any key mKeyboardSwitcher.onCancelInput(); } private void handleBackspace() { Loading Loading @@ -2283,15 +2284,18 @@ public class LatinIME extends InputMethodService } public void onPress(int primaryCode) { if (mKeyboardSwitcher.isVibrateAndSoundFeedbackRequired()) { vibrate(); playKeyClick(primaryCode); } final boolean distinctMultiTouch = mKeyboardSwitcher.hasDistinctMultitouch(); if (distinctMultiTouch && primaryCode == Keyboard.KEYCODE_SHIFT) { mShiftKeyState.onPress(); handleShift(); } else if (distinctMultiTouch && primaryCode == Keyboard.KEYCODE_MODE_CHANGE) { mSymbolKeyState.onPress(); changeKeyboardMode(); mSymbolKeyState.onPress(); mKeyboardSwitcher.setAutoModeSwitchStateMomentary(); } else { mShiftKeyState.onOtherKeyPressed(); mSymbolKeyState.onOtherKeyPressed(); Loading @@ -2308,7 +2312,9 @@ public class LatinIME extends InputMethodService resetShift(); mShiftKeyState.onRelease(); } 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(); mSymbolKeyState.onRelease(); } Loading Loading @@ -2562,7 +2568,7 @@ public class LatinIME extends InputMethodService mOptionsDialog.show(); } private void changeKeyboardMode() { public void changeKeyboardMode() { mKeyboardSwitcher.toggleSymbols(); if (mCapsLock && mKeyboardSwitcher.isAlphabetMode()) { mKeyboardSwitcher.setShiftLocked(mCapsLock); Loading
java/src/com/android/inputmethod/latin/LatinKeyboardBaseView.java +26 −5 Original line number Diff line number Diff line Loading @@ -343,7 +343,7 @@ public class LatinKeyboardBaseView extends View implements PointerTracker.UIProx cancelPopupPreview(); cancelDismissPreview(); } }; } static class PointerQueue { private LinkedList<PointerTracker> mQueue = new LinkedList<PointerTracker>(); Loading Loading @@ -391,6 +391,14 @@ public class LatinKeyboardBaseView extends View implements PointerTracker.UIProx public void remove(PointerTracker 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) { Loading Loading @@ -1086,6 +1094,7 @@ public class LatinKeyboardBaseView extends View implements PointerTracker.UIProx } public void onCancel() { mKeyboardActionListener.onCancel(); dismissPopupKeyboard(); } Loading Loading @@ -1294,15 +1303,29 @@ public class LatinKeyboardBaseView extends View implements PointerTracker.UIProx return pointers.get(id); } public boolean isInSlidingKeyInput() { if (mMiniKeyboard != null) { return mMiniKeyboard.isInSlidingKeyInput(); } else { return mPointerQueue.isInSlidingKeyInput(); } } public int getPointerCount() { return mOldPointerCount; } @Override public boolean onTouchEvent(MotionEvent me) { final int pointerCount = me.getPointerCount(); 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? // If the device does not have distinct multi-touch support panel, ignore all multi-touch // events except a transition from/to single-touch. if (!mHasDistinctMultitouch && pointerCount > 1 && mOldPointerCount > 1) { if (!mHasDistinctMultitouch && pointerCount > 1 && oldPointerCount > 1) { return true; } Loading Loading @@ -1358,7 +1381,6 @@ public class LatinKeyboardBaseView extends View implements PointerTracker.UIProx if (!mHasDistinctMultitouch) { // Use only main (id=0) pointer tracker. PointerTracker tracker = getPointerTracker(0); int oldPointerCount = mOldPointerCount; if (pointerCount == 1 && oldPointerCount == 2) { // Multi-touch to single touch transition. // Send a down event for the latest pointer. Loading @@ -1373,7 +1395,6 @@ public class LatinKeyboardBaseView extends View implements PointerTracker.UIProx Log.w(TAG, "Unknown touch panel behavior: pointer count is " + pointerCount + " (old " + oldPointerCount + ")"); } mOldPointerCount = pointerCount; return true; } Loading
java/src/com/android/inputmethod/latin/PointerTracker.java +74 −24 Original line number Diff line number Diff line Loading @@ -51,6 +51,7 @@ public class PointerTracker { private final UIHandler mHandler; private final KeyDetector mKeyDetector; private OnKeyboardActionListener mListener; private final KeyboardSwitcher mKeyboardSwitcher; private final boolean mHasDistinctMultitouch; private Key[] mKeys; Loading @@ -58,12 +59,18 @@ public class PointerTracker { 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) private boolean mKeyAlreadyProcessed; // true if this pointer is repeatable key private boolean mIsRepeatableKey; // true if this pointer is in sliding key input private boolean mIsInSlidingKeyInput; // For multi-tap private int mLastSentIndex; private int mTapCount; Loading Loading @@ -157,10 +164,6 @@ public class PointerTracker { public int onUpKey(int x, int y) { return onMoveKeyInternal(x, y); } public void onSetKeyboard() { mKeyIndex = mKeyDetector.getKeyIndexAndNearbyCodes(mKeyX, mKeyY, null); } } public PointerTracker(int id, UIHandler handler, KeyDetector keyDetector, UIProxy proxy, Loading @@ -171,6 +174,7 @@ public class PointerTracker { mProxy = proxy; mHandler = handler; mKeyDetector = keyDetector; mKeyboardSwitcher = KeyboardSwitcher.getInstance(); mKeyState = new KeyState(keyDetector); mHasDistinctMultitouch = proxy.hasDistinctMultitouch(); mDelayBeforeKeyRepeatStart = res.getInteger(R.integer.config_delay_before_key_repeat_start); Loading @@ -188,8 +192,12 @@ public class PointerTracker { throw new IllegalArgumentException(); mKeys = keys; mKeyHysteresisDistanceSquared = (int)(keyHysteresisDistance * keyHysteresisDistance); // Update current key index because keyboard layout has been changed. mKeyState.onSetKeyboard(); // Mark that keyboard layout has been changed. mKeyboardLayoutHasBeenChanged = true; } public boolean isInSlidingKeyInput() { return mIsInSlidingKeyInput; } private boolean isValidKeyIndex(int keyIndex) { Loading Loading @@ -268,15 +276,21 @@ public class PointerTracker { if (DEBUG) debugLog("onDownEvent:", x, y); int keyIndex = mKeyState.onDownKey(x, y, eventTime); mKeyboardLayoutHasBeenChanged = false; mKeyAlreadyProcessed = false; mIsRepeatableKey = false; mIsInSlidingKeyInput = false; checkMultiTap(eventTime, keyIndex); if (mListener != null) { if (isValidKeyIndex(keyIndex)) { mListener.onPress(mKeys[keyIndex].codes[0]); // This onPress call may have changed keyboard layout and have updated mKeyIndex. // If that's the case, mKeyIndex has been updated in setKeyboard(). keyIndex = mKeyState.getKeyIndex(); // 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 = mKeyState.onDownKey(x, y, eventTime); } } } if (isValidKeyIndex(keyIndex)) { Loading @@ -285,7 +299,7 @@ public class PointerTracker { mHandler.startKeyRepeatTimer(mDelayBeforeKeyRepeatStart, keyIndex, this); mIsRepeatableKey = true; } mHandler.startLongPressTimer(mLongPressKeyTimeout, keyIndex, this); startLongPressTimer(keyIndex); } showKeyPreviewAndUpdateKey(keyIndex); } Loading @@ -295,42 +309,70 @@ public class PointerTracker { debugLog("onMoveEvent:", x, y); if (mKeyAlreadyProcessed) return; KeyState keyState = mKeyState; final int keyIndex = keyState.onMoveKey(x, y); final KeyState keyState = mKeyState; int keyIndex = keyState.onMoveKey(x, y); final Key oldKey = getKey(keyState.getKeyIndex()); if (isValidKeyIndex(keyIndex)) { 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); mHandler.startLongPressTimer(mLongPressKeyTimeout, keyIndex, this); startLongPressTimer(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) mListener.onRelease(oldKey.codes[0]); 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); mHandler.startLongPressTimer(mLongPressKeyTimeout, keyIndex, this); startLongPressTimer(keyIndex); } } 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) mListener.onRelease(oldKey.codes[0]); keyState.onMoveToNewKey(keyIndex, x ,y); mHandler.cancelLongPressTimer(); } else if (!isMinorMoveBounce(x, y, keyIndex)) { resetMultiTap(); keyState.onMoveToNewKey(keyIndex, x ,y); mHandler.cancelLongPressTimer(); } } showKeyPreviewAndUpdateKey(mKeyState.getKeyIndex()); showKeyPreviewAndUpdateKey(keyState.getKeyIndex()); } public void onUpEvent(int x, int y, long eventTime) { if (DEBUG) debugLog("onUpEvent :", x, y); if (mKeyAlreadyProcessed) return; mHandler.cancelKeyTimers(); mHandler.cancelPopupPreview(); showKeyPreviewAndUpdateKey(NOT_A_KEY); mIsInSlidingKeyInput = false; if (mKeyAlreadyProcessed) return; int keyIndex = mKeyState.onUpKey(x, y); if (isMinorMoveBounce(x, y, keyIndex)) { // Use previous fixed key index and coordinates. Loading @@ -338,7 +380,6 @@ public class PointerTracker { x = mKeyState.getKeyX(); y = mKeyState.getKeyY(); } showKeyPreviewAndUpdateKey(NOT_A_KEY); if (!mIsRepeatableKey) { detectAndSendKey(keyIndex, x, y, eventTime); } Loading @@ -353,6 +394,7 @@ public class PointerTracker { mHandler.cancelKeyTimers(); mHandler.cancelPopupPreview(); showKeyPreviewAndUpdateKey(NOT_A_KEY); mIsInSlidingKeyInput = false; int keyIndex = mKeyState.getKeyIndex(); if (isValidKeyIndex(keyIndex)) mProxy.invalidateKey(mKeys[keyIndex]); Loading Loading @@ -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) { final OnKeyboardActionListener listener = mListener; final Key key = getKey(index); Loading @@ -436,11 +487,10 @@ public class PointerTracker { if (key.text != null) { if (listener != null) { listener.onText(key.text); listener.onRelease(NOT_A_KEY); listener.onRelease(0); // dummy key code } } else { int code = key.codes[0]; //TextEntryState.keyPressedAt(key, x, y); int[] codes = mKeyDetector.newCodeArray(); mKeyDetector.getKeyIndexAndNearbyCodes(x, y, codes); // Multi-tap Loading