Merge "Test that delete actually removes any selected text."
diff --git a/java/src/com/android/inputmethod/keyboard/KeyboardSwitcher.java b/java/src/com/android/inputmethod/keyboard/KeyboardSwitcher.java
index cb80d05..1398bae 100644
--- a/java/src/com/android/inputmethod/keyboard/KeyboardSwitcher.java
+++ b/java/src/com/android/inputmethod/keyboard/KeyboardSwitcher.java
@@ -216,44 +216,6 @@
         return null;
     }
 
-    // Implements {@link KeyboardState.SwitchActions}.
-    @Override
-    public void setShifted(int shiftMode) {
-        mInputMethodService.mHandler.cancelUpdateShiftState();
-        Keyboard keyboard = getKeyboard();
-        if (keyboard == null)
-            return;
-        switch (shiftMode) {
-        case AUTOMATIC_SHIFT:
-            keyboard.setAutomaticTemporaryUpperCase();
-            break;
-        case MANUAL_SHIFT:
-            keyboard.setShifted(true);
-            break;
-        case UNSHIFT:
-            keyboard.setShifted(false);
-            break;
-        }
-        mKeyboardView.invalidateAllKeys();
-    }
-
-    // Implements {@link KeyboardState.SwitchActions}.
-    @Override
-    public void setShiftLocked(boolean shiftLocked) {
-        mInputMethodService.mHandler.cancelUpdateShiftState();
-        Keyboard keyboard = getKeyboard();
-        if (keyboard == null)
-            return;
-        keyboard.setShiftLocked(shiftLocked);
-        mKeyboardView.invalidateAllKeys();
-        if (!shiftLocked) {
-            // To be able to turn off caps lock by "double tap" on shift key, we should ignore
-            // the second tap of the "double tap" from now for a while because we just have
-            // already turned off caps lock above.
-            mKeyboardView.startIgnoringDoubleTap();
-        }
-    }
-
     /**
      * Update keyboard shift state triggered by connected EditText status change.
      */
@@ -273,16 +235,73 @@
         mState.onCancelInput(isSinglePointer());
     }
 
