Merge "[step3] Add spell checker service."
diff --git a/java/res/values/attrs.xml b/java/res/values/attrs.xml
index 4c3ea83..e503d98 100644
--- a/java/res/values/attrs.xml
+++ b/java/res/values/attrs.xml
@@ -150,6 +150,8 @@
         <attr name="popupKeyboardTemplate" format="reference" />
         <!-- Locale of the keyboard layout -->
         <attr name="keyboardLocale" format="string" />
+        <!-- True if the keyboard is Right-To-Left -->
+        <attr name="isRtlKeyboard" format="boolean" />
         <!-- Icon set for key top and key preview. -->
         <attr name="iconShiftKey" format="reference" />
         <attr name="iconToSymbolKey" format="reference" />
diff --git a/java/res/values/donottranslate.xml b/java/res/values/donottranslate.xml
index f88d2df..4cf9303 100644
--- a/java/res/values/donottranslate.xml
+++ b/java/res/values/donottranslate.xml
@@ -169,4 +169,7 @@
         <item>English (UK)</item>
         <item>Deutsch (QWERTY)</item>
     </string-array>
+
+    <!-- Generic subtype label -->
+    <string name="subtype_generic">%s</string>
 </resources>
diff --git a/java/res/values/strings.xml b/java/res/values/strings.xml
index 574c2c7..efc32a7 100644
--- a/java/res/values/strings.xml
+++ b/java/res/values/strings.xml
@@ -329,89 +329,12 @@
     <!-- Title of the item to change the keyboard theme [CHAR LIMIT=20]-->
     <string name="keyboard_layout">Keyboard theme</string>
 
-    <!-- Description for Czech keyboard subtype [CHAR LIMIT=35] -->
-    <string name="subtype_mode_cs_keyboard">Czech Keyboard</string>
-    <!-- Description for Arabic keyboard subtype [CHAR LIMIT=35] -->
-    <string name="subtype_mode_ar_keyboard">Arabic Keyboard</string>
-    <!-- Description for Danish keyboard subtype [CHAR LIMIT=35] -->
-    <string name="subtype_mode_da_keyboard">Danish Keyboard</string>
-    <!-- Description for German keyboard subtype [CHAR LIMIT=35] -->
-    <string name="subtype_mode_de_keyboard">German Keyboard</string>
     <!-- Description for German QWERTY keyboard subtype [CHAR LIMIT=35] -->
-    <string name="subtype_mode_de_qwerty_keyboard">German QWERTY Keyboard</string>
+    <string name="subtype_de_qwerty">German QWERTY</string>
     <!-- Description for English (United Kingdom) keyboard subtype [CHAR LIMIT=35] -->
-    <string name="subtype_mode_en_GB_keyboard">English (UK) Keyboard</string>
+    <string name="subtype_en_GB">English (UK)</string>
     <!-- Description for English (United States) keyboard subtype [CHAR LIMIT=35] -->
-    <string name="subtype_mode_en_US_keyboard">English (US) Keyboard</string>
-    <!-- Description for Spanish keyboard subtype [CHAR LIMIT=35] -->
-    <string name="subtype_mode_es_keyboard">Spanish Keyboard</string>
-    <!-- Description for Finnish keyboard subtype [CHAR LIMIT=35] -->
-    <string name="subtype_mode_fi_keyboard">Finnish Keyboard</string>
-    <!-- Description for French keyboard subtype [CHAR LIMIT=35] -->
-    <string name="subtype_mode_fr_keyboard">French Keyboard</string>
-    <!-- Description for French (Canada) keyboard subtype [CHAR LIMIT=35] -->
-    <string name="subtype_mode_fr_CA_keyboard">French (Canada) Keyboard</string>
-    <!-- Description for French (Switzerland) keyboard subtype [CHAR LIMIT=35] -->
-    <string name="subtype_mode_fr_CH_keyboard">French (Switzerland) Keyboard</string>
-    <!-- Description for Croatian keyboard subtype [CHAR LIMIT=35] -->
-    <string name="subtype_mode_hr_keyboard">Croatian Keyboard</string>
-    <!-- Description for Hungarian keyboard subtype [CHAR LIMIT=35] -->
-    <string name="subtype_mode_hu_keyboard">Hungarian Keyboard</string>
-    <!-- Description for Hebrew keyboard subtype [CHAR LIMIT=35] -->
-    <!-- Java uses the deprecated "iw" code instead of the standard "he" code -->
-    <string name="subtype_mode_iw_keyboard">Hebrew Keyboard</string>
-    <!-- Description for Italian keyboard subtype [CHAR LIMIT=35] -->
-    <string name="subtype_mode_it_keyboard">Italian Keyboard</string>
-    <!-- Description for Norwegian keyboard subtype [CHAR LIMIT=35] -->
-    <string name="subtype_mode_nb_keyboard">Norwegian Keyboard</string>
-    <!-- Description for Dutch keyboard subtype [CHAR LIMIT=35] -->
-    <string name="subtype_mode_nl_keyboard">Dutch Keyboard</string>
-    <!-- Description for Polish keyboard subtype [CHAR LIMIT=35] -->
-    <string name="subtype_mode_pl_keyboard">Polish Keyboard</string>
-    <!-- Description for Portuguese keyboard subtype [CHAR LIMIT=35] -->
-    <string name="subtype_mode_pt_keyboard">Portuguese Keyboard</string>
-    <!-- Description for Russian keyboard subtype [CHAR LIMIT=35] -->
-    <string name="subtype_mode_ru_keyboard">Russian Keyboard</string>
-    <!-- Description for Serbian keyboard subtype [CHAR LIMIT=35] -->
-    <string name="subtype_mode_sr_keyboard">Serbian Keyboard</string>
-    <!-- Description for Swedish keyboard subtype [CHAR LIMIT=35] -->
-    <string name="subtype_mode_sv_keyboard">Swedish Keyboard</string>
-    <!-- Description for Turkish keyboard subtype [CHAR LIMIT=35] -->
-    <string name="subtype_mode_tr_keyboard">Turkish Keyboard</string>
-    <!-- Description for Afrikaans voice input subtype [CHAR LIMIT=35] -->
-    <string name="subtype_mode_af_voice">Afrikaans Voice</string>
-    <!-- Description for Czech voice input subtype [CHAR LIMIT=35] -->
-    <string name="subtype_mode_cs_voice">Czech Voice</string>
-    <!-- Description for German voice input subtype [CHAR LIMIT=35] -->
-    <string name="subtype_mode_de_voice">German Voice</string>
-    <!-- Description for English voice input subtype [CHAR LIMIT=35] -->
-    <string name="subtype_mode_en_voice">English Voice</string>
-    <!-- Description for Spanish voice input subtype [CHAR LIMIT=35] -->
-    <string name="subtype_mode_es_voice">Spanish Voice</string>
-    <!-- Description for French voice input subtype [CHAR LIMIT=35] -->
-    <string name="subtype_mode_fr_voice">French Voice</string>
-    <!-- Description for Italian voice input subtype [CHAR LIMIT=35] -->
-    <string name="subtype_mode_it_voice">Italian Voice</string>
-    <!-- Description for Japanese voice input subtype [CHAR LIMIT=35] -->
-    <string name="subtype_mode_ja_voice">Japanese Voice</string>
-    <!-- Description for Korean voice input subtype [CHAR LIMIT=35] -->
-    <string name="subtype_mode_ko_voice">Korean Voice</string>
-    <!-- Description for Dutch voice input subtype [CHAR LIMIT=35] -->
-    <string name="subtype_mode_nl_voice">Dutch Voice</string>
-    <!-- Description for Polish voice input subtype [CHAR LIMIT=35] -->
-    <string name="subtype_mode_pl_voice">Polish Voice</string>
-    <!-- Description for Portuguese voice input subtype [CHAR LIMIT=35] -->
-    <string name="subtype_mode_pt_voice">Portuguese Voice</string>
-    <!-- Description for Russian voice input subtype [CHAR LIMIT=35] -->
-    <string name="subtype_mode_ru_voice">Russian Voice</string>
-    <!-- Description for Turkish voice input subtype [CHAR LIMIT=35] -->
-    <string name="subtype_mode_tr_voice">Turkish Voice</string>
-    <!-- Description for Chinese, Yue voice input subtype [CHAR LIMIT=35] -->
-    <string name="subtype_mode_yue_voice">Chinese, Yue Voice</string>
-    <!-- Description for Chinese, Mandarin voice input subtype [CHAR LIMIT=35] -->
-    <string name="subtype_mode_zh_voice">Chinese, Mandarin Voice</string>
-    <!-- Description for isiZulu voice input subtype [CHAR LIMIT=35] -->
-    <string name="subtype_mode_zu_voice">isiZulu Voice</string>
+    <string name="subtype_en_US">English (US)</string>
 
     <!-- Title of an option for usability study mode -->
     <string name="prefs_usability_study_mode">Usability study mode</string>
diff --git a/java/res/xml-ar/kbd_symbols.xml b/java/res/xml-ar/kbd_symbols.xml
new file mode 100644
index 0000000..9e5c255
--- /dev/null
+++ b/java/res/xml-ar/kbd_symbols.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+**
+** Copyright 2011, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+**     http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+-->
+
+<Keyboard
+    xmlns:latin="http://schemas.android.com/apk/res/com.android.inputmethod.latin"
+    latin:isRtlKeyboard="true"
+>
+    <include
+        latin:keyboardLayout="@xml/kbd_rows_symbols" />
+</Keyboard>
diff --git a/java/res/xml-ar/kbd_symbols_shift.xml b/java/res/xml-ar/kbd_symbols_shift.xml
new file mode 100644
index 0000000..934e6f8
--- /dev/null
+++ b/java/res/xml-ar/kbd_symbols_shift.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+**
+** Copyright 2011, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+**     http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+-->
+
+<Keyboard
+    xmlns:latin="http://schemas.android.com/apk/res/com.android.inputmethod.latin"
+    latin:isRtlKeyboard="true"
+>
+    <include
+        latin:keyboardLayout="@xml/kbd_rows_symbols_shift" />
+</Keyboard>
diff --git a/java/res/xml-iw/kbd_symbols.xml b/java/res/xml-iw/kbd_symbols.xml
new file mode 100644
index 0000000..9e5c255
--- /dev/null
+++ b/java/res/xml-iw/kbd_symbols.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+**
+** Copyright 2011, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+**     http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+-->
+
+<Keyboard
+    xmlns:latin="http://schemas.android.com/apk/res/com.android.inputmethod.latin"
+    latin:isRtlKeyboard="true"
+>
+    <include
+        latin:keyboardLayout="@xml/kbd_rows_symbols" />
+</Keyboard>
diff --git a/java/res/xml-iw/kbd_symbols_shift.xml b/java/res/xml-iw/kbd_symbols_shift.xml
new file mode 100644
index 0000000..934e6f8
--- /dev/null
+++ b/java/res/xml-iw/kbd_symbols_shift.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+**
+** Copyright 2011, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+**     http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+-->
+
+<Keyboard
+    xmlns:latin="http://schemas.android.com/apk/res/com.android.inputmethod.latin"
+    latin:isRtlKeyboard="true"
+>
+    <include
+        latin:keyboardLayout="@xml/kbd_rows_symbols_shift" />
+</Keyboard>
diff --git a/java/res/xml-sw600dp/kbd_symbols.xml b/java/res/xml-sw600dp/kbd_rows_symbols.xml
similarity index 99%
rename from java/res/xml-sw600dp/kbd_symbols.xml
rename to java/res/xml-sw600dp/kbd_rows_symbols.xml
index fc89cd3..9982f45 100644
--- a/java/res/xml-sw600dp/kbd_symbols.xml
+++ b/java/res/xml-sw600dp/kbd_rows_symbols.xml
@@ -18,7 +18,7 @@
 */
 -->
 
