From 9b6d1d52d91f8f18952ae3841f4bb0d7309bfc0e Mon Sep 17 00:00:00 2001
From: "Tadashi G. Takaoka" <takaoka@google.com>
Date: Tue, 14 Dec 2010 15:31:47 +0900
Subject: [PATCH] Add popupKeyboardTemplate attribute to Keyboard

This change
- introduces the popupKeyboardTemplate attribute of Keyboard to
  specify XML Keyboard file for popup mini keyboard.
- introduces the maxPopupKeyboardColumn attribute of Keyboard to
  specify the maximum column of popup mini keyboard.
- changes the content format of the popupCharacters attribute of Key.
  It now represents keyLabel, codes and keyTextOutput of each key of
  popup mini keyboard using CSV format.

Bug: 2214959
Change-Id: I539e310f7e38a049ee193de0b4ad5d7afdce37b1
---
 .../res/values-cs/donottranslate-altchars.xml |  21 +-
 .../res/values-da/donottranslate-altchars.xml |  28 +--
 .../res/values-de/donottranslate-altchars.xml |  10 +-
 .../res/values-el/donottranslate-altchars.xml |  30 ---
 .../res/values-en/donottranslate-altchars.xml |  11 +-
 .../values-es-rUS/donottranslate-altchars.xml |  30 ---
 .../res/values-es/donottranslate-altchars.xml |  10 +-
 .../res/values-fr/donottranslate-altchars.xml |  10 +-
 .../res/values-it/donottranslate-altchars.xml |  11 +-
 .../res/values-ja/donottranslate-altchars.xml |  30 ---
 .../res/values-ko/donottranslate-altchars.xml |  30 ---
 .../res/values-nb/donottranslate-altchars.xml |  25 +--
 .../res/values-nl/donottranslate-altchars.xml |  30 ---
 .../res/values-pl/donottranslate-altchars.xml |   9 +-
 .../values-pt-rPT/donottranslate-altchars.xml |  30 ---
 .../res/values-pt/donottranslate-altchars.xml |  30 ---
 .../res/values-rm/donottranslate-altchars.xml |  10 +-
 .../res/values-ru/donottranslate-altchars.xml |   9 -
 .../res/values-sv/donottranslate-altchars.xml |  28 +--
 .../res/values-tr/donottranslate-altchars.xml |  14 +-
 java/res/values-xlarge/bools.xml              |  27 ---
 java/res/values-xlarge/config.xml             |   7 +
 .../values-zh-rCN/donottranslate-altchars.xml |  30 ---
 .../values-zh-rTW/donottranslate-altchars.xml |  30 ---
 java/res/values/attrs.xml                     |   6 +-
 java/res/values/bools.xml                     |  11 -
 java/res/values/config.xml                    |  12 +
 java/res/values/donottranslate-altchars.xml   |  31 ++-
 java/res/xml-da-xlarge/kbd_qwerty.xml         |   2 +
 java/res/xml-da/kbd_qwerty.xml                |   2 +
 java/res/xml-de-xlarge/kbd_qwerty.xml         |   2 +
 java/res/xml-de/kbd_qwerty.xml                |   2 +
 java/res/xml-fr-rCA-xlarge/kbd_qwerty.xml     |   2 +
 java/res/xml-fr-rCA/kbd_qwerty.xml            |   2 +
 java/res/xml-fr-rCH-xlarge/kbd_qwerty.xml     |   2 +
 java/res/xml-fr-rCH/kbd_qwerty.xml            |   2 +
 java/res/xml-fr-xlarge/kbd_qwerty.xml         |   2 +
 java/res/xml-fr/kbd_qwerty.xml                |   2 +
 java/res/xml-iw/kbd_qwerty.xml                |   2 +
 java/res/xml-nb-xlarge/kbd_qwerty.xml         |   2 +
 java/res/xml-nb/kbd_qwerty.xml                |   2 +
 java/res/xml-ru-xlarge/kbd_qwerty.xml         |  13 +-
 java/res/xml-ru/kbd_qwerty.xml                |  13 +-
 java/res/xml-sr-xlarge/kbd_qwerty.xml         |  14 +-
 java/res/xml-sr/kbd_qwerty.xml                |  12 +-
 java/res/xml-sv-xlarge/kbd_qwerty.xml         |   2 +
 java/res/xml-sv/kbd_qwerty.xml                |   2 +
 java/res/xml-xlarge-land/popup_domains.xml    |  46 ----
 java/res/xml-xlarge-land/popup_smileys.xml    |  89 --------
 java/res/xml-xlarge/kbd_azerty_rows.xml       |  22 --
 java/res/xml-xlarge/kbd_key_styles.xml        |  13 +-
 java/res/xml-xlarge/kbd_number.xml            |   2 +
 java/res/xml-xlarge/kbd_phone.xml             |   2 +
 java/res/xml-xlarge/kbd_phone_symbols.xml     |   2 +
 java/res/xml-xlarge/kbd_qwerty.xml            |   2 +
 java/res/xml-xlarge/kbd_qwerty_row1.xml       |  10 -
 java/res/xml-xlarge/kbd_qwerty_row2.xml       |   5 -
 java/res/xml-xlarge/kbd_qwerty_row3.xml       |   6 -
 java/res/xml-xlarge/kbd_qwerty_row4.xml       |   5 -
 .../kbd_qwerty_rows_scandinavia.xml           |  17 --
 java/res/xml-xlarge/kbd_qwertz_rows.xml       |  16 --
 java/res/xml-xlarge/kbd_symbols.xml           |  48 ++--
 java/res/xml-xlarge/kbd_symbols_shift.xml     |  11 +-
 java/res/xml-xlarge/popup_domains.xml         |  46 ----
 java/res/xml-xlarge/popup_smileys.xml         |  89 --------
 java/res/xml/kbd_azerty_rows.xml              |  20 +-
 java/res/xml/kbd_key_styles.xml               |   7 +-
 java/res/xml/kbd_number.xml                   |   4 +-
 java/res/xml/kbd_phone.xml                    |   2 +
 java/res/xml/kbd_phone_symbols.xml            |   2 +
 java/res/xml/kbd_qwerty.xml                   |   2 +
 java/res/xml/kbd_qwerty_f1.xml                |   6 +-
 java/res/xml/kbd_qwerty_row1.xml              |  10 -
 java/res/xml/kbd_qwerty_row2.xml              |   5 -
 java/res/xml/kbd_qwerty_row3.xml              |   4 -
 java/res/xml/kbd_qwerty_row4.xml              |   6 +-
 java/res/xml/kbd_qwerty_rows_scandinavia.xml  |  17 --
 java/res/xml/kbd_qwertz_rows.xml              |  14 --
 java/res/xml/kbd_symbols.xml                  |  42 ++--
 java/res/xml/kbd_symbols_f1.xml               |   2 +-
 java/res/xml/kbd_symbols_row4.xml             |   6 +-
 java/res/xml/kbd_symbols_shift.xml            |  19 +-
 java/res/xml/popup_at.xml                     |  39 ----
 java/res/xml/popup_comma.xml                  |  39 ----
 java/res/xml/popup_domains.xml                |  46 ----
 java/res/xml/popup_mic.xml                    |  40 ----
 java/res/xml/popup_punctuation.xml            |  68 ------
 java/res/xml/popup_slash.xml                  |  39 ----
 java/res/xml/popup_smileys.xml                |  89 --------
 java/res/xml/prefs.xml                        |   2 +-
 .../com/android/inputmethod/keyboard/Key.java | 172 ++++++++-------
 .../inputmethod/keyboard/KeyStyles.java       | 145 +++++++++----
 .../inputmethod/keyboard/Keyboard.java        | 122 ++++-------
 .../inputmethod/keyboard/KeyboardId.java      |  40 +++-
 .../inputmethod/keyboard/KeyboardParser.java  | 149 ++++++-------
 .../keyboard/KeyboardSwitcher.java            |  23 +-
 .../inputmethod/keyboard/KeyboardView.java    |  20 +-
 .../inputmethod/keyboard/LatinKeyboard.java   |  13 +-
 .../keyboard/MiniKeyboardBuilder.java         | 104 +++++++++
 .../keyboard/PopupCharactersParser.java       | 177 +++++++++++++++
 .../com/android/inputmethod/keyboard/Row.java |   4 +-
 .../android/inputmethod/latin/LatinIME.java   |   2 +-
 .../inputmethod/keyboard/KeyStylesTests.java  | 120 ++++++++++
 .../keyboard/PopupCharactersParserTests.java  | 205 ++++++++++++++++++
 104 files changed, 1201 insertions(+), 1698 deletions(-)
 delete mode 100644 java/res/values-el/donottranslate-altchars.xml
 delete mode 100644 java/res/values-es-rUS/donottranslate-altchars.xml
 delete mode 100644 java/res/values-ja/donottranslate-altchars.xml
 delete mode 100644 java/res/values-ko/donottranslate-altchars.xml
 delete mode 100644 java/res/values-nl/donottranslate-altchars.xml
 delete mode 100644 java/res/values-pt-rPT/donottranslate-altchars.xml
 delete mode 100644 java/res/values-pt/donottranslate-altchars.xml
 delete mode 100644 java/res/values-xlarge/bools.xml
 delete mode 100644 java/res/values-zh-rCN/donottranslate-altchars.xml
 delete mode 100644 java/res/values-zh-rTW/donottranslate-altchars.xml
 delete mode 100644 java/res/xml-xlarge-land/popup_domains.xml
 delete mode 100644 java/res/xml-xlarge-land/popup_smileys.xml
 delete mode 100644 java/res/xml-xlarge/popup_domains.xml
 delete mode 100644 java/res/xml-xlarge/popup_smileys.xml
 delete mode 100644 java/res/xml/popup_at.xml
 delete mode 100644 java/res/xml/popup_comma.xml
 delete mode 100644 java/res/xml/popup_domains.xml
 delete mode 100644 java/res/xml/popup_mic.xml
 delete mode 100644 java/res/xml/popup_punctuation.xml
 delete mode 100644 java/res/xml/popup_slash.xml
 delete mode 100644 java/res/xml/popup_smileys.xml
 create mode 100644 java/src/com/android/inputmethod/keyboard/MiniKeyboardBuilder.java
 create mode 100644 java/src/com/android/inputmethod/keyboard/PopupCharactersParser.java
 create mode 100644 tests/src/com/android/inputmethod/keyboard/KeyStylesTests.java
 create mode 100644 tests/src/com/android/inputmethod/keyboard/PopupCharactersParserTests.java

diff --git a/java/res/values-cs/donottranslate-altchars.xml b/java/res/values-cs/donottranslate-altchars.xml
index d91a0e449e..f19ac0026c 100644
--- a/java/res/values-cs/donottranslate-altchars.xml
+++ b/java/res/values-cs/donottranslate-altchars.xml
@@ -18,17 +18,16 @@
 */
 -->
 <resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="alternates_for_a">áàâãäåæ</string>
-    <string name="alternates_for_e">3éěèêë</string>
-    <string name="alternates_for_i">íìîï8</string>
-    <string name="alternates_for_o">óòôõöœø9</string>
-    <string name="alternates_for_u">ůúùûü7</string>
-    <string name="alternates_for_s">š§ß</string>
-    <string name="alternates_for_n">ňñ</string>
-    <string name="alternates_for_c">čç</string>
-    <string name="alternates_for_y">ýÿ6</string>
+    <string name="alternates_for_a">á,à,â,ã,ä,å,æ</string>
+    <string name="alternates_for_e">3,é,ě,è,ê,ë</string>
+    <string name="alternates_for_i">í,ì,î,ï,8</string>
+    <string name="alternates_for_o">ó,ò,ô,õ,ö,œ,ø,9</string>
+    <string name="alternates_for_u">ů,ú,ù,û,ü,7</string>
+    <string name="alternates_for_s">š,§,ß</string>
+    <string name="alternates_for_n">ň,ñ</string>
+    <string name="alternates_for_c">č,ç</string>
     <string name="alternates_for_d">ď</string>
-    <string name="alternates_for_r">ř4</string>
-    <string name="alternates_for_t">Å¥5</string>
+    <string name="alternates_for_r">Å™,4</string>
+    <string name="alternates_for_t">Å¥,5</string>
     <string name="alternates_for_z">ž</string>
 </resources>
diff --git a/java/res/values-da/donottranslate-altchars.xml b/java/res/values-da/donottranslate-altchars.xml
index 596994ca46..ca1df7c25d 100644
--- a/java/res/values-da/donottranslate-altchars.xml
+++ b/java/res/values-da/donottranslate-altchars.xml
@@ -18,23 +18,23 @@
 */
 -->
 <resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="alternates_for_a">áàâąã</string>
-    <string name="alternates_for_e">3éèêëę€</string>
-    <string name="alternates_for_i">íìîï8</string>
-    <string name="alternates_for_o">óòôõ9</string>
-    <string name="alternates_for_u">úùûū7</string>
-    <string name="alternates_for_s">śšşß</string>
-    <string name="alternates_for_n">ńñň</string>
-    <string name="alternates_for_c">çćč</string>
-    <string name="alternates_for_y">ýÿü6</string>
-    <string name="alternates_for_d">ðď</string>
-    <string name="alternates_for_r">ř4</string>
-    <string name="alternates_for_t">ťþ5</string>
-    <string name="alternates_for_z">źžż</string>
+    <string name="alternates_for_a">á,à,â,ą,ã</string>
+    <string name="alternates_for_e">3,é,è,ê,ë,ę,€</string>
+    <string name="alternates_for_i">í,ì,î,ï,8</string>
+    <string name="alternates_for_o">ó,ò,ô,õ,9</string>
+    <string name="alternates_for_u">ú,ù,û,ū,7</string>
+    <string name="alternates_for_s">ś,š,ş,ß</string>
+    <string name="alternates_for_n">ń,ñ,ň</string>
+    <string name="alternates_for_c">ç,ć,č</string>
+    <string name="alternates_for_y">ý,ÿ,ü,6</string>
+    <string name="alternates_for_d">ð,ď</string>
+    <string name="alternates_for_r">Å™,4</string>
+    <string name="alternates_for_t">ť,þ,5</string>
+    <string name="alternates_for_z">ź,ž,ż</string>
     <string name="alternates_for_l">Å‚</string>
     <string name="alternates_for_v">w</string>
     <string name="keylabel_for_scandinavia_row2_10">æ</string>
     <string name="keylabel_for_scandinavia_row2_11">ø</string>
     <string name="alternates_for_scandinavia_row2_10">ä</string>
-    <string name="alternates_for_scandinavia_row2_11">öœ</string>
+    <string name="alternates_for_scandinavia_row2_11">ö,œ</string>
 </resources>
diff --git a/java/res/values-de/donottranslate-altchars.xml b/java/res/values-de/donottranslate-altchars.xml
index df27bce28e..6c1abc6d02 100644
--- a/java/res/values-de/donottranslate-altchars.xml
+++ b/java/res/values-de/donottranslate-altchars.xml
@@ -19,13 +19,7 @@
 -->
 <resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="alternates_for_a">ä</string>
-    <string name="alternates_for_e">3èéêë</string>
-    <string name="alternates_for_i">ìíîï8</string>
-    <string name="alternates_for_o">ö9</string>
-    <string name="alternates_for_u">ùúûü7</string>
-    <string name="alternates_for_s">§ß</string>
-    <string name="alternates_for_n">ñ</string>
-    <string name="alternates_for_c">ç</string>
-    <string name="alternates_for_y">ýÿ</string>
+    <string name="alternates_for_o">ö,9</string>
+    <string name="alternates_for_y">ý,ÿ</string>
     <string name="alternates_for_z">6</string>
 </resources>
diff --git a/java/res/values-el/donottranslate-altchars.xml b/java/res/values-el/donottranslate-altchars.xml
deleted file mode 100644
index d3beafade0..0000000000
--- a/java/res/values-el/donottranslate-altchars.xml
+++ /dev/null
@@ -1,30 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-/*
-**
-** Copyright 2010, 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.
-*/
--->
-<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="alternates_for_a">àáâãäåæ</string>
-    <string name="alternates_for_e">3èéêë</string>
-    <string name="alternates_for_i">ìíîï8</string>
-    <string name="alternates_for_o">òóôõöœø9</string>
-    <string name="alternates_for_u">ùúûü7</string>
-    <string name="alternates_for_s">§ß</string>
-    <string name="alternates_for_n">ñ</string>
-    <string name="alternates_for_c">ç</string>
-    <string name="alternates_for_y">ýÿ6</string>
-</resources>
diff --git a/java/res/values-en/donottranslate-altchars.xml b/java/res/values-en/donottranslate-altchars.xml
index 083befa19e..baded885a9 100644
--- a/java/res/values-en/donottranslate-altchars.xml
+++ b/java/res/values-en/donottranslate-altchars.xml
@@ -18,10 +18,9 @@
 */
 -->
 <resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="alternates_for_a">àáâãäåāæ</string>
-    <string name="alternates_for_e">3èéêëē</string>
-    <string name="alternates_for_i">ìíîïī8</string>
-    <string name="alternates_for_o">òóôõöōœø9</string>
-    <string name="alternates_for_u">ùúûüū7</string>
-    <string name="alternates_for_y">ýÿ6</string>
+    <string name="alternates_for_a">à,á,â,ã,ä,å,ā,æ</string>
+    <string name="alternates_for_e">3,è,é,ê,ë,ē</string>
+    <string name="alternates_for_i">ì,í,î,ï,ī,8</string>
+    <string name="alternates_for_o">ò,ó,ô,õ,ö,ō,œ,ø,9</string>
+    <string name="alternates_for_u">ù,ú,û,ü,ū,7</string>
 </resources>
diff --git a/java/res/values-es-rUS/donottranslate-altchars.xml b/java/res/values-es-rUS/donottranslate-altchars.xml
deleted file mode 100644
index d3beafade0..0000000000
--- a/java/res/values-es-rUS/donottranslate-altchars.xml
+++ /dev/null
@@ -1,30 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-/*
-**
-** Copyright 2010, 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.
-*/
--->
-<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="alternates_for_a">àáâãäåæ</string>
-    <string name="alternates_for_e">3èéêë</string>
-    <string name="alternates_for_i">ìíîï8</string>
-    <string name="alternates_for_o">òóôõöœø9</string>
-    <string name="alternates_for_u">ùúûü7</string>
-    <string name="alternates_for_s">§ß</string>
-    <string name="alternates_for_n">ñ</string>
-    <string name="alternates_for_c">ç</string>
-    <string name="alternates_for_y">ýÿ6</string>
-</resources>
diff --git a/java/res/values-es/donottranslate-altchars.xml b/java/res/values-es/donottranslate-altchars.xml
index 721062d2d2..35187d0de1 100644
--- a/java/res/values-es/donottranslate-altchars.xml
+++ b/java/res/values-es/donottranslate-altchars.xml
@@ -19,12 +19,6 @@
 -->
 <resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="alternates_for_a">á</string>
-    <string name="alternates_for_e">3é</string>
-    <string name="alternates_for_i">ìíîï8</string>
-    <string name="alternates_for_o">ó9</string>
-    <string name="alternates_for_u">ùúûü7</string>
-    <string name="alternates_for_s">§ß</string>
-    <string name="alternates_for_n">ñ</string>
-    <string name="alternates_for_c">ç</string>
-    <string name="alternates_for_y">ýÿ6</string>
+    <string name="alternates_for_e">3,é</string>
+    <string name="alternates_for_o">ó,9</string>
 </resources>
diff --git a/java/res/values-fr/donottranslate-altchars.xml b/java/res/values-fr/donottranslate-altchars.xml
index 874d89dab3..830119d52a 100644
--- a/java/res/values-fr/donottranslate-altchars.xml
+++ b/java/res/values-fr/donottranslate-altchars.xml
@@ -18,15 +18,7 @@
 */
 -->
 <resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="alternates_for_a">1àáâãäåæ</string>
-    <string name="alternates_for_e">3èéêë</string>
-    <string name="alternates_for_i">ìíîï8</string>
-    <string name="alternates_for_o">òóôõöœø9</string>
-    <string name="alternates_for_u">ùúûü7</string>
-    <string name="alternates_for_s">§ß</string>
-    <string name="alternates_for_n">ñ</string>
-    <string name="alternates_for_c">ç</string>
-    <string name="alternates_for_y">ýÿ6</string>
+    <string name="alternates_for_a">1,à,á,â,ã,ä,å,æ</string>
     <string name="alternates_for_q"></string>
     <string name="alternates_for_w"></string>
     <string name="alternates_for_z">2</string>
diff --git a/java/res/values-it/donottranslate-altchars.xml b/java/res/values-it/donottranslate-altchars.xml
index 23960171b3..0e4a285f16 100644
--- a/java/res/values-it/donottranslate-altchars.xml
+++ b/java/res/values-it/donottranslate-altchars.xml
@@ -18,13 +18,8 @@
 */
 -->
 <resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="alternates_for_a">àá</string>
-    <string name="alternates_for_e">3èé</string>
-    <string name="alternates_for_i">ìíîï8</string>
-    <string name="alternates_for_o">òó9</string>
-    <string name="alternates_for_u">ùúûü7</string>
+    <string name="alternates_for_a">à,á</string>
+    <string name="alternates_for_e">3,è,é</string>
+    <string name="alternates_for_o">ò,ó,9</string>
     <string name="alternates_for_s">§</string>
-    <string name="alternates_for_n">ñ</string>
-    <string name="alternates_for_c">ç</string>
-    <string name="alternates_for_y">ýÿ6</string>
 </resources>
diff --git a/java/res/values-ja/donottranslate-altchars.xml b/java/res/values-ja/donottranslate-altchars.xml
deleted file mode 100644
index d3beafade0..0000000000
--- a/java/res/values-ja/donottranslate-altchars.xml
+++ /dev/null
@@ -1,30 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-/*
-**
-** Copyright 2010, 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.
-*/
--->
-<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="alternates_for_a">àáâãäåæ</string>
-    <string name="alternates_for_e">3èéêë</string>
-    <string name="alternates_for_i">ìíîï8</string>
-    <string name="alternates_for_o">òóôõöœø9</string>
-    <string name="alternates_for_u">ùúûü7</string>
-    <string name="alternates_for_s">§ß</string>
-    <string name="alternates_for_n">ñ</string>
-    <string name="alternates_for_c">ç</string>
-    <string name="alternates_for_y">ýÿ6</string>
-</resources>
diff --git a/java/res/values-ko/donottranslate-altchars.xml b/java/res/values-ko/donottranslate-altchars.xml
deleted file mode 100644
index d3beafade0..0000000000
--- a/java/res/values-ko/donottranslate-altchars.xml
+++ /dev/null
@@ -1,30 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-/*
-**
-** Copyright 2010, 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.
-*/
--->
-<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="alternates_for_a">àáâãäåæ</string>
-    <string name="alternates_for_e">3èéêë</string>
-    <string name="alternates_for_i">ìíîï8</string>
-    <string name="alternates_for_o">òóôõöœø9</string>
-    <string name="alternates_for_u">ùúûü7</string>
-    <string name="alternates_for_s">§ß</string>
-    <string name="alternates_for_n">ñ</string>
-    <string name="alternates_for_c">ç</string>
-    <string name="alternates_for_y">ýÿ6</string>
-</resources>
diff --git a/java/res/values-nb/donottranslate-altchars.xml b/java/res/values-nb/donottranslate-altchars.xml
index 2644029874..c65dea9fba 100644
--- a/java/res/values-nb/donottranslate-altchars.xml
+++ b/java/res/values-nb/donottranslate-altchars.xml
@@ -18,19 +18,18 @@
 */
 -->
 <resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="alternates_for_a">äáàâąã</string>
-    <string name="alternates_for_e">3éèêëę€</string>
-    <string name="alternates_for_i">íìîï8</string>
-    <string name="alternates_for_o">öóòôõ9</string>
-    <string name="alternates_for_u">üúùûū7</string>
-    <string name="alternates_for_s">śšşß</string>
-    <string name="alternates_for_n">ńñň</string>
-    <string name="alternates_for_c">çćč</string>
-    <string name="alternates_for_y">ýÿ6</string>
-    <string name="alternates_for_d">ðď</string>
-    <string name="alternates_for_r">ř4</string>
-    <string name="alternates_for_t">ťþ5</string>
-    <string name="alternates_for_z">źžż</string>
+    <string name="alternates_for_a">ä,á,à,â,ą,ã</string>
+    <string name="alternates_for_e">3,é,è,ê,ë,ę,€</string>
+    <string name="alternates_for_i">í,ì,î,ï,8</string>
+    <string name="alternates_for_o">ö,ó,ò,ô,õ,9</string>
+    <string name="alternates_for_u">ü,ú,ù,û,ū,7</string>
+    <string name="alternates_for_s">ś,š,ş,ß</string>
+    <string name="alternates_for_n">ń,ñ,ň</string>
+    <string name="alternates_for_c">ç,ć,č</string>
+    <string name="alternates_for_d">ð,ď</string>
+    <string name="alternates_for_r">Å™,4</string>
+    <string name="alternates_for_t">ť,þ,5</string>
+    <string name="alternates_for_z">ź,ž,ż</string>
     <string name="alternates_for_l">Å‚</string>
     <string name="alternates_for_v">w</string>
     <string name="keylabel_for_scandinavia_row2_10">ø</string>
diff --git a/java/res/values-nl/donottranslate-altchars.xml b/java/res/values-nl/donottranslate-altchars.xml
deleted file mode 100644
index d3beafade0..0000000000
--- a/java/res/values-nl/donottranslate-altchars.xml
+++ /dev/null
@@ -1,30 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-/*
-**
-** Copyright 2010, 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.
-*/
--->
-<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="alternates_for_a">àáâãäåæ</string>
-    <string name="alternates_for_e">3èéêë</string>
-    <string name="alternates_for_i">ìíîï8</string>
-    <string name="alternates_for_o">òóôõöœø9</string>
-    <string name="alternates_for_u">ùúûü7</string>
-    <string name="alternates_for_s">§ß</string>
-    <string name="alternates_for_n">ñ</string>
-    <string name="alternates_for_c">ç</string>
-    <string name="alternates_for_y">ýÿ6</string>
-</resources>
diff --git a/java/res/values-pl/donottranslate-altchars.xml b/java/res/values-pl/donottranslate-altchars.xml
index da6b5fd5ca..df8c52b479 100644
--- a/java/res/values-pl/donottranslate-altchars.xml
+++ b/java/res/values-pl/donottranslate-altchars.xml
@@ -19,14 +19,11 @@
 -->
 <resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="alternates_for_a">Ä…</string>
-    <string name="alternates_for_e">ę3</string>
-    <string name="alternates_for_i">ìíîï8</string>
-    <string name="alternates_for_o">ó9</string>
-    <string name="alternates_for_u">ùúûü7</string>
+    <string name="alternates_for_e">Ä™,3</string>
+    <string name="alternates_for_o">ó,9</string>
     <string name="alternates_for_s">Å›</string>
     <string name="alternates_for_n">Å„</string>
     <string name="alternates_for_c">ć</string>
-    <string name="alternates_for_y">ýÿ6</string>
-    <string name="alternates_for_z">źż</string>
+    <string name="alternates_for_z">ź,ż</string>
     <string name="alternates_for_l">Å‚</string>
 </resources>
diff --git a/java/res/values-pt-rPT/donottranslate-altchars.xml b/java/res/values-pt-rPT/donottranslate-altchars.xml
deleted file mode 100644
index d3beafade0..0000000000
--- a/java/res/values-pt-rPT/donottranslate-altchars.xml
+++ /dev/null
@@ -1,30 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-/*
-**
-** Copyright 2010, 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.
-*/
--->
-<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="alternates_for_a">àáâãäåæ</string>
-    <string name="alternates_for_e">3èéêë</string>
-    <string name="alternates_for_i">ìíîï8</string>
-    <string name="alternates_for_o">òóôõöœø9</string>
-    <string name="alternates_for_u">ùúûü7</string>
-    <string name="alternates_for_s">§ß</string>
-    <string name="alternates_for_n">ñ</string>
-    <string name="alternates_for_c">ç</string>
-    <string name="alternates_for_y">ýÿ6</string>
-</resources>
diff --git a/java/res/values-pt/donottranslate-altchars.xml b/java/res/values-pt/donottranslate-altchars.xml
deleted file mode 100644
index d3beafade0..0000000000
--- a/java/res/values-pt/donottranslate-altchars.xml
+++ /dev/null
@@ -1,30 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-/*
-**
-** Copyright 2010, 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.
-*/
--->
-<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="alternates_for_a">àáâãäåæ</string>
-    <string name="alternates_for_e">3èéêë</string>
-    <string name="alternates_for_i">ìíîï8</string>
-    <string name="alternates_for_o">òóôõöœø9</string>
-    <string name="alternates_for_u">ùúûü7</string>
-    <string name="alternates_for_s">§ß</string>
-    <string name="alternates_for_n">ñ</string>
-    <string name="alternates_for_c">ç</string>
-    <string name="alternates_for_y">ýÿ6</string>
-</resources>
diff --git a/java/res/values-rm/donottranslate-altchars.xml b/java/res/values-rm/donottranslate-altchars.xml
index f17026fa92..b44c3c0057 100644
--- a/java/res/values-rm/donottranslate-altchars.xml
+++ b/java/res/values-rm/donottranslate-altchars.xml
@@ -18,13 +18,5 @@
 */
 -->
 <resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="alternates_for_a">àáâãäåæ</string>
-    <string name="alternates_for_e">3èéêë</string>
-    <string name="alternates_for_i">ìíîï8</string>
-    <string name="alternates_for_o">òóöôõœø9</string>
-    <string name="alternates_for_u">ùúûü7</string>
-    <string name="alternates_for_s">§ß</string>
-    <string name="alternates_for_n">ñ</string>
-    <string name="alternates_for_c">ç</string>
-    <string name="alternates_for_y">ýÿ6</string>
+    <string name="alternates_for_o">ò,ó,ö,ô,õ,œ,ø,9</string>
 </resources>
diff --git a/java/res/values-ru/donottranslate-altchars.xml b/java/res/values-ru/donottranslate-altchars.xml
index 46241a62af..c4f9d66d38 100644
--- a/java/res/values-ru/donottranslate-altchars.xml
+++ b/java/res/values-ru/donottranslate-altchars.xml
@@ -18,15 +18,6 @@
 */
 -->
 <resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="alternates_for_a">àáâãäåæ</string>
-    <string name="alternates_for_e">èéêë</string>
-    <string name="alternates_for_i">ìíîï</string>
-    <string name="alternates_for_o">òóôõöœø</string>
-    <string name="alternates_for_u">ùúûü</string>
-    <string name="alternates_for_s">§ß</string>
-    <string name="alternates_for_n">ñ</string>
-    <string name="alternates_for_c">ç</string>
-    <string name="alternates_for_y">ýÿ</string>
     <string name="alternates_for_cyrillic_e">Ñ‘5</string>
     <string name="alternates_for_cyrillic_soft_sign">ÑŠ</string>
 </resources>