-    // Implements {@link KeyboardState.SwitchActions}.
-    @Override
-    public void setSymbolsKeyboard() {
-        setKeyboard(mKeyboardSet.getSymbolsKeyboard());
+    // TODO: Remove these constants.
+    private static final int ALPHABET_UNSHIFTED = 0;
+    private static final int ALPHABET_MANUAL_SHIFTED = 1;
+    private static final int ALPHABET_AUTOMATIC_SHIFTED = 2;
+    private static final int ALPHABET_SHIFT_LOCKED = 3;
+
+    // TODO: Remove this method.
+    private void updateAlphabetKeyboardShiftState(int shiftMode) {
+        mInputMethodService.mHandler.cancelUpdateShiftState();
+        Keyboard keyboard = getKeyboard();
+        if (keyboard == null)
+            return;
+        switch (shiftMode) {
+        case ALPHABET_UNSHIFTED:
+            keyboard.setShifted(false);
+            break;
+        case ALPHABET_MANUAL_SHIFTED:
+            keyboard.setShifted(true);
+            break;
+        case ALPHABET_AUTOMATIC_SHIFTED:
+            keyboard.setAutomaticTemporaryUpperCase();
+            break;
+        case ALPHABET_SHIFT_LOCKED:
+            keyboard.setShiftLocked(true);
+            break;
+        }
+        mKeyboardView.invalidateAllKeys();
+        if (shiftMode != ALPHABET_SHIFT_LOCKED) {
+            // To be able to turn off caps lock by "double tap" on shift key, we should ignore
+            // the second tap of the "double tap" from now for a while because we just have
+            // already turned off caps lock above.
+            mKeyboardView.startIgnoringDoubleTap();
+        }
     }
 
     // Implements {@link KeyboardState.SwitchActions}.
     @Override
     public void setAlphabetKeyboard() {
         setKeyboard(mKeyboardSet.getMainKeyboard());
+        updateAlphabetKeyboardShiftState(ALPHABET_UNSHIFTED);
+    }
+
+    // Implements {@link KeyboardState.SwitchActions}.
+    @Override
+    public void setAlphabetManualShiftedKeyboard() {
+        setKeyboard(mKeyboardSet.getMainKeyboard());
+        updateAlphabetKeyboardShiftState(ALPHABET_MANUAL_SHIFTED);
+    }
+
+    // Implements {@link KeyboardState.SwitchActions}.
+    @Override
+    public void setAlphabetAutomaticShiftedKeyboard() {
+        setKeyboard(mKeyboardSet.getMainKeyboard());
+        updateAlphabetKeyboardShiftState(ALPHABET_AUTOMATIC_SHIFTED);
+    }
+
+    // Implements {@link KeyboardState.SwitchActions}.
+    @Override
+    public void setAlphabetShiftLockedKeyboard() {
+        setKeyboard(mKeyboardSet.getMainKeyboard());
+        updateAlphabetKeyboardShiftState(ALPHABET_SHIFT_LOCKED);
+    }
+
+    // Implements {@link KeyboardState.SwitchActions}.
+    @Override
+    public void setSymbolsKeyboard() {
+        setKeyboard(mKeyboardSet.getSymbolsKeyboard());
     }
 
     // Implements {@link KeyboardState.SwitchActions}.
diff --git a/java/src/com/android/inputmethod/keyboard/internal/KeyboardState.java b/java/src/com/android/inputmethod/keyboard/internal/KeyboardState.java
index babf600..bc8b7e3 100644
--- a/java/src/com/android/inputmethod/keyboard/internal/KeyboardState.java
+++ b/java/src/com/android/inputmethod/keyboard/internal/KeyboardState.java
@@ -40,16 +40,10 @@
 
     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 setAlphabetManualShiftedKeyboard();
+        public void setAlphabetAutomaticShiftedKeyboard();
+        public void setAlphabetShiftLockedKeyboard();
         public void setSymbolsKeyboard();
-
         public void setSymbolsShiftedKeyboard();
 
         /**
@@ -63,14 +57,14 @@
     private ShiftKeyState mShiftKeyState = new ShiftKeyState("Shift");
     private ModifierKeyState mSymbolKeyState = new ModifierKeyState("Symbol");
 
+    // TODO: Merge {@link #mSwitchState}, {@link #mIsAlphabetMode}, {@link #mAlphabetShiftState},
+    // {@link #mIsSymbolShifted}, {@link #mPrevMainKeyboardWasShiftLocked}, and
+    // {@link #mPrevSymbolsKeyboardWasShifted} into single state variable.
     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;
 
@@ -88,6 +82,7 @@
         public boolean mIsShiftLocked;
         public boolean mIsShifted;
 
+        @Override
         public String toString() {
             if (!mIsValid) return "INVALID";
             if (mIsAlphabetMode) {
@@ -155,7 +150,7 @@
         if (state.mIsAlphabetMode) {
             setShiftLocked(state.mIsShiftLocked);
             if (!state.mIsShiftLocked) {
-                setShifted(state.mIsShifted ? SwitchActions.MANUAL_SHIFT : SwitchActions.UNSHIFT);
+                setShifted(state.mIsShifted ? MANUAL_SHIFT : UNSHIFT);
             }
         }
     }
@@ -165,30 +160,58 @@
         return mAlphabetShiftState.isShiftLocked();
     }
 
+    private static final int UNSHIFT = 0;
+    private static final int MANUAL_SHIFT = 1;
+    private static final int AUTOMATIC_SHIFT = 2;
+
     private void setShifted(int shiftMode) {
         if (DEBUG_ACTION) {
-            Log.d(TAG, "setShifted: shiftMode=" + shiftModeToString(shiftMode));
+            Log.d(TAG, "setShifted: shiftMode=" + shiftModeToString(shiftMode) + " " + this);
+        }
+        if (!mIsAlphabetMode) return;
+        final int prevShiftMode;
+        if (mAlphabetShiftState.isAutomaticTemporaryUpperCase()) {
+            prevShiftMode = AUTOMATIC_SHIFT;
+        } else if (mAlphabetShiftState.isManualTemporaryUpperCase()) {
+            prevShiftMode = MANUAL_SHIFT;
+        } else {
+            prevShiftMode = UNSHIFT;
         }
         switch (shiftMode) {
-        case SwitchActions.AUTOMATIC_SHIFT:
+        case AUTOMATIC_SHIFT:
             mAlphabetShiftState.setAutomaticTemporaryUpperCase();
+            if (shiftMode != prevShiftMode) {
+                mSwitchActions.setAlphabetAutomaticShiftedKeyboard();
+            }
             break;
-        case SwitchActions.MANUAL_SHIFT:
+        case MANUAL_SHIFT:
             mAlphabetShiftState.setShifted(true);
+            if (shiftMode != prevShiftMode) {
+                mSwitchActions.setAlphabetManualShiftedKeyboard();
+            }
             break;
-        case SwitchActions.UNSHIFT:
+        case UNSHIFT:
             mAlphabetShiftState.setShifted(false);
+            if (shiftMode != prevShiftMode) {
+                mSwitchActions.setAlphabetKeyboard();
+            }
             break;
         }
-        mSwitchActions.setShifted(shiftMode);
     }
 
     private void setShiftLocked(boolean shiftLocked) {
         if (DEBUG_ACTION) {
-            Log.d(TAG, "setShiftLocked: shiftLocked=" + shiftLocked);
+            Log.d(TAG, "setShiftLocked: shiftLocked=" + shiftLocked + " " + this);
+        }
+        if (!mIsAlphabetMode) return;
+        if (shiftLocked && (!mAlphabetShiftState.isShiftLocked()
+                || mAlphabetShiftState.isShiftLockShifted())) {
+            mSwitchActions.setAlphabetShiftLockedKeyboard();
+        }
+        if (!shiftLocked && mAlphabetShiftState.isShiftLocked()) {
+            mSwitchActions.setAlphabetKeyboard();
         }
         mAlphabetShiftState.setShiftLocked(shiftLocked);
-        mSwitchActions.setShiftLocked(shiftLocked);
     }
 
     private void toggleAlphabetAndSymbols() {
@@ -276,8 +299,7 @@
         if (code == Keyboard.CODE_SHIFT) {
             onReleaseShift(withSliding);
         } else if (code == Keyboard.CODE_SWITCH_ALPHA_SYMBOL) {
-            // TODO: Make use of withSliding instead of relying on mSwitchState.
-            onReleaseSymbol();
+            onReleaseSymbol(withSliding);
         }
     }
 
@@ -287,11 +309,16 @@
         mSwitchState = SWITCH_STATE_MOMENTARY_ALPHA_AND_SYMBOL;
     }
 
-    private void onReleaseSymbol() {
-        // Switch 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) {
+    private void onReleaseSymbol(boolean withSliding) {
+        if (mSymbolKeyState.isChording()) {
+            // Switch back to the previous keyboard mode if the user chords the mode change key and
+            // another key, then releases the mode change key.
             toggleAlphabetAndSymbols();
+        } else if (!withSliding) {
+            // If the mode change key is being released without sliding, we should forget the
+            // previous symbols keyboard shift state and simply switch back to symbols layout
+            // (never symbols shifted) next time the mode gets changed to symbols layout.
+            mPrevSymbolsKeyboardWasShifted = false;
         }
         mSymbolKeyState.onRelease();
     }
@@ -300,24 +327,18 @@
         if (DEBUG_EVENT) {
             Log.d(TAG, "onUpdateShiftState: autoCaps=" + autoCaps + " " + this);
         }
-        onUpdateShiftStateInternal(autoCaps);
+        updateAlphabetShiftState(autoCaps);
     }
 
-    private void onUpdateShiftStateInternal(boolean autoCaps) {
-        if (mIsAlphabetMode) {
-            if (!mAlphabetShiftState.isShiftLocked() && !mShiftKeyState.isIgnoring()) {
-                if (mShiftKeyState.isReleasing() && autoCaps) {
-                    // Only when shift key is releasing, automatic temporary upper case will be set.
-                    setShifted(SwitchActions.AUTOMATIC_SHIFT);
-                } else {
-                    setShifted(mShiftKeyState.isMomentary()
-                            ? SwitchActions.MANUAL_SHIFT : SwitchActions.UNSHIFT);
-                }
+    private void updateAlphabetShiftState(boolean autoCaps) {
+        if (!mIsAlphabetMode) return;
+        if (!mAlphabetShiftState.isShiftLocked() && !mShiftKeyState.isIgnoring()) {
+            if (mShiftKeyState.isReleasing() && autoCaps) {
+                // Only when shift key is releasing, automatic temporary upper case will be set.
+                setShifted(AUTOMATIC_SHIFT);
+            } else {
+                setShifted(mShiftKeyState.isChording() ? MANUAL_SHIFT : UNSHIFT);
             }
-        } else {
-            // In symbol keyboard mode, we should clear shift key state because only alphabet
-            // keyboard has shift key.
-            mSymbolKeyState.onRelease();
         }
     }
 
@@ -326,12 +347,12 @@
             if (mAlphabetShiftState.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(SwitchActions.MANUAL_SHIFT);
+                setShifted(MANUAL_SHIFT);
                 mShiftKeyState.onPress();
             } else if (mAlphabetShiftState.isAutomaticTemporaryUpperCase()) {
                 // Shift key is pressed while automatic temporary upper case, we have to move to
                 // manual temporary upper case.
-                setShifted(SwitchActions.MANUAL_SHIFT);
+                setShifted(MANUAL_SHIFT);
                 mShiftKeyState.onPress();
             } else if (mAlphabetShiftState.isShiftedOrShiftLocked()) {
                 // In manual upper case state, we just record shift key has been pressing while
@@ -339,7 +360,7 @@
                 mShiftKeyState.onPressOnShifted();
             } else {
                 // In base layout, chording or manual temporary upper case mode is started.
-                setShifted(SwitchActions.MANUAL_SHIFT);
+                setShifted(MANUAL_SHIFT);
                 mShiftKeyState.onPress();
             }
         } else {
@@ -353,13 +374,17 @@
     private void onReleaseShift(boolean withSliding) {
         if (mIsAlphabetMode) {
             final boolean isShiftLocked = mAlphabetShiftState.isShiftLocked();
-            if (mShiftKeyState.isMomentary()) {
-                // After chording input while normal state.
+            if (mShiftKeyState.isChording()) {
                 if (mAlphabetShiftState.isShiftLockShifted()) {
+                    // After chording input while caps lock state.
                     setShiftLocked(true);
                 } else {
-                    setShifted(SwitchActions.UNSHIFT);
+                    // After chording input while normal state.
+                    setShifted(UNSHIFT);
                 }
+            } else if (mAlphabetShiftState.isShiftLockShifted() && withSliding) {
+                // In caps lock state, shift has been pressed and slid out to other key.
+                setShiftLocked(true);
             } else if (isShiftLocked && !mAlphabetShiftState.isShiftLockShifted()
                     && (mShiftKeyState.isPressing() || mShiftKeyState.isPressingOnShifted())
                     && !withSliding) {
@@ -370,17 +395,17 @@
             } else if (mAlphabetShiftState.isShiftedOrShiftLocked()
                     && mShiftKeyState.isPressingOnShifted() && !withSliding) {
                 // Shift has been pressed without chording while shifted state.
-                setShifted(SwitchActions.UNSHIFT);
+                setShifted(UNSHIFT);
             } else if (mAlphabetShiftState.isManualTemporaryUpperCaseFromAuto()
                     && mShiftKeyState.isPressing() && !withSliding) {
                 // Shift has been pressed without chording while manual temporary upper case
                 // transited from automatic temporary upper case.
-                setShifted(SwitchActions.UNSHIFT);
+                setShifted(UNSHIFT);
             }
         } else {
             // In symbol mode, switch 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) {
+            if (mShiftKeyState.isChording()) {
                 toggleShiftInSymbols();
             }
         }
@@ -436,12 +461,6 @@
 
         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 (mIsAlphabetMode) {
@@ -455,10 +474,6 @@
                 // If the user cancels the sliding input, switching back to the previous keyboard
                 // mode is handled by {@link #onCancelInput}.
                 toggleAlphabetAndSymbols();
-            } else {
-                // Chording input is being started. The keyboard mode will be switched 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:
@@ -470,10 +485,6 @@
                 // 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 switched 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:
@@ -487,7 +498,6 @@
             }
             break;
         case SWITCH_STATE_SYMBOL:
-        case SWITCH_STATE_CHORDING_SYMBOL:
             // Switch 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)) {
@@ -498,15 +508,15 @@
 
         // If the code is a letter, update keyboard shift state.
         if (Keyboard.isLetterCode(code)) {
-            onUpdateShiftStateInternal(autoCaps);
+            updateAlphabetShiftState(autoCaps);
         }
     }
 
     private static String shiftModeToString(int shiftMode) {
         switch (shiftMode) {
-        case SwitchActions.UNSHIFT: return "UNSHIFT";
-        case SwitchActions.MANUAL_SHIFT: return "MANUAL";
-        case SwitchActions.AUTOMATIC_SHIFT: return "AUTOMATIC";
+        case UNSHIFT: return "UNSHIFT";
+        case MANUAL_SHIFT: return "MANUAL";
+        case AUTOMATIC_SHIFT: return "AUTOMATIC";
         default: return null;
         }
     }
@@ -518,8 +528,6 @@
         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;
         }
     }
diff --git a/java/src/com/android/inputmethod/keyboard/internal/ModifierKeyState.java b/java/src/com/android/inputmethod/keyboard/internal/ModifierKeyState.java
index f95c916..b39b977 100644
--- a/java/src/com/android/inputmethod/keyboard/internal/ModifierKeyState.java
+++ b/java/src/com/android/inputmethod/keyboard/internal/ModifierKeyState.java
@@ -19,12 +19,12 @@
 import android.util.Log;
 
 /* package */ class ModifierKeyState {
-    protected static final String TAG = "ModifierKeyState";
+    protected static final String TAG = ModifierKeyState.class.getSimpleName();
     protected static final boolean DEBUG = false;
 
     protected static final int RELEASING = 0;
     protected static final int PRESSING = 1;
-    protected static final int MOMENTARY = 2;
+    protected static final int CHORDING = 2;
 
     protected final String mName;
     protected int mState = RELEASING;
@@ -50,7 +50,7 @@
     public void onOtherKeyPressed() {
         final int oldState = mState;
         if (oldState == PRESSING)
-            mState = MOMENTARY;
+            mState = CHORDING;
         if (DEBUG)
             Log.d(TAG, mName + ".onOtherKeyPressed: " + toString(oldState) + " > " + this);
     }
@@ -63,8 +63,8 @@
         return mState == RELEASING;
     }
 
-    public boolean isMomentary() {
-        return mState == MOMENTARY;
+    public boolean isChording() {
+        return mState == CHORDING;
     }
 
     @Override
@@ -76,7 +76,7 @@
         switch (state) {
         case RELEASING: return "RELEASING";
         case PRESSING: return "PRESSING";
-        case MOMENTARY: return "MOMENTARY";
+        case CHORDING: return "CHORDING";
         default: return "UNKNOWN";
         }
     }
diff --git a/java/src/com/android/inputmethod/keyboard/internal/ShiftKeyState.java b/java/src/com/android/inputmethod/keyboard/internal/ShiftKeyState.java
index 6f54b4f..edb40c8 100644
--- a/java/src/com/android/inputmethod/keyboard/internal/ShiftKeyState.java
+++ b/java/src/com/android/inputmethod/keyboard/internal/ShiftKeyState.java
@@ -30,7 +30,7 @@
     public void onOtherKeyPressed() {
         int oldState = mState;
         if (oldState == PRESSING) {
-            mState = MOMENTARY;
+            mState = CHORDING;
         } else if (oldState == PRESSING_ON_SHIFTED) {
             mState = IGNORING;
         }
diff --git a/java/src/com/android/inputmethod/latin/LatinIME.java b/java/src/com/android/inputmethod/latin/LatinIME.java
index a053b9b..31cbc4e 100644
--- a/java/src/com/android/inputmethod/latin/LatinIME.java
+++ b/java/src/com/android/inputmethod/latin/LatinIME.java
@@ -203,9 +203,11 @@
     private WordComposer mWordComposer = new WordComposer();
 
     private int mCorrectionMode;
+
     // Keep track of the last selection range to decide if we need to show word alternatives
-    private int mLastSelectionStart;
-    private int mLastSelectionEnd;
+    private static final int NOT_A_CURSOR_POSITION = -1;
+    private int mLastSelectionStart = NOT_A_CURSOR_POSITION;
+    private int mLastSelectionEnd = NOT_A_CURSOR_POSITION;
 
     // Whether we are expecting an onUpdateSelection event to fire. If it does when we don't
     // "expect" it, it means the user actually moved the cursor.
@@ -1401,9 +1403,29 @@
                 // inconsistent with backspacing after selecting other suggestions.
                 restartSuggestionsOnManuallyPickedTypedWord(ic);
             } else {
-                ic.deleteSurroundingText(1, 0);
-                if (mDeleteCount > DELETE_ACCELERATE_AT) {
-                    ic.deleteSurroundingText(1, 0);
+                // Here we must check whether there is a selection. If so we should remove the
+                // selected text, otherwise we should just delete the character before the cursor.
+                if (mLastSelectionStart != mLastSelectionEnd) {
+                    final int lengthToDelete = mLastSelectionEnd - mLastSelectionStart;
+                    ic.setSelection(mLastSelectionEnd, mLastSelectionEnd);
+                    ic.deleteSurroundingText(lengthToDelete, 0);
+                } else {
+                    if (NOT_A_CURSOR_POSITION == mLastSelectionEnd) {
+                        // We don't know whether there is a selection or not. We just send a false
+                        // hardware key event and let TextView sort it out for us. The problem
+                        // here is, this is asynchronous with respect to the input connection
+                        // batch edit, so it may flicker. But this only ever happens if backspace
+                        // is pressed just after the IME is invoked, and then again only once.
+                        // TODO: add an API call that gets the selection indices. This is available
+                        // to the IME in the general case via onUpdateSelection anyway, and would
+                        // allow us to remove this race condition.
+                        sendDownUpKeyEvents(KeyEvent.KEYCODE_DEL);
+                    } else {
+                        ic.deleteSurroundingText(1, 0);
+                    }
+                    if (mDeleteCount > DELETE_ACCELERATE_AT) {
+                        ic.deleteSurroundingText(1, 0);
+                    }
                 }
                 if (isSuggestionsRequested()) {
                     restartSuggestionsOnWordBeforeCursorIfAtEndOfWord(ic);
diff --git a/tests/src/com/android/inputmethod/keyboard/internal/KeyboardStateSingleTouchTests.java b/tests/src/com/android/inputmethod/keyboard/internal/KeyboardStateSingleTouchTests.java
index 4c06309..2204fca 100644
--- a/tests/src/com/android/inputmethod/keyboard/internal/KeyboardStateSingleTouchTests.java
+++ b/tests/src/com/android/inputmethod/keyboard/internal/KeyboardStateSingleTouchTests.java
@@ -61,9 +61,8 @@
         pressAndReleaseKey(CODE_SHIFT, SYMBOLS_SHIFTED, SYMBOLS_SHIFTED);
         // Press/release "ABC" key, back to alphabet.
         pressAndReleaseKey(CODE_SYMBOL, ALPHABET_UNSHIFTED, ALPHABET_UNSHIFTED);
-//        // TODO: This test failed due to bug.
-//        // Press/release "?123" key, back to symbols (not symbols shifted).
-//        pressAndReleaseKey(CODE_SYMBOL, SYMBOLS_UNSHIFTED, SYMBOLS_UNSHIFTED);
+        // Press/release "?123" key, back to symbols (not symbols shifted).
+        pressAndReleaseKey(CODE_SYMBOL, SYMBOLS_UNSHIFTED, SYMBOLS_UNSHIFTED);
     }
 
     // Switching between alphabet shifted and symbols.
@@ -93,18 +92,17 @@
 
         // Press/release "?123" key, enter into symbols.
         pressAndReleaseKey(CODE_SYMBOL, SYMBOLS_UNSHIFTED, SYMBOLS_UNSHIFTED);
-        // Press/release "ABC" key, back to alphabet shift locked (not alphabet).
+        // Press/release "ABC" key, back to alphabet shift locked.
         pressAndReleaseKey(CODE_SYMBOL, ALPHABET_SHIFT_LOCKED, ALPHABET_SHIFT_LOCKED);
         // Press/release "?123" key, enter into symbols.
         pressAndReleaseKey(CODE_SYMBOL, SYMBOLS_UNSHIFTED, SYMBOLS_UNSHIFTED);
 
         // Press/release "=\<" key, enter into symbols shifted.
         pressAndReleaseKey(CODE_SHIFT, SYMBOLS_SHIFTED, SYMBOLS_SHIFTED);
-//        // TODO: This test fails due to bug.
-//        // Press/release "ABC" key, back to alphabet shift locked (not alphabet).
-//        pressAndReleaseKey(CODE_SYMBOL, ALPHABET_UNSHIFTED, ALPHABET_SHIFT_LOCKED);
-//        // Press/release "?123" key, back to symbols (not symbols shifted).
-//        pressAndReleaseKey(CODE_SYMBOL, SYMBOLS_UNSHIFTED, SYMBOLS_UNSHIFTED);
+        // Press/release "ABC" key, back to alphabet shift locked.
+        pressAndReleaseKey(CODE_SYMBOL, ALPHABET_SHIFT_LOCKED, ALPHABET_SHIFT_LOCKED);
+        // Press/release "?123" key, back to symbols (not symbols shifted).
+        pressAndReleaseKey(CODE_SYMBOL, SYMBOLS_UNSHIFTED, SYMBOLS_UNSHIFTED);
     }
 
     // Automatic switch back to alphabet by space key.
@@ -135,7 +133,7 @@
         pressAndReleaseKey(CODE_SYMBOL, SYMBOLS_UNSHIFTED, SYMBOLS_UNSHIFTED);
         // Enter symbol letter.
         pressAndReleaseKey('1', SYMBOLS_UNSHIFTED, SYMBOLS_UNSHIFTED);
-        // Enter space, switch back to alphabet shift locked (not alphabet).
+        // Enter space, switch back to alphabet shift locked.
         pressAndReleaseKey(CODE_SPACE, SYMBOLS_UNSHIFTED, ALPHABET_SHIFT_LOCKED);
 
         // Press/release "?123" key, enter into symbols.
@@ -144,7 +142,7 @@
         pressAndReleaseKey(CODE_SHIFT, SYMBOLS_SHIFTED, SYMBOLS_SHIFTED);
         // Enter symbol shift letter.
         pressAndReleaseKey('~', SYMBOLS_SHIFTED, SYMBOLS_SHIFTED);
-        // Enter space, switch back to alphabet shift locked (not alphabet).
+        // Enter space, switch back to alphabet shift locked.
         pressAndReleaseKey(CODE_SPACE, SYMBOLS_SHIFTED, ALPHABET_SHIFT_LOCKED);
     }
 
@@ -187,7 +185,7 @@
         pressAndReleaseKey(CODE_SYMBOL, SYMBOLS_UNSHIFTED, SYMBOLS_UNSHIFTED);
         // Enter symbol letter.
         pressAndReleaseKey('1', SYMBOLS_UNSHIFTED, SYMBOLS_UNSHIFTED);
-        // Enter switch back letter, switch back to alphabet shift locked. (not alphabet).
+        // Enter switch back letter, switch back to alphabet shift locked.
         pressAndReleaseKey(switchBackCode, SYMBOLS_UNSHIFTED, ALPHABET_SHIFT_LOCKED);
 
         // Press/release "?123" key, enter into symbols.
@@ -196,7 +194,7 @@
         pressAndReleaseKey(CODE_SHIFT, SYMBOLS_SHIFTED, SYMBOLS_SHIFTED);
         // Enter symbol shift letter.
         pressAndReleaseKey(CODE_SPACE, SYMBOLS_SHIFTED, SYMBOLS_SHIFTED);
-        // Enter switch back letter, switch back to alphabet shift locked (not alphabet).
+        // Enter switch back letter, switch back to alphabet shift locked.
         pressAndReleaseKey(switchBackCode, SYMBOLS_SHIFTED, ALPHABET_SHIFT_LOCKED);
     }
 
