diff --git a/java/res/xml-sw600dp/kbd_phone_symbols.xml b/java/res/xml-sw600dp/kbd_phone_shift.xml
similarity index 100%
rename from java/res/xml-sw600dp/kbd_phone_symbols.xml
rename to java/res/xml-sw600dp/kbd_phone_shift.xml
diff --git a/java/res/xml-sw768dp/kbd_phone_symbols.xml b/java/res/xml-sw768dp/kbd_phone_shift.xml
similarity index 100%
rename from java/res/xml-sw768dp/kbd_phone_symbols.xml
rename to java/res/xml-sw768dp/kbd_phone_shift.xml
diff --git a/java/res/xml/kbd_numkey_styles.xml b/java/res/xml/kbd_numkey_styles.xml
index a40acaf553330add08a401bce761e25329402b1e..c6ba23d390fa9ddeb55e5c7cfb68a992160e4d50 100644
--- a/java/res/xml/kbd_numkey_styles.xml
+++ b/java/res/xml/kbd_numkey_styles.xml
@@ -91,12 +91,12 @@
         latin:parentStyle="numKeyStyle" />
     <key-style
         latin:styleName="numSwitchToAltKeyStyle"
-        latin:code="@integer/key_switch_alpha_symbol"
+        latin:code="@integer/key_shift"
         latin:keyLabel="@string/label_to_phone_symbols_key"
         latin:parentStyle="numModeKeyStyle" />
     <key-style
         latin:styleName="numSwitchToNumericKeyStyle"
-        latin:code="@integer/key_switch_alpha_symbol"
+        latin:code="@integer/key_shift"
         latin:keyLabel="@string/label_to_phone_numeric_key"
         latin:parentStyle="numModeKeyStyle" />
     <key-style
diff --git a/java/res/xml/kbd_phone_symbols.xml b/java/res/xml/kbd_phone_shift.xml
similarity index 100%
rename from java/res/xml/kbd_phone_symbols.xml
rename to java/res/xml/kbd_phone_shift.xml
diff --git a/java/src/com/android/inputmethod/accessibility/KeyCodeDescriptionMapper.java b/java/src/com/android/inputmethod/accessibility/KeyCodeDescriptionMapper.java
index d196c8955b45c7591d164bc859372ee21463c48b..ec4287dda8b152906c1953909f93396328b4c2a3 100644
--- a/java/src/com/android/inputmethod/accessibility/KeyCodeDescriptionMapper.java
+++ b/java/src/com/android/inputmethod/accessibility/KeyCodeDescriptionMapper.java
@@ -164,7 +164,7 @@ public class KeyCodeDescriptionMapper {
             return context.getString(R.string.spoken_description_to_symbol);
         } else if (id.isSymbolsKeyboard()) {
             return context.getString(R.string.spoken_description_to_alpha);
-        } else if (id.isPhoneSymbolsKeyboard()) {
+        } else if (id.isPhoneShiftKeyboard()) {
             return context.getString(R.string.spoken_description_to_numeric);
         } else if (id.isPhoneKeyboard()) {
             return context.getString(R.string.spoken_description_to_symbol);
diff --git a/java/src/com/android/inputmethod/keyboard/KeyboardId.java b/java/src/com/android/inputmethod/keyboard/KeyboardId.java
index a71e31ec6ce7b74ba3626ba585888e54f5a5083d..3f30165aaee7c80856e0b0e3fdf919343e08e6a4 100644
--- a/java/src/com/android/inputmethod/keyboard/KeyboardId.java
+++ b/java/src/com/android/inputmethod/keyboard/KeyboardId.java
@@ -119,12 +119,6 @@ public class KeyboardId {
                 mWidth, mMode, mAttribute, false, F2KEY_MODE_NONE, false, false, false, false);
     }
 