diff --git a/java/res/values-sv/donottranslate-altchars.xml b/java/res/values-sv/donottranslate-altchars.xml
index 902a4c9bb9..e156de896f 100644
--- a/java/res/values-sv/donottranslate-altchars.xml
+++ b/java/res/values-sv/donottranslate-altchars.xml
@@ -18,23 +18,23 @@
 */
 -->
 <resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="alternates_for_a">áàâąã</string>
-    <string name="alternates_for_e">3éèêëę€</string>
-    <string name="alternates_for_i">íìîï8</string>
-    <string name="alternates_for_o">óòôõ9</string>
-    <string name="alternates_for_u">úùûū7</string>
-    <string name="alternates_for_s">śšşß</string>
-    <string name="alternates_for_n">ńñň</string>
-    <string name="alternates_for_c">çćč</string>
-    <string name="alternates_for_y">ýÿü6</string>
-    <string name="alternates_for_d">ðď</string>
-    <string name="alternates_for_r">ř4</string>
-    <string name="alternates_for_t">ťþ5</string>
-    <string name="alternates_for_z">źžż</string>
+    <string name="alternates_for_a">á,à,â,ą,ã</string>
+    <string name="alternates_for_e">3,é,è,ê,ë,ę,€</string>
+    <string name="alternates_for_i">í,ì,î,ï,8</string>
+    <string name="alternates_for_o">ó,ò,ô,õ,9</string>
+    <string name="alternates_for_u">ú,ù,û,ū,7</string>
+    <string name="alternates_for_s">ś,š,ş,ß</string>
+    <string name="alternates_for_n">ń,ñ,ň</string>
+    <string name="alternates_for_c">ç,ć,č</string>
+    <string name="alternates_for_y">ý,ÿ,ü,6</string>
+    <string name="alternates_for_d">ð,ď</string>
+    <string name="alternates_for_r">Å™,4</string>
+    <string name="alternates_for_t">ť,þ,5</string>
+    <string name="alternates_for_z">ź,ž,ż</string>
     <string name="alternates_for_l">Å‚</string>
     <string name="alternates_for_v">w</string>
     <string name="keylabel_for_scandinavia_row2_10">ö</string>
     <string name="keylabel_for_scandinavia_row2_11">ä</string>
-    <string name="alternates_for_scandinavia_row2_10">øœ</string>
+    <string name="alternates_for_scandinavia_row2_10">ø,œ</string>
     <string name="alternates_for_scandinavia_row2_11">æ</string>
 </resources>
diff --git a/java/res/values-tr/donottranslate-altchars.xml b/java/res/values-tr/donottranslate-altchars.xml
index 4200d949e6..5e98cc30e1 100644
--- a/java/res/values-tr/donottranslate-altchars.xml
+++ b/java/res/values-tr/donottranslate-altchars.xml
@@ -18,14 +18,8 @@
 */
 -->
 <resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="alternates_for_a">àáâãäåæ</string>
-    <string name="alternates_for_e">3èéêë</string>
-    <string name="alternates_for_i">ìíîï8</string>
-    <string name="alternates_for_o">öòóôõœø9</string>
-    <string name="alternates_for_u">üùúû7</string>
-    <string name="alternates_for_s">ş§ß</string>
-    <string name="alternates_for_n">ñ</string>
-    <string name="alternates_for_c">ç</string>
-    <string name="alternates_for_y">ýÿ6</string>
+    <string name="alternates_for_o">ö,ò,ó,ô,õ,œ,ø,9</string>
+    <string name="alternates_for_u">ü,ù,ú,û,7</string>
+    <string name="alternates_for_s">ş,§,ß</string>
     <string name="alternates_for_g">ÄŸ</string>
-</resources>
\ No newline at end of file
+</resources>
diff --git a/java/res/values-xlarge/bools.xml b/java/res/values-xlarge/bools.xml
deleted file mode 100644
index 9fb670c54c..0000000000
--- a/java/res/values-xlarge/bools.xml
+++ /dev/null
@@ -1,27 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-/*
-**
-** Copyright 2010, 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.
-*/
--->
-<resources>
-    <!-- Whether or not Popup on key press is enabled by default -->
-    <bool name="default_popup_preview">false</bool>
-    <bool name="config_enable_show_settings_key_option">false</bool>
-    <bool name="config_enable_show_subtype_settings">false</bool>
-    <bool name="config_enable_show_voice_key_option">false</bool>
-    <bool name="config_candidate_highlight_font_color_enabled">false</bool>
-</resources>
diff --git a/java/res/values-xlarge/config.xml b/java/res/values-xlarge/config.xml
index 0a1d4e48d7..26c23dd1c7 100644
--- a/java/res/values-xlarge/config.xml
+++ b/java/res/values-xlarge/config.xml
@@ -19,5 +19,12 @@
 -->
 
 <resources>
+    <bool name="config_enable_show_settings_key_option">false</bool>
+    <bool name="config_enable_show_subtype_settings">false</bool>
+    <bool name="config_enable_show_voice_key_option">false</bool>
+    <bool name="config_candidate_highlight_font_color_enabled">false</bool>
+    <!-- Whether or not Popup on key press is enabled by default -->
+    <bool name="config_default_popup_preview">false</bool>
     <string name="config_text_size_of_language_on_spacebar">medium</string>
+    <integer name="config_max_popup_keyboard_column">9</integer>
 </resources>
diff --git a/java/res/values-zh-rCN/donottranslate-altchars.xml b/java/res/values-zh-rCN/donottranslate-altchars.xml
deleted file mode 100644
index c165b11c50..0000000000
--- a/java/res/values-zh-rCN/donottranslate-altchars.xml
+++ /dev/null
@@ -1,30 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-/*
-**
-** Copyright 2010, 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.
-*/
--->
-<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="alternates_for_a">àáâãäåæ</string>
-    <string name="alternates_for_e">èéêë</string>
-    <string name="alternates_for_i">ìíîï</string>
-    <string name="alternates_for_o">òóôõöœø</string>
-    <string name="alternates_for_u">ùúûü</string>
-    <string name="alternates_for_s">§ß</string>
-    <string name="alternates_for_n">ñ</string>
-    <string name="alternates_for_c">ç</string>
-    <string name="alternates_for_y">ýÿ</string>
-</resources>
diff --git a/java/res/values-zh-rTW/donottranslate-altchars.xml b/java/res/values-zh-rTW/donottranslate-altchars.xml
deleted file mode 100644
index c165b11c50..0000000000
--- a/java/res/values-zh-rTW/donottranslate-altchars.xml
+++ /dev/null
@@ -1,30 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-/*
-**
-** Copyright 2010, 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.
-*/
--->
-<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="alternates_for_a">àáâãäåæ</string>
-    <string name="alternates_for_e">èéêë</string>
-    <string name="alternates_for_i">ìíîï</string>
-    <string name="alternates_for_o">òóôõöœø</string>
-    <string name="alternates_for_u">ùúûü</string>
-    <string name="alternates_for_s">§ß</string>
-    <string name="alternates_for_n">ñ</string>
-    <string name="alternates_for_c">ç</string>
-    <string name="alternates_for_y">ýÿ</string>
-</resources>
diff --git a/java/res/values/attrs.xml b/java/res/values/attrs.xml
index 55c806ca45..549c2ae81f 100644
--- a/java/res/values/attrs.xml
+++ b/java/res/values/attrs.xml
@@ -89,15 +89,17 @@
         <attr name="horizontalGap" format="dimension|fraction" />
         <!-- Default vertical gap between rows of keys. -->
         <attr name="verticalGap" format="dimension|fraction" />
+        <!-- Popup keyboard layout template -->
+        <attr name="popupKeyboardTemplate" format="reference" />
     </declare-styleable>
 
     <declare-styleable name="Keyboard_Key">
         <!-- The unicode value or comma-separated values that this key outputs. -->
         <attr name="codes" format="integer|string" />
-        <!-- The XML keyboard layout of any popup keyboard. -->
-        <attr name="popupKeyboard" format="reference" />
         <!-- The characters to display in the popup keyboard. -->
         <attr name="popupCharacters" format="string" />
+        <!-- Maximum column of popup keyboard -->
+        <attr name="maxPopupKeyboardColumn" format="integer" />
         <!-- Key edge flags. -->
         <attr name="keyEdgeFlags">
             <!-- Key is anchored to the left of the keyboard. -->
diff --git a/java/res/values/bools.xml b/java/res/values/bools.xml
index 8742676ad3..2a181e17d3 100644
--- a/java/res/values/bools.xml
+++ b/java/res/values/bools.xml
@@ -18,20 +18,9 @@
 */
 -->
 <resources>
-    <!-- Whether or not auto-correction should be enabled by default -->
-    <bool name="enable_autocorrect">true</bool>
     <!-- Whether this input method should be used as the default for a locale. Override it
          for latin languages. -->
     <bool name="im_is_default">false</bool>
     <!-- Whether or not voice input is enabled by default. -->
     <bool name="voice_input_default">true</bool>
-    <bool name="config_swipeDisambiguation">true</bool>
-    <!-- Whether or not Popup on key press is enabled by default -->
-    <bool name="default_popup_preview">true</bool>
-    <bool name="default_recorrection_enabled">true</bool>
-    <bool name="config_long_press_comma_for_settings_enabled">true</bool>
-    <bool name="config_enable_show_settings_key_option">true</bool>
-    <bool name="config_enable_show_subtype_settings">true</bool>
-    <bool name="config_enable_show_voice_key_option">true</bool>
-    <bool name="config_candidate_highlight_font_color_enabled">true</bool>
 </resources>
diff --git a/java/res/values/config.xml b/java/res/values/config.xml
index 14b8d00600..d82960e15a 100644
--- a/java/res/values/config.xml
+++ b/java/res/values/config.xml
@@ -19,6 +19,15 @@
 -->
 
 <resources>
+    <bool name="config_swipeDisambiguation">true</bool>
+    <bool name="default_recorrection_enabled">true</bool>
+    <bool name="config_long_press_comma_for_settings_enabled">true</bool>
+    <bool name="config_enable_show_settings_key_option">true</bool>
+    <bool name="config_enable_show_subtype_settings">true</bool>
+    <bool name="config_enable_show_voice_key_option">true</bool>
+    <bool name="config_candidate_highlight_font_color_enabled">true</bool>
+    <!-- Whether or not Popup on key press is enabled by default -->
+    <bool name="config_default_popup_preview">true</bool>
     <integer name="config_delay_before_preview">0</integer>
     <integer name="config_delay_after_preview">10</integer>
     <integer name="config_preview_fadein_anim_time">0</integer>
@@ -33,6 +42,9 @@
     <integer name="config_long_press_shift_key_timeout">1200</integer>
     <integer name="config_multi_tap_key_timeout">800</integer>
     <string name="config_text_size_of_language_on_spacebar">small</string>
+    <integer name="config_max_popup_keyboard_column">9</integer>
+    <!-- Whether or not auto-correction should be enabled by default -->
+    <bool name="enable_autocorrect">true</bool>
     <string-array name="auto_correction_threshold_values">
         <!-- Off, When auto correction setting is Off, this value is not used. -->
         <item></item>
diff --git a/java/res/values/donottranslate-altchars.xml b/java/res/values/donottranslate-altchars.xml
index de4250edf4..85e06f23b4 100644
--- a/java/res/values/donottranslate-altchars.xml
+++ b/java/res/values/donottranslate-altchars.xml
@@ -18,15 +18,15 @@
 */
 -->
 <resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="alternates_for_a">àáâãäåæ</string>
-    <string name="alternates_for_e">èéêë</string>
-    <string name="alternates_for_i">ìíîï</string>
-    <string name="alternates_for_o">òóôõöœø</string>
-    <string name="alternates_for_u">ùúûü</string>
-    <string name="alternates_for_s">§ß</string>
+    <string name="alternates_for_a">à,á,â,ã,ä,å,æ</string>
+    <string name="alternates_for_e">3,è,é,ê,ë</string>
+    <string name="alternates_for_i">ì,í,î,ï,8</string>
+    <string name="alternates_for_o">ò,ó,ô,õ,ö,œ,ø,9</string>
+    <string name="alternates_for_u">ù,ú,û,ü,7</string>
+    <string name="alternates_for_s">§,ß</string>
     <string name="alternates_for_n">ñ</string>
     <string name="alternates_for_c">ç</string>
-    <string name="alternates_for_y">ýÿ</string>
+    <string name="alternates_for_y">ý,ÿ,6</string>
     <string name="alternates_for_q">1</string>
     <string name="alternates_for_w">2</string>
     <string name="alternates_for_d"></string>
@@ -43,14 +43,13 @@
     <string name="alternates_for_scandinavia_row2_11"></string>
     <string name="alternates_for_cyrillic_e"></string>
     <string name="alternates_for_cyrillic_soft_sign"></string>
+    <string name="alternates_for_mic">"\@drawable/sym_keyboard_settings|\@integer/key_settings,\@drawable/sym_keyboard_mic|\@integer/key_voice"</string>
+    <string name="alternates_for_smiley">":-)|:-) ,:-(|:-( ,;-)|;-) ,:-P|:-P ,=-O|=-O ,:-*|:-* ,:O|:O ,B-)|B-) ,:-$|:-$ ,:-!|:-! ,:-[|:-[ ,O:-)|O:-) ,:-\\\\\\\\|:-\\\\\\\\ ,:\'(|:\'( ,:-D|:-D "</string>
+    <string name="alternates_for_settings_slash">"\@drawable/sym_keyboard_settings|\@integer/key_settings,/"</string>
+    <string name="alternates_for_settings_at">"\@drawable/sym_keyboard_settings|\@integer/key_settings,\@"</string>
+    <string name="alternates_for_settings_comma">"\@drawable/sym_keyboard_settings|\@integer/key_settings,\\,"</string>
+    <string name="alternates_for_punctuation">":,/,&amp;,(,),-,+,;,\@,\',\",\?,!,\\,"</string>
+    <string name="keylabel_for_popular_domain">".com"</string>
     <!-- popular web domains for the locale - most popular, displayed on the keyboard -->
-    <string name="popular_domain_0">".com"</string>
-    <!-- popular web domains for the locale - item 1, displayed in the popup -->
-    <string name="popular_domain_1">".net"</string>
-    <!-- popular web domains for the locale - item 2, displayed in the popup -->
-    <string name="popular_domain_2">".org"</string>
-    <!-- popular web domains for the locale - item 3, displayed in the popup -->
-    <string name="popular_domain_3">".gov"</string>
-    <!-- popular web domains for the locale - item 4, displayed in the popup -->
-    <string name="popular_domain_4">".edu"</string>
+    <string name="alternates_for_popular_domain">".net,.org,.gov,.edu"</string>
 </resources>
diff --git a/java/res/xml-da-xlarge/kbd_qwerty.xml b/java/res/xml-da-xlarge/kbd_qwerty.xml
index 2852c765a0..aa868e4539 100644
--- a/java/res/xml-da-xlarge/kbd_qwerty.xml
+++ b/java/res/xml-da-xlarge/kbd_qwerty.xml
@@ -31,6 +31,8 @@
     latin:rowHeight="25%p"
     latin:horizontalGap="@dimen/key_horizontal_gap"
     latin:verticalGap="@dimen/key_bottom_gap"
+    latin:popupKeyboardTemplate="@xml/kbd_popup_template"
+    latin:maxPopupKeyboardColumn="@integer/config_max_popup_keyboard_column"
 >
     <include
         latin:keyboardLayout="@xml/kbd_qwerty_rows_scandinavia" />
diff --git a/java/res/xml-da/kbd_qwerty.xml b/java/res/xml-da/kbd_qwerty.xml
index 43215de2d7..dfca1cdd2d 100644
--- a/java/res/xml-da/kbd_qwerty.xml
+++ b/java/res/xml-da/kbd_qwerty.xml
@@ -32,6 +32,8 @@
     latin:keyWidth="9.09%p"
     latin:horizontalGap="@dimen/key_horizontal_gap"
     latin:verticalGap="@dimen/key_bottom_gap"
+    latin:popupKeyboardTemplate="@xml/kbd_popup_template"
+    latin:maxPopupKeyboardColumn="@integer/config_max_popup_keyboard_column"
 >
     <include
         latin:keyboardLayout="@xml/kbd_qwerty_rows_scandinavia" />
diff --git a/java/res/xml-de-xlarge/kbd_qwerty.xml b/java/res/xml-de-xlarge/kbd_qwerty.xml
index 3c92b29a13..a23e4fbf0a 100644
--- a/java/res/xml-de-xlarge/kbd_qwerty.xml
+++ b/java/res/xml-de-xlarge/kbd_qwerty.xml
@@ -26,6 +26,8 @@
     latin:keyWidth="10%p"
     latin:horizontalGap="@dimen/key_horizontal_gap"
     latin:verticalGap="@dimen/key_bottom_gap"
+    latin:popupKeyboardTemplate="@xml/kbd_popup_template"
+    latin:maxPopupKeyboardColumn="@integer/config_max_popup_keyboard_column"
 >
     <include
         latin:keyboardLayout="@xml/kbd_qwertz_rows" />
diff --git a/java/res/xml-de/kbd_qwerty.xml b/java/res/xml-de/kbd_qwerty.xml
index 3c92b29a13..a23e4fbf0a 100644
--- a/java/res/xml-de/kbd_qwerty.xml
+++ b/java/res/xml-de/kbd_qwerty.xml
@@ -26,6 +26,8 @@
     latin:keyWidth="10%p"
     latin:horizontalGap="@dimen/key_horizontal_gap"
     latin:verticalGap="@dimen/key_bottom_gap"
+    latin:popupKeyboardTemplate="@xml/kbd_popup_template"
+    latin:maxPopupKeyboardColumn="@integer/config_max_popup_keyboard_column"
 >
     <include
         latin:keyboardLayout="@xml/kbd_qwertz_rows" />
diff --git a/java/res/xml-fr-rCA-xlarge/kbd_qwerty.xml b/java/res/xml-fr-rCA-xlarge/kbd_qwerty.xml
index b102eb6033..92d92f0e6d 100644
--- a/java/res/xml-fr-rCA-xlarge/kbd_qwerty.xml
+++ b/java/res/xml-fr-rCA-xlarge/kbd_qwerty.xml
@@ -26,6 +26,8 @@
     latin:keyWidth="10%p"
     latin:horizontalGap="@dimen/key_horizontal_gap"
     latin:verticalGap="@dimen/key_bottom_gap"
+    latin:popupKeyboardTemplate="@xml/kbd_popup_template"
+    latin:maxPopupKeyboardColumn="@integer/config_max_popup_keyboard_column"
 >
     <include
         latin:keyboardLayout="@xml/kbd_qwerty_rows" />
diff --git a/java/res/xml-fr-rCA/kbd_qwerty.xml b/java/res/xml-fr-rCA/kbd_qwerty.xml
index b102eb6033..92d92f0e6d 100644
--- a/java/res/xml-fr-rCA/kbd_qwerty.xml
+++ b/java/res/xml-fr-rCA/kbd_qwerty.xml
@@ -26,6 +26,8 @@
     latin:keyWidth="10%p"
     latin:horizontalGap="@dimen/key_horizontal_gap"
     latin:verticalGap="@dimen/key_bottom_gap"
+    latin:popupKeyboardTemplate="@xml/kbd_popup_template"
+    latin:maxPopupKeyboardColumn="@integer/config_max_popup_keyboard_column"
 >
     <include
         latin:keyboardLayout="@xml/kbd_qwerty_rows" />
diff --git a/java/res/xml-fr-rCH-xlarge/kbd_qwerty.xml b/java/res/xml-fr-rCH-xlarge/kbd_qwerty.xml
index 3c92b29a13..a23e4fbf0a 100644
--- a/java/res/xml-fr-rCH-xlarge/kbd_qwerty.xml
+++ b/java/res/xml-fr-rCH-xlarge/kbd_qwerty.xml
@@ -26,6 +26,8 @@
     latin:keyWidth="10%p"
     latin:horizontalGap="@dimen/key_horizontal_gap"
     latin:verticalGap="@dimen/key_bottom_gap"
+    latin:popupKeyboardTemplate="@xml/kbd_popup_template"
+    latin:maxPopupKeyboardColumn="@integer/config_max_popup_keyboard_column"
 >
     <include
         latin:keyboardLayout="@xml/kbd_qwertz_rows" />
diff --git a/java/res/xml-fr-rCH/kbd_qwerty.xml b/java/res/xml-fr-rCH/kbd_qwerty.xml
index 3c92b29a13..a23e4fbf0a 100644
--- a/java/res/xml-fr-rCH/kbd_qwerty.xml
+++ b/java/res/xml-fr-rCH/kbd_qwerty.xml
@@ -26,6 +26,8 @@
     latin:keyWidth="10%p"
     latin:horizontalGap="@dimen/key_horizontal_gap"
     latin:verticalGap="@dimen/key_bottom_gap"
+    latin:popupKeyboardTemplate="@xml/kbd_popup_template"
+    latin:maxPopupKeyboardColumn="@integer/config_max_popup_keyboard_column"
 >
     <include
         latin:keyboardLayout="@xml/kbd_qwertz_rows" />
diff --git a/java/res/xml-fr-xlarge/kbd_qwerty.xml b/java/res/xml-fr-xlarge/kbd_qwerty.xml
index a943843242..2d0b42baf4 100644
--- a/java/res/xml-fr-xlarge/kbd_qwerty.xml
+++ b/java/res/xml-fr-xlarge/kbd_qwerty.xml
@@ -26,6 +26,8 @@
     latin:keyWidth="10%p"
     latin:horizontalGap="@dimen/key_horizontal_gap"
     latin:verticalGap="@dimen/key_bottom_gap"
+    latin:popupKeyboardTemplate="@xml/kbd_popup_template"
+    latin:maxPopupKeyboardColumn="@integer/config_max_popup_keyboard_column"
 >
     <include
         latin:keyboardLayout="@xml/kbd_azerty_rows" />
diff --git a/java/res/xml-fr/kbd_qwerty.xml b/java/res/xml-fr/kbd_qwerty.xml
index a943843242..2d0b42baf4 100644
--- a/java/res/xml-fr/kbd_qwerty.xml
+++ b/java/res/xml-fr/kbd_qwerty.xml
@@ -26,6 +26,8 @@
     latin:keyWidth="10%p"
     latin:horizontalGap="@dimen/key_horizontal_gap"
     latin:verticalGap="@dimen/key_bottom_gap"
+    latin:popupKeyboardTemplate="@xml/kbd_popup_template"
+    latin:maxPopupKeyboardColumn="@integer/config_max_popup_keyboard_column"
 >
     <include
         latin:keyboardLayout="@xml/kbd_azerty_rows" />
diff --git a/java/res/xml-iw/kbd_qwerty.xml b/java/res/xml-iw/kbd_qwerty.xml
index 7d7774efe8..98bfd7e0b2 100644
--- a/java/res/xml-iw/kbd_qwerty.xml
+++ b/java/res/xml-iw/kbd_qwerty.xml
@@ -26,6 +26,8 @@
     latin:keyWidth="10%p"
     latin:horizontalGap="@dimen/key_horizontal_gap"
     latin:verticalGap="@dimen/key_bottom_gap"
+    latin:popupKeyboardTemplate="@xml/kbd_popup_template"
+    latin:maxPopupKeyboardColumn="@integer/config_max_popup_keyboard_column"
 >
     <include
         latin:keyboardLayout="@xml/kbd_key_styles" />
diff --git a/java/res/xml-nb-xlarge/kbd_qwerty.xml b/java/res/xml-nb-xlarge/kbd_qwerty.xml
index e64796628d..5dfdc4f566 100644
--- a/java/res/xml-nb-xlarge/kbd_qwerty.xml
+++ b/java/res/xml-nb-xlarge/kbd_qwerty.xml
@@ -31,6 +31,8 @@
     latin:rowHeight="25%p"
     latin:horizontalGap="@dimen/key_horizontal_gap"
     latin:verticalGap="@dimen/key_bottom_gap"
+    latin:popupKeyboardTemplate="@xml/kbd_popup_template"
+    latin:maxPopupKeyboardColumn="@integer/config_max_popup_keyboard_column"
 >
     <include
         latin:keyboardLayout="@xml/kbd_qwerty_rows_scandinavia" />
diff --git a/java/res/xml-nb/kbd_qwerty.xml b/java/res/xml-nb/kbd_qwerty.xml
index 2985354e56..06f1b947ae 100644
--- a/java/res/xml-nb/kbd_qwerty.xml
+++ b/java/res/xml-nb/kbd_qwerty.xml
@@ -32,6 +32,8 @@
     latin:keyWidth="9.09%p"
     latin:horizontalGap="@dimen/key_horizontal_gap"
     latin:verticalGap="@dimen/key_bottom_gap"
+    latin:popupKeyboardTemplate="@xml/kbd_popup_template"
+    latin:maxPopupKeyboardColumn="@integer/config_max_popup_keyboard_column"
 >
     <include
         latin:keyboardLayout="@xml/kbd_qwerty_rows_scandinavia" />
diff --git a/java/res/xml-ru-xlarge/kbd_qwerty.xml b/java/res/xml-ru-xlarge/kbd_qwerty.xml
index 10f02415bb..b86002a310 100644
--- a/java/res/xml-ru-xlarge/kbd_qwerty.xml
+++ b/java/res/xml-ru-xlarge/kbd_qwerty.xml
@@ -26,6 +26,8 @@
     latin:keyWidth="9.091%p"
     latin:horizontalGap="@dimen/key_horizontal_gap"
     latin:verticalGap="@dimen/key_bottom_gap"
+    latin:popupKeyboardTemplate="@xml/kbd_popup_template"
+    latin:maxPopupKeyboardColumn="@integer/config_max_popup_keyboard_column"
 >
     <include
         latin:keyboardLayout="@xml/kbd_key_styles" />
@@ -40,43 +42,33 @@
             latin:keyEdgeFlags="left" />
         <Key
             latin:keyLabel="й"
-            latin:popupKeyboard="@xml/kbd_popup_template"
             latin:popupCharacters="1" />
         <Key
             latin:keyLabel="ц"
-            latin:popupKeyboard="@xml/kbd_popup_template"
             latin:popupCharacters="2" />
         <Key
             latin:keyLabel="у"
-            latin:popupKeyboard="@xml/kbd_popup_template"
             latin:popupCharacters="3" />
         <Key
             latin:keyLabel="к"
-            latin:popupKeyboard="@xml/kbd_popup_template"
             latin:popupCharacters="4" />
         <Key
             latin:keyLabel="е"
-            latin:popupKeyboard="@xml/kbd_popup_template"
             latin:popupCharacters="@string/alternates_for_cyrillic_e" />
         <Key
             latin:keyLabel="н"
-            latin:popupKeyboard="@xml/kbd_popup_template"
             latin:popupCharacters="6" />
         <Key
             latin:keyLabel="г"
-            latin:popupKeyboard="@xml/kbd_popup_template"
             latin:popupCharacters="7" />
         <Key
             latin:keyLabel="ш"
-            latin:popupKeyboard="@xml/kbd_popup_template"
             latin:popupCharacters="8" />
         <Key
             latin:keyLabel="щ"
-            latin:popupKeyboard="@xml/kbd_popup_template"
             latin:popupCharacters="9" />
         <Key
             latin:keyLabel="з"
-            latin:popupKeyboard="@xml/kbd_popup_template"
             latin:popupCharacters="0" />
         <Key
             latin:keyLabel="Ñ…" />
@@ -141,7 +133,6 @@
             latin:keyLabel="Ñ‚" />
         <Key
             latin:keyLabel="ь"
-            latin:popupKeyboard="@xml/kbd_popup_template"
             latin:popupCharacters="@string/alternates_for_cyrillic_soft_sign" />
         <Key
             latin:keyLabel="б" />
diff --git a/java/res/xml-ru/kbd_qwerty.xml b/java/res/xml-ru/kbd_qwerty.xml
index b96c958c76..08d7a4ac59 100644
--- a/java/res/xml-ru/kbd_qwerty.xml
+++ b/java/res/xml-ru/kbd_qwerty.xml
@@ -26,6 +26,8 @@
     latin:keyWidth="9.091%p"
     latin:horizontalGap="@dimen/key_horizontal_gap"
     latin:verticalGap="@dimen/key_bottom_gap"
+    latin:popupKeyboardTemplate="@xml/kbd_popup_template"
+    latin:maxPopupKeyboardColumn="@integer/config_max_popup_keyboard_column"
 >
     <include
         latin:keyboardLayout="@xml/kbd_key_styles" />
@@ -35,54 +37,44 @@
         <Key
             latin:keyLabel="й"
             latin:keyHintIcon="@drawable/keyboard_hint_1"
-            latin:popupKeyboard="@xml/kbd_popup_template"
             latin:popupCharacters="1"
             latin:keyWidth="8.75%p"
             latin:keyEdgeFlags="left" />
         <Key
             latin:keyLabel="ц"
             latin:keyHintIcon="@drawable/keyboard_hint_2"
-            latin:popupKeyboard="@xml/kbd_popup_template"
             latin:popupCharacters="2" />
         <Key
             latin:keyLabel="у"
             latin:keyHintIcon="@drawable/keyboard_hint_3"
-            latin:popupKeyboard="@xml/kbd_popup_template"
             latin:popupCharacters="3" />
         <Key
             latin:keyLabel="к"
             latin:keyHintIcon="@drawable/keyboard_hint_4"
-            latin:popupKeyboard="@xml/kbd_popup_template"
             latin:popupCharacters="4" />
         <Key
             latin:keyLabel="е"
             latin:keyHintIcon="@drawable/keyboard_hint_5"
-            latin:popupKeyboard="@xml/kbd_popup_template"
             latin:popupCharacters="@string/alternates_for_cyrillic_e" />
         <Key
             latin:keyLabel="н"
             latin:keyHintIcon="@drawable/keyboard_hint_6"
-            latin:popupKeyboard="@xml/kbd_popup_template"
             latin:popupCharacters="6" />
         <Key
             latin:keyLabel="г"
             latin:keyHintIcon="@drawable/keyboard_hint_7"
-            latin:popupKeyboard="@xml/kbd_popup_template"
             latin:popupCharacters="7" />
         <Key
             latin:keyLabel="ш"
             latin:keyHintIcon="@drawable/keyboard_hint_8"
-            latin:popupKeyboard="@xml/kbd_popup_template"
             latin:popupCharacters="8" />
         <Key
             latin:keyLabel="щ"
             latin:keyHintIcon="@drawable/keyboard_hint_9"
-            latin:popupKeyboard="@xml/kbd_popup_template"
             latin:popupCharacters="9" />
         <Key
             latin:keyLabel="з"
             latin:keyHintIcon="@drawable/keyboard_hint_0"
-            latin:popupKeyboard="@xml/kbd_popup_template"
             latin:popupCharacters="0" />
         <Key
             latin:keyLabel="Ñ…"