@@ -223,7 +221,7 @@
         pressAndReleaseKey(CODE_SYMBOL, SYMBOLS_UNSHIFTED, SYMBOLS_UNSHIFTED);
         // Press/release symbol letter key, remain in symbols.
         pressAndReleaseKey('1', SYMBOLS_UNSHIFTED, SYMBOLS_UNSHIFTED);
-        // Press/release space, switch back to automatic shifted (not alphabet).
+        // Press/release space, switch back to automatic shifted.
         pressAndReleaseKey(CODE_SPACE, SYMBOLS_UNSHIFTED, ALPHABET_AUTOMATIC_SHIFTED);
 
         // Press/release "?123" key, enter into symbols.
@@ -232,7 +230,7 @@
         pressAndReleaseKey(CODE_SHIFT, SYMBOLS_SHIFTED, SYMBOLS_SHIFTED);
         // Press/release symbol shift letter key, remain in symbols shifted.
         pressAndReleaseKey('~', SYMBOLS_SHIFTED, SYMBOLS_SHIFTED);
-        // Press/release space, switch back to automatic shifted (not alphabet).
+        // Press/release space, switch back to automatic shifted.
         pressAndReleaseKey(CODE_SPACE, SYMBOLS_SHIFTED, ALPHABET_AUTOMATIC_SHIFTED);
     }
 