-<Keyboard
+<merge
     xmlns:latin="http://schemas.android.com/apk/res/com.android.inputmethod.latin"
 >
     <include
@@ -170,4 +170,4 @@
         <include
             latin:keyboardLayout="@xml/kbd_qwerty_f2" />
     </Row>
-</Keyboard>
+</merge>
diff --git a/java/res/xml-sw600dp/kbd_symbols_shift.xml b/java/res/xml-sw600dp/kbd_rows_symbols_shift.xml
similarity index 99%
rename from java/res/xml-sw600dp/kbd_symbols_shift.xml
rename to java/res/xml-sw600dp/kbd_rows_symbols_shift.xml
index 99b78a4..ac4b93f 100644
--- a/java/res/xml-sw600dp/kbd_symbols_shift.xml
+++ b/java/res/xml-sw600dp/kbd_rows_symbols_shift.xml
@@ -18,7 +18,7 @@
 */
 -->
 
-<Keyboard
+<merge
     xmlns:latin="http://schemas.android.com/apk/res/com.android.inputmethod.latin"
 >
     <include
@@ -147,4 +147,4 @@
         <include
             latin:keyboardLayout="@xml/kbd_qwerty_f2" />
     </Row>
-</Keyboard>
+</merge>
diff --git a/java/res/xml-sw768dp/kbd_symbols.xml b/java/res/xml-sw768dp/kbd_rows_symbols.xml
similarity index 98%
rename from java/res/xml-sw768dp/kbd_symbols.xml
rename to java/res/xml-sw768dp/kbd_rows_symbols.xml
index ba0715c..1d5bd9d 100644
--- a/java/res/xml-sw768dp/kbd_symbols.xml
+++ b/java/res/xml-sw768dp/kbd_rows_symbols.xml
@@ -2,7 +2,7 @@
 <!--
 /*
 **
-** Copyright 2010, The Android Open Source Project
+** Copyright 2011, The Android Open Source Project
 **
 ** Licensed under the Apache License, Version 2.0 (the "License");
 ** you may not use this file except in compliance with the License.
@@ -18,7 +18,7 @@
 */
 -->
 
-<Keyboard
+<merge
     xmlns:latin="http://schemas.android.com/apk/res/com.android.inputmethod.latin"
 >
     <include
@@ -189,4 +189,4 @@
             </case>
         </switch>
     </Row>
-</Keyboard>
+</merge>
diff --git a/java/res/xml-sw768dp/kbd_symbols_shift.xml b/java/res/xml-sw768dp/kbd_rows_symbols_shift.xml
similarity index 98%
rename from java/res/xml-sw768dp/kbd_symbols_shift.xml
rename to java/res/xml-sw768dp/kbd_rows_symbols_shift.xml
index 1ddd64b..2219bdc 100644
--- a/java/res/xml-sw768dp/kbd_symbols_shift.xml
+++ b/java/res/xml-sw768dp/kbd_rows_symbols_shift.xml
@@ -2,7 +2,7 @@
 <!--
 /*
 **
-** Copyright 2010, The Android Open Source Project
+** Copyright 2011, The Android Open Source Project
 **
 ** Licensed under the Apache License, Version 2.0 (the "License");
 ** you may not use this file except in compliance with the License.
@@ -18,7 +18,7 @@
 */
 -->
 
-<Keyboard
+<merge
     xmlns:latin="http://schemas.android.com/apk/res/com.android.inputmethod.latin"
 >
     <include
@@ -164,4 +164,4 @@
             </case>
         </switch>
     </Row>
-</Keyboard>
+</merge>
diff --git a/java/res/xml-sw600dp/kbd_symbols.xml b/java/res/xml/kbd_rows_symbols.xml
similarity index 64%
copy from java/res/xml-sw600dp/kbd_symbols.xml
copy to java/res/xml/kbd_rows_symbols.xml
index fc89cd3..a8eeb3b 100644
--- a/java/res/xml-sw600dp/kbd_symbols.xml
+++ b/java/res/xml/kbd_rows_symbols.xml
@@ -18,7 +18,7 @@
 */
 -->
 
-<Keyboard
+<merge
     xmlns:latin="http://schemas.android.com/apk/res/com.android.inputmethod.latin"
 >
     <include
@@ -26,7 +26,7 @@
     <include
         latin:keyboardLayout="@xml/kbd_currency_key_styles" />
     <Row
-        latin:keyWidth="9.0%p"
+        latin:keyWidth="10%p"
     >
         <Key
             latin:keyLabel="@string/keylabel_for_symbols_1"
@@ -57,18 +57,16 @@
             latin:popupCharacters="@string/alternates_for_symbols_9" />
         <Key
             latin:keyLabel="@string/keylabel_for_symbols_0"
-            latin:popupCharacters="@string/alternates_for_symbols_0" />
-        <Key
-            latin:keyStyle="deleteKeyStyle"
-            latin:keyXPos="-10.0%p"
-            latin:keyWidth="fillBoth" />
+            latin:popupCharacters="@string/alternates_for_symbols_0"
+            latin:keyWidth="fillRight" />
     </Row>
     <Row
-        latin:keyWidth="9.0%p"
+        latin:keyWidth="10%p"
     >
         <Key
-            latin:keyLabel="#"
-            latin:keyXPos="4.5%p" />
+            latin:keyLabel="\@" />
+        <Key
+            latin:keyLabel="\#" />
         <Key
             latin:keyStyle="currencyKeyStyle" />
         <Key
@@ -90,84 +88,43 @@
             latin:popupCharacters="[,{,&lt;" />
         <Key
             latin:keyLabel=")"
-            latin:popupCharacters="],},&gt;" />
-        <Key
-            latin:keyStyle="returnKeyStyle"
-            latin:keyXPos="-14.6%p"
-            latin:keyWidth="fillBoth" />
+            latin:popupCharacters="],},&gt;"
+            latin:keyWidth="fillRight" />
     </Row>
     <Row
-        latin:keyWidth="8.9%p"
+        latin:keyWidth="10%p"
     >
         <Key
-            latin:keyStyle="moreKeyStyle"
-            latin:keyWidth="10.0%p" />
+            latin:keyStyle="altKeyStyle"
+            latin:keyWidth="15%p"
+            latin:visualInsetsRight="1%p" />
         <Key
-            latin:keyLabel="&lt;"
-            latin:popupCharacters="≤,«,‹" />
+            latin:keyLabel="!"
+            latin:popupCharacters="¡" />
+        <!-- Note: DroidSans doesn't have double-high-reversed-quotation '\u201f' glyph. -->
+        <!-- latin:popupCharacters="“,”,„,‟,«,»" -->
         <Key
-            latin:keyLabel="&gt;"
-            latin:popupCharacters="≥,»,›" />
+            latin:keyLabel="&quot;"
+            latin:popupCharacters="“,”,«,»"
+            latin:maxPopupKeyboardColumn="6" />
         <Key
-            latin:keyLabel="="
-            latin:popupCharacters="≠,≈" />
-        <switch>
-            <case
-                latin:mode="url"
-            >
-                <Key
-                    latin:keyLabel="\'"
-                    latin:popupCharacters="‘,’,‚,‛" />
-            </case>
-            <default>
-                <Key
-                    latin:keyLabel=":" />
-            </default>
-        </switch>
+            latin:keyLabel="\'"
+            latin:popupCharacters="‘,’,‚,‛" />
+        <Key
+            latin:keyLabel=":" />
         <Key
             latin:keyLabel="@string/keylabel_for_symbols_semicolon"
             latin:popupCharacters="@string/alternates_for_symbols_semicolon" />
         <Key
-            latin:keyLabel="@string/keylabel_for_symbols_comma"
-            latin:popupCharacters="@string/alternates_for_symbols_comma" />
-        <Key
-            latin:keyLabel="." />
-        <Key
-            latin:keyLabel="!"
-            latin:popupCharacters="¡" />
+            latin:keyLabel="/" />
         <Key
             latin:keyLabel="@string/keylabel_for_symbols_question"
             latin:popupCharacters="@string/alternates_for_symbols_question" />
         <Key
-            latin:keyLabel="/"
-            latin:keyWidth="fillRight" />
+            latin:keyStyle="deleteKeyStyle"
+            latin:keyWidth="fillRight"
+            latin:visualInsetsLeft="1%p" />
     </Row>
-    <Row
-        latin:keyWidth="8.9%p"
-    >
-        <Key
-            latin:keyStyle="toAlphaKeyStyle"
-            latin:keyLabelOption="alignLeft"
-            latin:keyWidth="13.0%p" />
-        <Key
-            latin:keyStyle="tabKeyStyle" />
-        <Key
-            latin:keyLabel="\@" />
-        <Key
-            latin:keyStyle="spaceKeyStyle"
-            latin:keyXPos="30.750%p"
-            latin:keyWidth="39.750%p" />
-        <!-- Note: DroidSans doesn't have double-high-reversed-quotation '\u201f' glyph. -->
-        <!-- latin:popupCharacters="“,”,„,‟,«,»,‘,’,‚,‛" -->
-        <Key
-            latin:keyLabel="&quot;"
-            latin:popupCharacters="“,”,«,»,‘,’,‚,‛" />
-        <Key
-            latin:keyLabel="_" />
-        <Spacer
-            latin:keyXPos="-10.00%p"
-            latin:keyWidth="0%p" />
-        <include
-            latin:keyboardLayout="@xml/kbd_qwerty_f2" />
-    </Row>
-</Keyboard>
+    <include
+        latin:keyboardLayout="@xml/kbd_symbols_row4" />
+</merge>
diff --git a/java/res/xml-sw600dp/kbd_symbols_shift.xml b/java/res/xml/kbd_rows_symbols_shift.xml
similarity index 60%
copy from java/res/xml-sw600dp/kbd_symbols_shift.xml
copy to java/res/xml/kbd_rows_symbols_shift.xml
index 99b78a4..0706cbc 100644
--- a/java/res/xml-sw600dp/kbd_symbols_shift.xml
+++ b/java/res/xml/kbd_rows_symbols_shift.xml
@@ -2,7 +2,7 @@
 <!--
 /*
 **
-** Copyright 2011, The Android Open Source Project
+** Copyright 2008, The Android Open Source Project
 **
 ** Licensed under the Apache License, Version 2.0 (the "License");
 ** you may not use this file except in compliance with the License.
@@ -18,13 +18,13 @@
 */
 -->
 
-<Keyboard
+<merge
     xmlns:latin="http://schemas.android.com/apk/res/com.android.inputmethod.latin"
 >
     <include
         latin:keyboardLayout="@xml/kbd_key_styles" />
     <Row
-        latin:keyWidth="9.0%p"
+        latin:keyWidth="10%p"
     >
         <Key
             latin:keyLabel="~" />
@@ -50,24 +50,19 @@
             latin:keyStyle="nonPasswordSymbolKeyStyle"
             latin:keyLabel="×" />
         <Key
-            latin:keyStyle="nonPasswordSymbolKeyStyle"
-            latin:keyLabel="§"
-            latin:popupCharacters="¶" />
+            latin:keyLabel="{" />
         <Key
-            latin:keyStyle="nonPasswordSymbolKeyStyle"
-            latin:keyLabel="Δ" />
-        <Key
-            latin:keyStyle="deleteKeyStyle"
-            latin:keyXPos="-10.0%p"
-            latin:keyWidth="fillBoth" />
+            latin:keyLabel="}"
+            latin:keyWidth="fillRight" />
     </Row>
     <Row
-        latin:keyWidth="9.0%p"
+        latin:keyWidth="10%p"
     >
         <Key
+            latin:keyStyle="nonSpecialBackgroundTabKeyStyle" />
+        <Key
             latin:keyStyle="nonPasswordSymbolKeyStyle"