-    public KeyboardId cloneWithNewLayout(String xmlName, int xmlId) {
-        return new KeyboardId(xmlName, xmlId, mLocale, mOrientation, mWidth, mMode, mAttribute,
-                mHasSettingsKey, mF2KeyMode, mClobberSettingsKey, mVoiceKeyEnabled, mHasVoiceKey,
-                mEnableShiftLock);
-    }
-
     public KeyboardId cloneWithNewGeometry(int orientation, int width) {
         if (mWidth == width)
             return this;
@@ -153,8 +147,8 @@ public class KeyboardId {
         return mMode == MODE_PHONE;
     }
 
-    public boolean isPhoneSymbolsKeyboard() {
-        return mXmlId == R.xml.kbd_phone_symbols;
+    public boolean isPhoneShiftKeyboard() {
+        return mXmlId == R.xml.kbd_phone_shift;
     }
 
     public boolean isNumberKeyboard() {
diff --git a/java/src/com/android/inputmethod/keyboard/KeyboardSwitcher.java b/java/src/com/android/inputmethod/keyboard/KeyboardSwitcher.java
index 088c9d140c5fe69fb75ea719f3e20c18b39fbcaa..552a3cd30bcdcd0c1a1ff8398302c0fe0b330d5f 100644
--- a/java/src/com/android/inputmethod/keyboard/KeyboardSwitcher.java
+++ b/java/src/com/android/inputmethod/keyboard/KeyboardSwitcher.java
@@ -67,20 +67,17 @@ public class KeyboardSwitcher implements SharedPreferences.OnSharedPreferenceCha
     private ShiftKeyState mShiftKeyState = new ShiftKeyState("Shift");
     private ModifierKeyState mSymbolKeyState = new ModifierKeyState("Symbol");
 
-    private KeyboardId mSymbolsId;
-    private KeyboardId mSymbolsShiftedId;
+    private KeyboardId mMainKeyboardId;
+    private KeyboardId mSymbolsKeyboardId;
+    private KeyboardId mSymbolsShiftedKeyboardId;
 
     private KeyboardId mCurrentId;
     private final HashMap<KeyboardId, SoftReference<LatinKeyboard>> mKeyboardCache =
             new HashMap<KeyboardId, SoftReference<LatinKeyboard>>();
 
-    private EditorInfo mAttribute;
-    private boolean mIsSymbols;
     /** mIsAutoCorrectionActive indicates that auto corrected word will be input instead of
      * what user actually typed. */
     private boolean mIsAutoCorrectionActive;
-    private boolean mVoiceKeyEnabled;
-    private boolean mVoiceButtonOnPrimary;
 
     // TODO: Encapsulate these state handling to separate class and combine with ShiftKeyState
     // and ModifierKeyState.
@@ -94,8 +91,6 @@ public class KeyboardSwitcher implements SharedPreferences.OnSharedPreferenceCha
     private static final int SWITCH_STATE_CHORDING_SYMBOL = 6;
     private int mSwitchState = SWITCH_STATE_ALPHA;
 
-    // Indicates whether or not we have the settings key in option of settings
-    private boolean mSettingsKeyEnabledInSettings;
     private static final int SETTINGS_KEY_MODE_AUTO = R.string.settings_key_mode_auto;
     private static final int SETTINGS_KEY_MODE_ALWAYS_SHOW =
             R.string.settings_key_mode_always_show;
@@ -151,42 +146,21 @@ public class KeyboardSwitcher implements SharedPreferences.OnSharedPreferenceCha
     public void loadKeyboard(EditorInfo attribute, Settings.Values settings) {
         mSwitchState = SWITCH_STATE_ALPHA;
         try {
-            loadKeyboardInternal(attribute, settings.isVoiceButtonEnabled(attribute),
-                    settings.isVoiceButtonOnPrimary(), false);
+            final boolean voiceKeyEnabled = settings.isVoiceKeyEnabled(attribute);
+            final boolean voiceKeyOnMain = settings.isVoiceKeyOnMain();
+            mMainKeyboardId = getKeyboardId(
+                    attribute, false, false, voiceKeyEnabled, voiceKeyOnMain);
+            mSymbolsKeyboardId = getKeyboardId(
+                    attribute, true, false, voiceKeyEnabled, voiceKeyOnMain);
+            mSymbolsShiftedKeyboardId = getKeyboardId(
+                    attribute, true, true, voiceKeyEnabled, voiceKeyOnMain);
+            setKeyboard(getKeyboard(mMainKeyboardId));
         } catch (RuntimeException e) {
-            // Get KeyboardId to record which keyboard has been failed to load.
-            final KeyboardId id = getKeyboardId(attribute, false);
-            Log.w(TAG, "loading keyboard failed: " + id, e);
-            LatinImeLogger.logOnException(id.toString(), e);
+            Log.w(TAG, "loading keyboard failed: " + mMainKeyboardId, e);
+            LatinImeLogger.logOnException(mMainKeyboardId.toString(), e);
         }
     }
 
-    private void loadKeyboardInternal(EditorInfo attribute, boolean voiceButtonEnabled,
-            boolean voiceButtonOnPrimary, boolean isSymbols) {
-        if (mKeyboardView == null) return;
-
-        mAttribute = attribute;
-        mVoiceKeyEnabled = voiceButtonEnabled;
-        mVoiceButtonOnPrimary = voiceButtonOnPrimary;
-        mIsSymbols = isSymbols;
-        // Update the settings key state because number of enabled IMEs could have been changed
-        mSettingsKeyEnabledInSettings = getSettingsKeyMode(mPrefs, mInputMethodService);
-        final KeyboardId id = getKeyboardId(attribute, isSymbols);
-
-        // Note: This comment is only applied for phone number keyboard layout.
-        // On non-xlarge device, "@integer/key_switch_alpha_symbol" key code is used to switch
-        // between "phone keyboard" and "phone symbols keyboard".  But on xlarge device,
-        // "@integer/key_shift" key code is used for that purpose in order to properly display
-        // "more" and "locked more" key labels.  To achieve these behavior, we should initialize
-        // mSymbolsId and mSymbolsShiftedId to "phone keyboard" and "phone symbols keyboard"
-        // respectively here for xlarge device's layout switching.
-        mSymbolsId = makeSiblingKeyboardId(id, R.xml.kbd_symbols, R.xml.kbd_phone);
-        mSymbolsShiftedId = makeSiblingKeyboardId(
-                id, R.xml.kbd_symbols_shift, R.xml.kbd_phone_symbols);
-
-        setKeyboard(getKeyboard(id));
-    }
-
     @SuppressWarnings("unused")
     public void onSizeChanged(int w, int h, int oldw, int oldh) {
         final int width = mInputMethodService.getWindow().getWindow().getDecorView().getWidth();
@@ -255,45 +229,40 @@ public class KeyboardSwitcher implements SharedPreferences.OnSharedPreferenceCha
         return keyboard;
     }
 
-    private boolean hasVoiceKey(boolean isSymbols) {
-        return mVoiceKeyEnabled && (isSymbols != mVoiceButtonOnPrimary);
-    }
-
-    private boolean hasSettingsKey(EditorInfo attribute) {
-        return mSettingsKeyEnabledInSettings
-            && !Utils.inPrivateImeOptions(mInputMethodService.getPackageName(),
+    private static boolean hasSettingsKey(SharedPreferences prefs, Context context,
+            EditorInfo attribute) {
+        return getSettingsKeyMode(prefs, context)
+            && !Utils.inPrivateImeOptions(context.getPackageName(),
                     LatinIME.IME_OPTION_NO_SETTINGS_KEY, attribute);
     }
 
-    private KeyboardId getKeyboardId(EditorInfo attribute, boolean isSymbols) {
+    private KeyboardId getKeyboardId(EditorInfo attribute, final boolean isSymbols,
+            final boolean isShift, final boolean voiceKeyEnabled, final boolean voiceKeyOnMain) {
         final int mode = Utils.getKeyboardMode(attribute);
-        final boolean hasVoiceKey = hasVoiceKey(isSymbols);
+        final boolean hasVoiceKey = voiceKeyEnabled && (isSymbols != voiceKeyOnMain);
         final int xmlId;
         final boolean enableShiftLock;
 
-        if (isSymbols) {
-            if (mode == KeyboardId.MODE_PHONE) {
-                xmlId = R.xml.kbd_phone_symbols;
-            } else if (mode == KeyboardId.MODE_NUMBER) {
-                // Note: MODE_NUMBER keyboard layout has no "switch alpha symbol" key.
-                xmlId = R.xml.kbd_number;
-            } else {
-                xmlId = R.xml.kbd_symbols;
-            }
+        switch (mode) {
+        case KeyboardId.MODE_PHONE:
+            xmlId = (isSymbols && isShift) ? R.xml.kbd_phone_shift : R.xml.kbd_phone;
+            enableShiftLock = true;
+            break;
+        case KeyboardId.MODE_NUMBER:
+            xmlId = R.xml.kbd_number;
             enableShiftLock = false;
-        } else {
-            if (mode == KeyboardId.MODE_PHONE) {
-                xmlId = R.xml.kbd_phone;
-                enableShiftLock = false;
-            } else if (mode == KeyboardId.MODE_NUMBER) {
-                xmlId = R.xml.kbd_number;
-                enableShiftLock = false;
+            break;
+        default:
+            if (isSymbols) {
+                xmlId = isShift ? R.xml.kbd_symbols_shift : R.xml.kbd_symbols;
             } else {
                 xmlId = R.xml.kbd_qwerty;
-                enableShiftLock = true;
             }
+            enableShiftLock = true;
+            break;
         }
-        final boolean hasSettingsKey = hasSettingsKey(attribute);
+
+        final boolean hasSettingsKey = hasSettingsKey(mPrefs, mInputMethodService, attribute);
         final int f2KeyMode = getF2KeyMode(mPrefs, mInputMethodService, attribute);
         final boolean clobberSettingsKey = Utils.inPrivateImeOptions(
                 mInputMethodService.getPackageName(), LatinIME.IME_OPTION_NO_SETTINGS_KEY,
@@ -305,16 +274,10 @@ public class KeyboardSwitcher implements SharedPreferences.OnSharedPreferenceCha
         final Locale locale = mSubtypeSwitcher.getInputLocale();
         return new KeyboardId(
                 res.getResourceEntryName(xmlId), xmlId, locale, orientation, mWindowWidth,
-                mode, attribute, hasSettingsKey, f2KeyMode, clobberSettingsKey, mVoiceKeyEnabled,
+                mode, attribute, hasSettingsKey, f2KeyMode, clobberSettingsKey, voiceKeyEnabled,
                 hasVoiceKey, enableShiftLock);
     }
 
-    private KeyboardId makeSiblingKeyboardId(KeyboardId base, int alphabet, int phone) {
-        final int xmlId = base.mMode == KeyboardId.MODE_PHONE ? phone : alphabet;
-        final String xmlName = mInputMethodService.getResources().getResourceEntryName(xmlId);
-        return base.cloneWithNewLayout(xmlName, xmlId);
-    }
-
     public int getKeyboardMode() {
         return mCurrentId != null ? mCurrentId.mMode : KeyboardId.MODE_TEXT;
     }
@@ -602,13 +565,15 @@ public class KeyboardSwitcher implements SharedPreferences.OnSharedPreferenceCha
         if (isAlphabetMode())
             return;
         final LatinKeyboard keyboard;
-        if (mCurrentId.equals(mSymbolsId) || !mCurrentId.equals(mSymbolsShiftedId)) {
-            keyboard = getKeyboard(mSymbolsShiftedId);
-            // Symbol shifted keyboard has an ALT key that has a caps lock style indicator. To
-            // enable the indicator, we need to call setShiftLocked(true).
-            keyboard.setShiftLocked(true);
+        if (mCurrentId.equals(mSymbolsKeyboardId)
+                || !mCurrentId.equals(mSymbolsShiftedKeyboardId)) {
+            keyboard = getKeyboard(mSymbolsShiftedKeyboardId);
+            // Symbol keyboard may have an ALT key that has a caps lock style indicator (a.k.a.
+            // sticky shift key). To show or dismiss the indicator, we need to call setShiftLocked()
+            // that takes care of the current keyboard having such ALT key or not.
+            keyboard.setShiftLocked(hasStickyShiftKey(keyboard));
         } else {
-            keyboard = getKeyboard(mSymbolsId);
+            keyboard = getKeyboard(mSymbolsKeyboardId);
             // Symbol keyboard has an ALT key that has a caps lock style indicator. To disable the
             // indicator, we need to call setShiftLocked(false).
             keyboard.setShiftLocked(false);
@@ -616,6 +581,14 @@ public class KeyboardSwitcher implements SharedPreferences.OnSharedPreferenceCha
         setKeyboard(keyboard);
     }
 
+    private static boolean hasStickyShiftKey(Keyboard keyboard) {
+        for (final Key shiftKey : keyboard.getShiftKeys()) {
+            if (shiftKey.mSticky)
+                return true;
+        }
+        return false;
+    }
+
     public boolean isInMomentarySwitchState() {
         return mSwitchState == SWITCH_STATE_MOMENTARY_ALPHA_AND_SYMBOL
                 || mSwitchState == SWITCH_STATE_MOMENTARY_SYMBOL_AND_MORE;
@@ -630,10 +603,11 @@ public class KeyboardSwitcher implements SharedPreferences.OnSharedPreferenceCha
     }
 
     private void toggleKeyboardMode() {
-        loadKeyboardInternal(mAttribute, mVoiceKeyEnabled, mVoiceButtonOnPrimary, !mIsSymbols);
-        if (mIsSymbols) {
+        if (mCurrentId.equals(mMainKeyboardId)) {
+            setKeyboard(getKeyboard(mSymbolsKeyboardId));
             mSwitchState = SWITCH_STATE_SYMBOL_BEGIN;
         } else {
+            setKeyboard(getKeyboard(mMainKeyboardId));
             mSwitchState = SWITCH_STATE_ALPHA;
         }
     }
@@ -684,10 +658,10 @@ public class KeyboardSwitcher implements SharedPreferences.OnSharedPreferenceCha
             // {@link #SWITCH_STATE_MOMENTARY}.
             if (code == Keyboard.CODE_SWITCH_ALPHA_SYMBOL) {
                 // Detected only the mode change key has been pressed, and then released.
-                if (mIsSymbols) {
-                    mSwitchState = SWITCH_STATE_SYMBOL_BEGIN;
-                } else {
+                if (mCurrentId.equals(mMainKeyboardId)) {
                     mSwitchState = SWITCH_STATE_ALPHA;
+                } else {
+                    mSwitchState = SWITCH_STATE_SYMBOL_BEGIN;
                 }
             } else if (getPointerCount() == 1) {
                 // Snap back to the previous keyboard mode if the user pressed the mode change key
@@ -800,8 +774,6 @@ public class KeyboardSwitcher implements SharedPreferences.OnSharedPreferenceCha
             final int layoutId = getKeyboardThemeIndex(mInputMethodService, sharedPreferences);
             postSetInputView(createInputView(layoutId, false));
         } else if (Settings.PREF_SETTINGS_KEY.equals(key)) {
-            mSettingsKeyEnabledInSettings = getSettingsKeyMode(sharedPreferences,
-                    mInputMethodService);
             postSetInputView(createInputView(mThemeIndex, true));
         }
     }
diff --git a/java/src/com/android/inputmethod/latin/Settings.java b/java/src/com/android/inputmethod/latin/Settings.java
index dbab227db972953f06130d974aa72281b3bdae19..e44ae29d92b184f3c2a5d90e0c9778cf52133da3 100644
--- a/java/src/com/android/inputmethod/latin/Settings.java
+++ b/java/src/com/android/inputmethod/latin/Settings.java
@@ -118,8 +118,8 @@ public class Settings extends InputMethodSettingsActivity
         public final boolean mBigramPredictionEnabled;
         public final boolean mUseContactsDict;
 
-        private final boolean mVoiceButtonEnabled;
-        private final boolean mVoiceButtonOnPrimary;
+        private final boolean mVoiceKeyEnabled;
+        private final boolean mVoiceKeyOnMain;
 
         public Values(final SharedPreferences prefs, final Context context,
                 final String localeStr) {
@@ -183,8 +183,8 @@ public class Settings extends InputMethodSettingsActivity
             final String voiceModeMain = res.getString(R.string.voice_mode_main);
             final String voiceModeOff = res.getString(R.string.voice_mode_off);
             final String voiceMode = prefs.getString(PREF_VOICE_SETTINGS_KEY, voiceModeMain);
-            mVoiceButtonEnabled = voiceMode != null && !voiceMode.equals(voiceModeOff);
-            mVoiceButtonOnPrimary = voiceMode != null && voiceMode.equals(voiceModeMain);
+            mVoiceKeyEnabled = voiceMode != null && !voiceMode.equals(voiceModeOff);
+            mVoiceKeyOnMain = voiceMode != null && voiceMode.equals(voiceModeMain);
 
             Utils.setSystemLocale(res, savedLocale);
         }
@@ -284,15 +284,15 @@ public class Settings extends InputMethodSettingsActivity
             return builder.setIsPunctuationSuggestions().build();
         }
 
-        public boolean isVoiceButtonEnabled(EditorInfo attribute) {
+        public boolean isVoiceKeyEnabled(EditorInfo attribute) {
             final boolean shortcutImeEnabled = SubtypeSwitcher.getInstance().isShortcutImeEnabled();
             final int inputType = (attribute != null) ? attribute.inputType : 0;
-            return shortcutImeEnabled && mVoiceButtonEnabled
+            return shortcutImeEnabled && mVoiceKeyEnabled
                     && !InputTypeCompatUtils.isPasswordInputType(inputType);
         }
 
-        public boolean isVoiceButtonOnPrimary() {
-            return mVoiceButtonOnPrimary;
+        public boolean isVoiceKeyOnMain() {
+            return mVoiceKeyOnMain;
         }
     }