@@ -138,7 +130,6 @@
             latin:keyLabel="Ñ‚" />
         <Key
             latin:keyLabel="ь"
-            latin:popupKeyboard="@xml/kbd_popup_template"
             latin:popupCharacters="@string/alternates_for_cyrillic_soft_sign" />
         <Key
             latin:keyLabel="б" />
diff --git a/java/res/xml-sr-xlarge/kbd_qwerty.xml b/java/res/xml-sr-xlarge/kbd_qwerty.xml
index 7290c13a04..48764e7c74 100644
--- a/java/res/xml-sr-xlarge/kbd_qwerty.xml
+++ b/java/res/xml-sr-xlarge/kbd_qwerty.xml
@@ -27,6 +27,8 @@
     latin:keyWidth="9.09%p"
     latin:horizontalGap="@dimen/key_horizontal_gap"
     latin:verticalGap="@dimen/key_bottom_gap"
+    latin:popupKeyboardTemplate="@xml/kbd_popup_template"
+    latin:maxPopupKeyboardColumn="@integer/config_max_popup_keyboard_column"
 >
     <include
         latin:keyboardLayout="@xml/kbd_key_styles" />
@@ -41,43 +43,33 @@
             latin:keyEdgeFlags="left" />
         <Key
             latin:keyLabel="Ñ™"
-            latin:popupKeyboard="@xml/kbd_popup_template"
             latin:popupCharacters="1" />
         <Key
             latin:keyLabel="Ñš"
-            latin:popupKeyboard="@xml/kbd_popup_template"
             latin:popupCharacters="2" />
         <Key
             latin:keyLabel="е"
-            latin:popupKeyboard="@xml/kbd_popup_template"
             latin:popupCharacters="3" />
         <Key
             latin:keyLabel="Ñ€"
-            latin:popupKeyboard="@xml/kbd_popup_template"
             latin:popupCharacters="4" />
         <Key
             latin:keyLabel="Ñ‚"
-            latin:popupKeyboard="@xml/kbd_popup_template"
             latin:popupCharacters="5" />
         <Key
             latin:keyLabel="з"
-            latin:popupKeyboard="@xml/kbd_popup_template"
             latin:popupCharacters="6" />
         <Key
             latin:keyLabel="у"
-            latin:popupKeyboard="@xml/kbd_popup_template"
             latin:popupCharacters="7" />
         <Key
             latin:keyLabel="и"
-            latin:popupKeyboard="@xml/kbd_popup_template"
             latin:popupCharacters="8" />
         <Key
             latin:keyLabel="о"
-            latin:popupKeyboard="@xml/kbd_popup_template"
             latin:popupCharacters="9" />
         <Key
             latin:keyLabel="п"
-            latin:popupKeyboard="@xml/kbd_popup_template"
             latin:popupCharacters="0" />
         <Key
             latin:keyLabel="ш" />
@@ -149,14 +141,12 @@
             latin:manualTemporaryUpperCaseCode="33"
             latin:keyHintIcon="@drawable/key_hint_exclamation_holo"
             latin:manualTemporaryUpperCaseHintIcon="@drawable/key_hint_exclamation_large_holo"
-            latin:popupKeyboard="@xml/kbd_popup_template"
             latin:popupCharacters="!" />
         <Key
             latin:keyLabel="."
             latin:manualTemporaryUpperCaseCode="63"
             latin:keyHintIcon="@drawable/key_hint_question_holo"
             latin:manualTemporaryUpperCaseHintIcon="@drawable/key_hint_question_large_holo"
-            latin:popupKeyboard="@xml/kbd_popup_template"
             latin:popupCharacters="\?" />
         <Key
             latin:keyStyle="shiftKeyStyle"
diff --git a/java/res/xml-sr/kbd_qwerty.xml b/java/res/xml-sr/kbd_qwerty.xml
index aa025b2cb2..0e5f5ff791 100644
--- a/java/res/xml-sr/kbd_qwerty.xml
+++ b/java/res/xml-sr/kbd_qwerty.xml
@@ -27,6 +27,8 @@
     latin:keyWidth="9.09%p"
     latin:horizontalGap="@dimen/key_horizontal_gap"
     latin:verticalGap="@dimen/key_bottom_gap"
+    latin:popupKeyboardTemplate="@xml/kbd_popup_template"
+    latin:maxPopupKeyboardColumn="@integer/config_max_popup_keyboard_column"
 >
     <include
         latin:keyboardLayout="@xml/kbd_key_styles" />
@@ -36,53 +38,43 @@
         <Key
             latin:keyLabel="Ñ™"
             latin:keyHintIcon="@drawable/keyboard_hint_1"
-            latin:popupKeyboard="@xml/kbd_popup_template"
             latin:popupCharacters="1"
             latin:keyEdgeFlags="left" />
         <Key
             latin:keyLabel="Ñš"
             latin:keyHintIcon="@drawable/keyboard_hint_2"
-            latin:popupKeyboard="@xml/kbd_popup_template"
             latin:popupCharacters="2" />
         <Key
             latin:keyLabel="е"
             latin:keyHintIcon="@drawable/keyboard_hint_3"
-            latin:popupKeyboard="@xml/kbd_popup_template"
             latin:popupCharacters="3" />
         <Key
             latin:keyLabel="Ñ€"
             latin:keyHintIcon="@drawable/keyboard_hint_4"
-            latin:popupKeyboard="@xml/kbd_popup_template"
             latin:popupCharacters="4" />
         <Key
             latin:keyLabel="Ñ‚"
             latin:keyHintIcon="@drawable/keyboard_hint_5"
-            latin:popupKeyboard="@xml/kbd_popup_template"
             latin:popupCharacters="5" />
         <Key
             latin:keyLabel="з"
             latin:keyHintIcon="@drawable/keyboard_hint_6"
-            latin:popupKeyboard="@xml/kbd_popup_template"
             latin:popupCharacters="6" />
         <Key
             latin:keyLabel="у"
             latin:keyHintIcon="@drawable/keyboard_hint_7"
-            latin:popupKeyboard="@xml/kbd_popup_template"
             latin:popupCharacters="7" />
         <Key
             latin:keyLabel="и"
             latin:keyHintIcon="@drawable/keyboard_hint_8"
-            latin:popupKeyboard="@xml/kbd_popup_template"
             latin:popupCharacters="8" />
         <Key
             latin:keyLabel="о"
             latin:keyHintIcon="@drawable/keyboard_hint_9"
-            latin:popupKeyboard="@xml/kbd_popup_template"
             latin:popupCharacters="9" />
         <Key
             latin:keyLabel="п"
             latin:keyHintIcon="@drawable/keyboard_hint_0"
-            latin:popupKeyboard="@xml/kbd_popup_template"
             latin:popupCharacters="0" />
         <Key
             latin:keyLabel="ш"
diff --git a/java/res/xml-sv-xlarge/kbd_qwerty.xml b/java/res/xml-sv-xlarge/kbd_qwerty.xml
index b6c80ad87c..bbb38585ee 100644
--- a/java/res/xml-sv-xlarge/kbd_qwerty.xml
+++ b/java/res/xml-sv-xlarge/kbd_qwerty.xml
@@ -33,6 +33,8 @@
     latin:rowHeight="25%p"
     latin:horizontalGap="@dimen/key_horizontal_gap"
     latin:verticalGap="@dimen/key_bottom_gap"
+    latin:popupKeyboardTemplate="@xml/kbd_popup_template"
+    latin:maxPopupKeyboardColumn="@integer/config_max_popup_keyboard_column"
 >
     <include
         latin:keyboardLayout="@xml/kbd_qwerty_rows_scandinavia" />
diff --git a/java/res/xml-sv/kbd_qwerty.xml b/java/res/xml-sv/kbd_qwerty.xml
index b3d1b8784d..60d3b45f94 100644
--- a/java/res/xml-sv/kbd_qwerty.xml
+++ b/java/res/xml-sv/kbd_qwerty.xml
@@ -34,6 +34,8 @@
     latin:keyWidth="9.09%p"
     latin:horizontalGap="@dimen/key_horizontal_gap"
     latin:verticalGap="@dimen/key_bottom_gap"
+    latin:popupKeyboardTemplate="@xml/kbd_popup_template"
+    latin:maxPopupKeyboardColumn="@integer/config_max_popup_keyboard_column"
 >
     <include
         latin:keyboardLayout="@xml/kbd_qwerty_rows_scandinavia" />