-            latin:keyLabel="£"
-            latin:keyXPos="4.5%p" />
+            latin:keyLabel="£" />
         <Key
             latin:keyStyle="nonPasswordSymbolKeyStyle"
             latin:keyLabel="¢" />
@@ -76,75 +71,55 @@
             latin:keyLabel="€" />
         <Key
             latin:keyStyle="nonPasswordSymbolKeyStyle"
-            latin:keyLabel="¥" />
+            latin:keyLabel="°"
+            latin:popupCharacters="′,″" />
         <Key
             latin:keyLabel="^"
             latin:popupCharacters="↑,↓,←,→" />
         <Key
-            latin:keyStyle="nonPasswordSymbolKeyStyle"
-            latin:keyLabel="°"
-            latin:popupCharacters="′,″" />
+            latin:keyLabel="_" />
         <Key
-            latin:keyStyle="nonPasswordSymbolKeyStyle"
-            latin:keyLabel="±"
-            latin:popupCharacters="∞" />
+            latin:keyLabel="="
+            latin:popupCharacters="≠,≈,∞" />
         <Key
-            latin:keyLabel="{" />
+            latin:keyLabel="[" />
         <Key
-            latin:keyLabel="}" />
-        <Key
-            latin:keyStyle="returnKeyStyle"
-            latin:keyXPos="-14.6%p"
-            latin:keyWidth="fillBoth" />
+            latin:keyLabel="]"
+            latin:keyWidth="fillRight" />
     </Row>
     <Row
-        latin:keyWidth="8.9%p"
+        latin:keyWidth="10%p"
     >
         <Key
-            latin:keyStyle="moreKeyStyle"
-            latin:keyWidth="10.0%p" />
-        <Key
-            latin:keyLabel="\\" />
-        <Key
-            latin:keyStyle="nonPasswordSymbolKeyStyle"
-            latin:keyLabel="©" />
-        <Key
-            latin:keyStyle="nonPasswordSymbolKeyStyle"
-            latin:keyLabel="®" />
+            latin:keyStyle="altKeyStyle"
+            latin:keyWidth="15%p"
+            latin:visualInsetsRight="1%p" />
         <Key
             latin:keyStyle="nonPasswordSymbolKeyStyle"
             latin:keyLabel="™" />
         <Key
             latin:keyStyle="nonPasswordSymbolKeyStyle"
-            latin:keyLabel="℅" />
-        <Key
-            latin:keyLabel="[" />
-        <Key
-            latin:keyLabel="]" />
+            latin:keyLabel="®" />
         <Key
             latin:keyStyle="nonPasswordSymbolKeyStyle"
-            latin:keyLabel="¡" />
+            latin:keyLabel="©" />
         <Key
             latin:keyStyle="nonPasswordSymbolKeyStyle"
-            latin:keyLabel="¿" />
+            latin:keyLabel="¶"
+            latin:popupCharacters="§" />
+        <Key
+            latin:keyLabel="\\" />
+        <Key
+            latin:keyLabel="&lt;"
+            latin:popupCharacters="≤,«,‹" />
+        <Key
+            latin:keyLabel="&gt;"
+            latin:popupCharacters="≥,»,›" />
+        <Key
+            latin:keyStyle="deleteKeyStyle"
+            latin:keyWidth="fillRight"
+            latin:visualInsetsLeft="1%p" />
     </Row>
-    <Row
-        latin:keyWidth="8.9%p"
-    >
-        <Key
-            latin:keyStyle="toAlphaKeyStyle"
-            latin:keyLabelOption="alignLeft"
-            latin:keyWidth="13.0%p" />
-        <Key
-            latin:keyStyle="tabKeyStyle" />
-        <Key
-            latin:keyStyle="spaceKeyStyle"
-            latin:keyXPos="30.750%p"
-            latin:keyWidth="39.750%p" />
-        <Spacer
-            latin:keyXPos="-10.00%p"
-            latin:keyWidth="0%p" />
-        <include
-            latin:keyboardLayout="@xml/kbd_qwerty_f2" />
-    </Row>
-</Keyboard>
+    <include
+        latin:keyboardLayout="@xml/kbd_symbols_shift_row4" />
+</merge>
diff --git a/java/res/xml/kbd_symbols.xml b/java/res/xml/kbd_symbols.xml
index 7bb8d02..737f684 100644
--- a/java/res/xml/kbd_symbols.xml
+++ b/java/res/xml/kbd_symbols.xml
@@ -1,125 +1,26 @@
 <?xml version="1.0" encoding="utf-8"?>
 <!--
-/* 
+/*
 **
 ** Copyright 2008, The Android Open Source Project
 **
-** Licensed under the Apache License, Version 2.0 (the "License"); 
-** you may not use this file except in compliance with the License. 
-** You may obtain a copy of the License at 
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
 **
-**     http://www.apache.org/licenses/LICENSE-2.0 
+**     http://www.apache.org/licenses/LICENSE-2.0
 **
-** Unless required by applicable law or agreed to in writing, software 
-** distributed under the License is distributed on an "AS IS" BASIS, 
-** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 
-** See the License for the specific language governing permissions and 
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
 ** limitations under the License.
 */
 -->
 
 <Keyboard
     xmlns:latin="http://schemas.android.com/apk/res/com.android.inputmethod.latin"
-    latin:keyWidth="10%p"
 >
     <include
-        latin:keyboardLayout="@xml/kbd_key_styles" />
-    <include
-        latin:keyboardLayout="@xml/kbd_currency_key_styles" />
-    <Row>
-        <Key
-            latin:keyLabel="@string/keylabel_for_symbols_1"
-            latin:popupCharacters="@string/alternates_for_symbols_1" />
-        <Key
-            latin:keyLabel="@string/keylabel_for_symbols_2"
-            latin:popupCharacters="@string/alternates_for_symbols_2" />
-        <Key
-            latin:keyLabel="@string/keylabel_for_symbols_3"
-            latin:popupCharacters="@string/alternates_for_symbols_3" />
-        <Key
-            latin:keyLabel="@string/keylabel_for_symbols_4"
-            latin:popupCharacters="@string/alternates_for_symbols_4" />
-        <Key
-            latin:keyLabel="@string/keylabel_for_symbols_5"
-            latin:popupCharacters="@string/alternates_for_symbols_5" />
-        <Key
-            latin:keyLabel="@string/keylabel_for_symbols_6"
-            latin:popupCharacters="@string/alternates_for_symbols_6" />
-        <Key
-            latin:keyLabel="@string/keylabel_for_symbols_7"
-            latin:popupCharacters="@string/alternates_for_symbols_7" />
-        <Key
-            latin:keyLabel="@string/keylabel_for_symbols_8"
-            latin:popupCharacters="@string/alternates_for_symbols_8" />
-        <Key
-            latin:keyLabel="@string/keylabel_for_symbols_9"
-            latin:popupCharacters="@string/alternates_for_symbols_9" />
-        <Key
-            latin:keyLabel="@string/keylabel_for_symbols_0"
-            latin:popupCharacters="@string/alternates_for_symbols_0"
-            latin:keyWidth="fillRight" />
-    </Row>
-    <Row>
-        <Key
-            latin:keyLabel="\@" />
-        <Key
-            latin:keyLabel="\#" />
-        <Key
-            latin:keyStyle="currencyKeyStyle" />
-        <Key
-            latin:keyLabel="@string/keylabel_for_symbols_percent"
-            latin:popupCharacters="@string/alternates_for_symbols_percent" />
-        <Key
-            latin:keyLabel="&amp;" />
-        <Key
-            latin:keyLabel="*"
-            latin:popupCharacters="†,‡,★" />
-        <Key
-            latin:keyLabel="-"
-            latin:popupCharacters="_,–,—" />
-        <Key
-            latin:keyLabel="+"
-            latin:popupCharacters="±" />
-        <Key
-            latin:keyLabel="("
-            latin:popupCharacters="[,{,&lt;" />
-        <Key
-            latin:keyLabel=")"
-            latin:popupCharacters="],},&gt;"
-            latin:keyWidth="fillRight" />
-    </Row>
-    <Row>
-        <Key
-            latin:keyStyle="altKeyStyle"
-            latin:keyWidth="15%p"
-            latin:visualInsetsRight="1%p" />
-        <Key
-            latin:keyLabel="!"
-            latin:popupCharacters="¡" />
-        <!-- Note: DroidSans doesn't have double-high-reversed-quotation '\u201f' glyph. -->
-        <!-- latin:popupCharacters="“,”,„,‟,«,»" -->
-        <Key
-            latin:keyLabel="&quot;"
-            latin:popupCharacters="“,”,«,»"
-            latin:maxPopupKeyboardColumn="6" />
-        <Key
-            latin:keyLabel="\'"
-            latin:popupCharacters="‘,’,‚,‛" />
-        <Key
-            latin:keyLabel=":" />
-        <Key
-            latin:keyLabel="@string/keylabel_for_symbols_semicolon"
-            latin:popupCharacters="@string/alternates_for_symbols_semicolon" />
-        <Key
-            latin:keyLabel="/" />
-        <Key
-            latin:keyLabel="@string/keylabel_for_symbols_question"
-            latin:popupCharacters="@string/alternates_for_symbols_question" />
-        <Key
-            latin:keyStyle="deleteKeyStyle"
-            latin:keyWidth="fillRight"
-            latin:visualInsetsLeft="1%p" />
-    </Row>
-    <include
-        latin:keyboardLayout="@xml/kbd_symbols_row4" />
+        latin:keyboardLayout="@xml/kbd_rows_symbols" />
 </Keyboard>
diff --git a/java/res/xml/kbd_symbols_shift.xml b/java/res/xml/kbd_symbols_shift.xml
index f4ccd6c..9c163d6 100644
--- a/java/res/xml/kbd_symbols_shift.xml
+++ b/java/res/xml/kbd_symbols_shift.xml
@@ -1,119 +1,26 @@
 <?xml version="1.0" encoding="utf-8"?>
 <!--
-/* 
+/*
 **
 ** Copyright 2008, The Android Open Source Project
 **
-** Licensed under the Apache License, Version 2.0 (the "License"); 
-** you may not use this file except in compliance with the License. 
-** You may obtain a copy of the License at 
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
 **
-**     http://www.apache.org/licenses/LICENSE-2.0 
+**     http://www.apache.org/licenses/LICENSE-2.0
 **
-** Unless required by applicable law or agreed to in writing, software 
-** distributed under the License is distributed on an "AS IS" BASIS, 
-** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 
-** See the License for the specific language governing permissions and 
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
 ** limitations under the License.
 */
 -->
 
 <Keyboard
     xmlns:latin="http://schemas.android.com/apk/res/com.android.inputmethod.latin"
-    latin:keyWidth="10%p"
 >
     <include