@@ -372,17 +370,14 @@
         longPressShiftKey(ALPHABET_MANUAL_SHIFTED, ALPHABET_SHIFT_LOCKED);
         // Press and slide from "123?" key, enter symbols.
         pressAndSlideFromKey(CODE_SYMBOL, SYMBOLS_UNSHIFTED, SYMBOLS_UNSHIFTED);
-        // Enter/release into symbol letter key, switch back to alphabet shift locked (not alphabet).
+        // Enter/release into symbol letter key, switch back to alphabet shift locked.
         pressAndReleaseKey('!', SYMBOLS_UNSHIFTED, ALPHABET_SHIFT_LOCKED);
 
         // Alphabet shift locked -> "?123" key + letter -> alphabet shift locked.
         // Press and slide from shift key, enter alphabet shifted.
-        pressAndSlideFromKey(CODE_SHIFT, ALPHABET_MANUAL_SHIFTED, ALPHABET_MANUAL_SHIFTED);
-        // Enter/release letter key, switch back to shift locked (not alphabet).
-        // TODO: This test fails due to bug, though the external behavior is correct.
-//        pressAndReleaseKey('Z', ALPHABET_MANUAL_SHIFTED, ALPHABET_SHIFT_LOCKED);
-        // TODO: Replace this with the above line once the bug fixed.
-        pressAndReleaseKey('Z', ALPHABET_MANUAL_SHIFTED, ALPHABET_MANUAL_SHIFTED);
+        pressAndSlideFromKey(CODE_SHIFT, ALPHABET_MANUAL_SHIFTED, ALPHABET_SHIFT_LOCKED);
+        // Enter/release letter key, switch back to shift locked.
+        pressAndReleaseKey('Z', ALPHABET_SHIFT_LOCKED, ALPHABET_SHIFT_LOCKED);
     }
 
     // Sliding input in symbols.