diff --git a/java/res/xml-xlarge-land/popup_domains.xml b/java/res/xml-xlarge-land/popup_domains.xml
deleted file mode 100644
index 03a3846f95..0000000000
--- a/java/res/xml-xlarge-land/popup_domains.xml
+++ /dev/null
@@ -1,46 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-/*
-**
-** Copyright 2010, 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:keyWidth="4.0%p"
-    latin:horizontalGap="0px"
-    latin:verticalGap="0px"
-    latin:rowHeight="@dimen/popup_key_height"
->
-    <Row
-        latin:rowEdgeFlags="top|bottom"
-    >
-        <Key
-            latin:keyLabel="@string/popular_domain_1"
-            latin:keyOutputText="@string/popular_domain_1"
-            latin:keyEdgeFlags="left" />
-        <Key
-            latin:keyLabel="@string/popular_domain_2"
-            latin:keyOutputText="@string/popular_domain_2" />
-        <Key
-            latin:keyLabel="@string/popular_domain_3"
-            latin:keyOutputText="@string/popular_domain_3" />
-        <Key
-            latin:keyLabel="@string/popular_domain_4"
-            latin:keyOutputText="@string/popular_domain_4"
-            latin:keyEdgeFlags="right" />
-    </Row>
-</Keyboard>
diff --git a/java/res/xml-xlarge-land/popup_smileys.xml b/java/res/xml-xlarge-land/popup_smileys.xml
deleted file mode 100644
index cc4acb06e9..0000000000
--- a/java/res/xml-xlarge-land/popup_smileys.xml
+++ /dev/null
@@ -1,89 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-/*
-**
-** Copyright 2010, 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:keyWidth="4.0%p"
-    latin:horizontalGap="0px"
-    latin:verticalGap="0px"
-    latin:rowHeight="@dimen/popup_key_height"
->
-    <Row
-        latin:rowEdgeFlags="top"
-    >
-        <Key
-            latin:keyLabel=":-)"
-            latin:keyOutputText=":-) "
-            latin:keyEdgeFlags="left" />
-        <Key
-            latin:keyLabel=":-("
-            latin:keyOutputText=":-( " />
-        <Key
-            latin:keyLabel=";-)"
-            latin:keyOutputText=";-) " />
-        <Key
-            latin:keyLabel=":-P"
-            latin:keyOutputText=":-P " />
-        <Key
-            latin:keyLabel="=-O"
-            latin:keyOutputText="=-O "
-            latin:keyEdgeFlags="right" />
-    </Row>
-    <Row>
-        <Key
-            latin:keyLabel=":-*"
-            latin:keyOutputText=":-* "
-            latin:keyEdgeFlags="left" />
-        <Key
-            latin:keyLabel=":O"
-            latin:keyOutputText=":O " />
-        <Key
-            latin:keyLabel="B-)"
-            latin:keyOutputText="B-) " />
-        <Key
-            latin:keyLabel=":-$"
-            latin:keyOutputText=":-$ " />
-        <Key
-            latin:keyLabel=":-!"
-            latin:keyOutputText=":-! "
-            latin:keyEdgeFlags="right" />
-    </Row>
-    <Row
-        latin:rowEdgeFlags="bottom"
-    >
-        <Key
-            latin:keyLabel=":-["
-            latin:keyOutputText=":-[ "
-            latin:keyEdgeFlags="left" />
-        <Key
-            latin:keyLabel="O:-)"
-            latin:keyOutputText="O:-) " />
-        <Key
-            latin:keyLabel=":-\\"
-            latin:keyOutputText=":-\\ " />
-        <Key
-            latin:keyLabel=":'("
-            latin:keyOutputText=":'( " />
-        <Key
-            latin:keyLabel=":-D"
-            latin:keyOutputText=":-D "
-            latin:keyEdgeFlags="right" />
-    </Row>
-</Keyboard>
diff --git a/java/res/xml-xlarge/kbd_azerty_rows.xml b/java/res/xml-xlarge/kbd_azerty_rows.xml
index b68e21481b..6162950658 100644
--- a/java/res/xml-xlarge/kbd_azerty_rows.xml
+++ b/java/res/xml-xlarge/kbd_azerty_rows.xml
@@ -33,43 +33,33 @@
             latin:keyEdgeFlags="left" />
         <Key
             latin:keyLabel="a"
-            latin:popupKeyboard="@xml/kbd_popup_template"
             latin:popupCharacters="@string/alternates_for_a" />
         <Key
             latin:keyLabel="z"
-            latin:popupKeyboard="@xml/kbd_popup_template"
             latin:popupCharacters="@string/alternates_for_z" />
         <Key
             latin:keyLabel="e"
-            latin:popupKeyboard="@xml/kbd_popup_template"
             latin:popupCharacters="@string/alternates_for_e" />
         <Key
             latin:keyLabel="r"
-            latin:popupKeyboard="@xml/kbd_popup_template"
             latin:popupCharacters="@string/alternates_for_r" />
         <Key
             latin:keyLabel="t"
-            latin:popupKeyboard="@xml/kbd_popup_template"
             latin:popupCharacters="@string/alternates_for_t" />
         <Key
             latin:keyLabel="y"
-            latin:popupKeyboard="@xml/kbd_popup_template"
             latin:popupCharacters="@string/alternates_for_y" />
         <Key
             latin:keyLabel="u"
-            latin:popupKeyboard="@xml/kbd_popup_template"
             latin:popupCharacters="@string/alternates_for_u" />
         <Key
             latin:keyLabel="i"
-            latin:popupKeyboard="@xml/kbd_popup_template"
             latin:popupCharacters="@string/alternates_for_i" />
         <Key
             latin:keyLabel="o"
-            latin:popupKeyboard="@xml/kbd_popup_template"
             latin:popupCharacters="@string/alternates_for_o" />
         <Key
             latin:keyLabel="p"
-            latin:popupKeyboard="@xml/kbd_popup_template"
             latin:popupCharacters="@string/alternates_for_p" />
         <Key
             latin:keyStyle="deleteKeyStyle"
@@ -86,21 +76,17 @@
             latin:keyEdgeFlags="left" />
         <Key
             latin:keyLabel="q"
-            latin:popupKeyboard="@xml/kbd_popup_template"
             latin:popupCharacters="@string/alternates_for_q" />
         <Key
             latin:keyLabel="s"
-            latin:popupKeyboard="@xml/kbd_popup_template"
             latin:popupCharacters="@string/alternates_for_s" />
         <Key
             latin:keyLabel="d"
-            latin:popupKeyboard="@xml/kbd_popup_template"
             latin:popupCharacters="@string/alternates_for_d" />
         <Key
             latin:keyLabel="f" />
         <Key
             latin:keyLabel="g"
-            latin:popupKeyboard="@xml/kbd_popup_template"
             latin:popupCharacters="@string/alternates_for_g" />
         <Key
             latin:keyLabel="h" />
@@ -110,7 +96,6 @@
             latin:keyLabel="k" />
         <Key
             latin:keyLabel="l"
-            latin:popupKeyboard="@xml/kbd_popup_template"
             latin:popupCharacters="@string/alternates_for_l" />
         <Key
             latin:keyLabel="m"
@@ -129,30 +114,25 @@
             latin:keyEdgeFlags="left" />
         <Key
             latin:keyLabel="w"
-            latin:popupKeyboard="@xml/kbd_popup_template"
             latin:popupCharacters="@string/alternates_for_w" />
         <Key
             latin:keyLabel="x" />
         <Key
             latin:keyLabel="c"
-            latin:popupKeyboard="@xml/kbd_popup_template"
             latin:popupCharacters="@string/alternates_for_c" />
         <Key
             latin:keyLabel="v"
-            latin:popupKeyboard="@xml/kbd_popup_template"
             latin:popupCharacters="@string/alternates_for_v" />
         <Key
             latin:keyLabel="b" />
         <Key
             latin:keyLabel="n"
-            latin:popupKeyboard="@xml/kbd_popup_template"
             latin:popupCharacters="@string/alternates_for_n" />
         <Key
             latin:keyLabel="\'"
             latin:manualTemporaryUpperCaseCode="58"
             latin:keyHintIcon="@drawable/key_hint_colon_holo"
             latin:manualTemporaryUpperCaseHintIcon="@drawable/key_hint_colon_large_holo"
-            latin:popupKeyboard="@xml/kbd_popup_template"
             latin:popupCharacters=":" />
         <switch>
             <case
@@ -169,14 +149,12 @@
                     latin:manualTemporaryUpperCaseCode="33"
                     latin:keyHintIcon="@drawable/key_hint_exclamation_holo"
                     latin:manualTemporaryUpperCaseHintIcon="@drawable/key_hint_exclamation_large_holo"
-                    latin:popupKeyboard="@xml/kbd_popup_template"
                     latin:popupCharacters="!" />
                 <Key
                     latin:keyLabel="."
                     latin:manualTemporaryUpperCaseCode="63"
                     latin:keyHintIcon="@drawable/key_hint_question_holo"
                     latin:manualTemporaryUpperCaseHintIcon="@drawable/key_hint_question_large_holo"
-                    latin:popupKeyboard="@xml/kbd_popup_template"
                     latin:popupCharacters="\?" />
             </default>
         </switch>
diff --git a/java/res/xml-xlarge/kbd_key_styles.xml b/java/res/xml-xlarge/kbd_key_styles.xml
index be4611195a..6c1df0b9e5 100644
--- a/java/res/xml-xlarge/kbd_key_styles.xml
+++ b/java/res/xml-xlarge/kbd_key_styles.xml
@@ -62,11 +62,11 @@
                 latin:iconPreview="@drawable/sym_keyboard_feedback_space" />
             <key-style
                 latin:styleName="smileyKeyStyle"
-                latin:codes="0"
                 latin:keyIcon="@drawable/sym_keyboard_smiley_holo"
                 latin:keyOutputText=";-) "
                 latin:keyHintIcon="@drawable/hint_popup_holo"
-                latin:popupKeyboard="@xml/popup_smileys" />
+                latin:popupCharacters="@string/alternates_for_smiley"
+                latin:maxPopupKeyboardColumn="5" />
             <key-style
                 latin:styleName="micKeyStyle"
                 latin:codes="@integer/key_voice"
@@ -115,7 +115,8 @@
                 latin:keyLabel=";-)"
                 latin:keyOutputText=";-) "
                 latin:keyHintIcon="@drawable/hint_popup_holo"
-                latin:popupKeyboard="@xml/popup_smileys" />
+                latin:popupCharacters="@string/alternates_for_smiley"
+                latin:maxPopupKeyboardColumn="5" />
             <key-style
                 latin:styleName="micKeyStyle"
                 latin:codes="@integer/key_voice"
@@ -151,9 +152,9 @@
         latin:isSticky="true" />
     <key-style
         latin:styleName="comKeyStyle"
-        latin:keyLabel="@string/popular_domain_0"
+        latin:keyLabel="@string/keylabel_for_popular_domain"
         latin:keyLabelOption="fontNormal"
-        latin:keyOutputText="@string/popular_domain_0"
+        latin:keyOutputText="@string/keylabel_for_popular_domain"
         latin:keyHintIcon="@drawable/hint_popup_holo"
-        latin:popupKeyboard="@xml/popup_domains" />
+        latin:popupCharacters="@string/alternates_for_popular_domain" />
 </merge>
diff --git a/java/res/xml-xlarge/kbd_number.xml b/java/res/xml-xlarge/kbd_number.xml
index 11cf8fb862..93bc1363ab 100644
--- a/java/res/xml-xlarge/kbd_number.xml
+++ b/java/res/xml-xlarge/kbd_number.xml
@@ -26,6 +26,8 @@
     latin:keyWidth="11.949%p"
     latin:horizontalGap="@dimen/key_horizontal_gap"
     latin:verticalGap="@dimen/key_bottom_gap"
+    latin:popupKeyboardTemplate="@xml/kbd_popup_template"
+    latin:maxPopupKeyboardColumn="@integer/config_max_popup_keyboard_column"
 >
     <include
         latin:keyboardLayout="@xml/kbd_key_styles" />
diff --git a/java/res/xml-xlarge/kbd_phone.xml b/java/res/xml-xlarge/kbd_phone.xml
index 6389a8ccd9..66f6020680 100644
--- a/java/res/xml-xlarge/kbd_phone.xml
+++ b/java/res/xml-xlarge/kbd_phone.xml
@@ -26,6 +26,8 @@
     latin:keyWidth="11.949%p"
     latin:horizontalGap="@dimen/key_horizontal_gap"
     latin:verticalGap="@dimen/key_bottom_gap"
+    latin:popupKeyboardTemplate="@xml/kbd_popup_template"
+    latin:maxPopupKeyboardColumn="@integer/config_max_popup_keyboard_column"
 >
     <include
         latin:keyboardLayout="@xml/kbd_key_styles" />
diff --git a/java/res/xml-xlarge/kbd_phone_symbols.xml b/java/res/xml-xlarge/kbd_phone_symbols.xml
index 59d8181f62..8c0df030db 100644
--- a/java/res/xml-xlarge/kbd_phone_symbols.xml
+++ b/java/res/xml-xlarge/kbd_phone_symbols.xml
@@ -26,6 +26,8 @@
     latin:keyWidth="11.949%p"
     latin:horizontalGap="@dimen/key_horizontal_gap"
     latin:verticalGap="@dimen/key_bottom_gap"
+    latin:popupKeyboardTemplate="@xml/kbd_popup_template"
+    latin:maxPopupKeyboardColumn="@integer/config_max_popup_keyboard_column"
 >
     <include
         latin:keyboardLayout="@xml/kbd_key_styles" />
diff --git a/java/res/xml-xlarge/kbd_qwerty.xml b/java/res/xml-xlarge/kbd_qwerty.xml
index 75fe183a3e..1c8d51ffe5 100644
--- a/java/res/xml-xlarge/kbd_qwerty.xml
+++ b/java/res/xml-xlarge/kbd_qwerty.xml
@@ -25,6 +25,8 @@
     latin:rowHeight="25%p"
     latin:horizontalGap="@dimen/key_horizontal_gap"
     latin:verticalGap="@dimen/key_bottom_gap"
+    latin:popupKeyboardTemplate="@xml/kbd_popup_template"
+    latin:maxPopupKeyboardColumn="@integer/config_max_popup_keyboard_column"
 >
     <include
         latin:keyboardLayout="@xml/kbd_qwerty_rows" />
diff --git a/java/res/xml-xlarge/kbd_qwerty_row1.xml b/java/res/xml-xlarge/kbd_qwerty_row1.xml
index 1596867928..f5135591c3 100644
--- a/java/res/xml-xlarge/kbd_qwerty_row1.xml
+++ b/java/res/xml-xlarge/kbd_qwerty_row1.xml
@@ -32,43 +32,33 @@
             latin:keyEdgeFlags="left" />
         <Key
             latin:keyLabel="q"
-            latin:popupKeyboard="@xml/kbd_popup_template"
             latin:popupCharacters="@string/alternates_for_q" />
         <Key
             latin:keyLabel="w"
-            latin:popupKeyboard="@xml/kbd_popup_template"
             latin:popupCharacters="@string/alternates_for_w" />
         <Key
             latin:keyLabel="e"
-            latin:popupKeyboard="@xml/kbd_popup_template"
             latin:popupCharacters="@string/alternates_for_e" />
         <Key
             latin:keyLabel="r"
-            latin:popupKeyboard="@xml/kbd_popup_template"
             latin:popupCharacters="@string/alternates_for_r" />
         <Key
             latin:keyLabel="t"
-            latin:popupKeyboard="@xml/kbd_popup_template"
             latin:popupCharacters="@string/alternates_for_t" />
         <Key
             latin:keyLabel="y"
-            latin:popupKeyboard="@xml/kbd_popup_template"
             latin:popupCharacters="@string/alternates_for_y" />
         <Key
             latin:keyLabel="u"
-            latin:popupKeyboard="@xml/kbd_popup_template"
             latin:popupCharacters="@string/alternates_for_u" />
         <Key
             latin:keyLabel="i"
-            latin:popupKeyboard="@xml/kbd_popup_template"
             latin:popupCharacters="@string/alternates_for_i" />
         <Key
             latin:keyLabel="o"
-            latin:popupKeyboard="@xml/kbd_popup_template"
             latin:popupCharacters="@string/alternates_for_o" />
         <Key
             latin:keyLabel="p"
-            latin:popupKeyboard="@xml/kbd_popup_template"
             latin:popupCharacters="@string/alternates_for_p" />
         <Key
             latin:keyStyle="deleteKeyStyle"
diff --git a/java/res/xml-xlarge/kbd_qwerty_row2.xml b/java/res/xml-xlarge/kbd_qwerty_row2.xml
index 2b9be10abb..1b4393ab2d 100644
--- a/java/res/xml-xlarge/kbd_qwerty_row2.xml
+++ b/java/res/xml-xlarge/kbd_qwerty_row2.xml
@@ -31,21 +31,17 @@
             latin:keyEdgeFlags="left" />
         <Key
             latin:keyLabel="a"
-            latin:popupKeyboard="@xml/kbd_popup_template"
             latin:popupCharacters="@string/alternates_for_a" />
         <Key
             latin:keyLabel="s"
-            latin:popupKeyboard="@xml/kbd_popup_template"
             latin:popupCharacters="@string/alternates_for_s" />
         <Key
             latin:keyLabel="d"
-            latin:popupKeyboard="@xml/kbd_popup_template"
             latin:popupCharacters="@string/alternates_for_d" />
         <Key
             latin:keyLabel="f" />
         <Key
             latin:keyLabel="g"
-            latin:popupKeyboard="@xml/kbd_popup_template"
             latin:popupCharacters="@string/alternates_for_g" />
         <Key
             latin:keyLabel="h" />
@@ -55,7 +51,6 @@
             latin:keyLabel="k" />
         <Key
             latin:keyLabel="l"
-            latin:popupKeyboard="@xml/kbd_popup_template"
             latin:popupCharacters="@string/alternates_for_l" />
         <Key
             latin:keyStyle="returnKeyStyle"
diff --git a/java/res/xml-xlarge/kbd_qwerty_row3.xml b/java/res/xml-xlarge/kbd_qwerty_row3.xml
index 209ef213d8..b7e9bcff9a 100644
--- a/java/res/xml-xlarge/kbd_qwerty_row3.xml
+++ b/java/res/xml-xlarge/kbd_qwerty_row3.xml
@@ -30,23 +30,19 @@
             latin:keyEdgeFlags="left" />
         <Key
             latin:keyLabel="z"
-            latin:popupKeyboard="@xml/kbd_popup_template"
             latin:popupCharacters="@string/alternates_for_z" />
         <Key
             latin:keyLabel="x" />
         <Key
             latin:keyLabel="c"
-            latin:popupKeyboard="@xml/kbd_popup_template"
             latin:popupCharacters="@string/alternates_for_c" />
         <Key
             latin:keyLabel="v"
-            latin:popupKeyboard="@xml/kbd_popup_template"
             latin:popupCharacters="@string/alternates_for_v" />
         <Key
             latin:keyLabel="b" />
         <Key
             latin:keyLabel="n"
-            latin:popupKeyboard="@xml/kbd_popup_template"
             latin:popupCharacters="@string/alternates_for_n" />
         <Key
             latin:keyLabel="m" />
@@ -65,14 +61,12 @@
                     latin:manualTemporaryUpperCaseCode="33"
                     latin:keyHintIcon="@drawable/key_hint_exclamation_holo"
                     latin:manualTemporaryUpperCaseHintIcon="@drawable/key_hint_exclamation_large_holo"
-                    latin:popupKeyboard="@xml/kbd_popup_template"
                     latin:popupCharacters="!" />
                 <Key
                     latin:keyLabel="."
                     latin:manualTemporaryUpperCaseCode="63"
                     latin:keyHintIcon="@drawable/key_hint_question_holo"
                     latin:manualTemporaryUpperCaseHintIcon="@drawable/key_hint_question_large_holo"
-                    latin:popupKeyboard="@xml/kbd_popup_template"
                     latin:popupCharacters="\?" />
             </default>
         </switch>
diff --git a/java/res/xml-xlarge/kbd_qwerty_row4.xml b/java/res/xml-xlarge/kbd_qwerty_row4.xml
index 205f2e0e37..e222a6bb00 100644
--- a/java/res/xml-xlarge/kbd_qwerty_row4.xml
+++ b/java/res/xml-xlarge/kbd_qwerty_row4.xml
@@ -54,7 +54,6 @@
                             latin:manualTemporaryUpperCaseCode="43"
                             latin:keyHintIcon="@drawable/key_hint_plus_holo"
                             latin:manualTemporaryUpperCaseHintIcon="@drawable/key_hint_plus_large_holo"
-                            latin:popupKeyboard="@xml/kbd_popup_template"
                             latin:popupCharacters="+" />
                     </case>
                     <default>
@@ -67,7 +66,6 @@
                     latin:manualTemporaryUpperCaseCode="64"
                     latin:keyHintIcon="@drawable/key_hint_at_holo"
                     latin:manualTemporaryUpperCaseHintIcon="@drawable/key_hint_at_large_holo"
-                    latin:popupKeyboard="@xml/kbd_popup_template"
                     latin:popupCharacters="\@" />
             </default>
         </switch>
@@ -89,7 +87,6 @@
                     latin:manualTemporaryUpperCaseCode="58"
                     latin:keyHintIcon="@drawable/key_hint_colon_holo"
                     latin:manualTemporaryUpperCaseHintIcon="@drawable/key_hint_colon_large_holo"
-                    latin:popupKeyboard="@xml/kbd_popup_template"
                     latin:popupCharacters=":" />
             </case>
             <default>
@@ -98,7 +95,6 @@
                     latin:manualTemporaryUpperCaseCode="34"
                     latin:keyHintIcon="@drawable/key_hint_quote_holo"
                     latin:manualTemporaryUpperCaseHintIcon="@drawable/key_hint_quote_large_holo"
-                    latin:popupKeyboard="@xml/kbd_popup_template"
                     latin:popupCharacters="&quot;" />
             </default>
         </switch>
@@ -115,7 +111,6 @@
                     latin:manualTemporaryUpperCaseCode="95"
                     latin:keyHintIcon="@drawable/key_hint_underline_holo"
                     latin:manualTemporaryUpperCaseHintIcon="@drawable/key_hint_underline_large_holo"
-                    latin:popupKeyboard="@xml/kbd_popup_template"
                     latin:popupCharacters="_" />
             </default>
         </switch>
diff --git a/java/res/xml-xlarge/kbd_qwerty_rows_scandinavia.xml b/java/res/xml-xlarge/kbd_qwerty_rows_scandinavia.xml
index c56d4f497a..8c583ba851 100644
--- a/java/res/xml-xlarge/kbd_qwerty_rows_scandinavia.xml
+++ b/java/res/xml-xlarge/kbd_qwerty_rows_scandinavia.xml
@@ -33,43 +33,33 @@
             latin:keyEdgeFlags="left" />
         <Key
             latin:keyLabel="q"
-            latin:popupKeyboard="@xml/kbd_popup_template"
             latin:popupCharacters="@string/alternates_for_q" />
         <Key
             latin:keyLabel="w"
-            latin:popupKeyboard="@xml/kbd_popup_template"
             latin:popupCharacters="@string/alternates_for_w" />
         <Key
             latin:keyLabel="e"
-            latin:popupKeyboard="@xml/kbd_popup_template"
             latin:popupCharacters="@string/alternates_for_e" />
         <Key
             latin:keyLabel="r"
-            latin:popupKeyboard="@xml/kbd_popup_template"
             latin:popupCharacters="@string/alternates_for_r" />
         <Key
             latin:keyLabel="t"
-            latin:popupKeyboard="@xml/kbd_popup_template"
             latin:popupCharacters="@string/alternates_for_t" />
         <Key
             latin:keyLabel="y"
-            latin:popupKeyboard="@xml/kbd_popup_template"
             latin:popupCharacters="@string/alternates_for_y" />
         <Key
             latin:keyLabel="u"
-            latin:popupKeyboard="@xml/kbd_popup_template"
             latin:popupCharacters="@string/alternates_for_u" />
         <Key
             latin:keyLabel="i"
-            latin:popupKeyboard="@xml/kbd_popup_template"
             latin:popupCharacters="@string/alternates_for_i" />
         <Key
             latin:keyLabel="o"
-            latin:popupKeyboard="@xml/kbd_popup_template"
             latin:popupCharacters="@string/alternates_for_o" />
         <Key
             latin:keyLabel="p"
-            latin:popupKeyboard="@xml/kbd_popup_template"
             latin:popupCharacters="@string/alternates_for_p" />
         <Key
             latin:keyLabel="Ã¥" />
@@ -88,21 +78,17 @@
             latin:keyEdgeFlags="left" />
         <Key
             latin:keyLabel="a"
-            latin:popupKeyboard="@xml/kbd_popup_template"
             latin:popupCharacters="@string/alternates_for_a" />
         <Key
             latin:keyLabel="s"
-            latin:popupKeyboard="@xml/kbd_popup_template"
             latin:popupCharacters="@string/alternates_for_s" />
         <Key
             latin:keyLabel="d"
-            latin:popupKeyboard="@xml/kbd_popup_template"
             latin:popupCharacters="@string/alternates_for_d" />
         <Key
             latin:keyLabel="f" />
         <Key
             latin:keyLabel="g"
-            latin:popupKeyboard="@xml/kbd_popup_template"
             latin:popupCharacters="@string/alternates_for_g" />
         <Key
             latin:keyLabel="h" />
@@ -112,15 +98,12 @@
             latin:keyLabel="k" />
         <Key
             latin:keyLabel="l"
-            latin:popupKeyboard="@xml/kbd_popup_template"
             latin:popupCharacters="@string/alternates_for_l" />
         <Key
             latin:keyLabel="@string/keylabel_for_scandinavia_row2_10"
-            latin:popupKeyboard="@xml/kbd_popup_template"
             latin:popupCharacters="@string/alternates_for_scandinavia_row2_10" />
         <Key
             latin:keyLabel="@string/keylabel_for_scandinavia_row2_11"
-            latin:popupKeyboard="@xml/kbd_popup_template"
             latin:popupCharacters="@string/alternates_for_scandinavia_row2_11" />
         <Key
             latin:keyStyle="returnKeyStyle"
diff --git a/java/res/xml-xlarge/kbd_qwertz_rows.xml b/java/res/xml-xlarge/kbd_qwertz_rows.xml
index de15b5fd64..3e99f05113 100644
--- a/java/res/xml-xlarge/kbd_qwertz_rows.xml
+++ b/java/res/xml-xlarge/kbd_qwertz_rows.xml
@@ -33,43 +33,33 @@
             latin:keyEdgeFlags="left" />
         <Key
             latin:keyLabel="q"
-            latin:popupKeyboard="@xml/kbd_popup_template"
             latin:popupCharacters="@string/alternates_for_q" />
         <Key
             latin:keyLabel="w"
-            latin:popupKeyboard="@xml/kbd_popup_template"
             latin:popupCharacters="@string/alternates_for_w" />
         <Key
             latin:keyLabel="e"
-            latin:popupKeyboard="@xml/kbd_popup_template"
             latin:popupCharacters="@string/alternates_for_e" />
         <Key
             latin:keyLabel="r"
-            latin:popupKeyboard="@xml/kbd_popup_template"
             latin:popupCharacters="@string/alternates_for_r" />
         <Key
             latin:keyLabel="t"
-            latin:popupKeyboard="@xml/kbd_popup_template"
             latin:popupCharacters="@string/alternates_for_t" />
         <Key
             latin:keyLabel="z"
-            latin:popupKeyboard="@xml/kbd_popup_template"
             latin:popupCharacters="@string/alternates_for_z" />
         <Key
             latin:keyLabel="u"
-            latin:popupKeyboard="@xml/kbd_popup_template"
             latin:popupCharacters="@string/alternates_for_u" />
         <Key
             latin:keyLabel="i"
-            latin:popupKeyboard="@xml/kbd_popup_template"
             latin:popupCharacters="@string/alternates_for_i" />
         <Key
             latin:keyLabel="o"
-            latin:popupKeyboard="@xml/kbd_popup_template"
             latin:popupCharacters="@string/alternates_for_o" />
         <Key
             latin:keyLabel="p"
-            latin:popupKeyboard="@xml/kbd_popup_template"
             latin:popupCharacters="@string/alternates_for_p" />
         <Key
             latin:keyStyle="deleteKeyStyle"
@@ -87,23 +77,19 @@
             latin:keyEdgeFlags="left" />
         <Key
             latin:keyLabel="y"
-            latin:popupKeyboard="@xml/kbd_popup_template"
             latin:popupCharacters="@string/alternates_for_y" />
         <Key
             latin:keyLabel="x" />
         <Key
             latin:keyLabel="c"
-            latin:popupKeyboard="@xml/kbd_popup_template"
             latin:popupCharacters="@string/alternates_for_c" />
         <Key
             latin:keyLabel="v"
-            latin:popupKeyboard="@xml/kbd_popup_template"
             latin:popupCharacters="@string/alternates_for_v" />
         <Key
             latin:keyLabel="b" />
         <Key
             latin:keyLabel="n"
-            latin:popupKeyboard="@xml/kbd_popup_template"
             latin:popupCharacters="@string/alternates_for_n" />
         <Key
             latin:keyLabel="m" />
@@ -122,14 +108,12 @@
                     latin:manualTemporaryUpperCaseCode="33"
                     latin:keyHintIcon="@drawable/key_hint_exclamation_holo"
                     latin:manualTemporaryUpperCaseHintIcon="@drawable/key_hint_exclamation_large_holo"
-                    latin:popupKeyboard="@xml/kbd_popup_template"
                     latin:popupCharacters="!" />
                 <Key
                     latin:keyLabel="."
                     latin:manualTemporaryUpperCaseCode="63"
                     latin:keyHintIcon="@drawable/key_hint_question_holo"
                     latin:manualTemporaryUpperCaseHintIcon="@drawable/key_hint_question_large_holo"
-                    latin:popupKeyboard="@xml/kbd_popup_template"
                     latin:popupCharacters="\?" />
             </default>
         </switch>
diff --git a/java/res/xml-xlarge/kbd_symbols.xml b/java/res/xml-xlarge/kbd_symbols.xml
index f78af8abd1..50b8e5307d 100644
--- a/java/res/xml-xlarge/kbd_symbols.xml
+++ b/java/res/xml-xlarge/kbd_symbols.xml
@@ -25,6 +25,8 @@
     latin:rowHeight="25%p"
     latin:horizontalGap="@dimen/key_horizontal_gap"
     latin:verticalGap="@dimen/key_bottom_gap"
+    latin:popupKeyboardTemplate="@xml/kbd_popup_template"
+    latin:maxPopupKeyboardColumn="@integer/config_max_popup_keyboard_column"
 >
     <include
         latin:keyboardLayout="@xml/kbd_key_styles" />
@@ -39,29 +41,23 @@
             latin:keyEdgeFlags="left" />
         <Key
             latin:keyLabel="1"
-            latin:popupKeyboard="@xml/kbd_popup_template"
-            latin:popupCharacters="¹½⅓¼⅛" />
+            latin:popupCharacters="¹,½,⅓,¼,⅛" />
         <Key
             latin:keyLabel="2"
-            latin:popupKeyboard="@xml/kbd_popup_template"
-            latin:popupCharacters="²⅔" />
+            latin:popupCharacters="²,⅔" />
         <Key
             latin:keyLabel="3"
-            latin:popupKeyboard="@xml/kbd_popup_template"
-            latin:popupCharacters="³¾⅜" />
+            latin:popupCharacters="³,¾,⅜" />
         <Key
             latin:keyLabel="4"
-            latin:popupKeyboard="@xml/kbd_popup_template"
             latin:popupCharacters="⁴" />
         <Key
             latin:keyLabel="5"
-            latin:popupKeyboard="@xml/kbd_popup_template"
             latin:popupCharacters="⅝" />
         <Key
             latin:keyLabel="6" />
         <Key
             latin:keyLabel="7"
-            latin:popupKeyboard="@xml/kbd_popup_template"
             latin:popupCharacters="â…ž" />
         <Key
             latin:keyLabel="8" />
@@ -69,8 +65,7 @@
             latin:keyLabel="9" />
         <Key
             latin:keyLabel="0"
-            latin:popupKeyboard="@xml/kbd_popup_template"
-            latin:popupCharacters="ⁿ∅" />
+            latin:popupCharacters="ⁿ,∅" />
         <Key
             latin:keyStyle="deleteKeyStyle"
             latin:keyWidth="9.331%p"
@@ -88,34 +83,27 @@
             latin:keyLabel="#" />
         <Key
             latin:keyLabel="$"
-            latin:popupKeyboard="@xml/kbd_popup_template"
-            latin:popupCharacters="¢£€¥₣₤₱" />
+            latin:popupCharacters="¢,£,€,¥,₣,₤,₱" />
         <Key
             latin:keyLabel="%"
-            latin:popupKeyboard="@xml/kbd_popup_template"
             latin:popupCharacters="‰" />
         <Key
             latin:keyLabel="&amp;" />
         <Key
             latin:keyLabel="*"
-            latin:popupKeyboard="@xml/kbd_popup_template"
-            latin:popupCharacters="†‡★" />
+            latin:popupCharacters="†,‡,★" />
         <Key
             latin:keyLabel="-"
-            latin:popupKeyboard="@xml/kbd_popup_template"
-            latin:popupCharacters="_–—" />
+            latin:popupCharacters="_,–,—" />
         <Key
             latin:keyLabel="+"
-            latin:popupKeyboard="@xml/kbd_popup_template"
             latin:popupCharacters="±" />
         <Key
             latin:keyLabel="("
-            latin:popupKeyboard="@xml/kbd_popup_template"
-            latin:popupCharacters="[{&lt;" />
+            latin:popupCharacters="[,{,&lt;" />
         <Key
             latin:keyLabel=")"
-            latin:popupKeyboard="@xml/kbd_popup_template"
-            latin:popupCharacters="]}&gt;" />
+            latin:popupCharacters="],},&gt;" />
         <Key
             latin:keyStyle="returnKeyStyle"
             latin:keyWidth="15.750%p"
@@ -130,16 +118,13 @@
             latin:keyEdgeFlags="left" />
         <Key
             latin:keyLabel="&lt;"
-            latin:popupKeyboard="@xml/kbd_popup_template"
-            latin:popupCharacters="≤«‹" />
+            latin:popupCharacters="≤,«,‹" />
         <Key
             latin:keyLabel="&gt;"
-            latin:popupKeyboard="@xml/kbd_popup_template"
-            latin:popupCharacters="≥»›" />
+            latin:popupCharacters="≥,»,›" />
         <Key
             latin:keyLabel="="
-            latin:popupKeyboard="@xml/kbd_popup_template"
-            latin:popupCharacters="≠≈" />
+            latin:popupCharacters="≠,≈" />
         <Key
             latin:keyLabel=":" />
         <Key
@@ -150,11 +135,9 @@
             latin:keyLabel="." />
         <Key
             latin:keyLabel="!"
-            latin:popupKeyboard="@xml/kbd_popup_template"
             latin:popupCharacters="¡" />
         <Key
             latin:keyLabel="\?"
-            latin:popupKeyboard="@xml/kbd_popup_template"
             latin:popupCharacters="¿" />
         <Key
             latin:keyStyle="moreKeyStyle"
@@ -176,8 +159,7 @@
             latin:keyWidth="37.454%p" />
         <Key
             latin:keyLabel="&quot;"
-            latin:popupKeyboard="@xml/kbd_popup_template"
-            latin:popupCharacters="“”«»˝" />
+            latin:popupCharacters="“,”,«,»,˝" />
         <Key
             latin:keyLabel="_" />
         <Key
diff --git a/java/res/xml-xlarge/kbd_symbols_shift.xml b/java/res/xml-xlarge/kbd_symbols_shift.xml
index f3fbf36d14..f586b1a288 100644
--- a/java/res/xml-xlarge/kbd_symbols_shift.xml
+++ b/java/res/xml-xlarge/kbd_symbols_shift.xml
@@ -25,6 +25,8 @@
     latin:rowHeight="25%p"
     latin:horizontalGap="@dimen/key_horizontal_gap"
     latin:verticalGap="@dimen/key_bottom_gap"
+    latin:popupKeyboardTemplate="@xml/kbd_popup_template"
+    latin:maxPopupKeyboardColumn="@integer/config_max_popup_keyboard_column"
 >
     <include
         latin:keyboardLayout="@xml/kbd_key_styles" />
@@ -45,13 +47,11 @@
             latin:keyLabel="|" />
         <Key
             latin:keyLabel="•"
-            latin:popupKeyboard="@xml/kbd_popup_template"
-            latin:popupCharacters="♪♥♠♦♣" />
+            latin:popupCharacters="♪,♥,♠,♦,♣" />
         <Key
             latin:keyLabel="√" />
         <Key
             latin:keyLabel="Ï€"
-            latin:popupKeyboard="@xml/kbd_popup_template"
             latin:popupCharacters="Π" />
         <Key
             latin:keyLabel="÷" />
@@ -59,7 +59,6 @@
             latin:keyLabel="×" />
         <Key
             latin:keyLabel="§"
-            latin:popupKeyboard="@xml/kbd_popup_template"
             latin:popupCharacters="¶" />
         <Key
             latin:keyLabel="Δ" />
@@ -86,13 +85,11 @@
             latin:keyLabel="Â¥" />
         <Key
             latin:keyLabel="^"
-            latin:popupKeyboard="@xml/kbd_popup_template"
-            latin:popupCharacters="↑↓←→" />
+            latin:popupCharacters="↑,↓,←,→" />
         <Key
             latin:keyLabel="°" />
         <Key
             latin:keyLabel="±"
-            latin:popupKeyboard="@xml/kbd_popup_template"
             latin:popupCharacters="∞" />
         <Key
             latin:keyLabel="{" />
diff --git a/java/res/xml-xlarge/popup_domains.xml b/java/res/xml-xlarge/popup_domains.xml
deleted file mode 100644
index 6d65c898b2..0000000000
--- a/java/res/xml-xlarge/popup_domains.xml
+++ /dev/null
@@ -1,46 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-/*
-**
-** Copyright 2010, 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:keyWidth="5.5%p"
-    latin:horizontalGap="0px"
-    latin:verticalGap="0px"
-    latin:rowHeight="@dimen/popup_key_height"
->
-    <Row
-        latin:rowEdgeFlags="top|bottom"
-    >
-        <Key
-            latin:keyLabel="@string/popular_domain_1"
-            latin:keyOutputText="@string/popular_domain_1"
-            latin:keyEdgeFlags="left" />
-        <Key
-            latin:keyLabel="@string/popular_domain_2"
-            latin:keyOutputText="@string/popular_domain_2" />
-        <Key
-            latin:keyLabel="@string/popular_domain_3"
-            latin:keyOutputText="@string/popular_domain_3" />
-        <Key
-            latin:keyLabel="@string/popular_domain_4"
-            latin:keyOutputText="@string/popular_domain_4"
-            latin:keyEdgeFlags="right" />
-    </Row>
-</Keyboard>
diff --git a/java/res/xml-xlarge/popup_smileys.xml b/java/res/xml-xlarge/popup_smileys.xml
deleted file mode 100644
index 552dc42a78..0000000000
--- a/java/res/xml-xlarge/popup_smileys.xml
+++ /dev/null
@@ -1,89 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-/*
-**
-** Copyright 2010, 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:keyWidth="5.0%p"
-    latin:horizontalGap="0px"
-    latin:verticalGap="0px"
-    latin:rowHeight="@dimen/popup_key_height"
->
-    <Row
-        latin:rowEdgeFlags="top"
-    >
-        <Key
-            latin:keyLabel=":-)"
-            latin:keyOutputText=":-) "
-            latin:keyEdgeFlags="left" />
-        <Key
-            latin:keyLabel=":-("
-            latin:keyOutputText=":-( " />
-        <Key
-            latin:keyLabel=";-)"
-            latin:keyOutputText=";-) " />
-        <Key
-            latin:keyLabel=":-P"
-            latin:keyOutputText=":-P " />
-        <Key
-            latin:keyLabel="=-O"
-            latin:keyOutputText="=-O "
-            latin:keyEdgeFlags="right" />
-    </Row>
-    <Row>
-        <Key
-            latin:keyLabel=":-*"
-            latin:keyOutputText=":-* "
-            latin:keyEdgeFlags="left" />
-        <Key
-            latin:keyLabel=":O"
-            latin:keyOutputText=":O " />
-        <Key
-            latin:keyLabel="B-)"
-            latin:keyOutputText="B-) " />
-        <Key
-            latin:keyLabel=":-$"
-            latin:keyOutputText=":-$ " />
-        <Key
-            latin:keyLabel=":-!"
-            latin:keyOutputText=":-! "
-            latin:keyEdgeFlags="right" />
-    </Row>
-    <Row
-        latin:rowEdgeFlags="bottom"
-    >
-        <Key
-            latin:keyLabel=":-["
-            latin:keyOutputText=":-[ "
-            latin:keyEdgeFlags="left" />
-        <Key
-            latin:keyLabel="O:-)"
-            latin:keyOutputText="O:-) " />
-        <Key
-            latin:keyLabel=":-\\"
-            latin:keyOutputText=":-\\ " />
-        <Key
-            latin:keyLabel=":'("
-            latin:keyOutputText=":'( " />
-        <Key
-            latin:keyLabel=":-D"
-            latin:keyOutputText=":-D "
-            latin:keyEdgeFlags="right" />
-    </Row>
-</Keyboard>
diff --git a/java/res/xml/kbd_azerty_rows.xml b/java/res/xml/kbd_azerty_rows.xml
index c06724c496..71109ab677 100644
--- a/java/res/xml/kbd_azerty_rows.xml
+++ b/java/res/xml/kbd_azerty_rows.xml
@@ -30,53 +30,43 @@
         <Key
             latin:keyLabel="a"
             latin:keyHintIcon="@drawable/keyboard_hint_1"
-            latin:popupKeyboard="@xml/kbd_popup_template"
             latin:popupCharacters="@string/alternates_for_a"
             latin:keyEdgeFlags="left" />
         <Key
             latin:keyLabel="z"
             latin:keyHintIcon="@drawable/keyboard_hint_2"
-            latin:popupKeyboard="@xml/kbd_popup_template"
             latin:popupCharacters="@string/alternates_for_z" />
         <Key
             latin:keyLabel="e"
             latin:keyHintIcon="@drawable/keyboard_hint_3"
-            latin:popupKeyboard="@xml/kbd_popup_template"
             latin:popupCharacters="@string/alternates_for_e" />
         <Key
             latin:keyLabel="r"
             latin:keyHintIcon="@drawable/keyboard_hint_4"
-            latin:popupKeyboard="@xml/kbd_popup_template"
             latin:popupCharacters="@string/alternates_for_r" />
         <Key
             latin:keyLabel="t"
             latin:keyHintIcon="@drawable/keyboard_hint_5"
-            latin:popupKeyboard="@xml/kbd_popup_template"
             latin:popupCharacters="@string/alternates_for_t" />
         <Key
             latin:keyLabel="y"
             latin:keyHintIcon="@drawable/keyboard_hint_6"
-            latin:popupKeyboard="@xml/kbd_popup_template"
             latin:popupCharacters="@string/alternates_for_y" />
         <Key
             latin:keyLabel="u"
             latin:keyHintIcon="@drawable/keyboard_hint_7"
-            latin:popupKeyboard="@xml/kbd_popup_template"
             latin:popupCharacters="@string/alternates_for_u" />
         <Key
             latin:keyLabel="i"
             latin:keyHintIcon="@drawable/keyboard_hint_8"
-            latin:popupKeyboard="@xml/kbd_popup_template"
             latin:popupCharacters="@string/alternates_for_i" />
         <Key
             latin:keyLabel="o"
             latin:keyHintIcon="@drawable/keyboard_hint_9"
-            latin:popupKeyboard="@xml/kbd_popup_template"
             latin:popupCharacters="@string/alternates_for_o" />
         <Key
             latin:keyLabel="p"
             latin:keyHintIcon="@drawable/keyboard_hint_0"
-            latin:popupKeyboard="@xml/kbd_popup_template"
             latin:popupCharacters="@string/alternates_for_p"
             latin:keyEdgeFlags="right" />
     </Row>
@@ -85,22 +75,18 @@
     >
         <Key
             latin:keyLabel="q"
-            latin:popupKeyboard="@xml/kbd_popup_template"
             latin:popupCharacters="@string/alternates_for_q"
             latin:keyEdgeFlags="left" />
         <Key
             latin:keyLabel="s"
-            latin:popupKeyboard="@xml/kbd_popup_template"
             latin:popupCharacters="@string/alternates_for_s" />
         <Key
             latin:keyLabel="d"
-            latin:popupKeyboard="@xml/kbd_popup_template"
             latin:popupCharacters="@string/alternates_for_d" />
         <Key
             latin:keyLabel="f" />
         <Key
             latin:keyLabel="g"
-            latin:popupKeyboard="@xml/kbd_popup_template"
             latin:popupCharacters="@string/alternates_for_g" />
         <Key
             latin:keyLabel="h" />
@@ -110,7 +96,6 @@
             latin:keyLabel="k" />
         <Key
             latin:keyLabel="l"
-            latin:popupKeyboard="@xml/kbd_popup_template"
             latin:popupCharacters="@string/alternates_for_l" />
         <Key
             latin:keyLabel="m"
@@ -125,23 +110,20 @@
             latin:keyEdgeFlags="left" />
         <Key
             latin:keyLabel="w"
-            latin:popupKeyboard="@xml/kbd_popup_template"
             latin:popupCharacters="@string/alternates_for_w" />
         <Key
             latin:keyLabel="x" />
         <Key
             latin:keyLabel="c"
-            latin:popupKeyboard="@xml/kbd_popup_template"
+
             latin:popupCharacters="@string/alternates_for_c" />
         <Key
             latin:keyLabel="v"
-            latin:popupKeyboard="@xml/kbd_popup_template"
             latin:popupCharacters="@string/alternates_for_v" />
         <Key
             latin:keyLabel="b" />
         <Key
             latin:keyLabel="n"
-            latin:popupKeyboard="@xml/kbd_popup_template"
             latin:popupCharacters="@string/alternates_for_n" />
         <Key
             latin:keyLabel="\'" />
diff --git a/java/res/xml/kbd_key_styles.xml b/java/res/xml/kbd_key_styles.xml
index 9dd166a411..3b6df73154 100644
--- a/java/res/xml/kbd_key_styles.xml
+++ b/java/res/xml/kbd_key_styles.xml
@@ -88,10 +88,10 @@
             <key-style
                 latin:styleName="micKeyStyle"
                 latin:codes="@integer/key_voice"
-                latin:popupKeyboard="@xml/popup_mic"
                 latin:keyIcon="@drawable/sym_keyboard_mic"
                 latin:iconPreview="@drawable/sym_keyboard_feedback_mic"
                 latin:keyHintIcon="@drawable/hint_popup"
+                latin:popupCharacters="@string/alternates_for_mic"
                 latin:parentStyle="functionalKeyStyle" />
             <!-- Note: This key style is not for functional tab key. This is used for the tab key
                  which is laid out as normal letter key. -->
@@ -165,10 +165,10 @@
             <key-style
                 latin:styleName="micKeyStyle"
                 latin:codes="@integer/key_voice"
-                latin:popupKeyboard="@xml/popup_mic"
                 latin:keyIcon="@drawable/sym_bkeyboard_mic"
                 latin:iconPreview="@drawable/sym_keyboard_feedback_mic"
                 latin:keyHintIcon="@drawable/hint_popup"
+                latin:popupCharacters="@string/alternates_for_mic"
                 latin:parentStyle="functionalKeyStyle" />
             <!-- Note: This key style is not for functional tab key. This is used for the tab key
                  which is laid out as normal letter key. -->
@@ -284,6 +284,7 @@
         latin:keyLabel=":-)"
         latin:keyOutputText=":-) "
         latin:keyHintIcon="@drawable/hint_popup"
-        latin:popupKeyboard="@xml/popup_smileys"
+        latin:popupCharacters="@string/alternates_for_smiley"
+        latin:maxPopupKeyboardColumn="5"
         latin:parentStyle="functionalKeyStyle" />
 </merge>
\ No newline at end of file
diff --git a/java/res/xml/kbd_number.xml b/java/res/xml/kbd_number.xml
index f09da86830..f4a5def950 100644
--- a/java/res/xml/kbd_number.xml
+++ b/java/res/xml/kbd_number.xml
@@ -26,10 +26,12 @@
     latin:keyWidth="26.67%p"
     latin:horizontalGap="@dimen/key_horizontal_gap"
     latin:verticalGap="@dimen/key_bottom_gap"
+    latin:popupKeyboardTemplate="@xml/kbd_popup_template"
+    latin:maxPopupKeyboardColumn="@integer/config_max_popup_keyboard_column"
 >
     <include
         latin:keyboardLayout="@xml/kbd_key_styles" />
-   <switch>
+    <switch>
         <case
             latin:colorScheme="white"
         >
diff --git a/java/res/xml/kbd_phone.xml b/java/res/xml/kbd_phone.xml
index fb0e6d7f2d..62fbdeeec8 100644
--- a/java/res/xml/kbd_phone.xml
+++ b/java/res/xml/kbd_phone.xml
@@ -26,6 +26,8 @@
     latin:keyWidth="26.67%p"
     latin:horizontalGap="@dimen/key_horizontal_gap"
     latin:verticalGap="@dimen/key_bottom_gap"
+    latin:popupKeyboardTemplate="@xml/kbd_popup_template"
+    latin:maxPopupKeyboardColumn="@integer/config_max_popup_keyboard_column"
 >
     <include
         latin:keyboardLayout="@xml/kbd_key_styles" />
diff --git a/java/res/xml/kbd_phone_symbols.xml b/java/res/xml/kbd_phone_symbols.xml
index 5f75ecb53f..1997a75fca 100644
--- a/java/res/xml/kbd_phone_symbols.xml
+++ b/java/res/xml/kbd_phone_symbols.xml
@@ -26,6 +26,8 @@
     latin:keyWidth="26.67%p"
     latin:horizontalGap="@dimen/key_horizontal_gap"
     latin:verticalGap="@dimen/key_bottom_gap"
+    latin:popupKeyboardTemplate="@xml/kbd_popup_template"
+    latin:maxPopupKeyboardColumn="@integer/config_max_popup_keyboard_column"
 >
     <include
         latin:keyboardLayout="@xml/kbd_key_styles" />
diff --git a/java/res/xml/kbd_qwerty.xml b/java/res/xml/kbd_qwerty.xml
index b102eb6033..92d92f0e6d 100644
--- a/java/res/xml/kbd_qwerty.xml
+++ b/java/res/xml/kbd_qwerty.xml
@@ -26,6 +26,8 @@
     latin:keyWidth="10%p"
     latin:horizontalGap="@dimen/key_horizontal_gap"
     latin:verticalGap="@dimen/key_bottom_gap"
+    latin:popupKeyboardTemplate="@xml/kbd_popup_template"
+    latin:maxPopupKeyboardColumn="@integer/config_max_popup_keyboard_column"
 >
     <include
         latin:keyboardLayout="@xml/kbd_qwerty_rows" />
diff --git a/java/res/xml/kbd_qwerty_f1.xml b/java/res/xml/kbd_qwerty_f1.xml
index 1f0ccfb98d..cbdb8c09a7 100644
--- a/java/res/xml/kbd_qwerty_f1.xml
+++ b/java/res/xml/kbd_qwerty_f1.xml
@@ -27,8 +27,8 @@
         >
             <Key
                 latin:keyLabel="/"
-                latin:popupKeyboard="@xml/popup_slash"
                 latin:keyHintIcon="@drawable/hint_popup"
+                latin:popupCharacters="@string/alternates_for_settings_slash"
                 latin:isModifier="true" />
         </case>
         <case
@@ -36,8 +36,8 @@
         >
             <Key
                 latin:keyLabel="\@"
-                latin:popupKeyboard="@xml/popup_at"
                 latin:keyHintIcon="@drawable/hint_popup"
+                latin:popupCharacters="@string/alternates_for_settings_at"
                 latin:isModifier="true" />
         </case>
         <default>
@@ -53,8 +53,8 @@
                 >
                     <Key
                         latin:keyLabel=","
-                        latin:popupKeyboard="@xml/popup_comma"
                         latin:keyHintIcon="@drawable/hint_popup"
+                        latin:popupCharacters="@string/alternates_for_settings_comma"
                         latin:isModifier="true" />
                 </case>
             </switch>
diff --git a/java/res/xml/kbd_qwerty_row1.xml b/java/res/xml/kbd_qwerty_row1.xml
index e4356a8add..d9249657e2 100644
--- a/java/res/xml/kbd_qwerty_row1.xml
+++ b/java/res/xml/kbd_qwerty_row1.xml
@@ -28,53 +28,43 @@
         <Key
             latin:keyLabel="q"
             latin:keyHintIcon="@drawable/keyboard_hint_1"
-            latin:popupKeyboard="@xml/kbd_popup_template"
             latin:popupCharacters="@string/alternates_for_q"
             latin:keyEdgeFlags="left" />
         <Key
             latin:keyLabel="w"
             latin:keyHintIcon="@drawable/keyboard_hint_2"
-            latin:popupKeyboard="@xml/kbd_popup_template"
             latin:popupCharacters="@string/alternates_for_w" />
         <Key
             latin:keyLabel="e"
             latin:keyHintIcon="@drawable/keyboard_hint_3"
-            latin:popupKeyboard="@xml/kbd_popup_template"
             latin:popupCharacters="@string/alternates_for_e" />
         <Key
             latin:keyLabel="r"
             latin:keyHintIcon="@drawable/keyboard_hint_4"
-            latin:popupKeyboard="@xml/kbd_popup_template"
             latin:popupCharacters="@string/alternates_for_r" />
         <Key
             latin:keyLabel="t"
             latin:keyHintIcon="@drawable/keyboard_hint_5"
-            latin:popupKeyboard="@xml/kbd_popup_template"
             latin:popupCharacters="@string/alternates_for_t" />
         <Key
             latin:keyLabel="y"
             latin:keyHintIcon="@drawable/keyboard_hint_6"
-            latin:popupKeyboard="@xml/kbd_popup_template"
             latin:popupCharacters="@string/alternates_for_y" />
         <Key
             latin:keyLabel="u"
             latin:keyHintIcon="@drawable/keyboard_hint_7"
-            latin:popupKeyboard="@xml/kbd_popup_template"
             latin:popupCharacters="@string/alternates_for_u" />
         <Key
             latin:keyLabel="i"
             latin:keyHintIcon="@drawable/keyboard_hint_8"
-            latin:popupKeyboard="@xml/kbd_popup_template"
             latin:popupCharacters="@string/alternates_for_i" />
         <Key
             latin:keyLabel="o"
             latin:keyHintIcon="@drawable/keyboard_hint_9"
-            latin:popupKeyboard="@xml/kbd_popup_narrow_template"
             latin:popupCharacters="@string/alternates_for_o" />
         <Key
             latin:keyLabel="p"
             latin:keyHintIcon="@drawable/keyboard_hint_0"
-            latin:popupKeyboard="@xml/kbd_popup_template"
             latin:popupCharacters="@string/alternates_for_p"
             latin:keyEdgeFlags="right" />
     </Row>
diff --git a/java/res/xml/kbd_qwerty_row2.xml b/java/res/xml/kbd_qwerty_row2.xml
index d5184954fa..dd0035d936 100644
--- a/java/res/xml/kbd_qwerty_row2.xml
+++ b/java/res/xml/kbd_qwerty_row2.xml
@@ -28,22 +28,18 @@
             latin:horizontalGap="5%p" />
         <Key
             latin:keyLabel="a"
-            latin:popupKeyboard="@xml/kbd_popup_template"
             latin:popupCharacters="@string/alternates_for_a"
             latin:keyEdgeFlags="left" />
         <Key
             latin:keyLabel="s"
-            latin:popupKeyboard="@xml/kbd_popup_template"
             latin:popupCharacters="@string/alternates_for_s" />
         <Key
             latin:keyLabel="d"
-            latin:popupKeyboard="@xml/kbd_popup_template"
             latin:popupCharacters="@string/alternates_for_d" />
         <Key
             latin:keyLabel="f" />
         <Key
             latin:keyLabel="g"
-            latin:popupKeyboard="@xml/kbd_popup_template"
             latin:popupCharacters="@string/alternates_for_g" />
         <Key
             latin:keyLabel="h" />
@@ -53,7 +49,6 @@
             latin:keyLabel="k" />
         <Key
             latin:keyLabel="l"
-            latin:popupKeyboard="@xml/kbd_popup_template"
             latin:popupCharacters="@string/alternates_for_l"
             latin:keyEdgeFlags="right" />
     </Row>
diff --git a/java/res/xml/kbd_qwerty_row3.xml b/java/res/xml/kbd_qwerty_row3.xml
index 71a5f9c4a0..26608fd716 100644
--- a/java/res/xml/kbd_qwerty_row3.xml
+++ b/java/res/xml/kbd_qwerty_row3.xml
@@ -30,23 +30,19 @@
             latin:keyEdgeFlags="left" />
         <Key
             latin:keyLabel="z"
-            latin:popupKeyboard="@xml/kbd_popup_template"
             latin:popupCharacters="@string/alternates_for_z" />
         <Key
             latin:keyLabel="x" />
         <Key
             latin:keyLabel="c"
-            latin:popupKeyboard="@xml/kbd_popup_template"
             latin:popupCharacters="@string/alternates_for_c" />
         <Key
             latin:keyLabel="v"
-            latin:popupKeyboard="@xml/kbd_popup_template"
             latin:popupCharacters="@string/alternates_for_v" />
         <Key
             latin:keyLabel="b" />
         <Key
             latin:keyLabel="n"
-            latin:popupKeyboard="@xml/kbd_popup_template"
             latin:popupCharacters="@string/alternates_for_n" />
         <Key
             latin:keyLabel="m" />
diff --git a/java/res/xml/kbd_qwerty_row4.xml b/java/res/xml/kbd_qwerty_row4.xml
index cd03c51fc9..0db0116725 100644
--- a/java/res/xml/kbd_qwerty_row4.xml
+++ b/java/res/xml/kbd_qwerty_row4.xml
@@ -55,7 +55,8 @@
                 <Key
                     latin:keyLabel="."
                     latin:keyHintIcon="@drawable/hint_popup"
-                    latin:popupKeyboard="@xml/popup_punctuation"
+                    latin:popupCharacters="@string/alternates_for_punctuation"
+                    latin:maxPopupKeyboardColumn="7"
                     latin:keyStyle="functionalKeyStyle" />
                 <switch>
                     <case
@@ -104,7 +105,8 @@
                 <Key
                     latin:keyLabel="."
                     latin:keyHintIcon="@drawable/hint_popup"
-                    latin:popupKeyboard="@xml/popup_punctuation"
+                    latin:popupCharacters="@string/alternates_for_punctuation"
+                    latin:maxPopupKeyboardColumn="7"
                     latin:keyStyle="functionalKeyStyle" />
                 <switch>
                     <case
diff --git a/java/res/xml/kbd_qwerty_rows_scandinavia.xml b/java/res/xml/kbd_qwerty_rows_scandinavia.xml
index 4fa2e6eda6..490652663a 100644
--- a/java/res/xml/kbd_qwerty_rows_scandinavia.xml
+++ b/java/res/xml/kbd_qwerty_rows_scandinavia.xml
@@ -30,54 +30,44 @@
         <Key
             latin:keyLabel="q"
             latin:keyHintIcon="@drawable/keyboard_hint_1"
-            latin:popupKeyboard="@xml/kbd_popup_template"
             latin:popupCharacters="@string/alternates_for_q"
             latin:keyWidth="8.75%p"
             latin:keyEdgeFlags="left" />
         <Key
             latin:keyLabel="w"
             latin:keyHintIcon="@drawable/keyboard_hint_2"
-            latin:popupKeyboard="@xml/kbd_popup_template"
             latin:popupCharacters="@string/alternates_for_w" />
         <Key
             latin:keyLabel="e"
             latin:keyHintIcon="@drawable/keyboard_hint_3"
-            latin:popupKeyboard="@xml/kbd_popup_template"
             latin:popupCharacters="@string/alternates_for_e" />
         <Key
             latin:keyLabel="r"
             latin:keyHintIcon="@drawable/keyboard_hint_4"
-            latin:popupKeyboard="@xml/kbd_popup_template"
             latin:popupCharacters="@string/alternates_for_r" />
         <Key
             latin:keyLabel="t"
             latin:keyHintIcon="@drawable/keyboard_hint_5"
-            latin:popupKeyboard="@xml/kbd_popup_template"
             latin:popupCharacters="@string/alternates_for_t" />
         <Key
             latin:keyLabel="y"
             latin:keyHintIcon="@drawable/keyboard_hint_6"
-            latin:popupKeyboard="@xml/kbd_popup_template"
             latin:popupCharacters="@string/alternates_for_y" />
         <Key
             latin:keyLabel="u"
             latin:keyHintIcon="@drawable/keyboard_hint_7"
-            latin:popupKeyboard="@xml/kbd_popup_template"
             latin:popupCharacters="@string/alternates_for_u" />
         <Key
             latin:keyLabel="i"
             latin:keyHintIcon="@drawable/keyboard_hint_8"
-            latin:popupKeyboard="@xml/kbd_popup_template"
             latin:popupCharacters="@string/alternates_for_i" />
         <Key
             latin:keyLabel="o"
             latin:keyHintIcon="@drawable/keyboard_hint_9"
-            latin:popupKeyboard="@xml/kbd_popup_template"
             latin:popupCharacters="@string/alternates_for_o" />
         <Key
             latin:keyLabel="p"
             latin:keyHintIcon="@drawable/keyboard_hint_0"
-            latin:popupKeyboard="@xml/kbd_popup_template"
             latin:popupCharacters="@string/alternates_for_p" />
         <Key
             latin:keyLabel="Ã¥"
@@ -89,23 +79,19 @@
     >
         <Key
             latin:keyLabel="a"
-            latin:popupKeyboard="@xml/kbd_popup_template"
             latin:popupCharacters="@string/alternates_for_a"
             latin:keyWidth="8.75%p"
             latin:keyEdgeFlags="left" />
         <Key
             latin:keyLabel="s"
-            latin:popupKeyboard="@xml/kbd_popup_template"
             latin:popupCharacters="@string/alternates_for_s" />
         <Key
             latin:keyLabel="d"
-            latin:popupKeyboard="@xml/kbd_popup_template"
             latin:popupCharacters="@string/alternates_for_d" />
         <Key
             latin:keyLabel="f" />
         <Key
             latin:keyLabel="g"
-            latin:popupKeyboard="@xml/kbd_popup_template"
             latin:popupCharacters="@string/alternates_for_g" />
         <Key
             latin:keyLabel="h" />
@@ -115,15 +101,12 @@
             latin:keyLabel="k" />
         <Key
             latin:keyLabel="l"
-            latin:popupKeyboard="@xml/kbd_popup_template"
             latin:popupCharacters="@string/alternates_for_l" />
         <Key
             latin:keyLabel="@string/keylabel_for_scandinavia_row2_10"
-            latin:popupKeyboard="@xml/kbd_popup_template"
             latin:popupCharacters="@string/alternates_for_scandinavia_row2_10" />
         <Key
             latin:keyLabel="@string/keylabel_for_scandinavia_row2_11"
-            latin:popupKeyboard="@xml/kbd_popup_template"
             latin:popupCharacters="@string/alternates_for_scandinavia_row2_11"
             latin:keyWidth="8.75%p"
             latin:keyEdgeFlags="right" />
diff --git a/java/res/xml/kbd_qwertz_rows.xml b/java/res/xml/kbd_qwertz_rows.xml
index 4dbb6412a2..375f123350 100644
--- a/java/res/xml/kbd_qwertz_rows.xml
+++ b/java/res/xml/kbd_qwertz_rows.xml
@@ -30,53 +30,43 @@
         <Key
             latin:keyLabel="q"
             latin:keyHintIcon="@drawable/keyboard_hint_1"
-            latin:popupKeyboard="@xml/kbd_popup_template"
             latin:popupCharacters="@string/alternates_for_q"
             latin:keyEdgeFlags="left" />
         <Key
             latin:keyLabel="w"
             latin:keyHintIcon="@drawable/keyboard_hint_2"
-            latin:popupKeyboard="@xml/kbd_popup_template"
             latin:popupCharacters="@string/alternates_for_w" />
         <Key
             latin:keyLabel="e"
             latin:keyHintIcon="@drawable/keyboard_hint_3"
-            latin:popupKeyboard="@xml/kbd_popup_template"
             latin:popupCharacters="@string/alternates_for_e" />
         <Key
             latin:keyLabel="r"
             latin:keyHintIcon="@drawable/keyboard_hint_4"
-            latin:popupKeyboard="@xml/kbd_popup_template"
             latin:popupCharacters="@string/alternates_for_r" />
         <Key
             latin:keyLabel="t"
             latin:keyHintIcon="@drawable/keyboard_hint_5"
-            latin:popupKeyboard="@xml/kbd_popup_template"
             latin:popupCharacters="@string/alternates_for_t" />
         <Key
             latin:keyLabel="z"
             latin:keyHintIcon="@drawable/keyboard_hint_6"
-            latin:popupKeyboard="@xml/kbd_popup_template"
             latin:popupCharacters="@string/alternates_for_z" />
         <Key
             latin:keyLabel="u"
             latin:keyHintIcon="@drawable/keyboard_hint_7"
-            latin:popupKeyboard="@xml/kbd_popup_template"
             latin:popupCharacters="@string/alternates_for_u" />
         <Key
             latin:keyLabel="i"
             latin:keyHintIcon="@drawable/keyboard_hint_8"
-            latin:popupKeyboard="@xml/kbd_popup_template"
             latin:popupCharacters="@string/alternates_for_i" />
         <Key
             latin:keyLabel="o"
             latin:keyHintIcon="@drawable/keyboard_hint_9"
-            latin:popupKeyboard="@xml/kbd_popup_template"
             latin:popupCharacters="@string/alternates_for_o" />
         <Key
             latin:keyLabel="p"
             latin:keyHintIcon="@drawable/keyboard_hint_0"
-            latin:popupKeyboard="@xml/kbd_popup_template"
             latin:popupCharacters="@string/alternates_for_p"
             latin:keyEdgeFlags="right" />
     </Row>
@@ -91,23 +81,19 @@
             latin:keyEdgeFlags="left" />
         <Key
             latin:keyLabel="y"
-            latin:popupKeyboard="@xml/kbd_popup_template"
             latin:popupCharacters="@string/alternates_for_y" />
         <Key
             latin:keyLabel="x" />
         <Key
             latin:keyLabel="c"
-            latin:popupKeyboard="@xml/kbd_popup_template"
             latin:popupCharacters="@string/alternates_for_c" />
         <Key
             latin:keyLabel="v"
-            latin:popupKeyboard="@xml/kbd_popup_template"
             latin:popupCharacters="@string/alternates_for_v" />
         <Key
             latin:keyLabel="b" />
         <Key
             latin:keyLabel="n"
-            latin:popupKeyboard="@xml/kbd_popup_template"
             latin:popupCharacters="@string/alternates_for_n" />
         <Key
             latin:keyLabel="m" />
diff --git a/java/res/xml/kbd_symbols.xml b/java/res/xml/kbd_symbols.xml
index a7da01151a..5d62deaa4e 100644
--- a/java/res/xml/kbd_symbols.xml
+++ b/java/res/xml/kbd_symbols.xml
@@ -26,6 +26,8 @@
     latin:keyWidth="10%p"
     latin:horizontalGap="@dimen/key_horizontal_gap"
     latin:verticalGap="@dimen/key_bottom_gap"
+    latin:popupKeyboardTemplate="@xml/kbd_popup_template"
+    latin:maxPopupKeyboardColumn="@integer/config_max_popup_keyboard_column"
 >
     <include
         latin:keyboardLayout="@xml/kbd_key_styles" />
@@ -34,30 +36,24 @@
     >
         <Key
             latin:keyLabel="1"
-            latin:popupKeyboard="@xml/kbd_popup_template"
-            latin:popupCharacters="¹½⅓¼⅛"
+            latin:popupCharacters="¹,½,⅓,¼,⅛"
             latin:keyEdgeFlags="left" />
         <Key
             latin:keyLabel="2"
-            latin:popupKeyboard="@xml/kbd_popup_template"
-            latin:popupCharacters="²⅔" />
+            latin:popupCharacters="²,⅔" />
         <Key
             latin:keyLabel="3"
-            latin:popupKeyboard="@xml/kbd_popup_template"
-            latin:popupCharacters="³¾⅜" />
+            latin:popupCharacters="³,¾,⅜" />
         <Key
             latin:keyLabel="4"
-            latin:popupKeyboard="@xml/kbd_popup_template"
             latin:popupCharacters="⁴" />
         <Key
             latin:keyLabel="5"
-            latin:popupKeyboard="@xml/kbd_popup_template"
             latin:popupCharacters="⅝" />
         <Key
             latin:keyLabel="6" />
         <Key
             latin:keyLabel="7"
-            latin:popupKeyboard="@xml/kbd_popup_template"
             latin:popupCharacters="â…ž" />
         <Key
             latin:keyLabel="8" />
@@ -65,8 +61,7 @@
             latin:keyLabel="9" />
         <Key
             latin:keyLabel="0"
-            latin:popupKeyboard="@xml/kbd_popup_template"
-            latin:popupCharacters="ⁿ∅"
+            latin:popupCharacters="ⁿ,∅"
             latin:keyEdgeFlags="right" />
     </Row>
     <Row>
@@ -77,34 +72,27 @@
             latin:keyLabel="\#" />
         <Key
             latin:keyLabel="$"
-            latin:popupKeyboard="@xml/kbd_popup_template"
-            latin:popupCharacters="¢£€¥₣₤₱" />
+            latin:popupCharacters="¢,£,€,¥,₣,₤,₱" />
         <Key
             latin:keyLabel="%"
-            latin:popupKeyboard="@xml/kbd_popup_template"
             latin:popupCharacters="‰" />
         <Key
             latin:keyLabel="&amp;" />
         <Key
             latin:keyLabel="*"
-            latin:popupKeyboard="@xml/kbd_popup_template"
-            latin:popupCharacters="†‡★" />
+            latin:popupCharacters="†,‡,★" />
         <Key
             latin:keyLabel="-"
-            latin:popupKeyboard="@xml/kbd_popup_template"
-            latin:popupCharacters="_–—" />
+            latin:popupCharacters="_,–,—" />
         <Key
             latin:keyLabel="+"
-            latin:popupKeyboard="@xml/kbd_popup_template"
             latin:popupCharacters="±" />
         <Key
             latin:keyLabel="("
-            latin:popupKeyboard="@xml/kbd_popup_template"
-            latin:popupCharacters="[{&lt;" />
+            latin:popupCharacters="[,{,&lt;" />
         <Key
             latin:keyLabel=")"
-            latin:popupKeyboard="@xml/kbd_popup_template"
-            latin:popupCharacters="]}&gt;"
+            latin:popupCharacters="],},&gt;"
             latin:keyEdgeFlags="right" />
     </Row>
     <Row>
@@ -114,16 +102,13 @@
             latin:keyEdgeFlags="left" />
         <Key
             latin:keyLabel="!"
-            latin:popupKeyboard="@xml/kbd_popup_template"
             latin:popupCharacters="¡" />
         <Key
             latin:keyLabel="&quot;"
-            latin:popupKeyboard="@xml/kbd_popup_template"
-            latin:popupCharacters="“”«»˝" />
+            latin:popupCharacters="“,”,«,»,˝" />
         <Key
             latin:keyLabel="\'"
-            latin:popupKeyboard="@xml/kbd_popup_template"
-            latin:popupCharacters="‘’" />
+            latin:popupCharacters="‘,’" />
         <Key
             latin:keyLabel=":" />
         <Key
@@ -132,7 +117,6 @@
             latin:keyLabel="/" />
         <Key
             latin:keyLabel="\?"
-            latin:popupKeyboard="@xml/kbd_popup_template"
             latin:popupCharacters="¿" />
         <Key
             latin:keyStyle="deleteKeyStyle"
diff --git a/java/res/xml/kbd_symbols_f1.xml b/java/res/xml/kbd_symbols_f1.xml
index 07ee4edd3b..8487b61585 100644
--- a/java/res/xml/kbd_symbols_f1.xml
+++ b/java/res/xml/kbd_symbols_f1.xml
@@ -33,8 +33,8 @@
         >
             <Key
                 latin:keyLabel=","
-                latin:popupKeyboard="@xml/popup_comma"
                 latin:keyHintIcon="@drawable/hint_popup"
+                latin:popupCharacters="@string/alternates_for_settings_comma"
                 latin:keyStyle="functionalKeyStyle" />
         </case>
     </switch>
diff --git a/java/res/xml/kbd_symbols_row4.xml b/java/res/xml/kbd_symbols_row4.xml
index 1a5417d08f..b330095af5 100644
--- a/java/res/xml/kbd_symbols_row4.xml
+++ b/java/res/xml/kbd_symbols_row4.xml
@@ -40,7 +40,8 @@
                 <Key
                     latin:keyLabel="."
                     latin:keyHintIcon="@drawable/hint_popup"
-                    latin:popupKeyboard="@xml/popup_punctuation"
+                    latin:popupCharacters="@string/alternates_for_punctuation"
+                    latin:maxPopupKeyboardColumn="7"
                     latin:keyStyle="functionalKeyStyle" />
                 <switch>
                     <case
@@ -76,7 +77,8 @@
                 <Key
                     latin:keyLabel="."
                     latin:keyHintIcon="@drawable/hint_popup"
-                    latin:popupKeyboard="@xml/popup_punctuation"
+                    latin:popupCharacters="@string/alternates_for_punctuation"
+                    latin:maxPopupKeyboardColumn="7"
                     latin:keyStyle="functionalKeyStyle" />
                 <switch>
                     <case
diff --git a/java/res/xml/kbd_symbols_shift.xml b/java/res/xml/kbd_symbols_shift.xml
index ee8d882184..368ee805b1 100644
--- a/java/res/xml/kbd_symbols_shift.xml
+++ b/java/res/xml/kbd_symbols_shift.xml
@@ -26,6 +26,8 @@
     latin:keyWidth="10%p"
     latin:horizontalGap="@dimen/key_horizontal_gap"
     latin:verticalGap="@dimen/key_bottom_gap"
+    latin:popupKeyboardTemplate="@xml/kbd_popup_template"
+    latin:maxPopupKeyboardColumn="@integer/config_max_popup_keyboard_column"
 >
     <include
         latin:keyboardLayout="@xml/kbd_key_styles" />
@@ -41,13 +43,11 @@
             latin:keyLabel="|" />
         <Key
             latin:keyLabel="•"
-            latin:popupKeyboard="@xml/kbd_popup_template"
-            latin:popupCharacters="♪♥♠♦♣" />
+            latin:popupCharacters="♪,♥,♠,♦,♣" />
         <Key
             latin:keyLabel="√" />
         <Key
             latin:keyLabel="Ï€"
-            latin:popupKeyboard="@xml/kbd_popup_template"
             latin:popupCharacters="Π" />
         <Key
             latin:keyLabel="÷" />
@@ -73,14 +73,12 @@
             latin:keyLabel="°" />
         <Key
             latin:keyLabel="^"
-            latin:popupKeyboard="@xml/kbd_popup_template"
-            latin:popupCharacters="↑↓←→" />
+            latin:popupCharacters="↑,↓,←,→" />
         <Key
             latin:keyLabel="_" />
         <Key
             latin:keyLabel="="
-            latin:popupKeyboard="@xml/kbd_popup_template"
-            latin:popupCharacters="≠≈∞" />
+            latin:popupCharacters="≠,≈,∞" />
         <Key
             latin:keyLabel="[" />
         <Key
@@ -100,18 +98,15 @@
             latin:keyLabel="©" />
         <Key
             latin:keyLabel="¶"
-            latin:popupKeyboard="@xml/kbd_popup_template"
             latin:popupCharacters="§" />
         <Key
             latin:keyLabel="\\" />
         <Key
             latin:keyLabel="&lt;"
-            latin:popupKeyboard="@xml/kbd_popup_template"
-            latin:popupCharacters="≤«‹" />
+            latin:popupCharacters="≤,«,‹" />
         <Key
             latin:keyLabel="&gt;"
-            latin:popupKeyboard="@xml/kbd_popup_template"
-            latin:popupCharacters="≥»›" />
+            latin:popupCharacters="≥,»,›" />
         <Key
             latin:keyStyle="deleteKeyStyle"
             latin:keyWidth="15%p"
diff --git a/java/res/xml/popup_at.xml b/java/res/xml/popup_at.xml
deleted file mode 100644
index 92ad816163..0000000000
--- a/java/res/xml/popup_at.xml
+++ /dev/null
@@ -1,39 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-/*
-**
-** Copyright 2010, 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:keyWidth="10%p"
-    latin:horizontalGap="0px"
-    latin:verticalGap="0px"
-    latin:rowHeight="@dimen/popup_key_height"
->
-    <Row
-        latin:rowEdgeFlags="top|bottom"
-    >
-        <Key
-            latin:codes="@integer/key_settings"
-            latin:keyIcon="@drawable/sym_keyboard_settings"
-            latin:keyEdgeFlags="left" />
-        <Key
-            latin:keyLabel="\@"
-            latin:keyEdgeFlags="right" />
-    </Row>
-</Keyboard>
diff --git a/java/res/xml/popup_comma.xml b/java/res/xml/popup_comma.xml
deleted file mode 100644
index 9ef49dfda0..0000000000
--- a/java/res/xml/popup_comma.xml
+++ /dev/null
@@ -1,39 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-/*
-**
-** Copyright 2010, 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:keyWidth="10%p"
-    latin:horizontalGap="0px"
-    latin:verticalGap="0px"
-    latin:rowHeight="@dimen/popup_key_height"
->
-    <Row
-        latin:rowEdgeFlags="top|bottom"
-    >
-        <Key
-            latin:codes="@integer/key_settings"
-            latin:keyIcon="@drawable/sym_keyboard_settings"
-            latin:keyEdgeFlags="left" />
-        <Key
-            latin:keyLabel=","
-            latin:keyEdgeFlags="right" />
-    </Row>
-</Keyboard>
diff --git a/java/res/xml/popup_domains.xml b/java/res/xml/popup_domains.xml
deleted file mode 100644
index 9fbbdec110..0000000000
--- a/java/res/xml/popup_domains.xml
+++ /dev/null
@@ -1,46 +0,0 @@
-<?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 
-**
-**     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:keyWidth="15%p"
-    latin:horizontalGap="0px"
-    latin:verticalGap="0px"
-    latin:rowHeight="@dimen/popup_key_height"
->
-    <Row
-        latin:rowEdgeFlags="top|bottom"
-    >
-        <Key
-            latin:keyLabel="@string/popular_domain_1"
-            latin:keyOutputText="@string/popular_domain_1"
-            latin:keyEdgeFlags="left" />
-        <Key
-            latin:keyLabel="@string/popular_domain_2"
-            latin:keyOutputText="@string/popular_domain_2" />
-        <Key
-            latin:keyLabel="@string/popular_domain_3"
-            latin:keyOutputText="@string/popular_domain_3" />
-        <Key
-            latin:keyLabel="@string/popular_domain_4"
-            latin:keyOutputText="@string/popular_domain_4"
-            latin:keyEdgeFlags="right" />
-    </Row>
-</Keyboard>
diff --git a/java/res/xml/popup_mic.xml b/java/res/xml/popup_mic.xml
deleted file mode 100644
index 1851eba989..0000000000
--- a/java/res/xml/popup_mic.xml
+++ /dev/null
@@ -1,40 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-/*
-**
-** Copyright 2010, 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:keyWidth="10%p"
-    latin:horizontalGap="0px"
-    latin:verticalGap="0px"
-    latin:rowHeight="@dimen/popup_key_height"
->
-    <Row
-        latin:rowEdgeFlags="top|bottom"
-    >
-        <Key
-            latin:codes="@integer/key_settings"
-            latin:keyIcon="@drawable/sym_keyboard_settings"
-            latin:keyEdgeFlags="left" />
-        <Key
-            latin:codes="@integer/key_voice"
-            latin:keyIcon="@drawable/sym_keyboard_mic"
-            latin:keyEdgeFlags="right" />
-    </Row>
-</Keyboard>
diff --git a/java/res/xml/popup_punctuation.xml b/java/res/xml/popup_punctuation.xml
deleted file mode 100644
index 6c778c724a..0000000000
--- a/java/res/xml/popup_punctuation.xml
+++ /dev/null
@@ -1,68 +0,0 @@
-<?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 
-**
-**     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:keyWidth="10%p"
-    latin:horizontalGap="0px"
-    latin:verticalGap="0px"
-    latin:rowHeight="@dimen/popup_key_height"
->
-    <Row
-        latin:rowEdgeFlags="top"
-    >
-        <Key
-            latin:keyLabel=":"
-            latin:keyEdgeFlags="left" />
-        <Key
-            latin:keyLabel="/" />
-        <Key
-            latin:keyLabel="&amp;" />
-        <Key
-            latin:keyLabel="(" />
-        <Key
-            latin:keyLabel=")" />
-        <Key
-            latin:keyLabel="-" />
-        <Key
-            latin:keyLabel="+"
-            latin:keyEdgeFlags="right" />
-    </Row>
-    <Row
-        latin:rowEdgeFlags="bottom"
-    >
-        <Key
-            latin:keyLabel=";"
-            latin:keyEdgeFlags="left" />
-        <Key
-            latin:keyLabel="\@" />
-        <Key
-            latin:keyLabel="\'" />
-        <Key
-            latin:keyLabel="&quot;" />
-        <Key
-            latin:keyLabel="\?" />
-        <Key
-            latin:keyLabel="!" />
-        <Key
-            latin:keyLabel=","
-            latin:keyEdgeFlags="right" />
-    </Row>
-</Keyboard>
diff --git a/java/res/xml/popup_slash.xml b/java/res/xml/popup_slash.xml
deleted file mode 100644
index 611500c9e8..0000000000
--- a/java/res/xml/popup_slash.xml
+++ /dev/null
@@ -1,39 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-/*
-**
-** Copyright 2010, 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:keyWidth="10%p"
-    latin:horizontalGap="0px"
-    latin:verticalGap="0px"
-    latin:rowHeight="@dimen/popup_key_height"
->
-    <Row
-        latin:rowEdgeFlags="top|bottom"
-    >
-        <Key
-            latin:codes="@integer/key_settings"
-            latin:keyIcon="@drawable/sym_keyboard_settings"
-            latin:keyEdgeFlags="left" />
-        <Key
-            latin:keyLabel="/"
-            latin:keyEdgeFlags="right" />
-    </Row>
-</Keyboard>
diff --git a/java/res/xml/popup_smileys.xml b/java/res/xml/popup_smileys.xml
deleted file mode 100644
index b87667222f..0000000000
--- a/java/res/xml/popup_smileys.xml
+++ /dev/null
@@ -1,89 +0,0 @@
-<?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 
-**
-**     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:keyWidth="15%p"
-    latin:horizontalGap="0px"
-    latin:verticalGap="0px"
-    latin:rowHeight="@dimen/popup_key_height"
->
-    <Row
-        latin:rowEdgeFlags="top"
-    >
-        <Key
-            latin:keyLabel=":-)"
-            latin:keyOutputText=":-) "
-            latin:keyEdgeFlags="left" />
-        <Key
-            latin:keyLabel=":-("
-            latin:keyOutputText=":-( " />
-        <Key
-            latin:keyLabel=";-)"
-            latin:keyOutputText=";-) " />
-        <Key
-            latin:keyLabel=":-P"
-            latin:keyOutputText=":-P " />
-        <Key
-            latin:keyLabel="=-O"
-            latin:keyOutputText="=-O "
-            latin:keyEdgeFlags="right" />
-    </Row>
-    <Row>
-        <Key
-            latin:keyLabel=":-*"
-            latin:keyOutputText=":-* "
-            latin:keyEdgeFlags="left" />
-        <Key
-            latin:keyLabel=":O"
-            latin:keyOutputText=":O " />
-        <Key
-            latin:keyLabel="B-)"
-            latin:keyOutputText="B-) " />
-        <Key
-            latin:keyLabel=":-$"
-            latin:keyOutputText=":-$ " />
-        <Key
-            latin:keyLabel=":-!"
-            latin:keyOutputText=":-! "
-            latin:keyEdgeFlags="right" />
-    </Row>
-    <Row
-        latin:rowEdgeFlags="bottom"
-    >
-        <Key
-            latin:keyLabel=":-["
-            latin:keyOutputText=":-[ "
-            latin:keyEdgeFlags="left" />
-        <Key
-            latin:keyLabel="O:-)"
-            latin:keyOutputText="O:-) " />
-        <Key
-            latin:keyLabel=":-\\"
-            latin:keyOutputText=":-\\ " />
-        <Key
-            latin:keyLabel=":'("
-            latin:keyOutputText=":'( " />
-        <Key
-            latin:keyLabel=":-D"
-            latin:keyOutputText=":-D "
-            latin:keyEdgeFlags="right" />
-    </Row>
-</Keyboard>
diff --git a/java/res/xml/prefs.xml b/java/res/xml/prefs.xml
index 47b3b45894..3b20298d27 100644
--- a/java/res/xml/prefs.xml
+++ b/java/res/xml/prefs.xml
@@ -34,7 +34,7 @@
             android:key="popup_on"
             android:title="@string/popup_on_keypress"
             android:persistent="true"
-            android:defaultValue="@bool/default_popup_preview"
+            android:defaultValue="@bool/config_default_popup_preview"
             />
 
     <CheckBoxPreference
diff --git a/java/src/com/android/inputmethod/keyboard/Key.java b/java/src/com/android/inputmethod/keyboard/Key.java
index e74c968ffd..1c165e323d 100644
--- a/java/src/com/android/inputmethod/keyboard/Key.java
+++ b/java/src/com/android/inputmethod/keyboard/Key.java
@@ -16,8 +16,8 @@
 
 package com.android.inputmethod.keyboard;
 
-import com.android.inputmethod.keyboard.KeyboardParser.ParseException;
 import com.android.inputmethod.keyboard.KeyStyles.KeyStyle;
+import com.android.inputmethod.keyboard.KeyboardParser.ParseException;
 import com.android.inputmethod.latin.R;
 
 import android.content.res.Resources;
@@ -71,12 +71,9 @@ public class Key {
     /** Text to output when pressed. This can be multiple characters, like ".com" */
     public final CharSequence mOutputText;
     /** Popup characters */