-        latin:keyboardLayout="@xml/kbd_key_styles" />
-    <Row>
-        <Key
-            latin:keyLabel="~" />
-        <Key
-            latin:keyLabel="`" />
-        <Key
-            latin:keyLabel="|" />
-        <Key
-            latin:keyStyle="nonPasswordSymbolKeyStyle"
-            latin:keyLabel="•"
-            latin:popupCharacters="♪,♥,♠,♦,♣" />
-        <Key
-            latin:keyStyle="nonPasswordSymbolKeyStyle"
-            latin:keyLabel="√" />
-        <Key
-            latin:keyStyle="nonPasswordSymbolKeyStyle"
-            latin:keyLabel="π"
-            latin:popupCharacters="Π" />
-        <Key
-            latin:keyStyle="nonPasswordSymbolKeyStyle"
-            latin:keyLabel="÷" />
-        <Key
-            latin:keyStyle="nonPasswordSymbolKeyStyle"
-            latin:keyLabel="×" />
-        <Key
-            latin:keyLabel="{" />
-        <Key
-            latin:keyLabel="}"
-            latin:keyWidth="fillRight" />
-    </Row>
-    <Row>
-        <Key
-            latin:keyStyle="nonSpecialBackgroundTabKeyStyle" />
-        <Key
-            latin:keyStyle="nonPasswordSymbolKeyStyle"
-            latin:keyLabel="£" />
-        <Key
-            latin:keyStyle="nonPasswordSymbolKeyStyle"
-            latin:keyLabel="¢" />
-        <Key
-            latin:keyStyle="nonPasswordSymbolKeyStyle"
-            latin:keyLabel="€" />
-        <Key
-            latin:keyStyle="nonPasswordSymbolKeyStyle"
-            latin:keyLabel="°"
-            latin:popupCharacters="′,″" />
-        <Key
-            latin:keyLabel="^"
-            latin:popupCharacters="↑,↓,←,→" />
-        <Key
-            latin:keyLabel="_" />
-        <Key
-            latin:keyLabel="="
-            latin:popupCharacters="≠,≈,∞" />
-        <Key
-            latin:keyLabel="[" />
-        <Key
-            latin:keyLabel="]"
-            latin:keyWidth="fillRight" />
-    </Row>
-    <Row>
-        <Key
-            latin:keyStyle="shiftKeyStyle"
-            latin:keyWidth="15%p"
-            latin:visualInsetsRight="1%p" />
-        <Key
-            latin:keyStyle="nonPasswordSymbolKeyStyle"
-            latin:keyLabel="™" />
-        <Key
-            latin:keyStyle="nonPasswordSymbolKeyStyle"
-            latin:keyLabel="®" />
-        <Key
-            latin:keyStyle="nonPasswordSymbolKeyStyle"
-            latin:keyLabel="©" />
-        <Key
-            latin:keyStyle="nonPasswordSymbolKeyStyle"
-            latin:keyLabel="¶"
-            latin:popupCharacters="§" />
-        <Key
-            latin:keyLabel="\\" />
-        <Key
-            latin:keyLabel="&lt;"
-            latin:popupCharacters="≤,«,‹" />
-        <Key
-            latin:keyLabel="&gt;"
-            latin:popupCharacters="≥,»,›" />
-        <Key
-            latin:keyStyle="deleteKeyStyle"
-            latin:keyWidth="fillRight"
-            latin:visualInsetsLeft="1%p" />
-    </Row>
-    <include latin:keyboardLayout="@xml/kbd_symbols_shift_row4" />
+        latin:keyboardLayout="@xml/kbd_rows_symbols_shift" />
 </Keyboard>
diff --git a/java/res/xml/method.xml b/java/res/xml/method.xml
index 7aaf57b..7a4e7ea 100644
--- a/java/res/xml/method.xml
+++ b/java/res/xml/method.xml
@@ -24,237 +24,132 @@
 <!-- Voice: af, cs, da, de, en, es, fr, it, ja, ko, nl, pl, pt, ru, tr, yue, zh, zu -->
 <!-- TODO: use <lang>_keyboard icon instead of a common keyboard icon. -->
 <!-- TODO: use <lang>_mic icon instead of a common mic icon. -->
-<!-- TODO: remove all comment outed voice subtypes -->
 <!-- If IME doesn't have an applicable subtype, the first subtype will be used as a default
      subtype.-->
 <input-method xmlns:android="http://schemas.android.com/apk/res/android"
         android:settingsActivity="com.android.inputmethod.latin.Settings"
         android:isDefault="@bool/im_is_default">
     <subtype android:icon="@drawable/ic_subtype_keyboard"
-            android:label="@string/subtype_mode_en_US_keyboard"
+            android:label="@string/subtype_en_US"
             android:imeSubtypeLocale="en_US"
             android:imeSubtypeMode="keyboard"
             android:imeSubtypeExtraValue="TrySuppressingImeSwitcher"
     />
-<!--     <subtype android:icon="@drawable/ic_subtype_mic" -->
-<!--             android:label="@string/subtype_mode_en_voice" -->
-<!--             android:imeSubtypeLocale="en" -->
-<!--             android:imeSubtypeMode="voice" -->
-<!--             android:imeSubtypeExtraValue="excludeFromLastInputMethod,requireNetworkConnectivity" -->
-<!--     /> -->
     <subtype android:icon="@drawable/ic_subtype_keyboard"
-            android:label="@string/subtype_mode_en_GB_keyboard"
+            android:label="@string/subtype_en_GB"
             android:imeSubtypeLocale="en_GB"
             android:imeSubtypeMode="keyboard"
             android:imeSubtypeExtraValue="TrySuppressingImeSwitcher"
     />
-    <!-- The file for Arabic layout is an alpha version. It needs to be run through UX. -->
     <subtype android:icon="@drawable/ic_subtype_keyboard"
-            android:label="@string/subtype_mode_ar_keyboard"
+            android:label="@string/subtype_generic"
             android:imeSubtypeLocale="ar"
             android:imeSubtypeMode="keyboard"
     />
     <subtype android:icon="@drawable/ic_subtype_keyboard"
-            android:label="@string/subtype_mode_cs_keyboard"
+            android:label="@string/subtype_generic"
             android:imeSubtypeLocale="cs"
             android:imeSubtypeMode="keyboard"
     />
-<!--     <subtype android:icon="@drawable/ic_subtype_mic" -->
-<!--             android:label="@string/subtype_mode_cs_voice" -->
-<!--             android:imeSubtypeLocale="cs" -->
-<!--             android:imeSubtypeMode="voice" -->
-<!--             android:imeSubtypeExtraValue="excludeFromLastInputMethod,requireNetworkConnectivity" -->
-<!--     /> -->
     <subtype android:icon="@drawable/ic_subtype_keyboard"
-            android:label="@string/subtype_mode_da_keyboard"
+            android:label="@string/subtype_generic"
             android:imeSubtypeLocale="da"
             android:imeSubtypeMode="keyboard"
     />
     <subtype android:icon="@drawable/ic_subtype_keyboard"
-            android:label="@string/subtype_mode_de_keyboard"
+            android:label="@string/subtype_generic"
             android:imeSubtypeLocale="de"
             android:imeSubtypeMode="keyboard"
     />
     <subtype android:icon="@drawable/ic_subtype_keyboard"
-            android:label="@string/subtype_mode_de_qwerty_keyboard"
+            android:label="@string/subtype_de_qwerty"
             android:imeSubtypeLocale="de_ZZ"
             android:imeSubtypeMode="keyboard"
     />
-<!--     <subtype android:icon="@drawable/ic_subtype_mic" -->
-<!--             android:label="@string/subtype_mode_de_voice" -->
-<!--             android:imeSubtypeLocale="de" -->
-<!--             android:imeSubtypeMode="voice" -->
-<!--             android:imeSubtypeExtraValue="excludeFromLastInputMethod,requireNetworkConnectivity" -->
-<!--     /> -->
     <subtype android:icon="@drawable/ic_subtype_keyboard"
-            android:label="@string/subtype_mode_es_keyboard"
+            android:label="@string/subtype_generic"
             android:imeSubtypeLocale="es"
             android:imeSubtypeMode="keyboard"
     />
-<!--     <subtype android:icon="@drawable/ic_subtype_mic" -->
-<!--             android:label="@string/subtype_mode_es_voice" -->
-<!--             android:imeSubtypeLocale="es" -->
-<!--             android:imeSubtypeMode="voice" -->
-<!--             android:imeSubtypeExtraValue="excludeFromLastInputMethod,requireNetworkConnectivity" -->
-<!--     /> -->
     <subtype android:icon="@drawable/ic_subtype_keyboard"
-            android:label="@string/subtype_mode_fi_keyboard"
+            android:label="@string/subtype_generic"
             android:imeSubtypeLocale="fi"
             android:imeSubtypeMode="keyboard"
     />
     <subtype android:icon="@drawable/ic_subtype_keyboard"
-            android:label="@string/subtype_mode_fr_keyboard"
+            android:label="@string/subtype_generic"
             android:imeSubtypeLocale="fr"
             android:imeSubtypeMode="keyboard"
     />
-<!--     <subtype android:icon="@drawable/ic_subtype_mic" -->
-<!--             android:label="@string/subtype_mode_fr_voice" -->
-<!--             android:imeSubtypeLocale="fr" -->
-<!--             android:imeSubtypeMode="voice" -->
-<!--             android:imeSubtypeExtraValue="excludeFromLastInputMethod,requireNetworkConnectivity" -->
-<!--     /> -->
     <subtype android:icon="@drawable/ic_subtype_keyboard"
-            android:label="@string/subtype_mode_fr_CA_keyboard"
+            android:label="@string/subtype_generic"
             android:imeSubtypeLocale="fr_CA"
             android:imeSubtypeMode="keyboard"
     />
     <subtype android:icon="@drawable/ic_subtype_keyboard"
-            android:label="@string/subtype_mode_fr_CH_keyboard"
+            android:label="@string/subtype_generic"
             android:imeSubtypeLocale="fr_CH"
             android:imeSubtypeMode="keyboard"
     />
     <subtype android:icon="@drawable/ic_subtype_keyboard"
-            android:label="@string/subtype_mode_hr_keyboard"
+            android:label="@string/subtype_generic"
             android:imeSubtypeLocale="hr"
             android:imeSubtypeMode="keyboard"
     />
     <subtype android:icon="@drawable/ic_subtype_keyboard"
-            android:label="@string/subtype_mode_hu_keyboard"
+            android:label="@string/subtype_generic"
             android:imeSubtypeLocale="hu"
             android:imeSubtypeMode="keyboard"
     />
     <subtype android:icon="@drawable/ic_subtype_keyboard"
-            android:label="@string/subtype_mode_it_keyboard"
+            android:label="@string/subtype_generic"
             android:imeSubtypeLocale="it"
             android:imeSubtypeMode="keyboard"
     />
     <!-- Java uses the deprecated "iw" code instead of the standard "he" code for Hebrew. -->
-    <!-- The file for Hebrew layout is an alpha version. It needs to be run through UX. -->
     <subtype android:icon="@drawable/ic_subtype_keyboard"
-            android:label="@string/subtype_mode_iw_keyboard"
+            android:label="@string/subtype_generic"
             android:imeSubtypeLocale="iw"
             android:imeSubtypeMode="keyboard"
     />
-<!--     <subtype android:icon="@drawable/ic_subtype_mic" -->
-<!--             android:label="@string/subtype_mode_it_voice" -->
-<!--             android:imeSubtypeLocale="it" -->
-<!--             android:imeSubtypeMode="voice" -->
-<!--             android:imeSubtypeExtraValue="excludeFromLastInputMethod,requireNetworkConnectivity" -->
-<!--     /> -->
     <subtype android:icon="@drawable/ic_subtype_keyboard"
-            android:label="@string/subtype_mode_nb_keyboard"
+            android:label="@string/subtype_generic"
             android:imeSubtypeLocale="nb"
             android:imeSubtypeMode="keyboard"
     />
     <subtype android:icon="@drawable/ic_subtype_keyboard"
-            android:label="@string/subtype_mode_nl_keyboard"
+            android:label="@string/subtype_generic"
             android:imeSubtypeLocale="nl"
             android:imeSubtypeMode="keyboard"
     />
-<!--     <subtype android:icon="@drawable/ic_subtype_mic" -->
-<!--             android:label="@string/subtype_mode_nl_voice" -->
-<!--             android:imeSubtypeLocale="nl" -->
-<!--             android:imeSubtypeMode="voice" -->
-<!--             android:imeSubtypeExtraValue="excludeFromLastInputMethod,requireNetworkConnectivity" -->
-<!--     /> -->
     <subtype android:icon="@drawable/ic_subtype_keyboard"
-            android:label="@string/subtype_mode_pl_keyboard"
+            android:label="@string/subtype_generic"
             android:imeSubtypeLocale="pl"
             android:imeSubtypeMode="keyboard"
     />
     <subtype android:icon="@drawable/ic_subtype_keyboard"
