diff --git a/java/res/xml-sw600dp/key_styles_common.xml b/java/res/xml-sw600dp/key_styles_common.xml
index e0676e8909533393941b6e7e92c3e2c32260cc85..0d54dfedb6c8cf576043681a3846efa61b7e96e2 100644
--- a/java/res/xml-sw600dp/key_styles_common.xml
+++ b/java/res/xml-sw600dp/key_styles_common.xml
@@ -98,7 +98,6 @@
         latin:code="!code/key_shortcut"
         latin:keyIcon="iconShortcutKey"
         latin:keyIconDisabled="iconDisabledShortcutKey"
-        latin:keyLabelFlags="preserveCase"
         latin:keyActionFlags="noKeyPreview|altCodeWhileTyping"
         latin:backgroundType="functional" />
     <key-style
@@ -132,7 +131,6 @@
         latin:styleName="toSymbolKeyStyle"
         latin:code="!code/key_switch_alpha_symbol"
         latin:keyLabel="!label/label_to_symbol_key"
-        latin:keyLabelFlags="preserveCase"
         latin:keyActionFlags="noKeyPreview"
         latin:backgroundType="functional" />
     <key-style
@@ -146,14 +144,12 @@
         latin:styleName="toMoreSymbolKeyStyle"
         latin:code="!code/key_shift"
         latin:keyLabel="!label/label_to_more_symbol_for_tablet_key"
-        latin:keyLabelFlags="preserveCase"
         latin:keyActionFlags="noKeyPreview"
         latin:backgroundType="functional" />
     <key-style
         latin:styleName="backFromMoreSymbolKeyStyle"
         latin:code="!code/key_shift"
         latin:keyLabel="!label/label_to_symbol_key"
-        latin:keyLabelFlags="preserveCase"
         latin:keyActionFlags="noKeyPreview"
         latin:backgroundType="functional" />
     <key-style
diff --git a/java/res/xml-sw768dp/key_styles_common.xml b/java/res/xml-sw768dp/key_styles_common.xml
index c3e2fd93dfc366f1c5713815f13099eb33496929..596bccb47b63095392d167877e3e56cdbf706704 100644
--- a/java/res/xml-sw768dp/key_styles_common.xml
+++ b/java/res/xml-sw768dp/key_styles_common.xml
@@ -97,7 +97,6 @@
         latin:code="!code/key_shortcut"
         latin:keyIcon="iconShortcutKey"
         latin:keyIconDisabled="iconDisabledShortcutKey"
-        latin:keyLabelFlags="preserveCase"
         latin:keyActionFlags="noKeyPreview|altCodeWhileTyping"
         latin:backgroundType="functional" />
     <key-style
@@ -131,7 +130,7 @@
         latin:styleName="toSymbolKeyStyle"
         latin:code="!code/key_switch_alpha_symbol"
         latin:keyLabel="!label/label_to_symbol_key"
-        latin:keyLabelFlags="fontNormal|preserveCase"
+        latin:keyLabelFlags="fontNormal"
         latin:keyActionFlags="noKeyPreview"
         latin:backgroundType="functional" />
     <key-style
@@ -145,14 +144,14 @@
         latin:styleName="toMoreSymbolKeyStyle"
         latin:code="!code/key_shift"
         latin:keyLabel="!label/label_to_more_symbol_for_tablet_key"
-        latin:keyLabelFlags="fontNormal|preserveCase"
+        latin:keyLabelFlags="fontNormal"
         latin:keyActionFlags="noKeyPreview"
         latin:backgroundType="functional" />
     <key-style
         latin:styleName="backFromMoreSymbolKeyStyle"
         latin:code="!code/key_shift"
         latin:keyLabel="!label/label_to_symbol_key"
-        latin:keyLabelFlags="fontNormal|preserveCase"
+        latin:keyLabelFlags="fontNormal"
         latin:keyActionFlags="noKeyPreview"
         latin:backgroundType="functional" />
     <key-style
diff --git a/java/res/xml/key_styles_common.xml b/java/res/xml/key_styles_common.xml
index ec011d08bdd80459fb16a4db91e59d9799cea2af..f8b6e37fc86811f3b651922d9eb034adee14cd37 100644
--- a/java/res/xml/key_styles_common.xml
+++ b/java/res/xml/key_styles_common.xml
@@ -34,7 +34,7 @@
         <default>
             <key-style
                 latin:styleName="f1MoreKeysStyle"
-                latin:keyLabelFlags="hasPopupHint|preserveCase"
+                latin:keyLabelFlags="hasPopupHint"
                 latin:moreKeys="!label/settings_as_more_key"
                 latin:backgroundType="functional" />
         </default>
@@ -122,7 +122,6 @@
         latin:code="!code/key_shortcut"
         latin:keyIcon="iconShortcutKey"
         latin:keyIconDisabled="iconDisabledShortcutKey"
-        latin:keyLabelFlags="preserveCase"
         latin:keyActionFlags="noKeyPreview|altCodeWhileTyping"
         latin:altCode="!code/key_space"
         latin:parentStyle="f1MoreKeysStyle" />
@@ -159,7 +158,7 @@
                 latin:code="!code/key_switch_alpha_symbol"
                 latin:keyIcon="iconShortcutForLabel"
                 latin:keyLabel="!label/label_to_symbol_with_microphone_key"
-                latin:keyLabelFlags="withIconRight|preserveCase"
+                latin:keyLabelFlags="withIconRight"
                 latin:keyActionFlags="noKeyPreview"
                 latin:backgroundType="functional" />
         </case>
@@ -168,7 +167,6 @@
                 latin:styleName="toSymbolKeyStyle"
                 latin:code="!code/key_switch_alpha_symbol"
                 latin:keyLabel="!label/label_to_symbol_key"
-                latin:keyLabelFlags="preserveCase"
                 latin:keyActionFlags="noKeyPreview"
                 latin:backgroundType="functional" />
         </default>
@@ -184,21 +182,19 @@
         latin:styleName="toMoreSymbolKeyStyle"
         latin:code="!code/key_shift"
         latin:keyLabel="!label/label_to_more_symbol_key"
-        latin:keyLabelFlags="preserveCase"
         latin:keyActionFlags="noKeyPreview"
         latin:backgroundType="functional" />
     <key-style
         latin:styleName="backFromMoreSymbolKeyStyle"
         latin:code="!code/key_shift"
         latin:keyLabel="!label/label_to_symbol_key"
-        latin:keyLabelFlags="preserveCase"
         latin:keyActionFlags="noKeyPreview"
         latin:backgroundType="functional" />
     <key-style
         latin:styleName="punctuationKeyStyle"
         latin:keyLabel="."
         latin:keyHintLabel="!label/keyhintlabel_for_punctuation"
-        latin:keyLabelFlags="hasPopupHint|preserveCase"
+        latin:keyLabelFlags="hasPopupHint"
         latin:moreKeys="!label/more_keys_for_punctuation"
         latin:backgroundType="functional" />
 </merge>
diff --git a/java/res/xml/key_styles_enter.xml b/java/res/xml/key_styles_enter.xml
index f4c7ad46bbf03f86877154934bf9dfd91ee5cc1c..506ed47d6020d99d4027778f872dff17d869ff90 100644
--- a/java/res/xml/key_styles_enter.xml
+++ b/java/res/xml/key_styles_enter.xml
@@ -94,7 +94,7 @@
         latin:styleName="defaultEnterKeyStyle"
         latin:code="!code/key_enter"
         latin:keyIcon="iconReturnKey"
-        latin:keyLabelFlags="autoXScale|preserveCase|followKeyLabelRatio"
+        latin:keyLabelFlags="autoXScale|followKeyLabelRatio"
         latin:keyActionFlags="noKeyPreview"
         latin:backgroundType="functional"
         latin:parentStyle="navigateMoreKeysStyle" />
diff --git a/java/res/xml/rowkeys_georgian1.xml b/java/res/xml/rowkeys_georgian1.xml
index fcdf3fa588d9bdb68c80e4dcfb5fc3d1649ab757..d31a4c79cbc67e2bba17a02fb4de17e1fe737d40 100644
--- a/java/res/xml/rowkeys_georgian1.xml
+++ b/java/res/xml/rowkeys_georgian1.xml
@@ -27,55 +27,45 @@
         >
             <Key
                 latin:keyLabel="Q"
-                latin:keyLabelFlags="preserveCase"
                 latin:keyHintLabel="1"
                 latin:additionalMoreKeys="1" />
             <!-- U+10ED: "ჭ" GEORGIAN LETTER CHAR -->
             <Key
                 latin:keyLabel="&#x10ED;"
-                latin:keyLabelFlags="preserveCase"
                 latin:keyHintLabel="2"
                 latin:additionalMoreKeys="2" />
             <Key
                 latin:keyLabel="E"
-                latin:keyLabelFlags="preserveCase"
                 latin:keyHintLabel="3"
                 latin:additionalMoreKeys="3" />
             <!-- U+10E6: "ღ" GEORGIAN LETTER GHAN -->
             <Key
                 latin:keyLabel="&#x10E6;"
-                latin:keyLabelFlags="preserveCase"
                 latin:keyHintLabel="4"
                 latin:additionalMoreKeys="4" />
             <!-- U+10D7: "თ" GEORGIAN LETTER TAN -->
             <Key
                 latin:keyLabel="&#x10D7;"
-                latin:keyLabelFlags="preserveCase"
                 latin:keyHintLabel="5"
                 latin:additionalMoreKeys="5" />
             <Key
                 latin:keyLabel="Y"
-                latin:keyLabelFlags="preserveCase"
                 latin:keyHintLabel="6"
                 latin:additionalMoreKeys="6" />
             <Key
                 latin:keyLabel="U"
-                latin:keyLabelFlags="preserveCase"
                 latin:keyHintLabel="7"
                 latin:additionalMoreKeys="7" />
             <Key
                 latin:keyLabel="I"
-                latin:keyLabelFlags="preserveCase"
                 latin:keyHintLabel="8"
                 latin:additionalMoreKeys="8" />
             <Key
                 latin:keyLabel="O"
-                latin:keyLabelFlags="preserveCase"
                 latin:keyHintLabel="9"
                 latin:additionalMoreKeys="9" />
             <Key
                 latin:keyLabel="P"
-                latin:keyLabelFlags="preserveCase"
                 latin:keyHintLabel="0"
                 latin:additionalMoreKeys="0" />
         </case>
@@ -83,13 +73,11 @@
             <!-- U+10E5: "ქ" GEORGIAN LETTER GHAN -->
             <Key
                 latin:keyLabel="&#x10E5;"
-                latin:keyLabelFlags="preserveCase"
                 latin:keyHintLabel="1"
                 latin:additionalMoreKeys="1" />
             <!-- U+10EC: "წ" GEORGIAN LETTER CIL -->
             <Key
                 latin:keyLabel="&#x10EC;"
-                latin:keyLabelFlags="preserveCase"
                 latin:keyHintLabel="2"
                 latin:additionalMoreKeys="2" />
             <!-- U+10D4: "ე" GEORGIAN LETTER EN
@@ -97,19 +85,16 @@
             <Key
                 latin:keyLabel="&#x10D4;"
                 latin:moreKeys="&#x10F1;"
-                latin:keyLabelFlags="preserveCase"
                 latin:keyHintLabel="3"
                 latin:additionalMoreKeys="3" />
             <!-- U+10E0: "რ" GEORGIAN LETTER RAE -->
             <Key
                 latin:keyLabel="&#x10E0;"
-                latin:keyLabelFlags="preserveCase"
                 latin:keyHintLabel="4"
                 latin:additionalMoreKeys="4" />
             <!-- U+10E2: "ტ" GEORGIAN LETTER TAR -->
             <Key
                 latin:keyLabel="&#x10E2;"