-    public final CharSequence mPopupCharacters;
-    /**
-     * If this key pops up a mini keyboard, this is the resource id for the XML layout for that
-     * keyboard.
-     */
-    public final int mPopupResId;
+    public final CharSequence[] mPopupCharacters;
+    /** Popup keyboard maximum column number */
+    public final int mMaxPopupColumn;
 
     /**
      * Flags that specify the anchoring to edges of the keyboard for detecting touch events
@@ -136,8 +133,13 @@ public class Key {
             android.R.attr.state_pressed
     };
 
-    /** Create an empty key with no attributes. */
-    public Key(Row row, char letter, int x, int y) {
+    private static final int[] DUMMY_CODES = { 0 };
+
+    /**
+     * Create an empty key with no attributes.
+     * This constructor is being used only for key in mini popup keyboard.
+     */
+    public Key(Resources res, Row row, CharSequence popupCharacter, int x, int y) {
         mKeyboard = row.getKeyboard();
         mHeight = row.mDefaultHeight - row.mVerticalGap;
         mGap = row.mDefaultHorizontalGap;
@@ -150,18 +152,21 @@ public class Key {
         mModifier = false;
         mSticky = false;
         mRepeatable = false;
-        mOutputText = null;
         mPopupCharacters = null;
-        mPopupResId = 0;
-        mLabel = String.valueOf(letter);
-        mCodes = new int[] { letter };
+        mMaxPopupColumn = 0;
+        final String popupSpecification = popupCharacter.toString();
+        mLabel = PopupCharactersParser.getLabel(popupSpecification);
+        mOutputText = PopupCharactersParser.getOutputText(popupSpecification);
+        mCodes = PopupCharactersParser.getCodes(res, popupSpecification);
+        mIcon = PopupCharactersParser.getIcon(res, popupSpecification);
         // Horizontal gap is divided equally to both sides of the key.
         mX = x + mGap / 2;
         mY = y;
     }
 
-    /** Create a key with the given top-left coordinate and extract its attributes from
-     * the XML parser.
+    /**
+     * Create a key with the given top-left coordinate and extract its attributes from the XML
+     * parser.
      * @param res resources associated with the caller's context
      * @param row the row that this key belongs to. The row must already be attached to
      * a {@link Keyboard}.
@@ -173,83 +178,84 @@ public class Key {
             KeyStyles keyStyles) {
         mKeyboard = row.getKeyboard();
 
-        TypedArray a = res.obtainAttributes(Xml.asAttributeSet(parser),
+        final TypedArray keyboardAttr = res.obtainAttributes(Xml.asAttributeSet(parser),
                 R.styleable.Keyboard);
-        mHeight = KeyboardParser.getDimensionOrFraction(a,
-                R.styleable.Keyboard_rowHeight,
-                mKeyboard.getKeyboardHeight(), row.mDefaultHeight) - row.mVerticalGap;
-        mGap = KeyboardParser.getDimensionOrFraction(a,
-                R.styleable.Keyboard_horizontalGap,
-                mKeyboard.getDisplayWidth(), row.mDefaultHorizontalGap);
-        mWidth = KeyboardParser.getDimensionOrFraction(a,
-                R.styleable.Keyboard_keyWidth,
-                mKeyboard.getDisplayWidth(), row.mDefaultWidth) - mGap;
-        a.recycle();
-
-        a = res.obtainAttributes(Xml.asAttributeSet(parser), R.styleable.Keyboard_Key);
-
-        final KeyStyle style;
-        if (a.hasValue(R.styleable.Keyboard_Key_keyStyle)) {
-            String styleName = a.getString(R.styleable.Keyboard_Key_keyStyle);
-            style = keyStyles.getKeyStyle(styleName);
-            if (style == null)
-                throw new ParseException("Unknown key style: " + styleName, parser);
-        } else {
-            style = keyStyles.getEmptyKeyStyle();
+        try {
+            mHeight = KeyboardParser.getDimensionOrFraction(keyboardAttr,
+                    R.styleable.Keyboard_rowHeight,
+                    mKeyboard.getKeyboardHeight(), row.mDefaultHeight) - row.mVerticalGap;
+            mGap = KeyboardParser.getDimensionOrFraction(keyboardAttr,
+                    R.styleable.Keyboard_horizontalGap,
+                    mKeyboard.getDisplayWidth(), row.mDefaultHorizontalGap);
+            mWidth = KeyboardParser.getDimensionOrFraction(keyboardAttr,
+                    R.styleable.Keyboard_keyWidth,
+                    mKeyboard.getDisplayWidth(), row.mDefaultWidth) - mGap;
+        } finally {
+            keyboardAttr.recycle();
         }
 
         // Horizontal gap is divided equally to both sides of the key.
         this.mX = x + mGap / 2;
         this.mY = y;
 
-        final CharSequence popupCharacters = style.getText(a,
-                R.styleable.Keyboard_Key_popupCharacters);
-        final int popupResId = style.getResourceId(a, R.styleable.Keyboard_Key_popupKeyboard, 0);
-        // We should not display mini keyboard when both popupResId and popupCharacters are
-        // specified but popupCharacters is empty string.
-        if (popupResId != 0 && popupCharacters != null && popupCharacters.length() == 0) {
-            mPopupResId = 0;
-            mPopupCharacters = null;
-        } else {
-            mPopupResId = popupResId;
-            mPopupCharacters = popupCharacters;
-        }
-
-        mRepeatable = style.getBoolean(a, R.styleable.Keyboard_Key_isRepeatable, false);
-        mModifier = style.getBoolean(a, R.styleable.Keyboard_Key_isModifier, false);
-        mSticky = style.getBoolean(a, R.styleable.Keyboard_Key_isSticky, false);
-        mEdgeFlags = style.getFlag(a, R.styleable.Keyboard_Key_keyEdgeFlags, 0)
-                | row.mRowEdgeFlags;
-
-        mPreviewIcon = style.getDrawable(a, R.styleable.Keyboard_Key_iconPreview);
-        Keyboard.setDefaultBounds(mPreviewIcon);
-        mIcon = style.getDrawable(a, R.styleable.Keyboard_Key_keyIcon);
-        Keyboard.setDefaultBounds(mIcon);
-        mHintIcon = style.getDrawable(a, R.styleable.Keyboard_Key_keyHintIcon);
-        Keyboard.setDefaultBounds(mHintIcon);
-        mManualTemporaryUpperCaseHintIcon = style.getDrawable(a,
-                R.styleable.Keyboard_Key_manualTemporaryUpperCaseHintIcon);
-        Keyboard.setDefaultBounds(mManualTemporaryUpperCaseHintIcon);
-
-        mLabel = style.getText(a, R.styleable.Keyboard_Key_keyLabel);
-        mLabelOption = style.getFlag(a, R.styleable.Keyboard_Key_keyLabelOption, 0);
-        mManualTemporaryUpperCaseCode = style.getInt(a,
-                R.styleable.Keyboard_Key_manualTemporaryUpperCaseCode, 0);
-        mOutputText = style.getText(a, R.styleable.Keyboard_Key_keyOutputText);
-        // Choose the first letter of the label as primary code if not specified.
-        final int[] codes = style.getIntArray(a, R.styleable.Keyboard_Key_codes);
-        if (codes == null && !TextUtils.isEmpty(mLabel)) {
-            mCodes = new int[] { mLabel.charAt(0) };
-        } else {
-            mCodes = codes;
-        }
+        final TypedArray keyAttr = res.obtainAttributes(Xml.asAttributeSet(parser),
+                R.styleable.Keyboard_Key);
+        try {
+            final KeyStyle style;
+            if (keyAttr.hasValue(R.styleable.Keyboard_Key_keyStyle)) {
+                String styleName = keyAttr.getString(R.styleable.Keyboard_Key_keyStyle);
+                style = keyStyles.getKeyStyle(styleName);
+                if (style == null)
+                    throw new ParseException("Unknown key style: " + styleName, parser);
+            } else {
+                style = keyStyles.getEmptyKeyStyle();
+            }
 
-        final Drawable shiftedIcon = style.getDrawable(a,
-                R.styleable.Keyboard_Key_shiftedIcon);
-        if (shiftedIcon != null)
-            mKeyboard.getShiftedIcons().put(this, shiftedIcon);
+            mPopupCharacters = style.getTextArray(keyAttr,
+                    R.styleable.Keyboard_Key_popupCharacters);
+            mMaxPopupColumn = style.getInt(keyboardAttr,
+                    R.styleable.Keyboard_Key_maxPopupKeyboardColumn,
+                    mKeyboard.getMaxPopupKeyboardColumn());
+
+            mRepeatable = style.getBoolean(keyAttr, R.styleable.Keyboard_Key_isRepeatable, false);
+            mModifier = style.getBoolean(keyAttr, R.styleable.Keyboard_Key_isModifier, false);
+            mSticky = style.getBoolean(keyAttr, R.styleable.Keyboard_Key_isSticky, false);
+            mEdgeFlags = style.getFlag(keyAttr, R.styleable.Keyboard_Key_keyEdgeFlags, 0)
+                    | row.mRowEdgeFlags;
+
+            mPreviewIcon = style.getDrawable(keyAttr, R.styleable.Keyboard_Key_iconPreview);
+            Keyboard.setDefaultBounds(mPreviewIcon);
+            mIcon = style.getDrawable(keyAttr, R.styleable.Keyboard_Key_keyIcon);
+            Keyboard.setDefaultBounds(mIcon);
+            mHintIcon = style.getDrawable(keyAttr, R.styleable.Keyboard_Key_keyHintIcon);
+            Keyboard.setDefaultBounds(mHintIcon);
+            mManualTemporaryUpperCaseHintIcon = style.getDrawable(keyAttr,
+                    R.styleable.Keyboard_Key_manualTemporaryUpperCaseHintIcon);
+            Keyboard.setDefaultBounds(mManualTemporaryUpperCaseHintIcon);
+
+            mLabel = style.getText(keyAttr, R.styleable.Keyboard_Key_keyLabel);
+            mLabelOption = style.getFlag(keyAttr, R.styleable.Keyboard_Key_keyLabelOption, 0);
+            mManualTemporaryUpperCaseCode = style.getInt(keyAttr,
+                    R.styleable.Keyboard_Key_manualTemporaryUpperCaseCode, 0);
+            mOutputText = style.getText(keyAttr, R.styleable.Keyboard_Key_keyOutputText);
+            // Choose the first letter of the label as primary code if not
+            // specified.
+            final int[] codes = style.getIntArray(keyAttr, R.styleable.Keyboard_Key_codes);
+            if (codes == null && !TextUtils.isEmpty(mLabel)) {
+                mCodes = new int[] { mLabel.charAt(0) };
+            } else if (codes != null) {
+                mCodes = codes;
+            } else {
+                mCodes = DUMMY_CODES;
+            }
 
-        a.recycle();
+            final Drawable shiftedIcon = style.getDrawable(keyAttr,
+                    R.styleable.Keyboard_Key_shiftedIcon);
+            if (shiftedIcon != null)
+                mKeyboard.getShiftedIcons().put(this, shiftedIcon);
+        } finally {
+            keyAttr.recycle();
+        }
     }
 
     public Drawable getIcon() {
diff --git a/java/src/com/android/inputmethod/keyboard/KeyStyles.java b/java/src/com/android/inputmethod/keyboard/KeyStyles.java
index e3b1071608..9e87816bcc 100644
--- a/java/src/com/android/inputmethod/keyboard/KeyStyles.java
+++ b/java/src/com/android/inputmethod/keyboard/KeyStyles.java
@@ -25,11 +25,13 @@ import android.graphics.drawable.Drawable;
 import android.util.Log;
 import android.util.TypedValue;
 
+import java.util.ArrayList;
 import java.util.HashMap;
 import java.util.StringTokenizer;
 
 public class KeyStyles {
     private static final String TAG = "KeyStyles";
+    private static final boolean DEBUG = false;
 
     private final HashMap<String, DeclaredKeyStyle> mStyles =
             new HashMap<String, DeclaredKeyStyle>();
@@ -37,15 +39,15 @@ public class KeyStyles {
 
     public interface KeyStyle {
         public int[] getIntArray(TypedArray a, int index);
+        public CharSequence[] getTextArray(TypedArray a, int index);
         public Drawable getDrawable(TypedArray a, int index);
         public CharSequence getText(TypedArray a, int index);
-        public int getResourceId(TypedArray a, int index, int defaultValue);
         public int getInt(TypedArray a, int index, int defaultValue);
         public int getFlag(TypedArray a, int index, int defaultValue);
         public boolean getBoolean(TypedArray a, int index, boolean defaultValue);
     }
 
-    public static class EmptyKeyStyle implements KeyStyle {
+    /* package */ static class EmptyKeyStyle implements KeyStyle {
         private EmptyKeyStyle() {
             // Nothing to do.
         }
@@ -55,6 +57,11 @@ public class KeyStyles {
             return parseIntArray(a, index);
         }
 
+        @Override
+        public CharSequence[] getTextArray(TypedArray a, int index) {
+            return parseTextArray(a, index);
+        }
+
         @Override
         public Drawable getDrawable(TypedArray a, int index) {
             return a.getDrawable(index);
@@ -65,11 +72,6 @@ public class KeyStyles {
             return a.getText(index);
         }
 
-        @Override
-        public int getResourceId(TypedArray a, int index, int defaultValue) {
-            return a.getResourceId(index, defaultValue);
-        }
-
         @Override
         public int getInt(TypedArray a, int index, int defaultValue) {
             return a.getInt(index, defaultValue);
@@ -85,19 +87,71 @@ public class KeyStyles {
             return a.getBoolean(index, defaultValue);
         }
 
+        protected static CharSequence[] parseTextArray(TypedArray a, int index) {
+            if (!a.hasValue(index))
+                return null;
+            final CharSequence text = a.getText(index);
+            return parseCsvText(text);
+        }
+
+        /* package */ static CharSequence[] parseCsvText(CharSequence text) {
+            final int size = text.length();
+            if (size == 0) return null;
+            if (size == 1) return new CharSequence[] { text };
+            final StringBuilder sb = new StringBuilder();
+            ArrayList<CharSequence> list = null;
+            int start = 0;
+            for (int pos = 0; pos < size; pos++) {
+                final char c = text.charAt(pos);
+                if (c == ',') {
+                    if (list == null) list = new ArrayList<CharSequence>();
+                    if (sb.length() == 0) {
+                        list.add(text.subSequence(start, pos));
+                    } else {
+                        list.add(sb.toString());
+                        sb.setLength(0);
+                    }
+                    start = pos + 1;
+                    continue;
+                } else if (c == '\\') {
+                    if (start == pos) {
+                        // Skip escape character at the beginning of the value.
+                        start++;
+                        pos++;
+                    } else {
+                        if (start < pos && sb.length() == 0)
+                            sb.append(text.subSequence(start, pos));
+                        pos++;
+                        if (pos < size)
+                            sb.append(text.charAt(pos));
+                    }
+                } else if (sb.length() > 0) {
+                    sb.append(c);
+                }
+            }
+            if (list == null) {
+                return new CharSequence[] { sb.length() > 0 ? sb : text.subSequence(start, size) };
+            } else {
+                list.add(sb.length() > 0 ? sb : text.subSequence(start, size));
+                return list.toArray(new CharSequence[list.size()]);
+            }
+        }
+
         protected static int[] parseIntArray(TypedArray a, int index) {
+            if (!a.hasValue(index))
+                return null;
             TypedValue v = new TypedValue();
             a.getValue(index, v);
             if (v.type == TypedValue.TYPE_INT_DEC || v.type == TypedValue.TYPE_INT_HEX) {
                 return new int[] { v.data };
             } else if (v.type == TypedValue.TYPE_STRING) {
-                return parseCSV(v.string.toString());
+                return parseCsvInt(v.string.toString());
             } else {
                 return null;
             }
         }
 
-        private static int[] parseCSV(String value) {
+        /* package */ static int[] parseCsvInt(String value) {
             int count = 0;
             int lastIndex = 0;
             if (value.length() > 0) {
@@ -110,17 +164,13 @@ public class KeyStyles {
             count = 0;
             StringTokenizer st = new StringTokenizer(value, ",");
             while (st.hasMoreTokens()) {
-                try {
-                    values[count++] = Integer.parseInt(st.nextToken());
-                } catch (NumberFormatException nfe) {
-                    Log.w(TAG, "Error parsing integer CSV " + value);
-                }
+                values[count++] = Integer.parseInt(st.nextToken());
             }
             return values;
         }
     }
 
-    public static class DeclaredKeyStyle extends EmptyKeyStyle {
+    private static class DeclaredKeyStyle extends EmptyKeyStyle {
         private final HashMap<Integer, Object> mAttributes = new HashMap<Integer, Object>();
 
         @Override
@@ -129,6 +179,12 @@ public class KeyStyles {
                     ? super.getIntArray(a, index) : (int[])mAttributes.get(index);
         }
 
+        @Override
+        public CharSequence[] getTextArray(TypedArray a, int index) {
+            return a.hasValue(index)
+                    ? super.getTextArray(a, index) : (CharSequence[])mAttributes.get(index);
+        }
+
         @Override
         public Drawable getDrawable(TypedArray a, int index) {
             return a.hasValue(index)
@@ -142,9 +198,9 @@ public class KeyStyles {
         }
 
         @Override
-        public int getResourceId(TypedArray a, int index, int defaultValue) {
+        public int getInt(TypedArray a, int index, int defaultValue) {
             final Integer value = (Integer)mAttributes.get(index);
-            return super.getResourceId(a, index, (value != null) ? value : defaultValue);
+            return super.getInt(a, index, (value != null) ? value : defaultValue);
         }
 
         @Override
@@ -163,20 +219,21 @@ public class KeyStyles {
             super();
         }
 
-        private void parseKeyStyleAttributes(TypedArray a) {
+        private void parseKeyStyleAttributes(TypedArray keyAttr) {
             // TODO: Currently not all Key attributes can be declared as style.
-            readIntArray(a, R.styleable.Keyboard_Key_codes);
-            readText(a, R.styleable.Keyboard_Key_keyLabel);
-            readFlag(a, R.styleable.Keyboard_Key_keyLabelOption);
-            readText(a, R.styleable.Keyboard_Key_keyOutputText);
-            readDrawable(a, R.styleable.Keyboard_Key_keyIcon);
-            readDrawable(a, R.styleable.Keyboard_Key_iconPreview);
-            readDrawable(a, R.styleable.Keyboard_Key_keyHintIcon);
-            readDrawable(a, R.styleable.Keyboard_Key_shiftedIcon);
-            readResourceId(a, R.styleable.Keyboard_Key_popupKeyboard);
-            readBoolean(a, R.styleable.Keyboard_Key_isModifier);
-            readBoolean(a, R.styleable.Keyboard_Key_isSticky);
-            readBoolean(a, R.styleable.Keyboard_Key_isRepeatable);
+            readIntArray(keyAttr, R.styleable.Keyboard_Key_codes);
+            readText(keyAttr, R.styleable.Keyboard_Key_keyLabel);
+            readFlag(keyAttr, R.styleable.Keyboard_Key_keyLabelOption);
+            readTextArray(keyAttr, R.styleable.Keyboard_Key_popupCharacters);
+            readInt(keyAttr, R.styleable.Keyboard_Key_maxPopupKeyboardColumn);
+            readText(keyAttr, R.styleable.Keyboard_Key_keyOutputText);
+            readDrawable(keyAttr, R.styleable.Keyboard_Key_keyIcon);
+            readDrawable(keyAttr, R.styleable.Keyboard_Key_iconPreview);
+            readDrawable(keyAttr, R.styleable.Keyboard_Key_keyHintIcon);
+            readDrawable(keyAttr, R.styleable.Keyboard_Key_shiftedIcon);
+            readBoolean(keyAttr, R.styleable.Keyboard_Key_isModifier);
+            readBoolean(keyAttr, R.styleable.Keyboard_Key_isSticky);
+            readBoolean(keyAttr, R.styleable.Keyboard_Key_isRepeatable);
         }
 
         private void readDrawable(TypedArray a, int index) {
@@ -189,9 +246,9 @@ public class KeyStyles {
                 mAttributes.put(index, a.getText(index));
         }
 
-        private void readResourceId(TypedArray a, int index) {
+        private void readInt(TypedArray a, int index) {
             if (a.hasValue(index))
-                mAttributes.put(index, a.getResourceId(index, 0));
+                mAttributes.put(index, a.getInt(index, 0));
         }
 
         private void readFlag(TypedArray a, int index) {
@@ -206,11 +263,15 @@ public class KeyStyles {
         }
 
         private void readIntArray(TypedArray a, int index) {
-            if (a.hasValue(index)) {
-                final int[] value = parseIntArray(a, index);
-                if (value != null)
-                    mAttributes.put(index, value);
-            }
+            final int[] value = parseIntArray(a, index);
+            if (value != null)
+                mAttributes.put(index, value);
+        }
+
+        private void readTextArray(TypedArray a, int index) {
+            final CharSequence[] value = parseTextArray(a, index);
+            if (value != null)
+                mAttributes.put(index, value);
         }
 
         private void addParent(DeclaredKeyStyle parentStyle) {
@@ -218,15 +279,17 @@ public class KeyStyles {
         }
     }
 
-    public void parseKeyStyleAttributes(TypedArray a, TypedArray keyAttrs,
+    public void parseKeyStyleAttributes(TypedArray keyStyleAttr, TypedArray keyAttrs,
             XmlResourceParser parser) {
-        String styleName = a.getString(R.styleable.Keyboard_KeyStyle_styleName);
+        String styleName = keyStyleAttr.getString(R.styleable.Keyboard_KeyStyle_styleName);
+        if (DEBUG) Log.d(TAG, String.format("<%s styleName=%s />",
+                KeyboardParser.TAG_KEY_STYLE, styleName));
         if (mStyles.containsKey(styleName))
             throw new ParseException("duplicate key style declared: " + styleName, parser);
 
         final DeclaredKeyStyle style = new DeclaredKeyStyle();
-        if (a.hasValue(R.styleable.Keyboard_KeyStyle_parentStyle)) {
-            String parentStyle = a.getString(
+        if (keyStyleAttr.hasValue(R.styleable.Keyboard_KeyStyle_parentStyle)) {
+            String parentStyle = keyStyleAttr.getString(
                     R.styleable.Keyboard_KeyStyle_parentStyle);
             final DeclaredKeyStyle parent = mStyles.get(parentStyle);
             if (parent == null)
diff --git a/java/src/com/android/inputmethod/keyboard/Keyboard.java b/java/src/com/android/inputmethod/keyboard/Keyboard.java
index 7641b4d7ae..a20c861420 100644
--- a/java/src/com/android/inputmethod/keyboard/Keyboard.java
+++ b/java/src/com/android/inputmethod/keyboard/Keyboard.java
@@ -22,7 +22,6 @@ import org.xmlpull.v1.XmlPullParserException;
 
 import android.content.Context;
 import android.content.res.Resources;
-import android.content.res.XmlResourceParser;
 import android.graphics.drawable.Drawable;
 import android.util.Log;
 
@@ -92,6 +91,12 @@ public class Keyboard {
     /** Default gap between rows */
     private int mDefaultVerticalGap;
 
+    /** Popup keyboard template */
+    private int mPopupKeyboardResId;
+
+    /** Maximum column for popup keyboard */
+    private int mMaxPopupColumn;
+
     /** 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>();
@@ -108,10 +113,10 @@ public class Keyboard {
     private int mTotalHeight;
 
     /**
-     * Total width of the keyboard, including left side gaps and keys, but not any gaps on the
-     * right side.
+     * Total width (minimum width) of the keyboard, including left side gaps and keys, but not any
+     * gaps on the right side.
      */
-    private int mTotalWidth;
+    private int mMinWidth;
 
     /** List of keys in this keyboard */
     private final List<Key> mKeys = new ArrayList<Key>();
@@ -140,31 +145,13 @@ public class Keyboard {
     /** Number of key widths from current touch point to search for nearest keys. */
     private static float SEARCH_DISTANCE = 1.2f;
 
-    /**
-     * Creates a keyboard from the given xml key layout file.
-     * @param context the application or service context
-     * @param xmlLayoutResId the resource file that contains the keyboard layout and keys.
-     */
-    public Keyboard(Context context, int xmlLayoutResId) {
-        this(context, xmlLayoutResId, null);
-    }
-
-    /**
-     * Creates a keyboard from the given keyboard identifier.
-     * @param context the application or service context
-     * @param id keyboard identifier
-     */
-    public Keyboard(Context context, KeyboardId id) {
-        this(context, id.getXmlId(), id);
-    }
-
     /**
      * Creates a keyboard from the given xml key layout file.
      * @param context the application or service context
      * @param xmlLayoutResId the resource file that contains the keyboard layout and keys.
      * @param id keyboard identifier
      */
-    private Keyboard(Context context, int xmlLayoutResId, KeyboardId id) {
+    public Keyboard(Context context, int xmlLayoutResId, KeyboardId id) {
         this(context, xmlLayoutResId, id,
                 context.getResources().getDisplayMetrics().widthPixels,
                 context.getResources().getDisplayMetrics().heightPixels);
@@ -188,49 +175,6 @@ public class Keyboard {
         loadKeyboard(context, xmlLayoutResId);
     }
 
-    /**
-     * <p>Creates a blank keyboard from the given resource file and populates it with the specified
-     * characters in left-to-right, top-to-bottom fashion, using the specified number of columns.
-     * </p>
-     * <p>If the specified number of columns is -1, then the keyboard will fit as many keys as
-     * possible in each row.</p>
-     * @param context the application or service context
-     * @param layoutTemplateResId the layout template file, containing no keys.
-     * @param characters the list of characters to display on the keyboard. One key will be created
-     * for each character.
-     * @param columns the number of columns of keys to display. If this number is greater than the
-     * number of keys that can fit in a row, it will be ignored. If this number is -1, the
-     * keyboard will fit as many keys as possible in each row.
-     */
-    public Keyboard(Context context, int layoutTemplateResId,
-            CharSequence characters, int columns, int horizontalPadding) {
-        this(context, layoutTemplateResId);
-        int x = 0;
-        int y = 0;
-        int column = 0;
-        mTotalWidth = 0;
-
-        final Row row = new Row(this);
-        final int maxColumns = columns == -1 ? Integer.MAX_VALUE : columns;
-        for (int i = 0; i < characters.length(); i++) {
-            char c = characters.charAt(i);
-            if (column >= maxColumns
-                    || x + mDefaultWidth + horizontalPadding > mDisplayWidth) {
-                x = 0;
-                y += mDefaultVerticalGap + mDefaultHeight;
-                column = 0;
-            }
-            final Key key = new Key(row, c, x, y);
-            column++;
-            x += key.mWidth + key.mGap;
-            mKeys.add(key);
-            if (x > mTotalWidth) {
-                mTotalWidth = x;
-            }
-        }
-        mTotalHeight = y + mDefaultHeight;
-    }
-
     public List<Key> getKeys() {
         return mKeys;
     }
@@ -277,8 +221,16 @@ public class Keyboard {
         return mTotalHeight;
     }
 
+    public void setHeight(int height) {
+        mTotalHeight = height;
+    }
+
     public int getMinWidth() {
-        return mTotalWidth;
+        return mMinWidth;
+    }
+
+    public void setMinWidth(int minWidth) {
+        mMinWidth = minWidth;
     }
 
     public int getDisplayHeight() {
@@ -297,6 +249,22 @@ public class Keyboard {
         mKeyboardHeight = height;
     }
 
+    public int getPopupKeyboardResId() {
+        return mPopupKeyboardResId;
+    }
+
+    public void setPopupKeyboardResId(int resId) {
+        mPopupKeyboardResId = resId;
+    }
+
+    public int getMaxPopupKeyboardColumn() {
+        return mMaxPopupColumn;
+    }
+
+    public void setMaxPopupKeyboardColumn(int column) {
+        mMaxPopupColumn = column;
+    }
+
     public List<Key> getShiftKeys() {
         return mShiftKeys;
     }
@@ -429,24 +397,12 @@ public class Keyboard {
         return EMPTY_INT_ARRAY;
     }
 
-    // TODO should be private
-    protected Row createRowFromXml(Resources res, XmlResourceParser parser) {
-        return new Row(res, this, parser);
-    }
-
-    // TODO should be private
-    protected Key createKeyFromXml(Resources res, Row parent, int x, int y,
-            XmlResourceParser parser, KeyStyles keyStyles) {
-        return new Key(res, parent, x, y, parser, keyStyles);
-    }
-
     private void loadKeyboard(Context context, int xmlLayoutResId) {
         try {
-            final Resources res = context.getResources();
-            KeyboardParser parser = new KeyboardParser(this, res);
-            parser.parseKeyboard(res.getXml(xmlLayoutResId));
-            // mTotalWidth is the width of this keyboard which is maximum width of row.
-            mTotalWidth = parser.getMaxRowWidth();
+            KeyboardParser parser = new KeyboardParser(this, context.getResources());
+            parser.parseKeyboard(xmlLayoutResId);
+            // mMinWidth is the width of this keyboard which is maximum width of row.
+            mMinWidth = parser.getMaxRowWidth();
             mTotalHeight = parser.getTotalHeight();
         } catch (XmlPullParserException e) {
             Log.w(TAG, "keyboard XML parse error: " + e);
diff --git a/java/src/com/android/inputmethod/keyboard/KeyboardId.java b/java/src/com/android/inputmethod/keyboard/KeyboardId.java
index 883d2175c7..f6577e7470 100644
--- a/java/src/com/android/inputmethod/keyboard/KeyboardId.java
+++ b/java/src/com/android/inputmethod/keyboard/KeyboardId.java
@@ -46,12 +46,13 @@ public class KeyboardId {
     public final boolean mHasVoiceKey;
     public final int mImeOptions;
     public final boolean mEnableShiftLock;
+    public final String mXmlName;
 
     private final int mHashCode;
 
-    public KeyboardId(Locale locale, int orientation, int mode,
-            int xmlId, int colorScheme, boolean hasSettingsKey, boolean voiceKeyEnabled,
-            boolean hasVoiceKey, int imeOptions, boolean enableShiftLock) {
+    public KeyboardId(String xmlName, int xmlId, Locale locale, int orientation, int mode,
+            int colorScheme, boolean hasSettingsKey, boolean voiceKeyEnabled, boolean hasVoiceKey,
+            int imeOptions, boolean enableShiftLock) {
         this.mLocale = locale;
         this.mOrientation = orientation;
         this.mMode = mode;
@@ -64,6 +65,7 @@ public class KeyboardId {
         this.mImeOptions = imeOptions
                 & (EditorInfo.IME_MASK_ACTION | EditorInfo.IME_FLAG_NO_ENTER_ACTION);
         this.mEnableShiftLock = enableShiftLock;
+        this.mXmlName = xmlName;
 
         this.mHashCode = Arrays.hashCode(new Object[] {
                 locale,
@@ -120,12 +122,12 @@ public class KeyboardId {
 
     @Override
     public String toString() {
-        return String.format("[%s %s %5s imeOptions=0x%08x xml=0x%08x %s%s%s%s%s]",
+        return String.format("[%s.xml %s %s %s imeOptions=%s %s%s%s%s%s]",
+                mXmlName,
                 mLocale,
                 (mOrientation == 1 ? "port" : "land"),
                 modeName(mMode),
-                mImeOptions,
-                mXmlId,
+                imeOptionsName(mImeOptions),
                 colorSchemeName(mColorScheme),
                 (mHasSettingsKey ? " hasSettingsKey" : ""),
                 (mVoiceKeyEnabled ? " voiceKeyEnabled" : ""),
@@ -133,7 +135,7 @@ public class KeyboardId {
                 (mEnableShiftLock ? " enableShiftLock" : ""));
     }
 
-    private static String modeName(int mode) {
+    public static String modeName(int mode) {
         switch (mode) {
         case MODE_TEXT: return "text";
         case MODE_URL: return "url";
@@ -146,11 +148,33 @@ public class KeyboardId {
         return null;
     }
 
-    private static String colorSchemeName(int colorScheme) {
+    public static String colorSchemeName(int colorScheme) {
         switch (colorScheme) {
         case KeyboardView.COLOR_SCHEME_WHITE: return "white";
         case KeyboardView.COLOR_SCHEME_BLACK: return "black";
         }
         return null;
     }
+
+    public static String imeOptionsName(int imeOptions) {
+        if (imeOptions == -1) return null;
+        final int actionNo = imeOptions & EditorInfo.IME_MASK_ACTION;
+        final String action;
+        switch (actionNo) {
+        case EditorInfo.IME_ACTION_UNSPECIFIED: action = "actionUnspecified"; break;
+        case EditorInfo.IME_ACTION_NONE: action = "actionNone"; break;
+        case EditorInfo.IME_ACTION_GO: action = "actionGo"; break;
+        case EditorInfo.IME_ACTION_SEARCH: action = "actionSearch"; break;
+        case EditorInfo.IME_ACTION_SEND: action = "actionSend"; break;
+        case EditorInfo.IME_ACTION_DONE: action = "actionDone"; break;
+        case EditorInfo.IME_ACTION_PREVIOUS: action = "actionPrevious"; break;
+        default: action = "actionUnknown(" + actionNo + ")"; break;
+        }
+        if ((imeOptions & EditorInfo.IME_FLAG_NO_ENTER_ACTION) != 0) {
+            return "flagNoEnterAction|" + action;
+        } else {
+            return action;
+        }
+    }
 }
+
diff --git a/java/src/com/android/inputmethod/keyboard/KeyboardParser.java b/java/src/com/android/inputmethod/keyboard/KeyboardParser.java
index 8a7d674510..ed59b8be54 100644
--- a/java/src/com/android/inputmethod/keyboard/KeyboardParser.java
+++ b/java/src/com/android/inputmethod/keyboard/KeyboardParser.java
@@ -30,6 +30,7 @@ import android.util.Xml;
 import android.view.InflateException;
 
 import java.io.IOException;
+import java.util.Arrays;
 import java.util.List;
 
 /**
@@ -103,7 +104,7 @@ import java.util.List;
 
 public class KeyboardParser {
     private static final String TAG = "KeyboardParser";
-    private static final boolean DEBUG_TAG = false;
+    private static final boolean DEBUG = false;
 
     // Keyboard XML Tags
     private static final String TAG_KEYBOARD = "Keyboard";
@@ -115,7 +116,7 @@ public class KeyboardParser {
     private static final String TAG_SWITCH = "switch";
     private static final String TAG_CASE = "case";
     private static final String TAG_DEFAULT = "default";
-    private static final String TAG_KEY_STYLE = "key-style";
+    public static final String TAG_KEY_STYLE = "key-style";
 
     private final Keyboard mKeyboard;
     private final Resources mResources;
@@ -140,13 +141,13 @@ public class KeyboardParser {
         return mTotalHeight;
     }
 
-    public void parseKeyboard(XmlResourceParser parser)
-            throws XmlPullParserException, IOException {
+    public void parseKeyboard(int resId) throws XmlPullParserException, IOException {
+        if (DEBUG) Log.d(TAG, String.format("<%s> %s", TAG_KEYBOARD, mKeyboard.mId));
+        final XmlResourceParser parser = mResources.getXml(resId);
         int event;
         while ((event = parser.next()) != XmlPullParser.END_DOCUMENT) {
             if (event == XmlPullParser.START_TAG) {
                 final String tag = parser.getName();
-                if (DEBUG_TAG) debugStartTag("parseKeyboard", tag, false);
                 if (TAG_KEYBOARD.equals(tag)) {
                     parseKeyboardAttributes(parser);
                     parseKeyboardContent(parser, mKeyboard.getKeys());
@@ -160,28 +161,38 @@ public class KeyboardParser {
 
     private void parseKeyboardAttributes(XmlResourceParser parser) {
         final Keyboard keyboard = mKeyboard;
-        final TypedArray a = mResources.obtainAttributes(Xml.asAttributeSet(parser),
+        final TypedArray keyboardAttr = mResources.obtainAttributes(Xml.asAttributeSet(parser),
                 R.styleable.Keyboard);
-        final int displayHeight = keyboard.getDisplayHeight();
-        final int keyboardHeight = (int)a.getDimension(
-                R.styleable.Keyboard_keyboardHeight, displayHeight / 2);
-        final int maxKeyboardHeight = getDimensionOrFraction(a,
-                R.styleable.Keyboard_maxKeyboardHeight, displayHeight,  displayHeight / 2);
-        // Keyboard height will not exceed maxKeyboardHeight.
-        final int height = Math.min(keyboardHeight, maxKeyboardHeight);
-        final int width = keyboard.getDisplayWidth();
-
-        keyboard.setKeyboardHeight(height);
-        keyboard.setKeyWidth(getDimensionOrFraction(a,
-                R.styleable.Keyboard_keyWidth, width, width / 10));
-        keyboard.setRowHeight(getDimensionOrFraction(a,
-                R.styleable.Keyboard_rowHeight, height, 50));
-        keyboard.setHorizontalGap(getDimensionOrFraction(a,
-                R.styleable.Keyboard_horizontalGap, width, 0));
-        keyboard.setVerticalGap(getDimensionOrFraction(a,
-                R.styleable.Keyboard_verticalGap, height, 0));
-        a.recycle();
-        if (DEBUG_TAG) Log.d(TAG, "id=" + keyboard.mId);
+        final TypedArray keyAttr = mResources.obtainAttributes(Xml.asAttributeSet(parser),
+                R.styleable.Keyboard_Key);
+        try {
+            final int displayHeight = keyboard.getDisplayHeight();
+            final int keyboardHeight = (int)keyboardAttr.getDimension(
+                    R.styleable.Keyboard_keyboardHeight, displayHeight / 2);
+            final int maxKeyboardHeight = getDimensionOrFraction(keyboardAttr,
+                    R.styleable.Keyboard_maxKeyboardHeight, displayHeight,  displayHeight / 2);
+            // Keyboard height will not exceed maxKeyboardHeight.
+            final int height = Math.min(keyboardHeight, maxKeyboardHeight);
+            final int width = keyboard.getDisplayWidth();
+
+            keyboard.setKeyboardHeight(height);
+            keyboard.setKeyWidth(getDimensionOrFraction(keyboardAttr,
+                    R.styleable.Keyboard_keyWidth, width, width / 10));
+            keyboard.setRowHeight(getDimensionOrFraction(keyboardAttr,
+                    R.styleable.Keyboard_rowHeight, height, 50));
+            keyboard.setHorizontalGap(getDimensionOrFraction(keyboardAttr,
+                    R.styleable.Keyboard_horizontalGap, width, 0));
+            keyboard.setVerticalGap(getDimensionOrFraction(keyboardAttr,
+                    R.styleable.Keyboard_verticalGap, height, 0));
+            keyboard.setPopupKeyboardResId(keyboardAttr.getResourceId(
+                    R.styleable.Keyboard_popupKeyboardTemplate, 0));
+
+            keyboard.setMaxPopupKeyboardColumn(keyAttr.getInt(
+                    R.styleable.Keyboard_Key_maxPopupKeyboardColumn, 5));
+        } finally {
+            keyAttr.recycle();
+            keyboardAttr.recycle();
+        }
     }
 
     private void parseKeyboardContent(XmlResourceParser parser, List<Key> keys)
@@ -190,9 +201,9 @@ public class KeyboardParser {
         while ((event = parser.next()) != XmlPullParser.END_DOCUMENT) {
             if (event == XmlPullParser.START_TAG) {
                 final String tag = parser.getName();
-                if (DEBUG_TAG) debugStartTag("parseKeyboardContent", tag, keys == null);
                 if (TAG_ROW.equals(tag)) {
-                    Row row = mKeyboard.createRowFromXml(mResources, parser);
+                    Row row = new Row(mResources, mKeyboard, parser);
+                    if (DEBUG) Log.d(TAG, String.format("<%s>", TAG_ROW));
                     if (keys != null)
                         startRow(row);
                     parseRowContent(parser, row, keys);
@@ -207,13 +218,12 @@ public class KeyboardParser {
                 }
             } else if (event == XmlPullParser.END_TAG) {
                 final String tag = parser.getName();
-                if (DEBUG_TAG) debugEndTag("parseKeyboardContent", tag, keys == null);
                 if (TAG_KEYBOARD.equals(tag)) {
                     endKeyboard(mKeyboard.getVerticalGap());
                     break;
-                } else if (TAG_CASE.equals(tag) || TAG_DEFAULT.equals(tag)) {
-                    break;
-                } else if (TAG_MERGE.equals(tag)) {
+                } else if (TAG_CASE.equals(tag) || TAG_DEFAULT.equals(tag)
+                        || TAG_MERGE.equals(tag)) {
+                    if (DEBUG) Log.d(TAG, String.format("</%s>", tag));
                     break;
                 } else if (TAG_KEY_STYLE.equals(tag)) {
                     continue;
@@ -230,7 +240,6 @@ public class KeyboardParser {
         while ((event = parser.next()) != XmlPullParser.END_DOCUMENT) {
             if (event == XmlPullParser.START_TAG) {
                 final String tag = parser.getName();
-                if (DEBUG_TAG) debugStartTag("parseRowContent", tag, keys == null);
                 if (TAG_KEY.equals(tag)) {
                     parseKey(parser, row, keys);
                 } else if (TAG_SPACER.equals(tag)) {
@@ -246,14 +255,14 @@ public class KeyboardParser {
                 }
             } else if (event == XmlPullParser.END_TAG) {
                 final String tag = parser.getName();
-                if (DEBUG_TAG) debugEndTag("parseRowContent", tag, keys == null);
                 if (TAG_ROW.equals(tag)) {
+                    if (DEBUG) Log.d(TAG, String.format("</%s>", TAG_ROW));
                     if (keys != null)
                         endRow();
                     break;
-                } else if (TAG_CASE.equals(tag) || TAG_DEFAULT.equals(tag)) {
-                    break;
-                } else if (TAG_MERGE.equals(tag)) {
+                } else if (TAG_CASE.equals(tag) || TAG_DEFAULT.equals(tag)
+                        || TAG_MERGE.equals(tag)) {
+                    if (DEBUG) Log.d(TAG, String.format("</%s>", tag));
                     break;
                 } else if (TAG_KEY_STYLE.equals(tag)) {
                     continue;
@@ -269,8 +278,10 @@ public class KeyboardParser {
         if (keys == null) {
             checkEndTag(TAG_KEY, parser);
         } else {
-            Key key = mKeyboard.createKeyFromXml(mResources, row, mCurrentX, mCurrentY, parser,
-                    mKeyStyles);
+            Key key = new Key(mResources, row, mCurrentX, mCurrentY, parser, mKeyStyles);
+            if (DEBUG) Log.d(TAG, String.format("<%s keyLabel=%s codes=%s popupCharacters=%s />",
+                    TAG_KEY, key.mLabel, Arrays.toString(key.mCodes),
+                    Arrays.toString(key.mPopupCharacters)));
             checkEndTag(TAG_KEY, parser);
             keys.add(key);
             if (key.mCodes[0] == Keyboard.CODE_SHIFT)
@@ -286,6 +297,7 @@ public class KeyboardParser {
         if (keys == null) {
             checkEndTag(TAG_SPACER, parser);
         } else {
+            if (DEBUG) Log.d(TAG, String.format("<%s />", TAG_SPACER));
             final TypedArray a = mResources.obtainAttributes(Xml.asAttributeSet(parser),
                     R.styleable.Keyboard);
             final int gap = getDimensionOrFraction(a, R.styleable.Keyboard_horizontalGap,
@@ -320,7 +332,8 @@ public class KeyboardParser {
             checkEndTag(TAG_INCLUDE, parser);
             if (keyboardLayout == 0)
                 throw new ParseException("No keyboardLayout attribute in <include/>", parser);
-            if (DEBUG_TAG) Log.d(TAG, String.format("  keyboardLayout=0x%08x", keyboardLayout));
+            if (DEBUG) Log.d(TAG, String.format("<%s keyboardLayout=%s />",
+                    TAG_INCLUDE, mResources.getResourceEntryName(keyboardLayout)));
             parseMerge(mResources.getLayout(keyboardLayout), row, keys);
         }
     }
@@ -331,7 +344,6 @@ public class KeyboardParser {
         while ((event = parser.next()) != XmlPullParser.END_DOCUMENT) {
             if (event == XmlPullParser.START_TAG) {
                 final String tag = parser.getName();
-                if (DEBUG_TAG) debugStartTag("parseMerge", tag, keys == null);
                 if (TAG_MERGE.equals(tag)) {
                     if (row == null) {
                         parseKeyboardContent(parser, keys);
@@ -359,13 +371,12 @@ public class KeyboardParser {
 
     private void parseSwitchInternal(XmlResourceParser parser, Row row, List<Key> keys)
             throws XmlPullParserException, IOException {
+        if (DEBUG) Log.d(TAG, String.format("<%s> %s", TAG_SWITCH, mKeyboard.mId));
         boolean selected = false;
         int event;
-        if (DEBUG_TAG) Log.d(TAG, "parseSwitchInternal: id=" + mKeyboard.mId);
         while ((event = parser.next()) != XmlPullParser.END_DOCUMENT) {
             if (event == XmlPullParser.START_TAG) {
                 final String tag = parser.getName();
-                if (DEBUG_TAG) debugStartTag("parseSwitchInternal", tag, keys == null);
                 if (TAG_CASE.equals(tag)) {
                     selected |= parseCase(parser, row, selected ? null : keys);
                 } else if (TAG_DEFAULT.equals(tag)) {
@@ -375,8 +386,8 @@ public class KeyboardParser {
                 }
             } else if (event == XmlPullParser.END_TAG) {
                 final String tag = parser.getName();
-                if (DEBUG_TAG) debugEndTag("parseRowContent", tag, keys == null);
                 if (TAG_SWITCH.equals(tag)) {
+                    if (DEBUG) Log.d(TAG, String.format("</%s>", TAG_SWITCH));
                     break;
                 } else {
                     throw new IllegalEndTag(parser, TAG_KEY);
@@ -426,20 +437,17 @@ public class KeyboardParser {
             final boolean selected = modeMatched && settingsKeyMatched && voiceEnabledMatched
                     && voiceKeyMatched && colorSchemeMatched && imeOptionsMatched;
 
-            if (DEBUG_TAG) {
-                Log.d(TAG, String.format(
-                        "parseCaseCondition: %s%s%s%s%s%s%s",
-                        Boolean.toString(selected).toUpperCase(),
-                        debugInteger(a, R.styleable.Keyboard_Case_mode, "mode"),
-                        debugBoolean(a, R.styleable.Keyboard_Case_hasSettingsKey,
-                                "hasSettingsKey"),
-                        debugBoolean(a, R.styleable.Keyboard_Case_voiceKeyEnabled,
-                                "voiceKeyEnabled"),
-                        debugBoolean(a, R.styleable.Keyboard_Case_hasVoiceKey, "hasVoiceKey"),
-                        debugInteger(viewAttr, R.styleable.KeyboardView_colorScheme,
-                                "colorScheme"),
-                        debugInteger(a, R.styleable.Keyboard_Case_imeOptions, "imeOptions")));
-            }
+            if (DEBUG) Log.d(TAG, String.format("<%s%s%s%s%s%s%s> %s", TAG_CASE,
+                    textAttr(KeyboardId.modeName(
+                            a.getInt(R.styleable.Keyboard_Case_mode, -1)), "mode"),
+                    textAttr(KeyboardId.colorSchemeName(
+                            a.getInt(R.styleable.KeyboardView_colorScheme, -1)), "colorSchemeName"),
+                    booleanAttr(a, R.styleable.Keyboard_Case_hasSettingsKey, "hasSettingsKey"),
+                    booleanAttr(a, R.styleable.Keyboard_Case_voiceKeyEnabled, "voiceKeyEnabled"),
+                    booleanAttr(a, R.styleable.Keyboard_Case_hasVoiceKey, "hasVoiceKey"),
+                    textAttr(KeyboardId.imeOptionsName(
+                            a.getInt(R.styleable.Keyboard_Case_imeOptions, -1)), "imeOptions"),
+                    Boolean.toString(selected)));
 
             return selected;
         } finally {
@@ -462,6 +470,7 @@ public class KeyboardParser {
 
     private boolean parseDefault(XmlResourceParser parser, Row row, List<Key> keys)
             throws XmlPullParserException, IOException {
+        if (DEBUG) Log.d(TAG, String.format("<%s>", TAG_DEFAULT));
         if (row == null) {
             parseKeyboardContent(parser, keys);
         } else {
@@ -471,18 +480,18 @@ public class KeyboardParser {
     }
 
     private void parseKeyStyle(XmlResourceParser parser, List<Key> keys) {
-        TypedArray a = mResources.obtainAttributes(Xml.asAttributeSet(parser),
+        TypedArray keyStyleAttr = mResources.obtainAttributes(Xml.asAttributeSet(parser),
                 R.styleable.Keyboard_KeyStyle);
         TypedArray keyAttrs = mResources.obtainAttributes(Xml.asAttributeSet(parser),
                 R.styleable.Keyboard_Key);
         try {
-            if (!a.hasValue(R.styleable.Keyboard_KeyStyle_styleName))
+            if (!keyStyleAttr.hasValue(R.styleable.Keyboard_KeyStyle_styleName))
                 throw new ParseException("<" + TAG_KEY_STYLE
                         + "/> needs styleName attribute", parser);
             if (keys != null)
-                mKeyStyles.parseKeyStyleAttributes(a, keyAttrs, parser);
+                mKeyStyles.parseKeyStyleAttributes(keyStyleAttr, keyAttrs, parser);
         } finally {
-            a.recycle();
+            keyStyleAttr.recycle();
             keyAttrs.recycle();
         }
     }
@@ -561,19 +570,11 @@ public class KeyboardParser {
         }
     }
 
-    private static void debugStartTag(String title, String tag, boolean skip) {
-        Log.d(TAG, title + ": <" + tag + ">" + (skip ? " skip" : ""));
-    }
-
-    private static void debugEndTag(String title, String tag, boolean skip) {
-        Log.d(TAG, title + ": </" + tag + ">" + (skip ? " skip" : ""));
-    }
-
-    private static String debugInteger(TypedArray a, int index, String name) {
-        return a.hasValue(index) ? " " + name + "=" + a.getInt(index, 0) : "";
+    private static String textAttr(String value, String name) {
+        return value != null ? String.format(" %s=%s", name, value) : "";
     }
 
-    private static String debugBoolean(TypedArray a, int index, String name) {
-        return a.hasValue(index) ? " " + name + "=" + a.getBoolean(index, false) : "";
+    private static String booleanAttr(TypedArray a, int index, String name) {
+        return a.hasValue(index) ? String.format(" %s=%s", name, a.getBoolean(index, false)) : "";
     }
 }
diff --git a/java/src/com/android/inputmethod/keyboard/KeyboardSwitcher.java b/java/src/com/android/inputmethod/keyboard/KeyboardSwitcher.java
index cd57db360b..17d01f89f4 100644
--- a/java/src/com/android/inputmethod/keyboard/KeyboardSwitcher.java
+++ b/java/src/com/android/inputmethod/keyboard/KeyboardSwitcher.java
@@ -115,7 +115,8 @@ public class KeyboardSwitcher implements SharedPreferences.OnSharedPreferenceCha
 
     private void makeSymbolsKeyboardIds() {
         final Locale locale = mSubtypeSwitcher.getInputLocale();
-        final int orientation = mInputMethodService.getResources().getConfiguration().orientation;
+        final Resources res = mInputMethodService.getResources();
+        final int orientation = res.getConfiguration().orientation;
         final int mode = mMode;
         final int colorScheme = getColorScheme();
         final boolean hasSettingsKey = mHasSettingsKey;
@@ -129,12 +130,14 @@ public class KeyboardSwitcher implements SharedPreferences.OnSharedPreferenceCha
         // "more" and "locked more" key labels.  To achieve these behavior, we should initialize
         // mSymbolsId and mSymbolsShiftedId to "phone keyboard" and "phone symbols keyboard"
         // respectively here for xlarge device's layout switching.
-        mSymbolsId = new KeyboardId(locale, orientation, mode,
-                mode == KeyboardId.MODE_PHONE ? R.xml.kbd_phone : R.xml.kbd_symbols,
-                colorScheme, hasSettingsKey, voiceKeyEnabled, hasVoiceKey, imeOptions, true);
-        mSymbolsShiftedId = new KeyboardId(locale, orientation, mode,
-                mode == KeyboardId.MODE_PHONE ? R.xml.kbd_phone_symbols : R.xml.kbd_symbols_shift,
-                colorScheme, hasSettingsKey, voiceKeyEnabled, hasVoiceKey, imeOptions, true);
+        int xmlId = mode == KeyboardId.MODE_PHONE ? R.xml.kbd_phone : R.xml.kbd_symbols;
+        mSymbolsId = new KeyboardId(
+                res.getResourceEntryName(xmlId), xmlId, locale, orientation, mode, colorScheme,
+                hasSettingsKey, voiceKeyEnabled, hasVoiceKey, imeOptions, true);
+        xmlId = mode == KeyboardId.MODE_PHONE ? R.xml.kbd_phone_symbols : R.xml.kbd_symbols_shift;
+        mSymbolsShiftedId = new KeyboardId(
+                res.getResourceEntryName(xmlId), xmlId, locale, orientation, mode, colorScheme,
+                hasSettingsKey, voiceKeyEnabled, hasVoiceKey, imeOptions, true);
     }
 
     private boolean hasVoiceKey(boolean isSymbols) {
@@ -230,9 +233,11 @@ public class KeyboardSwitcher implements SharedPreferences.OnSharedPreferenceCha
                 enableShiftLock = true;
             }
         }
-        final int orientation = mInputMethodService.getResources().getConfiguration().orientation;
+        final Resources res = mInputMethodService.getResources();
+        final int orientation = res.getConfiguration().orientation;
         final Locale locale = mSubtypeSwitcher.getInputLocale();
-        return new KeyboardId(locale, orientation, mode, xmlId, charColorId,
+        return new KeyboardId(
+                res.getResourceEntryName(xmlId), xmlId, locale, orientation, mode, charColorId,
                 mHasSettingsKey, mVoiceKeyEnabled, hasVoiceKey, imeOptions, enableShiftLock);
     }
 
diff --git a/java/src/com/android/inputmethod/keyboard/KeyboardView.java b/java/src/com/android/inputmethod/keyboard/KeyboardView.java
index 4a3a58b941..2ad414c90b 100644
--- a/java/src/com/android/inputmethod/keyboard/KeyboardView.java
+++ b/java/src/com/android/inputmethod/keyboard/KeyboardView.java
@@ -1000,7 +1000,7 @@ public class KeyboardView extends View implements PointerTracker.UIProxy {
         }
         // Set the preview background state
         mPreviewText.getBackground().setState(
-                key.mPopupResId != 0 ? LONG_PRESSABLE_STATE_SET : EMPTY_STATE_SET);
+                key.mPopupCharacters != null ? LONG_PRESSABLE_STATE_SET : EMPTY_STATE_SET);
         popupPreviewX += mOffsetInWindow[0];
         popupPreviewY += mOffsetInWindow[1];
 
@@ -1100,7 +1100,7 @@ public class KeyboardView extends View implements PointerTracker.UIProxy {
     }
 
     private View inflateMiniKeyboardContainer(Key popupKey) {
-        int popupKeyboardId = popupKey.mPopupResId;
+        int popupKeyboardResId = mKeyboard.getPopupKeyboardResId();
         LayoutInflater inflater = (LayoutInflater)getContext().getSystemService(
                 Context.LAYOUT_INFLATER_SERVICE);
         View container = inflater.inflate(mPopupLayout, null);
@@ -1157,13 +1157,8 @@ public class KeyboardView extends View implements PointerTracker.UIProxy {
         // Remove gesture detector on mini-keyboard
         miniKeyboard.mGestureDetector = null;
 
-        Keyboard keyboard;
-        if (popupKey.mPopupCharacters != null) {
-            keyboard = new Keyboard(getContext(), popupKeyboardId, popupKey.mPopupCharacters,
-                    -1, getPaddingLeft() + getPaddingRight());
-        } else {
-            keyboard = new Keyboard(getContext(), popupKeyboardId);
-        }
+        Keyboard keyboard = new MiniKeyboardBuilder(getContext(), popupKeyboardResId, popupKey)
+                .build();
         miniKeyboard.setKeyboard(keyboard);
         miniKeyboard.setPopupParent(this);
 
@@ -1194,7 +1189,7 @@ public class KeyboardView extends View implements PointerTracker.UIProxy {
      * method on the base class if the subclass doesn't wish to handle the call.
      */
     protected boolean onLongPress(Key popupKey) {
-        if (popupKey.mPopupResId == 0)
+        if (popupKey.mPopupCharacters == null)
             return false;
 
         View container = mMiniKeyboardCache.get(popupKey);
@@ -1272,15 +1267,14 @@ public class KeyboardView extends View implements PointerTracker.UIProxy {
     }
 
     private static boolean hasMultiplePopupChars(Key key) {
-        if (key.mPopupCharacters != null && key.mPopupCharacters.length() > 1) {
+        if (key.mPopupCharacters != null && key.mPopupCharacters.length > 1) {
             return true;
         }
         return false;
     }
 
     private static boolean isNumberAtLeftmostPopupChar(Key key) {
-        if (key.mPopupCharacters != null && key.mPopupCharacters.length() > 0
-                && isAsciiDigit(key.mPopupCharacters.charAt(0))) {
+        if (key.mPopupCharacters != null && isAsciiDigit(key.mPopupCharacters[0].charAt(0))) {
             return true;
         }
         return false;
diff --git a/java/src/com/android/inputmethod/keyboard/LatinKeyboard.java b/java/src/com/android/inputmethod/keyboard/LatinKeyboard.java
index 7cae4f1df2..b9041e36b6 100644
--- a/java/src/com/android/inputmethod/keyboard/LatinKeyboard.java
+++ b/java/src/com/android/inputmethod/keyboard/LatinKeyboard.java
@@ -44,15 +44,13 @@ public class LatinKeyboard extends Keyboard {
     public static final int OPACITY_FULLY_OPAQUE = 255;
     private static final int SPACE_LED_LENGTH_PERCENT = 80;
 
-    private Drawable mShiftLockPreviewIcon;
-    private Drawable mSpaceAutoCorrectionIndicator;
+    private final Drawable mSpaceAutoCorrectionIndicator;
     private final Drawable mButtonArrowLeftIcon;
     private final Drawable mButtonArrowRightIcon;
     private final int mSpaceBarTextShadowColor;
     private int mSpaceKeyIndex = -1;
     private int mSpaceDragStartX;
     private int mSpaceDragLastDiff;
-    private final Resources mRes;
     private final Context mContext;
     private boolean mCurrentlyInSpace;
     private SlidingLocaleDrawable mSlidingLocaleIcon;
@@ -79,10 +77,9 @@ public class LatinKeyboard extends Keyboard {
     private static final String MEDIUM_TEXT_SIZE_OF_LANGUAGE_ON_SPACEBAR = "medium";
 
     public LatinKeyboard(Context context, KeyboardId id) {
-        super(context, id);
+        super(context, id.getXmlId(), id);
         final Resources res = context.getResources();
         mContext = context;
-        mRes = res;
         if (id.mColorScheme == KeyboardView.COLOR_SCHEME_BLACK) {
             mSpaceBarTextShadowColor = res.getColor(
                     R.color.latinkeyboard_bar_language_shadow_black);
@@ -90,8 +87,6 @@ public class LatinKeyboard extends Keyboard {
             mSpaceBarTextShadowColor = res.getColor(
                     R.color.latinkeyboard_bar_language_shadow_white);
         }
-        mShiftLockPreviewIcon = res.getDrawable(R.drawable.sym_keyboard_feedback_shift_locked);
-        setDefaultBounds(mShiftLockPreviewIcon);
         mSpaceAutoCorrectionIndicator = res.getDrawable(R.drawable.sym_keyboard_space_led);
         mButtonArrowLeftIcon = res.getDrawable(R.drawable.sym_keyboard_language_arrows_left);
         mButtonArrowRightIcon = res.getDrawable(R.drawable.sym_keyboard_language_arrows_right);
@@ -109,7 +104,7 @@ public class LatinKeyboard extends Keyboard {
     }
 
     private void updateSpaceBarForLocale(boolean isAutoCorrection) {
-        final Resources res = mRes;
+        final Resources res = mContext.getResources();
         // If application locales are explicitly selected.
         if (SubtypeSwitcher.getInstance().needsToDisplayLanguage()) {
             mSpaceKey.setIcon(new BitmapDrawable(res,
@@ -181,7 +176,7 @@ public class LatinKeyboard extends Keyboard {
         final int height = mSpaceIcon.getIntrinsicHeight();
         final Bitmap buffer = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
         final Canvas canvas = new Canvas(buffer);
-        final Resources res = mRes;
+        final Resources res = mContext.getResources();
         canvas.drawColor(res.getColor(R.color.latinkeyboard_transparent), PorterDuff.Mode.CLEAR);
 
         SubtypeSwitcher subtypeSwitcher = SubtypeSwitcher.getInstance();
diff --git a/java/src/com/android/inputmethod/keyboard/MiniKeyboardBuilder.java b/java/src/com/android/inputmethod/keyboard/MiniKeyboardBuilder.java
new file mode 100644
index 0000000000..1eb0c3f371
--- /dev/null
+++ b/java/src/com/android/inputmethod/keyboard/MiniKeyboardBuilder.java
@@ -0,0 +1,104 @@
+/*
+ * Copyright (C) 2010 Google Inc.
+ *
+ * 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.
+ */
+
+package com.android.inputmethod.keyboard;
+
+import android.content.Context;
+import android.content.res.Resources;
+
+import java.util.List;
+
+public class MiniKeyboardBuilder {
+    private final Resources mRes;
+    private final Keyboard mKeyboard;
+    private final CharSequence[] mPopupCharacters;
+    private final int mMaxColumns;
+    private final int mNumRows;
+    private int mColPos;
+    private int mRowPos;
+    private Row mRow;
+    private int mX;
+    private int mY;
+
+    public MiniKeyboardBuilder(Context context, int layoutTemplateResId, Key popupKey) {
+        mRes = context.getResources();
+        mKeyboard = new Keyboard(context, layoutTemplateResId, null);
+        mPopupCharacters = popupKey.mPopupCharacters;
+        final int numKeys = mPopupCharacters.length;
+        final int maxColumns = popupKey.mMaxPopupColumn;
+        int numRows = numKeys / maxColumns;
+        if (numKeys % maxColumns != 0) numRows++;
+        mMaxColumns = maxColumns;
+        mNumRows = numRows;
+        // TODO: To determine key width we should pay attention to key label length.
+        mRow = new Row(mKeyboard, getRowFlags());
+        if (numRows > 1) {
+            mColPos = numKeys % maxColumns;
+            if (mColPos > 0) mColPos = maxColumns - mColPos;
+            // Centering top-row keys.
+            mX = mColPos * (mRow.mDefaultWidth + mRow.mDefaultHorizontalGap) / 2;
+        }
+        mKeyboard.setMinWidth(0);
+    }
+
+    public Keyboard build() {
+        List<Key> keys = mKeyboard.getKeys();
+        for (CharSequence label : mPopupCharacters) {
+            refresh();
+            final Key key = new Key(mRes, mRow, label, mX, mY);
+            keys.add(key);
+            advance();
+        }
+        finish();
+        return mKeyboard;
+    }
+
+    private int getRowFlags() {
+        final int rowPos = mRowPos;
+        int rowFlags = 0;
+        if (rowPos == 0) rowFlags |= Keyboard.EDGE_TOP;
+        if (rowPos == mNumRows - 1) rowFlags |= Keyboard.EDGE_BOTTOM;
+        return rowFlags;
+    }
+
+    private void refresh() {
+        if (mColPos >= mMaxColumns) {
+            final Row row = mRow;
+            // TODO: Allocate key position depending the precedence of popup characters.
+            mX = 0;
+            mY += row.mDefaultHeight + row.mVerticalGap;
+            mColPos = 0;
+            // TODO: To determine key width we should pay attention to key label length from
+            // bottom to up for rows.
+            mRow = new Row(mKeyboard, getRowFlags());
+            mRowPos++;
+        }
+    }
+
+    private void advance() {
+        final Row row = mRow;
+        final Keyboard keyboard = mKeyboard;
+        // TODO: Allocate key position depending the precedence of popup characters.
+        mX += row.mDefaultWidth + row.mDefaultHorizontalGap;
+        if (mX > keyboard.getMinWidth())
+            keyboard.setMinWidth(mX);
+        mColPos++;
+    }
+
+    private void finish() {
+        mKeyboard.setHeight(mY + mRow.mDefaultHeight);
+    }
+}
\ No newline at end of file
diff --git a/java/src/com/android/inputmethod/keyboard/PopupCharactersParser.java b/java/src/com/android/inputmethod/keyboard/PopupCharactersParser.java
new file mode 100644
index 0000000000..cad3da03eb
--- /dev/null
+++ b/java/src/com/android/inputmethod/keyboard/PopupCharactersParser.java
@@ -0,0 +1,177 @@
+/*
+ * Copyright (C) 2010 Google Inc.
+ *
+ * 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.
+ */
+
+package com.android.inputmethod.keyboard;
+
+import com.android.inputmethod.latin.R;
+
+import android.content.res.Resources;
+import android.graphics.drawable.Drawable;
+import android.text.TextUtils;
+
+/**
+ * String parser of popupCharacters attribute of Key.
+ * The string is comma separated texts each of which represents one popup key.
+ * Each popup key text is one of the following:
+ * - A single letter (Letter)
+ * - Label optionally followed by keyOutputText or code (keyLabel|keyOutputText).
+ * - Icon followed by keyOutputText or code (@drawable/icon|@integer/key_code)
+ * Special character, comma ',' backslash '\', and bar '|' can be escaped by '\'
+ * character.
+ * Note that the character '@' and '\' are also parsed by XML parser and CSV parser as well.
+ */
+public class PopupCharactersParser {
+    private static final char ESCAPE = '\\';
+    private static final String LABEL_END = "|";
+    private static final String PREFIX_AT = "@";
+    private static final String PREFIX_ICON = PREFIX_AT + "drawable/";
+    private static final String PREFIX_CODE = PREFIX_AT + "integer/";
+    private static final int[] DUMMY_CODES = { 0 };
+
+    private PopupCharactersParser() {
+        // Intentional empty constructor for utility class.
+    }
+
+    private static boolean hasIcon(String popupSpec) {
+        if (popupSpec.startsWith(PREFIX_ICON)) {
+            final int end = indexOfLabelEnd(popupSpec, 0);
+            if (end > 0)
+                return true;
+            throw new PopupCharactersParserError("outputText or code not specified: " + popupSpec);
+        }
+        return false;
+    }
+
+    private static boolean hasCode(String popupSpec) {
+        final int end = indexOfLabelEnd(popupSpec, 0);
+        if (end > 0 && end + 1 < popupSpec.length()
+                && popupSpec.substring(end + 1).startsWith(PREFIX_CODE)) {
+            return true;
+        }
+        return false;
+    }
+
+    private static String parseEscape(String text) {
+        if (text.indexOf(ESCAPE) < 0)
+            return text;
+        final int length = text.length();
+        final StringBuilder sb = new StringBuilder();
+        for (int pos = 0; pos < length; pos++) {
+            final char c = text.charAt(pos);
+            if (c == ESCAPE && pos + 1 < length) {
+                sb.append(text.charAt(++pos));
+            } else {
+                sb.append(c);
+            }
+        }
+        return sb.toString();
+    }
+
+    private static int indexOfLabelEnd(String popupSpec, int start) {
+        if (popupSpec.indexOf(ESCAPE, start) < 0) {
+            final int end = popupSpec.indexOf(LABEL_END, start);
+            if (end == 0)
+                throw new PopupCharactersParserError(LABEL_END + " at " + start + ": " + popupSpec);
+            return end;
+        }
+        final int length = popupSpec.length();
+        for (int pos = start; pos < length; pos++) {
+            final char c = popupSpec.charAt(pos);
+            if (c == ESCAPE && pos + 1 < length) {
+                pos++;
+            } else if (popupSpec.startsWith(LABEL_END, pos)) {
+                return pos;
+            }
+        }
+        return -1;
+    }
+
+    public static String getLabel(String popupSpec) {
+        if (hasIcon(popupSpec))
+            return null;
+        final int end = indexOfLabelEnd(popupSpec, 0);
+        final String label = (end > 0) ? parseEscape(popupSpec.substring(0, end))
+                : parseEscape(popupSpec);
+        if (TextUtils.isEmpty(label))
+            throw new PopupCharactersParserError("Empty label: " + popupSpec);
+        return label;
+    }
+
+    public static String getOutputText(String popupSpec) {
+        if (hasCode(popupSpec))
+            return null;
+        final int end = indexOfLabelEnd(popupSpec, 0);
+        if (end > 0) {
+            if (indexOfLabelEnd(popupSpec, end + 1) >= 0)
+                    throw new PopupCharactersParserError("Multiple " + LABEL_END + ": "
+                            + popupSpec);
+            final String outputText = parseEscape(popupSpec.substring(end + LABEL_END.length()));
+            if (!TextUtils.isEmpty(outputText))
+                return outputText;
+            throw new PopupCharactersParserError("Empty outputText: " + popupSpec);
+        }
+        final String label = getLabel(popupSpec);
+        if (label == null)
+            throw new PopupCharactersParserError("Empty label: " + popupSpec);
+        // Code is automatically generated for one letter label. See getCode().
+        if (label.length() == 1)
+            return null;
+        return label;
+    }
+
+    public static int[] getCodes(Resources res, String popupSpec) {
+        if (hasCode(popupSpec)) {
+            final int end = indexOfLabelEnd(popupSpec, 0);
+            if (indexOfLabelEnd(popupSpec, end + 1) >= 0)
+                throw new PopupCharactersParserError("Multiple " + LABEL_END + ": " + popupSpec);
+            final int resId = getResourceId(res,
+                    popupSpec.substring(end + LABEL_END.length() + PREFIX_AT.length()));
+            final int code = res.getInteger(resId);
+            return new int[] { code };
+        }
+        if (indexOfLabelEnd(popupSpec, 0) > 0)
+            return DUMMY_CODES;
+        final String label = getLabel(popupSpec);
+        // Code is automatically generated for one letter label.
+        if (label != null && label.length() == 1)
+            return new int[] { label.charAt(0) };
+        return DUMMY_CODES;
+    }
+
+    public static Drawable getIcon(Resources res, String popupSpec) {
+        if (hasIcon(popupSpec)) {
+            int end = popupSpec.indexOf(LABEL_END, PREFIX_ICON.length() + 1);
+            int resId = getResourceId(res, popupSpec.substring(PREFIX_AT.length(), end));
+            return res.getDrawable(resId);
+        }
+        return null;
+    }
+
+    private static int getResourceId(Resources res, String name) {
+        String packageName = res.getResourcePackageName(R.string.english_ime_name);
+        int resId = res.getIdentifier(name, null, packageName);
+        if (resId == 0)
+            throw new PopupCharactersParserError("Unknown resource: " + name);
+        return resId;
+    }
+
+    @SuppressWarnings("serial")
+    public static class PopupCharactersParserError extends RuntimeException {
+        public PopupCharactersParserError(String message) {
+            super(message);
+        }
+    }
+}
diff --git a/java/src/com/android/inputmethod/keyboard/Row.java b/java/src/com/android/inputmethod/keyboard/Row.java
index 7c158bca00..198f02ca8d 100644
--- a/java/src/com/android/inputmethod/keyboard/Row.java
+++ b/java/src/com/android/inputmethod/keyboard/Row.java
@@ -45,13 +45,13 @@ public class Row {
 
     private final Keyboard mKeyboard;
 
-    public Row(Keyboard keyboard) {
+    public Row(Keyboard keyboard, int rowFlags) {
         this.mKeyboard = keyboard;
         mDefaultHeight = keyboard.getRowHeight();
         mDefaultWidth = keyboard.getKeyWidth();
         mDefaultHorizontalGap = keyboard.getHorizontalGap();
         mVerticalGap = keyboard.getVerticalGap();
-        mRowEdgeFlags = Keyboard.EDGE_TOP | Keyboard.EDGE_BOTTOM;
+        mRowEdgeFlags = rowFlags;
     }
 
     public Row(Resources res, Keyboard keyboard, XmlResourceParser parser) {
diff --git a/java/src/com/android/inputmethod/latin/LatinIME.java b/java/src/com/android/inputmethod/latin/LatinIME.java
index 243306a359..12a24e87ae 100644
--- a/java/src/com/android/inputmethod/latin/LatinIME.java
+++ b/java/src/com/android/inputmethod/latin/LatinIME.java
@@ -2039,7 +2039,7 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
                 && prefs.getBoolean(Settings.PREF_VIBRATE_ON, false);
         mSoundOn = prefs.getBoolean(Settings.PREF_SOUND_ON, false);
         mPopupOn = prefs.getBoolean(Settings.PREF_POPUP_ON,
-                mResources.getBoolean(R.bool.default_popup_preview));
+                mResources.getBoolean(R.bool.config_default_popup_preview));
         mAutoCap = prefs.getBoolean(Settings.PREF_AUTO_CAP, true);
         mQuickFixes = prefs.getBoolean(Settings.PREF_QUICK_FIXES, true);
 
diff --git a/tests/src/com/android/inputmethod/keyboard/KeyStylesTests.java b/tests/src/com/android/inputmethod/keyboard/KeyStylesTests.java
new file mode 100644
index 0000000000..3f2b7f4d54
--- /dev/null
+++ b/tests/src/com/android/inputmethod/keyboard/KeyStylesTests.java
@@ -0,0 +1,120 @@
+/*
+ * Copyright (C) 2010 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.
+ */
+
+package com.android.inputmethod.keyboard;
+
+import com.android.inputmethod.keyboard.KeyStyles.EmptyKeyStyle;
+
+import android.test.AndroidTestCase;
+import android.text.TextUtils;
+
+public class KeyStylesTests extends AndroidTestCase {
+    private static void assertNumberFormatException(String message, String value) {
+        try {
+            EmptyKeyStyle.parseCsvInt(value);
+            fail(message);
+        } catch (NumberFormatException nfe) {
+            // success.
+        }
+    }
+
+    private static void assertIntArray(String message, String value, Integer ... expected) {
+        final int actual[] = EmptyKeyStyle.parseCsvInt(value);
+        assertSame(message + ": result length", expected.length, actual.length);
+        for (int i = 0; i < actual.length; i++) {
+            assertEquals(message + ": result at " + i + ":", (int)expected[i], actual[i]);
+        }
+    }
+
+    private static String format(String message, Object expected, Object actual) {
+        return message + " expected:<" + expected + "> but was:<" + actual + ">";
+    }
+
+    private static void assertTextArray(String message, CharSequence value,
+            CharSequence ... expected) {
+        final CharSequence actual[] = EmptyKeyStyle.parseCsvText(value);
+        if (expected.length == 0) {
+            assertNull(message, actual);
+            return;
+        }
+        assertSame(message + ": 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);
+        }
+    }
+
+    public void testParseCsvInt() {
+        assertIntArray("Empty string", "");
+        assertNumberFormatException("Spaces", "  ");
+        assertNumberFormatException("Non-decimal number", "abc");
+        assertIntArray("Single number", "123", 123);
+        assertIntArray("Negative number", "-123", -123);
+        assertNumberFormatException("Hexadecimal number", "1b2b");
+        assertIntArray("Multiple numbers", "123,456", 123, 456);
+        assertNumberFormatException("Non-decimal numbers", "123,abc");
+        assertNumberFormatException("Escaped comma", "123\\,456");
+        assertNumberFormatException("Escaped escape", "123\\\\,456");
+    }
+
+    public void testParseCsvTextZero() {
+        assertTextArray("Empty string", "");
+    }
+
+    public void testParseCsvTextSingle() {
+        assertTextArray("Single char", "a", "a");
+        assertTextArray("Space", " ", " ");
+        assertTextArray("Single label", "abc", "abc");
+        assertTextArray("Spaces", "   ", "   ");
+        assertTextArray("Spaces in label", "a b c", "a b c");
+        assertTextArray("Spaces at beginning of label", " abc", " abc");
+        assertTextArray("Spaces at end of label", "abc ", "abc ");
+        assertTextArray("label surrounded by spaces", " abc ", " abc ");
+    }
+
+    public void testParseCsvTextSingleEscaped() {
+        assertTextArray("Escaped char", "\\a", "a");
+        assertTextArray("Escaped comma", "\\,", ",");
+        assertTextArray("Escaped escape", "\\\\", "\\");
+        assertTextArray("Escaped label", "a\\bc", "abc");
+        assertTextArray("Escaped label at begininng", "\\abc", "abc");
+        assertTextArray("Escaped label with comma", "a\\,c", "a,c");
+        assertTextArray("Escaped label with comma at beginning", "\\,bc", ",bc");
+        assertTextArray("Escaped label with successive", "\\,\\\\bc", ",\\bc");
+        assertTextArray("Escaped label with escape", "a\\\\c", "a\\c");
+    }
+
+    public void testParseCsvTextMulti() {
+        assertTextArray("Multiple chars", "a,b,c", "a", "b", "c");
+        assertTextArray("Multiple chars surrounded by spaces", " a , b , c ", " a ", " b ", " c ");
+        assertTextArray("Multiple labels", "abc,def,ghi", "abc", "def", "ghi");
+        assertTextArray("Multiple labels surrounded by spaces", " abc , def , ghi ",
+                " abc ", " def ", " ghi ");
+    }
+
+    public void testParseCsvTextMultiEscaped() {
+        assertTextArray("Multiple chars with comma", "a,\\,,c", "a", ",", "c");
+        assertTextArray("Multiple chars with comma surrounded by spaces", " a , \\, , c ",
+                " a ", " , ", " c ");
+        assertTextArray("Multiple labels with escape", "\\abc,d\\ef,gh\\i", "abc", "def", "ghi");
+        assertTextArray("Multiple labels with escape surrounded by spaces",
+                " \\abc , d\\ef , gh\\i ", " abc ", " def ", " ghi ");
+        assertTextArray("Multiple labels with comma and escape",
+                "ab\\\\,d\\\\\\,,g\\,i", "ab\\", "d\\,", "g,i");
+        assertTextArray("Multiple labels with comma and escape surrounded by spaces",
+                " ab\\\\ , d\\\\\\, , g\\,i ", " ab\\ ", " d\\, ", " g,i ");
+    }
+}
diff --git a/tests/src/com/android/inputmethod/keyboard/PopupCharactersParserTests.java b/tests/src/com/android/inputmethod/keyboard/PopupCharactersParserTests.java
new file mode 100644
index 0000000000..77b62ca5da
--- /dev/null
+++ b/tests/src/com/android/inputmethod/keyboard/PopupCharactersParserTests.java
@@ -0,0 +1,205 @@
+/*
+ * Copyright (C) 2010 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.
+ */
+
+package com.android.inputmethod.keyboard;
+
+import com.android.inputmethod.latin.R;
+import android.content.res.Resources;
+import android.graphics.drawable.Drawable;
+import android.test.AndroidTestCase;
+
+public class PopupCharactersParserTests extends AndroidTestCase {
+    private Resources mRes;
+
+    private static final String CODE_SETTINGS = "@integer/key_settings";
+    private static final String ICON_SETTINGS = "@drawable/sym_keyboard_settings";
+    private static final String CODE_NON_EXISTING = "@integer/non_existing";
+    private static final String ICON_NON_EXISTING = "@drawable/non_existing";
+
+    private int mCodeSettings;
+    private Drawable mIconSettings;
+
+    private static final Integer[] DUMMY_CODES = { 0 };
+
+    @Override
+    protected void setUp() {
+        Resources res = getContext().getResources();
+        mRes = res;
+
+        final String packageName = res.getResourcePackageName(R.string.english_ime_name);
+        final int codeId = res.getIdentifier(CODE_SETTINGS.substring(1), null, packageName);
+        final int iconId = res.getIdentifier(ICON_SETTINGS.substring(1), null, packageName);
+        mCodeSettings = res.getInteger(codeId);
+        mIconSettings = res.getDrawable(iconId);
+    }
+
+    private void assertParser(String message, String popupSpec, String expectedLabel,
+            String expectedOutputText, Drawable expectedIcon, Integer ... expectedCodes) {
+        String actualLabel = PopupCharactersParser.getLabel(popupSpec);
+        assertEquals(message + ": label:", expectedLabel, actualLabel);
+
+        String actualOutputText = PopupCharactersParser.getOutputText(popupSpec);
+        assertEquals(message + ": ouptputText:", expectedOutputText, actualOutputText);
+
+        Drawable actualIcon = PopupCharactersParser.getIcon(mRes, popupSpec);
+        // We can not compare drawables, checking null or non-null instead.
+        if (expectedIcon == null) {
+            assertNull(message + ": icon null:", actualIcon);
+        } else {
+            assertNotNull(message + ": icon non-null:", actualIcon);
+        }
+
+        int[] actualCodes = PopupCharactersParser.getCodes(mRes, popupSpec);
+        if (expectedCodes == null) {
+            assertNull(message + ": codes null:", actualCodes);
+            return;
+        }
+        assertSame(message + ": codes length:", expectedCodes.length, actualCodes.length);
+        for (int i = 0; i < actualCodes.length; i++) {
+            assertEquals(message + ": codes value at " + i + ":", (int)expectedCodes[i],
+                    actualCodes[i]);
+        }
+    }
+
+    private void assertParserError(String message, String popupSpec, String expectedLabel,
+            String expectedOutputText, Drawable expectedIcon, Integer ... expectedCodes) {
+        try {
+            if (expectedCodes.length > 0) {
+                assertParser(message, popupSpec, expectedLabel, expectedOutputText, expectedIcon,
+                        expectedCodes);
+            } else {
+                assertParser(message, popupSpec, expectedLabel, expectedOutputText, expectedIcon,
+                        DUMMY_CODES);
+            }
+            fail(message);
+        } catch (PopupCharactersParser.PopupCharactersParserError pcpe) {
+            // success.
+        }
+    }
+
+    public void testSingleLetter() {
+        assertParser("Single letter", "a", "a", null, null, (int)'a');
+        assertParser("Single escaped bar", "\\|", "|", null, null, (int)'|');
+        assertParser("Single escaped escape", "\\\\", "\\", null, null, (int)'\\');
+        assertParser("Single comma", ",", ",", null, null, (int)',');
+        assertParser("Single escaped comma", "\\,", ",", null, null, (int)',');
+        assertParser("Single escaped letter", "\\a", "a", null, null, (int)'a');
+        assertParser("Single at", "@", "@", null, null, (int)'@');
+        assertParser("Single escaped at", "\\@", "@", null, null, (int)'@');
+        assertParser("Single letter with outputText", "a|abc", "a", "abc", null, DUMMY_CODES);
+        assertParser("Single letter with escaped outputText", "a|a\\|c", "a", "a|c", null,
+                DUMMY_CODES);
+        assertParser("Single letter with comma outputText", "a|a,b", "a", "a,b", null, DUMMY_CODES);
+        assertParser("Single letter with escaped comma outputText", "a|a\\,b", "a", "a,b", null,
+                DUMMY_CODES);
+        assertParser("Single letter with outputText starts with at", "a|@bc", "a", "@bc", null,
+                DUMMY_CODES);
+        assertParser("Single letter with outputText contains at", "a|a@c", "a", "a@c", null,
+                DUMMY_CODES);
+        assertParser("Single letter with escaped at outputText", "a|\\@bc", "a", "@bc", null,
+                DUMMY_CODES);
+        assertParser("Single escaped escape with outputText", "\\\\|\\\\", "\\", "\\", null,
+                DUMMY_CODES);
+        assertParser("Single escaped bar with outputText", "\\||\\|", "|", "|", null, DUMMY_CODES);
+        assertParser("Single letter with code", "a|" + CODE_SETTINGS, "a", null, null,
+                mCodeSettings);
+    }
+
+    public void testLabel() {
+        assertParser("Simple label", "abc", "abc", "abc", null, DUMMY_CODES);
+        assertParser("Label with escaped bar", "a\\|c", "a|c", "a|c", null, DUMMY_CODES);
+        assertParser("Label with escaped escape", "a\\\\c", "a\\c", "a\\c", null, DUMMY_CODES);
+        assertParser("Label with comma", "a,c", "a,c", "a,c", null, DUMMY_CODES);
+        assertParser("Label with escaped comma", "a\\,c", "a,c", "a,c", null, DUMMY_CODES);
+        assertParser("Label starts with at", "@bc", "@bc", "@bc", null, DUMMY_CODES);
+        assertParser("Label contains at", "a@c", "a@c", "a@c", null, DUMMY_CODES);
+        assertParser("Label with escaped at", "\\@bc", "@bc", "@bc", null, DUMMY_CODES);
+        assertParser("Label with escaped letter", "\\abc", "abc", "abc", null, DUMMY_CODES);
+        assertParser("Label with outputText", "abc|def", "abc", "def", null, DUMMY_CODES);
+        assertParser("Label with comma and outputText", "a,c|def", "a,c", "def", null, DUMMY_CODES);
+        assertParser("Escaped comma label with outputText", "a\\,c|def", "a,c", "def", null,
+                DUMMY_CODES);
+        assertParser("Escaped label with outputText", "a\\|c|def", "a|c", "def", null, DUMMY_CODES);
+        assertParser("Label with escaped bar outputText", "abc|d\\|f", "abc", "d|f", null,
+                DUMMY_CODES);
+        assertParser("Escaped escape label with outputText", "a\\\\|def", "a\\", "def", null,
+                DUMMY_CODES);
+        assertParser("Label starts with at and outputText", "@bc|def", "@bc", "def", null,
+                DUMMY_CODES);
+        assertParser("Label contains at label and outputText", "a@c|def", "a@c", "def", null,
+                DUMMY_CODES);
+        assertParser("Escaped at label with outputText", "\\@bc|def", "@bc", "def", null,
+                DUMMY_CODES);
+        assertParser("Label with comma outputText", "abc|a,b", "abc", "a,b", null, DUMMY_CODES);
+        assertParser("Label with escaped comma outputText", "abc|a\\,b", "abc", "a,b", null,
+                DUMMY_CODES);
+        assertParser("Label with outputText starts with at", "abc|@bc", "abc", "@bc", null,
+                DUMMY_CODES);
+        assertParser("Label with outputText contains at", "abc|a@c", "abc", "a@c", null,
+                DUMMY_CODES);
+        assertParser("Label with escaped at outputText", "abc|\\@bc", "abc", "@bc", null,
+                DUMMY_CODES);
+        assertParser("Label with escaped bar outputText", "abc|d\\|f", "abc", "d|f",
+                null, DUMMY_CODES);
+        assertParser("Escaped bar label with escaped bar outputText", "a\\|c|d\\|f", "a|c", "d|f",
+                null, DUMMY_CODES);
+        assertParser("Label with code", "abc|" + CODE_SETTINGS, "abc", null, null, mCodeSettings);
+        assertParser("Escaped label with code", "a\\|c|" + CODE_SETTINGS, "a|c", null, null,
+                mCodeSettings);
+    }
+
+    public void testIconAndCode() {
+        assertParser("Icon with outputText", ICON_SETTINGS + "|abc", null, "abc", mIconSettings,
+                DUMMY_CODES);
+        assertParser("Icon with outputText starts with at", ICON_SETTINGS + "|@bc", null, "@bc",
+                mIconSettings, DUMMY_CODES);
+        assertParser("Icon with outputText contains at", ICON_SETTINGS + "|a@c", null, "a@c",
+                mIconSettings, DUMMY_CODES);
+        assertParser("Icon with escaped at outputText", ICON_SETTINGS + "|\\@bc", null, "@bc",
+                mIconSettings, DUMMY_CODES);
+        assertParser("Label starts with at and code", "@bc|" + CODE_SETTINGS, "@bc", null, null,
+                mCodeSettings);
+        assertParser("Label contains at and code", "a@c|" + CODE_SETTINGS, "a@c", null, null,
+                mCodeSettings);
+        assertParser("Escaped at label with code", "\\@bc|" + CODE_SETTINGS, "@bc", null, null,
+                mCodeSettings);
+        assertParser("Icon with code", ICON_SETTINGS + "|" + CODE_SETTINGS, null, null,
+                mIconSettings, mCodeSettings);
+    }
+
+    public void testFormatError() {
+        assertParserError("Empty spec", "", null, null, null);
+        assertParserError("Empty label with outputText", "|a", null, "a", null);
+        assertParserError("Empty label with code", "|" + CODE_SETTINGS, null, null, null,
+                mCodeSettings);
+        assertParserError("Empty outputText with label", "a|", "a", null, null);
+        assertParserError("Empty outputText with icon", ICON_SETTINGS + "|", null, null,
+                mIconSettings);
+        assertParserError("Empty icon and code", "|", null, null, null);
+        assertParserError("Icon without code", ICON_SETTINGS, null, null, mIconSettings);
+        assertParserError("Non existing icon", ICON_NON_EXISTING + "|abc", null, "abc", null);
+        assertParserError("Non existing code", "abc|" + CODE_NON_EXISTING, "abc", null, null);
+        assertParserError("Third bar at end", "a|b|", "a", null, null);
+        assertParserError("Multiple bar", "a|b|c", "a", null, null);
+        assertParserError("Multiple bar with label and code", "a|" + CODE_SETTINGS + "|c", "a",
+                null, null, mCodeSettings);
+        assertParserError("Multiple bar with icon and outputText", ICON_SETTINGS + "|b|c", null,
+                null, mIconSettings);
+        assertParserError("Multiple bar with icon and code",
+                ICON_SETTINGS + "|" + CODE_SETTINGS + "|c", null, null, mIconSettings,
+                mCodeSettings);
+    }
+}
-- 
GitLab