-            android:label="@string/subtype_mode_pt_keyboard"
+            android:label="@string/subtype_generic"
             android:imeSubtypeLocale="pt"
             android:imeSubtypeMode="keyboard"
     />
     <subtype android:icon="@drawable/ic_subtype_keyboard"
-            android:label="@string/subtype_mode_ru_keyboard"
+            android:label="@string/subtype_generic"
             android:imeSubtypeLocale="ru"
             android:imeSubtypeMode="keyboard"
     />
     <subtype android:icon="@drawable/ic_subtype_keyboard"
-            android:label="@string/subtype_mode_sr_keyboard"
+            android:label="@string/subtype_generic"
             android:imeSubtypeLocale="sr"
             android:imeSubtypeMode="keyboard"
     />
     <subtype android:icon="@drawable/ic_subtype_keyboard"
-            android:label="@string/subtype_mode_sv_keyboard"
+            android:label="@string/subtype_generic"
             android:imeSubtypeLocale="sv"
             android:imeSubtypeMode="keyboard"
     />
     <subtype android:icon="@drawable/ic_subtype_keyboard"
-            android:label="@string/subtype_mode_tr_keyboard"
+            android:label="@string/subtype_generic"
             android:imeSubtypeLocale="tr"
             android:imeSubtypeMode="keyboard"
     />
-<!--     <subtype android:icon="@drawable/ic_subtype_mic" -->
-<!--             android:label="@string/subtype_mode_af_voice" -->
-<!--             android:imeSubtypeLocale="af" -->
-<!--             android:imeSubtypeMode="voice" -->
-<!--             android:imeSubtypeExtraValue="excludeFromLastInputMethod,requireNetworkConnectivity" -->
-<!--     /> -->
-<!--     <subtype android:icon="@drawable/ic_subtype_mic" -->
-<!--             android:label="@string/subtype_mode_ja_voice" -->
-<!--             android:imeSubtypeLocale="ja" -->
-<!--             android:imeSubtypeMode="voice" -->
-<!--             android:imeSubtypeExtraValue="excludeFromLastInputMethod,requireNetworkConnectivity" -->
-<!--     /> -->
-<!--     <subtype android:icon="@drawable/ic_subtype_mic" -->
-<!--             android:label="@string/subtype_mode_ko_voice" -->
-<!--             android:imeSubtypeLocale="ko" -->
-<!--             android:imeSubtypeMode="voice" -->
-<!--             android:imeSubtypeExtraValue="excludeFromLastInputMethod,requireNetworkConnectivity" -->
-<!--     /> -->
-<!--     <subtype android:icon="@drawable/ic_subtype_mic" -->
-<!--             android:label="@string/subtype_mode_pl_voice" -->
-<!--             android:imeSubtypeLocale="pl" -->
-<!--             android:imeSubtypeMode="voice" -->
-<!--             android:imeSubtypeExtraValue="excludeFromLastInputMethod,requireNetworkConnectivity" -->
-<!--     /> -->
-<!--     <subtype android:icon="@drawable/ic_subtype_mic" -->
-<!--             android:label="@string/subtype_mode_pt_voice" -->
-<!--             android:imeSubtypeLocale="pt" -->
-<!--             android:imeSubtypeMode="voice" -->
-<!--             android:imeSubtypeExtraValue="excludeFromLastInputMethod,requireNetworkConnectivity" -->
-<!--     /> -->
-<!--     <subtype android:icon="@drawable/ic_subtype_mic" -->
-<!--             android:label="@string/subtype_mode_ru_voice" -->
-<!--             android:imeSubtypeLocale="ru" -->
-<!--             android:imeSubtypeMode="voice" -->
-<!--             android:imeSubtypeExtraValue="excludeFromLastInputMethod,requireNetworkConnectivity" -->
-<!--     /> -->
-<!--     <subtype android:icon="@drawable/ic_subtype_mic" -->
-<!--             android:label="@string/subtype_mode_tr_voice" -->
-<!--             android:imeSubtypeLocale="tr" -->
-<!--             android:imeSubtypeMode="voice" -->
-<!--             android:imeSubtypeExtraValue="excludeFromLastInputMethod,requireNetworkConnectivity" -->
-<!--     /> -->
-<!--     <subtype android:icon="@drawable/ic_subtype_mic" -->
-<!--             android:label="@string/subtype_mode_yue_voice" -->
-<!--             android:imeSubtypeLocale="yue" -->
-<!--             android:imeSubtypeMode="voice" -->
-<!--             android:imeSubtypeExtraValue="excludeFromLastInputMethod,requireNetworkConnectivity" -->
-<!--     /> -->
-<!--     <subtype android:icon="@drawable/ic_subtype_mic" -->
-<!--             android:label="@string/subtype_mode_zh_voice" -->
-<!--             android:imeSubtypeLocale="zh" -->
-<!--             android:imeSubtypeMode="voice" -->
-<!--             android:imeSubtypeExtraValue="excludeFromLastInputMethod,requireNetworkConnectivity" -->
-<!--     /> -->
-<!--     <subtype android:icon="@drawable/ic_subtype_mic" -->
-<!--             android:label="@string/subtype_mode_zu_voice" -->
-<!--             android:imeSubtypeLocale="zu" -->
-<!--             android:imeSubtypeMode="voice" -->
-<!--             android:imeSubtypeExtraValue="excludeFromLastInputMethod,requireNetworkConnectivity" -->
-<!--     /> -->
 </input-method>