@@ -425,11 +420,11 @@
         longPressShiftKey(ALPHABET_MANUAL_SHIFTED, ALPHABET_SHIFT_LOCKED);
         // Press/release "?123" key, enter into symbols.
         pressAndReleaseKey(CODE_SYMBOL, SYMBOLS_UNSHIFTED, SYMBOLS_UNSHIFTED);
-        // Press and slide from "ABC" key, enter alphabet shift locked (not alphabet).
+        // Press and slide from "ABC" key, enter alphabet shift locked.
         pressAndSlideFromKey(CODE_SYMBOL, ALPHABET_SHIFT_LOCKED, ALPHABET_SHIFT_LOCKED);
         // Enter/release letter key, switch back to symbols.
         pressAndReleaseKey('A', ALPHABET_SHIFT_LOCKED, SYMBOLS_UNSHIFTED);
-        // Press/release "ABC" key, switch to alphabet shift locked. (not alphabet).
+        // Press/release "ABC" key, switch to alphabet shift locked.
         pressAndReleaseKey(CODE_SYMBOL, ALPHABET_SHIFT_LOCKED, ALPHABET_SHIFT_LOCKED);
     }
 
@@ -483,7 +478,7 @@
         pressAndSlideFromKey(CODE_SYMBOL, ALPHABET_SHIFT_LOCKED, ALPHABET_SHIFT_LOCKED);
         // Enter/release letter key, switch back to symbols shifted.
         pressAndReleaseKey('A', ALPHABET_SHIFT_LOCKED, SYMBOLS_SHIFTED);