-                latin:keyLabelFlags="preserveCase"
                 latin:keyHintLabel="5"
                 latin:additionalMoreKeys="5" />
             <!-- U+10E7: "ყ" GEORGIAN LETTER QAR
@@ -117,13 +102,11 @@
             <Key
                 latin:keyLabel="&#x10E7;"
                 latin:moreKeys="&#x10F8;"
-                latin:keyLabelFlags="preserveCase"
                 latin:keyHintLabel="6"
                 latin:additionalMoreKeys="6" />
             <!-- U+10E3: "უ" GEORGIAN LETTER UN -->
             <Key
                 latin:keyLabel="&#x10E3;"
-                latin:keyLabelFlags="preserveCase"
                 latin:keyHintLabel="7"
                 latin:additionalMoreKeys="7" />
             <!-- U+10D8: "ი" GEORGIAN LETTER IN
@@ -131,19 +114,16 @@
             <Key
                 latin:keyLabel="&#x10D8;"
                 latin:moreKeys="&#x10F2;"
-                latin:keyLabelFlags="preserveCase"
                 latin:keyHintLabel="8"
                 latin:additionalMoreKeys="8" />
             <!-- U+10DD: "ო" GEORGIAN LETTER ON -->
             <Key
                 latin:keyLabel="&#x10DD;"
-                latin:keyLabelFlags="preserveCase"
                 latin:keyHintLabel="9"
                 latin:additionalMoreKeys="9" />
             <!-- U+10DE: "პ" GEORGIAN LETTER PAR -->
             <Key
                 latin:keyLabel="&#x10DE;"
-                latin:keyLabelFlags="preserveCase"
                 latin:keyHintLabel="0"
                 latin:additionalMoreKeys="0" />
         </default>
diff --git a/java/res/xml/rowkeys_georgian2.xml b/java/res/xml/rowkeys_georgian2.xml
index 5673b900212271f89f5fe7eaca3d991dd3ba3c8c..cdccda31fb858d9f16a8b01fc38d6a14adbd3b9f 100644
--- a/java/res/xml/rowkeys_georgian2.xml
+++ b/java/res/xml/rowkeys_georgian2.xml
@@ -26,82 +26,64 @@
             latin:keyboardLayoutSetElement="alphabetManualShifted|alphabetShiftLocked|alphabetShiftLockShifted"
         >
             <Key
-                latin:keyLabel="A"
-                latin:keyLabelFlags="preserveCase" />
+                latin:keyLabel="A" />
             <!-- U+10E8: "შ" GEORGIAN LETTER SHIN -->
             <Key
-                latin:keyLabel="&#x10E8;"
-                latin:keyLabelFlags="preserveCase" />
+                latin:keyLabel="&#x10E8;" />
             <Key
-                latin:keyLabel="D"
-                latin:keyLabelFlags="preserveCase" />
+                latin:keyLabel="D" />
             <Key
-                latin:keyLabel="F"
-                latin:keyLabelFlags="preserveCase" />
+                latin:keyLabel="F" />
             <Key
-                latin:keyLabel="G"
-                latin:keyLabelFlags="preserveCase" />
+                latin:keyLabel="G" />
             <Key
-                latin:keyLabel="H"
-                latin:keyLabelFlags="preserveCase" />
+                latin:keyLabel="H" />
             <!-- U+10DF: "ჟ" GEORGIAN LETTER ZHAR -->
             <Key
-                latin:keyLabel="&#x10DF;"
-                latin:keyLabelFlags="preserveCase" />
+                latin:keyLabel="&#x10DF;" />
             <Key
-                latin:keyLabel="K"
-                latin:keyLabelFlags="preserveCase" />
+                latin:keyLabel="K" />
             <Key
-                latin:keyLabel="L"
-                latin:keyLabelFlags="preserveCase" />
+                latin:keyLabel="L" />
         </case>
         <default>
             <!-- U+10D0: "ა" GEORGIAN LETTER AN
                  U+10FA: "ჺ" GEORGIAN LETTER AIN -->
             <Key
                 latin:keyLabel="&#x10D0;"
-                latin:moreKeys="&#x10FA;"
-                latin:keyLabelFlags="preserveCase" />
+                latin:moreKeys="&#x10FA;" />
             <!-- U+10E1: "ს" GEORGIAN LETTER SAN -->
             <Key
-                latin:keyLabel="&#x10E1;"
-                latin:keyLabelFlags="preserveCase" />
+                latin:keyLabel="&#x10E1;" />
             <!-- U+10D3: "დ" GEORGIAN LETTER DON -->
             <Key
-                latin:keyLabel="&#x10D3;"
-                latin:keyLabelFlags="preserveCase" />
+                latin:keyLabel="&#x10D3;" />
             <!-- U+10E4: "ფ" GEORGIAN LETTER PHAR
                  U+10F6: "ჶ" GEORGIAN LETTER FI -->
             <Key
                 latin:keyLabel="&#x10E4;"
-                latin:moreKeys="&#x10F6;"
-                latin:keyLabelFlags="preserveCase" />
+                latin:moreKeys="&#x10F6;" />
             <!-- U+10D2: "გ" GEORGIAN LETTER GAN
                  U+10F9: "ჹ" GEORGIAN LETTER TURNED GAN -->
             <Key
                 latin:keyLabel="&#x10D2;"
-                latin:moreKeys="&#x10F9;"
-                latin:keyLabelFlags="preserveCase" />
+                latin:moreKeys="&#x10F9;" />
             <!-- U+10F0: "ჰ" GEORGIAN LETTER HAE
                  U+10F5: "ჵ" GEORGIAN LETTER HOE -->
             <Key
                 latin:keyLabel="&#x10F0;"
-                latin:moreKeys="&#x10F5;"
-                latin:keyLabelFlags="preserveCase" />
+                latin:moreKeys="&#x10F5;" />
             <!-- U+10EF: "ჯ" GEORGIAN LETTER JHAN
                  U+10F7: "ჷ" GEORGIAN LETTER YN -->
             <Key
                 latin:keyLabel="&#x10EF;"
-                latin:moreKeys="&#x10F7;"
-                latin:keyLabelFlags="preserveCase" />
+                latin:moreKeys="&#x10F7;" />
             <!-- U+10D9: "კ" GEORGIAN LETTER KAN -->
             <Key
-                latin:keyLabel="&#x10D9;"
-                latin:keyLabelFlags="preserveCase" />
+                latin:keyLabel="&#x10D9;" />
             <!-- U+10DA: "ლ" GEORGIAN LETTER LAS -->
             <Key
-                latin:keyLabel="&#x10DA;"
-                latin:keyLabelFlags="preserveCase" />
+                latin:keyLabel="&#x10DA;" />
         </default>
     </switch>
 </merge>
diff --git a/java/res/xml/rowkeys_georgian3.xml b/java/res/xml/rowkeys_georgian3.xml
index 1c0f45befd515d1c68a0620711762a82b88d7dcf..a3714586fbe30d455f772659ff176413a2d7c3fa 100644
--- a/java/res/xml/rowkeys_georgian3.xml
+++ b/java/res/xml/rowkeys_georgian3.xml
@@ -27,63 +27,49 @@
         >
             <!-- U+10EB: "ძ" GEORGIAN LETTER JIL -->
             <Key
-                latin:keyLabel="&#x10EB;"
-                latin:keyLabelFlags="preserveCase" />
+                latin:keyLabel="&#x10EB;" />
             <Key
-                latin:keyLabel="X"
-                latin:keyLabelFlags="preserveCase" />
+                latin:keyLabel="X" />
             <!-- U+10E9: "ჩ" GEORGIAN LETTER CHIN -->
             <Key
-                latin:keyLabel="&#x10E9;"
-                latin:keyLabelFlags="preserveCase" />
+                latin:keyLabel="&#x10E9;" />
             <Key
-                latin:keyLabel="V"
-                latin:keyLabelFlags="preserveCase" />
+                latin:keyLabel="V" />
             <Key
-                latin:keyLabel="B"
-                latin:keyLabelFlags="preserveCase" />
+                latin:keyLabel="B" />
             <Key
-                latin:keyLabel="N"
-                latin:keyLabelFlags="preserveCase" />
+                latin:keyLabel="N" />
             <Key
-                latin:keyLabel="M"
-                latin:keyLabelFlags="preserveCase" />
+                latin:keyLabel="M" />
         </case>
         <default>
             <!-- U+10D6: "ზ" GEORGIAN LETTER ZEN -->
             <Key
-                latin:keyLabel="&#x10D6;"
-                latin:keyLabelFlags="preserveCase" />
+                latin:keyLabel="&#x10D6;" />
             <!-- U+10EE: "ხ" GEORGIAN LETTER XAN
                  U+10F4: "ჴ" GEORGIAN LETTER HAR -->
             <Key
                 latin:keyLabel="&#x10EE;"
-                latin:moreKeys="&#x10F4;"
-                latin:keyLabelFlags="preserveCase" />
+                latin:moreKeys="&#x10F4;" />
             <!-- U+10EA: "ც" GEORGIAN LETTER CAN -->
             <Key
-                latin:keyLabel="&#x10EA;"
-                latin:keyLabelFlags="preserveCase" />
+                latin:keyLabel="&#x10EA;" />
             <!-- U+10D5: "ვ" GEORGIAN LETTER VIN
                  U+10F3: "ჳ" GEORGIAN LETTER WE -->
             <Key
                 latin:keyLabel="&#x10D5;"
-                latin:moreKeys="&#x10F3;"
-                latin:keyLabelFlags="preserveCase" />
+                latin:moreKeys="&#x10F3;" />
             <!-- U+10D1: "ბ" GEORGIAN LETTER BAN -->
             <Key
-                latin:keyLabel="&#x10D1;"
-                latin:keyLabelFlags="preserveCase" />
+                latin:keyLabel="&#x10D1;" />
             <!-- U+10DC: "ნ" GEORGIAN LETTER NAR
                  U+10FC: "ჼ" MODIFIER LETTER GEORGIAN NAR -->
             <Key
                 latin:keyLabel="&#x10DC;"
-                latin:moreKeys="&#x10FC;"
-                latin:keyLabelFlags="preserveCase" />
+                latin:moreKeys="&#x10FC;" />
             <!-- U+10DB: "მ" GEORGIAN LETTER MAN -->
             <Key
-                latin:keyLabel="&#x10DB;"
-                latin:keyLabelFlags="preserveCase" />
+                latin:keyLabel="&#x10DB;" />
         </default>
     </switch>
 </merge>