diff --git a/java/src/com/android/inputmethod/deprecated/VoiceProxy.java b/java/src/com/android/inputmethod/deprecated/VoiceProxy.java
index b8a8169..9397483 100644
--- a/java/src/com/android/inputmethod/deprecated/VoiceProxy.java
+++ b/java/src/com/android/inputmethod/deprecated/VoiceProxy.java
@@ -158,7 +158,10 @@
     }
 
     public void flushVoiceInputLogs(boolean configurationChanged) {
-        if (VOICE_INSTALLED && !configurationChanged) {
+        if (!VOICE_INSTALLED) {
+            return;
+        }
+        if (!configurationChanged) {
             if (mAfterVoiceInput) {
                 mVoiceInput.flushAllTextModificationCounters();
                 mVoiceInput.logInputEnded();
@@ -301,6 +304,9 @@
     }
 
     public void showPunctuationHintIfNecessary() {
+        if (!VOICE_INSTALLED) {
+            return;
+        }
         InputConnection ic = mService.getCurrentInputConnection();
         if (!mImmediatelyAfterVoiceInput && mAfterVoiceInput && ic != null) {
             if (mHints.showPunctuationHintIfNecessary(ic)) {
@@ -375,9 +381,6 @@
     }
 
     private void revertVoiceInput() {
-        if (!VOICE_INSTALLED) {
-            return;
-        }
         InputConnection ic = mService.getCurrentInputConnection();
         if (ic != null) ic.commitText("", 1);
         mService.updateSuggestions();
@@ -394,7 +397,10 @@
     }
 
     public boolean logAndRevertVoiceInput() {
-        if (VOICE_INSTALLED && mVoiceInputHighlighted) {
+        if (!VOICE_INSTALLED) {
+            return false;
+        }
+        if (mVoiceInputHighlighted) {
             mVoiceInput.incrementTextModificationDeleteCount(
                     mVoiceResults.candidates.get(0).toString().length());
             revertVoiceInput();
@@ -505,7 +511,10 @@
     }
 
     public void handleClose() {
-        if (VOICE_INSTALLED & mRecognizing) {
+        if (!VOICE_INSTALLED) {
+            return;
+        }
+        if (mRecognizing) {
             mVoiceInput.cancel();
         }
     }
@@ -553,6 +562,9 @@
     }
 
     public void switchToRecognitionStatusView(final Configuration configuration) {
+        if (!VOICE_INSTALLED) {
+            return;
+        }
         mHandler.post(new Runnable() {
             @Override
             public void run() {
@@ -597,6 +609,9 @@
     }
 
     private void switchToLastInputMethod() {
+        if (!VOICE_INSTALLED) {
+            return;
+        }
         final IBinder token = mService.getWindow().getWindow().getAttributes().token;
         new AsyncTask<Void, Void, Boolean>() {
             @Override
@@ -662,14 +677,15 @@
     }
 
     public void startListening(final boolean swipe, IBinder token) {
+        if (!VOICE_INSTALLED) {
+            return;
+        }
         // TODO: remove swipe which is no longer used.
-        if (VOICE_INSTALLED) {
-            if (needsToShowWarningDialog()) {
-                // Calls reallyStartListening if user clicks OK, does nothing if user clicks Cancel.
-                showVoiceWarningDialog(swipe, token);
-            } else {
-                reallyStartListening(swipe);
-            }
+        if (needsToShowWarningDialog()) {
+            // Calls reallyStartListening if user clicks OK, does nothing if user clicks Cancel.
+            showVoiceWarningDialog(swipe, token);
+        } else {
+            reallyStartListening(swipe);
         }
     }
 
@@ -704,17 +720,18 @@
         mLocaleSupportedForVoiceInput = SubtypeSwitcher.getInstance().isVoiceSupported(
                 SubtypeSwitcher.getInstance().getInputLocaleStr());
 
-        if (VOICE_INSTALLED) {
-            final String voiceMode = sp.getString(PREF_VOICE_MODE,
-                    mService.getString(R.string.voice_mode_main));
-            mVoiceButtonEnabled = !voiceMode.equals(mService.getString(R.string.voice_mode_off))
-                    && shouldShowVoiceButton(makeFieldContext(), attribute);
-            mVoiceButtonOnPrimary = voiceMode.equals(mService.getString(R.string.voice_mode_main));
-        }
+        final String voiceMode = sp.getString(PREF_VOICE_MODE,
+                mService.getString(R.string.voice_mode_main));
+        mVoiceButtonEnabled = !voiceMode.equals(mService.getString(R.string.voice_mode_off))
+                && shouldShowVoiceButton(makeFieldContext(), attribute);
+        mVoiceButtonOnPrimary = voiceMode.equals(mService.getString(R.string.voice_mode_main));
     }
 
     public void destroy() {
-        if (VOICE_INSTALLED && mVoiceInput != null) {
+        if (!VOICE_INSTALLED) {
+            return;
+        }
+        if (mVoiceInput != null) {
             mVoiceInput.destroy();
         }
     }
@@ -826,10 +843,16 @@
         }
 
         public void cancel() {
+            if (!VOICE_INSTALLED) {
+                return;
+            }
             if (mVoiceInput != null) mVoiceInput.cancel();
         }
 
         public void reset() {
+            if (!VOICE_INSTALLED) {
+                return;
+            }
             if (mVoiceInput != null) mVoiceInput.reset();
         }
     }
diff --git a/java/src/com/android/inputmethod/keyboard/Key.java b/java/src/com/android/inputmethod/keyboard/Key.java
index 33ab511..8b8930a 100644
--- a/java/src/com/android/inputmethod/keyboard/Key.java
+++ b/java/src/com/android/inputmethod/keyboard/Key.java
@@ -34,6 +34,8 @@
 import com.android.inputmethod.latin.R;
 
 import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.Map;
 
 /**
  * Class for describing the position and characteristics of a single key in the keyboard.
@@ -153,6 +155,38 @@
             android.R.attr.state_pressed
     };
 
+    // RTL parenthesis character swapping map.
+    private static final Map<Integer, Integer> sRtlParenthesisMap = new HashMap<Integer, Integer>();
+
+    static {
+        addRtlParenthesisPair('(', ')');
+        addRtlParenthesisPair('[', ']');
+        addRtlParenthesisPair('{', '}');
+        addRtlParenthesisPair('<', '>');
+        // \u00ab: LEFT-POINTING DOUBLE ANGLE QUOTATION MARK
+        // \u00bb: RIGHT-POINTING DOUBLE ANGLE QUOTATION MARK
+        addRtlParenthesisPair('\u00ab', '\u00bb');
+        // \u2039: SINGLE LEFT-POINTING ANGLE QUOTATION MARK
+        // \u203a: SINGLE RIGHT-POINTING ANGLE QUOTATION MARK
+        addRtlParenthesisPair('\u2039', '\u203a');
+        // \u2264: LESS-THAN OR EQUAL TO
+        // \u2265: GREATER-THAN OR EQUAL TO
+        addRtlParenthesisPair('\u2264', '\u2265');
+    }
+
+    private static void addRtlParenthesisPair(int left, int right) {
+        sRtlParenthesisMap.put(left, right);
+        sRtlParenthesisMap.put(right, left);
+    }
+
+    private static int getRtlParenthesisCode(int code) {
+        if (sRtlParenthesisMap.containsKey(code)) {
+            return sRtlParenthesisMap.get(code);
+        } else {
+            return code;
+        }
+    }
+
     /**
      * This constructor is being used only for key in popup mini keyboard.
      */
@@ -174,7 +208,8 @@
         final String popupSpecification = popupCharacter.toString();
         mLabel = PopupCharactersParser.getLabel(popupSpecification);
         mOutputText = PopupCharactersParser.getOutputText(popupSpecification);
-        mCode = PopupCharactersParser.getCode(res, popupSpecification);
+        final int code = PopupCharactersParser.getCode(res, popupSpecification);
+        mCode = keyboard.isRtlKeyboard() ? getRtlParenthesisCode(code) : code;
         mIcon = keyboard.mIconsSet.getIcon(PopupCharactersParser.getIconId(popupSpecification));
         // Horizontal gap is divided equally to both sides of the key.
         mX = x + mGap / 2;
@@ -298,7 +333,8 @@
             final int code = style.getInt(keyAttr, R.styleable.Keyboard_Key_code,
                     Keyboard.CODE_UNSPECIFIED);
             if (code == Keyboard.CODE_UNSPECIFIED && !TextUtils.isEmpty(mLabel)) {
-                mCode = mLabel.charAt(0);
+                final int firstChar = mLabel.charAt(0);
+                mCode = mKeyboard.isRtlKeyboard() ? getRtlParenthesisCode(firstChar) : firstChar;
             } else if (code != Keyboard.CODE_UNSPECIFIED) {
                 mCode = code;
             } else {
diff --git a/java/src/com/android/inputmethod/keyboard/Keyboard.java b/java/src/com/android/inputmethod/keyboard/Keyboard.java
index d4a23aa..19847c5 100644
--- a/java/src/com/android/inputmethod/keyboard/Keyboard.java
+++ b/java/src/com/android/inputmethod/keyboard/Keyboard.java
@@ -110,6 +110,9 @@
     /** Maximum column for popup keyboard */
     private int mMaxPopupColumn;
 
+    /** True if Right-To-Left keyboard */
+    private boolean mIsRtlKeyboard;
+
     /** List of shift keys in this keyboard and its icons and state */
     private final List<Key> mShiftKeys = new ArrayList<Key>();
     private final HashMap<Key, Drawable> mShiftedIcons = new HashMap<Key, Drawable>();
@@ -257,6 +260,14 @@
         mKeyboardHeight = height;
     }
 
+    public boolean isRtlKeyboard() {
+        return mIsRtlKeyboard;
+    }
+
+    public void setRtlKeyboard(boolean isRtl) {
+        mIsRtlKeyboard = isRtl;
+    }
+
     public int getPopupKeyboardResId() {
         return mPopupKeyboardResId;
     }
diff --git a/java/src/com/android/inputmethod/keyboard/KeyboardId.java b/java/src/com/android/inputmethod/keyboard/KeyboardId.java
index b2600dd..a71e31e 100644
--- a/java/src/com/android/inputmethod/keyboard/KeyboardId.java
+++ b/java/src/com/android/inputmethod/keyboard/KeyboardId.java
@@ -125,10 +125,10 @@
                 mEnableShiftLock);
     }
 
-    public KeyboardId cloneWithNewGeometry(int width) {
+    public KeyboardId cloneWithNewGeometry(int orientation, int width) {
         if (mWidth == width)
             return this;
-        return new KeyboardId(mXmlName, mXmlId, mLocale, mOrientation, width, mMode, mAttribute,
+        return new KeyboardId(mXmlName, mXmlId, mLocale, orientation, width, mMode, mAttribute,
                 mHasSettingsKey, mF2KeyMode, mClobberSettingsKey, mVoiceKeyEnabled, mHasVoiceKey,
                 mEnableShiftLock);
     }
diff --git a/java/src/com/android/inputmethod/keyboard/KeyboardSwitcher.java b/java/src/com/android/inputmethod/keyboard/KeyboardSwitcher.java
index 52ecfe2..088c9d1 100644
--- a/java/src/com/android/inputmethod/keyboard/KeyboardSwitcher.java
+++ b/java/src/com/android/inputmethod/keyboard/KeyboardSwitcher.java
@@ -106,7 +106,7 @@
 
     private int mThemeIndex = -1;
     private Context mThemeContext;
-    private int mKeyboardWidth;
+    private int mWindowWidth;
 
     private static final KeyboardSwitcher sInstance = new KeyboardSwitcher();
 
@@ -187,13 +187,24 @@
         setKeyboard(getKeyboard(id));
     }
 
-    public void onSizeChanged() {
+    @SuppressWarnings("unused")
+    public void onSizeChanged(int w, int h, int oldw, int oldh) {
         final int width = mInputMethodService.getWindow().getWindow().getDecorView().getWidth();
+        // If the window width hasn't fixed yet or keyboard doesn't exist, nothing to do with.
         if (width == 0 || mCurrentId == null)
             return;
-        mKeyboardWidth = width;
-        // Set keyboard with new width.
-        final KeyboardId newId = mCurrentId.cloneWithNewGeometry(width);
+        // The window width is fixed.
+        mWindowWidth = width;
+        // If this is the first time the {@link KeyboardView} has been shown, no need to reload
+        // keyboard.
+        if (oldw == 0 && oldh == 0)
+            return;
+        // Reload keyboard with new width.
+        final int orientation = mInputMethodService.getResources().getConfiguration().orientation;
+        final KeyboardId newId = mCurrentId.cloneWithNewGeometry(orientation, width);
+        // If the new keyboard is the same as the current one, no need to reload it.
+        if (newId.equals(mCurrentId))
+            return;
         setKeyboard(getKeyboard(newId));
     }
 
@@ -289,11 +300,11 @@
                 attribute);
         final Resources res = mInputMethodService.getResources();
         final int orientation = res.getConfiguration().orientation;
-        if (mKeyboardWidth == 0)
-            mKeyboardWidth = res.getDisplayMetrics().widthPixels;
+        if (mWindowWidth == 0)
+            mWindowWidth = res.getDisplayMetrics().widthPixels;
         final Locale locale = mSubtypeSwitcher.getInputLocale();
         return new KeyboardId(
-                res.getResourceEntryName(xmlId), xmlId, locale, orientation, mKeyboardWidth,
+                res.getResourceEntryName(xmlId), xmlId, locale, orientation, mWindowWidth,
                 mode, attribute, hasSettingsKey, f2KeyMode, clobberSettingsKey, mVoiceKeyEnabled,
                 hasVoiceKey, enableShiftLock);
     }
diff --git a/java/src/com/android/inputmethod/keyboard/KeyboardView.java b/java/src/com/android/inputmethod/keyboard/KeyboardView.java
index 1641dc4..1010adb 100644
--- a/java/src/com/android/inputmethod/keyboard/KeyboardView.java
+++ b/java/src/com/android/inputmethod/keyboard/KeyboardView.java
@@ -73,8 +73,6 @@
  * @attr ref R.styleable#KeyboardView_shadowRadius
  */
 public class KeyboardView extends View implements PointerTracker.DrawingProxy {
-    private static final boolean DEBUG_KEYBOARD_GRID = false;
-
     // Miscellaneous constants
     private static final int[] LONG_PRESSABLE_STATE_SET = { android.R.attr.state_long_pressable };
 
@@ -97,17 +95,15 @@
     private ViewGroup mPreviewPlacer;
 
     // Drawing
-    /** Whether the keyboard bitmap needs to be redrawn before it's blitted. **/
-    private boolean mDrawPending;
-    /** Notes if the keyboard just changed, so that we could possibly reallocate the mBuffer. */
-    private boolean mKeyboardChanged;
+    /** Whether the keyboard bitmap buffer needs to be redrawn before it's blitted. **/
+    private boolean mBufferNeedsUpdate;
     /** The dirty region in the keyboard bitmap */
     private final Rect mDirtyRect = new Rect();
     /** The key to invalidate. */
     private Key mInvalidatedKey;
     /** The dirty region for single key drawing */
     private final Rect mInvalidatedKeyRect = new Rect();
-    /** The keyboard bitmap for faster updates */
+    /** The keyboard bitmap buffer for faster updates */
     private Bitmap mBuffer;
     /** The canvas for the above mutable keyboard bitmap */
     private Canvas mCanvas;
@@ -175,10 +171,6 @@
             sendMessageDelayed(obtainMessage(MSG_DISMISS_KEY_PREVIEW, tracker), delay);
         }
 
-        public void cancelDismissKeyPreview(PointerTracker tracker) {
-            removeMessages(MSG_DISMISS_KEY_PREVIEW, tracker);
-        }
-
         public void cancelAllDismissKeyPreviews() {
             removeMessages(MSG_DISMISS_KEY_PREVIEW);
         }
@@ -363,7 +355,8 @@
         mKeyboard = keyboard;
         LatinImeLogger.onSetKeyboard(keyboard);
         requestLayout();
-        mKeyboardChanged = true;
+        mDirtyRect.set(0, 0, getWidth(), getHeight());
+        mBufferNeedsUpdate = true;
         invalidateAllKeys();
         final int keyHeight = keyboard.getRowHeight() - keyboard.getVerticalGap();
         mKeyDrawParams.updateKeyHeight(keyHeight);
@@ -401,25 +394,21 @@
     }
 
     @Override
-    public void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
-        // Round up a little
-        if (mKeyboard == null) {
-            setMeasuredDimension(
-                    getPaddingLeft() + getPaddingRight(), getPaddingTop() + getPaddingBottom());
+    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+        if (mKeyboard != null) {
+            // The main keyboard expands to the display width.
+            final int height = mKeyboard.getKeyboardHeight() + getPaddingTop() + getPaddingBottom();
+            setMeasuredDimension(widthMeasureSpec, height);
         } else {
-            int width = mKeyboard.getMinWidth() + getPaddingLeft() + getPaddingRight();
-            if (MeasureSpec.getSize(widthMeasureSpec) < width + 10) {
-                width = MeasureSpec.getSize(widthMeasureSpec);
-            }
-            setMeasuredDimension(
-                    width, mKeyboard.getHeight() + getPaddingTop() + getPaddingBottom());
+            super.onMeasure(widthMeasureSpec, heightMeasureSpec);
         }
     }
 
     @Override
     public void onDraw(Canvas canvas) {
         super.onDraw(canvas);
-        if (mDrawPending || mBuffer == null || mKeyboardChanged) {
+        if (mBufferNeedsUpdate || mBuffer == null) {
+            mBufferNeedsUpdate = false;
             onBufferDraw();
         }
         canvas.drawBitmap(mBuffer, 0, 0, null);
@@ -430,14 +419,11 @@
         final int height = getHeight();
         if (width == 0 || height == 0)
             return;
-        if (mBuffer == null || mKeyboardChanged) {
-            mKeyboardChanged = false;
-            mDirtyRect.union(0, 0, width, height);
-        }
         if (mBuffer == null || mBuffer.getWidth() != width || mBuffer.getHeight() != height) {
             if (mBuffer != null)
                 mBuffer.recycle();
             mBuffer = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
+            mDirtyRect.union(0, 0, width, height);
             if (mCanvas != null) {
                 mCanvas.setBitmap(mBuffer);
             } else {
@@ -471,24 +457,6 @@
             }
         }
 
-        // TODO: Move this function to ProximityInfo for getting rid of
-        // public declarations for
-        // GRID_WIDTH and GRID_HEIGHT
-        if (DEBUG_KEYBOARD_GRID) {
-            Paint p = new Paint();
-            p.setStyle(Paint.Style.STROKE);
-            p.setStrokeWidth(1.0f);
-            p.setColor(0x800000c0);
-            int cw = (mKeyboard.getMinWidth() + mKeyboard.GRID_WIDTH - 1)
-                    / mKeyboard.GRID_WIDTH;
-            int ch = (mKeyboard.getHeight() + mKeyboard.GRID_HEIGHT - 1)
-                    / mKeyboard.GRID_HEIGHT;
-            for (int i = 0; i <= mKeyboard.GRID_WIDTH; i++)
-                canvas.drawLine(i * cw, 0, i * cw, ch * mKeyboard.GRID_HEIGHT, p);
-            for (int i = 0; i <= mKeyboard.GRID_HEIGHT; i++)
-                canvas.drawLine(0, i * ch, cw * mKeyboard.GRID_WIDTH, i * ch, p);
-        }
-
         // Overlay a dark rectangle to dim the keyboard
         if (needsToDimKeyboard()) {
             mPaint.setColor((int) (mBackgroundDimAmount * 0xFF) << 24);
@@ -496,7 +464,6 @@
         }
 
         mInvalidatedKey = null;
-        mDrawPending = false;
         mDirtyRect.setEmpty();
     }
 
@@ -866,7 +833,7 @@
      */
     public void invalidateAllKeys() {
         mDirtyRect.union(0, 0, getWidth(), getHeight());
-        mDrawPending = true;
+        mBufferNeedsUpdate = true;
         invalidate();
     }
 
@@ -886,7 +853,7 @@
         final int y = key.mY + getPaddingTop();
         mInvalidatedKeyRect.set(x, y, x + key.mWidth, y + key.mHeight);
         mDirtyRect.union(mInvalidatedKeyRect);
-        onBufferDraw();
+        mBufferNeedsUpdate = true;
         invalidate(mInvalidatedKeyRect);
     }
 
diff --git a/java/src/com/android/inputmethod/keyboard/LatinKeyboardBaseView.java b/java/src/com/android/inputmethod/keyboard/LatinKeyboardBaseView.java
index fb57a2d..0ad91db 100644
--- a/java/src/com/android/inputmethod/keyboard/LatinKeyboardBaseView.java
+++ b/java/src/com/android/inputmethod/keyboard/LatinKeyboardBaseView.java
@@ -269,7 +269,7 @@
     @Override
     protected void onSizeChanged(int w, int h, int oldw, int oldh) {
         // TODO: Should notify InputMethodService instead?
-        KeyboardSwitcher.getInstance().onSizeChanged();
+        KeyboardSwitcher.getInstance().onSizeChanged(w, h, oldw, oldh);
     }
 
     /**
diff --git a/java/src/com/android/inputmethod/keyboard/MiniKeyboard.java b/java/src/com/android/inputmethod/keyboard/MiniKeyboard.java
index 44f9f19..95e3275 100644
--- a/java/src/com/android/inputmethod/keyboard/MiniKeyboard.java
+++ b/java/src/com/android/inputmethod/keyboard/MiniKeyboard.java
@@ -34,6 +34,8 @@
         // revert the above hacks and uncomment the following lines.
         //setHorizontalGap(parentKeyboard.getHorizontalGap());
         //setVerticalGap(parentKeyboard.getVerticalGap());
+
+        setRtlKeyboard(parentKeyboard.isRtlKeyboard());
     }
 
     public void setDefaultCoordX(int pos) {
diff --git a/java/src/com/android/inputmethod/keyboard/PointerTracker.java b/java/src/com/android/inputmethod/keyboard/PointerTracker.java
index 5b03ef4..e66ea7b 100644
--- a/java/src/com/android/inputmethod/keyboard/PointerTracker.java
+++ b/java/src/com/android/inputmethod/keyboard/PointerTracker.java
@@ -199,8 +199,7 @@
 
     public static void dismissAllKeyPreviews() {
         for (final PointerTracker tracker : sTrackers) {
-            tracker.setReleasedKeyGraphics();
-            tracker.dismissKeyPreview();
+            tracker.setReleasedKeyGraphics(tracker.mKeyIndex);
         }
     }
 
@@ -323,11 +322,8 @@
         return key != null && key.mCode == Keyboard.CODE_SPACE;
     }
 
-    public void setReleasedKeyGraphics() {
-        setReleasedKeyGraphics(mKeyIndex);
-    }
-
     private void setReleasedKeyGraphics(int keyIndex) {
+        mDrawingProxy.dismissKeyPreview(this);
         final Key key = getKey(keyIndex);
         if (key != null) {
             key.onReleased();
@@ -336,6 +332,9 @@
     }
 
     private void setPressedKeyGraphics(int keyIndex) {
+        if (isKeyPreviewRequired(keyIndex)) {
+            mDrawingProxy.showKeyPreview(keyIndex, this);
+        }
         final Key key = getKey(keyIndex);
         if (key != null && key.isEnabled()) {
             key.onPressed();
@@ -343,6 +342,18 @@
         }
     }
 
+    // The modifier key, such as shift key, should not show its key preview.
+    private boolean isKeyPreviewRequired(int keyIndex) {
+        final Key key = getKey(keyIndex);
+        if (key == null || !key.isEnabled())
+            return false;
+        final int code = key.mCode;
+        if (isModifierCode(code) || code == Keyboard.CODE_DELETE
+                || code == Keyboard.CODE_ENTER || code == Keyboard.CODE_SPACE)
+            return false;
+        return true;
+    }
+
     public int getLastX() {
         return mLastX;
     }
@@ -438,7 +449,6 @@
 
             startRepeatKey(keyIndex);
             startLongPressTimer(keyIndex);
-            showKeyPreview(keyIndex);
             setPressedKeyGraphics(keyIndex);
         }
     }
@@ -471,7 +481,6 @@
                     keyIndex = onMoveKey(x, y);
                 onMoveToNewKey(keyIndex, x, y);
                 startLongPressTimer(keyIndex);
-                showKeyPreview(keyIndex);
                 setPressedKeyGraphics(keyIndex);
             } else if (isMajorEnoughMoveToBeOnNewKey(x, y, keyIndex)) {
                 // The pointer has been slid in to the new key from the previous key, we must call
@@ -491,7 +500,6 @@
                     onMoveToNewKey(keyIndex, x, y);
                     startLongPressTimer(keyIndex);
                     setPressedKeyGraphics(keyIndex);
-                    showKeyPreview(keyIndex);
                 } else {
                     // HACK: On some devices, quick successive touches may be translated to sudden
                     // move by touch panel firmware. This hack detects the case and translates the
@@ -503,11 +511,10 @@
                         if (DEBUG_MODE)
                             Log.w(TAG, String.format("onMoveEvent: sudden move is translated to "
                                     + "up[%d,%d]/down[%d,%d] events", lastX, lastY, x, y));
-                        onUpEventInternal(lastX, lastY, eventTime, true);
+                        onUpEventInternal(lastX, lastY, eventTime);
                         onDownEventInternal(x, y, eventTime);
                     } else {
                         mKeyAlreadyProcessed = true;
-                        dismissKeyPreview();
                         setReleasedKeyGraphics(oldKeyIndex);
                     }
                 }
@@ -524,7 +531,6 @@
                     onMoveToNewKey(keyIndex, x, y);
                 } else {
                     mKeyAlreadyProcessed = true;
-                    dismissKeyPreview();
                 }
             }
         }
@@ -539,27 +545,26 @@
             if (isModifier()) {
                 // Before processing an up event of modifier key, all pointers already being
                 // tracked should be released.
-                queue.releaseAllPointersExcept(this, eventTime, true);
+                queue.releaseAllPointersExcept(this, eventTime);
             } else {
                 queue.releaseAllPointersOlderThan(this, eventTime);
             }
             queue.remove(this);
         }
-        onUpEventInternal(x, y, eventTime, true);
+        onUpEventInternal(x, y, eventTime);
     }
 
     // Let this pointer tracker know that one of newer-than-this pointer trackers got an up event.
     // This pointer tracker needs to keep the key top graphics "pressed", but needs to get a
     // "virtual" up event.
-    public void onPhantomUpEvent(int x, int y, long eventTime, boolean updateReleasedKeyGraphics) {
+    public void onPhantomUpEvent(int x, int y, long eventTime) {
         if (DEBUG_EVENT)
             printTouchEvent("onPhntEvent:", x, y, eventTime);
-        onUpEventInternal(x, y, eventTime, updateReleasedKeyGraphics);
+        onUpEventInternal(x, y, eventTime);
         mKeyAlreadyProcessed = true;
     }
 
-    private void onUpEventInternal(int x, int y, long eventTime,
-            boolean updateReleasedKeyGraphics) {
+    private void onUpEventInternal(int x, int y, long eventTime) {
         mTimerProxy.cancelKeyTimers();
         mDrawingProxy.cancelShowKeyPreview(this);
         mIsInSlidingKeyInput = false;
@@ -573,9 +578,7 @@
             keyY = mKeyY;
         }
         final int keyIndex = onUpKey(keyX, keyY, eventTime);
-        dismissKeyPreview();
-        if (updateReleasedKeyGraphics)
-            setReleasedKeyGraphics(keyIndex);
+        setReleasedKeyGraphics(keyIndex);
         if (mKeyAlreadyProcessed)
             return;
         if (!mIsRepeatableKey) {
@@ -585,8 +588,7 @@
 
     public void onLongPressed() {
         mKeyAlreadyProcessed = true;
-        setReleasedKeyGraphics();
-        dismissKeyPreview();
+        setReleasedKeyGraphics(mKeyIndex);
         final PointerTrackerQueue queue = sPointerTrackerQueue;
         if (queue != null) {
             queue.remove(this);
@@ -599,7 +601,7 @@
 
         final PointerTrackerQueue queue = sPointerTrackerQueue;
         if (queue != null) {
-            queue.releaseAllPointersExcept(this, eventTime, true);
+            queue.releaseAllPointersExcept(this, eventTime);
             queue.remove(this);
         }
         onCancelEventInternal();
@@ -608,7 +610,6 @@
     private void onCancelEventInternal() {
         mTimerProxy.cancelKeyTimers();
         mDrawingProxy.cancelShowKeyPreview(this);
-        dismissKeyPreview();
         setReleasedKeyGraphics(mKeyIndex);
         mIsInSlidingKeyInput = false;
     }
@@ -616,7 +617,6 @@
     private void startRepeatKey(int keyIndex) {
         final Key key = getKey(keyIndex);
         if (key != null && key.mRepeatable) {
-            dismissKeyPreview();
             onRepeatKey(keyIndex);
             mTimerProxy.startKeyRepeatTimer(sDelayBeforeKeyRepeatStart, keyIndex, this);
             mIsRepeatableKey = true;
@@ -646,26 +646,6 @@
         }
     }
 
-    // The modifier key, such as shift key, should not show its key preview.
-    private boolean isKeyPreviewNotRequired(int keyIndex) {
-        final Key key = getKey(keyIndex);
-        if (key == null || !key.isEnabled())
-            return true;
-        final int code = key.mCode;
-        return isModifierCode(code) || code == Keyboard.CODE_DELETE
-                || code == Keyboard.CODE_ENTER || code == Keyboard.CODE_SPACE;
-    }
-
-    private void showKeyPreview(int keyIndex) {
-        if (isKeyPreviewNotRequired(keyIndex))
-            return;
-        mDrawingProxy.showKeyPreview(keyIndex, this);
-    }
-
-    private void dismissKeyPreview() {
-        mDrawingProxy.dismissKeyPreview(this);
-    }
-
     private void startLongPressTimer(int keyIndex) {
         Key key = getKey(keyIndex);
         if (key.mCode == Keyboard.CODE_SHIFT) {
diff --git a/java/src/com/android/inputmethod/keyboard/PopupMiniKeyboardView.java b/java/src/com/android/inputmethod/keyboard/PopupMiniKeyboardView.java
index 5ab44d0..a90f57c 100644
--- a/java/src/com/android/inputmethod/keyboard/PopupMiniKeyboardView.java
+++ b/java/src/com/android/inputmethod/keyboard/PopupMiniKeyboardView.java
@@ -108,6 +108,18 @@
     }
 
     @Override
+    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+        final Keyboard keyboard = getKeyboard();
+        if (keyboard != null) {
+            final int width = keyboard.getMinWidth() + getPaddingLeft() + getPaddingRight();
+            final int height = keyboard.getKeyboardHeight() + getPaddingTop() + getPaddingBottom();
+            setMeasuredDimension(width, height);
+        } else {
+            super.onMeasure(widthMeasureSpec, heightMeasureSpec);
+        }
+    }
+
+    @Override
     public void setKeyboard(Keyboard keyboard) {
         super.setKeyboard(keyboard);
         mKeyDetector.setKeyboard(keyboard, -getPaddingLeft(),
diff --git a/java/src/com/android/inputmethod/keyboard/internal/KeyboardParser.java b/java/src/com/android/inputmethod/keyboard/internal/KeyboardParser.java
index 6f73499..8eae2bb 100644
--- a/java/src/com/android/inputmethod/keyboard/internal/KeyboardParser.java
+++ b/java/src/com/android/inputmethod/keyboard/internal/KeyboardParser.java
@@ -222,8 +222,9 @@
             final int height = Math.max(
                     Math.min(keyboardHeight, maxKeyboardHeight), minKeyboardHeight);
 
-
             keyboard.setKeyboardHeight(height);
+            keyboard.setRtlKeyboard(keyboardAttr.getBoolean(
+                    R.styleable.Keyboard_isRtlKeyboard, false));
             keyboard.setKeyWidth(getDimensionOrFraction(keyboardAttr,
                     R.styleable.Keyboard_keyWidth, displayWidth, displayWidth / 10));
             keyboard.setRowHeight(getDimensionOrFraction(keyboardAttr,
diff --git a/java/src/com/android/inputmethod/keyboard/internal/MiniKeyboardBuilder.java b/java/src/com/android/inputmethod/keyboard/internal/MiniKeyboardBuilder.java
index cc89579..965c679 100644
--- a/java/src/com/android/inputmethod/keyboard/internal/MiniKeyboardBuilder.java
+++ b/java/src/com/android/inputmethod/keyboard/internal/MiniKeyboardBuilder.java
@@ -218,7 +218,7 @@
         mParams = params;
 
         keyboard.setRowHeight(params.mRowHeight);
-        keyboard.setHeight(params.getKeyboardHeight());
+        keyboard.setKeyboardHeight(params.getKeyboardHeight());
         keyboard.setMinWidth(params.getKeyboardWidth());
         keyboard.setDefaultCoordX(params.getDefaultKeyCoordX() + params.mKeyWidth / 2);
     }
diff --git a/java/src/com/android/inputmethod/keyboard/internal/PointerTrackerQueue.java b/java/src/com/android/inputmethod/keyboard/internal/PointerTrackerQueue.java
index 545b27f..55175e0 100644
--- a/java/src/com/android/inputmethod/keyboard/internal/PointerTrackerQueue.java
+++ b/java/src/com/android/inputmethod/keyboard/internal/PointerTrackerQueue.java
@@ -37,22 +37,21 @@
             if (t.isModifier()) {
                 oldestPos++;
             } else {
-                t.onPhantomUpEvent(t.getLastX(), t.getLastY(), eventTime, true);
+                t.onPhantomUpEvent(t.getLastX(), t.getLastY(), eventTime);
                 queue.remove(oldestPos);
             }
         }
     }
 
     public void releaseAllPointers(long eventTime) {
-        releaseAllPointersExcept(null, eventTime, true);
+        releaseAllPointersExcept(null, eventTime);
     }
 
-    public void releaseAllPointersExcept(PointerTracker tracker, long eventTime,
-            boolean updateReleasedKeyGraphics) {
+    public void releaseAllPointersExcept(PointerTracker tracker, long eventTime) {
         for (PointerTracker t : mQueue) {
             if (t == tracker)
                 continue;
-            t.onPhantomUpEvent(t.getLastX(), t.getLastY(), eventTime, updateReleasedKeyGraphics);
+            t.onPhantomUpEvent(t.getLastX(), t.getLastY(), eventTime);
         }
         mQueue.clear();
         if (tracker != null)
diff --git a/java/src/com/android/inputmethod/latin/LatinIME.java b/java/src/com/android/inputmethod/latin/LatinIME.java
index 324a130..3457ac9 100644
--- a/java/src/com/android/inputmethod/latin/LatinIME.java
+++ b/java/src/com/android/inputmethod/latin/LatinIME.java
@@ -174,7 +174,6 @@
 
     private int mCorrectionMode;
     private int mCommittedLength;
-    private int mOrientation;
     // Keep track of the last selection range to decide if we need to show word alternatives
     private int mLastSelectionStart;
     private int mLastSelectionEnd;
@@ -193,6 +192,11 @@
     // TODO: Move this flag to VoiceProxy
     private boolean mConfigurationChanging;
 
+    // Member variables for remembering the current device orientation.
+    private int mDisplayOrientation;
+    private int mDisplayWidth;
+    private int mDisplayHeight;
+
     // Object for reacting to adding/removing a dictionary pack.
     private BroadcastReceiver mDictionaryPackInstallReceiver =
             new DictionaryPackInstallBroadcastReceiver(this);
@@ -200,7 +204,6 @@
     // Keeps track of most recently inserted text (multi-character key) for reverting
     private CharSequence mEnteredText;
 
-
     public final UIHandler mHandler = new UIHandler(this);
 
     public static class UIHandler extends StaticInnerHandlerWrapper<LatinIME> {
@@ -212,6 +215,29 @@
         private static final int MSG_DISMISS_LANGUAGE_ON_SPACEBAR = 5;
         private static final int MSG_SPACE_TYPED = 6;
         private static final int MSG_SET_BIGRAM_PREDICTIONS = 7;
+        private static final int MSG_CONFIRM_ORIENTATION_CHANGE = 8;
+        private static final int MSG_START_INPUT_VIEW = 9;
+
+        private static class OrientationChangeArgs {
+            public final int mOldWidth;
+            public final int mOldHeight;
+            private int mRetryCount;
+
+            public OrientationChangeArgs(int oldw, int oldh) {
+                mOldWidth = oldw;
+                mOldHeight = oldh;
+                mRetryCount = 0;
+            }
+
+            public boolean hasTimedOut() {
+                mRetryCount++;
+                return mRetryCount >= 10;
+            }
+
+            public boolean hasOrientationChangeFinished(DisplayMetrics dm) {
+                return dm.widthPixels != mOldWidth && dm.heightPixels != mOldHeight;
+            }
+        }
 
         public UIHandler(LatinIME outerInstance) {
             super(outerInstance);
@@ -260,6 +286,21 @@
                             (LatinKeyboard)msg.obj);
                 }
                 break;
+            case MSG_CONFIRM_ORIENTATION_CHANGE: {
+                final OrientationChangeArgs args = (OrientationChangeArgs)msg.obj;
+                final Resources res = latinIme.mResources;
+                final DisplayMetrics dm = res.getDisplayMetrics();
+                if (args.hasTimedOut() || args.hasOrientationChangeFinished(dm)) {
+                    latinIme.setDisplayGeometry(res.getConfiguration(), dm);
+                } else {
+                    // It seems orientation changing is on going.
+                    postConfirmOrientationChange(args);
+                }
+                break;
+            }
+            case MSG_START_INPUT_VIEW:
+                latinIme.onStartInputView((EditorInfo)msg.obj, false);
+                break;
             }
         }
 
@@ -349,6 +390,33 @@
         public boolean isAcceptingDoubleSpaces() {
             return hasMessages(MSG_SPACE_TYPED);
         }
+
+        private void postConfirmOrientationChange(OrientationChangeArgs args) {
+            removeMessages(MSG_CONFIRM_ORIENTATION_CHANGE);
+            // Will confirm whether orientation change has finished or not after 2ms again.
+            sendMessageDelayed(obtainMessage(MSG_CONFIRM_ORIENTATION_CHANGE, args), 2);
+        }
+
+        public void startOrientationChanging(int oldw, int oldh) {
+            postConfirmOrientationChange(new OrientationChangeArgs(oldw, oldh));
+        }
+
+        public boolean postStartInputView(EditorInfo attribute) {
+            if (hasMessages(MSG_CONFIRM_ORIENTATION_CHANGE) || hasMessages(MSG_START_INPUT_VIEW)) {
+                removeMessages(MSG_START_INPUT_VIEW);
+                // Postpone onStartInputView 20ms afterward and see if orientation change has
+                // finished.
+                sendMessageDelayed(obtainMessage(MSG_START_INPUT_VIEW, attribute), 20);
+                return true;
+            }
+            return false;
+        }
+    }
+
+    private void setDisplayGeometry(Configuration conf, DisplayMetrics metric) {
+        mDisplayOrientation = conf.orientation;
+        mDisplayWidth = metric.widthPixels;
+        mDisplayHeight = metric.heightPixels;
     }
 
     @Override
@@ -388,7 +456,7 @@
             }
         }
 
-        mOrientation = res.getConfiguration().orientation;
+        setDisplayGeometry(res.getConfiguration(), res.getDisplayMetrics());
 
         // Register to receive ringer mode change and network state change.
         // Also receive installation and removal of a dictionary pack.
@@ -486,11 +554,11 @@
     public void onConfigurationChanged(Configuration conf) {
         mSubtypeSwitcher.onConfigurationChanged(conf);
         // If orientation changed while predicting, commit the change
-        if (conf.orientation != mOrientation) {
+        if (conf.orientation != mDisplayOrientation) {
+            mHandler.startOrientationChanging(mDisplayWidth, mDisplayHeight);
             InputConnection ic = getCurrentInputConnection();
             commitTyped(ic);
             if (ic != null) ic.finishComposingText(); // For voice input
-            mOrientation = conf.orientation;
             if (isShowingOptionDialog())
                 mOptionsDialog.dismiss();
         }
@@ -527,6 +595,10 @@
 
     @Override
     public void onStartInputView(EditorInfo attribute, boolean restarting) {
+        if (mHandler.postStartInputView(attribute)) {
+            return;
+        }
+
         final KeyboardSwitcher switcher = mKeyboardSwitcher;
         LatinKeyboardView inputView = switcher.getKeyboardView();
 
@@ -1446,7 +1518,7 @@
     public boolean isShowingSuggestionsStrip() {
         return (mSuggestionVisibility == SUGGESTION_VISIBILILTY_SHOW_VALUE)
                 || (mSuggestionVisibility == SUGGESTION_VISIBILILTY_SHOW_ONLY_PORTRAIT_VALUE
-                        && mOrientation == Configuration.ORIENTATION_PORTRAIT);
+                        && mDisplayOrientation == Configuration.ORIENTATION_PORTRAIT);
     }
 
     public boolean isCandidateStripVisible() {