-        // Press/release "ABC" key, switch to alphabet shift locked. (not alphabet).
+        // Press/release "ABC" key, switch to alphabet shift locked.
         pressAndReleaseKey(CODE_SYMBOL, ALPHABET_SHIFT_LOCKED, ALPHABET_SHIFT_LOCKED);
     }
 
diff --git a/tests/src/com/android/inputmethod/keyboard/internal/MockKeyboardSwitcher.java b/tests/src/com/android/inputmethod/keyboard/internal/MockKeyboardSwitcher.java
index a675a84..fce698a 100644
--- a/tests/src/com/android/inputmethod/keyboard/internal/MockKeyboardSwitcher.java
+++ b/tests/src/com/android/inputmethod/keyboard/internal/MockKeyboardSwitcher.java
@@ -78,23 +78,18 @@
     }
 
     @Override
-    public void setShifted(int shiftMode) {
-        if (shiftMode == SwitchActions.UNSHIFT) {
-            mLayout = Constants.ALPHABET_UNSHIFTED;
-        } else if (shiftMode == SwitchActions.MANUAL_SHIFT) {
-            mLayout = Constants.ALPHABET_MANUAL_SHIFTED;
-        } else if (shiftMode == SwitchActions.AUTOMATIC_SHIFT) {
-            mLayout = Constants.ALPHABET_AUTOMATIC_SHIFTED;
-        }
+    public void setAlphabetManualShiftedKeyboard() {
+        mLayout = Constants.ALPHABET_MANUAL_SHIFTED;
     }
 
     @Override
-    public void setShiftLocked(boolean shiftLocked) {
-        if (shiftLocked) {
-            mLayout = Constants.ALPHABET_SHIFT_LOCKED;
-        } else {
-            mLayout = Constants.ALPHABET_UNSHIFTED;
-        }
+    public void setAlphabetAutomaticShiftedKeyboard() {
+        mLayout = Constants.ALPHABET_AUTOMATIC_SHIFTED;
+    }
+
+    @Override
+    public void setAlphabetShiftLockedKeyboard() {
+        mLayout = Constants.ALPHABET_SHIFT_LOCKED;
     }
 
     @Override