diff --git a/java/src/com/android/inputmethod/keyboard/internal/KeySpecParser.java b/java/src/com/android/inputmethod/keyboard/internal/KeySpecParser.java
index 8261400b2e54af30e74c7129dac49b7a109d5795..e24cfa0e75b075e853254e5e376943029c1405e7 100644
--- a/java/src/com/android/inputmethod/keyboard/internal/KeySpecParser.java
+++ b/java/src/com/android/inputmethod/keyboard/internal/KeySpecParser.java
@@ -76,7 +76,7 @@ public class KeySpecParser {
     }
 
     private static boolean hasIcon(String moreKeySpec) {
-        if (moreKeySpec.startsWith(PREFIX_ICON)) {
+        if (moreKeySpec.regionMatches(true, 0, PREFIX_ICON, 0, PREFIX_ICON.length())) {
             final int end = indexOfLabelEnd(moreKeySpec, 0);
             if (end > 0) {
                 return true;
@@ -88,8 +88,8 @@ public class KeySpecParser {
 
     private static boolean hasCode(String moreKeySpec) {
         final int end = indexOfLabelEnd(moreKeySpec, 0);
-        if (end > 0 && end + 1 < moreKeySpec.length()
-                && moreKeySpec.substring(end + 1).startsWith(PREFIX_CODE)) {
+        if (end > 0 && end + 1 < moreKeySpec.length() && moreKeySpec.regionMatches(
+                true, end + 1, PREFIX_CODE, 0, PREFIX_CODE.length())) {
             return true;
         }
         return false;
@@ -210,9 +210,9 @@ public class KeySpecParser {
 
     public static int parseCode(String text, KeyboardCodesSet codesSet, int defCode) {
         if (text == null) return defCode;
-        if (text.startsWith(PREFIX_CODE)) {
+        if (text.regionMatches(true, 0, PREFIX_CODE, 0, PREFIX_CODE.length())) {
             return codesSet.getCode(text.substring(PREFIX_CODE.length()));
-        } else if (text.startsWith(PREFIX_HEX)) {
+        } else if (text.regionMatches(true, 0, PREFIX_HEX, 0, PREFIX_HEX.length())) {
             return Integer.parseInt(text.substring(PREFIX_HEX.length()), 16);
         } else {
             return Integer.parseInt(text);
@@ -350,20 +350,22 @@ public class KeySpecParser {
                 throw new RuntimeException("too many @string/resource indirection: " + text);
             }
 
+            final int prefixLen = PREFIX_LABEL.length();
             final int size = text.length();
-            if (size < PREFIX_LABEL.length()) {
+            if (size < prefixLen) {
                 return text;
             }
 
             sb = null;
             for (int pos = 0; pos < size; pos++) {
                 final char c = text.charAt(pos);
-                if (text.startsWith(PREFIX_LABEL, pos) && labelsSet != null) {
+                if (text.regionMatches(true, pos, PREFIX_LABEL, 0, prefixLen)
+                        && labelsSet != null) {
                     if (sb == null) {
                         sb = new StringBuilder(text.substring(0, pos));
                     }
-                    final int end = searchLabelNameEnd(text, pos + PREFIX_LABEL.length());
-                    final String name = text.substring(pos + PREFIX_LABEL.length(), end);
+                    final int end = searchLabelNameEnd(text, pos + prefixLen);
+                    final String name = text.substring(pos + prefixLen, end);
                     sb.append(labelsSet.getLabel(name));
                     pos = end - 1;
                 } else if (c == ESCAPE_CHAR) {
@@ -389,8 +391,9 @@ public class KeySpecParser {
         final int size = text.length();
         for (int pos = start; pos < size; pos++) {
             final char c = text.charAt(pos);
-            // String resource name should be consisted of [a-z_0-9].
-            if ((c >= 'a' && c <= 'z') || c == '_' || (c >= '0' && c <= '9')) {
+            // Label name should be consisted of [a-zA-Z_0-9].
+            if ((c >= 'a' && c <= 'z') || c == '_' || (c >= '0' && c <= '9')
+                    || (c >= 'A' && c <= 'Z')) {
                 continue;
             }
             return pos;
@@ -442,17 +445,19 @@ public class KeySpecParser {
         if (moreKeys == null) {
             return defaultValue;
         }
+        final int keyLen = key.length();
         boolean foundValue = false;
         int value = defaultValue;
         for (int i = 0; i < moreKeys.length; i++) {
             final String moreKeySpec = moreKeys[i];
-            if (moreKeySpec == null || !moreKeySpec.startsWith(key)) {
+            if (moreKeySpec == null || !moreKeySpec.regionMatches(true, 0, key, 0, keyLen)) {
                 continue;
             }
             moreKeys[i] = null;
             try {
                 if (!foundValue) {
-                    value = Integer.parseInt(moreKeySpec.substring(key.length()));
+                    value = Integer.parseInt(moreKeySpec.substring(keyLen));
+                    foundValue = true;
                 }
             } catch (NumberFormatException e) {
                 throw new RuntimeException(
@@ -469,7 +474,7 @@ public class KeySpecParser {
         boolean value = false;
         for (int i = 0; i < moreKeys.length; i++) {
             final String moreKeySpec = moreKeys[i];
-            if (moreKeySpec == null || !moreKeySpec.equals(key)) {
+            if (moreKeySpec == null || !moreKeySpec.equalsIgnoreCase(key)) {
                 continue;
             }
             moreKeys[i] = null;
diff --git a/java/src/com/android/inputmethod/keyboard/internal/KeyboardCodesSet.java b/java/src/com/android/inputmethod/keyboard/internal/KeyboardCodesSet.java
index 736a96c568c178e7c492840300a4be8add918f8b..c10a394c1fa214adccfbc789083c94ae595a14df 100644
--- a/java/src/com/android/inputmethod/keyboard/internal/KeyboardCodesSet.java
+++ b/java/src/com/android/inputmethod/keyboard/internal/KeyboardCodesSet.java
@@ -33,7 +33,10 @@ public class KeyboardCodesSet {
     }
 
     public int getCode(final String name) {
-        final Integer id = sNameToIdMap.get(name);
+        Integer id = sNameToIdMap.get(name);
+        if (id == null) {
+            id = sNameToIdMap.get(name.toLowerCase());
+        }
         if (id == null) throw new RuntimeException("Unknown key code: " + name);
         return mCodes[id];
     }
diff --git a/java/src/com/android/inputmethod/keyboard/internal/KeyboardIconsSet.java b/java/src/com/android/inputmethod/keyboard/internal/KeyboardIconsSet.java
index ded89b1b8b46c1fcfdaee275bc8c219f7efcb1c6..07636249f9d4fb7505bb298e50de89a113f6ec01 100644
--- a/java/src/com/android/inputmethod/keyboard/internal/KeyboardIconsSet.java
+++ b/java/src/com/android/inputmethod/keyboard/internal/KeyboardIconsSet.java
@@ -36,7 +36,9 @@ public class KeyboardIconsSet {
 
     private static final HashMap<Integer, Integer> ATTR_ID_TO_ICON_ID
             = new HashMap<Integer, Integer>();
-    private static final HashMap<String, Integer> NAME_TO_ICON_ID = new HashMap<String, Integer>();
+    // Lower case icon name to icon id map.
+    private static final HashMap<String, Integer> sLowerCaseNameToIdsMap =
+            new HashMap<String, Integer>();
     private static final String[] ICON_NAMES = new String[NUM_ICONS + 1];
 
     private static final int ATTR_UNDEFINED = 0;
@@ -66,7 +68,7 @@ public class KeyboardIconsSet {
         if (attrId != ATTR_UNDEFINED) {
             ATTR_ID_TO_ICON_ID.put(attrId,  iconId);
         }
-        NAME_TO_ICON_ID.put(name, iconId);
+        sLowerCaseNameToIdsMap.put(name.toLowerCase(), iconId);
         ICON_NAMES[iconId] = name;
     }
 
@@ -94,7 +96,10 @@ public class KeyboardIconsSet {
     }
 
     public static int getIconId(final String name) {
-        final Integer iconId = NAME_TO_ICON_ID.get(name);
+        Integer iconId = sLowerCaseNameToIdsMap.get(name);
+        if (iconId == null) {
+            iconId = sLowerCaseNameToIdsMap.get(name.toLowerCase());
+        }
         if (iconId != null) {
             return iconId;
         }
diff --git a/java/src/com/android/inputmethod/keyboard/internal/KeyboardLabelsSet.java b/java/src/com/android/inputmethod/keyboard/internal/KeyboardLabelsSet.java
index 0518b07ff4da2ee2241d177d5b5d6ba92f6356a9..bd95848a9531aed6b24acee8cf10b1096507ddea 100644
--- a/java/src/com/android/inputmethod/keyboard/internal/KeyboardLabelsSet.java
+++ b/java/src/com/android/inputmethod/keyboard/internal/KeyboardLabelsSet.java
@@ -31,7 +31,8 @@ public final class KeyboardLabelsSet {
     // Language to labels map.
     private static final HashMap<String, String[]> sLocaleToLabelsMap =
             new HashMap<String, String[]>();
-    private static final HashMap<String, Integer> sNameToIdMap = new HashMap<String, Integer>();
+    private static final HashMap<String, Integer> sLowerCaseNameToIdsMap =
+            new HashMap<String, Integer>();
 
     private String[] mLabels;
     // Resource name to label map.
@@ -60,12 +61,21 @@ public final class KeyboardLabelsSet {
     }
 
     public String getLabel(final String name) {
-        if (mResourceNameToLabelsMap.containsKey(name)) {
-            return mResourceNameToLabelsMap.get(name);
+        String lowerCaseName = null;
+        String label = mResourceNameToLabelsMap.get(name);
+        if (label == null) {
+            lowerCaseName = name.toLowerCase();
+            label = mResourceNameToLabelsMap.get(lowerCaseName);
+        }
+        if (label != null) {
+            return label;
+        }
+        Integer id = sLowerCaseNameToIdsMap.get(name);
+        if (id == null) {
+            id = sLowerCaseNameToIdsMap.get(lowerCaseName); // lowerCaseName != null
         }
-        final Integer id = sNameToIdMap.get(name);
         if (id == null) throw new RuntimeException("Unknown label: " + name);
-        final String label = (id < mLabels.length) ? mLabels[id] : null;
+        label = (id < mLabels.length) ? mLabels[id] : null;
         return (label == null) ? LANGUAGE_DEFAULT[id] : label;
     }
 
@@ -2492,7 +2502,7 @@ public final class KeyboardLabelsSet {
     static {
         int id = 0;
         for (final String name : NAMES) {
-            sNameToIdMap.put(name, id++);
+            sLowerCaseNameToIdsMap.put(name, id++);
         }
 
         for (int i = 0; i < LANGUAGES_AND_LABELS.length; i += 2) {
diff --git a/tests/res/values/donottranslate.xml b/tests/res/values/donottranslate.xml
index 42181ed92387b566b8bff6dff1c234503b20eff5..875a55804f29b50f1d308f6aed3263d62fac996a 100644
--- a/tests/res/values/donottranslate.xml
+++ b/tests/res/values/donottranslate.xml
@@ -52,5 +52,11 @@
     <string name="multiple_labels_with_comma_and_escape_surrounded_by_spaces">" ab\\\\ , d\\\\\\, , g\\,i "</string>
     <string name="indirect_string">!label/multiple_chars</string>
     <string name="indirect_string_with_literal">x,!label/multiple_chars,y</string>
+    <string name="indirect2_string">!label/indirect_string</string>
     <string name="infinite_indirection">infinite,!label/infinite_indirection,loop</string>
+    <string name="upper_indirect_string">!LABEL/MULTIPLE_CHARS</string>
+    <string name="upper_indirect_string_with_literal">x,!LABEL/MULTIPLE_CHARS,y</string>
+    <string name="upper_indirect2_string">!LABEL/UPPER_INDIRECT_STRING</string>
+    <string name="upper_infinite_indirection">infinite,!LABEL/INFINITE_INDIRECTION,loop</string>
+    <string name="indirect_navigate_actions_as_more_key">!fixedColumnOrder!2,!label/action_previous_as_more_key,!label/action_next_as_more_key</string>
 </resources>
diff --git a/tests/src/com/android/inputmethod/keyboard/internal/KeySpecParserCsvTests.java b/tests/src/com/android/inputmethod/keyboard/internal/KeySpecParserCsvTests.java
index d780352b7c2daffb465dc2e5cbb5352b889e3d1b..093f75247831fa071d580f7278f58e5274abbbb4 100644
--- a/tests/src/com/android/inputmethod/keyboard/internal/KeySpecParserCsvTests.java
+++ b/tests/src/com/android/inputmethod/keyboard/internal/KeySpecParserCsvTests.java
@@ -17,7 +17,6 @@
 package com.android.inputmethod.keyboard.internal;
 
 import android.test.AndroidTestCase;
-import android.text.TextUtils;
 
 import java.lang.reflect.Field;
 import java.util.ArrayList;
@@ -32,6 +31,7 @@ public class KeySpecParserCsvTests extends AndroidTestCase {
         super.setUp();
 
         mLabelsSet.setLanguage(Locale.ENGLISH.getLanguage());
+        mLabelsSet.loadStringResources(getContext());
         final String[] testResourceNames = getAllResourceIdNames(
                 com.android.inputmethod.latin.tests.R.string.class);
         mLabelsSet.loadStringResourcesInternal(getTestContext(),
@@ -49,22 +49,30 @@ public class KeySpecParserCsvTests extends AndroidTestCase {
         return names.toArray(new String[names.size()]);
     }
 
-    private void assertTextArray(String message, String value, String ... expected) {
-        final String actual[] = KeySpecParser.parseCsvString(value, mLabelsSet);
-        if (expected.length == 0) {
-            assertNull(message + ": expected=null actual=" + Arrays.toString(actual),
-                    actual);
+    private static void assertArrayEquals(String message, Object[] expected, Object[] actual) {
+        if (expected == actual) {
             return;
         }
-        assertEquals(message + ": expected=" + Arrays.toString(expected)
-                + " actual=" + Arrays.toString(actual)
-                + ": result length", expected.length, actual.length);
-        for (int i = 0; i < actual.length; i++) {
-            final boolean equals = TextUtils.equals(expected[i], actual[i]);
-            assertTrue(format(message + ": result at " + i + ":", expected[i], actual[i]), equals);
+        if (expected == null || actual == null) {
+            assertEquals(message, Arrays.toString(expected), Arrays.toString(actual));
+            return;
+        }
+        if (expected.length != actual.length) {
+            assertEquals(message + " [length]", Arrays.toString(expected), Arrays.toString(actual));
+            return;
+        }
+        for (int i = 0; i < expected.length; i++) {
+            assertEquals(message + " [" + i + "]",
+                    Arrays.toString(expected), Arrays.toString(actual));
         }
     }
 
+    private void assertTextArray(String message, String value, String ... expectedArray) {
+        final String[] actual = KeySpecParser.parseCsvString(value, mLabelsSet);
+        final String[] expected = (expectedArray.length == 0) ? null : expectedArray;
+        assertArrayEquals(message, expected, actual);
+    }
+
     private void assertError(String message, String value, String ... expected) {
         try {
             assertTextArray(message, value, expected);
@@ -116,6 +124,7 @@ public class KeySpecParserCsvTests extends AndroidTestCase {
 
         assertTextArray("Incomplete resource reference 1", "label", "label");
         assertTextArray("Incomplete resource reference 2", "!label", "!label");
+        assertTextArray("Incomplete RESOURCE REFERENCE 2", "!LABEL", "!LABEL");
         assertTextArray("Incomplete resource reference 3", "label/", "label/");
         assertTextArray("Incomplete resource reference 4", "!" + SURROGATE2, "!" + SURROGATE2);
     }
@@ -150,7 +159,9 @@ public class KeySpecParserCsvTests extends AndroidTestCase {
 
         assertTextArray("Escaped !label", "\\!label", "\\!label");
         assertTextArray("Escaped !label/", "\\!label/", "\\!label/");
-        assertTextArray("Escaped !label/", "\\!label/empty_string", "\\!label/empty_string");
+        assertTextArray("Escaped !LABEL/", "\\!LABEL/", "\\!LABEL/");
+        assertTextArray("Escaped !label/name", "\\!label/empty_string", "\\!label/empty_string");
+        assertTextArray("Escaped !LABEL/NAME", "\\!LABEL/EMPTY_STRING", "\\!LABEL/EMPTY_STRING");
     }
 
     public void testParseCsvTextMulti() {
@@ -183,6 +194,8 @@ public class KeySpecParserCsvTests extends AndroidTestCase {
 
         assertTextArray("Multiple escaped !label", "\\!,\\!label/empty_string",
                 "\\!", "\\!label/empty_string");
+        assertTextArray("Multiple escaped !LABEL", "\\!,\\!LABEL/EMPTY_STRING",
+                "\\!", "\\!LABEL/EMPTY_STRING");
     }
 
     public void testParseCsvResourceError() {
@@ -193,11 +206,15 @@ public class KeySpecParserCsvTests extends AndroidTestCase {
     public void testParseCsvResourceZero() {
         assertTextArray("Empty string",
                 "!label/empty_string");
+        assertTextArray("EMPTY STRING",
+                "!LABEL/EMPTY_STRING");
     }
 
     public void testParseCsvResourceSingle() {
         assertTextArray("Single char",
                 "!label/single_char", "a");
+        assertTextArray("SINGLE CHAR",
+                "!LABEL/SINGLE_CHAR", "a");
         assertTextArray("Space",
                 "!label/space", " ");
         assertTextArray("Single label",
@@ -215,6 +232,8 @@ public class KeySpecParserCsvTests extends AndroidTestCase {
 
         assertTextArray("Escape and single char",
                 "\\\\!label/single_char", "\\\\a");
+        assertTextArray("Escape and SINGLE CHAR",
+                "\\\\!LABEL/SINGLE_CHAR", "\\\\a");
     }
 
     public void testParseCsvResourceSingleEscaped() {
@@ -247,6 +266,8 @@ public class KeySpecParserCsvTests extends AndroidTestCase {
     public void testParseCsvResourceMulti() {
         assertTextArray("Multiple chars",
                 "!label/multiple_chars", "a", "b", "c");
+        assertTextArray("MULTIPLE CHARS",
+                "!LABEL/MULTIPLE_CHARS", "a", "b", "c");
         assertTextArray("Multiple chars surrounded by spaces",
                 "!label/multiple_chars_surrounded_by_spaces",
                 " a ", " b ", " c ");
@@ -280,6 +301,8 @@ public class KeySpecParserCsvTests extends AndroidTestCase {
     public void testParseMultipleResources() {
         assertTextArray("Literals and resources",
                 "1,!label/multiple_chars,z", "1", "a", "b", "c", "z");
+        assertTextArray("Literals and RESOURCES",
+                "1,!LABEL/MULTIPLE_CHARS,z", "1", "a", "b", "c", "z");
         assertTextArray("Literals and resources and escape at end",
                 "\\1,!label/multiple_chars,z\\", "\\1", "a", "b", "c", "z\\");
         assertTextArray("Multiple single resource chars and labels",
@@ -288,6 +311,9 @@ public class KeySpecParserCsvTests extends AndroidTestCase {
         assertTextArray("Multiple single resource chars and labels 2",
                 "!label/single_char,!label/single_label,!label/escaped_comma_escape",
                 "a", "abc", "a\\,\\");
+        assertTextArray("Multiple single RESOURCE chars and LABELS 2",
+                "!LABEL/SINGLE_CHAR,!LABEL/SINGLE_LABEL,!LABEL/ESCAPED_COMMA_ESCAPE",
+                "a", "abc", "a\\,\\");
         assertTextArray("Multiple multiple resource chars and labels",
                 "!label/multiple_chars,!label/multiple_labels,!label/multiple_chars_with_comma",
                 "a", "b", "c", "abc", "def", "ghi", "a", "\\,", "c");
@@ -304,10 +330,68 @@ public class KeySpecParserCsvTests extends AndroidTestCase {
                 "!label/indirect_string", "a", "b", "c");
         assertTextArray("Indirect with literal",
                 "1,!label/indirect_string_with_literal,2", "1", "x", "a", "b", "c", "y", "2");
+        assertTextArray("Indirect2",
+                "!label/indirect2_string", "a", "b", "c");
+
+        assertTextArray("INDIRECT",
+                "!LABEL/INDIRECT_STRING", "a", "b", "c");
+        assertTextArray("INDIRECT with literal",
+                "1,!LABEL/INDIRECT_STRING_WITH_LITERAL,2", "1", "x", "a", "b", "c", "y", "2");
+        assertTextArray("INDIRECT2",
+                "!LABEL/INDIRECT2_STRING", "a", "b", "c");
+
+        assertTextArray("Upper indirect",
+                "!label/upper_indirect_string", "a", "b", "c");
+        assertTextArray("Upper indirect with literal",
+                "1,!label/upper_indirect_string_with_literal,2", "1", "x", "a", "b", "c", "y", "2");
+        assertTextArray("Upper indirect2",
+                "!label/upper_indirect2_string", "a", "b", "c");
+
+        assertTextArray("UPPER INDIRECT",
+                "!LABEL/upper_INDIRECT_STRING", "a", "b", "c");
+        assertTextArray("Upper INDIRECT with literal",
+                "1,!LABEL/upper_INDIRECT_STRING_WITH_LITERAL,2", "1", "x", "a", "b", "c", "y", "2");
+        assertTextArray("Upper INDIRECT2",
+                "!LABEL/upper_INDIRECT2_STRING", "a", "b", "c");
     }
 
     public void testParseInfiniteIndirectReference() {
         assertError("Infinite indirection",
                 "1,!label/infinite_indirection,2", "1", "infinite", "<infinite>", "loop", "2");
+        assertError("INFINITE INDIRECTION",
+                "1,!LABEL/INFINITE_INDIRECTION,2", "1", "infinite", "<infinite>", "loop", "2");
+
+        assertError("Upper infinite indirection",
+                "1,!label/upper_infinite_indirection,2",
+                "1", "infinite", "<infinite>", "loop", "2");
+        assertError("Upper INFINITE INDIRECTION",
+                "1,!LABEL/UPPER_INFINITE_INDIRECTION,2",
+                "1", "infinite", "<infinite>", "loop", "2");
+    }
+
+    public void testLabelReferece() {
+        assertTextArray("Label time am", "!label/label_time_am", "AM");
+        assertTextArray("LABEL TIME AM", "!LABEL/LABEL_TIME_AM", "AM");
+
+        assertTextArray("More keys for am pm", "!label/more_keys_for_am_pm",
+                "!fixedColumnOrder!2", "!hasLabels!", "AM", "PM");
+        assertTextArray("MORE KEYS FOR AM OM", "!LABEL/MORE_KEYS_FOR_AM_PM",
+                "!fixedColumnOrder!2", "!hasLabels!", "AM", "PM");
+
+        assertTextArray("Settings as more key", "!label/settings_as_more_key",
+                "!icon/settingsKey|!code/key_settings");
+        assertTextArray("SETTINGS AS MORE KEY", "!LABEL/SETTINGS_AS_MORE_KEY",
+                "!icon/settingsKey|!code/key_settings");
+
+        assertTextArray("Indirect naviagte actions as more key",
+                "!label/indirect_navigate_actions_as_more_key",
+                "!fixedColumnOrder!2",
+                "!hasLabels!", "Prev|!code/key_action_previous",
+                "!hasLabels!", "Next|!code/key_action_next");
+        assertTextArray("INDIRECT NAVIGATE ACTIONS AS MORE KEY",
+                "!LABEL/INDIRECT_NAVIGATE_ACTIONS_AS_MORE_KEY",
+                "!fixedColumnOrder!2",
+                "!hasLabels!", "Prev|!code/key_action_previous",
+                "!hasLabels!", "Next|!code/key_action_next");
     }
 }
diff --git a/tests/src/com/android/inputmethod/keyboard/internal/KeySpecParserTests.java b/tests/src/com/android/inputmethod/keyboard/internal/KeySpecParserTests.java
index f6a0c5604e83e006011803f9c4a7b192eb49e967..79d8d849e724fc977e91daff18f09b8b9d7df280 100644
--- a/tests/src/com/android/inputmethod/keyboard/internal/KeySpecParserTests.java
+++ b/tests/src/com/android/inputmethod/keyboard/internal/KeySpecParserTests.java
@@ -16,9 +16,12 @@
 
 package com.android.inputmethod.keyboard.internal;
 
+import static com.android.inputmethod.keyboard.Keyboard.CODE_OUTPUT_TEXT;
+import static com.android.inputmethod.keyboard.Keyboard.CODE_UNSPECIFIED;
+import static com.android.inputmethod.keyboard.internal.KeyboardIconsSet.ICON_UNDEFINED;
+
 import android.test.AndroidTestCase;
 
-import com.android.inputmethod.keyboard.Keyboard;
 import com.android.inputmethod.keyboard.internal.KeySpecParser.MoreKeySpec;
 
 import java.util.Arrays;
@@ -26,32 +29,40 @@ import java.util.Locale;
 
 public class KeySpecParserTests extends AndroidTestCase {
     private final KeyboardCodesSet mCodesSet = new KeyboardCodesSet();
-
-    private static final int ICON_UNDEFINED = KeyboardIconsSet.ICON_UNDEFINED;
+    private final KeyboardLabelsSet mLabelsSet = new KeyboardLabelsSet();
 
     private static final String CODE_SETTINGS_NAME = "key_settings";
     private static final String ICON_SETTINGS_NAME = "settingsKey";
 
     private static final String CODE_SETTINGS = "!code/" + CODE_SETTINGS_NAME;
     private static final String ICON_SETTINGS = "!icon/" + ICON_SETTINGS_NAME;
+    private static final String CODE_SETTINGS_UPPERCASE = CODE_SETTINGS.toUpperCase();
+    private static final String ICON_SETTINGS_UPPERCASE = ICON_SETTINGS.toUpperCase();
     private static final String CODE_NON_EXISTING = "!code/non_existing";
     private static final String ICON_NON_EXISTING = "!icon/non_existing";
 
     private int mCodeSettings;
+    private int mCodeActionNext;
     private int mSettingsIconId;
 
     @Override
     protected void setUp() throws Exception {
         super.setUp();
 
-        mCodesSet.setLanguage(Locale.ENGLISH.getLanguage());
+        final String language = Locale.ENGLISH.getLanguage();
+        mCodesSet.setLanguage(language);
+        mLabelsSet.setLanguage(language);
+        mLabelsSet.loadStringResources(getContext());
+
         mCodeSettings = mCodesSet.getCode(CODE_SETTINGS_NAME);
+        mCodeActionNext = mCodesSet.getCode("key_action_next");
         mSettingsIconId = KeyboardIconsSet.getIconId(ICON_SETTINGS_NAME);
     }
 
     private void assertParser(String message, String moreKeySpec, String expectedLabel,
             String expectedOutputText, int expectedIcon, int expectedCode) {
-        final MoreKeySpec spec = new MoreKeySpec(moreKeySpec, mCodesSet);
+        final MoreKeySpec spec = new MoreKeySpec(
+                KeySpecParser.resolveLabelReference(moreKeySpec, mLabelsSet), mCodesSet);
         assertEquals(message + ": label:", expectedLabel, spec.mLabel);
         assertEquals(message + ": ouptputText:", expectedOutputText, spec.mOutputText);
         assertEquals(message + ": icon:", expectedIcon, spec.mIconId);
@@ -106,195 +117,253 @@ public class KeySpecParserTests extends AndroidTestCase {
         assertParser("Single surrogate pair outputText", "G Clef|" + PAIR1,
                 "G Clef", null, ICON_UNDEFINED, CODE1);
         assertParser("Single letter with outputText", "a|abc",
-                "a", "abc", ICON_UNDEFINED, Keyboard.CODE_OUTPUT_TEXT);
+                "a", "abc", ICON_UNDEFINED, CODE_OUTPUT_TEXT);
         assertParser("Single letter with surrogate outputText", "a|" + SURROGATE1,
-                "a", SURROGATE1, ICON_UNDEFINED, Keyboard.CODE_OUTPUT_TEXT);
+                "a", SURROGATE1, ICON_UNDEFINED, CODE_OUTPUT_TEXT);
         assertParser("Single surrogate with outputText", PAIR3 + "|abc",
-                PAIR3, "abc", ICON_UNDEFINED, Keyboard.CODE_OUTPUT_TEXT);
+                PAIR3, "abc", ICON_UNDEFINED, CODE_OUTPUT_TEXT);
         assertParser("Single letter with escaped outputText", "a|a\\|c",
-                "a", "a|c", ICON_UNDEFINED, Keyboard.CODE_OUTPUT_TEXT);
+                "a", "a|c", ICON_UNDEFINED, CODE_OUTPUT_TEXT);
         assertParser("Single letter with escaped surrogate outputText",
                 "a|" + PAIR1 + "\\|" + PAIR2,
-                "a", PAIR1 + "|" + PAIR2, ICON_UNDEFINED, Keyboard.CODE_OUTPUT_TEXT);
+                "a", PAIR1 + "|" + PAIR2, ICON_UNDEFINED, CODE_OUTPUT_TEXT);
         assertParser("Single letter with comma outputText", "a|a,b",
-                "a", "a,b", ICON_UNDEFINED, Keyboard.CODE_OUTPUT_TEXT);
+                "a", "a,b", ICON_UNDEFINED, CODE_OUTPUT_TEXT);
         assertParser("Single letter with escaped comma outputText", "a|a\\,b",
-                "a", "a,b", ICON_UNDEFINED, Keyboard.CODE_OUTPUT_TEXT);
+                "a", "a,b", ICON_UNDEFINED, CODE_OUTPUT_TEXT);
         assertParser("Single letter with outputText starts with bang", "a|!bc",
-                "a", "!bc", ICON_UNDEFINED, Keyboard.CODE_OUTPUT_TEXT);
+                "a", "!bc", ICON_UNDEFINED, CODE_OUTPUT_TEXT);
         assertParser("Single letter with surrogate outputText starts with bang", "a|!" + SURROGATE2,
-                "a", "!" + SURROGATE2, ICON_UNDEFINED, Keyboard.CODE_OUTPUT_TEXT);
+                "a", "!" + SURROGATE2, ICON_UNDEFINED, CODE_OUTPUT_TEXT);
         assertParser("Single letter with outputText contains bang", "a|a!c",
-                "a", "a!c", ICON_UNDEFINED, Keyboard.CODE_OUTPUT_TEXT);
+                "a", "a!c", ICON_UNDEFINED, CODE_OUTPUT_TEXT);
         assertParser("Single letter with escaped bang outputText", "a|\\!bc",
-                "a", "!bc", ICON_UNDEFINED, Keyboard.CODE_OUTPUT_TEXT);
+                "a", "!bc", ICON_UNDEFINED, CODE_OUTPUT_TEXT);
         assertParser("Single escaped escape with single outputText", "\\\\|\\\\",
                 "\\", null, ICON_UNDEFINED, '\\');
         assertParser("Single escaped bar with single outputText", "\\||\\|",
                 "|", null, ICON_UNDEFINED, '|');
         assertParser("Single letter with code", "a|" + CODE_SETTINGS,
                 "a", null, ICON_UNDEFINED, mCodeSettings);
+        assertParser("Single letter with CODE", "a|" + CODE_SETTINGS_UPPERCASE,
+                "a", null, ICON_UNDEFINED, mCodeSettings);
     }
 
     public void testLabel() {
         assertParser("Simple label", "abc",
-                "abc", "abc", ICON_UNDEFINED, Keyboard.CODE_OUTPUT_TEXT);
+                "abc", "abc", ICON_UNDEFINED, CODE_OUTPUT_TEXT);
         assertParser("Simple surrogate label", SURROGATE1,
-                SURROGATE1, SURROGATE1, ICON_UNDEFINED, Keyboard.CODE_OUTPUT_TEXT);
+                SURROGATE1, SURROGATE1, ICON_UNDEFINED, CODE_OUTPUT_TEXT);
         assertParser("Label with escaped bar", "a\\|c",
-                "a|c", "a|c", ICON_UNDEFINED, Keyboard.CODE_OUTPUT_TEXT);
+                "a|c", "a|c", ICON_UNDEFINED, CODE_OUTPUT_TEXT);
         assertParser("Surrogate label with escaped bar", PAIR1 + "\\|" + PAIR2,
                 PAIR1 + "|" + PAIR2, PAIR1 + "|" + PAIR2,
-                ICON_UNDEFINED, Keyboard.CODE_OUTPUT_TEXT);
+                ICON_UNDEFINED, CODE_OUTPUT_TEXT);
         assertParser("Label with escaped escape", "a\\\\c",
-                "a\\c", "a\\c", ICON_UNDEFINED, Keyboard.CODE_OUTPUT_TEXT);
+                "a\\c", "a\\c", ICON_UNDEFINED, CODE_OUTPUT_TEXT);
         assertParser("Label with comma", "a,c",
-                "a,c", "a,c", ICON_UNDEFINED, Keyboard.CODE_OUTPUT_TEXT);
+                "a,c", "a,c", ICON_UNDEFINED, CODE_OUTPUT_TEXT);
         assertParser("Label with escaped comma", "a\\,c",
-                "a,c", "a,c", ICON_UNDEFINED, Keyboard.CODE_OUTPUT_TEXT);
+                "a,c", "a,c", ICON_UNDEFINED, CODE_OUTPUT_TEXT);
         assertParser("Label starts with bang", "!bc",
-                "!bc", "!bc", ICON_UNDEFINED, Keyboard.CODE_OUTPUT_TEXT);
+                "!bc", "!bc", ICON_UNDEFINED, CODE_OUTPUT_TEXT);
         assertParser("Surrogate label starts with bang", "!" + SURROGATE1,
-                "!" + SURROGATE1, "!" + SURROGATE1, ICON_UNDEFINED, Keyboard.CODE_OUTPUT_TEXT);
+                "!" + SURROGATE1, "!" + SURROGATE1, ICON_UNDEFINED, CODE_OUTPUT_TEXT);
         assertParser("Label contains bang", "a!c",
-                "a!c", "a!c", ICON_UNDEFINED, Keyboard.CODE_OUTPUT_TEXT);
+                "a!c", "a!c", ICON_UNDEFINED, CODE_OUTPUT_TEXT);
         assertParser("Label with escaped bang", "\\!bc",
-                "!bc", "!bc", ICON_UNDEFINED, Keyboard.CODE_OUTPUT_TEXT);
+                "!bc", "!bc", ICON_UNDEFINED, CODE_OUTPUT_TEXT);
         assertParser("Label with escaped letter", "\\abc",
-                "abc", "abc", ICON_UNDEFINED, Keyboard.CODE_OUTPUT_TEXT);
+                "abc", "abc", ICON_UNDEFINED, CODE_OUTPUT_TEXT);
         assertParser("Label with outputText", "abc|def",
-                "abc", "def", ICON_UNDEFINED, Keyboard.CODE_OUTPUT_TEXT);
+                "abc", "def", ICON_UNDEFINED, CODE_OUTPUT_TEXT);
         assertParser("Label with comma and outputText", "a,c|def",
-                "a,c", "def", ICON_UNDEFINED, Keyboard.CODE_OUTPUT_TEXT);
+                "a,c", "def", ICON_UNDEFINED, CODE_OUTPUT_TEXT);
         assertParser("Escaped comma label with outputText", "a\\,c|def",
-                "a,c", "def", ICON_UNDEFINED, Keyboard.CODE_OUTPUT_TEXT);
+                "a,c", "def", ICON_UNDEFINED, CODE_OUTPUT_TEXT);
         assertParser("Escaped label with outputText", "a\\|c|def",
-                "a|c", "def", ICON_UNDEFINED, Keyboard.CODE_OUTPUT_TEXT);
+                "a|c", "def", ICON_UNDEFINED, CODE_OUTPUT_TEXT);
         assertParser("Label with escaped bar outputText", "abc|d\\|f",
-                "abc", "d|f", ICON_UNDEFINED, Keyboard.CODE_OUTPUT_TEXT);
+                "abc", "d|f", ICON_UNDEFINED, CODE_OUTPUT_TEXT);
         assertParser("Escaped escape label with outputText", "a\\\\|def",
-                "a\\", "def", ICON_UNDEFINED, Keyboard.CODE_OUTPUT_TEXT);
+                "a\\", "def", ICON_UNDEFINED, CODE_OUTPUT_TEXT);
         assertParser("Label starts with bang and outputText", "!bc|def",
-                "!bc", "def", ICON_UNDEFINED, Keyboard.CODE_OUTPUT_TEXT);
+                "!bc", "def", ICON_UNDEFINED, CODE_OUTPUT_TEXT);
         assertParser("Label contains bang label and outputText", "a!c|def",
-                "a!c", "def", ICON_UNDEFINED, Keyboard.CODE_OUTPUT_TEXT);
+                "a!c", "def", ICON_UNDEFINED, CODE_OUTPUT_TEXT);
         assertParser("Escaped bang label with outputText", "\\!bc|def",
-                "!bc", "def", ICON_UNDEFINED, Keyboard.CODE_OUTPUT_TEXT);
+                "!bc", "def", ICON_UNDEFINED, CODE_OUTPUT_TEXT);
         assertParser("Label with comma outputText", "abc|a,b",
-                "abc", "a,b", ICON_UNDEFINED, Keyboard.CODE_OUTPUT_TEXT);
+                "abc", "a,b", ICON_UNDEFINED, CODE_OUTPUT_TEXT);
         assertParser("Label with escaped comma outputText", "abc|a\\,b",
-                "abc", "a,b", ICON_UNDEFINED, Keyboard.CODE_OUTPUT_TEXT);
+                "abc", "a,b", ICON_UNDEFINED, CODE_OUTPUT_TEXT);
         assertParser("Label with outputText starts with bang", "abc|!bc",
-                "abc", "!bc", ICON_UNDEFINED, Keyboard.CODE_OUTPUT_TEXT);
+                "abc", "!bc", ICON_UNDEFINED, CODE_OUTPUT_TEXT);
         assertParser("Label with outputText contains bang", "abc|a!c",
-                "abc", "a!c", ICON_UNDEFINED, Keyboard.CODE_OUTPUT_TEXT);
+                "abc", "a!c", ICON_UNDEFINED, CODE_OUTPUT_TEXT);
         assertParser("Label with escaped bang outputText", "abc|\\!bc",
-                "abc", "!bc", ICON_UNDEFINED, Keyboard.CODE_OUTPUT_TEXT);
+                "abc", "!bc", ICON_UNDEFINED, CODE_OUTPUT_TEXT);
         assertParser("Label with escaped bar outputText", "abc|d\\|f",
-                "abc", "d|f", ICON_UNDEFINED, Keyboard.CODE_OUTPUT_TEXT);
+                "abc", "d|f", ICON_UNDEFINED, CODE_OUTPUT_TEXT);
         assertParser("Escaped bar label with escaped bar outputText", "a\\|c|d\\|f",
-                "a|c", "d|f", ICON_UNDEFINED, Keyboard.CODE_OUTPUT_TEXT);
+                "a|c", "d|f", ICON_UNDEFINED, CODE_OUTPUT_TEXT);
         assertParser("Label with code", "abc|" + CODE_SETTINGS,
                 "abc", null, ICON_UNDEFINED, mCodeSettings);
+        assertParser("Label with CODE", "abc|" + CODE_SETTINGS_UPPERCASE,
+                "abc", null, ICON_UNDEFINED, mCodeSettings);
         assertParser("Escaped label with code", "a\\|c|" + CODE_SETTINGS,
                 "a|c", null, ICON_UNDEFINED, mCodeSettings);
+        assertParser("Escaped label with CODE", "a\\|c|" + CODE_SETTINGS_UPPERCASE,
+                "a|c", null, ICON_UNDEFINED, mCodeSettings);
     }
 
     public void testIconAndCode() {
         assertParser("Icon with outputText", ICON_SETTINGS + "|abc",
-                null, "abc", mSettingsIconId, Keyboard.CODE_OUTPUT_TEXT);
+                null, "abc", mSettingsIconId, CODE_OUTPUT_TEXT);
+        assertParser("ICON with outputText", ICON_SETTINGS_UPPERCASE + "|abc",
+                null, "abc", mSettingsIconId, CODE_OUTPUT_TEXT);
         assertParser("Icon with outputText starts with bang", ICON_SETTINGS + "|!bc",
-                null, "!bc", mSettingsIconId, Keyboard.CODE_OUTPUT_TEXT);
+                null, "!bc", mSettingsIconId, CODE_OUTPUT_TEXT);
+        assertParser("ICON with outputText starts with bang", ICON_SETTINGS_UPPERCASE + "|!bc",
+                null, "!bc", mSettingsIconId, CODE_OUTPUT_TEXT);
         assertParser("Icon with outputText contains bang", ICON_SETTINGS + "|a!c",
-                null, "a!c", mSettingsIconId, Keyboard.CODE_OUTPUT_TEXT);
+                null, "a!c", mSettingsIconId, CODE_OUTPUT_TEXT);
+        assertParser("ICON with outputText contains bang", ICON_SETTINGS_UPPERCASE + "|a!c",
+                null, "a!c", mSettingsIconId, CODE_OUTPUT_TEXT);
         assertParser("Icon with escaped bang outputText", ICON_SETTINGS + "|\\!bc",
-                null, "!bc", mSettingsIconId, Keyboard.CODE_OUTPUT_TEXT);
+                null, "!bc", mSettingsIconId, CODE_OUTPUT_TEXT);
+        assertParser("ICON with escaped bang outputText", ICON_SETTINGS_UPPERCASE + "|\\!bc",
+                null, "!bc", mSettingsIconId, CODE_OUTPUT_TEXT);
         assertParser("Label starts with bang and code", "!bc|" + CODE_SETTINGS,
                 "!bc", null, ICON_UNDEFINED, mCodeSettings);
+        assertParser("Label starts with bang and CODE", "!bc|" + CODE_SETTINGS_UPPERCASE,
+                "!bc", null, ICON_UNDEFINED, mCodeSettings);
         assertParser("Label contains bang and code", "a!c|" + CODE_SETTINGS,
                 "a!c", null, ICON_UNDEFINED, mCodeSettings);
+        assertParser("Label contains bang and CODE", "a!c|" + CODE_SETTINGS_UPPERCASE,
+                "a!c", null, ICON_UNDEFINED, mCodeSettings);
         assertParser("Escaped bang label with code", "\\!bc|" + CODE_SETTINGS,
                 "!bc", null, ICON_UNDEFINED, mCodeSettings);
+        assertParser("Escaped bang label with CODE", "\\!bc|" + CODE_SETTINGS_UPPERCASE,
+                "!bc", null, ICON_UNDEFINED, mCodeSettings);
         assertParser("Icon with code", ICON_SETTINGS + "|" + CODE_SETTINGS,
                 null, null, mSettingsIconId, mCodeSettings);
+        assertParser("ICON with CODE", ICON_SETTINGS_UPPERCASE + "|" + CODE_SETTINGS_UPPERCASE,
+                null, null, mSettingsIconId, mCodeSettings);
+    }
+
+    public void testResourceReference() {
+        assertParser("Settings as more key", "!label/settings_as_more_key",
+                null, null, mSettingsIconId, mCodeSettings);
+        assertParser("SETTINGS AS MORE KEY", "!LABEL/SETTINGS_AS_MORE_KEY",
+                null, null, mSettingsIconId, mCodeSettings);
+
+        assertParser("Action next as more key", "!label/label_next_key|!code/key_action_next",
+                "Next", null, ICON_UNDEFINED, mCodeActionNext);
+        assertParser("ACTION NEXT AS MORE KEY", "!LABEL/LABEL_NEXT_KEY|!CODE/KEY_ACTION_NEXT",
+                "Next", null, ICON_UNDEFINED, mCodeActionNext);
+
+        assertParser("Popular domain",
+                "!label/keylabel_for_popular_domain|!label/keylabel_for_popular_domain ",
+                ".com", ".com ", ICON_UNDEFINED, CODE_OUTPUT_TEXT);
+        assertParser("POPULAR DOMAIN",
+                "!LABEL/KEYLABEL_FOR_POPULAR_DOMAIN|!LABEL/KEYLABEL_FOR_POPULAR_DOMAIN ",
+                ".com", ".com ", ICON_UNDEFINED, CODE_OUTPUT_TEXT);
     }
 
     public void testFormatError() {
         assertParserError("Empty spec", "", null,
-                null, ICON_UNDEFINED, Keyboard.CODE_UNSPECIFIED);
+                null, ICON_UNDEFINED, CODE_UNSPECIFIED);
         assertParserError("Empty label with outputText", "|a",
-                null, "a", ICON_UNDEFINED, Keyboard.CODE_UNSPECIFIED);
+                null, "a", ICON_UNDEFINED, CODE_UNSPECIFIED);
         assertParserError("Empty label with code", "|" + CODE_SETTINGS,
                 null, null, ICON_UNDEFINED, mCodeSettings);
+        assertParserError("Empty label with CODE", "|" + CODE_SETTINGS_UPPERCASE,
+                null, null, ICON_UNDEFINED, mCodeSettings);
         assertParserError("Empty outputText with label", "a|",
-                "a", null, ICON_UNDEFINED, Keyboard.CODE_UNSPECIFIED);
+                "a", null, ICON_UNDEFINED, CODE_UNSPECIFIED);
         assertParserError("Empty outputText with icon", ICON_SETTINGS + "|",
-                null, null, mSettingsIconId, Keyboard.CODE_UNSPECIFIED);
+                null, null, mSettingsIconId, CODE_UNSPECIFIED);
+        assertParserError("Empty outputText with ICON", ICON_SETTINGS_UPPERCASE + "|",
+                null, null, mSettingsIconId, CODE_UNSPECIFIED);
         assertParserError("Empty icon and code", "|",
-                null, null, ICON_UNDEFINED, Keyboard.CODE_UNSPECIFIED);
+                null, null, ICON_UNDEFINED, CODE_UNSPECIFIED);
         assertParserError("Icon without code", ICON_SETTINGS,
-                null, null, mSettingsIconId, Keyboard.CODE_UNSPECIFIED);
+                null, null, mSettingsIconId, CODE_UNSPECIFIED);
+        assertParserError("ICON without code", ICON_SETTINGS_UPPERCASE,
+                null, null, mSettingsIconId, CODE_UNSPECIFIED);
         assertParserError("Non existing icon", ICON_NON_EXISTING + "|abc",
-                null, "abc", ICON_UNDEFINED, Keyboard.CODE_OUTPUT_TEXT);
+                null, "abc", ICON_UNDEFINED, CODE_OUTPUT_TEXT);
         assertParserError("Non existing code", "abc|" + CODE_NON_EXISTING,
-                "abc", null, ICON_UNDEFINED, Keyboard.CODE_UNSPECIFIED);
+                "abc", null, ICON_UNDEFINED, CODE_UNSPECIFIED);
         assertParserError("Third bar at end", "a|b|",
-                "a", null, ICON_UNDEFINED, Keyboard.CODE_UNSPECIFIED);
+                "a", null, ICON_UNDEFINED, CODE_UNSPECIFIED);
         assertParserError("Multiple bar", "a|b|c",
-                "a", null, ICON_UNDEFINED, Keyboard.CODE_UNSPECIFIED);
+                "a", null, ICON_UNDEFINED, CODE_UNSPECIFIED);
         assertParserError("Multiple bar with label and code", "a|" + CODE_SETTINGS + "|c",
                 "a", null, ICON_UNDEFINED, mCodeSettings);
+        assertParserError("Multiple bar with label and CODE", "a|" + CODE_SETTINGS_UPPERCASE + "|c",
+                "a", null, ICON_UNDEFINED, mCodeSettings);
         assertParserError("Multiple bar with icon and outputText", ICON_SETTINGS + "|b|c",
-                null, null, mSettingsIconId, Keyboard.CODE_UNSPECIFIED);
+                null, null, mSettingsIconId, CODE_UNSPECIFIED);
+        assertParserError("Multiple bar with ICON and outputText", ICON_SETTINGS_UPPERCASE + "|b|c",
+                null, null, mSettingsIconId, CODE_UNSPECIFIED);
         assertParserError("Multiple bar with icon and code",
                 ICON_SETTINGS + "|" + CODE_SETTINGS + "|c",
                 null, null, mSettingsIconId, mCodeSettings);
+        assertParserError("Multiple bar with ICON and CODE",
+                ICON_SETTINGS_UPPERCASE + "|" + CODE_SETTINGS_UPPERCASE + "|c",
+                null, null, mSettingsIconId, mCodeSettings);
     }
 
-    private static void assertMoreKeys(String message, String[] moreKeys,
-            String[] additionalMoreKeys, String[] expected) {
-        final String[] actual = KeySpecParser.insertAdditionalMoreKeys(
-                moreKeys, additionalMoreKeys);
-        if (expected == null && actual == null) {
+    private static void assertArrayEquals(String message, Object[] expected, Object[] actual) {
+        if (expected == actual) {
             return;
         }
         if (expected == null || actual == null) {
             assertEquals(message, Arrays.toString(expected), Arrays.toString(actual));
-        } else {
-            if (expected.length != actual.length) {
-                assertEquals(message, Arrays.toString(expected), Arrays.toString(actual));
-            }
-            for (int i = 0; i < expected.length; i++) {
-                if (!actual[i].equals(expected[i])) {
-                    assertEquals(message, Arrays.toString(expected), Arrays.toString(actual));
-                }
-            }
+            return;
+        }
+        if (expected.length != actual.length) {
+            assertEquals(message + " [length]", Arrays.toString(expected), Arrays.toString(actual));
+            return;
         }
+        for (int i = 0; i < expected.length; i++) {
+            assertEquals(message + " [" + i + "]",
+                    Arrays.toString(expected), Arrays.toString(actual));
+        }
+    }
+
+    private static void assertInsertAdditionalMoreKeys(String message, String[] moreKeys,
+            String[] additionalMoreKeys, String[] expected) {
+        final String[] actual =
+                KeySpecParser.insertAdditionalMoreKeys( moreKeys, additionalMoreKeys);
+        assertArrayEquals(message, expected, actual);
     }
 
     public void testEmptyEntry() {
-        assertMoreKeys("null more keys and null additons",
+        assertInsertAdditionalMoreKeys("null more keys and null additons",
                 null,
                 null,
                 null);
-        assertMoreKeys("null more keys and empty additons",
+        assertInsertAdditionalMoreKeys("null more keys and empty additons",
                 null,
                 new String[0],
                 null);
-        assertMoreKeys("empty more keys and null additons",
+        assertInsertAdditionalMoreKeys("empty more keys and null additons",
                 new String[0],
                 null,
                 null);
-        assertMoreKeys("empty more keys and empty additons",
+        assertInsertAdditionalMoreKeys("empty more keys and empty additons",
                 new String[0],
                 new String[0],
                 null);
 
-        assertMoreKeys("filter out empty more keys",
+        assertInsertAdditionalMoreKeys("filter out empty more keys",
                 new String[] { null, "a", "", "b", null },
                 null,
                 new String[] { "a", "b" });
-        assertMoreKeys("filter out empty additons",
+        assertInsertAdditionalMoreKeys("filter out empty additons",
                 new String[] { "a", "%", "b", "%", "c", "%", "d" },
                 new String[] { null, "A", "", "B", null },
                 new String[] { "a", "A", "b", "B", "c", "d" });
@@ -302,188 +371,261 @@ public class KeySpecParserTests extends AndroidTestCase {
 
     public void testInsertAdditionalMoreKeys() {
         // Escaped marker.
-        assertMoreKeys("escaped marker",
+        assertInsertAdditionalMoreKeys("escaped marker",
                 new String[] { "\\%", "%-)" },
                 new String[] { "1", "2" },
                 new String[] { "1", "2", "\\%", "%-)" });
 
         // 0 more key.
-        assertMoreKeys("null & null", null, null, null);
-        assertMoreKeys("null & 1 additon",
+        assertInsertAdditionalMoreKeys("null & null", null, null, null);
+        assertInsertAdditionalMoreKeys("null & 1 additon",
                 null,
                 new String[] { "1" },
                 new String[] { "1" });
-        assertMoreKeys("null & 2 additons",
+        assertInsertAdditionalMoreKeys("null & 2 additons",
                 null,
                 new String[] { "1", "2" },
                 new String[] { "1", "2" });
 
         // 0 additional more key.
-        assertMoreKeys("1 more key & null",
+        assertInsertAdditionalMoreKeys("1 more key & null",
                 new String[] { "A" },
                 null,
                 new String[] { "A" });
-        assertMoreKeys("2 more keys & null",
+        assertInsertAdditionalMoreKeys("2 more keys & null",
                 new String[] { "A", "B" },
                 null,
                 new String[] { "A", "B" });
 
         // No marker.
-        assertMoreKeys("1 more key & 1 addtional & no marker",
+        assertInsertAdditionalMoreKeys("1 more key & 1 addtional & no marker",
                 new String[] { "A" },
                 new String[] { "1" },
                 new String[] { "1", "A" });
-        assertMoreKeys("1 more key & 2 addtionals & no marker",
+        assertInsertAdditionalMoreKeys("1 more key & 2 addtionals & no marker",
                 new String[] { "A" },
                 new String[] { "1", "2" },
                 new String[] { "1", "2", "A" });
-        assertMoreKeys("2 more keys & 1 addtional & no marker",
+        assertInsertAdditionalMoreKeys("2 more keys & 1 addtional & no marker",
                 new String[] { "A", "B" },
                 new String[] { "1" },
                 new String[] { "1", "A", "B" });
-        assertMoreKeys("2 more keys & 2 addtionals & no marker",
+        assertInsertAdditionalMoreKeys("2 more keys & 2 addtionals & no marker",
                 new String[] { "A", "B" },
                 new String[] { "1", "2" },
                 new String[] { "1", "2", "A", "B" });
 
         // 1 marker.
-        assertMoreKeys("1 more key & 1 additon & marker at head",
+        assertInsertAdditionalMoreKeys("1 more key & 1 additon & marker at head",
                 new String[] { "%", "A" },
                 new String[] { "1" },
                 new String[] { "1", "A" });
-        assertMoreKeys("1 more key & 1 additon & marker at tail",
+        assertInsertAdditionalMoreKeys("1 more key & 1 additon & marker at tail",
                 new String[] { "A", "%" },
                 new String[] { "1" },
                 new String[] { "A", "1" });
-        assertMoreKeys("2 more keys & 1 additon & marker at middle",
+        assertInsertAdditionalMoreKeys("2 more keys & 1 additon & marker at middle",
                 new String[] { "A", "%", "B" },
                 new String[] { "1" },
                 new String[] { "A", "1", "B" });
 
         // 1 marker & excess additional more keys.
-        assertMoreKeys("1 more key & 2 additons & marker at head",
+        assertInsertAdditionalMoreKeys("1 more key & 2 additons & marker at head",
                 new String[] { "%", "A", "B" },
                 new String[] { "1", "2" },
                 new String[] { "1", "A", "B", "2" });
-        assertMoreKeys("1 more key & 2 additons & marker at tail",
+        assertInsertAdditionalMoreKeys("1 more key & 2 additons & marker at tail",
                 new String[] { "A", "B", "%" },
                 new String[] { "1", "2" },
                 new String[] { "A", "B", "1", "2" });
-        assertMoreKeys("2 more keys & 2 additons & marker at middle",
+        assertInsertAdditionalMoreKeys("2 more keys & 2 additons & marker at middle",
                 new String[] { "A", "%", "B" },
                 new String[] { "1", "2" },
                 new String[] { "A", "1", "B", "2" });
 
         // 2 markers.
-        assertMoreKeys("0 more key & 2 addtional & 2 markers",
+        assertInsertAdditionalMoreKeys("0 more key & 2 addtional & 2 markers",
                 new String[] { "%", "%" },
                 new String[] { "1", "2" },
                 new String[] { "1", "2" });
-        assertMoreKeys("1 more key & 2 addtional & 2 markers at head",
+        assertInsertAdditionalMoreKeys("1 more key & 2 addtional & 2 markers at head",
                 new String[] { "%", "%", "A" },
                 new String[] { "1", "2" },
                 new String[] { "1", "2", "A" });
-        assertMoreKeys("1 more key & 2 addtional & 2 markers at tail",
+        assertInsertAdditionalMoreKeys("1 more key & 2 addtional & 2 markers at tail",
                 new String[] { "A", "%", "%" },
                 new String[] { "1", "2" },
                 new String[] { "A", "1", "2" });
-        assertMoreKeys("2 more keys & 2 addtional & 2 markers at middle",
+        assertInsertAdditionalMoreKeys("2 more keys & 2 addtional & 2 markers at middle",
                 new String[] { "A", "%", "%", "B" },
                 new String[] { "1", "2" },
                 new String[] { "A", "1", "2", "B" });
-        assertMoreKeys("2 more keys & 2 addtional & 2 markers at head & middle",
+        assertInsertAdditionalMoreKeys("2 more keys & 2 addtional & 2 markers at head & middle",
                 new String[] { "%", "A", "%", "B" },
                 new String[] { "1", "2" },
                 new String[] { "1", "A", "2", "B" });
-        assertMoreKeys("2 more keys & 2 addtional & 2 markers at head & tail",
+        assertInsertAdditionalMoreKeys("2 more keys & 2 addtional & 2 markers at head & tail",
                 new String[] { "%", "A", "B", "%" },
                 new String[] { "1", "2" },
                 new String[] { "1", "A", "B", "2" });
-        assertMoreKeys("2 more keys & 2 addtional & 2 markers at middle & tail",
+        assertInsertAdditionalMoreKeys("2 more keys & 2 addtional & 2 markers at middle & tail",
                 new String[] { "A", "%", "B", "%" },
                 new String[] { "1", "2" },
                 new String[] { "A", "1", "B", "2" });
 
         // 2 markers & excess additional more keys.
-        assertMoreKeys("0 more key & 2 additons & 2 markers",
+        assertInsertAdditionalMoreKeys("0 more key & 2 additons & 2 markers",
                 new String[] { "%", "%" },
                 new String[] { "1", "2", "3" },
                 new String[] { "1", "2", "3" });
-        assertMoreKeys("1 more key & 2 additons & 2 markers at head",
+        assertInsertAdditionalMoreKeys("1 more key & 2 additons & 2 markers at head",
                 new String[] { "%", "%", "A" },
                 new String[] { "1", "2", "3" },
                 new String[] { "1", "2", "A", "3" });
-        assertMoreKeys("1 more key & 2 additons & 2 markers at tail",
+        assertInsertAdditionalMoreKeys("1 more key & 2 additons & 2 markers at tail",
                 new String[] { "A", "%", "%" },
                 new String[] { "1", "2", "3" },
                 new String[] { "A", "1", "2", "3" });
-        assertMoreKeys("2 more keys & 2 additons & 2 markers at middle",
+        assertInsertAdditionalMoreKeys("2 more keys & 2 additons & 2 markers at middle",
                 new String[] { "A", "%", "%", "B" },
                 new String[] { "1", "2", "3" },
                 new String[] { "A", "1", "2", "B", "3" });
-        assertMoreKeys("2 more keys & 2 additons & 2 markers at head & middle",
+        assertInsertAdditionalMoreKeys("2 more keys & 2 additons & 2 markers at head & middle",
                 new String[] { "%", "A", "%", "B" },
                 new String[] { "1", "2", "3" },
                 new String[] { "1", "A", "2", "B", "3" });
-        assertMoreKeys("2 more keys & 2 additons & 2 markers at head & tail",
+        assertInsertAdditionalMoreKeys("2 more keys & 2 additons & 2 markers at head & tail",
                 new String[] { "%", "A", "B", "%" },
                 new String[] { "1", "2", "3" },
                 new String[] { "1", "A", "B", "2", "3" });
-        assertMoreKeys("2 more keys & 2 additons & 2 markers at middle & tail",
+        assertInsertAdditionalMoreKeys("2 more keys & 2 additons & 2 markers at middle & tail",
                 new String[] { "A", "%", "B", "%" },
                 new String[] { "1", "2", "3" },
                 new String[] { "A", "1", "B", "2", "3" });
 
         // 0 addtional more key and excess markers.
-        assertMoreKeys("0 more key & null & excess marker",
+        assertInsertAdditionalMoreKeys("0 more key & null & excess marker",
                 new String[] { "%" },
                 null,
                 null);
-        assertMoreKeys("1 more key & null & excess marker at head",
+        assertInsertAdditionalMoreKeys("1 more key & null & excess marker at head",
                 new String[] { "%", "A" },
                 null,
                 new String[] { "A" });
-        assertMoreKeys("1 more key & null & excess marker at tail",
+        assertInsertAdditionalMoreKeys("1 more key & null & excess marker at tail",
                 new String[] { "A", "%" },
                 null,
                 new String[] { "A" });
-        assertMoreKeys("2 more keys & null & excess marker at middle",
+        assertInsertAdditionalMoreKeys("2 more keys & null & excess marker at middle",
                 new String[] { "A", "%", "B" },
                 null,
                 new String[] { "A", "B" });
-        assertMoreKeys("2 more keys & null & excess markers",
+        assertInsertAdditionalMoreKeys("2 more keys & null & excess markers",
                 new String[] { "%", "A", "%", "B", "%" },
                 null,
                 new String[] { "A", "B" });
 
         // Excess markers.
-        assertMoreKeys("0 more key & 1 additon & excess marker",
+        assertInsertAdditionalMoreKeys("0 more key & 1 additon & excess marker",
                 new String[] { "%", "%" },
                 new String[] { "1" },
                 new String[] { "1" });
-        assertMoreKeys("1 more key & 1 additon & excess marker at head",
+        assertInsertAdditionalMoreKeys("1 more key & 1 additon & excess marker at head",
                 new String[] { "%", "%", "A" },
                 new String[] { "1" },
                 new String[] { "1", "A" });
-        assertMoreKeys("1 more key & 1 additon & excess marker at tail",
+        assertInsertAdditionalMoreKeys("1 more key & 1 additon & excess marker at tail",
                 new String[] { "A", "%", "%" },
                 new String[] { "1" },
                 new String[] { "A", "1" });
-        assertMoreKeys("2 more keys & 1 additon & excess marker at middle",
+        assertInsertAdditionalMoreKeys("2 more keys & 1 additon & excess marker at middle",
                 new String[] { "A", "%", "%", "B" },
                 new String[] { "1" },
                 new String[] { "A", "1", "B" });
-        assertMoreKeys("2 more keys & 1 additon & excess markers",
+        assertInsertAdditionalMoreKeys("2 more keys & 1 additon & excess markers",
                 new String[] { "%", "A", "%", "B", "%" },
                 new String[] { "1" },
                 new String[] { "1", "A", "B" });
-        assertMoreKeys("2 more keys & 2 additons & excess markers",
+        assertInsertAdditionalMoreKeys("2 more keys & 2 additons & excess markers",
                 new String[] { "%", "A", "%", "B", "%" },
                 new String[] { "1", "2" },
                 new String[] { "1", "A", "2", "B" });
-        assertMoreKeys("2 more keys & 3 additons & excess markers",
+        assertInsertAdditionalMoreKeys("2 more keys & 3 additons & excess markers",
                 new String[] { "%", "A", "%", "%", "B", "%" },
                 new String[] { "1", "2", "3" },
                 new String[] { "1", "A", "2", "3", "B" });
     }
+
+    private static final String HAS_LABEL = "!hasLabel!";
+    private static final String NEEDS_DIVIDER = "!needsDividers!";
+    private static final String AUTO_COLUMN_ORDER = "!autoColumnOrder!";
+    private static final String FIXED_COLUMN_ORDER = "!fixedColumnOrder!";
+
+    private static void assertGetBooleanValue(String message, String key, String[] moreKeys,
+            String[] expected, boolean expectedValue) {
+        final String[] actual = Arrays.copyOf(moreKeys, moreKeys.length);
+        final boolean actualValue = KeySpecParser.getBooleanValue(actual, key);
+        assertEquals(message + " [value]", expectedValue, actualValue);
+        assertArrayEquals(message, expected, actual);
+    }
+
+    public void testGetBooleanValue() {
+        assertGetBooleanValue("Has label", HAS_LABEL,
+                new String[] { HAS_LABEL, "a", "b", "c" },
+                new String[] { null, "a", "b", "c" }, true);
+        assertGetBooleanValue("HAS LABEL", HAS_LABEL,
+                new String[] { HAS_LABEL.toUpperCase(), "a", "b", "c" },
+                new String[] { null, "a", "b", "c" }, true);
+
+        assertGetBooleanValue("No has label", HAS_LABEL,
+                new String[] { "a", "b", "c" },
+                new String[] { "a", "b", "c" }, false);
+        assertGetBooleanValue("No has label with fixed clumn order", HAS_LABEL,
+                new String[] { FIXED_COLUMN_ORDER + "3", "a", "b", "c" },
+                new String[] { FIXED_COLUMN_ORDER + "3", "a", "b", "c" }, false);
+
+        assertGetBooleanValue("Multiple has label", HAS_LABEL,
+                new String[] {
+                    "a", HAS_LABEL.toUpperCase(), "b", "c", HAS_LABEL, "d" },
+                new String[] {
+                    "a", null, "b", "c", null, "d" }, true);
+        assertGetBooleanValue("Multiple has label with needs dividers", HAS_LABEL,
+                new String[] {
+                    "a", HAS_LABEL, "b", NEEDS_DIVIDER, HAS_LABEL.toUpperCase(), "d" },
+                new String[] {
+                    "a", null, "b", NEEDS_DIVIDER, null, "d" }, true);
+    }
+
+    private static void assertGetIntValue(String message, String key, int defaultValue,
+            String[] moreKeys, String[] expected, int expectedValue) {
+        final String[] actual = Arrays.copyOf(moreKeys, moreKeys.length);
+        final int actualValue = KeySpecParser.getIntValue(actual, key, defaultValue);
+        assertEquals(message + " [value]", expectedValue, actualValue);
+        assertArrayEquals(message, expected, actual);
+    }
+
+    public void testGetIntValue() {
+        assertGetIntValue("Fixed column order 3", FIXED_COLUMN_ORDER, -1,
+                new String[] { FIXED_COLUMN_ORDER + "3", "a", "b", "c" },
+                new String[] { null, "a", "b", "c" }, 3);
+        assertGetIntValue("FIXED COLUMN ORDER 3", FIXED_COLUMN_ORDER, -1,
+                new String[] { FIXED_COLUMN_ORDER.toUpperCase() + "3", "a", "b", "c" },
+                new String[] { null, "a", "b", "c" }, 3);
+
+        assertGetIntValue("No fixed column order", FIXED_COLUMN_ORDER, -1,
+                new String[] { "a", "b", "c" },
+                new String[] { "a", "b", "c" }, -1);
+        assertGetIntValue("No fixed column order with auto column order", FIXED_COLUMN_ORDER, -1,
+                new String[] { AUTO_COLUMN_ORDER + "5", "a", "b", "c" },
+                new String[] { AUTO_COLUMN_ORDER + "5", "a", "b", "c" }, -1);
+
+        assertGetIntValue("Multiple fixed column order 3,5", FIXED_COLUMN_ORDER, -1,
+                new String[] { FIXED_COLUMN_ORDER + "3", "a", FIXED_COLUMN_ORDER + "5", "b" },
+                new String[] { null, "a", null, "b" }, 3);
+        assertGetIntValue("Multiple fixed column order 5,3 with has label", FIXED_COLUMN_ORDER, -1,
+                new String[] {
+                    FIXED_COLUMN_ORDER.toUpperCase() + "5", HAS_LABEL, "a",
+                    FIXED_COLUMN_ORDER + "3", "b" },
+                new String[] { null, HAS_LABEL, "a", null, "b" }, 5);
+    }
 }
diff --git a/tools/makelabel/res/com/android/inputmethod/keyboard/internal/KeyboardLabelsSet.tmpl b/tools/makelabel/res/com/android/inputmethod/keyboard/internal/KeyboardLabelsSet.tmpl
index 0e887e49452eb5a46f0152f0da861b9d0c510e1a..72f4edda747835e0a62040c88d1d3e346dfd1de5 100644
--- a/tools/makelabel/res/com/android/inputmethod/keyboard/internal/KeyboardLabelsSet.tmpl
+++ b/tools/makelabel/res/com/android/inputmethod/keyboard/internal/KeyboardLabelsSet.tmpl
@@ -31,7 +31,8 @@ public final class KeyboardLabelsSet {
     // Language to labels map.
     private static final HashMap<String, String[]> sLocaleToLabelsMap =
             new HashMap<String, String[]>();
-    private static final HashMap<String, Integer> sNameToIdMap = new HashMap<String, Integer>();
+    private static final HashMap<String, Integer> sLowerCaseNameToIdsMap =
+            new HashMap<String, Integer>();
 
     private String[] mLabels;
     // Resource name to label map.
@@ -60,12 +61,21 @@ public final class KeyboardLabelsSet {
     }
 
     public String getLabel(final String name) {
-        if (mResourceNameToLabelsMap.containsKey(name)) {
-            return mResourceNameToLabelsMap.get(name);
+        String lowerCaseName = null;
+        String label = mResourceNameToLabelsMap.get(name);
+        if (label == null) {
+            lowerCaseName = name.toLowerCase();
+            label = mResourceNameToLabelsMap.get(lowerCaseName);
+        }
+        if (label != null) {
+            return label;
+        }
+        Integer id = sLowerCaseNameToIdsMap.get(name);
+        if (id == null) {
+            id = sLowerCaseNameToIdsMap.get(lowerCaseName); // lowerCaseName != null
         }
-        final Integer id = sNameToIdMap.get(name);
         if (id == null) throw new RuntimeException("Unknown label: " + name);
-        final String label = (id < mLabels.length) ? mLabels[id] : null;
+        label = (id < mLabels.length) ? mLabels[id] : null;
         return (label == null) ? LANGUAGE_DEFAULT[id] : label;
     }
 
@@ -105,7 +115,7 @@ public final class KeyboardLabelsSet {
     static {
         int id = 0;
         for (final String name : NAMES) {
-            sNameToIdMap.put(name, id++);
+            sLowerCaseNameToIdsMap.put(name, id++);
         }
 
         for (int i = 0; i < LANGUAGES_AND_LABELS.length; i += 2) {