keep history after reset to jb-ub-latinimegoogle-bayo
diff --git a/dictionaries/fr_wordlist.combined.gz b/dictionaries/fr_wordlist.combined.gz
index 0763b62..1d988d6 100644
--- a/dictionaries/fr_wordlist.combined.gz
+++ b/dictionaries/fr_wordlist.combined.gz
Binary files differ
diff --git a/dictionaries/iw_wordlist.combined.gz b/dictionaries/iw_wordlist.combined.gz
new file mode 100644
index 0000000..36b0478
--- /dev/null
+++ b/dictionaries/iw_wordlist.combined.gz
Binary files differ
diff --git a/dictionaries/pt_BR_wordlist.combined.gz b/dictionaries/pt_BR_wordlist.combined.gz
index 0dd8472..221ea75 100644
--- a/dictionaries/pt_BR_wordlist.combined.gz
+++ b/dictionaries/pt_BR_wordlist.combined.gz
Binary files differ
diff --git a/dictionaries/pt_PT_wordlist.combined.gz b/dictionaries/pt_PT_wordlist.combined.gz
index 00d50d0..6a041d9 100644
--- a/dictionaries/pt_PT_wordlist.combined.gz
+++ b/dictionaries/pt_PT_wordlist.combined.gz
Binary files differ
diff --git a/dictionaries/ru_wordlist.combined.gz b/dictionaries/ru_wordlist.combined.gz
index 1c85d66..572314d 100644
--- a/dictionaries/ru_wordlist.combined.gz
+++ b/dictionaries/ru_wordlist.combined.gz
Binary files differ
diff --git a/java/res/color/emoji_tab_label_color_gb.xml b/java/res/color/emoji_tab_label_color_gb.xml
new file mode 100644
index 0000000..e1d2f71
--- /dev/null
+++ b/java/res/color/emoji_tab_label_color_gb.xml
@@ -0,0 +1,33 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+**
+** Copyright 2013, 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.
+*/
+-->
+
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+    <item
+        android:state_focused="true"
+        android:color="@color/key_text_color_gb" />
+    <item
+        android:state_pressed="true"
+        android:color="@color/key_text_color_gb" />
+    <item
+        android:state_selected="true"
+        android:color="@color/key_text_color_gb" />
+    <item
+        android:color="@color/key_text_inactivated_color_gb" />
+</selector>
diff --git a/java/res/color/emoji_tab_label_color_ics.xml b/java/res/color/emoji_tab_label_color_ics.xml
new file mode 100644
index 0000000..36e1d30
--- /dev/null
+++ b/java/res/color/emoji_tab_label_color_ics.xml
@@ -0,0 +1,33 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+**
+** Copyright 2013, 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.
+*/
+-->
+
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+    <item
+        android:state_focused="true"
+        android:color="@color/key_text_color_ics" />
+    <item
+        android:state_pressed="true"
+        android:color="@color/key_text_color_ics" />
+    <item
+        android:state_selected="true"
+        android:color="@color/key_text_color_ics" />
+    <item
+        android:color="@color/key_text_inactivated_color_ics" />
+</selector>
diff --git a/java/res/drawable-hdpi/btn_center_default.9.png b/java/res/drawable-hdpi/btn_center_default.9.png
deleted file mode 100644
index 4f5f01c..0000000
--- a/java/res/drawable-hdpi/btn_center_default.9.png
+++ /dev/null
Binary files differ
diff --git a/java/res/drawable-hdpi/btn_center_pressed.9.png b/java/res/drawable-hdpi/btn_center_pressed.9.png
deleted file mode 100644
index 213b482..0000000
--- a/java/res/drawable-hdpi/btn_center_pressed.9.png
+++ /dev/null
Binary files differ
diff --git a/java/res/drawable-hdpi/btn_center_selected.9.png b/java/res/drawable-hdpi/btn_center_selected.9.png
deleted file mode 100644
index 213b482..0000000
--- a/java/res/drawable-hdpi/btn_center_selected.9.png
+++ /dev/null
Binary files differ
diff --git a/java/res/drawable-hdpi/btn_keyboard_key_dark_active_holo.9.png b/java/res/drawable-hdpi/btn_keyboard_key_dark_active_holo.9.png
index 9aa8db6..87211a5 100644
--- a/java/res/drawable-hdpi/btn_keyboard_key_dark_active_holo.9.png
+++ b/java/res/drawable-hdpi/btn_keyboard_key_dark_active_holo.9.png
Binary files differ
diff --git a/java/res/drawable-hdpi/btn_keyboard_key_dark_normal_holo.9.png b/java/res/drawable-hdpi/btn_keyboard_key_dark_normal_holo.9.png
index 5e6a9d6..fa2cb85 100644
--- a/java/res/drawable-hdpi/btn_keyboard_key_dark_normal_holo.9.png
+++ b/java/res/drawable-hdpi/btn_keyboard_key_dark_normal_holo.9.png
Binary files differ
diff --git a/java/res/drawable-hdpi/btn_keyboard_key_dark_normal_off_holo.9.png b/java/res/drawable-hdpi/btn_keyboard_key_dark_normal_off_holo.9.png
index a3ba223..b1af23b 100644
--- a/java/res/drawable-hdpi/btn_keyboard_key_dark_normal_off_holo.9.png
+++ b/java/res/drawable-hdpi/btn_keyboard_key_dark_normal_off_holo.9.png
Binary files differ
diff --git a/java/res/drawable-hdpi/btn_keyboard_key_dark_normal_on_holo.9.png b/java/res/drawable-hdpi/btn_keyboard_key_dark_normal_on_holo.9.png
index 9f4587b..814e402 100644
--- a/java/res/drawable-hdpi/btn_keyboard_key_dark_normal_on_holo.9.png
+++ b/java/res/drawable-hdpi/btn_keyboard_key_dark_normal_on_holo.9.png
Binary files differ
diff --git a/java/res/drawable-hdpi/btn_keyboard_key_dark_pressed_holo.9.png b/java/res/drawable-hdpi/btn_keyboard_key_dark_pressed_holo.9.png
index 7ec33dd..90abe39 100644
--- a/java/res/drawable-hdpi/btn_keyboard_key_dark_pressed_holo.9.png
+++ b/java/res/drawable-hdpi/btn_keyboard_key_dark_pressed_holo.9.png
Binary files differ
diff --git a/java/res/drawable-hdpi/btn_keyboard_key_dark_pressed_off_holo.9.png b/java/res/drawable-hdpi/btn_keyboard_key_dark_pressed_off_holo.9.png
index 655bc01..48eeb3f 100644
--- a/java/res/drawable-hdpi/btn_keyboard_key_dark_pressed_off_holo.9.png
+++ b/java/res/drawable-hdpi/btn_keyboard_key_dark_pressed_off_holo.9.png
Binary files differ
diff --git a/java/res/drawable-hdpi/btn_keyboard_key_dark_pressed_on_holo.9.png b/java/res/drawable-hdpi/btn_keyboard_key_dark_pressed_on_holo.9.png
index 138e915..71e0683 100644
--- a/java/res/drawable-hdpi/btn_keyboard_key_dark_pressed_on_holo.9.png
+++ b/java/res/drawable-hdpi/btn_keyboard_key_dark_pressed_on_holo.9.png
Binary files differ
diff --git a/java/res/drawable-hdpi/btn_keyboard_key_fulltrans_normal.9.png b/java/res/drawable-hdpi/btn_keyboard_key_fulltrans_normal.9.png
deleted file mode 100644
index 1163290..0000000
--- a/java/res/drawable-hdpi/btn_keyboard_key_fulltrans_normal.9.png
+++ /dev/null
Binary files differ
diff --git a/java/res/drawable-hdpi/btn_keyboard_key_fulltrans_pressed.9.png b/java/res/drawable-hdpi/btn_keyboard_key_fulltrans_pressed.9.png
deleted file mode 100644
index 207c90d..0000000
--- a/java/res/drawable-hdpi/btn_keyboard_key_fulltrans_pressed.9.png
+++ /dev/null
Binary files differ
diff --git a/java/res/drawable-hdpi/btn_keyboard_key_light_normal_holo.9.png b/java/res/drawable-hdpi/btn_keyboard_key_light_normal_holo.9.png
index baff858..6da273b 100644
--- a/java/res/drawable-hdpi/btn_keyboard_key_light_normal_holo.9.png
+++ b/java/res/drawable-hdpi/btn_keyboard_key_light_normal_holo.9.png
Binary files differ
diff --git a/java/res/drawable-hdpi/btn_keyboard_key_light_pressed_holo.9.png b/java/res/drawable-hdpi/btn_keyboard_key_light_pressed_holo.9.png
index 5612c51..6768241 100644
--- a/java/res/drawable-hdpi/btn_keyboard_key_light_pressed_holo.9.png
+++ b/java/res/drawable-hdpi/btn_keyboard_key_light_pressed_holo.9.png
Binary files differ
diff --git a/java/res/drawable-hdpi/btn_keyboard_key_normal_off_stone.9.png b/java/res/drawable-hdpi/btn_keyboard_key_normal_off_stone.9.png
deleted file mode 100644
index cdd6c8b..0000000
--- a/java/res/drawable-hdpi/btn_keyboard_key_normal_off_stone.9.png
+++ /dev/null
Binary files differ
diff --git a/java/res/drawable-hdpi/btn_keyboard_key_normal_on_stone.9.png b/java/res/drawable-hdpi/btn_keyboard_key_normal_on_stone.9.png
deleted file mode 100644
index d842174..0000000
--- a/java/res/drawable-hdpi/btn_keyboard_key_normal_on_stone.9.png
+++ /dev/null
Binary files differ
diff --git a/java/res/drawable-hdpi/btn_keyboard_key_normal_stone.9.png b/java/res/drawable-hdpi/btn_keyboard_key_normal_stone.9.png
deleted file mode 100644
index 671d4e5..0000000
--- a/java/res/drawable-hdpi/btn_keyboard_key_normal_stone.9.png
+++ /dev/null
Binary files differ
diff --git a/java/res/drawable-hdpi/btn_keyboard_key_popup_selected_holo.9.png b/java/res/drawable-hdpi/btn_keyboard_key_popup_selected_holo.9.png
index c2e8b37..10f8e97 100644
--- a/java/res/drawable-hdpi/btn_keyboard_key_popup_selected_holo.9.png
+++ b/java/res/drawable-hdpi/btn_keyboard_key_popup_selected_holo.9.png
Binary files differ
diff --git a/java/res/drawable-hdpi/ic_emoji_dark.png b/java/res/drawable-hdpi/ic_emoji_dark.png
deleted file mode 100644
index a9f18cd..0000000
--- a/java/res/drawable-hdpi/ic_emoji_dark.png
+++ /dev/null
Binary files differ
diff --git a/java/res/drawable-hdpi/ic_emoji_nature_light_activated.png b/java/res/drawable-hdpi/ic_emoji_nature_light_activated.png
new file mode 100644
index 0000000..5525df2
--- /dev/null
+++ b/java/res/drawable-hdpi/ic_emoji_nature_light_activated.png
Binary files differ
diff --git a/java/res/drawable-hdpi/ic_emoji_nature_light_normal.png b/java/res/drawable-hdpi/ic_emoji_nature_light_normal.png
new file mode 100644
index 0000000..34e16b9
--- /dev/null
+++ b/java/res/drawable-hdpi/ic_emoji_nature_light_normal.png
Binary files differ
diff --git a/java/res/drawable-hdpi/ic_emoji_objects_light_activated.png b/java/res/drawable-hdpi/ic_emoji_objects_light_activated.png
new file mode 100644
index 0000000..c3c7ec1
--- /dev/null
+++ b/java/res/drawable-hdpi/ic_emoji_objects_light_activated.png
Binary files differ
diff --git a/java/res/drawable-hdpi/ic_emoji_objects_light_normal.png b/java/res/drawable-hdpi/ic_emoji_objects_light_normal.png
new file mode 100644
index 0000000..f012d77
--- /dev/null
+++ b/java/res/drawable-hdpi/ic_emoji_objects_light_normal.png
Binary files differ
diff --git a/java/res/drawable-hdpi/ic_emoji_people_light_activated.png b/java/res/drawable-hdpi/ic_emoji_people_light_activated.png
new file mode 100644
index 0000000..cfacbc2
--- /dev/null
+++ b/java/res/drawable-hdpi/ic_emoji_people_light_activated.png
Binary files differ
diff --git a/java/res/drawable-hdpi/ic_emoji_light.png b/java/res/drawable-hdpi/ic_emoji_people_light_normal.png
similarity index 92%
rename from java/res/drawable-hdpi/ic_emoji_light.png
rename to java/res/drawable-hdpi/ic_emoji_people_light_normal.png
index 2e3638b..c54dbc1 100644
--- a/java/res/drawable-hdpi/ic_emoji_light.png
+++ b/java/res/drawable-hdpi/ic_emoji_people_light_normal.png
Binary files differ
diff --git a/java/res/drawable-hdpi/ic_emoji_places_light_activated.png b/java/res/drawable-hdpi/ic_emoji_places_light_activated.png
new file mode 100644
index 0000000..959dfdf
--- /dev/null
+++ b/java/res/drawable-hdpi/ic_emoji_places_light_activated.png
Binary files differ
diff --git a/java/res/drawable-hdpi/ic_emoji_places_light_normal.png b/java/res/drawable-hdpi/ic_emoji_places_light_normal.png
new file mode 100644
index 0000000..fc0d971
--- /dev/null
+++ b/java/res/drawable-hdpi/ic_emoji_places_light_normal.png
Binary files differ
diff --git a/java/res/drawable-hdpi/ic_emoji_recent_light_activated.png b/java/res/drawable-hdpi/ic_emoji_recent_light_activated.png
new file mode 100644
index 0000000..de570a1
--- /dev/null
+++ b/java/res/drawable-hdpi/ic_emoji_recent_light_activated.png
Binary files differ
diff --git a/java/res/drawable-hdpi/ic_emoji_recent_light_normal.png b/java/res/drawable-hdpi/ic_emoji_recent_light_normal.png
new file mode 100644
index 0000000..b256208
--- /dev/null
+++ b/java/res/drawable-hdpi/ic_emoji_recent_light_normal.png
Binary files differ
diff --git a/java/res/drawable-hdpi/ic_emoji_symbols_light_activated.png b/java/res/drawable-hdpi/ic_emoji_symbols_light_activated.png
new file mode 100644
index 0000000..af1fd27
--- /dev/null
+++ b/java/res/drawable-hdpi/ic_emoji_symbols_light_activated.png
Binary files differ
diff --git a/java/res/drawable-hdpi/ic_emoji_symbols_light_normal.png b/java/res/drawable-hdpi/ic_emoji_symbols_light_normal.png
new file mode 100644
index 0000000..02b84d5
--- /dev/null
+++ b/java/res/drawable-hdpi/ic_emoji_symbols_light_normal.png
Binary files differ
diff --git a/java/res/drawable-hdpi/ic_ime_switcher_dark.png b/java/res/drawable-hdpi/ic_ime_switcher_dark.png
new file mode 100644
index 0000000..7506af5
--- /dev/null
+++ b/java/res/drawable-hdpi/ic_ime_switcher_dark.png
Binary files differ
diff --git a/java/res/drawable-hdpi/ic_settings_language.png b/java/res/drawable-hdpi/ic_settings_language.png
deleted file mode 100644
index f635b2e..0000000
--- a/java/res/drawable-hdpi/ic_settings_language.png
+++ /dev/null
Binary files differ
diff --git a/java/res/drawable-hdpi/ic_subtype_keyboard.png b/java/res/drawable-hdpi/ic_subtype_keyboard.png
deleted file mode 100644
index 4843056..0000000
--- a/java/res/drawable-hdpi/ic_subtype_keyboard.png
+++ /dev/null
Binary files differ
diff --git a/java/res/drawable-hdpi/ic_subtype_mic_dark.png b/java/res/drawable-hdpi/ic_subtype_mic_dark.png
new file mode 100644
index 0000000..eacbcd2
--- /dev/null
+++ b/java/res/drawable-hdpi/ic_subtype_mic_dark.png
Binary files differ
diff --git a/java/res/drawable-hdpi/keyboard_background.9.png b/java/res/drawable-hdpi/keyboard_background.9.png
deleted file mode 100644
index d57463f..0000000
--- a/java/res/drawable-hdpi/keyboard_background.9.png
+++ /dev/null
Binary files differ
diff --git a/java/res/drawable-hdpi/keyboard_dark_background.9.png b/java/res/drawable-hdpi/keyboard_background_gb.9.png
similarity index 100%
rename from java/res/drawable-hdpi/keyboard_dark_background.9.png
rename to java/res/drawable-hdpi/keyboard_background_gb.9.png
Binary files differ
diff --git a/java/res/drawable-hdpi/keyboard_key_feedback_background_holo.9.png b/java/res/drawable-hdpi/keyboard_key_feedback_background_holo.9.png
index 28b406a..50ed568 100644
--- a/java/res/drawable-hdpi/keyboard_key_feedback_background_holo.9.png
+++ b/java/res/drawable-hdpi/keyboard_key_feedback_background_holo.9.png
Binary files differ
diff --git a/java/res/drawable-hdpi/keyboard_key_feedback_left_background_holo.9.png b/java/res/drawable-hdpi/keyboard_key_feedback_left_background_holo.9.png
index e42cd88..9fa6d00 100644
--- a/java/res/drawable-hdpi/keyboard_key_feedback_left_background_holo.9.png
+++ b/java/res/drawable-hdpi/keyboard_key_feedback_left_background_holo.9.png
Binary files differ
diff --git a/java/res/drawable-hdpi/keyboard_key_feedback_left_more_background_holo.9.png b/java/res/drawable-hdpi/keyboard_key_feedback_left_more_background_holo.9.png
index 1603440..c73269b 100644
--- a/java/res/drawable-hdpi/keyboard_key_feedback_left_more_background_holo.9.png
+++ b/java/res/drawable-hdpi/keyboard_key_feedback_left_more_background_holo.9.png
Binary files differ
diff --git a/java/res/drawable-hdpi/keyboard_key_feedback_more_background_holo.9.png b/java/res/drawable-hdpi/keyboard_key_feedback_more_background_holo.9.png
index a40d427..fffd402 100644
--- a/java/res/drawable-hdpi/keyboard_key_feedback_more_background_holo.9.png
+++ b/java/res/drawable-hdpi/keyboard_key_feedback_more_background_holo.9.png
Binary files differ
diff --git a/java/res/drawable-hdpi/keyboard_key_feedback_right_background_holo.9.png b/java/res/drawable-hdpi/keyboard_key_feedback_right_background_holo.9.png
index 1f68073..61c23c1 100644
--- a/java/res/drawable-hdpi/keyboard_key_feedback_right_background_holo.9.png
+++ b/java/res/drawable-hdpi/keyboard_key_feedback_right_background_holo.9.png
Binary files differ
diff --git a/java/res/drawable-hdpi/keyboard_key_feedback_right_more_background_holo.9.png b/java/res/drawable-hdpi/keyboard_key_feedback_right_more_background_holo.9.png
index ec53593..827d743 100644
--- a/java/res/drawable-hdpi/keyboard_key_feedback_right_more_background_holo.9.png
+++ b/java/res/drawable-hdpi/keyboard_key_feedback_right_more_background_holo.9.png
Binary files differ
diff --git a/java/res/drawable-hdpi/keyboard_popup_panel_background.9.png b/java/res/drawable-hdpi/keyboard_popup_panel_background_gb.9.png
similarity index 100%
rename from java/res/drawable-hdpi/keyboard_popup_panel_background.9.png
rename to java/res/drawable-hdpi/keyboard_popup_panel_background_gb.9.png
Binary files differ
diff --git a/java/res/drawable-hdpi/keyboard_popup_panel_background_holo.9.png b/java/res/drawable-hdpi/keyboard_popup_panel_background_holo.9.png
index 53d7b6f..dc2fc7d 100644
--- a/java/res/drawable-hdpi/keyboard_popup_panel_background_holo.9.png
+++ b/java/res/drawable-hdpi/keyboard_popup_panel_background_holo.9.png
Binary files differ
diff --git a/java/res/drawable-hdpi/keyboard_suggest_strip.9.png b/java/res/drawable-hdpi/keyboard_suggest_strip_gb.9.png
similarity index 100%
rename from java/res/drawable-hdpi/keyboard_suggest_strip.9.png
rename to java/res/drawable-hdpi/keyboard_suggest_strip_gb.9.png
Binary files differ
diff --git a/java/res/drawable-hdpi/keyboard_suggest_strip_holo.9.png b/java/res/drawable-hdpi/keyboard_suggest_strip_holo.9.png
index e173beb..32f4264 100644
--- a/java/res/drawable-hdpi/keyboard_suggest_strip_holo.9.png
+++ b/java/res/drawable-hdpi/keyboard_suggest_strip_holo.9.png
Binary files differ
diff --git a/java/res/drawable-hdpi/sym_bkeyboard_delete.png b/java/res/drawable-hdpi/sym_bkeyboard_delete.png
deleted file mode 100644
index 1d24cc8..0000000
--- a/java/res/drawable-hdpi/sym_bkeyboard_delete.png
+++ /dev/null
Binary files differ
diff --git a/java/res/drawable-hdpi/sym_bkeyboard_label_mic.png b/java/res/drawable-hdpi/sym_bkeyboard_label_mic.png
deleted file mode 100644
index 25702cf..0000000
--- a/java/res/drawable-hdpi/sym_bkeyboard_label_mic.png
+++ /dev/null
Binary files differ
diff --git a/java/res/drawable-hdpi/sym_bkeyboard_mic.png b/java/res/drawable-hdpi/sym_bkeyboard_mic.png
deleted file mode 100644
index 512f460..0000000
--- a/java/res/drawable-hdpi/sym_bkeyboard_mic.png
+++ /dev/null
Binary files differ
diff --git a/java/res/drawable-hdpi/sym_bkeyboard_return.png b/java/res/drawable-hdpi/sym_bkeyboard_return.png
deleted file mode 100644
index 426e159..0000000
--- a/java/res/drawable-hdpi/sym_bkeyboard_return.png
+++ /dev/null
Binary files differ
diff --git a/java/res/drawable-hdpi/sym_bkeyboard_search.png b/java/res/drawable-hdpi/sym_bkeyboard_search.png
deleted file mode 100644
index 1b6f884..0000000
--- a/java/res/drawable-hdpi/sym_bkeyboard_search.png
+++ /dev/null
Binary files differ
diff --git a/java/res/drawable-hdpi/sym_bkeyboard_settings.png b/java/res/drawable-hdpi/sym_bkeyboard_settings.png
deleted file mode 100644
index 08ba18f..0000000
--- a/java/res/drawable-hdpi/sym_bkeyboard_settings.png
+++ /dev/null
Binary files differ
diff --git a/java/res/drawable-hdpi/sym_bkeyboard_shift.png b/java/res/drawable-hdpi/sym_bkeyboard_shift.png
deleted file mode 100644
index 5a22dd3..0000000
--- a/java/res/drawable-hdpi/sym_bkeyboard_shift.png
+++ /dev/null
Binary files differ
diff --git a/java/res/drawable-hdpi/sym_bkeyboard_shift_locked.png b/java/res/drawable-hdpi/sym_bkeyboard_shift_locked.png
deleted file mode 100644
index 5664491..0000000
--- a/java/res/drawable-hdpi/sym_bkeyboard_shift_locked.png
+++ /dev/null
Binary files differ
diff --git a/java/res/drawable-hdpi/sym_bkeyboard_space.png b/java/res/drawable-hdpi/sym_bkeyboard_space.png
deleted file mode 100644
index cd0ebe2..0000000
--- a/java/res/drawable-hdpi/sym_bkeyboard_space.png
+++ /dev/null
Binary files differ
diff --git a/java/res/drawable-hdpi/sym_bkeyboard_tab.png b/java/res/drawable-hdpi/sym_bkeyboard_tab.png
deleted file mode 100644
index 3466e12..0000000
--- a/java/res/drawable-hdpi/sym_bkeyboard_tab.png
+++ /dev/null
Binary files differ
diff --git a/java/res/drawable-hdpi/sym_bkeyboard_voice_off.png b/java/res/drawable-hdpi/sym_bkeyboard_voice_off.png
deleted file mode 100644
index 081a130..0000000
--- a/java/res/drawable-hdpi/sym_bkeyboard_voice_off.png
+++ /dev/null
Binary files differ
diff --git a/java/res/drawable-hdpi/sym_keyboard_delete.png b/java/res/drawable-hdpi/sym_keyboard_delete.png
deleted file mode 100644
index 0591b82..0000000
--- a/java/res/drawable-hdpi/sym_keyboard_delete.png
+++ /dev/null
Binary files differ
diff --git a/java/res/drawable-hdpi/sym_keyboard_delete_holo.png b/java/res/drawable-hdpi/sym_keyboard_delete_holo.png
deleted file mode 100644
index d3e1088..0000000
--- a/java/res/drawable-hdpi/sym_keyboard_delete_holo.png
+++ /dev/null
Binary files differ
diff --git a/java/res/drawable-hdpi/sym_keyboard_delete_holo_dark.png b/java/res/drawable-hdpi/sym_keyboard_delete_holo_dark.png
new file mode 100644
index 0000000..d2d3560
--- /dev/null
+++ b/java/res/drawable-hdpi/sym_keyboard_delete_holo_dark.png
Binary files differ
diff --git a/java/res/drawable-hdpi/sym_keyboard_label_mic.png b/java/res/drawable-hdpi/sym_keyboard_label_mic.png
deleted file mode 100644
index 4e0a8ed..0000000
--- a/java/res/drawable-hdpi/sym_keyboard_label_mic.png
+++ /dev/null
Binary files differ
diff --git a/java/res/drawable-hdpi/sym_keyboard_label_mic_holo.png b/java/res/drawable-hdpi/sym_keyboard_label_mic_holo.png
deleted file mode 100644
index f8df447..0000000
--- a/java/res/drawable-hdpi/sym_keyboard_label_mic_holo.png
+++ /dev/null
Binary files differ
diff --git a/java/res/drawable-hdpi/sym_keyboard_language_switch.png b/java/res/drawable-hdpi/sym_keyboard_language_switch.png
deleted file mode 100644
index 7b980a0..0000000
--- a/java/res/drawable-hdpi/sym_keyboard_language_switch.png
+++ /dev/null
Binary files differ
diff --git a/java/res/drawable-hdpi/sym_keyboard_language_switch_dark.png b/java/res/drawable-hdpi/sym_keyboard_language_switch_dark.png
new file mode 100644
index 0000000..78d3a1f
--- /dev/null
+++ b/java/res/drawable-hdpi/sym_keyboard_language_switch_dark.png
Binary files differ
diff --git a/java/res/drawable-hdpi/sym_keyboard_mic.png b/java/res/drawable-hdpi/sym_keyboard_mic.png
deleted file mode 100644
index 520a40f..0000000
--- a/java/res/drawable-hdpi/sym_keyboard_mic.png
+++ /dev/null
Binary files differ
diff --git a/java/res/drawable-hdpi/sym_keyboard_mic_holo_dark.png b/java/res/drawable-hdpi/sym_keyboard_mic_holo_dark.png
new file mode 100644
index 0000000..3c54694
--- /dev/null
+++ b/java/res/drawable-hdpi/sym_keyboard_mic_holo_dark.png
Binary files differ
diff --git a/java/res/drawable-hdpi/sym_keyboard_return.png b/java/res/drawable-hdpi/sym_keyboard_return.png
deleted file mode 100644
index 9743c7f..0000000
--- a/java/res/drawable-hdpi/sym_keyboard_return.png
+++ /dev/null
Binary files differ
diff --git a/java/res/drawable-hdpi/sym_keyboard_return_holo.png b/java/res/drawable-hdpi/sym_keyboard_return_holo.png
deleted file mode 100644
index 8978934..0000000
--- a/java/res/drawable-hdpi/sym_keyboard_return_holo.png
+++ /dev/null
Binary files differ
diff --git a/java/res/drawable-hdpi/sym_keyboard_return_holo_dark.png b/java/res/drawable-hdpi/sym_keyboard_return_holo_dark.png
new file mode 100644
index 0000000..60d893c
--- /dev/null
+++ b/java/res/drawable-hdpi/sym_keyboard_return_holo_dark.png
Binary files differ
diff --git a/java/res/drawable-hdpi/sym_keyboard_search.png b/java/res/drawable-hdpi/sym_keyboard_search.png
deleted file mode 100644
index 8cd28c6..0000000
--- a/java/res/drawable-hdpi/sym_keyboard_search.png
+++ /dev/null
Binary files differ
diff --git a/java/res/drawable-hdpi/sym_keyboard_search_holo.png b/java/res/drawable-hdpi/sym_keyboard_search_holo.png
deleted file mode 100644
index b987a20..0000000
--- a/java/res/drawable-hdpi/sym_keyboard_search_holo.png
+++ /dev/null
Binary files differ
diff --git a/java/res/drawable-hdpi/sym_keyboard_search_holo_dark.png b/java/res/drawable-hdpi/sym_keyboard_search_holo_dark.png
new file mode 100644
index 0000000..fa0d1bd
--- /dev/null
+++ b/java/res/drawable-hdpi/sym_keyboard_search_holo_dark.png
Binary files differ
diff --git a/java/res/drawable-hdpi/sym_keyboard_settings.png b/java/res/drawable-hdpi/sym_keyboard_settings.png
deleted file mode 100644
index 1e5bf93..0000000
--- a/java/res/drawable-hdpi/sym_keyboard_settings.png
+++ /dev/null
Binary files differ
diff --git a/java/res/drawable-hdpi/sym_keyboard_settings_holo.png b/java/res/drawable-hdpi/sym_keyboard_settings_holo.png
deleted file mode 100644
index 5af09ad..0000000
--- a/java/res/drawable-hdpi/sym_keyboard_settings_holo.png
+++ /dev/null
Binary files differ
diff --git a/java/res/drawable-hdpi/sym_keyboard_settings_holo_dark.png b/java/res/drawable-hdpi/sym_keyboard_settings_holo_dark.png
new file mode 100644
index 0000000..c76008a
--- /dev/null
+++ b/java/res/drawable-hdpi/sym_keyboard_settings_holo_dark.png
Binary files differ
diff --git a/java/res/drawable-hdpi/sym_keyboard_shift.png b/java/res/drawable-hdpi/sym_keyboard_shift.png
deleted file mode 100644
index 8e3d032..0000000
--- a/java/res/drawable-hdpi/sym_keyboard_shift.png
+++ /dev/null
Binary files differ
diff --git a/java/res/drawable-hdpi/sym_keyboard_shift_holo.png b/java/res/drawable-hdpi/sym_keyboard_shift_holo.png
deleted file mode 100644
index c58f9ab..0000000
--- a/java/res/drawable-hdpi/sym_keyboard_shift_holo.png
+++ /dev/null
Binary files differ
diff --git a/java/res/drawable-hdpi/sym_keyboard_shift_holo_dark.png b/java/res/drawable-hdpi/sym_keyboard_shift_holo_dark.png
new file mode 100644
index 0000000..544b7e1
--- /dev/null
+++ b/java/res/drawable-hdpi/sym_keyboard_shift_holo_dark.png
Binary files differ
diff --git a/java/res/drawable-hdpi/sym_keyboard_shift_locked.png b/java/res/drawable-hdpi/sym_keyboard_shift_locked.png
deleted file mode 100644
index d345634..0000000
--- a/java/res/drawable-hdpi/sym_keyboard_shift_locked.png
+++ /dev/null
Binary files differ
diff --git a/java/res/drawable-hdpi/sym_keyboard_shift_locked_holo.png b/java/res/drawable-hdpi/sym_keyboard_shift_locked_holo.png
deleted file mode 100644
index 7a5c037..0000000
--- a/java/res/drawable-hdpi/sym_keyboard_shift_locked_holo.png
+++ /dev/null
Binary files differ
diff --git a/java/res/drawable-hdpi/sym_keyboard_shift_locked_holo_dark.png b/java/res/drawable-hdpi/sym_keyboard_shift_locked_holo_dark.png
new file mode 100644
index 0000000..9b1d6a0
--- /dev/null
+++ b/java/res/drawable-hdpi/sym_keyboard_shift_locked_holo_dark.png
Binary files differ
diff --git a/java/res/drawable-hdpi/sym_keyboard_space_holo.png b/java/res/drawable-hdpi/sym_keyboard_space_holo.png
deleted file mode 100644
index e8bc390..0000000
--- a/java/res/drawable-hdpi/sym_keyboard_space_holo.png
+++ /dev/null
Binary files differ
diff --git a/java/res/drawable-hdpi/sym_keyboard_space_holo_dark.png b/java/res/drawable-hdpi/sym_keyboard_space_holo_dark.png
new file mode 100644
index 0000000..12e27ad
--- /dev/null
+++ b/java/res/drawable-hdpi/sym_keyboard_space_holo_dark.png
Binary files differ
diff --git a/java/res/drawable-hdpi/sym_keyboard_space_led.9.png b/java/res/drawable-hdpi/sym_keyboard_space_led_gb.9.png
similarity index 100%
rename from java/res/drawable-hdpi/sym_keyboard_space_led.9.png
rename to java/res/drawable-hdpi/sym_keyboard_space_led_gb.9.png
Binary files differ
diff --git a/java/res/drawable-hdpi/sym_keyboard_tab.png b/java/res/drawable-hdpi/sym_keyboard_tab.png
deleted file mode 100644
index 3d1c5c0..0000000
--- a/java/res/drawable-hdpi/sym_keyboard_tab.png
+++ /dev/null
Binary files differ
diff --git a/java/res/drawable-hdpi/sym_keyboard_tab_holo.png b/java/res/drawable-hdpi/sym_keyboard_tab_holo.png
deleted file mode 100644
index 8d10d05..0000000
--- a/java/res/drawable-hdpi/sym_keyboard_tab_holo.png
+++ /dev/null
Binary files differ
diff --git a/java/res/drawable-hdpi/sym_keyboard_tab_holo_dark.png b/java/res/drawable-hdpi/sym_keyboard_tab_holo_dark.png
new file mode 100644
index 0000000..2e5f811
--- /dev/null
+++ b/java/res/drawable-hdpi/sym_keyboard_tab_holo_dark.png
Binary files differ
diff --git a/java/res/drawable-hdpi/sym_keyboard_voice_holo.png b/java/res/drawable-hdpi/sym_keyboard_voice_holo.png
deleted file mode 100644
index 8a6336a..0000000
--- a/java/res/drawable-hdpi/sym_keyboard_voice_holo.png
+++ /dev/null
Binary files differ
diff --git a/java/res/drawable-hdpi/sym_keyboard_voice_holo_dark.png b/java/res/drawable-hdpi/sym_keyboard_voice_holo_dark.png
new file mode 100644
index 0000000..c1e16a6
--- /dev/null
+++ b/java/res/drawable-hdpi/sym_keyboard_voice_holo_dark.png
Binary files differ
diff --git a/java/res/drawable-hdpi/sym_keyboard_voice_off_holo.png b/java/res/drawable-hdpi/sym_keyboard_voice_off_holo.png
deleted file mode 100644
index edf1379..0000000
--- a/java/res/drawable-hdpi/sym_keyboard_voice_off_holo.png
+++ /dev/null
Binary files differ
diff --git a/java/res/drawable-hdpi/sym_keyboard_voice_off_holo_dark.png b/java/res/drawable-hdpi/sym_keyboard_voice_off_holo_dark.png
new file mode 100644
index 0000000..26d0684
--- /dev/null
+++ b/java/res/drawable-hdpi/sym_keyboard_voice_off_holo_dark.png
Binary files differ
diff --git a/java/res/drawable-hdpi/sym_keyboard_zwj_holo.png b/java/res/drawable-hdpi/sym_keyboard_zwj_holo.png
deleted file mode 100644
index 5fa30ce..0000000
--- a/java/res/drawable-hdpi/sym_keyboard_zwj_holo.png
+++ /dev/null
Binary files differ
diff --git a/java/res/drawable-hdpi/sym_keyboard_zwj_holo_dark.png b/java/res/drawable-hdpi/sym_keyboard_zwj_holo_dark.png
new file mode 100644
index 0000000..9f9bc17
--- /dev/null
+++ b/java/res/drawable-hdpi/sym_keyboard_zwj_holo_dark.png
Binary files differ
diff --git a/java/res/drawable-hdpi/sym_keyboard_zwnj_holo.png b/java/res/drawable-hdpi/sym_keyboard_zwnj_holo.png
deleted file mode 100644
index 91367f3..0000000
--- a/java/res/drawable-hdpi/sym_keyboard_zwnj_holo.png
+++ /dev/null
Binary files differ
diff --git a/java/res/drawable-hdpi/sym_keyboard_zwnj_holo_dark.png b/java/res/drawable-hdpi/sym_keyboard_zwnj_holo_dark.png
new file mode 100644
index 0000000..f0f832e
--- /dev/null
+++ b/java/res/drawable-hdpi/sym_keyboard_zwnj_holo_dark.png
Binary files differ
diff --git a/java/res/drawable-hdpi/tab_selected.9.png b/java/res/drawable-hdpi/tab_selected.9.png
new file mode 100644
index 0000000..84e63df
--- /dev/null
+++ b/java/res/drawable-hdpi/tab_selected.9.png
Binary files differ
diff --git a/java/res/drawable-hdpi/tab_unselected.9.png b/java/res/drawable-hdpi/tab_unselected.9.png
new file mode 100644
index 0000000..bbcfb2c
--- /dev/null
+++ b/java/res/drawable-hdpi/tab_unselected.9.png
Binary files differ
diff --git a/java/res/drawable-mdpi/btn_center_default.9.png b/java/res/drawable-mdpi/btn_center_default.9.png
deleted file mode 100644
index d5ec36b..0000000
--- a/java/res/drawable-mdpi/btn_center_default.9.png
+++ /dev/null
Binary files differ
diff --git a/java/res/drawable-mdpi/btn_center_pressed.9.png b/java/res/drawable-mdpi/btn_center_pressed.9.png
deleted file mode 100644
index 593a679..0000000
--- a/java/res/drawable-mdpi/btn_center_pressed.9.png
+++ /dev/null
Binary files differ
diff --git a/java/res/drawable-mdpi/btn_center_selected.9.png b/java/res/drawable-mdpi/btn_center_selected.9.png
deleted file mode 100644
index f1914a8..0000000
--- a/java/res/drawable-mdpi/btn_center_selected.9.png
+++ /dev/null
Binary files differ
diff --git a/java/res/drawable-mdpi/btn_keyboard_key_dark_active_holo.9.png b/java/res/drawable-mdpi/btn_keyboard_key_dark_active_holo.9.png
index e810c77..f98653e 100644
--- a/java/res/drawable-mdpi/btn_keyboard_key_dark_active_holo.9.png
+++ b/java/res/drawable-mdpi/btn_keyboard_key_dark_active_holo.9.png
Binary files differ
diff --git a/java/res/drawable-mdpi/btn_keyboard_key_dark_normal_holo.9.png b/java/res/drawable-mdpi/btn_keyboard_key_dark_normal_holo.9.png
index d449d76..8e9a349 100644
--- a/java/res/drawable-mdpi/btn_keyboard_key_dark_normal_holo.9.png
+++ b/java/res/drawable-mdpi/btn_keyboard_key_dark_normal_holo.9.png
Binary files differ
diff --git a/java/res/drawable-mdpi/btn_keyboard_key_dark_normal_off_holo.9.png b/java/res/drawable-mdpi/btn_keyboard_key_dark_normal_off_holo.9.png
index fa24d59..58a316f 100644
--- a/java/res/drawable-mdpi/btn_keyboard_key_dark_normal_off_holo.9.png
+++ b/java/res/drawable-mdpi/btn_keyboard_key_dark_normal_off_holo.9.png
Binary files differ
diff --git a/java/res/drawable-mdpi/btn_keyboard_key_dark_normal_on_holo.9.png b/java/res/drawable-mdpi/btn_keyboard_key_dark_normal_on_holo.9.png
index f3fc641..b7b2dca 100644
--- a/java/res/drawable-mdpi/btn_keyboard_key_dark_normal_on_holo.9.png
+++ b/java/res/drawable-mdpi/btn_keyboard_key_dark_normal_on_holo.9.png
Binary files differ
diff --git a/java/res/drawable-mdpi/btn_keyboard_key_dark_pressed_holo.9.png b/java/res/drawable-mdpi/btn_keyboard_key_dark_pressed_holo.9.png
index 8f340d3..4a92b80 100644
--- a/java/res/drawable-mdpi/btn_keyboard_key_dark_pressed_holo.9.png
+++ b/java/res/drawable-mdpi/btn_keyboard_key_dark_pressed_holo.9.png
Binary files differ
diff --git a/java/res/drawable-mdpi/btn_keyboard_key_dark_pressed_off_holo.9.png b/java/res/drawable-mdpi/btn_keyboard_key_dark_pressed_off_holo.9.png
index 53ea5f8..72125a0 100644
--- a/java/res/drawable-mdpi/btn_keyboard_key_dark_pressed_off_holo.9.png
+++ b/java/res/drawable-mdpi/btn_keyboard_key_dark_pressed_off_holo.9.png
Binary files differ
diff --git a/java/res/drawable-mdpi/btn_keyboard_key_dark_pressed_on_holo.9.png b/java/res/drawable-mdpi/btn_keyboard_key_dark_pressed_on_holo.9.png
index 69c84e7..82413d4 100644
--- a/java/res/drawable-mdpi/btn_keyboard_key_dark_pressed_on_holo.9.png
+++ b/java/res/drawable-mdpi/btn_keyboard_key_dark_pressed_on_holo.9.png
Binary files differ
diff --git a/java/res/drawable-mdpi/btn_keyboard_key_fulltrans_normal.9.png b/java/res/drawable-mdpi/btn_keyboard_key_fulltrans_normal.9.png
deleted file mode 100644
index 4b1a78c..0000000
--- a/java/res/drawable-mdpi/btn_keyboard_key_fulltrans_normal.9.png
+++ /dev/null
Binary files differ
diff --git a/java/res/drawable-mdpi/btn_keyboard_key_fulltrans_pressed.9.png b/java/res/drawable-mdpi/btn_keyboard_key_fulltrans_pressed.9.png
deleted file mode 100644
index 697683e..0000000
--- a/java/res/drawable-mdpi/btn_keyboard_key_fulltrans_pressed.9.png
+++ /dev/null
Binary files differ
diff --git a/java/res/drawable-mdpi/btn_keyboard_key_light_normal_holo.9.png b/java/res/drawable-mdpi/btn_keyboard_key_light_normal_holo.9.png
index 976083f..2915588 100644
--- a/java/res/drawable-mdpi/btn_keyboard_key_light_normal_holo.9.png
+++ b/java/res/drawable-mdpi/btn_keyboard_key_light_normal_holo.9.png
Binary files differ
diff --git a/java/res/drawable-mdpi/btn_keyboard_key_light_pressed_holo.9.png b/java/res/drawable-mdpi/btn_keyboard_key_light_pressed_holo.9.png
index c39dd4a..0493859 100644
--- a/java/res/drawable-mdpi/btn_keyboard_key_light_pressed_holo.9.png
+++ b/java/res/drawable-mdpi/btn_keyboard_key_light_pressed_holo.9.png
Binary files differ
diff --git a/java/res/drawable-mdpi/btn_keyboard_key_normal_off_stone.9.png b/java/res/drawable-mdpi/btn_keyboard_key_normal_off_stone.9.png
deleted file mode 100644
index cdd6c8b..0000000
--- a/java/res/drawable-mdpi/btn_keyboard_key_normal_off_stone.9.png
+++ /dev/null
Binary files differ
diff --git a/java/res/drawable-mdpi/btn_keyboard_key_normal_on_stone.9.png b/java/res/drawable-mdpi/btn_keyboard_key_normal_on_stone.9.png
deleted file mode 100644
index d842174..0000000
--- a/java/res/drawable-mdpi/btn_keyboard_key_normal_on_stone.9.png
+++ /dev/null
Binary files differ
diff --git a/java/res/drawable-mdpi/btn_keyboard_key_normal_stone.9.png b/java/res/drawable-mdpi/btn_keyboard_key_normal_stone.9.png
deleted file mode 100644
index 73cf35d..0000000
--- a/java/res/drawable-mdpi/btn_keyboard_key_normal_stone.9.png
+++ /dev/null
Binary files differ
diff --git a/java/res/drawable-mdpi/btn_keyboard_key_popup_selected_holo.9.png b/java/res/drawable-mdpi/btn_keyboard_key_popup_selected_holo.9.png
index 93a6e79..ee0aae2 100644
--- a/java/res/drawable-mdpi/btn_keyboard_key_popup_selected_holo.9.png
+++ b/java/res/drawable-mdpi/btn_keyboard_key_popup_selected_holo.9.png
Binary files differ
diff --git a/java/res/drawable-mdpi/ic_emoji_dark.png b/java/res/drawable-mdpi/ic_emoji_dark.png
deleted file mode 100644
index d0047a4..0000000
--- a/java/res/drawable-mdpi/ic_emoji_dark.png
+++ /dev/null
Binary files differ
diff --git a/java/res/drawable-mdpi/ic_emoji_nature_light_activated.png b/java/res/drawable-mdpi/ic_emoji_nature_light_activated.png
new file mode 100644
index 0000000..d4c8d8d
--- /dev/null
+++ b/java/res/drawable-mdpi/ic_emoji_nature_light_activated.png
Binary files differ
diff --git a/java/res/drawable-mdpi/ic_emoji_nature_light_normal.png b/java/res/drawable-mdpi/ic_emoji_nature_light_normal.png
new file mode 100644
index 0000000..1555aa7
--- /dev/null
+++ b/java/res/drawable-mdpi/ic_emoji_nature_light_normal.png
Binary files differ
diff --git a/java/res/drawable-mdpi/ic_emoji_objects_light_activated.png b/java/res/drawable-mdpi/ic_emoji_objects_light_activated.png
new file mode 100644
index 0000000..081dc66
--- /dev/null
+++ b/java/res/drawable-mdpi/ic_emoji_objects_light_activated.png
Binary files differ
diff --git a/java/res/drawable-mdpi/ic_emoji_objects_light_normal.png b/java/res/drawable-mdpi/ic_emoji_objects_light_normal.png
new file mode 100644
index 0000000..58e6f6e
--- /dev/null
+++ b/java/res/drawable-mdpi/ic_emoji_objects_light_normal.png
Binary files differ
diff --git a/java/res/drawable-mdpi/ic_emoji_people_light_activated.png b/java/res/drawable-mdpi/ic_emoji_people_light_activated.png
new file mode 100644
index 0000000..067ad54
--- /dev/null
+++ b/java/res/drawable-mdpi/ic_emoji_people_light_activated.png
Binary files differ
diff --git a/java/res/drawable-mdpi/ic_emoji_light.png b/java/res/drawable-mdpi/ic_emoji_people_light_normal.png
similarity index 88%
rename from java/res/drawable-mdpi/ic_emoji_light.png
rename to java/res/drawable-mdpi/ic_emoji_people_light_normal.png
index a319504..d835d4e 100644
--- a/java/res/drawable-mdpi/ic_emoji_light.png
+++ b/java/res/drawable-mdpi/ic_emoji_people_light_normal.png
Binary files differ
diff --git a/java/res/drawable-mdpi/ic_emoji_places_light_activated.png b/java/res/drawable-mdpi/ic_emoji_places_light_activated.png
new file mode 100644
index 0000000..1aecec5
--- /dev/null
+++ b/java/res/drawable-mdpi/ic_emoji_places_light_activated.png
Binary files differ
diff --git a/java/res/drawable-mdpi/ic_emoji_places_light_normal.png b/java/res/drawable-mdpi/ic_emoji_places_light_normal.png
new file mode 100644
index 0000000..c70e484
--- /dev/null
+++ b/java/res/drawable-mdpi/ic_emoji_places_light_normal.png
Binary files differ
diff --git a/java/res/drawable-mdpi/ic_emoji_recent_light_activated.png b/java/res/drawable-mdpi/ic_emoji_recent_light_activated.png
new file mode 100644
index 0000000..8009e93
--- /dev/null
+++ b/java/res/drawable-mdpi/ic_emoji_recent_light_activated.png
Binary files differ
diff --git a/java/res/drawable-mdpi/ic_emoji_recent_light_normal.png b/java/res/drawable-mdpi/ic_emoji_recent_light_normal.png
new file mode 100644
index 0000000..c2e598d
--- /dev/null
+++ b/java/res/drawable-mdpi/ic_emoji_recent_light_normal.png
Binary files differ
diff --git a/java/res/drawable-mdpi/ic_emoji_symbols_light_activated.png b/java/res/drawable-mdpi/ic_emoji_symbols_light_activated.png
new file mode 100644
index 0000000..caea871
--- /dev/null
+++ b/java/res/drawable-mdpi/ic_emoji_symbols_light_activated.png
Binary files differ
diff --git a/java/res/drawable-mdpi/ic_emoji_symbols_light_normal.png b/java/res/drawable-mdpi/ic_emoji_symbols_light_normal.png
new file mode 100644
index 0000000..0edada6
--- /dev/null
+++ b/java/res/drawable-mdpi/ic_emoji_symbols_light_normal.png
Binary files differ
diff --git a/java/res/drawable-mdpi/ic_ime_switcher_dark.png b/java/res/drawable-mdpi/ic_ime_switcher_dark.png
new file mode 100644
index 0000000..152f653
--- /dev/null
+++ b/java/res/drawable-mdpi/ic_ime_switcher_dark.png
Binary files differ
diff --git a/java/res/drawable-mdpi/ic_settings_language.png b/java/res/drawable-mdpi/ic_settings_language.png
deleted file mode 100644
index f8aca67..0000000
--- a/java/res/drawable-mdpi/ic_settings_language.png
+++ /dev/null
Binary files differ
diff --git a/java/res/drawable-mdpi/ic_subtype_keyboard.png b/java/res/drawable-mdpi/ic_subtype_keyboard.png
deleted file mode 100644
index d28efc1..0000000
--- a/java/res/drawable-mdpi/ic_subtype_keyboard.png
+++ /dev/null
Binary files differ
diff --git a/java/res/drawable-mdpi/keyboard_background.9.png b/java/res/drawable-mdpi/keyboard_background.9.png
deleted file mode 100644
index 2bd4b62..0000000
--- a/java/res/drawable-mdpi/keyboard_background.9.png
+++ /dev/null
Binary files differ
diff --git a/java/res/drawable-mdpi/keyboard_dark_background.9.png b/java/res/drawable-mdpi/keyboard_background_gb.9.png
similarity index 100%
rename from java/res/drawable-mdpi/keyboard_dark_background.9.png
rename to java/res/drawable-mdpi/keyboard_background_gb.9.png
Binary files differ
diff --git a/java/res/drawable-mdpi/keyboard_key_feedback_background_holo.9.png b/java/res/drawable-mdpi/keyboard_key_feedback_background_holo.9.png
index 7a9f640..564f546 100644
--- a/java/res/drawable-mdpi/keyboard_key_feedback_background_holo.9.png
+++ b/java/res/drawable-mdpi/keyboard_key_feedback_background_holo.9.png
Binary files differ
diff --git a/java/res/drawable-mdpi/keyboard_key_feedback_left_background_holo.9.png b/java/res/drawable-mdpi/keyboard_key_feedback_left_background_holo.9.png
index 5b06f09..427c870 100644
--- a/java/res/drawable-mdpi/keyboard_key_feedback_left_background_holo.9.png
+++ b/java/res/drawable-mdpi/keyboard_key_feedback_left_background_holo.9.png
Binary files differ
diff --git a/java/res/drawable-mdpi/keyboard_key_feedback_left_more_background_holo.9.png b/java/res/drawable-mdpi/keyboard_key_feedback_left_more_background_holo.9.png
index fd992d6..ea75729 100644
--- a/java/res/drawable-mdpi/keyboard_key_feedback_left_more_background_holo.9.png
+++ b/java/res/drawable-mdpi/keyboard_key_feedback_left_more_background_holo.9.png
Binary files differ
diff --git a/java/res/drawable-mdpi/keyboard_key_feedback_more_background_holo.9.png b/java/res/drawable-mdpi/keyboard_key_feedback_more_background_holo.9.png
index 128dcd6..1911c42 100644
--- a/java/res/drawable-mdpi/keyboard_key_feedback_more_background_holo.9.png
+++ b/java/res/drawable-mdpi/keyboard_key_feedback_more_background_holo.9.png
Binary files differ
diff --git a/java/res/drawable-mdpi/keyboard_key_feedback_right_background_holo.9.png b/java/res/drawable-mdpi/keyboard_key_feedback_right_background_holo.9.png
index 0b08d17..cdef116 100644
--- a/java/res/drawable-mdpi/keyboard_key_feedback_right_background_holo.9.png
+++ b/java/res/drawable-mdpi/keyboard_key_feedback_right_background_holo.9.png
Binary files differ
diff --git a/java/res/drawable-mdpi/keyboard_key_feedback_right_more_background_holo.9.png b/java/res/drawable-mdpi/keyboard_key_feedback_right_more_background_holo.9.png
index cf0b33c..dea5d07 100644
--- a/java/res/drawable-mdpi/keyboard_key_feedback_right_more_background_holo.9.png
+++ b/java/res/drawable-mdpi/keyboard_key_feedback_right_more_background_holo.9.png
Binary files differ
diff --git a/java/res/drawable-mdpi/keyboard_popup_panel_background.9.png b/java/res/drawable-mdpi/keyboard_popup_panel_background_gb.9.png
similarity index 100%
rename from java/res/drawable-mdpi/keyboard_popup_panel_background.9.png
rename to java/res/drawable-mdpi/keyboard_popup_panel_background_gb.9.png
Binary files differ
diff --git a/java/res/drawable-mdpi/keyboard_popup_panel_background_holo.9.png b/java/res/drawable-mdpi/keyboard_popup_panel_background_holo.9.png
index 61988a8..441edc3 100644
--- a/java/res/drawable-mdpi/keyboard_popup_panel_background_holo.9.png
+++ b/java/res/drawable-mdpi/keyboard_popup_panel_background_holo.9.png
Binary files differ
diff --git a/java/res/drawable-mdpi/keyboard_suggest_strip.9.png b/java/res/drawable-mdpi/keyboard_suggest_strip_gb.9.png
similarity index 100%
rename from java/res/drawable-mdpi/keyboard_suggest_strip.9.png
rename to java/res/drawable-mdpi/keyboard_suggest_strip_gb.9.png
Binary files differ
diff --git a/java/res/drawable-mdpi/suggestions_strip_divider.png b/java/res/drawable-mdpi/suggestions_strip_divider.png
index 2dbe2f9..21e9049 100644
--- a/java/res/drawable-mdpi/suggestions_strip_divider.png
+++ b/java/res/drawable-mdpi/suggestions_strip_divider.png
Binary files differ
diff --git a/java/res/drawable-mdpi/sym_bkeyboard_delete.png b/java/res/drawable-mdpi/sym_bkeyboard_delete.png
deleted file mode 100644
index 1a5ff43..0000000
--- a/java/res/drawable-mdpi/sym_bkeyboard_delete.png
+++ /dev/null
Binary files differ
diff --git a/java/res/drawable-mdpi/sym_bkeyboard_label_mic.png b/java/res/drawable-mdpi/sym_bkeyboard_label_mic.png
deleted file mode 100644
index 7f0b135..0000000
--- a/java/res/drawable-mdpi/sym_bkeyboard_label_mic.png
+++ /dev/null
Binary files differ
diff --git a/java/res/drawable-mdpi/sym_bkeyboard_mic.png b/java/res/drawable-mdpi/sym_bkeyboard_mic.png
deleted file mode 100644
index a6cb1cc..0000000
--- a/java/res/drawable-mdpi/sym_bkeyboard_mic.png
+++ /dev/null
Binary files differ
diff --git a/java/res/drawable-mdpi/sym_bkeyboard_return.png b/java/res/drawable-mdpi/sym_bkeyboard_return.png
deleted file mode 100644
index e76225d..0000000
--- a/java/res/drawable-mdpi/sym_bkeyboard_return.png
+++ /dev/null
Binary files differ
diff --git a/java/res/drawable-mdpi/sym_bkeyboard_search.png b/java/res/drawable-mdpi/sym_bkeyboard_search.png
deleted file mode 100644
index 1f18015..0000000
--- a/java/res/drawable-mdpi/sym_bkeyboard_search.png
+++ /dev/null
Binary files differ
diff --git a/java/res/drawable-mdpi/sym_bkeyboard_settings.png b/java/res/drawable-mdpi/sym_bkeyboard_settings.png
deleted file mode 100644
index 08ba18f..0000000
--- a/java/res/drawable-mdpi/sym_bkeyboard_settings.png
+++ /dev/null
Binary files differ
diff --git a/java/res/drawable-mdpi/sym_bkeyboard_shift.png b/java/res/drawable-mdpi/sym_bkeyboard_shift.png
deleted file mode 100644
index c981188..0000000
--- a/java/res/drawable-mdpi/sym_bkeyboard_shift.png
+++ /dev/null
Binary files differ
diff --git a/java/res/drawable-mdpi/sym_bkeyboard_shift_locked.png b/java/res/drawable-mdpi/sym_bkeyboard_shift_locked.png
deleted file mode 100644
index b8cebd0..0000000
--- a/java/res/drawable-mdpi/sym_bkeyboard_shift_locked.png
+++ /dev/null
Binary files differ
diff --git a/java/res/drawable-mdpi/sym_bkeyboard_space.png b/java/res/drawable-mdpi/sym_bkeyboard_space.png
deleted file mode 100644
index 4da7ee8..0000000
--- a/java/res/drawable-mdpi/sym_bkeyboard_space.png
+++ /dev/null
Binary files differ
diff --git a/java/res/drawable-mdpi/sym_bkeyboard_tab.png b/java/res/drawable-mdpi/sym_bkeyboard_tab.png
deleted file mode 100644
index 2cb991c..0000000
--- a/java/res/drawable-mdpi/sym_bkeyboard_tab.png
+++ /dev/null
Binary files differ
diff --git a/java/res/drawable-mdpi/sym_bkeyboard_voice_off.png b/java/res/drawable-mdpi/sym_bkeyboard_voice_off.png
deleted file mode 100644
index 081a130..0000000
--- a/java/res/drawable-mdpi/sym_bkeyboard_voice_off.png
+++ /dev/null
Binary files differ
diff --git a/java/res/drawable-mdpi/sym_keyboard_delete.png b/java/res/drawable-mdpi/sym_keyboard_delete.png
deleted file mode 100644
index 1b0f3f8..0000000
--- a/java/res/drawable-mdpi/sym_keyboard_delete.png
+++ /dev/null
Binary files differ
diff --git a/java/res/drawable-mdpi/sym_keyboard_delete_holo.png b/java/res/drawable-mdpi/sym_keyboard_delete_holo.png
deleted file mode 100644
index 86be351..0000000
--- a/java/res/drawable-mdpi/sym_keyboard_delete_holo.png
+++ /dev/null
Binary files differ
diff --git a/java/res/drawable-mdpi/sym_keyboard_delete_holo_dark.png b/java/res/drawable-mdpi/sym_keyboard_delete_holo_dark.png
new file mode 100644
index 0000000..edd9d16
--- /dev/null
+++ b/java/res/drawable-mdpi/sym_keyboard_delete_holo_dark.png
Binary files differ
diff --git a/java/res/drawable-mdpi/sym_keyboard_label_mic.png b/java/res/drawable-mdpi/sym_keyboard_label_mic.png
deleted file mode 100644
index a354d53..0000000
--- a/java/res/drawable-mdpi/sym_keyboard_label_mic.png
+++ /dev/null
Binary files differ
diff --git a/java/res/drawable-mdpi/sym_keyboard_label_mic_holo.png b/java/res/drawable-mdpi/sym_keyboard_label_mic_holo.png
deleted file mode 100644
index 15606e9..0000000
--- a/java/res/drawable-mdpi/sym_keyboard_label_mic_holo.png
+++ /dev/null
Binary files differ
diff --git a/java/res/drawable-mdpi/sym_keyboard_label_mic_holo_dark.png b/java/res/drawable-mdpi/sym_keyboard_label_mic_holo_dark.png
new file mode 100644
index 0000000..537f39b
--- /dev/null
+++ b/java/res/drawable-mdpi/sym_keyboard_label_mic_holo_dark.png
Binary files differ
diff --git a/java/res/drawable-mdpi/sym_keyboard_language_switch.png b/java/res/drawable-mdpi/sym_keyboard_language_switch.png
deleted file mode 100644
index f840a63..0000000
--- a/java/res/drawable-mdpi/sym_keyboard_language_switch.png
+++ /dev/null
Binary files differ
diff --git a/java/res/drawable-mdpi/sym_keyboard_language_switch_dark.png b/java/res/drawable-mdpi/sym_keyboard_language_switch_dark.png
new file mode 100644
index 0000000..828929b
--- /dev/null
+++ b/java/res/drawable-mdpi/sym_keyboard_language_switch_dark.png
Binary files differ
diff --git a/java/res/drawable-mdpi/sym_keyboard_mic.png b/java/res/drawable-mdpi/sym_keyboard_mic.png
deleted file mode 100644
index e926b3f..0000000
--- a/java/res/drawable-mdpi/sym_keyboard_mic.png
+++ /dev/null
Binary files differ
diff --git a/java/res/drawable-mdpi/sym_keyboard_mic_holo_dark.png b/java/res/drawable-mdpi/sym_keyboard_mic_holo_dark.png
new file mode 100644
index 0000000..5e58866
--- /dev/null
+++ b/java/res/drawable-mdpi/sym_keyboard_mic_holo_dark.png
Binary files differ
diff --git a/java/res/drawable-mdpi/sym_keyboard_mic_holo_light.png b/java/res/drawable-mdpi/sym_keyboard_mic_holo_light.png
new file mode 100644
index 0000000..84a63dc
--- /dev/null
+++ b/java/res/drawable-mdpi/sym_keyboard_mic_holo_light.png
Binary files differ
diff --git a/java/res/drawable-mdpi/sym_keyboard_return.png b/java/res/drawable-mdpi/sym_keyboard_return.png
deleted file mode 100644
index 0c10f00..0000000
--- a/java/res/drawable-mdpi/sym_keyboard_return.png
+++ /dev/null
Binary files differ
diff --git a/java/res/drawable-mdpi/sym_keyboard_return_holo.png b/java/res/drawable-mdpi/sym_keyboard_return_holo.png
deleted file mode 100644
index bfcb913..0000000
--- a/java/res/drawable-mdpi/sym_keyboard_return_holo.png
+++ /dev/null
Binary files differ
diff --git a/java/res/drawable-mdpi/sym_keyboard_return_holo_dark.png b/java/res/drawable-mdpi/sym_keyboard_return_holo_dark.png
new file mode 100644
index 0000000..e10103c
--- /dev/null
+++ b/java/res/drawable-mdpi/sym_keyboard_return_holo_dark.png
Binary files differ
diff --git a/java/res/drawable-mdpi/sym_keyboard_search.png b/java/res/drawable-mdpi/sym_keyboard_search.png
deleted file mode 100644
index 614f85f..0000000
--- a/java/res/drawable-mdpi/sym_keyboard_search.png
+++ /dev/null
Binary files differ
diff --git a/java/res/drawable-mdpi/sym_keyboard_search_holo.png b/java/res/drawable-mdpi/sym_keyboard_search_holo.png
deleted file mode 100644
index dd3c83a..0000000
--- a/java/res/drawable-mdpi/sym_keyboard_search_holo.png
+++ /dev/null
Binary files differ
diff --git a/java/res/drawable-mdpi/sym_keyboard_search_holo_dark.png b/java/res/drawable-mdpi/sym_keyboard_search_holo_dark.png
new file mode 100644
index 0000000..290cde4
--- /dev/null
+++ b/java/res/drawable-mdpi/sym_keyboard_search_holo_dark.png
Binary files differ
diff --git a/java/res/drawable-mdpi/sym_keyboard_settings.png b/java/res/drawable-mdpi/sym_keyboard_settings.png
deleted file mode 100644
index ad7618f..0000000
--- a/java/res/drawable-mdpi/sym_keyboard_settings.png
+++ /dev/null
Binary files differ
diff --git a/java/res/drawable-mdpi/sym_keyboard_settings_holo.png b/java/res/drawable-mdpi/sym_keyboard_settings_holo.png
deleted file mode 100644
index 36c8c96..0000000
--- a/java/res/drawable-mdpi/sym_keyboard_settings_holo.png
+++ /dev/null
Binary files differ
diff --git a/java/res/drawable-mdpi/sym_keyboard_settings_holo_dark.png b/java/res/drawable-mdpi/sym_keyboard_settings_holo_dark.png
new file mode 100644
index 0000000..a76a976
--- /dev/null
+++ b/java/res/drawable-mdpi/sym_keyboard_settings_holo_dark.png
Binary files differ
diff --git a/java/res/drawable-mdpi/sym_keyboard_shift.png b/java/res/drawable-mdpi/sym_keyboard_shift.png
deleted file mode 100644
index 5109b04..0000000
--- a/java/res/drawable-mdpi/sym_keyboard_shift.png
+++ /dev/null
Binary files differ
diff --git a/java/res/drawable-mdpi/sym_keyboard_shift_holo.png b/java/res/drawable-mdpi/sym_keyboard_shift_holo.png
deleted file mode 100644
index 6219464..0000000
--- a/java/res/drawable-mdpi/sym_keyboard_shift_holo.png
+++ /dev/null
Binary files differ
diff --git a/java/res/drawable-mdpi/sym_keyboard_shift_holo_dark.png b/java/res/drawable-mdpi/sym_keyboard_shift_holo_dark.png
new file mode 100644
index 0000000..37375d9
--- /dev/null
+++ b/java/res/drawable-mdpi/sym_keyboard_shift_holo_dark.png
Binary files differ
diff --git a/java/res/drawable-mdpi/sym_keyboard_shift_locked.png b/java/res/drawable-mdpi/sym_keyboard_shift_locked.png
deleted file mode 100644
index 244179c..0000000
--- a/java/res/drawable-mdpi/sym_keyboard_shift_locked.png
+++ /dev/null
Binary files differ
diff --git a/java/res/drawable-mdpi/sym_keyboard_shift_locked_holo.png b/java/res/drawable-mdpi/sym_keyboard_shift_locked_holo.png
deleted file mode 100644
index fb3a020..0000000
--- a/java/res/drawable-mdpi/sym_keyboard_shift_locked_holo.png
+++ /dev/null
Binary files differ
diff --git a/java/res/drawable-mdpi/sym_keyboard_shift_locked_holo_dark.png b/java/res/drawable-mdpi/sym_keyboard_shift_locked_holo_dark.png
new file mode 100644
index 0000000..3654868
--- /dev/null
+++ b/java/res/drawable-mdpi/sym_keyboard_shift_locked_holo_dark.png
Binary files differ
diff --git a/java/res/drawable-mdpi/sym_keyboard_smiley_holo_dark.png b/java/res/drawable-mdpi/sym_keyboard_smiley_holo_dark.png
new file mode 100644
index 0000000..71272bb
--- /dev/null
+++ b/java/res/drawable-mdpi/sym_keyboard_smiley_holo_dark.png
Binary files differ
diff --git a/java/res/drawable-mdpi/sym_keyboard_space_holo.png b/java/res/drawable-mdpi/sym_keyboard_space_holo.png
deleted file mode 100644
index 1f787d5..0000000
--- a/java/res/drawable-mdpi/sym_keyboard_space_holo.png
+++ /dev/null
Binary files differ
diff --git a/java/res/drawable-mdpi/sym_keyboard_space_holo_dark.png b/java/res/drawable-mdpi/sym_keyboard_space_holo_dark.png
new file mode 100644
index 0000000..a38f994
--- /dev/null
+++ b/java/res/drawable-mdpi/sym_keyboard_space_holo_dark.png
Binary files differ
diff --git a/java/res/drawable-mdpi/sym_keyboard_space_led.9.png b/java/res/drawable-mdpi/sym_keyboard_space_led_gb.9.png
similarity index 100%
rename from java/res/drawable-mdpi/sym_keyboard_space_led.9.png
rename to java/res/drawable-mdpi/sym_keyboard_space_led_gb.9.png
Binary files differ
diff --git a/java/res/drawable-mdpi/sym_keyboard_tab.png b/java/res/drawable-mdpi/sym_keyboard_tab.png
deleted file mode 100644
index eddb9a5..0000000
--- a/java/res/drawable-mdpi/sym_keyboard_tab.png
+++ /dev/null
Binary files differ
diff --git a/java/res/drawable-mdpi/sym_keyboard_tab_holo.png b/java/res/drawable-mdpi/sym_keyboard_tab_holo.png
deleted file mode 100644
index 8d20153..0000000
--- a/java/res/drawable-mdpi/sym_keyboard_tab_holo.png
+++ /dev/null
Binary files differ
diff --git a/java/res/drawable-mdpi/sym_keyboard_tab_holo_dark.png b/java/res/drawable-mdpi/sym_keyboard_tab_holo_dark.png
new file mode 100644
index 0000000..f883807
--- /dev/null
+++ b/java/res/drawable-mdpi/sym_keyboard_tab_holo_dark.png
Binary files differ
diff --git a/java/res/drawable-mdpi/sym_keyboard_voice_holo.png b/java/res/drawable-mdpi/sym_keyboard_voice_holo.png
deleted file mode 100644
index 0795fcc..0000000
--- a/java/res/drawable-mdpi/sym_keyboard_voice_holo.png
+++ /dev/null
Binary files differ
diff --git a/java/res/drawable-mdpi/sym_keyboard_voice_holo_dark.png b/java/res/drawable-mdpi/sym_keyboard_voice_holo_dark.png
new file mode 100644
index 0000000..16be37d
--- /dev/null
+++ b/java/res/drawable-mdpi/sym_keyboard_voice_holo_dark.png
Binary files differ
diff --git a/java/res/drawable-mdpi/sym_keyboard_voice_off_holo.png b/java/res/drawable-mdpi/sym_keyboard_voice_off_holo.png
deleted file mode 100644
index f76da57..0000000
--- a/java/res/drawable-mdpi/sym_keyboard_voice_off_holo.png
+++ /dev/null
Binary files differ
diff --git a/java/res/drawable-mdpi/sym_keyboard_voice_off_holo_dark.png b/java/res/drawable-mdpi/sym_keyboard_voice_off_holo_dark.png
new file mode 100644
index 0000000..95d718a
--- /dev/null
+++ b/java/res/drawable-mdpi/sym_keyboard_voice_off_holo_dark.png
Binary files differ
diff --git a/java/res/drawable-mdpi/sym_keyboard_zwj_holo.png b/java/res/drawable-mdpi/sym_keyboard_zwj_holo.png
deleted file mode 100644
index 70370d8..0000000
--- a/java/res/drawable-mdpi/sym_keyboard_zwj_holo.png
+++ /dev/null
Binary files differ
diff --git a/java/res/drawable-mdpi/sym_keyboard_zwj_holo_dark.png b/java/res/drawable-mdpi/sym_keyboard_zwj_holo_dark.png
new file mode 100644
index 0000000..8957e28
--- /dev/null
+++ b/java/res/drawable-mdpi/sym_keyboard_zwj_holo_dark.png
Binary files differ
diff --git a/java/res/drawable-mdpi/sym_keyboard_zwnj_holo.png b/java/res/drawable-mdpi/sym_keyboard_zwnj_holo.png
deleted file mode 100644
index a69eade..0000000
--- a/java/res/drawable-mdpi/sym_keyboard_zwnj_holo.png
+++ /dev/null
Binary files differ
diff --git a/java/res/drawable-mdpi/sym_keyboard_zwnj_holo_dark.png b/java/res/drawable-mdpi/sym_keyboard_zwnj_holo_dark.png
new file mode 100644
index 0000000..5f49e64
--- /dev/null
+++ b/java/res/drawable-mdpi/sym_keyboard_zwnj_holo_dark.png
Binary files differ
diff --git a/java/res/drawable-mdpi/tab_selected.9.png b/java/res/drawable-mdpi/tab_selected.9.png
new file mode 100644
index 0000000..4b00f35
--- /dev/null
+++ b/java/res/drawable-mdpi/tab_selected.9.png
Binary files differ
diff --git a/java/res/drawable-mdpi/tab_unselected.9.png b/java/res/drawable-mdpi/tab_unselected.9.png
new file mode 100644
index 0000000..bb45ab9
--- /dev/null
+++ b/java/res/drawable-mdpi/tab_unselected.9.png
Binary files differ
diff --git a/java/res/drawable-xhdpi/btn_center_default.9.png b/java/res/drawable-xhdpi/btn_center_default.9.png
deleted file mode 100644
index e847425..0000000
--- a/java/res/drawable-xhdpi/btn_center_default.9.png
+++ /dev/null
Binary files differ
diff --git a/java/res/drawable-xhdpi/btn_center_pressed.9.png b/java/res/drawable-xhdpi/btn_center_pressed.9.png
deleted file mode 100644
index facfd43..0000000
--- a/java/res/drawable-xhdpi/btn_center_pressed.9.png
+++ /dev/null
Binary files differ
diff --git a/java/res/drawable-xhdpi/btn_center_selected.9.png b/java/res/drawable-xhdpi/btn_center_selected.9.png
deleted file mode 100644
index facfd43..0000000
--- a/java/res/drawable-xhdpi/btn_center_selected.9.png
+++ /dev/null
Binary files differ
diff --git a/java/res/drawable-xhdpi/btn_keyboard_key_dark_active_holo.9.png b/java/res/drawable-xhdpi/btn_keyboard_key_dark_active_holo.9.png
index d990c02..738316d 100644
--- a/java/res/drawable-xhdpi/btn_keyboard_key_dark_active_holo.9.png
+++ b/java/res/drawable-xhdpi/btn_keyboard_key_dark_active_holo.9.png
Binary files differ
diff --git a/java/res/drawable-xhdpi/btn_keyboard_key_dark_normal_holo.9.png b/java/res/drawable-xhdpi/btn_keyboard_key_dark_normal_holo.9.png
index d2cd029..a2f6ac0 100644
--- a/java/res/drawable-xhdpi/btn_keyboard_key_dark_normal_holo.9.png
+++ b/java/res/drawable-xhdpi/btn_keyboard_key_dark_normal_holo.9.png
Binary files differ
diff --git a/java/res/drawable-xhdpi/btn_keyboard_key_dark_normal_off_holo.9.png b/java/res/drawable-xhdpi/btn_keyboard_key_dark_normal_off_holo.9.png
index bca39cf..2f00fc6 100644
--- a/java/res/drawable-xhdpi/btn_keyboard_key_dark_normal_off_holo.9.png
+++ b/java/res/drawable-xhdpi/btn_keyboard_key_dark_normal_off_holo.9.png
Binary files differ
diff --git a/java/res/drawable-xhdpi/btn_keyboard_key_dark_normal_on_holo.9.png b/java/res/drawable-xhdpi/btn_keyboard_key_dark_normal_on_holo.9.png
index ab8fb2e..20251a0 100644
--- a/java/res/drawable-xhdpi/btn_keyboard_key_dark_normal_on_holo.9.png
+++ b/java/res/drawable-xhdpi/btn_keyboard_key_dark_normal_on_holo.9.png
Binary files differ
diff --git a/java/res/drawable-xhdpi/btn_keyboard_key_dark_pressed_holo.9.png b/java/res/drawable-xhdpi/btn_keyboard_key_dark_pressed_holo.9.png
index 3871689..84d1739 100644
--- a/java/res/drawable-xhdpi/btn_keyboard_key_dark_pressed_holo.9.png
+++ b/java/res/drawable-xhdpi/btn_keyboard_key_dark_pressed_holo.9.png
Binary files differ
diff --git a/java/res/drawable-xhdpi/btn_keyboard_key_dark_pressed_off_holo.9.png b/java/res/drawable-xhdpi/btn_keyboard_key_dark_pressed_off_holo.9.png
index 9125063..ee4490e 100644
--- a/java/res/drawable-xhdpi/btn_keyboard_key_dark_pressed_off_holo.9.png
+++ b/java/res/drawable-xhdpi/btn_keyboard_key_dark_pressed_off_holo.9.png
Binary files differ
diff --git a/java/res/drawable-xhdpi/btn_keyboard_key_dark_pressed_on_holo.9.png b/java/res/drawable-xhdpi/btn_keyboard_key_dark_pressed_on_holo.9.png
index 35ce67f..e812477 100644
--- a/java/res/drawable-xhdpi/btn_keyboard_key_dark_pressed_on_holo.9.png
+++ b/java/res/drawable-xhdpi/btn_keyboard_key_dark_pressed_on_holo.9.png
Binary files differ
diff --git a/java/res/drawable-xhdpi/btn_keyboard_key_fulltrans_normal.9.png b/java/res/drawable-xhdpi/btn_keyboard_key_fulltrans_normal.9.png
deleted file mode 100644
index f7e32f7..0000000
--- a/java/res/drawable-xhdpi/btn_keyboard_key_fulltrans_normal.9.png
+++ /dev/null
Binary files differ
diff --git a/java/res/drawable-xhdpi/btn_keyboard_key_fulltrans_pressed.9.png b/java/res/drawable-xhdpi/btn_keyboard_key_fulltrans_pressed.9.png
deleted file mode 100644
index df3b5ba..0000000
--- a/java/res/drawable-xhdpi/btn_keyboard_key_fulltrans_pressed.9.png
+++ /dev/null
Binary files differ
diff --git a/java/res/drawable-xhdpi/btn_keyboard_key_light_normal_holo.9.png b/java/res/drawable-xhdpi/btn_keyboard_key_light_normal_holo.9.png
index b26f1d2..0ef4a4b 100644
--- a/java/res/drawable-xhdpi/btn_keyboard_key_light_normal_holo.9.png
+++ b/java/res/drawable-xhdpi/btn_keyboard_key_light_normal_holo.9.png
Binary files differ
diff --git a/java/res/drawable-xhdpi/btn_keyboard_key_light_pressed_holo.9.png b/java/res/drawable-xhdpi/btn_keyboard_key_light_pressed_holo.9.png
index c23a4b2..f770962 100644
--- a/java/res/drawable-xhdpi/btn_keyboard_key_light_pressed_holo.9.png
+++ b/java/res/drawable-xhdpi/btn_keyboard_key_light_pressed_holo.9.png
Binary files differ
diff --git a/java/res/drawable-xhdpi/btn_keyboard_key_normal_off_stone.9.png b/java/res/drawable-xhdpi/btn_keyboard_key_normal_off_stone.9.png
deleted file mode 100644
index dec2193..0000000
--- a/java/res/drawable-xhdpi/btn_keyboard_key_normal_off_stone.9.png
+++ /dev/null
Binary files differ
diff --git a/java/res/drawable-xhdpi/btn_keyboard_key_normal_on_stone.9.png b/java/res/drawable-xhdpi/btn_keyboard_key_normal_on_stone.9.png
deleted file mode 100644
index 3c77b3c..0000000
--- a/java/res/drawable-xhdpi/btn_keyboard_key_normal_on_stone.9.png
+++ /dev/null
Binary files differ
diff --git a/java/res/drawable-xhdpi/btn_keyboard_key_normal_stone.9.png b/java/res/drawable-xhdpi/btn_keyboard_key_normal_stone.9.png
deleted file mode 100644
index 5cdfc42..0000000
--- a/java/res/drawable-xhdpi/btn_keyboard_key_normal_stone.9.png
+++ /dev/null
Binary files differ
diff --git a/java/res/drawable-xhdpi/btn_keyboard_key_popup_selected_holo.9.png b/java/res/drawable-xhdpi/btn_keyboard_key_popup_selected_holo.9.png
index 0c7bfda..891d000 100644
--- a/java/res/drawable-xhdpi/btn_keyboard_key_popup_selected_holo.9.png
+++ b/java/res/drawable-xhdpi/btn_keyboard_key_popup_selected_holo.9.png
Binary files differ
diff --git a/java/res/drawable-xhdpi/ic_emoji_dark.png b/java/res/drawable-xhdpi/ic_emoji_dark.png
deleted file mode 100644
index 22daec2..0000000
--- a/java/res/drawable-xhdpi/ic_emoji_dark.png
+++ /dev/null
Binary files differ
diff --git a/java/res/drawable-xhdpi/ic_emoji_nature_light_activated.png b/java/res/drawable-xhdpi/ic_emoji_nature_light_activated.png
new file mode 100644
index 0000000..3e67443
--- /dev/null
+++ b/java/res/drawable-xhdpi/ic_emoji_nature_light_activated.png
Binary files differ
diff --git a/java/res/drawable-xhdpi/ic_emoji_nature_light_normal.png b/java/res/drawable-xhdpi/ic_emoji_nature_light_normal.png
new file mode 100644
index 0000000..5344a9e
--- /dev/null
+++ b/java/res/drawable-xhdpi/ic_emoji_nature_light_normal.png
Binary files differ
diff --git a/java/res/drawable-xhdpi/ic_emoji_objects_light_activated.png b/java/res/drawable-xhdpi/ic_emoji_objects_light_activated.png
new file mode 100644
index 0000000..75695d4
--- /dev/null
+++ b/java/res/drawable-xhdpi/ic_emoji_objects_light_activated.png
Binary files differ
diff --git a/java/res/drawable-xhdpi/ic_emoji_objects_light_normal.png b/java/res/drawable-xhdpi/ic_emoji_objects_light_normal.png
new file mode 100644
index 0000000..2adb186
--- /dev/null
+++ b/java/res/drawable-xhdpi/ic_emoji_objects_light_normal.png
Binary files differ
diff --git a/java/res/drawable-xhdpi/ic_emoji_people_light_activated.png b/java/res/drawable-xhdpi/ic_emoji_people_light_activated.png
new file mode 100644
index 0000000..e6baa2e
--- /dev/null
+++ b/java/res/drawable-xhdpi/ic_emoji_people_light_activated.png
Binary files differ
diff --git a/java/res/drawable-xhdpi/ic_emoji_light.png b/java/res/drawable-xhdpi/ic_emoji_people_light_normal.png
similarity index 94%
rename from java/res/drawable-xhdpi/ic_emoji_light.png
rename to java/res/drawable-xhdpi/ic_emoji_people_light_normal.png
index 21bc909..c26aa4e 100644
--- a/java/res/drawable-xhdpi/ic_emoji_light.png
+++ b/java/res/drawable-xhdpi/ic_emoji_people_light_normal.png
Binary files differ
diff --git a/java/res/drawable-xhdpi/ic_emoji_places_light_activated.png b/java/res/drawable-xhdpi/ic_emoji_places_light_activated.png
new file mode 100644
index 0000000..eaa3b86
--- /dev/null
+++ b/java/res/drawable-xhdpi/ic_emoji_places_light_activated.png
Binary files differ
diff --git a/java/res/drawable-xhdpi/ic_emoji_places_light_normal.png b/java/res/drawable-xhdpi/ic_emoji_places_light_normal.png
new file mode 100644
index 0000000..d6e1eaa
--- /dev/null
+++ b/java/res/drawable-xhdpi/ic_emoji_places_light_normal.png
Binary files differ
diff --git a/java/res/drawable-xhdpi/ic_emoji_recent_light_activated.png b/java/res/drawable-xhdpi/ic_emoji_recent_light_activated.png
new file mode 100644
index 0000000..06003b8
--- /dev/null
+++ b/java/res/drawable-xhdpi/ic_emoji_recent_light_activated.png
Binary files differ
diff --git a/java/res/drawable-xhdpi/ic_emoji_recent_light_normal.png b/java/res/drawable-xhdpi/ic_emoji_recent_light_normal.png
new file mode 100644
index 0000000..da2effe
--- /dev/null
+++ b/java/res/drawable-xhdpi/ic_emoji_recent_light_normal.png
Binary files differ
diff --git a/java/res/drawable-xhdpi/ic_emoji_symbols_light_activated.png b/java/res/drawable-xhdpi/ic_emoji_symbols_light_activated.png
new file mode 100644
index 0000000..438fde2
--- /dev/null
+++ b/java/res/drawable-xhdpi/ic_emoji_symbols_light_activated.png
Binary files differ
diff --git a/java/res/drawable-xhdpi/ic_emoji_symbols_light_normal.png b/java/res/drawable-xhdpi/ic_emoji_symbols_light_normal.png
new file mode 100644
index 0000000..7578632
--- /dev/null
+++ b/java/res/drawable-xhdpi/ic_emoji_symbols_light_normal.png
Binary files differ
diff --git a/java/res/drawable-xhdpi/ic_ime_switcher_dark.png b/java/res/drawable-xhdpi/ic_ime_switcher_dark.png
new file mode 100644
index 0000000..c567077
--- /dev/null
+++ b/java/res/drawable-xhdpi/ic_ime_switcher_dark.png
Binary files differ
diff --git a/java/res/drawable-xhdpi/ic_settings_language.png b/java/res/drawable-xhdpi/ic_settings_language.png
deleted file mode 100644
index 2c42db3..0000000
--- a/java/res/drawable-xhdpi/ic_settings_language.png
+++ /dev/null
Binary files differ
diff --git a/java/res/drawable-xhdpi/ic_subtype_keyboard.png b/java/res/drawable-xhdpi/ic_subtype_keyboard.png
deleted file mode 100644
index a79bb34..0000000
--- a/java/res/drawable-xhdpi/ic_subtype_keyboard.png
+++ /dev/null
Binary files differ
diff --git a/java/res/drawable-xhdpi/ic_subtype_mic_dark.png b/java/res/drawable-xhdpi/ic_subtype_mic_dark.png
new file mode 100644
index 0000000..17581ba
--- /dev/null
+++ b/java/res/drawable-xhdpi/ic_subtype_mic_dark.png
Binary files differ
diff --git a/java/res/drawable-xhdpi/keyboard_background.9.png b/java/res/drawable-xhdpi/keyboard_background.9.png
deleted file mode 100644
index 2639963..0000000
--- a/java/res/drawable-xhdpi/keyboard_background.9.png
+++ /dev/null
Binary files differ
diff --git a/java/res/drawable-xhdpi/keyboard_dark_background.9.png b/java/res/drawable-xhdpi/keyboard_background_gb.9.png
similarity index 100%
rename from java/res/drawable-xhdpi/keyboard_dark_background.9.png
rename to java/res/drawable-xhdpi/keyboard_background_gb.9.png
Binary files differ
diff --git a/java/res/drawable-xhdpi/keyboard_key_feedback_background_holo.9.png b/java/res/drawable-xhdpi/keyboard_key_feedback_background_holo.9.png
index d999127..e8c65f6 100644
--- a/java/res/drawable-xhdpi/keyboard_key_feedback_background_holo.9.png
+++ b/java/res/drawable-xhdpi/keyboard_key_feedback_background_holo.9.png
Binary files differ
diff --git a/java/res/drawable-xhdpi/keyboard_key_feedback_left_background_holo.9.png b/java/res/drawable-xhdpi/keyboard_key_feedback_left_background_holo.9.png
index c4d6941..543bc76 100644
--- a/java/res/drawable-xhdpi/keyboard_key_feedback_left_background_holo.9.png
+++ b/java/res/drawable-xhdpi/keyboard_key_feedback_left_background_holo.9.png
Binary files differ
diff --git a/java/res/drawable-xhdpi/keyboard_key_feedback_left_more_background_holo.9.png b/java/res/drawable-xhdpi/keyboard_key_feedback_left_more_background_holo.9.png
index 5429c17..ec42aad 100644
--- a/java/res/drawable-xhdpi/keyboard_key_feedback_left_more_background_holo.9.png
+++ b/java/res/drawable-xhdpi/keyboard_key_feedback_left_more_background_holo.9.png
Binary files differ
diff --git a/java/res/drawable-xhdpi/keyboard_key_feedback_more_background_holo.9.png b/java/res/drawable-xhdpi/keyboard_key_feedback_more_background_holo.9.png
index 5135a08..319e9d7 100644
--- a/java/res/drawable-xhdpi/keyboard_key_feedback_more_background_holo.9.png
+++ b/java/res/drawable-xhdpi/keyboard_key_feedback_more_background_holo.9.png
Binary files differ
diff --git a/java/res/drawable-xhdpi/keyboard_key_feedback_right_background_holo.9.png b/java/res/drawable-xhdpi/keyboard_key_feedback_right_background_holo.9.png
index 19a77a2..052032b 100644
--- a/java/res/drawable-xhdpi/keyboard_key_feedback_right_background_holo.9.png
+++ b/java/res/drawable-xhdpi/keyboard_key_feedback_right_background_holo.9.png
Binary files differ
diff --git a/java/res/drawable-xhdpi/keyboard_key_feedback_right_more_background_holo.9.png b/java/res/drawable-xhdpi/keyboard_key_feedback_right_more_background_holo.9.png
index ae2ffff..c7e9d1c 100644
--- a/java/res/drawable-xhdpi/keyboard_key_feedback_right_more_background_holo.9.png
+++ b/java/res/drawable-xhdpi/keyboard_key_feedback_right_more_background_holo.9.png
Binary files differ
diff --git a/java/res/drawable-xhdpi/keyboard_popup_panel_background.9.png b/java/res/drawable-xhdpi/keyboard_popup_panel_background_gb.9.png
similarity index 100%
rename from java/res/drawable-xhdpi/keyboard_popup_panel_background.9.png
rename to java/res/drawable-xhdpi/keyboard_popup_panel_background_gb.9.png
Binary files differ
diff --git a/java/res/drawable-xhdpi/keyboard_popup_panel_background_holo.9.png b/java/res/drawable-xhdpi/keyboard_popup_panel_background_holo.9.png
index 1dee699..dde1856 100644
--- a/java/res/drawable-xhdpi/keyboard_popup_panel_background_holo.9.png
+++ b/java/res/drawable-xhdpi/keyboard_popup_panel_background_holo.9.png
Binary files differ
diff --git a/java/res/drawable-xhdpi/keyboard_suggest_strip.9.png b/java/res/drawable-xhdpi/keyboard_suggest_strip_gb.9.png
similarity index 100%
rename from java/res/drawable-xhdpi/keyboard_suggest_strip.9.png
rename to java/res/drawable-xhdpi/keyboard_suggest_strip_gb.9.png
Binary files differ
diff --git a/java/res/drawable-xhdpi/suggestions_strip_divider.png b/java/res/drawable-xhdpi/suggestions_strip_divider.png
index 0d8b984..4101ebc 100644
--- a/java/res/drawable-xhdpi/suggestions_strip_divider.png
+++ b/java/res/drawable-xhdpi/suggestions_strip_divider.png
Binary files differ
diff --git a/java/res/drawable-xhdpi/sym_bkeyboard_delete.png b/java/res/drawable-xhdpi/sym_bkeyboard_delete.png
deleted file mode 100644
index b84ee76..0000000
--- a/java/res/drawable-xhdpi/sym_bkeyboard_delete.png
+++ /dev/null
Binary files differ
diff --git a/java/res/drawable-xhdpi/sym_bkeyboard_label_mic.png b/java/res/drawable-xhdpi/sym_bkeyboard_label_mic.png
deleted file mode 100644
index 9bd1d65..0000000
--- a/java/res/drawable-xhdpi/sym_bkeyboard_label_mic.png
+++ /dev/null
Binary files differ
diff --git a/java/res/drawable-xhdpi/sym_bkeyboard_mic.png b/java/res/drawable-xhdpi/sym_bkeyboard_mic.png
deleted file mode 100644
index 8c3f11d..0000000
--- a/java/res/drawable-xhdpi/sym_bkeyboard_mic.png
+++ /dev/null
Binary files differ
diff --git a/java/res/drawable-xhdpi/sym_bkeyboard_return.png b/java/res/drawable-xhdpi/sym_bkeyboard_return.png
deleted file mode 100644
index 1632ecd..0000000
--- a/java/res/drawable-xhdpi/sym_bkeyboard_return.png
+++ /dev/null
Binary files differ
diff --git a/java/res/drawable-xhdpi/sym_bkeyboard_search.png b/java/res/drawable-xhdpi/sym_bkeyboard_search.png
deleted file mode 100644
index 69d8b22..0000000
--- a/java/res/drawable-xhdpi/sym_bkeyboard_search.png
+++ /dev/null
Binary files differ
diff --git a/java/res/drawable-xhdpi/sym_bkeyboard_settings.png b/java/res/drawable-xhdpi/sym_bkeyboard_settings.png
deleted file mode 100644
index 050154a..0000000
--- a/java/res/drawable-xhdpi/sym_bkeyboard_settings.png
+++ /dev/null
Binary files differ
diff --git a/java/res/drawable-xhdpi/sym_bkeyboard_shift.png b/java/res/drawable-xhdpi/sym_bkeyboard_shift.png
deleted file mode 100644
index d15d11a..0000000
--- a/java/res/drawable-xhdpi/sym_bkeyboard_shift.png
+++ /dev/null
Binary files differ
diff --git a/java/res/drawable-xhdpi/sym_bkeyboard_shift_locked.png b/java/res/drawable-xhdpi/sym_bkeyboard_shift_locked.png
deleted file mode 100644
index 83b287f..0000000
--- a/java/res/drawable-xhdpi/sym_bkeyboard_shift_locked.png
+++ /dev/null
Binary files differ
diff --git a/java/res/drawable-xhdpi/sym_bkeyboard_space.png b/java/res/drawable-xhdpi/sym_bkeyboard_space.png
deleted file mode 100644
index 5ca62c7..0000000
--- a/java/res/drawable-xhdpi/sym_bkeyboard_space.png
+++ /dev/null
Binary files differ
diff --git a/java/res/drawable-xhdpi/sym_bkeyboard_tab.png b/java/res/drawable-xhdpi/sym_bkeyboard_tab.png
deleted file mode 100644
index 6ca1997..0000000
--- a/java/res/drawable-xhdpi/sym_bkeyboard_tab.png
+++ /dev/null
Binary files differ
diff --git a/java/res/drawable-xhdpi/sym_bkeyboard_voice_off.png b/java/res/drawable-xhdpi/sym_bkeyboard_voice_off.png
deleted file mode 100644
index fc6a4eb..0000000
--- a/java/res/drawable-xhdpi/sym_bkeyboard_voice_off.png
+++ /dev/null
Binary files differ
diff --git a/java/res/drawable-xhdpi/sym_keyboard_delete.png b/java/res/drawable-xhdpi/sym_keyboard_delete.png
deleted file mode 100644
index 3c0b8b1..0000000
--- a/java/res/drawable-xhdpi/sym_keyboard_delete.png
+++ /dev/null
Binary files differ
diff --git a/java/res/drawable-xhdpi/sym_keyboard_delete_holo.png b/java/res/drawable-xhdpi/sym_keyboard_delete_holo.png
deleted file mode 100644
index 354c09e..0000000
--- a/java/res/drawable-xhdpi/sym_keyboard_delete_holo.png
+++ /dev/null
Binary files differ
diff --git a/java/res/drawable-xhdpi/sym_keyboard_delete_holo_dark.png b/java/res/drawable-xhdpi/sym_keyboard_delete_holo_dark.png
new file mode 100644
index 0000000..e3e37d5
--- /dev/null
+++ b/java/res/drawable-xhdpi/sym_keyboard_delete_holo_dark.png
Binary files differ
diff --git a/java/res/drawable-xhdpi/sym_keyboard_label_mic.png b/java/res/drawable-xhdpi/sym_keyboard_label_mic.png
deleted file mode 100644
index 49810a0..0000000
--- a/java/res/drawable-xhdpi/sym_keyboard_label_mic.png
+++ /dev/null
Binary files differ
diff --git a/java/res/drawable-xhdpi/sym_keyboard_label_mic_holo.png b/java/res/drawable-xhdpi/sym_keyboard_label_mic_holo.png
deleted file mode 100644
index 8eeb179..0000000
--- a/java/res/drawable-xhdpi/sym_keyboard_label_mic_holo.png
+++ /dev/null
Binary files differ
diff --git a/java/res/drawable-xhdpi/sym_keyboard_language_switch.png b/java/res/drawable-xhdpi/sym_keyboard_language_switch.png
deleted file mode 100644
index 6c2fb53..0000000
--- a/java/res/drawable-xhdpi/sym_keyboard_language_switch.png
+++ /dev/null
Binary files differ
diff --git a/java/res/drawable-xhdpi/sym_keyboard_language_switch_dark.png b/java/res/drawable-xhdpi/sym_keyboard_language_switch_dark.png
new file mode 100644
index 0000000..b8687f5
--- /dev/null
+++ b/java/res/drawable-xhdpi/sym_keyboard_language_switch_dark.png
Binary files differ
diff --git a/java/res/drawable-xhdpi/sym_keyboard_mic.png b/java/res/drawable-xhdpi/sym_keyboard_mic.png
deleted file mode 100644
index 1323b6d..0000000
--- a/java/res/drawable-xhdpi/sym_keyboard_mic.png
+++ /dev/null
Binary files differ
diff --git a/java/res/drawable-xhdpi/sym_keyboard_mic_holo_dark.png b/java/res/drawable-xhdpi/sym_keyboard_mic_holo_dark.png
new file mode 100644
index 0000000..566ba1f
--- /dev/null
+++ b/java/res/drawable-xhdpi/sym_keyboard_mic_holo_dark.png
Binary files differ
diff --git a/java/res/drawable-xhdpi/sym_keyboard_return.png b/java/res/drawable-xhdpi/sym_keyboard_return.png
deleted file mode 100644
index ad06122..0000000
--- a/java/res/drawable-xhdpi/sym_keyboard_return.png
+++ /dev/null
Binary files differ
diff --git a/java/res/drawable-xhdpi/sym_keyboard_return_holo.png b/java/res/drawable-xhdpi/sym_keyboard_return_holo.png
deleted file mode 100644
index ba424ad..0000000
--- a/java/res/drawable-xhdpi/sym_keyboard_return_holo.png
+++ /dev/null
Binary files differ
diff --git a/java/res/drawable-xhdpi/sym_keyboard_return_holo_dark.png b/java/res/drawable-xhdpi/sym_keyboard_return_holo_dark.png
new file mode 100644
index 0000000..7b7ad17
--- /dev/null
+++ b/java/res/drawable-xhdpi/sym_keyboard_return_holo_dark.png
Binary files differ
diff --git a/java/res/drawable-xhdpi/sym_keyboard_search.png b/java/res/drawable-xhdpi/sym_keyboard_search.png
deleted file mode 100644
index aa785a2..0000000
--- a/java/res/drawable-xhdpi/sym_keyboard_search.png
+++ /dev/null
Binary files differ
diff --git a/java/res/drawable-xhdpi/sym_keyboard_search_holo.png b/java/res/drawable-xhdpi/sym_keyboard_search_holo.png
deleted file mode 100644
index f2fb2a2..0000000
--- a/java/res/drawable-xhdpi/sym_keyboard_search_holo.png
+++ /dev/null
Binary files differ
diff --git a/java/res/drawable-xhdpi/sym_keyboard_search_holo_dark.png b/java/res/drawable-xhdpi/sym_keyboard_search_holo_dark.png
new file mode 100644
index 0000000..36b1646
--- /dev/null
+++ b/java/res/drawable-xhdpi/sym_keyboard_search_holo_dark.png
Binary files differ
diff --git a/java/res/drawable-xhdpi/sym_keyboard_settings.png b/java/res/drawable-xhdpi/sym_keyboard_settings.png
deleted file mode 100644
index 5070425..0000000
--- a/java/res/drawable-xhdpi/sym_keyboard_settings.png
+++ /dev/null
Binary files differ
diff --git a/java/res/drawable-xhdpi/sym_keyboard_settings_holo.png b/java/res/drawable-xhdpi/sym_keyboard_settings_holo.png
deleted file mode 100644
index 99ee97d..0000000
--- a/java/res/drawable-xhdpi/sym_keyboard_settings_holo.png
+++ /dev/null
Binary files differ
diff --git a/java/res/drawable-xhdpi/sym_keyboard_settings_holo_dark.png b/java/res/drawable-xhdpi/sym_keyboard_settings_holo_dark.png
new file mode 100644
index 0000000..05eaffe
--- /dev/null
+++ b/java/res/drawable-xhdpi/sym_keyboard_settings_holo_dark.png
Binary files differ
diff --git a/java/res/drawable-xhdpi/sym_keyboard_shift.png b/java/res/drawable-xhdpi/sym_keyboard_shift.png
deleted file mode 100644
index 2901706..0000000
--- a/java/res/drawable-xhdpi/sym_keyboard_shift.png
+++ /dev/null
Binary files differ
diff --git a/java/res/drawable-xhdpi/sym_keyboard_shift_holo.png b/java/res/drawable-xhdpi/sym_keyboard_shift_holo.png
deleted file mode 100644
index 1046b45..0000000
--- a/java/res/drawable-xhdpi/sym_keyboard_shift_holo.png
+++ /dev/null
Binary files differ
diff --git a/java/res/drawable-xhdpi/sym_keyboard_shift_holo_dark.png b/java/res/drawable-xhdpi/sym_keyboard_shift_holo_dark.png
new file mode 100644
index 0000000..5ab5491
--- /dev/null
+++ b/java/res/drawable-xhdpi/sym_keyboard_shift_holo_dark.png
Binary files differ
diff --git a/java/res/drawable-xhdpi/sym_keyboard_shift_locked.png b/java/res/drawable-xhdpi/sym_keyboard_shift_locked.png
deleted file mode 100644
index a5deb60..0000000
--- a/java/res/drawable-xhdpi/sym_keyboard_shift_locked.png
+++ /dev/null
Binary files differ
diff --git a/java/res/drawable-xhdpi/sym_keyboard_shift_locked_holo.png b/java/res/drawable-xhdpi/sym_keyboard_shift_locked_holo.png
deleted file mode 100644
index 6acb565..0000000
--- a/java/res/drawable-xhdpi/sym_keyboard_shift_locked_holo.png
+++ /dev/null
Binary files differ
diff --git a/java/res/drawable-xhdpi/sym_keyboard_shift_locked_holo_dark.png b/java/res/drawable-xhdpi/sym_keyboard_shift_locked_holo_dark.png
new file mode 100644
index 0000000..b820eaa
--- /dev/null
+++ b/java/res/drawable-xhdpi/sym_keyboard_shift_locked_holo_dark.png
Binary files differ
diff --git a/java/res/drawable-xhdpi/sym_keyboard_smiley_holo_dark.png b/java/res/drawable-xhdpi/sym_keyboard_smiley_holo_dark.png
new file mode 100644
index 0000000..686831f
--- /dev/null
+++ b/java/res/drawable-xhdpi/sym_keyboard_smiley_holo_dark.png
Binary files differ
diff --git a/java/res/drawable-xhdpi/sym_keyboard_space_holo.png b/java/res/drawable-xhdpi/sym_keyboard_space_holo.png
deleted file mode 100644
index 504a3ed..0000000
--- a/java/res/drawable-xhdpi/sym_keyboard_space_holo.png
+++ /dev/null
Binary files differ
diff --git a/java/res/drawable-xhdpi/sym_keyboard_space_holo_dark.png b/java/res/drawable-xhdpi/sym_keyboard_space_holo_dark.png
new file mode 100644
index 0000000..7114b74
--- /dev/null
+++ b/java/res/drawable-xhdpi/sym_keyboard_space_holo_dark.png
Binary files differ
diff --git a/java/res/drawable-xhdpi/sym_keyboard_space_led.9.png b/java/res/drawable-xhdpi/sym_keyboard_space_led_gb.9.png
similarity index 100%
rename from java/res/drawable-xhdpi/sym_keyboard_space_led.9.png
rename to java/res/drawable-xhdpi/sym_keyboard_space_led_gb.9.png
Binary files differ
diff --git a/java/res/drawable-xhdpi/sym_keyboard_tab.png b/java/res/drawable-xhdpi/sym_keyboard_tab.png
deleted file mode 100644
index 0ef2ab5..0000000
--- a/java/res/drawable-xhdpi/sym_keyboard_tab.png
+++ /dev/null
Binary files differ
diff --git a/java/res/drawable-xhdpi/sym_keyboard_tab_holo.png b/java/res/drawable-xhdpi/sym_keyboard_tab_holo.png
deleted file mode 100644
index ff380ee..0000000
--- a/java/res/drawable-xhdpi/sym_keyboard_tab_holo.png
+++ /dev/null
Binary files differ
diff --git a/java/res/drawable-xhdpi/sym_keyboard_tab_holo_dark.png b/java/res/drawable-xhdpi/sym_keyboard_tab_holo_dark.png
new file mode 100644
index 0000000..73ebfe5
--- /dev/null
+++ b/java/res/drawable-xhdpi/sym_keyboard_tab_holo_dark.png
Binary files differ
diff --git a/java/res/drawable-xhdpi/sym_keyboard_voice_holo.png b/java/res/drawable-xhdpi/sym_keyboard_voice_holo.png
deleted file mode 100644
index b2bb9b8..0000000
--- a/java/res/drawable-xhdpi/sym_keyboard_voice_holo.png
+++ /dev/null
Binary files differ
diff --git a/java/res/drawable-xhdpi/sym_keyboard_voice_holo_dark.png b/java/res/drawable-xhdpi/sym_keyboard_voice_holo_dark.png
new file mode 100644
index 0000000..944a852
--- /dev/null
+++ b/java/res/drawable-xhdpi/sym_keyboard_voice_holo_dark.png
Binary files differ
diff --git a/java/res/drawable-xhdpi/sym_keyboard_voice_off_holo.png b/java/res/drawable-xhdpi/sym_keyboard_voice_off_holo.png
deleted file mode 100644
index 23e75bf..0000000
--- a/java/res/drawable-xhdpi/sym_keyboard_voice_off_holo.png
+++ /dev/null
Binary files differ
diff --git a/java/res/drawable-xhdpi/sym_keyboard_voice_off_holo_dark.png b/java/res/drawable-xhdpi/sym_keyboard_voice_off_holo_dark.png
new file mode 100644
index 0000000..2016caf
--- /dev/null
+++ b/java/res/drawable-xhdpi/sym_keyboard_voice_off_holo_dark.png
Binary files differ
diff --git a/java/res/drawable-xhdpi/sym_keyboard_zwj_holo.png b/java/res/drawable-xhdpi/sym_keyboard_zwj_holo.png
deleted file mode 100644
index 2669427..0000000
--- a/java/res/drawable-xhdpi/sym_keyboard_zwj_holo.png
+++ /dev/null
Binary files differ
diff --git a/java/res/drawable-xhdpi/sym_keyboard_zwj_holo_dark.png b/java/res/drawable-xhdpi/sym_keyboard_zwj_holo_dark.png
new file mode 100644
index 0000000..2f9607a
--- /dev/null
+++ b/java/res/drawable-xhdpi/sym_keyboard_zwj_holo_dark.png
Binary files differ
diff --git a/java/res/drawable-xhdpi/sym_keyboard_zwnj_holo.png b/java/res/drawable-xhdpi/sym_keyboard_zwnj_holo.png
deleted file mode 100644
index 75a22b6..0000000
--- a/java/res/drawable-xhdpi/sym_keyboard_zwnj_holo.png
+++ /dev/null
Binary files differ
diff --git a/java/res/drawable-xhdpi/sym_keyboard_zwnj_holo_dark.png b/java/res/drawable-xhdpi/sym_keyboard_zwnj_holo_dark.png
new file mode 100644
index 0000000..ab07f75
--- /dev/null
+++ b/java/res/drawable-xhdpi/sym_keyboard_zwnj_holo_dark.png
Binary files differ
diff --git a/java/res/drawable-xhdpi/tab_selected.9.png b/java/res/drawable-xhdpi/tab_selected.9.png
new file mode 100644
index 0000000..95e5f43
--- /dev/null
+++ b/java/res/drawable-xhdpi/tab_selected.9.png
Binary files differ
diff --git a/java/res/drawable-xhdpi/tab_unselected.9.png b/java/res/drawable-xhdpi/tab_unselected.9.png
new file mode 100644
index 0000000..8cede8d
--- /dev/null
+++ b/java/res/drawable-xhdpi/tab_unselected.9.png
Binary files differ
diff --git a/java/res/drawable-xxhdpi/btn_keyboard_key_dark_active_holo.9.png b/java/res/drawable-xxhdpi/btn_keyboard_key_dark_active_holo.9.png
index 680421e..b35c29f 100644
--- a/java/res/drawable-xxhdpi/btn_keyboard_key_dark_active_holo.9.png
+++ b/java/res/drawable-xxhdpi/btn_keyboard_key_dark_active_holo.9.png
Binary files differ
diff --git a/java/res/drawable-xxhdpi/btn_keyboard_key_dark_normal_holo.9.png b/java/res/drawable-xxhdpi/btn_keyboard_key_dark_normal_holo.9.png
index ae26750..17f0a7a 100644
--- a/java/res/drawable-xxhdpi/btn_keyboard_key_dark_normal_holo.9.png
+++ b/java/res/drawable-xxhdpi/btn_keyboard_key_dark_normal_holo.9.png
Binary files differ
diff --git a/java/res/drawable-xxhdpi/btn_keyboard_key_dark_normal_off_holo.9.png b/java/res/drawable-xxhdpi/btn_keyboard_key_dark_normal_off_holo.9.png
index c92a669..b0e815e 100644
--- a/java/res/drawable-xxhdpi/btn_keyboard_key_dark_normal_off_holo.9.png
+++ b/java/res/drawable-xxhdpi/btn_keyboard_key_dark_normal_off_holo.9.png
Binary files differ
diff --git a/java/res/drawable-xxhdpi/btn_keyboard_key_dark_normal_on_holo.9.png b/java/res/drawable-xxhdpi/btn_keyboard_key_dark_normal_on_holo.9.png
index 40f5011..97f9625 100644
--- a/java/res/drawable-xxhdpi/btn_keyboard_key_dark_normal_on_holo.9.png
+++ b/java/res/drawable-xxhdpi/btn_keyboard_key_dark_normal_on_holo.9.png
Binary files differ
diff --git a/java/res/drawable-xxhdpi/btn_keyboard_key_dark_pressed_holo.9.png b/java/res/drawable-xxhdpi/btn_keyboard_key_dark_pressed_holo.9.png
index 6ff6319..dfb16a7 100644
--- a/java/res/drawable-xxhdpi/btn_keyboard_key_dark_pressed_holo.9.png
+++ b/java/res/drawable-xxhdpi/btn_keyboard_key_dark_pressed_holo.9.png
Binary files differ
diff --git a/java/res/drawable-xxhdpi/btn_keyboard_key_dark_pressed_off_holo.9.png b/java/res/drawable-xxhdpi/btn_keyboard_key_dark_pressed_off_holo.9.png
index 818ea70..bf1d346 100644
--- a/java/res/drawable-xxhdpi/btn_keyboard_key_dark_pressed_off_holo.9.png
+++ b/java/res/drawable-xxhdpi/btn_keyboard_key_dark_pressed_off_holo.9.png
Binary files differ
diff --git a/java/res/drawable-xxhdpi/btn_keyboard_key_dark_pressed_on_holo.9.png b/java/res/drawable-xxhdpi/btn_keyboard_key_dark_pressed_on_holo.9.png
index a476d2a..9622771 100644
--- a/java/res/drawable-xxhdpi/btn_keyboard_key_dark_pressed_on_holo.9.png
+++ b/java/res/drawable-xxhdpi/btn_keyboard_key_dark_pressed_on_holo.9.png
Binary files differ
diff --git a/java/res/drawable-xxhdpi/btn_keyboard_key_light_normal_holo.9.png b/java/res/drawable-xxhdpi/btn_keyboard_key_light_normal_holo.9.png
index 9c280a6..4ddfdcb 100644
--- a/java/res/drawable-xxhdpi/btn_keyboard_key_light_normal_holo.9.png
+++ b/java/res/drawable-xxhdpi/btn_keyboard_key_light_normal_holo.9.png
Binary files differ
diff --git a/java/res/drawable-xxhdpi/btn_keyboard_key_light_pressed_holo.9.png b/java/res/drawable-xxhdpi/btn_keyboard_key_light_pressed_holo.9.png
index 3c17c5e..17144b6 100644
--- a/java/res/drawable-xxhdpi/btn_keyboard_key_light_pressed_holo.9.png
+++ b/java/res/drawable-xxhdpi/btn_keyboard_key_light_pressed_holo.9.png
Binary files differ
diff --git a/java/res/drawable-xxhdpi/btn_keyboard_key_popup_selected_holo.9.png b/java/res/drawable-xxhdpi/btn_keyboard_key_popup_selected_holo.9.png
index 6d2af59..0cbb2ec 100644
--- a/java/res/drawable-xxhdpi/btn_keyboard_key_popup_selected_holo.9.png
+++ b/java/res/drawable-xxhdpi/btn_keyboard_key_popup_selected_holo.9.png
Binary files differ
diff --git a/java/res/drawable-xxhdpi/ic_emoji_nature_light_activated.png b/java/res/drawable-xxhdpi/ic_emoji_nature_light_activated.png
new file mode 100644
index 0000000..470fd69
--- /dev/null
+++ b/java/res/drawable-xxhdpi/ic_emoji_nature_light_activated.png
Binary files differ
diff --git a/java/res/drawable-xxhdpi/ic_emoji_nature_light_normal.png b/java/res/drawable-xxhdpi/ic_emoji_nature_light_normal.png
new file mode 100644
index 0000000..a7fde0e
--- /dev/null
+++ b/java/res/drawable-xxhdpi/ic_emoji_nature_light_normal.png
Binary files differ
diff --git a/java/res/drawable-xxhdpi/ic_emoji_objects_light_activated.png b/java/res/drawable-xxhdpi/ic_emoji_objects_light_activated.png
new file mode 100644
index 0000000..c582b70
--- /dev/null
+++ b/java/res/drawable-xxhdpi/ic_emoji_objects_light_activated.png
Binary files differ
diff --git a/java/res/drawable-xxhdpi/ic_emoji_objects_light_normal.png b/java/res/drawable-xxhdpi/ic_emoji_objects_light_normal.png
new file mode 100644
index 0000000..acc95d7
--- /dev/null
+++ b/java/res/drawable-xxhdpi/ic_emoji_objects_light_normal.png
Binary files differ
diff --git a/java/res/drawable-xxhdpi/ic_emoji_people_light_activated.png b/java/res/drawable-xxhdpi/ic_emoji_people_light_activated.png
new file mode 100644
index 0000000..5973ac3
--- /dev/null
+++ b/java/res/drawable-xxhdpi/ic_emoji_people_light_activated.png
Binary files differ
diff --git a/java/res/drawable-xxhdpi/ic_emoji_people_light_normal.png b/java/res/drawable-xxhdpi/ic_emoji_people_light_normal.png
new file mode 100644
index 0000000..22e06f8
--- /dev/null
+++ b/java/res/drawable-xxhdpi/ic_emoji_people_light_normal.png
Binary files differ
diff --git a/java/res/drawable-xxhdpi/ic_emoji_places_light_activated.png b/java/res/drawable-xxhdpi/ic_emoji_places_light_activated.png
new file mode 100644
index 0000000..690e95f
--- /dev/null
+++ b/java/res/drawable-xxhdpi/ic_emoji_places_light_activated.png
Binary files differ
diff --git a/java/res/drawable-xxhdpi/ic_emoji_places_light_normal.png b/java/res/drawable-xxhdpi/ic_emoji_places_light_normal.png
new file mode 100644
index 0000000..ced4b08
--- /dev/null
+++ b/java/res/drawable-xxhdpi/ic_emoji_places_light_normal.png
Binary files differ
diff --git a/java/res/drawable-xxhdpi/ic_emoji_recent_light_activated.png b/java/res/drawable-xxhdpi/ic_emoji_recent_light_activated.png
new file mode 100644
index 0000000..25e847e
--- /dev/null
+++ b/java/res/drawable-xxhdpi/ic_emoji_recent_light_activated.png
Binary files differ
diff --git a/java/res/drawable-xxhdpi/ic_emoji_recent_light_normal.png b/java/res/drawable-xxhdpi/ic_emoji_recent_light_normal.png
new file mode 100644
index 0000000..c86368d
--- /dev/null
+++ b/java/res/drawable-xxhdpi/ic_emoji_recent_light_normal.png
Binary files differ
diff --git a/java/res/drawable-xxhdpi/ic_emoji_symbols_light_activated.png b/java/res/drawable-xxhdpi/ic_emoji_symbols_light_activated.png
new file mode 100644
index 0000000..29dfc71
--- /dev/null
+++ b/java/res/drawable-xxhdpi/ic_emoji_symbols_light_activated.png
Binary files differ
diff --git a/java/res/drawable-xxhdpi/ic_emoji_symbols_light_normal.png b/java/res/drawable-xxhdpi/ic_emoji_symbols_light_normal.png
new file mode 100644
index 0000000..0570567
--- /dev/null
+++ b/java/res/drawable-xxhdpi/ic_emoji_symbols_light_normal.png
Binary files differ
diff --git a/java/res/drawable-xxhdpi/ic_ime_switcher_dark.png b/java/res/drawable-xxhdpi/ic_ime_switcher_dark.png
new file mode 100644
index 0000000..f99f7d0
--- /dev/null
+++ b/java/res/drawable-xxhdpi/ic_ime_switcher_dark.png
Binary files differ
diff --git a/java/res/drawable-xxhdpi/ic_subtype_keyboard.png b/java/res/drawable-xxhdpi/ic_subtype_keyboard.png
deleted file mode 100644
index 0bb4283..0000000
--- a/java/res/drawable-xxhdpi/ic_subtype_keyboard.png
+++ /dev/null
Binary files differ
diff --git a/java/res/drawable-xxhdpi/ic_subtype_mic_dark.png b/java/res/drawable-xxhdpi/ic_subtype_mic_dark.png
new file mode 100644
index 0000000..811103a
--- /dev/null
+++ b/java/res/drawable-xxhdpi/ic_subtype_mic_dark.png
Binary files differ
diff --git a/java/res/drawable-xxhdpi/keyboard_key_feedback_background_holo.9.png b/java/res/drawable-xxhdpi/keyboard_key_feedback_background_holo.9.png
index bd1ef3c..11eee94 100644
--- a/java/res/drawable-xxhdpi/keyboard_key_feedback_background_holo.9.png
+++ b/java/res/drawable-xxhdpi/keyboard_key_feedback_background_holo.9.png
Binary files differ
diff --git a/java/res/drawable-xxhdpi/keyboard_key_feedback_left_background_holo.9.png b/java/res/drawable-xxhdpi/keyboard_key_feedback_left_background_holo.9.png
index 65af4b5..2079e04 100644
--- a/java/res/drawable-xxhdpi/keyboard_key_feedback_left_background_holo.9.png
+++ b/java/res/drawable-xxhdpi/keyboard_key_feedback_left_background_holo.9.png
Binary files differ
diff --git a/java/res/drawable-xxhdpi/keyboard_key_feedback_left_more_background_holo.9.png b/java/res/drawable-xxhdpi/keyboard_key_feedback_left_more_background_holo.9.png
index ac6750d..c4178d9 100644
--- a/java/res/drawable-xxhdpi/keyboard_key_feedback_left_more_background_holo.9.png
+++ b/java/res/drawable-xxhdpi/keyboard_key_feedback_left_more_background_holo.9.png
Binary files differ
diff --git a/java/res/drawable-xxhdpi/keyboard_key_feedback_more_background_holo.9.png b/java/res/drawable-xxhdpi/keyboard_key_feedback_more_background_holo.9.png
index cea7c05..121411a 100644
--- a/java/res/drawable-xxhdpi/keyboard_key_feedback_more_background_holo.9.png
+++ b/java/res/drawable-xxhdpi/keyboard_key_feedback_more_background_holo.9.png
Binary files differ
diff --git a/java/res/drawable-xxhdpi/keyboard_key_feedback_right_background_holo.9.png b/java/res/drawable-xxhdpi/keyboard_key_feedback_right_background_holo.9.png
index 520fa7c..d3d8733 100644
--- a/java/res/drawable-xxhdpi/keyboard_key_feedback_right_background_holo.9.png
+++ b/java/res/drawable-xxhdpi/keyboard_key_feedback_right_background_holo.9.png
Binary files differ
diff --git a/java/res/drawable-xxhdpi/keyboard_key_feedback_right_more_background_holo.9.png b/java/res/drawable-xxhdpi/keyboard_key_feedback_right_more_background_holo.9.png
index eee2217..d7ec8bc 100644
--- a/java/res/drawable-xxhdpi/keyboard_key_feedback_right_more_background_holo.9.png
+++ b/java/res/drawable-xxhdpi/keyboard_key_feedback_right_more_background_holo.9.png
Binary files differ
diff --git a/java/res/drawable-xxhdpi/keyboard_popup_panel_background_holo.9.png b/java/res/drawable-xxhdpi/keyboard_popup_panel_background_holo.9.png
index 721c244..ca576de 100644
--- a/java/res/drawable-xxhdpi/keyboard_popup_panel_background_holo.9.png
+++ b/java/res/drawable-xxhdpi/keyboard_popup_panel_background_holo.9.png
Binary files differ
diff --git a/java/res/drawable-xxhdpi/sym_keyboard_delete_holo.png b/java/res/drawable-xxhdpi/sym_keyboard_delete_holo.png
deleted file mode 100644
index be3cb7c..0000000
--- a/java/res/drawable-xxhdpi/sym_keyboard_delete_holo.png
+++ /dev/null
Binary files differ
diff --git a/java/res/drawable-xxhdpi/sym_keyboard_delete_holo_dark.png b/java/res/drawable-xxhdpi/sym_keyboard_delete_holo_dark.png
new file mode 100644
index 0000000..92be792
--- /dev/null
+++ b/java/res/drawable-xxhdpi/sym_keyboard_delete_holo_dark.png
Binary files differ
diff --git a/java/res/drawable-xxhdpi/sym_keyboard_label_mic_holo.png b/java/res/drawable-xxhdpi/sym_keyboard_label_mic_holo.png
deleted file mode 100644
index b6d4477..0000000
--- a/java/res/drawable-xxhdpi/sym_keyboard_label_mic_holo.png
+++ /dev/null
Binary files differ
diff --git a/java/res/drawable-xxhdpi/sym_keyboard_language_switch.png b/java/res/drawable-xxhdpi/sym_keyboard_language_switch.png
deleted file mode 100644
index 7cd0684..0000000
--- a/java/res/drawable-xxhdpi/sym_keyboard_language_switch.png
+++ /dev/null
Binary files differ
diff --git a/java/res/drawable-xxhdpi/sym_keyboard_language_switch_dark.png b/java/res/drawable-xxhdpi/sym_keyboard_language_switch_dark.png
new file mode 100644
index 0000000..88b55bb
--- /dev/null
+++ b/java/res/drawable-xxhdpi/sym_keyboard_language_switch_dark.png
Binary files differ
diff --git a/java/res/drawable-xxhdpi/sym_keyboard_mic_holo_dark.png b/java/res/drawable-xxhdpi/sym_keyboard_mic_holo_dark.png
new file mode 100644
index 0000000..f55af30
--- /dev/null
+++ b/java/res/drawable-xxhdpi/sym_keyboard_mic_holo_dark.png
Binary files differ
diff --git a/java/res/drawable-xxhdpi/sym_keyboard_return_holo.png b/java/res/drawable-xxhdpi/sym_keyboard_return_holo.png
deleted file mode 100644
index 7d95807..0000000
--- a/java/res/drawable-xxhdpi/sym_keyboard_return_holo.png
+++ /dev/null
Binary files differ
diff --git a/java/res/drawable-xxhdpi/sym_keyboard_return_holo_dark.png b/java/res/drawable-xxhdpi/sym_keyboard_return_holo_dark.png
new file mode 100644
index 0000000..46ee50e
--- /dev/null
+++ b/java/res/drawable-xxhdpi/sym_keyboard_return_holo_dark.png
Binary files differ
diff --git a/java/res/drawable-xxhdpi/sym_keyboard_search_holo.png b/java/res/drawable-xxhdpi/sym_keyboard_search_holo.png
deleted file mode 100644
index 6b09d8e..0000000
--- a/java/res/drawable-xxhdpi/sym_keyboard_search_holo.png
+++ /dev/null
Binary files differ
diff --git a/java/res/drawable-xxhdpi/sym_keyboard_search_holo_dark.png b/java/res/drawable-xxhdpi/sym_keyboard_search_holo_dark.png
new file mode 100644
index 0000000..f518748
--- /dev/null
+++ b/java/res/drawable-xxhdpi/sym_keyboard_search_holo_dark.png
Binary files differ
diff --git a/java/res/drawable-xxhdpi/sym_keyboard_settings_holo.png b/java/res/drawable-xxhdpi/sym_keyboard_settings_holo.png
deleted file mode 100644
index 7041bb6..0000000
--- a/java/res/drawable-xxhdpi/sym_keyboard_settings_holo.png
+++ /dev/null
Binary files differ
diff --git a/java/res/drawable-xxhdpi/sym_keyboard_settings_holo_dark.png b/java/res/drawable-xxhdpi/sym_keyboard_settings_holo_dark.png
new file mode 100644
index 0000000..e435846
--- /dev/null
+++ b/java/res/drawable-xxhdpi/sym_keyboard_settings_holo_dark.png
Binary files differ
diff --git a/java/res/drawable-xxhdpi/sym_keyboard_shift_holo.png b/java/res/drawable-xxhdpi/sym_keyboard_shift_holo.png
deleted file mode 100644
index 2b4fbbb..0000000
--- a/java/res/drawable-xxhdpi/sym_keyboard_shift_holo.png
+++ /dev/null
Binary files differ
diff --git a/java/res/drawable-xxhdpi/sym_keyboard_shift_holo_dark.png b/java/res/drawable-xxhdpi/sym_keyboard_shift_holo_dark.png
new file mode 100644
index 0000000..523286e
--- /dev/null
+++ b/java/res/drawable-xxhdpi/sym_keyboard_shift_holo_dark.png
Binary files differ
diff --git a/java/res/drawable-xxhdpi/sym_keyboard_shift_locked_holo.png b/java/res/drawable-xxhdpi/sym_keyboard_shift_locked_holo.png
deleted file mode 100644
index 91c8603..0000000
--- a/java/res/drawable-xxhdpi/sym_keyboard_shift_locked_holo.png
+++ /dev/null
Binary files differ
diff --git a/java/res/drawable-xxhdpi/sym_keyboard_shift_locked_holo_dark.png b/java/res/drawable-xxhdpi/sym_keyboard_shift_locked_holo_dark.png
new file mode 100644
index 0000000..87926d9
--- /dev/null
+++ b/java/res/drawable-xxhdpi/sym_keyboard_shift_locked_holo_dark.png
Binary files differ
diff --git a/java/res/drawable-xxhdpi/sym_keyboard_smiley_holo_dark.png b/java/res/drawable-xxhdpi/sym_keyboard_smiley_holo_dark.png
new file mode 100644
index 0000000..04b7216
--- /dev/null
+++ b/java/res/drawable-xxhdpi/sym_keyboard_smiley_holo_dark.png
Binary files differ
diff --git a/java/res/drawable-xxhdpi/sym_keyboard_space_holo.png b/java/res/drawable-xxhdpi/sym_keyboard_space_holo.png
deleted file mode 100644
index 65aa5ea..0000000
--- a/java/res/drawable-xxhdpi/sym_keyboard_space_holo.png
+++ /dev/null
Binary files differ
diff --git a/java/res/drawable-xxhdpi/sym_keyboard_space_holo_dark.png b/java/res/drawable-xxhdpi/sym_keyboard_space_holo_dark.png
new file mode 100644
index 0000000..1dab1f4
--- /dev/null
+++ b/java/res/drawable-xxhdpi/sym_keyboard_space_holo_dark.png
Binary files differ
diff --git a/java/res/drawable-xxhdpi/sym_keyboard_tab_holo.png b/java/res/drawable-xxhdpi/sym_keyboard_tab_holo.png
deleted file mode 100644
index 1f4ae3d..0000000
--- a/java/res/drawable-xxhdpi/sym_keyboard_tab_holo.png
+++ /dev/null
Binary files differ
diff --git a/java/res/drawable-xxhdpi/sym_keyboard_tab_holo_dark.png b/java/res/drawable-xxhdpi/sym_keyboard_tab_holo_dark.png
new file mode 100644
index 0000000..6eb3eb0
--- /dev/null
+++ b/java/res/drawable-xxhdpi/sym_keyboard_tab_holo_dark.png
Binary files differ
diff --git a/java/res/drawable-xxhdpi/sym_keyboard_voice_holo.png b/java/res/drawable-xxhdpi/sym_keyboard_voice_holo.png
deleted file mode 100644
index f04cadf..0000000
--- a/java/res/drawable-xxhdpi/sym_keyboard_voice_holo.png
+++ /dev/null
Binary files differ
diff --git a/java/res/drawable-xxhdpi/sym_keyboard_voice_holo_dark.png b/java/res/drawable-xxhdpi/sym_keyboard_voice_holo_dark.png
new file mode 100644
index 0000000..6809f07
--- /dev/null
+++ b/java/res/drawable-xxhdpi/sym_keyboard_voice_holo_dark.png
Binary files differ
diff --git a/java/res/drawable-xxhdpi/sym_keyboard_voice_off_holo.png b/java/res/drawable-xxhdpi/sym_keyboard_voice_off_holo.png
deleted file mode 100644
index e74d523..0000000
--- a/java/res/drawable-xxhdpi/sym_keyboard_voice_off_holo.png
+++ /dev/null
Binary files differ
diff --git a/java/res/drawable-xxhdpi/sym_keyboard_voice_off_holo_dark.png b/java/res/drawable-xxhdpi/sym_keyboard_voice_off_holo_dark.png
new file mode 100644
index 0000000..6bd506a
--- /dev/null
+++ b/java/res/drawable-xxhdpi/sym_keyboard_voice_off_holo_dark.png
Binary files differ
diff --git a/java/res/drawable-xxhdpi/sym_keyboard_zwj_holo.png b/java/res/drawable-xxhdpi/sym_keyboard_zwj_holo.png
deleted file mode 100644
index 85289b2..0000000
--- a/java/res/drawable-xxhdpi/sym_keyboard_zwj_holo.png
+++ /dev/null
Binary files differ
diff --git a/java/res/drawable-xxhdpi/sym_keyboard_zwj_holo_dark.png b/java/res/drawable-xxhdpi/sym_keyboard_zwj_holo_dark.png
new file mode 100644
index 0000000..5e225b8
--- /dev/null
+++ b/java/res/drawable-xxhdpi/sym_keyboard_zwj_holo_dark.png
Binary files differ
diff --git a/java/res/drawable-xxhdpi/sym_keyboard_zwnj_holo.png b/java/res/drawable-xxhdpi/sym_keyboard_zwnj_holo.png
deleted file mode 100644
index e610678..0000000
--- a/java/res/drawable-xxhdpi/sym_keyboard_zwnj_holo.png
+++ /dev/null
Binary files differ
diff --git a/java/res/drawable-xxhdpi/sym_keyboard_zwnj_holo_dark.png b/java/res/drawable-xxhdpi/sym_keyboard_zwnj_holo_dark.png
new file mode 100644
index 0000000..cdfc029
--- /dev/null
+++ b/java/res/drawable-xxhdpi/sym_keyboard_zwnj_holo_dark.png
Binary files differ
diff --git a/java/res/drawable-xxhdpi/tab_selected.9.png b/java/res/drawable-xxhdpi/tab_selected.9.png
new file mode 100644
index 0000000..e5efc58
--- /dev/null
+++ b/java/res/drawable-xxhdpi/tab_selected.9.png
Binary files differ
diff --git a/java/res/drawable-xxhdpi/tab_unselected.9.png b/java/res/drawable-xxhdpi/tab_unselected.9.png
new file mode 100644
index 0000000..3891886
--- /dev/null
+++ b/java/res/drawable-xxhdpi/tab_unselected.9.png
Binary files differ
diff --git a/java/res/drawable/btn_center.xml b/java/res/drawable/btn_center.xml
deleted file mode 100644
index 3ac2129..0000000
--- a/java/res/drawable/btn_center.xml
+++ /dev/null
@@ -1,40 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-/*
-**
-** Copyright 2011, The Android Open Source Project
-**
-** Licensed under the Apache License, Version 2.0 (the "License");
-** you may not use this file except in compliance with the License.
-** You may obtain a copy of the License at
-**
-**     http://www.apache.org/licenses/LICENSE-2.0
-**
-** Unless required by applicable law or agreed to in writing, software
-** distributed under the License is distributed on an "AS IS" BASIS,
-** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-** See the License for the specific language governing permissions and
-** limitations under the License.
-*/
--->
-
-<selector
-        xmlns:android="http://schemas.android.com/apk/res/android"
-        android:exitFadeDuration="@android:integer/config_mediumAnimTime">
-    <item
-        android:state_window_focused="false"
-        android:state_enabled="true"
-        android:drawable="@drawable/btn_center_default" />
-    <item
-        android:state_pressed="true"
-        android:drawable="@drawable/btn_center_pressed" />
-    <item
-        android:state_focused="true"
-        android:state_enabled="true"
-        android:drawable="@drawable/btn_center_selected" />
-    <item
-        android:state_enabled="true"
-        android:drawable="@drawable/btn_center_default" />
-    <item
-        android:drawable="@drawable/btn_center_default" />
-</selector>
diff --git a/java/res/drawable/btn_keyboard_key.xml b/java/res/drawable/btn_keyboard_key.xml
deleted file mode 100644
index 797bc10..0000000
--- a/java/res/drawable/btn_keyboard_key.xml
+++ /dev/null
@@ -1,38 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 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.
--->
-
-<selector xmlns:android="http://schemas.android.com/apk/res/android">
-
-    <!-- Toggle keys. Use checkable/checked state. -->
-
-    <item android:state_checkable="true" android:state_checked="true"
-          android:state_pressed="true"
-          android:drawable="@drawable/btn_keyboard_key_pressed_on" />
-    <item android:state_checkable="true" android:state_pressed="true"
-          android:drawable="@drawable/btn_keyboard_key_pressed_off" />
-    <item android:state_checkable="true" android:state_checked="true"
-          android:drawable="@drawable/btn_keyboard_key_normal_on" />
-    <item android:state_checkable="true"
-          android:drawable="@drawable/btn_keyboard_key_normal_off" />
-
-    <!-- Normal keys -->
-
-    <item android:state_pressed="true"
-          android:drawable="@drawable/btn_keyboard_key_pressed" />
-    <item
-          android:drawable="@drawable/btn_keyboard_key_normal" />
-
-</selector>
diff --git a/java/res/drawable/btn_keyboard_key3.xml b/java/res/drawable/btn_keyboard_key3.xml
deleted file mode 100644
index dbe82d5..0000000
--- a/java/res/drawable/btn_keyboard_key3.xml
+++ /dev/null
@@ -1,36 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- 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.
--->
-
-<selector xmlns:android="http://schemas.android.com/apk/res/android">
-
-    <!-- Toggle keys. Use checkable/checked state. -->
-
-    <item android:state_checkable="true" android:state_checked="true"
-          android:state_pressed="true"
-          android:drawable="@drawable/btn_keyboard_key_pressed_on" />
-    <item android:state_checkable="true" android:state_pressed="true"
-          android:drawable="@drawable/btn_keyboard_key_fulltrans_normal" />
-    <item android:state_checkable="true" android:state_checked="true"
-          android:drawable="@drawable/btn_keyboard_key_normal_on" />
-    <item android:state_checkable="true"
-          android:drawable="@drawable/btn_keyboard_key_fulltrans_pressed" />
-
-    <!-- Normal keys -->
-
-    <item android:state_pressed="true"
-          android:drawable="@drawable/btn_keyboard_key_fulltrans_normal" />
-    <item android:drawable="@drawable/btn_keyboard_key_fulltrans_pressed" />
-</selector>
diff --git a/java/res/drawable/btn_keyboard_key_functional_gb.xml b/java/res/drawable/btn_keyboard_key_functional_gb.xml
new file mode 100644
index 0000000..431359c
--- /dev/null
+++ b/java/res/drawable/btn_keyboard_key_functional_gb.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2013 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.
+-->
+
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+    <!-- Functional keys. -->
+    <item android:state_pressed="true"
+          android:drawable="@drawable/btn_keyboard_key_dark_pressed" />
+    <item android:drawable="@drawable/btn_keyboard_key_dark_normal" />
+</selector>
diff --git a/java/res/drawable/btn_keyboard_key_functional_ics.xml b/java/res/drawable/btn_keyboard_key_functional_ics.xml
new file mode 100644
index 0000000..5dcde5f
--- /dev/null
+++ b/java/res/drawable/btn_keyboard_key_functional_ics.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2013 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.
+-->
+
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+    <!-- Functional keys. -->
+    <item android:state_pressed="true"
+          android:drawable="@drawable/btn_keyboard_key_dark_pressed_holo" />
+    <item android:drawable="@drawable/btn_keyboard_key_dark_normal_holo" />
+</selector>
diff --git a/java/res/drawable/btn_keyboard_key_gingerbread.xml b/java/res/drawable/btn_keyboard_key_gb.xml
similarity index 94%
rename from java/res/drawable/btn_keyboard_key_gingerbread.xml
rename to java/res/drawable/btn_keyboard_key_gb.xml
index 5b4399e..3fc253e 100644
--- a/java/res/drawable/btn_keyboard_key_gingerbread.xml
+++ b/java/res/drawable/btn_keyboard_key_gb.xml
@@ -15,23 +15,19 @@
 -->
 
 <selector xmlns:android="http://schemas.android.com/apk/res/android">
-
     <!-- Functional keys. -->
-
     <item android:state_single="true" android:state_pressed="true"
           android:drawable="@drawable/btn_keyboard_key_dark_pressed" />
     <item android:state_single="true"
           android:drawable="@drawable/btn_keyboard_key_dark_normal" />
 
     <!-- Action keys. -->
-
     <item android:state_active="true" android:state_pressed="true"
           android:drawable="@drawable/btn_keyboard_key_dark_pressed" />
     <item android:state_active="true"
           android:drawable="@drawable/btn_keyboard_key_dark_normal" />
 
     <!-- Toggle keys. Use checkable/checked state. -->
-
     <item android:state_checkable="true" android:state_checked="true" android:state_pressed="true"
           android:drawable="@drawable/btn_keyboard_key_dark_pressed_on" />
     <item android:state_checkable="true" android:state_pressed="true"
@@ -41,8 +37,11 @@
     <item android:state_checkable="true"
           android:drawable="@drawable/btn_keyboard_key_dark_normal_off" />
 
-    <!-- Normal keys. -->
+    <!-- Empty background keys. -->
+    <item android:state_empty="true"
+          android:drawable="@drawable/transparent" />
 
+    <!-- Normal keys. -->
     <item android:state_pressed="true"
           android:drawable="@drawable/btn_keyboard_key_light_pressed" />
     <item android:drawable="@drawable/btn_keyboard_key_light_normal" />
diff --git a/java/res/drawable/btn_keyboard_key_ics.xml b/java/res/drawable/btn_keyboard_key_ics.xml
index e893da1..0c86e16 100644
--- a/java/res/drawable/btn_keyboard_key_ics.xml
+++ b/java/res/drawable/btn_keyboard_key_ics.xml
@@ -15,23 +15,19 @@
 -->
 
 <selector xmlns:android="http://schemas.android.com/apk/res/android">
-
     <!-- Functional keys. -->
-
     <item android:state_single="true" android:state_pressed="true"
           android:drawable="@drawable/btn_keyboard_key_dark_pressed_holo" />
     <item android:state_single="true"
           android:drawable="@drawable/btn_keyboard_key_dark_normal_holo" />
 
     <!-- Action keys. -->
-
     <item android:state_active="true" android:state_pressed="true"
           android:drawable="@drawable/btn_keyboard_key_dark_pressed_holo" />
     <item android:state_active="true"
           android:drawable="@drawable/btn_keyboard_key_dark_active_holo" />
 
     <!-- Toggle keys. Use checkable/checked state. -->
-
     <item android:state_checkable="true" android:state_checked="true" android:state_pressed="true"
           android:drawable="@drawable/btn_keyboard_key_dark_pressed_on_holo" />
     <item android:state_checkable="true" android:state_pressed="true"
@@ -41,8 +37,11 @@
     <item android:state_checkable="true"
           android:drawable="@drawable/btn_keyboard_key_dark_normal_off_holo" />
 
-    <!-- Normal keys. -->
+    <!-- Empty background keys. -->
+    <item android:state_empty="true"
+          android:drawable="@drawable/transparent" />
 
+    <!-- Normal keys. -->
     <item android:state_pressed="true"
           android:drawable="@drawable/btn_keyboard_key_light_pressed_holo" />
     <item android:drawable="@drawable/btn_keyboard_key_light_normal_holo" />
diff --git a/java/res/drawable/btn_keyboard_key_popup.xml b/java/res/drawable/btn_keyboard_key_popup_gb.xml
similarity index 100%
rename from java/res/drawable/btn_keyboard_key_popup.xml
rename to java/res/drawable/btn_keyboard_key_popup_gb.xml
diff --git a/java/res/drawable/btn_keyboard_key_stone.xml b/java/res/drawable/btn_keyboard_key_stone.xml
deleted file mode 100644
index 9bc3f18..0000000
--- a/java/res/drawable/btn_keyboard_key_stone.xml
+++ /dev/null
@@ -1,50 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- 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.
--->
-
-<selector xmlns:android="http://schemas.android.com/apk/res/android">
-
-    <!-- Functional keys. -->
-
-    <item android:state_single="true" android:state_pressed="true"
-          android:drawable="@drawable/btn_keyboard_key_fulltrans_pressed" />
-    <item android:state_single="true"
-          android:drawable="@drawable/btn_keyboard_key_normal_stone" />
-
-    <!-- Action keys. -->
-
-    <item android:state_active="true" android:state_pressed="true"
-          android:drawable="@drawable/btn_keyboard_key_fulltrans_pressed" />
-    <item android:state_active="true"
-          android:drawable="@drawable/btn_keyboard_key_normal_stone" />
-
-    <!-- Toggle keys. Use checkable/checked state. -->
-
-    <item android:state_checkable="true" android:state_checked="true"
-          android:state_pressed="true"
-          android:drawable="@drawable/btn_keyboard_key_normal_on_stone" />
-    <item android:state_checkable="true" android:state_pressed="true"
-          android:drawable="@drawable/btn_keyboard_key_normal_off_stone" />
-    <item android:state_checkable="true" android:state_checked="true"
-          android:drawable="@drawable/btn_keyboard_key_normal_on_stone" />
-    <item android:state_checkable="true"
-          android:drawable="@drawable/btn_keyboard_key_normal_off_stone" />
-
-    <!-- Normal keys. -->
-
-    <item android:state_pressed="true"
-          android:drawable="@drawable/btn_keyboard_key_fulltrans_pressed" />
-    <item android:drawable="@drawable/btn_keyboard_key_normal_stone" />
-</selector>
diff --git a/java/res/drawable/btn_suggestion.xml b/java/res/drawable/btn_suggestion_gb.xml
similarity index 100%
rename from java/res/drawable/btn_suggestion.xml
rename to java/res/drawable/btn_suggestion_gb.xml
diff --git a/java/res/drawable/ic_emoji_nature_light.xml b/java/res/drawable/ic_emoji_nature_light.xml
new file mode 100644
index 0000000..543409e
--- /dev/null
+++ b/java/res/drawable/ic_emoji_nature_light.xml
@@ -0,0 +1,33 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+**
+** Copyright 2013, 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.
+*/
+-->
+
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+    <item
+        android:state_focused="true"
+        android:drawable="@drawable/ic_emoji_nature_light_activated" />
+    <item
+        android:state_pressed="true"
+        android:drawable="@drawable/ic_emoji_nature_light_activated" />
+    <item
+        android:state_selected="true"
+        android:drawable="@drawable/ic_emoji_nature_light_activated" />
+    <item
+        android:drawable="@drawable/ic_emoji_nature_light_normal" />
+</selector>
diff --git a/java/res/drawable/ic_emoji_objects_light.xml b/java/res/drawable/ic_emoji_objects_light.xml
new file mode 100644
index 0000000..4096e69
--- /dev/null
+++ b/java/res/drawable/ic_emoji_objects_light.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+**
+** Copyright 2013, 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.
+*/
+-->
+
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+    <item
+        android:state_focused="true"
+        android:drawable="@drawable/ic_emoji_objects_light_activated" />
+    <item
+        android:state_pressed="true"
+        android:drawable="@drawable/ic_emoji_objects_light_activated" />
+    <item
+        android:state_selected="true"
+        android:drawable="@drawable/ic_emoji_objects_light_activated" />
+    <item android:drawable="@drawable/ic_emoji_objects_light_normal" />
+</selector>
diff --git a/java/res/drawable/ic_emoji_people_light.xml b/java/res/drawable/ic_emoji_people_light.xml
new file mode 100644
index 0000000..ea9e406
--- /dev/null
+++ b/java/res/drawable/ic_emoji_people_light.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+**
+** Copyright 2013, 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.
+*/
+-->
+
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+    <item
+        android:state_focused="true"
+        android:drawable="@drawable/ic_emoji_people_light_activated" />
+    <item
+        android:state_pressed="true"
+        android:drawable="@drawable/ic_emoji_people_light_activated" />
+    <item
+        android:state_selected="true"
+        android:drawable="@drawable/ic_emoji_people_light_activated" />
+    <item android:drawable="@drawable/ic_emoji_people_light_normal" />
+</selector>
diff --git a/java/res/drawable/ic_emoji_places_light.xml b/java/res/drawable/ic_emoji_places_light.xml
new file mode 100644
index 0000000..312cad9
--- /dev/null
+++ b/java/res/drawable/ic_emoji_places_light.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+**
+** Copyright 2013, 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.
+*/
+-->
+
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+    <item
+        android:state_focused="true"
+        android:drawable="@drawable/ic_emoji_places_light_activated" />
+    <item
+        android:state_pressed="true"
+        android:drawable="@drawable/ic_emoji_places_light_activated" />
+    <item
+        android:state_selected="true"
+        android:drawable="@drawable/ic_emoji_places_light_activated" />
+    <item android:drawable="@drawable/ic_emoji_places_light_normal" />
+</selector>
diff --git a/java/res/drawable/ic_emoji_recent_light.xml b/java/res/drawable/ic_emoji_recent_light.xml
new file mode 100644
index 0000000..8c2123f
--- /dev/null
+++ b/java/res/drawable/ic_emoji_recent_light.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+**
+** Copyright 2013, 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.
+*/
+-->
+
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+    <item
+        android:state_focused="true"
+        android:drawable="@drawable/ic_emoji_recent_light_activated" />
+    <item
+        android:state_pressed="true"
+        android:drawable="@drawable/ic_emoji_recent_light_activated" />
+    <item
+        android:state_selected="true"
+        android:drawable="@drawable/ic_emoji_recent_light_activated" />
+    <item android:drawable="@drawable/ic_emoji_recent_light_normal" />
+</selector>
diff --git a/java/res/drawable/ic_emoji_symbols_light.xml b/java/res/drawable/ic_emoji_symbols_light.xml
new file mode 100644
index 0000000..79aaf0f
--- /dev/null
+++ b/java/res/drawable/ic_emoji_symbols_light.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+**
+** Copyright 2013, 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.
+*/
+-->
+
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+    <item
+        android:state_focused="true"
+        android:drawable="@drawable/ic_emoji_symbols_light_activated" />
+    <item
+        android:state_pressed="true"
+        android:drawable="@drawable/ic_emoji_symbols_light_activated" />
+    <item
+        android:state_selected="true"
+        android:drawable="@drawable/ic_emoji_symbols_light_activated" />
+    <item android:drawable="@drawable/ic_emoji_symbols_light_normal" />
+</selector>
diff --git a/java/res/drawable/keyboard_key_feedback.xml b/java/res/drawable/keyboard_key_feedback_gb.xml
similarity index 100%
rename from java/res/drawable/keyboard_key_feedback.xml
rename to java/res/drawable/keyboard_key_feedback_gb.xml
diff --git a/java/res/layout/emoji_keyboard_page.xml b/java/res/layout/emoji_keyboard_page.xml
new file mode 100644
index 0000000..e0b752b
--- /dev/null
+++ b/java/res/layout/emoji_keyboard_page.xml
@@ -0,0 +1,33 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+**
+** Copyright 2013, 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.
+*/
+-->
+
+<com.android.inputmethod.keyboard.internal.ScrollViewWithNotifier
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:id="@+id/emoji_keyboard_scroller"
+    android:clipToPadding="false"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+>
+    <com.android.inputmethod.keyboard.internal.ScrollKeyboardView
+        android:id="@+id/emoji_keyboard_page"
+        android:layoutDirection="ltr"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content" />
+</com.android.inputmethod.keyboard.internal.ScrollViewWithNotifier>
diff --git a/java/res/layout/key_preview.xml b/java/res/layout/emoji_keyboard_tab_icon.xml
similarity index 73%
copy from java/res/layout/key_preview.xml
copy to java/res/layout/emoji_keyboard_tab_icon.xml
index 2fcd0c4..d79276e 100644
--- a/java/res/layout/key_preview.xml
+++ b/java/res/layout/emoji_keyboard_tab_icon.xml
@@ -2,7 +2,7 @@
 <!--
 /*
 **
-** Copyright 2010, The Android Open Source Project
+** Copyright 2013, The Android Open Source Project
 **
 ** Licensed under the Apache License, Version 2.0 (the "License");
 ** you may not use this file except in compliance with the License.
@@ -18,10 +18,9 @@
 */
 -->
 
-<TextView xmlns:android="http://schemas.android.com/apk/res/android"
-    android:layout_width="wrap_content"
+<ImageView xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="0dip"
+    android:layout_weight="1.0"
     android:layout_height="wrap_content"
-    android:background="@drawable/keyboard_key_feedback"
-    android:minWidth="32dp"
     android:gravity="center"
 />
diff --git a/java/res/layout/key_preview.xml b/java/res/layout/emoji_keyboard_tab_label.xml
similarity index 80%
copy from java/res/layout/key_preview.xml
copy to java/res/layout/emoji_keyboard_tab_label.xml
index 2fcd0c4..62c552d 100644
--- a/java/res/layout/key_preview.xml
+++ b/java/res/layout/emoji_keyboard_tab_label.xml
@@ -2,7 +2,7 @@
 <!--
 /*
 **
-** Copyright 2010, The Android Open Source Project
+** Copyright 2013, 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.
@@ -19,9 +19,8 @@
 -->
 
 <TextView xmlns:android="http://schemas.android.com/apk/res/android"
-    android:layout_width="wrap_content"
+    android:layout_width="0dip"
+    android:layout_weight="1.0"
     android:layout_height="wrap_content"
-    android:background="@drawable/keyboard_key_feedback"
-    android:minWidth="32dp"
     android:gravity="center"
 />
diff --git a/java/res/layout/emoji_keyboard_view.xml b/java/res/layout/emoji_keyboard_view.xml
new file mode 100644
index 0000000..4566a5a
--- /dev/null
+++ b/java/res/layout/emoji_keyboard_view.xml
@@ -0,0 +1,110 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+**
+** Copyright 2013, 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.
+*/
+-->
+
+<com.android.inputmethod.keyboard.EmojiKeyboardView
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:id="@+id/emoji_keyboard_view"
+    android:orientation="vertical"
+    android:layout_width="match_parent"
+    android:layout_height="wrap_content"
+    style="?attr/emojiKeyboardViewStyle"
+>
+    <LinearLayout
+        android:orientation="horizontal"
+        android:layout_width="match_parent"
+        android:layout_height="@dimen/suggestions_strip_height"
+    >
+        <TabHost
+            android:id="@+id/emoji_category_tabhost"
+            android:layout_width="0dip"
+            android:layout_weight="87.5"
+            android:layout_height="match_parent"
+        >
+            <TabWidget
+                android:id="@android:id/tabs"
+                android:layout_width="match_parent"
+                android:layout_height="match_parent"
+                android:background="@drawable/tab_selected"
+                android:divider="@null"
+                android:tabStripEnabled="true"
+                android:tabStripLeft="@drawable/tab_unselected"
+                android:tabStripRight="@drawable/tab_unselected" />
+            <FrameLayout
+                android:id="@android:id/tabcontent"
+                android:layout_width="0dip"
+                android:layout_height="0dip"
+            >
+                <!-- Empty placeholder that TabHost requires. But we don't use it to actually
+                     display anything. We monitor the tab changes and change the ViewPager.
+                     Similarly the ViewPager swipes are intercepted and passed to the TabHost. -->
+                <View
+                    android:id="@+id/emoji_keyboard_dummy"
+                    android:layout_width="0dip"
+                    android:layout_height="0dip"
+                    android:visibility="gone" />
+            </FrameLayout>
+        </TabHost>
+        <View
+            android:layout_width="2dip"
+            android:layout_height="match_parent"
+            android:background="@drawable/suggestions_strip_divider" />
+        <ImageButton
+            android:id="@+id/emoji_keyboard_delete"
+            android:layout_width="0dip"
+            android:layout_weight="12.5"
+            android:layout_height="match_parent"
+            android:background="@color/emoji_key_background_color"
+            android:src="@drawable/sym_keyboard_delete_holo_dark" />
+    </LinearLayout>
+    <android.support.v4.view.ViewPager
+        android:id="@+id/emoji_keyboard_pager"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content" />
+    <com.android.inputmethod.keyboard.EmojiCategoryPageIndicatorView
+        android:id="@+id/emoji_category_page_id_view"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:background="@color/emoji_category_page_id_view_background" />
+    <LinearLayout
+        android:id="@+id/emoji_action_bar"
+        android:orientation="horizontal"
+        android:layout_width="match_parent"
+        android:layout_height="0dip"
+        android:layout_weight="1"
+    >
+        <ImageButton
+            android:id="@+id/emoji_keyboard_alphabet"
+            android:layout_width="0dip"
+            android:layout_weight="0.15"
+            android:layout_height="match_parent"
+            android:src="@drawable/ic_ime_switcher_dark" />
+        <ImageButton
+            android:id="@+id/emoji_keyboard_space"
+            android:layout_width="0dip"
+            android:layout_weight="0.70"
+            android:layout_height="match_parent" />
+        <ImageButton
+            android:id="@+id/emoji_keyboard_send"
+            android:layout_width="0dip"
+            android:layout_weight="0.15"
+            android:layout_height="match_parent"
+            android:src="@drawable/sym_keyboard_return_holo_dark" />
+    </LinearLayout>
+</com.android.inputmethod.keyboard.EmojiKeyboardView>
diff --git a/java/res/layout/hint_add_to_dictionary.xml b/java/res/layout/hint_add_to_dictionary.xml
index 73de44f..68a9faf 100644
--- a/java/res/layout/hint_add_to_dictionary.xml
+++ b/java/res/layout/hint_add_to_dictionary.xml
@@ -33,4 +33,4 @@
     android:clickable="false"
     android:singleLine="true"
     android:ellipsize="none"
-    style="?attr/suggestionBackgroundStyle" />
+    style="?attr/suggestionWordStyle" />
diff --git a/java/res/layout/input_view.xml b/java/res/layout/input_view.xml
index 1ffb8a3..0b682d1 100644
--- a/java/res/layout/input_view.xml
+++ b/java/res/layout/input_view.xml
@@ -20,34 +20,41 @@
 
 <com.android.inputmethod.latin.InputView
     xmlns:android="http://schemas.android.com/apk/res/android"
-    android:orientation="vertical"
     android:layout_width="match_parent"
     android:layout_height="wrap_content"
->
+    android:gravity="bottom|center_horizontal"
+    android:orientation="vertical" >
     <!-- The height of key_preview_backing view will automatically be determined by code. -->
     <View
         android:id="@+id/key_preview_backing"
         android:layout_width="match_parent"
         android:layout_height="0dp" />
+    <LinearLayout
+        android:id="@+id/main_keyboard_frame"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:orientation="vertical" >
 
-    <!-- To ensure that key preview popup is correctly placed when the current system locale is
-         one of RTL locales, layoutDirection="ltr" is needed in the SDK version 17+. -->
-    <com.android.inputmethod.latin.suggestions.SuggestionStripView
-        android:id="@+id/suggestion_strip_view"
-        android:layoutDirection="ltr"
-        android:layout_width="match_parent"
-        android:layout_height="@dimen/suggestions_strip_height"
-        android:gravity="center_vertical"
-        android:paddingRight="@dimen/suggestions_strip_padding"
-        android:paddingLeft="@dimen/suggestions_strip_padding"
-        style="?attr/suggestionStripViewStyle" />
+        <!-- To ensure that key preview popup is correctly placed when the current system locale is
+             one of RTL locales, layoutDirection="ltr" is needed in the SDK version 17+. -->
+        <com.android.inputmethod.latin.suggestions.SuggestionStripView
+            android:id="@+id/suggestion_strip_view"
+            android:layoutDirection="ltr"
+            android:layout_width="match_parent"
+            android:layout_height="@dimen/suggestions_strip_height"
+            android:gravity="center_vertical"
+            android:paddingRight="@dimen/suggestions_strip_padding"
+            android:paddingLeft="@dimen/suggestions_strip_padding"
+            style="?attr/suggestionStripViewStyle" />
 
-    <!-- To ensure that key preview popup is correctly placed when the current system locale is
-         one of RTL locales, layoutDirection="ltr" is needed in the SDK version 17+. -->
-    <com.android.inputmethod.keyboard.MainKeyboardView
-        android:id="@+id/keyboard_view"
-        android:layoutDirection="ltr"
-        android:layout_alignParentBottom="true"
-        android:layout_width="match_parent"
-        android:layout_height="wrap_content" />
+        <!-- To ensure that key preview popup is correctly placed when the current system locale is
+             one of RTL locales, layoutDirection="ltr" is needed in the SDK version 17+. -->
+        <com.android.inputmethod.keyboard.MainKeyboardView
+            android:id="@+id/keyboard_view"
+            android:layoutDirection="ltr"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content" />
+    </LinearLayout>
+    <include
+        layout="@layout/emoji_keyboard_view" />
 </com.android.inputmethod.latin.InputView>
diff --git a/java/res/layout/key_preview.xml b/java/res/layout/key_preview_gb.xml
similarity index 93%
rename from java/res/layout/key_preview.xml
rename to java/res/layout/key_preview_gb.xml
index 2fcd0c4..2f2a321 100644
--- a/java/res/layout/key_preview.xml
+++ b/java/res/layout/key_preview_gb.xml
@@ -21,7 +21,7 @@
 <TextView xmlns:android="http://schemas.android.com/apk/res/android"
     android:layout_width="wrap_content"
     android:layout_height="wrap_content"
-    android:background="@drawable/keyboard_key_feedback"
+    android:background="@drawable/keyboard_key_feedback_gb"
     android:minWidth="32dp"
     android:gravity="center"
 />
diff --git a/java/res/layout/more_keys_keyboard.xml b/java/res/layout/more_keys_keyboard.xml
index 6b2464b..6637117 100644
--- a/java/res/layout/more_keys_keyboard.xml
+++ b/java/res/layout/more_keys_keyboard.xml
@@ -17,17 +17,17 @@
 ** limitations under the License.
 */
 -->
+
 <LinearLayout
-        xmlns:android="http://schemas.android.com/apk/res/android"
-        android:layout_width="wrap_content"
-        android:layout_height="wrap_content"
-        android:orientation="horizontal"
-        style="?attr/moreKeysKeyboardPanelStyle"
-        >
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="wrap_content"
+    android:layout_height="wrap_content"
+    android:orientation="horizontal"
+    style="?attr/moreKeysKeyboardContainerStyle"
+>
     <com.android.inputmethod.keyboard.MoreKeysKeyboardView
-            xmlns:latin="http://schemas.android.com/apk/res/com.android.inputmethod.latin"
-            android:id="@+id/more_keys_keyboard_view"
-            android:layout_width="wrap_content"
-            android:layout_height="wrap_content"
-            />
+        xmlns:latin="http://schemas.android.com/apk/res/com.android.inputmethod.latin"
+        android:id="@+id/more_keys_keyboard_view"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content" />
 </LinearLayout>
diff --git a/java/res/layout/more_suggestions.xml b/java/res/layout/more_suggestions.xml
index b41bb8a..8659f07 100644
--- a/java/res/layout/more_suggestions.xml
+++ b/java/res/layout/more_suggestions.xml
@@ -17,21 +17,21 @@
 ** limitations under the License.
 */
 -->
+
 <LinearLayout
-        xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="wrap_content"
+    android:layout_height="wrap_content"
+    android:orientation="horizontal"
+    style="?attr/moreKeysKeyboardContainerStyle"
+>
+    <com.android.inputmethod.latin.suggestions.MoreSuggestionsView
+        xmlns:latin="http://schemas.android.com/apk/res/com.android.inputmethod.latin"
+        android:id="@+id/more_suggestions_view"
         android:layout_width="wrap_content"
         android:layout_height="wrap_content"
-        android:orientation="horizontal"
-        style="?attr/moreKeysKeyboardPanelStyle"
-        >
-    <com.android.inputmethod.latin.suggestions.MoreSuggestionsView
-            xmlns:latin="http://schemas.android.com/apk/res/com.android.inputmethod.latin"
-            android:id="@+id/more_suggestions_view"
-            android:layout_width="wrap_content"
-            android:layout_height="wrap_content"
-            latin:keyLetterSize="@dimen/suggestion_text_size"
-            latin:keyLabelSize="@dimen/suggestion_text_size"
-            latin:keyHintLetterRatio="@fraction/more_suggestions_info_ratio"
-            latin:keyHintLetterColor="@android:color/white"
-            />
+        latin:keyLetterSize="@dimen/suggestion_text_size"
+        latin:keyLabelSize="@dimen/suggestion_text_size"
+        latin:keyHintLetterRatio="@fraction/more_suggestions_info_ratio"
+        latin:keyHintLetterColor="@android:color/white" />
 </LinearLayout>
diff --git a/java/res/layout/suggestion_info.xml b/java/res/layout/suggestion_info.xml
index a4ad6df..0aa2600 100644
--- a/java/res/layout/suggestion_info.xml
+++ b/java/res/layout/suggestion_info.xml
@@ -24,4 +24,4 @@
     android:layout_height="wrap_content"
     android:textSize="6dp"
     android:textColor="@android:color/white"
-    style="?attr/suggestionBackgroundStyle" />
+    style="?attr/suggestionWordStyle" />
diff --git a/java/res/layout/suggestion_word.xml b/java/res/layout/suggestion_word.xml
index fa00e04..c82a13c 100644
--- a/java/res/layout/suggestion_word.xml
+++ b/java/res/layout/suggestion_word.xml
@@ -36,4 +36,4 @@
     android:clickable="false"
     android:singleLine="true"
     android:ellipsize="none"
-    style="?attr/suggestionBackgroundStyle" />
+    style="?attr/suggestionWordStyle" />
diff --git a/java/res/raw/main_fr.dict b/java/res/raw/main_fr.dict
index 31fb2af..10adad0 100644
--- a/java/res/raw/main_fr.dict
+++ b/java/res/raw/main_fr.dict
Binary files differ
diff --git a/java/res/raw/main_pt_br.dict b/java/res/raw/main_pt_br.dict
index 557d46e..f9ae9b5 100644
--- a/java/res/raw/main_pt_br.dict
+++ b/java/res/raw/main_pt_br.dict
Binary files differ
diff --git a/java/res/raw/main_ru.dict b/java/res/raw/main_ru.dict
index 86c368e..7dec624 100644
--- a/java/res/raw/main_ru.dict
+++ b/java/res/raw/main_ru.dict
Binary files differ
diff --git a/java/res/values-af/strings.xml b/java/res/values-af/strings.xml
index fc71368..01a57e7 100644
--- a/java/res/values-af/strings.xml
+++ b/java/res/values-af/strings.xml
@@ -143,6 +143,7 @@
     <string name="subtype_with_layout_en_GB" msgid="2179097748724725906">"Engels (VK) (<xliff:g id="LAYOUT">%s</xliff:g>)"</string>
     <string name="subtype_with_layout_en_US" msgid="1362581347576714579">"Engels (VS) (<xliff:g id="LAYOUT">%s</xliff:g>)"</string>
     <string name="subtype_with_layout_es_US" msgid="6261791057007890189">"Spaans (VS) (<xliff:g id="LAYOUT">%s</xliff:g>)"</string>
+    <string name="subtype_nepali_traditional" msgid="9032247506728040447">"<xliff:g id="LANGUAGE">%s</xliff:g> (Tradisioneel)"</string>
     <string name="subtype_no_language" msgid="7137390094240139495">"Geen taal nie (alfabet)"</string>
     <string name="subtype_no_language_qwerty" msgid="244337630616742604">"Alfabet (QWERTY)"</string>
     <string name="subtype_no_language_qwertz" msgid="443066912507547976">"Alfabet (QWERTZ)"</string>
@@ -150,6 +151,7 @@
     <string name="subtype_no_language_dvorak" msgid="1564494667584718094">"Alfabet (Dvorak)"</string>
     <string name="subtype_no_language_colemak" msgid="5837418400010302623">"Alfabet (Colemak)"</string>
     <string name="subtype_no_language_pcqwerty" msgid="5354918232046200018">"Alfabet (PC)"</string>
+    <string name="subtype_emoji" msgid="7483586578074549196">"Emosiekone"</string>
     <string name="custom_input_styles_title" msgid="8429952441821251512">"Gepasmaakte invoerstyle"</string>
     <string name="add_style" msgid="6163126614514489951">"Voeg styl by"</string>
     <string name="add" msgid="8299699805688017798">"Voeg by"</string>
diff --git a/java/res/values-am/strings.xml b/java/res/values-am/strings.xml
index 93f9a60..774f2c5 100644
--- a/java/res/values-am/strings.xml
+++ b/java/res/values-am/strings.xml
@@ -143,6 +143,7 @@
     <string name="subtype_with_layout_en_GB" msgid="2179097748724725906">"እንግሊዘኛ (ዩናይትድ ኪንግደም) (<xliff:g id="LAYOUT">%s</xliff:g>)"</string>
     <string name="subtype_with_layout_en_US" msgid="1362581347576714579">"እንግሊዘኛ (አሜሪካ) (<xliff:g id="LAYOUT">%s</xliff:g>)"</string>
     <string name="subtype_with_layout_es_US" msgid="6261791057007890189">"ስፓኒሽኛ (ዩኤስ) (<xliff:g id="LAYOUT">%s</xliff:g>)"</string>
+    <string name="subtype_nepali_traditional" msgid="9032247506728040447">"<xliff:g id="LANGUAGE">%s</xliff:g> (ተለምዷዊ)"</string>
     <string name="subtype_no_language" msgid="7137390094240139495">"ምንም ቋንቋ (ፊደላት)"</string>
     <string name="subtype_no_language_qwerty" msgid="244337630616742604">"ፊደላት (QWERTY)"</string>
     <string name="subtype_no_language_qwertz" msgid="443066912507547976">"ፊደላት (QWERTZ)"</string>
@@ -150,6 +151,7 @@
     <string name="subtype_no_language_dvorak" msgid="1564494667584718094">"ፊደላት (Dvorak)"</string>
     <string name="subtype_no_language_colemak" msgid="5837418400010302623">"ፊደላት (Colemak)"</string>
     <string name="subtype_no_language_pcqwerty" msgid="5354918232046200018">"ፊደላት (ፒሲ)"</string>
+    <string name="subtype_emoji" msgid="7483586578074549196">"ኢሞጂ"</string>
     <string name="custom_input_styles_title" msgid="8429952441821251512">"የተበጁ የግቤት ስታይሎች"</string>
     <string name="add_style" msgid="6163126614514489951">"ስታይል አክል"</string>
     <string name="add" msgid="8299699805688017798">"አክል"</string>
diff --git a/java/res/values-ar/strings.xml b/java/res/values-ar/strings.xml
index efb2949..7e39b59 100644
--- a/java/res/values-ar/strings.xml
+++ b/java/res/values-ar/strings.xml
@@ -143,6 +143,7 @@
     <string name="subtype_with_layout_en_GB" msgid="2179097748724725906">"الإنجليزية (المملكة المتحدة) (<xliff:g id="LAYOUT">%s</xliff:g>)"</string>
     <string name="subtype_with_layout_en_US" msgid="1362581347576714579">"الإنجليزية (الولايات المتحدة) (<xliff:g id="LAYOUT">%s</xliff:g>)"</string>
     <string name="subtype_with_layout_es_US" msgid="6261791057007890189">"الإسبانية (الأمريكية) (<xliff:g id="LAYOUT">%s</xliff:g>)"</string>
+    <string name="subtype_nepali_traditional" msgid="9032247506728040447">"<xliff:g id="LANGUAGE">%s</xliff:g> (التقليدية)"</string>
     <string name="subtype_no_language" msgid="7137390094240139495">"بدون لغة (أبجدية)"</string>
     <string name="subtype_no_language_qwerty" msgid="244337630616742604">"الأبجدية (QWERTY)"</string>
     <string name="subtype_no_language_qwertz" msgid="443066912507547976">"الأبجدية (QWERTZ)"</string>
@@ -150,6 +151,7 @@
     <string name="subtype_no_language_dvorak" msgid="1564494667584718094">"الأبجدية (Dvorak)"</string>
     <string name="subtype_no_language_colemak" msgid="5837418400010302623">"الأبجدية (Colemak)"</string>
     <string name="subtype_no_language_pcqwerty" msgid="5354918232046200018">"الأبجدية (PC)"</string>
+    <string name="subtype_emoji" msgid="7483586578074549196">"الرموز التعبيرية"</string>
     <string name="custom_input_styles_title" msgid="8429952441821251512">"أنماط الإدخال المخصصة"</string>
     <string name="add_style" msgid="6163126614514489951">"إضافة نمط"</string>
     <string name="add" msgid="8299699805688017798">"إضافة"</string>
diff --git a/java/res/values-az-rAZ/strings.xml b/java/res/values-az-rAZ/strings.xml
deleted file mode 100644
index c9d6ac7..0000000
--- a/java/res/values-az-rAZ/strings.xml
+++ /dev/null
@@ -1,242 +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.
-*/
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="english_ime_input_options" msgid="3909945612939668554">"Daxiletmə seçimləri"</string>
-    <string name="english_ime_research_log" msgid="8492602295696577851">"Araşdırma Jurnalı Əmrləri"</string>
-    <string name="use_contacts_for_spellchecking_option_title" msgid="5374120998125353898">"Kontakt adlarına baxın"</string>
-    <string name="use_contacts_for_spellchecking_option_summary" msgid="8754413382543307713">"Orfoqrafik yoxlanış kontakt siyahınızdakı qeydlərdən istifadə edir"</string>
-    <string name="vibrate_on_keypress" msgid="5258079494276955460">"Vibrasiyalı klikləmə"</string>
-    <string name="sound_on_keypress" msgid="6093592297198243644">"Klikləmə səsi"</string>
-    <string name="popup_on_keypress" msgid="123894815723512944">"Klikləmədə popup"</string>
-    <string name="general_category" msgid="1859088467017573195">"Ümumi"</string>
-    <string name="correction_category" msgid="2236750915056607613">"Mətn korreksiyası"</string>
-    <string name="gesture_typing_category" msgid="497263612130532630">"Jestlərlə yazma"</string>
-    <string name="misc_category" msgid="6894192814868233453">"Digər seçənəklər"</string>
-    <string name="advanced_settings" msgid="362895144495591463">"Qabaqcıl ayarlar"</string>
-    <string name="advanced_settings_summary" msgid="4487980456152830271">"Ekspertlər üçün seçimlər"</string>
-    <string name="include_other_imes_in_language_switch_list" msgid="4533689960308565519">"Digər daxiletmə metodlarına keçin"</string>
-    <string name="include_other_imes_in_language_switch_list_summary" msgid="840637129103317635">"Dil keçid düyməsi başqa daxiletmə metodlarını da əhatə edir"</string>
-    <string name="show_language_switch_key" msgid="5915478828318774384">"Dil keçidi düyməsi"</string>
-    <string name="show_language_switch_key_summary" msgid="7343403647474265713">"Çoxsaylı daxiletmə dilləri aktivləşdikdə göstər"</string>
-    <string name="sliding_key_input_preview" msgid="6604262359510068370">"Slayd indikatorunu göstər"</string>
-    <string name="sliding_key_input_preview_summary" msgid="6340524345729093886">"Sürüşdürmə və ya Simvol düymələrinə keçərkən vizual işarəni göstər"</string>
-    <string name="key_preview_popup_dismiss_delay" msgid="6213164897443068248">"Klaviş popup kənarlaşdırılmasında gecikmə"</string>
-    <string name="key_preview_popup_dismiss_no_delay" msgid="2096123151571458064">"Gecikmə yoxdur"</string>
-    <string name="key_preview_popup_dismiss_default_delay" msgid="2166964333903906734">"Varsayılan"</string>
-    <string name="abbreviation_unit_milliseconds" msgid="8700286094028323363">"<xliff:g id="MILLISECONDS">%s</xliff:g> millisaniyə"</string>
-    <string name="settings_system_default" msgid="6268225104743331821">"Sistem defoltu"</string>
-    <string name="use_contacts_dict" msgid="4435317977804180815">"Kontakt adları təklif edin"</string>
-    <string name="use_contacts_dict_summary" msgid="6599983334507879959">"Təklif və korreksiya üçün Kontaktlardakı adlardan istifadə edin"</string>
-    <string name="use_double_space_period" msgid="8781529969425082860">"İkili boşluq periodu"</string>
-    <string name="use_double_space_period_summary" msgid="6532892187247952799">"Boşluqdakı iki klik boşluqdan sonra pauza daxil edir"</string>
-    <string name="auto_cap" msgid="1719746674854628252">"Avtomatik böyük hərfləşmə"</string>
-    <string name="auto_cap_summary" msgid="7934452761022946874">"Hər cümlənin ilk sözünü böyük hərflə yaz"</string>
-    <string name="edit_personal_dictionary" msgid="3996910038952940420">"Şəxsi lüğət"</string>
-    <string name="configure_dictionaries_title" msgid="4238652338556902049">"Əlavə lüğətlər"</string>
-    <string name="main_dictionary" msgid="4798763781818361168">"Əsas lüğət"</string>
-    <string name="prefs_show_suggestions" msgid="8026799663445531637">"Korreksiya təkliflərini göstər"</string>
-    <string name="prefs_show_suggestions_summary" msgid="1583132279498502825">"Yazarkən təklif edilən sözləri ekranda göstər"</string>
-    <string name="prefs_suggestion_visibility_show_name" msgid="3219916594067551303">"Həmişə göstər"</string>
-    <string name="prefs_suggestion_visibility_show_only_portrait_name" msgid="3859783767435239118">"Portret rejimində göstər"</string>
-    <string name="prefs_suggestion_visibility_hide_name" msgid="6309143926422234673">"Həmişə gizlət"</string>
-    <string name="prefs_block_potentially_offensive_title" msgid="5078480071057408934">"Təhqiredici sözləri əngəlləyin"</string>
-    <string name="prefs_block_potentially_offensive_summary" msgid="2371835479734991364">"Potensial təhqiredici sözlər təklif etməyin"</string>
-    <string name="auto_correction" msgid="7630720885194996950">"Avtomatik-korreksiya"</string>
-    <string name="auto_correction_summary" msgid="5625751551134658006">"Boşluq və punktuasiya avtomatik yanlış sözləri düzəldir"</string>
-    <string name="auto_correction_threshold_mode_off" msgid="8470882665417944026">"Deaktiv"</string>
-    <string name="auto_correction_threshold_mode_modest" msgid="8788366690620799097">"Orta"</string>
-    <string name="auto_correction_threshold_mode_aggressive" msgid="7319007299148899623">"Aqressiv"</string>
-    <string name="auto_correction_threshold_mode_very_aggressive" msgid="1853309024129480416">"Çox aqressiv"</string>
-    <string name="bigram_prediction" msgid="1084449187723948550">"Növbəti-söz təklifləri"</string>
-    <string name="bigram_prediction_summary" msgid="3896362682751109677">"Təkliflər edilməsində əvvəlki sözdən istifadə et"</string>
-    <string name="gesture_input" msgid="826951152254563827">"Jestlərlə yazmağı aktiv et"</string>
-    <string name="gesture_input_summary" msgid="9180350639305731231">"Hərflər üzərində sürüşdürərək söz daxil edin"</string>
-    <string name="gesture_preview_trail" msgid="3802333369335722221">"Jest izini göstər"</string>
-    <string name="gesture_floating_preview_text" msgid="4443240334739381053">"Dinamik üzmə önizləməsi"</string>
-    <string name="gesture_floating_preview_text_summary" msgid="4472696213996203533">"Jest zamanı təklif edilmiş sözə baxın"</string>
-    <string name="added_word" msgid="8993883354622484372">"<xliff:g id="WORD">%s</xliff:g> : Saxlanmış"</string>
-    <string name="label_go_key" msgid="1635148082137219148">"Get"</string>
-    <string name="label_next_key" msgid="362972844525672568">"Növbəti"</string>
-    <string name="label_previous_key" msgid="1211868118071386787">"Əvvəlki"</string>
-    <string name="label_done_key" msgid="2441578748772529288">"Hazırdır"</string>
-    <string name="label_send_key" msgid="2815056534433717444">"Göndər"</string>
-    <string name="label_pause_key" msgid="181098308428035340">"Pauza"</string>
-    <string name="label_wait_key" msgid="6402152600878093134">"Gözlə"</string>
-    <string name="spoken_use_headphones" msgid="896961781287283493">"Parolu səsli eşitmək üçün qulaqcığı taxın"</string>
-    <string name="spoken_current_text_is" msgid="2485723011272583845">"Cari mətn %s\'dir"</string>
-    <string name="spoken_no_text_entered" msgid="7479685225597344496">"Mətn daxil edilməyib"</string>
-    <string name="spoken_description_unknown" msgid="3197434010402179157">"%d açar kodu"</string>
-    <string name="spoken_description_shift" msgid="244197883292549308">"Sürüşdürmə"</string>
-    <string name="spoken_description_shift_shifted" msgid="1681877323344195035">"Sürüşdürmə aktivdir (deaktiv etmək üçün klikləyin)"</string>
-    <string name="spoken_description_caps_lock" msgid="3276478269526304432">"Böyük hərf kilidi aktivdir (deaktiv etmək üçün klikləyin)"</string>
-    <string name="spoken_description_delete" msgid="8740376944276199801">"Sil"</string>
-    <string name="spoken_description_to_symbol" msgid="5486340107500448969">"Simvollar"</string>
-    <string name="spoken_description_to_alpha" msgid="23129338819771807">"Hərflər"</string>
-    <string name="spoken_description_to_numeric" msgid="591752092685161732">"Nömrələr"</string>
-    <string name="spoken_description_settings" msgid="4627462689603838099">"Parametrlər"</string>
-    <string name="spoken_description_tab" msgid="2667716002663482248">"Tab"</string>
-    <string name="spoken_description_space" msgid="2582521050049860859">"Boşluq"</string>
-    <string name="spoken_description_mic" msgid="615536748882611950">"Səs daxiletməsi"</string>
-    <string name="spoken_description_smiley" msgid="2256309826200113918">"Smaylik"</string>
-    <string name="spoken_description_return" msgid="8178083177238315647">"Qayıt"</string>
-    <string name="spoken_description_search" msgid="1247236163755920808">"Axtar"</string>
-    <string name="spoken_description_dot" msgid="40711082435231673">"Nöqtə"</string>
-    <string name="spoken_description_language_switch" msgid="5507091328222331316">"Dil keçidi"</string>
-    <string name="spoken_description_action_next" msgid="8636078276664150324">"Növbəti"</string>
-    <string name="spoken_description_action_previous" msgid="800872415009336208">"Əvvəlki"</string>
-    <string name="spoken_description_shiftmode_on" msgid="5700440798609574589">"Sürüşdürmə aktivdir"</string>
-    <string name="spoken_description_shiftmode_locked" msgid="593175803181701830">"Böyük hərf kilidi aktivdir"</string>
-    <string name="spoken_description_shiftmode_off" msgid="657219998449174808">"Sürüşdürmə deaktivdir"</string>
-    <string name="spoken_description_mode_symbol" msgid="7183343879909747642">"Simvol rejimi"</string>
-    <string name="spoken_description_mode_alpha" msgid="3528307674390156956">"Hərf rejimi"</string>
-    <string name="spoken_description_mode_phone" msgid="6520207943132026264">"Telefon rejimi"</string>
-    <string name="spoken_description_mode_phone_shift" msgid="5499629753962641227">"Telefon simvol rejimi"</string>
-    <string name="announce_keyboard_hidden" msgid="8718927835531429807">"Gizlədilmiş klaviatura"</string>
-    <string name="announce_keyboard_mode" msgid="4729081055438508321">"<xliff:g id="MODE">%s</xliff:g> klaviaturası göstərilir"</string>
-    <string name="keyboard_mode_date" msgid="3137520166817128102">"tarix"</string>
-    <string name="keyboard_mode_date_time" msgid="339593358488851072">"gün və tarix"</string>
-    <string name="keyboard_mode_email" msgid="6216248078128294262">"E-poçt"</string>
-    <string name="keyboard_mode_im" msgid="1137405089766557048">"mesajlaşma"</string>
-    <string name="keyboard_mode_number" msgid="7991623440699957069">"nömrə"</string>
-    <string name="keyboard_mode_phone" msgid="6851627527401433229">"telefon"</string>
-    <string name="keyboard_mode_text" msgid="6479436687899701619">"mətn"</string>
-    <string name="keyboard_mode_time" msgid="4381856885582143277">"vaxt"</string>
-    <string name="keyboard_mode_url" msgid="1519819835514911218">"URL"</string>
-    <string name="voice_input" msgid="3583258583521397548">"Səs daxiletmə klavişi"</string>
-    <string name="voice_input_modes_main_keyboard" msgid="3360660341121083174">"Əsas klaviaturada"</string>
-    <string name="voice_input_modes_symbols_keyboard" msgid="7203213240786084067">"Simvol klaviaturasında"</string>
-    <string name="voice_input_modes_off" msgid="3745699748218082014">"Qapalı"</string>
-    <string name="voice_input_modes_summary_main_keyboard" msgid="6586544292900314339">"Əsas klaviaturada mikrofon"</string>
-    <string name="voice_input_modes_summary_symbols_keyboard" msgid="5233725927281932391">"Simvol klaviaturasında mikrofon"</string>
-    <string name="voice_input_modes_summary_off" msgid="63875609591897607">"Səs daxiletməsi deaktiv edildi"</string>
-    <string name="configure_input_method" msgid="373356270290742459">"Daxiletmə üsullarını sazla"</string>
-    <string name="language_selection_title" msgid="1651299598555326750">"Daxiletmə dilləri"</string>
-    <string name="send_feedback" msgid="1780431884109392046">"Cavab rəyi göndərin"</string>
-    <string name="select_language" msgid="3693815588777926848">"Daxiletmə dilləri"</string>
-    <string name="hint_add_to_dictionary" msgid="573678656946085380">"Yadda saxlamaq üçün yenidən toxunun"</string>
-    <string name="has_dictionary" msgid="6071847973466625007">"Lüğət mövcuddur"</string>
-    <string name="prefs_enable_log" msgid="6620424505072963557">"İstifadəçi əks əlaqəsini aktiv et"</string>
-    <string name="prefs_description_log" msgid="7525225584555429211">"İstifadə statistikası və xəta haqqında hesabatları avtomatik göndərməklə daxiletmə metodu redaktəsini təkmilləşdirməyə kömək edin."</string>
-    <string name="keyboard_layout" msgid="8451164783510487501">"Klaviatura teması"</string>
-    <string name="subtype_en_GB" msgid="88170601942311355">"İngilis (BK)"</string>
-    <string name="subtype_en_US" msgid="6160452336634534239">"İngilis (ABŞ)"</string>
-    <string name="subtype_es_US" msgid="5583145191430180200">"İspan (ABŞ)"</string>
-    <string name="subtype_with_layout_en_GB" msgid="2179097748724725906">"İngilis (BK) (<xliff:g id="LAYOUT">%s</xliff:g>)"</string>
-    <string name="subtype_with_layout_en_US" msgid="1362581347576714579">"İngilis (ABŞ) (<xliff:g id="LAYOUT">%s</xliff:g>)"</string>
-    <string name="subtype_with_layout_es_US" msgid="6261791057007890189">"İspan (ABŞ) (<xliff:g id="LAYOUT">%s</xliff:g>)"</string>
-    <string name="subtype_no_language" msgid="7137390094240139495">"Dil yoxdur (Əlifba)"</string>
-    <string name="subtype_no_language_qwerty" msgid="244337630616742604">"Əlifba (QWERTY)"</string>
-    <string name="subtype_no_language_qwertz" msgid="443066912507547976">"Əlifba (QWERTZ)"</string>
-    <string name="subtype_no_language_azerty" msgid="8144348527575640087">"Əlifba (AZERTY)"</string>
-    <string name="subtype_no_language_dvorak" msgid="1564494667584718094">"Əlifba (Dvorak)"</string>
-    <string name="subtype_no_language_colemak" msgid="5837418400010302623">"Əlifba (Colemak)"</string>
-    <string name="subtype_no_language_pcqwerty" msgid="5354918232046200018">"Əlifba (PC)"</string>
-    <string name="custom_input_styles_title" msgid="8429952441821251512">"Xüsusi daxiletmə üslubları"</string>
-    <string name="add_style" msgid="6163126614514489951">"Stil əlavə et"</string>
-    <string name="add" msgid="8299699805688017798">"Əlavə et"</string>
-    <string name="remove" msgid="4486081658752944606">"Ləğv et"</string>
-    <string name="save" msgid="7646738597196767214">"Yadda saxla"</string>
-    <string name="subtype_locale" msgid="8576443440738143764">"Dil"</string>
-    <string name="keyboard_layout_set" msgid="4309233698194565609">"Tərtibat"</string>
-    <string name="custom_input_style_note_message" msgid="8826731320846363423">"Xüsusi daxiletmə üslubunuz istifadəyə başlamazdan əvvəl aktivləşdirilməlidir. Aktiv etmək istəyirsiniz?"</string>
-    <string name="enable" msgid="5031294444630523247">"Aktiv et"</string>
-    <string name="not_now" msgid="6172462888202790482">"İndi yox"</string>
-    <string name="custom_input_style_already_exists" msgid="8008728952215449707">"Eyni daxiletmə üslubu artıq mövcuddur: <xliff:g id="INPUT_STYLE_NAME">%s</xliff:g>"</string>
-    <string name="prefs_usability_study_mode" msgid="1261130555134595254">"Rahat işləmə rejimi"</string>
-    <string name="prefs_key_longpress_timeout_settings" msgid="6102240298932897873">"Klavişi uzun müddət basmada gecikmə"</string>
-    <string name="prefs_keypress_vibration_duration_settings" msgid="7918341459947439226">"Vibrasiyalı klikləmə müddəti"</string>
-    <string name="prefs_keypress_sound_volume_settings" msgid="6027007337036891623">"Səsli klikləmə səsi"</string>
-    <string name="prefs_read_external_dictionary" msgid="2588931418575013067">"Xarici lüğət faylını oxuyun"</string>
-    <string name="read_external_dictionary_no_files_message" msgid="4947420942224623792">"Endirmə Qovluğunda heç bir lüğət faylı yoxdur"</string>
-    <string name="read_external_dictionary_multiple_files_title" msgid="7637749044265808628">"Yükləmək üçün lüğət faylı seçin"</string>
-    <string name="read_external_dictionary_confirm_install_message" msgid="6898610163768980870">"Bu faylı həqiqətən <xliff:g id="LOCALE_NAME">%s</xliff:g> adlı yerə quraşdıraq?"</string>
-    <string name="error" msgid="8940763624668513648">"Xəta var idi"</string>
-    <string name="button_default" msgid="3988017840431881491">"Defolt"</string>
-    <string name="setup_welcome_title" msgid="6112821709832031715">"<xliff:g id="APPLICATION_NAME">%s</xliff:g> təbiqinə xoş gəlmisiniz"</string>
-    <string name="setup_welcome_additional_description" msgid="8150252008545768953">"Jest Yazısı ilə"</string>
-    <string name="setup_start_action" msgid="8936036460897347708">"Başlayın"</string>
-    <string name="setup_next_action" msgid="371821437915144603">"Növbəti addım"</string>
-    <string name="setup_steps_title" msgid="6400373034871816182">"<xliff:g id="APPLICATION_NAME">%s</xliff:g> quraşdırılır"</string>
-    <string name="setup_step1_title" msgid="3147967630253462315">"<xliff:g id="APPLICATION_NAME">%s</xliff:g> tətbiqini aktivləşdir"</string>
-    <string name="setup_step1_instruction" msgid="2578631936624637241">"Lütfən, \"<xliff:g id="APPLICATION_NAME">%s</xliff:g>\" tətbiqini Dil və daxiletmə parametrlərinizdə yoxlayın. Bununla tətbiqin cihazınızda işləməsinə icazə veriləcək."</string>
-    <string name="setup_step1_finished_instruction" msgid="10761482004957994">"<xliff:g id="APPLICATION_NAME">%s</xliff:g> artıq sizin Dil və daxiletmə parametrlərinizdə aktivləşdirildi, beləliklə da bu mərhələ tamamlandı. İndi isə növbəti mərhələyə eçin!"</string>
-    <string name="setup_step1_action" msgid="4366513534999901728">"Parametrlərdə aktivləşdir"</string>
-    <string name="setup_step2_title" msgid="6860725447906690594">"<xliff:g id="APPLICATION_NAME">%s</xliff:g> tətbiqinə keçin"</string>
-    <string name="setup_step2_instruction" msgid="9141481964870023336">"Sonra, \"<xliff:g id="APPLICATION_NAME">%s</xliff:g>\" tətbiqini aktiv mətn-daxiletmə metodu olaraq seçin."</string>
-    <string name="setup_step2_action" msgid="1660330307159824337">"Daxil metodlarına keç"</string>
-    <string name="setup_step3_title" msgid="3154757183631490281">"Təbrik edirik, tam hazırsınız!"</string>
-    <string name="setup_step3_instruction" msgid="8025981829605426000">"İndi siz <xliff:g id="APPLICATION_NAME">%s</xliff:g> ilə bütün sevimli tətbiqlərinizdə yaza bilərsiniz."</string>
-    <string name="setup_step3_action" msgid="600879797256942259">"Əlavə dillər quraşdır"</string>
-    <string name="setup_finish_action" msgid="276559243409465389">"Sona çatdı"</string>
-    <string name="show_setup_wizard_icon" msgid="5008028590593710830">"Tətbiq ikonasını göstər"</string>
-    <string name="show_setup_wizard_icon_summary" msgid="4119998322536880213">"Başlatma panelində tətbiq ikonasını göstər"</string>
-    <string name="app_name" msgid="6320102637491234792">"Lüğət Provayderi"</string>
-    <string name="dictionary_provider_name" msgid="3027315045397363079">"Lüğət Provayderi"</string>
-    <string name="dictionary_service_name" msgid="6237472350693511448">"Lüğət Xidməti"</string>
-    <string name="download_description" msgid="6014835283119198591">"Lüğət yeniləmə məlumatı"</string>
-    <string name="dictionary_settings_title" msgid="8091417676045693313">"Əlavə lüğətlər"</string>
-    <string name="dictionary_install_over_metered_network_prompt" msgid="3587517870006332980">"Lüğət mövcuddur"</string>
-    <string name="dictionary_settings_summary" msgid="5305694987799824349">"Lüğət üçün ayarlar"</string>
-    <string name="user_dictionaries" msgid="3582332055892252845">"İstifadəçi lüğətləri"</string>
-    <string name="default_user_dict_pref_name" msgid="1625055720489280530">"İstifadəçi lüğəti"</string>
-    <string name="dictionary_available" msgid="4728975345815214218">"Lüğət mövcuddur"</string>
-    <string name="dictionary_downloading" msgid="2982650524622620983">"Hazırda endirilir"</string>
-    <string name="dictionary_installed" msgid="8081558343559342962">"Quraşdırılıb"</string>
-    <string name="dictionary_disabled" msgid="8950383219564621762">"Quraşdırılıb, deaktiv edilib"</string>
-    <string name="cannot_connect_to_dict_service" msgid="9216933695765732398">"Lüğət xidmətinə bağlantı problemi"</string>
-    <string name="no_dictionaries_available" msgid="8039920716566132611">"Lüğət mövcud deyil"</string>
-    <string name="check_for_updates_now" msgid="8087688440916388581">"Təzələ"</string>
-    <string name="last_update" msgid="730467549913588780">"Son yeniləmə"</string>
-    <string name="message_updating" msgid="4457761393932375219">"Güncəlləmələr yoxlanılır"</string>
-    <string name="message_loading" msgid="8689096636874758814">"Yüklənir..."</string>
-    <string name="main_dict_description" msgid="3072821352793492143">"Əsas lüğət"</string>
-    <string name="cancel" msgid="6830980399865683324">"Ləğv et"</string>
-    <string name="install_dict" msgid="180852772562189365">"Quraşdırın"</string>
-    <string name="cancel_download_dict" msgid="7843340278507019303">"Ləğv et"</string>
-    <string name="delete_dict" msgid="756853268088330054">"Sil"</string>
-    <string name="should_download_over_metered_prompt" msgid="2878629598667658845">"Mobil cihazınızda seçilmiş dil üzrə lüğət mövcuddur.&lt;br/&gt; Yazı təcrübənizi təkmilləşdirmək üçün <xliff:g id="LANGUAGE">%1$s</xliff:g> lüğətini &lt;b&gt;endirməyi&lt;/b&gt; tövsiyə edirik.&lt;br/&gt; &lt;br/&gt; Endirmə 3G ilə bir və ya iki dəqiqə çəkəcək. &lt;b&gt;Limitsiz data planınız&lt;/b&gt;.&lt;br/&gt; olmadığı halda əlavə xərc tutula bilər, endirməni avtomatik başlatmaq üçün Wi-Fi bağlantı tapmanızı tövsiyə edirik.&lt;br/&gt; &lt;br/&gt; Məsləhət: Siz lüğətləri mobil cihazınızın &lt;b&gt;Dil və daxiletmə&lt;/b&gt; <b>Parametrlərindən</b> endirə və ya ləğv edə bilərsiniz."</string>
-    <string name="download_over_metered" msgid="1643065851159409546">"İndi endirin (<xliff:g id="SIZE_IN_MEGABYTES">%1$.1f</xliff:g> MB)"</string>
-    <string name="do_not_download_over_metered" msgid="2176209579313941583">"Wi-Fi ilə endir"</string>
-    <string name="dict_available_notification_title" msgid="6514288591959117288">"<xliff:g id="LANGUAGE">%1$s</xliff:g> üçün lüğət mövcuddur"</string>
-    <string name="dict_available_notification_description" msgid="1075194169443163487">"Nəzərdən keçirmək və endirmək üçün klikləyin"</string>
-    <string name="toast_downloading_suggestions" msgid="1313027353588566660">"Endirilir: <xliff:g id="LANGUAGE">%1$s</xliff:g> üçün təkliflər tezliklə hazır olacaq."</string>
-    <string name="version_text" msgid="2715354215568469385">"<xliff:g id="VERSION_NUMBER">%1$s</xliff:g> nömrəli versiya"</string>
-    <string name="user_dict_settings_add_menu_title" msgid="1254195365689387076">"Əlavə edin"</string>
-    <string name="user_dict_settings_add_dialog_title" msgid="4096700390211748168">"Lüğətə əlavə edin"</string>
-    <string name="user_dict_settings_add_screen_title" msgid="5818914331629278758">"İfadə"</string>
-    <string name="user_dict_settings_add_dialog_more_options" msgid="5671682004887093112">"Daha çox seçim"</string>
-    <string name="user_dict_settings_add_dialog_less_options" msgid="2716586567241724126">"Daha az seçim"</string>
-    <string name="user_dict_settings_add_dialog_confirm" msgid="4703129507388332950">"OK"</string>
-    <string name="user_dict_settings_add_word_option_name" msgid="6665558053408962865">"Söz:"</string>
-    <string name="user_dict_settings_add_shortcut_option_name" msgid="3094731590655523777">"Qısayol:"</string>
-    <string name="user_dict_settings_add_locale_option_name" msgid="4738643440987277705">"Dil:"</string>
-    <string name="user_dict_settings_add_word_hint" msgid="4902434148985906707">"Bir söz yazın"</string>
-    <string name="user_dict_settings_add_shortcut_hint" msgid="2265453012555060178">"Könüllü qısayol"</string>
-    <string name="user_dict_settings_edit_dialog_title" msgid="3765774633869590352">"Sözü redaktə edin"</string>
-    <string name="user_dict_settings_context_menu_edit_title" msgid="6812255903472456302">"Düzəliş edin"</string>
-    <string name="user_dict_settings_context_menu_delete_title" msgid="8142932447689461181">"Silin"</string>
-    <string name="user_dict_settings_empty_text" msgid="558499587532668203">"İstifadəçi lüğətinizdə heç bir söz yoxdur. Əlavə et (+) düyməsinə toxunmqla bir söz əlavə edin."</string>
-    <string name="user_dict_settings_all_languages" msgid="8276126583216298886">"Bütün dillər üçün"</string>
-    <string name="user_dict_settings_more_languages" msgid="7131268499685180461">"Digər dillər​​..."</string>
-    <string name="user_dict_settings_delete" msgid="110413335187193859">"Silin"</string>
-    <string name="user_dict_fast_scroll_alphabet" msgid="5431919401558285473">" ABCDEFGHIJKLMNOPQRSTUVWXYZ"</string>
-</resources>
diff --git a/java/res/values-az/strings.xml b/java/res/values-az/strings.xml
deleted file mode 100644
index 7fb13f7..0000000
--- a/java/res/values-az/strings.xml
+++ /dev/null
@@ -1,242 +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.
-*/
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="english_ime_input_options" msgid="3909945612939668554">"Daxiletmə seçənəkləri"</string>
-    <string name="english_ime_research_log" msgid="8492602295696577851">"Araşdırma Giriş Əmrləri"</string>
-    <string name="use_contacts_for_spellchecking_option_title" msgid="5374120998125353898">"Kontakt adlarına baxın"</string>
-    <string name="use_contacts_for_spellchecking_option_summary" msgid="8754413382543307713">"Orfoqrafik yoxlanış kontakt siyahınızdakı qeydlərdən istifadə edir"</string>
-    <string name="vibrate_on_keypress" msgid="5258079494276955460">"Vibrasiyalı klikləmə"</string>
-    <string name="sound_on_keypress" msgid="6093592297198243644">"Klikləmə səsi"</string>
-    <string name="popup_on_keypress" msgid="123894815723512944">"Klikləmədə popup"</string>
-    <string name="general_category" msgid="1859088467017573195">"Ümumi"</string>
-    <string name="correction_category" msgid="2236750915056607613">"Mətn korreksiyası"</string>
-    <string name="gesture_typing_category" msgid="497263612130532630">"Jestlərlə yazma"</string>
-    <string name="misc_category" msgid="6894192814868233453">"Digər seçənəklər"</string>
-    <string name="advanced_settings" msgid="362895144495591463">"İnkişaf etmiş parametrlər"</string>
-    <string name="advanced_settings_summary" msgid="4487980456152830271">"Mütəxəssislər üçün Seçənəklər"</string>
-    <string name="include_other_imes_in_language_switch_list" msgid="4533689960308565519">"Digər daxiletmə metodlarına keçin"</string>
-    <string name="include_other_imes_in_language_switch_list_summary" msgid="840637129103317635">"Dil keçid düyməsi başqa daxiletmə metodlarını da əhatə edir"</string>
-    <string name="show_language_switch_key" msgid="5915478828318774384">"Dil keçidi düyməsi"</string>
-    <string name="show_language_switch_key_summary" msgid="7343403647474265713">"Çoxsaylı daxiletmə dilləri aktivləşdikdə göstər"</string>
-    <string name="sliding_key_input_preview" msgid="6604262359510068370">"Slayd indikatorunu göstər"</string>
-    <string name="sliding_key_input_preview_summary" msgid="6340524345729093886">"Sürüşdürmə və ya Simvol düymələrinə keçərkən vizual işarəni göstər"</string>
-    <string name="key_preview_popup_dismiss_delay" msgid="6213164897443068248">"Klaviş popup kənarlaşdırılmasında gecikmə"</string>
-    <string name="key_preview_popup_dismiss_no_delay" msgid="2096123151571458064">"Gecikmə yoxdur"</string>
-    <string name="key_preview_popup_dismiss_default_delay" msgid="2166964333903906734">"Varsayılan"</string>
-    <string name="abbreviation_unit_milliseconds" msgid="8700286094028323363">"<xliff:g id="MILLISECONDS">%s</xliff:g> ms"</string>
-    <string name="settings_system_default" msgid="6268225104743331821">"Sistem defoltu"</string>
-    <string name="use_contacts_dict" msgid="4435317977804180815">"Kontakt adları təklif edin"</string>
-    <string name="use_contacts_dict_summary" msgid="6599983334507879959">"Təklif və korreksiya üçün Kontaktlardakı adlardan istifadə edin"</string>
-    <string name="use_double_space_period" msgid="8781529969425082860">"İkili boşluq periodu"</string>
-    <string name="use_double_space_period_summary" msgid="6532892187247952799">"Boşluqdakı ikiqat tıklama bolşuqdan sonrakı periodu əlavə edir"</string>
-    <string name="auto_cap" msgid="1719746674854628252">"Avtomatik böyük hərf"</string>
-    <string name="auto_cap_summary" msgid="7934452761022946874">"Hər cümlənin ilk sözünü böyük hərflə yaz"</string>
-    <string name="edit_personal_dictionary" msgid="3996910038952940420">"Şəxsi lüğət"</string>
-    <string name="configure_dictionaries_title" msgid="4238652338556902049">"Əlavə lüğətlər"</string>
-    <string name="main_dictionary" msgid="4798763781818361168">"Əsas lüğət"</string>
-    <string name="prefs_show_suggestions" msgid="8026799663445531637">"Korreksiya təkliflərini göstər"</string>
-    <string name="prefs_show_suggestions_summary" msgid="1583132279498502825">"Yazarkən təklif edilən sözləri ekranda göstər"</string>
-    <string name="prefs_suggestion_visibility_show_name" msgid="3219916594067551303">"Həmişə göstər"</string>
-    <string name="prefs_suggestion_visibility_show_only_portrait_name" msgid="3859783767435239118">"Portret rejimində göstər"</string>
-    <string name="prefs_suggestion_visibility_hide_name" msgid="6309143926422234673">"Həmişə gizlət"</string>
-    <string name="prefs_block_potentially_offensive_title" msgid="5078480071057408934">"Təhqiredici sözləri əngəlləyin"</string>
-    <string name="prefs_block_potentially_offensive_summary" msgid="2371835479734991364">"Potensial təhqiredici sözlər təklif etməyin"</string>
-    <string name="auto_correction" msgid="7630720885194996950">"Avtomatik-korreksiya"</string>
-    <string name="auto_correction_summary" msgid="5625751551134658006">"Boşluq və punktuasiya avtomatik yanlış sözləri düzəldir"</string>
-    <string name="auto_correction_threshold_mode_off" msgid="8470882665417944026">"Deaktiv"</string>
-    <string name="auto_correction_threshold_mode_modest" msgid="8788366690620799097">"Orta"</string>
-    <string name="auto_correction_threshold_mode_aggressive" msgid="7319007299148899623">"Aktiv"</string>
-    <string name="auto_correction_threshold_mode_very_aggressive" msgid="1853309024129480416">"Çox aktiv"</string>
-    <string name="bigram_prediction" msgid="1084449187723948550">"Növbəti söz təklifləri"</string>
-    <string name="bigram_prediction_summary" msgid="3896362682751109677">"Təkliflər edilməsində əvvəlki sözdən istifadə et"</string>
-    <string name="gesture_input" msgid="826951152254563827">"Jestlərlə yazmağı aktiv et"</string>
-    <string name="gesture_input_summary" msgid="9180350639305731231">"Hərflər üzərində sürüşdürərək söz daxil edin"</string>
-    <string name="gesture_preview_trail" msgid="3802333369335722221">"Jest izini göstər"</string>
-    <string name="gesture_floating_preview_text" msgid="4443240334739381053">"Dinamik işlətmə önizləməsi"</string>
-    <string name="gesture_floating_preview_text_summary" msgid="4472696213996203533">"Jest zamanı təklif edilən sözə baxın"</string>
-    <string name="added_word" msgid="8993883354622484372">"<xliff:g id="WORD">%s</xliff:g>: Yadda saxlanıldı"</string>
-    <string name="label_go_key" msgid="1635148082137219148">"Get"</string>
-    <string name="label_next_key" msgid="362972844525672568">"Növbəti"</string>
-    <string name="label_previous_key" msgid="1211868118071386787">"Əvvəlki"</string>
-    <string name="label_done_key" msgid="2441578748772529288">"Hazırdır"</string>
-    <string name="label_send_key" msgid="2815056534433717444">"Göndər"</string>
-    <string name="label_pause_key" msgid="181098308428035340">"Pauza"</string>
-    <string name="label_wait_key" msgid="6402152600878093134">"Gözlə"</string>
-    <string name="spoken_use_headphones" msgid="896961781287283493">"Parolu səsli eşitmək üçün qulaqcığı taxın"</string>
-    <string name="spoken_current_text_is" msgid="2485723011272583845">"Cari mətn %s\'dir"</string>
-    <string name="spoken_no_text_entered" msgid="7479685225597344496">"Mətn daxil edilməyib"</string>
-    <string name="spoken_description_unknown" msgid="3197434010402179157">"%d açar kodu"</string>
-    <string name="spoken_description_shift" msgid="244197883292549308">"Sürüşdürmə"</string>
-    <string name="spoken_description_shift_shifted" msgid="1681877323344195035">"Sürüşdürmə aktivdir (deaktiv etmək üçün klikləyin)"</string>
-    <string name="spoken_description_caps_lock" msgid="3276478269526304432">"Böyük hərf kilidi aktivdir (deaktiv etmək üçün klikləyin)"</string>
-    <string name="spoken_description_delete" msgid="8740376944276199801">"Sil"</string>
-    <string name="spoken_description_to_symbol" msgid="5486340107500448969">"Simvollar"</string>
-    <string name="spoken_description_to_alpha" msgid="23129338819771807">"Hərflər"</string>
-    <string name="spoken_description_to_numeric" msgid="591752092685161732">"Nömrələr"</string>
-    <string name="spoken_description_settings" msgid="4627462689603838099">"Parametrlər"</string>
-    <string name="spoken_description_tab" msgid="2667716002663482248">"Tab"</string>
-    <string name="spoken_description_space" msgid="2582521050049860859">"Boşluq"</string>
-    <string name="spoken_description_mic" msgid="615536748882611950">"Səs daxiletməsi"</string>
-    <string name="spoken_description_smiley" msgid="2256309826200113918">"Smaylik"</string>
-    <string name="spoken_description_return" msgid="8178083177238315647">"Qayıt"</string>
-    <string name="spoken_description_search" msgid="1247236163755920808">"Axtar"</string>
-    <string name="spoken_description_dot" msgid="40711082435231673">"Nöqtə"</string>
-    <string name="spoken_description_language_switch" msgid="5507091328222331316">"Dil keçidi"</string>
-    <string name="spoken_description_action_next" msgid="8636078276664150324">"Növbəti"</string>
-    <string name="spoken_description_action_previous" msgid="800872415009336208">"Əvvəlki"</string>
-    <string name="spoken_description_shiftmode_on" msgid="5700440798609574589">"Sürüşdürmə aktivdir"</string>
-    <string name="spoken_description_shiftmode_locked" msgid="593175803181701830">"Böyük hərf kilidi aktivdir"</string>
-    <string name="spoken_description_shiftmode_off" msgid="657219998449174808">"Sürüşdürmə deaktivdir"</string>
-    <string name="spoken_description_mode_symbol" msgid="7183343879909747642">"Simvol rejimi"</string>
-    <string name="spoken_description_mode_alpha" msgid="3528307674390156956">"Hərf rejimi"</string>
-    <string name="spoken_description_mode_phone" msgid="6520207943132026264">"Telefon rejimi"</string>
-    <string name="spoken_description_mode_phone_shift" msgid="5499629753962641227">"Telefon simvol rejimi"</string>
-    <string name="announce_keyboard_hidden" msgid="8718927835531429807">"Klaviatura gizlədilib"</string>
-    <string name="announce_keyboard_mode" msgid="4729081055438508321">"<xliff:g id="MODE">%s</xliff:g> klaviaturası göstərilir"</string>
-    <string name="keyboard_mode_date" msgid="3137520166817128102">"tarix"</string>
-    <string name="keyboard_mode_date_time" msgid="339593358488851072">"gün və tarix"</string>
-    <string name="keyboard_mode_email" msgid="6216248078128294262">"e-poçt"</string>
-    <string name="keyboard_mode_im" msgid="1137405089766557048">"mesajlaşma"</string>
-    <string name="keyboard_mode_number" msgid="7991623440699957069">"nömrə"</string>
-    <string name="keyboard_mode_phone" msgid="6851627527401433229">"telefon"</string>
-    <string name="keyboard_mode_text" msgid="6479436687899701619">"mətn"</string>
-    <string name="keyboard_mode_time" msgid="4381856885582143277">"vaxt"</string>
-    <string name="keyboard_mode_url" msgid="1519819835514911218">"URL"</string>
-    <string name="voice_input" msgid="3583258583521397548">"Səs daxiletmə klavişi"</string>
-    <string name="voice_input_modes_main_keyboard" msgid="3360660341121083174">"Əsas klaviaturada"</string>
-    <string name="voice_input_modes_symbols_keyboard" msgid="7203213240786084067">"Simvol klaviaturasında"</string>
-    <string name="voice_input_modes_off" msgid="3745699748218082014">"Qapalı"</string>
-    <string name="voice_input_modes_summary_main_keyboard" msgid="6586544292900314339">"Əsas klaviaturada mikrofon"</string>
-    <string name="voice_input_modes_summary_symbols_keyboard" msgid="5233725927281932391">"Simvol klaviaturasında mikrofon"</string>
-    <string name="voice_input_modes_summary_off" msgid="63875609591897607">"Səs daxiletməsi deaktiv edildi"</string>
-    <string name="configure_input_method" msgid="373356270290742459">"Daxiletmə üsullarını quraşdırın"</string>
-    <string name="language_selection_title" msgid="1651299598555326750">"Daxiletmə dilləri"</string>
-    <string name="send_feedback" msgid="1780431884109392046">"Cavab rəyi göndərin"</string>
-    <string name="select_language" msgid="3693815588777926848">"Daxiletmə dilləri"</string>
-    <string name="hint_add_to_dictionary" msgid="573678656946085380">"Yadda saxlamaq üçün yenidən toxunun"</string>
-    <string name="has_dictionary" msgid="6071847973466625007">"Lüğət mövcuddur"</string>
-    <string name="prefs_enable_log" msgid="6620424505072963557">"İstifadəçi əks əlaqəsini aktiv et"</string>
-    <string name="prefs_description_log" msgid="7525225584555429211">"İstifadə statistikası və xəta haqqında hesabatları avtomatik göndərməklə daxiletmə metodu redaktəsini təkmilləşdirməyə kömək edin."</string>
-    <string name="keyboard_layout" msgid="8451164783510487501">"Klaviatura teması"</string>
-    <string name="subtype_en_GB" msgid="88170601942311355">"İngilis (BK)"</string>
-    <string name="subtype_en_US" msgid="6160452336634534239">"İngilis (ABŞ)"</string>
-    <string name="subtype_es_US" msgid="5583145191430180200">"İspan (ABŞ)"</string>
-    <string name="subtype_with_layout_en_GB" msgid="2179097748724725906">"İngilis (BK) (<xliff:g id="LAYOUT">%s</xliff:g>)"</string>
-    <string name="subtype_with_layout_en_US" msgid="1362581347576714579">"İngilis (ABŞ) (<xliff:g id="LAYOUT">%s</xliff:g>)"</string>
-    <string name="subtype_with_layout_es_US" msgid="6261791057007890189">"İspan (ABŞ) (<xliff:g id="LAYOUT">%s</xliff:g>)"</string>
-    <string name="subtype_no_language" msgid="7137390094240139495">"Dil yoxdur (Əlifba)"</string>
-    <string name="subtype_no_language_qwerty" msgid="244337630616742604">"Əlifba (QWERTY)"</string>
-    <string name="subtype_no_language_qwertz" msgid="443066912507547976">"Əlifba (QWERTZ)"</string>
-    <string name="subtype_no_language_azerty" msgid="8144348527575640087">"Əlifba (AZERTY)"</string>
-    <string name="subtype_no_language_dvorak" msgid="1564494667584718094">"Əlifba (Dvorak)"</string>
-    <string name="subtype_no_language_colemak" msgid="5837418400010302623">"Əlifba (Colemak)"</string>
-    <string name="subtype_no_language_pcqwerty" msgid="5354918232046200018">"Əlifba (PC)"</string>
-    <string name="custom_input_styles_title" msgid="8429952441821251512">"Xüsusi daxiletmə üslubları"</string>
-    <string name="add_style" msgid="6163126614514489951">"Üslub əlavə edin"</string>
-    <string name="add" msgid="8299699805688017798">"Əlavə edin"</string>
-    <string name="remove" msgid="4486081658752944606">"Ləğv et"</string>
-    <string name="save" msgid="7646738597196767214">"Yadda saxla"</string>
-    <string name="subtype_locale" msgid="8576443440738143764">"Dil"</string>
-    <string name="keyboard_layout_set" msgid="4309233698194565609">"Tərtibat"</string>
-    <string name="custom_input_style_note_message" msgid="8826731320846363423">"Xüsusi daxiletmə üslubunuz istifadəyə başlamazdan əvvəl aktivləşdirilməlidir. Aktiv etmək istəyirsiniz?"</string>
-    <string name="enable" msgid="5031294444630523247">"Aktiv et"</string>
-    <string name="not_now" msgid="6172462888202790482">"İndi yox"</string>
-    <string name="custom_input_style_already_exists" msgid="8008728952215449707">"Eyni daxiletmə üslubu artıq mövcuddur: <xliff:g id="INPUT_STYLE_NAME">%s</xliff:g>"</string>
-    <string name="prefs_usability_study_mode" msgid="1261130555134595254">"Rahat işləmə rejimi"</string>
-    <string name="prefs_key_longpress_timeout_settings" msgid="6102240298932897873">"Klavişi uzun müddət basmada gecikmə"</string>
-    <string name="prefs_keypress_vibration_duration_settings" msgid="7918341459947439226">"Vibrasiyalı klikləmə müddəti"</string>
-    <string name="prefs_keypress_sound_volume_settings" msgid="6027007337036891623">"Səsli klikləmə səsi"</string>
-    <string name="prefs_read_external_dictionary" msgid="2588931418575013067">"Xarici lüğət faylını oxuyun"</string>
-    <string name="read_external_dictionary_no_files_message" msgid="4947420942224623792">"Endirmə Qovluğunda heç bir lüğət faylı yoxdur"</string>
-    <string name="read_external_dictionary_multiple_files_title" msgid="7637749044265808628">"Yükləmək üçün lüğət faylı seçin"</string>
-    <string name="read_external_dictionary_confirm_install_message" msgid="6898610163768980870">"Bu faylı həqiqətən <xliff:g id="LOCALE_NAME">%s</xliff:g> adlı yerə quraşdıraq?"</string>
-    <string name="error" msgid="8940763624668513648">"Xəta var idi"</string>
-    <string name="button_default" msgid="3988017840431881491">"Varsayılan"</string>
-    <string name="setup_welcome_title" msgid="6112821709832031715">"<xliff:g id="APPLICATION_NAME">%s</xliff:g> təbiqinə xoş gəlmisiniz"</string>
-    <string name="setup_welcome_additional_description" msgid="8150252008545768953">"Jest Yazısı ilə"</string>
-    <string name="setup_start_action" msgid="8936036460897347708">"Başlayın"</string>
-    <string name="setup_next_action" msgid="371821437915144603">"Növbəti addım"</string>
-    <string name="setup_steps_title" msgid="6400373034871816182">"<xliff:g id="APPLICATION_NAME">%s</xliff:g> quraşdırılır"</string>
-    <string name="setup_step1_title" msgid="3147967630253462315">"<xliff:g id="APPLICATION_NAME">%s</xliff:g> tətbiqini aktivləşdir"</string>
-    <string name="setup_step1_instruction" msgid="2578631936624637241">"Lütfən, \"<xliff:g id="APPLICATION_NAME">%s</xliff:g>\" tətbiqini Dil və daxiletmə parametrlərinizdə yoxlayın. Bununla tətbiqin cihazınızda işləməsinə icazə veriləcək."</string>
-    <string name="setup_step1_finished_instruction" msgid="10761482004957994">"<xliff:g id="APPLICATION_NAME">%s</xliff:g> artıq sizin Dil və daxiletmə parametrlərinizdə aktivləşdirildi, beləliklə da bu mərhələ tamamlandı. İndi isə növbəti mərhələyə eçin!"</string>
-    <string name="setup_step1_action" msgid="4366513534999901728">"Parametrlərdə aktivləşdir"</string>
-    <string name="setup_step2_title" msgid="6860725447906690594">"<xliff:g id="APPLICATION_NAME">%s</xliff:g> tətbiqinə keçin"</string>
-    <string name="setup_step2_instruction" msgid="9141481964870023336">"Sonra, \"<xliff:g id="APPLICATION_NAME">%s</xliff:g>\" tətbiqini aktiv mətn-daxiletmə metodu olaraq seçin."</string>
-    <string name="setup_step2_action" msgid="1660330307159824337">"Daxil metodlarına keç"</string>
-    <string name="setup_step3_title" msgid="3154757183631490281">"Təbrik edirik, tam hazırsınız!"</string>
-    <string name="setup_step3_instruction" msgid="8025981829605426000">"İndi siz <xliff:g id="APPLICATION_NAME">%s</xliff:g> ilə bütün sevimli tətbiqlərinizdə yaza bilərsiniz."</string>
-    <string name="setup_step3_action" msgid="600879797256942259">"Əlavə dillər quraşdır"</string>
-    <string name="setup_finish_action" msgid="276559243409465389">"Sona çatdı"</string>
-    <string name="show_setup_wizard_icon" msgid="5008028590593710830">"Tətbiq ikonasını göstər"</string>
-    <string name="show_setup_wizard_icon_summary" msgid="4119998322536880213">"Başlatma panelində tətbiq ikonasını göstər"</string>
-    <string name="app_name" msgid="6320102637491234792">"Lüğət Provayderi"</string>
-    <string name="dictionary_provider_name" msgid="3027315045397363079">"Lüğət Provayderi"</string>
-    <string name="dictionary_service_name" msgid="6237472350693511448">"Lüğət Xidməti"</string>
-    <string name="download_description" msgid="6014835283119198591">"Lüğət güncəlləmə məlumatı"</string>
-    <string name="dictionary_settings_title" msgid="8091417676045693313">"Əlavə lüğətlər"</string>
-    <string name="dictionary_install_over_metered_network_prompt" msgid="3587517870006332980">"Lüğət mövcuddur"</string>
-    <string name="dictionary_settings_summary" msgid="5305694987799824349">"Lüğət seçənəkləri"</string>
-    <string name="user_dictionaries" msgid="3582332055892252845">"İstifadəçi lüğətləri"</string>
-    <string name="default_user_dict_pref_name" msgid="1625055720489280530">"İstifadəçi lüğəti"</string>
-    <string name="dictionary_available" msgid="4728975345815214218">"Lüğət mövcuddur"</string>
-    <string name="dictionary_downloading" msgid="2982650524622620983">"Hazırda endirilir"</string>
-    <string name="dictionary_installed" msgid="8081558343559342962">"Quraşdırılıb"</string>
-    <string name="dictionary_disabled" msgid="8950383219564621762">"Quraşdırılıb, deaktiv edilib"</string>
-    <string name="cannot_connect_to_dict_service" msgid="9216933695765732398">"Lüğət xidməti ilə bağlantı problemi"</string>
-    <string name="no_dictionaries_available" msgid="8039920716566132611">"Lüğət mövcud deyil"</string>
-    <string name="check_for_updates_now" msgid="8087688440916388581">"Təzələ"</string>
-    <string name="last_update" msgid="730467549913588780">"Son yeniləmə"</string>
-    <string name="message_updating" msgid="4457761393932375219">"Güncəlləmələr yoxlanılır"</string>
-    <string name="message_loading" msgid="8689096636874758814">"Yüklənir..."</string>
-    <string name="main_dict_description" msgid="3072821352793492143">"Əsas lüğət"</string>
-    <string name="cancel" msgid="6830980399865683324">"ləğv et"</string>
-    <string name="install_dict" msgid="180852772562189365">"Quraşdırın"</string>
-    <string name="cancel_download_dict" msgid="7843340278507019303">"Ləğv et"</string>
-    <string name="delete_dict" msgid="756853268088330054">"Silin"</string>
-    <string name="should_download_over_metered_prompt" msgid="2878629598667658845">"Mobil cihazınızda seçilmiş dil üzrə lüğət mövcuddur.&lt;br/&gt; Yazı təcrübənizi təkmilləşdirmək üçün <xliff:g id="LANGUAGE">%1$s</xliff:g> lüğətini &lt;b&gt;endirməyi&lt;/b&gt; tövsiyə edirik.&lt;br/&gt; &lt;br/&gt; Endirmə 3G ilə bir və ya iki dəqiqə çəkəcək. &lt;b&gt;Limitsiz data planınız&lt;/b&gt;.&lt;br/&gt; olmadığı halda əlavə xərc tutula bilər, endirməni avtomatik başlatmaq üçün Wi-Fi bağlantı tapmanızı tövsiyə edirik.&lt;br/&gt; &lt;br/&gt; Məsləhət: Siz lüğətləri mobil cihazınızın &lt;b&gt;Dil və daxiletmə&lt;/b&gt; <b>Parametrlərindən</b> endirə və ya ləğv edə bilərsiniz."</string>
-    <string name="download_over_metered" msgid="1643065851159409546">"İndi endirin (<xliff:g id="SIZE_IN_MEGABYTES">%1$.1f</xliff:g> MB)"</string>
-    <string name="do_not_download_over_metered" msgid="2176209579313941583">"Wi-Fi ilə endir"</string>
-    <string name="dict_available_notification_title" msgid="6514288591959117288">"<xliff:g id="LANGUAGE">%1$s</xliff:g> üçün lüğət mövcuddur"</string>
-    <string name="dict_available_notification_description" msgid="1075194169443163487">"Nəzərdən keçirmək və endirmək üçün klikləyin"</string>
-    <string name="toast_downloading_suggestions" msgid="1313027353588566660">"Endirilir: <xliff:g id="LANGUAGE">%1$s</xliff:g> üçün təkliflər tezliklə hazır olacaq."</string>
-    <string name="version_text" msgid="2715354215568469385">"<xliff:g id="VERSION_NUMBER">%1$s</xliff:g> nömrəli versiya"</string>
-    <string name="user_dict_settings_add_menu_title" msgid="1254195365689387076">"Əlavə edin"</string>
-    <string name="user_dict_settings_add_dialog_title" msgid="4096700390211748168">"Lüğətə əlavə edin"</string>
-    <string name="user_dict_settings_add_screen_title" msgid="5818914331629278758">"İfadə"</string>
-    <string name="user_dict_settings_add_dialog_more_options" msgid="5671682004887093112">"Daha çox seçim"</string>
-    <string name="user_dict_settings_add_dialog_less_options" msgid="2716586567241724126">"Daha az seçim"</string>
-    <string name="user_dict_settings_add_dialog_confirm" msgid="4703129507388332950">"OK"</string>
-    <string name="user_dict_settings_add_word_option_name" msgid="6665558053408962865">"Söz:"</string>
-    <string name="user_dict_settings_add_shortcut_option_name" msgid="3094731590655523777">"Qısayol:"</string>
-    <string name="user_dict_settings_add_locale_option_name" msgid="4738643440987277705">"Dil:"</string>
-    <string name="user_dict_settings_add_word_hint" msgid="4902434148985906707">"Bir söz yazın"</string>
-    <string name="user_dict_settings_add_shortcut_hint" msgid="2265453012555060178">"Könüllü qısayol"</string>
-    <string name="user_dict_settings_edit_dialog_title" msgid="3765774633869590352">"Sözü redaktə edin"</string>
-    <string name="user_dict_settings_context_menu_edit_title" msgid="6812255903472456302">"Düzəliş edin"</string>
-    <string name="user_dict_settings_context_menu_delete_title" msgid="8142932447689461181">"Silin"</string>
-    <string name="user_dict_settings_empty_text" msgid="558499587532668203">"İstifadəçi lüğətinizdə heç bir söz yoxdur. Əlavə et (+) düyməsinə toxunmqla bir söz əlavə edin."</string>
-    <string name="user_dict_settings_all_languages" msgid="8276126583216298886">"Bütün dillər üçün"</string>
-    <string name="user_dict_settings_more_languages" msgid="7131268499685180461">"Digər dillər​​..."</string>
-    <string name="user_dict_settings_delete" msgid="110413335187193859">"Silin"</string>
-    <string name="user_dict_fast_scroll_alphabet" msgid="5431919401558285473">" ABCDEFGHIJKLMNOPQRSTUVWXYZ"</string>
-</resources>
diff --git a/java/res/values-be/strings.xml b/java/res/values-be/strings.xml
index 21adcca..db40c8a 100644
--- a/java/res/values-be/strings.xml
+++ b/java/res/values-be/strings.xml
@@ -146,6 +146,8 @@
     <string name="subtype_with_layout_en_GB" msgid="2179097748724725906">"Англійская (Вялікабрытанія) (<xliff:g id="LAYOUT">%s</xliff:g>)"</string>
     <string name="subtype_with_layout_en_US" msgid="1362581347576714579">"Англійская (ЗША) (<xliff:g id="LAYOUT">%s</xliff:g>)"</string>
     <string name="subtype_with_layout_es_US" msgid="6261791057007890189">"iспанская (ЗША) ( <xliff:g id="LAYOUT">%s</xliff:g> )"</string>
+    <!-- no translation found for subtype_nepali_traditional (9032247506728040447) -->
+    <skip />
     <!-- no translation found for subtype_no_language (7137390094240139495) -->
     <skip />
     <!-- no translation found for subtype_no_language_qwerty (244337630616742604) -->
@@ -160,6 +162,8 @@
     <skip />
     <!-- no translation found for subtype_no_language_pcqwerty (5354918232046200018) -->
     <skip />
+    <!-- no translation found for subtype_emoji (7483586578074549196) -->
+    <skip />
     <string name="custom_input_styles_title" msgid="8429952441821251512">"Карыстальніцкія стылі ўводу"</string>
     <string name="add_style" msgid="6163126614514489951">"Дадаць стыль"</string>
     <string name="add" msgid="8299699805688017798">"Дадаць"</string>
diff --git a/java/res/values-bg/strings.xml b/java/res/values-bg/strings.xml
index 301f84e..d3879cc 100644
--- a/java/res/values-bg/strings.xml
+++ b/java/res/values-bg/strings.xml
@@ -143,6 +143,7 @@
     <string name="subtype_with_layout_en_GB" msgid="2179097748724725906">"английски (Великобр.) (<xliff:g id="LAYOUT">%s</xliff:g>)"</string>
     <string name="subtype_with_layout_en_US" msgid="1362581347576714579">"английски (САЩ) (<xliff:g id="LAYOUT">%s</xliff:g>)"</string>
     <string name="subtype_with_layout_es_US" msgid="6261791057007890189">"испански (САЩ) (<xliff:g id="LAYOUT">%s</xliff:g>)"</string>
+    <string name="subtype_nepali_traditional" msgid="9032247506728040447">"<xliff:g id="LANGUAGE">%s</xliff:g> (традиционен)"</string>
     <string name="subtype_no_language" msgid="7137390094240139495">"Без език (латиница)"</string>
     <string name="subtype_no_language_qwerty" msgid="244337630616742604">"Латиница (QWERTY)"</string>
     <string name="subtype_no_language_qwertz" msgid="443066912507547976">"Латиница (QWERTZ)"</string>
@@ -150,6 +151,7 @@
     <string name="subtype_no_language_dvorak" msgid="1564494667584718094">"Латиница (Дворак)"</string>
     <string name="subtype_no_language_colemak" msgid="5837418400010302623">"Латиница (Коулмак)"</string>
     <string name="subtype_no_language_pcqwerty" msgid="5354918232046200018">"Латиница (PC)"</string>
+    <string name="subtype_emoji" msgid="7483586578074549196">"Емотикони"</string>
     <string name="custom_input_styles_title" msgid="8429952441821251512">"Персон. стилове за въвежд."</string>
     <string name="add_style" msgid="6163126614514489951">"+ стил"</string>
     <string name="add" msgid="8299699805688017798">"Добавяне"</string>
diff --git a/java/res/values-ca/strings.xml b/java/res/values-ca/strings.xml
index 37e7d56..03e2f3f 100644
--- a/java/res/values-ca/strings.xml
+++ b/java/res/values-ca/strings.xml
@@ -143,6 +143,7 @@
     <string name="subtype_with_layout_en_GB" msgid="2179097748724725906">"Anglès (Regne Unit) (<xliff:g id="LAYOUT">%s</xliff:g>)"</string>
     <string name="subtype_with_layout_en_US" msgid="1362581347576714579">"Anglès (Estats Units) (<xliff:g id="LAYOUT">%s</xliff:g>)"</string>
     <string name="subtype_with_layout_es_US" msgid="6261791057007890189">"Espanyol (EUA) (<xliff:g id="LAYOUT">%s</xliff:g>)"</string>
+    <string name="subtype_nepali_traditional" msgid="9032247506728040447">"<xliff:g id="LANGUAGE">%s</xliff:g> (tradicional)"</string>
     <string name="subtype_no_language" msgid="7137390094240139495">"Cap idioma (alfabet)"</string>
     <string name="subtype_no_language_qwerty" msgid="244337630616742604">"Alfabet (QWERTY)"</string>
     <string name="subtype_no_language_qwertz" msgid="443066912507547976">"Alfabet (QWERTZ)"</string>
@@ -150,6 +151,7 @@
     <string name="subtype_no_language_dvorak" msgid="1564494667584718094">"Alfabet (Dvorak)"</string>
     <string name="subtype_no_language_colemak" msgid="5837418400010302623">"Alfabet (Colemak)"</string>
     <string name="subtype_no_language_pcqwerty" msgid="5354918232046200018">"Alfabet (PC)"</string>
+    <string name="subtype_emoji" msgid="7483586578074549196">"Emoji"</string>
     <string name="custom_input_styles_title" msgid="8429952441821251512">"Estils d\'entrada personalitzats"</string>
     <string name="add_style" msgid="6163126614514489951">"Afeg. estil"</string>
     <string name="add" msgid="8299699805688017798">"Afegeix"</string>
diff --git a/java/res/values-cs/strings.xml b/java/res/values-cs/strings.xml
index 8aeaf73..a707f26 100644
--- a/java/res/values-cs/strings.xml
+++ b/java/res/values-cs/strings.xml
@@ -143,6 +143,7 @@
     <string name="subtype_with_layout_en_GB" msgid="2179097748724725906">"angličtina (VB) (<xliff:g id="LAYOUT">%s</xliff:g>)"</string>
     <string name="subtype_with_layout_en_US" msgid="1362581347576714579">"angličtina (USA) (<xliff:g id="LAYOUT">%s</xliff:g>)"</string>
     <string name="subtype_with_layout_es_US" msgid="6261791057007890189">"španělština (USA) (<xliff:g id="LAYOUT">%s</xliff:g>)"</string>
+    <string name="subtype_nepali_traditional" msgid="9032247506728040447">"<xliff:g id="LANGUAGE">%s</xliff:g> (tradiční)"</string>
     <string name="subtype_no_language" msgid="7137390094240139495">"Žádný jazyk (latinka)"</string>
     <string name="subtype_no_language_qwerty" msgid="244337630616742604">"Latinka (QWERTY)"</string>
     <string name="subtype_no_language_qwertz" msgid="443066912507547976">"Latinka (QWERTZ)"</string>
@@ -150,6 +151,7 @@
     <string name="subtype_no_language_dvorak" msgid="1564494667584718094">"Latinka (Dvorak)"</string>
     <string name="subtype_no_language_colemak" msgid="5837418400010302623">"Latinka (Colemak)"</string>
     <string name="subtype_no_language_pcqwerty" msgid="5354918232046200018">"Latinka (PC)"</string>
+    <string name="subtype_emoji" msgid="7483586578074549196">"Emodži"</string>
     <string name="custom_input_styles_title" msgid="8429952441821251512">"Vlastní styl zadávání"</string>
     <string name="add_style" msgid="6163126614514489951">"Přidat styl"</string>
     <string name="add" msgid="8299699805688017798">"Přidat"</string>
diff --git a/java/res/values-da/strings.xml b/java/res/values-da/strings.xml
index afd9265..1876895 100644
--- a/java/res/values-da/strings.xml
+++ b/java/res/values-da/strings.xml
@@ -143,6 +143,7 @@
     <string name="subtype_with_layout_en_GB" msgid="2179097748724725906">"Engelsk (Storbritannien) (<xliff:g id="LAYOUT">%s</xliff:g>)"</string>
     <string name="subtype_with_layout_en_US" msgid="1362581347576714579">"Engelsk (USA) (<xliff:g id="LAYOUT">%s</xliff:g>)"</string>
     <string name="subtype_with_layout_es_US" msgid="6261791057007890189">"Spansk (USA) (<xliff:g id="LAYOUT">%s</xliff:g>)"</string>
+    <string name="subtype_nepali_traditional" msgid="9032247506728040447">"<xliff:g id="LANGUAGE">%s</xliff:g> (traditionelt)"</string>
     <string name="subtype_no_language" msgid="7137390094240139495">"Intet sprog (Alfabet)"</string>
     <string name="subtype_no_language_qwerty" msgid="244337630616742604">"Alfabet (QWERTY)"</string>
     <string name="subtype_no_language_qwertz" msgid="443066912507547976">"Alfabet (QWERTZ)"</string>
@@ -150,6 +151,7 @@
     <string name="subtype_no_language_dvorak" msgid="1564494667584718094">"Alfabet (Dvorak)"</string>
     <string name="subtype_no_language_colemak" msgid="5837418400010302623">"Alfabet (Colemak)"</string>
     <string name="subtype_no_language_pcqwerty" msgid="5354918232046200018">"Alfabet (PC)"</string>
+    <string name="subtype_emoji" msgid="7483586578074549196">"Emoji"</string>
     <string name="custom_input_styles_title" msgid="8429952441821251512">"Tilpasset inputtypografi"</string>
     <string name="add_style" msgid="6163126614514489951">"Tilføj typografi"</string>
     <string name="add" msgid="8299699805688017798">"Tilføj"</string>
diff --git a/java/res/values-de/strings.xml b/java/res/values-de/strings.xml
index 12386d8..5f3e619 100644
--- a/java/res/values-de/strings.xml
+++ b/java/res/values-de/strings.xml
@@ -143,6 +143,7 @@
     <string name="subtype_with_layout_en_GB" msgid="2179097748724725906">"Englisch (GB) (<xliff:g id="LAYOUT">%s</xliff:g>)"</string>
     <string name="subtype_with_layout_en_US" msgid="1362581347576714579">"Englisch (USA) (<xliff:g id="LAYOUT">%s</xliff:g>)"</string>
     <string name="subtype_with_layout_es_US" msgid="6261791057007890189">"Spanisch (USA) (<xliff:g id="LAYOUT">%s</xliff:g>)"</string>
+    <string name="subtype_nepali_traditional" msgid="9032247506728040447">"<xliff:g id="LANGUAGE">%s</xliff:g> (traditionell)"</string>
     <string name="subtype_no_language" msgid="7137390094240139495">"Keine Sprache (lat. Alphabet)"</string>
     <string name="subtype_no_language_qwerty" msgid="244337630616742604">"Lat. Alphabet (QWERTY)"</string>
     <string name="subtype_no_language_qwertz" msgid="443066912507547976">"Lat. Alphabet (QWERTZ)"</string>
@@ -150,6 +151,7 @@
     <string name="subtype_no_language_dvorak" msgid="1564494667584718094">"Lat. Alphabet (Dvorak)"</string>
     <string name="subtype_no_language_colemak" msgid="5837418400010302623">"Lat. Alphabet (Colemak)"</string>
     <string name="subtype_no_language_pcqwerty" msgid="5354918232046200018">"Lat. Alphabet (PC)"</string>
+    <string name="subtype_emoji" msgid="7483586578074549196">"Emoji"</string>
     <string name="custom_input_styles_title" msgid="8429952441821251512">"Benutzerdefinierte Eingabestile"</string>
     <string name="add_style" msgid="6163126614514489951">"Stil hinzufügen"</string>
     <string name="add" msgid="8299699805688017798">"Hinzufügen"</string>
diff --git a/java/res/values-el/strings.xml b/java/res/values-el/strings.xml
index b010e44..27eb865 100644
--- a/java/res/values-el/strings.xml
+++ b/java/res/values-el/strings.xml
@@ -143,6 +143,7 @@
     <string name="subtype_with_layout_en_GB" msgid="2179097748724725906">"Αγγλικά (ΗΒ) (<xliff:g id="LAYOUT">%s</xliff:g>)"</string>
     <string name="subtype_with_layout_en_US" msgid="1362581347576714579">"Αγγλικά (ΗΠΑ) (<xliff:g id="LAYOUT">%s</xliff:g>)"</string>
     <string name="subtype_with_layout_es_US" msgid="6261791057007890189">"Ισπανικά (ΗΠΑ) (<xliff:g id="LAYOUT">%s</xliff:g>)"</string>
+    <string name="subtype_nepali_traditional" msgid="9032247506728040447">"<xliff:g id="LANGUAGE">%s</xliff:g> (Παραδοσιακά)"</string>
     <string name="subtype_no_language" msgid="7137390094240139495">"Καμία γλώσσα (Αλφάβητο)"</string>
     <string name="subtype_no_language_qwerty" msgid="244337630616742604">"Αλφάβητο (QWERTY)"</string>
     <string name="subtype_no_language_qwertz" msgid="443066912507547976">"Αλφάβητο (QWERTZ)"</string>
@@ -150,6 +151,7 @@
     <string name="subtype_no_language_dvorak" msgid="1564494667584718094">"Αλφάβητο (Dvorak)"</string>
     <string name="subtype_no_language_colemak" msgid="5837418400010302623">"Αλφάβητο (Colemak)"</string>
     <string name="subtype_no_language_pcqwerty" msgid="5354918232046200018">"Αλφάβητο (PC)"</string>
+    <string name="subtype_emoji" msgid="7483586578074549196">"Emoticon"</string>
     <string name="custom_input_styles_title" msgid="8429952441821251512">"Προσαρμοσ. στυλ εισαγ."</string>
     <string name="add_style" msgid="6163126614514489951">"Προσθ. στυλ"</string>
     <string name="add" msgid="8299699805688017798">"Προσθήκη"</string>
diff --git a/java/res/values-en-rGB/strings.xml b/java/res/values-en-rGB/strings.xml
index 130572a..6337aa0 100644
--- a/java/res/values-en-rGB/strings.xml
+++ b/java/res/values-en-rGB/strings.xml
@@ -143,6 +143,7 @@
     <string name="subtype_with_layout_en_GB" msgid="2179097748724725906">"English (UK) (<xliff:g id="LAYOUT">%s</xliff:g>)"</string>
     <string name="subtype_with_layout_en_US" msgid="1362581347576714579">"English (US) (<xliff:g id="LAYOUT">%s</xliff:g>)"</string>
     <string name="subtype_with_layout_es_US" msgid="6261791057007890189">"Spanish (US) (<xliff:g id="LAYOUT">%s</xliff:g>)"</string>
+    <string name="subtype_nepali_traditional" msgid="9032247506728040447">"<xliff:g id="LANGUAGE">%s</xliff:g> (Traditional)"</string>
     <string name="subtype_no_language" msgid="7137390094240139495">"No language (Alphabet)"</string>
     <string name="subtype_no_language_qwerty" msgid="244337630616742604">"Alphabet (QWERTY)"</string>
     <string name="subtype_no_language_qwertz" msgid="443066912507547976">"Alphabet (QWERTZ)"</string>
@@ -150,6 +151,7 @@
     <string name="subtype_no_language_dvorak" msgid="1564494667584718094">"Alphabet (Dvorak)"</string>
     <string name="subtype_no_language_colemak" msgid="5837418400010302623">"Alphabet (Colemak)"</string>
     <string name="subtype_no_language_pcqwerty" msgid="5354918232046200018">"Alphabet (PC)"</string>
+    <string name="subtype_emoji" msgid="7483586578074549196">"Emoji"</string>
     <string name="custom_input_styles_title" msgid="8429952441821251512">"Customised input styles"</string>
     <string name="add_style" msgid="6163126614514489951">"Add style"</string>
     <string name="add" msgid="8299699805688017798">"Add"</string>
diff --git a/java/res/values-en-rIN/strings-appname.xml b/java/res/values-en-rIN/strings-appname.xml
deleted file mode 100644
index 5ad5eae..0000000
--- a/java/res/values-en-rIN/strings-appname.xml
+++ /dev/null
@@ -1,27 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!-- 
-/*
-**
-** Copyright 2013, 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:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="english_ime_name" msgid="5940510615957428904">"Android Keyboard (AOSP)"</string>
-    <string name="spell_checker_service_name" msgid="1254221805440242662">"Android Spell Checker (AOSP)"</string>
-    <string name="english_ime_settings" msgid="5760361067176802794">"Android Keyboard Settings (AOSP)"</string>
-    <string name="android_spell_checker_settings" msgid="6123949487832861885">"Android Spell Checker Settings (AOSP)"</string>
-</resources>
diff --git a/java/res/values-en-rIN/strings.xml b/java/res/values-en-rIN/strings.xml
deleted file mode 100644
index 130572a..0000000
--- a/java/res/values-en-rIN/strings.xml
+++ /dev/null
@@ -1,242 +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.
-*/
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="english_ime_input_options" msgid="3909945612939668554">"Input options"</string>
-    <string name="english_ime_research_log" msgid="8492602295696577851">"Research Log Commands"</string>
-    <string name="use_contacts_for_spellchecking_option_title" msgid="5374120998125353898">"Look up contact names"</string>
-    <string name="use_contacts_for_spellchecking_option_summary" msgid="8754413382543307713">"Spell checker uses entries from your contact list"</string>
-    <string name="vibrate_on_keypress" msgid="5258079494276955460">"Vibrate on keypress"</string>
-    <string name="sound_on_keypress" msgid="6093592297198243644">"Sound on keypress"</string>
-    <string name="popup_on_keypress" msgid="123894815723512944">"Pop-up on key press"</string>
-    <string name="general_category" msgid="1859088467017573195">"General"</string>
-    <string name="correction_category" msgid="2236750915056607613">"Text correction"</string>
-    <string name="gesture_typing_category" msgid="497263612130532630">"Gesture typing"</string>
-    <string name="misc_category" msgid="6894192814868233453">"Other options"</string>
-    <string name="advanced_settings" msgid="362895144495591463">"Advanced settings"</string>
-    <string name="advanced_settings_summary" msgid="4487980456152830271">"Options for experts"</string>
-    <string name="include_other_imes_in_language_switch_list" msgid="4533689960308565519">"Switch to other input methods"</string>
-    <string name="include_other_imes_in_language_switch_list_summary" msgid="840637129103317635">"Language switch key also covers other input methods"</string>
-    <string name="show_language_switch_key" msgid="5915478828318774384">"Language switch key"</string>
-    <string name="show_language_switch_key_summary" msgid="7343403647474265713">"Show when multiple input languages are enabled"</string>
-    <string name="sliding_key_input_preview" msgid="6604262359510068370">"Show slide indicator"</string>
-    <string name="sliding_key_input_preview_summary" msgid="6340524345729093886">"Display visual cue while sliding from Shift or Symbol keys"</string>
-    <string name="key_preview_popup_dismiss_delay" msgid="6213164897443068248">"Key pop-up dismiss delay"</string>
-    <string name="key_preview_popup_dismiss_no_delay" msgid="2096123151571458064">"No delay"</string>
-    <string name="key_preview_popup_dismiss_default_delay" msgid="2166964333903906734">"Default"</string>
-    <string name="abbreviation_unit_milliseconds" msgid="8700286094028323363">"<xliff:g id="MILLISECONDS">%s</xliff:g>ms"</string>
-    <string name="settings_system_default" msgid="6268225104743331821">"System default"</string>
-    <string name="use_contacts_dict" msgid="4435317977804180815">"Suggest Contact names"</string>
-    <string name="use_contacts_dict_summary" msgid="6599983334507879959">"Use names from Contacts for suggestions and corrections"</string>
-    <string name="use_double_space_period" msgid="8781529969425082860">"Double-space full stop"</string>
-    <string name="use_double_space_period_summary" msgid="6532892187247952799">"Double tap on spacebar inserts a full stop followed by a space"</string>
-    <string name="auto_cap" msgid="1719746674854628252">"Auto-capitalisation"</string>
-    <string name="auto_cap_summary" msgid="7934452761022946874">"Capitalise the first word of each sentence"</string>
-    <string name="edit_personal_dictionary" msgid="3996910038952940420">"Personal dictionary"</string>
-    <string name="configure_dictionaries_title" msgid="4238652338556902049">"Add-on dictionaries"</string>
-    <string name="main_dictionary" msgid="4798763781818361168">"Main dictionary"</string>
-    <string name="prefs_show_suggestions" msgid="8026799663445531637">"Show correction suggestions"</string>
-    <string name="prefs_show_suggestions_summary" msgid="1583132279498502825">"Display suggested words while typing"</string>
-    <string name="prefs_suggestion_visibility_show_name" msgid="3219916594067551303">"Always show"</string>
-    <string name="prefs_suggestion_visibility_show_only_portrait_name" msgid="3859783767435239118">"Show in portrait mode"</string>
-    <string name="prefs_suggestion_visibility_hide_name" msgid="6309143926422234673">"Always hide"</string>
-    <string name="prefs_block_potentially_offensive_title" msgid="5078480071057408934">"Block offensive words"</string>
-    <string name="prefs_block_potentially_offensive_summary" msgid="2371835479734991364">"Do not suggest potentially offensive words"</string>
-    <string name="auto_correction" msgid="7630720885194996950">"Auto-correction"</string>
-    <string name="auto_correction_summary" msgid="5625751551134658006">"Correct mistyped words automatically with spacebar and punctuation"</string>
-    <string name="auto_correction_threshold_mode_off" msgid="8470882665417944026">"Off"</string>
-    <string name="auto_correction_threshold_mode_modest" msgid="8788366690620799097">"Modest"</string>
-    <string name="auto_correction_threshold_mode_aggressive" msgid="7319007299148899623">"Aggressive"</string>
-    <string name="auto_correction_threshold_mode_very_aggressive" msgid="1853309024129480416">"Very aggressive"</string>
-    <string name="bigram_prediction" msgid="1084449187723948550">"Next-word suggestions"</string>
-    <string name="bigram_prediction_summary" msgid="3896362682751109677">"Use the previous word when making suggestions"</string>
-    <string name="gesture_input" msgid="826951152254563827">"Enable gesture typing"</string>
-    <string name="gesture_input_summary" msgid="9180350639305731231">"Input a word by sliding through the letters"</string>
-    <string name="gesture_preview_trail" msgid="3802333369335722221">"Show gesture trail"</string>
-    <string name="gesture_floating_preview_text" msgid="4443240334739381053">"Dynamic floating preview"</string>
-    <string name="gesture_floating_preview_text_summary" msgid="4472696213996203533">"See the suggested word while gesturing"</string>
-    <string name="added_word" msgid="8993883354622484372">"<xliff:g id="WORD">%s</xliff:g> : Saved"</string>
-    <string name="label_go_key" msgid="1635148082137219148">"Go"</string>
-    <string name="label_next_key" msgid="362972844525672568">"Next"</string>
-    <string name="label_previous_key" msgid="1211868118071386787">"Prev"</string>
-    <string name="label_done_key" msgid="2441578748772529288">"Finished"</string>
-    <string name="label_send_key" msgid="2815056534433717444">"Send"</string>
-    <string name="label_pause_key" msgid="181098308428035340">"Pause"</string>
-    <string name="label_wait_key" msgid="6402152600878093134">"Wait"</string>
-    <string name="spoken_use_headphones" msgid="896961781287283493">"Plug in a headset to hear password keys spoken aloud."</string>
-    <string name="spoken_current_text_is" msgid="2485723011272583845">"Current text is %s"</string>
-    <string name="spoken_no_text_entered" msgid="7479685225597344496">"No text entered"</string>
-    <string name="spoken_description_unknown" msgid="3197434010402179157">"Key code %d"</string>
-    <string name="spoken_description_shift" msgid="244197883292549308">"Shift"</string>
-    <string name="spoken_description_shift_shifted" msgid="1681877323344195035">"Shift on (tap to disable)"</string>
-    <string name="spoken_description_caps_lock" msgid="3276478269526304432">"Caps lock on (tap to disable)"</string>
-    <string name="spoken_description_delete" msgid="8740376944276199801">"Delete"</string>
-    <string name="spoken_description_to_symbol" msgid="5486340107500448969">"Symbols"</string>
-    <string name="spoken_description_to_alpha" msgid="23129338819771807">"Letters"</string>
-    <string name="spoken_description_to_numeric" msgid="591752092685161732">"Numbers"</string>
-    <string name="spoken_description_settings" msgid="4627462689603838099">"Settings"</string>
-    <string name="spoken_description_tab" msgid="2667716002663482248">"Tab"</string>
-    <string name="spoken_description_space" msgid="2582521050049860859">"Space"</string>
-    <string name="spoken_description_mic" msgid="615536748882611950">"Voice input"</string>
-    <string name="spoken_description_smiley" msgid="2256309826200113918">"Smiley face"</string>
-    <string name="spoken_description_return" msgid="8178083177238315647">"Return"</string>
-    <string name="spoken_description_search" msgid="1247236163755920808">"Search"</string>
-    <string name="spoken_description_dot" msgid="40711082435231673">"Dot"</string>
-    <string name="spoken_description_language_switch" msgid="5507091328222331316">"Switch language"</string>
-    <string name="spoken_description_action_next" msgid="8636078276664150324">"Next"</string>
-    <string name="spoken_description_action_previous" msgid="800872415009336208">"Previous"</string>
-    <string name="spoken_description_shiftmode_on" msgid="5700440798609574589">"Shift enabled"</string>
-    <string name="spoken_description_shiftmode_locked" msgid="593175803181701830">"Caps lock enabled"</string>
-    <string name="spoken_description_shiftmode_off" msgid="657219998449174808">"Shift disabled"</string>
-    <string name="spoken_description_mode_symbol" msgid="7183343879909747642">"Symbols mode"</string>
-    <string name="spoken_description_mode_alpha" msgid="3528307674390156956">"Letters mode"</string>
-    <string name="spoken_description_mode_phone" msgid="6520207943132026264">"Phone mode"</string>
-    <string name="spoken_description_mode_phone_shift" msgid="5499629753962641227">"Phone symbols mode"</string>
-    <string name="announce_keyboard_hidden" msgid="8718927835531429807">"Keyboard hidden"</string>
-    <string name="announce_keyboard_mode" msgid="4729081055438508321">"Showing <xliff:g id="MODE">%s</xliff:g> keyboard"</string>
-    <string name="keyboard_mode_date" msgid="3137520166817128102">"date"</string>
-    <string name="keyboard_mode_date_time" msgid="339593358488851072">"date and time"</string>
-    <string name="keyboard_mode_email" msgid="6216248078128294262">"email"</string>
-    <string name="keyboard_mode_im" msgid="1137405089766557048">"messaging"</string>
-    <string name="keyboard_mode_number" msgid="7991623440699957069">"number"</string>
-    <string name="keyboard_mode_phone" msgid="6851627527401433229">"phone"</string>
-    <string name="keyboard_mode_text" msgid="6479436687899701619">"text"</string>
-    <string name="keyboard_mode_time" msgid="4381856885582143277">"time"</string>
-    <string name="keyboard_mode_url" msgid="1519819835514911218">"URL"</string>
-    <string name="voice_input" msgid="3583258583521397548">"Voice input key"</string>
-    <string name="voice_input_modes_main_keyboard" msgid="3360660341121083174">"On main keyboard"</string>
-    <string name="voice_input_modes_symbols_keyboard" msgid="7203213240786084067">"On symbols keyboard"</string>
-    <string name="voice_input_modes_off" msgid="3745699748218082014">"Off"</string>
-    <string name="voice_input_modes_summary_main_keyboard" msgid="6586544292900314339">"Mic on main keyboard"</string>
-    <string name="voice_input_modes_summary_symbols_keyboard" msgid="5233725927281932391">"Mic on symbols keyboard"</string>
-    <string name="voice_input_modes_summary_off" msgid="63875609591897607">"Voice input is disabled"</string>
-    <string name="configure_input_method" msgid="373356270290742459">"Configure input methods"</string>
-    <string name="language_selection_title" msgid="1651299598555326750">"Input languages"</string>
-    <string name="send_feedback" msgid="1780431884109392046">"Send feedback"</string>
-    <string name="select_language" msgid="3693815588777926848">"Input languages"</string>
-    <string name="hint_add_to_dictionary" msgid="573678656946085380">"Touch again to save"</string>
-    <string name="has_dictionary" msgid="6071847973466625007">"Dictionary available"</string>
-    <string name="prefs_enable_log" msgid="6620424505072963557">"Enable user feedback"</string>
-    <string name="prefs_description_log" msgid="7525225584555429211">"Help improve this input method editor by automatically sending usage statistics and crash reports"</string>
-    <string name="keyboard_layout" msgid="8451164783510487501">"Keyboard theme"</string>
-    <string name="subtype_en_GB" msgid="88170601942311355">"English (UK)"</string>
-    <string name="subtype_en_US" msgid="6160452336634534239">"English (US)"</string>
-    <string name="subtype_es_US" msgid="5583145191430180200">"Spanish (US)"</string>
-    <string name="subtype_with_layout_en_GB" msgid="2179097748724725906">"English (UK) (<xliff:g id="LAYOUT">%s</xliff:g>)"</string>
-    <string name="subtype_with_layout_en_US" msgid="1362581347576714579">"English (US) (<xliff:g id="LAYOUT">%s</xliff:g>)"</string>
-    <string name="subtype_with_layout_es_US" msgid="6261791057007890189">"Spanish (US) (<xliff:g id="LAYOUT">%s</xliff:g>)"</string>
-    <string name="subtype_no_language" msgid="7137390094240139495">"No language (Alphabet)"</string>
-    <string name="subtype_no_language_qwerty" msgid="244337630616742604">"Alphabet (QWERTY)"</string>
-    <string name="subtype_no_language_qwertz" msgid="443066912507547976">"Alphabet (QWERTZ)"</string>
-    <string name="subtype_no_language_azerty" msgid="8144348527575640087">"Alphabet (AZERTY)"</string>
-    <string name="subtype_no_language_dvorak" msgid="1564494667584718094">"Alphabet (Dvorak)"</string>
-    <string name="subtype_no_language_colemak" msgid="5837418400010302623">"Alphabet (Colemak)"</string>
-    <string name="subtype_no_language_pcqwerty" msgid="5354918232046200018">"Alphabet (PC)"</string>
-    <string name="custom_input_styles_title" msgid="8429952441821251512">"Customised input styles"</string>
-    <string name="add_style" msgid="6163126614514489951">"Add style"</string>
-    <string name="add" msgid="8299699805688017798">"Add"</string>
-    <string name="remove" msgid="4486081658752944606">"Remove"</string>
-    <string name="save" msgid="7646738597196767214">"Save"</string>
-    <string name="subtype_locale" msgid="8576443440738143764">"Language"</string>
-    <string name="keyboard_layout_set" msgid="4309233698194565609">"Layout"</string>
-    <string name="custom_input_style_note_message" msgid="8826731320846363423">"Your customised input style needs to be enabled before you start using it. Do you want to enable it now?"</string>
-    <string name="enable" msgid="5031294444630523247">"Enable"</string>
-    <string name="not_now" msgid="6172462888202790482">"Not now"</string>
-    <string name="custom_input_style_already_exists" msgid="8008728952215449707">"The same input style already exists: <xliff:g id="INPUT_STYLE_NAME">%s</xliff:g>"</string>
-    <string name="prefs_usability_study_mode" msgid="1261130555134595254">"Usability study mode"</string>
-    <string name="prefs_key_longpress_timeout_settings" msgid="6102240298932897873">"Key long press delay"</string>
-    <string name="prefs_keypress_vibration_duration_settings" msgid="7918341459947439226">"Keypress vibration duration"</string>
-    <string name="prefs_keypress_sound_volume_settings" msgid="6027007337036891623">"Keypress sound volume"</string>
-    <string name="prefs_read_external_dictionary" msgid="2588931418575013067">"Read external dictionary file"</string>
-    <string name="read_external_dictionary_no_files_message" msgid="4947420942224623792">"No dictionary files in the Downloads folder"</string>
-    <string name="read_external_dictionary_multiple_files_title" msgid="7637749044265808628">"Select a dictionary file to install"</string>
-    <string name="read_external_dictionary_confirm_install_message" msgid="6898610163768980870">"Really install this file for <xliff:g id="LOCALE_NAME">%s</xliff:g>?"</string>
-    <string name="error" msgid="8940763624668513648">"There was an error"</string>
-    <string name="button_default" msgid="3988017840431881491">"Default"</string>
-    <string name="setup_welcome_title" msgid="6112821709832031715">"Welcome to <xliff:g id="APPLICATION_NAME">%s</xliff:g>"</string>
-    <string name="setup_welcome_additional_description" msgid="8150252008545768953">"with Gesture Typing"</string>
-    <string name="setup_start_action" msgid="8936036460897347708">"Get started"</string>
-    <string name="setup_next_action" msgid="371821437915144603">"Next step"</string>
-    <string name="setup_steps_title" msgid="6400373034871816182">"Setting up <xliff:g id="APPLICATION_NAME">%s</xliff:g>"</string>
-    <string name="setup_step1_title" msgid="3147967630253462315">"Enable <xliff:g id="APPLICATION_NAME">%s</xliff:g>"</string>
-    <string name="setup_step1_instruction" msgid="2578631936624637241">"Please tick \"<xliff:g id="APPLICATION_NAME">%s</xliff:g>\" in your Language &amp; input settings. This will authorise it to run on your device."</string>
-    <string name="setup_step1_finished_instruction" msgid="10761482004957994">"<xliff:g id="APPLICATION_NAME">%s</xliff:g> is already enabled in your Language &amp; input settings, so this step is done. On to the next one!"</string>
-    <string name="setup_step1_action" msgid="4366513534999901728">"Enable in Settings"</string>
-    <string name="setup_step2_title" msgid="6860725447906690594">"Switch to <xliff:g id="APPLICATION_NAME">%s</xliff:g>"</string>
-    <string name="setup_step2_instruction" msgid="9141481964870023336">"Next, select \"<xliff:g id="APPLICATION_NAME">%s</xliff:g>\" as your active text-input method."</string>
-    <string name="setup_step2_action" msgid="1660330307159824337">"Switch input methods"</string>
-    <string name="setup_step3_title" msgid="3154757183631490281">"Congratulations, you\'re all set!"</string>
-    <string name="setup_step3_instruction" msgid="8025981829605426000">"Now you can type in all your favourite apps with <xliff:g id="APPLICATION_NAME">%s</xliff:g>."</string>
-    <string name="setup_step3_action" msgid="600879797256942259">"Configure additional languages"</string>
-    <string name="setup_finish_action" msgid="276559243409465389">"Finished"</string>
-    <string name="show_setup_wizard_icon" msgid="5008028590593710830">"Show app icon"</string>
-    <string name="show_setup_wizard_icon_summary" msgid="4119998322536880213">"Display application icon in the launcher"</string>
-    <string name="app_name" msgid="6320102637491234792">"Dictionary Provider"</string>
-    <string name="dictionary_provider_name" msgid="3027315045397363079">"Dictionary Provider"</string>
-    <string name="dictionary_service_name" msgid="6237472350693511448">"Dictionary Service"</string>
-    <string name="download_description" msgid="6014835283119198591">"Dictionary update information"</string>
-    <string name="dictionary_settings_title" msgid="8091417676045693313">"Add-on dictionaries"</string>
-    <string name="dictionary_install_over_metered_network_prompt" msgid="3587517870006332980">"Dictionary available"</string>
-    <string name="dictionary_settings_summary" msgid="5305694987799824349">"Settings for dictionaries"</string>
-    <string name="user_dictionaries" msgid="3582332055892252845">"User dictionaries"</string>
-    <string name="default_user_dict_pref_name" msgid="1625055720489280530">"User dictionary"</string>
-    <string name="dictionary_available" msgid="4728975345815214218">"Dictionary available"</string>
-    <string name="dictionary_downloading" msgid="2982650524622620983">"Currently downloading"</string>
-    <string name="dictionary_installed" msgid="8081558343559342962">"Installed"</string>
-    <string name="dictionary_disabled" msgid="8950383219564621762">"Installed, disabled"</string>
-    <string name="cannot_connect_to_dict_service" msgid="9216933695765732398">"Problem connecting to dictionary service"</string>
-    <string name="no_dictionaries_available" msgid="8039920716566132611">"No dictionaries available"</string>
-    <string name="check_for_updates_now" msgid="8087688440916388581">"Refresh"</string>
-    <string name="last_update" msgid="730467549913588780">"Last updated"</string>
-    <string name="message_updating" msgid="4457761393932375219">"Checking for updates"</string>
-    <string name="message_loading" msgid="8689096636874758814">"Loading..."</string>
-    <string name="main_dict_description" msgid="3072821352793492143">"Main dictionary"</string>
-    <string name="cancel" msgid="6830980399865683324">"Cancel"</string>
-    <string name="install_dict" msgid="180852772562189365">"Install"</string>
-    <string name="cancel_download_dict" msgid="7843340278507019303">"Cancel"</string>
-    <string name="delete_dict" msgid="756853268088330054">"Delete"</string>
-    <string name="should_download_over_metered_prompt" msgid="2878629598667658845">"The selected language on your mobile device has an available dictionary.&lt;br/&gt; We recommend &lt;b&gt;downloading&lt;/b&gt; the <xliff:g id="LANGUAGE">%1$s</xliff:g> dictionary to improve your typing experience.&lt;br/&gt; &lt;br/&gt; The download could take a minute or two over 3G. Charges may apply if you don\'t have an &lt;b&gt;unlimited data plan&lt;/b&gt;.&lt;br/&gt; If you are not sure which data plan you have, we recommend finding a Wi-Fi connection to start the download automatically.&lt;br/&gt; &lt;br/&gt; Tip: You can download and remove dictionaries by going to &lt;b&gt;Language &amp; input&lt;/b&gt; in the &lt;b&gt;Settings&lt;/b&gt; menu of your mobile device."</string>
-    <string name="download_over_metered" msgid="1643065851159409546">"Download now (<xliff:g id="SIZE_IN_MEGABYTES">%1$.1f</xliff:g>MB)"</string>
-    <string name="do_not_download_over_metered" msgid="2176209579313941583">"Download over Wi-Fi"</string>
-    <string name="dict_available_notification_title" msgid="6514288591959117288">"A dictionary is available for <xliff:g id="LANGUAGE">%1$s</xliff:g>"</string>
-    <string name="dict_available_notification_description" msgid="1075194169443163487">"Press to review and download"</string>
-    <string name="toast_downloading_suggestions" msgid="1313027353588566660">"Downloading: suggestions for <xliff:g id="LANGUAGE">%1$s</xliff:g> will be ready soon."</string>
-    <string name="version_text" msgid="2715354215568469385">"Version <xliff:g id="VERSION_NUMBER">%1$s</xliff:g>"</string>
-    <string name="user_dict_settings_add_menu_title" msgid="1254195365689387076">"Add"</string>
-    <string name="user_dict_settings_add_dialog_title" msgid="4096700390211748168">"Add to dictionary"</string>
-    <string name="user_dict_settings_add_screen_title" msgid="5818914331629278758">"Phrase"</string>
-    <string name="user_dict_settings_add_dialog_more_options" msgid="5671682004887093112">"More options"</string>
-    <string name="user_dict_settings_add_dialog_less_options" msgid="2716586567241724126">"Fewer options"</string>
-    <string name="user_dict_settings_add_dialog_confirm" msgid="4703129507388332950">"OK"</string>
-    <string name="user_dict_settings_add_word_option_name" msgid="6665558053408962865">"Word:"</string>
-    <string name="user_dict_settings_add_shortcut_option_name" msgid="3094731590655523777">"Shortcut:"</string>
-    <string name="user_dict_settings_add_locale_option_name" msgid="4738643440987277705">"Language:"</string>
-    <string name="user_dict_settings_add_word_hint" msgid="4902434148985906707">"Type a word"</string>
-    <string name="user_dict_settings_add_shortcut_hint" msgid="2265453012555060178">"Optional shortcut"</string>
-    <string name="user_dict_settings_edit_dialog_title" msgid="3765774633869590352">"Edit word"</string>
-    <string name="user_dict_settings_context_menu_edit_title" msgid="6812255903472456302">"Edit"</string>
-    <string name="user_dict_settings_context_menu_delete_title" msgid="8142932447689461181">"Delete"</string>
-    <string name="user_dict_settings_empty_text" msgid="558499587532668203">"You don\'t have any words in the user dictionary. Add a word by touching the Add (+) button."</string>
-    <string name="user_dict_settings_all_languages" msgid="8276126583216298886">"For all languages"</string>
-    <string name="user_dict_settings_more_languages" msgid="7131268499685180461">"More languages…"</string>
-    <string name="user_dict_settings_delete" msgid="110413335187193859">"Delete"</string>
-    <string name="user_dict_fast_scroll_alphabet" msgid="5431919401558285473">" ABCDEFGHIJKLMNOPQRSTUVWXYZ"</string>
-</resources>
diff --git a/java/res/values-es-rUS/strings.xml b/java/res/values-es-rUS/strings.xml
index 52abe82..b1a4214 100644
--- a/java/res/values-es-rUS/strings.xml
+++ b/java/res/values-es-rUS/strings.xml
@@ -143,6 +143,7 @@
     <string name="subtype_with_layout_en_GB" msgid="2179097748724725906">"Inglés (Reino Unido) (<xliff:g id="LAYOUT">%s</xliff:g>)"</string>
     <string name="subtype_with_layout_en_US" msgid="1362581347576714579">"Inglés (EE.UU.) (<xliff:g id="LAYOUT">%s</xliff:g>)"</string>
     <string name="subtype_with_layout_es_US" msgid="6261791057007890189">"Español (EE.UU.) (<xliff:g id="LAYOUT">%s</xliff:g>)"</string>
+    <string name="subtype_nepali_traditional" msgid="9032247506728040447">"<xliff:g id="LANGUAGE">%s</xliff:g> (tradicional)"</string>
     <string name="subtype_no_language" msgid="7137390094240139495">"Ningún idioma (alfabeto)"</string>
     <string name="subtype_no_language_qwerty" msgid="244337630616742604">"Alfabeto (QWERTY)"</string>
     <string name="subtype_no_language_qwertz" msgid="443066912507547976">"Alfabeto (QWERTZ)"</string>
@@ -150,6 +151,7 @@
     <string name="subtype_no_language_dvorak" msgid="1564494667584718094">"Alfabeto (Dvorak)"</string>
     <string name="subtype_no_language_colemak" msgid="5837418400010302623">"Alfabeto (Colemak)"</string>
     <string name="subtype_no_language_pcqwerty" msgid="5354918232046200018">"Alfabeto (PC)"</string>
+    <string name="subtype_emoji" msgid="7483586578074549196">"Emoji"</string>
     <string name="custom_input_styles_title" msgid="8429952441821251512">"Estilos de entrada personalizados"</string>
     <string name="add_style" msgid="6163126614514489951">"Agr. estilo"</string>
     <string name="add" msgid="8299699805688017798">"Agregar"</string>
diff --git a/java/res/values-es/strings.xml b/java/res/values-es/strings.xml
index c25058c..9ff9430 100644
--- a/java/res/values-es/strings.xml
+++ b/java/res/values-es/strings.xml
@@ -143,6 +143,7 @@
     <string name="subtype_with_layout_en_GB" msgid="2179097748724725906">"Inglés (Reino Unido) (<xliff:g id="LAYOUT">%s</xliff:g>)"</string>
     <string name="subtype_with_layout_en_US" msgid="1362581347576714579">"Inglés (EE.UU.) (<xliff:g id="LAYOUT">%s</xliff:g>)"</string>
     <string name="subtype_with_layout_es_US" msgid="6261791057007890189">"Español (EE.UU.) (<xliff:g id="LAYOUT">%s</xliff:g>)"</string>
+    <string name="subtype_nepali_traditional" msgid="9032247506728040447">"<xliff:g id="LANGUAGE">%s</xliff:g> (tradicional)"</string>
     <string name="subtype_no_language" msgid="7137390094240139495">"Ningún idioma (alfabeto)"</string>
     <string name="subtype_no_language_qwerty" msgid="244337630616742604">"Alfabeto (QWERTY)"</string>
     <string name="subtype_no_language_qwertz" msgid="443066912507547976">"Alfabeto (QWERTZ)"</string>
@@ -150,6 +151,7 @@
     <string name="subtype_no_language_dvorak" msgid="1564494667584718094">"Alfabeto (Dvorak)"</string>
     <string name="subtype_no_language_colemak" msgid="5837418400010302623">"Alfabeto (Colemak)"</string>
     <string name="subtype_no_language_pcqwerty" msgid="5354918232046200018">"Alfabeto (PC)"</string>
+    <string name="subtype_emoji" msgid="7483586578074549196">"Emoji"</string>
     <string name="custom_input_styles_title" msgid="8429952441821251512">"Estilos de entrada personalizados"</string>
     <string name="add_style" msgid="6163126614514489951">"Añadir estilo"</string>
     <string name="add" msgid="8299699805688017798">"Añadir"</string>
diff --git a/java/res/values-et-rEE/strings-appname.xml b/java/res/values-et-rEE/strings-appname.xml
deleted file mode 100644
index bbc13d2..0000000
--- a/java/res/values-et-rEE/strings-appname.xml
+++ /dev/null
@@ -1,27 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!-- 
-/*
-**
-** Copyright 2013, 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:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="english_ime_name" msgid="5940510615957428904">"Androidi klaviatuur (AOSP)"</string>
-    <string name="spell_checker_service_name" msgid="1254221805440242662">"Androidi õigekirjakontroll (AOSP)"</string>
-    <string name="english_ime_settings" msgid="5760361067176802794">"Androidi klaviatuuri seaded (AOSP)"</string>
-    <string name="android_spell_checker_settings" msgid="6123949487832861885">"Androidi õigekirjakontrolli seaded (AOSP)"</string>
-</resources>
diff --git a/java/res/values-et-rEE/strings.xml b/java/res/values-et-rEE/strings.xml
index 534983f..62197d7 100644
--- a/java/res/values-et-rEE/strings.xml
+++ b/java/res/values-et-rEE/strings.xml
@@ -143,6 +143,7 @@
     <string name="subtype_with_layout_en_GB" msgid="2179097748724725906">"Inglise (UK) (<xliff:g id="LAYOUT">%s</xliff:g>)"</string>
     <string name="subtype_with_layout_en_US" msgid="1362581347576714579">"Inglise (USA) (<xliff:g id="LAYOUT">%s</xliff:g>)"</string>
     <string name="subtype_with_layout_es_US" msgid="6261791057007890189">"hispaania (USA) (<xliff:g id="LAYOUT">%s</xliff:g>)"</string>
+    <string name="subtype_nepali_traditional" msgid="9032247506728040447">"<xliff:g id="LANGUAGE">%s</xliff:g> (traditsiooniline)"</string>
     <string name="subtype_no_language" msgid="7137390094240139495">"Keel puudub (tähestik)"</string>
     <string name="subtype_no_language_qwerty" msgid="244337630616742604">"Tähestik (QWERTY)"</string>
     <string name="subtype_no_language_qwertz" msgid="443066912507547976">"Tähestik (QWERTZ)"</string>
@@ -150,6 +151,7 @@
     <string name="subtype_no_language_dvorak" msgid="1564494667584718094">"Tähestik (Dvorak)"</string>
     <string name="subtype_no_language_colemak" msgid="5837418400010302623">"Tähestik (Colemak)"</string>
     <string name="subtype_no_language_pcqwerty" msgid="5354918232046200018">"Tähestik (PC)"</string>
+    <string name="subtype_emoji" msgid="7483586578074549196">"Emotikon"</string>
     <string name="custom_input_styles_title" msgid="8429952441821251512">"Kohandage sisendlaadid"</string>
     <string name="add_style" msgid="6163126614514489951">"Lisage laad"</string>
     <string name="add" msgid="8299699805688017798">"Lisa"</string>
diff --git a/java/res/values-fa/strings.xml b/java/res/values-fa/strings.xml
index a2dac7b..5d89516 100644
--- a/java/res/values-fa/strings.xml
+++ b/java/res/values-fa/strings.xml
@@ -147,6 +147,7 @@
     <string name="subtype_with_layout_en_GB" msgid="2179097748724725906">"انگلیسی (انگلستان) (<xliff:g id="LAYOUT">%s</xliff:g>)"</string>
     <string name="subtype_with_layout_en_US" msgid="1362581347576714579">"انگلیسی (ایالات متحده) (<xliff:g id="LAYOUT">%s</xliff:g>)"</string>
     <string name="subtype_with_layout_es_US" msgid="6261791057007890189">"اسپانیایی (آمریکا) (<xliff:g id="LAYOUT">%s</xliff:g>)"</string>
+    <string name="subtype_nepali_traditional" msgid="9032247506728040447">"<xliff:g id="LANGUAGE">%s</xliff:g> (سنتی)"</string>
     <string name="subtype_no_language" msgid="7137390094240139495">"بدون زبان (حروف الفبا)"</string>
     <string name="subtype_no_language_qwerty" msgid="244337630616742604">"حروف الفبا (QWERTY)"</string>
     <string name="subtype_no_language_qwertz" msgid="443066912507547976">"حروف الفبا (QWERTZ)"</string>
@@ -154,6 +155,7 @@
     <string name="subtype_no_language_dvorak" msgid="1564494667584718094">"حروف الفبا (Dvorak)"</string>
     <string name="subtype_no_language_colemak" msgid="5837418400010302623">"حروف الفبا (Colemak)"</string>
     <string name="subtype_no_language_pcqwerty" msgid="5354918232046200018">"حروف الفبا (PC)"</string>
+    <string name="subtype_emoji" msgid="7483586578074549196">"Emoji"</string>
     <string name="custom_input_styles_title" msgid="8429952441821251512">"سبک‌های ورودی سفارشی"</string>
     <string name="add_style" msgid="6163126614514489951">"افزودن سبک"</string>
     <string name="add" msgid="8299699805688017798">"افزودن"</string>
diff --git a/java/res/values-fi/strings.xml b/java/res/values-fi/strings.xml
index 74e1232..4cdf621 100644
--- a/java/res/values-fi/strings.xml
+++ b/java/res/values-fi/strings.xml
@@ -143,6 +143,7 @@
     <string name="subtype_with_layout_en_GB" msgid="2179097748724725906">"englanti (Iso-Br.) (<xliff:g id="LAYOUT">%s</xliff:g>)"</string>
     <string name="subtype_with_layout_en_US" msgid="1362581347576714579">"englanti (USA) (<xliff:g id="LAYOUT">%s</xliff:g>)"</string>
     <string name="subtype_with_layout_es_US" msgid="6261791057007890189">"espanja (Yhdysvallat) (<xliff:g id="LAYOUT">%s</xliff:g>)"</string>
+    <string name="subtype_nepali_traditional" msgid="9032247506728040447">"<xliff:g id="LANGUAGE">%s</xliff:g> (perinteinen)"</string>
     <string name="subtype_no_language" msgid="7137390094240139495">"Ei kieltä (aakkoset)"</string>
     <string name="subtype_no_language_qwerty" msgid="244337630616742604">"Aakkoset (QWERTY)"</string>
     <string name="subtype_no_language_qwertz" msgid="443066912507547976">"Aakkoset (QWERTZ)"</string>
@@ -150,6 +151,7 @@
     <string name="subtype_no_language_dvorak" msgid="1564494667584718094">"Aakkoset (Dvorak)"</string>
     <string name="subtype_no_language_colemak" msgid="5837418400010302623">"Aakkoset (Colemak)"</string>
     <string name="subtype_no_language_pcqwerty" msgid="5354918232046200018">"Aakkoset (PC)"</string>
+    <string name="subtype_emoji" msgid="7483586578074549196">"Emoji"</string>
     <string name="custom_input_styles_title" msgid="8429952441821251512">"Muokatut syöttötyylit"</string>
     <string name="add_style" msgid="6163126614514489951">"Lisää tyyli"</string>
     <string name="add" msgid="8299699805688017798">"Lisää"</string>
diff --git a/java/res/values-fr-rCA/donottranslate.xml b/java/res/values-fr-rCA/donottranslate.xml
new file mode 100644
index 0000000..21f18d8
--- /dev/null
+++ b/java/res/values-fr-rCA/donottranslate.xml
@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+**
+** Copyright 2009, 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">
+    <!-- Symbols that are normally preceded by a space (used to add an auto-space before these) -->
+    <!-- This is similar to French with the exception of "!" "?" and ";" which do not take a space before in Canadian French. Note that ":" does take a space before according to Canadian rules. -->
+    <string name="symbols_preceded_by_space">([{&amp;:</string>
+    <!-- Symbols that are normally followed by a space (used to add an auto-space after these) -->
+    <string name="symbols_followed_by_space">.,;:!?)]}&amp;</string>
+    <!-- Symbols that separate words -->
+    <!-- Don't remove the enclosing double quotes, they protect whitespace (not just U+0020) -->
+    <string name="symbols_word_separators">"&#x0009;&#x0020;\n"()[]{}*&amp;&lt;&gt;+=|.,;:!?/_\"</string>
+    <!-- Word connectors -->
+    <string name="symbols_word_connectors">\'-</string>
+</resources>
diff --git a/java/res/values-fr-rCA/strings-appname.xml b/java/res/values-fr-rCA/strings-appname.xml
deleted file mode 100644
index d45e239..0000000
--- a/java/res/values-fr-rCA/strings-appname.xml
+++ /dev/null
@@ -1,27 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!-- 
-/*
-**
-** Copyright 2013, 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:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="english_ime_name" msgid="5940510615957428904">"Clavier Android (AOSP)"</string>
-    <string name="spell_checker_service_name" msgid="1254221805440242662">"Correcteur orthographique Android (AOSP)"</string>
-    <string name="english_ime_settings" msgid="5760361067176802794">"Paramètres du clavier Android (AOSP)"</string>
-    <string name="android_spell_checker_settings" msgid="6123949487832861885">"Paramètres du correcteur orthographique Android (AOSP)"</string>
-</resources>
diff --git a/java/res/values-fr-rCA/strings.xml b/java/res/values-fr-rCA/strings.xml
index f8c9fff..b56463e 100644
--- a/java/res/values-fr-rCA/strings.xml
+++ b/java/res/values-fr-rCA/strings.xml
@@ -1,242 +1,19 @@
 <?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.
-*/
- -->
+<!-- Copyright (C) 2009 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:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="english_ime_input_options" msgid="3909945612939668554">"Options de saisie"</string>
-    <string name="english_ime_research_log" msgid="8492602295696577851">"Commandes journaux rech."</string>
-    <string name="use_contacts_for_spellchecking_option_title" msgid="5374120998125353898">"Rechercher noms contacts"</string>
-    <string name="use_contacts_for_spellchecking_option_summary" msgid="8754413382543307713">"Correcteur orthographique utilise entrées de liste de contacts."</string>
-    <string name="vibrate_on_keypress" msgid="5258079494276955460">"Vibrer à chaque touche"</string>
-    <string name="sound_on_keypress" msgid="6093592297198243644">"Son à chaque touche"</string>
-    <string name="popup_on_keypress" msgid="123894815723512944">"Agrandir les caractères"</string>
-    <string name="general_category" msgid="1859088467017573195">"Général"</string>
-    <string name="correction_category" msgid="2236750915056607613">"Correction du texte"</string>
-    <string name="gesture_typing_category" msgid="497263612130532630">"Saisie gestuelle"</string>
-    <string name="misc_category" msgid="6894192814868233453">"Autres options"</string>
-    <string name="advanced_settings" msgid="362895144495591463">"Paramètres avancés"</string>
-    <string name="advanced_settings_summary" msgid="4487980456152830271">"Options destinées aux experts"</string>
-    <string name="include_other_imes_in_language_switch_list" msgid="4533689960308565519">"Autres modes de saisie"</string>
-    <string name="include_other_imes_in_language_switch_list_summary" msgid="840637129103317635">"La touche de sélection de langue couvre d\'autres modes de saisie"</string>
-    <string name="show_language_switch_key" msgid="5915478828318774384">"Touche de sélection de langue"</string>
-    <string name="show_language_switch_key_summary" msgid="7343403647474265713">"Afficher lorsque plusieurs langues de saisie sont activées"</string>
-    <string name="sliding_key_input_preview" msgid="6604262359510068370">"Aff. indicateur saisie gestuelle"</string>
-    <string name="sliding_key_input_preview_summary" msgid="6340524345729093886">"Aff. un repère visuel si l\'utilisateur appuie sur Maj ou Symboles"</string>
-    <string name="key_preview_popup_dismiss_delay" msgid="6213164897443068248">"Masquer touche agrandie"</string>
-    <string name="key_preview_popup_dismiss_no_delay" msgid="2096123151571458064">"Aucun délai"</string>
-    <string name="key_preview_popup_dismiss_default_delay" msgid="2166964333903906734">"Par défaut"</string>
-    <string name="abbreviation_unit_milliseconds" msgid="8700286094028323363">"<xliff:g id="MILLISECONDS">%s</xliff:g> ms"</string>
-    <string name="settings_system_default" msgid="6268225104743331821">"Paramètres par défaut"</string>
-    <string name="use_contacts_dict" msgid="4435317977804180815">"Proposer noms de contacts"</string>
-    <string name="use_contacts_dict_summary" msgid="6599983334507879959">"Utiliser des noms de contacts pour les suggestions et corrections"</string>
-    <string name="use_double_space_period" msgid="8781529969425082860">"Point et espace"</string>
-    <string name="use_double_space_period_summary" msgid="6532892187247952799">"Appuyez deux fois sur la barre d\'espace pour insérer un point et une espace"</string>
-    <string name="auto_cap" msgid="1719746674854628252">"Majuscules automatiques"</string>
-    <string name="auto_cap_summary" msgid="7934452761022946874">"Majuscule au premier mot de chaque phrase"</string>
-    <string name="edit_personal_dictionary" msgid="3996910038952940420">"Dictionnaire personnel"</string>
-    <string name="configure_dictionaries_title" msgid="4238652338556902049">"Dictionnaires complémentaires"</string>
-    <string name="main_dictionary" msgid="4798763781818361168">"Dictionnaire principal"</string>
-    <string name="prefs_show_suggestions" msgid="8026799663445531637">"Suggestions de correction"</string>
-    <string name="prefs_show_suggestions_summary" msgid="1583132279498502825">"Afficher les suggestions de terme lors de la saisie"</string>
-    <string name="prefs_suggestion_visibility_show_name" msgid="3219916594067551303">"Toujours afficher"</string>
-    <string name="prefs_suggestion_visibility_show_only_portrait_name" msgid="3859783767435239118">"Afficher en mode Portrait"</string>
-    <string name="prefs_suggestion_visibility_hide_name" msgid="6309143926422234673">"Toujours masquer"</string>
-    <string name="prefs_block_potentially_offensive_title" msgid="5078480071057408934">"Bloquer les termes choquants"</string>
-    <string name="prefs_block_potentially_offensive_summary" msgid="2371835479734991364">"Pas de termes potentiellement choquants"</string>
-    <string name="auto_correction" msgid="7630720885194996950">"Correction auto"</string>
-    <string name="auto_correction_summary" msgid="5625751551134658006">"Corriger autom. orthographe (pression sur barre espace/signes ponctuation)"</string>
-    <string name="auto_correction_threshold_mode_off" msgid="8470882665417944026">"Désactiver"</string>
-    <string name="auto_correction_threshold_mode_modest" msgid="8788366690620799097">"Simple"</string>
-    <string name="auto_correction_threshold_mode_aggressive" msgid="7319007299148899623">"Proactive"</string>
-    <string name="auto_correction_threshold_mode_very_aggressive" msgid="1853309024129480416">"Très exigeante"</string>
-    <string name="bigram_prediction" msgid="1084449187723948550">"Suggestions pour le mot suivant"</string>
-    <string name="bigram_prediction_summary" msgid="3896362682751109677">"Utiliser le mot précédent pour les suggestions"</string>
-    <string name="gesture_input" msgid="826951152254563827">"Activer la saisie gestuelle"</string>
-    <string name="gesture_input_summary" msgid="9180350639305731231">"Saisir un mot en faisant glisser le doigt sur les lettres"</string>
-    <string name="gesture_preview_trail" msgid="3802333369335722221">"Afficher le tracé du geste"</string>
-    <string name="gesture_floating_preview_text" msgid="4443240334739381053">"Aperçu flottant dynamique"</string>
-    <string name="gesture_floating_preview_text_summary" msgid="4472696213996203533">"Afficher le mot suggéré lors des gestes"</string>
-    <string name="added_word" msgid="8993883354622484372">"<xliff:g id="WORD">%s</xliff:g> : enregistré"</string>
-    <string name="label_go_key" msgid="1635148082137219148">"Aller"</string>
-    <string name="label_next_key" msgid="362972844525672568">"Suivant"</string>
-    <string name="label_previous_key" msgid="1211868118071386787">"Préc."</string>
-    <string name="label_done_key" msgid="2441578748772529288">"Terminé"</string>
-    <string name="label_send_key" msgid="2815056534433717444">"Envoyer"</string>
-    <string name="label_pause_key" msgid="181098308428035340">"Suspendre"</string>
-    <string name="label_wait_key" msgid="6402152600878093134">"Attendre"</string>
-    <string name="spoken_use_headphones" msgid="896961781287283493">"Branchez des écouteurs pour entendre l\'énoncé à haute voix des touches lors de la saisie du mot de passe."</string>
-    <string name="spoken_current_text_is" msgid="2485723011272583845">"Le texte actuel est %s"</string>
-    <string name="spoken_no_text_entered" msgid="7479685225597344496">"Aucun texte saisi"</string>
-    <string name="spoken_description_unknown" msgid="3197434010402179157">"Code touche %d"</string>
-    <string name="spoken_description_shift" msgid="244197883292549308">"Maj"</string>
-    <string name="spoken_description_shift_shifted" msgid="1681877323344195035">"Touche Maj activée (appuyer pour désactiver)"</string>
-    <string name="spoken_description_caps_lock" msgid="3276478269526304432">"Verrouillage des majuscules activé (appuyer pour désactiver)"</string>
-    <string name="spoken_description_delete" msgid="8740376944276199801">"Supprimer"</string>
-    <string name="spoken_description_to_symbol" msgid="5486340107500448969">"Symboles"</string>
-    <string name="spoken_description_to_alpha" msgid="23129338819771807">"Lettres"</string>
-    <string name="spoken_description_to_numeric" msgid="591752092685161732">"Nombres"</string>
-    <string name="spoken_description_settings" msgid="4627462689603838099">"Paramètres"</string>
-    <string name="spoken_description_tab" msgid="2667716002663482248">"Onglet"</string>
-    <string name="spoken_description_space" msgid="2582521050049860859">"Espace"</string>
-    <string name="spoken_description_mic" msgid="615536748882611950">"Saisie vocale"</string>
-    <string name="spoken_description_smiley" msgid="2256309826200113918">"Émoticône"</string>
-    <string name="spoken_description_return" msgid="8178083177238315647">"Renvoyer"</string>
-    <string name="spoken_description_search" msgid="1247236163755920808">"Rechercher"</string>
-    <string name="spoken_description_dot" msgid="40711082435231673">"Point"</string>
-    <string name="spoken_description_language_switch" msgid="5507091328222331316">"Changer de langue"</string>
-    <string name="spoken_description_action_next" msgid="8636078276664150324">"Suivant"</string>
-    <string name="spoken_description_action_previous" msgid="800872415009336208">"Précédent"</string>
-    <string name="spoken_description_shiftmode_on" msgid="5700440798609574589">"Touche Maj activée"</string>
-    <string name="spoken_description_shiftmode_locked" msgid="593175803181701830">"Verrouillage des majuscules activé"</string>
-    <string name="spoken_description_shiftmode_off" msgid="657219998449174808">"Touche Maj désactivée"</string>
-    <string name="spoken_description_mode_symbol" msgid="7183343879909747642">"Mode Symboles"</string>
-    <string name="spoken_description_mode_alpha" msgid="3528307674390156956">"Mode Lettres"</string>
-    <string name="spoken_description_mode_phone" msgid="6520207943132026264">"Mode Téléphone"</string>
-    <string name="spoken_description_mode_phone_shift" msgid="5499629753962641227">"Mode Symboles du téléphone"</string>
-    <string name="announce_keyboard_hidden" msgid="8718927835531429807">"Clavier masqué"</string>
-    <string name="announce_keyboard_mode" msgid="4729081055438508321">"Affichage du clavier <xliff:g id="MODE">%s</xliff:g>"</string>
-    <string name="keyboard_mode_date" msgid="3137520166817128102">"Date"</string>
-    <string name="keyboard_mode_date_time" msgid="339593358488851072">"Date et heure"</string>
-    <string name="keyboard_mode_email" msgid="6216248078128294262">"Courriel"</string>
-    <string name="keyboard_mode_im" msgid="1137405089766557048">"SMS/MMS"</string>
-    <string name="keyboard_mode_number" msgid="7991623440699957069">"Nombre"</string>
-    <string name="keyboard_mode_phone" msgid="6851627527401433229">"Numéro de téléphone"</string>
-    <string name="keyboard_mode_text" msgid="6479436687899701619">"Texte"</string>
-    <string name="keyboard_mode_time" msgid="4381856885582143277">"Heure"</string>
-    <string name="keyboard_mode_url" msgid="1519819835514911218">"URL"</string>
-    <string name="voice_input" msgid="3583258583521397548">"Touche de saisie vocale"</string>
-    <string name="voice_input_modes_main_keyboard" msgid="3360660341121083174">"Sur le clavier principal"</string>
-    <string name="voice_input_modes_symbols_keyboard" msgid="7203213240786084067">"Sur clavier symboles"</string>
-    <string name="voice_input_modes_off" msgid="3745699748218082014">"Désactiver"</string>
-    <string name="voice_input_modes_summary_main_keyboard" msgid="6586544292900314339">"Micro sur le clavier principal"</string>
-    <string name="voice_input_modes_summary_symbols_keyboard" msgid="5233725927281932391">"Micro sur le clavier des symboles"</string>
-    <string name="voice_input_modes_summary_off" msgid="63875609591897607">"Saisie vocale désactivée"</string>
-    <string name="configure_input_method" msgid="373356270290742459">"Configurer les modes de saisie"</string>
-    <string name="language_selection_title" msgid="1651299598555326750">"Langues de saisie"</string>
-    <string name="send_feedback" msgid="1780431884109392046">"Envoyer des commentaires"</string>
-    <string name="select_language" msgid="3693815588777926848">"Langues de saisie"</string>
-    <string name="hint_add_to_dictionary" msgid="573678656946085380">"Appuyer de nouveau pour enregistrer"</string>
-    <string name="has_dictionary" msgid="6071847973466625007">"Dictionnaire disponible"</string>
-    <string name="prefs_enable_log" msgid="6620424505072963557">"Autoriser les commentaires des utilisateurs"</string>
-    <string name="prefs_description_log" msgid="7525225584555429211">"Contribuer à l\'amélioration de cet éditeur du mode de saisie grâce à l\'envoi automatique de statistiques d\'utilisation et de rapports d\'erreur"</string>
-    <string name="keyboard_layout" msgid="8451164783510487501">"Thème du clavier"</string>
-    <string name="subtype_en_GB" msgid="88170601942311355">"Anglais (britannique)"</string>
-    <string name="subtype_en_US" msgid="6160452336634534239">"Anglais (États-Unis)"</string>
-    <string name="subtype_es_US" msgid="5583145191430180200">"Espagnol (États-Unis)"</string>
-    <string name="subtype_with_layout_en_GB" msgid="2179097748724725906">"Anglais (Royaume-Uni) (<xliff:g id="LAYOUT">%s</xliff:g>)"</string>
-    <string name="subtype_with_layout_en_US" msgid="1362581347576714579">"Anglais (États-Unis) (<xliff:g id="LAYOUT">%s</xliff:g>)"</string>
-    <string name="subtype_with_layout_es_US" msgid="6261791057007890189">"Espagnol, États-Unis (<xliff:g id="LAYOUT">%s</xliff:g>)"</string>
-    <string name="subtype_no_language" msgid="7137390094240139495">"Aucune langue (alphabet)"</string>
-    <string name="subtype_no_language_qwerty" msgid="244337630616742604">"Alphabet latin (QWERTY)"</string>
-    <string name="subtype_no_language_qwertz" msgid="443066912507547976">"Alphabet latin (QWERTZ)"</string>
-    <string name="subtype_no_language_azerty" msgid="8144348527575640087">"Alphabet latin (AZERTY)"</string>
-    <string name="subtype_no_language_dvorak" msgid="1564494667584718094">"Alphabet latin (Dvorak)"</string>
-    <string name="subtype_no_language_colemak" msgid="5837418400010302623">"Alphabet latin (Colemak)"</string>
-    <string name="subtype_no_language_pcqwerty" msgid="5354918232046200018">"Alphabet latin (PC)"</string>
-    <string name="custom_input_styles_title" msgid="8429952441821251512">"Styles saisie personnalisés"</string>
-    <string name="add_style" msgid="6163126614514489951">"Ajouter style"</string>
-    <string name="add" msgid="8299699805688017798">"Ajouter"</string>
-    <string name="remove" msgid="4486081658752944606">"Supprimer"</string>
-    <string name="save" msgid="7646738597196767214">"Enregistrer"</string>
-    <string name="subtype_locale" msgid="8576443440738143764">"Langue"</string>
-    <string name="keyboard_layout_set" msgid="4309233698194565609">"Disposition"</string>
-    <string name="custom_input_style_note_message" msgid="8826731320846363423">"Vous devez activer votre style de saisie personnalisé avant de l\'utiliser. Voulez-vous le faire maintenant ?"</string>
-    <string name="enable" msgid="5031294444630523247">"Activer"</string>
-    <string name="not_now" msgid="6172462888202790482">"Pas maintenant"</string>
-    <string name="custom_input_style_already_exists" msgid="8008728952215449707">"Le style de saisie suivant existe déjà : <xliff:g id="INPUT_STYLE_NAME">%s</xliff:g>."</string>
-    <string name="prefs_usability_study_mode" msgid="1261130555134595254">"Mode d\'étude de l\'utilisabilité"</string>
-    <string name="prefs_key_longpress_timeout_settings" msgid="6102240298932897873">"Délai appui prolongé sur touche"</string>
-    <string name="prefs_keypress_vibration_duration_settings" msgid="7918341459947439226">"Durée vibration press. touche"</string>
-    <string name="prefs_keypress_sound_volume_settings" msgid="6027007337036891623">"Volume pression de touche"</string>
-    <string name="prefs_read_external_dictionary" msgid="2588931418575013067">"Lire un fichier de dictionnaire externe"</string>
-    <string name="read_external_dictionary_no_files_message" msgid="4947420942224623792">"Aucun fichier de dictionnaire dans le dossier \"Téléchargements\""</string>
-    <string name="read_external_dictionary_multiple_files_title" msgid="7637749044265808628">"Sélectionner un fichier de dictionnaire à installer"</string>
-    <string name="read_external_dictionary_confirm_install_message" msgid="6898610163768980870">"Installer ce fichier pour la langue \"<xliff:g id="LOCALE_NAME">%s</xliff:g>\" ?"</string>
-    <string name="error" msgid="8940763624668513648">"Une erreur s\'est produite"</string>
-    <string name="button_default" msgid="3988017840431881491">"Par défaut"</string>
-    <string name="setup_welcome_title" msgid="6112821709832031715">"Bienvenue dans <xliff:g id="APPLICATION_NAME">%s</xliff:g>"</string>
-    <string name="setup_welcome_additional_description" msgid="8150252008545768953">"avec la saisie gestuelle"</string>
-    <string name="setup_start_action" msgid="8936036460897347708">"Commencer"</string>
-    <string name="setup_next_action" msgid="371821437915144603">"Étape suivante"</string>
-    <string name="setup_steps_title" msgid="6400373034871816182">"Configurer <xliff:g id="APPLICATION_NAME">%s</xliff:g>"</string>
-    <string name="setup_step1_title" msgid="3147967630253462315">"Activer <xliff:g id="APPLICATION_NAME">%s</xliff:g>"</string>
-    <string name="setup_step1_instruction" msgid="2578631936624637241">"Sous \"Langue et saisie\", cochez \"<xliff:g id="APPLICATION_NAME">%s</xliff:g>\" pour autoriser son exécution sur l\'appareil."</string>
-    <string name="setup_step1_finished_instruction" msgid="10761482004957994">"L\'application \"<xliff:g id="APPLICATION_NAME">%s</xliff:g>\" est déjà activée dans vos paramètres \"Langue et saisie\". Passez à l\'étape suivante."</string>
-    <string name="setup_step1_action" msgid="4366513534999901728">"Activer le clavier dans les paramètres"</string>
-    <string name="setup_step2_title" msgid="6860725447906690594">"Basculer vers <xliff:g id="APPLICATION_NAME">%s</xliff:g>"</string>
-    <string name="setup_step2_instruction" msgid="9141481964870023336">"Sélectionnez ensuite \"<xliff:g id="APPLICATION_NAME">%s</xliff:g>\" comme mode de saisie actif."</string>
-    <string name="setup_step2_action" msgid="1660330307159824337">"Changer de mode de saisie"</string>
-    <string name="setup_step3_title" msgid="3154757183631490281">"Félicitations, l\'opération est terminée"</string>
-    <string name="setup_step3_instruction" msgid="8025981829605426000">"Avec <xliff:g id="APPLICATION_NAME">%s</xliff:g>, vous pouvez saisir du texte dans toutes vos applications préférées."</string>
-    <string name="setup_step3_action" msgid="600879797256942259">"Configurer des langues supplémentaires"</string>
-    <string name="setup_finish_action" msgid="276559243409465389">"OK"</string>
-    <string name="show_setup_wizard_icon" msgid="5008028590593710830">"Afficher icône application"</string>
-    <string name="show_setup_wizard_icon_summary" msgid="4119998322536880213">"Afficher l\'icône de l\'application dans le lanceur"</string>
-    <string name="app_name" msgid="6320102637491234792">"Fournisseur de dictionnaires"</string>
-    <string name="dictionary_provider_name" msgid="3027315045397363079">"Fournisseur de dictionnaires"</string>
-    <string name="dictionary_service_name" msgid="6237472350693511448">"Service de dictionnaires"</string>
-    <string name="download_description" msgid="6014835283119198591">"Informations relatives à la mise à jour des dictionnaires"</string>
-    <string name="dictionary_settings_title" msgid="8091417676045693313">"Dictionnaires complémentaires"</string>
-    <string name="dictionary_install_over_metered_network_prompt" msgid="3587517870006332980">"Dictionnaire disponible"</string>
-    <string name="dictionary_settings_summary" msgid="5305694987799824349">"Paramètres des dictionnaires"</string>
-    <string name="user_dictionaries" msgid="3582332055892252845">"Dictionnaires personnels"</string>
-    <string name="default_user_dict_pref_name" msgid="1625055720489280530">"Dictionnaire personnel"</string>
-    <string name="dictionary_available" msgid="4728975345815214218">"Dictionnaire disponible"</string>
-    <string name="dictionary_downloading" msgid="2982650524622620983">"Téléchargement en cours…"</string>
-    <string name="dictionary_installed" msgid="8081558343559342962">"Installé"</string>
-    <string name="dictionary_disabled" msgid="8950383219564621762">"Installé, désactivé"</string>
-    <string name="cannot_connect_to_dict_service" msgid="9216933695765732398">"Pas de service dico."</string>
-    <string name="no_dictionaries_available" msgid="8039920716566132611">"Aucun dictionnaire"</string>
-    <string name="check_for_updates_now" msgid="8087688440916388581">"Actualiser"</string>
-    <string name="last_update" msgid="730467549913588780">"Dernière mise à jour"</string>
-    <string name="message_updating" msgid="4457761393932375219">"Recherche de mises à jour en cours…"</string>
-    <string name="message_loading" msgid="8689096636874758814">"Chargement en cours..."</string>
-    <string name="main_dict_description" msgid="3072821352793492143">"Dictionnaire principal"</string>
-    <string name="cancel" msgid="6830980399865683324">"Annuler"</string>
-    <string name="install_dict" msgid="180852772562189365">"Installer"</string>
-    <string name="cancel_download_dict" msgid="7843340278507019303">"Annuler"</string>
-    <string name="delete_dict" msgid="756853268088330054">"Supprimer"</string>
-    <string name="should_download_over_metered_prompt" msgid="2878629598667658845">"Un dictionnaire est disponible pour la langue sélectionnée sur votre appareil mobile.&lt;br/&gt; Nous vous invitons à &lt;b&gt;télécharger&lt;/b&gt; le dictionnaire <xliff:g id="LANGUAGE">%1$s</xliff:g> pour faciliter votre saisie.&lt;br/&gt; &lt;br/&gt; Le téléchargement peut prendre une à deux minutes via une connexion 3G. Des frais peuvent s\'appliquer si vous ne disposez pas d\'un &lt;b&gt;forfait Internet illimité&lt;/b&gt;.&lt;br/&gt; Si vous n\'êtes pas sûr de votre forfait, nous vous conseillons d\'utiliser une connexion Wi-Fi pour lancer automatiquement le téléchargement.&lt;br/&gt; &lt;br/&gt; Astuce : Vous pouvez télécharger et supprimer des dictionnaires dans la section &lt;b&gt;Langue et saisie&lt;/b&gt; du menu &lt;b&gt;Paramètres&lt;/b&gt; de votre appareil mobile."</string>
-    <string name="download_over_metered" msgid="1643065851159409546">"Télécharger (<xliff:g id="SIZE_IN_MEGABYTES">%1$.1f</xliff:g> Mo)"</string>
-    <string name="do_not_download_over_metered" msgid="2176209579313941583">"Télécharger via Wi-Fi"</string>
-    <string name="dict_available_notification_title" msgid="6514288591959117288">"Un dictionnaire est disponible en <xliff:g id="LANGUAGE">%1$s</xliff:g>"</string>
-    <string name="dict_available_notification_description" msgid="1075194169443163487">"Appuyez ici pour consulter et télécharger le dictionnaire."</string>
-    <string name="toast_downloading_suggestions" msgid="1313027353588566660">"En cours de téléchargement. Des suggestions pour la langue suivante seront bientôt disponibles : <xliff:g id="LANGUAGE">%1$s</xliff:g>."</string>
-    <string name="version_text" msgid="2715354215568469385">"Version <xliff:g id="VERSION_NUMBER">%1$s</xliff:g>"</string>
-    <string name="user_dict_settings_add_menu_title" msgid="1254195365689387076">"Ajouter"</string>
-    <string name="user_dict_settings_add_dialog_title" msgid="4096700390211748168">"Ajouter au dictionnaire"</string>
-    <string name="user_dict_settings_add_screen_title" msgid="5818914331629278758">"Expression"</string>
-    <string name="user_dict_settings_add_dialog_more_options" msgid="5671682004887093112">"Plus d\'options"</string>
-    <string name="user_dict_settings_add_dialog_less_options" msgid="2716586567241724126">"Moins d\'options"</string>
-    <string name="user_dict_settings_add_dialog_confirm" msgid="4703129507388332950">"OK"</string>
-    <string name="user_dict_settings_add_word_option_name" msgid="6665558053408962865">"Mot :"</string>
-    <string name="user_dict_settings_add_shortcut_option_name" msgid="3094731590655523777">"Raccourci :"</string>
-    <string name="user_dict_settings_add_locale_option_name" msgid="4738643440987277705">"Langue :"</string>
-    <string name="user_dict_settings_add_word_hint" msgid="4902434148985906707">"Saisissez un mot"</string>
-    <string name="user_dict_settings_add_shortcut_hint" msgid="2265453012555060178">"Raccourci facultatif"</string>
-    <string name="user_dict_settings_edit_dialog_title" msgid="3765774633869590352">"Modifier le mot"</string>
-    <string name="user_dict_settings_context_menu_edit_title" msgid="6812255903472456302">"Modifier"</string>
-    <string name="user_dict_settings_context_menu_delete_title" msgid="8142932447689461181">"Supprimer"</string>
-    <string name="user_dict_settings_empty_text" msgid="558499587532668203">"Votre dictionnaire personnel ne contient aucun mot. Ajoutez un mot en appuyant sur le bouton d\'ajout (\"+\")."</string>
-    <string name="user_dict_settings_all_languages" msgid="8276126583216298886">"Pour toutes les langues"</string>
-    <string name="user_dict_settings_more_languages" msgid="7131268499685180461">"Plus de langues…"</string>
-    <string name="user_dict_settings_delete" msgid="110413335187193859">"Supprimer"</string>
-    <string name="user_dict_fast_scroll_alphabet" msgid="5431919401558285473">" ABCDEFGHIJKLMNOPQRSTUVWXYZ"</string>
+    <string name="english_ime_name" msgid="7252517407088836577">"Clavier Android"</string>
 </resources>
diff --git a/java/res/values-fr/strings.xml b/java/res/values-fr/strings.xml
index edabdb3..e096a1f 100644
--- a/java/res/values-fr/strings.xml
+++ b/java/res/values-fr/strings.xml
@@ -143,6 +143,7 @@
     <string name="subtype_with_layout_en_GB" msgid="2179097748724725906">"Anglais (Royaume-Uni) (<xliff:g id="LAYOUT">%s</xliff:g>)"</string>
     <string name="subtype_with_layout_en_US" msgid="1362581347576714579">"Anglais (États-Unis) (<xliff:g id="LAYOUT">%s</xliff:g>)"</string>
     <string name="subtype_with_layout_es_US" msgid="6261791057007890189">"Espagnol (États-Unis) (<xliff:g id="LAYOUT">%s</xliff:g>)"</string>
+    <string name="subtype_nepali_traditional" msgid="9032247506728040447">"<xliff:g id="LANGUAGE">%s</xliff:g> (traditionnel)"</string>
     <string name="subtype_no_language" msgid="7137390094240139495">"Aucune langue (latin)"</string>
     <string name="subtype_no_language_qwerty" msgid="244337630616742604">"Alphabet latin (QWERTY)"</string>
     <string name="subtype_no_language_qwertz" msgid="443066912507547976">"Alphabet latin (QWERTZ)"</string>
@@ -150,6 +151,7 @@
     <string name="subtype_no_language_dvorak" msgid="1564494667584718094">"Alphabet latin (Dvorak)"</string>
     <string name="subtype_no_language_colemak" msgid="5837418400010302623">"Alphabet latin (Colemak)"</string>
     <string name="subtype_no_language_pcqwerty" msgid="5354918232046200018">"Alphabet latin (PC)"</string>
+    <string name="subtype_emoji" msgid="7483586578074549196">"Emoji"</string>
     <string name="custom_input_styles_title" msgid="8429952441821251512">"Styles saisie personnalisés"</string>
     <string name="add_style" msgid="6163126614514489951">"Ajouter style"</string>
     <string name="add" msgid="8299699805688017798">"Ajouter"</string>
diff --git a/java/res/values-hi/strings.xml b/java/res/values-hi/strings.xml
index 78a8ffe..dcbbcd9 100644
--- a/java/res/values-hi/strings.xml
+++ b/java/res/values-hi/strings.xml
@@ -143,6 +143,7 @@
     <string name="subtype_with_layout_en_GB" msgid="2179097748724725906">"अंग्रेज़ी (यूके) (<xliff:g id="LAYOUT">%s</xliff:g>)"</string>
     <string name="subtype_with_layout_en_US" msgid="1362581347576714579">"अंग्रेज़ी (यूएस) (<xliff:g id="LAYOUT">%s</xliff:g>)"</string>
     <string name="subtype_with_layout_es_US" msgid="6261791057007890189">"स्पेनिश (यूएस) (<xliff:g id="LAYOUT">%s</xliff:g>)"</string>
+    <string name="subtype_nepali_traditional" msgid="9032247506728040447">"<xliff:g id="LANGUAGE">%s</xliff:g> (पारंपरिक)"</string>
     <string name="subtype_no_language" msgid="7137390094240139495">"भाषा उपलब्ध नहीं है (लैटिन वर्णाक्षर)"</string>
     <string name="subtype_no_language_qwerty" msgid="244337630616742604">"वर्णाक्षर (QWERTY)"</string>
     <string name="subtype_no_language_qwertz" msgid="443066912507547976">"वर्णाक्षर (QWERTZ)"</string>
@@ -150,6 +151,7 @@
     <string name="subtype_no_language_dvorak" msgid="1564494667584718094">"वर्णाक्षर (Dvorak)"</string>
     <string name="subtype_no_language_colemak" msgid="5837418400010302623">"वर्णाक्षर (Colemak)"</string>
     <string name="subtype_no_language_pcqwerty" msgid="5354918232046200018">"वर्णाक्षर (PC)"</string>
+    <string name="subtype_emoji" msgid="7483586578074549196">"Emoji"</string>
     <string name="custom_input_styles_title" msgid="8429952441821251512">"कस्‍टम इनपुट शैलियां"</string>
     <string name="add_style" msgid="6163126614514489951">"शैली जोड़ें"</string>
     <string name="add" msgid="8299699805688017798">"जोड़ें"</string>
diff --git a/java/res/values-hr/strings.xml b/java/res/values-hr/strings.xml
index 084d5b6..0dca64e 100644
--- a/java/res/values-hr/strings.xml
+++ b/java/res/values-hr/strings.xml
@@ -143,6 +143,7 @@
     <string name="subtype_with_layout_en_GB" msgid="2179097748724725906">"engleski (UK) (<xliff:g id="LAYOUT">%s</xliff:g>)"</string>
     <string name="subtype_with_layout_en_US" msgid="1362581347576714579">"engleski (SAD) (<xliff:g id="LAYOUT">%s</xliff:g>)"</string>
     <string name="subtype_with_layout_es_US" msgid="6261791057007890189">"španjolski (SAD) (<xliff:g id="LAYOUT">%s</xliff:g>)"</string>
+    <string name="subtype_nepali_traditional" msgid="9032247506728040447">"<xliff:g id="LANGUAGE">%s</xliff:g> (tradicionalni)"</string>
     <string name="subtype_no_language" msgid="7137390094240139495">"Nema jezika (abeceda)"</string>
     <string name="subtype_no_language_qwerty" msgid="244337630616742604">"Abeceda (QWERTY)"</string>
     <string name="subtype_no_language_qwertz" msgid="443066912507547976">"Abeceda (QWERTZ)"</string>
@@ -150,6 +151,7 @@
     <string name="subtype_no_language_dvorak" msgid="1564494667584718094">"Abeceda (Dvorak)"</string>
     <string name="subtype_no_language_colemak" msgid="5837418400010302623">"Abeceda (Colemak)"</string>
     <string name="subtype_no_language_pcqwerty" msgid="5354918232046200018">"Abeceda (PC)"</string>
+    <string name="subtype_emoji" msgid="7483586578074549196">"Emoji"</string>
     <string name="custom_input_styles_title" msgid="8429952441821251512">"Prilagođeni stilovi unosa"</string>
     <string name="add_style" msgid="6163126614514489951">"Dodaj stil"</string>
     <string name="add" msgid="8299699805688017798">"Dodaj"</string>
diff --git a/java/res/values-hu/strings.xml b/java/res/values-hu/strings.xml
index a335aa9..2abed74 100644
--- a/java/res/values-hu/strings.xml
+++ b/java/res/values-hu/strings.xml
@@ -143,6 +143,7 @@
     <string name="subtype_with_layout_en_GB" msgid="2179097748724725906">"angol (brit) (<xliff:g id="LAYOUT">%s</xliff:g>)"</string>
     <string name="subtype_with_layout_en_US" msgid="1362581347576714579">"angol (amerikai) (<xliff:g id="LAYOUT">%s</xliff:g>)"</string>
     <string name="subtype_with_layout_es_US" msgid="6261791057007890189">"spanyol (USA) (<xliff:g id="LAYOUT">%s</xliff:g>)"</string>
+    <string name="subtype_nepali_traditional" msgid="9032247506728040447">"<xliff:g id="LANGUAGE">%s</xliff:g> (hagyományos)"</string>
     <string name="subtype_no_language" msgid="7137390094240139495">"Nincs nyelv (ábécé)"</string>
     <string name="subtype_no_language_qwerty" msgid="244337630616742604">"Ábécé (QWERTY)"</string>
     <string name="subtype_no_language_qwertz" msgid="443066912507547976">"Ábécé (QWERTZ)"</string>
@@ -150,6 +151,7 @@
     <string name="subtype_no_language_dvorak" msgid="1564494667584718094">"Ábécé (Dvorak)"</string>
     <string name="subtype_no_language_colemak" msgid="5837418400010302623">"Ábécé (Colemak)"</string>
     <string name="subtype_no_language_pcqwerty" msgid="5354918232046200018">"Ábécé (PC)"</string>
+    <string name="subtype_emoji" msgid="7483586578074549196">"Hangulatjel"</string>
     <string name="custom_input_styles_title" msgid="8429952441821251512">"Egyedi bevitelstílusok"</string>
     <string name="add_style" msgid="6163126614514489951">"Új stílus"</string>
     <string name="add" msgid="8299699805688017798">"Hozzáadás"</string>
diff --git a/java/res/values-hy-rAM/strings-appname.xml b/java/res/values-hy-rAM/strings-appname.xml
deleted file mode 100644
index dc3c0c6..0000000
--- a/java/res/values-hy-rAM/strings-appname.xml
+++ /dev/null
@@ -1,27 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!-- 
-/*
-**
-** Copyright 2013, 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:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="english_ime_name" msgid="5940510615957428904">"Android Ստեղնաշար (AOSP)"</string>
-    <string name="spell_checker_service_name" msgid="1254221805440242662">"Android տառասխալների ուղղիչ (AOSP)"</string>
-    <string name="english_ime_settings" msgid="5760361067176802794">"Android ստեղնաշարի կարգավորումներ (AOSP)"</string>
-    <string name="android_spell_checker_settings" msgid="6123949487832861885">"Android տառասխալների ուղղիչի կարգավորումներ (AOSP)"</string>
-</resources>
diff --git a/java/res/values-hy-rAM/strings.xml b/java/res/values-hy-rAM/strings.xml
deleted file mode 100644
index ec4eb46..0000000
--- a/java/res/values-hy-rAM/strings.xml
+++ /dev/null
@@ -1,242 +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.
-*/
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="english_ime_input_options" msgid="3909945612939668554">"Ներածման ընտրանքներ"</string>
-    <string name="english_ime_research_log" msgid="8492602295696577851">"Հետազոտական գրառումների հրամաններ"</string>
-    <string name="use_contacts_for_spellchecking_option_title" msgid="5374120998125353898">"Փնտրել կոնտակտային անուններ"</string>
-    <string name="use_contacts_for_spellchecking_option_summary" msgid="8754413382543307713">"Տառասխալների ուղղիչն օգտագործում է ձեր կոնտակտների ցանկի տվյալները"</string>
-    <string name="vibrate_on_keypress" msgid="5258079494276955460">"Թրթռալ սեղմման ժամանակ"</string>
-    <string name="sound_on_keypress" msgid="6093592297198243644">"Ձայնը սեղմման ժամանակ"</string>
-    <string name="popup_on_keypress" msgid="123894815723512944">"Ելնող պատուհան՝ ստեղնի հպման դեպքում"</string>
-    <string name="general_category" msgid="1859088467017573195">"Ընդհանուր"</string>
-    <string name="correction_category" msgid="2236750915056607613">"Տեքստի ուղղում"</string>
-    <string name="gesture_typing_category" msgid="497263612130532630">"Ժեստերով մուտքագրում"</string>
-    <string name="misc_category" msgid="6894192814868233453">"Այլ ընտրանքներ"</string>
-    <string name="advanced_settings" msgid="362895144495591463">"Ընդլայնված կարգավորումներ"</string>
-    <string name="advanced_settings_summary" msgid="4487980456152830271">"Ընտրանքներ փորձագետների համար"</string>
-    <string name="include_other_imes_in_language_switch_list" msgid="4533689960308565519">"Անցնել մուտքագրման այլ եղանակների"</string>
-    <string name="include_other_imes_in_language_switch_list_summary" msgid="840637129103317635">"Լեզվի փոխարկման բանալին ընդգրկում է այլ մուտքագրման եղանակներ ևս"</string>
-    <string name="show_language_switch_key" msgid="5915478828318774384">"Լեզվի փոխարկման ստեղն"</string>
-    <string name="show_language_switch_key_summary" msgid="7343403647474265713">"Ցույց տալ, երբ մուտքագրման մի քանի լեզուներ են միացված"</string>
-    <string name="sliding_key_input_preview" msgid="6604262359510068370">"Ցուցադրել սահքի ցուցիչը"</string>
-    <string name="sliding_key_input_preview_summary" msgid="6340524345729093886">"Ցուցադրել տեսողական հուշումը Shift-ի կամ նշանների ստեղներից սահեցման ընթացքում"</string>
-    <string name="key_preview_popup_dismiss_delay" msgid="6213164897443068248">"Ելնող պատուհանի հեռացման հետաձգման ստեղն"</string>
-    <string name="key_preview_popup_dismiss_no_delay" msgid="2096123151571458064">"Առանց հետաձգման"</string>
-    <string name="key_preview_popup_dismiss_default_delay" msgid="2166964333903906734">"Նախնականը"</string>
-    <string name="abbreviation_unit_milliseconds" msgid="8700286094028323363">"<xliff:g id="MILLISECONDS">%s</xliff:g>մվ"</string>
-    <string name="settings_system_default" msgid="6268225104743331821">"Համակարգի լռելյայնները"</string>
-    <string name="use_contacts_dict" msgid="4435317977804180815">"Առաջարկել կոնտակտների անունները"</string>
-    <string name="use_contacts_dict_summary" msgid="6599983334507879959">"Օգտագործել կոնտակտների անունները՝ առաջարկների և ուղղումների համար"</string>
-    <string name="use_double_space_period" msgid="8781529969425082860">"Կրկնաբացակի վերջակետ"</string>
-    <string name="use_double_space_period_summary" msgid="6532892187247952799">"Բացակի ստեղնի կրկնակի հպումը բացակից հետո վերջակետ է դնում"</string>
-    <string name="auto_cap" msgid="1719746674854628252">"Ավտոմատ գլխատառացում"</string>
-    <string name="auto_cap_summary" msgid="7934452761022946874">"Գլխատառել յուրաքանչյուր նախադասության առաջին բառը"</string>
-    <string name="edit_personal_dictionary" msgid="3996910038952940420">"Անհատական բառարան"</string>
-    <string name="configure_dictionaries_title" msgid="4238652338556902049">"Ավելացնել բառարաններ"</string>
-    <string name="main_dictionary" msgid="4798763781818361168">"Հիմնական բառարան"</string>
-    <string name="prefs_show_suggestions" msgid="8026799663445531637">"Ցուցադրել ուղղումների առաջարկներ"</string>
-    <string name="prefs_show_suggestions_summary" msgid="1583132279498502825">"Ցուցադրել առաջարկվող բառերը մուտքագրման ընթացքում"</string>
-    <string name="prefs_suggestion_visibility_show_name" msgid="3219916594067551303">"Միշտ ցուցադրել"</string>
-    <string name="prefs_suggestion_visibility_show_only_portrait_name" msgid="3859783767435239118">"Ցուցադրել դիմանկարային ռեժիմում"</string>
-    <string name="prefs_suggestion_visibility_hide_name" msgid="6309143926422234673">"Միշտ թաքցնել"</string>
-    <string name="prefs_block_potentially_offensive_title" msgid="5078480071057408934">"Արգելափակել վիրավորական բառերը"</string>
-    <string name="prefs_block_potentially_offensive_summary" msgid="2371835479734991364">"Չառաջարկել հավանական վիրավորական բառերը"</string>
-    <string name="auto_correction" msgid="7630720885194996950">"Ինքնուղղում"</string>
-    <string name="auto_correction_summary" msgid="5625751551134658006">"Տպագրական սխալով բառերում ավտոմատ տեղադրել բացակներն ու կետադրական նշանները"</string>
-    <string name="auto_correction_threshold_mode_off" msgid="8470882665417944026">"Անջատված"</string>
-    <string name="auto_correction_threshold_mode_modest" msgid="8788366690620799097">"Համեստ"</string>
-    <string name="auto_correction_threshold_mode_aggressive" msgid="7319007299148899623">"Ագրեսիվ"</string>
-    <string name="auto_correction_threshold_mode_very_aggressive" msgid="1853309024129480416">"Շատ ագրեսիվ"</string>
-    <string name="bigram_prediction" msgid="1084449187723948550">"Հաջորդ բառի առաջարկներ"</string>
-    <string name="bigram_prediction_summary" msgid="3896362682751109677">"Առաջարկներ կազմելու համար օգտագործել նախորդ բառը"</string>
-    <string name="gesture_input" msgid="826951152254563827">"Միացնել ժեստերով մուտքագրումը"</string>
-    <string name="gesture_input_summary" msgid="9180350639305731231">"Մուտքագրեք բառ` սահեցնելով տառերը"</string>
-    <string name="gesture_preview_trail" msgid="3802333369335722221">"Ցույց տալ ժեստի հետագիծը"</string>
-    <string name="gesture_floating_preview_text" msgid="4443240334739381053">"Դինամիկ սահող նախատեսք"</string>
-    <string name="gesture_floating_preview_text_summary" msgid="4472696213996203533">"Տեսեք առաջարկված բառը՝ ժեստի միջոցով"</string>
-    <string name="added_word" msgid="8993883354622484372">"<xliff:g id="WORD">%s</xliff:g>` պահված է"</string>
-    <string name="label_go_key" msgid="1635148082137219148">"Առաջ"</string>
-    <string name="label_next_key" msgid="362972844525672568">"Հաջորդը"</string>
-    <string name="label_previous_key" msgid="1211868118071386787">"Նխրդ"</string>
-    <string name="label_done_key" msgid="2441578748772529288">"Կատարված է"</string>
-    <string name="label_send_key" msgid="2815056534433717444">"Ուղարկել"</string>
-    <string name="label_pause_key" msgid="181098308428035340">"Դադար"</string>
-    <string name="label_wait_key" msgid="6402152600878093134">"Սպասել"</string>
-    <string name="spoken_use_headphones" msgid="896961781287283493">"Միացրեք ականջակալը՝ բարձրաձայն արտասանվող գաղտնաբառը լսելու համար:"</string>
-    <string name="spoken_current_text_is" msgid="2485723011272583845">"Տվյալ տեքստը %s է"</string>
-    <string name="spoken_no_text_entered" msgid="7479685225597344496">"Տեքստ չի մուտքագրվել"</string>
-    <string name="spoken_description_unknown" msgid="3197434010402179157">"Բանալու կոդը՝ %d"</string>
-    <string name="spoken_description_shift" msgid="244197883292549308">"Shift"</string>
-    <string name="spoken_description_shift_shifted" msgid="1681877323344195035">"Shift-ը միացված է (հպել անջատելու համար)"</string>
-    <string name="spoken_description_caps_lock" msgid="3276478269526304432">"Caps lock-ը միացված է (հպել՝ անջատելու համար)"</string>
-    <string name="spoken_description_delete" msgid="8740376944276199801">"Ջնջել"</string>
-    <string name="spoken_description_to_symbol" msgid="5486340107500448969">"Նշաններ"</string>
-    <string name="spoken_description_to_alpha" msgid="23129338819771807">"Տառեր"</string>
-    <string name="spoken_description_to_numeric" msgid="591752092685161732">"Թվեր"</string>
-    <string name="spoken_description_settings" msgid="4627462689603838099">"Կարգավորումներ"</string>
-    <string name="spoken_description_tab" msgid="2667716002663482248">"Tab"</string>
-    <string name="spoken_description_space" msgid="2582521050049860859">"Բացակ"</string>
-    <string name="spoken_description_mic" msgid="615536748882611950">"Ձայնային մուտքագրում"</string>
-    <string name="spoken_description_smiley" msgid="2256309826200113918">"Ժպիտ"</string>
-    <string name="spoken_description_return" msgid="8178083177238315647">"Վերադարձ"</string>
-    <string name="spoken_description_search" msgid="1247236163755920808">"Որոնել"</string>
-    <string name="spoken_description_dot" msgid="40711082435231673">"Կետ"</string>
-    <string name="spoken_description_language_switch" msgid="5507091328222331316">"Փոխել լեզուն"</string>
-    <string name="spoken_description_action_next" msgid="8636078276664150324">"Հաջորդը"</string>
-    <string name="spoken_description_action_previous" msgid="800872415009336208">"Նախորդը"</string>
-    <string name="spoken_description_shiftmode_on" msgid="5700440798609574589">"Shift-ը միացված է"</string>
-    <string name="spoken_description_shiftmode_locked" msgid="593175803181701830">"Caps Lock-ը միացված է"</string>
-    <string name="spoken_description_shiftmode_off" msgid="657219998449174808">"Shift-ն անջատված է"</string>
-    <string name="spoken_description_mode_symbol" msgid="7183343879909747642">"Նշանների ռեժիմ"</string>
-    <string name="spoken_description_mode_alpha" msgid="3528307674390156956">"Տառերի ռեժիմ"</string>
-    <string name="spoken_description_mode_phone" msgid="6520207943132026264">"Հեռախոսային ռեժիմ"</string>
-    <string name="spoken_description_mode_phone_shift" msgid="5499629753962641227">"Հեռախոսի նշանների ռեժիմ"</string>
-    <string name="announce_keyboard_hidden" msgid="8718927835531429807">"Ստեղնաշարը թաքցված է"</string>
-    <string name="announce_keyboard_mode" msgid="4729081055438508321">"Ցուցադրված է <xliff:g id="MODE">%s</xliff:g> ստեղնաշարը"</string>
-    <string name="keyboard_mode_date" msgid="3137520166817128102">"ամսաթիվ"</string>
-    <string name="keyboard_mode_date_time" msgid="339593358488851072">"ամսաթիվ և ժամ"</string>
-    <string name="keyboard_mode_email" msgid="6216248078128294262">"էլփոստ"</string>
-    <string name="keyboard_mode_im" msgid="1137405089766557048">"նամակագրություն"</string>
-    <string name="keyboard_mode_number" msgid="7991623440699957069">"թվեր"</string>
-    <string name="keyboard_mode_phone" msgid="6851627527401433229">"հեռախոսահամար"</string>
-    <string name="keyboard_mode_text" msgid="6479436687899701619">"տեքստ"</string>
-    <string name="keyboard_mode_time" msgid="4381856885582143277">"ժամանակ"</string>
-    <string name="keyboard_mode_url" msgid="1519819835514911218">"URL"</string>
-    <string name="voice_input" msgid="3583258583521397548">"Ձայնային մուտքագրման ստեղն"</string>
-    <string name="voice_input_modes_main_keyboard" msgid="3360660341121083174">"Հիմնական ստեղնաշարի վրա"</string>
-    <string name="voice_input_modes_symbols_keyboard" msgid="7203213240786084067">"Նշանների ստեղնաշարի վրա"</string>
-    <string name="voice_input_modes_off" msgid="3745699748218082014">"Անջատված"</string>
-    <string name="voice_input_modes_summary_main_keyboard" msgid="6586544292900314339">"Բարձրախոս հիմնական ստեղնաշարի վրա"</string>
-    <string name="voice_input_modes_summary_symbols_keyboard" msgid="5233725927281932391">"Բարձրախոս նշանների ստեղնաշարի վրա"</string>
-    <string name="voice_input_modes_summary_off" msgid="63875609591897607">"Ձայնային մուտքագրումն անջատված է"</string>
-    <string name="configure_input_method" msgid="373356270290742459">"Կարգավորել մուտքագրման մեթոդները"</string>
-    <string name="language_selection_title" msgid="1651299598555326750">"Մուտքագրման լեզուներ"</string>
-    <string name="send_feedback" msgid="1780431884109392046">"Արձագանքել"</string>
-    <string name="select_language" msgid="3693815588777926848">"Մուտքագրման լեզուներ"</string>
-    <string name="hint_add_to_dictionary" msgid="573678656946085380">"Պահպանելու համար կրկին հպեք"</string>
-    <string name="has_dictionary" msgid="6071847973466625007">"Բառարանն առկա է"</string>
-    <string name="prefs_enable_log" msgid="6620424505072963557">"Միացնել օգտվողի արձագանքը"</string>
-    <string name="prefs_description_log" msgid="7525225584555429211">"Օգնել բարելավել այս մուտքագրման եղանակի խմբագրիչը՝ ինքնուրույն ուղարկելով Google-ին օգտագործման վիճակագրությունն ու վթարների հաշվետվությունները:"</string>
-    <string name="keyboard_layout" msgid="8451164783510487501">"Ստեղնաշարի թեման"</string>
-    <string name="subtype_en_GB" msgid="88170601942311355">"Անգլերեն (ՄԹ)"</string>
-    <string name="subtype_en_US" msgid="6160452336634534239">"Անգլերեն (ԱՄՆ)"</string>
-    <string name="subtype_es_US" msgid="5583145191430180200">"Իսպաներեն (ԱՄՆ)"</string>
-    <string name="subtype_with_layout_en_GB" msgid="2179097748724725906">"Անգլերեն (ՄԹ) (<xliff:g id="LAYOUT">%s</xliff:g>)"</string>
-    <string name="subtype_with_layout_en_US" msgid="1362581347576714579">"Անգլերեն (ԱՄՆ) (<xliff:g id="LAYOUT">%s</xliff:g>)"</string>
-    <string name="subtype_with_layout_es_US" msgid="6261791057007890189">"Իսպաներեն (ԱՄՆ) (<xliff:g id="LAYOUT">%s</xliff:g>)"</string>
-    <string name="subtype_no_language" msgid="7137390094240139495">"Ոչ մի լեզվով (Այբուբեն)"</string>
-    <string name="subtype_no_language_qwerty" msgid="244337630616742604">"Այբուբեն (QWERTY)"</string>
-    <string name="subtype_no_language_qwertz" msgid="443066912507547976">"Այբուբեն (QWERTZ)"</string>
-    <string name="subtype_no_language_azerty" msgid="8144348527575640087">"Այբուբեն (AZERTY)"</string>
-    <string name="subtype_no_language_dvorak" msgid="1564494667584718094">"Այբուբեն (Dvorak)"</string>
-    <string name="subtype_no_language_colemak" msgid="5837418400010302623">"Այբուբեն (Colemak)"</string>
-    <string name="subtype_no_language_pcqwerty" msgid="5354918232046200018">"Այբուբեն (PC)"</string>
-    <string name="custom_input_styles_title" msgid="8429952441821251512">"Մուտքագրման հատուկ ոճեր"</string>
-    <string name="add_style" msgid="6163126614514489951">"Ավելացնել ոճ"</string>
-    <string name="add" msgid="8299699805688017798">"Ավելացնել"</string>
-    <string name="remove" msgid="4486081658752944606">"Հեռացնել"</string>
-    <string name="save" msgid="7646738597196767214">"Պահել"</string>
-    <string name="subtype_locale" msgid="8576443440738143764">"Lեզու"</string>
-    <string name="keyboard_layout_set" msgid="4309233698194565609">"Դասավորություն"</string>
-    <string name="custom_input_style_note_message" msgid="8826731320846363423">"Մուտքագրման ձեր հատուկ ոճը պետք է միացված լինի նախքան դուք կսկսեք օգտագործել այն: Ցանկանո՞ւմ եք միացնել այն հիմա:"</string>
-    <string name="enable" msgid="5031294444630523247">"Միացնել"</string>
-    <string name="not_now" msgid="6172462888202790482">"Ոչ հիմա"</string>
-    <string name="custom_input_style_already_exists" msgid="8008728952215449707">"Մուտքագրման այսպիսի ոճ արդեն գոյություն ունի՝ <xliff:g id="INPUT_STYLE_NAME">%s</xliff:g>"</string>
-    <string name="prefs_usability_study_mode" msgid="1261130555134595254">"Հարմարավետության ուսումնասիրության ռեժիմ"</string>
-    <string name="prefs_key_longpress_timeout_settings" msgid="6102240298932897873">"Ստեղնի երկար սեղմման ուշացում"</string>
-    <string name="prefs_keypress_vibration_duration_settings" msgid="7918341459947439226">"Սեղմման թրթռոցի տևողություն"</string>
-    <string name="prefs_keypress_sound_volume_settings" msgid="6027007337036891623">"Սեղմման ձայնի բարձրությունը"</string>
-    <string name="prefs_read_external_dictionary" msgid="2588931418575013067">"Կարդալ արտաքին բառարանի ֆայլը"</string>
-    <string name="read_external_dictionary_no_files_message" msgid="4947420942224623792">"Ներբեռնումների թղթապանակում բառարանային ֆայլեր չկան"</string>
-    <string name="read_external_dictionary_multiple_files_title" msgid="7637749044265808628">"Ընտրեք բառարանային ֆայլը տեղադրման համար"</string>
-    <string name="read_external_dictionary_confirm_install_message" msgid="6898610163768980870">"Իրո՞ք ուզում եք տեղադրել այս ֆայլը <xliff:g id="LOCALE_NAME">%s</xliff:g>-ում:"</string>
-    <string name="error" msgid="8940763624668513648">"Տեղի է ունեցել սխալ"</string>
-    <string name="button_default" msgid="3988017840431881491">"Լռելյայնը"</string>
-    <string name="setup_welcome_title" msgid="6112821709832031715">"Բարի գալուստ <xliff:g id="APPLICATION_NAME">%s</xliff:g>"</string>
-    <string name="setup_welcome_additional_description" msgid="8150252008545768953">"Ժեստային մուտքագրմամբ"</string>
-    <string name="setup_start_action" msgid="8936036460897347708">"Սկսել"</string>
-    <string name="setup_next_action" msgid="371821437915144603">"Հաջորդ քայլը"</string>
-    <string name="setup_steps_title" msgid="6400373034871816182">"Տեղադրվում է <xliff:g id="APPLICATION_NAME">%s</xliff:g>-ը"</string>
-    <string name="setup_step1_title" msgid="3147967630253462315">"Միացնել <xliff:g id="APPLICATION_NAME">%s</xliff:g>-ը"</string>
-    <string name="setup_step1_instruction" msgid="2578631936624637241">"Խնդրում ենք ստուգել «<xliff:g id="APPLICATION_NAME">%s</xliff:g>»-ը ձեր Լեզվի &amp; մուտքագրման կարգավորումներում: Դա կլիազորի նրան գործարկվել ձեր սարքում:"</string>
-    <string name="setup_step1_finished_instruction" msgid="10761482004957994">"<xliff:g id="APPLICATION_NAME">%s</xliff:g>-ն արդեն միացված է ձեր Լեզվի &amp; մուտքագրման կարգավորումներում, ուստի այս քայլն արված է: Անցնել հաջորդին:"</string>
-    <string name="setup_step1_action" msgid="4366513534999901728">"Միացնել կարգավորումներից"</string>
-    <string name="setup_step2_title" msgid="6860725447906690594">"Փոխարկել <xliff:g id="APPLICATION_NAME">%s</xliff:g>-ին"</string>
-    <string name="setup_step2_instruction" msgid="9141481964870023336">"Հաջորդիվ, ընտրեք «<xliff:g id="APPLICATION_NAME">%s</xliff:g>»-ը որպես ձեր ակտիվ տեքստային մուտքագրման եղանակ:"</string>
-    <string name="setup_step2_action" msgid="1660330307159824337">"Փոխարկել մուտքագրման եղանակները"</string>
-    <string name="setup_step3_title" msgid="3154757183631490281">"Շնորհավորում ենք, դուք տեղադրեցիք բոլորը:"</string>
-    <string name="setup_step3_instruction" msgid="8025981829605426000">"Այժմ դուք կարող եք մուտքագրել ձեր բոլոր սիրելի հավելվածներում <xliff:g id="APPLICATION_NAME">%s</xliff:g>-ով:"</string>
-    <string name="setup_step3_action" msgid="600879797256942259">"Կարգավորել լրացուցիչ լեզուները"</string>
-    <string name="setup_finish_action" msgid="276559243409465389">"Ավարտված"</string>
-    <string name="show_setup_wizard_icon" msgid="5008028590593710830">"Ցույց տալ հավելվածի պատկերակը"</string>
-    <string name="show_setup_wizard_icon_summary" msgid="4119998322536880213">"Ցուցադրել հավելվածի պատկերակը թողարկչում"</string>
-    <string name="app_name" msgid="6320102637491234792">"Բառարանի մատակարար"</string>
-    <string name="dictionary_provider_name" msgid="3027315045397363079">"Բառարանի մատակարար"</string>
-    <string name="dictionary_service_name" msgid="6237472350693511448">"Բառարանի ծառայություն"</string>
-    <string name="download_description" msgid="6014835283119198591">"Տեղեկություններ բառարանների թարմացման մասին"</string>
-    <string name="dictionary_settings_title" msgid="8091417676045693313">"Ավելացնել բառարաններ"</string>
-    <string name="dictionary_install_over_metered_network_prompt" msgid="3587517870006332980">"Բառարանն առկա է"</string>
-    <string name="dictionary_settings_summary" msgid="5305694987799824349">"Բառարանների կարգավորումներ"</string>
-    <string name="user_dictionaries" msgid="3582332055892252845">"Օգտվողի բառարաններ"</string>
-    <string name="default_user_dict_pref_name" msgid="1625055720489280530">"Օգտվողի բառարան"</string>
-    <string name="dictionary_available" msgid="4728975345815214218">"Բառարանն առկա է"</string>
-    <string name="dictionary_downloading" msgid="2982650524622620983">"Այս պահին ներբեռնվում է"</string>
-    <string name="dictionary_installed" msgid="8081558343559342962">"Տեղադրված է"</string>
-    <string name="dictionary_disabled" msgid="8950383219564621762">"Տեղադրված է, անջատված է"</string>
-    <string name="cannot_connect_to_dict_service" msgid="9216933695765732398">"Բառարանային ծառայությանը միացման խնդիր կա"</string>
-    <string name="no_dictionaries_available" msgid="8039920716566132611">"Բառարաններ չկան"</string>
-    <string name="check_for_updates_now" msgid="8087688440916388581">"Թարմացնել"</string>
-    <string name="last_update" msgid="730467549913588780">"Վերջին անգամ թարմացվել է"</string>
-    <string name="message_updating" msgid="4457761393932375219">"Ստուգվում է թարմացումների առկայությունը"</string>
-    <string name="message_loading" msgid="8689096636874758814">"Բեռնվում է..."</string>
-    <string name="main_dict_description" msgid="3072821352793492143">"Հիմնական բառարան"</string>
-    <string name="cancel" msgid="6830980399865683324">"Չեղարկել"</string>
-    <string name="install_dict" msgid="180852772562189365">"Տեղադրել"</string>
-    <string name="cancel_download_dict" msgid="7843340278507019303">"Չեղարկել"</string>
-    <string name="delete_dict" msgid="756853268088330054">"Ջնջել"</string>
-    <string name="should_download_over_metered_prompt" msgid="2878629598667658845">"Ձեր բջջային սարքում ընտրված լեզվով առկա է բառարան:<br/> Խորհուրդ ենք տալիս &lt;b&gt;ներբեռնել&lt;/b&gt; <xliff:g id="LANGUAGE">%1$s</xliff:g> բառարանը ձեր մուտքագրման հմտությունների բարելավման համար:&lt;br/&gt; &lt;br/&gt; Ներբեռնումը կարող է խլել մեկ կամ երկու րոպե 3G-ի դեպքում: Հնարավոր է գանձում կատարվի, եթե դուք չունեք &lt;b&gt;տվյալների անսահմանափակ փաթեթ&lt;/b&gt;.&lt;br/&gt; Եթե դուք վստահ չեք, թե տվյալների որ փաթեթն ունեք, խորհուրդ ենք տալիս գտնել Wi-Fi կապ՝ ներբեռնումն ավտոմատ սկսելու համար:&lt;br/&gt; &lt;br/&gt; Հուշում. դուք կարող եք ներբեռնել և հեռացնել բառարաններ՝ գնալով ձեր բջջային սարքի &lt;b&gt;Կարգավորումներ ցանկի Լեզու &amp; մուտքագրման&lt;/b&gt; բաժինը:"</string>
-    <string name="download_over_metered" msgid="1643065851159409546">"Ներբեռնել հիմա (<xliff:g id="SIZE_IN_MEGABYTES">%1$.1f</xliff:g>Մբ)"</string>
-    <string name="do_not_download_over_metered" msgid="2176209579313941583">"Ներբեռնել Wi-Fi-ով"</string>
-    <string name="dict_available_notification_title" msgid="6514288591959117288">"<xliff:g id="LANGUAGE">%1$s</xliff:g>-ով առկա է մի բառարան"</string>
-    <string name="dict_available_notification_description" msgid="1075194169443163487">"Սեղմեք՝ վերանայելու և ներբեռնելու համար"</string>
-    <string name="toast_downloading_suggestions" msgid="1313027353588566660">"Ներբեռնվում է. <xliff:g id="LANGUAGE">%1$s</xliff:g>-ի համար առաջարկները շուտով պատրաստ կլինեն:"</string>
-    <string name="version_text" msgid="2715354215568469385">"Տարբերակ <xliff:g id="VERSION_NUMBER">%1$s</xliff:g>"</string>
-    <string name="user_dict_settings_add_menu_title" msgid="1254195365689387076">"Ավելացնել"</string>
-    <string name="user_dict_settings_add_dialog_title" msgid="4096700390211748168">"Ավելացնել բառարանում"</string>
-    <string name="user_dict_settings_add_screen_title" msgid="5818914331629278758">"Արտահայտություն"</string>
-    <string name="user_dict_settings_add_dialog_more_options" msgid="5671682004887093112">"Այլ ընտրանքներ"</string>
-    <string name="user_dict_settings_add_dialog_less_options" msgid="2716586567241724126">"Սակավ ընտրանքներ"</string>
-    <string name="user_dict_settings_add_dialog_confirm" msgid="4703129507388332950">"Լավ"</string>
-    <string name="user_dict_settings_add_word_option_name" msgid="6665558053408962865">"Բառը՝"</string>
-    <string name="user_dict_settings_add_shortcut_option_name" msgid="3094731590655523777">"Դյուրանցումը՝"</string>
-    <string name="user_dict_settings_add_locale_option_name" msgid="4738643440987277705">"Lեզուն՝"</string>
-    <string name="user_dict_settings_add_word_hint" msgid="4902434148985906707">"Մուտքագրեք բառը"</string>
-    <string name="user_dict_settings_add_shortcut_hint" msgid="2265453012555060178">"Ընտրովի դյուրանցում"</string>
-    <string name="user_dict_settings_edit_dialog_title" msgid="3765774633869590352">"Խմբագրել բառը"</string>
-    <string name="user_dict_settings_context_menu_edit_title" msgid="6812255903472456302">"Խմբագրել"</string>
-    <string name="user_dict_settings_context_menu_delete_title" msgid="8142932447689461181">"Ջնջել"</string>
-    <string name="user_dict_settings_empty_text" msgid="558499587532668203">"Դուք չունեք ոչ մի բառ օգտվողի բառարանում: Ավելացնել բառեր՝ հպելով Ավելացնել (+) կոճակը:"</string>
-    <string name="user_dict_settings_all_languages" msgid="8276126583216298886">"Բոլոր լեզուներով"</string>
-    <string name="user_dict_settings_more_languages" msgid="7131268499685180461">"Ավելի շատ լեզուներով..."</string>
-    <string name="user_dict_settings_delete" msgid="110413335187193859">"Ջնջել"</string>
-    <string name="user_dict_fast_scroll_alphabet" msgid="5431919401558285473">" ԱԲԳԴԵԶԷԸԹԺԻԼԽԾԿՀՁՂՃՄՅՆՇՈՉՊՋՌՍՎՏՐՑՈՒՓՔԵւՕՖ"</string>
-</resources>
diff --git a/java/res/values-hy/donottranslate.xml b/java/res/values-hy/donottranslate.xml
new file mode 100644
index 0000000..4a6d188
--- /dev/null
+++ b/java/res/values-hy/donottranslate.xml
@@ -0,0 +1,29 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+**
+** Copyright 2013, 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">
+    <!-- Same list as in English, but add armenian period and comma: -->
+    <!-- U+055D: "՝" ARMENIAN COMMA -->
+    <!-- U+0589: "։" ARMENIAN FULL STOP -->
+    <!-- Symbols that are normally followed by a space (used to add an auto-space after these) -->
+    <string name="symbols_followed_by_space">.,;:!?)]}&amp;&#x0589;&#x055D;</string>
+    <!-- Symbols that separate words. Adding armenian period and comma. -->
+    <!-- Don't remove the enclosing double quotes, they protect whitespace (not just U+0020) -->
+    <string name="symbols_word_separators">"&#x0009;&#x0020;\n"()[]{}*&amp;&lt;&gt;+=|.,;:!?/_\"&#x0589;&#x055D;</string>
+</resources>
diff --git a/java/res/values-hy/strings.xml b/java/res/values-hy/strings.xml
deleted file mode 100644
index ec4eb46..0000000
--- a/java/res/values-hy/strings.xml
+++ /dev/null
@@ -1,242 +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.
-*/
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="english_ime_input_options" msgid="3909945612939668554">"Ներածման ընտրանքներ"</string>
-    <string name="english_ime_research_log" msgid="8492602295696577851">"Հետազոտական գրառումների հրամաններ"</string>
-    <string name="use_contacts_for_spellchecking_option_title" msgid="5374120998125353898">"Փնտրել կոնտակտային անուններ"</string>
-    <string name="use_contacts_for_spellchecking_option_summary" msgid="8754413382543307713">"Տառասխալների ուղղիչն օգտագործում է ձեր կոնտակտների ցանկի տվյալները"</string>
-    <string name="vibrate_on_keypress" msgid="5258079494276955460">"Թրթռալ սեղմման ժամանակ"</string>
-    <string name="sound_on_keypress" msgid="6093592297198243644">"Ձայնը սեղմման ժամանակ"</string>
-    <string name="popup_on_keypress" msgid="123894815723512944">"Ելնող պատուհան՝ ստեղնի հպման դեպքում"</string>
-    <string name="general_category" msgid="1859088467017573195">"Ընդհանուր"</string>
-    <string name="correction_category" msgid="2236750915056607613">"Տեքստի ուղղում"</string>
-    <string name="gesture_typing_category" msgid="497263612130532630">"Ժեստերով մուտքագրում"</string>
-    <string name="misc_category" msgid="6894192814868233453">"Այլ ընտրանքներ"</string>
-    <string name="advanced_settings" msgid="362895144495591463">"Ընդլայնված կարգավորումներ"</string>
-    <string name="advanced_settings_summary" msgid="4487980456152830271">"Ընտրանքներ փորձագետների համար"</string>
-    <string name="include_other_imes_in_language_switch_list" msgid="4533689960308565519">"Անցնել մուտքագրման այլ եղանակների"</string>
-    <string name="include_other_imes_in_language_switch_list_summary" msgid="840637129103317635">"Լեզվի փոխարկման բանալին ընդգրկում է այլ մուտքագրման եղանակներ ևս"</string>
-    <string name="show_language_switch_key" msgid="5915478828318774384">"Լեզվի փոխարկման ստեղն"</string>
-    <string name="show_language_switch_key_summary" msgid="7343403647474265713">"Ցույց տալ, երբ մուտքագրման մի քանի լեզուներ են միացված"</string>
-    <string name="sliding_key_input_preview" msgid="6604262359510068370">"Ցուցադրել սահքի ցուցիչը"</string>
-    <string name="sliding_key_input_preview_summary" msgid="6340524345729093886">"Ցուցադրել տեսողական հուշումը Shift-ի կամ նշանների ստեղներից սահեցման ընթացքում"</string>
-    <string name="key_preview_popup_dismiss_delay" msgid="6213164897443068248">"Ելնող պատուհանի հեռացման հետաձգման ստեղն"</string>
-    <string name="key_preview_popup_dismiss_no_delay" msgid="2096123151571458064">"Առանց հետաձգման"</string>
-    <string name="key_preview_popup_dismiss_default_delay" msgid="2166964333903906734">"Նախնականը"</string>
-    <string name="abbreviation_unit_milliseconds" msgid="8700286094028323363">"<xliff:g id="MILLISECONDS">%s</xliff:g>մվ"</string>
-    <string name="settings_system_default" msgid="6268225104743331821">"Համակարգի լռելյայնները"</string>
-    <string name="use_contacts_dict" msgid="4435317977804180815">"Առաջարկել կոնտակտների անունները"</string>
-    <string name="use_contacts_dict_summary" msgid="6599983334507879959">"Օգտագործել կոնտակտների անունները՝ առաջարկների և ուղղումների համար"</string>
-    <string name="use_double_space_period" msgid="8781529969425082860">"Կրկնաբացակի վերջակետ"</string>
-    <string name="use_double_space_period_summary" msgid="6532892187247952799">"Բացակի ստեղնի կրկնակի հպումը բացակից հետո վերջակետ է դնում"</string>
-    <string name="auto_cap" msgid="1719746674854628252">"Ավտոմատ գլխատառացում"</string>
-    <string name="auto_cap_summary" msgid="7934452761022946874">"Գլխատառել յուրաքանչյուր նախադասության առաջին բառը"</string>
-    <string name="edit_personal_dictionary" msgid="3996910038952940420">"Անհատական բառարան"</string>
-    <string name="configure_dictionaries_title" msgid="4238652338556902049">"Ավելացնել բառարաններ"</string>
-    <string name="main_dictionary" msgid="4798763781818361168">"Հիմնական բառարան"</string>
-    <string name="prefs_show_suggestions" msgid="8026799663445531637">"Ցուցադրել ուղղումների առաջարկներ"</string>
-    <string name="prefs_show_suggestions_summary" msgid="1583132279498502825">"Ցուցադրել առաջարկվող բառերը մուտքագրման ընթացքում"</string>
-    <string name="prefs_suggestion_visibility_show_name" msgid="3219916594067551303">"Միշտ ցուցադրել"</string>
-    <string name="prefs_suggestion_visibility_show_only_portrait_name" msgid="3859783767435239118">"Ցուցադրել դիմանկարային ռեժիմում"</string>
-    <string name="prefs_suggestion_visibility_hide_name" msgid="6309143926422234673">"Միշտ թաքցնել"</string>
-    <string name="prefs_block_potentially_offensive_title" msgid="5078480071057408934">"Արգելափակել վիրավորական բառերը"</string>
-    <string name="prefs_block_potentially_offensive_summary" msgid="2371835479734991364">"Չառաջարկել հավանական վիրավորական բառերը"</string>
-    <string name="auto_correction" msgid="7630720885194996950">"Ինքնուղղում"</string>
-    <string name="auto_correction_summary" msgid="5625751551134658006">"Տպագրական սխալով բառերում ավտոմատ տեղադրել բացակներն ու կետադրական նշանները"</string>
-    <string name="auto_correction_threshold_mode_off" msgid="8470882665417944026">"Անջատված"</string>
-    <string name="auto_correction_threshold_mode_modest" msgid="8788366690620799097">"Համեստ"</string>
-    <string name="auto_correction_threshold_mode_aggressive" msgid="7319007299148899623">"Ագրեսիվ"</string>
-    <string name="auto_correction_threshold_mode_very_aggressive" msgid="1853309024129480416">"Շատ ագրեսիվ"</string>
-    <string name="bigram_prediction" msgid="1084449187723948550">"Հաջորդ բառի առաջարկներ"</string>
-    <string name="bigram_prediction_summary" msgid="3896362682751109677">"Առաջարկներ կազմելու համար օգտագործել նախորդ բառը"</string>
-    <string name="gesture_input" msgid="826951152254563827">"Միացնել ժեստերով մուտքագրումը"</string>
-    <string name="gesture_input_summary" msgid="9180350639305731231">"Մուտքագրեք բառ` սահեցնելով տառերը"</string>
-    <string name="gesture_preview_trail" msgid="3802333369335722221">"Ցույց տալ ժեստի հետագիծը"</string>
-    <string name="gesture_floating_preview_text" msgid="4443240334739381053">"Դինամիկ սահող նախատեսք"</string>
-    <string name="gesture_floating_preview_text_summary" msgid="4472696213996203533">"Տեսեք առաջարկված բառը՝ ժեստի միջոցով"</string>
-    <string name="added_word" msgid="8993883354622484372">"<xliff:g id="WORD">%s</xliff:g>` պահված է"</string>
-    <string name="label_go_key" msgid="1635148082137219148">"Առաջ"</string>
-    <string name="label_next_key" msgid="362972844525672568">"Հաջորդը"</string>
-    <string name="label_previous_key" msgid="1211868118071386787">"Նխրդ"</string>
-    <string name="label_done_key" msgid="2441578748772529288">"Կատարված է"</string>
-    <string name="label_send_key" msgid="2815056534433717444">"Ուղարկել"</string>
-    <string name="label_pause_key" msgid="181098308428035340">"Դադար"</string>
-    <string name="label_wait_key" msgid="6402152600878093134">"Սպասել"</string>
-    <string name="spoken_use_headphones" msgid="896961781287283493">"Միացրեք ականջակալը՝ բարձրաձայն արտասանվող գաղտնաբառը լսելու համար:"</string>
-    <string name="spoken_current_text_is" msgid="2485723011272583845">"Տվյալ տեքստը %s է"</string>
-    <string name="spoken_no_text_entered" msgid="7479685225597344496">"Տեքստ չի մուտքագրվել"</string>
-    <string name="spoken_description_unknown" msgid="3197434010402179157">"Բանալու կոդը՝ %d"</string>
-    <string name="spoken_description_shift" msgid="244197883292549308">"Shift"</string>
-    <string name="spoken_description_shift_shifted" msgid="1681877323344195035">"Shift-ը միացված է (հպել անջատելու համար)"</string>
-    <string name="spoken_description_caps_lock" msgid="3276478269526304432">"Caps lock-ը միացված է (հպել՝ անջատելու համար)"</string>
-    <string name="spoken_description_delete" msgid="8740376944276199801">"Ջնջել"</string>
-    <string name="spoken_description_to_symbol" msgid="5486340107500448969">"Նշաններ"</string>
-    <string name="spoken_description_to_alpha" msgid="23129338819771807">"Տառեր"</string>
-    <string name="spoken_description_to_numeric" msgid="591752092685161732">"Թվեր"</string>
-    <string name="spoken_description_settings" msgid="4627462689603838099">"Կարգավորումներ"</string>
-    <string name="spoken_description_tab" msgid="2667716002663482248">"Tab"</string>
-    <string name="spoken_description_space" msgid="2582521050049860859">"Բացակ"</string>
-    <string name="spoken_description_mic" msgid="615536748882611950">"Ձայնային մուտքագրում"</string>
-    <string name="spoken_description_smiley" msgid="2256309826200113918">"Ժպիտ"</string>
-    <string name="spoken_description_return" msgid="8178083177238315647">"Վերադարձ"</string>
-    <string name="spoken_description_search" msgid="1247236163755920808">"Որոնել"</string>
-    <string name="spoken_description_dot" msgid="40711082435231673">"Կետ"</string>
-    <string name="spoken_description_language_switch" msgid="5507091328222331316">"Փոխել լեզուն"</string>
-    <string name="spoken_description_action_next" msgid="8636078276664150324">"Հաջորդը"</string>
-    <string name="spoken_description_action_previous" msgid="800872415009336208">"Նախորդը"</string>
-    <string name="spoken_description_shiftmode_on" msgid="5700440798609574589">"Shift-ը միացված է"</string>
-    <string name="spoken_description_shiftmode_locked" msgid="593175803181701830">"Caps Lock-ը միացված է"</string>
-    <string name="spoken_description_shiftmode_off" msgid="657219998449174808">"Shift-ն անջատված է"</string>
-    <string name="spoken_description_mode_symbol" msgid="7183343879909747642">"Նշանների ռեժիմ"</string>
-    <string name="spoken_description_mode_alpha" msgid="3528307674390156956">"Տառերի ռեժիմ"</string>
-    <string name="spoken_description_mode_phone" msgid="6520207943132026264">"Հեռախոսային ռեժիմ"</string>
-    <string name="spoken_description_mode_phone_shift" msgid="5499629753962641227">"Հեռախոսի նշանների ռեժիմ"</string>
-    <string name="announce_keyboard_hidden" msgid="8718927835531429807">"Ստեղնաշարը թաքցված է"</string>
-    <string name="announce_keyboard_mode" msgid="4729081055438508321">"Ցուցադրված է <xliff:g id="MODE">%s</xliff:g> ստեղնաշարը"</string>
-    <string name="keyboard_mode_date" msgid="3137520166817128102">"ամսաթիվ"</string>
-    <string name="keyboard_mode_date_time" msgid="339593358488851072">"ամսաթիվ և ժամ"</string>
-    <string name="keyboard_mode_email" msgid="6216248078128294262">"էլփոստ"</string>
-    <string name="keyboard_mode_im" msgid="1137405089766557048">"նամակագրություն"</string>
-    <string name="keyboard_mode_number" msgid="7991623440699957069">"թվեր"</string>
-    <string name="keyboard_mode_phone" msgid="6851627527401433229">"հեռախոսահամար"</string>
-    <string name="keyboard_mode_text" msgid="6479436687899701619">"տեքստ"</string>
-    <string name="keyboard_mode_time" msgid="4381856885582143277">"ժամանակ"</string>
-    <string name="keyboard_mode_url" msgid="1519819835514911218">"URL"</string>
-    <string name="voice_input" msgid="3583258583521397548">"Ձայնային մուտքագրման ստեղն"</string>
-    <string name="voice_input_modes_main_keyboard" msgid="3360660341121083174">"Հիմնական ստեղնաշարի վրա"</string>
-    <string name="voice_input_modes_symbols_keyboard" msgid="7203213240786084067">"Նշանների ստեղնաշարի վրա"</string>
-    <string name="voice_input_modes_off" msgid="3745699748218082014">"Անջատված"</string>
-    <string name="voice_input_modes_summary_main_keyboard" msgid="6586544292900314339">"Բարձրախոս հիմնական ստեղնաշարի վրա"</string>
-    <string name="voice_input_modes_summary_symbols_keyboard" msgid="5233725927281932391">"Բարձրախոս նշանների ստեղնաշարի վրա"</string>
-    <string name="voice_input_modes_summary_off" msgid="63875609591897607">"Ձայնային մուտքագրումն անջատված է"</string>
-    <string name="configure_input_method" msgid="373356270290742459">"Կարգավորել մուտքագրման մեթոդները"</string>
-    <string name="language_selection_title" msgid="1651299598555326750">"Մուտքագրման լեզուներ"</string>
-    <string name="send_feedback" msgid="1780431884109392046">"Արձագանքել"</string>
-    <string name="select_language" msgid="3693815588777926848">"Մուտքագրման լեզուներ"</string>
-    <string name="hint_add_to_dictionary" msgid="573678656946085380">"Պահպանելու համար կրկին հպեք"</string>
-    <string name="has_dictionary" msgid="6071847973466625007">"Բառարանն առկա է"</string>
-    <string name="prefs_enable_log" msgid="6620424505072963557">"Միացնել օգտվողի արձագանքը"</string>
-    <string name="prefs_description_log" msgid="7525225584555429211">"Օգնել բարելավել այս մուտքագրման եղանակի խմբագրիչը՝ ինքնուրույն ուղարկելով Google-ին օգտագործման վիճակագրությունն ու վթարների հաշվետվությունները:"</string>
-    <string name="keyboard_layout" msgid="8451164783510487501">"Ստեղնաշարի թեման"</string>
-    <string name="subtype_en_GB" msgid="88170601942311355">"Անգլերեն (ՄԹ)"</string>
-    <string name="subtype_en_US" msgid="6160452336634534239">"Անգլերեն (ԱՄՆ)"</string>
-    <string name="subtype_es_US" msgid="5583145191430180200">"Իսպաներեն (ԱՄՆ)"</string>
-    <string name="subtype_with_layout_en_GB" msgid="2179097748724725906">"Անգլերեն (ՄԹ) (<xliff:g id="LAYOUT">%s</xliff:g>)"</string>
-    <string name="subtype_with_layout_en_US" msgid="1362581347576714579">"Անգլերեն (ԱՄՆ) (<xliff:g id="LAYOUT">%s</xliff:g>)"</string>
-    <string name="subtype_with_layout_es_US" msgid="6261791057007890189">"Իսպաներեն (ԱՄՆ) (<xliff:g id="LAYOUT">%s</xliff:g>)"</string>
-    <string name="subtype_no_language" msgid="7137390094240139495">"Ոչ մի լեզվով (Այբուբեն)"</string>
-    <string name="subtype_no_language_qwerty" msgid="244337630616742604">"Այբուբեն (QWERTY)"</string>
-    <string name="subtype_no_language_qwertz" msgid="443066912507547976">"Այբուբեն (QWERTZ)"</string>
-    <string name="subtype_no_language_azerty" msgid="8144348527575640087">"Այբուբեն (AZERTY)"</string>
-    <string name="subtype_no_language_dvorak" msgid="1564494667584718094">"Այբուբեն (Dvorak)"</string>
-    <string name="subtype_no_language_colemak" msgid="5837418400010302623">"Այբուբեն (Colemak)"</string>
-    <string name="subtype_no_language_pcqwerty" msgid="5354918232046200018">"Այբուբեն (PC)"</string>
-    <string name="custom_input_styles_title" msgid="8429952441821251512">"Մուտքագրման հատուկ ոճեր"</string>
-    <string name="add_style" msgid="6163126614514489951">"Ավելացնել ոճ"</string>
-    <string name="add" msgid="8299699805688017798">"Ավելացնել"</string>
-    <string name="remove" msgid="4486081658752944606">"Հեռացնել"</string>
-    <string name="save" msgid="7646738597196767214">"Պահել"</string>
-    <string name="subtype_locale" msgid="8576443440738143764">"Lեզու"</string>
-    <string name="keyboard_layout_set" msgid="4309233698194565609">"Դասավորություն"</string>
-    <string name="custom_input_style_note_message" msgid="8826731320846363423">"Մուտքագրման ձեր հատուկ ոճը պետք է միացված լինի նախքան դուք կսկսեք օգտագործել այն: Ցանկանո՞ւմ եք միացնել այն հիմա:"</string>
-    <string name="enable" msgid="5031294444630523247">"Միացնել"</string>
-    <string name="not_now" msgid="6172462888202790482">"Ոչ հիմա"</string>
-    <string name="custom_input_style_already_exists" msgid="8008728952215449707">"Մուտքագրման այսպիսի ոճ արդեն գոյություն ունի՝ <xliff:g id="INPUT_STYLE_NAME">%s</xliff:g>"</string>
-    <string name="prefs_usability_study_mode" msgid="1261130555134595254">"Հարմարավետության ուսումնասիրության ռեժիմ"</string>
-    <string name="prefs_key_longpress_timeout_settings" msgid="6102240298932897873">"Ստեղնի երկար սեղմման ուշացում"</string>
-    <string name="prefs_keypress_vibration_duration_settings" msgid="7918341459947439226">"Սեղմման թրթռոցի տևողություն"</string>
-    <string name="prefs_keypress_sound_volume_settings" msgid="6027007337036891623">"Սեղմման ձայնի բարձրությունը"</string>
-    <string name="prefs_read_external_dictionary" msgid="2588931418575013067">"Կարդալ արտաքին բառարանի ֆայլը"</string>
-    <string name="read_external_dictionary_no_files_message" msgid="4947420942224623792">"Ներբեռնումների թղթապանակում բառարանային ֆայլեր չկան"</string>
-    <string name="read_external_dictionary_multiple_files_title" msgid="7637749044265808628">"Ընտրեք բառարանային ֆայլը տեղադրման համար"</string>
-    <string name="read_external_dictionary_confirm_install_message" msgid="6898610163768980870">"Իրո՞ք ուզում եք տեղադրել այս ֆայլը <xliff:g id="LOCALE_NAME">%s</xliff:g>-ում:"</string>
-    <string name="error" msgid="8940763624668513648">"Տեղի է ունեցել սխալ"</string>
-    <string name="button_default" msgid="3988017840431881491">"Լռելյայնը"</string>
-    <string name="setup_welcome_title" msgid="6112821709832031715">"Բարի գալուստ <xliff:g id="APPLICATION_NAME">%s</xliff:g>"</string>
-    <string name="setup_welcome_additional_description" msgid="8150252008545768953">"Ժեստային մուտքագրմամբ"</string>
-    <string name="setup_start_action" msgid="8936036460897347708">"Սկսել"</string>
-    <string name="setup_next_action" msgid="371821437915144603">"Հաջորդ քայլը"</string>
-    <string name="setup_steps_title" msgid="6400373034871816182">"Տեղադրվում է <xliff:g id="APPLICATION_NAME">%s</xliff:g>-ը"</string>
-    <string name="setup_step1_title" msgid="3147967630253462315">"Միացնել <xliff:g id="APPLICATION_NAME">%s</xliff:g>-ը"</string>
-    <string name="setup_step1_instruction" msgid="2578631936624637241">"Խնդրում ենք ստուգել «<xliff:g id="APPLICATION_NAME">%s</xliff:g>»-ը ձեր Լեզվի &amp; մուտքագրման կարգավորումներում: Դա կլիազորի նրան գործարկվել ձեր սարքում:"</string>
-    <string name="setup_step1_finished_instruction" msgid="10761482004957994">"<xliff:g id="APPLICATION_NAME">%s</xliff:g>-ն արդեն միացված է ձեր Լեզվի &amp; մուտքագրման կարգավորումներում, ուստի այս քայլն արված է: Անցնել հաջորդին:"</string>
-    <string name="setup_step1_action" msgid="4366513534999901728">"Միացնել կարգավորումներից"</string>
-    <string name="setup_step2_title" msgid="6860725447906690594">"Փոխարկել <xliff:g id="APPLICATION_NAME">%s</xliff:g>-ին"</string>
-    <string name="setup_step2_instruction" msgid="9141481964870023336">"Հաջորդիվ, ընտրեք «<xliff:g id="APPLICATION_NAME">%s</xliff:g>»-ը որպես ձեր ակտիվ տեքստային մուտքագրման եղանակ:"</string>
-    <string name="setup_step2_action" msgid="1660330307159824337">"Փոխարկել մուտքագրման եղանակները"</string>
-    <string name="setup_step3_title" msgid="3154757183631490281">"Շնորհավորում ենք, դուք տեղադրեցիք բոլորը:"</string>
-    <string name="setup_step3_instruction" msgid="8025981829605426000">"Այժմ դուք կարող եք մուտքագրել ձեր բոլոր սիրելի հավելվածներում <xliff:g id="APPLICATION_NAME">%s</xliff:g>-ով:"</string>
-    <string name="setup_step3_action" msgid="600879797256942259">"Կարգավորել լրացուցիչ լեզուները"</string>
-    <string name="setup_finish_action" msgid="276559243409465389">"Ավարտված"</string>
-    <string name="show_setup_wizard_icon" msgid="5008028590593710830">"Ցույց տալ հավելվածի պատկերակը"</string>
-    <string name="show_setup_wizard_icon_summary" msgid="4119998322536880213">"Ցուցադրել հավելվածի պատկերակը թողարկչում"</string>
-    <string name="app_name" msgid="6320102637491234792">"Բառարանի մատակարար"</string>
-    <string name="dictionary_provider_name" msgid="3027315045397363079">"Բառարանի մատակարար"</string>
-    <string name="dictionary_service_name" msgid="6237472350693511448">"Բառարանի ծառայություն"</string>
-    <string name="download_description" msgid="6014835283119198591">"Տեղեկություններ բառարանների թարմացման մասին"</string>
-    <string name="dictionary_settings_title" msgid="8091417676045693313">"Ավելացնել բառարաններ"</string>
-    <string name="dictionary_install_over_metered_network_prompt" msgid="3587517870006332980">"Բառարանն առկա է"</string>
-    <string name="dictionary_settings_summary" msgid="5305694987799824349">"Բառարանների կարգավորումներ"</string>
-    <string name="user_dictionaries" msgid="3582332055892252845">"Օգտվողի բառարաններ"</string>
-    <string name="default_user_dict_pref_name" msgid="1625055720489280530">"Օգտվողի բառարան"</string>
-    <string name="dictionary_available" msgid="4728975345815214218">"Բառարանն առկա է"</string>
-    <string name="dictionary_downloading" msgid="2982650524622620983">"Այս պահին ներբեռնվում է"</string>
-    <string name="dictionary_installed" msgid="8081558343559342962">"Տեղադրված է"</string>
-    <string name="dictionary_disabled" msgid="8950383219564621762">"Տեղադրված է, անջատված է"</string>
-    <string name="cannot_connect_to_dict_service" msgid="9216933695765732398">"Բառարանային ծառայությանը միացման խնդիր կա"</string>
-    <string name="no_dictionaries_available" msgid="8039920716566132611">"Բառարաններ չկան"</string>
-    <string name="check_for_updates_now" msgid="8087688440916388581">"Թարմացնել"</string>
-    <string name="last_update" msgid="730467549913588780">"Վերջին անգամ թարմացվել է"</string>
-    <string name="message_updating" msgid="4457761393932375219">"Ստուգվում է թարմացումների առկայությունը"</string>
-    <string name="message_loading" msgid="8689096636874758814">"Բեռնվում է..."</string>
-    <string name="main_dict_description" msgid="3072821352793492143">"Հիմնական բառարան"</string>
-    <string name="cancel" msgid="6830980399865683324">"Չեղարկել"</string>
-    <string name="install_dict" msgid="180852772562189365">"Տեղադրել"</string>
-    <string name="cancel_download_dict" msgid="7843340278507019303">"Չեղարկել"</string>
-    <string name="delete_dict" msgid="756853268088330054">"Ջնջել"</string>
-    <string name="should_download_over_metered_prompt" msgid="2878629598667658845">"Ձեր բջջային սարքում ընտրված լեզվով առկա է բառարան:<br/> Խորհուրդ ենք տալիս &lt;b&gt;ներբեռնել&lt;/b&gt; <xliff:g id="LANGUAGE">%1$s</xliff:g> բառարանը ձեր մուտքագրման հմտությունների բարելավման համար:&lt;br/&gt; &lt;br/&gt; Ներբեռնումը կարող է խլել մեկ կամ երկու րոպե 3G-ի դեպքում: Հնարավոր է գանձում կատարվի, եթե դուք չունեք &lt;b&gt;տվյալների անսահմանափակ փաթեթ&lt;/b&gt;.&lt;br/&gt; Եթե դուք վստահ չեք, թե տվյալների որ փաթեթն ունեք, խորհուրդ ենք տալիս գտնել Wi-Fi կապ՝ ներբեռնումն ավտոմատ սկսելու համար:&lt;br/&gt; &lt;br/&gt; Հուշում. դուք կարող եք ներբեռնել և հեռացնել բառարաններ՝ գնալով ձեր բջջային սարքի &lt;b&gt;Կարգավորումներ ցանկի Լեզու &amp; մուտքագրման&lt;/b&gt; բաժինը:"</string>
-    <string name="download_over_metered" msgid="1643065851159409546">"Ներբեռնել հիմա (<xliff:g id="SIZE_IN_MEGABYTES">%1$.1f</xliff:g>Մբ)"</string>
-    <string name="do_not_download_over_metered" msgid="2176209579313941583">"Ներբեռնել Wi-Fi-ով"</string>
-    <string name="dict_available_notification_title" msgid="6514288591959117288">"<xliff:g id="LANGUAGE">%1$s</xliff:g>-ով առկա է մի բառարան"</string>
-    <string name="dict_available_notification_description" msgid="1075194169443163487">"Սեղմեք՝ վերանայելու և ներբեռնելու համար"</string>
-    <string name="toast_downloading_suggestions" msgid="1313027353588566660">"Ներբեռնվում է. <xliff:g id="LANGUAGE">%1$s</xliff:g>-ի համար առաջարկները շուտով պատրաստ կլինեն:"</string>
-    <string name="version_text" msgid="2715354215568469385">"Տարբերակ <xliff:g id="VERSION_NUMBER">%1$s</xliff:g>"</string>
-    <string name="user_dict_settings_add_menu_title" msgid="1254195365689387076">"Ավելացնել"</string>
-    <string name="user_dict_settings_add_dialog_title" msgid="4096700390211748168">"Ավելացնել բառարանում"</string>
-    <string name="user_dict_settings_add_screen_title" msgid="5818914331629278758">"Արտահայտություն"</string>
-    <string name="user_dict_settings_add_dialog_more_options" msgid="5671682004887093112">"Այլ ընտրանքներ"</string>
-    <string name="user_dict_settings_add_dialog_less_options" msgid="2716586567241724126">"Սակավ ընտրանքներ"</string>
-    <string name="user_dict_settings_add_dialog_confirm" msgid="4703129507388332950">"Լավ"</string>
-    <string name="user_dict_settings_add_word_option_name" msgid="6665558053408962865">"Բառը՝"</string>
-    <string name="user_dict_settings_add_shortcut_option_name" msgid="3094731590655523777">"Դյուրանցումը՝"</string>
-    <string name="user_dict_settings_add_locale_option_name" msgid="4738643440987277705">"Lեզուն՝"</string>
-    <string name="user_dict_settings_add_word_hint" msgid="4902434148985906707">"Մուտքագրեք բառը"</string>
-    <string name="user_dict_settings_add_shortcut_hint" msgid="2265453012555060178">"Ընտրովի դյուրանցում"</string>
-    <string name="user_dict_settings_edit_dialog_title" msgid="3765774633869590352">"Խմբագրել բառը"</string>
-    <string name="user_dict_settings_context_menu_edit_title" msgid="6812255903472456302">"Խմբագրել"</string>
-    <string name="user_dict_settings_context_menu_delete_title" msgid="8142932447689461181">"Ջնջել"</string>
-    <string name="user_dict_settings_empty_text" msgid="558499587532668203">"Դուք չունեք ոչ մի բառ օգտվողի բառարանում: Ավելացնել բառեր՝ հպելով Ավելացնել (+) կոճակը:"</string>
-    <string name="user_dict_settings_all_languages" msgid="8276126583216298886">"Բոլոր լեզուներով"</string>
-    <string name="user_dict_settings_more_languages" msgid="7131268499685180461">"Ավելի շատ լեզուներով..."</string>
-    <string name="user_dict_settings_delete" msgid="110413335187193859">"Ջնջել"</string>
-    <string name="user_dict_fast_scroll_alphabet" msgid="5431919401558285473">" ԱԲԳԴԵԶԷԸԹԺԻԼԽԾԿՀՁՂՃՄՅՆՇՈՉՊՋՌՍՎՏՐՑՈՒՓՔԵւՕՖ"</string>
-</resources>
diff --git a/java/res/values-in/strings.xml b/java/res/values-in/strings.xml
index 229cf85..c73c26c 100644
--- a/java/res/values-in/strings.xml
+++ b/java/res/values-in/strings.xml
@@ -143,6 +143,7 @@
     <string name="subtype_with_layout_en_GB" msgid="2179097748724725906">"Inggris (Inggris) (<xliff:g id="LAYOUT">%s</xliff:g>)"</string>
     <string name="subtype_with_layout_en_US" msgid="1362581347576714579">"Inggris (AS) (<xliff:g id="LAYOUT">%s</xliff:g>)"</string>
     <string name="subtype_with_layout_es_US" msgid="6261791057007890189">"Spanyol (AS) (<xliff:g id="LAYOUT">%s</xliff:g>)"</string>
+    <string name="subtype_nepali_traditional" msgid="9032247506728040447">"<xliff:g id="LANGUAGE">%s</xliff:g> (Tradisional)"</string>
     <string name="subtype_no_language" msgid="7137390094240139495">"Tidak ada bahasa (Abjad)"</string>
     <string name="subtype_no_language_qwerty" msgid="244337630616742604">"Abjad (QWERTY)"</string>
     <string name="subtype_no_language_qwertz" msgid="443066912507547976">"Abjad (QWERTZ)"</string>
@@ -150,6 +151,7 @@
     <string name="subtype_no_language_dvorak" msgid="1564494667584718094">"Abjad (Dvorak)"</string>
     <string name="subtype_no_language_colemak" msgid="5837418400010302623">"Abjad (Colemak)"</string>
     <string name="subtype_no_language_pcqwerty" msgid="5354918232046200018">"Abjad (PC)"</string>
+    <string name="subtype_emoji" msgid="7483586578074549196">"Emoji"</string>
     <string name="custom_input_styles_title" msgid="8429952441821251512">"Gaya masukan khusus"</string>
     <string name="add_style" msgid="6163126614514489951">"Tambah gaya"</string>
     <string name="add" msgid="8299699805688017798">"Tambahkan"</string>
diff --git a/java/res/values-it/strings.xml b/java/res/values-it/strings.xml
index 310ffee..eb8e1e9 100644
--- a/java/res/values-it/strings.xml
+++ b/java/res/values-it/strings.xml
@@ -143,6 +143,7 @@
     <string name="subtype_with_layout_en_GB" msgid="2179097748724725906">"Inglese (Regno Unito) (<xliff:g id="LAYOUT">%s</xliff:g>)"</string>
     <string name="subtype_with_layout_en_US" msgid="1362581347576714579">"Inglese (Stati Uniti) (<xliff:g id="LAYOUT">%s</xliff:g>)"</string>
     <string name="subtype_with_layout_es_US" msgid="6261791057007890189">"Spagnolo (USA) (<xliff:g id="LAYOUT">%s</xliff:g>)"</string>
+    <string name="subtype_nepali_traditional" msgid="9032247506728040447">"<xliff:g id="LANGUAGE">%s</xliff:g> (tradizionale)"</string>
     <string name="subtype_no_language" msgid="7137390094240139495">"Nessuna lingua (alfabeto)"</string>
     <string name="subtype_no_language_qwerty" msgid="244337630616742604">"Alfabeto (QWERTY)"</string>
     <string name="subtype_no_language_qwertz" msgid="443066912507547976">"Alfabeto (QWERTZ)"</string>
@@ -150,6 +151,7 @@
     <string name="subtype_no_language_dvorak" msgid="1564494667584718094">"Alfabeto (Dvorak)"</string>
     <string name="subtype_no_language_colemak" msgid="5837418400010302623">"Alfabeto (Colemak)"</string>
     <string name="subtype_no_language_pcqwerty" msgid="5354918232046200018">"Alfabeto (PC)"</string>
+    <string name="subtype_emoji" msgid="7483586578074549196">"Emoji"</string>
     <string name="custom_input_styles_title" msgid="8429952441821251512">"Stili personalizzati"</string>
     <string name="add_style" msgid="6163126614514489951">"Aggiungi stile"</string>
     <string name="add" msgid="8299699805688017798">"Aggiungi"</string>
diff --git a/java/res/values-iw/strings.xml b/java/res/values-iw/strings.xml
index d9421f2..e71bd31 100644
--- a/java/res/values-iw/strings.xml
+++ b/java/res/values-iw/strings.xml
@@ -143,6 +143,7 @@
     <string name="subtype_with_layout_en_GB" msgid="2179097748724725906">"אנגלית (בריטניה) (<xliff:g id="LAYOUT">%s</xliff:g>)"</string>
     <string name="subtype_with_layout_en_US" msgid="1362581347576714579">"אנגלית (ארה\"ב) (<xliff:g id="LAYOUT">%s</xliff:g>)"</string>
     <string name="subtype_with_layout_es_US" msgid="6261791057007890189">"ספרדית (ארצות הברית) (<xliff:g id="LAYOUT">%s</xliff:g>)"</string>
+    <string name="subtype_nepali_traditional" msgid="9032247506728040447">"<xliff:g id="LANGUAGE">%s</xliff:g> (מסורתית)"</string>
     <string name="subtype_no_language" msgid="7137390094240139495">"ללא שפה (אלף-בית)"</string>
     <string name="subtype_no_language_qwerty" msgid="244337630616742604">"אלף-בית (QWERTY)"</string>
     <string name="subtype_no_language_qwertz" msgid="443066912507547976">"אלף-בית (QWERTZ)"</string>
@@ -150,6 +151,7 @@
     <string name="subtype_no_language_dvorak" msgid="1564494667584718094">"אלף-בית (Dvorak)"</string>
     <string name="subtype_no_language_colemak" msgid="5837418400010302623">"אלף-בית (Colemak)"</string>
     <string name="subtype_no_language_pcqwerty" msgid="5354918232046200018">"אלף-בית (PC)"</string>
+    <string name="subtype_emoji" msgid="7483586578074549196">"אמוג\'י"</string>
     <string name="custom_input_styles_title" msgid="8429952441821251512">"סגנונות קלט מותאמים אישית"</string>
     <string name="add_style" msgid="6163126614514489951">"הוסף סגנון"</string>
     <string name="add" msgid="8299699805688017798">"הוסף"</string>
diff --git a/java/res/values-ja/strings.xml b/java/res/values-ja/strings.xml
index 5942206..4d3f155 100644
--- a/java/res/values-ja/strings.xml
+++ b/java/res/values-ja/strings.xml
@@ -143,6 +143,7 @@
     <string name="subtype_with_layout_en_GB" msgid="2179097748724725906">"英語 (英国) (<xliff:g id="LAYOUT">%s</xliff:g>)"</string>
     <string name="subtype_with_layout_en_US" msgid="1362581347576714579">"英語 (米国) (<xliff:g id="LAYOUT">%s</xliff:g>)"</string>
     <string name="subtype_with_layout_es_US" msgid="6261791057007890189">"スペイン語 (米国) (<xliff:g id="LAYOUT">%s</xliff:g>)"</string>
+    <string name="subtype_nepali_traditional" msgid="9032247506728040447">"<xliff:g id="LANGUAGE">%s</xliff:g>(伝統言語)"</string>
     <string name="subtype_no_language" msgid="7137390094240139495">"言語なし(アルファベット)"</string>
     <string name="subtype_no_language_qwerty" msgid="244337630616742604">"アルファベット(QWERTY)"</string>
     <string name="subtype_no_language_qwertz" msgid="443066912507547976">"アルファベット(QWERTZ)"</string>
@@ -150,6 +151,7 @@
     <string name="subtype_no_language_dvorak" msgid="1564494667584718094">"アルファベット(Dvorak)"</string>
     <string name="subtype_no_language_colemak" msgid="5837418400010302623">"アルファベット(Colemak)"</string>
     <string name="subtype_no_language_pcqwerty" msgid="5354918232046200018">"アルファベット(PC)"</string>
+    <string name="subtype_emoji" msgid="7483586578074549196">"絵文字"</string>
     <string name="custom_input_styles_title" msgid="8429952441821251512">"カスタム入力スタイル"</string>
     <string name="add_style" msgid="6163126614514489951">"スタイル追加"</string>
     <string name="add" msgid="8299699805688017798">"追加"</string>
diff --git a/java/res/values-ka-rGE/strings-appname.xml b/java/res/values-ka-rGE/strings-appname.xml
deleted file mode 100644
index 703c66a..0000000
--- a/java/res/values-ka-rGE/strings-appname.xml
+++ /dev/null
@@ -1,27 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!-- 
-/*
-**
-** Copyright 2013, 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:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="english_ime_name" msgid="5940510615957428904">"Android-ის კლავიატურა (AOSP)"</string>
-    <string name="spell_checker_service_name" msgid="1254221805440242662">"Android-ის მართლწერის შემმოწმებელი (AOSP)"</string>
-    <string name="english_ime_settings" msgid="5760361067176802794">"Android-ის კლავიატურის პარამეტრები (AOSP)"</string>
-    <string name="android_spell_checker_settings" msgid="6123949487832861885">"Android-ის მართლწერის შემმოწმებლის პარამეტრები (AOSP)"</string>
-</resources>
diff --git a/java/res/values-ka-rGE/strings.xml b/java/res/values-ka-rGE/strings.xml
index 860a395..043c14c 100644
--- a/java/res/values-ka-rGE/strings.xml
+++ b/java/res/values-ka-rGE/strings.xml
@@ -143,6 +143,7 @@
     <string name="subtype_with_layout_en_GB" msgid="2179097748724725906">"ინგლისური (გაერთ. სამ.) (<xliff:g id="LAYOUT">%s</xliff:g>)"</string>
     <string name="subtype_with_layout_en_US" msgid="1362581347576714579">"ინგლისური (აშშ) (<xliff:g id="LAYOUT">%s</xliff:g>)"</string>
     <string name="subtype_with_layout_es_US" msgid="6261791057007890189">"ესპანური (აშშ) (<xliff:g id="LAYOUT">%s</xliff:g>)"</string>
+    <string name="subtype_nepali_traditional" msgid="9032247506728040447">"<xliff:g id="LANGUAGE">%s</xliff:g> (ტრადიციული)"</string>
     <string name="subtype_no_language" msgid="7137390094240139495">"ენის გარეშე (ანბანი)"</string>
     <string name="subtype_no_language_qwerty" msgid="244337630616742604">"ანბანი (QWERTY)"</string>
     <string name="subtype_no_language_qwertz" msgid="443066912507547976">"ანბანი (QWERTZ)"</string>
@@ -150,6 +151,7 @@
     <string name="subtype_no_language_dvorak" msgid="1564494667584718094">"ანბანი (Dvorak)"</string>
     <string name="subtype_no_language_colemak" msgid="5837418400010302623">"ანბანი (Colemak)"</string>
     <string name="subtype_no_language_pcqwerty" msgid="5354918232046200018">"ანბანი (PC)"</string>
+    <string name="subtype_emoji" msgid="7483586578074549196">"სიცილაკები"</string>
     <string name="custom_input_styles_title" msgid="8429952441821251512">"შეყვანის სტილების კონფიგურაცია"</string>
     <string name="add_style" msgid="6163126614514489951">"სტილის დამატება"</string>
     <string name="add" msgid="8299699805688017798">"დამატება"</string>
diff --git a/java/res/values-ka/strings-appname.xml b/java/res/values-ka/strings-appname.xml
deleted file mode 100644
index 703c66a..0000000
--- a/java/res/values-ka/strings-appname.xml
+++ /dev/null
@@ -1,27 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!-- 
-/*
-**
-** Copyright 2013, 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:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="english_ime_name" msgid="5940510615957428904">"Android-ის კლავიატურა (AOSP)"</string>
-    <string name="spell_checker_service_name" msgid="1254221805440242662">"Android-ის მართლწერის შემმოწმებელი (AOSP)"</string>
-    <string name="english_ime_settings" msgid="5760361067176802794">"Android-ის კლავიატურის პარამეტრები (AOSP)"</string>
-    <string name="android_spell_checker_settings" msgid="6123949487832861885">"Android-ის მართლწერის შემმოწმებლის პარამეტრები (AOSP)"</string>
-</resources>
diff --git a/java/res/values-kk/strings-appname.xml b/java/res/values-kk/strings-appname.xml
deleted file mode 100644
index 1e201fa..0000000
--- a/java/res/values-kk/strings-appname.xml
+++ /dev/null
@@ -1,27 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!-- 
-/*
-**
-** Copyright 2013, 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:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="english_ime_name" msgid="5940510615957428904">"Android пернетақтасы (AOSP)"</string>
-    <string name="spell_checker_service_name" msgid="1254221805440242662">"Android емлені тексеру құралы (AOSP)"</string>
-    <string name="english_ime_settings" msgid="5760361067176802794">"Android пернетақтасының параметрлері (AOSP)"</string>
-    <string name="android_spell_checker_settings" msgid="6123949487832861885">"Android емлені тексеру құралының параметрлері (AOSP)"</string>
-</resources>
diff --git a/java/res/values-km-rKH/strings-appname.xml b/java/res/values-km-rKH/strings-appname.xml
deleted file mode 100644
index e7b2707..0000000
--- a/java/res/values-km-rKH/strings-appname.xml
+++ /dev/null
@@ -1,27 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!-- 
-/*
-**
-** Copyright 2013, 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:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="english_ime_name" msgid="5940510615957428904">"ក្ដារចុច Android (AOSP)"</string>
-    <string name="spell_checker_service_name" msgid="1254221805440242662">"កម្មវិធី​ពិនិត្យ​អក្ខរាវិរុទ្ធ Android  (AOSP​)"</string>
-    <string name="english_ime_settings" msgid="5760361067176802794">"ការ​កំណត់​ក្ដារ​ចុច Android (AOSP)"</string>
-    <string name="android_spell_checker_settings" msgid="6123949487832861885">"កំណត់​​កម្មវិធី​ពិនិត្យ​​អក្ខរាវិរុទ្ធ​សម្រាប់ ​​Android (AOSP)"</string>
-</resources>
diff --git a/java/res/values-km-rKH/strings.xml b/java/res/values-km-rKH/strings.xml
deleted file mode 100644
index 7ff2603..0000000
--- a/java/res/values-km-rKH/strings.xml
+++ /dev/null
@@ -1,242 +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.
-*/
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="english_ime_input_options" msgid="3909945612939668554">"ជម្រើស​​បញ្ចូល"</string>
-    <string name="english_ime_research_log" msgid="8492602295696577851">"ពាក្យ​បញ្ជា​កំណត់​ហេតុ​​ការ​ស្រាវជ្រាវ"</string>
-    <string name="use_contacts_for_spellchecking_option_title" msgid="5374120998125353898">"រក​មើល​ឈ្មោះ​ទំនាក់ទំនង"</string>
-    <string name="use_contacts_for_spellchecking_option_summary" msgid="8754413382543307713">"កម្មវិធី​ពិនិត្យ​អក្ខរាវិរុទ្ធ​ប្រើ​ធាតុ​ពី​​ក្នុង​បញ្ជី​ទំនាក់ទំនង​របស់​អ្នក"</string>
-    <string name="vibrate_on_keypress" msgid="5258079494276955460">"ញ័រ​នៅ​ពេល​ចុច​គ្រាប់ចុច"</string>
-    <string name="sound_on_keypress" msgid="6093592297198243644">"សំឡេង​នៅ​ពេល​ចុច​គ្រាប់ចុច"</string>
-    <string name="popup_on_keypress" msgid="123894815723512944">"លេច​ឡើង​នៅ​​ពេល​ចុច​គ្រាប់​ចុច"</string>
-    <string name="general_category" msgid="1859088467017573195">"ទូទៅ"</string>
-    <string name="correction_category" msgid="2236750915056607613">"ការ​កែ​អត្ថបទ"</string>
-    <string name="gesture_typing_category" msgid="497263612130532630">"បញ្ចូល​ដោយ​ប្រើ​កាយវិការ"</string>
-    <string name="misc_category" msgid="6894192814868233453">"ជម្រើស​ផ្សេងទៀត"</string>
-    <string name="advanced_settings" msgid="362895144495591463">"ការ​កំណត់​កម្រិត​ខ្ពស់"</string>
-    <string name="advanced_settings_summary" msgid="4487980456152830271">"ជម្រើស​សម្រាប់​អ្នក​ជំនាញ"</string>
-    <string name="include_other_imes_in_language_switch_list" msgid="4533689960308565519">"ប្ដូរ​ទៅ​​​វិធីសាស្ត្រ​បញ្ចូល​​​ផ្សេង​ទៀត"</string>
-    <string name="include_other_imes_in_language_switch_list_summary" msgid="840637129103317635">"គ្រាប់ចុច​ប្ដូរ​ភាសា​តាម​វិធីសាស្ត្រ​បញ្ចូល​ផ្សេងទៀត"</string>
-    <string name="show_language_switch_key" msgid="5915478828318774384">"គ្រាប់​ចុច​ប្ដូរ​​ភាសា"</string>
-    <string name="show_language_switch_key_summary" msgid="7343403647474265713">"បង្ហាញ​នៅ​ពេល​ដែល​បើក​ភាសា​បញ្ចូល​ច្រើន"</string>
-    <string name="sliding_key_input_preview" msgid="6604262359510068370">"បង្ហាញ​ទ្រនិច​បង្ហាញ​ស្លាយ"</string>
-    <string name="sliding_key_input_preview_summary" msgid="6340524345729093886">"បង្ហាញ​​សញ្ញា​មើល​​ឃើញ​ខណៈ​ពេល​ដែល​រុញ​ពី​ឆ្វេង ឬ​​គ្រាប់​ចុច​​និមិត្ត​សញ្ញា"</string>
-    <string name="key_preview_popup_dismiss_delay" msgid="6213164897443068248">"​សោ​លេចឡើង​បោះបង់​ការ​​ពន្យារពេល"</string>
-    <string name="key_preview_popup_dismiss_no_delay" msgid="2096123151571458064">"គ្មាន​ការ​ពន្យារពេល"</string>
-    <string name="key_preview_popup_dismiss_default_delay" msgid="2166964333903906734">"លំនាំដើម"</string>
-    <string name="abbreviation_unit_milliseconds" msgid="8700286094028323363">"<xliff:g id="MILLISECONDS">%s</xliff:g> មិល្លី​វិនាទី"</string>
-    <string name="settings_system_default" msgid="6268225104743331821">"លំនាំ​ដើម​​​ប្រព័ន្ធ"</string>
-    <string name="use_contacts_dict" msgid="4435317977804180815">"ស្នើ​ឈ្មោះ​ទំនាក់ទំនង"</string>
-    <string name="use_contacts_dict_summary" msgid="6599983334507879959">"ប្រើ​ឈ្មោះ​ពី​ទំនាក់ទំនង​សម្រាប់​ការ​​ស្នើ និង​​​កែ"</string>
-    <string name="use_double_space_period" msgid="8781529969425082860">"រយៈ​ពេល​ចុច​ដកឃ្លា​ពីរដង"</string>
-    <string name="use_double_space_period_summary" msgid="6532892187247952799">"ប៉ះ​ដកឃ្លា​ពីរ​​ដង​បញ្ចូល​​​រយៈ​ពេល​ដែល​អនុវត្ត​តាម​ដកឃ្លា"</string>
-    <string name="auto_cap" msgid="1719746674854628252">"ការ​សរសេរ​ជា​អក្សរ​ធំ​​ស្វ័យប្រវត្តិ"</string>
-    <string name="auto_cap_summary" msgid="7934452761022946874">"សរសេរ​ពាក្យ​ដំបូង​​​ជា​អក្សរ​ធំ​​នៃ​ប្រយោគ​នីមួយ​ៗ"</string>
-    <string name="edit_personal_dictionary" msgid="3996910038952940420">"វចនានុក្រម​ផ្ទាល់ខ្លួន"</string>
-    <string name="configure_dictionaries_title" msgid="4238652338556902049">"ផ្នែក​បន្ថែម​វចនានុក្រម"</string>
-    <string name="main_dictionary" msgid="4798763781818361168">"វចនានុក្រម​ចម្បង"</string>
-    <string name="prefs_show_suggestions" msgid="8026799663445531637">"បង្ហាញ​ការ​ស្នើ​​កែ"</string>
-    <string name="prefs_show_suggestions_summary" msgid="1583132279498502825">"បង្ហាញ​ពាក្យ​​បាន​​ផ្ដល់​​ស្នើ​​ខណៈ​ពេល​​​វាយ​បញ្ចូល"</string>
-    <string name="prefs_suggestion_visibility_show_name" msgid="3219916594067551303">"បង្ហាញ​ជា​និច្ច"</string>
-    <string name="prefs_suggestion_visibility_show_only_portrait_name" msgid="3859783767435239118">"បង្ហាញ​នៅ​ក្នុង​របៀប​បញ្ឈរ"</string>
-    <string name="prefs_suggestion_visibility_hide_name" msgid="6309143926422234673">"លាក់​ជានិច្ច"</string>
-    <string name="prefs_block_potentially_offensive_title" msgid="5078480071057408934">"ទប់ស្កាត់​​ពាក្យ​​បំពាន"</string>
-    <string name="prefs_block_potentially_offensive_summary" msgid="2371835479734991364">"កុំ​ស្នើ​ឲ្យ​ពាក្យ​បំពាន​មាន​សក្ដានុពល"</string>
-    <string name="auto_correction" msgid="7630720885194996950">"ការ​កែ​​​ស្វ័យប្រវត្តិ"</string>
-    <string name="auto_correction_summary" msgid="5625751551134658006">"ចន្លោះ​មិន​ឃើញ ​និង​សញ្ញា​​វណ្ណយុត្ត​កែ​ពាក្យ​ដែល​បាន​វាយ​ខុស​ស្វ័យប្រវត្តិ"</string>
-    <string name="auto_correction_threshold_mode_off" msgid="8470882665417944026">"បិទ"</string>
-    <string name="auto_correction_threshold_mode_modest" msgid="8788366690620799097">"ល្មម"</string>
-    <string name="auto_correction_threshold_mode_aggressive" msgid="7319007299148899623">"បំពាន"</string>
-    <string name="auto_correction_threshold_mode_very_aggressive" msgid="1853309024129480416">"បំពាន​ខ្លាំង"</string>
-    <string name="bigram_prediction" msgid="1084449187723948550">"ការ​ស្នើ​ពាក្យ​បន្ទាប់"</string>
-    <string name="bigram_prediction_summary" msgid="3896362682751109677">"ប្រើ​ពាក្យ​មុន​​នៅ​ពេល​ធ្វើ​ការ​​​ស្នើ"</string>
-    <string name="gesture_input" msgid="826951152254563827">"បើក​ការ​​បញ្ចូល​​កាយវិការ"</string>
-    <string name="gesture_input_summary" msgid="9180350639305731231">"បញ្ចូល​ពាក្យ​ដោយ​រំកិល​​​តាម​​អក្សរ"</string>
-    <string name="gesture_preview_trail" msgid="3802333369335722221">"បង្ហាញ​ដាន​កាយវិការ"</string>
-    <string name="gesture_floating_preview_text" msgid="4443240334739381053">"មើល​ការ​​អណ្ដែត​ដែល​មាន​ចលនា​ជា​មុន"</string>
-    <string name="gesture_floating_preview_text_summary" msgid="4472696213996203533">"​មើល​ពាក្យ​​​ដែល​បាន​ស្នើ​​​ខណៈ​ពេល​កំពុង​ធ្វើ​កាយ​វិការ"</string>
-    <string name="added_word" msgid="8993883354622484372">"បាន​រក្សាទុក <xliff:g id="WORD">%s</xliff:g> ៖"</string>
-    <string name="label_go_key" msgid="1635148082137219148">"ទៅ"</string>
-    <string name="label_next_key" msgid="362972844525672568">"បន្ទាប់"</string>
-    <string name="label_previous_key" msgid="1211868118071386787">"មុន"</string>
-    <string name="label_done_key" msgid="2441578748772529288">"រួចរាល់"</string>
-    <string name="label_send_key" msgid="2815056534433717444">"ផ្ញើ"</string>
-    <string name="label_pause_key" msgid="181098308428035340">"ផ្អាក"</string>
-    <string name="label_wait_key" msgid="6402152600878093134">"រង់ចាំ"</string>
-    <string name="spoken_use_headphones" msgid="896961781287283493">"ដោត​កាស ដើម្បី​ស្ដាប់​ពាក្យ​សម្ងាត់។"</string>
-    <string name="spoken_current_text_is" msgid="2485723011272583845">"អត្ថបទ​បច្ចុប្បន្ន​គឺ %s"</string>
-    <string name="spoken_no_text_entered" msgid="7479685225597344496">"គ្មាន​អត្ថបទ​​​បាន​បញ្ចូល"</string>
-    <string name="spoken_description_unknown" msgid="3197434010402179157">"កូដ​គ្រាប់​ចុច %d"</string>
-    <string name="spoken_description_shift" msgid="244197883292549308">"Shift"</string>
-    <string name="spoken_description_shift_shifted" msgid="1681877323344195035">"បើក Shift (​ប៉ះ​ដើម្បី​បិទ)"</string>
-    <string name="spoken_description_caps_lock" msgid="3276478269526304432">"បើក Caps lock (ប៉ះ​​ដើម្បី​បិទ)"</string>
-    <string name="spoken_description_delete" msgid="8740376944276199801">"Delete"</string>
-    <string name="spoken_description_to_symbol" msgid="5486340107500448969">"និមិត្ត​សញ្ញា"</string>
-    <string name="spoken_description_to_alpha" msgid="23129338819771807">"អក្សរ"</string>
-    <string name="spoken_description_to_numeric" msgid="591752092685161732">"លេខ"</string>
-    <string name="spoken_description_settings" msgid="4627462689603838099">"ការ​កំណត់"</string>
-    <string name="spoken_description_tab" msgid="2667716002663482248">"Tab"</string>
-    <string name="spoken_description_space" msgid="2582521050049860859">"ដកឃ្លា"</string>
-    <string name="spoken_description_mic" msgid="615536748882611950">"បញ្ចូលសំឡេង"</string>
-    <string name="spoken_description_smiley" msgid="2256309826200113918">"មុខ​ញញឹម"</string>
-    <string name="spoken_description_return" msgid="8178083177238315647">"Return"</string>
-    <string name="spoken_description_search" msgid="1247236163755920808">"ស្វែងរក"</string>
-    <string name="spoken_description_dot" msgid="40711082435231673">"Dot"</string>
-    <string name="spoken_description_language_switch" msgid="5507091328222331316">"ប្ដូរ​​ភាសា"</string>
-    <string name="spoken_description_action_next" msgid="8636078276664150324">"បន្ទាប់"</string>
-    <string name="spoken_description_action_previous" msgid="800872415009336208">"មុន"</string>
-    <string name="spoken_description_shiftmode_on" msgid="5700440798609574589">"បាន​បើក Shift"</string>
-    <string name="spoken_description_shiftmode_locked" msgid="593175803181701830">"បាន​បើក Caps lock"</string>
-    <string name="spoken_description_shiftmode_off" msgid="657219998449174808">"បាន​បិទ Shift"</string>
-    <string name="spoken_description_mode_symbol" msgid="7183343879909747642">"របៀប​និមិត្តសញ្ញា"</string>
-    <string name="spoken_description_mode_alpha" msgid="3528307674390156956">"របៀប​អក្សរ"</string>
-    <string name="spoken_description_mode_phone" msgid="6520207943132026264">"របៀប​ទូរស័ព្ទ"</string>
-    <string name="spoken_description_mode_phone_shift" msgid="5499629753962641227">"​របៀប​និមិត្ត​សញ្ញា​ទូរស័ព្ទ"</string>
-    <string name="announce_keyboard_hidden" msgid="8718927835531429807">"បាន​លាក់​ក្ដារចុច"</string>
-    <string name="announce_keyboard_mode" msgid="4729081055438508321">"បង្ហាញ​ក្ដារ​ចុច <xliff:g id="MODE">%s</xliff:g>"</string>
-    <string name="keyboard_mode_date" msgid="3137520166817128102">"កាលបរិច្ឆេទ"</string>
-    <string name="keyboard_mode_date_time" msgid="339593358488851072">"កាល​បរិច្ឆេទ​ និង​ពេល​វេលា"</string>
-    <string name="keyboard_mode_email" msgid="6216248078128294262">"អ៊ីមែល"</string>
-    <string name="keyboard_mode_im" msgid="1137405089766557048">"​ផ្ញើ​សារ"</string>
-    <string name="keyboard_mode_number" msgid="7991623440699957069">"លេខ"</string>
-    <string name="keyboard_mode_phone" msgid="6851627527401433229">"ទូរស័ព្ទ"</string>
-    <string name="keyboard_mode_text" msgid="6479436687899701619">"អត្ថបទ"</string>
-    <string name="keyboard_mode_time" msgid="4381856885582143277">"ពេលវេលា"</string>
-    <string name="keyboard_mode_url" msgid="1519819835514911218">"URL"</string>
-    <string name="voice_input" msgid="3583258583521397548">"គ្រាប់​ចុច​បញ្ចូល​​សំឡេង"</string>
-    <string name="voice_input_modes_main_keyboard" msgid="3360660341121083174">"នៅ​លើ​ក្ដារចុច​ចម្បង"</string>
-    <string name="voice_input_modes_symbols_keyboard" msgid="7203213240786084067">"នៅ​លើ​ក្ដារចុច​​និមិត្ត​សញ្ញា"</string>
-    <string name="voice_input_modes_off" msgid="3745699748218082014">"បិទ"</string>
-    <string name="voice_input_modes_summary_main_keyboard" msgid="6586544292900314339">"មីក្រូហ្វូន​នៅ​លើ​​ក្ដារចុច​ចម្បង"</string>
-    <string name="voice_input_modes_summary_symbols_keyboard" msgid="5233725927281932391">"មីក្រូហ្វូន​នៅ​លើ​​ក្ដារចុច​និមិត្ត​សញ្ញា"</string>
-    <string name="voice_input_modes_summary_off" msgid="63875609591897607">"បាន​បិទ​ការ​បញ្ចូល​សំឡេង"</string>
-    <string name="configure_input_method" msgid="373356270290742459">"កំណត់​រចនាសម្ព័ន្ធ​វិធីសាស្ត្រ​បញ្ចូល"</string>
-    <string name="language_selection_title" msgid="1651299598555326750">"បញ្ចូល​ភាសា"</string>
-    <string name="send_feedback" msgid="1780431884109392046">"ផ្ញើ​មតិ​អ្នក​ប្រើ"</string>
-    <string name="select_language" msgid="3693815588777926848">"​​បញ្ចូល​ភាសា"</string>
-    <string name="hint_add_to_dictionary" msgid="573678656946085380">"ប៉ះ​ម្ដង​ទៀត​ ដើម្បី​រក្សា​ទុក"</string>
-    <string name="has_dictionary" msgid="6071847973466625007">"មាន​វចនានុក្រម"</string>
-    <string name="prefs_enable_log" msgid="6620424505072963557">"បើក​មតិត្រឡប់"</string>
-    <string name="prefs_description_log" msgid="7525225584555429211">"ជំនួយ​​​ធ្វើ​ឲ្យ​​ប្រសើរ​ឡើង​​នៃ​កម្មវិធី​កែ​​វិធី​សាស្ត្រ​​បញ្ចូល​ដោយ​ស្វ័យ​ប្រវត្តិ​ ដោយ​ផ្ញើ​ស្ថិតិ​​ប្រើ​ប្រាស់​ ​និង​របាយការណ៍​គាំង"</string>
-    <string name="keyboard_layout" msgid="8451164783510487501">"រូបរាង​ក្ដារចុច"</string>
-    <string name="subtype_en_GB" msgid="88170601942311355">"អង់គ្លេស (​អង់គ្លេស)"</string>
-    <string name="subtype_en_US" msgid="6160452336634534239">"អង់គ្លេស (សហរដ្ឋ​អាមេរិក)"</string>
-    <string name="subtype_es_US" msgid="5583145191430180200">"អេស្ប៉ាញ (សហរដ្ឋ​អាមេរិក​)"</string>
-    <string name="subtype_with_layout_en_GB" msgid="2179097748724725906">"អង់គ្លេស (ចក្រភព​អង់គ្លេស) (<xliff:g id="LAYOUT">%s</xliff:g>)"</string>
-    <string name="subtype_with_layout_en_US" msgid="1362581347576714579">"អង់គ្លេស (អាមេរិក) (<xliff:g id="LAYOUT">%s</xliff:g>)"</string>
-    <string name="subtype_with_layout_es_US" msgid="6261791057007890189">"អេស្ប៉ាញ (អាមេរិក​) ( <xliff:g id="LAYOUT">%s</xliff:g> )"</string>
-    <string name="subtype_no_language" msgid="7137390094240139495">"គ្មាន​ភាសា (អក្សរ​ក្រម)"</string>
-    <string name="subtype_no_language_qwerty" msgid="244337630616742604">"តាម​លំដាប់​អក្សរក្រម (QWERTY)"</string>
-    <string name="subtype_no_language_qwertz" msgid="443066912507547976">"តាម​លំដាប់​អក្សរក្រម (QWERTZ)"</string>
-    <string name="subtype_no_language_azerty" msgid="8144348527575640087">"តាម​លំដាប់​អក្សរក្រម (AZERTY)"</string>
-    <string name="subtype_no_language_dvorak" msgid="1564494667584718094">"តាម​លំដាប់​អក្សរក្រម (Dvorak)"</string>
-    <string name="subtype_no_language_colemak" msgid="5837418400010302623">"តាម​លំដាប់​អក្សរក្រម (Colemak)"</string>
-    <string name="subtype_no_language_pcqwerty" msgid="5354918232046200018">"តាម​លំដាប់​អក្សរក្រម (កុំព្យូទ័រ)"</string>
-    <string name="custom_input_styles_title" msgid="8429952441821251512">"រចនាប័ទ្ម​បញ្ចូល​ផ្ទាល់ខ្លួន"</string>
-    <string name="add_style" msgid="6163126614514489951">"បន្ថែម​រចនាប័ទ្ម"</string>
-    <string name="add" msgid="8299699805688017798">"បន្ថែម"</string>
-    <string name="remove" msgid="4486081658752944606">"លុប​ចេញ"</string>
-    <string name="save" msgid="7646738597196767214">"រក្សាទុក"</string>
-    <string name="subtype_locale" msgid="8576443440738143764">"ភាសា"</string>
-    <string name="keyboard_layout_set" msgid="4309233698194565609">"ប្លង់"</string>
-    <string name="custom_input_style_note_message" msgid="8826731320846363423">"ចាំបាច់​ត្រូវ​បើក​រចនាប័ទ្ម​បញ្ចូល​ផ្ទាល់​ខ្លួន​របស់​អ្នក មុន​ពេល​អ្នក​ចាប់ផ្ដើម​ប្រើ​វា។ តើ​អ្នក​ចង់​បើក​វា​ឥឡូវ​នេះ​ឬ?"</string>
-    <string name="enable" msgid="5031294444630523247">"បើក"</string>
-    <string name="not_now" msgid="6172462888202790482">"មិនមែន​ឥឡូវ"</string>
-    <string name="custom_input_style_already_exists" msgid="8008728952215449707">"មាន​រចនាប័ទ្ម​បញ្ចូល​ដូច​គ្នា​ដូច​ហើយ៖ <xliff:g id="INPUT_STYLE_NAME">%s</xliff:g>"</string>
-    <string name="prefs_usability_study_mode" msgid="1261130555134595254">"របៀប​ការ​សិក្សា​ដែល​អាច​ប្រើ​បាន"</string>
-    <string name="prefs_key_longpress_timeout_settings" msgid="6102240298932897873">"ពន្យារពេល​​​ចុច​គ្រាប់​ចុច​ឲ្យ​​យូរ"</string>
-    <string name="prefs_keypress_vibration_duration_settings" msgid="7918341459947439226">"ថិរវេលា​​ញ័រ​​ពេល​ចុច​គ្រាប់ចុច"</string>
-    <string name="prefs_keypress_sound_volume_settings" msgid="6027007337036891623">"កម្រិត​សំឡេង​ពេល​ចុច​គ្រាប់​ចុច"</string>
-    <string name="prefs_read_external_dictionary" msgid="2588931418575013067">"អាន​ឯកសារ​វចនានុក្រម​ខាង​ក្រៅ"</string>
-    <string name="read_external_dictionary_no_files_message" msgid="4947420942224623792">"គ្មាន​ឯកសារ​វចនានុក្រម​នៅ​ក្នុង​ថត​ទាញ​យក"</string>
-    <string name="read_external_dictionary_multiple_files_title" msgid="7637749044265808628">"ជ្រើស​ឯកសារ​វចនានុក្រម​ ដើម្បី​ដំឡើង"</string>
-    <string name="read_external_dictionary_confirm_install_message" msgid="6898610163768980870">"ពិត​ជា​ដំឡើង​ឯកសារ​នេះ​សម្រាប់ <xliff:g id="LOCALE_NAME">%s</xliff:g> ឬ?"</string>
-    <string name="error" msgid="8940763624668513648">"មាន​កំហុស"</string>
-    <string name="button_default" msgid="3988017840431881491">"លំនាំដើម"</string>
-    <string name="setup_welcome_title" msgid="6112821709832031715">"សូម​ស្វាគមន៍​មក​កាន់ <xliff:g id="APPLICATION_NAME">%s</xliff:g>"</string>
-    <string name="setup_welcome_additional_description" msgid="8150252008545768953">"ជាមួយ​​​ការ​វាយ​បញ្ចូល​ដោយ​ប្រើ​​​កាយវិការ"</string>
-    <string name="setup_start_action" msgid="8936036460897347708">"បាន​ចាប់ផ្ដើម"</string>
-    <string name="setup_next_action" msgid="371821437915144603">"ជំហាន​បន្ទាប់"</string>
-    <string name="setup_steps_title" msgid="6400373034871816182">"រៀបចំ <xliff:g id="APPLICATION_NAME">%s</xliff:g>"</string>
-    <string name="setup_step1_title" msgid="3147967630253462315">"បើក <xliff:g id="APPLICATION_NAME">%s</xliff:g>"</string>
-    <string name="setup_step1_instruction" msgid="2578631936624637241">"សូម​ពិនិត្យ​ \"<xliff:g id="APPLICATION_NAME">%s</xliff:g>\" នៅ​ក្នុង​ការ​កំណត់​ភាសា &amp; និង​ការ​បញ្ចូល​របស់​អ្នក។ វា​នឹង​ដំណើរការ​នៅ​លើ​ឧបករណ៍​របស់​អ្នក។"</string>
-    <string name="setup_step1_finished_instruction" msgid="10761482004957994">"<xliff:g id="APPLICATION_NAME">%s</xliff:g>បាន​បើក​នៅ​ក្នុង​​ការ​កំណត់​​ភាសា​ &amp; ការ​បញ្ចូល​របស់ ដូច្នេះ​ជំហាន​នេះ​រួចរាល់​ហើយ។ បន្ត​ទៅ​ជំហាន​បន្ទាប់!"</string>
-    <string name="setup_step1_action" msgid="4366513534999901728">"បើក​នៅ​ក្នុង​ការ​កំណត់"</string>
-    <string name="setup_step2_title" msgid="6860725447906690594">"ប្ដូរ​ទៅ <xliff:g id="APPLICATION_NAME">%s</xliff:g>"</string>
-    <string name="setup_step2_instruction" msgid="9141481964870023336">"បន្ទាប់ ជ្រើស \"<xliff:g id="APPLICATION_NAME">%s</xliff:g>\" ជា​វិធី​សាស្ត្រ​បញ្ចូល​អត្ថបទ​សកម្ម​របស់​អ្នក។"</string>
-    <string name="setup_step2_action" msgid="1660330307159824337">"ប្ដូរ​វិធីសាស្ត្រ​បញ្ចូល"</string>
-    <string name="setup_step3_title" msgid="3154757183631490281">"សូម​អបអរ​សាទរ,​ អ្នក​​បាន​កំណត់​​រួចរាល់​ហើយ!"</string>
-    <string name="setup_step3_instruction" msgid="8025981829605426000">"ឥឡូវ​​អ្នក​អាច​​វាយ​បញ្ចូល​នៅ​ក្នុង​​កម្មវិធី​​ពេញ​ចិត្ត​របស់​អ្នក​ទាំងអស់​ជាមួយ <xliff:g id="APPLICATION_NAME">%s</xliff:g> ។"</string>
-    <string name="setup_step3_action" msgid="600879797256942259">"កំណត់​រចនា​សម្ព័ន្ធ​ភាសា​បន្ថែម"</string>
-    <string name="setup_finish_action" msgid="276559243409465389">"បាន​បញ្ចប់"</string>
-    <string name="show_setup_wizard_icon" msgid="5008028590593710830">"បង្ហាញ​រូប​តំណាង​កម្មវិធី"</string>
-    <string name="show_setup_wizard_icon_summary" msgid="4119998322536880213">"បង្ហាញ​រូប​តំណាង​កម្មវិធី​នៅ​ក្នុង​កម្ម​​វិធី​ចាប់ផ្ដើម"</string>
-    <string name="app_name" msgid="6320102637491234792">"កម្មវិធី​ផ្ដល់​វចនានុក្រម"</string>
-    <string name="dictionary_provider_name" msgid="3027315045397363079">"កម្មវិធី​ផ្ដល់​វចនានុក្រម"</string>
-    <string name="dictionary_service_name" msgid="6237472350693511448">"សេវាកម្ម​​វចនានុក្រម"</string>
-    <string name="download_description" msgid="6014835283119198591">"ព័ត៌មាន​បច្ចុប្បន្នភាព​វចនានុក្រម"</string>
-    <string name="dictionary_settings_title" msgid="8091417676045693313">"ផ្នែក​បន្ថែម​វចនានុក្រម"</string>
-    <string name="dictionary_install_over_metered_network_prompt" msgid="3587517870006332980">"វចនានុក្រម​​​​​អាច​ប្រើ​បាន"</string>
-    <string name="dictionary_settings_summary" msgid="5305694987799824349">"ការ​កំណត់​សម្រាប់​វចនានុក្រម"</string>
-    <string name="user_dictionaries" msgid="3582332055892252845">"វចនានុក្រម​​​អ្នក​ប្រើ"</string>
-    <string name="default_user_dict_pref_name" msgid="1625055720489280530">"វចនានុក្រម​អ្នកប្រើ"</string>
-    <string name="dictionary_available" msgid="4728975345815214218">"វចនានុក្រម​​​អាច​ប្រើ​បាន"</string>
-    <string name="dictionary_downloading" msgid="2982650524622620983">"បច្ចុប្បន្ន​កំពុង​ទាញ​យក"</string>
-    <string name="dictionary_installed" msgid="8081558343559342962">"បាន​ដំឡើង"</string>
-    <string name="dictionary_disabled" msgid="8950383219564621762">"បាន​ដំឡើង បាន​បិទ"</string>
-    <string name="cannot_connect_to_dict_service" msgid="9216933695765732398">"មាន​បញ្ហា​ក្នុង​ការ​​ភ្ជាប់​ទៅ​​សេវា​កម្ម​វចនានុក្រម"</string>
-    <string name="no_dictionaries_available" msgid="8039920716566132611">"គ្មាន​វចនានុក្រម"</string>
-    <string name="check_for_updates_now" msgid="8087688440916388581">"ផ្ទុក​ឡើងវិញ"</string>
-    <string name="last_update" msgid="730467549913588780">"បាន​ធ្វើ​បច្ចុប្បន្នភាព​ចុងក្រោយ"</string>
-    <string name="message_updating" msgid="4457761393932375219">"ពិនិត្យមើល​បច្ចុប្បន្នភាព"</string>
-    <string name="message_loading" msgid="8689096636874758814">"កំពុង​ផ្ទុក..."</string>
-    <string name="main_dict_description" msgid="3072821352793492143">"វចនានុក្រម​ចម្បង"</string>
-    <string name="cancel" msgid="6830980399865683324">"បោះ​បង់"</string>
-    <string name="install_dict" msgid="180852772562189365">"ដំឡើង"</string>
-    <string name="cancel_download_dict" msgid="7843340278507019303">"បោះ​បង់"</string>
-    <string name="delete_dict" msgid="756853268088330054">"លុប"</string>
-    <string name="should_download_over_metered_prompt" msgid="2878629598667658845">"ភាសា​ដែល​បាន​ជ្រើស​នៅ​លើ​ឧបករណ៍​របស់​អ្នក​មាន​វចនានុក្រម។ &lt;br/&gt; យើង​បាន​ផ្ដល់​អនុសាសន៍ &lt;b&gt;ទាញ​យក​&lt;/b&gt;  <xliff:g id="LANGUAGE">%1$s</xliff:g> វចនានុក្រម ដើម្បី​ធ្វើ​ឲ្យ​ការ​វាយ​បញ្ចូល​របស់​អ្នក​ប្រសើរ​ឡើង។ &lt;br/&gt; &lt;br/&gt; ការ​ទាញ​យក​អាច​ចំណាយ​ពេល​​មួយ ឬ​ពីរ​នាទី​​​តាម 3G ។ ការ​​កាត់​លុយ​អាច​អនុវត្ត​ ប្រសិន​​​បើ​អ្នក​​បាន​​ &lt;b&gt;កំណត់​ទិន្នន័យ​គ្មាន​ដែន​កំណត់ &lt;/b&gt;.&lt;br/&gt; ប្រសិនបើ​​អ្នក​មិន​ប្រាកដ​​ថា​ទិន្នន័យ​អ្នក​​មិន​បាន​​កំណត់ យើង​បាន​ផ្ដល់​អនុសាសន៍​ដោយ​ស្វែងរក​ការ​ភ្ជាប់​​វ៉ាយហ្វាយ ដើម្បី​ចាប់ផ្ដើម​ទាញ​យក​ដោយ​ស្វ័យប្រវត្តិ។&lt;br/&gt; &lt;br/&gt; ព័ត៌មាន​ជំនួយ៖ អ្នក​អាច​ទាញ​យក និង​លុប​​វចនានុក្រម​​ដោយ​ចូល​ទៅ​ &lt;b&gt;ភាសា&amp; បញ្ចូល&lt;/b&gt;​នៅ​ក្នុង​ម៉ឺនុយ &lt;b&gt;ការ​កំណត់ &lt;/b&gt; របស់​ឧបករណ៍​ចល័ត។"</string>
-    <string name="download_over_metered" msgid="1643065851159409546">"ទាញ​យក​ឥឡូវ​នេះ (<xliff:g id="SIZE_IN_MEGABYTES">%1$.1f</xliff:g> មេកាបៃ)"</string>
-    <string name="do_not_download_over_metered" msgid="2176209579313941583">"ទាញ​យក​តាម​វ៉ាយហ្វាយ"</string>
-    <string name="dict_available_notification_title" msgid="6514288591959117288">"វចនានុក្រម​​អាច​ប្រើ​បាន​​សម្រាប់ <xliff:g id="LANGUAGE">%1$s</xliff:g>"</string>
-    <string name="dict_available_notification_description" msgid="1075194169443163487">"ចុច​ ដើម្បី​ពិនិត្យ​មើល​ឡើង​​វិញ​ និង​ទាញ​យក"</string>
-    <string name="toast_downloading_suggestions" msgid="1313027353588566660">"ទាញ​យក៖ ការ​​ស្នើ​សម្រាប់ <xliff:g id="LANGUAGE">%1$s</xliff:g> នឹង​បញ្ចប់​ឆាប់ៗ។"</string>
-    <string name="version_text" msgid="2715354215568469385">"កំណែ <xliff:g id="VERSION_NUMBER">%1$s</xliff:g>"</string>
-    <string name="user_dict_settings_add_menu_title" msgid="1254195365689387076">"បន្ថែម"</string>
-    <string name="user_dict_settings_add_dialog_title" msgid="4096700390211748168">"បន្ថែម​ទៅ​វចនានុក្រម"</string>
-    <string name="user_dict_settings_add_screen_title" msgid="5818914331629278758">"ឃ្លា"</string>
-    <string name="user_dict_settings_add_dialog_more_options" msgid="5671682004887093112">"ជម្រើស​ច្រើន"</string>
-    <string name="user_dict_settings_add_dialog_less_options" msgid="2716586567241724126">"ជម្រើស​តិច"</string>
-    <string name="user_dict_settings_add_dialog_confirm" msgid="4703129507388332950">"យល់ព្រម"</string>
-    <string name="user_dict_settings_add_word_option_name" msgid="6665558053408962865">"ពាក្យ៖"</string>
-    <string name="user_dict_settings_add_shortcut_option_name" msgid="3094731590655523777">"ផ្លូវកាត់​៖"</string>
-    <string name="user_dict_settings_add_locale_option_name" msgid="4738643440987277705">"ភាសា៖"</string>
-    <string name="user_dict_settings_add_word_hint" msgid="4902434148985906707">"វាយ​បញ្ចូល​ពាក្យ"</string>
-    <string name="user_dict_settings_add_shortcut_hint" msgid="2265453012555060178">"ផ្លូវកាត់​ជា​ជម្រើស"</string>
-    <string name="user_dict_settings_edit_dialog_title" msgid="3765774633869590352">"កែ​ពាក្យ"</string>
-    <string name="user_dict_settings_context_menu_edit_title" msgid="6812255903472456302">"កែ"</string>
-    <string name="user_dict_settings_context_menu_delete_title" msgid="8142932447689461181">"លុប"</string>
-    <string name="user_dict_settings_empty_text" msgid="558499587532668203">"អ្នក​មិន​មាន​ពាក្យ​ណាមួយ​នៅ​ក្នុង​វចនានុក្រម​អ្នក​ប្រើ​ទេ។ បន្ថែម​ពាក្យ​ដោយ​​​​ប៉ះ​ប៊ូតុង​ បន្ថែម ​ (+)។"</string>
-    <string name="user_dict_settings_all_languages" msgid="8276126583216298886">"សម្រាប់​ភាសា​ទាំងអស់"</string>
-    <string name="user_dict_settings_more_languages" msgid="7131268499685180461">"ភាសា​ច្រើន​ទៀត…"</string>
-    <string name="user_dict_settings_delete" msgid="110413335187193859">"លុប"</string>
-    <string name="user_dict_fast_scroll_alphabet" msgid="5431919401558285473">" ABCDEFGHIJKLMNOPQRSTUVWXYZ"</string>
-</resources>
diff --git a/java/res/xml-sw768dp/kbd_more_keys_keyboard_template.xml b/java/res/values-km/donottranslate.xml
similarity index 68%
copy from java/res/xml-sw768dp/kbd_more_keys_keyboard_template.xml
copy to java/res/values-km/donottranslate.xml
index f89a0a6..a9893fe 100644
--- a/java/res/xml-sw768dp/kbd_more_keys_keyboard_template.xml
+++ b/java/res/values-km/donottranslate.xml
@@ -2,7 +2,7 @@
 <!--
 /*
 **
-** Copyright 2008, The Android Open Source Project
+** Copyright 2013, 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.
@@ -17,10 +17,7 @@
 ** limitations under the License.
 */
 -->
-
-<Keyboard xmlns:latin="http://schemas.android.com/apk/res/com.android.inputmethod.latin"
-    latin:keyWidth="5.0%p"
-    latin:rowHeight="@dimen/popup_key_height"
-    style="?attr/moreKeysKeyboardStyle"
-    >
-</Keyboard>
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <!-- Whether this language uses spaces between words -->
+    <bool name="current_language_has_spaces">false</bool>
+</resources>
diff --git a/java/res/values-km/strings.xml b/java/res/values-km/strings.xml
deleted file mode 100644
index 03b9738..0000000
--- a/java/res/values-km/strings.xml
+++ /dev/null
@@ -1,242 +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.
-*/
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="english_ime_input_options" msgid="3909945612939668554">"ជម្រើស​ការ​បញ្ចូល"</string>
-    <string name="english_ime_research_log" msgid="8492602295696577851">"ពាក្យ​បញ្ជា​កំណត់​ហេតុ​​ការ​ស្រាវជ្រាវ"</string>
-    <string name="use_contacts_for_spellchecking_option_title" msgid="5374120998125353898">"រក​មើល​ឈ្មោះ​ទំនាក់ទំនង"</string>
-    <string name="use_contacts_for_spellchecking_option_summary" msgid="8754413382543307713">"កម្មវិធី​ពិនិត្យ​អក្ខរាវិរុទ្ធ​ប្រើ​ធាតុ​ពី​​ក្នុង​បញ្ជី​ទំនាក់ទំនង​របស់​អ្នក"</string>
-    <string name="vibrate_on_keypress" msgid="5258079494276955460">"ញ័រ​នៅ​ពេល​ចុច​គ្រាប់ចុច"</string>
-    <string name="sound_on_keypress" msgid="6093592297198243644">"សំឡេង​នៅ​ពេល​ចុច​គ្រាប់ចុច"</string>
-    <string name="popup_on_keypress" msgid="123894815723512944">"លេច​ឡើង​នៅ​​ពេល​ចុច​គ្រាប់​ចុច"</string>
-    <string name="general_category" msgid="1859088467017573195">"ទូទៅ"</string>
-    <string name="correction_category" msgid="2236750915056607613">"ការ​កែ​អត្ថបទ"</string>
-    <string name="gesture_typing_category" msgid="497263612130532630">"វាយ​ដោយ​ប្រើ​កាយវិការ"</string>
-    <string name="misc_category" msgid="6894192814868233453">"ជម្រើស​ផ្សេងទៀត"</string>
-    <string name="advanced_settings" msgid="362895144495591463">"ការ​កំណត់​កម្រិត​ខ្ពស់"</string>
-    <string name="advanced_settings_summary" msgid="4487980456152830271">"ជម្រើស​សម្រាប់​អ្នក​ជំនាញ"</string>
-    <string name="include_other_imes_in_language_switch_list" msgid="4533689960308565519">"ប្ដូរ​ទៅ​​​វិធីសាស្ត្រ​បញ្ចូល​​​ផ្សេង​ទៀត"</string>
-    <string name="include_other_imes_in_language_switch_list_summary" msgid="840637129103317635">"គ្រាប់ចុច​ប្ដូរ​ភាសា​តាម​វិធីសាស្ត្រ​បញ្ចូល​ផ្សេងទៀត"</string>
-    <string name="show_language_switch_key" msgid="5915478828318774384">"គ្រាប់​ចុច​ប្ដូរ​​ភាសា"</string>
-    <string name="show_language_switch_key_summary" msgid="7343403647474265713">"បង្ហាញ​នៅ​ពេល​ដែល​បើក​ភាសា​បញ្ចូល​ច្រើន"</string>
-    <string name="sliding_key_input_preview" msgid="6604262359510068370">"បង្ហាញ​ទ្រនិច​បង្ហាញ​ស្លាយ"</string>
-    <string name="sliding_key_input_preview_summary" msgid="6340524345729093886">"បង្ហាញ​អត្ថបទ​ដែល​មើល​ឃើញ​ខណៈ​ពេល​ដែល sliding ពី​គ្រាប់ចុច​ប្ដូរ​ឬ​និមិត្ត​សញ្ញា"</string>
-    <string name="key_preview_popup_dismiss_delay" msgid="6213164897443068248">"កូនសោ​លេចឡើង​បោះបង់​ការ​​ពន្យារពេល"</string>
-    <string name="key_preview_popup_dismiss_no_delay" msgid="2096123151571458064">"គ្មាន​ការ​ពន្យារពេល"</string>
-    <string name="key_preview_popup_dismiss_default_delay" msgid="2166964333903906734">"លំនាំដើម"</string>
-    <string name="abbreviation_unit_milliseconds" msgid="8700286094028323363">"<xliff:g id="MILLISECONDS">%s</xliff:g>ms"</string>
-    <string name="settings_system_default" msgid="6268225104743331821">"លំនាំ​ដើម​​​ប្រព័ន្ធ"</string>
-    <string name="use_contacts_dict" msgid="4435317977804180815">"ស្នើ​ឈ្មោះ​ទំនាក់ទំនង"</string>
-    <string name="use_contacts_dict_summary" msgid="6599983334507879959">"ប្រើ​ឈ្មោះ​ពី​ទំនាក់ទំនង​សម្រាប់​ការ​​ស្នើ និង​ការ​កែ"</string>
-    <string name="use_double_space_period" msgid="8781529969425082860">"រយៈ​ពេល​ចុច space ពីរដង"</string>
-    <string name="use_double_space_period_summary" msgid="6532892187247952799">"ចុច​ tap ពីរ​​ដង​លើ​ spacebar រយៈ​ពេល​​បញ្ចូល​​​ដែល​បាន​អនុវត្ត​ដោយ​ចុច space"</string>
-    <string name="auto_cap" msgid="1719746674854628252">"ការ​សរសេរ​ជា​អក្សរ​ធំ​​ស្វ័យប្រវត្តិ"</string>
-    <string name="auto_cap_summary" msgid="7934452761022946874">"សរសេរ​ពាក្យ​ដំបូង​​​ជា​អក្សរ​ធំ​​នៃ​ប្រយោគ​នីមួយ"</string>
-    <string name="edit_personal_dictionary" msgid="3996910038952940420">"វចនានុក្រម​ផ្ទាល់ខ្លួន"</string>
-    <string name="configure_dictionaries_title" msgid="4238652338556902049">"ផ្នែក​បន្ថែម​វចនានុក្រម"</string>
-    <string name="main_dictionary" msgid="4798763781818361168">"វចនានុក្រម​ចម្បង"</string>
-    <string name="prefs_show_suggestions" msgid="8026799663445531637">"បង្ហាញ​ការ​ស្នើ​ការ​កែ"</string>
-    <string name="prefs_show_suggestions_summary" msgid="1583132279498502825">"បង្ហាញ​ពាក្យ​ដែល​បាន​​ផ្ដល់​យោបល់​ខណៈ​ពេល​​​វាយ​បញ្ចូល"</string>
-    <string name="prefs_suggestion_visibility_show_name" msgid="3219916594067551303">"បង្ហាញ​ជា​និច្ច"</string>
-    <string name="prefs_suggestion_visibility_show_only_portrait_name" msgid="3859783767435239118">"បង្ហាញ​នៅ​ក្នុង​របៀប​បញ្ឈរ"</string>
-    <string name="prefs_suggestion_visibility_hide_name" msgid="6309143926422234673">"លាក់​ជានិច្ច"</string>
-    <string name="prefs_block_potentially_offensive_title" msgid="5078480071057408934">"ប្លុក​ពាក្យ​ប្រមាថ​មើលងាយ"</string>
-    <string name="prefs_block_potentially_offensive_summary" msgid="2371835479734991364">"កុំ​ស្នើ​ឲ្យ​ពាក្យ​ប្រមាថ​មើលងាយ​មាន​សក្ដានុពល"</string>
-    <string name="auto_correction" msgid="7630720885194996950">"ការ​កែ​​​ស្វ័យប្រវត្តិ"</string>
-    <string name="auto_correction_summary" msgid="5625751551134658006">"Spacebar ​និង​សញ្ញា​​វណ្ណយុត្ត​កែ​ពាក្យ​ដែល​បាន​វាយ​ខុស​ស្វ័យប្រវត្តិ"</string>
-    <string name="auto_correction_threshold_mode_off" msgid="8470882665417944026">"បិទ"</string>
-    <string name="auto_correction_threshold_mode_modest" msgid="8788366690620799097">"តិចតួច"</string>
-    <string name="auto_correction_threshold_mode_aggressive" msgid="7319007299148899623">"បំពាន"</string>
-    <string name="auto_correction_threshold_mode_very_aggressive" msgid="1853309024129480416">"បំពាន​ខ្លាំង"</string>
-    <string name="bigram_prediction" msgid="1084449187723948550">"ការ​ស្នើ​ពាក្យ​បន្ទាប់"</string>
-    <string name="bigram_prediction_summary" msgid="3896362682751109677">"ប្រើ​ពាក្យ​មុន​​នៅ​ពេល​ធ្វើ​ការ​​​ស្នើ"</string>
-    <string name="gesture_input" msgid="826951152254563827">"បើក​ការ​​បញ្ចូល​​កាយវិការ"</string>
-    <string name="gesture_input_summary" msgid="9180350639305731231">"បញ្ចូល​ពាក្យ​ដោយ​អនុវត្ត​​តាម​​អក្សរ"</string>
-    <string name="gesture_preview_trail" msgid="3802333369335722221">"បង្ហាញ​ដាន​កាយវិការ"</string>
-    <string name="gesture_floating_preview_text" msgid="4443240334739381053">"មើល​ការ​​អណ្ដែត​ដែល​មាន​ចលនា"</string>
-    <string name="gesture_floating_preview_text_summary" msgid="4472696213996203533">"​មើល​ពាក្យ​​​ដែល​បាន​ស្នើ​​​ខណៈ​ពេល​កំពុង​ធ្វើ​កាយ​វិការ"</string>
-    <string name="added_word" msgid="8993883354622484372">"<xliff:g id="WORD">%s</xliff:g> : បាន​រក្សាទុក"</string>
-    <string name="label_go_key" msgid="1635148082137219148">"ទៅ"</string>
-    <string name="label_next_key" msgid="362972844525672568">"បន្ទាប់"</string>
-    <string name="label_previous_key" msgid="1211868118071386787">"មុន"</string>
-    <string name="label_done_key" msgid="2441578748772529288">"រួចរាល់"</string>
-    <string name="label_send_key" msgid="2815056534433717444">"ផ្ញើ"</string>
-    <string name="label_pause_key" msgid="181098308428035340">"ផ្អាក"</string>
-    <string name="label_wait_key" msgid="6402152600878093134">"រង់ចាំ"</string>
-    <string name="spoken_use_headphones" msgid="896961781287283493">"ដោត​កាស ដើម្បី​ស្ដាប់​ពាក្យ​សម្ងាត់។"</string>
-    <string name="spoken_current_text_is" msgid="2485723011272583845">"អត្ថបទ​បច្ចុប្បន្ន​គឺ %s"</string>
-    <string name="spoken_no_text_entered" msgid="7479685225597344496">"គ្មាន​អត្ថបទ​ដែល​បាន​បញ្ចូល"</string>
-    <string name="spoken_description_unknown" msgid="3197434010402179157">"កូដ​គ្រាប់​ចុច %d"</string>
-    <string name="spoken_description_shift" msgid="244197883292549308">"Shift"</string>
-    <string name="spoken_description_shift_shifted" msgid="1681877323344195035">"បើក Shift (ចុច tap ដើម្បី​បិទ)"</string>
-    <string name="spoken_description_caps_lock" msgid="3276478269526304432">"បើក Caps lock (ចុច​ tap ដើម្បី​បិទ)"</string>
-    <string name="spoken_description_delete" msgid="8740376944276199801">"Delete"</string>
-    <string name="spoken_description_to_symbol" msgid="5486340107500448969">"និមិត្ត​សញ្ញា"</string>
-    <string name="spoken_description_to_alpha" msgid="23129338819771807">"Letters"</string>
-    <string name="spoken_description_to_numeric" msgid="591752092685161732">"លេខ"</string>
-    <string name="spoken_description_settings" msgid="4627462689603838099">"ការ​កំណត់"</string>
-    <string name="spoken_description_tab" msgid="2667716002663482248">"Tab"</string>
-    <string name="spoken_description_space" msgid="2582521050049860859">"Space"</string>
-    <string name="spoken_description_mic" msgid="615536748882611950">"ការ​បញ្ចូល​សំឡេង"</string>
-    <string name="spoken_description_smiley" msgid="2256309826200113918">"មុខ​ញញឹម"</string>
-    <string name="spoken_description_return" msgid="8178083177238315647">"Return"</string>
-    <string name="spoken_description_search" msgid="1247236163755920808">"ស្វែងរក"</string>
-    <string name="spoken_description_dot" msgid="40711082435231673">"Dot"</string>
-    <string name="spoken_description_language_switch" msgid="5507091328222331316">"ប្ដូរ​​ភាសា"</string>
-    <string name="spoken_description_action_next" msgid="8636078276664150324">"បន្ទាប់"</string>
-    <string name="spoken_description_action_previous" msgid="800872415009336208">"មុន"</string>
-    <string name="spoken_description_shiftmode_on" msgid="5700440798609574589">"បាន​បើក Shift"</string>
-    <string name="spoken_description_shiftmode_locked" msgid="593175803181701830">"បាន​បើក Caps lock"</string>
-    <string name="spoken_description_shiftmode_off" msgid="657219998449174808">"បាន​បិទ Shift"</string>
-    <string name="spoken_description_mode_symbol" msgid="7183343879909747642">"របៀប​និមិត្តសញ្ញា"</string>
-    <string name="spoken_description_mode_alpha" msgid="3528307674390156956">"របៀប​អក្សរ"</string>
-    <string name="spoken_description_mode_phone" msgid="6520207943132026264">"របៀប​ទូរស័ព្ទ"</string>
-    <string name="spoken_description_mode_phone_shift" msgid="5499629753962641227">"​របៀប​និមិត្ត​សញ្ញា​ទូរស័ព្ទ"</string>
-    <string name="announce_keyboard_hidden" msgid="8718927835531429807">"បាន​លាក់​ក្ដារចុច"</string>
-    <string name="announce_keyboard_mode" msgid="4729081055438508321">"បង្ហាញ​ក្ដារ​ចុច <xliff:g id="MODE">%s</xliff:g>"</string>
-    <string name="keyboard_mode_date" msgid="3137520166817128102">"កាលបរិច្ឆេទ"</string>
-    <string name="keyboard_mode_date_time" msgid="339593358488851072">"កាល​បរិច្ឆេទ​ និង​ពេល​វេលា"</string>
-    <string name="keyboard_mode_email" msgid="6216248078128294262">"អ៊ីមែល"</string>
-    <string name="keyboard_mode_im" msgid="1137405089766557048">"ការ​ផ្ញើ​សារ"</string>
-    <string name="keyboard_mode_number" msgid="7991623440699957069">"លេខ"</string>
-    <string name="keyboard_mode_phone" msgid="6851627527401433229">"ទូរស័ព្ទ"</string>
-    <string name="keyboard_mode_text" msgid="6479436687899701619">"អត្ថបទ"</string>
-    <string name="keyboard_mode_time" msgid="4381856885582143277">"ពេលវេលា"</string>
-    <string name="keyboard_mode_url" msgid="1519819835514911218">"URL"</string>
-    <string name="voice_input" msgid="3583258583521397548">"គ្រាប់​ចុច​បញ្ចូល​​សំឡេង"</string>
-    <string name="voice_input_modes_main_keyboard" msgid="3360660341121083174">"នៅ​លើ​ក្ដារចុច​ចម្បង"</string>
-    <string name="voice_input_modes_symbols_keyboard" msgid="7203213240786084067">"នៅ​លើ​ក្ដារចុច​​និមិត្ត​សញ្ញា"</string>
-    <string name="voice_input_modes_off" msgid="3745699748218082014">"បិទ"</string>
-    <string name="voice_input_modes_summary_main_keyboard" msgid="6586544292900314339">"មីក្រូហ្វូន​នៅ​លើ​​ក្ដារចុច​ចម្បង"</string>
-    <string name="voice_input_modes_summary_symbols_keyboard" msgid="5233725927281932391">"មីក្រូហ្វូន​នៅ​លើ​​ក្ដារចុច​និមិត្ត​សញ្ញា"</string>
-    <string name="voice_input_modes_summary_off" msgid="63875609591897607">"បាន​បិទ​ការ​បញ្ចូល​សំឡេង"</string>
-    <string name="configure_input_method" msgid="373356270290742459">"កំណត់​រចនាសម្ព័ន្ធ​វិធីសាស្ត្រ​បញ្ចូល"</string>
-    <string name="language_selection_title" msgid="1651299598555326750">"បញ្ចូល​ភាសា"</string>
-    <string name="send_feedback" msgid="1780431884109392046">"ផ្ញើ​មតិ​អ្នក​ប្រើ"</string>
-    <string name="select_language" msgid="3693815588777926848">"បញ្ចូល​ភាសា"</string>
-    <string name="hint_add_to_dictionary" msgid="573678656946085380">"ប៉ះ​ម្ដង​ទៀត​ដើម្បី​រក្សា​ទុក"</string>
-    <string name="has_dictionary" msgid="6071847973466625007">"មាន​វចនានុក្រម"</string>
-    <string name="prefs_enable_log" msgid="6620424505072963557">"បើក​មតិ​អ្នកប្រើ"</string>
-    <string name="prefs_description_log" msgid="7525225584555429211">"ជំនួយ​​​ធ្វើ​ឲ្យ​​ប្រសើរ​ឡើង​​នៃ​កម្មវិធី​កែ​​វិធី​សាស្ត្រ​​បញ្ចូល​ដោយ​ស្វ័យ​ប្រវត្តិ​ដោយ​ការ​ផ្ញើ​ស្ថិតិ​ការ​ប្រើ​ប្រាស់​ ​និង​របាយការណ៍​គាំង"</string>
-    <string name="keyboard_layout" msgid="8451164783510487501">"រូបរាង​ក្ដារចុច"</string>
-    <string name="subtype_en_GB" msgid="88170601942311355">"អង់គ្លេស (ចក្រភព​អង់គ្លេស)"</string>
-    <string name="subtype_en_US" msgid="6160452336634534239">"អង់គ្លេស (សហរដ្ឋ​អាមេរិក)"</string>
-    <string name="subtype_es_US" msgid="5583145191430180200">"អេស្ប៉ាញ (សហរដ្ឋ​អាមេរិក​)"</string>
-    <string name="subtype_with_layout_en_GB" msgid="2179097748724725906">"អង់គ្លេស (ចក្រភព​អង់គ្លេស) (<xliff:g id="LAYOUT">%s</xliff:g>)"</string>
-    <string name="subtype_with_layout_en_US" msgid="1362581347576714579">"អង់គ្លេស (អាមេរិក) (<xliff:g id="LAYOUT">%s</xliff:g>)"</string>
-    <string name="subtype_with_layout_es_US" msgid="6261791057007890189">"អេស្ប៉ាញ (អាមេរិក​) ( <xliff:g id="LAYOUT">%s</xliff:g> )"</string>
-    <string name="subtype_no_language" msgid="7137390094240139495">"គ្មាន​ភាសា (អក្សរ​ក្រម)"</string>
-    <string name="subtype_no_language_qwerty" msgid="244337630616742604">"អក្សរ​ក្រម (QWERTY)"</string>
-    <string name="subtype_no_language_qwertz" msgid="443066912507547976">"អក្សរ​ក្រម (QWERTZ)"</string>
-    <string name="subtype_no_language_azerty" msgid="8144348527575640087">"អក្សរ​ក្រម (AZERTY)"</string>
-    <string name="subtype_no_language_dvorak" msgid="1564494667584718094">"អក្សរ​ក្រម (Dvorak)"</string>
-    <string name="subtype_no_language_colemak" msgid="5837418400010302623">"អក្សរ​ក្រម (Colemak)"</string>
-    <string name="subtype_no_language_pcqwerty" msgid="5354918232046200018">"អក្សរ​ក្រម (កុំព្យូទ័រ)"</string>
-    <string name="custom_input_styles_title" msgid="8429952441821251512">"បញ្ចូល​រចនាប័ទ្ម​ផ្ទាល់​ខ្លួន"</string>
-    <string name="add_style" msgid="6163126614514489951">"បន្ថែម​រចនាប័ទ្ម"</string>
-    <string name="add" msgid="8299699805688017798">"បន្ថែម"</string>
-    <string name="remove" msgid="4486081658752944606">"លុប​ចេញ"</string>
-    <string name="save" msgid="7646738597196767214">"រក្សាទុក"</string>
-    <string name="subtype_locale" msgid="8576443440738143764">"ភាសា"</string>
-    <string name="keyboard_layout_set" msgid="4309233698194565609">"ប្លង់"</string>
-    <string name="custom_input_style_note_message" msgid="8826731320846363423">"ចាំបាច់​ត្រូវ​បើក​រចនាប័ទ្ម​បញ្ចូល​ផ្ទាល់​ខ្លួន​របស់​អ្នក មុន​ពេល​អ្នក​ចាប់ផ្ដើម​ប្រើ​វា។ តើ​អ្នក​ចង់​បើក​វា​ឥឡូវ​នេះ​ឬ?"</string>
-    <string name="enable" msgid="5031294444630523247">"បើក"</string>
-    <string name="not_now" msgid="6172462888202790482">"មិនមែន​ឥឡូវ"</string>
-    <string name="custom_input_style_already_exists" msgid="8008728952215449707">"បញ្ចូល​រចនាប័ទ្ម​ដូចគ្នា​រួច​ហើយ​: <xliff:g id="INPUT_STYLE_NAME">%s</xliff:g>"</string>
-    <string name="prefs_usability_study_mode" msgid="1261130555134595254">"របៀប​ការ​សិក្សា​ដែល​អាច​ប្រើ​បាន"</string>
-    <string name="prefs_key_longpress_timeout_settings" msgid="6102240298932897873">"ពន្យារពេល​​​ចុច​គ្រាប់​ចុច​ឲ្យ​​យូរ"</string>
-    <string name="prefs_keypress_vibration_duration_settings" msgid="7918341459947439226">"​ពេលវេលា​ញ័រ​​ពេល​ចុច​គ្រាប់ចុច"</string>
-    <string name="prefs_keypress_sound_volume_settings" msgid="6027007337036891623">"កម្រិត​សំឡេង​ពេល​ចុច​គ្រាប់​ចុច"</string>
-    <string name="prefs_read_external_dictionary" msgid="2588931418575013067">"អាន​ឯកសារ​វចនានុក្រម​ខាង​ក្រៅ"</string>
-    <string name="read_external_dictionary_no_files_message" msgid="4947420942224623792">"គ្មាន​ឯកសារ​វចនានុក្រម​នៅ​ក្នុង​ថត​ទាញ​យក​ទេ"</string>
-    <string name="read_external_dictionary_multiple_files_title" msgid="7637749044265808628">"ជ្រើស​ឯកសារ​វចនានុក្រម​ដើម្បី​ដំឡើង"</string>
-    <string name="read_external_dictionary_confirm_install_message" msgid="6898610163768980870">"ពិត​ជា​ដំឡើង​ឯកសារ​នេះ​សម្រាប់ <xliff:g id="LOCALE_NAME">%s</xliff:g> ឬ?"</string>
-    <string name="error" msgid="8940763624668513648">"មាន​កំហុស"</string>
-    <string name="button_default" msgid="3988017840431881491">"លំនាំដើម"</string>
-    <string name="setup_welcome_title" msgid="6112821709832031715">"សូម​ស្វាគមន៍​មក​កាន់ <xliff:g id="APPLICATION_NAME">%s</xliff:g>"</string>
-    <string name="setup_welcome_additional_description" msgid="8150252008545768953">"ជាមួយ​​​ការ​វាយ​ដោយ​ប្រើ​​​កាយវិការ"</string>
-    <string name="setup_start_action" msgid="8936036460897347708">"បាន​ចាប់ផ្ដើម"</string>
-    <string name="setup_next_action" msgid="371821437915144603">"ជំហាន​បន្ទាប់"</string>
-    <string name="setup_steps_title" msgid="6400373034871816182">"រៀបចំ <xliff:g id="APPLICATION_NAME">%s</xliff:g>"</string>
-    <string name="setup_step1_title" msgid="3147967630253462315">"បើក <xliff:g id="APPLICATION_NAME">%s</xliff:g>"</string>
-    <string name="setup_step1_instruction" msgid="2578631936624637241">"សូម​ពិនិត្យមើល \"<xliff:g id="APPLICATION_NAME">%s</xliff:g>\" នៅ​ក្នុង​ការ​កំណត់​ភាសា &amp; និង​ការ​បញ្ចូល​របស់​អ្នក។ វា​នឹង​ដំណើរការ​នៅ​លើ​ឧបករណ៍​របស់​អ្នក។"</string>
-    <string name="setup_step1_finished_instruction" msgid="10761482004957994">"<xliff:g id="APPLICATION_NAME">%s</xliff:g> បាន​បើក​នៅ​ក្នុង​​ការ​កំណត់​​ភាសា​ &amp; ការ​បញ្ចូល​របស់ ដូច្នេះ​ជំហាន​នេះ​រួចរាល់​ហើយ។ បន្ត​ទៅ​ជំហាន​បន្ទាប់!"</string>
-    <string name="setup_step1_action" msgid="4366513534999901728">"បើក​នៅ​ក្នុង​ការ​កំណត់"</string>
-    <string name="setup_step2_title" msgid="6860725447906690594">"ប្ដូរ​ទៅ <xliff:g id="APPLICATION_NAME">%s</xliff:g>"</string>
-    <string name="setup_step2_instruction" msgid="9141481964870023336">"បន្ទាប់ ជ្រើស \"<xliff:g id="APPLICATION_NAME">%s</xliff:g>\" ជា​វិធី​សាស្ត្រ​បញ្ចូល​អត្ថបទ​សកម្ម​របស់​អ្នក។"</string>
-    <string name="setup_step2_action" msgid="1660330307159824337">"ប្ដូរ​វិធីសាស្ត្រ​បញ្ចូល"</string>
-    <string name="setup_step3_title" msgid="3154757183631490281">"អបអរ​សាទរ​ អ្នក​​បាន​កំណត់​ទាំងអស់​ហើយ!"</string>
-    <string name="setup_step3_instruction" msgid="8025981829605426000">"ឥឡូវ​នេះ​អ្នក​អាច​​វាយ​បញ្ចូល​នៅ​ក្នុង​​កម្មវិធី​សំណព្វ​របស់​អ្នក​ទាំងអស់​ជាមួយ <xliff:g id="APPLICATION_NAME">%s</xliff:g> ។"</string>
-    <string name="setup_step3_action" msgid="600879797256942259">"កំណត់​រចនា​សម្ព័ន្ធ​ភាសា​បន្ថែម"</string>
-    <string name="setup_finish_action" msgid="276559243409465389">"បាន​បញ្ចប់"</string>
-    <string name="show_setup_wizard_icon" msgid="5008028590593710830">"បង្ហាញ​រូប​តំណាង​កម្មវិធី"</string>
-    <string name="show_setup_wizard_icon_summary" msgid="4119998322536880213">"បង្ហាញ​រូប​តំណាង​កម្មវិធី​នៅ​ក្នុង​កម្ម​​វិធី​ចាប់ផ្ដើម"</string>
-    <string name="app_name" msgid="6320102637491234792">"កម្មវិធី​ផ្ដល់​វចនានុក្រម"</string>
-    <string name="dictionary_provider_name" msgid="3027315045397363079">"កម្មវិធី​ផ្ដល់​វចនានុក្រម"</string>
-    <string name="dictionary_service_name" msgid="6237472350693511448">"សេវា​វចនានុក្រម"</string>
-    <string name="download_description" msgid="6014835283119198591">"ព័ត៌មាន​បច្ចុប្បន្នភាព​វចនានុក្រម"</string>
-    <string name="dictionary_settings_title" msgid="8091417676045693313">"ផ្នែក​បន្ថែម​វចនានុក្រម"</string>
-    <string name="dictionary_install_over_metered_network_prompt" msgid="3587517870006332980">"វចនានុក្រម​ដែល​​​អាច​ប្រើ​បាន"</string>
-    <string name="dictionary_settings_summary" msgid="5305694987799824349">"ការ​កំណត់​សម្រាប់​វចនានុក្រម"</string>
-    <string name="user_dictionaries" msgid="3582332055892252845">"វចនានុក្រម​​​អ្នក​ប្រើ"</string>
-    <string name="default_user_dict_pref_name" msgid="1625055720489280530">"វចនានុក្រម​អ្នក​ប្រើ"</string>
-    <string name="dictionary_available" msgid="4728975345815214218">"វចនានុក្រម​​ដែល​អាច​ប្រើ​បាន"</string>
-    <string name="dictionary_downloading" msgid="2982650524622620983">"បច្ចុប្បន្ន​កំពុង​ទាញយក"</string>
-    <string name="dictionary_installed" msgid="8081558343559342962">"បាន​ដំឡើង"</string>
-    <string name="dictionary_disabled" msgid="8950383219564621762">"បាន​ដំឡើង បាន​បិទ"</string>
-    <string name="cannot_connect_to_dict_service" msgid="9216933695765732398">"មាន​បញ្ហា​ក្នុង​ការ​តភ្ជាប់​ទៅ​​សេវា​វចនានុក្រម"</string>
-    <string name="no_dictionaries_available" msgid="8039920716566132611">"គ្មាន​វចនានុក្រម"</string>
-    <string name="check_for_updates_now" msgid="8087688440916388581">"ធ្វើ​ឲ្យ​ស្រស់"</string>
-    <string name="last_update" msgid="730467549913588780">"បាន​ធ្វើ​បច្ចុប្បន្នភាព​ចុងក្រោយ"</string>
-    <string name="message_updating" msgid="4457761393932375219">"ពិនិត្យមើល​បច្ចុប្បន្នភាព"</string>
-    <string name="message_loading" msgid="8689096636874758814">"កំពុង​ផ្ទុក..."</string>
-    <string name="main_dict_description" msgid="3072821352793492143">"វចនានុក្រម​ចម្បង"</string>
-    <string name="cancel" msgid="6830980399865683324">"បោះ​បង់"</string>
-    <string name="install_dict" msgid="180852772562189365">"ដំឡើង"</string>
-    <string name="cancel_download_dict" msgid="7843340278507019303">"បោះ​បង់"</string>
-    <string name="delete_dict" msgid="756853268088330054">"លុប"</string>
-    <string name="should_download_over_metered_prompt" msgid="2878629598667658845">"ភាសា​ដែល​បាន​ជ្រើស​នៅ​លើ​ឧបករណ៍​របស់​អ្នក​មាន​វចនានុក្រម។ &lt;br/&gt; យើង​បាន​ផ្ដល់​អនុសាសន៍ &lt;b&gt; ទាញ​យក​ &lt;/b&gt;  <xliff:g id="LANGUAGE">%1$s</xliff:g> វចនានុក្រម ដើម្បី​ធ្វើ​ឲ្យ​ការ​វាយ​បញ្ចូល​របស់​អ្នក​ប្រសើរ​ឡើង។ &lt;br/&gt; &lt;br/&gt; ការ​ទាញ​យក​អាច​ចំណាយ​ពេល​​មួយ ឬ​ពីរ​នាទី​​​តាម 3G ។ អាច​អនុវត្ត​ប្រសិនបើ​អ្នក​មិន​​បាន​ &lt;b&gt; កំណត់​ទិន្នន័យ​គ្មាន​ដែន​កំណត់ &lt;/b&gt;.&lt;br/&gt; ប្រសិនបើ​​អ្នក​មិន​ប្រាកដ​​ថា​ទិន្នន័យ​អ្នក​​មិន​បាន​​កំណត់ យើង​បាន​ផ្ដល់​អនុសាសន៍​ដោយ​ស្វែងរក​ការ​តភ្ជាប់​​វ៉ាយហ្វាយ ដើម្បី​ចាប់ផ្ដើម​ទាញ​យក​ដោយ​ស្វ័យប្រវត្តិ។&lt;br/&gt; &lt;br/&gt; ព័ត៌មាន​ជំនួយ៖ អ្នក​អាច​ទាញ​យក ហើយ​យក​វចនានុក្រម​ចេញ​ដោយ​ចូល​ទៅ​កាន់ &lt;b&gt; ភាសា &amp; បញ្ចូល &lt;/b&gt; នៅ​ក្នុង &lt;b&gt; ការ​កំណត់ &lt;/b&gt; ម៉ឺនុយ​របស់​ឧបករណ៍​ចល័ត។"</string>
-    <string name="download_over_metered" msgid="1643065851159409546">"ទាញ​យក​ឥឡូវ​នេះ (<xliff:g id="SIZE_IN_MEGABYTES">%1$.1f</xliff:g>MB)"</string>
-    <string name="do_not_download_over_metered" msgid="2176209579313941583">"ទាញ​យក​តាម​វ៉ាយហ្វាយ"</string>
-    <string name="dict_available_notification_title" msgid="6514288591959117288">"វចនានុក្រម​​​ដែល​អាច​ប្រើ​បាន​​សម្រាប់ <xliff:g id="LANGUAGE">%1$s</xliff:g>"</string>
-    <string name="dict_available_notification_description" msgid="1075194169443163487">"ចុច​ដើម្បី​ពិនិត្យ​មើល​ឡើង​​វិញ​ និង​ទាញ​យក"</string>
-    <string name="toast_downloading_suggestions" msgid="1313027353588566660">"ទាញ​យក៖ ការ​​ស្នើ <xliff:g id="LANGUAGE">%1$s</xliff:g> នឹង​បញ្ចប់​ឆាប់ៗ។"</string>
-    <string name="version_text" msgid="2715354215568469385">"កំណែ <xliff:g id="VERSION_NUMBER">%1$s</xliff:g>"</string>
-    <string name="user_dict_settings_add_menu_title" msgid="1254195365689387076">"បន្ថែម"</string>
-    <string name="user_dict_settings_add_dialog_title" msgid="4096700390211748168">"បន្ថែម​ទៅ​វចនានុក្រម"</string>
-    <string name="user_dict_settings_add_screen_title" msgid="5818914331629278758">"ឃ្លា"</string>
-    <string name="user_dict_settings_add_dialog_more_options" msgid="5671682004887093112">"ជម្រើស​ច្រើន"</string>
-    <string name="user_dict_settings_add_dialog_less_options" msgid="2716586567241724126">"ជម្រើស​តិច"</string>
-    <string name="user_dict_settings_add_dialog_confirm" msgid="4703129507388332950">"យល់​ព្រម"</string>
-    <string name="user_dict_settings_add_word_option_name" msgid="6665558053408962865">"ពាក្យ៖"</string>
-    <string name="user_dict_settings_add_shortcut_option_name" msgid="3094731590655523777">"ផ្លូវកាត់​៖"</string>
-    <string name="user_dict_settings_add_locale_option_name" msgid="4738643440987277705">"ភាសា៖"</string>
-    <string name="user_dict_settings_add_word_hint" msgid="4902434148985906707">"វាយ​បញ្ចូល​ពាក្យ​មួយ"</string>
-    <string name="user_dict_settings_add_shortcut_hint" msgid="2265453012555060178">"ផ្លូវកាត់​ជា​ជម្រើស"</string>
-    <string name="user_dict_settings_edit_dialog_title" msgid="3765774633869590352">"កែ​ពាក្យ"</string>
-    <string name="user_dict_settings_context_menu_edit_title" msgid="6812255903472456302">"កែ"</string>
-    <string name="user_dict_settings_context_menu_delete_title" msgid="8142932447689461181">"លុប"</string>
-    <string name="user_dict_settings_empty_text" msgid="558499587532668203">"អ្នក​មិន​មាន​ពាក្យ​ណាមួយ​នៅ​ក្នុង​វចនានុក្រម​អ្នក​ប្រើ​ទេ។ បន្ថែម​ពាក្យ​ដោយ​​​​ប៉ះ​ប៊ូតុង​ (+) បន្ថែម។"</string>
-    <string name="user_dict_settings_all_languages" msgid="8276126583216298886">"សម្រាប់​ភាសា​ទាំងអស់"</string>
-    <string name="user_dict_settings_more_languages" msgid="7131268499685180461">"ភាសា​ច្រើន…"</string>
-    <string name="user_dict_settings_delete" msgid="110413335187193859">"លុប"</string>
-    <string name="user_dict_fast_scroll_alphabet" msgid="5431919401558285473">" ABCDEFGHIJKLMNOPQRSTUVWXYZ"</string>
-</resources>
diff --git a/java/res/values-ko/strings.xml b/java/res/values-ko/strings.xml
index e729fc6..a74b4f1 100644
--- a/java/res/values-ko/strings.xml
+++ b/java/res/values-ko/strings.xml
@@ -143,6 +143,7 @@
     <string name="subtype_with_layout_en_GB" msgid="2179097748724725906">"영어(영국) (<xliff:g id="LAYOUT">%s</xliff:g>)"</string>
     <string name="subtype_with_layout_en_US" msgid="1362581347576714579">"영어(미국) (<xliff:g id="LAYOUT">%s</xliff:g>)"</string>
     <string name="subtype_with_layout_es_US" msgid="6261791057007890189">"스페인어(미국)(<xliff:g id="LAYOUT">%s</xliff:g>)"</string>
+    <string name="subtype_nepali_traditional" msgid="9032247506728040447">"<xliff:g id="LANGUAGE">%s</xliff:g>(일반)"</string>
     <string name="subtype_no_language" msgid="7137390094240139495">"언어 없음(알파벳)"</string>
     <string name="subtype_no_language_qwerty" msgid="244337630616742604">"알파벳(QWERTY)"</string>
     <string name="subtype_no_language_qwertz" msgid="443066912507547976">"알파벳(QWERTZ)"</string>
@@ -150,6 +151,7 @@
     <string name="subtype_no_language_dvorak" msgid="1564494667584718094">"알파벳(드보락)"</string>
     <string name="subtype_no_language_colemak" msgid="5837418400010302623">"알파벳(콜맥)"</string>
     <string name="subtype_no_language_pcqwerty" msgid="5354918232046200018">"알파벳(PC)"</string>
+    <string name="subtype_emoji" msgid="7483586578074549196">"이모티콘"</string>
     <string name="custom_input_styles_title" msgid="8429952441821251512">"맞춤 입력 스타일"</string>
     <string name="add_style" msgid="6163126614514489951">"스타일 추가"</string>
     <string name="add" msgid="8299699805688017798">"추가"</string>
diff --git a/java/res/values-land/dimens.xml b/java/res/values-land/dimens.xml
index c78c25f..42a746b 100644
--- a/java/res/values-land/dimens.xml
+++ b/java/res/values-land/dimens.xml
@@ -26,14 +26,8 @@
     <!-- key_height + key_bottom_gap = popup_key_height -->
     <dimen name="popup_key_height">44.8dp</dimen>
 
-    <fraction name="keyboard_top_padding">1.818%p</fraction>
-    <fraction name="keyboard_bottom_padding">0.0%p</fraction>
-    <fraction name="key_bottom_gap">4.330%p</fraction>
-    <fraction name="key_horizontal_gap">0.405%p</fraction>
-
-    <fraction name="key_bottom_gap_stone">5.010%p</fraction>
-    <fraction name="key_horizontal_gap_stone">1.159%p</fraction>
-
+    <fraction name="keyboard_top_padding_gb">1.818%p</fraction>
+    <fraction name="keyboard_bottom_padding_gb">0.0%p</fraction>
     <fraction name="key_bottom_gap_gb">5.941%p</fraction>
     <fraction name="key_horizontal_gap_gb">0.997%p</fraction>
 
@@ -53,7 +47,7 @@
     <fraction name="key_uppercase_letter_ratio">40%</fraction>
     <fraction name="key_preview_text_ratio">90%</fraction>
     <fraction name="spacebar_text_ratio">40.000%</fraction>
-    <dimen name="key_preview_offset">0.0dp</dimen>
+    <dimen name="key_preview_offset_gb">0.0dp</dimen>
 
     <!-- For 5-row keyboard -->
     <fraction name="key_bottom_gap_5row">3.20%p</fraction>
@@ -72,11 +66,18 @@
     <!-- popup_key_height x 1.2 -->
     <dimen name="more_keys_keyboard_slide_allowance">53.76dp</dimen>
     <!-- popup_key_height x -1.0 -->
-    <dimen name="more_keys_keyboard_vertical_correction">-44.8dp</dimen>
+    <dimen name="more_keys_keyboard_vertical_correction_gb">-44.8dp</dimen>
 
     <!-- Gesture floating preview text parameters -->
     <dimen name="gesture_floating_preview_text_size">23dp</dimen>
     <dimen name="gesture_floating_preview_text_offset">54dp</dimen>
     <dimen name="gesture_floating_preview_horizontal_padding">23dp</dimen>
     <dimen name="gesture_floating_preview_vertical_padding">15dp</dimen>
+
+    <!-- Emoji keyboard -->
+    <fraction name="emoji_keyboard_key_width">10%p</fraction>
+    <fraction name="emoji_keyboard_row_height">50%p</fraction>
+    <fraction name="emoji_keyboard_key_letter_size">60%p</fraction>
+    <integer name="emoji_keyboard_max_key_count">20</integer>
+
 </resources>
diff --git a/java/res/values-lo-rLA/strings-appname.xml b/java/res/values-lo-rLA/strings-appname.xml
deleted file mode 100644
index 17a0094..0000000
--- a/java/res/values-lo-rLA/strings-appname.xml
+++ /dev/null
@@ -1,27 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!-- 
-/*
-**
-** Copyright 2013, 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:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="english_ime_name" msgid="5940510615957428904">"ແປ້ນພິມ Android (AOSP)"</string>
-    <string name="spell_checker_service_name" msgid="1254221805440242662">"ໂຕກວດການສະກົດຄຳໃນ Android (AOSP)"</string>
-    <string name="english_ime_settings" msgid="5760361067176802794">"ຕັ້ງຄ່າແປ້ນພິມ Android (AOSP)"</string>
-    <string name="android_spell_checker_settings" msgid="6123949487832861885">"Android Spell Checker Settings (AOSP)"</string>
-</resources>
diff --git a/java/res/values-lo-rLA/strings.xml b/java/res/values-lo-rLA/strings.xml
deleted file mode 100644
index c57b86b..0000000
--- a/java/res/values-lo-rLA/strings.xml
+++ /dev/null
@@ -1,242 +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.
-*/
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="english_ime_input_options" msgid="3909945612939668554">"ຕົວເລືອກການປ້ອນຂໍ້ມູນ"</string>
-    <string name="english_ime_research_log" msgid="8492602295696577851">"Research Log Commands"</string>
-    <string name="use_contacts_for_spellchecking_option_title" msgid="5374120998125353898">"ເບິ່ງທີ່ຊື່ຂອງລາຍຊື່ຜູ່ຕິດຕໍ່"</string>
-    <string name="use_contacts_for_spellchecking_option_summary" msgid="8754413382543307713">"ໂຕຊ່ວຍສະກົດໃຊ້ຂໍ້ມູນຈາກລາຍການຂອງລາຍຊື່ຜູ່ຕິດຕໍ່ຂອງທ່ານ"</string>
-    <string name="vibrate_on_keypress" msgid="5258079494276955460">"ສັ່ນເຕືອນເມື່ອພິມ"</string>
-    <string name="sound_on_keypress" msgid="6093592297198243644">"ສຽງໃນການກົດປຸ່ມ"</string>
-    <string name="popup_on_keypress" msgid="123894815723512944">"ໂຕອັກສອນເວລາພິມ"</string>
-    <string name="general_category" msgid="1859088467017573195">"ທົ່ວໄປ"</string>
-    <string name="correction_category" msgid="2236750915056607613">"ໂຕຊ່ວຍແປງຂໍ້ຄວາມ"</string>
-    <string name="gesture_typing_category" msgid="497263612130532630">"ການພິມແບບ Gesture"</string>
-    <string name="misc_category" msgid="6894192814868233453">"ໂຕເລືອກ​ອື່ນໆ"</string>
-    <string name="advanced_settings" msgid="362895144495591463">"ການຕັ້ງຄ່າຂັ້ນສູງ"</string>
-    <string name="advanced_settings_summary" msgid="4487980456152830271">"ຕົວເລືອກສຳລັບຜູ່ທີ່ຊຳນານ"</string>
-    <string name="include_other_imes_in_language_switch_list" msgid="4533689960308565519">"ປ່ຽນໄປໃຊ້ການປ້ອນຂໍ້ມູນແບບອື່ນ"</string>
-    <string name="include_other_imes_in_language_switch_list_summary" msgid="840637129103317635">"ໂຕປ່ຽນພາສາເປັນທັງໂຕປ່ຽນຮູບແບບການປ້ອນຂໍ້ມູນເຊັ່ນກັນ"</string>
-    <string name="show_language_switch_key" msgid="5915478828318774384">"ປຸ່ມປ່ຽນພາສາ"</string>
-    <string name="show_language_switch_key_summary" msgid="7343403647474265713">"ສະແດງໃນເວລາທີ່ຕົວເລືອກການປ້ອນຂໍ້ມູນຫຼາຍໂຕຖືກເປີດຢູ່"</string>
-    <string name="sliding_key_input_preview" msgid="6604262359510068370">"ສະແດງໂຕບົ່ງບອກການສະໄລ້"</string>
-    <string name="sliding_key_input_preview_summary" msgid="6340524345729093886">"ສະແດງແນວທາງໃນຂະນະທີ່ສະໄລ້ຈາກ Shift ຫຼື ປຸ່ມເຄື່ອງໝາຍ"</string>
-    <string name="key_preview_popup_dismiss_delay" msgid="6213164897443068248">"ໄລຍະເວລາການສະແດງໂຕອັກສອນ"</string>
-    <string name="key_preview_popup_dismiss_no_delay" msgid="2096123151571458064">"ບໍ່ຕ້ອໜ່ວງເວລາ"</string>
-    <string name="key_preview_popup_dismiss_default_delay" msgid="2166964333903906734">"ຄ່າເລີ່ມຕົ້ນ"</string>
-    <string name="abbreviation_unit_milliseconds" msgid="8700286094028323363">"<xliff:g id="MILLISECONDS">%s</xliff:g>ms"</string>
-    <string name="settings_system_default" msgid="6268225104743331821">"ຄ່າເລີ່ມຕົ້ນຂອງລະບົບ"</string>
-    <string name="use_contacts_dict" msgid="4435317977804180815">"ແນະນຳລາຍຊື່ຜູ່ຕິດຕໍ່"</string>
-    <string name="use_contacts_dict_summary" msgid="6599983334507879959">"ໃຊ້ຊື່ຈາກລາຍຊື່ຜູ່ຕິດຕໍ່ສຳລັບການແນະນຳ ແລະ ການຊ່ວຍແກ້ຄຳ"</string>
-    <string name="use_double_space_period" msgid="8781529969425082860">"ຍະຫວ່າງສອງເທື່ອເພື່ອໃສ່ຈ້ຳເມັດ"</string>
-    <string name="use_double_space_period_summary" msgid="6532892187247952799">"ກົດທີ່ປຸ່ມຍະຫວ່າງສອງເທື່ອເພື່ອໃສ່ຈ້ຳເມັດແລ້ວຕາມດ້ວຍການຍະຫວ່າງ"</string>
-    <string name="auto_cap" msgid="1719746674854628252">"ເຮັດໂຕພິມໃຫຍ່ອັດຕະໂນມັດ"</string>
-    <string name="auto_cap_summary" msgid="7934452761022946874">"ເຮັດໂຕພິມໃຫຍ່ໃຫ້ໂຕອັກສອນທຳອິດຂອງແຕ່ລໃນປະໂຫຍກ"</string>
-    <string name="edit_personal_dictionary" msgid="3996910038952940420">"ວັດຈະນານຸກົມສ່ວນໂຕ"</string>
-    <string name="configure_dictionaries_title" msgid="4238652338556902049">"ໂຕເສີມວັດຈະນານຸກົມ"</string>
-    <string name="main_dictionary" msgid="4798763781818361168">"ວັດຈະນານຸກົມຫຼັກ"</string>
-    <string name="prefs_show_suggestions" msgid="8026799663445531637">"ສະແດງການແນະນຳຄຳທີ່ຖືກຕ້ອງ"</string>
-    <string name="prefs_show_suggestions_summary" msgid="1583132279498502825">"ສະແດງຄຳສັບທີ່ແນະນຳໃນເວລາທີ່ກຳລັງພິມ"</string>
-    <string name="prefs_suggestion_visibility_show_name" msgid="3219916594067551303">"ສະແດງຕະຫລອດ"</string>
-    <string name="prefs_suggestion_visibility_show_only_portrait_name" msgid="3859783767435239118">"ກົດທີ່ຄຳສັບທີ່ພິມລົງໄປເພື່ອແປງໃຫ້ມັນຖືກຕ້ອງ"</string>
-    <string name="prefs_suggestion_visibility_hide_name" msgid="6309143926422234673">"ເຊື່ອງໄວ້ຕະຫລອດ"</string>
-    <string name="prefs_block_potentially_offensive_title" msgid="5078480071057408934">"ປິດກັ້ນຄຳທີ່ບໍ່ສຸພາບ"</string>
-    <string name="prefs_block_potentially_offensive_summary" msgid="2371835479734991364">"ຫ້າມແນະນຳຄຳທີ່ບໍ່ສຸພາບ"</string>
-    <string name="auto_correction" msgid="7630720885194996950">"ໂຕຊ່ວຍສະກົດຄຳ"</string>
-    <string name="auto_correction_summary" msgid="5625751551134658006">"ການຍະຫວ່າງ ແລະ ການໃສ່ເຄື່ອງໝາຍຈະຖືກປ່ຽນແປງໃຫ້ຖືກຕ້ອງ ໃນຄຳທີ່ພິມຜິດໂດຍອັດຕະໂນມັດ"</string>
-    <string name="auto_correction_threshold_mode_off" msgid="8470882665417944026">"ປິດ"</string>
-    <string name="auto_correction_threshold_mode_modest" msgid="8788366690620799097">"ປານກາງ"</string>
-    <string name="auto_correction_threshold_mode_aggressive" msgid="7319007299148899623">"ສູງ"</string>
-    <string name="auto_correction_threshold_mode_very_aggressive" msgid="1853309024129480416">"ສູງສຸດ"</string>
-    <string name="bigram_prediction" msgid="1084449187723948550">"ການແນະນຳຄຳຕໍ່ໄປ"</string>
-    <string name="bigram_prediction_summary" msgid="3896362682751109677">"ໃຊ້ຄຳທີ່ຜ່ານມາໃນການແນະນຳຄຳ"</string>
-    <string name="gesture_input" msgid="826951152254563827">"ເປີດນຳໃຊ້ການພິມແບບ Gesture"</string>
-    <string name="gesture_input_summary" msgid="9180350639305731231">"ໃສ່ຄຳສັບລົງໄປໂດຍການສະໄລ້ຜ່ານໂຕອັກສອນ"</string>
-    <string name="gesture_preview_trail" msgid="3802333369335722221">"ສະແດງຫາງຂອງ Gesture"</string>
-    <string name="gesture_floating_preview_text" msgid="4443240334739381053">"ມີຄຳຕົວຢ່າງລອຍຂຶ້ນມາ"</string>
-    <string name="gesture_floating_preview_text_summary" msgid="4472696213996203533">"ເບິ່ງຄຳທີ່ຖືກແນະນຳໃນເວລາທີ່ກຳລັງຊີ້"</string>
-    <string name="added_word" msgid="8993883354622484372">"<xliff:g id="WORD">%s</xliff:g> : ບັນທຶກແລ້ວ"</string>
-    <string name="label_go_key" msgid="1635148082137219148">"ໄປ"</string>
-    <string name="label_next_key" msgid="362972844525672568">"ຕໍ່ໄປ"</string>
-    <string name="label_previous_key" msgid="1211868118071386787">"ກ່ອນໜ້າ"</string>
-    <string name="label_done_key" msgid="2441578748772529288">"ແລ້ວໆ"</string>
-    <string name="label_send_key" msgid="2815056534433717444">"ສົ່ງ"</string>
-    <string name="label_pause_key" msgid="181098308428035340">"ຄ້າງໄວ້"</string>
-    <string name="label_wait_key" msgid="6402152600878093134">"ລໍຖ້າ"</string>
-    <string name="spoken_use_headphones" msgid="896961781287283493">"ສຽບສາຍຫູຟັງເພື່ອຟັງລະຫັດຜ່ານ."</string>
-    <string name="spoken_current_text_is" msgid="2485723011272583845">"ຂໍ້ຄວາມປະຈຸບັນແມ່ນ %s"</string>
-    <string name="spoken_no_text_entered" msgid="7479685225597344496">"ບໍ່ມີການໃສ່ຂໍ້ຄວາມ"</string>
-    <string name="spoken_description_unknown" msgid="3197434010402179157">"ລະຫັດກະແຈ %d"</string>
-    <string name="spoken_description_shift" msgid="244197883292549308">"Shift"</string>
-    <string name="spoken_description_shift_shifted" msgid="1681877323344195035">"Shift ເປີດນຳໃຊ້ຢູ່ (ກົດເພື່ອປິດນຳໃຊ້)"</string>
-    <string name="spoken_description_caps_lock" msgid="3276478269526304432">"Caps lock ເປີດຢູ່ (ກົດເພື່ອປິດນຳໃຊ້)"</string>
-    <string name="spoken_description_delete" msgid="8740376944276199801">"ລຶບ"</string>
-    <string name="spoken_description_to_symbol" msgid="5486340107500448969">"ສັນຍາລັກ"</string>
-    <string name="spoken_description_to_alpha" msgid="23129338819771807">"ໂຕອັກ​ສອນ"</string>
-    <string name="spoken_description_to_numeric" msgid="591752092685161732">"ໂຕເລກ"</string>
-    <string name="spoken_description_settings" msgid="4627462689603838099">"ການຕັ້ງຄ່າ"</string>
-    <string name="spoken_description_tab" msgid="2667716002663482248">"ແທັບ"</string>
-    <string name="spoken_description_space" msgid="2582521050049860859">"ຍະຫວ່າງ"</string>
-    <string name="spoken_description_mic" msgid="615536748882611950">"ການປ້ອນຂໍ້ມູນດ້ວຍສຽງ"</string>
-    <string name="spoken_description_smiley" msgid="2256309826200113918">"ຮອຍຍິ້ມ"</string>
-    <string name="spoken_description_return" msgid="8178083177238315647">"ກັບຄືນ"</string>
-    <string name="spoken_description_search" msgid="1247236163755920808">"ຊອກຫາ"</string>
-    <string name="spoken_description_dot" msgid="40711082435231673">"ຈ້ຳ"</string>
-    <string name="spoken_description_language_switch" msgid="5507091328222331316">"ສະລັບພາສາ"</string>
-    <string name="spoken_description_action_next" msgid="8636078276664150324">"ຕໍ່ໄປ"</string>
-    <string name="spoken_description_action_previous" msgid="800872415009336208">"ກ່ອນໜ້າ"</string>
-    <string name="spoken_description_shiftmode_on" msgid="5700440798609574589">"Shift ເປີດນຳໃຊ້ຢູ່"</string>
-    <string name="spoken_description_shiftmode_locked" msgid="593175803181701830">"Caps lock ເປີດນຳໃຊ້ຢູ່"</string>
-    <string name="spoken_description_shiftmode_off" msgid="657219998449174808">"Shift ປິດນຳໃຊ້ຢູ່"</string>
-    <string name="spoken_description_mode_symbol" msgid="7183343879909747642">"ໂຫມດສັນຍາລັກ"</string>
-    <string name="spoken_description_mode_alpha" msgid="3528307674390156956">"ໂຫມດ​ໂຕອັກ​ສອນ"</string>
-    <string name="spoken_description_mode_phone" msgid="6520207943132026264">"ໂຫມດໂທລະສັບ"</string>
-    <string name="spoken_description_mode_phone_shift" msgid="5499629753962641227">"ໂຫມດສັນຍາລັກໂທລະສັບ"</string>
-    <string name="announce_keyboard_hidden" msgid="8718927835531429807">"ແປ້ນ​ພິມ​ເຊື່ອງ​ໄວ້"</string>
-    <string name="announce_keyboard_mode" msgid="4729081055438508321">"ກຳລັງສະແດງແປ້ນພິມ <xliff:g id="MODE">%s</xliff:g>"</string>
-    <string name="keyboard_mode_date" msgid="3137520166817128102">"ວັນທີ"</string>
-    <string name="keyboard_mode_date_time" msgid="339593358488851072">"ວັນ​ທີ​ແລະ​ເວ​ລາ"</string>
-    <string name="keyboard_mode_email" msgid="6216248078128294262">"email"</string>
-    <string name="keyboard_mode_im" msgid="1137405089766557048">"ຂໍ້ຄວາມ"</string>
-    <string name="keyboard_mode_number" msgid="7991623440699957069">"ໂຕເລກ"</string>
-    <string name="keyboard_mode_phone" msgid="6851627527401433229">"ໂທລະສັບ"</string>
-    <string name="keyboard_mode_text" msgid="6479436687899701619">"ຂໍ້ຄວາມ"</string>
-    <string name="keyboard_mode_time" msgid="4381856885582143277">"ເວລາ"</string>
-    <string name="keyboard_mode_url" msgid="1519819835514911218">"URL"</string>
-    <string name="voice_input" msgid="3583258583521397548">"ປຸ່ມປ້ອນຂໍ້ມູນດ້ວຍສຽງ"</string>
-    <string name="voice_input_modes_main_keyboard" msgid="3360660341121083174">"ແປ້ນພິມຫຼັກ"</string>
-    <string name="voice_input_modes_symbols_keyboard" msgid="7203213240786084067">"ໃນແປ້ນພິມສັນຍາລັກ"</string>
-    <string name="voice_input_modes_off" msgid="3745699748218082014">"ປິດ"</string>
-    <string name="voice_input_modes_summary_main_keyboard" msgid="6586544292900314339">"ໄມໃນແປ້ນພິມຫຼັກ"</string>
-    <string name="voice_input_modes_summary_symbols_keyboard" msgid="5233725927281932391">"ໄມໃນແປ້ນພິມສັນຍາລັກ"</string>
-    <string name="voice_input_modes_summary_off" msgid="63875609591897607">"ການປ້ອນຂໍ້ມູນດ້ວຍສຽງປິດນຳໃຊ້ຢູ່"</string>
-    <string name="configure_input_method" msgid="373356270290742459">"ຕັ້ງຄ່າຮູບແບບການປ້ອນຂໍ້ມູນ"</string>
-    <string name="language_selection_title" msgid="1651299598555326750">"ພາສາການປ້ອນຂໍ້ມູນ"</string>
-    <string name="send_feedback" msgid="1780431884109392046">"ສົ່ງຄຳຕິຊົມ"</string>
-    <string name="select_language" msgid="3693815588777926848">"ພາສາການປ້ອນຂໍ້ມູນ"</string>
-    <string name="hint_add_to_dictionary" msgid="573678656946085380">"ກົດອີກຄັ້ງເພື່ອບັນທຶກ"</string>
-    <string name="has_dictionary" msgid="6071847973466625007">"ມີວັດຈະນານຸກົມ"</string>
-    <string name="prefs_enable_log" msgid="6620424505072963557">"ເປີດນຳໃຊ້ຄຳຕິຊົມຈາກຜູ່ໃຊ້"</string>
-    <string name="prefs_description_log" msgid="7525225584555429211">"ຊ່ວຍເພີ່ມປະສິດທິພາບໂຕແກ້ໄຂການປ້ອນຂໍ້ມູນ ໂດຍການສົ່ງສະຖິຕິການນຳໃຊ້ ແລະການລາຍການຂໍ້ຜິດພາດໂດຍອັດຕະໂນມັດ"</string>
-    <string name="keyboard_layout" msgid="8451164783510487501">"ສີສັນແປ້ນພິມ"</string>
-    <string name="subtype_en_GB" msgid="88170601942311355">"ອັງກິດ (ສະຫະລາດຊະອານາຈັກ)"</string>
-    <string name="subtype_en_US" msgid="6160452336634534239">"ອັງກິດ (ສະຫະລັດຯ)"</string>
-    <string name="subtype_es_US" msgid="5583145191430180200">"ສະເປນ (ອາເມລິກາ)"</string>
-    <string name="subtype_with_layout_en_GB" msgid="2179097748724725906">"ພາສາອັງກິດ (ອັງກິດ) (<xliff:g id="LAYOUT">%s</xliff:g>)"</string>
-    <string name="subtype_with_layout_en_US" msgid="1362581347576714579">"ອັງກິດ (ອາເມລິກາ) (<xliff:g id="LAYOUT">%s</xliff:g>)"</string>
-    <string name="subtype_with_layout_es_US" msgid="6261791057007890189">"ແອສປາໂຍນ (ສະ​ຫະ​ລັດ​) (<xliff:g id="LAYOUT">%s</xliff:g>)"</string>
-    <string name="subtype_no_language" msgid="7137390094240139495">"ບໍ່ມີພາສາ (ໂຕອັກສອນ)"</string>
-    <string name="subtype_no_language_qwerty" msgid="244337630616742604">"ໂຕອັກສອນ (QWERTY)"</string>
-    <string name="subtype_no_language_qwertz" msgid="443066912507547976">"ໂຕອັກສອນ (QWERTZ)"</string>
-    <string name="subtype_no_language_azerty" msgid="8144348527575640087">"ໂຕອັກສອນ (AZERTY)"</string>
-    <string name="subtype_no_language_dvorak" msgid="1564494667584718094">"ໂຕອັກສອນ (Dvorak)"</string>
-    <string name="subtype_no_language_colemak" msgid="5837418400010302623">"ໂຕອັກສອນ (Colemak)"</string>
-    <string name="subtype_no_language_pcqwerty" msgid="5354918232046200018">"ໂຕອັກສອນ (PC)"</string>
-    <string name="custom_input_styles_title" msgid="8429952441821251512">"ຮູບແບບການປ້ອນຂໍ້ມູນສ່ວນຕົວ"</string>
-    <string name="add_style" msgid="6163126614514489951">"ເພີ່ມຮູບແບບ"</string>
-    <string name="add" msgid="8299699805688017798">"ເພີ່ມ"</string>
-    <string name="remove" msgid="4486081658752944606">"ລຶບອອກ"</string>
-    <string name="save" msgid="7646738597196767214">"ບັນທຶກ"</string>
-    <string name="subtype_locale" msgid="8576443440738143764">"ພາສາ"</string>
-    <string name="keyboard_layout_set" msgid="4309233698194565609">"ຮູບແບບ"</string>
-    <string name="custom_input_style_note_message" msgid="8826731320846363423">"ຮູບແບບການປ້ອນຂໍ້ມູນແບບສ່ວນຕົວຂອງທ່ານ ຕ້ອງຖືກເປີດນຳໃຊ້ຢູ່ກ່ອນທີ່ທ່ານຈະສາມາດໃຊ້ມັນໄດ້. ທ່ານຕ້ອງການທີ່ຈະເປີດໃຊ້ມັນດຽວນີ້ບໍ່?"</string>
-    <string name="enable" msgid="5031294444630523247">"ເປີດນຳໃຊ້"</string>
-    <string name="not_now" msgid="6172462888202790482">"ບໍ່ແມ່ນຕອນນີ້"</string>
-    <string name="custom_input_style_already_exists" msgid="8008728952215449707">"ຮູບແບບການປ້ອນຂໍ້ມູນທີ່ຄືກັນມີຢູ່ແລ້ວ: <xliff:g id="INPUT_STYLE_NAME">%s</xliff:g>"</string>
-    <string name="prefs_usability_study_mode" msgid="1261130555134595254">"ໂໝດການສຶກສາ Usability"</string>
-    <string name="prefs_key_longpress_timeout_settings" msgid="6102240298932897873">"ໄລຍະເວລາຂອງການກົດປຸ່ມ"</string>
-    <string name="prefs_keypress_vibration_duration_settings" msgid="7918341459947439226">"ໄລຍະເວລາຂອງການສັ່ນໃນການກົດປຸ່ມ"</string>
-    <string name="prefs_keypress_sound_volume_settings" msgid="6027007337036891623">"ລະດັບສຽງຂອງການກົດປຸ່ມ"</string>
-    <string name="prefs_read_external_dictionary" msgid="2588931418575013067">"ອ່ານໄຟລ໌ວັດຈະນານຸກົມພາຍນອກ"</string>
-    <string name="read_external_dictionary_no_files_message" msgid="4947420942224623792">"ບໍ່ມີໄຟລ໌ວັດຈະນານຸກົມໃນໂຟນເດີຂອງການດາວໂຫລດ"</string>
-    <string name="read_external_dictionary_multiple_files_title" msgid="7637749044265808628">"ເລືອກໄຟລ໌ວັດຈະນານຸກົມເພື່ອຕິດຕັ້ງ"</string>
-    <string name="read_external_dictionary_confirm_install_message" msgid="6898610163768980870">"ຕິດຕັ້ງໄຟລ໌ນີ້ສຳລັບ <xliff:g id="LOCALE_NAME">%s</xliff:g> ແທ້ບໍ່?"</string>
-    <string name="error" msgid="8940763624668513648">"ມີຂໍ້ຜິດພາດເກີດຂຶ້ນ"</string>
-    <string name="button_default" msgid="3988017840431881491">"ຄ່າເລີ່ມຕົ້ນ"</string>
-    <string name="setup_welcome_title" msgid="6112821709832031715">"ຍິນ​ດີ​ຕ້ອນ​ຮັບສູ່ <xliff:g id="APPLICATION_NAME">%s</xliff:g>"</string>
-    <string name="setup_welcome_additional_description" msgid="8150252008545768953">"ດ້ວຍການພິມແບບ Gesture"</string>
-    <string name="setup_start_action" msgid="8936036460897347708">"ເລີ່ມຕົ້ນ"</string>
-    <string name="setup_next_action" msgid="371821437915144603">"ຂັ້ນຕອນຕໍ່ໄປ"</string>
-    <string name="setup_steps_title" msgid="6400373034871816182">"ຕັ້ງຄ່າ <xliff:g id="APPLICATION_NAME">%s</xliff:g>"</string>
-    <string name="setup_step1_title" msgid="3147967630253462315">"ເປີດນຳໃຊ້ <xliff:g id="APPLICATION_NAME">%s</xliff:g>"</string>
-    <string name="setup_step1_instruction" msgid="2578631936624637241">"ກະລຸນາກວດເບິ່ງ \"<xliff:g id="APPLICATION_NAME">%s</xliff:g>\" ໃນການຕັ້ງຄ່າພາສາ &amp; ການປ້ອນຂໍ້ມູນຂອງທ່ານ. ນີ້ຈະເປັນການອະນຸຍາດໃຫ້ມັນເຮັດວຽກໃນອຸປະກອນຂອງທ່ານ"</string>
-    <string name="setup_step1_finished_instruction" msgid="10761482004957994">"<xliff:g id="APPLICATION_NAME">%s</xliff:g> ຖືກເປີດນຳໃຊ້ໃນການຕັ້ງຄ່າພາສາ &amp; ການປ້ອນຂໍ້ມູນຂອງທ່ານແລ້ວ, ສະນັ້ນຂັ້ນຕອນນີ້ແມ່ນສຳເລັດໄປແລ້ວ. ໄປທີ່ຂັ້ນຕອນຕໍ່ໄປ!"</string>
-    <string name="setup_step1_action" msgid="4366513534999901728">"ເປີດນຳໃຊ້ໃນການຕັ້ງຄ່າ"</string>
-    <string name="setup_step2_title" msgid="6860725447906690594">"ປ່ຽນເປັນ <xliff:g id="APPLICATION_NAME">%s</xliff:g>"</string>
-    <string name="setup_step2_instruction" msgid="9141481964870023336">"ຕໍ່ໄປ, ເລືອກເອົາ \"<xliff:g id="APPLICATION_NAME">%s</xliff:g>\" ເປັນຮູບແບບການປ້ອນຂໍ້ມູນຂອງທ່ານ."</string>
-    <string name="setup_step2_action" msgid="1660330307159824337">"ປ່ຽນຮູບແບບການປ້ອນຂໍ້ມູນ"</string>
-    <string name="setup_step3_title" msgid="3154757183631490281">"ຍິນດີດ້ວຍ, ທ່ານເຮັດແລ້ວໆ!"</string>
-    <string name="setup_step3_instruction" msgid="8025981829605426000">"ຕອນນີ້ທ່ານສາມາດພິມໃນແອັບຯທີ່ທ່ານມັກໄດ້ທຸກແອັບຯດ້ວຍ <xliff:g id="APPLICATION_NAME">%s</xliff:g>."</string>
-    <string name="setup_step3_action" msgid="600879797256942259">"ປັບຄ່າພາສາເພີ່ມເຕີມ"</string>
-    <string name="setup_finish_action" msgid="276559243409465389">"ສຳເລັດແລ້ວ"</string>
-    <string name="show_setup_wizard_icon" msgid="5008028590593710830">"ສະແດງໄອຄອນຂອງແອັບຯ"</string>
-    <string name="show_setup_wizard_icon_summary" msgid="4119998322536880213">"ສະແດງໄອຄອນຂອງແອັບຯໃນ Launcher"</string>
-    <string name="app_name" msgid="6320102637491234792">"ຜູ່​ສະ​ຫນອງ​ວັດຈະ​ນາ​ນຸ​ກົມ"</string>
-    <string name="dictionary_provider_name" msgid="3027315045397363079">"ຜູ່​ສະ​ຫນອງ​ວັດຈະ​ນາ​ນຸ​ກົມ"</string>
-    <string name="dictionary_service_name" msgid="6237472350693511448">"ບໍລິການວັດຈະນານຸກົມ"</string>
-    <string name="download_description" msgid="6014835283119198591">"ຂໍ້ມູນການອັບເດດວັດຈະນານຸກົມ"</string>
-    <string name="dictionary_settings_title" msgid="8091417676045693313">"ໂຕເສີມວັດຈະນານຸກົມ"</string>
-    <string name="dictionary_install_over_metered_network_prompt" msgid="3587517870006332980">"ມີວັດຈະນານຸກົມ"</string>
-    <string name="dictionary_settings_summary" msgid="5305694987799824349">"ການຕັ້ງຄ່າສຳລັບວັດຈະນານຸກົມ"</string>
-    <string name="user_dictionaries" msgid="3582332055892252845">"ວັດຈະນານຸກົມຜູ່ໃຊ້"</string>
-    <string name="default_user_dict_pref_name" msgid="1625055720489280530">"ວັດຈະນານຸກົມຜູ່ໃຊ້"</string>
-    <string name="dictionary_available" msgid="4728975345815214218">"ມີວັດຈະນານຸກົມ"</string>
-    <string name="dictionary_downloading" msgid="2982650524622620983">"ກຳລັງດາວໂຫລດ"</string>
-    <string name="dictionary_installed" msgid="8081558343559342962">"ຕິດຕັ້ງແລ້ວ"</string>
-    <string name="dictionary_disabled" msgid="8950383219564621762">"ຕິດຕັ້ງແລ້ວ, ປິດການນຳໃຊ້ແລ້ວ"</string>
-    <string name="cannot_connect_to_dict_service" msgid="9216933695765732398">"ມີປັນຫາໃນການເຊື່ອມຕໍ່ກັບບໍລິການວັດຈະນານຸກົມ"</string>
-    <string name="no_dictionaries_available" msgid="8039920716566132611">"ບໍ່ມີວັດຈະນານຸກົມ"</string>
-    <string name="check_for_updates_now" msgid="8087688440916388581">"ດຶງຂໍ້ມູນໃຫມ່"</string>
-    <string name="last_update" msgid="730467549913588780">"ອັບເດດຫຼ້າສຸດ"</string>
-    <string name="message_updating" msgid="4457761393932375219">"ກຳລັງກວດການອັບເດດ"</string>
-    <string name="message_loading" msgid="8689096636874758814">"ກຳລັງໂຫລດ..."</string>
-    <string name="main_dict_description" msgid="3072821352793492143">"ວັດຈະນານຸກົມຫຼັກ"</string>
-    <string name="cancel" msgid="6830980399865683324">"ຍົກເລີກ"</string>
-    <string name="install_dict" msgid="180852772562189365">"ຕິດຕັ້ງ"</string>
-    <string name="cancel_download_dict" msgid="7843340278507019303">"ຍົກເລີກ"</string>
-    <string name="delete_dict" msgid="756853268088330054">"ລຶບ"</string>
-    <string name="should_download_over_metered_prompt" msgid="2878629598667658845">"ພາສາທີ່ທ່ານເລືອກໃຊ້ໃນອຸປະກອນຂອງທ່ານນັ້ນ ມີວັດຈະນານຸກົມໃຫ້ໃຊ້ພ້ອມ.&lt;br/&gt; ພວກເຮົາແນະນຳໃຫ້ &lt;b&gt;ດາວໂຫລດ&lt;/b&gt; <xliff:g id="LANGUAGE">%1$s</xliff:g> ວັດຈະນານຸກົມດັ່ງກ່າວ ເພື່ອເພີ່ມປະສົບການໃນການພິມຂອງທ່ານ.&lt;br/&gt; &lt;br/&gt; ການດາວໂຫລດອາດຈະໃຊ້ເວລາພຽງໜຶ່ງເຖິງສອງນາທີ ໂດຍການໃຊ້ 3G. ທ່ານອາດຈະເສຍຄ່າບໍລິການສຳລັບອິນເຕີເນັດ ຫາກທ່ານບໍ່ມີ &lt;b&gt;ການນຳໃຊ້ອິນເຕີເນັດແບບບໍ່ຈຳກັດ&lt;/b&gt;.&lt;br/&gt; ຫາກທ່ານບໍ່ແນ່ໃຈວ່າຮູບແບບການໃຊ້ໃດທີ່ທ່ານມີຢູ່ ພວກເຮົາແນະນຳໃຫ້ຊອກຫາການເຊື່ອມຕໍ່ Wi-Fi ເພື່ອດາວໂຫລດມັນໂດຍອັດຕະໂນມັດ.&lt;br/&gt; &lt;br/&gt; ເຄັດລັບ: ທ່ານສາມາດດາວໂຫລດ ແລະ ລຶບວັດຈະນານຸກົມໄດ້ທີ່ &lt;b&gt;ພາສາ &amp; ການປ້ອນຂໍ້ມູນ&lt;/b&gt; ຢູ່ໃນເມນູ &lt;b&gt;ການຕັ້ງຄ່າ&lt;/b&gt; ຂອງອຸປະກອນພົກພາຂອງທ່ານ."</string>
-    <string name="download_over_metered" msgid="1643065851159409546">"ດາວໂຫລດດຽວນີ້ (<xliff:g id="SIZE_IN_MEGABYTES">%1$.1f</xliff:g>MB)"</string>
-    <string name="do_not_download_over_metered" msgid="2176209579313941583">"ດາວ​ໂຫລດຜ່ານ Wi-Fi"</string>
-    <string name="dict_available_notification_title" msgid="6514288591959117288">"ວັດຈະນານຸກົມສາມາດໃຊ້ໄດ້ກັບ <xliff:g id="LANGUAGE">%1$s</xliff:g>"</string>
-    <string name="dict_available_notification_description" msgid="1075194169443163487">"ກົດທີ່ກວດຄືນ ແລະ ດາວໂຫລດ"</string>
-    <string name="toast_downloading_suggestions" msgid="1313027353588566660">"ກຳລັງດາວໂຫລດ: ການແນະນຳສຳລັບ <xliff:g id="LANGUAGE">%1$s</xliff:g> ແລະມັນຈະພ້ອມນຳໃຊ້ໄວໆນີ້"</string>
-    <string name="version_text" msgid="2715354215568469385">"ເວີຊັນ <xliff:g id="VERSION_NUMBER">%1$s</xliff:g>"</string>
-    <string name="user_dict_settings_add_menu_title" msgid="1254195365689387076">"ເພີ່ມ"</string>
-    <string name="user_dict_settings_add_dialog_title" msgid="4096700390211748168">"ເພີ່ມໄປທີ່ວັດຈະນານຸກົມ"</string>
-    <string name="user_dict_settings_add_screen_title" msgid="5818914331629278758">"ປະໂຫຍກ"</string>
-    <string name="user_dict_settings_add_dialog_more_options" msgid="5671682004887093112">"ຕົວເລືອກເພີ່ມເຕີມ"</string>
-    <string name="user_dict_settings_add_dialog_less_options" msgid="2716586567241724126">"ຕົວເລືອກໜ້ອຍລົງ"</string>
-    <string name="user_dict_settings_add_dialog_confirm" msgid="4703129507388332950">"ຕົກລົງ"</string>
-    <string name="user_dict_settings_add_word_option_name" msgid="6665558053408962865">"ຄຳສັບ:"</string>
-    <string name="user_dict_settings_add_shortcut_option_name" msgid="3094731590655523777">"ທາງລັດ:"</string>
-    <string name="user_dict_settings_add_locale_option_name" msgid="4738643440987277705">"ພາສາ:"</string>
-    <string name="user_dict_settings_add_word_hint" msgid="4902434148985906707">"ພິມ​ຄໍາ​ສັບ​ໃດ​ນຶ່ງ"</string>
-    <string name="user_dict_settings_add_shortcut_hint" msgid="2265453012555060178">"ໂຕເລືອກທາງລັດ"</string>
-    <string name="user_dict_settings_edit_dialog_title" msgid="3765774633869590352">"ແກ້ໄຂຄຳສັບ"</string>
-    <string name="user_dict_settings_context_menu_edit_title" msgid="6812255903472456302">"ແກ້ໄຂ"</string>
-    <string name="user_dict_settings_context_menu_delete_title" msgid="8142932447689461181">"ລຶບ"</string>
-    <string name="user_dict_settings_empty_text" msgid="558499587532668203">"ທ່ານບໍ່ມີຄຳສັບໃດໆໃນວັດຈະນານຸກົມຜູ່ໃຊ້ເທື່ອ. ເພີ່ມຄຳສັບໄດ້ໂດຍການສຳພັດທີ່ປຸ່ມ ເພີ່ມ (+)."</string>
-    <string name="user_dict_settings_all_languages" msgid="8276126583216298886">"ສໍາ​ລັບ​ທຸກໆ​ພາ​ສາ"</string>
-    <string name="user_dict_settings_more_languages" msgid="7131268499685180461">"ພາສາອື່ນໆ..."</string>
-    <string name="user_dict_settings_delete" msgid="110413335187193859">"ລຶບ"</string>
-    <string name="user_dict_fast_scroll_alphabet" msgid="5431919401558285473">" ABCDEFGHIJKLMNOPQRSTUVWXYZ"</string>
-</resources>
diff --git a/java/res/xml-sw768dp/kbd_more_keys_keyboard_template.xml b/java/res/values-lo/donottranslate.xml
similarity index 68%
copy from java/res/xml-sw768dp/kbd_more_keys_keyboard_template.xml
copy to java/res/values-lo/donottranslate.xml
index f89a0a6..a9893fe 100644
--- a/java/res/xml-sw768dp/kbd_more_keys_keyboard_template.xml
+++ b/java/res/values-lo/donottranslate.xml
@@ -2,7 +2,7 @@
 <!--
 /*
 **
-** Copyright 2008, The Android Open Source Project
+** Copyright 2013, 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.
@@ -17,10 +17,7 @@
 ** limitations under the License.
 */
 -->
-
-<Keyboard xmlns:latin="http://schemas.android.com/apk/res/com.android.inputmethod.latin"
-    latin:keyWidth="5.0%p"
-    latin:rowHeight="@dimen/popup_key_height"
-    style="?attr/moreKeysKeyboardStyle"
-    >
-</Keyboard>
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <!-- Whether this language uses spaces between words -->
+    <bool name="current_language_has_spaces">false</bool>
+</resources>
diff --git a/java/res/values-lo/strings.xml b/java/res/values-lo/strings.xml
deleted file mode 100644
index cc15a42..0000000
--- a/java/res/values-lo/strings.xml
+++ /dev/null
@@ -1,242 +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.
-*/
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="english_ime_input_options" msgid="3909945612939668554">"ຕົວເລືອກການປ້ອນຂໍ້ມູນ"</string>
-    <string name="english_ime_research_log" msgid="8492602295696577851">"Research Log Commands"</string>
-    <string name="use_contacts_for_spellchecking_option_title" msgid="5374120998125353898">"ເບິ່ງທີ່ຊື່ຂອງລາຍຊື່ຜູ່ຕິດຕໍ່"</string>
-    <string name="use_contacts_for_spellchecking_option_summary" msgid="8754413382543307713">"ໂຕຊ່ວຍສະກົດໃຊ້ຂໍ້ມູນຈາກລາຍການຂອງລາຍຊື່ຜູ່ຕິດຕໍ່ຂອງທ່ານ"</string>
-    <string name="vibrate_on_keypress" msgid="5258079494276955460">"ການສັ່ນໃນການພິມ"</string>
-    <string name="sound_on_keypress" msgid="6093592297198243644">"ສຽງໃນການກົດປຸ່ມ"</string>
-    <string name="popup_on_keypress" msgid="123894815723512944">"ໂຕອັກສອນເວລາພິມ"</string>
-    <string name="general_category" msgid="1859088467017573195">"ທົ່ວໄປ"</string>
-    <string name="correction_category" msgid="2236750915056607613">"ໂຕຊ່ວຍແປງຂໍ້ຄວາມ"</string>
-    <string name="gesture_typing_category" msgid="497263612130532630">"ການພິມແບບ Gesture"</string>
-    <string name="misc_category" msgid="6894192814868233453">"ໂຕເລືອກ​ອື່ນໆ"</string>
-    <string name="advanced_settings" msgid="362895144495591463">"ການຕັ້ງຄ່າຂັ້ນສູງ"</string>
-    <string name="advanced_settings_summary" msgid="4487980456152830271">"ຕົວເລືອກສຳລັບຜູ່ທີ່ຊຳນານ"</string>
-    <string name="include_other_imes_in_language_switch_list" msgid="4533689960308565519">"ປ່ຽນໄປໃຊ້ການປ້ອນຂໍ້ມູນແບບອື່ນ"</string>
-    <string name="include_other_imes_in_language_switch_list_summary" msgid="840637129103317635">"ໂຕປ່ຽນພາສາເປັນທັງໂຕປ່ຽນຮູບແບບການປ້ອນຂໍ້ມູນເຊັ່ນກັນ"</string>
-    <string name="show_language_switch_key" msgid="5915478828318774384">"ປຸ່ມປ່ຽນພາສາ"</string>
-    <string name="show_language_switch_key_summary" msgid="7343403647474265713">"ສະແດງໃນເວລາທີ່ຕົວເລືອກການປ້ອນຂໍ້ມູນຫຼາຍໂຕຖືກເປີດຢູ່"</string>
-    <string name="sliding_key_input_preview" msgid="6604262359510068370">"ສະແດງໂຕບົ່ງບອກການສະໄລ້"</string>
-    <string name="sliding_key_input_preview_summary" msgid="6340524345729093886">"ສະແດງແນວທາງໃນຂະນະທີ່ສະໄລ້ຈາກ Shift ຫຼື ປຸ່ມເຄື່ອງໝາຍ"</string>
-    <string name="key_preview_popup_dismiss_delay" msgid="6213164897443068248">"ໄລຍະເວລາການສະແດງໂຕອັກສອນ"</string>
-    <string name="key_preview_popup_dismiss_no_delay" msgid="2096123151571458064">"ບໍ່ຕ້ອໜ່ວງເວລາ"</string>
-    <string name="key_preview_popup_dismiss_default_delay" msgid="2166964333903906734">"ຄ່າເລີ່ມຕົ້ນ"</string>
-    <string name="abbreviation_unit_milliseconds" msgid="8700286094028323363">"<xliff:g id="MILLISECONDS">%s</xliff:g>ms"</string>
-    <string name="settings_system_default" msgid="6268225104743331821">"ຄ່າເລີ່ມຕົ້ນຂອງລະບົບ"</string>
-    <string name="use_contacts_dict" msgid="4435317977804180815">"ແນະນຳລາຍຊື່ຜູ່ຕິດຕໍ່"</string>
-    <string name="use_contacts_dict_summary" msgid="6599983334507879959">"ໃຊ້ຊື່ຈາກລາຍຊື່ຜູ່ຕິດຕໍ່ສຳລັບການແນະນຳ ແລະ ການຊ່ວຍແກ້ຄຳ"</string>
-    <string name="use_double_space_period" msgid="8781529969425082860">"ຍະຫວ່າງສອງເທື່ອເພື່ອໃສ່ຈ້ຳເມັດ"</string>
-    <string name="use_double_space_period_summary" msgid="6532892187247952799">"ກົດທີ່ປຸ່ມຍະຫວ່າງສອງເທື່ອເພື່ອໃສ່ຈ້ຳເມັດແລ້ວຕາມດ້ວຍການຍະຫວ່າງ"</string>
-    <string name="auto_cap" msgid="1719746674854628252">"ເຮັດໂຕພິມໃຫຍ່ອັດຕະໂນມັດ"</string>
-    <string name="auto_cap_summary" msgid="7934452761022946874">"ເຮັດໂຕພິມໃຫຍ່ໃຫ້ໂຕອັກສອນທຳອິດຂອງແຕ່ລະຄຳໃນປະໂຫຍກ"</string>
-    <string name="edit_personal_dictionary" msgid="3996910038952940420">"ວັດຈະນານຸກົມສ່ວນໂຕ"</string>
-    <string name="configure_dictionaries_title" msgid="4238652338556902049">"ໂຕເສີມວັດຈະນານຸກົມ"</string>
-    <string name="main_dictionary" msgid="4798763781818361168">"ວັດຈະນານຸກົມຫຼັກ"</string>
-    <string name="prefs_show_suggestions" msgid="8026799663445531637">"ສະແດງການແນະນຳຄຳທີ່ຖືກຕ້ອງ"</string>
-    <string name="prefs_show_suggestions_summary" msgid="1583132279498502825">"ສະແດງຄຳສັບທີ່ແນະນຳໃນເວລາທີ່ກຳລັງພິມ"</string>
-    <string name="prefs_suggestion_visibility_show_name" msgid="3219916594067551303">"ສະແດງຕະຫລອດ"</string>
-    <string name="prefs_suggestion_visibility_show_only_portrait_name" msgid="3859783767435239118">"ສະແດງໃນໂຫມດແນວຕັ້ງ"</string>
-    <string name="prefs_suggestion_visibility_hide_name" msgid="6309143926422234673">"ເຊື່ອງໄວ້ຕະຫລອດ"</string>
-    <string name="prefs_block_potentially_offensive_title" msgid="5078480071057408934">"ປິດກັ້ນຄຳທີ່ບໍ່ສຸພາບ"</string>
-    <string name="prefs_block_potentially_offensive_summary" msgid="2371835479734991364">"ຫ້າມແນະນຳຄຳທີ່ບໍ່ສຸພາບ"</string>
-    <string name="auto_correction" msgid="7630720885194996950">"ໂຕຊ່ວຍສະກົດຄຳ"</string>
-    <string name="auto_correction_summary" msgid="5625751551134658006">"ການຍະຫວ່າງ ແລະ ການໃສ່ເຄື່ອງໝາຍຈະຖືກປ່ຽນແປງໃຫ້ຖືກຕ້ອງ ໃນຄຳທີ່ພິມຜິດໂດຍອັດຕະໂນມັດ"</string>
-    <string name="auto_correction_threshold_mode_off" msgid="8470882665417944026">"ປິດ"</string>
-    <string name="auto_correction_threshold_mode_modest" msgid="8788366690620799097">"ປານກາງ"</string>
-    <string name="auto_correction_threshold_mode_aggressive" msgid="7319007299148899623">"ສູງ"</string>
-    <string name="auto_correction_threshold_mode_very_aggressive" msgid="1853309024129480416">"ສູງສຸດ"</string>
-    <string name="bigram_prediction" msgid="1084449187723948550">"ການແນະນຳຄຳຕໍ່ໄປ"</string>
-    <string name="bigram_prediction_summary" msgid="3896362682751109677">"ໃຊ້ຄຳທີ່ຜ່ານມາໃນການແນະນຳຄຳ"</string>
-    <string name="gesture_input" msgid="826951152254563827">"ເປີດນຳໃຊ້ການພິມແບບ Gesture"</string>
-    <string name="gesture_input_summary" msgid="9180350639305731231">"ໃສ່ຄຳສັບລົງໄປໂດຍການສະໄລ້ຜ່ານໂຕອັກສອນ"</string>
-    <string name="gesture_preview_trail" msgid="3802333369335722221">"ສະແດງຫາງຂອງ Gesture"</string>
-    <string name="gesture_floating_preview_text" msgid="4443240334739381053">"ມີຄຳຕົວຢ່າງລອຍຂຶ້ນມາ"</string>
-    <string name="gesture_floating_preview_text_summary" msgid="4472696213996203533">"ເບິ່ງຄຳທີ່ຖືກແນະນຳໃນເວລາທີ່ກຳລັງຊີ້"</string>
-    <string name="added_word" msgid="8993883354622484372">"<xliff:g id="WORD">%s</xliff:g> : ບັນທຶກແລ້ວ"</string>
-    <string name="label_go_key" msgid="1635148082137219148">"ໄປ"</string>
-    <string name="label_next_key" msgid="362972844525672568">"ຕໍ່ໄປ"</string>
-    <string name="label_previous_key" msgid="1211868118071386787">"ກ່ອນໜ້າ"</string>
-    <string name="label_done_key" msgid="2441578748772529288">"ແລ້ວໆ"</string>
-    <string name="label_send_key" msgid="2815056534433717444">"ສົ່ງ"</string>
-    <string name="label_pause_key" msgid="181098308428035340">"ຄ້າງໄວ້"</string>
-    <string name="label_wait_key" msgid="6402152600878093134">"ລໍຖ້າ"</string>
-    <string name="spoken_use_headphones" msgid="896961781287283493">"ສຽບສາຍຫູຟັງເພື່ອຟັງລະຫັດຜ່ານ."</string>
-    <string name="spoken_current_text_is" msgid="2485723011272583845">"ຂໍ້ຄວາມປະຈຸບັນແມ່ນ %s"</string>
-    <string name="spoken_no_text_entered" msgid="7479685225597344496">"ບໍ່ມີການໃສ່ຂໍ້ຄວາມ"</string>
-    <string name="spoken_description_unknown" msgid="3197434010402179157">"ລະຫັດກະແຈ %d"</string>
-    <string name="spoken_description_shift" msgid="244197883292549308">"Shift"</string>
-    <string name="spoken_description_shift_shifted" msgid="1681877323344195035">"Shift ເປີດນຳໃຊ້ຢູ່ (ກົດເພື່ອປິດນຳໃຊ້)"</string>
-    <string name="spoken_description_caps_lock" msgid="3276478269526304432">"Caps lock ເປີດຢູ່ (ກົດເພື່ອປິດນຳໃຊ້)"</string>
-    <string name="spoken_description_delete" msgid="8740376944276199801">"ລຶບ"</string>
-    <string name="spoken_description_to_symbol" msgid="5486340107500448969">"ສັນ​ຍາ​ລັກ"</string>
-    <string name="spoken_description_to_alpha" msgid="23129338819771807">"ໂຕອັກ​ສອນ"</string>
-    <string name="spoken_description_to_numeric" msgid="591752092685161732">"ໂຕເລກ"</string>
-    <string name="spoken_description_settings" msgid="4627462689603838099">"ການຕັ້ງຄ່າ"</string>
-    <string name="spoken_description_tab" msgid="2667716002663482248">"ແທັບ"</string>
-    <string name="spoken_description_space" msgid="2582521050049860859">"ຍະຫວ່າງ"</string>
-    <string name="spoken_description_mic" msgid="615536748882611950">"ການປ້ອນຂໍ້ມູນດ້ວຍສຽງ"</string>
-    <string name="spoken_description_smiley" msgid="2256309826200113918">"ຮອຍຍິ້ມ"</string>
-    <string name="spoken_description_return" msgid="8178083177238315647">"ກັບຄືນ"</string>
-    <string name="spoken_description_search" msgid="1247236163755920808">"ຊອກຫາ"</string>
-    <string name="spoken_description_dot" msgid="40711082435231673">"ຈ້ຳ"</string>
-    <string name="spoken_description_language_switch" msgid="5507091328222331316">"ສະລັບພາສາ"</string>
-    <string name="spoken_description_action_next" msgid="8636078276664150324">"ຕໍ່ໄປ"</string>
-    <string name="spoken_description_action_previous" msgid="800872415009336208">"ກ່ອນໜ້າ"</string>
-    <string name="spoken_description_shiftmode_on" msgid="5700440798609574589">"Shift ເປີດນຳໃຊ້ຢູ່"</string>
-    <string name="spoken_description_shiftmode_locked" msgid="593175803181701830">"Caps lock ເປີດນຳໃຊ້ຢູ່"</string>
-    <string name="spoken_description_shiftmode_off" msgid="657219998449174808">"Shift ປິດນຳໃຊ້ຢູ່"</string>
-    <string name="spoken_description_mode_symbol" msgid="7183343879909747642">"ໂຫມດສັນຍາລັກ"</string>
-    <string name="spoken_description_mode_alpha" msgid="3528307674390156956">"ໂຫມດ​ໂຕອັກ​ສອນ"</string>
-    <string name="spoken_description_mode_phone" msgid="6520207943132026264">"ໂຫມດໂທລະສັບ"</string>
-    <string name="spoken_description_mode_phone_shift" msgid="5499629753962641227">"ໂຫມດສັນຍາລັກໂທລະສັບ"</string>
-    <string name="announce_keyboard_hidden" msgid="8718927835531429807">"ແປ້ນ​ພິມ​ເຊື່ອງ​ໄວ້"</string>
-    <string name="announce_keyboard_mode" msgid="4729081055438508321">"ກຳລັງສະແດງແປ້ນພິມ <xliff:g id="MODE">%s</xliff:g>"</string>
-    <string name="keyboard_mode_date" msgid="3137520166817128102">"ວັນທີ"</string>
-    <string name="keyboard_mode_date_time" msgid="339593358488851072">"ວັນ​ທີ​ແລະ​ເວ​ລາ"</string>
-    <string name="keyboard_mode_email" msgid="6216248078128294262">"ອີເມວ"</string>
-    <string name="keyboard_mode_im" msgid="1137405089766557048">"ຂໍ້ຄວາມ"</string>
-    <string name="keyboard_mode_number" msgid="7991623440699957069">"ໂຕເລກ"</string>
-    <string name="keyboard_mode_phone" msgid="6851627527401433229">"ໂທລະສັບ"</string>
-    <string name="keyboard_mode_text" msgid="6479436687899701619">"ຂໍ້ຄວາມ"</string>
-    <string name="keyboard_mode_time" msgid="4381856885582143277">"ເວລາ"</string>
-    <string name="keyboard_mode_url" msgid="1519819835514911218">"URL"</string>
-    <string name="voice_input" msgid="3583258583521397548">"ປຸ່ມປ້ອນຂໍ້ມູນດ້ວຍສຽງ"</string>
-    <string name="voice_input_modes_main_keyboard" msgid="3360660341121083174">"ແປ້ນພິມຫຼັກ"</string>
-    <string name="voice_input_modes_symbols_keyboard" msgid="7203213240786084067">"ໃນແປ້ນພິມສັນຍາລັກ"</string>
-    <string name="voice_input_modes_off" msgid="3745699748218082014">"ປິດ"</string>
-    <string name="voice_input_modes_summary_main_keyboard" msgid="6586544292900314339">"ໄມໃນແປ້ນພິມຫຼັກ"</string>
-    <string name="voice_input_modes_summary_symbols_keyboard" msgid="5233725927281932391">"ໄມໃນແປ້ນພິມສັນຍາລັກ"</string>
-    <string name="voice_input_modes_summary_off" msgid="63875609591897607">"ການປ້ອນຂໍ້ມູນດ້ວຍສຽງປິດນຳໃຊ້ຢູ່"</string>
-    <string name="configure_input_method" msgid="373356270290742459">"ຕັ້ງຄ່າຮູບແບບການປ້ອນຂໍ້ມູນ"</string>
-    <string name="language_selection_title" msgid="1651299598555326750">"ພາສາການປ້ອນຂໍ້ມູນ"</string>
-    <string name="send_feedback" msgid="1780431884109392046">"ສົ່ງຄຳຕິຊົມ"</string>
-    <string name="select_language" msgid="3693815588777926848">"ພາສາການປ້ອນຂໍ້ມູນ"</string>
-    <string name="hint_add_to_dictionary" msgid="573678656946085380">"ກົດອີກຄັ້ງເພື່ອບັນທຶກ"</string>
-    <string name="has_dictionary" msgid="6071847973466625007">"ມີວັດຈະນານຸກົມ"</string>
-    <string name="prefs_enable_log" msgid="6620424505072963557">"ເປີດນຳໃຊ້ຄຳຕິຊົມຈາກຜູ່ໃຊ້"</string>
-    <string name="prefs_description_log" msgid="7525225584555429211">"ຊ່ວຍເພີ່ມປະສິດທິພາບໂຕແກ້ໄຂການປ້ອນຂໍ້ມູນ ໂດຍການສົ່ງສະຖິຕິການນຳໃຊ້ ແລະການລາຍການຂໍ້ຜິດພາດໂດຍອັດຕະໂນມັດ"</string>
-    <string name="keyboard_layout" msgid="8451164783510487501">"ສີສັນແປ້ນພິມ"</string>
-    <string name="subtype_en_GB" msgid="88170601942311355">"ອັງກິດ (UK)"</string>
-    <string name="subtype_en_US" msgid="6160452336634534239">"ອັງກິດ (ອາເມລິກາ)"</string>
-    <string name="subtype_es_US" msgid="5583145191430180200">"ສະເປນ (ອາເມລິກາ)"</string>
-    <string name="subtype_with_layout_en_GB" msgid="2179097748724725906">"ພາສາອັງກິດ (ອັງກິດ) (<xliff:g id="LAYOUT">%s</xliff:g>)"</string>
-    <string name="subtype_with_layout_en_US" msgid="1362581347576714579">"ອັງກິດ (ອາເມລິກາ) (<xliff:g id="LAYOUT">%s</xliff:g>)"</string>
-    <string name="subtype_with_layout_es_US" msgid="6261791057007890189">"ແອສປາໂຍນ (ສະ​ຫະ​ລັດ​) (<xliff:g id="LAYOUT">%s</xliff:g>)"</string>
-    <string name="subtype_no_language" msgid="7137390094240139495">"ບໍ່ມີພາສາ (ໂຕອັກສອນ)"</string>
-    <string name="subtype_no_language_qwerty" msgid="244337630616742604">"ໂຕອັກສອນ (QWERTY​)"</string>
-    <string name="subtype_no_language_qwertz" msgid="443066912507547976">"ໂຕອັກສອນ (QWERTZ​)"</string>
-    <string name="subtype_no_language_azerty" msgid="8144348527575640087">"ໂຕອັກສອນ (AZERTY​)"</string>
-    <string name="subtype_no_language_dvorak" msgid="1564494667584718094">"ໂຕອັກສອນ (Dvorak​)"</string>
-    <string name="subtype_no_language_colemak" msgid="5837418400010302623">"ໂຕອັກສອນ (Colemak​)"</string>
-    <string name="subtype_no_language_pcqwerty" msgid="5354918232046200018">"ໂຕອັກສອນ (PC)"</string>
-    <string name="custom_input_styles_title" msgid="8429952441821251512">"ຮູບແບບການປ້ອນຂໍ້ມູນສ່ວນຕົວ"</string>
-    <string name="add_style" msgid="6163126614514489951">"ເພີ່ມຮູບແບບ"</string>
-    <string name="add" msgid="8299699805688017798">"ເພີ່ມ"</string>
-    <string name="remove" msgid="4486081658752944606">"ລຶບອອກ"</string>
-    <string name="save" msgid="7646738597196767214">"ບັນທຶກ"</string>
-    <string name="subtype_locale" msgid="8576443440738143764">"ພາສາ"</string>
-    <string name="keyboard_layout_set" msgid="4309233698194565609">"ຮູບແບບ"</string>
-    <string name="custom_input_style_note_message" msgid="8826731320846363423">"ຮູບແບບການປ້ອນຂໍ້ມູນແບບສ່ວນຕົວຂອງທ່ານ ຕ້ອງຖືກເປີດນຳໃຊ້ຢູ່ກ່ອນທີ່ທ່ານຈະສາມາດໃຊ້ມັນໄດ້. ທ່ານຕ້ອງການທີ່ຈະເປີດໃຊ້ມັນດຽວນີ້ບໍ່?"</string>
-    <string name="enable" msgid="5031294444630523247">"ເປີດນຳໃຊ້"</string>
-    <string name="not_now" msgid="6172462888202790482">"ບໍ່ແມ່ນຕອນນີ້"</string>
-    <string name="custom_input_style_already_exists" msgid="8008728952215449707">"ຮູບແບບການປ້ອນຂໍ້ມູນທີ່ຄືກັນມີຢູ່ແລ້ວ: <xliff:g id="INPUT_STYLE_NAME">%s</xliff:g>"</string>
-    <string name="prefs_usability_study_mode" msgid="1261130555134595254">"ໂໝດການສຶກສາ Usability"</string>
-    <string name="prefs_key_longpress_timeout_settings" msgid="6102240298932897873">"ໄລຍະເວລາຂອງການກົດປຸ່ມ"</string>
-    <string name="prefs_keypress_vibration_duration_settings" msgid="7918341459947439226">"ໄລຍະເວລາຂອງການສັ່ນໃນການກົດປຸ່ມ"</string>
-    <string name="prefs_keypress_sound_volume_settings" msgid="6027007337036891623">"ລະດັບສຽງຂອງການກົດປຸ່ມ"</string>
-    <string name="prefs_read_external_dictionary" msgid="2588931418575013067">"ອ່ານໄຟລ໌ວັດຈະນານຸກົມພາຍນອກ"</string>
-    <string name="read_external_dictionary_no_files_message" msgid="4947420942224623792">"ບໍ່ມີໄຟລ໌ວັດຈະນານຸກົມໃນໂຟນເດີຂອງການດາວໂຫລດ"</string>
-    <string name="read_external_dictionary_multiple_files_title" msgid="7637749044265808628">"ເລືອກໄຟລ໌ວັດຈະນານຸກົມເພື່ອຕິດຕັ້ງ"</string>
-    <string name="read_external_dictionary_confirm_install_message" msgid="6898610163768980870">"ຕິດຕັ້ງໄຟລ໌ນີ້ສຳລັບ <xliff:g id="LOCALE_NAME">%s</xliff:g> ແທ້ບໍ່?"</string>
-    <string name="error" msgid="8940763624668513648">"ມີຂໍ້ຜິດພາດເກີດຂຶ້ນ"</string>
-    <string name="button_default" msgid="3988017840431881491">"ຄ່າເລີ່ມຕົ້ນ"</string>
-    <string name="setup_welcome_title" msgid="6112821709832031715">"ຍິນ​ດີ​ຕ້ອນ​ຮັບສູ່ <xliff:g id="APPLICATION_NAME">%s</xliff:g>"</string>
-    <string name="setup_welcome_additional_description" msgid="8150252008545768953">"ດ້ວຍການພິມແບບ Gesture"</string>
-    <string name="setup_start_action" msgid="8936036460897347708">"ເລີ່ມກັນເລີຍ!"</string>
-    <string name="setup_next_action" msgid="371821437915144603">"ຂັ້ນຕອນຕໍ່ໄປ"</string>
-    <string name="setup_steps_title" msgid="6400373034871816182">"ຕັ້ງຄ່າ <xliff:g id="APPLICATION_NAME">%s</xliff:g>"</string>
-    <string name="setup_step1_title" msgid="3147967630253462315">"ເປີດນຳໃຊ້ <xliff:g id="APPLICATION_NAME">%s</xliff:g>"</string>
-    <string name="setup_step1_instruction" msgid="2578631936624637241">"ກະລຸນາກວດເບິ່ງ \"<xliff:g id="APPLICATION_NAME">%s</xliff:g>\" ໃນການຕັ້ງຄ່າພາສາ &amp; ການປ້ອນຂໍ້ມູນຂອງທ່ານ. ນີ້ຈະເປັນການອະນຸຍາດໃຫ້ມັນເຮັດວຽກໃນອຸປະກອນຂອງທ່ານ"</string>
-    <string name="setup_step1_finished_instruction" msgid="10761482004957994">"<xliff:g id="APPLICATION_NAME">%s</xliff:g> ຖືກເປີດນຳໃຊ້ໃນການຕັ້ງຄ່າພາສາ &amp; ການປ້ອນຂໍ້ມູນຂອງທ່ານແລ້ວ, ສະນັ້ນຂັ້ນຕອນນີ້ແມ່ນສຳເລັດໄປແລ້ວ. ໄປທີ່ຂັ້ນຕອນຕໍ່ໄປ!"</string>
-    <string name="setup_step1_action" msgid="4366513534999901728">"ເປີດນຳໃຊ້ໃນການຕັ້ງຄ່າ"</string>
-    <string name="setup_step2_title" msgid="6860725447906690594">"ປ່ຽນເປັນ <xliff:g id="APPLICATION_NAME">%s</xliff:g>"</string>
-    <string name="setup_step2_instruction" msgid="9141481964870023336">"ຕໍ່ໄປ, ເລືອກເອົາ \"<xliff:g id="APPLICATION_NAME">%s</xliff:g>\" ເປັນຮູບແບບການປ້ອນຂໍ້ມູນຂອງທ່ານ."</string>
-    <string name="setup_step2_action" msgid="1660330307159824337">"ປ່ຽນຮູບແບບການປ້ອນຂໍ້ມູນ"</string>
-    <string name="setup_step3_title" msgid="3154757183631490281">"ຍິນດີດ້ວຍ, ທ່ານເຮັດແລ້ວໆ!"</string>
-    <string name="setup_step3_instruction" msgid="8025981829605426000">"ຕອນນີ້ທ່ານສາມາດພິມໃນແອັບຯທີ່ທ່ານມັກໄດ້ທຸກແອັບຯດ້ວຍ <xliff:g id="APPLICATION_NAME">%s</xliff:g>."</string>
-    <string name="setup_step3_action" msgid="600879797256942259">"ປັບຄ່າພາສາເພີ່ມເຕີມ"</string>
-    <string name="setup_finish_action" msgid="276559243409465389">"ສຳເລັດແລ້ວ"</string>
-    <string name="show_setup_wizard_icon" msgid="5008028590593710830">"ສະແດງໄອຄອນຂອງແອັບຯ"</string>
-    <string name="show_setup_wizard_icon_summary" msgid="4119998322536880213">"ສະແດງໄອຄອນຂອງແອັບຯໃນ Launcher"</string>
-    <string name="app_name" msgid="6320102637491234792">"ຜູ່​ສະ​ຫນອງ​ວັດຈະ​ນາ​ນຸ​ກົມ"</string>
-    <string name="dictionary_provider_name" msgid="3027315045397363079">"ຜູ່​ສະ​ຫນອງ​ວັດຈະ​ນາ​ນຸ​ກົມ"</string>
-    <string name="dictionary_service_name" msgid="6237472350693511448">"ບໍລິການວັດຈະນານຸກົມ"</string>
-    <string name="download_description" msgid="6014835283119198591">"ຂໍ້ມູນການອັບເດດວັດຈະນານຸກົມ"</string>
-    <string name="dictionary_settings_title" msgid="8091417676045693313">"ໂຕເສີມວັດຈະນານຸກົມ"</string>
-    <string name="dictionary_install_over_metered_network_prompt" msgid="3587517870006332980">"ມີວັດຈະນານຸກົມ"</string>
-    <string name="dictionary_settings_summary" msgid="5305694987799824349">"ການຕັ້ງຄ່າສຳລັບວັດຈະນານຸກົມ"</string>
-    <string name="user_dictionaries" msgid="3582332055892252845">"ວັດຈະນານຸກົມຜູ່ໃຊ້"</string>
-    <string name="default_user_dict_pref_name" msgid="1625055720489280530">"ວັດຈະນານຸກົມຜູ່ໃຊ້"</string>
-    <string name="dictionary_available" msgid="4728975345815214218">"ມີວັດຈະນານຸກົມ"</string>
-    <string name="dictionary_downloading" msgid="2982650524622620983">"ກຳລັງດາວໂຫລດ"</string>
-    <string name="dictionary_installed" msgid="8081558343559342962">"ຕິດຕັ້ງແລ້ວ"</string>
-    <string name="dictionary_disabled" msgid="8950383219564621762">"ຕິດຕັ້ງແລ້ວ, ປິດການນຳໃຊ້ແລ້ວ"</string>
-    <string name="cannot_connect_to_dict_service" msgid="9216933695765732398">"ມີປັນຫາໃນການເຊື່ອມຕໍ່ກັບບໍລິການວັດຈະນານຸກົມ"</string>
-    <string name="no_dictionaries_available" msgid="8039920716566132611">"ບໍ່ມີວັດຈະນານຸກົມ"</string>
-    <string name="check_for_updates_now" msgid="8087688440916388581">"ດຶງຂໍ້ມູນໃຫມ່"</string>
-    <string name="last_update" msgid="730467549913588780">"ອັບເດດຫຼ້າສຸດ"</string>
-    <string name="message_updating" msgid="4457761393932375219">"ກຳລັງກວດການອັບເດດ"</string>
-    <string name="message_loading" msgid="8689096636874758814">"ກຳລັງໂຫລດ..."</string>
-    <string name="main_dict_description" msgid="3072821352793492143">"ວັດຈະນານຸກົມຫຼັກ"</string>
-    <string name="cancel" msgid="6830980399865683324">"ຍົກເລີກ"</string>
-    <string name="install_dict" msgid="180852772562189365">"ຕິດຕັ້ງ"</string>
-    <string name="cancel_download_dict" msgid="7843340278507019303">"ຍົກເລີກ"</string>
-    <string name="delete_dict" msgid="756853268088330054">"ລຶບ"</string>
-    <string name="should_download_over_metered_prompt" msgid="2878629598667658845">"ພາສາທີ່ທ່ານເລືອກໃຊ້ໃນອຸປະກອນຂອງທ່ານນັ້ນ ມີວັດຈະນານຸກົມໃຫ້ໃຊ້ພ້ອມ.&lt;br/&gt; ພວກເຮົາແນະນຳໃຫ້ &lt;b&gt;ດາວໂຫລດ&lt;/b&gt; <xliff:g id="LANGUAGE">%1$s</xliff:g> ວັດຈະນານຸກົມດັ່ງກ່າວ ເພື່ອເພີ່ມປະສົບການໃນການພິມຂອງທ່ານ.&lt;br/&gt; &lt;br/&gt; ການດາວໂຫລດອາດຈະໃຊ້ເວລາພຽງໜຶ່ງເຖິງສອງນາທີ ໂດຍການໃຊ້ 3G. ທ່ານອາດຈະເສຍຄ່າບໍລິການສຳລັບອິນເຕີເນັດ ຫາກທ່ານບໍ່ມີ &lt;b&gt;ການນຳໃຊ້ອິນເຕີເນັດແບບບໍ່ຈຳກັດ&lt;/b&gt;.&lt;br/&gt; ຫາກທ່ານບໍ່ແນ່ໃຈວ່າຮູບແບບການໃຊ້ໃດທີ່ທ່ານມີຢູ່ ພວກເຮົາແນະນຳໃຫ້ຊອກຫາການເຊື່ອມຕໍ່ Wi-Fi ເພື່ອດາວໂຫລດມັນໂດຍອັດຕະໂນມັດ.&lt;br/&gt; &lt;br/&gt; ເຄັດລັບ: ທ່ານສາມາດດາວໂຫລດ ແລະ ລຶບວັດຈະນານຸກົມໄດ້ທີ່ &lt;b&gt;ພາສາ &amp; ການປ້ອນຂໍ້ມູນ&lt;/b&gt; ຢູ່ໃນເມນູ &lt;b&gt;ການຕັ້ງຄ່າ&lt;/b&gt; ຂອງອຸປະກອນພົກພາຂອງທ່ານ."</string>
-    <string name="download_over_metered" msgid="1643065851159409546">"ດາວໂຫລດດຽວນີ້ (<xliff:g id="SIZE_IN_MEGABYTES">%1$.1f</xliff:g>MB)"</string>
-    <string name="do_not_download_over_metered" msgid="2176209579313941583">"ດາວ​ໂຫລດຜ່ານ Wi-Fi"</string>
-    <string name="dict_available_notification_title" msgid="6514288591959117288">"ວັດຈະນານຸກົມສາມາດໃຊ້ໄດ້ກັບ <xliff:g id="LANGUAGE">%1$s</xliff:g>"</string>
-    <string name="dict_available_notification_description" msgid="1075194169443163487">"ກົດທີ່ກວດຄືນ ແລະ ດາວໂຫລດ"</string>
-    <string name="toast_downloading_suggestions" msgid="1313027353588566660">"ກຳລັງດາວໂຫລດ: ການແນະນຳສຳລັບ <xliff:g id="LANGUAGE">%1$s</xliff:g> ແລະມັນຈະພ້ອມນຳໃຊ້ໄວໆນີ້"</string>
-    <string name="version_text" msgid="2715354215568469385">"ເວີຊັນ <xliff:g id="VERSION_NUMBER">%1$s</xliff:g>"</string>
-    <string name="user_dict_settings_add_menu_title" msgid="1254195365689387076">"ເພີ່ມ"</string>
-    <string name="user_dict_settings_add_dialog_title" msgid="4096700390211748168">"ເພີ່ມໄປທີ່ວັດຈະນານຸກົມ"</string>
-    <string name="user_dict_settings_add_screen_title" msgid="5818914331629278758">"ປະໂຫຍກ"</string>
-    <string name="user_dict_settings_add_dialog_more_options" msgid="5671682004887093112">"ຕົວເລືອກເພີ່ມເຕີມ"</string>
-    <string name="user_dict_settings_add_dialog_less_options" msgid="2716586567241724126">"ຕົວເລືອກໜ້ອຍລົງ"</string>
-    <string name="user_dict_settings_add_dialog_confirm" msgid="4703129507388332950">"ຕົກລົງ"</string>
-    <string name="user_dict_settings_add_word_option_name" msgid="6665558053408962865">"ຄຳສັບ:"</string>
-    <string name="user_dict_settings_add_shortcut_option_name" msgid="3094731590655523777">"ທາງລັດ:"</string>
-    <string name="user_dict_settings_add_locale_option_name" msgid="4738643440987277705">"ພາສາ:"</string>
-    <string name="user_dict_settings_add_word_hint" msgid="4902434148985906707">"ພິມ​ຄໍາ​ສັບ​ໃດ​ນຶ່ງ"</string>
-    <string name="user_dict_settings_add_shortcut_hint" msgid="2265453012555060178">"ໂຕເລືອກທາງລັດ"</string>
-    <string name="user_dict_settings_edit_dialog_title" msgid="3765774633869590352">"ແກ້ໄຂຄຳ"</string>
-    <string name="user_dict_settings_context_menu_edit_title" msgid="6812255903472456302">"ແກ້ໄຂ"</string>
-    <string name="user_dict_settings_context_menu_delete_title" msgid="8142932447689461181">"ລຶບ"</string>
-    <string name="user_dict_settings_empty_text" msgid="558499587532668203">"ທ່ານບໍ່ມີຄຳສັບໃດໆໃນວັດຈະນານຸກົມຜູ່ໃຊ້ເທື່ອ. ເພີ່ມຄຳສັບໄດ້ໂດຍການສຳພັດທີ່ປຸ່ມ ເພີ່ມ (+)."</string>
-    <string name="user_dict_settings_all_languages" msgid="8276126583216298886">"ສໍາ​ລັບ​ທຸກໆ​ພາ​ສາ"</string>
-    <string name="user_dict_settings_more_languages" msgid="7131268499685180461">"ພາສາອື່ນໆ..."</string>
-    <string name="user_dict_settings_delete" msgid="110413335187193859">"ລຶບ"</string>
-    <string name="user_dict_fast_scroll_alphabet" msgid="5431919401558285473">" ABCDEFGHIJKLMNOPQRSTUVWXYZ"</string>
-</resources>
diff --git a/java/res/values-lt/strings.xml b/java/res/values-lt/strings.xml
index 44e01a9..6110858 100644
--- a/java/res/values-lt/strings.xml
+++ b/java/res/values-lt/strings.xml
@@ -143,6 +143,7 @@
     <string name="subtype_with_layout_en_GB" msgid="2179097748724725906">"Angliška (JK) (<xliff:g id="LAYOUT">%s</xliff:g>)"</string>
     <string name="subtype_with_layout_en_US" msgid="1362581347576714579">"Angliška (JAV) (<xliff:g id="LAYOUT">%s</xliff:g>)"</string>
     <string name="subtype_with_layout_es_US" msgid="6261791057007890189">"Ispanų k. (JAV) (<xliff:g id="LAYOUT">%s</xliff:g>)"</string>
+    <string name="subtype_nepali_traditional" msgid="9032247506728040447">"<xliff:g id="LANGUAGE">%s</xliff:g> (tradicinė)"</string>
     <string name="subtype_no_language" msgid="7137390094240139495">"Kalbos nėra (abėcėlė)"</string>
     <string name="subtype_no_language_qwerty" msgid="244337630616742604">"Abėcėlė (QWERTY)"</string>
     <string name="subtype_no_language_qwertz" msgid="443066912507547976">"Abėcėlė (QWERTZ)"</string>
@@ -150,6 +151,7 @@
     <string name="subtype_no_language_dvorak" msgid="1564494667584718094">"Abėcėlė (Dvorako)"</string>
     <string name="subtype_no_language_colemak" msgid="5837418400010302623">"Abėcėlė („Colemak“)"</string>
     <string name="subtype_no_language_pcqwerty" msgid="5354918232046200018">"Abėcėlė (PC)"</string>
+    <string name="subtype_emoji" msgid="7483586578074549196">"Jaustukai"</string>
     <string name="custom_input_styles_title" msgid="8429952441821251512">"Pasirinkti įvesties stilių"</string>
     <string name="add_style" msgid="6163126614514489951">"Prid. stilių"</string>
     <string name="add" msgid="8299699805688017798">"Pridėti"</string>
diff --git a/java/res/values-lv/strings.xml b/java/res/values-lv/strings.xml
index 152ed85..ecd4e51 100644
--- a/java/res/values-lv/strings.xml
+++ b/java/res/values-lv/strings.xml
@@ -143,6 +143,7 @@
     <string name="subtype_with_layout_en_GB" msgid="2179097748724725906">"Angļu (Lielbritānija) (<xliff:g id="LAYOUT">%s</xliff:g>)"</string>
     <string name="subtype_with_layout_en_US" msgid="1362581347576714579">"Angļu (ASV) (<xliff:g id="LAYOUT">%s</xliff:g>)"</string>
     <string name="subtype_with_layout_es_US" msgid="6261791057007890189">"Spāņu (ASV) (<xliff:g id="LAYOUT">%s</xliff:g>)"</string>
+    <string name="subtype_nepali_traditional" msgid="9032247506728040447">"<xliff:g id="LANGUAGE">%s</xliff:g> (tradicionālā)"</string>
     <string name="subtype_no_language" msgid="7137390094240139495">"Nav valodas (alfabēts)"</string>
     <string name="subtype_no_language_qwerty" msgid="244337630616742604">"Alfabēts (QWERTY)"</string>
     <string name="subtype_no_language_qwertz" msgid="443066912507547976">"Alfabēts (QWERTZ)"</string>
@@ -150,6 +151,7 @@
     <string name="subtype_no_language_dvorak" msgid="1564494667584718094">"Alfabēts (Dvorak)"</string>
     <string name="subtype_no_language_colemak" msgid="5837418400010302623">"Alfabēts (Colemak)"</string>
     <string name="subtype_no_language_pcqwerty" msgid="5354918232046200018">"Alfabēts (PC)"</string>
+    <string name="subtype_emoji" msgid="7483586578074549196">"Japāņu emocijzīmes"</string>
     <string name="custom_input_styles_title" msgid="8429952441821251512">"Pielāg. ievades stili"</string>
     <string name="add_style" msgid="6163126614514489951">"Piev. stilu"</string>
     <string name="add" msgid="8299699805688017798">"Pievienot"</string>
diff --git a/java/res/values-mn-rMN/strings-appname.xml b/java/res/values-mn-rMN/strings-appname.xml
deleted file mode 100644
index 6c27e3d..0000000
--- a/java/res/values-mn-rMN/strings-appname.xml
+++ /dev/null
@@ -1,27 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!-- 
-/*
-**
-** Copyright 2013, 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:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="english_ime_name" msgid="5940510615957428904">"Андройд Гар (AOSP)"</string>
-    <string name="spell_checker_service_name" msgid="1254221805440242662">"Андройд Алдаа Шалгагч (AOSP)"</string>
-    <string name="english_ime_settings" msgid="5760361067176802794">"Андройд Гарын Тохиргоо (AOSP)"</string>
-    <string name="android_spell_checker_settings" msgid="6123949487832861885">"Андройд Алдаа Шалгагчийн Тохиргоо (AOSP)"</string>
-</resources>
diff --git a/java/res/values-mn-rMN/strings.xml b/java/res/values-mn-rMN/strings.xml
index 22f64ed..92c2bef 100644
--- a/java/res/values-mn-rMN/strings.xml
+++ b/java/res/values-mn-rMN/strings.xml
@@ -143,6 +143,7 @@
     <string name="subtype_with_layout_en_GB" msgid="2179097748724725906">"Англи (ИБ) (<xliff:g id="LAYOUT">%s</xliff:g>)"</string>
     <string name="subtype_with_layout_en_US" msgid="1362581347576714579">"Англи (АНУ) (<xliff:g id="LAYOUT">%s</xliff:g>)"</string>
     <string name="subtype_with_layout_es_US" msgid="6261791057007890189">"Испани (АНУ) (<xliff:g id="LAYOUT">%s</xliff:g>)"</string>
+    <string name="subtype_nepali_traditional" msgid="9032247506728040447">"<xliff:g id="LANGUAGE">%s</xliff:g> (Уламжлалт)"</string>
     <string name="subtype_no_language" msgid="7137390094240139495">"Хэл байхгүй (Цагаан толгой)"</string>
     <string name="subtype_no_language_qwerty" msgid="244337630616742604">"Цагаан толгой (QWERTY)"</string>
     <string name="subtype_no_language_qwertz" msgid="443066912507547976">"Цагаан толгой (QWERTZ)"</string>
@@ -150,6 +151,7 @@
     <string name="subtype_no_language_dvorak" msgid="1564494667584718094">"Цагаан толгой (Dvorak)"</string>
     <string name="subtype_no_language_colemak" msgid="5837418400010302623">"Цагаан толгой (Colemak)"</string>
     <string name="subtype_no_language_pcqwerty" msgid="5354918232046200018">"Цагаан толгой (PC)"</string>
+    <string name="subtype_emoji" msgid="7483586578074549196">"Инээмсэглэл"</string>
     <string name="custom_input_styles_title" msgid="8429952441821251512">"Өөрийн оруулах загвар"</string>
     <string name="add_style" msgid="6163126614514489951">"Загвар нэмэх"</string>
     <string name="add" msgid="8299699805688017798">"Нэмэх"</string>
diff --git a/java/res/values-ms-rMY/strings-appname.xml b/java/res/values-ms-rMY/strings-appname.xml
deleted file mode 100644
index 76d1d29..0000000
--- a/java/res/values-ms-rMY/strings-appname.xml
+++ /dev/null
@@ -1,27 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!-- 
-/*
-**
-** Copyright 2013, 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:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="english_ime_name" msgid="5940510615957428904">"Papan Kekunci Android (AOSP)"</string>
-    <string name="spell_checker_service_name" msgid="1254221805440242662">"Penyemak Ejaan Android (AOSP)"</string>
-    <string name="english_ime_settings" msgid="5760361067176802794">"Tetapan Papan Kekunci Android (AOSP)"</string>
-    <string name="android_spell_checker_settings" msgid="6123949487832861885">"Tetapan Penyemak Ejaan Android (AOSP)"</string>
-</resources>
diff --git a/java/res/values-ms-rMY/strings.xml b/java/res/values-ms-rMY/strings.xml
index 50be969..49efdf1 100644
--- a/java/res/values-ms-rMY/strings.xml
+++ b/java/res/values-ms-rMY/strings.xml
@@ -143,6 +143,7 @@
     <string name="subtype_with_layout_en_GB" msgid="2179097748724725906">"Bahasa Inggeris (UK) (<xliff:g id="LAYOUT">%s</xliff:g>)"</string>
     <string name="subtype_with_layout_en_US" msgid="1362581347576714579">"Bahasa Inggeris (AS) (<xliff:g id="LAYOUT">%s</xliff:g>)"</string>
     <string name="subtype_with_layout_es_US" msgid="6261791057007890189">"Bahasa Sepanyol (AS) (<xliff:g id="LAYOUT">%s</xliff:g>)"</string>
+    <string name="subtype_nepali_traditional" msgid="9032247506728040447">"<xliff:g id="LANGUAGE">%s</xliff:g> (Tradisional)"</string>
     <string name="subtype_no_language" msgid="7137390094240139495">"Tiada bahasa (Abjad)"</string>
     <string name="subtype_no_language_qwerty" msgid="244337630616742604">"Abjad (QWERTY)"</string>
     <string name="subtype_no_language_qwertz" msgid="443066912507547976">"Abjad (QWERTZ)"</string>
@@ -150,6 +151,7 @@
     <string name="subtype_no_language_dvorak" msgid="1564494667584718094">"Abjad (Dvorak)"</string>
     <string name="subtype_no_language_colemak" msgid="5837418400010302623">"Abjad (Colemak)"</string>
     <string name="subtype_no_language_pcqwerty" msgid="5354918232046200018">"Abjad (PC)"</string>
+    <string name="subtype_emoji" msgid="7483586578074549196">"Emoji"</string>
     <string name="custom_input_styles_title" msgid="8429952441821251512">"Gaya input peribadi"</string>
     <string name="add_style" msgid="6163126614514489951">"Tambah gaya"</string>
     <string name="add" msgid="8299699805688017798">"Tambah"</string>
diff --git a/java/res/values-nb/strings.xml b/java/res/values-nb/strings.xml
index 1f9dbed..9b30ea3 100644
--- a/java/res/values-nb/strings.xml
+++ b/java/res/values-nb/strings.xml
@@ -143,6 +143,7 @@
     <string name="subtype_with_layout_en_GB" msgid="2179097748724725906">"Engelsk (Storbritannia) (<xliff:g id="LAYOUT">%s</xliff:g>)"</string>
     <string name="subtype_with_layout_en_US" msgid="1362581347576714579">"Engelsk (USA) (<xliff:g id="LAYOUT">%s</xliff:g>)"</string>
     <string name="subtype_with_layout_es_US" msgid="6261791057007890189">"Spansk (USA) (<xliff:g id="LAYOUT">%s</xliff:g>)"</string>
+    <string name="subtype_nepali_traditional" msgid="9032247506728040447">"<xliff:g id="LANGUAGE">%s</xliff:g> (tradisjonell)"</string>
     <string name="subtype_no_language" msgid="7137390094240139495">"Ingen språk (alfabet)"</string>
     <string name="subtype_no_language_qwerty" msgid="244337630616742604">"Alfabet (QWERTY)"</string>
     <string name="subtype_no_language_qwertz" msgid="443066912507547976">"Alfabet (QWERTZ)"</string>
@@ -150,6 +151,7 @@
     <string name="subtype_no_language_dvorak" msgid="1564494667584718094">"Alfabet (Dvorak)"</string>
     <string name="subtype_no_language_colemak" msgid="5837418400010302623">"Alfabet (Colemak)"</string>
     <string name="subtype_no_language_pcqwerty" msgid="5354918232046200018">"Alfabet (PC)"</string>
+    <string name="subtype_emoji" msgid="7483586578074549196">"Emoji"</string>
     <string name="custom_input_styles_title" msgid="8429952441821251512">"Egendefinerte inndata"</string>
     <string name="add_style" msgid="6163126614514489951">"Legg til stil"</string>
     <string name="add" msgid="8299699805688017798">"Legg til"</string>
diff --git a/java/res/values-ne-rNP/strings.xml b/java/res/values-ne-rNP/strings.xml
deleted file mode 100644
index c62d246..0000000
--- a/java/res/values-ne-rNP/strings.xml
+++ /dev/null
@@ -1,242 +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.
-*/
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="english_ime_input_options" msgid="3909945612939668554">"इनपुट विकल्पहरू"</string>
-    <string name="english_ime_research_log" msgid="8492602295696577851">"लग निर्देशनहरू शोध गर्नुहोस्"</string>
-    <string name="use_contacts_for_spellchecking_option_title" msgid="5374120998125353898">"सम्पर्क नामहरू हेर्नुहोस्"</string>
-    <string name="use_contacts_for_spellchecking_option_summary" msgid="8754413382543307713">"तपाईँको सम्पर्क सूचीबाट हिज्जे परीक्षकले प्रविष्टिहरूको प्रयोग गर्छ"</string>
-    <string name="vibrate_on_keypress" msgid="5258079494276955460">"कुञ्जी थिच्दा भाइब्रेट"</string>
-    <string name="sound_on_keypress" msgid="6093592297198243644">"कुञ्जी थिच्दा आवाज"</string>
-    <string name="popup_on_keypress" msgid="123894815723512944">"कुञ्जी दबाउँदा पपअप"</string>
-    <string name="general_category" msgid="1859088467017573195">"सामान्य"</string>
-    <string name="correction_category" msgid="2236750915056607613">"पाठ सुधार"</string>
-    <string name="gesture_typing_category" msgid="497263612130532630">"इशारा टाइप गर्ने"</string>
-    <string name="misc_category" msgid="6894192814868233453">"अन्य विकल्पहरू"</string>
-    <string name="advanced_settings" msgid="362895144495591463">"जटिल सेटिङहरू"</string>
-    <string name="advanced_settings_summary" msgid="4487980456152830271">"विज्ञहरूका लागि विकल्पहरू"</string>
-    <string name="include_other_imes_in_language_switch_list" msgid="4533689960308565519">"अन्य इनपुट विधिमा स्विच गर्नुहोस्"</string>
-    <string name="include_other_imes_in_language_switch_list_summary" msgid="840637129103317635">"भाषा स्विच किले अन्य इनपुट विधि पनि समेट्छ"</string>
-    <string name="show_language_switch_key" msgid="5915478828318774384">"भाषा स्विच कुञ्जी"</string>
-    <string name="show_language_switch_key_summary" msgid="7343403647474265713">"जब बहुसङ्ख्यक इनपुट भाषाहरू सक्षम भएपछि देखाउनुहोस्"</string>
-    <string name="sliding_key_input_preview" msgid="6604262359510068370">"स्लाइड सूचक देखाउनुहोस्"</string>
-    <string name="sliding_key_input_preview_summary" msgid="6340524345729093886">"सिफ्ट वा प्रतिक कुञ्जीमा स्लाइड गर्ने बेला दृश्य सङ्केत देखाउनुहोस्"</string>
-    <string name="key_preview_popup_dismiss_delay" msgid="6213164897443068248">"कि पपअप खारेजी ढिलाइ"</string>
-    <string name="key_preview_popup_dismiss_no_delay" msgid="2096123151571458064">"ढिलाइ छैन"</string>
-    <string name="key_preview_popup_dismiss_default_delay" msgid="2166964333903906734">"पूर्वनिर्धारित"</string>
-    <string name="abbreviation_unit_milliseconds" msgid="8700286094028323363">"<xliff:g id="MILLISECONDS">%s</xliff:g> मिलिसेकेन्ड"</string>
-    <string name="settings_system_default" msgid="6268225104743331821">"प्रणाली पूर्वनिर्धारित"</string>
-    <string name="use_contacts_dict" msgid="4435317977804180815">"सम्पर्क नामहरू सुझाव गर्नुहोस्"</string>
-    <string name="use_contacts_dict_summary" msgid="6599983334507879959">"सुझाव र सुधारका लागि सम्पर्कबाट नामहरू प्रयोग गर्नुहोस्"</string>
-    <string name="use_double_space_period" msgid="8781529969425082860">"डबल-स्पेस पूर्णविराम"</string>
-    <string name="use_double_space_period_summary" msgid="6532892187247952799">"स्पेसबारमा डबल ट्याप गर्नाले पूर्णविरामपछि स्पेस राख्दछ"</string>
-    <string name="auto_cap" msgid="1719746674854628252">"स्वतः पूँजिकरण"</string>
-    <string name="auto_cap_summary" msgid="7934452761022946874">"प्रत्येक वाक्यको पहिलो शब्द क्यापिटल गर्नुहोस्"</string>
-    <string name="edit_personal_dictionary" msgid="3996910038952940420">"व्यक्तिगत शब्दकोश"</string>
-    <string name="configure_dictionaries_title" msgid="4238652338556902049">"एड-अन शब्दकोश"</string>
-    <string name="main_dictionary" msgid="4798763781818361168">"मुख्य शब्दकोश"</string>
-    <string name="prefs_show_suggestions" msgid="8026799663445531637">"सुधार सुझावहरू देखाउने"</string>
-    <string name="prefs_show_suggestions_summary" msgid="1583132279498502825">"टाइप गर्ने बेलामा सुझाव शब्दहरू देखाउनुहोस्"</string>
-    <string name="prefs_suggestion_visibility_show_name" msgid="3219916594067551303">"सधैँ देखाउने"</string>
-    <string name="prefs_suggestion_visibility_show_only_portrait_name" msgid="3859783767435239118">"चित्र मोडमा देखाउनुहोस्"</string>
-    <string name="prefs_suggestion_visibility_hide_name" msgid="6309143926422234673">"सधैँ लुकाउने"</string>
-    <string name="prefs_block_potentially_offensive_title" msgid="5078480071057408934">"आपत्तिजनक शब्दहरूलाई रोक्नुहोस्"</string>
-    <string name="prefs_block_potentially_offensive_summary" msgid="2371835479734991364">"सम्भावित आपत्तिजनक शब्दहरू सुझाव नगर्नुहोस्"</string>
-    <string name="auto_correction" msgid="7630720885194996950">"स्वतः सुधार"</string>
-    <string name="auto_correction_summary" msgid="5625751551134658006">"गल्ती टाइप भएका शब्दहरूलाई स्पेसबार र पङ्चुएसनले स्वचालित रूपमा सच्याउँछन्।"</string>
-    <string name="auto_correction_threshold_mode_off" msgid="8470882665417944026">"बन्द"</string>
-    <string name="auto_correction_threshold_mode_modest" msgid="8788366690620799097">"सामान्य"</string>
-    <string name="auto_correction_threshold_mode_aggressive" msgid="7319007299148899623">"आक्रामक"</string>
-    <string name="auto_correction_threshold_mode_very_aggressive" msgid="1853309024129480416">"ज्यादै आक्रामक"</string>
-    <string name="bigram_prediction" msgid="1084449187723948550">"अर्को शब्द सुझाव"</string>
-    <string name="bigram_prediction_summary" msgid="3896362682751109677">"सुझावहरू निर्माण गर्न अघिल्लो शब्द प्रयोग गर्नुहोस्"</string>
-    <string name="gesture_input" msgid="826951152254563827">"इशारा टाइप गर्ने सक्षम पार्नुहोस्"</string>
-    <string name="gesture_input_summary" msgid="9180350639305731231">"अक्षर स्लाइड गरी शब्द इनपुट गर्नुहोस्"</string>
-    <string name="gesture_preview_trail" msgid="3802333369335722221">"इशारा ट्रेल देखाउनुहोस्"</string>
-    <string name="gesture_floating_preview_text" msgid="4443240334739381053">"गतिशील फ्लोटिङ पूर्वावलोकन"</string>
-    <string name="gesture_floating_preview_text_summary" msgid="4472696213996203533">"इशारा गर्दा सुझाव दिइएको शब्द हेर्नुहोस्"</string>
-    <string name="added_word" msgid="8993883354622484372">"<xliff:g id="WORD">%s</xliff:g> : बचत गरियो"</string>
-    <string name="label_go_key" msgid="1635148082137219148">"जानुहोस्"</string>
-    <string name="label_next_key" msgid="362972844525672568">"अर्को"</string>
-    <string name="label_previous_key" msgid="1211868118071386787">"अघिल्लो"</string>
-    <string name="label_done_key" msgid="2441578748772529288">"भयो"</string>
-    <string name="label_send_key" msgid="2815056534433717444">"पठाउनुहोस्"</string>
-    <string name="label_pause_key" msgid="181098308428035340">"रोक्नुहोस्"</string>
-    <string name="label_wait_key" msgid="6402152600878093134">"प्रतीक्षा गर्ने"</string>
-    <string name="spoken_use_headphones" msgid="896961781287283493">"हेडसेट प्लग इन गर्नुहोस्"</string>
-    <string name="spoken_current_text_is" msgid="2485723011272583845">"वर्तमान पाठ %s हो"</string>
-    <string name="spoken_no_text_entered" msgid="7479685225597344496">"कुनै पाठ प्रविष्टि गरिएको छैन"</string>
-    <string name="spoken_description_unknown" msgid="3197434010402179157">"कुञ्जी कोड %d"</string>
-    <string name="spoken_description_shift" msgid="244197883292549308">"सिफ्ट"</string>
-    <string name="spoken_description_shift_shifted" msgid="1681877323344195035">"सिप्ट सक्रिय (असक्षम पार्न ट्याप गर्नुहोस्)"</string>
-    <string name="spoken_description_caps_lock" msgid="3276478269526304432">"क्याप्स लक सक्रिय छ (असक्षम पार्न ट्याप गर्नुहोस्)"</string>
-    <string name="spoken_description_delete" msgid="8740376944276199801">"मेट्ने"</string>
-    <string name="spoken_description_to_symbol" msgid="5486340107500448969">"प्रतिकहरू"</string>
-    <string name="spoken_description_to_alpha" msgid="23129338819771807">"अक्षरहरू"</string>
-    <string name="spoken_description_to_numeric" msgid="591752092685161732">"नम्बरहरू"</string>
-    <string name="spoken_description_settings" msgid="4627462689603838099">"सेटिङहरू"</string>
-    <string name="spoken_description_tab" msgid="2667716002663482248">"ट्याब"</string>
-    <string name="spoken_description_space" msgid="2582521050049860859">"स्पेस"</string>
-    <string name="spoken_description_mic" msgid="615536748882611950">"आवाज इनपुट"</string>
-    <string name="spoken_description_smiley" msgid="2256309826200113918">"मुस्कुराएको अनुहार"</string>
-    <string name="spoken_description_return" msgid="8178083177238315647">"फर्कनुहोस्"</string>
-    <string name="spoken_description_search" msgid="1247236163755920808">"खोज्नुहोस्"</string>
-    <string name="spoken_description_dot" msgid="40711082435231673">"डट"</string>
-    <string name="spoken_description_language_switch" msgid="5507091328222331316">"भाषा स्विच गर्नुहोस्"</string>
-    <string name="spoken_description_action_next" msgid="8636078276664150324">"अर्को"</string>
-    <string name="spoken_description_action_previous" msgid="800872415009336208">"अघिल्लो"</string>
-    <string name="spoken_description_shiftmode_on" msgid="5700440798609574589">"सिफ्ट सक्षम पारिएको छ"</string>
-    <string name="spoken_description_shiftmode_locked" msgid="593175803181701830">"क्याप्स लक सक्षम पारिएको छ"</string>
-    <string name="spoken_description_shiftmode_off" msgid="657219998449174808">"सिफ्ट असक्षम पारिएको छ"</string>
-    <string name="spoken_description_mode_symbol" msgid="7183343879909747642">"प्रतिक मोड"</string>
-    <string name="spoken_description_mode_alpha" msgid="3528307674390156956">"अक्षर मोड"</string>
-    <string name="spoken_description_mode_phone" msgid="6520207943132026264">"फोन मोड"</string>
-    <string name="spoken_description_mode_phone_shift" msgid="5499629753962641227">"फोन प्रतिक मोड"</string>
-    <string name="announce_keyboard_hidden" msgid="8718927835531429807">"किबोर्ड लुकाइएको छ"</string>
-    <string name="announce_keyboard_mode" msgid="4729081055438508321">"<xliff:g id="MODE">%s</xliff:g> किबोर्ड देखाइँदै"</string>
-    <string name="keyboard_mode_date" msgid="3137520166817128102">"मिति"</string>
-    <string name="keyboard_mode_date_time" msgid="339593358488851072">"मिति र समय"</string>
-    <string name="keyboard_mode_email" msgid="6216248078128294262">"इमेल"</string>
-    <string name="keyboard_mode_im" msgid="1137405089766557048">"सन्देश गर्दै"</string>
-    <string name="keyboard_mode_number" msgid="7991623440699957069">"सङ्ख्या"</string>
-    <string name="keyboard_mode_phone" msgid="6851627527401433229">"फोन"</string>
-    <string name="keyboard_mode_text" msgid="6479436687899701619">"पाठ"</string>
-    <string name="keyboard_mode_time" msgid="4381856885582143277">"समय"</string>
-    <string name="keyboard_mode_url" msgid="1519819835514911218">"URL"</string>
-    <string name="voice_input" msgid="3583258583521397548">"आवाज इनपुट कुञ्जी"</string>
-    <string name="voice_input_modes_main_keyboard" msgid="3360660341121083174">"मुख्य किबोर्डमा"</string>
-    <string name="voice_input_modes_symbols_keyboard" msgid="7203213240786084067">"प्रतिकहरू किबोर्डमा"</string>
-    <string name="voice_input_modes_off" msgid="3745699748218082014">"बन्द"</string>
-    <string name="voice_input_modes_summary_main_keyboard" msgid="6586544292900314339">"मुख्य किबोर्डमा माइक"</string>
-    <string name="voice_input_modes_summary_symbols_keyboard" msgid="5233725927281932391">"प्रतिकहरू किबोर्डमा माइक"</string>
-    <string name="voice_input_modes_summary_off" msgid="63875609591897607">"आवाज इनपुट असक्षम पारियो"</string>
-    <string name="configure_input_method" msgid="373356270290742459">"इनपुट विधिहरू कन्फिगर गर्नुहोस्"</string>
-    <string name="language_selection_title" msgid="1651299598555326750">"इनपुट भाषाहरू"</string>
-    <string name="send_feedback" msgid="1780431884109392046">"प्रतिक्रिया पठाउनुहोस्"</string>
-    <string name="select_language" msgid="3693815588777926848">"इनपुट भाषाहरू"</string>
-    <string name="hint_add_to_dictionary" msgid="573678656946085380">"बचत गर्न पुनः छुनुहोस्"</string>
-    <string name="has_dictionary" msgid="6071847973466625007">"उपलब्ध शब्दकोश"</string>
-    <string name="prefs_enable_log" msgid="6620424505072963557">"प्रयोगकर्ता प्रतिक्रिया सक्षम पार्नुहोस्"</string>
-    <string name="prefs_description_log" msgid="7525225584555429211">"स्वचालित रूपमा प्रयोग तथ्याङ्कहरू र क्यास रिपोर्टहरू पठाएर यस इनपुट विधि सम्पादकलाई सुधार्न सहयोग गर्नुहोस्।"</string>
-    <string name="keyboard_layout" msgid="8451164783510487501">"किबोर्ड थिम"</string>
-    <string name="subtype_en_GB" msgid="88170601942311355">"अंग्रेजी (युके)"</string>
-    <string name="subtype_en_US" msgid="6160452336634534239">"अंग्रेजी (युएस्)"</string>
-    <string name="subtype_es_US" msgid="5583145191430180200">"स्पेनिस (युएस्)"</string>
-    <string name="subtype_with_layout_en_GB" msgid="2179097748724725906">"अंग्रेजी (युके) ( <xliff:g id="LAYOUT">%s</xliff:g> )"</string>
-    <string name="subtype_with_layout_en_US" msgid="1362581347576714579">"अंग्रेजी (युएस्) ( <xliff:g id="LAYOUT">%s</xliff:g> )"</string>
-    <string name="subtype_with_layout_es_US" msgid="6261791057007890189">"स्पेनेली (युएस्) ( <xliff:g id="LAYOUT">%s</xliff:g> )"</string>
-    <string name="subtype_no_language" msgid="7137390094240139495">"कुनै भाषा होइन (वर्णमाला)"</string>
-    <string name="subtype_no_language_qwerty" msgid="244337630616742604">"वर्णमाला (QWERTY)"</string>
-    <string name="subtype_no_language_qwertz" msgid="443066912507547976">"वर्णमाला (QWERTZ)"</string>
-    <string name="subtype_no_language_azerty" msgid="8144348527575640087">"वर्णमाला (AZERTY)"</string>
-    <string name="subtype_no_language_dvorak" msgid="1564494667584718094">"वर्णमाला (Dvorak)"</string>
-    <string name="subtype_no_language_colemak" msgid="5837418400010302623">"वर्णमाला (Colemak)"</string>
-    <string name="subtype_no_language_pcqwerty" msgid="5354918232046200018">"वर्णमाला (PC)"</string>
-    <string name="custom_input_styles_title" msgid="8429952441821251512">"अनुकूलन इनपुट शैली"</string>
-    <string name="add_style" msgid="6163126614514489951">"शैली थप्नुहोस्"</string>
-    <string name="add" msgid="8299699805688017798">"थप्नुहोस्"</string>
-    <string name="remove" msgid="4486081658752944606">"हटाउनुहोस्"</string>
-    <string name="save" msgid="7646738597196767214">"बचत गर्नुहोस्"</string>
-    <string name="subtype_locale" msgid="8576443440738143764">"भाषा"</string>
-    <string name="keyboard_layout_set" msgid="4309233698194565609">"लेआउट"</string>
-    <string name="custom_input_style_note_message" msgid="8826731320846363423">"तपाईँले प्रयोग गर्न सुरु गर्न अघि तपाईँको अनुकूलन इनपुट शैली सक्षम पारिनु पर्छ। के तपाईँ यसलाई अहिले सक्षम पार्न चाहनु हुन्छ?"</string>
-    <string name="enable" msgid="5031294444630523247">"सक्षम पार्नुहोस्"</string>
-    <string name="not_now" msgid="6172462888202790482">"अहिले होइन"</string>
-    <string name="custom_input_style_already_exists" msgid="8008728952215449707">"यस्तो इनपुट शैली पहिले नै अवस्थित छ: <xliff:g id="INPUT_STYLE_NAME">%s</xliff:g>"</string>
-    <string name="prefs_usability_study_mode" msgid="1261130555134595254">"प्रयोग अध्ययन मोड"</string>
-    <string name="prefs_key_longpress_timeout_settings" msgid="6102240298932897873">"कुञ्जी लामो थिचाइ ढिलाइ"</string>
-    <string name="prefs_keypress_vibration_duration_settings" msgid="7918341459947439226">"कुञ्जी थिचाइ भाइब्रेसन अवधि"</string>
-    <string name="prefs_keypress_sound_volume_settings" msgid="6027007337036891623">"कुञ्जी थिचाइ आवाज भोल्युम"</string>
-    <string name="prefs_read_external_dictionary" msgid="2588931418575013067">"बाह्य शब्दकोश फाइल पढ्नुहोस्"</string>
-    <string name="read_external_dictionary_no_files_message" msgid="4947420942224623792">"डाउनलोड फोल्डरमा कुनै शब्दकोश फाइलहरू छैनन्।"</string>
-    <string name="read_external_dictionary_multiple_files_title" msgid="7637749044265808628">"स्थापना गर्न कुनै शब्दकोश फाइल चयन गर्नुहोस्"</string>
-    <string name="read_external_dictionary_confirm_install_message" msgid="6898610163768980870">"<xliff:g id="LOCALE_NAME">%s</xliff:g>का लागि साँच्चिकै यो फाइल स्थापना गर्ने हो?"</string>
-    <string name="error" msgid="8940763624668513648">"कुनै त्रुटि भयो"</string>
-    <string name="button_default" msgid="3988017840431881491">"पूर्वनिर्धारित"</string>
-    <string name="setup_welcome_title" msgid="6112821709832031715">"तपाईँलाई स्वागत छ<xliff:g id="APPLICATION_NAME">%s</xliff:g>"</string>
-    <string name="setup_welcome_additional_description" msgid="8150252008545768953">"इशारा टाइप गर्नेसँग"</string>
-    <string name="setup_start_action" msgid="8936036460897347708">"सुरु गरौं"</string>
-    <string name="setup_next_action" msgid="371821437915144603">"अर्को चरण"</string>
-    <string name="setup_steps_title" msgid="6400373034871816182">"स्थापना गर्दै <xliff:g id="APPLICATION_NAME">%s</xliff:g>"</string>
-    <string name="setup_step1_title" msgid="3147967630253462315">"सक्षम पार्नुहोस् <xliff:g id="APPLICATION_NAME">%s</xliff:g>"</string>
-    <string name="setup_step1_instruction" msgid="2578631936624637241">"कृपया जाँच गर्नुहोस् \"<xliff:g id="APPLICATION_NAME">%s</xliff:g>\" तपाईँको भाषा र इनपुट सेटिङमा। यसले तपाईँलाई तपाईँको उपकरणमा सञ्चालन गर्न आधिकारिकता प्रदान गर्छ।"</string>
-    <string name="setup_step1_finished_instruction" msgid="10761482004957994">"<xliff:g id="APPLICATION_NAME">%s</xliff:g> पहिले नै तपाईँको भाषा र इनपुट सेटिङमा सक्षम पारिएको छ, त्यसैले यो कदम सकिसकिएको छ। अर्कोमा जानुहोस्!"</string>
-    <string name="setup_step1_action" msgid="4366513534999901728">"सेटिङहरूमा सक्षम पार्नुहोस्"</string>
-    <string name="setup_step2_title" msgid="6860725447906690594">"<xliff:g id="APPLICATION_NAME">%s</xliff:g>मा स्विच गर्नुहोस्"</string>
-    <string name="setup_step2_instruction" msgid="9141481964870023336">"त्यसपछि, \"<xliff:g id="APPLICATION_NAME">%s</xliff:g>\" लाई तपाईँको सक्रिय पाठ इनपुट विधिका रूपमा चयन गर्नुहोस्।"</string>
-    <string name="setup_step2_action" msgid="1660330307159824337">"इनपुट विधि स्विच गर्नुहोस्"</string>
-    <string name="setup_step3_title" msgid="3154757183631490281">"बधाई छ, तपाईँले सेट पुरा गर्नुभयो!"</string>
-    <string name="setup_step3_instruction" msgid="8025981829605426000">"अब तपाईँ <xliff:g id="APPLICATION_NAME">%s</xliff:g>का साथ तपाईँका सम्पूर्ण मनपर्ने अनुप्रयोगहरू टाइप गर्न सक्नुहुन्छ।"</string>
-    <string name="setup_step3_action" msgid="600879797256942259">"थप भाषाहरू कन्फिगर गर्नुहोस्"</string>
-    <string name="setup_finish_action" msgid="276559243409465389">"समाप्त भयो"</string>
-    <string name="show_setup_wizard_icon" msgid="5008028590593710830">"अनुप्रयोग आइकन देखाउनुहोस्"</string>
-    <string name="show_setup_wizard_icon_summary" msgid="4119998322536880213">"लन्चरमा अनुप्रयोग आइकन देखाउनुहोस्"</string>
-    <string name="app_name" msgid="6320102637491234792">"शब्दकोश प्रदायक"</string>
-    <string name="dictionary_provider_name" msgid="3027315045397363079">"शब्दकोश प्रदायक"</string>
-    <string name="dictionary_service_name" msgid="6237472350693511448">"शब्दकोश सेवा"</string>
-    <string name="download_description" msgid="6014835283119198591">"शब्दकोश अद्यावधिक जानकारी"</string>
-    <string name="dictionary_settings_title" msgid="8091417676045693313">"एड-अन शब्दकोश"</string>
-    <string name="dictionary_install_over_metered_network_prompt" msgid="3587517870006332980">"उपलब्ध शब्दकोश"</string>
-    <string name="dictionary_settings_summary" msgid="5305694987799824349">"शब्दकोशहरूका लागि सेटिङहरू"</string>
-    <string name="user_dictionaries" msgid="3582332055892252845">"प्रयोगकर्ता शब्दकोशहरू"</string>
-    <string name="default_user_dict_pref_name" msgid="1625055720489280530">"प्रयोगकर्ता शब्दकोश"</string>
-    <string name="dictionary_available" msgid="4728975345815214218">"उपलब्ध शब्दकोश"</string>
-    <string name="dictionary_downloading" msgid="2982650524622620983">"हाल डाउनलोड गर्दै"</string>
-    <string name="dictionary_installed" msgid="8081558343559342962">"स्थापित"</string>
-    <string name="dictionary_disabled" msgid="8950383219564621762">"स्थापित, असक्षम पारिएको"</string>
-    <string name="cannot_connect_to_dict_service" msgid="9216933695765732398">"शब्दकोश सेवासँग जोड्न समस्या"</string>
-    <string name="no_dictionaries_available" msgid="8039920716566132611">"शब्दकोशहरू उपलब्ध छैनन्"</string>
-    <string name="check_for_updates_now" msgid="8087688440916388581">"पुनः ताजा गर्नुहोस्"</string>
-    <string name="last_update" msgid="730467549913588780">"पछिल्लो अद्यावधिक"</string>
-    <string name="message_updating" msgid="4457761393932375219">"अद्यावधिकको लागि जाँच गर्दै"</string>
-    <string name="message_loading" msgid="8689096636874758814">"लोड हुँदै..."</string>
-    <string name="main_dict_description" msgid="3072821352793492143">"मुख्य शब्दकोश"</string>
-    <string name="cancel" msgid="6830980399865683324">"रद्द गर्नुहोस्"</string>
-    <string name="install_dict" msgid="180852772562189365">"स्थापना गर्नुहोस्"</string>
-    <string name="cancel_download_dict" msgid="7843340278507019303">"रद्द गर्नुहोस्"</string>
-    <string name="delete_dict" msgid="756853268088330054">"मेट्नुहोस्"</string>
-    <string name="should_download_over_metered_prompt" msgid="2878629598667658845">"तपाईँको मोबाइल उपकरणमा चयन गरिएको भाषामा शब्दकोश उपलब्ध छ। हामी सिफारिश गर्छौं <xliff:g id="LANGUAGE">%1$s</xliff:g> शब्दकोश डाउनलोड गर्नका लागि तपाईँको टाइपिङ अनुभव सुधार्न। यस डाउनलोड 3G मा एक वा दुई मिनेट लाग्छ। शुल्कहरू लाग्न सक्छ यदि तपाईँसँग असीमित डेटा योजना छैन भने। यदि आफूसँग कुन डेटा योजना छ तपाईँ यकिन हुनुहुन्न भने हामी स्वचालित रूपमा डाउनलोड सुरु गर्न वाइ-फाइ जडान खोज्न सिफारिस गर्छौं। सल्लाह: तपाईँको मोबाइल उपकरणको भाषा र इनपुट सेटिङ मेनुमा गई तपाईँ शब्दकोशलाई डाउनलोड वा हटाउन सक्नुहुन्छ।"</string>
-    <string name="download_over_metered" msgid="1643065851159409546">"(अब डाउनलोड गर्नुहोस्<xliff:g id="SIZE_IN_MEGABYTES">%1$.1f</xliff:g> MB)"</string>
-    <string name="do_not_download_over_metered" msgid="2176209579313941583">"वाइ-फाइको माध्ययमद्वार डाउनलोड गर्नुहोस्"</string>
-    <string name="dict_available_notification_title" msgid="6514288591959117288">"एक शब्दकोश  <xliff:g id="LANGUAGE">%1$s</xliff:g> का लागि उपलब्ध छ"</string>
-    <string name="dict_available_notification_description" msgid="1075194169443163487">"समीक्षा गर्न थिच्नुहोस् र डाउनलोड गर्नुहोस्"</string>
-    <string name="toast_downloading_suggestions" msgid="1313027353588566660">"डाउनलोड गरिदै: <xliff:g id="LANGUAGE">%1$s</xliff:g>का लागि सुझावहरू चाँडै तयार हुने छन्।"</string>
-    <string name="version_text" msgid="2715354215568469385">"संस्करण <xliff:g id="VERSION_NUMBER">%1$s</xliff:g>"</string>
-    <string name="user_dict_settings_add_menu_title" msgid="1254195365689387076">"थप्नुहोस्"</string>
-    <string name="user_dict_settings_add_dialog_title" msgid="4096700390211748168">"शब्दकोशमा थप्नुहोस्"</string>
-    <string name="user_dict_settings_add_screen_title" msgid="5818914331629278758">"पदावली"</string>
-    <string name="user_dict_settings_add_dialog_more_options" msgid="5671682004887093112">"थप विकल्पहरू"</string>
-    <string name="user_dict_settings_add_dialog_less_options" msgid="2716586567241724126">"कम विकल्पहरू"</string>
-    <string name="user_dict_settings_add_dialog_confirm" msgid="4703129507388332950">"ठीक छ"</string>
-    <string name="user_dict_settings_add_word_option_name" msgid="6665558053408962865">"शब्द:"</string>
-    <string name="user_dict_settings_add_shortcut_option_name" msgid="3094731590655523777">"सर्टकट:"</string>
-    <string name="user_dict_settings_add_locale_option_name" msgid="4738643440987277705">"भाषा:"</string>
-    <string name="user_dict_settings_add_word_hint" msgid="4902434148985906707">"एउटा शब्द टाइप गर्नुहोस्"</string>
-    <string name="user_dict_settings_add_shortcut_hint" msgid="2265453012555060178">"वैकल्पिक सर्टकट"</string>
-    <string name="user_dict_settings_edit_dialog_title" msgid="3765774633869590352">"शब्द सम्पादन गर्नुहोस्"</string>
-    <string name="user_dict_settings_context_menu_edit_title" msgid="6812255903472456302">"सम्पादन गर्नुहोस्"</string>
-    <string name="user_dict_settings_context_menu_delete_title" msgid="8142932447689461181">"मेट्नुहोस्"</string>
-    <string name="user_dict_settings_empty_text" msgid="558499587532668203">"तपाईँसँग प्रयोगकर्ता शब्दकोशमा कुनै शब्द छैन।\"थप्नुहोस्\"(+) बटनमा छोएर एउटा शब्द थप्नुहोस्।"</string>
-    <string name="user_dict_settings_all_languages" msgid="8276126583216298886">"सबै भाषाहरूका लागि"</string>
-    <string name="user_dict_settings_more_languages" msgid="7131268499685180461">"थप भाषाहरू..."</string>
-    <string name="user_dict_settings_delete" msgid="110413335187193859">"मेट्नुहोस्"</string>
-    <string name="user_dict_fast_scroll_alphabet" msgid="5431919401558285473">" ABCDEFGHIJKLMNOPQRSTUVWXYZ"</string>
-</resources>
diff --git a/java/res/values-ne/strings.xml b/java/res/values-ne/strings.xml
deleted file mode 100644
index 6c14945..0000000
--- a/java/res/values-ne/strings.xml
+++ /dev/null
@@ -1,242 +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.
-*/
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="english_ime_input_options" msgid="3909945612939668554">"इनपुट विकल्पहरू"</string>
-    <string name="english_ime_research_log" msgid="8492602295696577851">"लग निर्देशनहरू शोध गर्नुहोस्"</string>
-    <string name="use_contacts_for_spellchecking_option_title" msgid="5374120998125353898">"सम्पर्क नामहरू हेर्नुहोस्"</string>
-    <string name="use_contacts_for_spellchecking_option_summary" msgid="8754413382543307713">"तपाईँको सम्पर्क सूचीबाट हिज्जे परीक्षकले प्रविष्टिहरूको प्रयोग गर्छ"</string>
-    <string name="vibrate_on_keypress" msgid="5258079494276955460">"कुञ्जी थिच्दा भाइब्रेट"</string>
-    <string name="sound_on_keypress" msgid="6093592297198243644">"कुञ्जी थिच्दा आवाज"</string>
-    <string name="popup_on_keypress" msgid="123894815723512944">"कुञ्जी दबाउँदा पपअप"</string>
-    <string name="general_category" msgid="1859088467017573195">"सामान्य"</string>
-    <string name="correction_category" msgid="2236750915056607613">"पाठ सुधार"</string>
-    <string name="gesture_typing_category" msgid="497263612130532630">"इशारा टाइप गर्ने"</string>
-    <string name="misc_category" msgid="6894192814868233453">"अन्य विकल्पहरू"</string>
-    <string name="advanced_settings" msgid="362895144495591463">"जटिल सेटिङहरू"</string>
-    <string name="advanced_settings_summary" msgid="4487980456152830271">"विज्ञहरूका लागि विकल्पहरू"</string>
-    <string name="include_other_imes_in_language_switch_list" msgid="4533689960308565519">"अन्य इनपुट विधिमा स्विच गर्नुहोस्"</string>
-    <string name="include_other_imes_in_language_switch_list_summary" msgid="840637129103317635">"भाषा स्विच किले अन्य इनपुट विधि पनि समेट्छ"</string>
-    <string name="show_language_switch_key" msgid="5915478828318774384">"भाषा स्विच कुञ्जी"</string>
-    <string name="show_language_switch_key_summary" msgid="7343403647474265713">"जब बहुसङ्ख्यक इनपुट भाषाहरू सक्षम भएपछि देखाउनुहोस्"</string>
-    <string name="sliding_key_input_preview" msgid="6604262359510068370">"स्लाइड सूचक देखाउनुहोस्"</string>
-    <string name="sliding_key_input_preview_summary" msgid="6340524345729093886">"सिफ्ट वा प्रतिक कुञ्जीमा स्लाइड गर्ने बेला दृश्य सङ्केत देखाउनुहोस्"</string>
-    <string name="key_preview_popup_dismiss_delay" msgid="6213164897443068248">"कि पपअप खारेजी ढिलाइ"</string>
-    <string name="key_preview_popup_dismiss_no_delay" msgid="2096123151571458064">"ढिलाइ छैन"</string>
-    <string name="key_preview_popup_dismiss_default_delay" msgid="2166964333903906734">"पूर्वनिर्धारित"</string>
-    <string name="abbreviation_unit_milliseconds" msgid="8700286094028323363">"<xliff:g id="MILLISECONDS">%s</xliff:g> मिलिसेकेन्ड"</string>
-    <string name="settings_system_default" msgid="6268225104743331821">"प्रणाली पूर्वनिर्धारित"</string>
-    <string name="use_contacts_dict" msgid="4435317977804180815">"सम्पर्क नामहरू सुझाव गर्नुहोस्"</string>
-    <string name="use_contacts_dict_summary" msgid="6599983334507879959">"सुझाव र सुधारका लागि सम्पर्कबाट नामहरू प्रयोग गर्नुहोस्"</string>
-    <string name="use_double_space_period" msgid="8781529969425082860">"डबल-स्पेस पूर्णविराम"</string>
-    <string name="use_double_space_period_summary" msgid="6532892187247952799">"स्पेसबारमा डबल ट्याप गर्नाले पूर्णविरामपछि स्पेस राख्दछ"</string>
-    <string name="auto_cap" msgid="1719746674854628252">"स्वतः पूँजिकरण"</string>
-    <string name="auto_cap_summary" msgid="7934452761022946874">"प्रत्येक वाक्यको पहिलो शब्द क्यापिटल गर्नुहोस्"</string>
-    <string name="edit_personal_dictionary" msgid="3996910038952940420">"व्यक्तिगत शब्दकोश"</string>
-    <string name="configure_dictionaries_title" msgid="4238652338556902049">"एड-अन शब्दकोश"</string>
-    <string name="main_dictionary" msgid="4798763781818361168">"मुख्य शब्दकोश"</string>
-    <string name="prefs_show_suggestions" msgid="8026799663445531637">"सुधार सुझावहरू देखाउनुहोस्"</string>
-    <string name="prefs_show_suggestions_summary" msgid="1583132279498502825">"टाइप गर्ने बेलामा सुझाव शब्दहरू देखाउनुहोस्"</string>
-    <string name="prefs_suggestion_visibility_show_name" msgid="3219916594067551303">"सधैँ देखाउनुहोस्"</string>
-    <string name="prefs_suggestion_visibility_show_only_portrait_name" msgid="3859783767435239118">"चित्र मोडमा देखाउनुहोस्"</string>
-    <string name="prefs_suggestion_visibility_hide_name" msgid="6309143926422234673">"सधैँ लुकाउनुहोस्"</string>
-    <string name="prefs_block_potentially_offensive_title" msgid="5078480071057408934">"आपत्तिजनक शब्दहरूलाई रोक्नुहोस्"</string>
-    <string name="prefs_block_potentially_offensive_summary" msgid="2371835479734991364">"सम्भावित आपत्तिजनक शब्दहरू सुझाव नगर्नुहोस्"</string>
-    <string name="auto_correction" msgid="7630720885194996950">"स्वतः सुधार"</string>
-    <string name="auto_correction_summary" msgid="5625751551134658006">"गल्ती टाइप भएका शब्दहरूलाई स्पेसबार र पङ्चुएसनले स्वचालित रूपमा सच्याउँछन्।"</string>
-    <string name="auto_correction_threshold_mode_off" msgid="8470882665417944026">"बन्द"</string>
-    <string name="auto_correction_threshold_mode_modest" msgid="8788366690620799097">"सामान्य"</string>
-    <string name="auto_correction_threshold_mode_aggressive" msgid="7319007299148899623">"आक्रामक"</string>
-    <string name="auto_correction_threshold_mode_very_aggressive" msgid="1853309024129480416">"ज्यादै आक्रामक"</string>
-    <string name="bigram_prediction" msgid="1084449187723948550">"अर्को शब्द सुझाव"</string>
-    <string name="bigram_prediction_summary" msgid="3896362682751109677">"सुझावहरू निर्माण गर्न अघिल्लो शब्द प्रयोग गर्नुहोस्"</string>
-    <string name="gesture_input" msgid="826951152254563827">"इशारा टाइप गर्ने सक्षम पार्नुहोस्"</string>
-    <string name="gesture_input_summary" msgid="9180350639305731231">"अक्षर स्लाइड गरी शब्द इनपुट गर्नुहोस्"</string>
-    <string name="gesture_preview_trail" msgid="3802333369335722221">"इशारा ट्रेल देखाउनुहोस्"</string>
-    <string name="gesture_floating_preview_text" msgid="4443240334739381053">"गतिशील फ्लोटिङ पूर्वावलोकन"</string>
-    <string name="gesture_floating_preview_text_summary" msgid="4472696213996203533">"इशारा गर्दा सुझाव दिइएको शब्द हेर्नुहोस्"</string>
-    <string name="added_word" msgid="8993883354622484372">"<xliff:g id="WORD">%s</xliff:g> : बचत गरियो"</string>
-    <string name="label_go_key" msgid="1635148082137219148">"जानुहोस्"</string>
-    <string name="label_next_key" msgid="362972844525672568">"अर्को"</string>
-    <string name="label_previous_key" msgid="1211868118071386787">"अघिल्लो"</string>
-    <string name="label_done_key" msgid="2441578748772529288">"भयो"</string>
-    <string name="label_send_key" msgid="2815056534433717444">"पठाउनुहोस्"</string>
-    <string name="label_pause_key" msgid="181098308428035340">"रोक्नुहोस्"</string>
-    <string name="label_wait_key" msgid="6402152600878093134">"प्रतीक्षा गर्नुहोस्"</string>
-    <string name="spoken_use_headphones" msgid="896961781287283493">"हेडसेट प्लग इन गर्नुहोस्"</string>
-    <string name="spoken_current_text_is" msgid="2485723011272583845">"वर्तमान पाठ %s हो"</string>
-    <string name="spoken_no_text_entered" msgid="7479685225597344496">"कुनै पाठ प्रविष्टि गरिएको छैन"</string>
-    <string name="spoken_description_unknown" msgid="3197434010402179157">"कुञ्जी कोड %d"</string>
-    <string name="spoken_description_shift" msgid="244197883292549308">"सिफ्ट"</string>
-    <string name="spoken_description_shift_shifted" msgid="1681877323344195035">"सिप्ट सक्रिय (असक्षम पार्न ट्याप गर्नुहोस्)"</string>
-    <string name="spoken_description_caps_lock" msgid="3276478269526304432">"क्याप्स लक सक्रिय छ (असक्षम पार्न ट्याप गर्नुहोस्)"</string>
-    <string name="spoken_description_delete" msgid="8740376944276199801">"मेट्नुहोस्"</string>
-    <string name="spoken_description_to_symbol" msgid="5486340107500448969">"प्रतिकहरू"</string>
-    <string name="spoken_description_to_alpha" msgid="23129338819771807">"अक्षरहरू"</string>
-    <string name="spoken_description_to_numeric" msgid="591752092685161732">"नम्बरहरू"</string>
-    <string name="spoken_description_settings" msgid="4627462689603838099">"सेटिङहरू"</string>
-    <string name="spoken_description_tab" msgid="2667716002663482248">"ट्याब"</string>
-    <string name="spoken_description_space" msgid="2582521050049860859">"स्पेस"</string>
-    <string name="spoken_description_mic" msgid="615536748882611950">"आवाज इनपुट"</string>
-    <string name="spoken_description_smiley" msgid="2256309826200113918">"मुस्कुराएको अनुहार"</string>
-    <string name="spoken_description_return" msgid="8178083177238315647">"फर्कनुहोस्"</string>
-    <string name="spoken_description_search" msgid="1247236163755920808">"खोज्नुहोस्"</string>
-    <string name="spoken_description_dot" msgid="40711082435231673">"डट"</string>
-    <string name="spoken_description_language_switch" msgid="5507091328222331316">"भाषा स्विच गर्नुहोस्"</string>
-    <string name="spoken_description_action_next" msgid="8636078276664150324">"अर्को"</string>
-    <string name="spoken_description_action_previous" msgid="800872415009336208">"अघिल्लो"</string>
-    <string name="spoken_description_shiftmode_on" msgid="5700440798609574589">"सिफ्ट सक्षम पारिएको छ"</string>
-    <string name="spoken_description_shiftmode_locked" msgid="593175803181701830">"क्याप्स लक सक्षम पारिएको छ"</string>
-    <string name="spoken_description_shiftmode_off" msgid="657219998449174808">"सिफ्ट असक्षम पारिएको छ"</string>
-    <string name="spoken_description_mode_symbol" msgid="7183343879909747642">"प्रतिक मोड"</string>
-    <string name="spoken_description_mode_alpha" msgid="3528307674390156956">"अक्षर मोड"</string>
-    <string name="spoken_description_mode_phone" msgid="6520207943132026264">"फोन मोड"</string>
-    <string name="spoken_description_mode_phone_shift" msgid="5499629753962641227">"फोन प्रतिक मोड"</string>
-    <string name="announce_keyboard_hidden" msgid="8718927835531429807">"किबोर्ड लुकाइएको छ"</string>
-    <string name="announce_keyboard_mode" msgid="4729081055438508321">"<xliff:g id="MODE">%s</xliff:g> किबोर्ड देखाइँदै"</string>
-    <string name="keyboard_mode_date" msgid="3137520166817128102">"मिति"</string>
-    <string name="keyboard_mode_date_time" msgid="339593358488851072">"मिति र समय"</string>
-    <string name="keyboard_mode_email" msgid="6216248078128294262">"इमेल"</string>
-    <string name="keyboard_mode_im" msgid="1137405089766557048">"सन्देश गर्दै"</string>
-    <string name="keyboard_mode_number" msgid="7991623440699957069">"सङ्ख्या"</string>
-    <string name="keyboard_mode_phone" msgid="6851627527401433229">"फोन"</string>
-    <string name="keyboard_mode_text" msgid="6479436687899701619">"पाठ"</string>
-    <string name="keyboard_mode_time" msgid="4381856885582143277">"समय"</string>
-    <string name="keyboard_mode_url" msgid="1519819835514911218">"URL"</string>
-    <string name="voice_input" msgid="3583258583521397548">"आवाज इनपुट कुञ्जी"</string>
-    <string name="voice_input_modes_main_keyboard" msgid="3360660341121083174">"मुख्य किबोर्डमा"</string>
-    <string name="voice_input_modes_symbols_keyboard" msgid="7203213240786084067">"प्रतिकहरू किबोर्डमा"</string>
-    <string name="voice_input_modes_off" msgid="3745699748218082014">"बन्द"</string>
-    <string name="voice_input_modes_summary_main_keyboard" msgid="6586544292900314339">"मुख्य किबोर्डमा माइक"</string>
-    <string name="voice_input_modes_summary_symbols_keyboard" msgid="5233725927281932391">"प्रतिकहरू किबोर्डमा माइक"</string>
-    <string name="voice_input_modes_summary_off" msgid="63875609591897607">"आवाज इनपुट असक्षम पारियो"</string>
-    <string name="configure_input_method" msgid="373356270290742459">"इनपुट विधिहरू कन्फिगर गर्नुहोस्"</string>
-    <string name="language_selection_title" msgid="1651299598555326750">"इनपुट भाषाहरू"</string>
-    <string name="send_feedback" msgid="1780431884109392046">"प्रतिक्रिया पठाउनुहोस्"</string>
-    <string name="select_language" msgid="3693815588777926848">"इनपुट भाषाहरू"</string>
-    <string name="hint_add_to_dictionary" msgid="573678656946085380">"बचत गर्न पुनः छुनुहोस्"</string>
-    <string name="has_dictionary" msgid="6071847973466625007">"उपलब्ध शब्दकोश"</string>
-    <string name="prefs_enable_log" msgid="6620424505072963557">"प्रयोगकर्ता प्रतिक्रिया सक्षम पार्नुहोस्"</string>
-    <string name="prefs_description_log" msgid="7525225584555429211">"स्वचालित रूपमा प्रयोग तथ्याङ्कहरू र क्यास रिपोर्टहरू पठाएर यस इनपुट विधि सम्पादकलाई सुधार्न सहयोग गर्नुहोस्।"</string>
-    <string name="keyboard_layout" msgid="8451164783510487501">"किबोर्ड थिम"</string>
-    <string name="subtype_en_GB" msgid="88170601942311355">"अंग्रेजी (युके)"</string>
-    <string name="subtype_en_US" msgid="6160452336634534239">"अंग्रेजी (युएस्)"</string>
-    <string name="subtype_es_US" msgid="5583145191430180200">"स्पेनिस (युएस्)"</string>
-    <string name="subtype_with_layout_en_GB" msgid="2179097748724725906">"अंग्रेजी (युके) ( <xliff:g id="LAYOUT">%s</xliff:g> )"</string>
-    <string name="subtype_with_layout_en_US" msgid="1362581347576714579">"अंग्रेजी (युएस्) ( <xliff:g id="LAYOUT">%s</xliff:g> )"</string>
-    <string name="subtype_with_layout_es_US" msgid="6261791057007890189">"स्पेनेली (युएस्) ( <xliff:g id="LAYOUT">%s</xliff:g> )"</string>
-    <string name="subtype_no_language" msgid="7137390094240139495">"कुनै भाषा होइन (वर्णमाला)"</string>
-    <string name="subtype_no_language_qwerty" msgid="244337630616742604">"वर्णमाला (QWERTY)"</string>
-    <string name="subtype_no_language_qwertz" msgid="443066912507547976">"वर्णमाला (QWERTZ)"</string>
-    <string name="subtype_no_language_azerty" msgid="8144348527575640087">"वर्णमाला (AZERTY)"</string>
-    <string name="subtype_no_language_dvorak" msgid="1564494667584718094">"वर्णमाला (Dvorak)"</string>
-    <string name="subtype_no_language_colemak" msgid="5837418400010302623">"वर्णमाला (Colemak)"</string>
-    <string name="subtype_no_language_pcqwerty" msgid="5354918232046200018">"वर्णमाला (PC)"</string>
-    <string name="custom_input_styles_title" msgid="8429952441821251512">"अनुकूलन इनपुट शैली"</string>
-    <string name="add_style" msgid="6163126614514489951">"शैली थप्नुहोस्"</string>
-    <string name="add" msgid="8299699805688017798">"थप्नुहोस्"</string>
-    <string name="remove" msgid="4486081658752944606">"हटाउनुहोस्"</string>
-    <string name="save" msgid="7646738597196767214">"बचत गर्नुहोस्"</string>
-    <string name="subtype_locale" msgid="8576443440738143764">"भाषा"</string>
-    <string name="keyboard_layout_set" msgid="4309233698194565609">"लेआउट"</string>
-    <string name="custom_input_style_note_message" msgid="8826731320846363423">"तपाईँले प्रयोग गर्न सुरु गर्न अघि तपाईँको अनुकूलन इनपुट शैली सक्षम पारिनु पर्छ। के तपाईँ यसलाई अहिले सक्षम पार्न चाहनु हुन्छ?"</string>
-    <string name="enable" msgid="5031294444630523247">"सक्षम पार्नुहोस्"</string>
-    <string name="not_now" msgid="6172462888202790482">"अहिले होइन"</string>
-    <string name="custom_input_style_already_exists" msgid="8008728952215449707">"यस्तो इनपुट शैली पहिले नै अवस्थित छ: <xliff:g id="INPUT_STYLE_NAME">%s</xliff:g>"</string>
-    <string name="prefs_usability_study_mode" msgid="1261130555134595254">"प्रयोग अध्ययन मोड"</string>
-    <string name="prefs_key_longpress_timeout_settings" msgid="6102240298932897873">"कुञ्जी लामो थिचाइ ढिलाइ"</string>
-    <string name="prefs_keypress_vibration_duration_settings" msgid="7918341459947439226">"कुञ्जी थिचाइ भाइब्रेसन अवधि"</string>
-    <string name="prefs_keypress_sound_volume_settings" msgid="6027007337036891623">"कुञ्जी थिचाइ आवाज भोल्युम"</string>
-    <string name="prefs_read_external_dictionary" msgid="2588931418575013067">"बाह्य शब्दकोश फाइल पढ्नुहोस्"</string>
-    <string name="read_external_dictionary_no_files_message" msgid="4947420942224623792">"डाउनलोड फोल्डरमा कुनै शब्दकोश फाइलहरू छैनन्।"</string>
-    <string name="read_external_dictionary_multiple_files_title" msgid="7637749044265808628">"स्थापना गर्न कुनै शब्दकोश फाइल चयन गर्नुहोस्"</string>
-    <string name="read_external_dictionary_confirm_install_message" msgid="6898610163768980870">"<xliff:g id="LOCALE_NAME">%s</xliff:g>का लागि साँच्चिकै यो फाइल स्थापना गर्ने हो?"</string>
-    <string name="error" msgid="8940763624668513648">"कुनै त्रुटि भयो"</string>
-    <string name="button_default" msgid="3988017840431881491">"पूर्वनिर्धारित"</string>
-    <string name="setup_welcome_title" msgid="6112821709832031715">"तपाईँलाई स्वागत छ<xliff:g id="APPLICATION_NAME">%s</xliff:g>"</string>
-    <string name="setup_welcome_additional_description" msgid="8150252008545768953">"इशारा टाइप गर्नेसँग"</string>
-    <string name="setup_start_action" msgid="8936036460897347708">"सुरु गरौं"</string>
-    <string name="setup_next_action" msgid="371821437915144603">"अर्को चरण"</string>
-    <string name="setup_steps_title" msgid="6400373034871816182">"स्थापना गर्दै <xliff:g id="APPLICATION_NAME">%s</xliff:g>"</string>
-    <string name="setup_step1_title" msgid="3147967630253462315">"सक्षम पार्नुहोस् <xliff:g id="APPLICATION_NAME">%s</xliff:g>"</string>
-    <string name="setup_step1_instruction" msgid="2578631936624637241">"कृपया जाँच गर्नुहोस् \"<xliff:g id="APPLICATION_NAME">%s</xliff:g>\" तपाईँको भाषा र इनपुट सेटिङमा। यसले तपाईँलाई तपाईँको उपकरणमा सञ्चालन गर्न आधिकारिकता प्रदान गर्छ।"</string>
-    <string name="setup_step1_finished_instruction" msgid="10761482004957994">"<xliff:g id="APPLICATION_NAME">%s</xliff:g> पहिले नै तपाईँको भाषा र इनपुट सेटिङमा सक्षम पारिएको छ, त्यसैले यो कदम सकिसकिएको छ। अर्कोमा जानुहोस्!"</string>
-    <string name="setup_step1_action" msgid="4366513534999901728">"सेटिङहरूमा सक्षम पार्नुहोस्"</string>
-    <string name="setup_step2_title" msgid="6860725447906690594">"<xliff:g id="APPLICATION_NAME">%s</xliff:g>मा स्विच गर्नुहोस्"</string>
-    <string name="setup_step2_instruction" msgid="9141481964870023336">"त्यसपछि, \"<xliff:g id="APPLICATION_NAME">%s</xliff:g>\" लाई तपाईँको सक्रिय पाठ इनपुट विधिका रूपमा चयन गर्नुहोस्।"</string>
-    <string name="setup_step2_action" msgid="1660330307159824337">"इनपुट विधि स्विच गर्नुहोस्"</string>
-    <string name="setup_step3_title" msgid="3154757183631490281">"बधाई छ, तपाईँले सेट पुरा गर्नुभयो!"</string>
-    <string name="setup_step3_instruction" msgid="8025981829605426000">"अब तपाईँ <xliff:g id="APPLICATION_NAME">%s</xliff:g>का साथ तपाईँका सम्पूर्ण मनपर्ने अनुप्रयोगहरू टाइप गर्न सक्नुहुन्छ।"</string>
-    <string name="setup_step3_action" msgid="600879797256942259">"थप भाषाहरू कन्फिगर गर्नुहोस्"</string>
-    <string name="setup_finish_action" msgid="276559243409465389">"समाप्त भयो"</string>
-    <string name="show_setup_wizard_icon" msgid="5008028590593710830">"अनुप्रयोग आइकन देखाउनुहोस्"</string>
-    <string name="show_setup_wizard_icon_summary" msgid="4119998322536880213">"लन्चरमा अनुप्रयोग आइकन देखाउनुहोस्"</string>
-    <string name="app_name" msgid="6320102637491234792">"शब्दकोश प्रदायक"</string>
-    <string name="dictionary_provider_name" msgid="3027315045397363079">"शब्दकोश प्रदायक"</string>
-    <string name="dictionary_service_name" msgid="6237472350693511448">"शब्दकोश सेवा"</string>
-    <string name="download_description" msgid="6014835283119198591">"शब्दकोश अद्यावधिक जानकारी"</string>
-    <string name="dictionary_settings_title" msgid="8091417676045693313">"एड-अन शब्दकोश"</string>
-    <string name="dictionary_install_over_metered_network_prompt" msgid="3587517870006332980">"उपलब्ध शब्दकोश"</string>
-    <string name="dictionary_settings_summary" msgid="5305694987799824349">"शब्दकोशहरूका लागि सेटिङहरू"</string>
-    <string name="user_dictionaries" msgid="3582332055892252845">"प्रयोगकर्ता शब्दकोशहरू"</string>
-    <string name="default_user_dict_pref_name" msgid="1625055720489280530">"प्रयोगकर्ता शब्दकोश"</string>
-    <string name="dictionary_available" msgid="4728975345815214218">"उपलब्ध शब्दकोश"</string>
-    <string name="dictionary_downloading" msgid="2982650524622620983">"हाल डाउनलोड गर्दै"</string>
-    <string name="dictionary_installed" msgid="8081558343559342962">"स्थापित"</string>
-    <string name="dictionary_disabled" msgid="8950383219564621762">"स्थापित, असक्षम पारिएको"</string>
-    <string name="cannot_connect_to_dict_service" msgid="9216933695765732398">"शब्दकोश सेवासँग जोड्न समस्या"</string>
-    <string name="no_dictionaries_available" msgid="8039920716566132611">"शब्दकोशहरू उपलब्ध छैनन्"</string>
-    <string name="check_for_updates_now" msgid="8087688440916388581">"पुनः ताजा गर्नुहोस्"</string>
-    <string name="last_update" msgid="730467549913588780">"पछिल्लो अद्यावधिक"</string>
-    <string name="message_updating" msgid="4457761393932375219">"अद्यावधिकको लागि जाँच गर्दै"</string>
-    <string name="message_loading" msgid="8689096636874758814">"लोड हुँदै..."</string>
-    <string name="main_dict_description" msgid="3072821352793492143">"मुख्य शब्दकोश"</string>
-    <string name="cancel" msgid="6830980399865683324">"रद्द गर्नुहोस्"</string>
-    <string name="install_dict" msgid="180852772562189365">"स्थापना गर्नुहोस्"</string>
-    <string name="cancel_download_dict" msgid="7843340278507019303">"रद्द गर्नुहोस्"</string>
-    <string name="delete_dict" msgid="756853268088330054">"मेट्नुहोस्"</string>
-    <string name="should_download_over_metered_prompt" msgid="2878629598667658845">"तपाईँको मोबाइल उपकरणमा चयन गरिएको भाषामा शब्दकोश उपलब्ध छ। हामी सिफारिश गर्छौं <xliff:g id="LANGUAGE">%1$s</xliff:g> शब्दकोश डाउनलोड गर्नका लागि तपाईँको टाइपिङ अनुभव सुधार्न। यस डाउनलोड 3G मा एक वा दुई मिनेट लाग्छ। शुल्कहरू लाग्न सक्छ यदि तपाईँसँग असीमित डेटा योजना छैन भने। यदि आफूसँग कुन डेटा योजना छ तपाईँ यकिन हुनुहुन्न भने हामी स्वचालित रूपमा डाउनलोड सुरु गर्न वाइ-फाइ जडान खोज्न सिफारिस गर्छौं। सल्लाह: तपाईँको मोबाइल उपकरणको भाषा र इनपुट सेटिङ मेनुमा गई तपाईँ शब्दकोशलाई डाउनलोड वा हटाउन सक्नुहुन्छ।"</string>
-    <string name="download_over_metered" msgid="1643065851159409546">"(अब डाउनलोड गर्नुहोस्<xliff:g id="SIZE_IN_MEGABYTES">%1$.1f</xliff:g> MB)"</string>
-    <string name="do_not_download_over_metered" msgid="2176209579313941583">"वाइ-फाइको माध्ययमद्वार डाउनलोड गर्नुहोस्"</string>
-    <string name="dict_available_notification_title" msgid="6514288591959117288">"एक शब्दकोश  <xliff:g id="LANGUAGE">%1$s</xliff:g> का लागि उपलब्ध छ"</string>
-    <string name="dict_available_notification_description" msgid="1075194169443163487">"समीक्षा गर्न थिच्नुहोस् र डाउनलोड गर्नुहोस्"</string>
-    <string name="toast_downloading_suggestions" msgid="1313027353588566660">"डाउनलोड गरिदै: <xliff:g id="LANGUAGE">%1$s</xliff:g>का लागि सुझावहरू चाँडै तयार हुने छन्।"</string>
-    <string name="version_text" msgid="2715354215568469385">"संस्करण <xliff:g id="VERSION_NUMBER">%1$s</xliff:g>"</string>
-    <string name="user_dict_settings_add_menu_title" msgid="1254195365689387076">"थप्नुहोस्"</string>
-    <string name="user_dict_settings_add_dialog_title" msgid="4096700390211748168">"शब्दकोशमा थप्नुहोस्"</string>
-    <string name="user_dict_settings_add_screen_title" msgid="5818914331629278758">"पदावली"</string>
-    <string name="user_dict_settings_add_dialog_more_options" msgid="5671682004887093112">"थप विकल्पहरू"</string>
-    <string name="user_dict_settings_add_dialog_less_options" msgid="2716586567241724126">"कम विकल्पहरू"</string>
-    <string name="user_dict_settings_add_dialog_confirm" msgid="4703129507388332950">"ठीक छ"</string>
-    <string name="user_dict_settings_add_word_option_name" msgid="6665558053408962865">"शब्द:"</string>
-    <string name="user_dict_settings_add_shortcut_option_name" msgid="3094731590655523777">"सर्टकट:"</string>
-    <string name="user_dict_settings_add_locale_option_name" msgid="4738643440987277705">"भाषा:"</string>
-    <string name="user_dict_settings_add_word_hint" msgid="4902434148985906707">"एउटा शब्द टाइप गर्नुहोस्"</string>
-    <string name="user_dict_settings_add_shortcut_hint" msgid="2265453012555060178">"वैकल्पिक सर्टकट"</string>
-    <string name="user_dict_settings_edit_dialog_title" msgid="3765774633869590352">"शब्द सम्पादन गर्नुहोस्"</string>
-    <string name="user_dict_settings_context_menu_edit_title" msgid="6812255903472456302">"सम्पादन गर्नुहोस्"</string>
-    <string name="user_dict_settings_context_menu_delete_title" msgid="8142932447689461181">"मेट्नुहोस्"</string>
-    <string name="user_dict_settings_empty_text" msgid="558499587532668203">"तपाईँसँग प्रयोगकर्ता शब्दकोशमा कुनै शब्द छैन।\"थप्नुहोस्\"(+) बटनमा छोएर एउटा शब्द थप्नुहोस्।"</string>
-    <string name="user_dict_settings_all_languages" msgid="8276126583216298886">"सबै भाषाहरूका लागि"</string>
-    <string name="user_dict_settings_more_languages" msgid="7131268499685180461">"थप भाषाहरू..."</string>
-    <string name="user_dict_settings_delete" msgid="110413335187193859">"मेट्नुहोस्"</string>
-    <string name="user_dict_fast_scroll_alphabet" msgid="5431919401558285473">" ABCDEFGHIJKLMNOPQRSTUVWXYZ"</string>
-</resources>
diff --git a/java/res/values-nl/strings.xml b/java/res/values-nl/strings.xml
index 3eed154..c61b090 100644
--- a/java/res/values-nl/strings.xml
+++ b/java/res/values-nl/strings.xml
@@ -143,6 +143,7 @@
     <string name="subtype_with_layout_en_GB" msgid="2179097748724725906">"Engels (VK) (<xliff:g id="LAYOUT">%s</xliff:g>)"</string>
     <string name="subtype_with_layout_en_US" msgid="1362581347576714579">"Engels (VS) (<xliff:g id="LAYOUT">%s</xliff:g>)"</string>
     <string name="subtype_with_layout_es_US" msgid="6261791057007890189">"Spaans (VS) (<xliff:g id="LAYOUT">%s</xliff:g>)"</string>
+    <string name="subtype_nepali_traditional" msgid="9032247506728040447">"<xliff:g id="LANGUAGE">%s</xliff:g> (traditioneel)"</string>
     <string name="subtype_no_language" msgid="7137390094240139495">"Geen taal (alfabet)"</string>
     <string name="subtype_no_language_qwerty" msgid="244337630616742604">"Alfabet (QWERTY)"</string>
     <string name="subtype_no_language_qwertz" msgid="443066912507547976">"Alfabet (QWERTZ)"</string>
@@ -150,6 +151,7 @@
     <string name="subtype_no_language_dvorak" msgid="1564494667584718094">"Alfabet (Dvorak)"</string>
     <string name="subtype_no_language_colemak" msgid="5837418400010302623">"Alfabet (Colemak)"</string>
     <string name="subtype_no_language_pcqwerty" msgid="5354918232046200018">"Alfabet (pc)"</string>
+    <string name="subtype_emoji" msgid="7483586578074549196">"Emoji"</string>
     <string name="custom_input_styles_title" msgid="8429952441821251512">"Aangep. invoerstijlen"</string>
     <string name="add_style" msgid="6163126614514489951">"Stijl toev."</string>
     <string name="add" msgid="8299699805688017798">"Toevoegen"</string>
diff --git a/java/res/values-pl/strings.xml b/java/res/values-pl/strings.xml
index b4f2612..31eace5 100644
--- a/java/res/values-pl/strings.xml
+++ b/java/res/values-pl/strings.xml
@@ -143,6 +143,7 @@
     <string name="subtype_with_layout_en_GB" msgid="2179097748724725906">"angielski (UK) (<xliff:g id="LAYOUT">%s</xliff:g>)"</string>
     <string name="subtype_with_layout_en_US" msgid="1362581347576714579">"angielski (USA) (<xliff:g id="LAYOUT">%s</xliff:g>)"</string>
     <string name="subtype_with_layout_es_US" msgid="6261791057007890189">"hiszpański (USA) (<xliff:g id="LAYOUT">%s</xliff:g>)"</string>
+    <string name="subtype_nepali_traditional" msgid="9032247506728040447">"<xliff:g id="LANGUAGE">%s</xliff:g> (tradycyjny)"</string>
     <string name="subtype_no_language" msgid="7137390094240139495">"Bez języka (alfabet)"</string>
     <string name="subtype_no_language_qwerty" msgid="244337630616742604">"Alfabet (QWERTY)"</string>
     <string name="subtype_no_language_qwertz" msgid="443066912507547976">"Alfabet (QWERTZ)"</string>
@@ -150,6 +151,7 @@
     <string name="subtype_no_language_dvorak" msgid="1564494667584718094">"Alfabet (Dvorak)"</string>
     <string name="subtype_no_language_colemak" msgid="5837418400010302623">"Alfabet (Colemak)"</string>
     <string name="subtype_no_language_pcqwerty" msgid="5354918232046200018">"Alfabet (PC)"</string>
+    <string name="subtype_emoji" msgid="7483586578074549196">"Emotikony"</string>
     <string name="custom_input_styles_title" msgid="8429952441821251512">"Style niestandardowe"</string>
     <string name="add_style" msgid="6163126614514489951">"Dodaj styl"</string>
     <string name="add" msgid="8299699805688017798">"Dodaj"</string>
diff --git a/java/res/values-pt-rPT/strings.xml b/java/res/values-pt-rPT/strings.xml
index 96a2b0a..cd89a77 100644
--- a/java/res/values-pt-rPT/strings.xml
+++ b/java/res/values-pt-rPT/strings.xml
@@ -143,6 +143,7 @@
     <string name="subtype_with_layout_en_GB" msgid="2179097748724725906">"Inglês (RU) (<xliff:g id="LAYOUT">%s</xliff:g>)"</string>
     <string name="subtype_with_layout_en_US" msgid="1362581347576714579">"Inglês (EUA) (<xliff:g id="LAYOUT">%s</xliff:g>)"</string>
     <string name="subtype_with_layout_es_US" msgid="6261791057007890189">"Espanhol (EUA) (<xliff:g id="LAYOUT">%s</xliff:g>)"</string>
+    <string name="subtype_nepali_traditional" msgid="9032247506728040447">"<xliff:g id="LANGUAGE">%s</xliff:g> (Tradicional)"</string>
     <string name="subtype_no_language" msgid="7137390094240139495">"Sem idioma (alfabeto)"</string>
     <string name="subtype_no_language_qwerty" msgid="244337630616742604">"Alfabeto (QWERTY)"</string>
     <string name="subtype_no_language_qwertz" msgid="443066912507547976">"Alfabeto (QWERTZ)"</string>
@@ -150,6 +151,7 @@
     <string name="subtype_no_language_dvorak" msgid="1564494667584718094">"Alfabeto (Dvorak)"</string>
     <string name="subtype_no_language_colemak" msgid="5837418400010302623">"Alfabeto (Colemak)"</string>
     <string name="subtype_no_language_pcqwerty" msgid="5354918232046200018">"Alfabeto (PC)"</string>
+    <string name="subtype_emoji" msgid="7483586578074549196">"Emoji"</string>
     <string name="custom_input_styles_title" msgid="8429952441821251512">"Estilos entrada pers."</string>
     <string name="add_style" msgid="6163126614514489951">"Adic. estilo"</string>
     <string name="add" msgid="8299699805688017798">"Adicionar"</string>
diff --git a/java/res/values-pt/strings.xml b/java/res/values-pt/strings.xml
index e319255..820f577 100644
--- a/java/res/values-pt/strings.xml
+++ b/java/res/values-pt/strings.xml
@@ -143,6 +143,7 @@
     <string name="subtype_with_layout_en_GB" msgid="2179097748724725906">"Inglês (Reino Unido) (<xliff:g id="LAYOUT">%s</xliff:g>)"</string>
     <string name="subtype_with_layout_en_US" msgid="1362581347576714579">"Inglês (EUA) (<xliff:g id="LAYOUT">%s</xliff:g>)"</string>
     <string name="subtype_with_layout_es_US" msgid="6261791057007890189">"espanhol (EUA) (<xliff:g id="LAYOUT">%s</xliff:g>)"</string>
+    <string name="subtype_nepali_traditional" msgid="9032247506728040447">"<xliff:g id="LANGUAGE">%s</xliff:g> (tradicional)"</string>
     <string name="subtype_no_language" msgid="7137390094240139495">"Nenhum idioma (alfabeto)"</string>
     <string name="subtype_no_language_qwerty" msgid="244337630616742604">"Alfabeto (QWERTY)"</string>
     <string name="subtype_no_language_qwertz" msgid="443066912507547976">"Alfabeto (QWERTZ)"</string>
@@ -150,6 +151,7 @@
     <string name="subtype_no_language_dvorak" msgid="1564494667584718094">"Alfabeto (Dvorak)"</string>
     <string name="subtype_no_language_colemak" msgid="5837418400010302623">"Alfabeto (Colemak)"</string>
     <string name="subtype_no_language_pcqwerty" msgid="5354918232046200018">"Alfabeto (PC)"</string>
+    <string name="subtype_emoji" msgid="7483586578074549196">"Emoji"</string>
     <string name="custom_input_styles_title" msgid="8429952441821251512">"Estilos personalizados"</string>
     <string name="add_style" msgid="6163126614514489951">"Adic. estilo"</string>
     <string name="add" msgid="8299699805688017798">"Adicionar"</string>
diff --git a/java/res/values-rm/strings.xml b/java/res/values-rm/strings.xml
index 1d14b8f..1c509ad 100644
--- a/java/res/values-rm/strings.xml
+++ b/java/res/values-rm/strings.xml
@@ -254,6 +254,8 @@
     <skip />
     <!-- no translation found for subtype_with_layout_es_US (6261791057007890189) -->
     <skip />
+    <!-- no translation found for subtype_nepali_traditional (9032247506728040447) -->
+    <skip />
     <!-- no translation found for subtype_no_language (7137390094240139495) -->
     <skip />
     <!-- no translation found for subtype_no_language_qwerty (244337630616742604) -->
@@ -268,6 +270,8 @@
     <skip />
     <!-- no translation found for subtype_no_language_pcqwerty (5354918232046200018) -->
     <skip />
+    <!-- no translation found for subtype_emoji (7483586578074549196) -->
+    <skip />
     <!-- no translation found for custom_input_styles_title (8429952441821251512) -->
     <skip />
     <!-- no translation found for add_style (6163126614514489951) -->
diff --git a/java/res/values-ro/strings.xml b/java/res/values-ro/strings.xml
index 56ff8b4..3caff13 100644
--- a/java/res/values-ro/strings.xml
+++ b/java/res/values-ro/strings.xml
@@ -143,6 +143,7 @@
     <string name="subtype_with_layout_en_GB" msgid="2179097748724725906">"Engleză (Regatul Unit) (<xliff:g id="LAYOUT">%s</xliff:g>)"</string>
     <string name="subtype_with_layout_en_US" msgid="1362581347576714579">"Engleză (S.U.A.) (<xliff:g id="LAYOUT">%s</xliff:g>)"</string>
     <string name="subtype_with_layout_es_US" msgid="6261791057007890189">"Spaniolă (S.U.A.) (<xliff:g id="LAYOUT">%s</xliff:g>)"</string>
+    <string name="subtype_nepali_traditional" msgid="9032247506728040447">"<xliff:g id="LANGUAGE">%s</xliff:g> (tradițional)"</string>
     <string name="subtype_no_language" msgid="7137390094240139495">"Nicio limbă (alfabet)"</string>
     <string name="subtype_no_language_qwerty" msgid="244337630616742604">"Alfabet (QWERTY)"</string>
     <string name="subtype_no_language_qwertz" msgid="443066912507547976">"Alfabet (QWERTZ)"</string>
@@ -150,6 +151,7 @@
     <string name="subtype_no_language_dvorak" msgid="1564494667584718094">"Alfabet (Dvorak)"</string>
     <string name="subtype_no_language_colemak" msgid="5837418400010302623">"Alfabet (Colemak)"</string>
     <string name="subtype_no_language_pcqwerty" msgid="5354918232046200018">"Alfabet (PC)"</string>
+    <string name="subtype_emoji" msgid="7483586578074549196">"Emoji"</string>
     <string name="custom_input_styles_title" msgid="8429952441821251512">"Stiluri personalizate"</string>
     <string name="add_style" msgid="6163126614514489951">"Stil"</string>
     <string name="add" msgid="8299699805688017798">"Adăugaţi"</string>
diff --git a/java/res/values-ru/strings.xml b/java/res/values-ru/strings.xml
index b7539d1..affe88f 100644
--- a/java/res/values-ru/strings.xml
+++ b/java/res/values-ru/strings.xml
@@ -143,6 +143,7 @@
     <string name="subtype_with_layout_en_GB" msgid="2179097748724725906">"Английская (Великобр.) (<xliff:g id="LAYOUT">%s</xliff:g>)"</string>
     <string name="subtype_with_layout_en_US" msgid="1362581347576714579">"Английская (США) (<xliff:g id="LAYOUT">%s</xliff:g>)"</string>
     <string name="subtype_with_layout_es_US" msgid="6261791057007890189">"Испанский (США): <xliff:g id="LAYOUT">%s</xliff:g>"</string>
+    <string name="subtype_nepali_traditional" msgid="9032247506728040447">"<xliff:g id="LANGUAGE">%s</xliff:g> (традиционный)"</string>
     <string name="subtype_no_language" msgid="7137390094240139495">"Язык не определен (латиница)"</string>
     <string name="subtype_no_language_qwerty" msgid="244337630616742604">"Латиница (QWERTY)"</string>
     <string name="subtype_no_language_qwertz" msgid="443066912507547976">"Латиница (QWERTZ)"</string>
@@ -150,6 +151,7 @@
     <string name="subtype_no_language_dvorak" msgid="1564494667584718094">"Латиница (Dvorak)"</string>
     <string name="subtype_no_language_colemak" msgid="5837418400010302623">"Латиница (Colemak)"</string>
     <string name="subtype_no_language_pcqwerty" msgid="5354918232046200018">"Латиница (ПК)"</string>
+    <string name="subtype_emoji" msgid="7483586578074549196">"Эмодзи"</string>
     <string name="custom_input_styles_title" msgid="8429952441821251512">"Персонализированные стили"</string>
     <string name="add_style" msgid="6163126614514489951">"Добавить стиль"</string>
     <string name="add" msgid="8299699805688017798">"Добавить"</string>
diff --git a/java/res/values-si-rLK/strings.xml b/java/res/values-si-rLK/strings.xml
deleted file mode 100644
index e296d52..0000000
--- a/java/res/values-si-rLK/strings.xml
+++ /dev/null
@@ -1,242 +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.
-*/
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="english_ime_input_options" msgid="3909945612939668554">"ආදාන විකල්ප"</string>
-    <string name="english_ime_research_log" msgid="8492602295696577851">"පර්යේෂණ ලොග් විධාන"</string>
-    <string name="use_contacts_for_spellchecking_option_title" msgid="5374120998125353898">"සබඳතා නම් විමසන්න"</string>
-    <string name="use_contacts_for_spellchecking_option_summary" msgid="8754413382543307713">"අක්ෂර වින්‍යාස පරික්ෂකය ඔබගේ සබඳතා ලැයිස්තුව වෙතින් ඇතුළත් කිරීම් භාවිතා කරයි"</string>
-    <string name="vibrate_on_keypress" msgid="5258079494276955460">"යතුර එබීමට කම්පනය කිරීම සක්‍රියයි"</string>
-    <string name="sound_on_keypress" msgid="6093592297198243644">"යතුරු එබිම මත හඬ"</string>
-    <string name="popup_on_keypress" msgid="123894815723512944">"යතුරු එබීම මත උත්පතනය"</string>
-    <string name="general_category" msgid="1859088467017573195">"සාමාන්‍ය"</string>
-    <string name="correction_category" msgid="2236750915056607613">"පෙළ නිවැරදි කිරීම"</string>
-    <string name="gesture_typing_category" msgid="497263612130532630">"ඉංගිතයෙන් ටයිප් කිරීම"</string>
-    <string name="misc_category" msgid="6894192814868233453">"වෙනත් විකල්ප"</string>
-    <string name="advanced_settings" msgid="362895144495591463">"උසස් සැකසීම්"</string>
-    <string name="advanced_settings_summary" msgid="4487980456152830271">"ප්‍රවීනයන් සඳහා විකල්ප"</string>
-    <string name="include_other_imes_in_language_switch_list" msgid="4533689960308565519">"වෙනත් ආදාන ක්‍රම වෙත මාරුවන්න"</string>
-    <string name="include_other_imes_in_language_switch_list_summary" msgid="840637129103317635">"භාෂා මාරු යතුර වෙනත් ආදාන ක්‍රමද ආවරණය කරයි"</string>
-    <string name="show_language_switch_key" msgid="5915478828318774384">"භාෂා මාරු යතුර"</string>
-    <string name="show_language_switch_key_summary" msgid="7343403647474265713">"බහුවිධ ආදාන භාෂා සබල කර ඇති විට පෙන්වන්න"</string>
-    <string name="sliding_key_input_preview" msgid="6604262359510068370">"සර්පණ දර්ශකය පෙන්වන්න"</string>
-    <string name="sliding_key_input_preview_summary" msgid="6340524345729093886">"ෂිෆ්ට් හෝ සංකේත යතුරු වෙතින් සර්පණය කරන අතරතුර දෘෂ්‍ය ඉඟි දර්ශනය කරන්න"</string>
-    <string name="key_preview_popup_dismiss_delay" msgid="6213164897443068248">"යතුරු උත්පතන ඉවත් කිරීමේ ප්‍රමාදය"</string>
-    <string name="key_preview_popup_dismiss_no_delay" msgid="2096123151571458064">"ප්‍රමාද නැත"</string>
-    <string name="key_preview_popup_dismiss_default_delay" msgid="2166964333903906734">"සුපුරුදු"</string>
-    <string name="abbreviation_unit_milliseconds" msgid="8700286094028323363">"<xliff:g id="MILLISECONDS">%s</xliff:g>ms"</string>
-    <string name="settings_system_default" msgid="6268225104743331821">"පද්ධති සුපුරුදු"</string>
-    <string name="use_contacts_dict" msgid="4435317977804180815">"සබඳතා නම් යෝජනා කරන්න"</string>
-    <string name="use_contacts_dict_summary" msgid="6599983334507879959">"යෝජනා සහ නිවැරදි කිරීම් සඳහා සබඳතා වෙතින් නම් භාවිතා කරන්න"</string>
-    <string name="use_double_space_period" msgid="8781529969425082860">"දෙවරක්-ඉඩ නැවතීමේ ලකුණ"</string>
-    <string name="use_double_space_period_summary" msgid="6532892187247952799">"ඉඩ යතුර මත දෙවරක් තට්ටු කිරීම හිස් තැනකට අනුගාමිව නැවතීමේ ලකුණක් ඇතුළත් කරයි."</string>
-    <string name="auto_cap" msgid="1719746674854628252">"ස්වයං-ලොකු අකුරු කරණය"</string>
-    <string name="auto_cap_summary" msgid="7934452761022946874">"එක් එක් වාක්‍යයේ පළමු වචනය ලොකු අකුරු කරන්න"</string>
-    <string name="edit_personal_dictionary" msgid="3996910038952940420">"පුද්ගලික ශබ්ද කෝෂය"</string>
-    <string name="configure_dictionaries_title" msgid="4238652338556902049">"ඈඳුම් ශබ්දකෝෂ"</string>
-    <string name="main_dictionary" msgid="4798763781818361168">"ප්‍රධාන ශබ්ද කෝෂය"</string>
-    <string name="prefs_show_suggestions" msgid="8026799663445531637">"නිවැරදි කිරීම් යෝජනා පෙන්වන්න"</string>
-    <string name="prefs_show_suggestions_summary" msgid="1583132279498502825">"ටයිප් කරන අතරතුර යෝජිත වචන දර්ශනය කරන්න"</string>
-    <string name="prefs_suggestion_visibility_show_name" msgid="3219916594067551303">"සැමවිටම පෙන්වන්න"</string>
-    <string name="prefs_suggestion_visibility_show_only_portrait_name" msgid="3859783767435239118">"සිරස් ආකෘති ප්‍රකාරය තුළ පෙන්වන්න"</string>
-    <string name="prefs_suggestion_visibility_hide_name" msgid="6309143926422234673">"සැමවිට සඟවන්න"</string>
-    <string name="prefs_block_potentially_offensive_title" msgid="5078480071057408934">"අප්‍රසන්න වචන අවහිර කරන්න"</string>
-    <string name="prefs_block_potentially_offensive_summary" msgid="2371835479734991364">"විභව්‍යව අප්‍රසන්න වචන යෝජනා නොකරන්න"</string>
-    <string name="auto_correction" msgid="7630720885194996950">"ස්වයං-නිවැරදි කිරීම"</string>
-    <string name="auto_correction_summary" msgid="5625751551134658006">"ඉඩ යතුර සහ විරාම ලකුණ වැරදියට ටයිප් කළ වචන ස්වයංක්‍රියව නිවැරදි කරයි"</string>
-    <string name="auto_correction_threshold_mode_off" msgid="8470882665417944026">"අක්‍රියයි"</string>
-    <string name="auto_correction_threshold_mode_modest" msgid="8788366690620799097">"මධ්‍යස්"</string>
-    <string name="auto_correction_threshold_mode_aggressive" msgid="7319007299148899623">"ආක්‍රමණකාරී"</string>
-    <string name="auto_correction_threshold_mode_very_aggressive" msgid="1853309024129480416">"ඉතා ආක්‍රමණකාරී"</string>
-    <string name="bigram_prediction" msgid="1084449187723948550">"ඊළඟ-වචනයේ යෝජනා"</string>
-    <string name="bigram_prediction_summary" msgid="3896362682751109677">"යෝජනා කිරීමේදී පෙර වචනය භාවිතා කරන්න"</string>
-    <string name="gesture_input" msgid="826951152254563827">"ඉංගිතයෙන් ටයිප් කිරීම සබල කරන්න"</string>
-    <string name="gesture_input_summary" msgid="9180350639305731231">"අකුරු ඔස්සේ සර්පණය කිරීමෙන් වචනයක් ආදානය කරන්න"</string>
-    <string name="gesture_preview_trail" msgid="3802333369335722221">"ඉංගිතයෙන් මඟ පෙන්වන්න"</string>
-    <string name="gesture_floating_preview_text" msgid="4443240334739381053">"ගතිකව ඉපිලෙන පෙරදසුන"</string>
-    <string name="gesture_floating_preview_text_summary" msgid="4472696213996203533">"ඉංගිතය කරන අතරතුර යෝජිත වචන බලන්න"</string>
-    <string name="added_word" msgid="8993883354622484372">"<xliff:g id="WORD">%s</xliff:g> : සුරැකිණි"</string>
-    <string name="label_go_key" msgid="1635148082137219148">"යන්න"</string>
-    <string name="label_next_key" msgid="362972844525672568">"ඊළඟ"</string>
-    <string name="label_previous_key" msgid="1211868118071386787">"පෙර"</string>
-    <string name="label_done_key" msgid="2441578748772529288">"හරි"</string>
-    <string name="label_send_key" msgid="2815056534433717444">"යවන්න"</string>
-    <string name="label_pause_key" msgid="181098308428035340">"විරාම කරන්න"</string>
-    <string name="label_wait_key" msgid="6402152600878093134">"රැඳී සිටින්න"</string>
-    <string name="spoken_use_headphones" msgid="896961781287283493">"හඬ නගා කථනය කරන මුරපද යතුරු ඇසීමට හෙඩ්සෙට් එකක් පේනුගත කරන්න."</string>
-    <string name="spoken_current_text_is" msgid="2485723011272583845">"වර්තමාන පෙළ %s ය"</string>
-    <string name="spoken_no_text_entered" msgid="7479685225597344496">"පෙළ ඇතුළු කර නැත"</string>
-    <string name="spoken_description_unknown" msgid="3197434010402179157">"යතුරු කේතය %d"</string>
-    <string name="spoken_description_shift" msgid="244197883292549308">"ෂිෆ්ට්"</string>
-    <string name="spoken_description_shift_shifted" msgid="1681877323344195035">"ෂිෆ්ට් සක්‍රියයි (අබල කිරීමට තට්ටු කරන්න)"</string>
-    <string name="spoken_description_caps_lock" msgid="3276478269526304432">"කැප්ස් ලොක් සක්‍රියයි (අබල කිරීමට තට්ටු කරන්න)"</string>
-    <string name="spoken_description_delete" msgid="8740376944276199801">"මකන්න"</string>
-    <string name="spoken_description_to_symbol" msgid="5486340107500448969">"සංකේත"</string>
-    <string name="spoken_description_to_alpha" msgid="23129338819771807">"අකුරු"</string>
-    <string name="spoken_description_to_numeric" msgid="591752092685161732">"අංක"</string>
-    <string name="spoken_description_settings" msgid="4627462689603838099">"සැකසීම්"</string>
-    <string name="spoken_description_tab" msgid="2667716002663482248">"ටැබය"</string>
-    <string name="spoken_description_space" msgid="2582521050049860859">"හිඩස"</string>
-    <string name="spoken_description_mic" msgid="615536748882611950">"හඬ ආදානය"</string>
-    <string name="spoken_description_smiley" msgid="2256309826200113918">"සිනහ මුහුණ"</string>
-    <string name="spoken_description_return" msgid="8178083177238315647">"ආපසු එවන්න"</string>
-    <string name="spoken_description_search" msgid="1247236163755920808">"සෙවීම"</string>
-    <string name="spoken_description_dot" msgid="40711082435231673">"තිත"</string>
-    <string name="spoken_description_language_switch" msgid="5507091328222331316">"භාෂාව මාරු කරන්න"</string>
-    <string name="spoken_description_action_next" msgid="8636078276664150324">"ඊළඟ"</string>
-    <string name="spoken_description_action_previous" msgid="800872415009336208">"පෙර"</string>
-    <string name="spoken_description_shiftmode_on" msgid="5700440798609574589">"ෂිෆ්ට් සබල කර ඇත"</string>
-    <string name="spoken_description_shiftmode_locked" msgid="593175803181701830">"කැප්ස් ලොක් සබල කර ඇත"</string>
-    <string name="spoken_description_shiftmode_off" msgid="657219998449174808">"ෂිෆ්ට් අබල කර ඇත"</string>
-    <string name="spoken_description_mode_symbol" msgid="7183343879909747642">"සංකේත ආකාරය"</string>
-    <string name="spoken_description_mode_alpha" msgid="3528307674390156956">"අකුරු ආකාරය"</string>
-    <string name="spoken_description_mode_phone" msgid="6520207943132026264">"දුරකථන ආකාරය"</string>
-    <string name="spoken_description_mode_phone_shift" msgid="5499629753962641227">"දුරකථන සංකේත ආකාරය"</string>
-    <string name="announce_keyboard_hidden" msgid="8718927835531429807">"යතුරු පුවරුව සැඟවී ඇත"</string>
-    <string name="announce_keyboard_mode" msgid="4729081055438508321">"<xliff:g id="MODE">%s</xliff:g> යතුරු පුවරුව පෙන්නුම් කෙරේ"</string>
-    <string name="keyboard_mode_date" msgid="3137520166817128102">"දිනය"</string>
-    <string name="keyboard_mode_date_time" msgid="339593358488851072">"දිනය සහ වේලාව"</string>
-    <string name="keyboard_mode_email" msgid="6216248078128294262">"ඊ-තැපෑල"</string>
-    <string name="keyboard_mode_im" msgid="1137405089766557048">"පණිවිඩ යැවීම"</string>
-    <string name="keyboard_mode_number" msgid="7991623440699957069">"අංකය"</string>
-    <string name="keyboard_mode_phone" msgid="6851627527401433229">"දුරකථනය"</string>
-    <string name="keyboard_mode_text" msgid="6479436687899701619">"පෙළ"</string>
-    <string name="keyboard_mode_time" msgid="4381856885582143277">"කාලය"</string>
-    <string name="keyboard_mode_url" msgid="1519819835514911218">"URL"</string>
-    <string name="voice_input" msgid="3583258583521397548">"හඬ ආදාන යතුර"</string>
-    <string name="voice_input_modes_main_keyboard" msgid="3360660341121083174">"ප්‍රධාන යතුරු පුවරුව මත"</string>
-    <string name="voice_input_modes_symbols_keyboard" msgid="7203213240786084067">"සංකේත යතුරු පුවරුව මත"</string>
-    <string name="voice_input_modes_off" msgid="3745699748218082014">"අක්‍රියයි"</string>
-    <string name="voice_input_modes_summary_main_keyboard" msgid="6586544292900314339">"ප්‍රධාන යතුරු පුවරුව මත මයික්‍රෆෝනය"</string>
-    <string name="voice_input_modes_summary_symbols_keyboard" msgid="5233725927281932391">"සංකේත යතුරු පුවරුව මත මයික්‍රෆෝනය"</string>
-    <string name="voice_input_modes_summary_off" msgid="63875609591897607">"හඬ ආදානය අබල කර ඇත"</string>
-    <string name="configure_input_method" msgid="373356270290742459">"ආදාන ක්‍රම වින්‍යාස කරන්න"</string>
-    <string name="language_selection_title" msgid="1651299598555326750">"ආදාන භාෂා"</string>
-    <string name="send_feedback" msgid="1780431884109392046">"ප්‍රතිපෝෂණ යවන්න"</string>
-    <string name="select_language" msgid="3693815588777926848">"ආදාන භාෂා"</string>
-    <string name="hint_add_to_dictionary" msgid="573678656946085380">"සුරැකීමට නැවත ස්පර්ශ කරන්න"</string>
-    <string name="has_dictionary" msgid="6071847973466625007">"ශබ්ද කෝෂය ලබාගත හැක"</string>
-    <string name="prefs_enable_log" msgid="6620424505072963557">"පරිශීලක ප්‍රතිපෝෂණ සබල කරන්න"</string>
-    <string name="prefs_description_log" msgid="7525225584555429211">"භාවිතය පිළිබඳ සංඛ්‍යාලේඛන සහ බිඳ වැටීම් වාර්තා ස්වයංක්‍රියව යැවීම මගින් ආදාන ක්‍රම සංස්කාරක වැඩි දියුණු කිරීමට උදව් වන්න."</string>
-    <string name="keyboard_layout" msgid="8451164783510487501">"යතුරු පුවරු තේමාව"</string>
-    <string name="subtype_en_GB" msgid="88170601942311355">"ඉංග්‍රීසි (UK)"</string>
-    <string name="subtype_en_US" msgid="6160452336634534239">"ඉංග්‍රීසි (US)"</string>
-    <string name="subtype_es_US" msgid="5583145191430180200">"ස්පාඤ්ඤ (US)"</string>
-    <string name="subtype_with_layout_en_GB" msgid="2179097748724725906">"ඉංග්‍රිසි (UK) (<xliff:g id="LAYOUT">%s</xliff:g>)"</string>
-    <string name="subtype_with_layout_en_US" msgid="1362581347576714579">"ඉංග්‍රීසි (US) (<xliff:g id="LAYOUT">%s</xliff:g>)"</string>
-    <string name="subtype_with_layout_es_US" msgid="6261791057007890189">"ස්පාඤ්ඤ (US) (<xliff:g id="LAYOUT">%s</xliff:g>)"</string>
-    <string name="subtype_no_language" msgid="7137390094240139495">"භාෂාවක් නැත (අකාරාදිය)"</string>
-    <string name="subtype_no_language_qwerty" msgid="244337630616742604">"අකාරාදිය (QWERTY)"</string>
-    <string name="subtype_no_language_qwertz" msgid="443066912507547976">"අකාරාදිය (QWERTZ)"</string>
-    <string name="subtype_no_language_azerty" msgid="8144348527575640087">"අකාරාදිය (AZERTY)"</string>
-    <string name="subtype_no_language_dvorak" msgid="1564494667584718094">"අකාරාදිය (Dvorak)"</string>
-    <string name="subtype_no_language_colemak" msgid="5837418400010302623">"අකාරාදිය (Colemak)"</string>
-    <string name="subtype_no_language_pcqwerty" msgid="5354918232046200018">"අකාරාදිය (PC)"</string>
-    <string name="custom_input_styles_title" msgid="8429952441821251512">"අභිරුචි ආදාන විලාස"</string>
-    <string name="add_style" msgid="6163126614514489951">"විලාසය එක් කරන්න"</string>
-    <string name="add" msgid="8299699805688017798">"එක් කරන්න"</string>
-    <string name="remove" msgid="4486081658752944606">"ඉවත් කරන්න"</string>
-    <string name="save" msgid="7646738597196767214">"සුරකින්න"</string>
-    <string name="subtype_locale" msgid="8576443440738143764">"භාෂාව"</string>
-    <string name="keyboard_layout_set" msgid="4309233698194565609">"පිරිසැලසුම"</string>
-    <string name="custom_input_style_note_message" msgid="8826731320846363423">"ඔබ එය භාවිතය ආරම්භ කිරීමට පෙර ඔබගේ අභිරුචි ආදාන විලාසය සබල කිරීමට අවශ්‍යය. ඔබට එය දැන් සබල කිරීමට අවශ්‍යද?"</string>
-    <string name="enable" msgid="5031294444630523247">"සබල කරන්න"</string>
-    <string name="not_now" msgid="6172462888202790482">"දැන් නොවේ"</string>
-    <string name="custom_input_style_already_exists" msgid="8008728952215449707">"සමාන ආදාන විලාසය දැනටමත් පවතී: <xliff:g id="INPUT_STYLE_NAME">%s</xliff:g>"</string>
-    <string name="prefs_usability_study_mode" msgid="1261130555134595254">"උපයෝජ්‍යතා අධ්‍යයන ආකාරය"</string>
-    <string name="prefs_key_longpress_timeout_settings" msgid="6102240298932897873">"යතුරු දිගු එබීම් ප්‍රමාදය"</string>
-    <string name="prefs_keypress_vibration_duration_settings" msgid="7918341459947439226">"යතුරු එබිම් කම්පන කාලපරිච්ඡේදය"</string>
-    <string name="prefs_keypress_sound_volume_settings" msgid="6027007337036891623">"යතුරු එබීම් හඬ තීව්‍රතාවය"</string>
-    <string name="prefs_read_external_dictionary" msgid="2588931418575013067">"බාහිර ශබ්ද කෝෂ ගොනුව කියවන්න"</string>
-    <string name="read_external_dictionary_no_files_message" msgid="4947420942224623792">"බාගැනීම් ෆෝල්ඩරය තුළ ශබ්දකෝෂ ගොනු නොමැත"</string>
-    <string name="read_external_dictionary_multiple_files_title" msgid="7637749044265808628">"ස්ථාපනය කිරීමට ශබ්ද කෝෂ ගොනුවක් තෝරන්න"</string>
-    <string name="read_external_dictionary_confirm_install_message" msgid="6898610163768980870">"<xliff:g id="LOCALE_NAME">%s</xliff:g> සඳහා මෙම ගොනුව ස්ථාපනය කරන්නද?"</string>
-    <string name="error" msgid="8940763624668513648">"දෝෂයක් ඇති විය"</string>
-    <string name="button_default" msgid="3988017840431881491">"සුපුරුදු"</string>
-    <string name="setup_welcome_title" msgid="6112821709832031715">"<xliff:g id="APPLICATION_NAME">%s</xliff:g> වෙත සාදරයෙන් පිළිගනිමු"</string>
-    <string name="setup_welcome_additional_description" msgid="8150252008545768953">"ඉංගිත ටයිප් කිරීම් සමග"</string>
-    <string name="setup_start_action" msgid="8936036460897347708">"අරඹන්න"</string>
-    <string name="setup_next_action" msgid="371821437915144603">"ඊළඟ පියවර"</string>
-    <string name="setup_steps_title" msgid="6400373034871816182">"<xliff:g id="APPLICATION_NAME">%s</xliff:g> සැකසෙමින් පවතී"</string>
-    <string name="setup_step1_title" msgid="3147967630253462315">"<xliff:g id="APPLICATION_NAME">%s</xliff:g> සබල කරන්න"</string>
-    <string name="setup_step1_instruction" msgid="2578631936624637241">"කරණාකර ඔබගේ භාෂවෙහි සහ ආදාන සැකසීම් වල \"<xliff:g id="APPLICATION_NAME">%s</xliff:g>\" පරික්ෂා කරන්න. මෙය ඔබගේ උපාංගය මත එයට ධාවනය වීමට අනුමැතිය දෙනු ඇත."</string>
-    <string name="setup_step1_finished_instruction" msgid="10761482004957994">"<xliff:g id="APPLICATION_NAME">%s</xliff:g> දැනටමත් ඔබගේ භාෂාවෙන් සහ ආදාන සැකසීම්වල සබල කර ඇත, එම නිසා මෙම පියවර නිමයි. ඊළග එක වෙතට!"</string>
-    <string name="setup_step1_action" msgid="4366513534999901728">"සැකසීම් තුළ සබල කරන්න"</string>
-    <string name="setup_step2_title" msgid="6860725447906690594">"<xliff:g id="APPLICATION_NAME">%s</xliff:g> වෙත මාරුවන්න"</string>
-    <string name="setup_step2_instruction" msgid="9141481964870023336">"ඊළඟට, ඔබගේ සක්‍රිය පෙළ-ආදාන ක්‍රමය ලෙස \"<xliff:g id="APPLICATION_NAME">%s</xliff:g>\" තෝරන්න."</string>
-    <string name="setup_step2_action" msgid="1660330307159824337">"ආදාන ක්‍රම මාරු කරන්න"</string>
-    <string name="setup_step3_title" msgid="3154757183631490281">"සුබපැතුම්, ඔබ සියල්ල පිහිටුවා ඇත!"</string>
-    <string name="setup_step3_instruction" msgid="8025981829605426000">"දැන් ඔබට <xliff:g id="APPLICATION_NAME">%s</xliff:g> සමගින් ඔබගේ  සියළුම ප්‍රියතම යෙදුම් වලින් ටයිප් කළ හැක."</string>
-    <string name="setup_step3_action" msgid="600879797256942259">"අතිරේක භාෂා වින්‍යාස කරන්න"</string>
-    <string name="setup_finish_action" msgid="276559243409465389">"අවසන්"</string>
-    <string name="show_setup_wizard_icon" msgid="5008028590593710830">"යෙදුම් නිරූපකය පෙන්වන්න"</string>
-    <string name="show_setup_wizard_icon_summary" msgid="4119998322536880213">"දියත්කරනය තුළ යෙදුම් නිරූපකය දර්ශනය කරන්න"</string>
-    <string name="app_name" msgid="6320102637491234792">"ශබ්දකෝෂ සැපයුම්කරු"</string>
-    <string name="dictionary_provider_name" msgid="3027315045397363079">"ශබ්දකෝෂ සැපයුම්කරු"</string>
-    <string name="dictionary_service_name" msgid="6237472350693511448">"ශබ්ද කෝෂ සේවාව"</string>
-    <string name="download_description" msgid="6014835283119198591">"ශබ්ද කෝෂ යාවත්කාලීන තොරතුරු"</string>
-    <string name="dictionary_settings_title" msgid="8091417676045693313">"ඈඳුම් ශබ්ද කෝෂ"</string>
-    <string name="dictionary_install_over_metered_network_prompt" msgid="3587517870006332980">"ශබ්දකෝෂය ලබාගත හැක"</string>
-    <string name="dictionary_settings_summary" msgid="5305694987799824349">"ශබ්ද කෝෂ සඳහා සැකසීම්"</string>
-    <string name="user_dictionaries" msgid="3582332055892252845">"පරිශීලක ශබ්ද කෝෂ"</string>
-    <string name="default_user_dict_pref_name" msgid="1625055720489280530">"පරිශීලක ශබ්ද කෝෂය"</string>
-    <string name="dictionary_available" msgid="4728975345815214218">"ශබ්දකෝෂය ලබාගත හැක"</string>
-    <string name="dictionary_downloading" msgid="2982650524622620983">"දැනට බාගැනේ"</string>
-    <string name="dictionary_installed" msgid="8081558343559342962">"ස්ථාපිතයි"</string>
-    <string name="dictionary_disabled" msgid="8950383219564621762">"ස්ථාපනය කළ, අබල කළ"</string>
-    <string name="cannot_connect_to_dict_service" msgid="9216933695765732398">"ශබ්දකෝෂ සේවාව වෙත සම්බන්ධ වීමට ගැටලුවක්ද"</string>
-    <string name="no_dictionaries_available" msgid="8039920716566132611">"ශබ්ද කෝෂ ලබාගත නොහැක"</string>
-    <string name="check_for_updates_now" msgid="8087688440916388581">"නැවුම් කරන්න"</string>
-    <string name="last_update" msgid="730467549913588780">"අවසන් වරට යාවත්කාලීන කළේ"</string>
-    <string name="message_updating" msgid="4457761393932375219">"යාවත්කාලීන සඳහා පරික්ෂා කෙරේ"</string>
-    <string name="message_loading" msgid="8689096636874758814">"පූරණය වෙමින්..."</string>
-    <string name="main_dict_description" msgid="3072821352793492143">"ප්‍රධාන ශබ්ද කෝෂය"</string>
-    <string name="cancel" msgid="6830980399865683324">"අවලංගු කරන්න"</string>
-    <string name="install_dict" msgid="180852772562189365">"ස්ථාපනය"</string>
-    <string name="cancel_download_dict" msgid="7843340278507019303">"අවලංගු කරන්න"</string>
-    <string name="delete_dict" msgid="756853268088330054">"මකන්න"</string>
-    <string name="should_download_over_metered_prompt" msgid="2878629598667658845">"ඔබගේ ජංගම උපාංගය මත තෝරාගත් භාෂාවට ලබාගත හැකි ශබ්ද කෝෂයක් ඇත.&lt;br/&gt; අප ඔබගේ ටයිප් කිරීමේ පළපුරුද්ද වැඩි දියුණු කිරීමට <xliff:g id="LANGUAGE">%1$s</xliff:g> ශබ්ද කෝෂය &lt;b&gt;බාගැනීම&lt;/b&gt; නිර්දේශ කරමු.&lt;br/&gt; &lt;br/&gt; 3G හරහා බාගැනීම මිනිත්තුවක් හෝ දෙකක් ගත හැකිය. ඔබට &lt;b&gt;සීමාරහිත දත්ත සැලසුමක්&lt;/b&gt; නොමැති නම් ගාස්තු අදාළ විය හැක.&lt;br/&gt; ඔබට තිබෙන්නේ කුමන දත්ත සැලසුමක්ද යන්න පිළිබඳ විශ්වාසයක් නොමැති නම්, බාගැනීම ස්වයංක්‍රියව ආරම්භ කිරීමට Wi-Fi සම්බන්ධයක් සොයා ගැනීම අප නිර්දේශ කරමු.&lt;br/&gt; &lt;br/&gt; ඉඟිය: ඔබට ඔබගේ ජංගම උපාංගයේ &lt;b&gt;සැකසීම්&lt;/b&gt; මෙනුව තුළ &lt;b&gt;භාෂාව සහ ආදානය&lt;/b&gt; වෙත යාම මගින් ශබ්දකෝෂ බාගැනීමට සහ ඉවත් කිරීමට හැක."</string>
-    <string name="download_over_metered" msgid="1643065851159409546">"දැන් බාගන්න (<xliff:g id="SIZE_IN_MEGABYTES">%1$.1f</xliff:g>MB)"</string>
-    <string name="do_not_download_over_metered" msgid="2176209579313941583">"Wi-Fi හරහා බාගන්න"</string>
-    <string name="dict_available_notification_title" msgid="6514288591959117288">"<xliff:g id="LANGUAGE">%1$s</xliff:g> සඳහා ශබ්දකෝෂයක් ලබාගත හැක"</string>
-    <string name="dict_available_notification_description" msgid="1075194169443163487">"සමාලෝචනය කිරීමට සහ බාගැනීමට ඔබන්න"</string>
-    <string name="toast_downloading_suggestions" msgid="1313027353588566660">"බාගැනේ: <xliff:g id="LANGUAGE">%1$s</xliff:g> සඳහා යෝජනා ඉක්මනින් සුදානම් වනු ඇත."</string>
-    <string name="version_text" msgid="2715354215568469385">"<xliff:g id="VERSION_NUMBER">%1$s</xliff:g> අනුවාදය"</string>
-    <string name="user_dict_settings_add_menu_title" msgid="1254195365689387076">"එක් කරන්න"</string>
-    <string name="user_dict_settings_add_dialog_title" msgid="4096700390211748168">"ශබ්ද කෝෂය වෙත එක් කරන්න"</string>
-    <string name="user_dict_settings_add_screen_title" msgid="5818914331629278758">"වාක්‍ය ඛණ්ඩය"</string>
-    <string name="user_dict_settings_add_dialog_more_options" msgid="5671682004887093112">"තවත් විකල්ප"</string>
-    <string name="user_dict_settings_add_dialog_less_options" msgid="2716586567241724126">"අඩු විකල්ප"</string>
-    <string name="user_dict_settings_add_dialog_confirm" msgid="4703129507388332950">"හරි"</string>
-    <string name="user_dict_settings_add_word_option_name" msgid="6665558053408962865">"වචනය:"</string>
-    <string name="user_dict_settings_add_shortcut_option_name" msgid="3094731590655523777">"කෙටිමග:"</string>
-    <string name="user_dict_settings_add_locale_option_name" msgid="4738643440987277705">"භාෂාව:"</string>
-    <string name="user_dict_settings_add_word_hint" msgid="4902434148985906707">"වචනයක් ටයිප් කරන්න"</string>
-    <string name="user_dict_settings_add_shortcut_hint" msgid="2265453012555060178">"විකල්පමය කෙටිමග"</string>
-    <string name="user_dict_settings_edit_dialog_title" msgid="3765774633869590352">"වචනය සංස්කරණය කරන්න"</string>
-    <string name="user_dict_settings_context_menu_edit_title" msgid="6812255903472456302">"සංස්කරණය කරන්න"</string>
-    <string name="user_dict_settings_context_menu_delete_title" msgid="8142932447689461181">"මකන්න"</string>
-    <string name="user_dict_settings_empty_text" msgid="558499587532668203">"ඔබට පරිශීලක ශබ්ද කෝෂය තුළ වචන කිසිවක් නැත. එක් කරන්න (+) බොත්තම ස්පර්ශ කිරීම මගින් වචනයක් එක් කරන්න."</string>
-    <string name="user_dict_settings_all_languages" msgid="8276126583216298886">"සියලු භාෂාවන් සඳහා"</string>
-    <string name="user_dict_settings_more_languages" msgid="7131268499685180461">"තවත් භාෂා…"</string>
-    <string name="user_dict_settings_delete" msgid="110413335187193859">"මකන්න"</string>
-    <string name="user_dict_fast_scroll_alphabet" msgid="5431919401558285473">" ABCDEFGHIJKLMNOPQRSTUVWXYZ"</string>
-</resources>
diff --git a/java/res/values-si/strings.xml b/java/res/values-si/strings.xml
deleted file mode 100644
index 6d2a6f6..0000000
--- a/java/res/values-si/strings.xml
+++ /dev/null
@@ -1,242 +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.
-*/
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="english_ime_input_options" msgid="3909945612939668554">"ආදාන විකල්ප"</string>
-    <string name="english_ime_research_log" msgid="8492602295696577851">"පර්යේෂණ ලොග් විධාන"</string>
-    <string name="use_contacts_for_spellchecking_option_title" msgid="5374120998125353898">"සබඳතා නම් විමසන්න"</string>
-    <string name="use_contacts_for_spellchecking_option_summary" msgid="8754413382543307713">"අක්ෂර වින්‍යාස පරික්ෂකය ඔබගේ සබඳතා ලැයිස්තුව වෙතින් ඇතුළත් කිරීම් භාවිතා කරයි"</string>
-    <string name="vibrate_on_keypress" msgid="5258079494276955460">"යතුර එබීමට කම්පනය කිරීම සක්‍රියයි"</string>
-    <string name="sound_on_keypress" msgid="6093592297198243644">"යතුරු එබිම මත හඬ"</string>
-    <string name="popup_on_keypress" msgid="123894815723512944">"යතුරු එබීම මත උත්පතනය"</string>
-    <string name="general_category" msgid="1859088467017573195">"සාමාන්‍ය"</string>
-    <string name="correction_category" msgid="2236750915056607613">"පෙළ නිවැරදි කිරීම"</string>
-    <string name="gesture_typing_category" msgid="497263612130532630">"ඉංගිතයෙන් ටයිප් කිරීම"</string>
-    <string name="misc_category" msgid="6894192814868233453">"වෙනත් විකල්ප"</string>
-    <string name="advanced_settings" msgid="362895144495591463">"උසස් සැකසීම්"</string>
-    <string name="advanced_settings_summary" msgid="4487980456152830271">"ප්‍රවීනයන් සඳහා විකල්ප"</string>
-    <string name="include_other_imes_in_language_switch_list" msgid="4533689960308565519">"වෙනත් ආදාන ක්‍රම වෙත මාරුවන්න"</string>
-    <string name="include_other_imes_in_language_switch_list_summary" msgid="840637129103317635">"භාෂා මාරු යතුර වෙනත් ආදාන ක්‍රමද ආවරණය කරයි"</string>
-    <string name="show_language_switch_key" msgid="5915478828318774384">"භාෂා මාරු යතුර"</string>
-    <string name="show_language_switch_key_summary" msgid="7343403647474265713">"බහුවිධ ආදාන භාෂා සබල කර ඇති විට පෙන්වන්න"</string>
-    <string name="sliding_key_input_preview" msgid="6604262359510068370">"සර්පණ දර්ශකය පෙන්වන්න"</string>
-    <string name="sliding_key_input_preview_summary" msgid="6340524345729093886">"ෂිෆ්ට් හෝ සංකේත යතුරු වෙතින් සර්පණය කරන අතරතුර දෘෂ්‍ය ඉඟි දර්ශනය කරන්න"</string>
-    <string name="key_preview_popup_dismiss_delay" msgid="6213164897443068248">"යතුරු උත්පතන ඉවත් කිරීමේ ප්‍රමාදය"</string>
-    <string name="key_preview_popup_dismiss_no_delay" msgid="2096123151571458064">"ප්‍රමාද නැත"</string>
-    <string name="key_preview_popup_dismiss_default_delay" msgid="2166964333903906734">"සුපුරුදු"</string>
-    <string name="abbreviation_unit_milliseconds" msgid="8700286094028323363">"<xliff:g id="MILLISECONDS">%s</xliff:g>ms"</string>
-    <string name="settings_system_default" msgid="6268225104743331821">"පද්ධති සුපුරුදු"</string>
-    <string name="use_contacts_dict" msgid="4435317977804180815">"සබඳතා නම් යෝජනා කරන්න"</string>
-    <string name="use_contacts_dict_summary" msgid="6599983334507879959">"යෝජනා සහ නිවැරදි කිරීම් සඳහා සබඳතා වෙතින් නම් භාවිතා කරන්න"</string>
-    <string name="use_double_space_period" msgid="8781529969425082860">"දෙවරක්-ඉඩ නැවතීමේ ලකුණ"</string>
-    <string name="use_double_space_period_summary" msgid="6532892187247952799">"ඉඩ යතුර මත දෙවරක් තට්ටු කිරීම හිස් තැනකට අනුගාමිව නැවතීමේ ලකුණක් ඇතුළත් කරයි."</string>
-    <string name="auto_cap" msgid="1719746674854628252">"ස්වයං-ලොකු අකුරු කරණය"</string>
-    <string name="auto_cap_summary" msgid="7934452761022946874">"එක් එක් වාක්‍යයේ පළමු වචනය ලොකු අකුරු කරන්න"</string>
-    <string name="edit_personal_dictionary" msgid="3996910038952940420">"පුද්ගලික ශබ්ද කෝෂය"</string>
-    <string name="configure_dictionaries_title" msgid="4238652338556902049">"ඈඳුම් ශබ්දකෝෂ"</string>
-    <string name="main_dictionary" msgid="4798763781818361168">"ප්‍රධාන ශබ්ද කෝෂය"</string>
-    <string name="prefs_show_suggestions" msgid="8026799663445531637">"නිවැරදි කිරීම් යෝජනා පෙන්වන්න"</string>
-    <string name="prefs_show_suggestions_summary" msgid="1583132279498502825">"ටයිප් කරන අතරතුර යෝජිත වචන දර්ශනය කරන්න"</string>
-    <string name="prefs_suggestion_visibility_show_name" msgid="3219916594067551303">"සැමවිටම පෙන්වන්න"</string>
-    <string name="prefs_suggestion_visibility_show_only_portrait_name" msgid="3859783767435239118">"සිරස් ආකෘති ආකාරය තුළ පෙන්වන්න"</string>
-    <string name="prefs_suggestion_visibility_hide_name" msgid="6309143926422234673">"සැමවිට සඟවන්න"</string>
-    <string name="prefs_block_potentially_offensive_title" msgid="5078480071057408934">"අප්‍රසන්න වචන අවහිර කරන්න"</string>
-    <string name="prefs_block_potentially_offensive_summary" msgid="2371835479734991364">"විභව්‍යව අප්‍රසන්න වචන යෝජනා නොකරන්න"</string>
-    <string name="auto_correction" msgid="7630720885194996950">"ස්වයං-නිවැරදි කිරීම"</string>
-    <string name="auto_correction_summary" msgid="5625751551134658006">"ඉඩ යතුර සහ විරාම ලකුණ වැරදියට ටයිප් කළ වචන ස්වයංක්‍රියව නිවැරදි කරයි"</string>
-    <string name="auto_correction_threshold_mode_off" msgid="8470882665417944026">"අක්‍රිය කරන්න"</string>
-    <string name="auto_correction_threshold_mode_modest" msgid="8788366690620799097">"මධ්‍යස්ථ"</string>
-    <string name="auto_correction_threshold_mode_aggressive" msgid="7319007299148899623">"ආක්‍රමණකාරී"</string>
-    <string name="auto_correction_threshold_mode_very_aggressive" msgid="1853309024129480416">"ඉතා ආක්‍රමණකාරී"</string>
-    <string name="bigram_prediction" msgid="1084449187723948550">"ඊළඟ-වචනයේ යෝජනා"</string>
-    <string name="bigram_prediction_summary" msgid="3896362682751109677">"යෝජනා කිරීමේදී පෙර වචනය භාවිතා කරන්න"</string>
-    <string name="gesture_input" msgid="826951152254563827">"ඉංගිතයෙන් ටයිප් කිරීම සබල කරන්න"</string>
-    <string name="gesture_input_summary" msgid="9180350639305731231">"අකුරු ඔස්සේ සර්පණය කිරීමෙන් වචනයක් ආදානය කරන්න"</string>
-    <string name="gesture_preview_trail" msgid="3802333369335722221">"ඉංගිතයෙන් මඟ පෙන්වන්න"</string>
-    <string name="gesture_floating_preview_text" msgid="4443240334739381053">"ගතිකව ඉපිලෙන පෙරදසුන"</string>
-    <string name="gesture_floating_preview_text_summary" msgid="4472696213996203533">"ඉංගිතය කරන අතරතුර යෝජිත වචන බලන්න"</string>
-    <string name="added_word" msgid="8993883354622484372">"<xliff:g id="WORD">%s</xliff:g> : සුරැකිණි"</string>
-    <string name="label_go_key" msgid="1635148082137219148">"යන්න"</string>
-    <string name="label_next_key" msgid="362972844525672568">"මීලඟ"</string>
-    <string name="label_previous_key" msgid="1211868118071386787">"පෙර"</string>
-    <string name="label_done_key" msgid="2441578748772529288">"හරි"</string>
-    <string name="label_send_key" msgid="2815056534433717444">"යවන්න"</string>
-    <string name="label_pause_key" msgid="181098308428035340">"විරාම කරන්න"</string>
-    <string name="label_wait_key" msgid="6402152600878093134">"රැඳී සිටින්න"</string>
-    <string name="spoken_use_headphones" msgid="896961781287283493">"හඬ නගා කථනය කරන මුරපද යතුරු ඇසීමට හෙඩ්සෙට් එකක් පේනුගත කරන්න."</string>
-    <string name="spoken_current_text_is" msgid="2485723011272583845">"වර්තමාන පෙළ %s ය"</string>
-    <string name="spoken_no_text_entered" msgid="7479685225597344496">"පෙළ ඇතුළු කර නැත"</string>
-    <string name="spoken_description_unknown" msgid="3197434010402179157">"යතුරු කේතය %d"</string>
-    <string name="spoken_description_shift" msgid="244197883292549308">"ෂිෆ්ට්"</string>
-    <string name="spoken_description_shift_shifted" msgid="1681877323344195035">"ෂිෆ්ට් සක්‍රියයි (අබල කිරීමට තට්ටු කරන්න)"</string>
-    <string name="spoken_description_caps_lock" msgid="3276478269526304432">"කැප්ස් ලොක් සක්‍රියයි (අබල කිරීමට තට්ටු කරන්න)"</string>
-    <string name="spoken_description_delete" msgid="8740376944276199801">"මකන්න"</string>
-    <string name="spoken_description_to_symbol" msgid="5486340107500448969">"සංකේත"</string>
-    <string name="spoken_description_to_alpha" msgid="23129338819771807">"අකුරු"</string>
-    <string name="spoken_description_to_numeric" msgid="591752092685161732">"අංක"</string>
-    <string name="spoken_description_settings" msgid="4627462689603838099">"සැකසීම්"</string>
-    <string name="spoken_description_tab" msgid="2667716002663482248">"ටැබය"</string>
-    <string name="spoken_description_space" msgid="2582521050049860859">"හිඩස"</string>
-    <string name="spoken_description_mic" msgid="615536748882611950">"හඬ ආදානය"</string>
-    <string name="spoken_description_smiley" msgid="2256309826200113918">"සිනහ මුහුණ"</string>
-    <string name="spoken_description_return" msgid="8178083177238315647">"ආපසු එවන්න"</string>
-    <string name="spoken_description_search" msgid="1247236163755920808">"සෙවීම"</string>
-    <string name="spoken_description_dot" msgid="40711082435231673">"තිත"</string>
-    <string name="spoken_description_language_switch" msgid="5507091328222331316">"භාෂාව මාරු කරන්න"</string>
-    <string name="spoken_description_action_next" msgid="8636078276664150324">"මීලඟ"</string>
-    <string name="spoken_description_action_previous" msgid="800872415009336208">"පෙර"</string>
-    <string name="spoken_description_shiftmode_on" msgid="5700440798609574589">"ෂිෆ්ට් සබල කර ඇත"</string>
-    <string name="spoken_description_shiftmode_locked" msgid="593175803181701830">"කැප්ස් ලොක් සබල කර ඇත"</string>
-    <string name="spoken_description_shiftmode_off" msgid="657219998449174808">"ෂිෆ්ට් අබල කර ඇත"</string>
-    <string name="spoken_description_mode_symbol" msgid="7183343879909747642">"සංකේත ප්‍රකාරය"</string>
-    <string name="spoken_description_mode_alpha" msgid="3528307674390156956">"අකුරු ආකාරය"</string>
-    <string name="spoken_description_mode_phone" msgid="6520207943132026264">"දුරකථන ආකාරය"</string>
-    <string name="spoken_description_mode_phone_shift" msgid="5499629753962641227">"දුරකථන සංකේත ආකාරය"</string>
-    <string name="announce_keyboard_hidden" msgid="8718927835531429807">"යතුරු පුවරුව සැඟවී ඇත"</string>
-    <string name="announce_keyboard_mode" msgid="4729081055438508321">"<xliff:g id="MODE">%s</xliff:g> යතුරු පුවරුව පෙන්නුම් කෙරේ"</string>
-    <string name="keyboard_mode_date" msgid="3137520166817128102">"දිනය"</string>
-    <string name="keyboard_mode_date_time" msgid="339593358488851072">"දිනය සහ වේලාව"</string>
-    <string name="keyboard_mode_email" msgid="6216248078128294262">"ඊ-තැපෑල"</string>
-    <string name="keyboard_mode_im" msgid="1137405089766557048">"පණිවිඩ යැවීම"</string>
-    <string name="keyboard_mode_number" msgid="7991623440699957069">"අංකය"</string>
-    <string name="keyboard_mode_phone" msgid="6851627527401433229">"දුරකථනය"</string>
-    <string name="keyboard_mode_text" msgid="6479436687899701619">"පෙළ"</string>
-    <string name="keyboard_mode_time" msgid="4381856885582143277">"කාලය"</string>
-    <string name="keyboard_mode_url" msgid="1519819835514911218">"URL"</string>
-    <string name="voice_input" msgid="3583258583521397548">"හඬ ආදාන යතුර"</string>
-    <string name="voice_input_modes_main_keyboard" msgid="3360660341121083174">"ප්‍රධාන යතුරු පුවරුව මත"</string>
-    <string name="voice_input_modes_symbols_keyboard" msgid="7203213240786084067">"සංකේත යතුරු පුවරුව මත"</string>
-    <string name="voice_input_modes_off" msgid="3745699748218082014">"අක්‍රිය කරන්න"</string>
-    <string name="voice_input_modes_summary_main_keyboard" msgid="6586544292900314339">"ප්‍රධාන යතුරු පුවරුව මත මයික්‍රෆෝනය"</string>
-    <string name="voice_input_modes_summary_symbols_keyboard" msgid="5233725927281932391">"සංකේත යතුරු පුවරුව මත  මයික්‍රෆෝනය"</string>
-    <string name="voice_input_modes_summary_off" msgid="63875609591897607">"හඬ ආදානය අබල කර ඇත"</string>
-    <string name="configure_input_method" msgid="373356270290742459">"ආදාන ක්‍රම වින්‍යාස කරන්න"</string>
-    <string name="language_selection_title" msgid="1651299598555326750">"ආදාන භාෂා"</string>
-    <string name="send_feedback" msgid="1780431884109392046">"ප්‍රතිපෝෂණ යවන්න"</string>
-    <string name="select_language" msgid="3693815588777926848">"ආදාන භාෂා"</string>
-    <string name="hint_add_to_dictionary" msgid="573678656946085380">"සුරැකීමට නැවත ස්පර්ශ කරන්න"</string>
-    <string name="has_dictionary" msgid="6071847973466625007">"ශබ්ද කෝෂය ලබාගත හැක"</string>
-    <string name="prefs_enable_log" msgid="6620424505072963557">"පරිශීලක ප්‍රතිපෝෂණ සබල කරන්න"</string>
-    <string name="prefs_description_log" msgid="7525225584555429211">"භාවිතය පිළිබඳ සංඛ්‍යාලේඛන සහ බිඳ වැටීම් වාර්තා ස්වයංක්‍රියව යැවීම මගින් ආදාන ක්‍රම සංස්කාරක වැඩි දියුණු කිරීමට උදව් වන්න."</string>
-    <string name="keyboard_layout" msgid="8451164783510487501">"යතුරු පුවරු තේමාව"</string>
-    <string name="subtype_en_GB" msgid="88170601942311355">"ඉංග්‍රීසි (බ්‍රිතාන්‍ය)"</string>
-    <string name="subtype_en_US" msgid="6160452336634534239">"ඉංග්‍රීසි (ඇමෙරිකානු)"</string>
-    <string name="subtype_es_US" msgid="5583145191430180200">"ස්පාඤ්ඤ (එජ)"</string>
-    <string name="subtype_with_layout_en_GB" msgid="2179097748724725906">"ඉංග්‍රිසි (එරා) (<xliff:g id="LAYOUT">%s</xliff:g>)"</string>
-    <string name="subtype_with_layout_en_US" msgid="1362581347576714579">"ඉංග්‍රීසි (එජ) (<xliff:g id="LAYOUT">%s</xliff:g>)"</string>
-    <string name="subtype_with_layout_es_US" msgid="6261791057007890189">"ස්පාඤ්ඤ (එජ) (<xliff:g id="LAYOUT">%s</xliff:g>)"</string>
-    <string name="subtype_no_language" msgid="7137390094240139495">"භාෂාවක් නැත (අකාරාදිය)"</string>
-    <string name="subtype_no_language_qwerty" msgid="244337630616742604">"අකාරාදිය (QWERTY)"</string>
-    <string name="subtype_no_language_qwertz" msgid="443066912507547976">"අකාරාදිය (QWERTZ)"</string>
-    <string name="subtype_no_language_azerty" msgid="8144348527575640087">"අකාරාදිය (AZERTY)"</string>
-    <string name="subtype_no_language_dvorak" msgid="1564494667584718094">"අකාරාදිය (Dvorak)"</string>
-    <string name="subtype_no_language_colemak" msgid="5837418400010302623">"අකාරාදිය (Colemak)"</string>
-    <string name="subtype_no_language_pcqwerty" msgid="5354918232046200018">"අකාරාදිය (PC)"</string>
-    <string name="custom_input_styles_title" msgid="8429952441821251512">"අභිරුචි ආදාන විලාස"</string>
-    <string name="add_style" msgid="6163126614514489951">"විලාසය එක් කරන්න"</string>
-    <string name="add" msgid="8299699805688017798">"එක් කරන්න"</string>
-    <string name="remove" msgid="4486081658752944606">"ඉවත් කරන්න"</string>
-    <string name="save" msgid="7646738597196767214">"සුරකින්න"</string>
-    <string name="subtype_locale" msgid="8576443440738143764">"භාෂාව"</string>
-    <string name="keyboard_layout_set" msgid="4309233698194565609">"පිරිසැලසුම"</string>
-    <string name="custom_input_style_note_message" msgid="8826731320846363423">"ඔබ එය භාවිතය ආරම්භ කිරීමට පෙර ඔබගේ අභිරුචි ආදාන විලාසය සබල කිරීමට අවශ්‍යය. ඔබට එය දැන් සබල කිරීමට අවශ්‍යද?"</string>
-    <string name="enable" msgid="5031294444630523247">"සබල කරන්න"</string>
-    <string name="not_now" msgid="6172462888202790482">"දැන් නොවේ"</string>
-    <string name="custom_input_style_already_exists" msgid="8008728952215449707">"සමාන ආදාන විලාසය දැනටමත් පවතී: <xliff:g id="INPUT_STYLE_NAME">%s</xliff:g>"</string>
-    <string name="prefs_usability_study_mode" msgid="1261130555134595254">"උපයෝජ්‍යතා අධ්‍යයන ආකාරය"</string>
-    <string name="prefs_key_longpress_timeout_settings" msgid="6102240298932897873">"යතුරු දිගු එබීම් ප්‍රමාදය"</string>
-    <string name="prefs_keypress_vibration_duration_settings" msgid="7918341459947439226">"යතුරු එබිම් කම්පන කාලපරිච්ඡේදය"</string>
-    <string name="prefs_keypress_sound_volume_settings" msgid="6027007337036891623">"යතුරු එබීම් හඬ තීව්‍රතාවය"</string>
-    <string name="prefs_read_external_dictionary" msgid="2588931418575013067">"බාහිර ශබ්ද කෝෂ ගොනුව කියවන්න"</string>
-    <string name="read_external_dictionary_no_files_message" msgid="4947420942224623792">"බාගැනීම් ෆෝල්ඩරය තුළ ශබ්දකෝෂ ගොනු නොමැත"</string>
-    <string name="read_external_dictionary_multiple_files_title" msgid="7637749044265808628">"ස්ථාපනය කිරීමට ශබ්ද කෝෂ ගොනුවක් තෝරන්න"</string>
-    <string name="read_external_dictionary_confirm_install_message" msgid="6898610163768980870">"<xliff:g id="LOCALE_NAME">%s</xliff:g> සඳහා මෙම ගොනුව ස්ථාපනය කරන්නද?"</string>
-    <string name="error" msgid="8940763624668513648">"දෝෂයක් ඇති විය"</string>
-    <string name="button_default" msgid="3988017840431881491">"සුපුරුදු"</string>
-    <string name="setup_welcome_title" msgid="6112821709832031715">"<xliff:g id="APPLICATION_NAME">%s</xliff:g> වෙත සාදරයෙන් පිළිගනිමු"</string>
-    <string name="setup_welcome_additional_description" msgid="8150252008545768953">"ඉංගිතයෙන් ටයිප් කිරීම් සමග"</string>
-    <string name="setup_start_action" msgid="8936036460897347708">"ආරම්භ කර ගැනීම"</string>
-    <string name="setup_next_action" msgid="371821437915144603">"ඊළඟ පියවර"</string>
-    <string name="setup_steps_title" msgid="6400373034871816182">"<xliff:g id="APPLICATION_NAME">%s</xliff:g> සැකසෙමින් පවතී"</string>
-    <string name="setup_step1_title" msgid="3147967630253462315">"<xliff:g id="APPLICATION_NAME">%s</xliff:g> සබල කරන්න"</string>
-    <string name="setup_step1_instruction" msgid="2578631936624637241">"කරණාකර ඔබගේ භාෂවෙහි සහ ආදාන සැකසීම් වල \"<xliff:g id="APPLICATION_NAME">%s</xliff:g>\" පරික්ෂා කරන්න. මෙය ඔබගේ උපාංගය මත එයට ධාවනය වීමට අනුමැතිය දෙනු ඇත."</string>
-    <string name="setup_step1_finished_instruction" msgid="10761482004957994">"<xliff:g id="APPLICATION_NAME">%s</xliff:g> දැනටමත් ඔබගේ භාෂාවෙන් සහ ආදාන සැකසීම්වල සබල කර ඇත, එමනිසා මෙම පියවර නිමයි. ඊළග එක වෙතට!"</string>
-    <string name="setup_step1_action" msgid="4366513534999901728">"සැකසීම් තුළ සබල කරන්න"</string>
-    <string name="setup_step2_title" msgid="6860725447906690594">"<xliff:g id="APPLICATION_NAME">%s</xliff:g> වෙත මාරුවන්න"</string>
-    <string name="setup_step2_instruction" msgid="9141481964870023336">"ඊළඟට, ඔබගේ සක්‍රිය පෙළ-ආදාන ක්‍රමය ලෙස \"<xliff:g id="APPLICATION_NAME">%s</xliff:g>\" තෝරන්න."</string>
-    <string name="setup_step2_action" msgid="1660330307159824337">"ආදාන ක්‍රම මාරු කරන්න"</string>
-    <string name="setup_step3_title" msgid="3154757183631490281">"සුබපැතුම්, ඔබ සියල්ල පිහිටුවා ඇත!"</string>
-    <string name="setup_step3_instruction" msgid="8025981829605426000">"දැන් ඔබට <xliff:g id="APPLICATION_NAME">%s</xliff:g> සමගින් ඔබගේ  සියළුම ප්‍රියතම යෙදුම් වලින් ටයිප් කළ හැක."</string>
-    <string name="setup_step3_action" msgid="600879797256942259">"අතිරේක භාෂා වින්‍යාස කරන්න"</string>
-    <string name="setup_finish_action" msgid="276559243409465389">"අවසන්ය"</string>
-    <string name="show_setup_wizard_icon" msgid="5008028590593710830">"යෙදුම් අයිකනය පෙන්වන්න"</string>
-    <string name="show_setup_wizard_icon_summary" msgid="4119998322536880213">"දියත්කරනය තුළ යෙදුම් අයිකනය දර්ශනය කරන්න"</string>
-    <string name="app_name" msgid="6320102637491234792">"ශබ්දකෝෂ සැපයුම්කරු"</string>
-    <string name="dictionary_provider_name" msgid="3027315045397363079">"ශබ්දකෝෂ සැපයුම්කරු"</string>
-    <string name="dictionary_service_name" msgid="6237472350693511448">"ශබ්ද කෝෂ සේවාව"</string>
-    <string name="download_description" msgid="6014835283119198591">"ශබ්ද කෝෂ යාවත්කාලීන තොරතුරු"</string>
-    <string name="dictionary_settings_title" msgid="8091417676045693313">"ඈඳුම් ශබ්ද කෝෂ"</string>
-    <string name="dictionary_install_over_metered_network_prompt" msgid="3587517870006332980">"ශබ්දකෝෂය ලබාගත හැක"</string>
-    <string name="dictionary_settings_summary" msgid="5305694987799824349">"ශබ්ද කෝෂ සඳහා සැකසීම්"</string>
-    <string name="user_dictionaries" msgid="3582332055892252845">"පරිශීලක ශබ්ද කෝෂ"</string>
-    <string name="default_user_dict_pref_name" msgid="1625055720489280530">"පරිශීලක ශබ්ද කෝෂය"</string>
-    <string name="dictionary_available" msgid="4728975345815214218">"ශබ්දකෝෂය ලබාගත හැක"</string>
-    <string name="dictionary_downloading" msgid="2982650524622620983">"දැනට බාගැනේ"</string>
-    <string name="dictionary_installed" msgid="8081558343559342962">"පිහිටුවා ඇත"</string>
-    <string name="dictionary_disabled" msgid="8950383219564621762">"ස්ථාපනය කළ, අබල කළ"</string>
-    <string name="cannot_connect_to_dict_service" msgid="9216933695765732398">"ශබ්දකෝෂ සේවාව වෙත සම්බන්ධ වීමට ගැටලුවක්ද"</string>
-    <string name="no_dictionaries_available" msgid="8039920716566132611">"ශබ්ද කෝෂ ලබාගත නොහැක"</string>
-    <string name="check_for_updates_now" msgid="8087688440916388581">"නැවුම් කරන්න"</string>
-    <string name="last_update" msgid="730467549913588780">"අවසන් වරට යාවත්කාලීන කළේ"</string>
-    <string name="message_updating" msgid="4457761393932375219">"යාවත්කාලීන සඳහා පරික්ෂා කෙරේ"</string>
-    <string name="message_loading" msgid="8689096636874758814">"පූරණය වෙමින්..."</string>
-    <string name="main_dict_description" msgid="3072821352793492143">"ප්‍රධාන ශබ්ද කෝෂය"</string>
-    <string name="cancel" msgid="6830980399865683324">"අවලංගු කරන්න"</string>
-    <string name="install_dict" msgid="180852772562189365">"ස්ථාපනය"</string>
-    <string name="cancel_download_dict" msgid="7843340278507019303">"අවලංගු කරන්න"</string>
-    <string name="delete_dict" msgid="756853268088330054">"මකන්න"</string>
-    <string name="should_download_over_metered_prompt" msgid="2878629598667658845">"ඔබගේ ජංගම උපාංගය මත තෝරාගත් භාෂාවට ලබාගත හැකි ශබ්ද කෝෂයක් ඇත.&lt;br/&gt; අප ඔබගේ ටයිප් කිරීමේ පළපුරුද්ද වැඩි දියුණු කිරීමට <xliff:g id="LANGUAGE">%1$s</xliff:g> ශබ්ද කෝෂය &lt;b&gt;බාගැනීම&lt;/b&gt; නිර්දේශ කරමු.&lt;br/&gt; &lt;br/&gt; 3G හරහා බාගැනීම මිනිත්තුවක් හෝ දෙකක් ගත හැකිය. ඔබට &lt;b&gt;සීමාරහිත දත්ත සැලසුමක්&lt;/b&gt; නොමැති නම් ගාස්තු අදාළ විය හැක.&lt;br/&gt; ඔබට තිබෙන්නේ කුමන දත්ත සැලසුමක්ද යන්න පිළිබඳ විශ්වාසයක් නොමැති නම්, බාගැනීම ස්වයංක්‍රියව ආරම්භ කිරීමට Wi-Fi සම්බන්ධයක් සොයා ගැනීම අප නිර්දේශ කරමු.&lt;br/&gt; &lt;br/&gt; ඉඟිය: ඔබට ඔබගේ ජංගම උපාංගයේ &lt;b&gt;සැකසීම්&lt;/b&gt; මෙනුව තුළ &lt;b&gt;භාෂාව සහ ආදානය&lt;/b&gt; වෙත යාම මගින් ශබ්දකෝෂ බාගැනීමට සහ ඉවත් කිරීමට හැක."</string>
-    <string name="download_over_metered" msgid="1643065851159409546">"දැන් බාගන්න (<xliff:g id="SIZE_IN_MEGABYTES">%1$.1f</xliff:g>MB)"</string>
-    <string name="do_not_download_over_metered" msgid="2176209579313941583">"Wi-Fi හරහා බාගන්න"</string>
-    <string name="dict_available_notification_title" msgid="6514288591959117288">"<xliff:g id="LANGUAGE">%1$s</xliff:g> සඳහා ශබ්දකෝෂයක් ලබාගත හැක"</string>
-    <string name="dict_available_notification_description" msgid="1075194169443163487">"සමාලෝචනය කිරීමට සහ බාගැනීමට ඔබන්න"</string>
-    <string name="toast_downloading_suggestions" msgid="1313027353588566660">"බාගැනේ: <xliff:g id="LANGUAGE">%1$s</xliff:g> සඳහා යෝජනා ඉක්මනින් සුදානම් වනු ඇත."</string>
-    <string name="version_text" msgid="2715354215568469385">"අනුවාදය <xliff:g id="VERSION_NUMBER">%1$s</xliff:g>"</string>
-    <string name="user_dict_settings_add_menu_title" msgid="1254195365689387076">"එක් කරන්න"</string>
-    <string name="user_dict_settings_add_dialog_title" msgid="4096700390211748168">"ශබ්ද කෝෂය වෙත එක් කරන්න"</string>
-    <string name="user_dict_settings_add_screen_title" msgid="5818914331629278758">"වාක්‍ය ඛණ්ඩය"</string>
-    <string name="user_dict_settings_add_dialog_more_options" msgid="5671682004887093112">"තවත් විකල්ප"</string>
-    <string name="user_dict_settings_add_dialog_less_options" msgid="2716586567241724126">"අඩු විකල්ප"</string>
-    <string name="user_dict_settings_add_dialog_confirm" msgid="4703129507388332950">"හරි"</string>
-    <string name="user_dict_settings_add_word_option_name" msgid="6665558053408962865">"වචනය:"</string>
-    <string name="user_dict_settings_add_shortcut_option_name" msgid="3094731590655523777">"කෙටිමග:"</string>
-    <string name="user_dict_settings_add_locale_option_name" msgid="4738643440987277705">"භාෂාව:"</string>
-    <string name="user_dict_settings_add_word_hint" msgid="4902434148985906707">"වචනයක් ටයිප් කරන්න"</string>
-    <string name="user_dict_settings_add_shortcut_hint" msgid="2265453012555060178">"විකල්පමය කෙටිමග"</string>
-    <string name="user_dict_settings_edit_dialog_title" msgid="3765774633869590352">"වචනය සංස්කරණය කරන්න"</string>
-    <string name="user_dict_settings_context_menu_edit_title" msgid="6812255903472456302">"සංස්කරණය කරන්න"</string>
-    <string name="user_dict_settings_context_menu_delete_title" msgid="8142932447689461181">"මකන්න"</string>
-    <string name="user_dict_settings_empty_text" msgid="558499587532668203">"ඔබට පරිශීලක ශබ්ද කෝෂය තුළ වචන කිසිවක් නැත. එක් කරන්න (+) බොත්තම ස්පර්ශ කිරීම මගින් වචනයක් එක් කරන්න."</string>
-    <string name="user_dict_settings_all_languages" msgid="8276126583216298886">"සියලු භාෂාවන් සඳහා"</string>
-    <string name="user_dict_settings_more_languages" msgid="7131268499685180461">"තවත් භාෂා…"</string>
-    <string name="user_dict_settings_delete" msgid="110413335187193859">"මකන්න"</string>
-    <string name="user_dict_fast_scroll_alphabet" msgid="5431919401558285473">" ABCDEFGHIJKLMNOPQRSTUVWXYZ"</string>
-</resources>
diff --git a/java/res/values-sk/strings.xml b/java/res/values-sk/strings.xml
index c4e5a06..85d64c6 100644
--- a/java/res/values-sk/strings.xml
+++ b/java/res/values-sk/strings.xml
@@ -143,6 +143,7 @@
     <string name="subtype_with_layout_en_GB" msgid="2179097748724725906">"angličtina (UK) (<xliff:g id="LAYOUT">%s</xliff:g>)"</string>
     <string name="subtype_with_layout_en_US" msgid="1362581347576714579">"angličtina (USA) (<xliff:g id="LAYOUT">%s</xliff:g>)"</string>
     <string name="subtype_with_layout_es_US" msgid="6261791057007890189">"španielčina (USA) (<xliff:g id="LAYOUT">%s</xliff:g>)"</string>
+    <string name="subtype_nepali_traditional" msgid="9032247506728040447">"<xliff:g id="LANGUAGE">%s</xliff:g> (tradičná)"</string>
     <string name="subtype_no_language" msgid="7137390094240139495">"Žiadny jazyk (latinka)"</string>
     <string name="subtype_no_language_qwerty" msgid="244337630616742604">"Latinka (QWERTY)"</string>
     <string name="subtype_no_language_qwertz" msgid="443066912507547976">"Latinka (QWERTZ)"</string>
@@ -150,6 +151,7 @@
     <string name="subtype_no_language_dvorak" msgid="1564494667584718094">"Latinka (Dvorak)"</string>
     <string name="subtype_no_language_colemak" msgid="5837418400010302623">"Latinka (Colemak)"</string>
     <string name="subtype_no_language_pcqwerty" msgid="5354918232046200018">"Latinka (PC)"</string>
+    <string name="subtype_emoji" msgid="7483586578074549196">"Emoji"</string>
     <string name="custom_input_styles_title" msgid="8429952441821251512">"Vlastné štýly vstupu"</string>
     <string name="add_style" msgid="6163126614514489951">"Pridať štýl"</string>
     <string name="add" msgid="8299699805688017798">"Pridať"</string>
diff --git a/java/res/values-sl/strings.xml b/java/res/values-sl/strings.xml
index c6e7c24..7648d73 100644
--- a/java/res/values-sl/strings.xml
+++ b/java/res/values-sl/strings.xml
@@ -143,6 +143,7 @@
     <string name="subtype_with_layout_en_GB" msgid="2179097748724725906">"Angleška (Zdr. kralj.) (<xliff:g id="LAYOUT">%s</xliff:g>)"</string>
     <string name="subtype_with_layout_en_US" msgid="1362581347576714579">"Angleška (ZDA) (<xliff:g id="LAYOUT">%s</xliff:g>)"</string>
     <string name="subtype_with_layout_es_US" msgid="6261791057007890189">"španščina (ZDA) (<xliff:g id="LAYOUT">%s</xliff:g>)"</string>
+    <string name="subtype_nepali_traditional" msgid="9032247506728040447">"<xliff:g id="LANGUAGE">%s</xliff:g> (tradicionalna)"</string>
     <string name="subtype_no_language" msgid="7137390094240139495">"Brez jezika (latinice)"</string>
     <string name="subtype_no_language_qwerty" msgid="244337630616742604">"Latinica (QWERTY)"</string>
     <string name="subtype_no_language_qwertz" msgid="443066912507547976">"Latinica (QWERTZ)"</string>
@@ -150,6 +151,7 @@
     <string name="subtype_no_language_dvorak" msgid="1564494667584718094">"Latinica (Dvorak)"</string>
     <string name="subtype_no_language_colemak" msgid="5837418400010302623">"Latinica (Colemak)"</string>
     <string name="subtype_no_language_pcqwerty" msgid="5354918232046200018">"Latinica (PC)"</string>
+    <string name="subtype_emoji" msgid="7483586578074549196">"Znaki »emoji«"</string>
     <string name="custom_input_styles_title" msgid="8429952441821251512">"Slogi vnosa po meri"</string>
     <string name="add_style" msgid="6163126614514489951">"Dodaj slog"</string>
     <string name="add" msgid="8299699805688017798">"Dodaj"</string>
diff --git a/java/res/values-sr/strings.xml b/java/res/values-sr/strings.xml
index cb3e32b..db06bce 100644
--- a/java/res/values-sr/strings.xml
+++ b/java/res/values-sr/strings.xml
@@ -143,6 +143,7 @@
     <string name="subtype_with_layout_en_GB" msgid="2179097748724725906">"енглески (УК) (<xliff:g id="LAYOUT">%s</xliff:g>)"</string>
     <string name="subtype_with_layout_en_US" msgid="1362581347576714579">"енглески (САД) (<xliff:g id="LAYOUT">%s</xliff:g>)"</string>
     <string name="subtype_with_layout_es_US" msgid="6261791057007890189">"шпански (САД) (<xliff:g id="LAYOUT">%s</xliff:g>)"</string>
+    <string name="subtype_nepali_traditional" msgid="9032247506728040447">"<xliff:g id="LANGUAGE">%s</xliff:g> (традиционални)"</string>
     <string name="subtype_no_language" msgid="7137390094240139495">"Нема језика (абецеда)"</string>
     <string name="subtype_no_language_qwerty" msgid="244337630616742604">"Абецеда (QWERTY)"</string>
     <string name="subtype_no_language_qwertz" msgid="443066912507547976">"Абецеда (QWERTZ)"</string>
@@ -150,6 +151,7 @@
     <string name="subtype_no_language_dvorak" msgid="1564494667584718094">"Абецеда (Dvorak)"</string>
     <string name="subtype_no_language_colemak" msgid="5837418400010302623">"Абецеда (Colemak)"</string>
     <string name="subtype_no_language_pcqwerty" msgid="5354918232046200018">"Абецеда (PC)"</string>
+    <string name="subtype_emoji" msgid="7483586578074549196">"Емотикони"</string>
     <string name="custom_input_styles_title" msgid="8429952441821251512">"Прилаг. стилови уноса"</string>
     <string name="add_style" msgid="6163126614514489951">"Додав. стила"</string>
     <string name="add" msgid="8299699805688017798">"Додај"</string>
diff --git a/java/res/values-sv/strings.xml b/java/res/values-sv/strings.xml
index 5b34b2f..c663516 100644
--- a/java/res/values-sv/strings.xml
+++ b/java/res/values-sv/strings.xml
@@ -143,6 +143,7 @@
     <string name="subtype_with_layout_en_GB" msgid="2179097748724725906">"Engelskt (brittiskt) (<xliff:g id="LAYOUT">%s</xliff:g>)"</string>
     <string name="subtype_with_layout_en_US" msgid="1362581347576714579">"Engelskt (amerikanskt) (<xliff:g id="LAYOUT">%s</xliff:g>)"</string>
     <string name="subtype_with_layout_es_US" msgid="6261791057007890189">"spanska (USA (<xliff:g id="LAYOUT">%s</xliff:g>)"</string>
+    <string name="subtype_nepali_traditional" msgid="9032247506728040447">"<xliff:g id="LANGUAGE">%s</xliff:g> (traditionell)"</string>
     <string name="subtype_no_language" msgid="7137390094240139495">"Inget språk (alfabet)"</string>
     <string name="subtype_no_language_qwerty" msgid="244337630616742604">"Alfabet (QWERTY)"</string>
     <string name="subtype_no_language_qwertz" msgid="443066912507547976">"Alfabet (QWERTZ)"</string>
@@ -150,6 +151,7 @@
     <string name="subtype_no_language_dvorak" msgid="1564494667584718094">"Alfabet (Dvorak)"</string>
     <string name="subtype_no_language_colemak" msgid="5837418400010302623">"Alfabet (Colemak)"</string>
     <string name="subtype_no_language_pcqwerty" msgid="5354918232046200018">"Alfabet (PC)"</string>
+    <string name="subtype_emoji" msgid="7483586578074549196">"Humörsymbol"</string>
     <string name="custom_input_styles_title" msgid="8429952441821251512">"Anpassade indatastilar"</string>
     <string name="add_style" msgid="6163126614514489951">"Ny stil"</string>
     <string name="add" msgid="8299699805688017798">"Lägg till"</string>
diff --git a/java/res/values-sw/strings.xml b/java/res/values-sw/strings.xml
index 94759b6..188bbca 100644
--- a/java/res/values-sw/strings.xml
+++ b/java/res/values-sw/strings.xml
@@ -143,6 +143,7 @@
     <string name="subtype_with_layout_en_GB" msgid="2179097748724725906">"Kiingereza (Uingereza) (<xliff:g id="LAYOUT">%s</xliff:g>)"</string>
     <string name="subtype_with_layout_en_US" msgid="1362581347576714579">"Kiingereza (Marekani) (<xliff:g id="LAYOUT">%s</xliff:g>)"</string>
     <string name="subtype_with_layout_es_US" msgid="6261791057007890189">"Kihispania (Marekani) (<xliff:g id="LAYOUT">%s</xliff:g>)"</string>
+    <string name="subtype_nepali_traditional" msgid="9032247506728040447">"<xliff:g id="LANGUAGE">%s</xliff:g> (Asili)"</string>
     <string name="subtype_no_language" msgid="7137390094240139495">"Hakuna lugha (Alfabeti)"</string>
     <string name="subtype_no_language_qwerty" msgid="244337630616742604">"Alfabeti (QWERTY)"</string>
     <string name="subtype_no_language_qwertz" msgid="443066912507547976">"Alfabeti (QWERTZ)"</string>
@@ -150,6 +151,7 @@
     <string name="subtype_no_language_dvorak" msgid="1564494667584718094">"Alfabeti (Dvorak)"</string>
     <string name="subtype_no_language_colemak" msgid="5837418400010302623">"Alfabeti (Colemak)"</string>
     <string name="subtype_no_language_pcqwerty" msgid="5354918232046200018">"Alfabeti (PC)"</string>
+    <string name="subtype_emoji" msgid="7483586578074549196">"Emoji"</string>
     <string name="custom_input_styles_title" msgid="8429952441821251512">"Mitindo maalum ya ingizo"</string>
     <string name="add_style" msgid="6163126614514489951">"Ongeza mtindo"</string>
     <string name="add" msgid="8299699805688017798">"Ongeza"</string>
diff --git a/java/res/values-sw600dp-land/dimens.xml b/java/res/values-sw600dp-land/dimens.xml
index 51c710f..730b7d8 100644
--- a/java/res/values-sw600dp-land/dimens.xml
+++ b/java/res/values-sw600dp-land/dimens.xml
@@ -24,20 +24,15 @@
     <dimen name="keyboardHeight">283.5dp</dimen>
     <fraction name="minKeyboardHeight">45%p</fraction>
 
-    <fraction name="keyboard_top_padding">2.444%p</fraction>
-    <fraction name="keyboard_bottom_padding">0.0%p</fraction>
-    <fraction name="key_bottom_gap">4.911%p</fraction>
-    <fraction name="key_horizontal_gap">1.284%p</fraction>
-
-    <fraction name="key_bottom_gap_stone">4.355%p</fraction>
-    <fraction name="key_horizontal_gap_stone">1.505%p</fraction>
-
+    <fraction name="keyboard_top_padding_gb">2.444%p</fraction>
+    <fraction name="keyboard_bottom_padding_gb">0.0%p</fraction>
     <fraction name="key_bottom_gap_gb">5.200%p</fraction>
     <fraction name="key_horizontal_gap_gb">1.447%p</fraction>
 
+    <fraction name="keyboard_top_padding_ics">2.727%p</fraction>
+    <fraction name="keyboard_bottom_padding_ics">0.0%p</fraction>
     <fraction name="key_bottom_gap_ics">4.5%p</fraction>
     <fraction name="key_horizontal_gap_ics">0.9%p</fraction>
-    <fraction name="keyboard_bottom_padding_ics">0.0%p</fraction>
 
     <dimen name="popup_key_height">81.9dp</dimen>
 
@@ -67,4 +62,11 @@
     <dimen name="gesture_floating_preview_text_offset">76dp</dimen>
     <dimen name="gesture_floating_preview_horizontal_padding">26dp</dimen>
     <dimen name="gesture_floating_preview_vertical_padding">17dp</dimen>
+
+    <!-- Emoji keyboard -->
+    <fraction name="emoji_keyboard_key_width">10%p</fraction>
+    <fraction name="emoji_keyboard_row_height">33%p</fraction>
+    <fraction name="emoji_keyboard_key_letter_size">95%p</fraction>
+    <integer name="emoji_keyboard_max_key_count">30</integer>
+
 </resources>
diff --git a/java/res/values-sw600dp/config.xml b/java/res/values-sw600dp/config.xml
index 8265651..93862a7 100644
--- a/java/res/values-sw600dp/config.xml
+++ b/java/res/values-sw600dp/config.xml
@@ -28,8 +28,6 @@
     <bool name="config_auto_correction_spacebar_led_enabled">false</bool>
     <!-- The language is never displayed if == 0, always displayed if < 0 -->
     <integer name="config_delay_before_fadeout_language_on_spacebar">1200</integer>
-    <!-- This configuration is the index of the array {@link KeyboardSwitcher.KEYBOARD_THEMES}. -->
-    <string name="config_default_keyboard_theme_index" translatable="false">5</string>
     <integer name="config_max_more_keys_column">5</integer>
     <!--
         Configuration for MainKeyboardView
diff --git a/java/res/values-sw600dp/dimens.xml b/java/res/values-sw600dp/dimens.xml
index 75b476c..2bcf2fa 100644
--- a/java/res/values-sw600dp/dimens.xml
+++ b/java/res/values-sw600dp/dimens.xml
@@ -27,27 +27,22 @@
 
     <dimen name="popup_key_height">63.0dp</dimen>
 
-    <fraction name="keyboard_top_padding">2.291%p</fraction>
-    <fraction name="keyboard_bottom_padding">0.0%p</fraction>
-    <fraction name="key_bottom_gap">3.750%p</fraction>
-    <fraction name="key_horizontal_gap">1.857%p</fraction>
-
-    <fraction name="key_bottom_gap_stone">3.75%p</fraction>
-    <fraction name="key_horizontal_gap_stone">1.602%p</fraction>
-
+    <fraction name="keyboard_top_padding_gb">2.291%p</fraction>
+    <fraction name="keyboard_bottom_padding_gb">0.0%p</fraction>
     <fraction name="key_bottom_gap_gb">4.625%p</fraction>
     <fraction name="key_horizontal_gap_gb">2.113%p</fraction>
 
+    <fraction name="keyboard_top_padding_ics">2.335%p</fraction>
+    <fraction name="keyboard_bottom_padding_ics">4.0%p</fraction>
     <fraction name="key_bottom_gap_ics">4.5%p</fraction>
     <fraction name="key_horizontal_gap_ics">1.565%p</fraction>
-    <fraction name="keyboard_bottom_padding_ics">4.0%p</fraction>
 
     <dimen name="more_keys_keyboard_key_horizontal_padding">6dp</dimen>
     <!-- Amount of allowance for selecting keys in a mini popup keyboard by sliding finger. -->
     <!-- popup_key_height x 1.2 -->
     <dimen name="more_keys_keyboard_slide_allowance">98.3dp</dimen>
     <!-- popup_key_height x -1.0 -->
-    <dimen name="more_keys_keyboard_vertical_correction">-81.9dp</dimen>
+    <dimen name="more_keys_keyboard_vertical_correction_gb">-81.9dp</dimen>
 
     <!-- left or right padding of label alignment -->
     <dimen name="key_label_horizontal_padding">6dp</dimen>
@@ -64,7 +59,7 @@
     <fraction name="key_preview_text_ratio">50%</fraction>
     <fraction name="spacebar_text_ratio">28.0%</fraction>
     <dimen name="key_preview_height">94.5dp</dimen>
-    <dimen name="key_preview_offset">16.0dp</dimen>
+    <dimen name="key_preview_offset_gb">16.0dp</dimen>
 
     <!-- For 5-row keyboard -->
     <fraction name="key_bottom_gap_5row">3.20%p</fraction>
@@ -93,4 +88,11 @@
     <dimen name="gesture_floating_preview_horizontal_padding">28dp</dimen>
     <dimen name="gesture_floating_preview_vertical_padding">19dp</dimen>
     <dimen name="gesture_floating_preview_round_radius">3dp</dimen>
+
+    <!-- Emoji keyboard -->
+    <fraction name="emoji_keyboard_key_width">12.5%p</fraction>
+    <fraction name="emoji_keyboard_row_height">33%p</fraction>
+    <fraction name="emoji_keyboard_key_letter_size">85%p</fraction>
+    <integer name="emoji_keyboard_max_key_count">24</integer>
+
 </resources>
diff --git a/java/res/values-sw600dp/touch-position-correction.xml b/java/res/values-sw600dp/touch-position-correction.xml
index f77d3ae..df07c12 100644
--- a/java/res/values-sw600dp/touch-position-correction.xml
+++ b/java/res/values-sw600dp/touch-position-correction.xml
@@ -37,7 +37,7 @@
     </string-array>
 
     <string-array
-        name="touch_position_correction_data_gingerbread"
+        name="touch_position_correction_data_gb"
         translatable="false"
     >
         <!-- The default touch position data (See com.android.inputmethod.keyboard.ProximityInfo)
@@ -48,7 +48,7 @@
     </string-array>
 
     <string-array
-        name="touch_position_correction_data_ice_cream_sandwich"
+        name="touch_position_correction_data_ics"
         translatable="false"
     >
         <!-- The default touch position data (See com.android.inputmethod.keyboard.ProximityInfo)
diff --git a/java/res/values-sw768dp-land/dimens.xml b/java/res/values-sw768dp-land/dimens.xml
index f4a57ff..1e2e1c6 100644
--- a/java/res/values-sw768dp-land/dimens.xml
+++ b/java/res/values-sw768dp-land/dimens.xml
@@ -24,15 +24,8 @@
     <dimen name="keyboardHeight">365.4dp</dimen>
     <fraction name="minKeyboardHeight">45%p</fraction>
 
-    <fraction name="keyboard_top_padding">1.896%p</fraction>
-    <fraction name="keyboard_bottom_padding">0.0%p</fraction>
-
-    <fraction name="key_bottom_gap">4.103%p</fraction>
-    <fraction name="key_horizontal_gap">1.034%p</fraction>
-
-    <fraction name="key_bottom_gap_stone">3.379%p</fraction>
-    <fraction name="key_horizontal_gap_stone">1.062%p</fraction>
-
+    <fraction name="keyboard_top_padding_gb">1.896%p</fraction>
+    <fraction name="keyboard_bottom_padding_gb">0.0%p</fraction>
     <fraction name="key_bottom_gap_gb">3.896%p</fraction>
     <fraction name="key_horizontal_gap_gb">1.195%p</fraction>
 
@@ -70,4 +63,11 @@
     <dimen name="gesture_floating_preview_text_offset">100dp</dimen>
     <dimen name="gesture_floating_preview_horizontal_padding">32dp</dimen>
     <dimen name="gesture_floating_preview_vertical_padding">21dp</dimen>
+
+    <!-- Emoji keyboard -->
+    <fraction name="emoji_keyboard_key_width">7.69%p</fraction>
+    <fraction name="emoji_keyboard_row_height">33%p</fraction>
+    <fraction name="emoji_keyboard_key_letter_size">75%p</fraction>
+    <integer name="emoji_keyboard_max_key_count">39</integer>
+
 </resources>
diff --git a/java/res/values-sw768dp/config.xml b/java/res/values-sw768dp/config.xml
index 97f11cb..e1c07d6 100644
--- a/java/res/values-sw768dp/config.xml
+++ b/java/res/values-sw768dp/config.xml
@@ -26,8 +26,6 @@
     <bool name="config_default_key_preview_popup">false</bool>
     <bool name="config_default_sound_enabled">true</bool>
     <bool name="config_auto_correction_spacebar_led_enabled">false</bool>
-    <!-- This configuration is the index of the array {@link KeyboardSwitcher.KEYBOARD_THEMES}. -->
-    <string name="config_default_keyboard_theme_index" translatable="false">5</string>
     <integer name="config_max_more_keys_column">5</integer>
     <!--
         Configuration for MainKeyboardView
diff --git a/java/res/values-sw768dp/dimens.xml b/java/res/values-sw768dp/dimens.xml
index 91251f5..f62a536 100644
--- a/java/res/values-sw768dp/dimens.xml
+++ b/java/res/values-sw768dp/dimens.xml
@@ -25,18 +25,12 @@
     <fraction name="maxKeyboardHeight">46%p</fraction>
     <fraction name="minKeyboardHeight">-35.0%p</fraction>
 
-    <fraction name="keyboard_top_padding">2.291%p</fraction>
-    <fraction name="keyboard_bottom_padding">0.0%p</fraction>
-
-    <fraction name="key_bottom_gap">4.270%p</fraction>
-    <fraction name="key_horizontal_gap">1.551%p</fraction>
-
-    <fraction name="key_bottom_gap_stone">3.75%p</fraction>
-    <fraction name="key_horizontal_gap_stone">1.059%p</fraction>
-
+    <fraction name="keyboard_top_padding_gb">2.291%p</fraction>
+    <fraction name="keyboard_bottom_padding_gb">0.0%p</fraction>
     <fraction name="key_bottom_gap_gb">4.687%p</fraction>
     <fraction name="key_horizontal_gap_gb">1.272%p</fraction>
 
+    <fraction name="keyboard_top_padding_ics">2.335%p</fraction>
     <fraction name="keyboard_bottom_padding_ics">0.0%p</fraction>
     <fraction name="key_bottom_gap_ics">3.312%p</fraction>
     <fraction name="key_horizontal_gap_ics">1.066%p</fraction>
@@ -48,7 +42,7 @@
     <!-- popup_key_height x 1.2 -->
     <dimen name="more_keys_keyboard_slide_allowance">98.3dp</dimen>
     <!-- popup_key_height x -1.0 -->
-    <dimen name="more_keys_keyboard_vertical_correction">-81.9dp</dimen>
+    <dimen name="more_keys_keyboard_vertical_correction_gb">-81.9dp</dimen>
 
     <!-- left or right padding of label alignment -->
     <dimen name="key_label_horizontal_padding">6dp</dimen>
@@ -65,7 +59,7 @@
     <fraction name="key_preview_text_ratio">50%</fraction>
     <fraction name="spacebar_text_ratio">29.03%</fraction>
     <dimen name="key_preview_height">94.5dp</dimen>
-    <dimen name="key_preview_offset">16.0dp</dimen>
+    <dimen name="key_preview_offset_gb">16.0dp</dimen>
 
     <!-- For 5-row keyboard -->
     <fraction name="key_bottom_gap_5row">2.95%p</fraction>
@@ -94,4 +88,11 @@
     <dimen name="gesture_floating_preview_horizontal_padding">26dp</dimen>
     <dimen name="gesture_floating_preview_vertical_padding">17dp</dimen>
     <dimen name="gesture_floating_preview_round_radius">3dp</dimen>
+
+    <!-- Emoji keyboard -->
+    <fraction name="emoji_keyboard_key_width">10%p</fraction>
+    <fraction name="emoji_keyboard_row_height">33%p</fraction>
+    <fraction name="emoji_keyboard_key_letter_size">85%p</fraction>
+    <integer name="emoji_keyboard_max_key_count">30</integer>
+
 </resources>
diff --git a/java/res/xml-sw768dp/kbd_more_keys_keyboard_template.xml b/java/res/values-th/donottranslate.xml
similarity index 68%
copy from java/res/xml-sw768dp/kbd_more_keys_keyboard_template.xml
copy to java/res/values-th/donottranslate.xml
index f89a0a6..a9893fe 100644
--- a/java/res/xml-sw768dp/kbd_more_keys_keyboard_template.xml
+++ b/java/res/values-th/donottranslate.xml
@@ -2,7 +2,7 @@
 <!--
 /*
 **
-** Copyright 2008, The Android Open Source Project
+** Copyright 2013, 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.
@@ -17,10 +17,7 @@
 ** limitations under the License.
 */
 -->
-
-<Keyboard xmlns:latin="http://schemas.android.com/apk/res/com.android.inputmethod.latin"
-    latin:keyWidth="5.0%p"
-    latin:rowHeight="@dimen/popup_key_height"
-    style="?attr/moreKeysKeyboardStyle"
-    >
-</Keyboard>
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <!-- Whether this language uses spaces between words -->
+    <bool name="current_language_has_spaces">false</bool>
+</resources>
diff --git a/java/res/values-th/strings.xml b/java/res/values-th/strings.xml
index 58faeb0..632f39a 100644
--- a/java/res/values-th/strings.xml
+++ b/java/res/values-th/strings.xml
@@ -143,6 +143,7 @@
     <string name="subtype_with_layout_en_GB" msgid="2179097748724725906">"อังกฤษ (สหราชอาณาจักร) (<xliff:g id="LAYOUT">%s</xliff:g>)"</string>
     <string name="subtype_with_layout_en_US" msgid="1362581347576714579">"อังกฤษ (สหรัฐอเมริกา) (<xliff:g id="LAYOUT">%s</xliff:g>)"</string>
     <string name="subtype_with_layout_es_US" msgid="6261791057007890189">"สเปน (สหรัฐอเมริกา) (<xliff:g id="LAYOUT">%s</xliff:g>)"</string>
+    <string name="subtype_nepali_traditional" msgid="9032247506728040447">"<xliff:g id="LANGUAGE">%s</xliff:g> (ดั้งเดิม)"</string>
     <string name="subtype_no_language" msgid="7137390094240139495">"ไม่มีภาษา (ตัวอักษรละติน)"</string>
     <string name="subtype_no_language_qwerty" msgid="244337630616742604">"ตัวอักษร (QWERTY)"</string>
     <string name="subtype_no_language_qwertz" msgid="443066912507547976">"ตัวอักษร (QWERTZ)"</string>
@@ -150,6 +151,7 @@
     <string name="subtype_no_language_dvorak" msgid="1564494667584718094">"ตัวอักษร (Dvorak)"</string>
     <string name="subtype_no_language_colemak" msgid="5837418400010302623">"ตัวอักษร (Colemak)"</string>
     <string name="subtype_no_language_pcqwerty" msgid="5354918232046200018">"ตัวอักษร (PC)"</string>
+    <string name="subtype_emoji" msgid="7483586578074549196">"อีโมจิ"</string>
     <string name="custom_input_styles_title" msgid="8429952441821251512">"รูปแบบอินพุตกำหนดเอง"</string>
     <string name="add_style" msgid="6163126614514489951">"เพิ่มสไตล์"</string>
     <string name="add" msgid="8299699805688017798">"เพิ่ม"</string>
diff --git a/java/res/values-tl/strings.xml b/java/res/values-tl/strings.xml
index 1c27ffd..3ec962b 100644
--- a/java/res/values-tl/strings.xml
+++ b/java/res/values-tl/strings.xml
@@ -143,6 +143,7 @@
     <string name="subtype_with_layout_en_GB" msgid="2179097748724725906">"Ingles (UK) (<xliff:g id="LAYOUT">%s</xliff:g>)"</string>
     <string name="subtype_with_layout_en_US" msgid="1362581347576714579">"Ingles (US) (<xliff:g id="LAYOUT">%s</xliff:g>)"</string>
     <string name="subtype_with_layout_es_US" msgid="6261791057007890189">"Spanish (US) (<xliff:g id="LAYOUT">%s</xliff:g>)"</string>
+    <string name="subtype_nepali_traditional" msgid="9032247506728040447">"<xliff:g id="LANGUAGE">%s</xliff:g> (Traditional)"</string>
     <string name="subtype_no_language" msgid="7137390094240139495">"Walang wika (Alpabeto)"</string>
     <string name="subtype_no_language_qwerty" msgid="244337630616742604">"Alpabeto (QWERTY)"</string>
     <string name="subtype_no_language_qwertz" msgid="443066912507547976">"Alpabeto (QWERTZ)"</string>
@@ -150,6 +151,7 @@
     <string name="subtype_no_language_dvorak" msgid="1564494667584718094">"Alpabeto (Dvorak)"</string>
     <string name="subtype_no_language_colemak" msgid="5837418400010302623">"Alpabeto (Colemak)"</string>
     <string name="subtype_no_language_pcqwerty" msgid="5354918232046200018">"Alpabeto (PC)"</string>
+    <string name="subtype_emoji" msgid="7483586578074549196">"Emoji"</string>
     <string name="custom_input_styles_title" msgid="8429952441821251512">"Custom style ng input"</string>
     <string name="add_style" msgid="6163126614514489951">"Dagdag style"</string>
     <string name="add" msgid="8299699805688017798">"Idagdag"</string>
diff --git a/java/res/values-tr/strings.xml b/java/res/values-tr/strings.xml
index 6d46533..4d0a617 100644
--- a/java/res/values-tr/strings.xml
+++ b/java/res/values-tr/strings.xml
@@ -143,6 +143,7 @@
     <string name="subtype_with_layout_en_GB" msgid="2179097748724725906">"İngilizce (İngiltere) (<xliff:g id="LAYOUT">%s</xliff:g>)"</string>
     <string name="subtype_with_layout_en_US" msgid="1362581347576714579">"İngilizce (ABD) (<xliff:g id="LAYOUT">%s</xliff:g>)"</string>
     <string name="subtype_with_layout_es_US" msgid="6261791057007890189">"İspanyolca (ABD) (<xliff:g id="LAYOUT">%s</xliff:g>)"</string>
+    <string name="subtype_nepali_traditional" msgid="9032247506728040447">"<xliff:g id="LANGUAGE">%s</xliff:g> (Geleneksel)"</string>
     <string name="subtype_no_language" msgid="7137390094240139495">"Dil yok (Alfabe)"</string>
     <string name="subtype_no_language_qwerty" msgid="244337630616742604">"Alfabe (QWERTY)"</string>
     <string name="subtype_no_language_qwertz" msgid="443066912507547976">"Alfabe (QWERTZ)"</string>
@@ -150,6 +151,7 @@
     <string name="subtype_no_language_dvorak" msgid="1564494667584718094">"Alfabe (Dvorak)"</string>
     <string name="subtype_no_language_colemak" msgid="5837418400010302623">"Alfabe (Colemak)"</string>
     <string name="subtype_no_language_pcqwerty" msgid="5354918232046200018">"Alfabe (PC)"</string>
+    <string name="subtype_emoji" msgid="7483586578074549196">"Emoji"</string>
     <string name="custom_input_styles_title" msgid="8429952441821251512">"Özel giriş stilleri"</string>
     <string name="add_style" msgid="6163126614514489951">"Stil ekle"</string>
     <string name="add" msgid="8299699805688017798">"Ekle"</string>
diff --git a/java/res/values-uk/strings.xml b/java/res/values-uk/strings.xml
index affce86..df62f32 100644
--- a/java/res/values-uk/strings.xml
+++ b/java/res/values-uk/strings.xml
@@ -143,6 +143,7 @@
     <string name="subtype_with_layout_en_GB" msgid="2179097748724725906">"Англійська (Великобр.) (<xliff:g id="LAYOUT">%s</xliff:g>)"</string>
     <string name="subtype_with_layout_en_US" msgid="1362581347576714579">"Англійська (США) (<xliff:g id="LAYOUT">%s</xliff:g>)"</string>
     <string name="subtype_with_layout_es_US" msgid="6261791057007890189">"іспанська (США) (<xliff:g id="LAYOUT">%s</xliff:g>)"</string>
+    <string name="subtype_nepali_traditional" msgid="9032247506728040447">"<xliff:g id="LANGUAGE">%s</xliff:g> (традиційна)"</string>
     <string name="subtype_no_language" msgid="7137390094240139495">"Стандартна (латиниця)"</string>
     <string name="subtype_no_language_qwerty" msgid="244337630616742604">"Латиниця (QWERTY)"</string>
     <string name="subtype_no_language_qwertz" msgid="443066912507547976">"Латиниця (QWERTZ)"</string>
@@ -150,6 +151,7 @@
     <string name="subtype_no_language_dvorak" msgid="1564494667584718094">"Латиниця (Dvorak)"</string>
     <string name="subtype_no_language_colemak" msgid="5837418400010302623">"Латиниця (Colemak)"</string>
     <string name="subtype_no_language_pcqwerty" msgid="5354918232046200018">"Латиниця (ПК)"</string>
+    <string name="subtype_emoji" msgid="7483586578074549196">"Cмайли Emoji"</string>
     <string name="custom_input_styles_title" msgid="8429952441821251512">"Персональні стилі введення"</string>
     <string name="add_style" msgid="6163126614514489951">"Додати стиль"</string>
     <string name="add" msgid="8299699805688017798">"Додати"</string>
diff --git a/java/res/values-v18/emoji-categories.xml b/java/res/values-v18/emoji-categories.xml
new file mode 100644
index 0000000..2ea0815
--- /dev/null
+++ b/java/res/values-v18/emoji-categories.xml
@@ -0,0 +1,909 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2013 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.
+-->
+
+<!-- Note: This emoji code point list is valid on JB-MR2 (API == 18).
+     There is another emoji code point list for KLP and later under res/xml/values-v19. -->
+<resources>
+    <array
+        name="emoji_nature"
+        format="string"
+    >
+        <!-- <item>1f415</item> -->
+        <item>1f436</item>
+        <item>1f429</item>
+        <!-- <item>1f408</item> -->
+        <item>1f431</item>
+        <!-- <item>1f400</item> -->
+        <!-- <item>1f401</item> -->
+        <item>1f42d</item>
+        <item>1f439</item>
+        <item>1f422</item>
+        <!-- <item>1f407</item> -->
+        <item>1f430</item>
+        <!-- <item>1f413</item> -->
+        <item>1f414</item>
+        <item>1f423</item>
+        <item>1f424</item>
+        <item>1f425</item>
+        <item>1f426</item>
+        <!-- <item>1f40f</item> -->
+        <item>1f411</item>
+        <!-- <item>1f410</item> -->
+        <item>1f43a</item>
+        <!-- <item>1f403</item> -->
+        <!-- <item>1f402</item> -->
+        <!-- <item>1f404</item> -->
+        <item>1f42e</item>
+        <item>1f434</item>
+        <item>1f417</item>
+        <!-- <item>1f416</item> -->
+        <item>1f437</item>
+        <item>1f43d</item>
+        <item>1f438</item>
+        <item>1f40d</item>
+        <item>1f43c</item>
+        <item>1f427</item>
+        <item>1f418</item>
+        <item>1f428</item>
+        <item>1f412</item>
+        <item>1f435</item>
+        <!-- <item>1f406</item> -->
+        <item>1f42f</item>
+        <item>1f43b</item>
+        <item>1f42b</item>
+        <!-- <item>1f42a</item> -->
+        <!-- <item>1f40a</item> -->
+        <item>1f433</item>
+        <!-- <item>1f40b</item> -->
+        <item>1f41f</item>
+        <item>1f420</item>
+        <item>1f421</item>
+        <item>1f419</item>
+        <item>1f41a</item>
+        <item>1f42c</item>
+        <item>1f40c</item>
+        <item>1f41b</item>
+        <item>1f41c</item>
+        <item>1f41d</item>
+        <item>1f41e</item>
+        <item>1f432</item>
+        <!-- <item>1f409</item> -->
+        <item>1f43e</item>
+        <item>1f378</item>
+        <item>1f37a</item>
+        <item>1f37b</item>
+        <item>1f377</item>
+        <item>1f379</item>
+        <item>1f376</item>
+        <!-- <item>2615</item> -->
+        <item>1f375</item>
+        <!-- <item>1f37c</item> -->
+        <item>1f374</item>
+        <item>1f368</item>
+        <item>1f367</item>
+        <item>1f366</item>
+        <item>1f369</item>
+        <item>1f370</item>
+        <item>1f36a</item>
+        <item>1f36b</item>
+        <item>1f36c</item>
+        <item>1f36d</item>
+        <item>1f36e</item>
+        <item>1f36f</item>
+        <item>1f373</item>
+        <item>1f354</item>
+        <item>1f35f</item>
+        <item>1f35d</item>
+        <item>1f355</item>
+        <item>1f356</item>
+        <item>1f357</item>
+        <item>1f364</item>
+        <item>1f363</item>
+        <item>1f371</item>
+        <item>1f35e</item>
+        <item>1f35c</item>
+        <item>1f359</item>
+        <item>1f35a</item>
+        <item>1f35b</item>
+        <item>1f372</item>
+        <item>1f365</item>
+        <item>1f362</item>
+        <item>1f361</item>
+        <item>1f358</item>
+        <item>1f360</item>
+        <item>1f34c</item>
+        <item>1f34e</item>
+        <item>1f34f</item>
+        <item>1f34a</item>
+        <!-- <item>1f34b</item> -->
+        <item>1f344</item>
+        <item>1f345</item>
+        <item>1f346</item>
+        <item>1f347</item>
+        <item>1f348</item>
+        <item>1f349</item>
+        <!-- <item>1f350</item> -->
+        <item>1f351</item>
+        <item>1f352</item>
+        <item>1f353</item>
+        <item>1f34d</item>
+        <item>1f330</item>
+        <item>1f331</item>
+        <!-- <item>1f332</item> -->
+        <!-- <item>1f333</item> -->
+        <item>1f334</item>
+        <item>1f335</item>
+        <item>1f337</item>
+        <item>1f338</item>
+        <item>1f339</item>
+        <item>1f340</item>
+        <item>1f341</item>
+        <item>1f342</item>
+        <item>1f343</item>
+        <item>1f33a</item>
+        <item>1f33b</item>
+        <item>1f33c</item>
+        <item>1f33d</item>
+        <item>1f33e</item>
+        <item>1f33f</item>
+        <item>2600</item>
+        <item>1f308</item>
+        <item>26c5</item>
+        <item>2601</item>
+        <item>1f301</item>
+        <item>1f302</item>
+        <!-- <item>2614</item> -->
+        <item>1f4a7</item>
+        <item>26a1</item>
+        <item>1f300</item>
+        <item>2744</item>
+        <item>26c4</item>
+        <item>1f319</item>
+        <!-- <item>1f31e</item> -->
+        <!-- <item>1f31d</item> -->
+        <!-- <item>1f31a</item> -->
+        <item>1f31b</item>
+        <!-- <item>1f31c</item> -->
+        <item>1f311</item>
+        <!-- <item>1f312</item> -->
+        <item>1f313</item>
+        <item>1f314</item>
+        <item>1f315</item>
+        <!-- <item>1f316</item> -->
+        <!-- <item>1f317</item> -->
+        <!-- <item>1f318</item> -->
+        <item>1f391</item>
+        <item>1f304</item>
+        <item>1f305</item>
+        <item>1f307</item>
+        <item>1f306</item>
+        <item>1f303</item>
+        <item>1f30c</item>
+        <item>1f309</item>
+        <item>1f30a</item>
+        <item>1f30b</item>
+        <!-- <item>1f30e</item> -->
+        <item>1f30f</item>
+        <!-- <item>1f30d</item> -->
+        <!-- <item>1f310</item> -->
+    </array>
+    <array
+        name="emoji_symbols"
+        format="string"
+    >
+        <!-- <item>fe82e|0031,20e3</item> -->
+        <!-- <item>fe82f|0032,20e3</item> -->
+        <!-- <item>fe830|0033,20e3</item> -->
+        <!-- <item>fe831|0034,20e3</item> -->
+        <!-- <item>fe832|0035,20e3</item> -->
+        <!-- <item>fe833|0036,20e3</item> -->
+        <!-- <item>fe834|0037,20e3</item> -->
+        <!-- <item>fe835|0038,20e3</item> -->
+        <!-- <item>fe836|0039,20e3</item> -->
+        <!-- <item>fe837|0030,20e3</item> -->
+        <!-- <item>1f51f</item> -->
+        <!-- <item>fe82c|0023,20e3</item> -->
+        <item>1f51d</item>
+        <item>1f519</item>
+        <item>1f51b</item>
+        <item>1f51c</item>
+        <item>1f51a</item>
+        <item>23f3</item>
+        <item>231b</item>
+        <item>23f0</item>
+        <item>2648</item>
+        <item>2649</item>
+        <item>264a</item>
+        <item>264b</item>
+        <item>264c</item>
+        <item>264d</item>
+        <item>264e</item>
+        <item>264f</item>
+        <item>2650</item>
+        <item>2651</item>
+        <item>2652</item>
+        <item>2653</item>
+        <item>26ce</item>
+        <item>1f531</item>
+        <item>1f52f</item>
+        <item>1f6bb</item>
+        <!-- <item>1f6ae</item> -->
+        <!-- <item>1f6af</item> -->
+        <!-- <item>1f6b0</item> -->
+        <!-- <item>1f6b1</item> -->
+        <item>1f170</item>
+        <item>1f171</item>
+        <item>1f18e</item>
+        <item>1f17e</item>
+        <item>1f4ae</item>
+        <item>1f4af</item>
+        <item>1f520</item>
+        <item>1f521</item>
+        <item>1f522</item>
+        <item>1f523</item>
+        <item>1f524</item>
+        <item>27bf</item>
+        <item>1f4f6</item>
+        <item>1f4f3</item>
+        <item>1f4f4</item>
+        <!-- <item>1f4f5</item> -->
+        <item>1f6b9</item>
+        <item>1f6ba</item>
+        <item>1f6bc</item>
+        <item>267f</item>
+        <item>267b</item>
+        <item>1f6ad</item>
+        <item>1f6a9</item>
+        <item>26a0</item>
+        <item>1f201</item>
+        <item>1f51e</item>
+        <item>26d4</item>
+        <item>1f192</item>
+        <item>1f197</item>
+        <item>1f195</item>
+        <item>1f198</item>
+        <item>1f199</item>
+        <item>1f193</item>
+        <item>1f196</item>
+        <item>1f19a</item>
+        <item>1f232</item>
+        <item>1f233</item>
+        <item>1f234</item>
+        <item>1f235</item>
+        <item>1f236</item>
+        <item>1f237</item>
+        <item>1f238</item>
+        <item>1f239</item>
+        <item>1f202</item>
+        <item>1f23a</item>
+        <item>1f250</item>
+        <item>1f251</item>
+        <item>3299</item>
+        <item>00ae</item>
+        <item>00a9</item>
+        <item>2122</item>
+        <item>1f21a</item>
+        <item>1f22f</item>
+        <item>3297</item>
+        <item>2b55</item>
+        <item>274c</item>
+        <item>274e</item>
+        <item>2139</item>
+        <item>1f6ab</item>
+        <item>2705</item>
+        <item>2714</item>
+        <item>1f517</item>
+        <item>2734</item>
+        <item>2733</item>
+        <item>2795</item>
+        <item>2796</item>
+        <item>2716</item>
+        <item>2797</item>
+        <item>1f4a0</item>
+        <item>1f4a1</item>
+        <item>1f4a4</item>
+        <item>1f4a2</item>
+        <item>1f525</item>
+        <item>1f4a5</item>
+        <item>1f4a8</item>
+        <item>1f4a6</item>
+        <item>1f4ab</item>
+        <item>1f55b</item>
+        <!-- <item>1f567</item> -->
+        <item>1f550</item>
+        <!-- <item>1f55c</item> -->
+        <item>1f551</item>
+        <!-- <item>1f55d</item> -->
+        <item>1f552</item>
+        <!-- <item>1f55e</item> -->
+        <item>1f553</item>
+        <!-- <item>1f55f</item> -->
+        <item>1f554</item>
+        <!-- <item>1f560</item> -->
+        <item>1f555</item>
+        <!-- <item>1f561</item> -->
+        <item>1f556</item>
+        <!-- <item>1f562</item> -->
+        <item>1f557</item>
+        <!-- <item>1f563</item> -->
+        <item>1f558</item>
+        <!-- <item>1f564</item> -->
+        <item>1f559</item>
+        <!-- <item>1f565</item> -->
+        <item>1f55a</item>
+        <!-- <item>1f566</item> -->
+        <item>2195</item>
+        <item>2b06</item>
+        <item>2197</item>
+        <item>27a1</item>
+        <item>2198</item>
+        <item>2b07</item>
+        <item>2199</item>
+        <item>2b05</item>
+        <item>2196</item>
+        <item>2194</item>
+        <item>2934</item>
+        <item>2935</item>
+        <item>23ea</item>
+        <item>23eb</item>
+        <item>23ec</item>
+        <item>23e9</item>
+        <item>25c0</item>
+        <item>25b6</item>
+        <item>1f53d</item>
+        <item>1f53c</item>
+        <item>2747</item>
+        <item>2728</item>
+        <item>1f534</item>
+        <item>1f535</item>
+        <item>26aa</item>
+        <item>26ab</item>
+        <item>1f533</item>
+        <item>1f532</item>
+        <item>2b50</item>
+        <item>1f31f</item>
+        <item>1f320</item>
+        <item>25ab</item>
+        <item>25aa</item>
+        <item>25fd</item>
+        <item>25fe</item>
+        <item>25fb</item>
+        <item>25fc</item>
+        <item>2b1c</item>
+        <item>2b1b</item>
+        <item>1f538</item>
+        <item>1f539</item>
+        <item>1f536</item>
+        <item>1f537</item>
+        <item>1f53a</item>
+        <item>1f53b</item>
+        <item>2754</item>
+        <item>2753</item>
+        <item>2755</item>
+        <item>2757</item>
+        <item>203c</item>
+        <item>2049</item>
+        <item>3030</item>
+        <item>27b0</item>
+        <item>2660</item>
+        <item>2665</item>
+        <item>2663</item>
+        <item>2666</item>
+        <item>1f194</item>
+        <item>1f511</item>
+        <item>21a9</item>
+        <item>1f191</item>
+        <item>1f50d</item>
+        <item>1f512</item>
+        <item>1f513</item>
+        <item>21aa</item>
+        <item>1f510</item>
+        <!-- <item>2611</item> -->
+        <item>1f518</item>
+        <item>1f50e</item>
+        <item>1f516</item>
+        <item>1f50f</item>
+        <item>1f503</item>
+        <!-- <item>1f500</item> -->
+        <!-- <item>1f501</item> -->
+        <!-- <item>1f502</item> -->
+        <!-- <item>1f504</item> -->
+        <item>1f4e7</item>
+        <!-- <item>1f505</item> -->
+        <!-- <item>1f506</item> -->
+        <!-- <item>1f507</item> -->
+        <!-- <item>1f508</item> -->
+        <!-- <item>1f509</item> -->
+        <item>1f50a</item>
+    </array>
+    <array
+        name="emoji_faces"
+        format="string"
+    >
+        <item>263a</item>
+        <item>1f60a</item>
+        <!-- <item>1f600</item> -->
+        <item>1f601</item>
+        <item>1f602</item>
+        <item>1f603</item>
+        <item>1f604</item>
+        <item>1f605</item>
+        <item>1f606</item>
+        <!-- <item>1f607</item> -->
+        <!-- <item>1f608</item> -->
+        <item>1f609</item>
+        <!-- <item>1f62f</item> -->
+        <!-- <item>1f610</item> -->
+        <!-- <item>1f611</item> -->
+        <!-- <item>1f615</item> -->
+        <item>1f620</item>
+        <!-- <item>1f62c</item> -->
+        <item>1f621</item>
+        <item>1f622</item>
+        <!-- <item>1f634</item> -->
+        <!-- <item>1f62e</item> -->
+        <item>1f623</item>
+        <item>1f624</item>
+        <item>1f625</item>
+        <!-- <item>1f626</item> -->
+        <!-- <item>1f627</item> -->
+        <item>1f628</item>
+        <item>1f629</item>
+        <item>1f630</item>
+        <!-- <item>1f61f</item> -->
+        <item>1f631</item>
+        <item>1f632</item>
+        <item>1f633</item>
+        <item>1f635</item>
+        <!-- <item>1f636</item> -->
+        <item>1f637</item>
+        <item>1f61e</item>
+        <item>1f612</item>
+        <item>1f60d</item>
+        <!-- <item>1f61b</item> -->
+        <item>1f61c</item>
+        <item>1f61d</item>
+        <item>1f60b</item>
+        <!-- <item>1f617</item> -->
+        <!-- <item>1f619</item> -->
+        <item>1f618</item>
+        <item>1f61a</item>
+        <!-- <item>1f60e</item> -->
+        <item>1f62d</item>
+        <item>1f60c</item>
+        <item>1f616</item>
+        <item>1f614</item>
+        <item>1f62a</item>
+        <item>1f60f</item>
+        <item>1f613</item>
+        <item>1f62b</item>
+        <item>1f64b</item>
+        <item>1f64c</item>
+        <item>1f64d</item>
+        <item>1f645</item>
+        <item>1f646</item>
+        <item>1f647</item>
+        <item>1f64e</item>
+        <item>1f64f</item>
+        <item>1f63a</item>
+        <item>1f63c</item>
+        <item>1f638</item>
+        <item>1f639</item>
+        <item>1f63b</item>
+        <item>1f63d</item>
+        <item>1f63f</item>
+        <item>1f63e</item>
+        <item>1f640</item>
+        <item>1f648</item>
+        <item>1f649</item>
+        <item>1f64a</item>
+        <item>1f4a9</item>
+        <item>1f476</item>
+        <item>1f466</item>
+        <item>1f467</item>
+        <item>1f468</item>
+        <item>1f469</item>
+        <item>1f474</item>
+        <item>1f475</item>
+        <item>1f48f</item>
+        <item>1f491</item>
+        <item>1f46a</item>
+        <item>1f46b</item>
+        <!-- <item>1f46c</item> -->
+        <!-- <item>1f46d</item> -->
+        <item>1f464</item>
+        <!-- <item>1f465</item> -->
+        <item>1f46e</item>
+        <item>1f477</item>
+        <item>1f481</item>
+        <item>1f482</item>
+        <item>1f46f</item>
+        <item>1f470</item>
+        <item>1f478</item>
+        <item>1f385</item>
+        <item>1f47c</item>
+        <!-- <item>1f471</item> -->
+        <!-- <item>1f472</item> -->
+        <!-- <item>1f473</item> -->
+        <item>1f483</item>
+        <item>1f486</item>
+        <item>1f487</item>
+        <item>1f485</item>
+        <item>1f47b</item>
+        <item>1f479</item>
+        <item>1f47a</item>
+        <item>1f47d</item>
+        <item>1f47e</item>
+        <item>1f47f</item>
+        <item>1f480</item>
+        <item>1f4aa</item>
+        <item>1f440</item>
+        <item>1f442</item>
+        <item>1f443</item>
+        <item>1f463</item>
+        <item>1f444</item>
+        <item>1f445</item>
+        <item>1f48b</item>
+        <item>2764</item>
+        <item>1f499</item>
+        <item>1f49a</item>
+        <item>1f49b</item>
+        <item>1f49c</item>
+        <item>1f493</item>
+        <item>1f494</item>
+        <item>1f495</item>
+        <item>1f496</item>
+        <item>1f497</item>
+        <item>1f498</item>
+        <item>1f49d</item>
+        <item>1f49e</item>
+        <item>1f49f</item>
+        <item>1f44d</item>
+        <item>1f44e</item>
+        <item>1f44c</item>
+        <item>270a</item>
+        <item>270c</item>
+        <item>270b</item>
+        <item>1f44a</item>
+        <!-- <item>261d</item> -->
+        <item>1f446</item>
+        <item>1f447</item>
+        <item>1f448</item>
+        <item>1f449</item>
+        <item>1f44b</item>
+        <item>1f44f</item>
+        <!-- <item>1f450</item> -->
+    </array>
+    <array
+        name="emoji_objects"
+        format="string"
+    >
+        <item>1f530</item>
+        <item>1f484</item>
+        <item>1f45e</item>
+        <item>1f45f</item>
+        <item>1f451</item>
+        <item>1f452</item>
+        <item>1f3a9</item>
+        <item>1f393</item>
+        <item>1f453</item>
+        <item>231a</item>
+        <item>1f454</item>
+        <item>1f455</item>
+        <item>1f456</item>
+        <item>1f457</item>
+        <item>1f458</item>
+        <item>1f459</item>
+        <item>1f460</item>
+        <item>1f461</item>
+        <item>1f462</item>
+        <item>1f45a</item>
+        <item>1f45c</item>
+        <item>1f4bc</item>
+        <item>1f392</item>
+        <item>1f45d</item>
+        <item>1f45b</item>
+        <item>1f4b0</item>
+        <item>1f4b3</item>
+        <item>1f4b2</item>
+        <item>1f4b5</item>
+        <item>1f4b4</item>
+        <!-- <item>1f4b6</item> -->
+        <!-- <item>1f4b7</item> -->
+        <item>1f4b8</item>
+        <item>1f4b1</item>
+        <item>1f4b9</item>
+        <item>1f52b</item>
+        <item>1f52a</item>
+        <item>1f4a3</item>
+        <item>1f489</item>
+        <item>1f48a</item>
+        <item>1f6ac</item>
+        <item>1f514</item>
+        <!-- <item>1f515</item> -->
+        <item>1f6aa</item>
+        <!-- <item>1f52c</item> -->
+        <!-- <item>1f52d</item> -->
+        <item>1f52e</item>
+        <item>1f526</item>
+        <item>1f50b</item>
+        <item>1f50c</item>
+        <item>1f4dc</item>
+        <item>1f4d7</item>
+        <item>1f4d8</item>
+        <item>1f4d9</item>
+        <item>1f4da</item>
+        <item>1f4d4</item>
+        <item>1f4d2</item>
+        <item>1f4d1</item>
+        <item>1f4d3</item>
+        <item>1f4d5</item>
+        <item>1f4d6</item>
+        <item>1f4f0</item>
+        <item>1f4db</item>
+        <item>1f383</item>
+        <item>1f384</item>
+        <item>1f380</item>
+        <item>1f381</item>
+        <item>1f382</item>
+        <item>1f388</item>
+        <item>1f386</item>
+        <item>1f387</item>
+        <item>1f389</item>
+        <item>1f38a</item>
+        <item>1f38d</item>
+        <item>1f38f</item>
+        <item>1f38c</item>
+        <item>1f390</item>
+        <item>1f38b</item>
+        <item>1f38e</item>
+        <item>1f4f1</item>
+        <item>1f4f2</item>
+        <item>1f4df</item>
+        <item>260e</item>
+        <item>1f4de</item>
+        <item>1f4e0</item>
+        <item>1f4e6</item>
+        <item>2709</item>
+        <item>1f4e8</item>
+        <item>1f4e9</item>
+        <item>1f4ea</item>
+        <item>1f4eb</item>
+        <!-- <item>1f4ed</item> -->
+        <!-- <item>1f4ec</item> -->
+        <item>1f4ee</item>
+        <item>1f4e4</item>
+        <item>1f4e5</item>
+        <!-- <item>1f4ef</item> -->
+        <item>1f4e2</item>
+        <item>1f4e3</item>
+        <item>1f4e1</item>
+        <item>1f4ac</item>
+        <!-- <item>1f4ad</item> -->
+        <item>2712</item>
+        <item>270f</item>
+        <item>1f4dd</item>
+        <item>1f4cf</item>
+        <item>1f4d0</item>
+        <item>1f4cd</item>
+        <item>1f4cc</item>
+        <item>1f4ce</item>
+        <item>2702</item>
+        <item>1f4ba</item>
+        <item>1f4bb</item>
+        <item>1f4bd</item>
+        <item>1f4be</item>
+        <item>1f4bf</item>
+        <item>1f4c6</item>
+        <item>1f4c5</item>
+        <item>1f4c7</item>
+        <item>1f4cb</item>
+        <item>1f4c1</item>
+        <item>1f4c2</item>
+        <item>1f4c3</item>
+        <item>1f4c4</item>
+        <item>1f4ca</item>
+        <item>1f4c8</item>
+        <item>1f4c9</item>
+        <item>26fa</item>
+        <item>1f3a1</item>
+        <item>1f3a2</item>
+        <item>1f3a0</item>
+        <item>1f3aa</item>
+        <item>1f3a8</item>
+        <item>1f3ac</item>
+        <item>1f3a5</item>
+        <item>1f4f7</item>
+        <item>1f4f9</item>
+        <item>1f3a6</item>
+        <item>1f3ad</item>
+        <item>1f3ab</item>
+        <item>1f3ae</item>
+        <item>1f3b2</item>
+        <item>1f3b0</item>
+        <item>1f0cf</item>
+        <item>1f3b4</item>
+        <item>1f004</item>
+        <item>1f3af</item>
+        <item>1f4fa</item>
+        <item>1f4fb</item>
+        <item>1f4c0</item>
+        <item>1f4fc</item>
+        <item>1f3a7</item>
+        <item>1f3a4</item>
+        <item>1f3b5</item>
+        <item>1f3b6</item>
+        <item>1f3bc</item>
+        <item>1f3bb</item>
+        <item>1f3b9</item>
+        <item>1f3b7</item>
+        <item>1f3ba</item>
+        <item>1f3b8</item>
+        <item>303d</item>
+    </array>
+    <array
+        name="emoji_places"
+        format="string"
+    >
+        <item>1f3e0</item>
+        <item>1f3e1</item>
+        <item>1f3e2</item>
+        <item>1f3e3</item>
+        <!-- <item>1f3e4</item> -->
+        <item>1f3e5</item>
+        <item>1f3e6</item>
+        <item>1f3e7</item>
+        <item>1f3e8</item>
+        <item>1f3e9</item>
+        <item>1f3ea</item>
+        <item>1f3eb</item>
+        <item>26ea</item>
+        <item>26f2</item>
+        <item>1f3ec</item>
+        <item>1f3ef</item>
+        <item>1f3f0</item>
+        <item>1f3ed</item>
+        <item>1f5fb</item>
+        <item>1f5fc</item>
+        <item>1f5fd</item>
+        <item>1f5fe</item>
+        <item>1f5ff</item>
+        <item>2693</item>
+        <item>1f3ee</item>
+        <item>1f488</item>
+        <item>1f527</item>
+        <item>1f528</item>
+        <item>1f529</item>
+        <!-- <item>1f6bf</item> -->
+        <!-- <item>1f6c1</item> -->
+        <item>1f6c0</item>
+        <item>1f6bd</item>
+        <item>1f6be</item>
+        <item>1f3bd</item>
+        <item>1f3a3</item>
+        <item>1f3b1</item>
+        <item>1f3b3</item>
+        <item>26be</item>
+        <item>26f3</item>
+        <item>1f3be</item>
+        <item>26bd</item>
+        <item>1f3bf</item>
+        <item>1f3c0</item>
+        <item>1f3c1</item>
+        <item>1f3c2</item>
+        <item>1f3c3</item>
+        <item>1f3c4</item>
+        <item>1f3c6</item>
+        <!-- <item>1f3c7</item> -->
+        <item>1f40e</item>
+        <item>1f3c8</item>
+        <!-- <item>1f3c9</item> -->
+        <item>1f3ca</item>
+        <!-- <item>1f682</item> -->
+        <item>1f683</item>
+        <item>1f684</item>
+        <item>1f685</item>
+        <!-- <item>1f686</item> -->
+        <item>1f687</item>
+        <item>24c2</item>
+        <!-- <item>1f688</item> -->
+        <!-- <item>1f68a</item> -->
+        <!-- <item>1f68b</item> -->
+        <item>1f68c</item>
+        <!-- <item>1f68d</item> -->
+        <!-- <item>1f68e</item> -->
+        <item>1f68f</item>
+        <!-- <item>1f690</item> -->
+        <item>1f691</item>
+        <item>1f692</item>
+        <item>1f693</item>
+        <!-- <item>1f694</item> -->
+        <item>1f695</item>
+        <!-- <item>1f696</item> -->
+        <item>1f697</item>
+        <!-- <item>1f698</item> -->
+        <item>1f699</item>
+        <!-- <item>1f69a</item> -->
+        <!-- <item>1f69b</item> -->
+        <!-- <item>1f69c</item> -->
+        <!-- <item>1f69d</item> -->
+        <!-- <item>1f69e</item> -->
+        <!-- <item>1f69f</item> -->
+        <!-- <item>1f6a0</item> -->
+        <!-- <item>1f6a1</item> -->
+        <item>1f6a2</item>
+        <!-- <item>1f6a3</item> -->
+        <!-- <item>1f681</item> -->
+        <item>2708</item>
+        <!-- <item>1f6c2</item> -->
+        <!-- <item>1f6c3</item> -->
+        <!-- <item>1f6c4</item> -->
+        <!-- <item>1f6c5</item> -->
+        <item>26f5</item>
+        <item>1f6b2</item>
+        <!-- <item>1f6b3</item> -->
+        <!-- <item>1f6b4</item> -->
+        <!-- <item>1f6b5</item> -->
+        <!-- <item>1f6b7</item> -->
+        <!-- <item>1f6b8</item> -->
+        <item>1f689</item>
+        <item>1f680</item>
+        <item>1f6a4</item>
+        <item>1f6b6</item>
+        <item>26fd</item>
+        <item>1f17f</item>
+        <item>1f6a5</item>
+        <!-- <item>1f6a6</item> -->
+        <item>1f6a7</item>
+        <item>1f6a8</item>
+        <item>2668</item>
+        <item>1f48c</item>
+        <item>1f48d</item>
+        <item>1f48e</item>
+        <item>1f490</item>
+        <item>1f492</item>
+        <item>fe4e5|1f1ef,1f1f5</item>
+        <item>fe4e6|1f1fa,1f1f8</item>
+        <item>fe4e7|1f1eb,1f1f7</item>
+        <item>fe4e8|1f1e9,1f1ea</item>
+        <item>fe4e9|1f1ee,1f1f9</item>
+        <item>fe4ea|1f1ec,1f1e7</item>
+        <item>fe4eb|1f1ea,1f1f8</item>
+        <item>fe4ec|1f1f7,1f1fa</item>
+        <item>fe4ed|1f1e8,1f1f3</item>
+        <item>fe4ee|1f1f0,1f1f7</item>
+    </array>
+    <array
+        name="emoji_emoticons"
+        format="string"
+    >
+        <item>=-O</item>
+        <item>:-P</item>
+        <item>;-)</item>
+        <item>:-(</item>
+        <item>:-)</item>
+        <item>:-!</item>
+        <item>:-$</item>
+        <item>B-)</item>
+        <item>:O</item>
+        <item>:-*</item>
+        <item>:-D</item>
+        <item>:\'(</item>
+        <item>:-\\</item>
+        <item>O:-)</item>
+        <item>:-[</item>
+    </array>
+</resources>
diff --git a/java/res/values-v19/emoji-categories.xml b/java/res/values-v19/emoji-categories.xml
new file mode 100644
index 0000000..658bbfa
--- /dev/null
+++ b/java/res/values-v19/emoji-categories.xml
@@ -0,0 +1,909 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2013 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.
+-->
+
+<!-- Note: This emoji code point list is valid on KLP and later (API >= 19).
+     There is another emoji code point list for JB-MR2 under res/xml/values and values-v18.-->
+<resources>
+    <array
+        name="emoji_nature"
+        format="string"
+    >
+        <item>1f415</item>
+        <item>1f436</item>
+        <item>1f429</item>
+        <item>1f408</item>
+        <item>1f431</item>
+        <item>1f400</item>
+        <item>1f401</item>
+        <item>1f42d</item>
+        <item>1f439</item>
+        <item>1f422</item>
+        <item>1f407</item>
+        <item>1f430</item>
+        <item>1f413</item>
+        <item>1f414</item>
+        <item>1f423</item>
+        <item>1f424</item>
+        <item>1f425</item>
+        <item>1f426</item>
+        <item>1f40f</item>
+        <item>1f411</item>
+        <item>1f410</item>
+        <item>1f43a</item>
+        <item>1f403</item>
+        <item>1f402</item>
+        <item>1f404</item>
+        <item>1f42e</item>
+        <item>1f434</item>
+        <item>1f417</item>
+        <item>1f416</item>
+        <item>1f437</item>
+        <item>1f43d</item>
+        <item>1f438</item>
+        <item>1f40d</item>
+        <item>1f43c</item>
+        <item>1f427</item>
+        <item>1f418</item>
+        <item>1f428</item>
+        <item>1f412</item>
+        <item>1f435</item>
+        <item>1f406</item>
+        <item>1f42f</item>
+        <item>1f43b</item>
+        <item>1f42b</item>
+        <item>1f42a</item>
+        <item>1f40a</item>
+        <item>1f433</item>
+        <item>1f40b</item>
+        <item>1f41f</item>
+        <item>1f420</item>
+        <item>1f421</item>
+        <item>1f419</item>
+        <item>1f41a</item>
+        <item>1f42c</item>
+        <item>1f40c</item>
+        <item>1f41b</item>
+        <item>1f41c</item>
+        <item>1f41d</item>
+        <item>1f41e</item>
+        <item>1f432</item>
+        <item>1f409</item>
+        <item>1f43e</item>
+        <item>1f378</item>
+        <item>1f37a</item>
+        <item>1f37b</item>
+        <item>1f377</item>
+        <item>1f379</item>
+        <item>1f376</item>
+        <item>2615</item>
+        <item>1f375</item>
+        <item>1f37c</item>
+        <item>1f374</item>
+        <item>1f368</item>
+        <item>1f367</item>
+        <item>1f366</item>
+        <item>1f369</item>
+        <item>1f370</item>
+        <item>1f36a</item>
+        <item>1f36b</item>
+        <item>1f36c</item>
+        <item>1f36d</item>
+        <item>1f36e</item>
+        <item>1f36f</item>
+        <item>1f373</item>
+        <item>1f354</item>
+        <item>1f35f</item>
+        <item>1f35d</item>
+        <item>1f355</item>
+        <item>1f356</item>
+        <item>1f357</item>
+        <item>1f364</item>
+        <item>1f363</item>
+        <item>1f371</item>
+        <item>1f35e</item>
+        <item>1f35c</item>
+        <item>1f359</item>
+        <item>1f35a</item>
+        <item>1f35b</item>
+        <item>1f372</item>
+        <item>1f365</item>
+        <item>1f362</item>
+        <item>1f361</item>
+        <item>1f358</item>
+        <item>1f360</item>
+        <item>1f34c</item>
+        <item>1f34e</item>
+        <item>1f34f</item>
+        <item>1f34a</item>
+        <item>1f34b</item>
+        <item>1f344</item>
+        <item>1f345</item>
+        <item>1f346</item>
+        <item>1f347</item>
+        <item>1f348</item>
+        <item>1f349</item>
+        <item>1f350</item>
+        <item>1f351</item>
+        <item>1f352</item>
+        <item>1f353</item>
+        <item>1f34d</item>
+        <item>1f330</item>
+        <item>1f331</item>
+        <item>1f332</item>
+        <item>1f333</item>
+        <item>1f334</item>
+        <item>1f335</item>
+        <item>1f337</item>
+        <item>1f338</item>
+        <item>1f339</item>
+        <item>1f340</item>
+        <item>1f341</item>
+        <item>1f342</item>
+        <item>1f343</item>
+        <item>1f33a</item>
+        <item>1f33b</item>
+        <item>1f33c</item>
+        <item>1f33d</item>
+        <item>1f33e</item>
+        <item>1f33f</item>
+        <item>2600</item>
+        <item>1f308</item>
+        <item>26c5</item>
+        <item>2601</item>
+        <item>1f301</item>
+        <item>1f302</item>
+        <item>2614</item>
+        <item>1f4a7</item>
+        <item>26a1</item>
+        <item>1f300</item>
+        <item>2744</item>
+        <item>26c4</item>
+        <item>1f319</item>
+        <item>1f31e</item>
+        <item>1f31d</item>
+        <item>1f31a</item>
+        <item>1f31b</item>
+        <item>1f31c</item>
+        <item>1f311</item>
+        <item>1f312</item>
+        <item>1f313</item>
+        <item>1f314</item>
+        <item>1f315</item>
+        <item>1f316</item>
+        <item>1f317</item>
+        <item>1f318</item>
+        <item>1f391</item>
+        <item>1f304</item>
+        <item>1f305</item>
+        <item>1f307</item>
+        <item>1f306</item>
+        <item>1f303</item>
+        <item>1f30c</item>
+        <item>1f309</item>
+        <item>1f30a</item>
+        <item>1f30b</item>
+        <item>1f30e</item>
+        <item>1f30f</item>
+        <item>1f30d</item>
+        <item>1f310</item>
+    </array>
+    <array
+        name="emoji_symbols"
+        format="string"
+    >
+        <item>fe82e|0031,20e3</item>
+        <item>fe82f|0032,20e3</item>
+        <item>fe830|0033,20e3</item>
+        <item>fe831|0034,20e3</item>
+        <item>fe832|0035,20e3</item>
+        <item>fe833|0036,20e3</item>
+        <item>fe834|0037,20e3</item>
+        <item>fe835|0038,20e3</item>
+        <item>fe836|0039,20e3</item>
+        <item>fe837|0030,20e3</item>
+        <item>1f51f</item>
+        <item>fe82c|0023,20e3</item>
+        <item>1f51d</item>
+        <item>1f519</item>
+        <item>1f51b</item>
+        <item>1f51c</item>
+        <item>1f51a</item>
+        <item>23f3</item>
+        <item>231b</item>
+        <item>23f0</item>
+        <item>2648</item>
+        <item>2649</item>
+        <item>264a</item>
+        <item>264b</item>
+        <item>264c</item>
+        <item>264d</item>
+        <item>264e</item>
+        <item>264f</item>
+        <item>2650</item>
+        <item>2651</item>
+        <item>2652</item>
+        <item>2653</item>
+        <item>26ce</item>
+        <item>1f531</item>
+        <item>1f52f</item>
+        <item>1f6bb</item>
+        <item>1f6ae</item>
+        <item>1f6af</item>
+        <item>1f6b0</item>
+        <item>1f6b1</item>
+        <item>1f170</item>
+        <item>1f171</item>
+        <item>1f18e</item>
+        <item>1f17e</item>
+        <item>1f4ae</item>
+        <item>1f4af</item>
+        <item>1f520</item>
+        <item>1f521</item>
+        <item>1f522</item>
+        <item>1f523</item>
+        <item>1f524</item>
+        <item>27bf</item>
+        <item>1f4f6</item>
+        <item>1f4f3</item>
+        <item>1f4f4</item>
+        <item>1f4f5</item>
+        <item>1f6b9</item>
+        <item>1f6ba</item>
+        <item>1f6bc</item>
+        <item>267f</item>
+        <item>267b</item>
+        <item>1f6ad</item>
+        <item>1f6a9</item>
+        <item>26a0</item>
+        <item>1f201</item>
+        <item>1f51e</item>
+        <item>26d4</item>
+        <item>1f192</item>
+        <item>1f197</item>
+        <item>1f195</item>
+        <item>1f198</item>
+        <item>1f199</item>
+        <item>1f193</item>
+        <item>1f196</item>
+        <item>1f19a</item>
+        <item>1f232</item>
+        <item>1f233</item>
+        <item>1f234</item>
+        <item>1f235</item>
+        <item>1f236</item>
+        <item>1f237</item>
+        <item>1f238</item>
+        <item>1f239</item>
+        <item>1f202</item>
+        <item>1f23a</item>
+        <item>1f250</item>
+        <item>1f251</item>
+        <item>3299</item>
+        <item>00ae</item>
+        <item>00a9</item>
+        <item>2122</item>
+        <item>1f21a</item>
+        <item>1f22f</item>
+        <item>3297</item>
+        <item>2b55</item>
+        <item>274c</item>
+        <item>274e</item>
+        <item>2139</item>
+        <item>1f6ab</item>
+        <item>2705</item>
+        <item>2714</item>
+        <item>1f517</item>
+        <item>2734</item>
+        <item>2733</item>
+        <item>2795</item>
+        <item>2796</item>
+        <item>2716</item>
+        <item>2797</item>
+        <item>1f4a0</item>
+        <item>1f4a1</item>
+        <item>1f4a4</item>
+        <item>1f4a2</item>
+        <item>1f525</item>
+        <item>1f4a5</item>
+        <item>1f4a8</item>
+        <item>1f4a6</item>
+        <item>1f4ab</item>
+        <item>1f55b</item>
+        <item>1f567</item>
+        <item>1f550</item>
+        <item>1f55c</item>
+        <item>1f551</item>
+        <item>1f55d</item>
+        <item>1f552</item>
+        <item>1f55e</item>
+        <item>1f553</item>
+        <item>1f55f</item>
+        <item>1f554</item>
+        <item>1f560</item>
+        <item>1f555</item>
+        <item>1f561</item>
+        <item>1f556</item>
+        <item>1f562</item>
+        <item>1f557</item>
+        <item>1f563</item>
+        <item>1f558</item>
+        <item>1f564</item>
+        <item>1f559</item>
+        <item>1f565</item>
+        <item>1f55a</item>
+        <item>1f566</item>
+        <item>2195</item>
+        <item>2b06</item>
+        <item>2197</item>
+        <item>27a1</item>
+        <item>2198</item>
+        <item>2b07</item>
+        <item>2199</item>
+        <item>2b05</item>
+        <item>2196</item>
+        <item>2194</item>
+        <item>2934</item>
+        <item>2935</item>
+        <item>23ea</item>
+        <item>23eb</item>
+        <item>23ec</item>
+        <item>23e9</item>
+        <item>25c0</item>
+        <item>25b6</item>
+        <item>1f53d</item>
+        <item>1f53c</item>
+        <item>2747</item>
+        <item>2728</item>
+        <item>1f534</item>
+        <item>1f535</item>
+        <item>26aa</item>
+        <item>26ab</item>
+        <item>1f533</item>
+        <item>1f532</item>
+        <item>2b50</item>
+        <item>1f31f</item>
+        <item>1f320</item>
+        <item>25ab</item>
+        <item>25aa</item>
+        <item>25fd</item>
+        <item>25fe</item>
+        <item>25fb</item>
+        <item>25fc</item>
+        <item>2b1c</item>
+        <item>2b1b</item>
+        <item>1f538</item>
+        <item>1f539</item>
+        <item>1f536</item>
+        <item>1f537</item>
+        <item>1f53a</item>
+        <item>1f53b</item>
+        <item>2754</item>
+        <item>2753</item>
+        <item>2755</item>
+        <item>2757</item>
+        <item>203c</item>
+        <item>2049</item>
+        <item>3030</item>
+        <item>27b0</item>
+        <item>2660</item>
+        <item>2665</item>
+        <item>2663</item>
+        <item>2666</item>
+        <item>1f194</item>
+        <item>1f511</item>
+        <item>21a9</item>
+        <item>1f191</item>
+        <item>1f50d</item>
+        <item>1f512</item>
+        <item>1f513</item>
+        <item>21aa</item>
+        <item>1f510</item>
+        <item>2611</item>
+        <item>1f518</item>
+        <item>1f50e</item>
+        <item>1f516</item>
+        <item>1f50f</item>
+        <item>1f503</item>
+        <item>1f500</item>
+        <item>1f501</item>
+        <item>1f502</item>
+        <item>1f504</item>
+        <item>1f4e7</item>
+        <item>1f505</item>
+        <item>1f506</item>
+        <item>1f507</item>
+        <item>1f508</item>
+        <item>1f509</item>
+        <item>1f50a</item>
+    </array>
+    <array
+        name="emoji_faces"
+        format="string"
+    >
+        <item>263a</item>
+        <item>1f60a</item>
+        <item>1f600</item>
+        <item>1f601</item>
+        <item>1f602</item>
+        <item>1f603</item>
+        <item>1f604</item>
+        <item>1f605</item>
+        <item>1f606</item>
+        <item>1f607</item>
+        <item>1f608</item>
+        <item>1f609</item>
+        <item>1f62f</item>
+        <item>1f610</item>
+        <item>1f611</item>
+        <item>1f615</item>
+        <item>1f620</item>
+        <item>1f62c</item>
+        <item>1f621</item>
+        <item>1f622</item>
+        <item>1f634</item>
+        <item>1f62e</item>
+        <item>1f623</item>
+        <item>1f624</item>
+        <item>1f625</item>
+        <item>1f626</item>
+        <item>1f627</item>
+        <item>1f628</item>
+        <item>1f629</item>
+        <item>1f630</item>
+        <item>1f61f</item>
+        <item>1f631</item>
+        <item>1f632</item>
+        <item>1f633</item>
+        <item>1f635</item>
+        <item>1f636</item>
+        <item>1f637</item>
+        <item>1f61e</item>
+        <item>1f612</item>
+        <item>1f60d</item>
+        <item>1f61b</item>
+        <item>1f61c</item>
+        <item>1f61d</item>
+        <item>1f60b</item>
+        <item>1f617</item>
+        <item>1f619</item>
+        <item>1f618</item>
+        <item>1f61a</item>
+        <item>1f60e</item>
+        <item>1f62d</item>
+        <item>1f60c</item>
+        <item>1f616</item>
+        <item>1f614</item>
+        <item>1f62a</item>
+        <item>1f60f</item>
+        <item>1f613</item>
+        <item>1f62b</item>
+        <item>1f64b</item>
+        <item>1f64c</item>
+        <item>1f64d</item>
+        <item>1f645</item>
+        <item>1f646</item>
+        <item>1f647</item>
+        <item>1f64e</item>
+        <item>1f64f</item>
+        <item>1f63a</item>
+        <item>1f63c</item>
+        <item>1f638</item>
+        <item>1f639</item>
+        <item>1f63b</item>
+        <item>1f63d</item>
+        <item>1f63f</item>
+        <item>1f63e</item>
+        <item>1f640</item>
+        <item>1f648</item>
+        <item>1f649</item>
+        <item>1f64a</item>
+        <item>1f4a9</item>
+        <item>1f476</item>
+        <item>1f466</item>
+        <item>1f467</item>
+        <item>1f468</item>
+        <item>1f469</item>
+        <item>1f474</item>
+        <item>1f475</item>
+        <item>1f48f</item>
+        <item>1f491</item>
+        <item>1f46a</item>
+        <item>1f46b</item>
+        <item>1f46c</item>
+        <item>1f46d</item>
+        <item>1f464</item>
+        <item>1f465</item>
+        <item>1f46e</item>
+        <item>1f477</item>
+        <item>1f481</item>
+        <item>1f482</item>
+        <item>1f46f</item>
+        <item>1f470</item>
+        <item>1f478</item>
+        <item>1f385</item>
+        <item>1f47c</item>
+        <item>1f471</item>
+        <item>1f472</item>
+        <item>1f473</item>
+        <item>1f483</item>
+        <item>1f486</item>
+        <item>1f487</item>
+        <item>1f485</item>
+        <item>1f47b</item>
+        <item>1f479</item>
+        <item>1f47a</item>
+        <item>1f47d</item>
+        <item>1f47e</item>
+        <item>1f47f</item>
+        <item>1f480</item>
+        <item>1f4aa</item>
+        <item>1f440</item>
+        <item>1f442</item>
+        <item>1f443</item>
+        <item>1f463</item>
+        <item>1f444</item>
+        <item>1f445</item>
+        <item>1f48b</item>
+        <item>2764</item>
+        <item>1f499</item>
+        <item>1f49a</item>
+        <item>1f49b</item>
+        <item>1f49c</item>
+        <item>1f493</item>
+        <item>1f494</item>
+        <item>1f495</item>
+        <item>1f496</item>
+        <item>1f497</item>
+        <item>1f498</item>
+        <item>1f49d</item>
+        <item>1f49e</item>
+        <item>1f49f</item>
+        <item>1f44d</item>
+        <item>1f44e</item>
+        <item>1f44c</item>
+        <item>270a</item>
+        <item>270c</item>
+        <item>270b</item>
+        <item>1f44a</item>
+        <item>261d</item>
+        <item>1f446</item>
+        <item>1f447</item>
+        <item>1f448</item>
+        <item>1f449</item>
+        <item>1f44b</item>
+        <item>1f44f</item>
+        <item>1f450</item>
+    </array>
+    <array
+        name="emoji_objects"
+        format="string"
+    >
+        <item>1f530</item>
+        <item>1f484</item>
+        <item>1f45e</item>
+        <item>1f45f</item>
+        <item>1f451</item>
+        <item>1f452</item>
+        <item>1f3a9</item>
+        <item>1f393</item>
+        <item>1f453</item>
+        <item>231a</item>
+        <item>1f454</item>
+        <item>1f455</item>
+        <item>1f456</item>
+        <item>1f457</item>
+        <item>1f458</item>
+        <item>1f459</item>
+        <item>1f460</item>
+        <item>1f461</item>
+        <item>1f462</item>
+        <item>1f45a</item>
+        <item>1f45c</item>
+        <item>1f4bc</item>
+        <item>1f392</item>
+        <item>1f45d</item>
+        <item>1f45b</item>
+        <item>1f4b0</item>
+        <item>1f4b3</item>
+        <item>1f4b2</item>
+        <item>1f4b5</item>
+        <item>1f4b4</item>
+        <item>1f4b6</item>
+        <item>1f4b7</item>
+        <item>1f4b8</item>
+        <item>1f4b1</item>
+        <item>1f4b9</item>
+        <item>1f52b</item>
+        <item>1f52a</item>
+        <item>1f4a3</item>
+        <item>1f489</item>
+        <item>1f48a</item>
+        <item>1f6ac</item>
+        <item>1f514</item>
+        <item>1f515</item>
+        <item>1f6aa</item>
+        <item>1f52c</item>
+        <item>1f52d</item>
+        <item>1f52e</item>
+        <item>1f526</item>
+        <item>1f50b</item>
+        <item>1f50c</item>
+        <item>1f4dc</item>
+        <item>1f4d7</item>
+        <item>1f4d8</item>
+        <item>1f4d9</item>
+        <item>1f4da</item>
+        <item>1f4d4</item>
+        <item>1f4d2</item>
+        <item>1f4d1</item>
+        <item>1f4d3</item>
+        <item>1f4d5</item>
+        <item>1f4d6</item>
+        <item>1f4f0</item>
+        <item>1f4db</item>
+        <item>1f383</item>
+        <item>1f384</item>
+        <item>1f380</item>
+        <item>1f381</item>
+        <item>1f382</item>
+        <item>1f388</item>
+        <item>1f386</item>
+        <item>1f387</item>
+        <item>1f389</item>
+        <item>1f38a</item>
+        <item>1f38d</item>
+        <item>1f38f</item>
+        <item>1f38c</item>
+        <item>1f390</item>
+        <item>1f38b</item>
+        <item>1f38e</item>
+        <item>1f4f1</item>
+        <item>1f4f2</item>
+        <item>1f4df</item>
+        <item>260e</item>
+        <item>1f4de</item>
+        <item>1f4e0</item>
+        <item>1f4e6</item>
+        <item>2709</item>
+        <item>1f4e8</item>
+        <item>1f4e9</item>
+        <item>1f4ea</item>
+        <item>1f4eb</item>
+        <item>1f4ed</item>
+        <item>1f4ec</item>
+        <item>1f4ee</item>
+        <item>1f4e4</item>
+        <item>1f4e5</item>
+        <item>1f4ef</item>
+        <item>1f4e2</item>
+        <item>1f4e3</item>
+        <item>1f4e1</item>
+        <item>1f4ac</item>
+        <item>1f4ad</item>
+        <item>2712</item>
+        <item>270f</item>
+        <item>1f4dd</item>
+        <item>1f4cf</item>
+        <item>1f4d0</item>
+        <item>1f4cd</item>
+        <item>1f4cc</item>
+        <item>1f4ce</item>
+        <item>2702</item>
+        <item>1f4ba</item>
+        <item>1f4bb</item>
+        <item>1f4bd</item>
+        <item>1f4be</item>
+        <item>1f4bf</item>
+        <item>1f4c6</item>
+        <item>1f4c5</item>
+        <item>1f4c7</item>
+        <item>1f4cb</item>
+        <item>1f4c1</item>
+        <item>1f4c2</item>
+        <item>1f4c3</item>
+        <item>1f4c4</item>
+        <item>1f4ca</item>
+        <item>1f4c8</item>
+        <item>1f4c9</item>
+        <item>26fa</item>
+        <item>1f3a1</item>
+        <item>1f3a2</item>
+        <item>1f3a0</item>
+        <item>1f3aa</item>
+        <item>1f3a8</item>
+        <item>1f3ac</item>
+        <item>1f3a5</item>
+        <item>1f4f7</item>
+        <item>1f4f9</item>
+        <item>1f3a6</item>
+        <item>1f3ad</item>
+        <item>1f3ab</item>
+        <item>1f3ae</item>
+        <item>1f3b2</item>
+        <item>1f3b0</item>
+        <item>1f0cf</item>
+        <item>1f3b4</item>
+        <item>1f004</item>
+        <item>1f3af</item>
+        <item>1f4fa</item>
+        <item>1f4fb</item>
+        <item>1f4c0</item>
+        <item>1f4fc</item>
+        <item>1f3a7</item>
+        <item>1f3a4</item>
+        <item>1f3b5</item>
+        <item>1f3b6</item>
+        <item>1f3bc</item>
+        <item>1f3bb</item>
+        <item>1f3b9</item>
+        <item>1f3b7</item>
+        <item>1f3ba</item>
+        <item>1f3b8</item>
+        <item>303d</item>
+    </array>
+    <array
+        name="emoji_places"
+        format="string"
+    >
+        <item>1f3e0</item>
+        <item>1f3e1</item>
+        <item>1f3e2</item>
+        <item>1f3e3</item>
+        <item>1f3e4</item>
+        <item>1f3e5</item>
+        <item>1f3e6</item>
+        <item>1f3e7</item>
+        <item>1f3e8</item>
+        <item>1f3e9</item>
+        <item>1f3ea</item>
+        <item>1f3eb</item>
+        <item>26ea</item>
+        <item>26f2</item>
+        <item>1f3ec</item>
+        <item>1f3ef</item>
+        <item>1f3f0</item>
+        <item>1f3ed</item>
+        <item>1f5fb</item>
+        <item>1f5fc</item>
+        <item>1f5fd</item>
+        <item>1f5fe</item>
+        <item>1f5ff</item>
+        <item>2693</item>
+        <item>1f3ee</item>
+        <item>1f488</item>
+        <item>1f527</item>
+        <item>1f528</item>
+        <item>1f529</item>
+        <item>1f6bf</item>
+        <item>1f6c1</item>
+        <item>1f6c0</item>
+        <item>1f6bd</item>
+        <item>1f6be</item>
+        <item>1f3bd</item>
+        <item>1f3a3</item>
+        <item>1f3b1</item>
+        <item>1f3b3</item>
+        <item>26be</item>
+        <item>26f3</item>
+        <item>1f3be</item>
+        <item>26bd</item>
+        <item>1f3bf</item>
+        <item>1f3c0</item>
+        <item>1f3c1</item>
+        <item>1f3c2</item>
+        <item>1f3c3</item>
+        <item>1f3c4</item>
+        <item>1f3c6</item>
+        <item>1f3c7</item>
+        <item>1f40e</item>
+        <item>1f3c8</item>
+        <item>1f3c9</item>
+        <item>1f3ca</item>
+        <item>1f682</item>
+        <item>1f683</item>
+        <item>1f684</item>
+        <item>1f685</item>
+        <item>1f686</item>
+        <item>1f687</item>
+        <item>24c2</item>
+        <item>1f688</item>
+        <item>1f68a</item>
+        <item>1f68b</item>
+        <item>1f68c</item>
+        <item>1f68d</item>
+        <item>1f68e</item>
+        <item>1f68f</item>
+        <item>1f690</item>
+        <item>1f691</item>
+        <item>1f692</item>
+        <item>1f693</item>
+        <item>1f694</item>
+        <item>1f695</item>
+        <item>1f696</item>
+        <item>1f697</item>
+        <item>1f698</item>
+        <item>1f699</item>
+        <item>1f69a</item>
+        <item>1f69b</item>
+        <item>1f69c</item>
+        <item>1f69d</item>
+        <item>1f69e</item>
+        <item>1f69f</item>
+        <item>1f6a0</item>
+        <item>1f6a1</item>
+        <item>1f6a2</item>
+        <item>1f6a3</item>
+        <item>1f681</item>
+        <item>2708</item>
+        <item>1f6c2</item>
+        <item>1f6c3</item>
+        <item>1f6c4</item>
+        <item>1f6c5</item>
+        <item>26f5</item>
+        <item>1f6b2</item>
+        <item>1f6b3</item>
+        <item>1f6b4</item>
+        <item>1f6b5</item>
+        <item>1f6b7</item>
+        <item>1f6b8</item>
+        <item>1f689</item>
+        <item>1f680</item>
+        <item>1f6a4</item>
+        <item>1f6b6</item>
+        <item>26fd</item>
+        <item>1f17f</item>
+        <item>1f6a5</item>
+        <item>1f6a6</item>
+        <item>1f6a7</item>
+        <item>1f6a8</item>
+        <item>2668</item>
+        <item>1f48c</item>
+        <item>1f48d</item>
+        <item>1f48e</item>
+        <item>1f490</item>
+        <item>1f492</item>
+        <item>fe4e5|1f1ef,1f1f5</item>
+        <item>fe4e6|1f1fa,1f1f8</item>
+        <item>fe4e7|1f1eb,1f1f7</item>
+        <item>fe4e8|1f1e9,1f1ea</item>
+        <item>fe4e9|1f1ee,1f1f9</item>
+        <item>fe4ea|1f1ec,1f1e7</item>
+        <item>fe4eb|1f1ea,1f1f8</item>
+        <item>fe4ec|1f1f7,1f1fa</item>
+        <item>fe4ed|1f1e8,1f1f3</item>
+        <item>fe4ee|1f1f0,1f1f7</item>
+    </array>
+    <array
+        name="emoji_emoticons"
+        format="string"
+    >
+        <item>=-O</item>
+        <item>:-P</item>
+        <item>;-)</item>
+        <item>:-(</item>
+        <item>:-)</item>
+        <item>:-!</item>
+        <item>:-$</item>
+        <item>B-)</item>
+        <item>:O</item>
+        <item>:-*</item>
+        <item>:-D</item>
+        <item>:\'(</item>
+        <item>:-\\</item>
+        <item>O:-)</item>
+        <item>:-[</item>
+    </array>
+</resources>
diff --git a/java/res/values-vi/strings.xml b/java/res/values-vi/strings.xml
index 67b140e..e08d1c6 100644
--- a/java/res/values-vi/strings.xml
+++ b/java/res/values-vi/strings.xml
@@ -143,6 +143,7 @@
     <string name="subtype_with_layout_en_GB" msgid="2179097748724725906">"Tiếng Anh (Anh) (<xliff:g id="LAYOUT">%s</xliff:g>)"</string>
     <string name="subtype_with_layout_en_US" msgid="1362581347576714579">"Tiếng Anh (Mỹ) (<xliff:g id="LAYOUT">%s</xliff:g>)"</string>
     <string name="subtype_with_layout_es_US" msgid="6261791057007890189">"Tiếng Tây Ban Nha (Mỹ) (<xliff:g id="LAYOUT">%s</xliff:g>)"</string>
+    <string name="subtype_nepali_traditional" msgid="9032247506728040447">"<xliff:g id="LANGUAGE">%s</xliff:g> (Truyền thống)"</string>
     <string name="subtype_no_language" msgid="7137390094240139495">"Không ngôn ngữ nào (Bảng chữ cái)"</string>
     <string name="subtype_no_language_qwerty" msgid="244337630616742604">"Bảng chữ cái (QWERTY)"</string>
     <string name="subtype_no_language_qwertz" msgid="443066912507547976">"Bảng chữ cái (QWERTZ)"</string>
@@ -150,6 +151,7 @@
     <string name="subtype_no_language_dvorak" msgid="1564494667584718094">"Bảng chữ cái (Dvorak)"</string>
     <string name="subtype_no_language_colemak" msgid="5837418400010302623">"Bảng chữ cái (Colemak)"</string>
     <string name="subtype_no_language_pcqwerty" msgid="5354918232046200018">"Bảng chữ cái (PC)"</string>
+    <string name="subtype_emoji" msgid="7483586578074549196">"Biểu tượng cảm xúc"</string>
     <string name="custom_input_styles_title" msgid="8429952441821251512">"Kiểu nhập tùy chỉnh"</string>
     <string name="add_style" msgid="6163126614514489951">"Thêm kiểu"</string>
     <string name="add" msgid="8299699805688017798">"Thêm"</string>
diff --git a/java/res/values-zh-rCN/strings.xml b/java/res/values-zh-rCN/strings.xml
index 461d326..5980cbf 100644
--- a/java/res/values-zh-rCN/strings.xml
+++ b/java/res/values-zh-rCN/strings.xml
@@ -143,6 +143,7 @@
     <string name="subtype_with_layout_en_GB" msgid="2179097748724725906">"英语(英国)(<xliff:g id="LAYOUT">%s</xliff:g>)"</string>
     <string name="subtype_with_layout_en_US" msgid="1362581347576714579">"英语(美国)(<xliff:g id="LAYOUT">%s</xliff:g>)"</string>
     <string name="subtype_with_layout_es_US" msgid="6261791057007890189">"西班牙语(美国)(<xliff:g id="LAYOUT">%s</xliff:g>)"</string>
+    <string name="subtype_nepali_traditional" msgid="9032247506728040447">"<xliff:g id="LANGUAGE">%s</xliff:g>(传统)"</string>
     <string name="subtype_no_language" msgid="7137390094240139495">"无语言(字母)"</string>
     <string name="subtype_no_language_qwerty" msgid="244337630616742604">"字母 (QWERTY)"</string>
     <string name="subtype_no_language_qwertz" msgid="443066912507547976">"字母 (QWERTZ)"</string>
@@ -150,6 +151,7 @@
     <string name="subtype_no_language_dvorak" msgid="1564494667584718094">"字母 (Dvorak)"</string>
     <string name="subtype_no_language_colemak" msgid="5837418400010302623">"字母 (Colemak)"</string>
     <string name="subtype_no_language_pcqwerty" msgid="5354918232046200018">"字母 (PC)"</string>
+    <string name="subtype_emoji" msgid="7483586578074549196">"表情符号"</string>
     <string name="custom_input_styles_title" msgid="8429952441821251512">"自定义输入风格"</string>
     <string name="add_style" msgid="6163126614514489951">"添加样式"</string>
     <string name="add" msgid="8299699805688017798">"添加"</string>
diff --git a/java/res/values-zh-rHK/strings-appname.xml b/java/res/values-zh-rHK/strings-appname.xml
deleted file mode 100644
index 8761a98..0000000
--- a/java/res/values-zh-rHK/strings-appname.xml
+++ /dev/null
@@ -1,27 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!-- 
-/*
-**
-** Copyright 2013, 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:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="english_ime_name" msgid="5940510615957428904">"Android 鍵盤 (AOSP)"</string>
-    <string name="spell_checker_service_name" msgid="1254221805440242662">"Android 拼字檢查工具 (AOSP)"</string>
-    <string name="english_ime_settings" msgid="5760361067176802794">"Android 鍵盤設定 (AOSP)"</string>
-    <string name="android_spell_checker_settings" msgid="6123949487832861885">"Android 拼字檢查工具設定 (AOSP)"</string>
-</resources>
diff --git a/java/res/values-zh-rHK/strings.xml b/java/res/values-zh-rHK/strings.xml
deleted file mode 100644
index c68e55d..0000000
--- a/java/res/values-zh-rHK/strings.xml
+++ /dev/null
@@ -1,242 +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.
-*/
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="english_ime_input_options" msgid="3909945612939668554">"輸入選項"</string>
-    <string name="english_ime_research_log" msgid="8492602295696577851">"研究記錄指令"</string>
-    <string name="use_contacts_for_spellchecking_option_title" msgid="5374120998125353898">"查找聯絡人姓名"</string>
-    <string name="use_contacts_for_spellchecking_option_summary" msgid="8754413382543307713">"拼字檢查程式使用您的聯絡人名單中的各項記錄"</string>
-    <string name="vibrate_on_keypress" msgid="5258079494276955460">"按鍵時震動"</string>
-    <string name="sound_on_keypress" msgid="6093592297198243644">"按鍵時播放音效"</string>
-    <string name="popup_on_keypress" msgid="123894815723512944">"按鍵時顯示彈出式視窗"</string>
-    <string name="general_category" msgid="1859088467017573195">"一般設定"</string>
-    <string name="correction_category" msgid="2236750915056607613">"文字更正"</string>
-    <string name="gesture_typing_category" msgid="497263612130532630">"手勢輸入"</string>
-    <string name="misc_category" msgid="6894192814868233453">"其他選項"</string>
-    <string name="advanced_settings" msgid="362895144495591463">"進階設定"</string>
-    <string name="advanced_settings_summary" msgid="4487980456152830271">"專家選項"</string>
-    <string name="include_other_imes_in_language_switch_list" msgid="4533689960308565519">"切換至其他輸入法"</string>
-    <string name="include_other_imes_in_language_switch_list_summary" msgid="840637129103317635">"語言切換鍵包括其他輸入法"</string>
-    <string name="show_language_switch_key" msgid="5915478828318774384">"語言切換鍵"</string>
-    <string name="show_language_switch_key_summary" msgid="7343403647474265713">"在啟用多種輸入語言時顯示"</string>
-    <string name="sliding_key_input_preview" msgid="6604262359510068370">"顯示滑動指示器"</string>
-    <string name="sliding_key_input_preview_summary" msgid="6340524345729093886">"從 Shift 鍵或符號鍵開始滑動時顯示視覺提示"</string>
-    <string name="key_preview_popup_dismiss_delay" msgid="6213164897443068248">"關閉彈出式鍵盤的延遲時間"</string>
-    <string name="key_preview_popup_dismiss_no_delay" msgid="2096123151571458064">"不延遲"</string>
-    <string name="key_preview_popup_dismiss_default_delay" msgid="2166964333903906734">"預設"</string>
-    <string name="abbreviation_unit_milliseconds" msgid="8700286094028323363">"<xliff:g id="MILLISECONDS">%s</xliff:g> 毫秒"</string>
-    <string name="settings_system_default" msgid="6268225104743331821">"系統預設"</string>
-    <string name="use_contacts_dict" msgid="4435317977804180815">"建議聯絡人名稱"</string>
-    <string name="use_contacts_dict_summary" msgid="6599983334507879959">"使用「聯絡人」的名稱提供建議與修正"</string>
-    <string name="use_double_space_period" msgid="8781529969425082860">"按兩下空格鍵插入句號"</string>
-    <string name="use_double_space_period_summary" msgid="6532892187247952799">"只要輕按兩下空格鍵,即可插入句號並在後面加上一個空格"</string>
-    <string name="auto_cap" msgid="1719746674854628252">"自動大寫"</string>
-    <string name="auto_cap_summary" msgid="7934452761022946874">"每句首個字詞大寫"</string>
-    <string name="edit_personal_dictionary" msgid="3996910038952940420">"個人字典"</string>
-    <string name="configure_dictionaries_title" msgid="4238652338556902049">"附加字典"</string>
-    <string name="main_dictionary" msgid="4798763781818361168">"主要字典"</string>
-    <string name="prefs_show_suggestions" msgid="8026799663445531637">"顯示更正建議"</string>
-    <string name="prefs_show_suggestions_summary" msgid="1583132279498502825">"輸入時顯示建議字詞"</string>
-    <string name="prefs_suggestion_visibility_show_name" msgid="3219916594067551303">"永遠顯示"</string>
-    <string name="prefs_suggestion_visibility_show_only_portrait_name" msgid="3859783767435239118">"以垂直模式顯示"</string>
-    <string name="prefs_suggestion_visibility_hide_name" msgid="6309143926422234673">"永遠隱藏"</string>
-    <string name="prefs_block_potentially_offensive_title" msgid="5078480071057408934">"封鎖令人反感的字詞"</string>
-    <string name="prefs_block_potentially_offensive_summary" msgid="2371835479734991364">"不建議使用可能令人反感的字詞"</string>
-    <string name="auto_correction" msgid="7630720885194996950">"自動更正"</string>
-    <string name="auto_correction_summary" msgid="5625751551134658006">"自動插入空白鍵和標點符號鍵盤,以修正拼字錯誤"</string>
-    <string name="auto_correction_threshold_mode_off" msgid="8470882665417944026">"關閉"</string>
-    <string name="auto_correction_threshold_mode_modest" msgid="8788366690620799097">"普通模式"</string>
-    <string name="auto_correction_threshold_mode_aggressive" msgid="7319007299148899623">"加強模式"</string>
-    <string name="auto_correction_threshold_mode_very_aggressive" msgid="1853309024129480416">"極度加強模式"</string>
-    <string name="bigram_prediction" msgid="1084449187723948550">"建議下一個字詞"</string>
-    <string name="bigram_prediction_summary" msgid="3896362682751109677">"根據前一個字詞提出建議"</string>
-    <string name="gesture_input" msgid="826951152254563827">"啟用手勢輸入"</string>
-    <string name="gesture_input_summary" msgid="9180350639305731231">"透過滑動手指寫出字母來輸入字詞"</string>
-    <string name="gesture_preview_trail" msgid="3802333369335722221">"顯示手勢軌跡"</string>
-    <string name="gesture_floating_preview_text" msgid="4443240334739381053">"動態浮動預覽"</string>
-    <string name="gesture_floating_preview_text_summary" msgid="4472696213996203533">"在啟用手勢輸入時顯示建議的字詞"</string>
-    <string name="added_word" msgid="8993883354622484372">"<xliff:g id="WORD">%s</xliff:g>:已儲存"</string>
-    <string name="label_go_key" msgid="1635148082137219148">"開始"</string>
-    <string name="label_next_key" msgid="362972844525672568">"下一步"</string>
-    <string name="label_previous_key" msgid="1211868118071386787">"上一步"</string>
-    <string name="label_done_key" msgid="2441578748772529288">"完成"</string>
-    <string name="label_send_key" msgid="2815056534433717444">"發送"</string>
-    <string name="label_pause_key" msgid="181098308428035340">"暫停"</string>
-    <string name="label_wait_key" msgid="6402152600878093134">"等候"</string>
-    <string name="spoken_use_headphones" msgid="896961781287283493">"插上耳機即可聽到系統朗讀密碼鍵。"</string>
-    <string name="spoken_current_text_is" msgid="2485723011272583845">"目前文字為 %s"</string>
-    <string name="spoken_no_text_entered" msgid="7479685225597344496">"未輸入文字"</string>
-    <string name="spoken_description_unknown" msgid="3197434010402179157">"按鍵代碼 %d"</string>
-    <string name="spoken_description_shift" msgid="244197883292549308">"Shift 鍵"</string>
-    <string name="spoken_description_shift_shifted" msgid="1681877323344195035">"Shift 鍵已開啟 (輕按即可停用)"</string>
-    <string name="spoken_description_caps_lock" msgid="3276478269526304432">"大寫鎖定已開啟 (輕按即可停用)"</string>
-    <string name="spoken_description_delete" msgid="8740376944276199801">"刪除"</string>
-    <string name="spoken_description_to_symbol" msgid="5486340107500448969">"符號"</string>
-    <string name="spoken_description_to_alpha" msgid="23129338819771807">"字母"</string>
-    <string name="spoken_description_to_numeric" msgid="591752092685161732">"數字"</string>
-    <string name="spoken_description_settings" msgid="4627462689603838099">"設定"</string>
-    <string name="spoken_description_tab" msgid="2667716002663482248">"Tab 鍵"</string>
-    <string name="spoken_description_space" msgid="2582521050049860859">"空白鍵"</string>
-    <string name="spoken_description_mic" msgid="615536748882611950">"語音輸入"</string>
-    <string name="spoken_description_smiley" msgid="2256309826200113918">"笑臉"</string>
-    <string name="spoken_description_return" msgid="8178083177238315647">"Return 鍵"</string>
-    <string name="spoken_description_search" msgid="1247236163755920808">"搜尋"</string>
-    <string name="spoken_description_dot" msgid="40711082435231673">"點"</string>
-    <string name="spoken_description_language_switch" msgid="5507091328222331316">"切換語言"</string>
-    <string name="spoken_description_action_next" msgid="8636078276664150324">"下一步"</string>
-    <string name="spoken_description_action_previous" msgid="800872415009336208">"上一步"</string>
-    <string name="spoken_description_shiftmode_on" msgid="5700440798609574589">"Shift 鍵已啟用"</string>
-    <string name="spoken_description_shiftmode_locked" msgid="593175803181701830">"大寫鎖定已啟用"</string>
-    <string name="spoken_description_shiftmode_off" msgid="657219998449174808">"Shift 鍵已停用"</string>
-    <string name="spoken_description_mode_symbol" msgid="7183343879909747642">"符號模式"</string>
-    <string name="spoken_description_mode_alpha" msgid="3528307674390156956">"字母模式"</string>
-    <string name="spoken_description_mode_phone" msgid="6520207943132026264">"撥號模式"</string>
-    <string name="spoken_description_mode_phone_shift" msgid="5499629753962641227">"符號撥號模式"</string>
-    <string name="announce_keyboard_hidden" msgid="8718927835531429807">"鍵盤已隱藏"</string>
-    <string name="announce_keyboard_mode" msgid="4729081055438508321">"目前顯示的是<xliff:g id="MODE">%s</xliff:g>鍵盤"</string>
-    <string name="keyboard_mode_date" msgid="3137520166817128102">"日期"</string>
-    <string name="keyboard_mode_date_time" msgid="339593358488851072">"日期和時間"</string>
-    <string name="keyboard_mode_email" msgid="6216248078128294262">"電郵"</string>
-    <string name="keyboard_mode_im" msgid="1137405089766557048">"短訊"</string>
-    <string name="keyboard_mode_number" msgid="7991623440699957069">"數字"</string>
-    <string name="keyboard_mode_phone" msgid="6851627527401433229">"電話"</string>
-    <string name="keyboard_mode_text" msgid="6479436687899701619">"文字"</string>
-    <string name="keyboard_mode_time" msgid="4381856885582143277">"時間"</string>
-    <string name="keyboard_mode_url" msgid="1519819835514911218">"網址"</string>
-    <string name="voice_input" msgid="3583258583521397548">"語音輸入鍵"</string>
-    <string name="voice_input_modes_main_keyboard" msgid="3360660341121083174">"於主鍵盤"</string>
-    <string name="voice_input_modes_symbols_keyboard" msgid="7203213240786084067">"符號鍵盤上"</string>
-    <string name="voice_input_modes_off" msgid="3745699748218082014">"關閉"</string>
-    <string name="voice_input_modes_summary_main_keyboard" msgid="6586544292900314339">"主鍵盤上的麥克風"</string>
-    <string name="voice_input_modes_summary_symbols_keyboard" msgid="5233725927281932391">"符號鍵盤上的麥克風"</string>
-    <string name="voice_input_modes_summary_off" msgid="63875609591897607">"語音輸入已停用"</string>
-    <string name="configure_input_method" msgid="373356270290742459">"設定輸入法"</string>
-    <string name="language_selection_title" msgid="1651299598555326750">"輸入語言"</string>
-    <string name="send_feedback" msgid="1780431884109392046">"傳送意見"</string>
-    <string name="select_language" msgid="3693815588777926848">"輸入語言"</string>
-    <string name="hint_add_to_dictionary" msgid="573678656946085380">"再次輕觸即可儲存"</string>
-    <string name="has_dictionary" msgid="6071847973466625007">"可使用字典"</string>
-    <string name="prefs_enable_log" msgid="6620424505072963557">"啟用用戶意見反映"</string>
-    <string name="prefs_description_log" msgid="7525225584555429211">"自動傳送使用統計資料和當機報告,協助改良這個輸入法編輯器"</string>
-    <string name="keyboard_layout" msgid="8451164783510487501">"鍵盤主題"</string>
-    <string name="subtype_en_GB" msgid="88170601942311355">"英文 (英國)"</string>
-    <string name="subtype_en_US" msgid="6160452336634534239">"英文 (美國)"</string>
-    <string name="subtype_es_US" msgid="5583145191430180200">"西班牙文 (美國)"</string>
-    <string name="subtype_with_layout_en_GB" msgid="2179097748724725906">"英文 (英國) (<xliff:g id="LAYOUT">%s</xliff:g>)"</string>
-    <string name="subtype_with_layout_en_US" msgid="1362581347576714579">"英文 (美國) (<xliff:g id="LAYOUT">%s</xliff:g>)"</string>
-    <string name="subtype_with_layout_es_US" msgid="6261791057007890189">"西班牙文 (美國) (<xliff:g id="LAYOUT">%s</xliff:g>)"</string>
-    <string name="subtype_no_language" msgid="7137390094240139495">"無語言 (字母)"</string>
-    <string name="subtype_no_language_qwerty" msgid="244337630616742604">"字母 (QWERTY)"</string>
-    <string name="subtype_no_language_qwertz" msgid="443066912507547976">"字母 (QWERTZ)"</string>
-    <string name="subtype_no_language_azerty" msgid="8144348527575640087">"字母 (AZERTY)"</string>
-    <string name="subtype_no_language_dvorak" msgid="1564494667584718094">"字母 (Dvorak)"</string>
-    <string name="subtype_no_language_colemak" msgid="5837418400010302623">"字母 (Colemak)"</string>
-    <string name="subtype_no_language_pcqwerty" msgid="5354918232046200018">"字母 (PC)"</string>
-    <string name="custom_input_styles_title" msgid="8429952441821251512">"自訂輸入樣式"</string>
-    <string name="add_style" msgid="6163126614514489951">"新增樣式"</string>
-    <string name="add" msgid="8299699805688017798">"新增"</string>
-    <string name="remove" msgid="4486081658752944606">"移除"</string>
-    <string name="save" msgid="7646738597196767214">"儲存"</string>
-    <string name="subtype_locale" msgid="8576443440738143764">"語言"</string>
-    <string name="keyboard_layout_set" msgid="4309233698194565609">"配置"</string>
-    <string name="custom_input_style_note_message" msgid="8826731320846363423">"您必須先啟用自訂輸入樣式,才能開始使用。您要立即啟用嗎?"</string>
-    <string name="enable" msgid="5031294444630523247">"啟用"</string>
-    <string name="not_now" msgid="6172462888202790482">"暫時不要"</string>
-    <string name="custom_input_style_already_exists" msgid="8008728952215449707">"已存在相同的輸入樣式:<xliff:g id="INPUT_STYLE_NAME">%s</xliff:g>"</string>
-    <string name="prefs_usability_study_mode" msgid="1261130555134595254">"可用性研究模式"</string>
-    <string name="prefs_key_longpress_timeout_settings" msgid="6102240298932897873">"長按鍵延遲"</string>
-    <string name="prefs_keypress_vibration_duration_settings" msgid="7918341459947439226">"按鍵震動時間"</string>
-    <string name="prefs_keypress_sound_volume_settings" msgid="6027007337036891623">"按鍵音量"</string>
-    <string name="prefs_read_external_dictionary" msgid="2588931418575013067">"讀取外部字典檔案"</string>
-    <string name="read_external_dictionary_no_files_message" msgid="4947420942224623792">"「下載」資料夾中沒有任何字典檔案"</string>
-    <string name="read_external_dictionary_multiple_files_title" msgid="7637749044265808628">"選取要安裝的字典檔案"</string>
-    <string name="read_external_dictionary_confirm_install_message" msgid="6898610163768980870">"準備好要為<xliff:g id="LOCALE_NAME">%s</xliff:g>版本安裝這個檔案嗎?"</string>
-    <string name="error" msgid="8940763624668513648">"發生錯誤"</string>
-    <string name="button_default" msgid="3988017840431881491">"預設"</string>
-    <string name="setup_welcome_title" msgid="6112821709832031715">"歡迎使用「<xliff:g id="APPLICATION_NAME">%s</xliff:g>」"</string>
-    <string name="setup_welcome_additional_description" msgid="8150252008545768953">"配備觸控輸入功能"</string>
-    <string name="setup_start_action" msgid="8936036460897347708">"開始"</string>
-    <string name="setup_next_action" msgid="371821437915144603">"下一步"</string>
-    <string name="setup_steps_title" msgid="6400373034871816182">"設定「<xliff:g id="APPLICATION_NAME">%s</xliff:g>」"</string>
-    <string name="setup_step1_title" msgid="3147967630253462315">"啟用「<xliff:g id="APPLICATION_NAME">%s</xliff:g>」"</string>
-    <string name="setup_step1_instruction" msgid="2578631936624637241">"請在語言與輸入設定中勾選「<xliff:g id="APPLICATION_NAME">%s</xliff:g>」,授權這個應用程式在您的裝置上執行。"</string>
-    <string name="setup_step1_finished_instruction" msgid="10761482004957994">"您已在 [語言與輸入設定] 中啟用「<xliff:g id="APPLICATION_NAME">%s</xliff:g>」。這個步驟已完成,可繼續下一個步驟了!"</string>
-    <string name="setup_step1_action" msgid="4366513534999901728">"在設定中啟用"</string>
-    <string name="setup_step2_title" msgid="6860725447906690594">"切換至「<xliff:g id="APPLICATION_NAME">%s</xliff:g>」"</string>
-    <string name="setup_step2_instruction" msgid="9141481964870023336">"接著,請選取「<xliff:g id="APPLICATION_NAME">%s</xliff:g>」作為目前使用的文字輸入方法。"</string>
-    <string name="setup_step2_action" msgid="1660330307159824337">"切換輸入方法"</string>
-    <string name="setup_step3_title" msgid="3154757183631490281">"恭喜,一切就緒!"</string>
-    <string name="setup_step3_instruction" msgid="8025981829605426000">"現在,您可以在所有最愛的應用程式中使用「<xliff:g id="APPLICATION_NAME">%s</xliff:g>」輸入文字。"</string>
-    <string name="setup_step3_action" msgid="600879797256942259">"設定其他語言"</string>
-    <string name="setup_finish_action" msgid="276559243409465389">"完成"</string>
-    <string name="show_setup_wizard_icon" msgid="5008028590593710830">"顯示應用程式圖示"</string>
-    <string name="show_setup_wizard_icon_summary" msgid="4119998322536880213">"在啟動器中顯示應用程式圖示"</string>
-    <string name="app_name" msgid="6320102637491234792">"字典供應商"</string>
-    <string name="dictionary_provider_name" msgid="3027315045397363079">"字典供應商"</string>
-    <string name="dictionary_service_name" msgid="6237472350693511448">"字典服務"</string>
-    <string name="download_description" msgid="6014835283119198591">"字典更新資訊"</string>
-    <string name="dictionary_settings_title" msgid="8091417676045693313">"附加字典"</string>
-    <string name="dictionary_install_over_metered_network_prompt" msgid="3587517870006332980">"可使用字典"</string>
-    <string name="dictionary_settings_summary" msgid="5305694987799824349">"字典設定"</string>
-    <string name="user_dictionaries" msgid="3582332055892252845">"用戶字典"</string>
-    <string name="default_user_dict_pref_name" msgid="1625055720489280530">"用戶字典"</string>
-    <string name="dictionary_available" msgid="4728975345815214218">"可使用字典"</string>
-    <string name="dictionary_downloading" msgid="2982650524622620983">"目前下載中"</string>
-    <string name="dictionary_installed" msgid="8081558343559342962">"已安裝"</string>
-    <string name="dictionary_disabled" msgid="8950383219564621762">"已安裝,但已停用"</string>
-    <string name="cannot_connect_to_dict_service" msgid="9216933695765732398">"連線至字典服務時發生問題"</string>
-    <string name="no_dictionaries_available" msgid="8039920716566132611">"沒有可用的字典"</string>
-    <string name="check_for_updates_now" msgid="8087688440916388581">"重新整理"</string>
-    <string name="last_update" msgid="730467549913588780">"上次更新日期"</string>
-    <string name="message_updating" msgid="4457761393932375219">"正在查看更新"</string>
-    <string name="message_loading" msgid="8689096636874758814">"正在載入..."</string>
-    <string name="main_dict_description" msgid="3072821352793492143">"主要字典"</string>
-    <string name="cancel" msgid="6830980399865683324">"取消"</string>
-    <string name="install_dict" msgid="180852772562189365">"安裝"</string>
-    <string name="cancel_download_dict" msgid="7843340278507019303">"取消"</string>
-    <string name="delete_dict" msgid="756853268088330054">"刪除"</string>
-    <string name="should_download_over_metered_prompt" msgid="2878629598667658845">"您的流動裝置所選取的語言現有字典可供使用。&lt;br/&gt;建議您&lt;b&gt;下載&lt;/b&gt;<xliff:g id="LANGUAGE">%1$s</xliff:g>字典,讓您輸入時更方便。&lt;br/&gt;&lt;br/&gt;經由 3G 網絡下載需時一兩分鐘。如果您未使用&lt;b&gt;無限上網計劃&lt;/b&gt;,可能須另外付費。&lt;br/&gt;如果您不確定自己使用哪種上網計劃,建議您在連接 Wi-Fi 網絡後才開始自動下載。&lt;br/&gt;&lt;br/&gt;提示:您可以前往流動裝置的 [設定] &lt;b&gt;&lt;/b&gt;選單,透過其中的 [語言和輸入] &lt;b&gt;&lt;/b&gt;下載和移除字典。"</string>
-    <string name="download_over_metered" msgid="1643065851159409546">"立即下載 (<xliff:g id="SIZE_IN_MEGABYTES">%1$.1f</xliff:g> MB)"</string>
-    <string name="do_not_download_over_metered" msgid="2176209579313941583">"經由 Wi-Fi 下載"</string>
-    <string name="dict_available_notification_title" msgid="6514288591959117288">"可使用<xliff:g id="LANGUAGE">%1$s</xliff:g>字典"</string>
-    <string name="dict_available_notification_description" msgid="1075194169443163487">"按下即可查看並下載"</string>
-    <string name="toast_downloading_suggestions" msgid="1313027353588566660">"下載中:很快就能提供<xliff:g id="LANGUAGE">%1$s</xliff:g>字詞建議。"</string>
-    <string name="version_text" msgid="2715354215568469385">"版本 <xliff:g id="VERSION_NUMBER">%1$s</xliff:g>"</string>
-    <string name="user_dict_settings_add_menu_title" msgid="1254195365689387076">"新增"</string>
-    <string name="user_dict_settings_add_dialog_title" msgid="4096700390211748168">"加入字典"</string>
-    <string name="user_dict_settings_add_screen_title" msgid="5818914331629278758">"詞組"</string>
-    <string name="user_dict_settings_add_dialog_more_options" msgid="5671682004887093112">"更多選項"</string>
-    <string name="user_dict_settings_add_dialog_less_options" msgid="2716586567241724126">"較少選項"</string>
-    <string name="user_dict_settings_add_dialog_confirm" msgid="4703129507388332950">"確定"</string>
-    <string name="user_dict_settings_add_word_option_name" msgid="6665558053408962865">"字詞:"</string>
-    <string name="user_dict_settings_add_shortcut_option_name" msgid="3094731590655523777">"快速鍵:"</string>
-    <string name="user_dict_settings_add_locale_option_name" msgid="4738643440987277705">"語言:"</string>
-    <string name="user_dict_settings_add_word_hint" msgid="4902434148985906707">"輸入字詞"</string>
-    <string name="user_dict_settings_add_shortcut_hint" msgid="2265453012555060178">"自選快速鍵"</string>
-    <string name="user_dict_settings_edit_dialog_title" msgid="3765774633869590352">"編輯字詞"</string>
-    <string name="user_dict_settings_context_menu_edit_title" msgid="6812255903472456302">"編輯"</string>
-    <string name="user_dict_settings_context_menu_delete_title" msgid="8142932447689461181">"刪除"</string>
-    <string name="user_dict_settings_empty_text" msgid="558499587532668203">"您的用戶字典中沒有任何字詞。輕觸 [新增] (+) 按鈕即可新增字詞。"</string>
-    <string name="user_dict_settings_all_languages" msgid="8276126583216298886">"所有語言"</string>
-    <string name="user_dict_settings_more_languages" msgid="7131268499685180461">"更多語言..."</string>
-    <string name="user_dict_settings_delete" msgid="110413335187193859">"刪除"</string>
-    <string name="user_dict_fast_scroll_alphabet" msgid="5431919401558285473">" ABCDEFGHIJKLMNOPQRSTUVWXYZ"</string>
-</resources>
diff --git a/java/res/values-zh-rTW/strings.xml b/java/res/values-zh-rTW/strings.xml
index 3fa5cdd..b14a01a 100644
--- a/java/res/values-zh-rTW/strings.xml
+++ b/java/res/values-zh-rTW/strings.xml
@@ -143,6 +143,7 @@
     <string name="subtype_with_layout_en_GB" msgid="2179097748724725906">"英文 (英國) (<xliff:g id="LAYOUT">%s</xliff:g>)"</string>
     <string name="subtype_with_layout_en_US" msgid="1362581347576714579">"英文 (美國) (<xliff:g id="LAYOUT">%s</xliff:g>)"</string>
     <string name="subtype_with_layout_es_US" msgid="6261791057007890189">"西班牙文 (美國) (<xliff:g id="LAYOUT">%s</xliff:g>)"</string>
+    <string name="subtype_nepali_traditional" msgid="9032247506728040447">"<xliff:g id="LANGUAGE">%s</xliff:g> (傳統)"</string>
     <string name="subtype_no_language" msgid="7137390094240139495">"無語言 (字母)"</string>
     <string name="subtype_no_language_qwerty" msgid="244337630616742604">"字母 (QWERTY)"</string>
     <string name="subtype_no_language_qwertz" msgid="443066912507547976">"字母 (QWERTZ)"</string>
@@ -150,6 +151,7 @@
     <string name="subtype_no_language_dvorak" msgid="1564494667584718094">"字母 (Dvorak)"</string>
     <string name="subtype_no_language_colemak" msgid="5837418400010302623">"字母 (Colemak)"</string>
     <string name="subtype_no_language_pcqwerty" msgid="5354918232046200018">"字母 (PC)"</string>
+    <string name="subtype_emoji" msgid="7483586578074549196">"表情符號"</string>
     <string name="custom_input_styles_title" msgid="8429952441821251512">"自訂輸入樣式"</string>
     <string name="add_style" msgid="6163126614514489951">"新增樣式"</string>
     <string name="add" msgid="8299699805688017798">"新增"</string>
diff --git a/java/res/values-zu/strings.xml b/java/res/values-zu/strings.xml
index 38a5c27..9ed88a0 100644
--- a/java/res/values-zu/strings.xml
+++ b/java/res/values-zu/strings.xml
@@ -143,6 +143,7 @@
     <string name="subtype_with_layout_en_GB" msgid="2179097748724725906">"I-English (UK) (<xliff:g id="LAYOUT">%s</xliff:g>)"</string>
     <string name="subtype_with_layout_en_US" msgid="1362581347576714579">"I-English (US) (<xliff:g id="LAYOUT">%s</xliff:g>)"</string>
     <string name="subtype_with_layout_es_US" msgid="6261791057007890189">"I-Spanish (US) (<xliff:g id="LAYOUT">%s</xliff:g>)"</string>
+    <string name="subtype_nepali_traditional" msgid="9032247506728040447">"<xliff:g id="LANGUAGE">%s</xliff:g> (Ezosiko)"</string>
     <string name="subtype_no_language" msgid="7137390094240139495">"Alikho ulimi (Alfabhethi)"</string>
     <string name="subtype_no_language_qwerty" msgid="244337630616742604">"Alfabhethi (QWERTY)"</string>
     <string name="subtype_no_language_qwertz" msgid="443066912507547976">"Alfabhethi (QWERTZ)"</string>
@@ -150,6 +151,7 @@
     <string name="subtype_no_language_dvorak" msgid="1564494667584718094">"Alfabhethi (Dvorak)"</string>
     <string name="subtype_no_language_colemak" msgid="5837418400010302623">"Alfabhethi (Colemak)"</string>
     <string name="subtype_no_language_pcqwerty" msgid="5354918232046200018">"Alfabhethi (PC)"</string>
+    <string name="subtype_emoji" msgid="7483586578074549196">"I-Emoji"</string>
     <string name="custom_input_styles_title" msgid="8429952441821251512">"Izitayela zokufaka ngokwezifiso"</string>
     <string name="add_style" msgid="6163126614514489951">"Engeza isitayela"</string>
     <string name="add" msgid="8299699805688017798">"Engeza"</string>
diff --git a/java/res/values/attrs.xml b/java/res/values/attrs.xml
index eef9116..0978214 100644
--- a/java/res/values/attrs.xml
+++ b/java/res/values/attrs.xml
@@ -1,17 +1,21 @@
 <?xml version="1.0" encoding="utf-8"?>
-<!-- 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.
+<!--
+/*
+**
+** 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>
@@ -22,15 +26,18 @@
         <attr name="keyboardViewStyle" format="reference" />
         <!-- MainKeyboardView style -->
         <attr name="mainKeyboardViewStyle" format="reference" />
+        <!-- EmojiKeyboardView style -->
+        <attr name="emojiKeyboardViewStyle" format="reference" />
         <!-- MoreKeysKeyboard style -->
         <attr name="moreKeysKeyboardStyle" format="reference" />
         <!-- MoreKeysKeyboardView style -->
         <attr name="moreKeysKeyboardViewStyle" format="reference" />
-        <attr name="moreKeysKeyboardPanelStyle" format="reference" />
+        <!-- MoreKeysKeyboardView container style -->
+        <attr name="moreKeysKeyboardContainerStyle" format="reference" />
         <!-- Suggestions strip style -->
         <attr name="suggestionStripViewStyle" format="reference" />
-        <attr name="moreSuggestionsViewStyle" format="reference" />
-        <attr name="suggestionBackgroundStyle" format="reference" />
+        <!-- Suggestion word style -->
+        <attr name="suggestionWordStyle" format="reference" />
     </declare-styleable>
 
     <declare-styleable name="KeyboardView">
@@ -38,6 +45,8 @@
              possible states: normal, pressed, checkable, checkable+pressed, checkable+checked,
              checkable+checked+pressed. -->
         <attr name="keyBackground" format="reference" />
+        <!-- Image for the functional key used in Emoji layout. -->
+        <attr name="keyBackgroundEmojiFunctional" format="reference" />
 
         <!-- Horizontal padding of left/right aligned key label to the edge of the key. -->
         <attr name="keyLabelHorizontalPadding" format="dimension" />
@@ -158,6 +167,10 @@
         <attr name="suppressKeyPreviewAfterBatchInputDuration" format="integer" />
     </declare-styleable>
 
+    <declare-styleable name="EmojiKeyboardView">
+        <attr name="emojiTabLabelColor" format="reference" />
+    </declare-styleable>
+
     <declare-styleable name="SuggestionStripView">
         <attr name="suggestionStripOption" format="integer">
             <!-- This should be aligned with SuggestionStripLayoutHelper.AUTO_CORRECT_* and etc. -->
@@ -169,10 +182,6 @@
         <attr name="colorTypedWord" format="color" />
         <attr name="colorAutoCorrect" format="color" />
         <attr name="colorSuggested" format="color" />
-        <attr name="alphaValidTypedWord" format="fraction" />
-        <attr name="alphaTypedWord" format="fraction" />
-        <attr name="alphaAutoCorrect" format="fraction" />
-        <attr name="alphaSuggested" format="fraction" />
         <attr name="alphaObsoleted" format="fraction" />
         <attr name="suggestionsCountInStrip" format="integer" />
         <attr name="centerSuggestionPercentile" format="fraction" />
@@ -216,9 +225,15 @@
         <attr name="iconLanguageSwitchKey" format="reference" />
         <attr name="iconZwnjKey" format="reference" />
         <attr name="iconZwjKey" format="reference" />
+        <attr name="iconImeKey" format="reference" />
         <attr name="iconEmojiKey" format="reference" />
     </declare-styleable>
 
+    <declare-styleable name="Keyboard_GridRows">
+        <attr name="codesArray" format="reference" />
+        <attr name="textsArray" format="reference" />
+    </declare-styleable>
+
     <declare-styleable name="Keyboard_Key">
         <!-- The unicode value that this key outputs.
              Code value represented in hexadecimal prefixed with "0x" or code value reference using
@@ -240,11 +255,12 @@
         <attr name="maxMoreKeysColumn" format="integer" />
         <attr name="backgroundType" format="enum">
             <!-- This should be aligned with Key.BACKGROUND_TYPE_* -->
-            <enum name="normal" value="0" />
-            <enum name="functional" value="1" />
-            <enum name="action" value="2" />
-            <enum name="stickyOff" value="3" />
-            <enum name="stickyOn" value="4" />
+            <enum name="empty" value="0" />
+            <enum name="normal" value="1" />
+            <enum name="functional" value="2" />
+            <enum name="action" value="3" />
+            <enum name="stickyOff" value="4" />
+            <enum name="stickyOn" value="5" />
         </attr>
         <!-- The key action flags. -->
         <attr name="keyActionFlags" format="integer">
@@ -365,6 +381,7 @@
     </declare-styleable>
 
     <declare-styleable name="Keyboard_Case">
+        <attr name="keyboardLayoutSet" format="string" />
         <!-- This should be aligned with KeyboardLayoutSet_Element's elementName. -->
         <attr name="keyboardLayoutSetElement" format="enum|string">
             <enum name="alphabet" value="0" />
@@ -377,6 +394,13 @@
             <enum name="phone" value="7"  />
             <enum name="phoneSymbols" value="8"  />
             <enum name="number" value="9"  />
+            <enum name="emojiRecents" value="10" />
+            <enum name="emojiCategory1" value="11" />
+            <enum name="emojiCategory2" value="12" />
+            <enum name="emojiCategory3" value="13" />
+            <enum name="emojiCategory4" value="14" />
+            <enum name="emojiCategory5" value="15" />
+            <enum name="emojiCategory6" value="16" />
         </attr>
         <!-- This should be aligned with KeyboardId.MODE_* -->
         <attr name="mode" format="enum|string">
@@ -432,6 +456,13 @@
             <enum name="phone" value="7"  />
             <enum name="phoneSymbols" value="8"  />
             <enum name="number" value="9"  />
+            <enum name="emojiRecents" value="10" />
+            <enum name="emojiCategory1" value="11" />
+            <enum name="emojiCategory2" value="12" />
+            <enum name="emojiCategory3" value="13" />
+            <enum name="emojiCategory4" value="14" />
+            <enum name="emojiCategory5" value="15" />
+            <enum name="emojiCategory6" value="16" />
         </attr>
         <attr name="elementKeyboard" format="reference"/>
         <!-- Enable proximity characters correction. Disabled by default. -->
diff --git a/java/res/values/colors.xml b/java/res/values/colors.xml
index daa167c..3803cb7 100644
--- a/java/res/values/colors.xml
+++ b/java/res/values/colors.xml
@@ -1,47 +1,43 @@
 <?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2012 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.
+<!--
+/*
+**
+** Copyright 2012, 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:android="http://schemas.android.com/apk/res/android">
-    <!-- Color resources for default, and Gingerbread theme. -->
-    <color name="highlight_color_default">#FFFCAE00</color>
-    <color name="highlight_translucent_color_default">#99FCAE00</color>
-    <color name="key_text_color_default">@android:color/white</color>
-    <color name="key_text_shadow_color_default">#BB000000</color>
-    <color name="key_text_inactivated_color_default">@android:color/white</color>
-    <color name="key_hint_letter_color_default">#80000000</color>
-    <color name="key_hint_label_color_default">#E0E0E4E5</color>
-    <color name="key_shifted_letter_hint_inactivated_color_default">#66E0E4E5</color>
-    <color name="key_shifted_letter_hint_activated_color_default">#CCE0E4E5</color>
-    <color name="spacebar_text_color_default">#FFC0C0C0</color>
-    <color name="spacebar_text_shadow_color_default">#80000000</color>
-    <color name="typed_word_color_default">@android:color/white</color>
-    <color name="gesture_floating_preview_color_default">#C0000000</color>
-    <!-- Color resources for Stone theme. -->
-    <color name="key_text_color_stone">@android:color/black</color>
-    <color name="key_text_shadow_color_stone">@android:color/white</color>
-    <color name="key_text_inactivated_color_stone">#FF808080</color>
-    <color name="key_hint_letter_color_stone">#80000000</color>
-    <color name="key_hint_label_color_stone">#E0000000</color>
-    <color name="key_shifted_letter_hint_inactivated_color_stone">#66000000</color>
-    <color name="key_shifted_letter_hint_activated_color_stone">#CC000000</color>
-    <color name="spacebar_text_color_stone">@android:color/black</color>
-    <color name="spacebar_text_shadow_color_stone">#D0FFFFFF</color>
-    <!-- Color resources for IceCreamSandwich theme. -->
+    <!-- Color resources for Gingerbread theme. -->
+    <color name="highlight_color_gb">#FFFCAE00</color>
+    <color name="typed_word_color_gb">@android:color/white</color>
+    <color name="highlight_translucent_color_gb">#99FCAE00</color>
+    <color name="key_text_color_gb">@android:color/white</color>
+    <color name="key_text_shadow_color_gb">#BB000000</color>
+    <color name="key_text_inactivated_color_gb">#66E0E4E5</color>
+    <color name="key_hint_letter_color_gb">#80000000</color>
+    <color name="key_hint_label_color_gb">#E0E0E4E5</color>
+    <color name="key_shifted_letter_hint_inactivated_color_gb">#66E0E4E5</color>
+    <color name="key_shifted_letter_hint_activated_color_gb">#CCE0E4E5</color>
+    <color name="spacebar_text_color_gb">#FFC0C0C0</color>
+    <color name="spacebar_text_shadow_color_gb">#80000000</color>
+    <color name="gesture_floating_preview_color_gb">#C0000000</color>
+    <!-- Color resources for IceCreamSandwich theme. Base color = 33B5E5 -->
     <!-- android:color/holo_blue_light value is #FF33B5E5 -->
-    <color name="highlight_color_ics">@android:color/holo_blue_light</color>
+    <color name="highlight_color_ics">#FF33B5E5</color>
+    <color name="typed_word_color_ics">#D833B5E5</color>
+    <color name="suggested_word_color_ics">#B233B5E5</color>
     <color name="highlight_translucent_color_ics">#9933B5E5</color>
     <color name="key_text_color_ics">@android:color/white</color>
     <color name="key_text_shadow_color_ics">@android:color/transparent</color>
@@ -52,11 +48,22 @@
     <color name="key_shifted_letter_hint_activated_color_ics">@android:color/white</color>
     <color name="spacebar_text_color_ics">#FFC0C0C0</color>
     <color name="spacebar_text_shadow_color_ics">#80000000</color>
-    <color name="typed_word_color_ics">@color/highlight_color_ics</color>
+    <color name="gesture_floating_preview_color_ics">#C0000000</color>
+    <!-- Color resources for KLP theme. Base color = F0F0F0 -->
+    <color name="highlight_color_holo">#FFF0F0F0</color>
+    <color name="typed_word_color_holo">#D8F0F0F0</color>
+    <color name="suggested_word_color_holo">#B2F0F0F0</color>
+    <color name="highlight_translucent_color_holo">#99E0E0E0</color>
     <!-- Color resources for setup wizard and tutorial -->
     <color name="setup_background">#FFEBEBEB</color>
     <color name="setup_text_dark">#FF707070</color>
     <color name="setup_text_action">@android:color/holo_blue_light</color>
     <color name="setup_step_background">@android:color/background_light</color>
     <color name="setup_welcome_video_margin_color">#FFCCCCCC</color>
+    <color name="emoji_category_page_id_view_background">#FF000000</color>
+    <color name="emoji_category_page_id_view_foreground">#80FFFFFF</color>
+
+    <!-- TODO: Color which should be included in the theme -->
+    <color name="emoji_key_background_color">#00000000</color>
+    <color name="emoji_key_pressed_background_color">#30FFFFFF</color>
 </resources>
diff --git a/java/res/xml-sw768dp/kbd_more_keys_keyboard_template.xml b/java/res/values/config-additional-features.xml
similarity index 68%
rename from java/res/xml-sw768dp/kbd_more_keys_keyboard_template.xml
rename to java/res/values/config-additional-features.xml
index f89a0a6..47eb772 100644
--- a/java/res/xml-sw768dp/kbd_more_keys_keyboard_template.xml
+++ b/java/res/values/config-additional-features.xml
@@ -2,7 +2,7 @@
 <!--
 /*
 **
-** Copyright 2008, The Android Open Source Project
+** Copyright 2013, The Android Open Source Project
 **
 ** Licensed under the Apache License, Version 2.0 (the "License");
 ** you may not use this file except in compliance with the License.
@@ -18,9 +18,7 @@
 */
 -->
 
-<Keyboard xmlns:latin="http://schemas.android.com/apk/res/com.android.inputmethod.latin"
-    latin:keyWidth="5.0%p"
-    latin:rowHeight="@dimen/popup_key_height"
-    style="?attr/moreKeysKeyboardStyle"
-    >
-</Keyboard>
+<resources>
+    <!-- Whether phrase gestures are enabled by default -->
+    <bool name="config_default_phrase_gesture_enabled">false</bool>
+</resources>
diff --git a/java/res/values/config.xml b/java/res/values/config.xml
index d3a21f2..465d52c 100644
--- a/java/res/values/config.xml
+++ b/java/res/values/config.xml
@@ -42,7 +42,7 @@
     <integer name="config_keyboard_grid_height">16</integer>
     <integer name="config_double_space_period_timeout">1100</integer>
     <!-- This configuration is the index of the array {@link KeyboardSwitcher.KEYBOARD_THEMES}. -->
-    <string name="config_default_keyboard_theme_index" translatable="false">5</string>
+    <string name="config_default_keyboard_theme_index" translatable="false">0</string>
     <integer name="config_max_more_keys_column">5</integer>
 
     <!--
diff --git a/java/res/values/dimens.xml b/java/res/values/dimens.xml
index 98ae76c..4e3b2f5 100644
--- a/java/res/values/dimens.xml
+++ b/java/res/values/dimens.xml
@@ -29,18 +29,11 @@
 
     <dimen name="more_keys_keyboard_key_horizontal_padding">8dp</dimen>
 
-    <fraction name="keyboard_top_padding">1.556%p</fraction>
-    <fraction name="keyboard_bottom_padding">4.669%p</fraction>
     <fraction name="keyboard_left_padding">0%p</fraction>
     <fraction name="keyboard_right_padding">0%p</fraction>
-    <fraction name="key_bottom_gap">6.250%p</fraction>
-    <fraction name="key_horizontal_gap">1.352%p</fraction>
 
-    <fraction name="keyboard_top_padding_stone">1.556%p</fraction>
-    <fraction name="keyboard_bottom_padding_stone">0.778%p</fraction>
-    <fraction name="key_bottom_gap_stone">7.506%p</fraction>
-    <fraction name="key_horizontal_gap_stone">1.739%p</fraction>
-
+    <fraction name="keyboard_top_padding_gb">1.556%p</fraction>
+    <fraction name="keyboard_bottom_padding_gb">4.669%p</fraction>
     <fraction name="key_bottom_gap_gb">6.495%p</fraction>
     <fraction name="key_horizontal_gap_gb">1.971%p</fraction>
 
@@ -48,13 +41,12 @@
     <fraction name="keyboard_bottom_padding_ics">4.669%p</fraction>
     <fraction name="key_bottom_gap_ics">6.127%p</fraction>
     <fraction name="key_horizontal_gap_ics">1.739%p</fraction>
-    <dimen name="more_keys_keyboard_horizontal_edges_padding_ics">4dp</dimen>
 
     <!-- Amount of allowance for selecting keys in a mini popup keyboard by sliding finger. -->
     <!-- popup_key_height x 1.2 -->
     <dimen name="more_keys_keyboard_slide_allowance">63.36dp</dimen>
     <!-- popup_key_height x -1.0 -->
-    <dimen name="more_keys_keyboard_vertical_correction">-52.8dp</dimen>
+    <dimen name="more_keys_keyboard_vertical_correction_gb">-52.8dp</dimen>
     <dimen name="keyboard_vertical_correction">0.0dp</dimen>
 
     <fraction name="key_letter_ratio">55%</fraction>
@@ -67,7 +59,7 @@
     <fraction name="key_preview_text_ratio">82%</fraction>
     <fraction name="spacebar_text_ratio">33.735%</fraction>
     <dimen name="key_preview_height">80dp</dimen>
-    <dimen name="key_preview_offset">-8.0dp</dimen>
+    <dimen name="key_preview_offset_gb">-8.0dp</dimen>
 
     <dimen name="key_label_horizontal_padding">4dp</dimen>
     <dimen name="key_hint_letter_padding">1dp</dimen>
@@ -121,10 +113,20 @@
     <dimen name="gesture_floating_preview_text_offset">73dp</dimen>
     <dimen name="gesture_floating_preview_horizontal_padding">24dp</dimen>
     <dimen name="gesture_floating_preview_vertical_padding">16dp</dimen>
-    <dimen name="gesture_floating_preview_round_radius">3dp</dimen>
+    <dimen name="gesture_floating_preview_round_radius">2dp</dimen>
+
+    <!-- Emoji keyboard -->
+    <fraction name="emoji_keyboard_key_width">14.2857%p</fraction>
+    <fraction name="emoji_keyboard_row_height">33%p</fraction>
+    <fraction name="emoji_keyboard_key_letter_size">90%p</fraction>
+    <integer name="emoji_keyboard_max_key_count">21</integer>
+    <dimen name="emoji_category_page_id_height">3dp</dimen>
 
     <!-- Inset used in Accessibility mode to avoid accidental key presses when a finger slides off the screen. -->
     <dimen name="accessibility_edge_slop">8dp</dimen>
 
     <integer name="user_dictionary_max_word_length" translatable="false">48</integer>
+
+    <dimen name="language_on_spacebar_horizontal_margin">1dp</dimen>
+
 </resources>
diff --git a/java/res/values/donottranslate.xml b/java/res/values/donottranslate.xml
index e352f08..42e692d 100644
--- a/java/res/values/donottranslate.xml
+++ b/java/res/values/donottranslate.xml
@@ -18,6 +18,8 @@
 */
 -->
 <resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <!-- TODO: these settings depend on the language. They should be put either in the dictionary
+         header, or in the subtype maybe? -->
     <!-- Symbols that are suggested between words -->
     <string name="suggested_punctuations">!,?,\\,,:,;,\",(,),\',-,/,@,_</string>
     <!-- Symbols that are normally preceded by a space (used to add an auto-space before these) -->
@@ -29,6 +31,8 @@
     <string name="symbols_word_separators">"&#x0009;&#x0020;\n"()[]{}*&amp;&lt;&gt;+=|.,;:!?/_\"</string>
     <!-- Word connectors -->
     <string name="symbols_word_connectors">\'-</string>
+    <!-- Whether this language uses spaces between words -->
+    <bool name="current_language_has_spaces">true</bool>
 
     <!--  Always show the suggestion strip -->
     <string name="prefs_suggestion_visibility_show_value">0</string>
@@ -94,29 +98,17 @@
     <string name="prefs_force_non_distinct_multitouch">Force non-distinct multitouch</string>
 
     <!-- Keyboard theme names -->
-    <string name="layout_basic">Basic</string>
-    <string name="layout_high_contrast">Basic (High Contrast)</string>
-    <string name="layout_stone_bold">Stone (bold)</string>
-    <string name="layout_stone_normal">Stone (normal)</string>
     <string name="layout_gingerbread">Gingerbread</string>
     <string name="layout_ics">IceCreamSandwich</string>
 
     <!-- For keyboard theme switcher dialog -->
     <string-array name="keyboard_layout_modes">
-        <item>@string/layout_basic</item>
-        <item>@string/layout_high_contrast</item>
-        <item>@string/layout_stone_normal</item>
-        <item>@string/layout_stone_bold</item>
-        <item>@string/layout_gingerbread</item>
         <item>@string/layout_ics</item>
+        <item>@string/layout_gingerbread</item>
     </string-array>
     <string-array name="keyboard_layout_modes_values">
         <item>0</item>
         <item>1</item>
-        <item>2</item>
-        <item>3</item>
-        <item>4</item>
-        <item>5</item>
     </string-array>
 
     <!-- Subtype locale display name exceptions.
@@ -166,47 +158,47 @@
 
     <!-- Compatibility map from subtypeLocale:subtypeExtraValue to keyboardLayoutSet -->
     <string-array name="locale_and_extra_value_to_keyboard_layout_set_map">
-        <item>en_US:TrySuppressingImeSwitcher,AsciiCapable,SupportTouchPositionCorrection</item>
+        <item>en_US:TrySuppressingImeSwitcher,AsciiCapable,SupportTouchPositionCorrection,EmojiCapable</item>
         <item>qwerty</item>
-        <item>en_GB:TrySuppressingImeSwitcher,AsciiCapable,SupportTouchPositionCorrection</item>
+        <item>en_GB:TrySuppressingImeSwitcher,AsciiCapable,SupportTouchPositionCorrection,EmojiCapable</item>
         <item>qwerty</item>
-        <item>ar:SupportTouchPositionCorrection</item>
+        <item>ar:SupportTouchPositionCorrection,EmojiCapable</item>
         <item>arabic</item>
-        <item>cs:AsciiCapable,SupportTouchPositionCorrection</item>
+        <item>cs:AsciiCapable,SupportTouchPositionCorrection,EmojiCapable</item>
         <item>qwertz</item>
-        <item>da:AsciiCapable,SupportTouchPositionCorrection</item>
+        <item>da:AsciiCapable,SupportTouchPositionCorrection,EmojiCapable</item>
         <item>nordic</item>
-        <item>de:AsciiCapable,SupportTouchPositionCorrection</item>
+        <item>de:AsciiCapable,SupportTouchPositionCorrection,EmojiCapable</item>
         <item>qwertz</item>
-        <item>es:AsciiCapable,SupportTouchPositionCorrection</item>
+        <item>es:AsciiCapable,SupportTouchPositionCorrection,EmojiCapable</item>
         <item>spanish</item>
-        <item>fi:AsciiCapable,SupportTouchPositionCorrection</item>
+        <item>fi:AsciiCapable,SupportTouchPositionCorrection,EmojiCapable</item>
         <item>nordic</item>
-        <item>fr:AsciiCapable,SupportTouchPositionCorrection</item>
+        <item>fr:AsciiCapable,SupportTouchPositionCorrection,EmojiCapable</item>
         <item>azerty</item>
-        <item>fr_CA:AsciiCapable,SupportTouchPositionCorrection</item>
+        <item>fr_CA:AsciiCapable,SupportTouchPositionCorrection,EmojiCapable</item>
         <item>qwerty</item>
-        <item>hr:AsciiCapable,SupportTouchPositionCorrection</item>
+        <item>hr:AsciiCapable,SupportTouchPositionCorrection,EmojiCapable</item>
         <item>qwertz</item>
-        <item>hu:AsciiCapable,SupportTouchPositionCorrection</item>
+        <item>hu:AsciiCapable,SupportTouchPositionCorrection,EmojiCapable</item>
         <item>qwertz</item>
-        <item>it:AsciiCapable,SupportTouchPositionCorrection</item>
+        <item>it:AsciiCapable,SupportTouchPositionCorrection,EmojiCapable</item>
         <item>qwerty</item>
-        <item>iw:SupportTouchPositionCorrection</item>
+        <item>iw:SupportTouchPositionCorrection,EmojiCapable</item>
         <item>hebrew</item>
-        <item>nb:AsciiCapable,SupportTouchPositionCorrection</item>
+        <item>nb:AsciiCapable,SupportTouchPositionCorrection,EmojiCapable</item>
         <item>nordic</item>
-        <item>nl:AsciiCapable,SupportTouchPositionCorrection</item>
+        <item>nl:AsciiCapable,SupportTouchPositionCorrection,EmojiCapable</item>
         <item>qwerty</item>
-        <item>pl:AsciiCapable,SupportTouchPositionCorrection</item>
+        <item>pl:AsciiCapable,SupportTouchPositionCorrection,EmojiCapable</item>
         <item>qwerty</item>
-        <item>ru:SupportTouchPositionCorrection</item>
+        <item>ru:SupportTouchPositionCorrection,EmojiCapable</item>
         <item>east_slavic</item>
-        <item>sr:SupportTouchPositionCorrection</item>
+        <item>sr:SupportTouchPositionCorrection,EmojiCapable</item>
         <item>south_slavic</item>
-        <item>sv:AsciiCapable,SupportTouchPositionCorrection</item>
+        <item>sv:AsciiCapable,SupportTouchPositionCorrection,EmojiCapable</item>
         <item>nordic</item>
-        <item>tr:AsciiCapable,SupportTouchPositionCorrection</item>
+        <item>tr:AsciiCapable,SupportTouchPositionCorrection,EmojiCapable</item>
         <item>qwerty</item>
     </string-array>
 
diff --git a/java/res/values/emoji-categories.xml b/java/res/values/emoji-categories.xml
new file mode 100644
index 0000000..ce82a8b
--- /dev/null
+++ b/java/res/values/emoji-categories.xml
@@ -0,0 +1,127 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+**
+** Copyright 2013, 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.
+*/
+-->
+
+<!-- Note: This emoji code point list is valid prior to JB-MR2 (API < 18).
+     There is another emoji code point list for JB-MR2 and KLP and later under
+     res/xml/values-v1[89].-->
+<resources>
+    <!-- Dummy codeArrays for recents emoji keyboard.
+         Do not remove these keys, because they are used as a template. -->
+    <array
+        name="emoji_recents"
+        format="string"
+    >
+        <!-- These code point should be aligned with {@link RecentsKeyboard#TEMPLATE_KEY_CODE_*. -->
+        <item>30</item>
+        <item>31</item>
+    </array>
+    <array
+        name="emoji_nature"
+        format="string"
+    >
+        <item>2744</item> <!-- SNOWFLAKE -->
+    </array>
+    <array
+        name="emoji_symbols"
+        format="string"
+    >
+        <item>2460</item> <!-- CIRCLED DIGIT ONE -->
+        <item>2461</item> <!-- CIRCLED DIGIT TWO -->
+        <item>2462</item> <!-- CIRCLED DIGIT THREE -->
+        <item>2463</item> <!-- CIRCLED DIGIT FOUR -->
+        <item>2464</item> <!-- CIRCLED DIGIT FIVE -->
+        <item>2465</item> <!-- CIRCLED DIGIT SIX -->
+        <item>2466</item> <!-- CIRCLED DIGIT SEVEN -->
+        <item>2467</item> <!-- CIRCLED DIGIT EIGHT -->
+        <item>2468</item> <!-- CIRCLED DIGIT NINE -->
+        <item>2469</item> <!-- CIRCLED DIGIT TEN -->
+        <item>00ae</item> <!-- REGISTERED SIGN -->
+        <item>00a9</item> <!-- COPYRIGHT SIGN -->
+        <item>2122</item> <!-- TRADE MARK SIGN -->
+        <item>2734</item> <!-- EIGHT POINTED BLACK STAR -->
+        <item>2733</item> <!-- EIGHT POINTED PINWHEEL STAR -->
+        <item>2716</item> <!-- HEAVY MULTIPLICATION MARK -->
+        <item>2195</item> <!-- UP DOWN ARROW -->
+        <item>2197</item> <!-- NORTH EAST ARROW -->
+        <item>27a1</item> <!-- BLACK RIGHTWARDS ARROW -->
+        <item>2198</item> <!-- SOUTH EAST ARROW -->
+        <item>2199</item> <!-- SOUTH WEST ARROW -->
+        <item>2196</item> <!-- NORTH EAST ARROW -->
+        <item>2194</item> <!-- LEFT RIGHT ARROW -->
+        <item>25c0</item> <!-- BLACK LEFT-POINTING TRIANGLE -->
+        <item>25b6</item> <!-- BLACK ROGHT-POINTING TRIANGLE -->
+        <item>2747</item> <!-- SPARKLE -->
+        <item>25aa</item> <!-- BLACK SMALL SQUARE -->
+        <item>203c</item> <!-- DOUBLE EXCLAMATION MARK -->
+        <item>2660</item> <!-- BLACK SPADE SUIT -->
+        <item>2665</item> <!-- BLACK HEART SUIT -->
+        <item>2663</item> <!-- BLACK CLUB SUIT -->
+        <item>2666</item> <!-- BLACK DIAMOND SUIT -->
+        <item>21a9</item> <!-- LEFTWARDS ARROW WITH HOOK -->
+        <item>21aa</item> <!-- RIGHTWARDS ARROW WITH HOOK -->
+    </array>
+    <array
+        name="emoji_faces"
+        format="string"
+    >
+        <item>270C</item> <!-- VICTORY HAND -->
+        <item>2764</item> <!-- HEAVY BLACK HEART -->
+    </array>
+    <array
+        name="emoji_objects"
+        format="string"
+    >
+        <item>260e</item> <!-- BLACK TELEPHONE -->
+        <item>2709</item> <!-- ENVELOPE -->
+        <item>2712</item> <!-- BLACK NIB -->
+        <item>270f</item> <!-- PENCIL -->
+        <item>2702</item> <!-- BLACK SCISSORS -->
+        <item>2669</item> <!-- QUARTER NOTE -->
+        <item>266a</item> <!-- EIGHTH NOTE -->
+        <item>266c</item> <!-- BEAMED SIXTEENTH NOTES -->
+    </array>
+    <array
+        name="emoji_places"
+        format="string"
+    >
+        <item>2708</item> <!-- AIRPLANE -->
+        <item>2668</item> <!-- HOT SPRINGS -->
+    </array>
+    <array
+        name="emoji_emoticons"
+        format="string"
+    >
+        <item>=-O</item>
+        <item>:-P</item>
+        <item>;-)</item>
+        <item>:-(</item>
+        <item>:-)</item>
+        <item>:-!</item>
+        <item>:-$</item>
+        <item>B-)</item>
+        <item>:O</item>
+        <item>:-*</item>
+        <item>:-D</item>
+        <item>:\'(</item>
+        <item>:-\\</item>
+        <item>O:-)</item>
+        <item>:-[</item>
+    </array>
+</resources>
diff --git a/java/res/values/keyboard-icons-black.xml b/java/res/values/keyboard-icons-black.xml
deleted file mode 100644
index c1b1b65..0000000
--- a/java/res/values/keyboard-icons-black.xml
+++ /dev/null
@@ -1,44 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2011 The Android Open Source Project
-
-     Licensed under the Apache License, Version 2.0 (the "License");
-     you may not use this file except in compliance with the License.
-     You may obtain a copy of the License at
-
-          http://www.apache.org/licenses/LICENSE-2.0
-
-     Unless required by applicable law or agreed to in writing, software
-     distributed under the License is distributed on an "AS IS" BASIS,
-     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-     See the License for the specific language governing permissions and
-     limitations under the License.
--->
-
-<resources>
-    <style name="KeyboardIcons.Black">
-        <!-- Keyboard icons -->
-        <!-- TODO: The following holo icon for phone (drawable-hdpi and drawable-xhdpi) are too
-             ambiguous.
-             sym_bkeyboard_voice_off
-          -->
-        <item name="iconShiftKey">@drawable/sym_bkeyboard_shift</item>
-        <item name="iconDeleteKey">@drawable/sym_bkeyboard_delete</item>
-        <item name="iconSettingsKey">@drawable/sym_bkeyboard_settings</item>
-        <item name="iconSpaceKey">@drawable/sym_bkeyboard_space</item>
-        <item name="iconEnterKey">@drawable/sym_bkeyboard_return</item>
-        <item name="iconSearchKey">@drawable/sym_bkeyboard_search</item>
-        <item name="iconTabKey">@drawable/sym_bkeyboard_tab</item>
-        <item name="iconShortcutKey">@drawable/sym_bkeyboard_mic</item>
-        <item name="iconShortcutForLabel">@drawable/sym_bkeyboard_label_mic</item>
-        <item name="iconSpaceKeyForNumberLayout">@drawable/sym_bkeyboard_space</item>
-        <item name="iconShiftKeyShifted">@drawable/sym_bkeyboard_shift_locked</item>
-        <item name="iconShortcutKeyDisabled">@drawable/sym_bkeyboard_voice_off</item>
-        <item name="iconTabKeyPreview">@drawable/sym_keyboard_feedback_tab</item>
-        <!-- TODO: Needs dedicated black theme globe icon -->
-        <item name="iconLanguageSwitchKey">@drawable/sym_keyboard_language_switch</item>
-        <!-- TODO: Needs dedicated black theme ZWNJ and ZWJ icons -->
-        <item name="iconZwnjKey">@drawable/sym_keyboard_zwnj_holo</item>
-        <item name="iconZwjKey">@drawable/sym_keyboard_zwj_holo</item>
-        <item name="iconEmojiKey">@drawable/ic_emoji_light</item>
-    </style>
-</resources>
diff --git a/java/res/values/keyboard-icons-ics.xml b/java/res/values/keyboard-icons-ics.xml
deleted file mode 100644
index 5ada27a..0000000
--- a/java/res/values/keyboard-icons-ics.xml
+++ /dev/null
@@ -1,41 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2011 The Android Open Source Project
-
-     Licensed under the Apache License, Version 2.0 (the "License");
-     you may not use this file except in compliance with the License.
-     You may obtain a copy of the License at
-
-          http://www.apache.org/licenses/LICENSE-2.0
-
-     Unless required by applicable law or agreed to in writing, software
-     distributed under the License is distributed on an "AS IS" BASIS,
-     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-     See the License for the specific language governing permissions and
-     limitations under the License.
--->
-
-<resources>
-    <style name="KeyboardIcons.IceCreamSandwich">
-        <!-- Keyboard icons -->
-        <!-- TODO: The following holo icon for phone (drawable-hdpi and drawable-xhdpi) are missing.
-             sym_keyboard_123_mic_holo
-             -->
-        <item name="iconShiftKey">@drawable/sym_keyboard_shift_holo</item>
-        <item name="iconDeleteKey">@drawable/sym_keyboard_delete_holo</item>
-        <item name="iconSettingsKey">@drawable/sym_keyboard_settings_holo</item>
-        <item name="iconSpaceKey">@null</item>
-        <item name="iconEnterKey">@drawable/sym_keyboard_return_holo</item>
-        <item name="iconSearchKey">@drawable/sym_keyboard_search_holo</item>
-        <item name="iconTabKey">@drawable/sym_keyboard_tab_holo</item>
-        <item name="iconShortcutKey">@drawable/sym_keyboard_voice_holo</item>
-        <item name="iconShortcutForLabel">@drawable/sym_keyboard_label_mic_holo</item>
-        <item name="iconSpaceKeyForNumberLayout">@drawable/sym_keyboard_space_holo</item>
-        <item name="iconShiftKeyShifted">@drawable/sym_keyboard_shift_locked_holo</item>
-        <item name="iconShortcutKeyDisabled">@drawable/sym_keyboard_voice_off_holo</item>
-        <item name="iconTabKeyPreview">@drawable/sym_keyboard_feedback_tab</item>
-        <item name="iconLanguageSwitchKey">@drawable/sym_keyboard_language_switch</item>
-        <item name="iconZwnjKey">@drawable/sym_keyboard_zwnj_holo</item>
-        <item name="iconZwjKey">@drawable/sym_keyboard_zwj_holo</item>
-        <item name="iconEmojiKey">@drawable/ic_emoji_light</item>
-    </style>
-</resources>
diff --git a/java/res/values/keyboard-icons-white.xml b/java/res/values/keyboard-icons-white.xml
deleted file mode 100644
index 7c6de42..0000000
--- a/java/res/values/keyboard-icons-white.xml
+++ /dev/null
@@ -1,40 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2011 The Android Open Source Project
-
-     Licensed under the Apache License, Version 2.0 (the "License");
-     you may not use this file except in compliance with the License.
-     You may obtain a copy of the License at
-
-          http://www.apache.org/licenses/LICENSE-2.0
-
-     Unless required by applicable law or agreed to in writing, software
-     distributed under the License is distributed on an "AS IS" BASIS,
-     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-     See the License for the specific language governing permissions and
-     limitations under the License.
--->
-
-<resources>
-    <style name="KeyboardIcons">
-        <!-- Keyboard icons -->
-        <item name="iconShiftKey">@drawable/sym_keyboard_shift</item>
-        <item name="iconDeleteKey">@drawable/sym_keyboard_delete</item>
-        <item name="iconSettingsKey">@drawable/sym_keyboard_settings</item>
-        <item name="iconSpaceKey">@drawable/sym_keyboard_space</item>
-        <item name="iconEnterKey">@drawable/sym_keyboard_return</item>ZZ
-        <item name="iconSearchKey">@drawable/sym_keyboard_search</item>
-        <item name="iconTabKey">@drawable/sym_keyboard_tab</item>
-        <item name="iconShortcutKey">@drawable/sym_keyboard_mic</item>
-        <item name="iconShortcutForLabel">@drawable/sym_keyboard_label_mic</item>
-        <item name="iconSpaceKeyForNumberLayout">@drawable/sym_keyboard_space</item>
-        <item name="iconShiftKeyShifted">@drawable/sym_keyboard_shift_locked</item>
-        <!-- TODO: Needs non-holo disabled shortcut icon drawable -->
-        <item name="iconShortcutKeyDisabled">@drawable/sym_keyboard_voice_off_holo</item>
-        <item name="iconTabKeyPreview">@drawable/sym_keyboard_feedback_tab</item>
-        <item name="iconLanguageSwitchKey">@drawable/sym_keyboard_language_switch</item>
-        <!-- TODO: Needs dedicated black theme ZWNJ and ZWJ icons -->
-        <item name="iconZwnjKey">@drawable/sym_keyboard_zwnj_holo</item>
-        <item name="iconZwjKey">@drawable/sym_keyboard_zwj_holo</item>
-        <item name="iconEmojiKey">@drawable/ic_emoji_dark</item>
-    </style>
-</resources>
diff --git a/java/res/values/keypress-vibration-durations.xml b/java/res/values/keypress-vibration-durations.xml
index 53448c3..ee0ac00 100644
--- a/java/res/values/keypress-vibration-durations.xml
+++ b/java/res/values/keypress-vibration-durations.xml
@@ -55,8 +55,8 @@
         <item>MODEL=HTL22:MANUFACTURER=HTC,15</item>
         <!-- Motorola Razor M -->
         <item>MODEL=XT907:MANUFACTURER=motorola,30</item>
-        <!-- Sony Xperia Z -->
-        <item>MODEL=C6603:MANUFACTURER=Sony,35</item>
+        <!-- Sony Xperia Z, Z Ultra -->
+        <item>MODEL=C6603|C6806:MANUFACTURER=Sony,35</item>
         <!-- Default value for unknown device. The negative value means system default. -->
         <item>,-1</item>
     </string-array>
diff --git a/java/res/values/setup-styles.xml b/java/res/values/setup-styles.xml
index 1ffe8ca..c968b2f 100644
--- a/java/res/values/setup-styles.xml
+++ b/java/res/values/setup-styles.xml
@@ -1,17 +1,21 @@
 <?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2013 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.
+<!--
+/*
+**
+** Copyright 2013, 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:android="http://schemas.android.com/apk/res/android">
diff --git a/java/res/values/strings.xml b/java/res/values/strings.xml
index aae5b0b..69da1e8 100644
--- a/java/res/values/strings.xml
+++ b/java/res/values/strings.xml
@@ -371,6 +371,8 @@
     <!-- Description for Spanish (United States) keyboard subtype with explicit keyboard layout [CHAR LIMIT=25]
          This should be identical to subtype_es_US aside from the trailing (%s). -->
     <string name="subtype_with_layout_es_US">Spanish (US) (<xliff:g id="layout">%s</xliff:g>)</string>
+    <!-- Description for Nepali (Traditional) keyboard subtype [CHAR LIMIT=25] -->
+    <string name="subtype_nepali_traditional"><xliff:g id="language">%s</xliff:g> (Traditional)</string>
     <!-- TODO: Uncomment once we can handle IETF language tag with script name specified.
          Description for Serbian Cyrillic keyboard subtype [CHAR LIMIT=25]
     <string name="subtype_serbian_cyrillic">Serbian (Cyrillic)</string>
@@ -457,6 +459,8 @@
 disposition that offers additional keys, but smaller keys compared to other common dispositions for
 mobile devices. [CHAR LIMIT=25] -->
     <string name="subtype_no_language_pcqwerty">Alphabet (PC)</string>
+    <!-- Description for Emoji keyboard subtype [CHAR LIMIT=25] -->
+    <string name="subtype_emoji">Emoji</string>
 
     <!-- Title of the preference settings for custom input styles (language and keyboard layout pairs) [CHAR LIMIT=35]-->
     <string name="custom_input_styles_title">Custom input styles</string>
@@ -493,6 +497,8 @@
     <string name="prefs_read_external_dictionary">Read external dictionary file</string>
     <!-- Title of the settings for using only personalization dictionary -->
     <string name="prefs_use_only_personalization_dictionary" translatable="false">Use only personalization dictionary</string>
+    <!-- Title of the settings for boosting personalization dictionary -->
+    <string name="prefs_boost_personalization_dictionary" translatable="false">Boost personalization dictionary</string>
     <!-- Message to show when there are no files to install as an external dictionary [CHAR LIMIT=100] -->
     <string name="read_external_dictionary_no_files_message">No dictionary files in the Downloads folder</string>
     <!-- Title of the dialog that selects a file to install as an external dictionary [CHAR LIMIT=50] -->
diff --git a/java/res/values/styles.xml b/java/res/values/styles.xml
deleted file mode 100644
index 37c6a95..0000000
--- a/java/res/values/styles.xml
+++ /dev/null
@@ -1,415 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- 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.
--->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android">
-    <!-- Theme "Basic" -->
-    <style name="Keyboard">
-        <!-- This should be aligned with KeyboardSwitcher.KEYBOARD_THEMES[] -->
-        <item name="themeId">0</item>
-        <item name="touchPositionCorrectionData">@array/touch_position_correction_data_default</item>
-        <item name="rowHeight">25%p</item>
-        <item name="moreKeysTemplate">@xml/kbd_more_keys_keyboard_template</item>
-        <item name="keyboardTopPadding">@fraction/keyboard_top_padding</item>
-        <item name="keyboardBottomPadding">@fraction/keyboard_bottom_padding</item>
-        <item name="keyboardLeftPadding">@fraction/keyboard_left_padding</item>
-        <item name="keyboardRightPadding">@fraction/keyboard_right_padding</item>
-        <item name="horizontalGap">@fraction/key_horizontal_gap</item>
-        <item name="verticalGap">@fraction/key_bottom_gap</item>
-        <item name="maxMoreKeysColumn">@integer/config_max_more_keys_column</item>
-    </style>
-    <style name="KeyboardView">
-        <item name="android:background">@drawable/keyboard_background</item>
-        <item name="keyBackground">@drawable/btn_keyboard_key</item>
-        <item name="keyLetterSize">@fraction/key_letter_ratio</item>
-        <item name="keyLargeLetterRatio">@fraction/key_large_letter_ratio</item>
-        <item name="keyLabelSize">@fraction/key_label_ratio</item>
-        <item name="keyLargeLabelRatio">@fraction/key_large_label_ratio</item>
-        <item name="keyHintLetterRatio">@fraction/key_hint_letter_ratio</item>
-        <item name="keyHintLabelRatio">@fraction/key_hint_label_ratio</item>
-        <item name="keyShiftedLetterHintRatio">@fraction/key_uppercase_letter_ratio</item>
-        <item name="keyTypeface">normal</item>
-        <item name="keyTextColor">@color/key_text_color_default</item>
-        <item name="keyTextInactivatedColor">@color/key_text_color_default</item>
-        <item name="keyHintLetterColor">@color/key_hint_letter_color_default</item>
-        <item name="keyHintLabelColor">@color/key_hint_label_color_default</item>
-        <item name="keyShiftedLetterHintInactivatedColor">@color/key_shifted_letter_hint_inactivated_color_default</item>
-        <item name="keyShiftedLetterHintActivatedColor">@color/key_shifted_letter_hint_activated_color_default</item>
-        <item name="keyLabelHorizontalPadding">@dimen/key_label_horizontal_padding</item>
-        <item name="keyHintLetterPadding">@dimen/key_hint_letter_padding</item>
-        <item name="keyPopupHintLetterPadding">@dimen/key_popup_hint_letter_padding</item>
-        <item name="keyShiftedLetterHintPadding">@dimen/key_uppercase_letter_padding</item>
-        <item name="keyPreviewTextColor">@color/key_text_color_default</item>
-        <item name="keyPreviewTextRatio">@fraction/key_preview_text_ratio</item>
-        <item name="verticalCorrection">@dimen/keyboard_vertical_correction</item>
-        <item name="keyTextShadowColor">@color/key_text_shadow_color_default</item>
-        <item name="keyTextShadowRadius">2.75</item>
-        <item name="backgroundDimAlpha">128</item>
-        <item name="gestureFloatingPreviewTextSize">@dimen/gesture_floating_preview_text_size</item>
-        <item name="gestureFloatingPreviewTextColor">@color/highlight_color_default</item>
-        <item name="gestureFloatingPreviewTextOffset">@dimen/gesture_floating_preview_text_offset</item>
-        <item name="gestureFloatingPreviewColor">@color/gesture_floating_preview_color_default</item>
-        <item name="gestureFloatingPreviewHorizontalPadding">@dimen/gesture_floating_preview_horizontal_padding</item>
-        <item name="gestureFloatingPreviewVerticalPadding">@dimen/gesture_floating_preview_vertical_padding</item>
-        <item name="gestureFloatingPreviewRoundRadius">@dimen/gesture_floating_preview_round_radius</item>
-        <item name="gestureTrailMinSamplingDistance">@dimen/gesture_trail_min_sampling_distance</item>
-        <item name="gestureTrailMaxInterpolationAngularThreshold">@integer/gesture_trail_max_interpolation_angular_threshold</item>
-        <item name="gestureTrailMaxInterpolationDistanceThreshold">@dimen/gesture_trail_max_interpolation_distance_threshold</item>
-        <item name="gestureTrailMaxInterpolationSegments">@integer/gesture_trail_max_interpolation_segments</item>
-        <item name="gestureTrailFadeoutStartDelay">@integer/config_gesture_trail_fadeout_start_delay</item>
-        <item name="gestureTrailFadeoutDuration">@integer/config_gesture_trail_fadeout_duration</item>
-        <item name="gestureTrailUpdateInterval">@integer/config_gesture_trail_update_interval</item>
-        <item name="gestureTrailColor">@color/highlight_color_default</item>
-        <item name="gestureTrailStartWidth">@dimen/gesture_trail_start_width</item>
-        <item name="gestureTrailEndWidth">@dimen/gesture_trail_end_width</item>
-        <item name="gestureTrailBodyRatio">@integer/gesture_trail_body_ratio</item>
-        <item name="gestureTrailShadowRatio">@integer/gesture_trail_shadow_ratio</item>
-        <!-- Common attributes of MainKeyboardView -->
-        <item name="keyHysteresisDistance">@dimen/config_key_hysteresis_distance</item>
-        <item name="keyHysteresisDistanceForSlidingModifier">@dimen/config_key_hysteresis_distance_for_sliding_modifier</item>
-        <item name="touchNoiseThresholdTime">@integer/config_touch_noise_threshold_time</item>
-        <item name="touchNoiseThresholdDistance">@dimen/config_touch_noise_threshold_distance</item>
-        <item name="slidingKeyInputEnable">@bool/config_sliding_key_input_enabled</item>
-        <item name="slidingKeyInputPreviewColor">@color/highlight_translucent_color_default</item>
-        <item name="slidingKeyInputPreviewWidth">@dimen/config_sliding_key_input_preview_width</item>
-        <item name="slidingKeyInputPreviewBodyRatio">@integer/config_sliding_key_input_preview_body_ratio</item>
-        <item name="slidingKeyInputPreviewShadowRatio">@integer/config_sliding_key_input_preview_shadow_ratio</item>
-        <item name="keyRepeatStartTimeout">@integer/config_key_repeat_start_timeout</item>
-        <item name="keyRepeatInterval">@integer/config_key_repeat_interval</item>
-        <item name="longPressShiftLockTimeout">@integer/config_longpress_shift_lock_timeout</item>
-        <item name="ignoreAltCodeKeyTimeout">@integer/config_ignore_alt_code_key_timeout</item>
-        <item name="keyPreviewLayout">@layout/key_preview</item>
-        <item name="keyPreviewOffset">@dimen/key_preview_offset</item>
-        <item name="keyPreviewHeight">@dimen/key_preview_height</item>
-        <item name="keyPreviewLingerTimeout">@integer/config_key_preview_linger_timeout</item>
-        <item name="moreKeysKeyboardLayout">@layout/more_keys_keyboard</item>
-        <item name="showMoreKeysKeyboardAtTouchedPoint">@bool/config_show_more_keys_keyboard_at_touched_point</item>
-        <item name="languageOnSpacebarFinalAlpha">@integer/config_language_on_spacebar_final_alpha</item>
-        <item name="languageOnSpacebarFadeoutAnimator">@anim/language_on_spacebar_fadeout</item>
-        <!-- Remove animations for now because it could drain a non-negligible amount of battery while typing.
-        <item name="altCodeKeyWhileTypingFadeoutAnimator">@anim/alt_code_key_while_typing_fadeout</item>
-        <item name="altCodeKeyWhileTypingFadeinAnimator">@anim/alt_code_key_while_typing_fadein</item>
-        -->
-        <!-- Common attributes of MainKeyboardView for gesture typing detection and recognition -->
-        <item name="gestureFloatingPreviewTextLingerTimeout">@integer/config_gesture_floating_preview_text_linger_timeout</item>
-        <item name="gestureStaticTimeThresholdAfterFastTyping">@integer/config_gesture_static_time_threshold_after_fast_typing</item>
-        <item name="gestureDetectFastMoveSpeedThreshold">@fraction/config_gesture_detect_fast_move_speed_threshold</item>
-        <item name="gestureDynamicThresholdDecayDuration">@integer/config_gesture_dynamic_threshold_decay_duration</item>
-        <item name="gestureDynamicTimeThresholdFrom">@integer/config_gesture_dynamic_time_threshold_from</item>
-        <item name="gestureDynamicTimeThresholdTo">@integer/config_gesture_dynamic_time_threshold_to</item>
-        <item name="gestureDynamicDistanceThresholdFrom">@fraction/config_gesture_dynamic_distance_threshold_from</item>
-        <item name="gestureDynamicDistanceThresholdTo">@fraction/config_gesture_dynamic_distance_threshold_to</item>
-        <item name="gestureSamplingMinimumDistance">@fraction/config_gesture_sampling_minimum_distance</item>
-        <item name="gestureRecognitionMinimumTime">@integer/config_gesture_recognition_minimum_time</item>
-        <item name="gestureRecognitionUpdateTime">@integer/config_gesture_recognition_update_time</item>
-        <item name="gestureRecognitionSpeedThreshold">@fraction/config_gesture_recognition_speed_threshold</item>
-        <item name="suppressKeyPreviewAfterBatchInputDuration">@integer/config_suppress_key_preview_after_batch_input_duration</item>
-    </style>
-    <style
-        name="MainKeyboardView"
-        parent="KeyboardView">
-        <item name="autoCorrectionSpacebarLedEnabled">true</item>
-        <item name="autoCorrectionSpacebarLedIcon">@drawable/sym_keyboard_space_led</item>
-        <item name="spacebarTextRatio">@fraction/spacebar_text_ratio</item>
-        <item name="spacebarTextColor">@color/spacebar_text_color_default</item>
-        <item name="spacebarTextShadowColor">@color/spacebar_text_shadow_color_default</item>
-    </style>
-    <style
-        name="MoreKeysKeyboard"
-        parent="Keyboard"
-    >
-        <item name="keyboardTopPadding">0%p</item>
-        <item name="keyboardBottomPadding">0%p</item>
-        <item name="horizontalGap">0%p</item>
-        <item name="touchPositionCorrectionData">@null</item>
-    </style>
-    <style
-        name="MoreKeysKeyboardView"
-        parent="KeyboardView"
-    >
-        <item name="keyBackground">@drawable/btn_keyboard_key_popup</item>
-        <item name="verticalCorrection">@dimen/more_keys_keyboard_vertical_correction</item>
-    </style>
-    <style name="MoreKeysKeyboardPanelStyle">
-        <item name="android:background">@drawable/keyboard_popup_panel_background</item>
-    </style>
-    <style
-        name="SuggestionStripViewStyle"
-    >
-        <item name="android:background">@drawable/keyboard_suggest_strip</item>
-        <item name="suggestionStripOption">autoCorrectBold|validTypedWordBold</item>
-        <item name="colorValidTypedWord">@color/highlight_color_default</item>
-        <item name="colorTypedWord">@color/typed_word_color_default</item>
-        <item name="colorAutoCorrect">@color/highlight_color_default</item>
-        <item name="colorSuggested">@color/highlight_color_default</item>
-        <item name="alphaObsoleted">50%</item>
-        <item name="suggestionsCountInStrip">@integer/suggestions_count_in_strip</item>
-        <item name="centerSuggestionPercentile">@fraction/center_suggestion_percentile</item>
-        <item name="maxMoreSuggestionsRow">@integer/max_more_suggestions_row</item>
-        <item name="minMoreSuggestionsWidth">@fraction/min_more_suggestions_width</item>
-    </style>
-    <style
-        name="MoreSuggestionsViewStyle"
-        parent="MoreKeysKeyboardView"
-    >
-    </style>
-    <style name="SuggestionBackgroundStyle">
-        <item name="android:background">@drawable/btn_suggestion</item>
-    </style>
-    <!-- Theme "Basic high contrast" -->
-    <style
-        name="Keyboard.HighContrast"
-        parent="Keyboard"
-    >
-        <!-- This should be aligned with KeyboardSwitcher.KEYBOARD_THEMES[] -->
-        <item name="themeId">1</item>
-    </style>
-    <style
-        name="KeyboardView.HighContrast"
-        parent="KeyboardView"
-    >
-        <item name="android:background">@android:color/black</item>
-        <item name="keyBackground">@drawable/btn_keyboard_key3</item>
-    </style>
-    <style
-        name="MainKeyboardView.HighContrast"
-        parent="KeyboardView.HighContrast"
-    >
-        <item name="autoCorrectionSpacebarLedEnabled">true</item>
-        <item name="autoCorrectionSpacebarLedIcon">@drawable/sym_keyboard_space_led</item>
-        <item name="spacebarTextRatio">@fraction/spacebar_text_ratio</item>
-        <item name="spacebarTextColor">@color/spacebar_text_color_default</item>
-        <item name="spacebarTextShadowColor">@color/spacebar_text_shadow_color_default</item>
-    </style>
-    <!-- Theme "Stone" -->
-    <style
-        name="Keyboard.Stone"
-        parent="Keyboard"
-    >
-        <!-- This should be aligned with KeyboardSwitcher.KEYBOARD_THEMES[] -->
-        <item name="themeId">6</item>
-        <item name="keyboardTopPadding">@fraction/keyboard_top_padding_stone</item>
-        <item name="keyboardBottomPadding">@fraction/keyboard_bottom_padding_stone</item>
-        <item name="horizontalGap">@fraction/key_horizontal_gap_stone</item>
-        <item name="verticalGap">@fraction/key_bottom_gap_stone</item>
-    </style>
-    <style
-        name="KeyboardView.Stone"
-        parent="KeyboardView"
-    >
-        <item name="keyBackground">@drawable/btn_keyboard_key_stone</item>
-        <item name="keyTextColor">@color/key_text_color_stone</item>
-        <item name="keyTextInactivatedColor">@color/key_text_inactivated_color_stone</item>
-        <item name="keyHintLetterColor">@color/key_hint_letter_color_stone</item>
-        <item name="keyHintLabelColor">@color/key_hint_label_color_stone</item>
-        <item name="keyShiftedLetterHintInactivatedColor">@color/key_shifted_letter_hint_inactivated_color_stone</item>
-        <item name="keyShiftedLetterHintActivatedColor">@color/key_shifted_letter_hint_activated_color_stone</item>
-        <item name="keyTextShadowColor">@color/key_text_shadow_color_stone</item>
-    </style>
-    <style
-        name="MainKeyboardView.Stone"
-        parent="KeyboardView.Stone"
-    >
-        <item name="autoCorrectionSpacebarLedEnabled">true</item>
-        <item name="autoCorrectionSpacebarLedIcon">@drawable/sym_keyboard_space_led</item>
-        <item name="spacebarTextRatio">@fraction/spacebar_text_ratio</item>
-        <item name="spacebarTextColor">@color/spacebar_text_color_stone</item>
-        <item name="spacebarTextShadowColor">@color/spacebar_text_shadow_color_stone</item>
-    </style>
-    <style
-        name="MoreKeysKeyboard.Stone"
-        parent="Keyboard.Stone"
-    >
-        <item name="keyboardTopPadding">0%p</item>
-        <item name="keyboardBottomPadding">0%p</item>
-        <item name="horizontalGap">0%p</item>
-        <item name="touchPositionCorrectionData">@null</item>
-    </style>
-    <style
-        name="MoreKeysKeyboardView.Stone"
-        parent="MoreKeysKeyboardView"
-    >
-        <item name="keyBackground">@drawable/btn_keyboard_key_stone</item>
-        <item name="keyTextColor">@color/key_text_color_stone</item>
-        <item name="keyTextShadowColor">@color/key_text_shadow_color_stone</item>
-    </style>
-    <!-- Theme "Stone bold" -->
-    <style
-        name="Keyboard.Stone.Bold"
-        parent="Keyboard.Stone"
-    >
-        <!-- This should be aligned with KeyboardSwitcher.KEYBOARD_THEMES[] -->
-        <item name="themeId">7</item>
-    </style>
-    <style
-        name="KeyboardView.Stone.Bold"
-        parent="KeyboardView.Stone"
-    >
-        <item name="keyTypeface">bold</item>
-    </style>
-    <style
-        name="MainKeyboardView.Stone.Bold"
-        parent="KeyboardView.Stone.Bold"
-    >
-        <item name="autoCorrectionSpacebarLedEnabled">true</item>
-        <item name="autoCorrectionSpacebarLedIcon">@drawable/sym_keyboard_space_led</item>
-        <item name="spacebarTextRatio">@fraction/spacebar_text_ratio</item>
-        <item name="spacebarTextColor">@color/spacebar_text_color_stone</item>
-        <item name="spacebarTextShadowColor">@color/spacebar_text_shadow_color_stone</item>
-    </style>
-    <!-- Theme "Gingerbread" -->
-    <style
-        name="Keyboard.Gingerbread"
-        parent="Keyboard"
-    >
-        <!-- This should be aligned with KeyboardSwitcher.KEYBOARD_THEMES[] -->
-        <item name="themeId">8</item>
-        <item name="touchPositionCorrectionData">@array/touch_position_correction_data_gingerbread</item>
-        <item name="horizontalGap">@fraction/key_horizontal_gap_gb</item>
-        <item name="verticalGap">@fraction/key_bottom_gap_gb</item>
-    </style>
-    <style
-        name="KeyboardView.Gingerbread"
-        parent="KeyboardView"
-    >
-        <item name="android:background">@drawable/keyboard_dark_background</item>
-        <item name="keyBackground">@drawable/btn_keyboard_key_gingerbread</item>
-        <item name="keyTypeface">bold</item>
-    </style>
-    <style
-        name="MainKeyboardView.Gingerbread"
-        parent="KeyboardView.Gingerbread"
-    >
-        <item name="autoCorrectionSpacebarLedEnabled">true</item>
-        <item name="autoCorrectionSpacebarLedIcon">@drawable/sym_keyboard_space_led</item>
-        <item name="spacebarTextRatio">@fraction/spacebar_text_ratio</item>
-        <item name="spacebarTextColor">@color/spacebar_text_color_default</item>
-        <item name="spacebarTextShadowColor">@color/spacebar_text_shadow_color_default</item>
-    </style>
-    <style
-        name="MoreKeysKeyboard.Gingerbread"
-        parent="Keyboard.Gingerbread"
-    >
-        <item name="keyboardTopPadding">0%p</item>
-        <item name="keyboardBottomPadding">0%p</item>
-        <item name="horizontalGap">0%p</item>
-        <item name="touchPositionCorrectionData">@null</item>
-    </style>
-    <style
-        name="MoreKeysKeyboardView.Gingerbread"
-        parent="MoreKeysKeyboardView"
-    >
-        <item name="android:background">@null</item>
-    </style>
-    <!-- Theme "IceCreamSandwich" -->
-    <style
-        name="Keyboard.IceCreamSandwich"
-        parent="Keyboard"
-    >
-        <!-- This should be aligned with KeyboardSwitcher.KEYBOARD_THEMES[] -->
-        <item name="themeId">5</item>
-        <item name="keyboardTopPadding">@fraction/keyboard_top_padding_ics</item>
-        <item name="keyboardBottomPadding">@fraction/keyboard_bottom_padding_ics</item>
-        <item name="horizontalGap">@fraction/key_horizontal_gap_ics</item>
-        <item name="verticalGap">@fraction/key_bottom_gap_ics</item>
-        <item name="touchPositionCorrectionData">@array/touch_position_correction_data_ice_cream_sandwich</item>
-    </style>
-    <style
-        name="KeyboardView.IceCreamSandwich"
-        parent="KeyboardView"
-    >
-        <item name="android:background">@drawable/keyboard_background_holo</item>
-        <item name="keyBackground">@drawable/btn_keyboard_key_ics</item>
-        <item name="keyTypeface">bold</item>
-        <item name="keyTextInactivatedColor">@color/key_text_inactivated_color_ics</item>
-        <item name="keyHintLetterColor">@color/key_hint_letter_color_ics</item>
-        <item name="keyHintLabelColor">@color/key_hint_label_color_ics</item>
-        <item name="keyShiftedLetterHintInactivatedColor">@color/key_shifted_letter_hint_inactivated_color_ics</item>
-        <item name="keyShiftedLetterHintActivatedColor">@color/key_shifted_letter_hint_activated_color_ics</item>
-        <item name="keyPreviewLayout">@layout/key_preview_ics</item>
-        <item name="keyPreviewTextColor">@color/key_text_color_ics</item>
-        <item name="keyPreviewOffset">@dimen/key_preview_offset_ics</item>
-        <item name="keyTextShadowColor">@color/key_text_shadow_color_ics</item>
-        <item name="keyTextShadowRadius">0.0</item>
-        <item name="slidingKeyInputPreviewColor">@color/highlight_translucent_color_ics</item>
-        <item name="gestureFloatingPreviewTextColor">@color/highlight_color_ics</item>
-        <item name="gestureTrailColor">@color/highlight_color_ics</item>
-    </style>
-    <style
-        name="MainKeyboardView.IceCreamSandwich"
-        parent="KeyboardView.IceCreamSandwich"
-    >
-        <item name="autoCorrectionSpacebarLedEnabled">false</item>
-        <item name="autoCorrectionSpacebarLedIcon">@drawable/sym_keyboard_space_led_holo</item>
-        <item name="spacebarTextRatio">@fraction/spacebar_text_ratio</item>
-        <item name="spacebarTextColor">@color/spacebar_text_color_ics</item>
-        <item name="spacebarTextShadowColor">@color/spacebar_text_shadow_color_ics</item>
-    </style>
-    <style
-        name="MoreKeysKeyboard.IceCreamSandwich"
-        parent="Keyboard.IceCreamSandwich"
-    >
-        <item name="keyboardTopPadding">0%p</item>
-        <item name="keyboardBottomPadding">0%p</item>
-        <item name="horizontalGap">0%p</item>
-        <item name="touchPositionCorrectionData">@null</item>
-    </style>
-    <style
-        name="MoreKeysKeyboardView.IceCreamSandwich"
-        parent="MoreKeysKeyboardView"
-    >
-        <item name="android:background">@null</item>
-        <item name="keyBackground">@drawable/btn_keyboard_key_popup_ics</item>
-        <item name="verticalCorrection">@dimen/more_keys_keyboard_vertical_correction_ics</item>
-    </style>
-    <style name="MoreKeysKeyboardPanelStyle.IceCreamSandwich">
-        <item name="android:background">@drawable/keyboard_popup_panel_background_holo</item>
-    </style>
-    <style
-        name="SuggestionStripViewStyle.IceCreamSandwich"
-    >
-        <item name="android:background">@drawable/keyboard_suggest_strip_holo</item>
-        <item name="suggestionStripOption">autoCorrectBold|validTypedWordBold</item>
-        <item name="colorValidTypedWord">@color/highlight_color_ics</item>
-        <item name="colorTypedWord">@color/highlight_color_ics</item>
-        <item name="colorAutoCorrect">@color/highlight_color_ics</item>
-        <item name="colorSuggested">@color/highlight_color_ics</item>
-        <item name="alphaValidTypedWord">85%</item>
-        <item name="alphaTypedWord">85%</item>
-        <item name="alphaSuggested">70%</item>
-        <item name="alphaObsoleted">70%</item>
-        <item name="suggestionsCountInStrip">@integer/suggestions_count_in_strip</item>
-        <item name="centerSuggestionPercentile">@fraction/center_suggestion_percentile</item>
-        <item name="maxMoreSuggestionsRow">@integer/max_more_suggestions_row</item>
-        <item name="minMoreSuggestionsWidth">@fraction/min_more_suggestions_width</item>
-    </style>
-    <style
-        name="MoreSuggestionsViewStyle.IceCreamSandwich"
-        parent="MoreKeysKeyboardView.IceCreamSandwich"
-    >
-    </style>
-    <style name="SuggestionBackgroundStyle.IceCreamSandwich">
-        <item name="android:background">@drawable/btn_suggestion_ics</item>
-    </style>
-    <style
-        name="SuggestionPreviewBackgroundStyle.IceCreamSandwich"
-        parent="MoreKeysKeyboardPanelStyle.IceCreamSandwich"
-    >
-    </style>
-    <style name="MoreKeysKeyboardAnimation">
-        <item name="android:windowEnterAnimation">@anim/more_keys_keyboard_fadein</item>
-        <item name="android:windowExitAnimation">@anim/more_keys_keyboard_fadeout</item>
-    </style>
-</resources>
diff --git a/java/res/values/themes-basic-highcontrast.xml b/java/res/values/themes-basic-highcontrast.xml
deleted file mode 100644
index e81d473..0000000
--- a/java/res/values/themes-basic-highcontrast.xml
+++ /dev/null
@@ -1,29 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2011 The Android Open Source Project
-
-     Licensed under the Apache License, Version 2.0 (the "License");
-     you may not use this file except in compliance with the License.
-     You may obtain a copy of the License at
-
-          http://www.apache.org/licenses/LICENSE-2.0
-
-     Unless required by applicable law or agreed to in writing, software
-     distributed under the License is distributed on an "AS IS" BASIS,
-     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-     See the License for the specific language governing permissions and
-     limitations under the License.
--->
-
-<resources>
-    <style name="KeyboardTheme.HighContrast" parent="KeyboardIcons">
-        <item name="keyboardStyle">@style/Keyboard.HighContrast</item>
-        <item name="keyboardViewStyle">@style/KeyboardView.HighContrast</item>
-        <item name="mainKeyboardViewStyle">@style/MainKeyboardView.HighContrast</item>
-        <item name="moreKeysKeyboardStyle">@style/MoreKeysKeyboard</item>
-        <item name="moreKeysKeyboardViewStyle">@style/MoreKeysKeyboardView</item>
-        <item name="moreKeysKeyboardPanelStyle">@style/MoreKeysKeyboardPanelStyle</item>
-        <item name="suggestionStripViewStyle">@style/SuggestionStripViewStyle</item>
-        <item name="moreSuggestionsViewStyle">@style/MoreSuggestionsViewStyle</item>
-        <item name="suggestionBackgroundStyle">@style/SuggestionBackgroundStyle</item>
-    </style>
-</resources>
diff --git a/java/res/values/themes-basic.xml b/java/res/values/themes-basic.xml
deleted file mode 100644
index c44f0f6..0000000
--- a/java/res/values/themes-basic.xml
+++ /dev/null
@@ -1,29 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2011 The Android Open Source Project
-
-     Licensed under the Apache License, Version 2.0 (the "License");
-     you may not use this file except in compliance with the License.
-     You may obtain a copy of the License at
-
-          http://www.apache.org/licenses/LICENSE-2.0
-
-     Unless required by applicable law or agreed to in writing, software
-     distributed under the License is distributed on an "AS IS" BASIS,
-     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-     See the License for the specific language governing permissions and
-     limitations under the License.
--->
-
-<resources>
-    <style name="KeyboardTheme" parent="KeyboardIcons">
-        <item name="keyboardStyle">@style/Keyboard</item>
-        <item name="keyboardViewStyle">@style/KeyboardView</item>
-        <item name="mainKeyboardViewStyle">@style/MainKeyboardView</item>
-        <item name="moreKeysKeyboardStyle">@style/MoreKeysKeyboard</item>
-        <item name="moreKeysKeyboardViewStyle">@style/MoreKeysKeyboardView</item>
-        <item name="moreKeysKeyboardPanelStyle">@style/MoreKeysKeyboardPanelStyle</item>
-        <item name="suggestionStripViewStyle">@style/SuggestionStripViewStyle</item>
-        <item name="moreSuggestionsViewStyle">@style/MoreSuggestionsViewStyle</item>
-        <item name="suggestionBackgroundStyle">@style/SuggestionBackgroundStyle</item>
-    </style>
-</resources>
diff --git a/java/res/values/themes-common.xml b/java/res/values/themes-common.xml
new file mode 100644
index 0000000..8e9cfc9
--- /dev/null
+++ b/java/res/values/themes-common.xml
@@ -0,0 +1,131 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+**
+** Copyright 2013, 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:android="http://schemas.android.com/apk/res/android">
+    <style name="KeyboardIcons" />
+    <!-- Default theme values -->
+    <style name="Keyboard">
+        <item name="touchPositionCorrectionData">@array/touch_position_correction_data_default</item>
+        <item name="rowHeight">25%p</item>
+        <item name="moreKeysTemplate">@xml/kbd_more_keys_keyboard_template</item>
+        <item name="keyboardLeftPadding">@fraction/keyboard_left_padding</item>
+        <item name="keyboardRightPadding">@fraction/keyboard_right_padding</item>
+        <item name="maxMoreKeysColumn">@integer/config_max_more_keys_column</item>
+    </style>
+    <style name="KeyboardView">
+        <item name="keyBackground">@drawable/btn_keyboard_key_ics</item>
+        <item name="keyLetterSize">@fraction/key_letter_ratio</item>
+        <item name="keyLargeLetterRatio">@fraction/key_large_letter_ratio</item>
+        <item name="keyLabelSize">@fraction/key_label_ratio</item>
+        <item name="keyLargeLabelRatio">@fraction/key_large_label_ratio</item>
+        <item name="keyHintLetterRatio">@fraction/key_hint_letter_ratio</item>
+        <item name="keyHintLabelRatio">@fraction/key_hint_label_ratio</item>
+        <item name="keyShiftedLetterHintRatio">@fraction/key_uppercase_letter_ratio</item>
+        <item name="keyTypeface">normal</item>
+        <item name="keyLabelHorizontalPadding">@dimen/key_label_horizontal_padding</item>
+        <item name="keyHintLetterPadding">@dimen/key_hint_letter_padding</item>
+        <item name="keyPopupHintLetterPadding">@dimen/key_popup_hint_letter_padding</item>
+        <item name="keyShiftedLetterHintPadding">@dimen/key_uppercase_letter_padding</item>
+        <item name="keyPreviewTextRatio">@fraction/key_preview_text_ratio</item>
+        <item name="verticalCorrection">@dimen/keyboard_vertical_correction</item>
+        <item name="backgroundDimAlpha">128</item>
+        <item name="gestureFloatingPreviewTextSize">@dimen/gesture_floating_preview_text_size</item>
+        <item name="gestureFloatingPreviewTextOffset">@dimen/gesture_floating_preview_text_offset</item>
+        <item name="gestureFloatingPreviewHorizontalPadding">@dimen/gesture_floating_preview_horizontal_padding</item>
+        <item name="gestureFloatingPreviewVerticalPadding">@dimen/gesture_floating_preview_vertical_padding</item>
+        <item name="gestureFloatingPreviewRoundRadius">@dimen/gesture_floating_preview_round_radius</item>
+        <item name="gestureTrailMinSamplingDistance">@dimen/gesture_trail_min_sampling_distance</item>
+        <item name="gestureTrailMaxInterpolationAngularThreshold">@integer/gesture_trail_max_interpolation_angular_threshold</item>
+        <item name="gestureTrailMaxInterpolationDistanceThreshold">@dimen/gesture_trail_max_interpolation_distance_threshold</item>
+        <item name="gestureTrailMaxInterpolationSegments">@integer/gesture_trail_max_interpolation_segments</item>
+        <item name="gestureTrailFadeoutStartDelay">@integer/config_gesture_trail_fadeout_start_delay</item>
+        <item name="gestureTrailFadeoutDuration">@integer/config_gesture_trail_fadeout_duration</item>
+        <item name="gestureTrailUpdateInterval">@integer/config_gesture_trail_update_interval</item>
+        <item name="gestureTrailStartWidth">@dimen/gesture_trail_start_width</item>
+        <item name="gestureTrailEndWidth">@dimen/gesture_trail_end_width</item>
+        <item name="gestureTrailBodyRatio">@integer/gesture_trail_body_ratio</item>
+        <item name="gestureTrailShadowRatio">@integer/gesture_trail_shadow_ratio</item>
+        <!-- Common attributes of MainKeyboardView -->
+        <item name="keyHysteresisDistance">@dimen/config_key_hysteresis_distance</item>
+        <item name="keyHysteresisDistanceForSlidingModifier">@dimen/config_key_hysteresis_distance_for_sliding_modifier</item>
+        <item name="touchNoiseThresholdTime">@integer/config_touch_noise_threshold_time</item>
+        <item name="touchNoiseThresholdDistance">@dimen/config_touch_noise_threshold_distance</item>
+        <item name="slidingKeyInputEnable">@bool/config_sliding_key_input_enabled</item>
+        <item name="slidingKeyInputPreviewWidth">@dimen/config_sliding_key_input_preview_width</item>
+        <item name="slidingKeyInputPreviewBodyRatio">@integer/config_sliding_key_input_preview_body_ratio</item>
+        <item name="slidingKeyInputPreviewShadowRatio">@integer/config_sliding_key_input_preview_shadow_ratio</item>
+        <item name="keyRepeatStartTimeout">@integer/config_key_repeat_start_timeout</item>
+        <item name="keyRepeatInterval">@integer/config_key_repeat_interval</item>
+        <item name="longPressShiftLockTimeout">@integer/config_longpress_shift_lock_timeout</item>
+        <item name="ignoreAltCodeKeyTimeout">@integer/config_ignore_alt_code_key_timeout</item>
+        <item name="keyPreviewHeight">@dimen/key_preview_height</item>
+        <item name="keyPreviewLingerTimeout">@integer/config_key_preview_linger_timeout</item>
+        <item name="moreKeysKeyboardLayout">@layout/more_keys_keyboard</item>
+        <item name="showMoreKeysKeyboardAtTouchedPoint">@bool/config_show_more_keys_keyboard_at_touched_point</item>
+        <item name="spacebarTextRatio">@fraction/spacebar_text_ratio</item>
+        <item name="languageOnSpacebarFinalAlpha">@integer/config_language_on_spacebar_final_alpha</item>
+        <item name="languageOnSpacebarFadeoutAnimator">@anim/language_on_spacebar_fadeout</item>
+        <!-- Remove animations for now because it could drain a non-negligible amount of battery while typing.
+        <item name="altCodeKeyWhileTypingFadeoutAnimator">@anim/alt_code_key_while_typing_fadeout</item>
+        <item name="altCodeKeyWhileTypingFadeinAnimator">@anim/alt_code_key_while_typing_fadein</item>
+        -->
+        <!-- Common attributes of MainKeyboardView for gesture typing detection and recognition -->
+        <item name="gestureFloatingPreviewTextLingerTimeout">@integer/config_gesture_floating_preview_text_linger_timeout</item>
+        <item name="gestureStaticTimeThresholdAfterFastTyping">@integer/config_gesture_static_time_threshold_after_fast_typing</item>
+        <item name="gestureDetectFastMoveSpeedThreshold">@fraction/config_gesture_detect_fast_move_speed_threshold</item>
+        <item name="gestureDynamicThresholdDecayDuration">@integer/config_gesture_dynamic_threshold_decay_duration</item>
+        <item name="gestureDynamicTimeThresholdFrom">@integer/config_gesture_dynamic_time_threshold_from</item>
+        <item name="gestureDynamicTimeThresholdTo">@integer/config_gesture_dynamic_time_threshold_to</item>
+        <item name="gestureDynamicDistanceThresholdFrom">@fraction/config_gesture_dynamic_distance_threshold_from</item>
+        <item name="gestureDynamicDistanceThresholdTo">@fraction/config_gesture_dynamic_distance_threshold_to</item>
+        <item name="gestureSamplingMinimumDistance">@fraction/config_gesture_sampling_minimum_distance</item>
+        <item name="gestureRecognitionMinimumTime">@integer/config_gesture_recognition_minimum_time</item>
+        <item name="gestureRecognitionUpdateTime">@integer/config_gesture_recognition_update_time</item>
+        <item name="gestureRecognitionSpeedThreshold">@fraction/config_gesture_recognition_speed_threshold</item>
+        <item name="suppressKeyPreviewAfterBatchInputDuration">@integer/config_suppress_key_preview_after_batch_input_duration</item>
+    </style>
+    <style
+        name="MainKeyboardView"
+        parent="KeyboardView" />
+    <!-- Though {@link EmojiKeyboardView} doesn't extend {@link KeyboardView}, some views inside it,
+         for instance delete button, need themed {@link KeyboardView} attributes. -->
+    <style
+        name="EmojiKeyboardView"
+        parent="KeyboardView"
+    >
+        <item name="emojiTabLabelColor">@color/emoji_tab_label_color_ics</item>
+    </style>
+    <style name="MoreKeysKeyboard" />
+    <style
+        name="MoreKeysKeyboardView"
+        parent="MainKeyboardView" />
+    <style name="MoreKeysKeyboardContainer" />
+    <style name="SuggestionStripView">
+        <item name="suggestionsCountInStrip">@integer/suggestions_count_in_strip</item>
+        <item name="centerSuggestionPercentile">@fraction/center_suggestion_percentile</item>
+        <item name="maxMoreSuggestionsRow">@integer/max_more_suggestions_row</item>
+        <item name="minMoreSuggestionsWidth">@fraction/min_more_suggestions_width</item>
+    </style>
+    <style name="SuggestionWord" />
+    <style name="MoreKeysKeyboardAnimation">
+        <item name="android:windowEnterAnimation">@anim/more_keys_keyboard_fadein</item>
+        <item name="android:windowExitAnimation">@anim/more_keys_keyboard_fadeout</item>
+    </style>
+</resources>
\ No newline at end of file
diff --git a/java/res/values/themes-gb.xml b/java/res/values/themes-gb.xml
new file mode 100644
index 0000000..d9ac4ac
--- /dev/null
+++ b/java/res/values/themes-gb.xml
@@ -0,0 +1,146 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+**
+** Copyright 2011, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+**     http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+-->
+
+<resources>
+    <style name="KeyboardTheme.GB" parent="KeyboardIcons.GB">
+        <item name="keyboardStyle">@style/Keyboard.GB</item>
+        <item name="keyboardViewStyle">@style/KeyboardView.GB</item>
+        <item name="mainKeyboardViewStyle">@style/MainKeyboardView.GB</item>
+        <item name="emojiKeyboardViewStyle">@style/EmojiKeyboardView.GB</item>
+        <item name="moreKeysKeyboardStyle">@style/MoreKeysKeyboard.GB</item>
+        <item name="moreKeysKeyboardViewStyle">@style/MoreKeysKeyboardView.GB</item>
+        <item name="moreKeysKeyboardContainerStyle">@style/MoreKeysKeyboardContainer.GB</item>
+        <item name="suggestionStripViewStyle">@style/SuggestionStripView.GB</item>
+        <item name="suggestionWordStyle">@style/SuggestionWord.GB</item>
+    </style>
+    <style name="KeyboardIcons.GB">
+        <!-- Keyboard icons -->
+        <item name="iconShiftKey">@drawable/sym_keyboard_shift_holo_dark</item>
+        <item name="iconDeleteKey">@drawable/sym_keyboard_delete_holo_dark</item>
+        <item name="iconSettingsKey">@drawable/sym_keyboard_settings_holo_dark</item>
+        <item name="iconSpaceKey">@drawable/sym_keyboard_space_holo_dark</item>
+        <item name="iconEnterKey">@drawable/sym_keyboard_return_holo_dark</item>
+        <item name="iconSearchKey">@drawable/sym_keyboard_search_holo_dark</item>
+        <item name="iconTabKey">@drawable/sym_keyboard_tab_holo_dark</item>
+        <item name="iconShortcutKey">@drawable/sym_keyboard_mic_holo_dark</item>
+        <item name="iconShortcutForLabel">@drawable/sym_keyboard_label_mic_holo_dark</item>
+        <item name="iconSpaceKeyForNumberLayout">@drawable/sym_keyboard_space</item>
+        <item name="iconShiftKeyShifted">@drawable/sym_keyboard_shift_locked_holo_dark</item>
+        <!-- TODO: Needs non-holo disabled shortcut icon drawable -->
+        <item name="iconShortcutKeyDisabled">@drawable/sym_keyboard_voice_off_holo_dark</item>
+        <item name="iconTabKeyPreview">@drawable/sym_keyboard_feedback_tab</item>
+        <item name="iconLanguageSwitchKey">@drawable/sym_keyboard_language_switch_dark</item>
+        <!-- TODO: Needs dedicated black theme ZWNJ and ZWJ icons -->
+        <item name="iconZwnjKey">@drawable/sym_keyboard_zwnj_holo_dark</item>
+        <item name="iconZwjKey">@drawable/sym_keyboard_zwj_holo_dark</item>
+        <item name="iconEmojiKey">@drawable/sym_keyboard_smiley_holo_dark</item>
+    </style>
+    <style
+        name="Keyboard.GB"
+        parent="Keyboard"
+    >
+        <!-- This should be aligned with KeyboardSwitcher.KEYBOARD_THEMES[] -->
+        <item name="themeId">1</item>
+        <item name="touchPositionCorrectionData">@array/touch_position_correction_data_gb</item>
+        <item name="keyboardTopPadding">@fraction/keyboard_top_padding_gb</item>
+        <item name="keyboardBottomPadding">@fraction/keyboard_bottom_padding_gb</item>
+        <item name="horizontalGap">@fraction/key_horizontal_gap_gb</item>
+        <item name="verticalGap">@fraction/key_bottom_gap_gb</item>
+    </style>
+    <style
+        name="KeyboardView.GB"
+        parent="KeyboardView"
+    >
+        <item name="android:background">@drawable/keyboard_background_gb</item>
+        <item name="keyBackground">@drawable/btn_keyboard_key_gb</item>
+        <item name="keyTypeface">bold</item>
+        <item name="keyTextColor">@color/key_text_color_gb</item>
+        <item name="keyTextInactivatedColor">@color/key_text_inactivated_color_gb</item>
+        <item name="keyHintLetterColor">@color/key_hint_letter_color_gb</item>
+        <item name="keyHintLabelColor">@color/key_hint_label_color_gb</item>
+        <item name="keyShiftedLetterHintInactivatedColor">@color/key_shifted_letter_hint_inactivated_color_gb</item>
+        <item name="keyShiftedLetterHintActivatedColor">@color/key_shifted_letter_hint_activated_color_gb</item>
+        <item name="keyPreviewTextColor">@color/key_text_color_gb</item>
+        <item name="keyTextShadowColor">@color/key_text_shadow_color_gb</item>
+        <item name="keyTextShadowRadius">2.75</item>
+    </style>
+    <style
+        name="MainKeyboardView.GB"
+        parent="KeyboardView.GB"
+    >
+        <item name="keyPreviewLayout">@layout/key_preview_gb</item>
+        <item name="keyPreviewOffset">@dimen/key_preview_offset_gb</item>
+        <item name="gestureFloatingPreviewTextColor">@color/highlight_color_gb</item>
+        <item name="gestureFloatingPreviewColor">@color/gesture_floating_preview_color_gb</item>
+        <item name="gestureTrailColor">@color/highlight_color_gb</item>
+        <item name="slidingKeyInputPreviewColor">@color/highlight_translucent_color_gb</item>
+        <item name="autoCorrectionSpacebarLedEnabled">true</item>
+        <item name="autoCorrectionSpacebarLedIcon">@drawable/sym_keyboard_space_led_gb</item>
+        <item name="spacebarTextColor">@color/spacebar_text_color_gb</item>
+        <item name="spacebarTextShadowColor">@color/spacebar_text_shadow_color_gb</item>
+    </style>
+    <!-- Though {@link EmojiKeyboardView} doesn't extend {@link KeyboardView}, some views inside it,
+         for instance delete button, need themed {@link KeyboardView} attributes. -->
+    <style
+        name="EmojiKeyboardView.GB"
+        parent="KeyboardView.GB"
+    >
+        <item name="keyBackground">@drawable/btn_keyboard_key_functional_gb</item>
+        <item name="emojiTabLabelColor">@color/emoji_tab_label_color_gb</item>
+    </style>
+    <style
+        name="MoreKeysKeyboard.GB"
+        parent="Keyboard.GB"
+    >
+        <item name="keyboardTopPadding">0%p</item>
+        <item name="keyboardBottomPadding">0%p</item>
+        <item name="horizontalGap">0%p</item>
+        <item name="touchPositionCorrectionData">@null</item>
+    </style>
+    <style
+        name="MoreKeysKeyboardView.GB"
+        parent="KeyboardView.GB"
+    >
+        <item name="android:background">@null</item>
+        <item name="keyBackground">@drawable/btn_keyboard_key_popup_gb</item>
+        <item name="keyTypeface">normal</item>
+        <item name="verticalCorrection">@dimen/more_keys_keyboard_vertical_correction_gb</item>
+    </style>
+    <style
+        name="MoreKeysKeyboardContainer.GB"
+    >
+        <item name="android:background">@drawable/keyboard_popup_panel_background_gb</item>
+    </style>
+    <style
+        name="SuggestionStripView.GB"
+        parent="SuggestionStripView"
+    >
+        <item name="android:background">@drawable/keyboard_suggest_strip_gb</item>
+        <item name="suggestionStripOption">autoCorrectBold|validTypedWordBold</item>
+        <item name="colorValidTypedWord">@color/highlight_color_gb</item>
+        <item name="colorTypedWord">@color/typed_word_color_gb</item>
+        <item name="colorAutoCorrect">@color/highlight_color_gb</item>
+        <item name="colorSuggested">@color/highlight_color_gb</item>
+        <item name="alphaObsoleted">50%</item>
+    </style>
+    <style name="SuggestionWord.GB">
+        <item name="android:background">@drawable/btn_suggestion_gb</item>
+    </style>
+</resources>
diff --git a/java/res/values/themes-gingerbread.xml b/java/res/values/themes-gingerbread.xml
deleted file mode 100644
index 129afdf..0000000
--- a/java/res/values/themes-gingerbread.xml
+++ /dev/null
@@ -1,29 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2011 The Android Open Source Project
-
-     Licensed under the Apache License, Version 2.0 (the "License");
-     you may not use this file except in compliance with the License.
-     You may obtain a copy of the License at
-
-          http://www.apache.org/licenses/LICENSE-2.0
-
-     Unless required by applicable law or agreed to in writing, software
-     distributed under the License is distributed on an "AS IS" BASIS,
-     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-     See the License for the specific language governing permissions and
-     limitations under the License.
--->
-
-<resources>
-    <style name="KeyboardTheme.Gingerbread" parent="KeyboardIcons">
-        <item name="keyboardStyle">@style/Keyboard.Gingerbread</item>
-        <item name="keyboardViewStyle">@style/KeyboardView.Gingerbread</item>
-        <item name="mainKeyboardViewStyle">@style/MainKeyboardView.Gingerbread</item>
-        <item name="moreKeysKeyboardStyle">@style/MoreKeysKeyboard.Gingerbread</item>
-        <item name="moreKeysKeyboardViewStyle">@style/MoreKeysKeyboardView.Gingerbread</item>
-        <item name="moreKeysKeyboardPanelStyle">@style/MoreKeysKeyboardPanelStyle</item>
-        <item name="suggestionStripViewStyle">@style/SuggestionStripViewStyle</item>
-        <item name="moreSuggestionsViewStyle">@style/MoreSuggestionsViewStyle</item>
-        <item name="suggestionBackgroundStyle">@style/SuggestionBackgroundStyle</item>
-    </style>
-</resources>
diff --git a/java/res/values/themes-ics.xml b/java/res/values/themes-ics.xml
index 1264831..33dd50c 100644
--- a/java/res/values/themes-ics.xml
+++ b/java/res/values/themes-ics.xml
@@ -1,29 +1,147 @@
 <?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2011 The Android Open Source Project
-
-     Licensed under the Apache License, Version 2.0 (the "License");
-     you may not use this file except in compliance with the License.
-     You may obtain a copy of the License at
-
-          http://www.apache.org/licenses/LICENSE-2.0
-
-     Unless required by applicable law or agreed to in writing, software
-     distributed under the License is distributed on an "AS IS" BASIS,
-     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-     See the License for the specific language governing permissions and
-     limitations under the License.
+<!--
+/*
+**
+** Copyright 2011, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+**     http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
 -->
 
-<resources>
-    <style name="KeyboardTheme.IceCreamSandwich" parent="KeyboardIcons.IceCreamSandwich">
-        <item name="keyboardStyle">@style/Keyboard.IceCreamSandwich</item>
-        <item name="keyboardViewStyle">@style/KeyboardView.IceCreamSandwich</item>
-        <item name="mainKeyboardViewStyle">@style/MainKeyboardView.IceCreamSandwich</item>
-        <item name="moreKeysKeyboardStyle">@style/MoreKeysKeyboard.IceCreamSandwich</item>
-        <item name="moreKeysKeyboardViewStyle">@style/MoreKeysKeyboardView.IceCreamSandwich</item>
-        <item name="moreKeysKeyboardPanelStyle">@style/MoreKeysKeyboardPanelStyle.IceCreamSandwich</item>
-        <item name="suggestionStripViewStyle">@style/SuggestionStripViewStyle.IceCreamSandwich</item>
-        <item name="moreSuggestionsViewStyle">@style/MoreSuggestionsViewStyle.IceCreamSandwich</item>
-        <item name="suggestionBackgroundStyle">@style/SuggestionBackgroundStyle.IceCreamSandwich</item>
+<resources xmlns:android="http://schemas.android.com/apk/res/android">
+    <style name="KeyboardTheme.ICS" parent="KeyboardIcons.ICS">
+        <item name="keyboardStyle">@style/Keyboard.ICS</item>
+        <item name="keyboardViewStyle">@style/KeyboardView.ICS</item>
+        <item name="mainKeyboardViewStyle">@style/MainKeyboardView.ICS</item>
+        <item name="emojiKeyboardViewStyle">@style/EmojiKeyboardView.ICS</item>
+        <item name="moreKeysKeyboardStyle">@style/MoreKeysKeyboard.ICS</item>
+        <item name="moreKeysKeyboardViewStyle">@style/MoreKeysKeyboardView.ICS</item>
+        <item name="moreKeysKeyboardContainerStyle">@style/MoreKeysKeyboardContainer.ICS</item>
+        <item name="suggestionStripViewStyle">@style/SuggestionStripView.ICS</item>
+        <item name="suggestionWordStyle">@style/SuggestionWord.ICS</item>
+    </style>
+    <style name="KeyboardIcons.ICS">
+        <!-- Keyboard icons -->
+        <!-- TODO: The following holo icon for phone (drawable-hdpi and drawable-xhdpi) are missing.
+             sym_keyboard_123_mic_holo
+             -->
+        <item name="iconShiftKey">@drawable/sym_keyboard_shift_holo_dark</item>
+        <item name="iconDeleteKey">@drawable/sym_keyboard_delete_holo_dark</item>
+        <item name="iconSettingsKey">@drawable/sym_keyboard_settings_holo_dark</item>
+        <item name="iconSpaceKey">@null</item>
+        <item name="iconEnterKey">@drawable/sym_keyboard_return_holo_dark</item>
+        <item name="iconSearchKey">@drawable/sym_keyboard_search_holo_dark</item>
+        <item name="iconTabKey">@drawable/sym_keyboard_tab_holo_dark</item>
+        <item name="iconShortcutKey">@drawable/sym_keyboard_voice_holo_dark</item>
+        <item name="iconShortcutForLabel">@drawable/sym_keyboard_label_mic_holo_dark</item>
+        <item name="iconSpaceKeyForNumberLayout">@drawable/sym_keyboard_space_holo_dark</item>
+        <item name="iconShiftKeyShifted">@drawable/sym_keyboard_shift_locked_holo_dark</item>
+        <item name="iconShortcutKeyDisabled">@drawable/sym_keyboard_voice_off_holo_dark</item>
+        <item name="iconTabKeyPreview">@drawable/sym_keyboard_feedback_tab</item>
+        <item name="iconLanguageSwitchKey">@drawable/sym_keyboard_language_switch_dark</item>
+        <item name="iconZwnjKey">@drawable/sym_keyboard_zwnj_holo_dark</item>
+        <item name="iconZwjKey">@drawable/sym_keyboard_zwj_holo_dark</item>
+        <item name="iconEmojiKey">@drawable/sym_keyboard_smiley_holo_dark</item>
+    </style>
+    <style
+        name="Keyboard.ICS"
+        parent="Keyboard"
+    >
+        <!-- This should be aligned with KeyboardSwitcher.KEYBOARD_THEMES[] -->
+        <item name="themeId">0</item>
+        <item name="keyboardTopPadding">@fraction/keyboard_top_padding_ics</item>
+        <item name="keyboardBottomPadding">@fraction/keyboard_bottom_padding_ics</item>
+        <item name="horizontalGap">@fraction/key_horizontal_gap_ics</item>
+        <item name="verticalGap">@fraction/key_bottom_gap_ics</item>
+        <item name="touchPositionCorrectionData">@array/touch_position_correction_data_ics</item>
+    </style>
+    <style
+        name="KeyboardView.ICS"
+        parent="KeyboardView"
+    >
+        <item name="android:background">@drawable/keyboard_background_holo</item>
+        <item name="keyBackground">@drawable/btn_keyboard_key_ics</item>
+        <item name="keyTypeface">bold</item>
+        <item name="keyTextColor">@color/key_text_color_ics</item>
+        <item name="keyTextInactivatedColor">@color/key_text_inactivated_color_ics</item>
+        <item name="keyHintLetterColor">@color/key_hint_letter_color_ics</item>
+        <item name="keyHintLabelColor">@color/key_hint_label_color_ics</item>
+        <item name="keyShiftedLetterHintInactivatedColor">@color/key_shifted_letter_hint_inactivated_color_ics</item>
+        <item name="keyShiftedLetterHintActivatedColor">@color/key_shifted_letter_hint_activated_color_ics</item>
+        <item name="keyPreviewTextColor">@color/key_text_color_ics</item>
+        <item name="keyTextShadowColor">@color/key_text_shadow_color_ics</item>
+        <item name="keyTextShadowRadius">0.0</item>
+    </style>
+    <style
+        name="MainKeyboardView.ICS"
+        parent="KeyboardView.ICS"
+    >
+        <item name="keyPreviewLayout">@layout/key_preview_ics</item>
+        <item name="keyPreviewOffset">@dimen/key_preview_offset_ics</item>
+        <item name="gestureFloatingPreviewTextColor">@color/highlight_color_holo</item>
+        <item name="gestureFloatingPreviewColor">@color/gesture_floating_preview_color_ics</item>
+        <item name="gestureTrailColor">@color/highlight_color_holo</item>
+        <item name="slidingKeyInputPreviewColor">@color/highlight_translucent_color_holo</item>
+        <item name="autoCorrectionSpacebarLedEnabled">false</item>
+        <item name="autoCorrectionSpacebarLedIcon">@drawable/sym_keyboard_space_led_holo</item>
+        <item name="spacebarTextColor">@color/spacebar_text_color_ics</item>
+        <item name="spacebarTextShadowColor">@color/spacebar_text_shadow_color_ics</item>
+    </style>
+    <!-- Though {@link EmojiKeyboardView} doesn't extend {@link KeyboardView}, some views inside it,
+         for instance delete button, need themed {@link KeyboardView} attributes. -->
+    <style
+        name="EmojiKeyboardView.ICS"
+        parent="KeyboardView.ICS"
+    >
+        <item name="keyBackgroundEmojiFunctional">@drawable/btn_keyboard_key_functional_ics</item>
+        <item name="emojiTabLabelColor">@color/emoji_tab_label_color_ics</item>
+    </style>
+    <style
+        name="MoreKeysKeyboard.ICS"
+        parent="Keyboard.ICS"
+    >
+        <item name="keyboardTopPadding">0%p</item>
+        <item name="keyboardBottomPadding">0%p</item>
+        <item name="horizontalGap">0%p</item>
+        <item name="touchPositionCorrectionData">@null</item>
+    </style>
+    <style
+        name="MoreKeysKeyboardView.ICS"
+        parent="KeyboardView.ICS"
+    >
+        <item name="android:background">@null</item>
+        <item name="keyBackground">@drawable/btn_keyboard_key_popup_ics</item>
+        <item name="keyTypeface">normal</item>
+        <item name="verticalCorrection">@dimen/more_keys_keyboard_vertical_correction_ics</item>
+    </style>
+    <style
+        name="MoreKeysKeyboardContainer.ICS"
+    >
+        <item name="android:background">@drawable/keyboard_popup_panel_background_holo</item>
+    </style>
+    <style
+        name="SuggestionStripView.ICS"
+        parent="SuggestionStripView"
+    >
+        <item name="android:background">@drawable/keyboard_suggest_strip_holo</item>
+        <item name="suggestionStripOption">autoCorrectBold|validTypedWordBold</item>
+        <item name="colorValidTypedWord">@color/typed_word_color_holo</item>
+        <item name="colorTypedWord">@color/typed_word_color_holo</item>
+        <item name="colorAutoCorrect">@color/highlight_color_holo</item>
+        <item name="colorSuggested">@color/suggested_word_color_holo</item>
+        <item name="alphaObsoleted">70%</item>
+    </style>
+    <style name="SuggestionWord.ICS">
+        <item name="android:background">@drawable/btn_suggestion_ics</item>
     </style>
 </resources>
diff --git a/java/res/values/themes-stone-bold.xml b/java/res/values/themes-stone-bold.xml
deleted file mode 100644
index 196f3ef..0000000
--- a/java/res/values/themes-stone-bold.xml
+++ /dev/null
@@ -1,29 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2011 The Android Open Source Project
-
-     Licensed under the Apache License, Version 2.0 (the "License");
-     you may not use this file except in compliance with the License.
-     You may obtain a copy of the License at
-
-          http://www.apache.org/licenses/LICENSE-2.0
-
-     Unless required by applicable law or agreed to in writing, software
-     distributed under the License is distributed on an "AS IS" BASIS,
-     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-     See the License for the specific language governing permissions and
-     limitations under the License.
--->
-
-<resources>
-    <style name="KeyboardTheme.Stone.Bold" parent="KeyboardIcons.Black">
-        <item name="keyboardStyle">@style/Keyboard.Stone.Bold</item>
-        <item name="keyboardViewStyle">@style/KeyboardView.Stone.Bold</item>
-        <item name="mainKeyboardViewStyle">@style/MainKeyboardView.Stone.Bold</item>
-        <item name="moreKeysKeyboardStyle">@style/MoreKeysKeyboard.Stone</item>
-        <item name="moreKeysKeyboardViewStyle">@style/MoreKeysKeyboardView.Stone</item>
-        <item name="moreKeysKeyboardPanelStyle">@style/MoreKeysKeyboardPanelStyle</item>
-        <item name="suggestionStripViewStyle">@style/SuggestionStripViewStyle</item>
-        <item name="moreSuggestionsViewStyle">@style/MoreSuggestionsViewStyle</item>
-        <item name="suggestionBackgroundStyle">@style/SuggestionBackgroundStyle</item>
-    </style>
-</resources>
diff --git a/java/res/values/themes-stone.xml b/java/res/values/themes-stone.xml
deleted file mode 100644
index d0d35c2..0000000
--- a/java/res/values/themes-stone.xml
+++ /dev/null
@@ -1,29 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2011 The Android Open Source Project
-
-     Licensed under the Apache License, Version 2.0 (the "License");
-     you may not use this file except in compliance with the License.
-     You may obtain a copy of the License at
-
-          http://www.apache.org/licenses/LICENSE-2.0
-
-     Unless required by applicable law or agreed to in writing, software
-     distributed under the License is distributed on an "AS IS" BASIS,
-     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-     See the License for the specific language governing permissions and
-     limitations under the License.
--->
-
-<resources>
-    <style name="KeyboardTheme.Stone" parent="KeyboardIcons.Black">
-        <item name="keyboardStyle">@style/Keyboard.Stone</item>
-        <item name="keyboardViewStyle">@style/KeyboardView.Stone</item>
-        <item name="mainKeyboardViewStyle">@style/MainKeyboardView.Stone</item>
-        <item name="moreKeysKeyboardStyle">@style/MoreKeysKeyboard.Stone</item>
-        <item name="moreKeysKeyboardViewStyle">@style/MoreKeysKeyboardView.Stone</item>
-        <item name="moreKeysKeyboardPanelStyle">@style/MoreKeysKeyboardPanelStyle</item>
-        <item name="suggestionStripViewStyle">@style/SuggestionStripViewStyle</item>
-        <item name="moreSuggestionsViewStyle">@style/MoreSuggestionsViewStyle</item>
-        <item name="suggestionBackgroundStyle">@style/SuggestionBackgroundStyle</item>
-    </style>
-</resources>
diff --git a/java/res/values/touch-position-correction.xml b/java/res/values/touch-position-correction.xml
index 7df86f4..9df517b 100644
--- a/java/res/values/touch-position-correction.xml
+++ b/java/res/values/touch-position-correction.xml
@@ -37,7 +37,7 @@
     </string-array>
 
     <string-array
-        name="touch_position_correction_data_gingerbread"
+        name="touch_position_correction_data_gb"
         translatable="false"
     >
         <!-- First row -->
@@ -57,7 +57,7 @@
     </string-array>
 
     <string-array
-        name="touch_position_correction_data_ice_cream_sandwich"
+        name="touch_position_correction_data_ics"
         translatable="false"
     >
         <!-- First row -->
diff --git a/java/res/xml-sw600dp/kbd_10_10_7_symbols_shift.xml b/java/res/xml-sw600dp/kbd_10_10_7_symbols_shift.xml
deleted file mode 100644
index c36f009..0000000
--- a/java/res/xml-sw600dp/kbd_10_10_7_symbols_shift.xml
+++ /dev/null
@@ -1,27 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-/*
-**
-** Copyright 2012, 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:touchPositionCorrectionData="@array/touch_position_correction_data_default"
->
-    <include
-        latin:keyboardLayout="@xml/rows_10_10_7_symbols_shift" />
-</Keyboard>
diff --git a/java/res/xml-sw600dp/key_apostrophe.xml b/java/res/xml-sw600dp/key_apostrophe.xml
deleted file mode 100644
index 2aec347..0000000
--- a/java/res/xml-sw600dp/key_apostrophe.xml
+++ /dev/null
@@ -1,49 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-/*
-**
-** Copyright 2012, 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.
-*/
--->
-
-<merge
-    xmlns:latin="http://schemas.android.com/apk/res/com.android.inputmethod.latin"
->
-    <switch>
-        <case
-            latin:mode="email|url"
-        >
-            <Key
-                latin:keyLabel="-" />
-        </case>
-        <case
-            latin:languageCode="fa"
-        >
-            <Key
-                latin:keyLabel="!text/keylabel_for_apostrophe"
-                latin:keyHintLabel="!text/keyhintlabel_for_apostrophe"
-                latin:keyLabelFlags="hasPopupHint"
-                latin:moreKeys="!text/more_keys_for_apostrophe"
-                latin:keyStyle="hasShiftedLetterHintStyle" />
-        </case>
-        <default>
-            <Key
-                latin:keyLabel="!text/keylabel_for_apostrophe"
-                latin:keyHintLabel="!text/keyhintlabel_for_apostrophe"
-                latin:moreKeys="!text/more_keys_for_apostrophe"
-                latin:keyStyle="hasShiftedLetterHintStyle" />
-        </default>
-    </switch>
-</merge>
diff --git a/java/res/xml-sw600dp/key_dash.xml b/java/res/xml-sw600dp/key_dash.xml
deleted file mode 100644
index b139c29..0000000
--- a/java/res/xml-sw600dp/key_dash.xml
+++ /dev/null
@@ -1,49 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-/*
-**
-** Copyright 2012, 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.
-*/
--->
-
-<merge
-    xmlns:latin="http://schemas.android.com/apk/res/com.android.inputmethod.latin"
->
-    <switch>
-        <case
-            latin:mode="email|url"
-        >
-            <Key
-                latin:keyLabel="_" />
-        </case>
-        <case
-            latin:languageCode="ar|fa"
-        >
-            <Key
-                latin:keyLabel="."
-                latin:keyHintLabel="!text/keyhintlabel_for_arabic_diacritics"
-                latin:keyLabelFlags="hasPopupHint"
-                latin:moreKeys="!text/more_keys_for_arabic_diacritics"
-                latin:keyStyle="hasShiftedLetterHintStyle" />
-        </case>
-        <default>
-            <Key
-                latin:keyLabel="-"
-                latin:keyHintLabel="_"
-                latin:moreKeys="_"
-                latin:keyStyle="hasShiftedLetterHintStyle" />
-        </default>
-    </switch>
-</merge>
diff --git a/java/res/xml-sw600dp/key_f1.xml b/java/res/xml-sw600dp/key_f1.xml
index 77afe4e..ac00532 100644
--- a/java/res/xml-sw600dp/key_f1.xml
+++ b/java/res/xml-sw600dp/key_f1.xml
@@ -53,10 +53,7 @@
         </case>
         <default>
             <Key
-                latin:keyLabel="/"
-                latin:keyHintLabel="\@"
-                latin:moreKeys="\@"
-                latin:keyStyle="hasShiftedLetterHintStyle" />
+                latin:keyLabel="/" />
         </default>
     </switch>
 </merge>
diff --git a/java/res/xml-sw600dp/key_space.xml b/java/res/xml-sw600dp/key_space_5kw.xml
similarity index 100%
rename from java/res/xml-sw600dp/key_space.xml
rename to java/res/xml-sw600dp/key_space_5kw.xml
diff --git a/java/res/xml/kbd_10_10_7_symbols.xml b/java/res/xml-sw600dp/key_space_symbols.xml
similarity index 82%
copy from java/res/xml/kbd_10_10_7_symbols.xml
copy to java/res/xml-sw600dp/key_space_symbols.xml
index 4d9861b..07aa7d1 100644
--- a/java/res/xml/kbd_10_10_7_symbols.xml
+++ b/java/res/xml-sw600dp/key_space_symbols.xml
@@ -18,10 +18,9 @@
 */
 -->
 
-<Keyboard
+<merge
     xmlns:latin="http://schemas.android.com/apk/res/com.android.inputmethod.latin"
-    latin:touchPositionCorrectionData="@array/touch_position_correction_data_default"
 >
     <include
-        latin:keyboardLayout="@xml/rows_symbols" />
-</Keyboard>
+        latin:keyboardLayout="@xml/key_space_5kw" />
+</merge>
diff --git a/java/res/xml-sw600dp/key_styles_common.xml b/java/res/xml-sw600dp/key_styles_common.xml
index f407ba3..d817add 100644
--- a/java/res/xml-sw600dp/key_styles_common.xml
+++ b/java/res/xml-sw600dp/key_styles_common.xml
@@ -123,7 +123,8 @@
         latin:styleName="emojiKeyStyle"
         latin:code="!code/key_emoji"
         latin:keyIcon="!icon/emoji_key"
-        latin:keyActionFlags="noKeyPreview" />
+        latin:keyActionFlags="noKeyPreview"
+        latin:backgroundType="functional" />
     <key-style
         latin:styleName="settingsKeyStyle"
         latin:code="!code/key_settings"
diff --git a/java/res/xml-sw600dp/keys_comma_period.xml b/java/res/xml-sw600dp/keys_comma_period.xml
deleted file mode 100644
index 752f75b..0000000
--- a/java/res/xml-sw600dp/keys_comma_period.xml
+++ /dev/null
@@ -1,46 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-/*
-**
-** Copyright 2012, 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.
-*/
--->
-
-<merge
-    xmlns:latin="http://schemas.android.com/apk/res/com.android.inputmethod.latin"
->
-    <switch>
-        <case
-            latin:mode="email"
-        >
-            <Key
-                latin:keyLabel="," />
-            <Key
-                latin:keyLabel="." />
-        </case>
-        <default>
-            <Key
-                latin:keyLabel="!text/keylabel_for_tablet_comma"
-                latin:keyHintLabel="!text/keyhintlabel_for_tablet_comma"
-                latin:moreKeys="!text/more_keys_for_tablet_comma"
-                latin:keyStyle="hasShiftedLetterHintStyle" />
-            <Key
-                latin:keyLabel="."
-                latin:keyHintLabel="!text/keyhintlabel_for_tablet_period"
-                latin:moreKeys="!text/more_keys_for_tablet_period"
-                latin:keyStyle="hasShiftedLetterHintStyle" />
-        </default>
-    </switch>
-</merge>
diff --git a/java/res/xml/kbd_10_10_7_symbols.xml b/java/res/xml-sw600dp/keys_exclamation_question.xml
similarity index 75%
copy from java/res/xml/kbd_10_10_7_symbols.xml
copy to java/res/xml-sw600dp/keys_exclamation_question.xml
index 4d9861b..983ef38 100644
--- a/java/res/xml/kbd_10_10_7_symbols.xml
+++ b/java/res/xml-sw600dp/keys_exclamation_question.xml
@@ -2,7 +2,7 @@
 <!--
 /*
 **
-** Copyright 2012, The Android Open Source Project
+** Copyright 2013, The Android Open Source Project
 **
 ** Licensed under the Apache License, Version 2.0 (the "License");
 ** you may not use this file except in compliance with the License.
@@ -18,10 +18,11 @@
 */
 -->
 
-<Keyboard
+<merge
     xmlns:latin="http://schemas.android.com/apk/res/com.android.inputmethod.latin"
-    latin:touchPositionCorrectionData="@array/touch_position_correction_data_default"
 >
-    <include
-        latin:keyboardLayout="@xml/rows_symbols" />
-</Keyboard>
+    <Key
+        latin:keyLabel="\?" />
+    <Key
+        latin:keyLabel="!" />
+</merge>
diff --git a/java/res/xml-sw600dp/keys_pcqwerty2_right3.xml b/java/res/xml-sw600dp/keys_pcqwerty2_right3.xml
index 0a27ca7..324e025 100644
--- a/java/res/xml-sw600dp/keys_pcqwerty2_right3.xml
+++ b/java/res/xml-sw600dp/keys_pcqwerty2_right3.xml
@@ -23,36 +23,32 @@
 >
     <switch>
         <case
-            latin:keyboardLayoutSetElement="symbols|symbolsShifted"
+            latin:keyboardLayoutSetElement="alphabet|alphabetAutomaticShifted"
         >
             <Key
                 latin:keyLabel="["
-                latin:moreKeys="{" />
-            <Key
-                latin:keyLabel="]"
-                latin:moreKeys="}" />
-            <!-- U+00A6: "¦" BROKEN BAR -->
-            <Key
-                latin:keyLabel="\\"
-                latin:moreKeys="\\|,&#x00A6;" />
-        </case>
-        <default>
-            <Key
-                latin:keyLabel="["
                 latin:keyHintLabel="{"
-                latin:moreKeys="{"
+                latin:additionalMoreKeys="{"
                 latin:keyStyle="hasShiftedLetterHintStyle" />
             <Key
                 latin:keyLabel="]"
                 latin:keyHintLabel="}"
-                latin:moreKeys="}"
+                latin:additionalMoreKeys="}"
                 latin:keyStyle="hasShiftedLetterHintStyle" />
-            <!-- U+00A6: "¦" BROKEN BAR -->
             <Key
                 latin:keyLabel="\\"
                 latin:keyHintLabel="|"
-                latin:moreKeys="\\|,&#x00A6;"
+                latin:additionalMoreKeys="\\|"
                 latin:keyStyle="hasShiftedLetterHintStyle" />
+        </case>
+        <!-- keyboardLayoutSetElement="alphabetManualShifted|alphabetShiftLocked|alphabetShiftLockShifted" -->
+        <default>
+            <Key
+                latin:keyLabel="{" />
+            <Key
+                latin:keyLabel="}" />
+            <Key
+                latin:keyLabel="|" />
         </default>
     </switch>
-</merge>
+</merge>
\ No newline at end of file
diff --git a/java/res/xml-sw600dp/keys_pcqwerty3_right2.xml b/java/res/xml-sw600dp/keys_pcqwerty3_right2.xml
index 0e3013a..254b5e5 100644
--- a/java/res/xml-sw600dp/keys_pcqwerty3_right2.xml
+++ b/java/res/xml-sw600dp/keys_pcqwerty3_right2.xml
@@ -23,26 +23,27 @@
 >
     <switch>
         <case
-            latin:keyboardLayoutSetElement="symbols|symbolsShifted"
+            latin:keyboardLayoutSetElement="alphabet|alphabetAutomaticShifted"
         >
             <Key
                 latin:keyLabel=";"
-                latin:moreKeys=":" />
-            <Key
-                latin:keyLabel="\'"
-                latin:moreKeys="!fixedColumnOrder!3,!text/double_quotes,!text/single_quotes" />
-        </case>
-        <default>
-            <Key
-                latin:keyLabel=";"
                 latin:keyHintLabel=":"
-                latin:moreKeys=":"
+                latin:additionalMoreKeys=":"
                 latin:keyStyle="hasShiftedLetterHintStyle" />
             <Key
                 latin:keyLabel="\'"
                 latin:keyHintLabel="&quot;"
-                latin:moreKeys="!fixedColumnOrder!4,!text/double_quotes,&quot;,!text/single_quotes"
-                latin:keyStyle="hasShiftedLetterHintStyle" />
+                latin:additionalMoreKeys="&quot;"
+                latin:keyStyle="hasShiftedLetterHintStyle"
+                latin:moreKeys="!fixedColumnOrder!4,!text/double_quotes,%,!text/single_quotes" />
+        </case>
+        <!-- keyboardLayoutSetElement="alphabetManualShifted|alphabetShiftLocked|alphabetShiftLockShifted" -->
+        <default>
+            <Key
+                latin:keyLabel=":" />
+            <Key
+                latin:keyLabel="&quot;"
+                latin:moreKeys="!fixedColumnOrder!3,!text/double_quotes,!text/single_quotes" />
         </default>
     </switch>
 </merge>
diff --git a/java/res/xml-sw600dp/keys_pcqwerty4_right3.xml b/java/res/xml-sw600dp/keys_pcqwerty4_right3.xml
index ee5271a..774ff8d 100644
--- a/java/res/xml-sw600dp/keys_pcqwerty4_right3.xml
+++ b/java/res/xml-sw600dp/keys_pcqwerty4_right3.xml
@@ -23,28 +23,26 @@
 >
     <switch>
         <case
-            latin:keyboardLayoutSetElement="symbols|symbolsShifted"
+            latin:keyboardLayoutSetElement="alphabet|alphabetAutomaticShifted"
         >
-            <!-- U+2039: "‹" SINGLE LEFT-POINTING ANGLE QUOTATION MARK
-                 U+203A: "›" SINGLE RIGHT-POINTING ANGLE QUOTATION MARK
-                 U+2264: "≤" LESS-THAN OR EQUAL TO
-                 U+2265: "≥" GREATER-THAN EQUAL TO
-                 U+00AB: "«" LEFT-POINTING DOUBLE ANGLE QUOTATION MARK
-                 U+00BB: "»" RIGHT-POINTING DOUBLE ANGLE QUOTATION MARK -->
-           <Key
+            <Key
                 latin:keyLabel=","
+                latin:keyHintLabel="&lt;"
                 latin:additionalMoreKeys="&lt;"
-                latin:moreKeys="!fixedColumnOrder!4,&#x2039;,&#x2064;,&#x00AB;" />
+                latin:keyStyle="hasShiftedLetterHintStyle" />
             <Key
                 latin:keyLabel="."
+                latin:keyHintLabel="&gt;"
                 latin:additionalMoreKeys="&gt;"
-                latin:moreKeys="!fixedColumnOrder!4,&#x203A;,&#x2065;,&#x00BB;" />
-            <!-- U+00BF: "¿" INVERTED QUESTION MARK -->
+                latin:keyStyle="hasShiftedLetterHintStyle" />
             <Key
                 latin:keyLabel="/"
+                latin:keyHintLabel="\?"
                 latin:additionalMoreKeys="\?"
-                latin:moreKeys="&#x00BF;" />
+                latin:keyStyle="hasShiftedLetterHintStyle"
+                latin:moreKeys="!text/more_keys_for_symbols_question" />
         </case>
+        <!-- keyboardLayoutSetElement="alphabetManualShifted|alphabetShiftLocked|alphabetShiftLockShifted" -->
         <default>
             <!-- U+2039: "‹" SINGLE LEFT-POINTING ANGLE QUOTATION MARK
                  U+203A: "›" SINGLE RIGHT-POINTING ANGLE QUOTATION MARK
@@ -53,24 +51,14 @@
                  U+00AB: "«" LEFT-POINTING DOUBLE ANGLE QUOTATION MARK
                  U+00BB: "»" RIGHT-POINTING DOUBLE ANGLE QUOTATION MARK -->
             <Key
-                latin:keyLabel=","
-                latin:keyHintLabel="&lt;"
-                latin:additionalMoreKeys="&lt;"
-                latin:moreKeys="!fixedColumnOrder!4,&#x2039;,&#x2264;,&#x00AB;"
-                latin:keyStyle="hasShiftedLetterHintStyle" />
+                latin:keyLabel="&lt;"
+                latin:moreKeys="!fixedColumnOrder!3,&#x2039;,&#x2264;,&#x00AB;" />
             <Key
-                latin:keyLabel="."
-                latin:keyHintLabel="&gt;"
-                latin:additionalMoreKeys="&gt;"
-                latin:moreKeys="!fixedColumnOrder!4,&#x203A;,&#x2265;,&#x00BB;"
-                latin:keyStyle="hasShiftedLetterHintStyle" />
-            <!-- U+00BF: "¿" INVERTED QUESTION MARK -->
+                latin:keyLabel="&gt;"
+                latin:moreKeys="!fixedColumnOrder!3,&#x203A;,&#x2265;,&#x00BB;" />
             <Key
-                latin:keyLabel="/"
-                latin:keyHintLabel="\?"
-                latin:additionalMoreKeys="\?"
-                latin:moreKeys="&#x00BF;"
-                latin:keyStyle="hasShiftedLetterHintStyle" />
+                latin:keyLabel="\?"
+                latin:moreKeys="!text/more_keys_for_symbols_question" />
         </default>
     </switch>
 </merge>
diff --git a/java/res/xml-sw600dp/row_dvorak4.xml b/java/res/xml-sw600dp/row_dvorak4.xml
index 969cc14..11b4034 100644
--- a/java/res/xml-sw600dp/row_dvorak4.xml
+++ b/java/res/xml-sw600dp/row_dvorak4.xml
@@ -34,12 +34,15 @@
             latin:keyboardLayout="@xml/key_f1" />
         <include
             latin:keyXPos="28.0%p"
-            latin:keyboardLayout="@xml/key_space"
+            latin:keyboardLayout="@xml/key_space_5kw"
             latin:backgroundType="normal" />
         <include
             latin:keyboardLayout="@xml/key_question_exclamation" />
-        <include
-            latin:keyboardLayout="@xml/key_dash" />
+        <Key
+            latin:keyLabel="-"
+            latin:keyHintLabel="_"
+            latin:moreKeys="_"
+            latin:keyStyle="hasShiftedLetterHintStyle" />
         <include
             latin:keyboardLayout="@xml/key_f2" />
     </Row>
diff --git a/java/res/xml-sw600dp/row_hebrew4.xml b/java/res/xml-sw600dp/row_hebrew4.xml
deleted file mode 100644
index f429f97..0000000
--- a/java/res/xml-sw600dp/row_hebrew4.xml
+++ /dev/null
@@ -1,44 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-/*
-**
-** Copyright 2012, 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.
-*/
--->
-
-<merge
-    xmlns:latin="http://schemas.android.com/apk/res/com.android.inputmethod.latin"
->
-    <Row
-        latin:keyWidth="9.0%p"
-        latin:backgroundType="functional"
-    >
-        <Key
-            latin:keyStyle="toSymbolKeyStyle"
-            latin:keyWidth="10.0%p" />
-        <include
-            latin:keyboardLayout="@xml/key_shortcut" />
-        <include
-            latin:keyboardLayout="@xml/key_f1" />
-        <include
-            latin:keyXPos="28.0%p"
-            latin:keyboardLayout="@xml/key_space"
-            latin:backgroundType="normal" />
-        <include
-            latin:keyboardLayout="@xml/keys_comma_period" />
-        <include
-            latin:keyboardLayout="@xml/key_f2" />
-    </Row>
-</merge>
diff --git a/java/res/xml-sw600dp/row_pcqwerty5.xml b/java/res/xml-sw600dp/row_pcqwerty5.xml
index 3c4a466..b854f10 100644
--- a/java/res/xml-sw600dp/row_pcqwerty5.xml
+++ b/java/res/xml-sw600dp/row_pcqwerty5.xml
@@ -38,9 +38,23 @@
             latin:keyStyle="spaceKeyStyle"
             latin:keyXPos="25.5%p"
             latin:keyWidth="49.0%p" />
-         <include
-            latin:keyXPos="-9.0%p"
-            latin:keyWidth="fillRight"
-            latin:keyboardLayout="@xml/key_shortcut" />
+        <switch>
+            <case
+                latin:keyboardLayoutSetElement="alphabet|alphabetAutomaticShifted"
+            >
+                <include
+                    latin:keyXPos="-9.0%p"
+                    latin:keyWidth="9.0%p"
+                    latin:keyboardLayout="@xml/key_shortcut" />
+            </case>
+            <!-- keyboardLayoutSetElement="alphabetManualShifted|alphabetShiftLocked|alphabetShiftLockShifted" -->
+            <default>
+                <include
+                    latin:keyXPos="-9.0%p"
+                    latin:keyWidth="9.0%p"
+                    latin:backgroundType="functional"
+                    latin:keyboardLayout="@xml/key_f2" />
+            </default>
+        </switch>
     </Row>
 </merge>
diff --git a/java/res/xml-sw600dp/row_qwerty4.xml b/java/res/xml-sw600dp/row_qwerty4.xml
index fa43363..7969dd8 100644
--- a/java/res/xml-sw600dp/row_qwerty4.xml
+++ b/java/res/xml-sw600dp/row_qwerty4.xml
@@ -34,12 +34,10 @@
             latin:keyboardLayout="@xml/key_f1" />
         <include
             latin:keyXPos="28.0%p"
-            latin:keyboardLayout="@xml/key_space"
+            latin:keyboardLayout="@xml/key_space_5kw"
             latin:backgroundType="normal" />
         <include
-            latin:keyboardLayout="@xml/key_apostrophe" />
-        <include
-            latin:keyboardLayout="@xml/key_dash" />
+            latin:keyboardLayout="@xml/keys_comma_period" />
         <include
             latin:keyboardLayout="@xml/key_f2" />
     </Row>
diff --git a/java/res/xml-sw600dp/row_symbols4.xml b/java/res/xml-sw600dp/row_symbols4.xml
deleted file mode 100644
index f138d8e..0000000
--- a/java/res/xml-sw600dp/row_symbols4.xml
+++ /dev/null
@@ -1,48 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-/*
-**
-** Copyright 2012, 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.
-*/
--->
-
-<merge
-    xmlns:latin="http://schemas.android.com/apk/res/com.android.inputmethod.latin"
->
-    <Row
-        latin:keyWidth="9.0%p"
-        latin:backgroundType="functional"
-    >
-        <Key
-            latin:keyStyle="toAlphaKeyStyle"
-            latin:keyWidth="10.0%p" />
-        <Key
-            latin:keyLabel="/" />
-        <include
-            latin:keyboardLayout="@xml/key_f1" />
-        <include
-            latin:keyXPos="28.0%p"
-            latin:keyboardLayout="@xml/key_space"
-            latin:backgroundType="normal" />
-        <Key
-            latin:keyLabel="&quot;"
-            latin:moreKeys="!text/more_keys_for_tablet_double_quote" />
-        <Key
-            latin:keyLabel="_" />
-        <!-- Note: This Spacer prevents the above key from being marked as a right edge key. -->
-        <Spacer
-            latin:keyWidth="fillRight" />
-    </Row>
-</merge>
diff --git a/java/res/xml-sw600dp/row_symbols_shift4.xml b/java/res/xml-sw600dp/row_symbols_shift4.xml
deleted file mode 100644
index 29befa9..0000000
--- a/java/res/xml-sw600dp/row_symbols_shift4.xml
+++ /dev/null
@@ -1,40 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-/*
-**
-** Copyright 2012, 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.
-*/
--->
-
-<merge
-    xmlns:latin="http://schemas.android.com/apk/res/com.android.inputmethod.latin"
->
-    <Row
-        latin:keyWidth="9.0%p"
-        latin:backgroundType="functional"
-    >
-        <Key
-            latin:keyStyle="toAlphaKeyStyle"
-            latin:keyWidth="10.0%p" />
-        <!-- Here is empty space. -->
-        <include
-            latin:keyXPos="28.0%p"
-            latin:keyboardLayout="@xml/key_space"
-            latin:backgroundType="normal" />
-        <!-- Note: This Spacer prevents the above key from being marked as a right edge key. -->
-        <Spacer
-            latin:keyWidth="fillRight" />
-    </Row>
-</merge>
diff --git a/java/res/xml-sw600dp/rowkeys_pcqwerty1.xml b/java/res/xml-sw600dp/rowkeys_pcqwerty1.xml
index b11bbba..254d3fd 100644
--- a/java/res/xml-sw600dp/rowkeys_pcqwerty1.xml
+++ b/java/res/xml-sw600dp/rowkeys_pcqwerty1.xml
@@ -21,93 +21,87 @@
 <merge
     xmlns:latin="http://schemas.android.com/apk/res/com.android.inputmethod.latin"
 >
-    <switch>
-        <case
-            latin:keyboardLayoutSetElement="symbols|symbolsShifted"
-        >
-            <include
-                latin:keyboardLayout="@xml/keys_pcqwerty_symbols1" />
-        </case>
-        <!-- keyboardLayoutSetElement="alphabet*" -->
-        <default>
-            <!-- U+00AC: "¬" NOT SIGN -->
-            <Key
-                latin:keyLabel="`"
-                latin:keyHintLabel="~"
-                latin:additionalMoreKeys="~"
-                latin:moreKeys="&#x00AC;"
-                latin:keyStyle="hasShiftedLetterHintStyle" />
-            <!-- U+00A1: "¡" NVERTED EXCLAMATION MARK -->
-            <Key
-                latin:keyLabel="1"
-                latin:keyHintLabel="!"
-                latin:additionalMoreKeys="!"
-                latin:moreKeys="&#x00A1;,!text/more_keys_for_symbols_1"
-                latin:keyStyle="hasShiftedLetterHintStyle" />
-            <Key
-                latin:keyLabel="2"
-                latin:keyHintLabel="\@"
-                latin:additionalMoreKeys="\@"
-                latin:moreKeys="!text/more_keys_for_symbols_2"
-                latin:keyStyle="hasShiftedLetterHintStyle" />
-            <Key
-                latin:keyLabel="3"
-                latin:keyHintLabel="\#"
-                latin:additionalMoreKeys="\#"
-                latin:moreKeys="!text/more_keys_for_symbols_3"
-                latin:keyStyle="hasShiftedLetterHintStyle" />
-            <Key
-                latin:keyLabel="4"
-                latin:keyHintLabel="$"
-                latin:additionalMoreKeys="$"
-                latin:moreKeys="!text/more_keys_for_symbols_4"
-                latin:keyStyle="hasShiftedLetterHintStyle" />
-            <Key
-                latin:keyLabel="5"
-                latin:keyHintLabel="%"
-                latin:additionalMoreKeys="\\%"
-                latin:moreKeys="!text/more_keys_for_symbols_5"
-                latin:keyStyle="hasShiftedLetterHintStyle" />
-            <Key
-                latin:keyLabel="6"
-                latin:keyHintLabel="^"
-                latin:additionalMoreKeys="^"
-                latin:moreKeys="!text/more_keys_for_symbols_6"
-                latin:keyStyle="hasShiftedLetterHintStyle" />
-            <Key
-                latin:keyLabel="7"
-                latin:keyHintLabel="&amp;"
-                latin:additionalMoreKeys="&amp;"
-                latin:moreKeys="!text/more_keys_for_symbols_7"
-                latin:keyStyle="hasShiftedLetterHintStyle" />
-            <Key
-                latin:keyLabel="8"
-                latin:keyHintLabel="*"
-                latin:additionalMoreKeys="*"
-                latin:moreKeys="!text/more_keys_for_symbols_8"
-                latin:keyStyle="hasShiftedLetterHintStyle" />
-            <Key
-                latin:keyLabel="9"
-                latin:keyHintLabel="("
-                latin:additionalMoreKeys="("
-                latin:moreKeys="!text/more_keys_for_symbols_9"
-                latin:keyStyle="hasShiftedLetterHintStyle" />
-            <Key
-                latin:keyLabel="0"
-                latin:keyHintLabel=")"
-                latin:additionalMoreKeys=")"
-                latin:moreKeys="!text/more_keys_for_symbols_0"
-                latin:keyStyle="hasShiftedLetterHintStyle" />
-            <Key
-                latin:keyLabel="-"
-                latin:keyHintLabel="_"
-                latin:moreKeys="_"
-                latin:keyStyle="hasShiftedLetterHintStyle" />
-            <Key
-                latin:keyLabel="="
-                latin:keyHintLabel="+"
-                latin:moreKeys="+"
-                latin:keyStyle="hasShiftedLetterHintStyle" />
-        </default>
-    </switch>
+    <Key
+        latin:keyLabel="`"
+        latin:keyHintLabel="~"
+        latin:additionalMoreKeys="~"
+        latin:keyStyle="hasShiftedLetterHintStyle" />
+    <Key
+        latin:keyLabel="1"
+        latin:keyHintLabel="!"
+        latin:additionalMoreKeys="!"
+        latin:keyStyle="hasShiftedLetterHintStyle"
+        latin:moreKeys="!text/more_keys_for_symbols_exclamation,!text/more_keys_for_symbols_1" />
+    <Key
+        latin:keyLabel="2"
+        latin:keyHintLabel="\@"
+        latin:additionalMoreKeys="\@"
+        latin:keyStyle="hasShiftedLetterHintStyle"
+        latin:moreKeys="!text/more_keys_for_symbols_2" />
+    <Key
+        latin:keyLabel="3"
+        latin:keyHintLabel="\#"
+        latin:additionalMoreKeys="\#"
+        latin:keyStyle="hasShiftedLetterHintStyle"
+        latin:moreKeys="!text/more_keys_for_symbols_3" />
+    <Key
+        latin:keyLabel="4"
+        latin:keyHintLabel="$"
+        latin:additionalMoreKeys="$"
+        latin:keyStyle="hasShiftedLetterHintStyle"
+        latin:moreKeys="!text/more_keys_for_symbols_4" />
+    <Key
+        latin:keyLabel="5"
+        latin:keyHintLabel="%"
+        latin:additionalMoreKeys="\\%"
+        latin:keyStyle="hasShiftedLetterHintStyle"
+        latin:moreKeys="!text/more_keys_for_symbols_5" />
+    <Key
+        latin:keyLabel="6"
+        latin:keyHintLabel="^"
+        latin:additionalMoreKeys="^"
+        latin:keyStyle="hasShiftedLetterHintStyle"
+        latin:moreKeys="!text/more_keys_for_symbols_6" />
+    <Key
+        latin:keyLabel="7"
+        latin:keyHintLabel="&amp;"
+        latin:additionalMoreKeys="&amp;"
+        latin:keyStyle="hasShiftedLetterHintStyle"
+        latin:moreKeys="!text/more_keys_for_symbols_7" />
+    <Key
+        latin:keyLabel="8"
+        latin:keyHintLabel="*"
+        latin:additionalMoreKeys="*"
+        latin:keyStyle="hasShiftedLetterHintStyle"
+        latin:moreKeys="!text/more_keys_for_symbols_8" />
+    <Key
+        latin:keyLabel="9"
+        latin:keyHintLabel="("
+        latin:additionalMoreKeys="("
+        latin:keyStyle="hasShiftedLetterHintStyle"
+        latin:moreKeys="!text/more_keys_for_symbols_9" />
+    <Key
+        latin:keyLabel="0"
+        latin:keyHintLabel=")"
+        latin:additionalMoreKeys=")"
+        latin:keyStyle="hasShiftedLetterHintStyle"
+        latin:moreKeys="!text/more_keys_for_symbols_0" />
+    <!-- U+2013: "–" EN DASH
+         U+2014: "—" EM DASH
+         U+00B7: "·" MIDDLE DOT -->
+    <Key
+        latin:keyLabel="-"
+        latin:keyHintLabel="_"
+        latin:additionalMoreKeys="_"
+        latin:keyStyle="hasShiftedLetterHintStyle"
+        latin:moreKeys="&#x2013;,&#x2014;,&#x00B7;" />
+    <!-- U+221E: "∞" INFINITY
+         U+2260: "≠" NOT EQUAL TO
+         U+2248: "≈" ALMOST EQUAL TO -->
+    <Key
+        latin:keyLabel="="
+        latin:keyHintLabel="+"
+        latin:additionalMoreKeys="+"
+        latin:keyStyle="hasShiftedLetterHintStyle"
+        latin:moreKeys="&#x221E;,&#x2260;,&#x2248;" />
 </merge>
diff --git a/java/res/xml-sw600dp/rowkeys_symbols2.xml b/java/res/xml-sw600dp/rowkeys_symbols2.xml
deleted file mode 100644
index 14abb42..0000000
--- a/java/res/xml-sw600dp/rowkeys_symbols2.xml
+++ /dev/null
@@ -1,75 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-/*
-**
-** Copyright 2012, 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.
-*/
--->
-
-<merge
-    xmlns:latin="http://schemas.android.com/apk/res/com.android.inputmethod.latin"
->
-    <switch>
-        <case
-            latin:languageCode="fa"
-        >
-            <!-- U+066C: "٬" ARABIC THOUSANDS SEPARATOR -->
-            <Key
-                latin:keyLabel="&#x066C;"
-                latin:keyHintLabel="&amp;"
-                latin:keyLabelFlags="hasPopupHint|hasShiftedLetterHint"
-                latin:moreKeys="&amp;" />
-        </case>
-        <default>
-            <Key
-                latin:keyLabel="\#" />
-        </default>
-    </switch>
-    <Key
-        latin:keyStyle="currencyKeyStyle" />
-    <Key
-        latin:keyLabel="!text/keylabel_for_symbols_percent"
-        latin:moreKeys="!text/more_keys_for_symbols_percent" />
-    <switch>
-        <case
-            latin:languageCode="fa"
-        >
-            <!-- U+066B: "٫" ARABIC DECIMAL SEPARATOR -->
-            <Key
-                latin:keyLabel="&#x066B;"
-                latin:keyHintLabel="\#"
-                latin:keyLabelFlags="hasPopupHint|hasShiftedLetterHint"
-                latin:moreKeys="\#" />
-        </case>
-        <default>
-            <Key
-                latin:keyLabel="&amp;" />
-        </default>
-    </switch>
-    <Key
-        latin:keyLabel="*"
-        latin:moreKeys="!text/more_keys_for_star" />
-    <!-- U+2013: "–" EN DASH
-         U+2014: "—" EM DASH
-         U+00B7: "·" MIDDLE DOT -->
-    <Key
-        latin:keyLabel="-"
-        latin:moreKeys="_,&#x2013;,&#x2014;,&#x00B7;" />
-    <Key
-        latin:keyLabel="+"
-        latin:moreKeys="!text/more_keys_for_plus" />
-    <include
-        latin:keyboardLayout="@xml/keys_parentheses" />
-</merge>
diff --git a/java/res/xml-sw600dp/rowkeys_symbols3.xml b/java/res/xml-sw600dp/rowkeys_symbols3.xml
deleted file mode 100644
index 30fba38..0000000
--- a/java/res/xml-sw600dp/rowkeys_symbols3.xml
+++ /dev/null
@@ -1,57 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-/*
-**
-** Copyright 2012, 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.
-*/
--->
-
-<merge
-    xmlns:latin="http://schemas.android.com/apk/res/com.android.inputmethod.latin"
->
-    <include
-        latin:keyboardLayout="@xml/keys_less_greater" />
-    <!-- U+2260: "≠" NOT EQUAL TO
-         U+2248: "≈" ALMOST EQUAL TO -->
-    <Key
-        latin:keyLabel="="
-        latin:moreKeys="&#x2260;,&#x2248;" />
-    <switch>
-        <case
-            latin:mode="url"
-        >
-            <Key
-                latin:keyLabel="\'" />
-        </case>
-        <default>
-            <Key
-                latin:keyLabel=":" />
-        </default>
-    </switch>
-    <Key
-        latin:keyLabel="!text/keylabel_for_symbols_semicolon"
-        latin:moreKeys="!text/more_keys_for_symbols_semicolon" />
-    <Key
-        latin:keyLabel="!text/keylabel_for_comma"
-        latin:moreKeys="!text/more_keys_for_comma" />
-    <Key
-        latin:keyLabel="." />
-    <Key
-        latin:keyLabel="!"
-        latin:moreKeys="!text/more_keys_for_symbols_exclamation" />
-    <Key
-        latin:keyLabel="!text/keylabel_for_symbols_question"
-        latin:moreKeys="!text/more_keys_for_symbols_question" />
-</merge>
diff --git a/java/res/xml-sw600dp/rowkeys_symbols_shift1.xml b/java/res/xml-sw600dp/rowkeys_symbols_shift1.xml
deleted file mode 100644
index 3549fdd..0000000
--- a/java/res/xml-sw600dp/rowkeys_symbols_shift1.xml
+++ /dev/null
@@ -1,56 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-/*
-**
-** Copyright 2012, 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.
-*/
--->
-
-<merge
-    xmlns:latin="http://schemas.android.com/apk/res/com.android.inputmethod.latin"
->
-    <Key
-        latin:keyLabel="~" />
-    <Key
-        latin:keyLabel="`" />
-    <Key
-        latin:keyLabel="|" />
-    <!-- U+2022: "•" BULLET -->
-    <Key
-        latin:keyLabel="&#x2022;"
-        latin:moreKeys="!text/more_keys_for_bullet" />
-    <!-- U+221A: "√" SQUARE ROOT -->
-    <Key
-        latin:keyLabel="&#x221A;" />
-    <!-- U+03C0: "π" GREEK SMALL LETTER PI
-         U+03A0: "Π" GREEK CAPITAL LETTER PI -->
-    <Key
-        latin:keyLabel="&#x03C0;"
-        latin:moreKeys="&#x03A0;" />
-    <!-- U+00F7: "÷" DIVISION SIGN -->
-    <Key
-        latin:keyLabel="&#x00F7;" />
-    <!-- U+00D7: "×" MULTIPLICATION SIGN -->
-    <Key
-        latin:keyLabel="&#x00D7;" />
-    <!-- U+00A7: "§" SECTION SIGN
-         U+00B6: "¶" PILCROW SIGN -->
-    <Key
-        latin:keyLabel="&#x00A7;"
-        latin:moreKeys="&#x00B6;" />
-    <!-- U+0394: "Δ" GREEK CAPITAL LETTER DELTA -->
-    <Key
-        latin:keyLabel="&#x0394;" />
-</merge>
diff --git a/java/res/xml-sw600dp/rowkeys_symbols_shift2.xml b/java/res/xml-sw600dp/rowkeys_symbols_shift2.xml
deleted file mode 100644
index 2048b73..0000000
--- a/java/res/xml-sw600dp/rowkeys_symbols_shift2.xml
+++ /dev/null
@@ -1,52 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-/*
-**
-** Copyright 2012, 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.
-*/
--->
-
-<merge
-    xmlns:latin="http://schemas.android.com/apk/res/com.android.inputmethod.latin"
->
-    <Key
-        latin:keyStyle="moreCurrency1KeyStyle" />
-    <Key
-        latin:keyStyle="moreCurrency2KeyStyle" />
-    <Key
-        latin:keyStyle="moreCurrency3KeyStyle" />
-    <Key
-        latin:keyStyle="moreCurrency4KeyStyle" />
-    <!-- U+2191: "↑" UPWARDS ARROW
-         U+2193: "↓" DOWNWARDS ARROW
-         U+2190: "←" LEFTWARDS ARROW
-         U+2192: "→" RIGHTWARDS ARROW -->
-    <Key
-        latin:keyLabel="^"
-        latin:moreKeys="&#x2191;,&#x2193;,&#x2190;,&#x2192;" />
-    <!-- U+00B0: "°" DEGREE SIGN
-         U+2032: "′" PRIME
-         U+2033: "″" DOUBLE PRIME -->
-    <Key
-        latin:keyLabel="&#x00B0;"
-        latin:moreKeys="&#x2032;,&#x2033;" />
-    <!-- U+00B1: "±" PLUS-MINUS SIGN
-         U+221E: "∞" INFINITY -->
-    <Key
-        latin:keyLabel="&#x00B1;"
-        latin:moreKeys="&#x221E;" />
-    <include
-        latin:keyboardLayout="@xml/keys_curly_brackets" />
-</merge>
diff --git a/java/res/xml-sw600dp/rowkeys_symbols_shift3.xml b/java/res/xml-sw600dp/rowkeys_symbols_shift3.xml
deleted file mode 100644
index 8bd8656..0000000
--- a/java/res/xml-sw600dp/rowkeys_symbols_shift3.xml
+++ /dev/null
@@ -1,46 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-/*
-**
-** Copyright 2012, 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.
-*/
--->
-
-<merge
-    xmlns:latin="http://schemas.android.com/apk/res/com.android.inputmethod.latin"
->
-    <Key
-        latin:keyLabel="\\" />
-    <!-- U+00A9: "©" COPYRIGHT SIGN -->
-    <Key
-        latin:keyLabel="&#x00A9;" />
-    <!-- U+00AE: "®" REGISTERED SIGN -->
-    <Key
-        latin:keyLabel="&#x00AE;" />
-    <!-- U+2122: "™" TRADE MARK SIGN -->
-    <Key
-        latin:keyLabel="&#x2122;" />
-    <!-- U+2105: "℅" CARE OF -->
-    <Key
-        latin:keyLabel="&#x2105;" />
-    <include
-        latin:keyboardLayout="@xml/keys_square_brackets" />
-    <!-- U+00A1: "¡" INVERTED EXCLAMATION MARK -->
-    <Key
-        latin:keyLabel="&#x00A1;" />
-    <!-- U+00BF: "¿" INVERTED QUESTION MARK -->
-    <Key
-        latin:keyLabel="&#x00BF;" />
-</merge>
diff --git a/java/res/xml-sw600dp/rows_10_10_7_symbols_shift.xml b/java/res/xml-sw600dp/rows_10_10_7_symbols_shift.xml
deleted file mode 100644
index 3d3b59f..0000000
--- a/java/res/xml-sw600dp/rows_10_10_7_symbols_shift.xml
+++ /dev/null
@@ -1,60 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-/*
-**
-** Copyright 2012, 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.
-*/
--->
-
-<merge
-    xmlns:latin="http://schemas.android.com/apk/res/com.android.inputmethod.latin"
->
-    <include
-        latin:keyboardLayout="@xml/key_styles_common" />
-    <include
-        latin:keyboardLayout="@xml/key_styles_currency" />
-    <Row
-        latin:keyWidth="9.0%p"
-    >
-        <include
-            latin:keyboardLayout="@xml/rowkeys_symbols_shift1" />
-        <Key
-            latin:keyStyle="deleteKeyStyle"
-            latin:keyWidth="fillRight" />
-    </Row>
-    <Row
-        latin:keyWidth="9.0%p"
-    >
-        <include
-            latin:keyboardLayout="@xml/rowkeys_symbols_shift2" />
-        <Key
-            latin:keyStyle="enterKeyStyle"
-            latin:keyWidth="fillRight" />
-    </Row>
-    <Row
-        latin:keyWidth="9.0%p"
-    >
-        <Key
-            latin:keyStyle="backFromMoreSymbolKeyStyle"
-            latin:keyWidth="10.0%p" />
-        <include
-            latin:keyboardLayout="@xml/rowkeys_symbols_shift3" />
-        <Key
-            latin:keyStyle="backFromMoreSymbolKeyStyle"
-            latin:keyWidth="fillRight" />
-    </Row>
-    <include
-        latin:keyboardLayout="@xml/row_symbols_shift4" />
-</merge>
diff --git a/java/res/xml-sw768dp/rows_colemak.xml b/java/res/xml-sw600dp/rows_armenian_phonetic.xml
similarity index 63%
rename from java/res/xml-sw768dp/rows_colemak.xml
rename to java/res/xml-sw600dp/rows_armenian_phonetic.xml
index 073f812..9bc2a18 100644
--- a/java/res/xml-sw768dp/rows_colemak.xml
+++ b/java/res/xml-sw600dp/rows_armenian_phonetic.xml
@@ -2,7 +2,7 @@
 <!--
 /*
 **
-** Copyright 2010, The Android Open Source Project
+** Copyright 2013, 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.
@@ -24,43 +24,43 @@
     <include
         latin:keyboardLayout="@xml/key_styles_common" />
     <Row
-        latin:keyWidth="8.282%p"
+        latin:keyWidth="9.0%p"
     >
-        <Key
-            latin:keyStyle="tabKeyStyle"
-            latin:keyLabelFlags="alignLeft"
-            latin:keyWidth="7.969%p" />
         <include
-            latin:keyboardLayout="@xml/rowkeys_colemak1" />
-        <include
-            latin:keyboardLayout="@xml/key_colemak_colon" />
+            latin:keyboardLayout="@xml/rowkeys_armenian_phonetic1" />
         <Key
             latin:keyStyle="deleteKeyStyle"
             latin:keyWidth="fillRight" />
+        </Row>
+    <Row
+        latin:keyWidth="9.0%p"
+    >
+        <include
+            latin:keyboardLayout="@xml/rowkeys_armenian_phonetic2" />
+        <include
+            latin:keyboardLayout="@xml/key_armenian_xeh" />
     </Row>
     <Row
-        latin:keyWidth="8.125%p"
+        latin:keyWidth="9.0%p"
     >
-        <Key
-            latin:keyStyle="toSymbolKeyStyle"
-            latin:keyLabelFlags="alignLeft"
-            latin:keyWidth="10.167%p" />
         <include
-            latin:keyboardLayout="@xml/rowkeys_colemak2" />
+            latin:keyboardLayout="@xml/rowkeys_armenian_phonetic3" />
+        <include
+            latin:keyboardLayout="@xml/key_armenian_sha" />
         <Key
             latin:keyStyle="enterKeyStyle"
             latin:keyWidth="fillRight" />
     </Row>
     <Row
-        latin:keyWidth="8.047%p"
+        latin:keyWidth="8.8889%p"
     >
         <Key
             latin:keyStyle="shiftKeyStyle"
-            latin:keyWidth="13.829%p" />
+            latin:keyWidth="10.0%p" />
         <include
-            latin:keyboardLayout="@xml/rowkeys_colemak3" />
+            latin:keyboardLayout="@xml/rowkeys_armenian_phonetic4" />
         <include
-            latin:keyboardLayout="@xml/keys_comma_period" />
+            latin:keyboardLayout="@xml/keys_exclamation_question" />
         <Key
             latin:keyStyle="shiftKeyStyle"
             latin:keyWidth="fillRight" />
diff --git a/java/res/xml-sw600dp/rows_azerty.xml b/java/res/xml-sw600dp/rows_azerty.xml
index 5a5a7d1..cdc9185 100644
--- a/java/res/xml-sw600dp/rows_azerty.xml
+++ b/java/res/xml-sw600dp/rows_azerty.xml
@@ -50,7 +50,7 @@
         <include
             latin:keyboardLayout="@xml/rowkeys_azerty3" />
         <include
-            latin:keyboardLayout="@xml/keys_comma_period" />
+            latin:keyboardLayout="@xml/keys_exclamation_question" />
         <Key
             latin:keyStyle="shiftKeyStyle"
             latin:keyWidth="fillRight" />
diff --git a/java/res/xml-sw600dp/rows_bulgarian.xml b/java/res/xml-sw600dp/rows_bulgarian.xml
index 2635620..c73aa1c 100644
--- a/java/res/xml-sw600dp/rows_bulgarian.xml
+++ b/java/res/xml-sw600dp/rows_bulgarian.xml
@@ -50,7 +50,7 @@
         <include
             latin:keyboardLayout="@xml/rowkeys_bulgarian3" />
         <include
-            latin:keyboardLayout="@xml/keys_comma_period" />
+            latin:keyboardLayout="@xml/keys_exclamation_question" />
         <Key
             latin:keyStyle="shiftKeyStyle"
             latin:keyWidth="fillRight" />
diff --git a/java/res/xml-sw600dp/rows_bulgarian_bds.xml b/java/res/xml-sw600dp/rows_bulgarian_bds.xml
index 9439a63..bc773ee 100644
--- a/java/res/xml-sw600dp/rows_bulgarian_bds.xml
+++ b/java/res/xml-sw600dp/rows_bulgarian_bds.xml
@@ -50,7 +50,7 @@
         <include
             latin:keyboardLayout="@xml/rowkeys_bulgarian_bds3" />
         <include
-            latin:keyboardLayout="@xml/keys_comma_period" />
+            latin:keyboardLayout="@xml/keys_exclamation_question" />
     </Row>
     <include
         latin:keyboardLayout="@xml/row_qwerty4" />
diff --git a/java/res/xml-sw600dp/rows_colemak.xml b/java/res/xml-sw600dp/rows_colemak.xml
index 98a24e4..ab059da 100644
--- a/java/res/xml-sw600dp/rows_colemak.xml
+++ b/java/res/xml-sw600dp/rows_colemak.xml
@@ -52,7 +52,7 @@
         <include
             latin:keyboardLayout="@xml/rowkeys_colemak3" />
         <include
-            latin:keyboardLayout="@xml/keys_comma_period" />
+            latin:keyboardLayout="@xml/keys_exclamation_question" />
         <Key
             latin:keyStyle="shiftKeyStyle"
             latin:keyWidth="fillRight" />
diff --git a/java/res/xml-sw600dp/rows_east_slavic.xml b/java/res/xml-sw600dp/rows_east_slavic.xml
index b4160d6..c5045ff 100644
--- a/java/res/xml-sw600dp/rows_east_slavic.xml
+++ b/java/res/xml-sw600dp/rows_east_slavic.xml
@@ -50,7 +50,7 @@
         <include
             latin:keyboardLayout="@xml/rowkeys_east_slavic3" />
         <include
-            latin:keyboardLayout="@xml/keys_comma_period" />
+            latin:keyboardLayout="@xml/keys_exclamation_question" />
     </Row>
     <include
         latin:keyboardLayout="@xml/row_qwerty4" />
diff --git a/java/res/xml-sw600dp/rows_georgian.xml b/java/res/xml-sw600dp/rows_georgian.xml
index b0e9e35..891cbc1 100644
--- a/java/res/xml-sw600dp/rows_georgian.xml
+++ b/java/res/xml-sw600dp/rows_georgian.xml
@@ -51,7 +51,7 @@
         <include
             latin:keyboardLayout="@xml/rowkeys_georgian3" />
         <include
-            latin:keyboardLayout="@xml/keys_comma_period" />
+            latin:keyboardLayout="@xml/keys_exclamation_question" />
         <Key
             latin:keyStyle="shiftKeyStyle"
             latin:keyWidth="fillRight" />
diff --git a/java/res/xml-sw600dp/rows_greek.xml b/java/res/xml-sw600dp/rows_greek.xml
index de214c6..066dc47 100644
--- a/java/res/xml-sw600dp/rows_greek.xml
+++ b/java/res/xml-sw600dp/rows_greek.xml
@@ -53,7 +53,7 @@
         <include
             latin:keyboardLayout="@xml/rowkeys_greek3" />
         <include
-            latin:keyboardLayout="@xml/keys_comma_period" />
+            latin:keyboardLayout="@xml/keys_exclamation_question" />
         <Key
             latin:keyStyle="shiftKeyStyle"
             latin:keyWidth="fillRight" />
diff --git a/java/res/xml-sw600dp/rows_hebrew.xml b/java/res/xml-sw600dp/rows_hebrew.xml
index 9945dee..852e176 100644
--- a/java/res/xml-sw600dp/rows_hebrew.xml
+++ b/java/res/xml-sw600dp/rows_hebrew.xml
@@ -49,5 +49,5 @@
             latin:keyXPos="10.0%p" />
     </Row>
     <include
-        latin:keyboardLayout="@xml/row_hebrew4" />
+        latin:keyboardLayout="@xml/row_qwerty4" />
 </merge>
diff --git a/java/res/xml-sw600dp/rows_hindi.xml b/java/res/xml-sw600dp/rows_hindi.xml
index 2a9a419..ca581be 100644
--- a/java/res/xml-sw600dp/rows_hindi.xml
+++ b/java/res/xml-sw600dp/rows_hindi.xml
@@ -50,7 +50,7 @@
         <include
             latin:keyboardLayout="@xml/rowkeys_hindi3" />
         <include
-            latin:keyboardLayout="@xml/keys_comma_period" />
+            latin:keyboardLayout="@xml/keys_exclamation_question" />
     </Row>
     <include
         latin:keyboardLayout="@xml/row_qwerty4" />
diff --git a/java/res/xml-sw600dp/rows_khmer.xml b/java/res/xml-sw600dp/rows_khmer.xml
new file mode 100644
index 0000000..2824a5c
--- /dev/null
+++ b/java/res/xml-sw600dp/rows_khmer.xml
@@ -0,0 +1,72 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+**
+** Copyright 2013, 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.
+*/
+-->
+
+<merge
+    xmlns:latin="http://schemas.android.com/apk/res/com.android.inputmethod.latin"
+>
+    <include
+        latin:keyboardLayout="@xml/key_styles_common" />
+    <Row
+        latin:keyWidth="7.5%p"
+    >
+        <include
+            latin:keyboardLayout="@xml/rowkeys_khmer1" />
+        <Key
+            latin:keyStyle="deleteKeyStyle"
+            latin:keyWidth="fillRight" />
+        </Row>
+    <Row
+        latin:keyWidth="7.5%p"
+    >
+        <include
+            latin:keyboardLayout="@xml/rowkeys_khmer2" />
+    </Row>
+    <Row
+        latin:keyWidth="7.5%p"
+    >
+        <include
+            latin:keyboardLayout="@xml/rowkeys_khmer3" />
+        <Key
+            latin:keyStyle="enterKeyStyle"
+            latin:keyWidth="fillRight" />
+    </Row>
+    <Row
+        latin:keyWidth="7.5%p"
+    >
+        <Key
+            latin:keyStyle="shiftKeyStyle"
+            latin:keyWidth="10.0%p" />
+        <include
+            latin:keyboardLayout="@xml/rowkeys_khmer4" />
+        <switch>
+            <case
+                latin:keyboardLayoutSetElement="alphabetManualShifted|alphabetShiftLocked|alphabetShiftLockShifted"
+            >
+                <Spacer />
+            </case>
+            <default>
+                <include
+                    latin:keyboardLayout="@xml/keys_exclamation_question" />
+            </default>
+        </switch>
+    </Row>
+    <include
+        latin:keyboardLayout="@xml/row_qwerty4" />
+</merge>
diff --git a/java/res/xml-sw600dp/rows_lao.xml b/java/res/xml-sw600dp/rows_lao.xml
new file mode 100644
index 0000000..446d9bd
--- /dev/null
+++ b/java/res/xml-sw600dp/rows_lao.xml
@@ -0,0 +1,72 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+**
+** Copyright 2013, 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.
+*/
+-->
+
+<merge
+    xmlns:latin="http://schemas.android.com/apk/res/com.android.inputmethod.latin"
+>
+    <include
+        latin:keyboardLayout="@xml/key_styles_common" />
+    <Row
+        latin:keyWidth="7.5%p"
+    >
+        <include
+            latin:keyboardLayout="@xml/rowkeys_lao1" />
+        <Key
+            latin:keyStyle="deleteKeyStyle"
+            latin:keyWidth="fillRight" />
+        </Row>
+    <Row
+        latin:keyWidth="7.5%p"
+    >
+        <include
+            latin:keyboardLayout="@xml/rowkeys_lao2" />
+    </Row>
+    <Row
+        latin:keyWidth="7.5%p"
+    >
+        <include
+            latin:keyboardLayout="@xml/rowkeys_lao3" />
+        <Key
+            latin:keyStyle="enterKeyStyle"
+            latin:keyWidth="fillRight" />
+    </Row>
+    <Row
+        latin:keyWidth="7.5%p"
+    >
+        <Key
+            latin:keyStyle="shiftKeyStyle"
+            latin:keyWidth="10.0%p" />
+        <include
+            latin:keyboardLayout="@xml/rowkeys_lao4" />
+        <switch>
+            <case
+                latin:keyboardLayoutSetElement="alphabetManualShifted|alphabetShiftLocked|alphabetShiftLockShifted"
+            >
+                <Spacer />
+            </case>
+            <default>
+                <include
+                    latin:keyboardLayout="@xml/keys_exclamation_question" />
+            </default>
+        </switch>
+    </Row>
+    <include
+        latin:keyboardLayout="@xml/row_qwerty4" />
+</merge>
diff --git a/java/res/xml-sw600dp/rows_mongolian.xml b/java/res/xml-sw600dp/rows_mongolian.xml
index dc0c1fe..8e39e62 100644
--- a/java/res/xml-sw600dp/rows_mongolian.xml
+++ b/java/res/xml-sw600dp/rows_mongolian.xml
@@ -50,7 +50,7 @@
         <include
             latin:keyboardLayout="@xml/rowkeys_mongolian3" />
         <include
-            latin:keyboardLayout="@xml/keys_comma_period" />
+            latin:keyboardLayout="@xml/keys_exclamation_question" />
     </Row>
     <include
         latin:keyboardLayout="@xml/row_qwerty4" />
diff --git a/java/res/xml-sw600dp/rows_10_10_7_symbols.xml b/java/res/xml-sw600dp/rows_nepali_romanized.xml
similarity index 66%
rename from java/res/xml-sw600dp/rows_10_10_7_symbols.xml
rename to java/res/xml-sw600dp/rows_nepali_romanized.xml
index 0e4710c..21d1dc6 100644
--- a/java/res/xml-sw600dp/rows_10_10_7_symbols.xml
+++ b/java/res/xml-sw600dp/rows_nepali_romanized.xml
@@ -2,7 +2,7 @@
 <!--
 /*
 **
-** Copyright 2012, The Android Open Source Project
+** Copyright 2013, 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.
@@ -23,38 +23,35 @@
 >
     <include
         latin:keyboardLayout="@xml/key_styles_common" />
-    <include
-        latin:keyboardLayout="@xml/key_styles_currency" />
     <Row
-        latin:keyWidth="9.0%p"
+        latin:keyWidth="8.182%p"
     >
         <include
-            latin:keyboardLayout="@xml/rowkeys_symbols1" />
+            latin:keyboardLayout="@xml/rowkeys_nepali_romanized1" />
         <Key
             latin:keyStyle="deleteKeyStyle"
             latin:keyWidth="fillRight" />
     </Row>
     <Row
-        latin:keyWidth="9.0%p"
+        latin:keyWidth="8.182%p"
     >
         <include
-            latin:keyboardLayout="@xml/rowkeys_symbols2" />
+            latin:keyboardLayout="@xml/rowkeys_nepali_romanized2" />
         <Key
             latin:keyStyle="enterKeyStyle"
             latin:keyWidth="fillRight" />
     </Row>
     <Row
-        latin:keyWidth="9.0%p"
+        latin:keyWidth="8.182%p"
     >
         <Key
-            latin:keyStyle="toMoreSymbolKeyStyle"
+            latin:keyStyle="shiftKeyStyle"
             latin:keyWidth="10.0%p" />
         <include
-            latin:keyboardLayout="@xml/rowkeys_symbols3" />
-        <Key
-            latin:keyStyle="toMoreSymbolKeyStyle"
-            latin:keyWidth="fillRight" />
+            latin:keyboardLayout="@xml/rowkeys_nepali_romanized3" />
+        <include
+            latin:keyboardLayout="@xml/keys_exclamation_question" />
     </Row>
     <include
-        latin:keyboardLayout="@xml/row_symbols4" />
+        latin:keyboardLayout="@xml/row_qwerty4" />
 </merge>
diff --git a/java/res/xml-sw600dp/rows_10_10_7_symbols.xml b/java/res/xml-sw600dp/rows_nepali_traditional.xml
similarity index 65%
copy from java/res/xml-sw600dp/rows_10_10_7_symbols.xml
copy to java/res/xml-sw600dp/rows_nepali_traditional.xml
index 0e4710c..90703da 100644
--- a/java/res/xml-sw600dp/rows_10_10_7_symbols.xml
+++ b/java/res/xml-sw600dp/rows_nepali_traditional.xml
@@ -2,7 +2,7 @@
 <!--
 /*
 **
-** Copyright 2012, The Android Open Source Project
+** Copyright 2013, 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.
@@ -23,38 +23,35 @@
 >
     <include
         latin:keyboardLayout="@xml/key_styles_common" />
-    <include
-        latin:keyboardLayout="@xml/key_styles_currency" />
     <Row
-        latin:keyWidth="9.0%p"
+        latin:keyWidth="8.182%p"
     >
         <include
-            latin:keyboardLayout="@xml/rowkeys_symbols1" />
+            latin:keyboardLayout="@xml/rowkeys_nepali_traditional1" />
         <Key
             latin:keyStyle="deleteKeyStyle"
             latin:keyWidth="fillRight" />
     </Row>
     <Row
-        latin:keyWidth="9.0%p"
+        latin:keyWidth="8.182%p"
     >
         <include
-            latin:keyboardLayout="@xml/rowkeys_symbols2" />
+            latin:keyboardLayout="@xml/rowkeys_nepali_traditional2" />
         <Key
             latin:keyStyle="enterKeyStyle"
             latin:keyWidth="fillRight" />
     </Row>
     <Row
-        latin:keyWidth="9.0%p"
+        latin:keyWidth="8.182%p"
     >
         <Key
-            latin:keyStyle="toMoreSymbolKeyStyle"
+            latin:keyStyle="shiftKeyStyle"
             latin:keyWidth="10.0%p" />
         <include
-            latin:keyboardLayout="@xml/rowkeys_symbols3" />
-        <Key
-            latin:keyStyle="toMoreSymbolKeyStyle"
-            latin:keyWidth="fillRight" />
-    </Row>
+            latin:keyboardLayout="@xml/rowkeys_nepali_traditional3_left6" />
+        <include
+            latin:keyboardLayout="@xml/rowkeys_nepali_traditional3_right5" />
+        </Row>
     <include
-        latin:keyboardLayout="@xml/row_symbols4" />
+        latin:keyboardLayout="@xml/row_qwerty4" />
 </merge>
diff --git a/java/res/xml-sw600dp/rows_nordic.xml b/java/res/xml-sw600dp/rows_nordic.xml
index 299bf89..56fa406 100644
--- a/java/res/xml-sw600dp/rows_nordic.xml
+++ b/java/res/xml-sw600dp/rows_nordic.xml
@@ -52,7 +52,7 @@
         <include
             latin:keyboardLayout="@xml/rowkeys_qwerty3" />
         <include
-            latin:keyboardLayout="@xml/keys_comma_period" />
+            latin:keyboardLayout="@xml/keys_exclamation_question" />
         <Key
             latin:keyStyle="shiftKeyStyle"
             latin:keyXPos="-10.0%p"
diff --git a/java/res/xml-sw600dp/rows_pcqwerty.xml b/java/res/xml-sw600dp/rows_pcqwerty.xml
index fa6080a..8714815 100644
--- a/java/res/xml-sw600dp/rows_pcqwerty.xml
+++ b/java/res/xml-sw600dp/rows_pcqwerty.xml
@@ -26,8 +26,19 @@
     <Row
         latin:keyWidth="7.0%p"
     >
-        <include
-            latin:keyboardLayout="@xml/rowkeys_pcqwerty1" />
+        <switch>
+            <case
+                latin:keyboardLayoutSetElement="alphabet|alphabetAutomaticShifted"
+            >
+                <include
+                    latin:keyboardLayout="@xml/rowkeys_pcqwerty1" />
+            </case>
+            <!-- keyboardLayoutSetElement="alphabetManualShifted|alphabetShiftLocked|alphabetShiftLockShifted" -->
+            <default>
+                <include
+                     latin:keyboardLayout="@xml/rowkeys_pcqwerty1_shift" />
+            </default>
+        </switch>
         <Key
             latin:keyStyle="deleteKeyStyle"
             latin:keyWidth="fillRight" />
@@ -44,9 +55,7 @@
     <Row
         latin:keyWidth="7.0%p"
     >
-        <Key
-            latin:keyStyle="toSymbolKeyStyle"
-            latin:keyLabel="!text/label_to_symbol_key_pcqwerty"
+        <Spacer
             latin:keyWidth="12.0%p" />
         <include
             latin:keyboardLayout="@xml/rowkeys_pcqwerty3" />
diff --git a/java/res/xml-sw600dp/rows_pcqwerty_symbols.xml b/java/res/xml-sw600dp/rows_pcqwerty_symbols.xml
deleted file mode 100644
index 5e1aa63..0000000
--- a/java/res/xml-sw600dp/rows_pcqwerty_symbols.xml
+++ /dev/null
@@ -1,67 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-/*
-**
-** Copyright 2012, 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.
-*/
--->
-
-<merge
-    xmlns:latin="http://schemas.android.com/apk/res/com.android.inputmethod.latin"
->
-    <include
-        latin:keyboardLayout="@xml/key_styles_common" />
-    <include
-        latin:keyboardLayout="@xml/key_styles_currency" />
-    <Row
-        latin:keyWidth="7.0%p"
-    >
-        <include
-            latin:keyboardLayout="@xml/rowkeys_pcqwerty1" />
-        <Key
-            latin:keyStyle="deleteKeyStyle"
-            latin:keyWidth="fillRight" />
-    </Row>
-    <Row
-        latin:keyWidth="7.0%p"
-    >
-        <Key
-            latin:keyStyle="tabKeyStyle"
-            latin:keyWidth="9.0%p" />
-        <include
-            latin:keyboardLayout="@xml/rowkeys_pcqwerty2" />
-    </Row>
-    <Row
-        latin:keyWidth="7.0%p"
-    >
-        <Key
-            latin:keyStyle="toAlphaKeyStyle"
-            latin:keyWidth="12.0%p" />
-        <include
-            latin:keyboardLayout="@xml/rowkeys_pcqwerty3" />
-        <Key
-            latin:keyStyle="enterKeyStyle"
-            latin:keyWidth="fillRight" />
-    </Row>
-    <Row
-        latin:keyWidth="7.0%p"
-    >
-        <include
-            latin:keyboardLayout="@xml/rowkeys_pcqwerty4"
-            latin:keyXPos="15.0%p" />
-    </Row>
-    <include
-        latin:keyboardLayout="@xml/row_pcqwerty5" />
-</merge>
diff --git a/java/res/xml-sw600dp/rows_qwerty.xml b/java/res/xml-sw600dp/rows_qwerty.xml
index 722f9d1..58ba1d7 100644
--- a/java/res/xml-sw600dp/rows_qwerty.xml
+++ b/java/res/xml-sw600dp/rows_qwerty.xml
@@ -51,7 +51,7 @@
         <include
             latin:keyboardLayout="@xml/rowkeys_qwerty3" />
         <include
-            latin:keyboardLayout="@xml/keys_comma_period" />
+            latin:keyboardLayout="@xml/keys_exclamation_question" />
         <Key
             latin:keyStyle="shiftKeyStyle"
             latin:keyWidth="fillRight" />
diff --git a/java/res/xml-sw600dp/rows_qwertz.xml b/java/res/xml-sw600dp/rows_qwertz.xml
index f2f832c..3b59dec 100644
--- a/java/res/xml-sw600dp/rows_qwertz.xml
+++ b/java/res/xml-sw600dp/rows_qwertz.xml
@@ -51,7 +51,7 @@
         <include
             latin:keyboardLayout="@xml/rowkeys_qwertz3" />
         <include
-            latin:keyboardLayout="@xml/keys_comma_period" />
+            latin:keyboardLayout="@xml/keys_exclamation_question" />
         <Key
             latin:keyStyle="shiftKeyStyle"
             latin:keyWidth="fillRight" />
diff --git a/java/res/xml-sw600dp/rows_south_slavic.xml b/java/res/xml-sw600dp/rows_south_slavic.xml
index 6ef6643..5053988 100644
--- a/java/res/xml-sw600dp/rows_south_slavic.xml
+++ b/java/res/xml-sw600dp/rows_south_slavic.xml
@@ -50,7 +50,7 @@
         <include
             latin:keyboardLayout="@xml/rowkeys_south_slavic3" />
         <include
-            latin:keyboardLayout="@xml/keys_comma_period" />
+            latin:keyboardLayout="@xml/keys_exclamation_question" />
     </Row>
     <include
         latin:keyboardLayout="@xml/row_qwerty4" />
diff --git a/java/res/xml-sw600dp/rows_spanish.xml b/java/res/xml-sw600dp/rows_spanish.xml
index bca9bba..1092c26 100644
--- a/java/res/xml-sw600dp/rows_spanish.xml
+++ b/java/res/xml-sw600dp/rows_spanish.xml
@@ -50,7 +50,7 @@
         <include
             latin:keyboardLayout="@xml/rowkeys_qwerty3" />
         <include
-            latin:keyboardLayout="@xml/keys_comma_period" />
+            latin:keyboardLayout="@xml/keys_exclamation_question" />
         <Key
             latin:keyStyle="shiftKeyStyle"
             latin:keyWidth="fillRight" />
diff --git a/java/res/xml-sw600dp/rows_symbols.xml b/java/res/xml-sw600dp/rows_symbols.xml
index 3d0593d..fbd8492 100644
--- a/java/res/xml-sw600dp/rows_symbols.xml
+++ b/java/res/xml-sw600dp/rows_symbols.xml
@@ -50,12 +50,23 @@
         <Key
             latin:keyStyle="toMoreSymbolKeyStyle"
             latin:keyWidth="10.0%p" />
+        <Key
+            latin:keyLabel="\\" />
+        <Key
+            latin:keyLabel="=" />
         <include
             latin:keyboardLayout="@xml/rowkeys_symbols3" />
         <Key
             latin:keyStyle="toMoreSymbolKeyStyle"
             latin:keyWidth="fillRight" />
     </Row>
-    <include
-        latin:keyboardLayout="@xml/row_symbols4" />
+    <Row
+        latin:keyWidth="9.0%p"
+    >
+        <Key
+            latin:keyStyle="toAlphaKeyStyle"
+            latin:keyWidth="10.0%p" />
+        <include
+            latin:keyboardLayout="@xml/row_symbols4" />
+    </Row>
 </merge>
diff --git a/java/res/xml-sw600dp/rows_symbols_shift.xml b/java/res/xml-sw600dp/rows_symbols_shift.xml
index 0050c0c..aad047f 100644
--- a/java/res/xml-sw600dp/rows_symbols_shift.xml
+++ b/java/res/xml-sw600dp/rows_symbols_shift.xml
@@ -52,10 +52,23 @@
             latin:keyWidth="10.0%p" />
         <include
             latin:keyboardLayout="@xml/rowkeys_symbols_shift3" />
+        <!-- U+00A1: "¡" INVERTED EXCLAMATION MARK -->
+        <Key
+            latin:keyLabel="&#x00A1;" />
+        <!-- U+00BF: "¿" INVERTED QUESTION MARK -->
+        <Key
+            latin:keyLabel="&#x00BF;" />
         <Key
             latin:keyStyle="backFromMoreSymbolKeyStyle"
             latin:keyWidth="fillRight" />
     </Row>
-    <include
-        latin:keyboardLayout="@xml/row_symbols_shift4" />
+    <Row
+        latin:keyWidth="9.0%p"
+    >
+        <Key
+            latin:keyStyle="toAlphaKeyStyle"
+            latin:keyWidth="10%p" />
+        <include
+            latin:keyboardLayout="@xml/row_symbols_shift4" />
+    </Row>
 </merge>
diff --git a/java/res/xml-sw600dp/rows_thai.xml b/java/res/xml-sw600dp/rows_thai.xml
index bc89640..7738c7f 100644
--- a/java/res/xml-sw600dp/rows_thai.xml
+++ b/java/res/xml-sw600dp/rows_thai.xml
@@ -59,8 +59,17 @@
             latin:keyWidth="10.0%p" />
         <include
             latin:keyboardLayout="@xml/rowkeys_thai4" />
-        <include
-            latin:keyboardLayout="@xml/keys_comma_period" />
+        <switch>
+            <case
+                latin:keyboardLayoutSetElement="alphabetManualShifted|alphabetShiftLocked|alphabetShiftLockShifted"
+            >
+                <Spacer />
+            </case>
+            <default>
+                <include
+                    latin:keyboardLayout="@xml/keys_exclamation_question" />
+            </default>
+        </switch>
     </Row>
     <include
         latin:keyboardLayout="@xml/row_qwerty4" />
diff --git a/java/res/xml-sw768dp-land/kbd_more_keys_keyboard_template.xml b/java/res/xml-sw768dp-land/kbd_more_keys_keyboard_template.xml
deleted file mode 100644
index f593fa9..0000000
--- a/java/res/xml-sw768dp-land/kbd_more_keys_keyboard_template.xml
+++ /dev/null
@@ -1,26 +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="3.5%p"
-    latin:rowHeight="@dimen/popup_key_height"
-    style="?attr/moreKeysKeyboardStyle"
-    >
-</Keyboard>
diff --git a/java/res/xml-sw768dp-land/kbd_number.xml b/java/res/xml-sw768dp-land/kbd_number.xml
deleted file mode 100644
index 1cb775e..0000000
--- a/java/res/xml-sw768dp-land/kbd_number.xml
+++ /dev/null
@@ -1,30 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-/*
-**
-** Copyright 2011, The Android Open Source Project
-**
-** Licensed under the Apache License, Version 2.0 (the "License");
-** you may not use this file except in compliance with the License.
-** You may obtain a copy of the License at
-**
-**     http://www.apache.org/licenses/LICENSE-2.0
-**
-** Unless required by applicable law or agreed to in writing, software
-** distributed under the License is distributed on an "AS IS" BASIS,
-** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-** See the License for the specific language governing permissions and
-** limitations under the License.
-*/
--->
-
-<Keyboard
-    xmlns:latin="http://schemas.android.com/apk/res/com.android.inputmethod.latin"
-    latin:keyboardLeftPadding="10%p"
-    latin:keyboardRightPadding="10%p"
-    latin:keyWidth="13.250%p"
-    latin:touchPositionCorrectionData="@array/touch_position_correction_data_default"
->
-    <include
-        latin:keyboardLayout="@xml/rows_number" />
-</Keyboard>
diff --git a/java/res/xml-sw768dp-land/kbd_phone.xml b/java/res/xml-sw768dp-land/kbd_phone.xml
deleted file mode 100644
index 8905182..0000000
--- a/java/res/xml-sw768dp-land/kbd_phone.xml
+++ /dev/null
@@ -1,30 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-/*
-**
-** Copyright 2011, The Android Open Source Project
-**
-** Licensed under the Apache License, Version 2.0 (the "License");
-** you may not use this file except in compliance with the License.
-** You may obtain a copy of the License at
-**
-**     http://www.apache.org/licenses/LICENSE-2.0
-**
-** Unless required by applicable law or agreed to in writing, software
-** distributed under the License is distributed on an "AS IS" BASIS,
-** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-** See the License for the specific language governing permissions and
-** limitations under the License.
-*/
--->
-
-<Keyboard
-    xmlns:latin="http://schemas.android.com/apk/res/com.android.inputmethod.latin"
-    latin:keyboardLeftPadding="10%p"
-    latin:keyboardRightPadding="10%p"
-    latin:keyWidth="13.250%p"
-    latin:touchPositionCorrectionData="@array/touch_position_correction_data_default"
->
-    <include
-        latin:keyboardLayout="@xml/rows_phone" />
-</Keyboard>
diff --git a/java/res/xml-sw768dp-land/kbd_phone_symbols.xml b/java/res/xml-sw768dp-land/kbd_phone_symbols.xml
deleted file mode 100644
index 6038b1f..0000000
--- a/java/res/xml-sw768dp-land/kbd_phone_symbols.xml
+++ /dev/null
@@ -1,31 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-/*
-**
-** Copyright 2011, The Android Open Source Project
-**
-** Licensed under the Apache License, Version 2.0 (the "License");
-** you may not use this file except in compliance with the License.
-** You may obtain a copy of the License at
-**
-**     http://www.apache.org/licenses/LICENSE-2.0
-**
-** Unless required by applicable law or agreed to in writing, software
-** distributed under the License is distributed on an "AS IS" BASIS,
-** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-** See the License for the specific language governing permissions and
-** limitations under the License.
-*/
--->
-
-<Keyboard
-    xmlns:latin="http://schemas.android.com/apk/res/com.android.inputmethod.latin"
-    latin:keyboardLeftPadding="10%p"
-    latin:keyboardRightPadding="10%p"
-    latin:keyWidth="13.250%p"
-    latin:touchPositionCorrectionData="@array/touch_position_correction_data_default"
->
-    <!-- Tablet doesn't have phone symbols keyboard -->
-    <include
-        latin:keyboardLayout="@xml/rows_phone" />
-</Keyboard>
diff --git a/java/res/xml-sw768dp/kbd_number.xml b/java/res/xml-sw768dp/kbd_number.xml
deleted file mode 100644
index 1b46edd..0000000
--- a/java/res/xml-sw768dp/kbd_number.xml
+++ /dev/null
@@ -1,28 +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="13.250%p"
-    latin:touchPositionCorrectionData="@array/touch_position_correction_data_default"
->
-    <include
-        latin:keyboardLayout="@xml/rows_number" />
-</Keyboard>
diff --git a/java/res/xml-sw768dp/kbd_phone.xml b/java/res/xml-sw768dp/kbd_phone.xml
deleted file mode 100644
index 947ede0..0000000
--- a/java/res/xml-sw768dp/kbd_phone.xml
+++ /dev/null
@@ -1,28 +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="13.250%p"
-    latin:touchPositionCorrectionData="@array/touch_position_correction_data_default"
->
-    <include
-        latin:keyboardLayout="@xml/rows_phone" />
-</Keyboard>
diff --git a/java/res/xml-sw768dp/kbd_phone_symbols.xml b/java/res/xml-sw768dp/kbd_phone_symbols.xml
deleted file mode 100644
index dd9a6ae..0000000
--- a/java/res/xml-sw768dp/kbd_phone_symbols.xml
+++ /dev/null
@@ -1,29 +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="13.250%p"
-    latin:touchPositionCorrectionData="@array/touch_position_correction_data_default"
->
-    <!-- Tablet doesn't have phone symbols keyboard -->
-    <include
-        latin:keyboardLayout="@xml/rows_phone" />
-</Keyboard>
diff --git a/java/res/xml-sw768dp/kbd_thai_symbols_shift.xml b/java/res/xml-sw768dp/kbd_thai_symbols_shift.xml
deleted file mode 100644
index 135222b..0000000
--- a/java/res/xml-sw768dp/kbd_thai_symbols_shift.xml
+++ /dev/null
@@ -1,31 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-/*
-**
-** Copyright 2012, 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:rowHeight="20%p"
-    latin:verticalGap="@fraction/key_bottom_gap_5row"
-    latin:keyLetterSize="@fraction/key_letter_ratio_5row"
-    latin:keyShiftedLetterHintRatio="@fraction/key_uppercase_letter_ratio_5row"
-    latin:touchPositionCorrectionData="@array/touch_position_correction_data_default"
->
-    <include
-        latin:keyboardLayout="@xml/rows_thai_symbols_shift" />
-</Keyboard>
diff --git a/java/res/xml-sw768dp/key_shortcut.xml b/java/res/xml-sw768dp/key_shortcut.xml
deleted file mode 100644
index 2d09ebb..0000000
--- a/java/res/xml-sw768dp/key_shortcut.xml
+++ /dev/null
@@ -1,36 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-/*
-**
-** Copyright 2012, 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.
-*/
--->
-
-<merge
-    xmlns:latin="http://schemas.android.com/apk/res/com.android.inputmethod.latin"
->
-    <switch>
-        <case
-            latin:shortcutKeyEnabled="true"
-        >
-            <Key
-                latin:keyStyle="shortcutKeyStyle" />
-        </case>
-        <default>
-            <!-- The empty space instead of shortcut key. -->
-            <Spacer />
-        </default>
-    </switch>
-</merge>
diff --git a/java/res/xml-sw768dp/key_space.xml b/java/res/xml-sw768dp/key_space.xml
deleted file mode 100644
index 58e71d8..0000000
--- a/java/res/xml-sw768dp/key_space.xml
+++ /dev/null
@@ -1,63 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-/*
-**
-** Copyright 2012, 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.
-*/
--->
-
-<merge
-    xmlns:latin="http://schemas.android.com/apk/res/com.android.inputmethod.latin"
->
-    <switch>
-        <case
-            latin:languageCode="fa"
-            latin:languageSwitchKeyEnabled="true"
-        >
-            <Key
-                latin:keyStyle="languageSwitchKeyStyle" />
-            <Key
-                latin:keyStyle="spaceKeyStyle"
-                latin:keyWidth="24.141%p" />
-            <Key
-                latin:keyStyle="zwnjKeyStyle" />
-        </case>
-        <case
-            latin:languageCode="fa"
-            latin:languageSwitchKeyEnabled="false"
-        >
-            <Key
-                latin:keyStyle="spaceKeyStyle"
-                latin:keyWidth="32.188%p" />
-            <Key
-                latin:keyStyle="zwnjKeyStyle" />
-        </case>
-        <case
-            latin:languageSwitchKeyEnabled="true"
-        >
-            <Key
-                latin:keyStyle="languageSwitchKeyStyle" />
-            <Key
-                latin:keyStyle="spaceKeyStyle"
-                latin:keyWidth="32.188%p" />
-        </case>
-         <!-- languageSwitchKeyEnabled="false" -->
-        <default>
-            <Key
-                latin:keyStyle="spaceKeyStyle"
-                latin:keyWidth="40.235%p" />
-        </default>
-    </switch>
-</merge>
diff --git a/java/res/xml-sw768dp/key_styles_common.xml b/java/res/xml-sw768dp/key_styles_common.xml
deleted file mode 100644
index 7c0a82a..0000000
--- a/java/res/xml-sw768dp/key_styles_common.xml
+++ /dev/null
@@ -1,186 +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.
-*/
--->
-
-<merge
-    xmlns:latin="http://schemas.android.com/apk/res/com.android.inputmethod.latin"
->
-    <switch>
-        <case
-            latin:keyboardLayoutSetElement="alphabetManualShifted|alphabetShiftLockShifted"
-        >
-            <key-style
-                latin:styleName="hasShiftedLetterHintStyle"
-                latin:keyLabelFlags="hasShiftedLetterHint|shiftedLetterActivated" />
-        </case>
-        <default>
-            <key-style
-                latin:styleName="hasShiftedLetterHintStyle"
-                latin:keyLabelFlags="hasShiftedLetterHint" />
-        </default>
-    </switch>
-    <!-- Base style for shift key. A single space is used for dummy label in moreKeys. -->
-    <key-style
-        latin:styleName="baseForShiftKeyStyle"
-        latin:code="!code/key_shift"
-        latin:keyActionFlags="noKeyPreview"
-        latin:keyLabelFlags="preserveCase"
-        latin:moreKeys="!noPanelAutoMoreKey!, |!code/key_capslock" />
-    <switch>
-        <case
-            latin:keyboardLayoutSetElement="alphabetManualShifted|alphabetAutomaticShifted"
-        >
-            <key-style
-                latin:styleName="shiftKeyStyle"
-                latin:keyIcon="!icon/shift_key_shifted"
-                latin:backgroundType="stickyOff"
-                latin:parentStyle="baseForShiftKeyStyle" />
-        </case>
-        <case
-            latin:keyboardLayoutSetElement="alphabetShiftLocked|alphabetShiftLockShifted"
-        >
-            <key-style
-                latin:styleName="shiftKeyStyle"
-                latin:keyIcon="!icon/shift_key_shifted"
-                latin:backgroundType="stickyOn"
-                latin:parentStyle="baseForShiftKeyStyle" />
-        </case>
-        <default>
-            <key-style
-                latin:styleName="shiftKeyStyle"
-                latin:keyIcon="!icon/shift_key"
-                latin:backgroundType="stickyOff"
-                latin:parentStyle="baseForShiftKeyStyle" />
-        </default>
-    </switch>
-    <key-style
-        latin:styleName="deleteKeyStyle"
-        latin:code="!code/key_delete"
-        latin:keyIcon="!icon/delete_key"
-        latin:keyActionFlags="isRepeatable|noKeyPreview"
-        latin:backgroundType="functional" />
-    <include
-        latin:keyboardLayout="@xml/key_styles_enter" />
-    <key-style
-        latin:styleName="spaceKeyStyle"
-        latin:code="!code/key_space"
-        latin:keyActionFlags="noKeyPreview|enableLongPress" />
-    <!-- U+200C: ZERO WIDTH NON-JOINER
-         U+200D: ZERO WIDTH JOINER -->
-    <key-style
-        latin:styleName="zwnjKeyStyle"
-        latin:code="0x200C"
-        latin:keyIcon="!icon/zwnj_key"
-        latin:moreKeys="!icon/zwj_key|&#x200D;"
-        latin:keyLabelFlags="hasPopupHint"
-        latin:keyActionFlags="noKeyPreview" />
-    <key-style
-        latin:styleName="smileyKeyStyle"
-        latin:keyLabel=":-)"
-        latin:keyOutputText=":-) "
-        latin:keyLabelFlags="hasPopupHint|preserveCase"
-        latin:moreKeys="!text/more_keys_for_smiley" />
-    <key-style
-        latin:styleName="shortcutKeyStyle"
-        latin:code="!code/key_shortcut"
-        latin:keyIcon="!icon/shortcut_key"
-        latin:keyIconDisabled="!icon/shortcut_key_disabled"
-        latin:keyActionFlags="noKeyPreview"
-        latin:backgroundType="functional" />
-    <key-style
-        latin:styleName="languageSwitchKeyStyle"
-        latin:code="!code/key_language_switch"
-        latin:keyIcon="!icon/language_switch_key"
-        latin:keyActionFlags="noKeyPreview|altCodeWhileTyping|enableLongPress"
-        latin:altCode="!code/key_space" />
-    <key-style
-        latin:styleName="emojiKeyStyle"
-        latin:code="!code/key_emoji"
-        latin:keyIcon="!icon/emoji_key"
-        latin:keyActionFlags="noKeyPreview" />
-    <key-style
-        latin:styleName="settingsKeyStyle"
-        latin:code="!code/key_settings"
-        latin:keyIcon="!icon/settings_key"
-        latin:keyActionFlags="noKeyPreview"
-        latin:backgroundType="functional" />
-    <switch>
-        <case
-            latin:keyboardLayoutSetElement="alphabetManualShifted|alphabetShiftLockShifted"
-            latin:navigatePrevious="true"
-        >
-            <key-style
-                latin:styleName="tabKeyStyle"
-                latin:code="!code/key_action_previous"
-                latin:keyLabel="!text/label_tab_key"
-                latin:keyLabelFlags="fontNormal|preserveCase"
-                latin:backgroundType="functional" />
-        </case>
-        <case
-            latin:keyboardLayoutSetElement="alphabet|alphabetAutomaticShifted|alphabetShiftLocked"
-            latin:navigateNext="true"
-        >
-            <key-style
-                latin:styleName="tabKeyStyle"
-                latin:code="!code/key_action_next"
-                latin:keyLabel="!text/label_tab_key"
-                latin:keyLabelFlags="fontNormal|preserveCase"
-                latin:backgroundType="functional" />
-        </case>
-        <default>
-            <key-style
-                latin:styleName="tabKeyStyle"
-                latin:code="!code/key_tab"
-                latin:keyLabel="!text/label_tab_key"
-                latin:keyLabelFlags="fontNormal|preserveCase"
-                latin:backgroundType="functional" />
-        </default>
-    </switch>
-    <key-style
-        latin:styleName="baseForLayoutSwitchKeyStyle"
-        latin:keyLabelFlags="fontNormal|preserveCase"
-        latin:keyActionFlags="noKeyPreview"
-        latin:backgroundType="functional" />
-    <key-style
-        latin:styleName="toSymbolKeyStyle"
-        latin:code="!code/key_switch_alpha_symbol"
-        latin:keyLabel="!text/label_to_symbol_key"
-        latin:parentStyle="baseForLayoutSwitchKeyStyle" />
-    <key-style
-        latin:styleName="toAlphaKeyStyle"
-        latin:code="!code/key_switch_alpha_symbol"
-        latin:keyLabel="!text/label_to_alpha_key"
-        latin:parentStyle="baseForLayoutSwitchKeyStyle" />
-    <key-style
-        latin:styleName="toMoreSymbolKeyStyle"
-        latin:code="!code/key_shift"
-        latin:keyLabel="!text/label_to_more_symbol_for_tablet_key"
-        latin:parentStyle="baseForLayoutSwitchKeyStyle" />
-    <key-style
-        latin:styleName="backFromMoreSymbolKeyStyle"
-        latin:code="!code/key_shift"
-        latin:keyLabel="!text/label_to_symbol_key"
-        latin:parentStyle="baseForLayoutSwitchKeyStyle" />
-    <key-style
-        latin:styleName="comKeyStyle"
-        latin:keyLabel="!text/keylabel_for_popular_domain"
-        latin:keyLabelFlags="fontNormal|hasPopupHint|preserveCase"
-        latin:keyOutputText="!text/keylabel_for_popular_domain"
-        latin:moreKeys="!text/more_keys_for_popular_domain" />
-</merge>
diff --git a/java/res/xml-sw768dp/row_dvorak4.xml b/java/res/xml-sw768dp/row_dvorak4.xml
deleted file mode 100644
index 8f9230d..0000000
--- a/java/res/xml-sw768dp/row_dvorak4.xml
+++ /dev/null
@@ -1,51 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-/*
-**
-** Copyright 2012, 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.
-*/
--->
-
-<merge
-    xmlns:latin="http://schemas.android.com/apk/res/com.android.inputmethod.latin"
->
-    <Row
-        latin:keyWidth="8.047%p"
-        latin:backgroundType="functional"
-    >
-        <!-- Note: This Spacer prevents the below key from being marked as a left edge key. -->
-        <Spacer
-            latin:keyWidth="5.782%p" />
-        <include
-            latin:keyboardLayout="@xml/key_settings" />
-        <include
-            latin:keyboardLayout="@xml/key_shortcut" />
-        <include
-            latin:keyboardLayout="@xml/key_f1" />
-        <include
-            latin:keyXPos="29.923%p"
-            latin:keyboardLayout="@xml/key_space"
-            latin:backgroundType="normal" />
-        <include
-            latin:keyboardLayout="@xml/key_question_exclamation" />
-        <include
-            latin:keyboardLayout="@xml/key_dash" />
-        <include
-            latin:keyboardLayout="@xml/key_f2" />
-        <!-- Note: This Spacer prevents the above key from being marked as a right edge key. -->
-        <Spacer
-            latin:keyWidth="fillRight" />
-    </Row>
-</merge>
diff --git a/java/res/xml-sw768dp/row_hebrew4.xml b/java/res/xml-sw768dp/row_hebrew4.xml
deleted file mode 100644
index ae14f02..0000000
--- a/java/res/xml-sw768dp/row_hebrew4.xml
+++ /dev/null
@@ -1,49 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-/*
-**
-** Copyright 2012, 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.
-*/
--->
-
-<merge
-    xmlns:latin="http://schemas.android.com/apk/res/com.android.inputmethod.latin"
->
-    <Row
-        latin:keyWidth="8.047%p"
-        latin:backgroundType="functional"
-    >
-        <!-- Note: This Spacer prevents the below key from being marked as a left edge key. -->
-        <Spacer
-            latin:keyWidth="5.782%p" />
-        <include
-            latin:keyboardLayout="@xml/key_settings" />
-        <include
-            latin:keyboardLayout="@xml/key_shortcut" />
-        <include
-            latin:keyboardLayout="@xml/key_f1" />
-        <include
-            latin:keyXPos="29.923%p"
-            latin:keyboardLayout="@xml/key_space"
-            latin:backgroundType="normal" />
-        <include
-            latin:keyboardLayout="@xml/keys_comma_period" />
-        <include
-            latin:keyboardLayout="@xml/key_f2" />
-        <!-- Note: This Spacer prevents the above key from being marked as a right edge key. -->
-        <Spacer
-            latin:keyWidth="fillRight" />
-    </Row>
-</merge>
diff --git a/java/res/xml-sw768dp/row_pcqwerty5.xml b/java/res/xml-sw768dp/row_pcqwerty5.xml
deleted file mode 100644
index e27ec87..0000000
--- a/java/res/xml-sw768dp/row_pcqwerty5.xml
+++ /dev/null
@@ -1,59 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-/*
-**
-** Copyright 2012, 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.
-*/
--->
-
-<merge
-    xmlns:latin="http://schemas.android.com/apk/res/com.android.inputmethod.latin"
->
-    <Row
-        latin:keyWidth="8.047%p"
-    >
-        <include
-            latin:keyboardLayout="@xml/key_settings" />
-        <switch>
-            <case
-                latin:keyboardLayoutSetElement="symbols|symbolsShifted"
-            >
-                <Spacer
-                    latin:keyXPos="15.0%p"
-                    latin:keyWidth="10.5%p" />
-            </case>
-            <case
-                latin:mode="email|url"
-            >
-                <Key
-                    latin:keyStyle="comKeyStyle"
-                    latin:keyXPos="15.0%p"
-                    latin:keyWidth="10.5%p" />
-            </case>
-            <default>
-                <Spacer
-                    latin:keyXPos="15.0%p"
-                    latin:keyWidth="10.5%p" />
-            </default>
-        </switch>
-        <Key
-            latin:keyStyle="spaceKeyStyle"
-            latin:keyWidth="49.0%p" />
-        <include
-            latin:keyXPos="-8.047%p"
-            latin:keyWidth="fillRight"
-            latin:keyboardLayout="@xml/key_shortcut" />
-    </Row>
-</merge>
diff --git a/java/res/xml-sw768dp/row_qwerty4.xml b/java/res/xml-sw768dp/row_qwerty4.xml
deleted file mode 100644
index f1f4214..0000000
--- a/java/res/xml-sw768dp/row_qwerty4.xml
+++ /dev/null
@@ -1,51 +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.
-*/
--->
-
-<merge
-    xmlns:latin="http://schemas.android.com/apk/res/com.android.inputmethod.latin"
->
-    <Row
-        latin:keyWidth="8.047%p"
-        latin:backgroundType="functional"
-    >
-        <!-- Note: This Spacer prevents the below key from being marked as a left edge key. -->
-        <Spacer
-            latin:keyWidth="5.782%p" />
-        <include
-            latin:keyboardLayout="@xml/key_settings" />
-        <include
-            latin:keyboardLayout="@xml/key_shortcut" />
-        <include
-            latin:keyboardLayout="@xml/key_f1" />
-        <include
-            latin:keyXPos="29.923%p"
-            latin:keyboardLayout="@xml/key_space"
-            latin:backgroundType="normal" />
-        <include
-            latin:keyboardLayout="@xml/key_apostrophe" />
-        <include
-            latin:keyboardLayout="@xml/key_dash" />
-        <include
-            latin:keyboardLayout="@xml/key_f2" />
-        <!-- Note: This Spacer prevents the above key from being marked as a right edge key. -->
-        <Spacer
-            latin:keyWidth="fillRight" />
-    </Row>
-</merge>
diff --git a/java/res/xml-sw768dp/row_symbols4.xml b/java/res/xml-sw768dp/row_symbols4.xml
deleted file mode 100644
index b801a12..0000000
--- a/java/res/xml-sw768dp/row_symbols4.xml
+++ /dev/null
@@ -1,48 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-/*
-**
-** Copyright 2012, 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.
-*/
--->
-
-<merge
-    xmlns:latin="http://schemas.android.com/apk/res/com.android.inputmethod.latin"
->
-    <Row
-        latin:keyWidth="8.047%p"
-        latin:backgroundType="functional"
-    >
-        <!-- Note: This Spacer prevents the below key from being marked as a left edge key. -->
-        <Spacer
-            latin:keyWidth="13.829%p" />
-        <Key
-            latin:keyLabel="/" />
-        <include
-            latin:keyboardLayout="@xml/key_f1" />
-        <include
-            latin:keyXPos="29.923%p"
-            latin:keyboardLayout="@xml/key_space"
-            latin:backgroundType="normal" />
-        <Key
-            latin:keyLabel="&quot;"
-            latin:moreKeys="!text/more_keys_for_tablet_double_quote" />
-        <Key
-            latin:keyLabel="_" />
-        <!-- Note: This Spacer prevents the above key from being marked as a right edge key. -->
-        <Spacer
-            latin:keyWidth="fillRight" />
-    </Row>
-</merge>
diff --git a/java/res/xml-sw768dp/row_symbols_shift4.xml b/java/res/xml-sw768dp/row_symbols_shift4.xml
deleted file mode 100644
index f71864b..0000000
--- a/java/res/xml-sw768dp/row_symbols_shift4.xml
+++ /dev/null
@@ -1,38 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-/*
-**
-** Copyright 2012, 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.
-*/
--->
-
-<merge
-    xmlns:latin="http://schemas.android.com/apk/res/com.android.inputmethod.latin"
->
-    <Row
-        latin:keyWidth="8.047%p"
-        latin:backgroundType="functional"
-    >
-        <!-- Note: This Spacer prevents the below key from being marked as a left edge key. -->
-        <Spacer
-            latin:keyWidth="29.923%p" />
-        <include
-            latin:keyboardLayout="@xml/key_space"
-            latin:backgroundType="normal" />
-        <!-- Note: This Spacer prevents the above key from being marked as a right edge key. -->
-        <Spacer
-            latin:keyWidth="fillRight" />
-    </Row>
-</merge>
diff --git a/java/res/xml-sw768dp/rowkeys_thai_digits.xml b/java/res/xml-sw768dp/rowkeys_thai_digits.xml
deleted file mode 100644
index 55196eb..0000000
--- a/java/res/xml-sw768dp/rowkeys_thai_digits.xml
+++ /dev/null
@@ -1,64 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-/*
-**
-** Copyright 2012, 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.
-*/
--->
-
-<merge
-    xmlns:latin="http://schemas.android.com/apk/res/com.android.inputmethod.latin"
->
-    <!-- U+0E51: "๑" THAI DIGIT ONE -->
-    <Key
-        latin:keyLabel="&#x0E51;"
-        latin:keyLabelFlags="fontNormal" />
-    <!-- U+0E52: "๒" THAI DIGIT TWO -->
-    <Key
-        latin:keyLabel="&#x0E52;"
-        latin:keyLabelFlags="fontNormal" />
-    <!-- U+0E53: "๓" THAI DIGIT THREE -->
-    <Key
-        latin:keyLabel="&#x0E53;"
-        latin:keyLabelFlags="fontNormal" />
-    <!-- U+0E54: "๔" THAI DIGIT FOUR -->
-    <Key
-        latin:keyLabel="&#x0E54;"
-        latin:keyLabelFlags="fontNormal" />
-    <!-- U+0E55: "๕" THAI DIGIT FIVE -->
-    <Key
-        latin:keyLabel="&#x0E55;"
-        latin:keyLabelFlags="fontNormal" />
-    <!-- U+0E56: "๖" THAI DIGIT SIX -->
-    <Key
-        latin:keyLabel="&#x0E56;"
-        latin:keyLabelFlags="fontNormal" />
-    <!-- U+0E57: "๗" THAI DIGIT SEVEN -->
-    <Key
-        latin:keyLabel="&#x0E57;"
-        latin:keyLabelFlags="fontNormal" />
-    <!-- U+0E58: "๘" THAI DIGIT EIGHT -->
-    <Key
-        latin:keyLabel="&#x0E58;"
-        latin:keyLabelFlags="fontNormal" />
-    <!-- U+0E59: "๙" THAI DIGIT NINE -->
-    <Key
-        latin:keyLabel="&#x0E59;"
-        latin:keyLabelFlags="fontNormal" />
-    <!-- U+0E50: "๐" THAI DIGIT ZERO -->
-    <Key
-        latin:keyLabel="&#x0E50;"
-        latin:keyLabelFlags="fontNormal" />
-</merge>
diff --git a/java/res/xml-sw768dp/rows_10_10_7_symbols.xml b/java/res/xml-sw768dp/rows_10_10_7_symbols.xml
deleted file mode 100644
index d9b0d23..0000000
--- a/java/res/xml-sw768dp/rows_10_10_7_symbols.xml
+++ /dev/null
@@ -1,69 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-/*
-**
-** Copyright 2012, 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.
-*/
--->
-
-<merge
-    xmlns:latin="http://schemas.android.com/apk/res/com.android.inputmethod.latin"
->
-    <include
-        latin:keyboardLayout="@xml/key_styles_common" />
-    <include
-        latin:keyboardLayout="@xml/key_styles_currency" />
-    <Row
-        latin:keyWidth="8.282%p"
-    >
-        <Key
-            latin:keyStyle="tabKeyStyle"
-            latin:keyLabelFlags="alignLeft"
-            latin:keyWidth="7.969%p" />
-        <include
-            latin:keyboardLayout="@xml/rowkeys_symbols1" />
-        <Key
-            latin:keyStyle="deleteKeyStyle"
-            latin:keyWidth="fillRight" />
-    </Row>
-    <Row
-        latin:keyWidth="8.125%p"
-    >
-        <Key
-            latin:keyStyle="toAlphaKeyStyle"
-            latin:keyLabelFlags="alignLeft"
-            latin:keyWidth="10.167%p" />
-        <include
-            latin:keyboardLayout="@xml/rowkeys_symbols2" />
-        <Key
-            latin:keyStyle="enterKeyStyle"
-            latin:keyWidth="fillRight" />
-    </Row>
-    <Row
-        latin:keyWidth="8.047%p"
-    >
-        <Key
-            latin:keyStyle="toMoreSymbolKeyStyle"
-            latin:keyWidth="13.829%p" />
-        <include
-            latin:keyboardLayout="@xml/rowkeys_symbols3" />
-        <Key
-            latin:keyStyle="toMoreSymbolKeyStyle"
-            latin:keyXPos="-13.750%p"
-            latin:keyWidth="fillRight" />
-    </Row>
-    <include
-        latin:keyboardLayout="@xml/row_symbols4" />
-</merge>
diff --git a/java/res/xml-sw768dp/rows_10_10_7_symbols_shift.xml b/java/res/xml-sw768dp/rows_10_10_7_symbols_shift.xml
deleted file mode 100644
index a317dbf..0000000
--- a/java/res/xml-sw768dp/rows_10_10_7_symbols_shift.xml
+++ /dev/null
@@ -1,69 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-/*
-**
-** Copyright 2012, 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.
-*/
--->
-
-<merge
-    xmlns:latin="http://schemas.android.com/apk/res/com.android.inputmethod.latin"
->
-    <include
-        latin:keyboardLayout="@xml/key_styles_common" />
-    <include
-        latin:keyboardLayout="@xml/key_styles_currency" />
-    <Row
-        latin:keyWidth="8.282%p"
-    >
-        <Key
-            latin:keyStyle="tabKeyStyle"
-            latin:keyLabelFlags="alignLeft"
-            latin:keyWidth="7.969%p" />
-        <include
-            latin:keyboardLayout="@xml/rowkeys_symbols_shift1" />
-        <Key
-            latin:keyStyle="deleteKeyStyle"
-            latin:keyWidth="fillRight" />
-    </Row>
-    <Row
-        latin:keyWidth="8.125%p"
-    >
-        <Key
-            latin:keyStyle="toAlphaKeyStyle"
-            latin:keyLabelFlags="alignLeft"
-            latin:keyWidth="10.167%p" />
-        <include
-            latin:keyboardLayout="@xml/rowkeys_symbols_shift2" />
-        <Key
-            latin:keyStyle="enterKeyStyle"
-            latin:keyWidth="fillRight" />
-    </Row>
-    <Row
-        latin:keyWidth="8.047%p"
-    >
-        <Key
-            latin:keyStyle="backFromMoreSymbolKeyStyle"
-            latin:keyWidth="13.829%p" />
-        <include
-            latin:keyboardLayout="@xml/rowkeys_symbols_shift3" />
-        <Key
-            latin:keyStyle="backFromMoreSymbolKeyStyle"
-            latin:keyXPos="-13.750%p"
-            latin:keyWidth="fillRight" />
-    </Row>
-    <include
-        latin:keyboardLayout="@xml/row_symbols_shift4" />
-</merge>
diff --git a/java/res/xml-sw768dp/rows_arabic.xml b/java/res/xml-sw768dp/rows_arabic.xml
deleted file mode 100644
index 204f6d5..0000000
--- a/java/res/xml-sw768dp/rows_arabic.xml
+++ /dev/null
@@ -1,61 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-/*
-**
-** Copyright 2011, The Android Open Source Project
-**
-** Licensed under the Apache License, Version 2.0 (the "License");
-** you may not use this file except in compliance with the License.
-** You may obtain a copy of the License at
-**
-**     http://www.apache.org/licenses/LICENSE-2.0
-**
-** Unless required by applicable law or agreed to in writing, software
-** distributed under the License is distributed on an "AS IS" BASIS,
-** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-** See the License for the specific language governing permissions and
-** limitations under the License.
-*/
--->
-
-<merge
-    xmlns:latin="http://schemas.android.com/apk/res/com.android.inputmethod.latin"
->
-    <include
-        latin:keyboardLayout="@xml/key_styles_common" />
-    <Row
-        latin:keyWidth="7.375%p"
-    >
-        <Key
-            latin:keyStyle="tabKeyStyle"
-            latin:keyLabelFlags="alignLeft"
-            latin:keyWidth="7.969%p" />
-        <include
-            latin:keyboardLayout="@xml/rowkeys_arabic1" />
-        <Key
-            latin:keyStyle="deleteKeyStyle"
-            latin:keyWidth="fillRight" />
-    </Row>
-    <Row
-        latin:keyWidth="7.227%p"
-    >
-        <Key
-            latin:keyStyle="toSymbolKeyStyle"
-            latin:keyLabelFlags="alignLeft"
-            latin:keyWidth="11.172%p" />
-        <include
-            latin:keyboardLayout="@xml/rowkeys_arabic2" />
-        <Key
-            latin:keyStyle="enterKeyStyle"
-            latin:keyWidth="fillRight" />
-    </Row>
-    <Row
-        latin:keyWidth="7.227%p"
-    >
-        <include
-            latin:keyboardLayout="@xml/rowkeys_arabic3"
-            latin:keyXPos="6.602%p" />
-    </Row>
-    <include
-        latin:keyboardLayout="@xml/row_qwerty4" />
-</merge>
diff --git a/java/res/xml-sw768dp/rows_azerty.xml b/java/res/xml-sw768dp/rows_azerty.xml
deleted file mode 100644
index cf4bc92..0000000
--- a/java/res/xml-sw768dp/rows_azerty.xml
+++ /dev/null
@@ -1,68 +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.
-*/
--->
-
-<merge
-    xmlns:latin="http://schemas.android.com/apk/res/com.android.inputmethod.latin"
->
-    <include
-        latin:keyboardLayout="@xml/key_styles_common" />
-    <Row
-        latin:keyWidth="8.282%p"
-    >
-        <Key
-            latin:keyStyle="tabKeyStyle"
-            latin:keyLabelFlags="alignLeft"
-            latin:keyWidth="7.969%p" />
-        <include
-            latin:keyboardLayout="@xml/rowkeys_azerty1" />
-        <Key
-            latin:keyStyle="deleteKeyStyle"
-            latin:keyWidth="fillRight" />
-    </Row>
-    <Row
-        latin:keyWidth="8.125%p"
-    >
-        <Key
-            latin:keyStyle="toSymbolKeyStyle"
-            latin:keyLabelFlags="alignLeft"
-            latin:keyWidth="10.167%p" />
-        <include
-            latin:keyboardLayout="@xml/rowkeys_azerty2" />
-        <Key
-            latin:keyStyle="enterKeyStyle"
-            latin:keyWidth="fillRight" />
-    </Row>
-    <Row
-        latin:keyWidth="8.047%p"
-    >
-        <Key
-            latin:keyStyle="shiftKeyStyle"
-            latin:keyWidth="13.829%p" />
-        <include
-            latin:keyboardLayout="@xml/rowkeys_azerty3" />
-        <include
-            latin:keyboardLayout="@xml/keys_comma_period" />
-        <Key
-            latin:keyStyle="shiftKeyStyle"
-            latin:keyWidth="fillRight" />
-    </Row>
-    <include
-        latin:keyboardLayout="@xml/row_qwerty4" />
-</merge>
diff --git a/java/res/xml-sw768dp/rows_bulgarian.xml b/java/res/xml-sw768dp/rows_bulgarian.xml
deleted file mode 100644
index bdc1262..0000000
--- a/java/res/xml-sw768dp/rows_bulgarian.xml
+++ /dev/null
@@ -1,68 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-/*
-**
-** Copyright 2012, 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.
-*/
--->
-
-<merge
-    xmlns:latin="http://schemas.android.com/apk/res/com.android.inputmethod.latin"
->
-    <include
-        latin:keyboardLayout="@xml/key_styles_common" />
-    <Row
-        latin:keyWidth="7.375%p"
-    >
-        <Key
-            latin:keyStyle="tabKeyStyle"
-            latin:keyLabelFlags="alignLeft"
-            latin:keyWidth="7.969%p" />
-        <include
-            latin:keyboardLayout="@xml/rowkeys_bulgarian1" />
-        <Key
-            latin:keyStyle="deleteKeyStyle"
-            latin:keyWidth="fillRight" />
-    </Row>
-    <Row
-        latin:keyWidth="7.227%p"
-    >
-        <Key
-            latin:keyStyle="toSymbolKeyStyle"
-            latin:keyLabelFlags="alignLeft"
-            latin:keyWidth="11.172%p" />
-        <include
-            latin:keyboardLayout="@xml/rowkeys_bulgarian2" />
-        <Key
-            latin:keyStyle="enterKeyStyle"
-            latin:keyWidth="fillRight" />
-    </Row>
-    <Row
-        latin:keyWidth="7.186%p"
-    >
-        <Key
-            latin:keyStyle="shiftKeyStyle"
-            latin:keyWidth="13.829%p" />
-        <include
-            latin:keyboardLayout="@xml/rowkeys_bulgarian3" />
-        <include
-            latin:keyboardLayout="@xml/keys_comma_period" />
-        <Key
-            latin:keyStyle="shiftKeyStyle"
-            latin:keyWidth="fillRight" />
-    </Row>
-    <include
-        latin:keyboardLayout="@xml/row_qwerty4" />
-</merge>
diff --git a/java/res/xml-sw768dp/rows_bulgarian_bds.xml b/java/res/xml-sw768dp/rows_bulgarian_bds.xml
deleted file mode 100644
index 58c4611..0000000
--- a/java/res/xml-sw768dp/rows_bulgarian_bds.xml
+++ /dev/null
@@ -1,68 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-/*
-**
-** Copyright 2012, 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.
-*/
--->
-
-<merge
-    xmlns:latin="http://schemas.android.com/apk/res/com.android.inputmethod.latin"
->
-    <include
-        latin:keyboardLayout="@xml/key_styles_common" />
-    <Row
-        latin:keyWidth="7.375%p"
-    >
-        <Key
-            latin:keyStyle="tabKeyStyle"
-            latin:keyLabelFlags="alignLeft"
-            latin:keyWidth="7.969%p" />
-        <include
-            latin:keyboardLayout="@xml/rowkeys_bulgarian_bds1" />
-        <Key
-            latin:keyStyle="deleteKeyStyle"
-            latin:keyWidth="fillRight" />
-    </Row>
-    <Row
-        latin:keyWidth="7.227%p"
-    >
-        <Key
-            latin:keyStyle="toSymbolKeyStyle"
-            latin:keyLabelFlags="alignLeft"
-            latin:keyWidth="11.172%p" />
-        <include
-            latin:keyboardLayout="@xml/rowkeys_bulgarian_bds2" />
-        <Key
-            latin:keyStyle="enterKeyStyle"
-            latin:keyWidth="fillRight" />
-    </Row>
-    <Row
-        latin:keyWidth="7.000%p"
-    >
-        <Key
-            latin:keyStyle="shiftKeyStyle"
-            latin:keyWidth="13.829%p" />
-        <include
-            latin:keyboardLayout="@xml/rowkeys_bulgarian_bds3" />
-        <include
-            latin:keyboardLayout="@xml/keys_comma_period" />
-        <Key
-            latin:keyStyle="shiftKeyStyle"
-            latin:keyWidth="fillRight" />
-    </Row>
-    <include
-        latin:keyboardLayout="@xml/row_qwerty4" />
-</merge>
diff --git a/java/res/xml-sw768dp/rows_dvorak.xml b/java/res/xml-sw768dp/rows_dvorak.xml
deleted file mode 100644
index 60d5dd6..0000000
--- a/java/res/xml-sw768dp/rows_dvorak.xml
+++ /dev/null
@@ -1,66 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-/*
-**
-** Copyright 2012, 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.
-*/
--->
-
-<merge
-    xmlns:latin="http://schemas.android.com/apk/res/com.android.inputmethod.latin"
->
-    <include
-        latin:keyboardLayout="@xml/key_styles_common" />
-    <Row
-        latin:keyWidth="8.282%p"
-    >
-        <Key
-            latin:keyStyle="tabKeyStyle"
-            latin:keyLabelFlags="alignLeft"
-            latin:keyWidth="7.969%p" />
-        <include
-            latin:keyboardLayout="@xml/rowkeys_dvorak1" />
-        <Key
-            latin:keyStyle="deleteKeyStyle"
-            latin:keyWidth="fillRight" />
-    </Row>
-    <Row
-        latin:keyWidth="8.125%p"
-    >
-        <Key
-            latin:keyStyle="toSymbolKeyStyle"
-            latin:keyLabelFlags="alignLeft"
-            latin:keyWidth="10.167%p" />
-        <include
-            latin:keyboardLayout="@xml/rowkeys_dvorak2" />
-        <Key
-            latin:keyStyle="enterKeyStyle"
-            latin:keyWidth="fillRight" />
-    </Row>
-    <Row
-        latin:keyWidth="8.047%p"
-    >
-        <Key
-            latin:keyStyle="shiftKeyStyle"
-            latin:keyWidth="13.829%p" />
-        <include
-            latin:keyboardLayout="@xml/rowkeys_dvorak3" />
-        <Key
-            latin:keyStyle="shiftKeyStyle"
-            latin:keyWidth="fillRight" />
-    </Row>
-    <include
-        latin:keyboardLayout="@xml/row_dvorak4" />
-</merge>
diff --git a/java/res/xml-sw768dp/rows_east_slavic.xml b/java/res/xml-sw768dp/rows_east_slavic.xml
deleted file mode 100644
index 420307d..0000000
--- a/java/res/xml-sw768dp/rows_east_slavic.xml
+++ /dev/null
@@ -1,70 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-/*
-**
-** Copyright 2011, The Android Open Source Project
-**
-** Licensed under the Apache License, Version 2.0 (the "License");
-** you may not use this file except in compliance with the License.
-** You may obtain a copy of the License at
-**
-**     http://www.apache.org/licenses/LICENSE-2.0
-**
-** Unless required by applicable law or agreed to in writing, software
-** distributed under the License is distributed on an "AS IS" BASIS,
-** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-** See the License for the specific language governing permissions and
-** limitations under the License.
-*/
--->
-
-<merge
-    xmlns:latin="http://schemas.android.com/apk/res/com.android.inputmethod.latin"
->
-    <include
-        latin:keyboardLayout="@xml/key_styles_common" />
-    <Row
-        latin:keyWidth="7.000%p"
-    >
-        <Key
-            latin:keyStyle="tabKeyStyle"
-            latin:keyLabelFlags="alignLeft"
-            latin:keyWidth="7.969%p" />
-        <include
-            latin:keyboardLayout="@xml/rowkeys_east_slavic1" />
-        <Key
-            latin:keyLabel="!text/keylabel_for_east_slavic_row1_12" />
-        <Key
-            latin:keyStyle="deleteKeyStyle"
-            latin:keyWidth="fillRight" />
-    </Row>
-    <Row
-        latin:keyWidth="7.000%p"
-    >
-        <Key
-            latin:keyStyle="toSymbolKeyStyle"
-            latin:keyLabelFlags="alignLeft"
-            latin:keyWidth="11.172%p" />
-        <include
-            latin:keyboardLayout="@xml/rowkeys_east_slavic2" />
-        <Key
-            latin:keyStyle="enterKeyStyle"
-            latin:keyWidth="fillRight" />
-    </Row>
-    <Row
-        latin:keyWidth="7.000%p"
-    >
-        <Key
-            latin:keyStyle="shiftKeyStyle"
-            latin:keyWidth="13.829%p" />
-        <include
-            latin:keyboardLayout="@xml/rowkeys_east_slavic3" />
-        <include
-            latin:keyboardLayout="@xml/keys_comma_period" />
-        <Key
-            latin:keyStyle="shiftKeyStyle"
-            latin:keyWidth="fillRight" />
-    </Row>
-    <include
-        latin:keyboardLayout="@xml/row_qwerty4" />
-</merge>
diff --git a/java/res/xml-sw768dp/rows_farsi.xml b/java/res/xml-sw768dp/rows_farsi.xml
deleted file mode 100644
index 8d3fb05..0000000
--- a/java/res/xml-sw768dp/rows_farsi.xml
+++ /dev/null
@@ -1,61 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-/*
-**
-** Copyright 2012, 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.
-*/
--->
-
-<merge
-    xmlns:latin="http://schemas.android.com/apk/res/com.android.inputmethod.latin"
->
-    <include
-        latin:keyboardLayout="@xml/key_styles_common" />
-    <Row
-        latin:keyWidth="7.375%p"
-    >
-        <Key
-            latin:keyStyle="tabKeyStyle"
-            latin:keyLabelFlags="alignLeft"
-            latin:keyWidth="7.969%p" />
-        <include
-            latin:keyboardLayout="@xml/rowkeys_farsi1" />
-        <Key
-            latin:keyStyle="deleteKeyStyle"
-            latin:keyWidth="fillRight" />
-    </Row>
-    <Row
-        latin:keyWidth="7.227%p"
-    >
-        <Key
-            latin:keyStyle="toSymbolKeyStyle"
-            latin:keyLabelFlags="alignLeft"
-            latin:keyWidth="11.172%p" />
-        <include
-            latin:keyboardLayout="@xml/rowkeys_farsi2" />
-        <Key
-            latin:keyStyle="enterKeyStyle"
-            latin:keyWidth="fillRight" />
-    </Row>
-    <Row
-        latin:keyWidth="7.227%p"
-    >
-        <include
-            latin:keyboardLayout="@xml/rowkeys_farsi3"
-            latin:keyXPos="13.829%p" />
-    </Row>
-    <include
-        latin:keyboardLayout="@xml/row_qwerty4" />
-</merge>
diff --git a/java/res/xml-sw768dp/rows_georgian.xml b/java/res/xml-sw768dp/rows_georgian.xml
deleted file mode 100644
index 3f8bd45..0000000
--- a/java/res/xml-sw768dp/rows_georgian.xml
+++ /dev/null
@@ -1,68 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-/*
-**
-** Copyright 2012, 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.
-*/
--->
-
-<merge
-    xmlns:latin="http://schemas.android.com/apk/res/com.android.inputmethod.latin"
->
-    <include
-        latin:keyboardLayout="@xml/key_styles_common" />
-    <Row
-        latin:keyWidth="8.282%p"
-    >
-        <Key
-            latin:keyStyle="tabKeyStyle"
-            latin:keyLabelFlags="alignLeft"
-            latin:keyWidth="7.969%p" />
-        <include
-            latin:keyboardLayout="@xml/rowkeys_georgian1" />
-        <Key
-            latin:keyStyle="deleteKeyStyle"
-            latin:keyWidth="fillRight"/>
-    </Row>
-    <Row
-        latin:keyWidth="8.125%p"
-    >
-        <Key
-            latin:keyStyle="toSymbolKeyStyle"
-            latin:keyLabelFlags="alignLeft"
-            latin:keyWidth="11.172%p"/>
-        <include
-            latin:keyboardLayout="@xml/rowkeys_georgian2" />
-        <Key
-            latin:keyStyle="enterKeyStyle"
-            latin:keyWidth="fillRight" />
-    </Row>
-    <Row
-        latin:keyWidth="8.047%p"
-    >
-        <Key
-            latin:keyStyle="shiftKeyStyle"
-            latin:keyWidth="13.829%p"/>
-        <include
-            latin:keyboardLayout="@xml/rowkeys_georgian3" />
-        <include
-            latin:keyboardLayout="@xml/keys_comma_period" />
-        <Key
-            latin:keyStyle="shiftKeyStyle"
-            latin:keyWidth="fillRight" />
-    </Row>
-    <include
-        latin:keyboardLayout="@xml/row_qwerty4" />
-</merge>
diff --git a/java/res/xml-sw768dp/rows_greek.xml b/java/res/xml-sw768dp/rows_greek.xml
deleted file mode 100644
index 9e1e00b..0000000
--- a/java/res/xml-sw768dp/rows_greek.xml
+++ /dev/null
@@ -1,70 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-/*
-**
-** Copyright 2012, 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.
-*/
--->
-
-<merge
-    xmlns:latin="http://schemas.android.com/apk/res/com.android.inputmethod.latin"
->
-    <include
-        latin:keyboardLayout="@xml/key_styles_common" />
-    <Row
-        latin:keyWidth="8.282%p"
-    >
-        <Key
-            latin:keyStyle="tabKeyStyle"
-            latin:keyLabelFlags="alignLeft"
-            latin:keyWidth="7.969%p" />
-        <include
-            latin:keyboardLayout="@xml/key_greek_semicolon" />
-        <include
-            latin:keyboardLayout="@xml/rowkeys_greek1" />
-        <Key
-            latin:keyStyle="deleteKeyStyle"
-            latin:keyWidth="fillRight"/>
-    </Row>
-    <Row
-        latin:keyWidth="8.125%p"
-    >
-        <Key
-            latin:keyStyle="toSymbolKeyStyle"
-            latin:keyLabelFlags="alignLeft"
-            latin:keyWidth="11.172%p"/>
-        <include
-            latin:keyboardLayout="@xml/rowkeys_greek2" />
-        <Key
-            latin:keyStyle="enterKeyStyle"
-            latin:keyWidth="fillRight" />
-    </Row>
-    <Row
-        latin:keyWidth="8.047%p"
-    >
-        <Key
-            latin:keyStyle="shiftKeyStyle"
-            latin:keyWidth="13.829%p" />
-        <include
-            latin:keyboardLayout="@xml/rowkeys_greek3" />
-        <include
-            latin:keyboardLayout="@xml/keys_comma_period" />
-        <Key
-            latin:keyStyle="shiftKeyStyle"
-            latin:keyWidth="fillRight" />
-    </Row>
-   <include
-        latin:keyboardLayout="@xml/row_qwerty4" />
-</merge>
diff --git a/java/res/xml-sw768dp/rows_hebrew.xml b/java/res/xml-sw768dp/rows_hebrew.xml
deleted file mode 100644
index a5f6dfe..0000000
--- a/java/res/xml-sw768dp/rows_hebrew.xml
+++ /dev/null
@@ -1,61 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-/*
-**
-** Copyright 2011, The Android Open Source Project
-**
-** Licensed under the Apache License, Version 2.0 (the "License");
-** you may not use this file except in compliance with the License.
-** You may obtain a copy of the License at
-**
-**     http://www.apache.org/licenses/LICENSE-2.0
-**
-** Unless required by applicable law or agreed to in writing, software
-** distributed under the License is distributed on an "AS IS" BASIS,
-** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-** See the License for the specific language governing permissions and
-** limitations under the License.
-*/
--->
-
-<merge
-    xmlns:latin="http://schemas.android.com/apk/res/com.android.inputmethod.latin"
->
-    <include
-        latin:keyboardLayout="@xml/key_styles_common" />
-    <Row
-        latin:keyWidth="8.282%p"
-    >
-        <Key
-            latin:keyStyle="tabKeyStyle"
-            latin:keyLabelFlags="alignLeft"
-            latin:keyWidth="7.969%p" />
-        <include
-            latin:keyboardLayout="@xml/rowkeys_hebrew1" />
-        <Key
-            latin:keyStyle="deleteKeyStyle"
-            latin:keyWidth="fillRight" />
-    </Row>
-    <Row
-        latin:keyWidth="8.125%p"
-    >
-        <Key
-            latin:keyStyle="toSymbolKeyStyle"
-            latin:keyLabelFlags="alignLeft"
-            latin:keyWidth="10.167%p" />
-        <include
-            latin:keyboardLayout="@xml/rowkeys_hebrew2" />
-        <Key
-            latin:keyStyle="enterKeyStyle"
-            latin:keyWidth="fillRight" />
-        </Row>
-    <Row
-        latin:keyWidth="8.047%p"
-    >
-        <include
-            latin:keyboardLayout="@xml/rowkeys_hebrew3"
-            latin:keyXPos="13.829%p" />
-    </Row>
-    <include
-        latin:keyboardLayout="@xml/row_hebrew4" />
-</merge>
diff --git a/java/res/xml-sw768dp/rows_hindi.xml b/java/res/xml-sw768dp/rows_hindi.xml
deleted file mode 100644
index 6baf09e..0000000
--- a/java/res/xml-sw768dp/rows_hindi.xml
+++ /dev/null
@@ -1,68 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-/*
-**
-** Copyright 2012, 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.
-*/
--->
-
-<merge
-    xmlns:latin="http://schemas.android.com/apk/res/com.android.inputmethod.latin"
->
-    <include
-        latin:keyboardLayout="@xml/key_styles_common" />
-    <Row
-        latin:keyWidth="7.375%p"
-    >
-        <Key
-            latin:keyStyle="tabKeyStyle"
-            latin:keyLabelFlags="alignLeft"
-            latin:keyWidth="7.969%p" />
-        <include
-            latin:keyboardLayout="@xml/rowkeys_hindi1" />
-        <Key
-            latin:keyStyle="deleteKeyStyle"
-            latin:keyWidth="fillRight" />
-    </Row>
-    <Row
-        latin:keyWidth="7.227%p"
-    >
-        <Key
-            latin:keyStyle="toSymbolKeyStyle"
-            latin:keyLabelFlags="alignLeft"
-            latin:keyWidth="11.172%p" />
-        <include
-            latin:keyboardLayout="@xml/rowkeys_hindi2" />
-        <Key
-            latin:keyStyle="enterKeyStyle"
-            latin:keyWidth="fillRight" />
-    </Row>
-    <Row
-        latin:keyWidth="7.000%p"
-    >
-        <Key
-            latin:keyStyle="shiftKeyStyle"
-            latin:keyWidth="13.829%p" />
-        <include
-            latin:keyboardLayout="@xml/rowkeys_hindi3" />
-        <include
-            latin:keyboardLayout="@xml/keys_comma_period" />
-        <Key
-            latin:keyStyle="shiftKeyStyle"
-            latin:keyWidth="fillRight" />
-    </Row>
-    <include
-        latin:keyboardLayout="@xml/row_qwerty4" />
-</merge>
diff --git a/java/res/xml-sw768dp/rows_mongolian.xml b/java/res/xml-sw768dp/rows_mongolian.xml
deleted file mode 100644
index 5f37f87..0000000
--- a/java/res/xml-sw768dp/rows_mongolian.xml
+++ /dev/null
@@ -1,68 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-/*
-**
-** Copyright 2012, 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.
-*/
--->
-
-<merge
-    xmlns:latin="http://schemas.android.com/apk/res/com.android.inputmethod.latin"
->
-    <include
-        latin:keyboardLayout="@xml/key_styles_common" />
-    <Row
-        latin:keyWidth="7.375%p"
-    >
-        <Key
-            latin:keyStyle="tabKeyStyle"
-            latin:keyLabelFlags="alignLeft"
-            latin:keyWidth="7.969%p" />
-        <include
-            latin:keyboardLayout="@xml/rowkeys_mongolian1" />
-        <Key
-            latin:keyStyle="deleteKeyStyle"
-            latin:keyWidth="fillRight" />
-    </Row>
-    <Row
-        latin:keyWidth="7.227%p"
-    >
-        <Key
-            latin:keyStyle="toSymbolKeyStyle"
-            latin:keyLabelFlags="alignLeft"
-            latin:keyWidth="11.172%p" />
-        <include
-            latin:keyboardLayout="@xml/rowkeys_mongolian2" />
-        <Key
-            latin:keyStyle="enterKeyStyle"
-            latin:keyWidth="fillRight" />
-    </Row>
-    <Row
-        latin:keyWidth="7.000%p"
-    >
-        <Key
-            latin:keyStyle="shiftKeyStyle"
-            latin:keyWidth="13.829%p" />
-        <include
-            latin:keyboardLayout="@xml/rowkeys_mongolian3" />
-        <include
-            latin:keyboardLayout="@xml/keys_comma_period" />
-        <Key
-            latin:keyStyle="shiftKeyStyle"
-            latin:keyWidth="fillRight" />
-    </Row>
-    <include
-        latin:keyboardLayout="@xml/row_qwerty4" />
-</merge>
diff --git a/java/res/xml-sw768dp/rows_nordic.xml b/java/res/xml-sw768dp/rows_nordic.xml
deleted file mode 100644
index 13d9399..0000000
--- a/java/res/xml-sw768dp/rows_nordic.xml
+++ /dev/null
@@ -1,71 +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.
-*/
--->
-
-<merge
-    xmlns:latin="http://schemas.android.com/apk/res/com.android.inputmethod.latin"
->
-    <include
-        latin:keyboardLayout="@xml/key_styles_common" />
-    <Row
-        latin:keyWidth="7.375%p"
-    >
-        <Key
-            latin:keyStyle="tabKeyStyle"
-            latin:keyLabelFlags="alignLeft"
-            latin:keyWidth="7.969%p" />
-        <include
-            latin:keyboardLayout="@xml/rowkeys_nordic1" />
-        <Key
-            latin:keyStyle="deleteKeyStyle"
-            latin:keyWidth="fillRight" />
-    </Row>
-    <Row
-        latin:keyWidth="7.227%p"
-    >
-        <Key
-            latin:keyStyle="toSymbolKeyStyle"
-            latin:keyLabelFlags="alignLeft"
-            latin:keyWidth="11.172%p" />
-        <include
-            latin:keyboardLayout="@xml/rowkeys_nordic2" />
-        <Key
-            latin:keyStyle="enterKeyStyle"
-            latin:keyWidth="fillRight" />
-    </Row>
-    <Row
-        latin:keyWidth="7.227%p"
-    >
-        <Key
-            latin:keyStyle="shiftKeyStyle"
-            latin:keyWidth="13.829%p" />
-        <Spacer
-            latin:keyWidth="3.689%p" />
-        <include
-            latin:keyboardLayout="@xml/rowkeys_qwerty3" />
-        <include
-            latin:keyboardLayout="@xml/keys_comma_period" />
-        <Key
-            latin:keyStyle="shiftKeyStyle"
-            latin:keyXPos="-13.750%p"
-            latin:keyWidth="fillRight" />
-    </Row>
-    <include
-        latin:keyboardLayout="@xml/row_qwerty4" />
-</merge>
diff --git a/java/res/xml-sw768dp/rows_number_normal.xml b/java/res/xml-sw768dp/rows_number_normal.xml
deleted file mode 100644
index de49aba..0000000
--- a/java/res/xml-sw768dp/rows_number_normal.xml
+++ /dev/null
@@ -1,175 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-/*
-**
-** Copyright 2012, 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.
-*/
--->
-
-<merge
-    xmlns:latin="http://schemas.android.com/apk/res/com.android.inputmethod.latin"
->
-    <Row>
-        <Key
-            latin:keyStyle="tabKeyStyle"
-            latin:keyLabelFlags="alignLeft"
-            latin:keyWidth="11.172%p" />
-        <Key
-            latin:keyLabel="-"
-            latin:keyStyle="numKeyStyle"
-            latin:keyXPos="13.829%p"
-            latin:keyWidth="8.047%p"
-            latin:backgroundType="functional" />
-        <Key
-            latin:keyLabel="+"
-            latin:keyStyle="numKeyStyle"
-            latin:keyWidth="8.047%p"
-            latin:backgroundType="functional" />
-        <Key
-            latin:keyLabel="."
-            latin:keyStyle="numKeyStyle"
-            latin:keyWidth="8.047%p"
-            latin:backgroundType="functional" />
-        <Key
-            latin:keyLabel="1"
-            latin:keyStyle="numKeyStyle"
-            latin:keyXPos="43.125%p" />
-        <Key
-            latin:keyLabel="2"
-            latin:keyStyle="numKeyStyle" />
-        <Key
-            latin:keyLabel="3"
-            latin:keyStyle="numKeyStyle" />
-        <Key
-            latin:keyStyle="deleteKeyStyle"
-            latin:keyXPos="-11.172%p"
-            latin:keyWidth="fillRight" />
-    </Row>
-    <Row>
-        <!-- Note: This Spacer prevents the below key from being marked as a left edge key. -->
-        <Spacer
-            latin:keyWidth="13.829%p" />
-        <Key
-            latin:keyStyle="numStarKeyStyle"
-            latin:keyWidth="8.047%p"
-            latin:backgroundType="functional" />
-        <Key
-            latin:keyLabel="/"
-            latin:keyStyle="numKeyStyle"
-            latin:keyWidth="8.047%p"
-            latin:backgroundType="functional" />
-        <switch>
-            <case
-                latin:mode="time|datetime"
-            >
-                <Key
-                    latin:keyLabel=","
-                    latin:keyLabelFlags="hasPopupHint"
-                    latin:moreKeys="!text/more_keys_for_am_pm"
-                    latin:keyStyle="numKeyStyle"
-                    latin:keyWidth="8.047%p"
-                    latin:backgroundType="functional" />
-            </case>
-            <default>
-                <Key
-                    latin:keyLabel=","
-                    latin:keyStyle="numKeyStyle"
-                    latin:keyWidth="8.047%p"
-                    latin:backgroundType="functional" />
-            </default>
-        </switch>
-        <Key
-            latin:keyLabel="4"
-            latin:keyStyle="numKeyStyle"
-            latin:keyXPos="43.125%p" />
-        <Key
-            latin:keyLabel="5"
-            latin:keyStyle="numKeyStyle" />
-        <Key
-            latin:keyLabel="6"
-            latin:keyStyle="numKeyStyle" />
-        <Key
-            latin:keyStyle="enterKeyStyle"
-            latin:keyXPos="-11.172%p"
-            latin:keyWidth="fillRight" />
-    </Row>
-    <Row>
-        <!-- Note: This Spacer prevents the below key from being marked as a left edge key. -->
-        <Spacer
-            latin:keyWidth="13.829%p" />
-        <Key
-            latin:keyLabel="("
-            latin:keyStyle="numKeyStyle"
-            latin:keyWidth="8.047%p"
-            latin:backgroundType="functional" />
-        <Key
-            latin:keyLabel=")"
-            latin:keyStyle="numKeyStyle"
-            latin:keyWidth="8.047%p"
-            latin:backgroundType="functional" />
-        <switch>
-            <case
-                latin:mode="time|datetime"
-            >
-                <Key
-                    latin:keyLabel=":"
-                    latin:keyStyle="numKeyStyle"
-                    latin:keyWidth="8.047%p"
-                    latin:backgroundType="functional" />
-            </case>
-            <default>
-                <Key
-                    latin:keyLabel="="
-                    latin:keyStyle="numKeyStyle"
-                    latin:keyWidth="8.047%p"
-                    latin:backgroundType="functional" />
-            </default>
-        </switch>
-        <Key
-            latin:keyLabel="7"
-            latin:keyStyle="numKeyStyle"
-            latin:keyXPos="43.125%p" />
-        <Key
-            latin:keyLabel="8"
-            latin:keyStyle="numKeyStyle" />
-        <Key
-            latin:keyLabel="9"
-            latin:keyStyle="numKeyStyle" />
-        <!-- Note: This Spacer prevents the above key from being marked as a right edge key. -->
-        <Spacer />
-    </Row>
-    <Row>
-        <include
-            latin:keyboardLayout="@xml/key_settings"
-            latin:keyWidth="8.047%p" />
-        <Key
-            latin:keyStyle="spaceKeyStyle"
-            latin:keyXPos="13.829%p"
-            latin:keyWidth="24.140%p"
-            latin:backgroundType="functional" />
-        <Key
-            latin:keyStyle="numStarKeyStyle"
-            latin:keyXPos="43.125%p" />
-        <Key
-            latin:keyLabel="0"
-            latin:keyStyle="numKeyStyle" />
-        <Key
-            latin:keyLabel="\#"
-            latin:keyStyle="numKeyStyle" />
-        <!-- Note: This Spacer prevents the above key from being marked as a right edge key. -->
-        <Spacer
-            latin:keyWidth="fillRight" />
-    </Row>
-</merge>
diff --git a/java/res/xml-sw768dp/rows_number_password.xml b/java/res/xml-sw768dp/rows_number_password.xml
deleted file mode 100644
index cfa2214..0000000
--- a/java/res/xml-sw768dp/rows_number_password.xml
+++ /dev/null
@@ -1,79 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-/*
-**
-** Copyright 2012, 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.
-*/
--->
-
-<merge
-    xmlns:latin="http://schemas.android.com/apk/res/com.android.inputmethod.latin"
->
-    <Row>
-        <Key
-            latin:keyStyle="numTabKeyStyle"
-            latin:keyLabelFlags="alignLeft"
-            latin:keyWidth="11.172%p" />
-        <Key
-            latin:keyStyle="num1KeyStyle"
-            latin:keyXPos="32.076%p" />
-        <Key
-            latin:keyStyle="num2KeyStyle" />
-        <Key
-            latin:keyStyle="num3KeyStyle" />
-        <Key
-            latin:keyStyle="deleteKeyStyle"
-            latin:keyXPos="-11.172%p"
-            latin:keyWidth="fillRight" />
-    </Row>
-    <Row>
-        <!-- Note: This Spacer prevents the below key from being marked as a left edge key. -->
-        <Spacer
-            latin:keyWidth="32.076%p" />
-        <Key
-            latin:keyStyle="num4KeyStyle" />
-        <Key
-            latin:keyStyle="num5KeyStyle" />
-        <Key
-            latin:keyStyle="num6KeyStyle" />
-        <Key
-            latin:keyStyle="enterKeyStyle"
-            latin:keyXPos="-11.172%p"
-            latin:keyWidth="fillRight" />
-    </Row>
-    <Row>
-        <!-- Note: This Spacer prevents the below key from being marked as a left edge key. -->
-        <Spacer
-            latin:keyWidth="32.076%p" />
-        <Key
-            latin:keyStyle="num7KeyStyle" />
-        <Key
-            latin:keyStyle="num8KeyStyle" />
-        <Key
-            latin:keyStyle="num9KeyStyle" />
-        <!-- Note: This Spacer prevents the above key from being marked as a right edge key. -->
-        <Spacer />
-    </Row>
-    <Row>
-        <include
-            latin:keyboardLayout="@xml/key_settings"
-            latin:keyWidth="8.047%p" />
-        <Key
-            latin:keyXPos="45.326%p"
-            latin:keyStyle="num0KeyStyle" />
-        <!-- Note: This Spacer prevents the above key from being marked as a right edge key. -->
-        <Spacer />
-    </Row>
-</merge>
diff --git a/java/res/xml-sw768dp/rows_pcqwerty.xml b/java/res/xml-sw768dp/rows_pcqwerty.xml
deleted file mode 100644
index a844728..0000000
--- a/java/res/xml-sw768dp/rows_pcqwerty.xml
+++ /dev/null
@@ -1,71 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-/*
-**
-** Copyright 2012, 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.
-*/
--->
-
-<merge
-    xmlns:latin="http://schemas.android.com/apk/res/com.android.inputmethod.latin"
->
-    <include
-        latin:keyboardLayout="@xml/key_styles_common" />
-    <Row
-        latin:keyWidth="7.0%p"
-    >
-        <include
-            latin:keyboardLayout="@xml/rowkeys_pcqwerty1" />
-        <Key
-            latin:keyStyle="deleteKeyStyle"
-            latin:keyWidth="fillRight" />
-    </Row>
-    <Row
-        latin:keyWidth="7.0%p"
-    >
-        <Key
-            latin:keyStyle="tabKeyStyle"
-            latin:keyWidth="9.0%p" />
-        <include
-            latin:keyboardLayout="@xml/rowkeys_pcqwerty2" />
-    </Row>
-    <Row
-        latin:keyWidth="7.0%p"
-    >
-        <Key
-            latin:keyStyle="toSymbolKeyStyle"
-            latin:keyLabel="!text/label_to_symbol_key_pcqwerty"
-            latin:keyWidth="12.0%p" />
-        <include
-            latin:keyboardLayout="@xml/rowkeys_pcqwerty3" />
-        <Key
-            latin:keyStyle="enterKeyStyle"
-            latin:keyWidth="fillRight" />
-    </Row>
-    <Row
-        latin:keyWidth="7.0%p"
-    >
-        <Key
-            latin:keyStyle="shiftKeyStyle"
-            latin:keyWidth="15.0%p" />
-        <include
-            latin:keyboardLayout="@xml/rowkeys_pcqwerty4" />
-        <Key
-            latin:keyStyle="shiftKeyStyle"
-            latin:keyWidth="fillRight" />
-    </Row>
-    <include
-        latin:keyboardLayout="@xml/row_pcqwerty5" />
-</merge>
diff --git a/java/res/xml-sw768dp/rows_pcqwerty_symbols.xml b/java/res/xml-sw768dp/rows_pcqwerty_symbols.xml
deleted file mode 100644
index 956da97..0000000
--- a/java/res/xml-sw768dp/rows_pcqwerty_symbols.xml
+++ /dev/null
@@ -1,67 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-/*
-**
-** Copyright 2011, The Android Open Source Project
-**
-** Licensed under the Apache License, Version 2.0 (the "License");
-** you may not use this file except in compliance with the License.
-** You may obtain a copy of the License at
-**
-**     http://www.apache.org/licenses/LICENSE-2.0
-**
-** Unless required by applicable law or agreed to in writing, software
-** distributed under the License is distributed on an "AS IS" BASIS,
-** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-** See the License for the specific language governing permissions and
-** limitations under the License.
-*/
--->
-
-<merge
-    xmlns:latin="http://schemas.android.com/apk/res/com.android.inputmethod.latin"
->
-    <include
-        latin:keyboardLayout="@xml/key_styles_common" />
-    <include
-        latin:keyboardLayout="@xml/key_styles_currency" />
-    <Row
-        latin:keyWidth="7.0%p"
-    >
-        <include
-            latin:keyboardLayout="@xml/rowkeys_pcqwerty1" />
-        <Key
-            latin:keyStyle="deleteKeyStyle"
-            latin:keyWidth="fillRight" />
-    </Row>
-    <Row
-        latin:keyWidth="7.0%p"
-    >
-        <Key
-            latin:keyStyle="tabKeyStyle"
-            latin:keyWidth="9.0%p" />
-        <include
-            latin:keyboardLayout="@xml/rowkeys_pcqwerty2" />
-    </Row>
-    <Row
-        latin:keyWidth="7.0%p"
-    >
-        <Key
-            latin:keyStyle="toAlphaKeyStyle"
-            latin:keyWidth="12.0%p" />
-        <include
-            latin:keyboardLayout="@xml/rowkeys_pcqwerty3" />
-        <Key
-            latin:keyStyle="enterKeyStyle"
-            latin:keyWidth="fillRight" />
-    </Row>
-    <Row
-        latin:keyWidth="7.0%p"
-    >
-        <include
-            latin:keyboardLayout="@xml/rowkeys_pcqwerty4"
-            latin:keyXPos="15.0%p" />
-    </Row>
-    <include
-        latin:keyboardLayout="@xml/row_pcqwerty5" />
-</merge>
diff --git a/java/res/xml-sw768dp/rows_phone.xml b/java/res/xml-sw768dp/rows_phone.xml
deleted file mode 100644
index d06a63b..0000000
--- a/java/res/xml-sw768dp/rows_phone.xml
+++ /dev/null
@@ -1,138 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-/*
-**
-** Copyright 2011, The Android Open Source Project
-**
-** Licensed under the Apache License, Version 2.0 (the "License");
-** you may not use this file except in compliance with the License.
-** You may obtain a copy of the License at
-**
-**     http://www.apache.org/licenses/LICENSE-2.0
-**
-** Unless required by applicable law or agreed to in writing, software
-** distributed under the License is distributed on an "AS IS" BASIS,
-** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-** See the License for the specific language governing permissions and
-** limitations under the License.
-*/
--->
-
-<merge
-    xmlns:latin="http://schemas.android.com/apk/res/com.android.inputmethod.latin"
->
-    <include
-        latin:keyboardLayout="@xml/key_styles_common" />
-    <include
-        latin:keyboardLayout="@xml/key_styles_number" />
-    <Row>
-        <Key
-            latin:keyStyle="numTabKeyStyle"
-            latin:keyLabelFlags="alignLeft"
-            latin:keyWidth="11.172%p" />
-        <Key
-            latin:keyLabel="-"
-            latin:keyStyle="numKeyStyle"
-            latin:keyXPos="13.829%p"
-            latin:keyWidth="8.047%p"
-            latin:backgroundType="functional" />
-        <Key
-            latin:keyLabel="+"
-            latin:keyStyle="numKeyStyle"
-            latin:keyWidth="8.047%p"
-            latin:backgroundType="functional" />
-        <Key
-            latin:keyStyle="numPauseKeyStyle"
-            latin:keyWidth="8.047%p"
-            latin:backgroundType="functional" />
-        <Key
-            latin:keyStyle="num1KeyStyle"
-            latin:keyXPos="43.125%p" />
-        <Key
-            latin:keyStyle="num2KeyStyle" />
-        <Key
-            latin:keyStyle="num3KeyStyle" />
-        <Key
-            latin:keyStyle="deleteKeyStyle"
-            latin:keyXPos="-11.172%p"
-            latin:keyWidth="fillRight" />
-    </Row>
-    <Row>
-        <!-- Note: This Spacer prevents the below key from being marked as a left edge key. -->
-        <Spacer
-            latin:keyWidth="13.829%p" />
-        <Key
-            latin:keyLabel=","
-            latin:keyStyle="numKeyStyle"
-            latin:keyXPos="13.829%p"
-            latin:keyWidth="8.047%p"
-            latin:backgroundType="functional" />
-        <Key
-            latin:keyLabel="."
-            latin:keyStyle="numKeyStyle"
-            latin:keyWidth="8.047%p"
-            latin:backgroundType="functional" />
-        <Key
-            latin:keyStyle="numWaitKeyStyle"
-            latin:keyWidth="8.047%p"
-            latin:backgroundType="functional" />
-        <Key
-            latin:keyStyle="num4KeyStyle"
-            latin:keyXPos="43.125%p" />
-        <Key
-            latin:keyStyle="num5KeyStyle" />
-        <Key
-            latin:keyStyle="num6KeyStyle" />
-        <Key
-            latin:keyStyle="enterKeyStyle"
-            latin:keyXPos="-11.172%p"
-            latin:keyWidth="fillRight" />
-    </Row>
-    <Row>
-        <!-- Note: This Spacer prevents the below key from being marked as a left edge key. -->
-        <Spacer
-            latin:keyWidth="13.829%p" />
-        <Key
-            latin:keyLabel="("
-            latin:keyStyle="numKeyStyle"
-            latin:keyWidth="8.047%p"
-            latin:backgroundType="functional" />
-        <Key
-            latin:keyLabel=")"
-            latin:keyStyle="numKeyStyle"
-            latin:keyWidth="8.047%p"
-            latin:backgroundType="functional" />
-        <Key
-            latin:keyLabel="N"
-            latin:keyStyle="numKeyStyle"
-            latin:keyWidth="8.047%p"
-            latin:backgroundType="functional" />
-        <Key
-            latin:keyStyle="num7KeyStyle"
-            latin:keyXPos="43.125%p" />
-        <Key
-            latin:keyStyle="num8KeyStyle" />
-        <Key
-            latin:keyStyle="num9KeyStyle" />
-        <!-- Note: This Spacer prevents the above key from being marked as a right edge key. -->
-        <Spacer />
-    </Row>
-    <Row>
-        <include
-            latin:keyboardLayout="@xml/key_settings"
-            latin:keyWidth="8.047%p" />
-        <Key
-            latin:keyStyle="spaceKeyStyle"
-            latin:keyXPos="13.829%p"
-            latin:keyWidth="24.140%p"
-            latin:backgroundType="functional" />
-        <Key
-            latin:keyStyle="numStarKeyStyle"
-            latin:keyXPos="43.125%p" />
-        <Key
-            latin:keyStyle="num0KeyStyle" />
-        <Key
-            latin:keyLabel="\#"
-            latin:keyStyle="numKeyStyle" />
-   </Row>
-</merge>
diff --git a/java/res/xml-sw768dp/rows_qwerty.xml b/java/res/xml-sw768dp/rows_qwerty.xml
deleted file mode 100644
index 8af18ed..0000000
--- a/java/res/xml-sw768dp/rows_qwerty.xml
+++ /dev/null
@@ -1,68 +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.
-*/
--->
-
-<merge
-    xmlns:latin="http://schemas.android.com/apk/res/com.android.inputmethod.latin"
->
-    <include
-        latin:keyboardLayout="@xml/key_styles_common" />
-    <Row
-        latin:keyWidth="8.282%p"
-    >
-        <Key
-            latin:keyStyle="tabKeyStyle"
-            latin:keyLabelFlags="alignLeft"
-            latin:keyWidth="7.969%p" />
-        <include
-            latin:keyboardLayout="@xml/rowkeys_qwerty1" />
-        <Key
-            latin:keyStyle="deleteKeyStyle"
-            latin:keyWidth="fillRight"/>
-    </Row>
-    <Row
-        latin:keyWidth="8.125%p"
-    >
-        <Key
-            latin:keyStyle="toSymbolKeyStyle"
-            latin:keyLabelFlags="alignLeft"
-            latin:keyWidth="11.172%p"/>
-        <include
-            latin:keyboardLayout="@xml/rowkeys_qwerty2" />
-        <Key
-            latin:keyStyle="enterKeyStyle"
-            latin:keyWidth="fillRight" />
-    </Row>
-    <Row
-        latin:keyWidth="8.047%p"
-    >
-        <Key
-            latin:keyStyle="shiftKeyStyle"
-            latin:keyWidth="13.829%p"/>
-        <include
-            latin:keyboardLayout="@xml/rowkeys_qwerty3" />
-        <include
-            latin:keyboardLayout="@xml/keys_comma_period" />
-        <Key
-            latin:keyStyle="shiftKeyStyle"
-            latin:keyWidth="fillRight" />
-    </Row>
-    <include
-        latin:keyboardLayout="@xml/row_qwerty4" />
-</merge>
diff --git a/java/res/xml-sw768dp/rows_qwertz.xml b/java/res/xml-sw768dp/rows_qwertz.xml
deleted file mode 100644
index 0dd206d..0000000
--- a/java/res/xml-sw768dp/rows_qwertz.xml
+++ /dev/null
@@ -1,68 +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.
-*/
--->
-
-<merge
-    xmlns:latin="http://schemas.android.com/apk/res/com.android.inputmethod.latin"
->
-    <include
-        latin:keyboardLayout="@xml/key_styles_common" />
-    <Row
-        latin:keyWidth="8.282%p"
-    >
-        <Key
-            latin:keyStyle="tabKeyStyle"
-            latin:keyLabelFlags="alignLeft"
-            latin:keyWidth="7.969%p" />
-        <include
-            latin:keyboardLayout="@xml/rowkeys_qwertz1" />
-        <Key
-            latin:keyStyle="deleteKeyStyle"
-            latin:keyWidth="fillRight"/>
-    </Row>
-    <Row
-        latin:keyWidth="8.125%p"
-    >
-        <Key
-            latin:keyStyle="toSymbolKeyStyle"
-            latin:keyLabelFlags="alignLeft"
-            latin:keyWidth="11.172%p"/>
-        <include
-            latin:keyboardLayout="@xml/rowkeys_qwerty2" />
-        <Key
-            latin:keyStyle="enterKeyStyle"
-            latin:keyWidth="fillRight" />
-    </Row>
-    <Row
-        latin:keyWidth="8.047%p"
-    >
-        <Key
-            latin:keyStyle="shiftKeyStyle"
-            latin:keyWidth="13.829%p"/>
-        <include
-            latin:keyboardLayout="@xml/rowkeys_qwertz3" />
-        <include
-            latin:keyboardLayout="@xml/keys_comma_period" />
-        <Key
-            latin:keyStyle="shiftKeyStyle"
-            latin:keyWidth="fillRight" />
-    </Row>
-    <include
-        latin:keyboardLayout="@xml/row_qwerty4" />
-</merge>
diff --git a/java/res/xml-sw768dp/rows_south_slavic.xml b/java/res/xml-sw768dp/rows_south_slavic.xml
deleted file mode 100644
index 6b44c4e..0000000
--- a/java/res/xml-sw768dp/rows_south_slavic.xml
+++ /dev/null
@@ -1,68 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-/*
-**
-** Copyright 2011, The Android Open Source Project
-**
-** Licensed under the Apache License, Version 2.0 (the "License");
-** you may not use this file except in compliance with the License.
-** You may obtain a copy of the License at
-**
-**     http://www.apache.org/licenses/LICENSE-2.0
-**
-** Unless required by applicable law or agreed to in writing, software
-** distributed under the License is distributed on an "AS IS" BASIS,
-** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-** See the License for the specific language governing permissions and
-** limitations under the License.
-*/
--->
-
-<merge
-    xmlns:latin="http://schemas.android.com/apk/res/com.android.inputmethod.latin"
->
-    <include
-        latin:keyboardLayout="@xml/key_styles_common" />
-    <Row
-        latin:keyWidth="7.375%p"
-    >
-        <Key
-            latin:keyStyle="tabKeyStyle"
-            latin:keyLabelFlags="alignLeft"
-            latin:keyWidth="7.969%p" />
-        <include
-            latin:keyboardLayout="@xml/rowkeys_south_slavic1" />
-        <Key
-            latin:keyStyle="deleteKeyStyle"
-            latin:keyWidth="fillRight" />
-    </Row>
-    <Row
-        latin:keyWidth="7.227%p"
-    >
-        <Key
-            latin:keyStyle="toSymbolKeyStyle"
-            latin:keyLabelFlags="alignLeft"
-            latin:keyWidth="11.172%p" />
-        <include
-            latin:keyboardLayout="@xml/rowkeys_south_slavic2" />
-        <Key
-            latin:keyStyle="enterKeyStyle"
-            latin:keyWidth="fillRight" />
-    </Row>
-    <Row
-        latin:keyWidth="7.000%p"
-    >
-        <Key
-            latin:keyStyle="shiftKeyStyle"
-            latin:keyWidth="13.829%p" />
-        <include
-            latin:keyboardLayout="@xml/rowkeys_south_slavic3" />
-        <include
-            latin:keyboardLayout="@xml/keys_comma_period" />
-        <Key
-            latin:keyStyle="shiftKeyStyle"
-            latin:keyWidth="fillRight" />
-    </Row>
-    <include
-        latin:keyboardLayout="@xml/row_qwerty4" />
-</merge>
diff --git a/java/res/xml-sw768dp/rows_spanish.xml b/java/res/xml-sw768dp/rows_spanish.xml
deleted file mode 100644
index 4520c10..0000000
--- a/java/res/xml-sw768dp/rows_spanish.xml
+++ /dev/null
@@ -1,69 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-/*
-**
-** Copyright 2011, The Android Open Source Project
-**
-** Licensed under the Apache License, Version 2.0 (the "License");
-** you may not use this file except in compliance with the License.
-** You may obtain a copy of the License at
-**
-**     http://www.apache.org/licenses/LICENSE-2.0
-**
-** Unless required by applicable law or agreed to in writing, software
-** distributed under the License is distributed on an "AS IS" BASIS,
-** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-** See the License for the specific language governing permissions and
-** limitations under the License.
-*/
--->
-
-<merge
-    xmlns:latin="http://schemas.android.com/apk/res/com.android.inputmethod.latin"
->
-    <include
-        latin:keyboardLayout="@xml/key_styles_common" />
-    <Row
-        latin:keyWidth="8.282%p"
-    >
-        <Key
-            latin:keyStyle="tabKeyStyle"
-            latin:keyLabelFlags="alignLeft"
-            latin:keyWidth="7.969%p" />
-        <include
-            latin:keyboardLayout="@xml/rowkeys_qwerty1" />
-        <Key
-            latin:keyStyle="deleteKeyStyle"
-            latin:keyWidth="fillRight"/>
-    </Row>
-    <Row
-        latin:keyWidth="8.125%p"
-    >
-        <Key
-            latin:keyStyle="toSymbolKeyStyle"
-            latin:keyLabelFlags="alignLeft"
-            latin:keyWidth="10.167%p" />
-        <include
-            latin:keyboardLayout="@xml/rowkeys_spanish2" />
-        <Key
-            latin:keyStyle="enterKeyStyle"
-            latin:keyWidth="fillRight" />
-    </Row>
-    <Row
-        latin:keyWidth="8.047%p"
-    >
-        <Key
-            latin:keyStyle="shiftKeyStyle"
-            latin:keyWidth="13.829%p"/>
-        <include
-            latin:keyboardLayout="@xml/rowkeys_qwerty3" />
-        <include
-            latin:keyboardLayout="@xml/keys_comma_period" />
-        <Key
-            latin:keyStyle="shiftKeyStyle"
-            latin:keyXPos="-13.750%p"
-            latin:keyWidth="fillRight" />
-    </Row>
-    <include
-        latin:keyboardLayout="@xml/row_qwerty4" />
-</merge>
diff --git a/java/res/xml-sw768dp/rows_symbols.xml b/java/res/xml-sw768dp/rows_symbols.xml
deleted file mode 100644
index efd7735..0000000
--- a/java/res/xml-sw768dp/rows_symbols.xml
+++ /dev/null
@@ -1,68 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-/*
-**
-** Copyright 2011, The Android Open Source Project
-**
-** Licensed under the Apache License, Version 2.0 (the "License");
-** you may not use this file except in compliance with the License.
-** You may obtain a copy of the License at
-**
-**     http://www.apache.org/licenses/LICENSE-2.0
-**
-** Unless required by applicable law or agreed to in writing, software
-** distributed under the License is distributed on an "AS IS" BASIS,
-** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-** See the License for the specific language governing permissions and
-** limitations under the License.
-*/
--->
-
-<merge
-    xmlns:latin="http://schemas.android.com/apk/res/com.android.inputmethod.latin"
->
-    <include
-        latin:keyboardLayout="@xml/key_styles_common" />
-    <include
-        latin:keyboardLayout="@xml/key_styles_currency" />
-    <Row
-        latin:keyWidth="8.282%p"
-    >
-        <Key
-            latin:keyStyle="tabKeyStyle"
-            latin:keyLabelFlags="alignLeft"
-            latin:keyWidth="7.969%p" />
-        <include
-            latin:keyboardLayout="@xml/rowkeys_symbols1" />
-        <Key
-            latin:keyStyle="deleteKeyStyle"
-            latin:keyWidth="fillRight" />
-    </Row>
-    <Row
-        latin:keyWidth="8.125%p"
-    >
-        <Key
-            latin:keyStyle="toAlphaKeyStyle"
-            latin:keyLabelFlags="alignLeft"
-            latin:keyWidth="11.172%p" />
-        <include
-            latin:keyboardLayout="@xml/rowkeys_symbols2" />
-        <Key
-            latin:keyStyle="enterKeyStyle"
-            latin:keyWidth="fillRight" />
-    </Row>
-    <Row
-        latin:keyWidth="8.047%p"
-    >
-        <Key
-            latin:keyStyle="toMoreSymbolKeyStyle"
-            latin:keyWidth="13.829%p" />
-        <include
-            latin:keyboardLayout="@xml/rowkeys_symbols3" />
-        <Key
-            latin:keyStyle="toMoreSymbolKeyStyle"
-            latin:keyWidth="fillRight" />
-    </Row>
-    <include
-        latin:keyboardLayout="@xml/row_symbols4" />
-</merge>
diff --git a/java/res/xml-sw768dp/rows_symbols_shift.xml b/java/res/xml-sw768dp/rows_symbols_shift.xml
deleted file mode 100644
index fd1b93d..0000000
--- a/java/res/xml-sw768dp/rows_symbols_shift.xml
+++ /dev/null
@@ -1,68 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-/*
-**
-** Copyright 2011, The Android Open Source Project
-**
-** Licensed under the Apache License, Version 2.0 (the "License");
-** you may not use this file except in compliance with the License.
-** You may obtain a copy of the License at
-**
-**     http://www.apache.org/licenses/LICENSE-2.0
-**
-** Unless required by applicable law or agreed to in writing, software
-** distributed under the License is distributed on an "AS IS" BASIS,
-** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-** See the License for the specific language governing permissions and
-** limitations under the License.
-*/
--->
-
-<merge
-    xmlns:latin="http://schemas.android.com/apk/res/com.android.inputmethod.latin"
->
-    <include
-        latin:keyboardLayout="@xml/key_styles_common" />
-    <include
-        latin:keyboardLayout="@xml/key_styles_currency" />
-    <Row
-        latin:keyWidth="8.282%p"
-    >
-        <Key
-            latin:keyStyle="tabKeyStyle"
-            latin:keyLabelFlags="alignLeft"
-            latin:keyWidth="7.969%p" />
-        <include
-            latin:keyboardLayout="@xml/rowkeys_symbols_shift1" />
-        <Key
-            latin:keyStyle="deleteKeyStyle"
-            latin:keyWidth="fillRight" />
-    </Row>
-    <Row
-        latin:keyWidth="8.125%p"
-    >
-        <Key
-            latin:keyStyle="toAlphaKeyStyle"
-            latin:keyLabelFlags="alignLeft"
-            latin:keyWidth="11.172%p" />
-        <include
-            latin:keyboardLayout="@xml/rowkeys_symbols_shift2" />
-        <Key
-            latin:keyStyle="enterKeyStyle"
-            latin:keyWidth="fillRight" />
-    </Row>
-    <Row
-        latin:keyWidth="8.047%p"
-    >
-        <Key
-            latin:keyStyle="backFromMoreSymbolKeyStyle"
-            latin:keyWidth="13.829%p" />
-        <include
-            latin:keyboardLayout="@xml/rowkeys_symbols_shift3" />
-        <Key
-            latin:keyStyle="backFromMoreSymbolKeyStyle"
-            latin:keyWidth="fillRight" />
-    </Row>
-    <include
-        latin:keyboardLayout="@xml/row_symbols_shift4" />
-</merge>
diff --git a/java/res/xml-sw768dp/rows_thai.xml b/java/res/xml-sw768dp/rows_thai.xml
deleted file mode 100644
index 5f9b383..0000000
--- a/java/res/xml-sw768dp/rows_thai.xml
+++ /dev/null
@@ -1,74 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-/*
-**
-** Copyright 2012, 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.
-*/
--->
-
-<merge
-    xmlns:latin="http://schemas.android.com/apk/res/com.android.inputmethod.latin"
->
-    <include
-        latin:keyboardLayout="@xml/key_styles_common" />
-    <Row
-        latin:keyWidth="7.079%p"
-    >
-        <include
-            latin:keyboardLayout="@xml/rowkeys_thai1"
-            latin:keyXPos="3.799%p" />
-        <Key
-            latin:keyStyle="deleteKeyStyle"
-            latin:keyWidth="fillRight"/>
-    </Row>
-    <Row
-        latin:keyWidth="7.079%p"
-    >
-        <Key
-            latin:keyStyle="tabKeyStyle"
-            latin:keyLabelFlags="alignLeft"
-            latin:keyWidth="7.969%p" />
-        <include
-            latin:keyboardLayout="@xml/rowkeys_thai2" />
-        <include
-            latin:keyboardLayout="@xml/key_thai_kho_khuat" />
-    </Row>
-    <Row
-        latin:keyWidth="7.079%p"
-    >
-        <Key
-            latin:keyStyle="toSymbolKeyStyle"
-            latin:keyLabelFlags="alignLeft"
-            latin:keyWidth="11.172%p"/>
-        <include
-            latin:keyboardLayout="@xml/rowkeys_thai3" />
-        <Key
-            latin:keyStyle="enterKeyStyle"
-            latin:keyWidth="fillRight" />
-    </Row>
-    <Row
-        latin:keyWidth="7.181%p"
-    >
-        <Key
-            latin:keyStyle="shiftKeyStyle"
-            latin:keyWidth="13.829%p"/>
-        <include
-            latin:keyboardLayout="@xml/rowkeys_thai4" />
-        <include
-            latin:keyboardLayout="@xml/keys_comma_period" />
-    </Row>
-    <include
-        latin:keyboardLayout="@xml/row_qwerty4" />
-</merge>
diff --git a/java/res/xml-sw768dp/rows_thai_symbols.xml b/java/res/xml-sw768dp/rows_thai_symbols.xml
deleted file mode 100644
index 5285141..0000000
--- a/java/res/xml-sw768dp/rows_thai_symbols.xml
+++ /dev/null
@@ -1,76 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-/*
-**
-** Copyright 2012, 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.
-*/
--->
-
-<merge
-    xmlns:latin="http://schemas.android.com/apk/res/com.android.inputmethod.latin"
->
-    <include
-        latin:keyboardLayout="@xml/key_styles_common" />
-    <include
-        latin:keyboardLayout="@xml/key_styles_currency" />
-    <Row
-        latin:keyWidth="8.282%p"
-    >
-        <include
-            latin:keyboardLayout="@xml/rowkeys_thai_digits"
-            latin:keyXPos="7.969%p" />
-        <Key
-            latin:keyStyle="deleteKeyStyle"
-            latin:keyWidth="fillRight" />
-    </Row>
-    <Row
-        latin:keyWidth="8.282%p"
-    >
-        <Key
-            latin:keyStyle="tabKeyStyle"
-            latin:keyLabelFlags="alignLeft"
-            latin:keyWidth="7.969%p" />
-        <include
-            latin:keyboardLayout="@xml/rowkeys_symbols1" />
-    </Row>
-    <Row
-        latin:keyWidth="8.125%p"
-    >
-        <Key
-            latin:keyStyle="toAlphaKeyStyle"
-            latin:keyLabelFlags="alignLeft"
-            latin:keyWidth="11.172%p" />
-        <include
-            latin:keyboardLayout="@xml/rowkeys_symbols2" />
-        <Key
-            latin:keyStyle="enterKeyStyle"
-            latin:keyWidth="fillRight" />
-    </Row>
-    <Row
-        latin:keyWidth="8.047%p"
-    >
-        <Key
-            latin:keyStyle="toMoreSymbolKeyStyle"
-            latin:keyWidth="13.829%p" />
-        <include
-            latin:keyboardLayout="@xml/rowkeys_symbols3" />
-        <Key
-            latin:keyStyle="toMoreSymbolKeyStyle"
-            latin:keyXPos="-13.750%p"
-            latin:keyWidth="fillRight" />
-    </Row>
-    <include
-        latin:keyboardLayout="@xml/row_symbols4" />
-</merge>
diff --git a/java/res/xml-sw768dp/rows_thai_symbols_shift.xml b/java/res/xml-sw768dp/rows_thai_symbols_shift.xml
deleted file mode 100644
index 9d2694b..0000000
--- a/java/res/xml-sw768dp/rows_thai_symbols_shift.xml
+++ /dev/null
@@ -1,76 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-/*
-**
-** Copyright 2012, 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.
-*/
--->
-
-<merge
-    xmlns:latin="http://schemas.android.com/apk/res/com.android.inputmethod.latin"
->
-    <include
-        latin:keyboardLayout="@xml/key_styles_common" />
-    <include
-        latin:keyboardLayout="@xml/key_styles_currency" />
-    <Row
-        latin:keyWidth="8.282%p"
-    >
-        <include
-            latin:keyboardLayout="@xml/rowkeys_thai_digits"
-            latin:keyXPos="7.969%p" />
-        <Key
-            latin:keyStyle="deleteKeyStyle"
-            latin:keyWidth="fillRight" />
-    </Row>
-    <Row
-        latin:keyWidth="8.282%p"
-    >
-        <Key
-            latin:keyStyle="tabKeyStyle"
-            latin:keyLabelFlags="alignLeft"
-            latin:keyWidth="7.969%p" />
-        <include
-            latin:keyboardLayout="@xml/rowkeys_symbols_shift1" />
-    </Row>
-    <Row
-        latin:keyWidth="8.125%p"
-    >
-        <Key
-            latin:keyStyle="toAlphaKeyStyle"
-            latin:keyLabelFlags="alignLeft"
-            latin:keyWidth="11.172%p" />
-        <include
-            latin:keyboardLayout="@xml/rowkeys_symbols_shift2" />
-        <Key
-            latin:keyStyle="enterKeyStyle"
-            latin:keyWidth="fillRight" />
-    </Row>
-    <Row
-        latin:keyWidth="8.047%p"
-    >
-        <Key
-            latin:keyStyle="backFromMoreSymbolKeyStyle"
-            latin:keyWidth="13.829%p" />
-        <include
-            latin:keyboardLayout="@xml/rowkeys_symbols_shift3" />
-        <Key
-            latin:keyStyle="backFromMoreSymbolKeyStyle"
-            latin:keyXPos="-13.750%p"
-            latin:keyWidth="fillRight" />
-    </Row>
-    <include
-        latin:keyboardLayout="@xml/row_symbols_shift4" />
-</merge>
diff --git a/java/res/xml-v16/key_hindi1_shift.xml b/java/res/xml-v16/key_devanagari_sign_anusvara.xml
similarity index 78%
copy from java/res/xml-v16/key_hindi1_shift.xml
copy to java/res/xml-v16/key_devanagari_sign_anusvara.xml
index 19b9643..27c7bff 100644
--- a/java/res/xml-v16/key_hindi1_shift.xml
+++ b/java/res/xml-v16/key_devanagari_sign_anusvara.xml
@@ -20,13 +20,13 @@
 
 <!-- The code point U+25CC for key label is needed because the font rendering system prior to
      API version 16 can't automatically render dotted circle for incomplete combining letter
-     of Hindi. The files named res/xml/{key,keys}_hindi*.xml have this U+25CC hack, although the
-     counterpart files named res/xml-v16/{key,keys}_hindi*.xml don't have this hack. -->
+     of some scripts. The files named res/xml/key_*.xml have this U+25CC hack, although the
+     counterpart files named res/xml-v16/key_*.xml don't have this hack. -->
 <merge
     xmlns:latin="http://schemas.android.com/apk/res/com.android.inputmethod.latin"
 >
-    <!-- U+0903: "ः" DEVANAGARI SIGN VISARGA -->
+    <!-- U+0902: "ं" DEVANAGARI SIGN ANUSVARA -->
     <Key
-        latin:keyLabel="&#x0903;"
+        latin:keyLabel="&#x0902;"
         latin:keyLabelFlags="fontNormal" />
 </merge>
diff --git a/java/res/xml-v16/key_devanagari_sign_candrabindu.xml b/java/res/xml-v16/key_devanagari_sign_candrabindu.xml
new file mode 100644
index 0000000..03017dd
--- /dev/null
+++ b/java/res/xml-v16/key_devanagari_sign_candrabindu.xml
@@ -0,0 +1,48 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+**
+** Copyright 2013, 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.
+*/
+-->
+
+<!-- The code point U+25CC for key label is needed because the font rendering system prior to
+     API version 16 can't automatically render dotted circle for incomplete combining letter
+     of some scripts. The files named res/xml/key_*.xml have this U+25CC hack, although the
+     counterpart files named res/xml-v16/key_*.xml don't have this hack. -->
+<merge
+    xmlns:latin="http://schemas.android.com/apk/res/com.android.inputmethod.latin"
+>
+    <switch>
+        <case
+            latin:keyboardLayoutSet="hindi"
+        >
+            <!-- U+0901: "ँ" DEVANAGARI SIGN CANDRABINDU
+                 U+0945: "ॅ" DEVANAGARI VOWEL SIGN CANDRA E -->
+            <key-style
+                latin:styleName="moreKeysDevanagariSignCandrabindu"
+                latin:moreKeys="&#x0945;" />
+        </case>
+        <default>
+             <key-style
+                latin:styleName="moreKeysDevanagariSignCandrabindu" />
+        </default>
+    </switch>
+    <!-- U+0901: "ँ" DEVANAGARI SIGN CANDRABINDU -->
+    <Key
+        latin:keyStyle="moreKeysDevanagariSignCandrabindu"
+        latin:keyLabel="&#x0901;"
+        latin:keyLabelFlags="fontNormal" />
+</merge>
diff --git a/java/res/xml-v16/key_devanagari_sign_nukta.xml b/java/res/xml-v16/key_devanagari_sign_nukta.xml
new file mode 100644
index 0000000..09c3477
--- /dev/null
+++ b/java/res/xml-v16/key_devanagari_sign_nukta.xml
@@ -0,0 +1,49 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+**
+** Copyright 2013, 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.
+*/
+-->
+
+<!-- The code point U+25CC for key label is needed because the font rendering system prior to
+     API version 16 can't automatically render dotted circle for incomplete combining letter
+     of some scripts. The files named res/xml/key_*.xml have this U+25CC hack, although the
+     counterpart files named res/xml-v16/key_*.xml don't have this hack. -->
+<merge
+    xmlns:latin="http://schemas.android.com/apk/res/com.android.inputmethod.latin"
+>
+    <switch>
+        <case
+            latin:keyboardLayoutSet="hindi"
+        >
+            <!-- U+097D: "ॽ" DEVANAGARI LETTER GLOTTAL STOP
+                 U+0970: "॰" DEVANAGARI ABBREVIATION SIGN
+                 U+093D: "ऽ" DEVANAGARI SIGN AVAGRAHA -->
+             <key-style
+                latin:styleName="moreKeysDevanagariSignNukta"
+                latin:moreKeys="&#x097D;,&#x0970;,&#x093D;" />
+        </case>
+        <default>
+             <key-style
+                latin:styleName="moreKeysDevanagariSignNukta" />
+        </default>
+    </switch>
+    <!-- U+093C: "़" DEVANAGARI SIGN NUKTA -->
+    <Key
+        latin:keyStyle="moreKeysDevanagariSignNukta"
+        latin:keyLabel="&#x093C;"
+        latin:keyLabelFlags="fontNormal" />
+</merge>
diff --git a/java/res/xml-v16/key_hindi1_shift.xml b/java/res/xml-v16/key_devanagari_vowel_sign_candra_o.xml
similarity index 78%
copy from java/res/xml-v16/key_hindi1_shift.xml
copy to java/res/xml-v16/key_devanagari_vowel_sign_candra_o.xml
index 19b9643..0316a7b 100644
--- a/java/res/xml-v16/key_hindi1_shift.xml
+++ b/java/res/xml-v16/key_devanagari_vowel_sign_candra_o.xml
@@ -20,13 +20,13 @@
 
 <!-- The code point U+25CC for key label is needed because the font rendering system prior to
      API version 16 can't automatically render dotted circle for incomplete combining letter
-     of Hindi. The files named res/xml/{key,keys}_hindi*.xml have this U+25CC hack, although the
-     counterpart files named res/xml-v16/{key,keys}_hindi*.xml don't have this hack. -->
+     of some scripts. The files named res/xml/key_*.xml have this U+25CC hack, although the
+     counterpart files named res/xml-v16/key_*.xml don't have this hack. -->
 <merge
     xmlns:latin="http://schemas.android.com/apk/res/com.android.inputmethod.latin"
 >
-    <!-- U+0903: "ः" DEVANAGARI SIGN VISARGA -->
+    <!-- U+0949: "ॉ" DEVANAGARI VOWEL SIGN CANDRA O -->
     <Key
-        latin:keyLabel="&#x0903;"
+        latin:keyLabel="&#x0949;"
         latin:keyLabelFlags="fontNormal" />
 </merge>
diff --git a/java/res/xml-v16/key_devanagari_vowel_sign_vocalic_r.xml b/java/res/xml-v16/key_devanagari_vowel_sign_vocalic_r.xml
new file mode 100644
index 0000000..4dd3e85
--- /dev/null
+++ b/java/res/xml-v16/key_devanagari_vowel_sign_vocalic_r.xml
@@ -0,0 +1,55 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+**
+** Copyright 2013, 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.
+*/
+-->
+
+<!-- The code point U+25CC for key label is needed because the font rendering system prior to
+     API version 16 can't automatically render dotted circle for incomplete combining letter
+     of some scripts. The files named res/xml/key_*.xml have this U+25CC hack, although the
+     counterpart files named res/xml-v16/key_*.xml don't have this hack. -->
+<merge
+    xmlns:latin="http://schemas.android.com/apk/res/com.android.inputmethod.latin"
+>
+    <switch>
+        <case
+            latin:keyboardLayoutSet="hindi"
+        >
+            <!-- U+0944: "ॄ" DEVANAGARI VOWEL SIGN VOCALIC RR -->
+            <key-style
+                latin:styleName="moreKeysDevanagariVowelSignVocalicR"
+                latin:moreKeys="&#x0944;" />
+        </case>
+        <case
+            latin:keyboardLayoutSet="nepali_traditional"
+        >
+            <!-- U+0913: "ओ" DEVANAGARI LETTER O -->
+            <key-style
+                latin:styleName="moreKeysDevanagariVowelSignVocalicR"
+                latin:moreKeys="&#x0913;" />
+        </case>
+        <default>
+             <key-style
+                latin:styleName="moreKeysDevanagariVowelSignVocalicR" />
+        </default>
+    </switch>
+    <!-- U+0943: "ृ" DEVANAGARI VOWEL SIGN VOCALIC R -->
+    <Key
+        latin:keyStyle="moreKeysDevanagariVowelSignVocalicR"
+        latin:keyLabel="&#x0943;"
+        latin:keyLabelFlags="fontNormal" />
+</merge>
diff --git a/java/res/xml-v16/key_hindi3_right.xml b/java/res/xml-v16/key_hindi3_right.xml
deleted file mode 100644
index 232810f..0000000
--- a/java/res/xml-v16/key_hindi3_right.xml
+++ /dev/null
@@ -1,36 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-/*
-**
-** Copyright 2013, 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.
-*/
--->
-
-<!-- The code point U+25CC for key label is needed because the font rendering system prior to
-     API version 16 can't automatically render dotted circle for incomplete combining letter
-     of Hindi. The files named res/xml/{key,keys}_hindi*.xml have this U+25CC hack, although the
-     counterpart files named res/xml-v16/{key,keys}_hindi*.xml don't have this hack. -->
-<merge
-    xmlns:latin="http://schemas.android.com/apk/res/com.android.inputmethod.latin"
->
-    <!-- U+093C: "़" DEVANAGARI SIGN NUKTA
-         U+097D: "ॽ" DEVANAGARI LETTER GLOTTAL STOP
-         U+0970: "॰" DEVANAGARI ABBREVIATION SIGN
-         U+093D: "ऽ" DEVANAGARI SIGN AVAGRAHA -->
-    <Key
-        latin:keyLabel="&#x093C;"
-        latin:moreKeys="&#x097D;,&#x0970;,&#x093D;"
-        latin:keyLabelFlags="fontNormal" />
-</merge>
diff --git a/java/res/xml-v16/key_hindi3_shift_left.xml b/java/res/xml-v16/key_hindi3_shift_left.xml
deleted file mode 100644
index 1eb1768..0000000
--- a/java/res/xml-v16/key_hindi3_shift_left.xml
+++ /dev/null
@@ -1,34 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-/*
-**
-** Copyright 2013, 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.
-*/
--->
-
-<!-- The code point U+25CC for key label is needed because the font rendering system prior to
-     API version 16 can't automatically render dotted circle for incomplete combining letter
-     of Hindi. The files named res/xml/{key,keys}_hindi*.xml have this U+25CC hack, although the
-     counterpart files named res/xml-v16/{key,keys}_hindi*.xml don't have this hack. -->
-<merge
-    xmlns:latin="http://schemas.android.com/apk/res/com.android.inputmethod.latin"
->
-    <!-- U+0901: "ँ" DEVANAGARI SIGN CANDRABINDU
-         U+0945: "ॅ" DEVANAGARI VOWEL SIGN CANDRA E -->
-    <Key
-        latin:keyLabel="&#x0901;"
-        latin:moreKeys="&#x0945;"
-        latin:keyLabelFlags="fontNormal" />
-</merge>
diff --git a/java/res/xml-v16/key_hindi3_shift_right.xml b/java/res/xml-v16/key_hindi3_shift_right.xml
deleted file mode 100644
index 0f26cb5..0000000
--- a/java/res/xml-v16/key_hindi3_shift_right.xml
+++ /dev/null
@@ -1,34 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-/*
-**
-** Copyright 2013, 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.
-*/
--->
-
-<!-- The code point U+25CC for key label is needed because the font rendering system prior to
-     API version 16 can't automatically render dotted circle for incomplete combining letter
-     of Hindi. The files named res/xml/{key,keys}_hindi*.xml have this U+25CC hack, although the
-     counterpart files named res/xml-v16/{key,keys}_hindi*.xml don't have this hack. -->
-<merge
-    xmlns:latin="http://schemas.android.com/apk/res/com.android.inputmethod.latin"
->
-    <!-- U+0943: "ृ" DEVANAGARI VOWEL SIGN VOCALIC R
-         U+0944: "ॄ" DEVANAGARI VOWEL SIGN VOCALIC RR -->
-    <Key
-        latin:keyLabel="&#x0943;"
-        latin:moreKeys="&#x0944;"
-        latin:keyLabelFlags="fontNormal" />
-</merge>
diff --git a/java/res/xml-v16/keys_hindi1_left5.xml b/java/res/xml-v16/keys_hindi1_left5.xml
deleted file mode 100644
index e3ad299..0000000
--- a/java/res/xml-v16/keys_hindi1_left5.xml
+++ /dev/null
@@ -1,75 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-/*
-**
-** Copyright 2013, 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.
-*/
--->
-
-<!-- The code point U+25CC for key label is needed because the font rendering system prior to
-     API version 16 can't automatically render dotted circle for incomplete combining letter
-     of Hindi. The files named res/xml/{key,keys}_hindi*.xml have this U+25CC hack, although the
-     counterpart files named res/xml-v16/{key,keys}_hindi*.xml don't have this hack. -->
-<merge
-    xmlns:latin="http://schemas.android.com/apk/res/com.android.inputmethod.latin"
->
-    <!-- U+094C: "ौ" DEVANAGARI VOWEL SIGN AU
-         U+094C/U+0902: "ौं" DEVANAGARI VOWEL SIGN AU/DEVANAGARI SIGN ANUSVARA
-         U+0967: "१" DEVANAGARI DIGIT ONE -->
-    <Key
-        latin:keyLabel="&#x094C;"
-        latin:moreKeys="&#x094C;&#x0902;,%"
-        latin:keyHintLabel="1"
-        latin:additionalMoreKeys="&#x0967;,1"
-        latin:keyLabelFlags="fontNormal" />
-    <!-- U+0948: "ै" DEVANAGARI VOWEL SIGN AI
-         U+0948/U+0902: "ैं" DEVANAGARI VOWEL SIGN AI/DEVANAGARI SIGN ANUSVARA
-         U+0968: "२" DEVANAGARI DIGIT TWO -->
-    <Key
-        latin:keyLabel="&#x0948;"
-        latin:moreKeys="&#x0948;&#x0902;,%"
-        latin:keyHintLabel="2"
-        latin:additionalMoreKeys="&#x0968;,2"
-        latin:keyLabelFlags="fontNormal" />
-    <!-- U+093E: "ा" DEVANAGARI VOWEL SIGN AA
-         U+093E/U+0902: "ां" DEVANAGARI VOWEL SIGN AA/DEVANAGARI SIGN ANUSVARA
-         U+093E/U+0901: "ाँ" DEVANAGARI VOWEL SIGN AA/DEVANAGARI SIGN CANDRABINDU
-         U+0969: "३" DEVANAGARI DIGIT THREE -->
-    <Key
-        latin:keyLabel="&#x093E;"
-        latin:moreKeys="&#x093E;&#x0902;,&#x093E;&#x0901;,%"
-        latin:keyHintLabel="3"
-        latin:additionalMoreKeys="&#x0969;,3"
-        latin:keyLabelFlags="fontNormal" />
-    <!-- U+0940: "ी" DEVANAGARI VOWEL SIGN II
-         U+0940/U+0902: "ीं" DEVANAGARI VOWEL SIGN II/DEVANAGARI SIGN ANUSVARA
-         U+096A: "४" DEVANAGARI DIGIT FOUR -->
-    <Key
-        latin:keyLabel="&#x0940;"
-        latin:moreKeys="&#x0940;&#x0902;,%"
-        latin:keyHintLabel="4"
-        latin:additionalMoreKeys="&#x096A;,4"
-        latin:keyLabelFlags="fontNormal" />
-    <!-- U+0942: "ू" DEVANAGARI VOWEL SIGN UU
-         U+0942/U+0902: "ूं" DEVANAGARI VOWEL SIGN UU/DEVANAGARI SIGN ANUSVARA
-         U+0942/U+0901: "ूँ" DEVANAGARI VOWEL SIGN UU/DEVANAGARI SIGN CANDRABINDU
-         U+096B: "५" DEVANAGARI DIGIT FIVE -->
-    <Key
-        latin:keyLabel="&#x0942;"
-        latin:moreKeys="&#x0942;&#x0902;,&#x0942;&#x0901;,%"
-        latin:keyHintLabel="5"
-        latin:additionalMoreKeys="&#x096B;,5"
-        latin:keyLabelFlags="fontNormal" />
-</merge>
diff --git a/java/res/xml-v16/keys_hindi2_left5.xml b/java/res/xml-v16/keys_hindi2_left5.xml
deleted file mode 100644
index 05c4f57..0000000
--- a/java/res/xml-v16/keys_hindi2_left5.xml
+++ /dev/null
@@ -1,59 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-/*
-**
-** Copyright 2013, 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.
-*/
--->
-
-<!-- The code point U+25CC for key label is needed because the font rendering system prior to
-     API version 16 can't automatically render dotted circle for incomplete combining letter
-     of Hindi. The files named res/xml/{key,keys}_hindi*.xml have this U+25CC hack, although the
-     counterpart files named res/xml-v16/{key,keys}_hindi*.xml don't have this hack. -->
-<merge
-    xmlns:latin="http://schemas.android.com/apk/res/com.android.inputmethod.latin"
->
-    <!-- U+094B: "ो" DEVANAGARI VOWEL SIGN O
-         U+094B/U+0902: "қं" DEVANAGARI VOWEL SIGN O/DEVANAGARI SIGN ANUSVARA
-         U+0949: "ॉ" DEVANAGARI VOWEL SIGN CANDRA O
-         U+094A: "ॊ" DEVANAGARI VOWEL SIGN SHORT O -->
-    <Key
-        latin:keyLabel="&#x094B;"
-        latin:moreKeys="&#x094B;&#x0902;,&#x0949;,&#x094A;"
-        latin:keyLabelFlags="fontNormal" />
-    <!-- U+0947: "े" DEVANAGARI VOWEL SIGN E
-         U+0947/U+0902: "ें" DEVANAGARI VOWEL SIGN E/DEVANAGARI SIGN ANUSVARA -->
-    <Key
-        latin:keyLabel="&#x0947;"
-        latin:moreKeys="&#x0947;&#x0902;"
-        latin:keyLabelFlags="fontNormal" />
-    <!-- U+094D: "्" DEVANAGARI SIGN VIRAMA -->
-    <Key
-        latin:keyLabel="&#x094D;"
-        latin:keyLabelFlags="fontNormal" />
-    <!-- U+093F: "ि" DEVANAGARI VOWEL SIGN I
-         U+093F/U+0902: "िं" DEVANAGARI VOWEL SIGN I/DEVANAGARI SIGN ANUSVARA -->
-    <Key
-        latin:keyLabel="&#x093F;"
-        latin:moreKeys="&#x093F;&#x0902;"
-        latin:keyLabelFlags="fontNormal" />
-    <!-- U+0941: "ु" DEVANAGARI VOWEL SIGN U
-         U+0941/U+0902: "ुं" DEVANAGARI VOWEL SIGN U/DEVANAGARI SIGN ANUSVARA
-         U+0941/U+0901: "ुँ" DEVANAGARI VOWEL SIGN U/DEVANAGARI SIGN CANDRABINDU -->
-    <Key
-        latin:keyLabel="&#x0941;"
-        latin:moreKeys="&#x0941;&#x0902;,&#x0941;&#x0901;"
-        latin:keyLabelFlags="fontNormal" />
-</merge>
diff --git a/java/res/xml-v16/keys_hindi3_left2.xml b/java/res/xml-v16/keys_hindi3_left2.xml
deleted file mode 100644
index 9474c17..0000000
--- a/java/res/xml-v16/keys_hindi3_left2.xml
+++ /dev/null
@@ -1,36 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-/*
-**
-** Copyright 2013, 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.
-*/
--->
-
-<!-- The code point U+25CC for key label is needed because the font rendering system prior to
-     API version 16 can't automatically render dotted circle for incomplete combining letter
-     of Hindi. The files named res/xml/{key,keys}_hindi*.xml have this U+25CC hack, although the
-     counterpart files named res/xml-v16/{key,keys}_hindi*.xml don't have this hack. -->
-<merge
-    xmlns:latin="http://schemas.android.com/apk/res/com.android.inputmethod.latin"
->
-    <!-- U+0949: "ॉ" DEVANAGARI VOWEL SIGN CANDRA O -->
-    <Key
-        latin:keyLabel="&#x0949;"
-        latin:keyLabelFlags="fontNormal" />
-    <!-- U+0902: "ं" DEVANAGARI SIGN ANUSVARA -->
-    <Key
-        latin:keyLabel="&#x0902;"
-        latin:keyLabelFlags="fontNormal" />
-</merge>
diff --git a/java/res/xml-v16/key_hindi1_shift.xml b/java/res/xml-v16/keystyle_devanagari_sign_virama.xml
similarity index 75%
copy from java/res/xml-v16/key_hindi1_shift.xml
copy to java/res/xml-v16/keystyle_devanagari_sign_virama.xml
index 19b9643..a2fbf53 100644
--- a/java/res/xml-v16/key_hindi1_shift.xml
+++ b/java/res/xml-v16/keystyle_devanagari_sign_virama.xml
@@ -20,13 +20,14 @@
 
 <!-- The code point U+25CC for key label is needed because the font rendering system prior to
      API version 16 can't automatically render dotted circle for incomplete combining letter
-     of Hindi. The files named res/xml/{key,keys}_hindi*.xml have this U+25CC hack, although the
-     counterpart files named res/xml-v16/{key,keys}_hindi*.xml don't have this hack. -->
+     of some scripts. The files named res/xml/key_*.xml have this U+25CC hack, although the
+     counterpart files named res/xml-v16/key_*.xml don't have this hack. -->
 <merge
     xmlns:latin="http://schemas.android.com/apk/res/com.android.inputmethod.latin"
 >
-    <!-- U+0903: "ः" DEVANAGARI SIGN VISARGA -->
-    <Key
-        latin:keyLabel="&#x0903;"
+    <!-- U+094D: "्" DEVANAGARI SIGN VIRAMA -->
+    <key-style
+        latin:styleName="baseKeyDevanagariSignVirama"
+        latin:keyLabel="&#x094D;"
         latin:keyLabelFlags="fontNormal" />
 </merge>
diff --git a/java/res/xml-v16/key_hindi1_shift.xml b/java/res/xml-v16/keystyle_devanagari_sign_visarga.xml
similarity index 81%
rename from java/res/xml-v16/key_hindi1_shift.xml
rename to java/res/xml-v16/keystyle_devanagari_sign_visarga.xml
index 19b9643..ac56cb7 100644
--- a/java/res/xml-v16/key_hindi1_shift.xml
+++ b/java/res/xml-v16/keystyle_devanagari_sign_visarga.xml
@@ -20,13 +20,14 @@
 
 <!-- The code point U+25CC for key label is needed because the font rendering system prior to
      API version 16 can't automatically render dotted circle for incomplete combining letter
-     of Hindi. The files named res/xml/{key,keys}_hindi*.xml have this U+25CC hack, although the
-     counterpart files named res/xml-v16/{key,keys}_hindi*.xml don't have this hack. -->
+     of some scripts. The files named res/xml/key_*.xml have this U+25CC hack, although the
+     counterpart files named res/xml-v16/key_*.xml don't have this hack. -->
 <merge
     xmlns:latin="http://schemas.android.com/apk/res/com.android.inputmethod.latin"
 >
     <!-- U+0903: "ः" DEVANAGARI SIGN VISARGA -->
-    <Key
+    <key-style
+        latin:styleName="baseKeyDevanagariSignVisarga"
         latin:keyLabel="&#x0903;"
         latin:keyLabelFlags="fontNormal" />
 </merge>
diff --git a/java/res/xml-v16/keystyle_devanagari_vowel_sign_aa.xml b/java/res/xml-v16/keystyle_devanagari_vowel_sign_aa.xml
new file mode 100644
index 0000000..8e25603
--- /dev/null
+++ b/java/res/xml-v16/keystyle_devanagari_vowel_sign_aa.xml
@@ -0,0 +1,49 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+**
+** Copyright 2013, 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.
+*/
+-->
+
+<!-- The code point U+25CC for key label is needed because the font rendering system prior to
+     API version 16 can't automatically render dotted circle for incomplete combining letter
+     of some scripts. The files named res/xml/key_*.xml have this U+25CC hack, although the
+     counterpart files named res/xml-v16/key_*.xml don't have this hack. -->
+<merge
+    xmlns:latin="http://schemas.android.com/apk/res/com.android.inputmethod.latin"
+>
+    <switch>
+        <case
+            latin:keyboardLayoutSet="hindi"
+        >
+            <!-- U+093E/U+0902: "ां" DEVANAGARI VOWEL SIGN AA/DEVANAGARI SIGN ANUSVARA
+                 U+093E/U+0901: "ाँ" DEVANAGARI VOWEL SIGN AA/DEVANAGARI SIGN CANDRABINDU -->
+            <key-style
+                latin:styleName="moreKeysDevanagariVowelSignAa"
+                latin:moreKeys="&#x093E;&#x0902;,&#x093E;&#x0901;,%" />
+        </case>
+        <default>
+            <key-style
+                latin:styleName="moreKeysDevanagariVowelSignAa" />
+        </default>
+    </switch>
+    <!-- U+093E: "ा" DEVANAGARI VOWEL SIGN AA -->
+    <key-style
+        latin:styleName="baseKeyDevanagariVowelSignAa"
+        latin:parentStyle="moreKeysDevanagariVowelSignAa"
+        latin:keyLabel="&#x093E;"
+        latin:keyLabelFlags="fontNormal" />
+</merge>
diff --git a/java/res/xml-v16/keystyle_devanagari_vowel_sign_ai.xml b/java/res/xml-v16/keystyle_devanagari_vowel_sign_ai.xml
new file mode 100644
index 0000000..e790339
--- /dev/null
+++ b/java/res/xml-v16/keystyle_devanagari_vowel_sign_ai.xml
@@ -0,0 +1,56 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+**
+** Copyright 2013, 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.
+*/
+-->
+
+<!-- The code point U+25CC for key label is needed because the font rendering system prior to
+     API version 16 can't automatically render dotted circle for incomplete combining letter
+     of some scripts. The files named res/xml/key_*.xml have this U+25CC hack, although the
+     counterpart files named res/xml-v16/key_*.xml don't have this hack. -->
+<merge
+    xmlns:latin="http://schemas.android.com/apk/res/com.android.inputmethod.latin"
+>
+    <switch>
+        <case
+            latin:keyboardLayoutSet="hindi"
+        >
+            <!-- U+0948/U+0902: "ैं" DEVANAGARI VOWEL SIGN AI/DEVANAGARI SIGN ANUSVARA -->
+            <key-style
+                latin:styleName="moreKeysDevanagariVowelSignAi"
+                latin:moreKeys="&#x0948;&#x0902;,%" />
+        </case>
+        <case
+            latin:keyboardLayoutSet="nepali_traditional"
+        >
+            <!-- U+0936/U+094D/U+0930: "श्र" DEVANAGARI LETTER SHA/DEVANAGARI SIGN VIRAMA/DEVANAGARI LETTER RA -->
+            <key-style
+                latin:styleName="moreKeysDevanagariVowelSignAi"
+                latin:moreKeys="&#x0936;&#x094D;&#x0930;" />
+        </case>
+        <default>
+            <key-style
+                latin:styleName="moreKeysDevanagariVowelSignAi" />
+        </default>
+    </switch>
+    <!-- U+0948: "ै" DEVANAGARI VOWEL SIGN AI -->
+    <key-style
+        latin:styleName="baseKeyDevanagariVowelSignAi"
+        latin:parentStyle="moreKeysDevanagariVowelSignAi"
+        latin:keyLabel="&#x0948;"
+        latin:keyLabelFlags="fontNormal" />
+</merge>
diff --git a/java/res/xml-v16/keystyle_devanagari_vowel_sign_au.xml b/java/res/xml-v16/keystyle_devanagari_vowel_sign_au.xml
new file mode 100644
index 0000000..43387a3
--- /dev/null
+++ b/java/res/xml-v16/keystyle_devanagari_vowel_sign_au.xml
@@ -0,0 +1,48 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+**
+** Copyright 2013, 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.
+*/
+-->
+
+<!-- The code point U+25CC for key label is needed because the font rendering system prior to
+     API version 16 can't automatically render dotted circle for incomplete combining letter
+     of some scripts. The files named res/xml/key_*.xml have this U+25CC hack, although the
+     counterpart files named res/xml-v16/key_*.xml don't have this hack. -->
+<merge
+    xmlns:latin="http://schemas.android.com/apk/res/com.android.inputmethod.latin"
+>
+    <switch>
+        <case
+            latin:keyboardLayoutSet="hindi"
+        >
+            <!--U+094C/U+0902: "ौं" DEVANAGARI VOWEL SIGN AU/DEVANAGARI SIGN ANUSVARA -->
+            <key-style
+                latin:styleName="moreKeysDevanagariVowelSignAu"
+                latin:moreKeys="&#x094C;&#x0902;,%" />
+        </case>
+        <default>
+             <key-style
+                latin:styleName="moreKeysDevanagariVowelSignAu" />
+        </default>
+    </switch>
+    <!-- U+094C: "ौ" DEVANAGARI VOWEL SIGN AU -->
+    <key-style
+        latin:styleName="baseKeyDevanagariVowelSignAu"
+        latin:parentStyle="moreKeysDevanagariVowelSignAu"
+        latin:keyLabel="&#x094C;"
+        latin:keyLabelFlags="fontNormal" />
+</merge>
diff --git a/java/res/xml-v16/keystyle_devanagari_vowel_sign_e.xml b/java/res/xml-v16/keystyle_devanagari_vowel_sign_e.xml
new file mode 100644
index 0000000..c70d9d9
--- /dev/null
+++ b/java/res/xml-v16/keystyle_devanagari_vowel_sign_e.xml
@@ -0,0 +1,57 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+**
+** Copyright 2013, 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.
+*/
+-->
+
+<!-- The code point U+25CC for key label is needed because the font rendering system prior to
+     API version 16 can't automatically render dotted circle for incomplete combining letter
+     of some scripts. The files named res/xml/key_*.xml have this U+25CC hack, although the
+     counterpart files named res/xml-v16/key_*.xml don't have this hack. -->
+<merge
+    xmlns:latin="http://schemas.android.com/apk/res/com.android.inputmethod.latin"
+>
+    <switch>
+        <case
+            latin:keyboardLayoutSet="hindi"
+        >
+            <!-- U+0947/U+0902: "ें" DEVANAGARI VOWEL SIGN E/DEVANAGARI SIGN ANUSVARA -->
+            <key-style
+                latin:styleName="moreKeysDevanagariVowelSignE"
+                latin:moreKeys="&#x0947;&#x0902;" />
+        </case>
+        <case
+            latin:keyboardLayoutSet="nepali_traditional"
+        >
+            <!-- U+0903: "ः‍" DEVANAGARI SIGN VISARGA
+                 U+093D: "ऽ" DEVANAGARI SIGN AVAGRAHA -->
+            <key-style
+                latin:styleName="moreKeysDevanagariVowelSignE"
+                latin:moreKeys="&#x0903;,&#x093D;" />
+        </case>
+        <default>
+             <key-style
+                latin:styleName="moreKeysDevanagariVowelSignE" />
+        </default>
+    </switch>
+    <!-- U+0947: "े" DEVANAGARI VOWEL SIGN E -->
+    <key-style
+        latin:styleName="baseKeyDevanagariVowelSignE"
+        latin:parentStyle="moreKeysDevanagariVowelSignE"
+        latin:keyLabel="&#x0947;"
+        latin:keyLabelFlags="fontNormal" />
+</merge>
diff --git a/java/res/xml-v16/keystyle_devanagari_vowel_sign_i.xml b/java/res/xml-v16/keystyle_devanagari_vowel_sign_i.xml
new file mode 100644
index 0000000..845c1b0
--- /dev/null
+++ b/java/res/xml-v16/keystyle_devanagari_vowel_sign_i.xml
@@ -0,0 +1,48 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+**
+** Copyright 2013, 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.
+*/
+-->
+
+<!-- The code point U+25CC for key label is needed because the font rendering system prior to
+     API version 16 can't automatically render dotted circle for incomplete combining letter
+     of some scripts. The files named res/xml/key_*.xml have this U+25CC hack, although the
+     counterpart files named res/xml-v16/key_*.xml don't have this hack. -->
+<merge
+    xmlns:latin="http://schemas.android.com/apk/res/com.android.inputmethod.latin"
+>
+    <switch>
+        <case
+            latin:keyboardLayoutSet="hindi"
+        >
+            <!-- U+093F/U+0902: "िं" DEVANAGARI VOWEL SIGN I/DEVANAGARI SIGN ANUSVARA -->
+            <key-style
+                latin:styleName="moreKeysDevanagariVowelSignI"
+                latin:moreKeys="&#x093F;&#x0902;" />
+        </case>
+        <default>
+             <key-style
+                latin:styleName="moreKeysDevanagariVowelSignI" />
+        </default>
+    </switch>
+    <!-- U+093F: "ि" DEVANAGARI VOWEL SIGN I -->
+    <key-style
+        latin:styleName="baseKeyDevanagariVowelSignI"
+        latin:parentStyle="moreKeysDevanagariVowelSignI"
+        latin:keyLabel="&#x093F;"
+        latin:keyLabelFlags="fontNormal" />
+</merge>
diff --git a/java/res/xml-v16/keystyle_devanagari_vowel_sign_ii.xml b/java/res/xml-v16/keystyle_devanagari_vowel_sign_ii.xml
new file mode 100644
index 0000000..0de9650
--- /dev/null
+++ b/java/res/xml-v16/keystyle_devanagari_vowel_sign_ii.xml
@@ -0,0 +1,48 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+**
+** Copyright 2013, 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.
+*/
+-->
+
+<!-- The code point U+25CC for key label is needed because the font rendering system prior to
+     API version 16 can't automatically render dotted circle for incomplete combining letter
+     of some scripts. The files named res/xml/key_*.xml have this U+25CC hack, although the
+     counterpart files named res/xml-v16/key_*.xml don't have this hack. -->
+<merge
+    xmlns:latin="http://schemas.android.com/apk/res/com.android.inputmethod.latin"
+>
+    <switch>
+        <case
+            latin:keyboardLayoutSet="hindi"
+        >
+            <!-- U+0940/U+0902: "ीं" DEVANAGARI VOWEL SIGN II/DEVANAGARI SIGN ANUSVARA -->
+            <key-style
+                latin:styleName="moreKeysDevanagariVowelSignIi"
+                latin:moreKeys="&#x0940;&#x0902;,%" />
+        </case>
+        <default>
+             <key-style
+                latin:styleName="moreKeysDevanagariVowelSignIi" />
+        </default>
+    </switch>
+    <!-- U+0940: "ी" DEVANAGARI VOWEL SIGN II -->
+    <key-style
+        latin:styleName="baseKeyDevanagariVowelSignIi"
+        latin:parentStyle="moreKeysDevanagariVowelSignIi"
+        latin:keyLabel="&#x0940;"
+        latin:keyLabelFlags="fontNormal" />
+</merge>
diff --git a/java/res/xml-v16/keystyle_devanagari_vowel_sign_o.xml b/java/res/xml-v16/keystyle_devanagari_vowel_sign_o.xml
new file mode 100644
index 0000000..06f07fa
--- /dev/null
+++ b/java/res/xml-v16/keystyle_devanagari_vowel_sign_o.xml
@@ -0,0 +1,50 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+**
+** Copyright 2013, 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.
+*/
+-->
+
+<!-- The code point U+25CC for key label is needed because the font rendering system prior to
+     API version 16 can't automatically render dotted circle for incomplete combining letter
+     of some scripts. The files named res/xml/key_*.xml have this U+25CC hack, although the
+     counterpart files named res/xml-v16/key_*.xml don't have this hack. -->
+<merge
+    xmlns:latin="http://schemas.android.com/apk/res/com.android.inputmethod.latin"
+>
+    <switch>
+        <case
+            latin:keyboardLayoutSet="hindi"
+        >
+            <!-- U+094B/U+0902: "қं" DEVANAGARI VOWEL SIGN O/DEVANAGARI SIGN ANUSVARA
+                 U+0949: "ॉ" DEVANAGARI VOWEL SIGN CANDRA O
+                 U+094A: "ॊ" DEVANAGARI VOWEL SIGN SHORT O -->
+            <key-style
+                latin:styleName="moreKeysDevanagariVowelSignO"
+                latin:moreKeys="&#x094B;&#x0902;,&#x0949;,&#x094A;" />
+        </case>
+        <default>
+             <key-style
+                latin:styleName="moreKeysDevanagariVowelSignO" />
+        </default>
+    </switch>
+    <!-- U+094B: "ो" DEVANAGARI VOWEL SIGN O -->
+    <key-style
+        latin:styleName="baseKeyDevanagariVowelSignO"
+        latin:parentStyle="moreKeysDevanagariVowelSignO"
+        latin:keyLabel="&#x094B;"
+        latin:keyLabelFlags="fontNormal" />
+</merge>
diff --git a/java/res/xml-v16/keystyle_devanagari_vowel_sign_u.xml b/java/res/xml-v16/keystyle_devanagari_vowel_sign_u.xml
new file mode 100644
index 0000000..469a27b
--- /dev/null
+++ b/java/res/xml-v16/keystyle_devanagari_vowel_sign_u.xml
@@ -0,0 +1,49 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+**
+** Copyright 2013, 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.
+*/
+-->
+
+<!-- The code point U+25CC for key label is needed because the font rendering system prior to
+     API version 16 can't automatically render dotted circle for incomplete combining letter
+     of some scripts. The files named res/xml/key_*.xml have this U+25CC hack, although the
+     counterpart files named res/xml-v16/key_*.xml don't have this hack. -->
+<merge
+    xmlns:latin="http://schemas.android.com/apk/res/com.android.inputmethod.latin"
+>
+    <switch>
+        <case
+            latin:keyboardLayoutSet="hindi"
+        >
+            <!-- U+0941/U+0902: "ुं" DEVANAGARI VOWEL SIGN U/DEVANAGARI SIGN ANUSVARA
+                 U+0941/U+0901: "ुँ" DEVANAGARI VOWEL SIGN U/DEVANAGARI SIGN CANDRABINDU -->
+            <key-style
+                latin:styleName="moreKeysDevanagariVowelSignU"
+                latin:moreKeys="&#x0941;&#x0902;,&#x0941;&#x0901;" />
+        </case>
+        <default>
+             <key-style
+                latin:styleName="moreKeysDevanagariVowelSignU" />
+        </default>
+    </switch>
+    <!-- U+0941: "ु" DEVANAGARI VOWEL SIGN U -->
+    <key-style
+        latin:styleName="baseKeyDevanagariVowelSignU"
+        latin:parentStyle="moreKeysDevanagariVowelSignU"
+        latin:keyLabel="&#x0941;"
+        latin:keyLabelFlags="fontNormal" />
+</merge>
diff --git a/java/res/xml-v16/keystyle_devanagari_vowel_sign_uu.xml b/java/res/xml-v16/keystyle_devanagari_vowel_sign_uu.xml
new file mode 100644
index 0000000..25867c0
--- /dev/null
+++ b/java/res/xml-v16/keystyle_devanagari_vowel_sign_uu.xml
@@ -0,0 +1,49 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+**
+** Copyright 2013, 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.
+*/
+-->
+
+<!-- The code point U+25CC for key label is needed because the font rendering system prior to
+     API version 16 can't automatically render dotted circle for incomplete combining letter
+     of some scripts. The files named res/xml/key_*.xml have this U+25CC hack, although the
+     counterpart files named res/xml-v16/key_*.xml don't have this hack. -->
+<merge
+    xmlns:latin="http://schemas.android.com/apk/res/com.android.inputmethod.latin"
+>
+    <switch>
+        <case
+            latin:keyboardLayoutSet="hindi"
+        >
+            <!-- U+0942/U+0902: "ूं" DEVANAGARI VOWEL SIGN UU/DEVANAGARI SIGN ANUSVARA
+                 U+0942/U+0901: "ूँ" DEVANAGARI VOWEL SIGN UU/DEVANAGARI SIGN CANDRABINDU -->
+            <key-style
+                latin:styleName="moreKeysDevanagariVowelSignUu"
+                latin:moreKeys="&#x0942;&#x0902;,&#x0942;&#x0901;,%" />
+        </case>
+        <default>
+             <key-style
+                latin:styleName="moreKeysDevanagariVowelSignUu" />
+        </default>
+    </switch>
+    <!-- U+0942: "ू" DEVANAGARI VOWEL SIGN UU -->
+    <key-style
+        latin:styleName="baseKeyDevanagariVowelSignUu"
+        latin:parentStyle="moreKeysDevanagariVowelSignUu"
+        latin:keyLabel="&#x0942;"
+        latin:keyLabelFlags="fontNormal" />
+</merge>
diff --git a/java/res/xml/kbd_10_10_7_symbols_shift.xml b/java/res/xml/kbd_10_10_7_symbols_shift.xml
deleted file mode 100644
index a2d67ca..0000000
--- a/java/res/xml/kbd_10_10_7_symbols_shift.xml
+++ /dev/null
@@ -1,27 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-/*
-**
-** Copyright 2012, 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:touchPositionCorrectionData="@array/touch_position_correction_data_default"
->
-    <include
-        latin:keyboardLayout="@xml/rows_symbols_shift" />
-</Keyboard>
diff --git a/java/res/xml-sw768dp/kbd_thai_symbols.xml b/java/res/xml/kbd_armenian_phonetic.xml
similarity index 89%
copy from java/res/xml-sw768dp/kbd_thai_symbols.xml
copy to java/res/xml/kbd_armenian_phonetic.xml
index 5ddf574..1eb3c7e 100644
--- a/java/res/xml-sw768dp/kbd_thai_symbols.xml
+++ b/java/res/xml/kbd_armenian_phonetic.xml
@@ -2,7 +2,7 @@
 <!--
 /*
 **
-** Copyright 2012, The Android Open Source Project
+** Copyright 2013, 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.
@@ -27,5 +27,5 @@
     latin:touchPositionCorrectionData="@array/touch_position_correction_data_default"
 >
     <include
-        latin:keyboardLayout="@xml/rows_thai_symbols" />
+        latin:keyboardLayout="@xml/rows_armenian_phonetic" />
 </Keyboard>
diff --git a/java/res/xml/kbd_10_10_7_symbols.xml b/java/res/xml/kbd_emoji_category1.xml
similarity index 68%
copy from java/res/xml/kbd_10_10_7_symbols.xml
copy to java/res/xml/kbd_emoji_category1.xml
index 4d9861b..c11a830 100644
--- a/java/res/xml/kbd_10_10_7_symbols.xml
+++ b/java/res/xml/kbd_emoji_category1.xml
@@ -2,7 +2,7 @@
 <!--
 /*
 **
-** Copyright 2012, The Android Open Source Project
+** Copyright 2013, 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.
@@ -20,8 +20,12 @@
 
 <Keyboard
     xmlns:latin="http://schemas.android.com/apk/res/com.android.inputmethod.latin"
-    latin:touchPositionCorrectionData="@array/touch_position_correction_data_default"
+    latin:keyWidth="@fraction/emoji_keyboard_key_width"
+    latin:keyLetterSize="90%p"
+    latin:rowHeight="@fraction/emoji_keyboard_row_height"
 >
-    <include
-        latin:keyboardLayout="@xml/rows_symbols" />
+    <GridRows
+        latin:codesArray="@array/emoji_faces"
+        latin:keyLabelFlags="fontNormal"
+        latin:backgroundType="empty" />
 </Keyboard>
diff --git a/java/res/xml-sw600dp/kbd_10_10_7_symbols.xml b/java/res/xml/kbd_emoji_category2.xml
similarity index 67%
rename from java/res/xml-sw600dp/kbd_10_10_7_symbols.xml
rename to java/res/xml/kbd_emoji_category2.xml
index dd545b5..d3e5890 100644
--- a/java/res/xml-sw600dp/kbd_10_10_7_symbols.xml
+++ b/java/res/xml/kbd_emoji_category2.xml
@@ -2,7 +2,7 @@
 <!--
 /*
 **
-** Copyright 2012, The Android Open Source Project
+** Copyright 2013, 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.
@@ -20,8 +20,12 @@
 
 <Keyboard
     xmlns:latin="http://schemas.android.com/apk/res/com.android.inputmethod.latin"
-    latin:touchPositionCorrectionData="@array/touch_position_correction_data_default"
+    latin:keyWidth="@fraction/emoji_keyboard_key_width"
+    latin:keyLetterSize="90%p"
+    latin:rowHeight="@fraction/emoji_keyboard_row_height"
 >
-    <include
-        latin:keyboardLayout="@xml/rows_10_10_7_symbols" />
+    <GridRows
+        latin:codesArray="@array/emoji_objects"
+        latin:keyLabelFlags="fontNormal"
+        latin:backgroundType="empty" />
 </Keyboard>
diff --git a/java/res/xml/kbd_10_10_7_symbols.xml b/java/res/xml/kbd_emoji_category3.xml
similarity index 68%
copy from java/res/xml/kbd_10_10_7_symbols.xml
copy to java/res/xml/kbd_emoji_category3.xml
index 4d9861b..0efafa8 100644
--- a/java/res/xml/kbd_10_10_7_symbols.xml
+++ b/java/res/xml/kbd_emoji_category3.xml
@@ -2,7 +2,7 @@
 <!--
 /*
 **
-** Copyright 2012, The Android Open Source Project
+** Copyright 2013, 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.
@@ -20,8 +20,12 @@
 
 <Keyboard
     xmlns:latin="http://schemas.android.com/apk/res/com.android.inputmethod.latin"
-    latin:touchPositionCorrectionData="@array/touch_position_correction_data_default"
+    latin:keyWidth="@fraction/emoji_keyboard_key_width"
+    latin:keyLetterSize="90%p"
+    latin:rowHeight="@fraction/emoji_keyboard_row_height"
 >
-    <include
-        latin:keyboardLayout="@xml/rows_symbols" />
+    <GridRows
+        latin:codesArray="@array/emoji_nature"
+        latin:keyLabelFlags="fontNormal"
+        latin:backgroundType="empty" />
 </Keyboard>
diff --git a/java/res/xml/kbd_10_10_7_symbols.xml b/java/res/xml/kbd_emoji_category4.xml
similarity index 68%
copy from java/res/xml/kbd_10_10_7_symbols.xml
copy to java/res/xml/kbd_emoji_category4.xml
index 4d9861b..e529120 100644
--- a/java/res/xml/kbd_10_10_7_symbols.xml
+++ b/java/res/xml/kbd_emoji_category4.xml
@@ -2,7 +2,7 @@
 <!--
 /*
 **
-** Copyright 2012, The Android Open Source Project
+** Copyright 2013, 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.
@@ -20,8 +20,12 @@
 
 <Keyboard
     xmlns:latin="http://schemas.android.com/apk/res/com.android.inputmethod.latin"
-    latin:touchPositionCorrectionData="@array/touch_position_correction_data_default"
+    latin:keyWidth="@fraction/emoji_keyboard_key_width"
+    latin:keyLetterSize="90%p"
+    latin:rowHeight="@fraction/emoji_keyboard_row_height"
 >
-    <include
-        latin:keyboardLayout="@xml/rows_symbols" />
+    <GridRows
+        latin:codesArray="@array/emoji_places"
+        latin:keyLabelFlags="fontNormal"
+        latin:backgroundType="empty" />
 </Keyboard>
diff --git a/java/res/xml-sw600dp/kbd_10_10_7_symbols.xml b/java/res/xml/kbd_emoji_category5.xml
similarity index 67%
copy from java/res/xml-sw600dp/kbd_10_10_7_symbols.xml
copy to java/res/xml/kbd_emoji_category5.xml
index dd545b5..1836879 100644
--- a/java/res/xml-sw600dp/kbd_10_10_7_symbols.xml
+++ b/java/res/xml/kbd_emoji_category5.xml
@@ -2,7 +2,7 @@
 <!--
 /*
 **
-** Copyright 2012, The Android Open Source Project
+** Copyright 2013, 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.
@@ -20,8 +20,12 @@
 
 <Keyboard
     xmlns:latin="http://schemas.android.com/apk/res/com.android.inputmethod.latin"
-    latin:touchPositionCorrectionData="@array/touch_position_correction_data_default"
+    latin:keyWidth="@fraction/emoji_keyboard_key_width"
+    latin:keyLetterSize="90%p"
+    latin:rowHeight="@fraction/emoji_keyboard_row_height"
 >
-    <include
-        latin:keyboardLayout="@xml/rows_10_10_7_symbols" />
+    <GridRows
+        latin:codesArray="@array/emoji_symbols"
+        latin:keyLabelFlags="fontNormal"
+        latin:backgroundType="empty" />
 </Keyboard>
diff --git a/java/res/xml-sw600dp/kbd_10_10_7_symbols.xml b/java/res/xml/kbd_emoji_category6.xml
similarity index 65%
copy from java/res/xml-sw600dp/kbd_10_10_7_symbols.xml
copy to java/res/xml/kbd_emoji_category6.xml
index dd545b5..b47ebfe 100644
--- a/java/res/xml-sw600dp/kbd_10_10_7_symbols.xml
+++ b/java/res/xml/kbd_emoji_category6.xml
@@ -2,7 +2,7 @@
 <!--
 /*
 **
-** Copyright 2012, The Android Open Source Project
+** Copyright 2013, 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.
@@ -20,8 +20,13 @@
 
 <Keyboard
     xmlns:latin="http://schemas.android.com/apk/res/com.android.inputmethod.latin"
-    latin:touchPositionCorrectionData="@array/touch_position_correction_data_default"
+    latin:keyWidth="@fraction/emoji_keyboard_key_width"
+    latin:keyLetterSize="90%p"
+    latin:keyLabelSize="60%p"
+    latin:rowHeight="@fraction/emoji_keyboard_row_height"
 >
-    <include
-        latin:keyboardLayout="@xml/rows_10_10_7_symbols" />
+    <GridRows
+        latin:textsArray="@array/emoji_emoticons"
+        latin:keyLabelFlags="fontNormal"
+        latin:backgroundType="empty" />
 </Keyboard>
diff --git a/java/res/xml/kbd_10_10_7_symbols.xml b/java/res/xml/kbd_emoji_recents.xml
similarity index 64%
copy from java/res/xml/kbd_10_10_7_symbols.xml
copy to java/res/xml/kbd_emoji_recents.xml
index 4d9861b..73926ec 100644
--- a/java/res/xml/kbd_10_10_7_symbols.xml
+++ b/java/res/xml/kbd_emoji_recents.xml
@@ -2,7 +2,7 @@
 <!--
 /*
 **
-** Copyright 2012, The Android Open Source Project
+** Copyright 2013, 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.
@@ -20,8 +20,13 @@
 
 <Keyboard
     xmlns:latin="http://schemas.android.com/apk/res/com.android.inputmethod.latin"
-    latin:touchPositionCorrectionData="@array/touch_position_correction_data_default"
+    latin:keyWidth="@fraction/emoji_keyboard_key_width"
+    latin:keyLetterSize="@fraction/emoji_keyboard_key_letter_size"
+    latin:keyLabelSize="60%p"
+    latin:rowHeight="@fraction/emoji_keyboard_row_height"
 >
-    <include
-        latin:keyboardLayout="@xml/rows_symbols" />
+    <GridRows
+        latin:codesArray="@array/emoji_recents"
+        latin:keyLabelFlags="fontNormal"
+        latin:backgroundType="empty" />
 </Keyboard>
diff --git a/java/res/xml-sw768dp/kbd_thai_symbols.xml b/java/res/xml/kbd_khmer.xml
similarity index 89%
copy from java/res/xml-sw768dp/kbd_thai_symbols.xml
copy to java/res/xml/kbd_khmer.xml
index 5ddf574..7a2337a 100644
--- a/java/res/xml-sw768dp/kbd_thai_symbols.xml
+++ b/java/res/xml/kbd_khmer.xml
@@ -2,7 +2,7 @@
 <!--
 /*
 **
-** Copyright 2012, The Android Open Source Project
+** Copyright 2013, 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.
@@ -27,5 +27,5 @@
     latin:touchPositionCorrectionData="@array/touch_position_correction_data_default"
 >
     <include
-        latin:keyboardLayout="@xml/rows_thai_symbols" />
+        latin:keyboardLayout="@xml/rows_khmer" />
 </Keyboard>
diff --git a/java/res/xml-sw768dp/kbd_thai_symbols.xml b/java/res/xml/kbd_lao.xml
similarity index 89%
rename from java/res/xml-sw768dp/kbd_thai_symbols.xml
rename to java/res/xml/kbd_lao.xml
index 5ddf574..2bba330 100644
--- a/java/res/xml-sw768dp/kbd_thai_symbols.xml
+++ b/java/res/xml/kbd_lao.xml
@@ -2,7 +2,7 @@
 <!--
 /*
 **
-** Copyright 2012, The Android Open Source Project
+** Copyright 2013, 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.
@@ -27,5 +27,5 @@
     latin:touchPositionCorrectionData="@array/touch_position_correction_data_default"
 >
     <include
-        latin:keyboardLayout="@xml/rows_thai_symbols" />
+        latin:keyboardLayout="@xml/rows_lao" />
 </Keyboard>
diff --git a/java/res/xml/kbd_10_10_7_symbols.xml b/java/res/xml/kbd_nepali_romanized.xml
similarity index 79%
copy from java/res/xml/kbd_10_10_7_symbols.xml
copy to java/res/xml/kbd_nepali_romanized.xml
index 4d9861b..9e43813 100644
--- a/java/res/xml/kbd_10_10_7_symbols.xml
+++ b/java/res/xml/kbd_nepali_romanized.xml
@@ -2,7 +2,7 @@
 <!--
 /*
 **
-** Copyright 2012, The Android Open Source Project
+** Copyright 2013, 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.
@@ -20,8 +20,7 @@
 
 <Keyboard
     xmlns:latin="http://schemas.android.com/apk/res/com.android.inputmethod.latin"
-    latin:touchPositionCorrectionData="@array/touch_position_correction_data_default"
 >
     <include
-        latin:keyboardLayout="@xml/rows_symbols" />
+        latin:keyboardLayout="@xml/rows_nepali_romanized" />
 </Keyboard>
diff --git a/java/res/xml/kbd_10_10_7_symbols.xml b/java/res/xml/kbd_nepali_traditional.xml
similarity index 79%
copy from java/res/xml/kbd_10_10_7_symbols.xml
copy to java/res/xml/kbd_nepali_traditional.xml
index 4d9861b..6854e32 100644
--- a/java/res/xml/kbd_10_10_7_symbols.xml
+++ b/java/res/xml/kbd_nepali_traditional.xml
@@ -2,7 +2,7 @@
 <!--
 /*
 **
-** Copyright 2012, The Android Open Source Project
+** Copyright 2013, 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.
@@ -20,8 +20,7 @@
 
 <Keyboard
     xmlns:latin="http://schemas.android.com/apk/res/com.android.inputmethod.latin"
-    latin:touchPositionCorrectionData="@array/touch_position_correction_data_default"
 >
     <include
-        latin:keyboardLayout="@xml/rows_symbols" />
+        latin:keyboardLayout="@xml/rows_nepali_traditional" />
 </Keyboard>
diff --git a/java/res/xml/kbd_pcqwerty_symbols.xml b/java/res/xml/kbd_pcqwerty_symbols.xml
deleted file mode 100644
index bfb39e8..0000000
--- a/java/res/xml/kbd_pcqwerty_symbols.xml
+++ /dev/null
@@ -1,31 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-/*
-**
-** Copyright 2012, 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:rowHeight="20%p"
-    latin:verticalGap="@fraction/key_bottom_gap_5row"
-    latin:keyLetterSize="@fraction/key_letter_ratio_5row"
-    latin:keyShiftedLetterHintRatio="@fraction/key_uppercase_letter_ratio_5row"
-    latin:touchPositionCorrectionData="@array/touch_position_correction_data_default"
->
-    <include
-        latin:keyboardLayout="@xml/rows_pcqwerty_symbols" />
-</Keyboard>
diff --git a/java/res/xml/kbd_thai_symbols.xml b/java/res/xml/kbd_thai_symbols.xml
deleted file mode 100644
index 4d9861b..0000000
--- a/java/res/xml/kbd_thai_symbols.xml
+++ /dev/null
@@ -1,27 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-/*
-**
-** Copyright 2012, 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:touchPositionCorrectionData="@array/touch_position_correction_data_default"
->
-    <include
-        latin:keyboardLayout="@xml/rows_symbols" />
-</Keyboard>
diff --git a/java/res/xml/kbd_thai_symbols_shift.xml b/java/res/xml/kbd_thai_symbols_shift.xml
deleted file mode 100644
index a2d67ca..0000000
--- a/java/res/xml/kbd_thai_symbols_shift.xml
+++ /dev/null
@@ -1,27 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-/*
-**
-** Copyright 2012, 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:touchPositionCorrectionData="@array/touch_position_correction_data_default"
->
-    <include
-        latin:keyboardLayout="@xml/rows_symbols_shift" />
-</Keyboard>
diff --git a/java/res/xml/kbd_10_10_7_symbols.xml b/java/res/xml/key_armenian_sha.xml
similarity index 75%
copy from java/res/xml/kbd_10_10_7_symbols.xml
copy to java/res/xml/key_armenian_sha.xml
index 4d9861b..3865c19 100644
--- a/java/res/xml/kbd_10_10_7_symbols.xml
+++ b/java/res/xml/key_armenian_sha.xml
@@ -2,7 +2,7 @@
 <!--
 /*
 **
-** Copyright 2012, The Android Open Source Project
+** Copyright 2013, The Android Open Source Project
 **
 ** Licensed under the Apache License, Version 2.0 (the "License");
 ** you may not use this file except in compliance with the License.
@@ -18,10 +18,11 @@
 */
 -->
 
-<Keyboard
+<merge
     xmlns:latin="http://schemas.android.com/apk/res/com.android.inputmethod.latin"
-    latin:touchPositionCorrectionData="@array/touch_position_correction_data_default"
 >
-    <include
-        latin:keyboardLayout="@xml/rows_symbols" />
-</Keyboard>
+    <!-- U+0577: "շ" ARMENIAN SMALL LETTER SHA -->
+    <Key
+        latin:keyLabel="&#x0577;"
+        latin:keyLabelFlags="fontNormal" />
+</merge>
diff --git a/java/res/xml/kbd_10_10_7_symbols.xml b/java/res/xml/key_armenian_xeh.xml
similarity index 75%
copy from java/res/xml/kbd_10_10_7_symbols.xml
copy to java/res/xml/key_armenian_xeh.xml
index 4d9861b..007a580 100644
--- a/java/res/xml/kbd_10_10_7_symbols.xml
+++ b/java/res/xml/key_armenian_xeh.xml
@@ -2,7 +2,7 @@
 <!--
 /*
 **
-** Copyright 2012, The Android Open Source Project
+** Copyright 2013, The Android Open Source Project
 **
 ** Licensed under the Apache License, Version 2.0 (the "License");
 ** you may not use this file except in compliance with the License.
@@ -18,10 +18,11 @@
 */
 -->
 
-<Keyboard
+<merge
     xmlns:latin="http://schemas.android.com/apk/res/com.android.inputmethod.latin"
-    latin:touchPositionCorrectionData="@array/touch_position_correction_data_default"
 >
-    <include
-        latin:keyboardLayout="@xml/rows_symbols" />
-</Keyboard>
+    <!-- U+056D: "խ" ARMENIAN SMALL LETTER XEH -->
+    <Key
+        latin:keyLabel="&#x056D;"
+        latin:keyLabelFlags="fontNormal" />
+</merge>
diff --git a/java/res/xml/key_hindi1_shift.xml b/java/res/xml/key_devanagari_sign_anusvara.xml
similarity index 77%
copy from java/res/xml/key_hindi1_shift.xml
copy to java/res/xml/key_devanagari_sign_anusvara.xml
index 0db5ae9..0acd3bc 100644
--- a/java/res/xml/key_hindi1_shift.xml
+++ b/java/res/xml/key_devanagari_sign_anusvara.xml
@@ -20,15 +20,15 @@
 
 <!-- The code point U+25CC for key label is needed because the font rendering system prior to
      API version 16 can't automatically render dotted circle for incomplete combining letter
-     of Hindi. The files named res/xml/{key,keys}_hindi*.xml have this U+25CC hack, although the
-     counterpart files named res/xml-v16/{key,keys}_hindi*.xml don't have this hack. -->
+     of some scripts. The files named res/xml/key_*.xml have this U+25CC hack, although the
+     counterpart files named res/xml-v16/key_*.xml don't have this hack. -->
 <merge
     xmlns:latin="http://schemas.android.com/apk/res/com.android.inputmethod.latin"
 >
     <!-- U+25CC: "◌" DOTTED CIRCLE
-         U+0903: "ः" DEVANAGARI SIGN VISARGA -->
+         U+0902: "ं" DEVANAGARI SIGN ANUSVARA -->
     <Key
-        latin:keyLabel="&#x25CC;&#x0903;"
-        latin:code="0x0903"
+        latin:keyLabel="&#x25CC;&#x0902;"
+        latin:code="0x0902"
         latin:keyLabelFlags="fontNormal|followKeyLetterRatio" />
 </merge>
diff --git a/java/res/xml/key_devanagari_sign_candrabindu.xml b/java/res/xml/key_devanagari_sign_candrabindu.xml
new file mode 100644
index 0000000..df0c4e0
--- /dev/null
+++ b/java/res/xml/key_devanagari_sign_candrabindu.xml
@@ -0,0 +1,50 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+**
+** Copyright 2013, 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.
+*/
+-->
+
+<!-- The code point U+25CC for key label is needed because the font rendering system prior to
+     API version 16 can't automatically render dotted circle for incomplete combining letter
+     of some scripts. The files named res/xml/key_*.xml have this U+25CC hack, although the
+     counterpart files named res/xml-v16/key_*.xml don't have this hack. -->
+<merge
+    xmlns:latin="http://schemas.android.com/apk/res/com.android.inputmethod.latin"
+>
+    <switch>
+        <case
+            latin:keyboardLayoutSet="hindi"
+        >
+            <!-- U+25CC: "◌" DOTTED CIRCLE
+                 U+0945: "ॅ" DEVANAGARI VOWEL SIGN CANDRA E -->
+            <key-style
+                latin:styleName="moreKeysDevanagariSignCandrabindu"
+                latin:moreKeys="&#x25CC;&#x0945;|&#x0945;" />
+        </case>
+        <default>
+             <key-style
+                latin:styleName="moreKeysDevanagariSignCandrabindu" />
+        </default>
+    </switch>
+    <!-- U+25CC: "◌" DOTTED CIRCLE
+         U+0901: "ँ" DEVANAGARI SIGN CANDRABINDU -->
+    <Key
+        latin:keyStyle="moreKeysDevanagariSignCandrabindu"
+        latin:keyLabel="&#x25CC;&#x0901;"
+        latin:code="0x0901"
+        latin:keyLabelFlags="fontNormal|followKeyLetterRatio" />
+</merge>
diff --git a/java/res/xml/key_devanagari_sign_nukta.xml b/java/res/xml/key_devanagari_sign_nukta.xml
new file mode 100644
index 0000000..f7a03ee
--- /dev/null
+++ b/java/res/xml/key_devanagari_sign_nukta.xml
@@ -0,0 +1,52 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+**
+** Copyright 2013, 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.
+*/
+-->
+
+<!-- The code point U+25CC for key label is needed because the font rendering system prior to
+     API version 16 can't automatically render dotted circle for incomplete combining letter
+     of some scripts. The files named res/xml/key_*.xml have this U+25CC hack, although the
+     counterpart files named res/xml-v16/key_*.xml don't have this hack. -->
+<merge
+    xmlns:latin="http://schemas.android.com/apk/res/com.android.inputmethod.latin"
+>
+    <switch>
+        <case
+            latin:keyboardLayoutSet="hindi"
+        >
+            <!-- U+25CC: "◌" DOTTED CIRCLE
+                 U+097D: "ॽ" DEVANAGARI LETTER GLOTTAL STOP
+                 U+0970: "॰" DEVANAGARI ABBREVIATION SIGN
+                 U+093D: "ऽ" DEVANAGARI SIGN AVAGRAHA -->
+             <key-style
+                latin:styleName="moreKeysDevanagariSignNukta"
+                latin:moreKeys="&#x25CC;&#x097D;|&#x097D;,&#x25CC;&#x0970;|&#x0970;,&#x25CC;&#x093D;|&#x093D;" />
+        </case>
+        <default>
+             <key-style
+                latin:styleName="moreKeysDevanagariSignNukta" />
+        </default>
+    </switch>
+    <!-- U+25CC: "◌" DOTTED CIRCLE
+         U+093C: "़" DEVANAGARI SIGN NUKTA -->
+    <Key
+        latin:keyStyle="moreKeysDevanagariSignNukta"
+        latin:keyLabel="&#x25CC;&#x093C;"
+        latin:code="0x093C"
+        latin:keyLabelFlags="fontNormal|followKeyLetterRatio" />
+</merge>
diff --git a/java/res/xml/key_hindi1_shift.xml b/java/res/xml/key_devanagari_vowel_sign_candra_o.xml
similarity index 77%
copy from java/res/xml/key_hindi1_shift.xml
copy to java/res/xml/key_devanagari_vowel_sign_candra_o.xml
index 0db5ae9..370fc54 100644
--- a/java/res/xml/key_hindi1_shift.xml
+++ b/java/res/xml/key_devanagari_vowel_sign_candra_o.xml
@@ -20,15 +20,15 @@
 
 <!-- The code point U+25CC for key label is needed because the font rendering system prior to
      API version 16 can't automatically render dotted circle for incomplete combining letter
-     of Hindi. The files named res/xml/{key,keys}_hindi*.xml have this U+25CC hack, although the
-     counterpart files named res/xml-v16/{key,keys}_hindi*.xml don't have this hack. -->
+     of some scripts. The files named res/xml/key_*.xml have this U+25CC hack, although the
+     counterpart files named res/xml-v16/key_*.xml don't have this hack. -->
 <merge
     xmlns:latin="http://schemas.android.com/apk/res/com.android.inputmethod.latin"
 >
     <!-- U+25CC: "◌" DOTTED CIRCLE
-         U+0903: "ः" DEVANAGARI SIGN VISARGA -->
+         U+0949: "ॉ" DEVANAGARI VOWEL SIGN CANDRA O -->
     <Key
-        latin:keyLabel="&#x25CC;&#x0903;"
-        latin:code="0x0903"
+        latin:keyLabel="&#x25CC;&#x0949;"
+        latin:code="0x0949"
         latin:keyLabelFlags="fontNormal|followKeyLetterRatio" />
 </merge>
diff --git a/java/res/xml/key_devanagari_vowel_sign_vocalic_r.xml b/java/res/xml/key_devanagari_vowel_sign_vocalic_r.xml
new file mode 100644
index 0000000..f150d7e
--- /dev/null
+++ b/java/res/xml/key_devanagari_vowel_sign_vocalic_r.xml
@@ -0,0 +1,58 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+**
+** Copyright 2013, 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.
+*/
+-->
+
+<!-- The code point U+25CC for key label is needed because the font rendering system prior to
+     API version 16 can't automatically render dotted circle for incomplete combining letter
+     of some scripts. The files named res/xml/key_*.xml have this U+25CC hack, although the
+     counterpart files named res/xml-v16/key_*.xml don't have this hack. -->
+<merge
+    xmlns:latin="http://schemas.android.com/apk/res/com.android.inputmethod.latin"
+>
+    <switch>
+        <case
+            latin:keyboardLayoutSet="hindi"
+        >
+            <!-- U+25CC: "◌" DOTTED CIRCLE
+                 U+0944: "ॄ" DEVANAGARI VOWEL SIGN VOCALIC RR -->
+            <key-style
+                latin:styleName="moreKeysDevanagariVowelSignVocalicR"
+                latin:moreKeys="&#x25CC;&#x0944;|&#x0944;" />
+        </case>
+        <case
+            latin:keyboardLayoutSet="nepali_traditional"
+        >
+            <!-- U+0913: "ओ" DEVANAGARI LETTER O -->
+            <key-style
+                latin:styleName="moreKeysDevanagariVowelSignVocalicR"
+                latin:moreKeys="&#x0913;" />
+        </case>
+        <default>
+             <key-style
+                latin:styleName="moreKeysDevanagariVowelSignVocalicR" />
+        </default>
+    </switch>
+    <!-- U+25CC: "◌" DOTTED CIRCLE
+         U+0943: "ृ" DEVANAGARI VOWEL SIGN VOCALIC R -->
+    <Key
+        latin:keyStyle="moreKeysDevanagariVowelSignVocalicR"
+        latin:keyLabel="&#x25CC;&#x0943;"
+        latin:code="0x0943"
+        latin:keyLabelFlags="fontNormal|followKeyLetterRatio" />
+</merge>
diff --git a/java/res/xml-sw768dp/key_settings.xml b/java/res/xml/key_f2.xml
similarity index 78%
rename from java/res/xml-sw768dp/key_settings.xml
rename to java/res/xml/key_f2.xml
index 0d3bb59..473dd21 100644
--- a/java/res/xml-sw768dp/key_settings.xml
+++ b/java/res/xml/key_f2.xml
@@ -23,13 +23,16 @@
 >
     <switch>
         <case
-            latin:clobberSettingsKey="false"
+            latin:mode="email|url"
         >
             <Key
-                latin:keyStyle="settingsKeyStyle" />
+                latin:keyStyle="comKeyStyle"
+                latin:keyWidth="fillRight" />
         </case>
         <default>
-            <Spacer />
+            <Key
+                latin:keyStyle="emojiKeyStyle"
+                latin:keyWidth="fillRight" />
         </default>
     </switch>
 </merge>
diff --git a/java/res/xml/key_hindi3_right.xml b/java/res/xml/key_hindi3_right.xml
deleted file mode 100644
index 5a97355..0000000
--- a/java/res/xml/key_hindi3_right.xml
+++ /dev/null
@@ -1,38 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-/*
-**
-** Copyright 2013, 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.
-*/
--->
-
-<!-- The code point U+25CC for key label is needed because the font rendering system prior to
-     API version 16 can't automatically render dotted circle for incomplete combining letter
-     of Hindi. The files named res/xml/{key,keys}_hindi*.xml have this U+25CC hack, although the
-     counterpart files named res/xml-v16/{key,keys}_hindi*.xml don't have this hack. -->
-<merge
-    xmlns:latin="http://schemas.android.com/apk/res/com.android.inputmethod.latin"
->
-    <!-- U+25CC: "◌" DOTTED CIRCLE
-         U+093C: "़" DEVANAGARI SIGN NUKTA
-         U+097D: "ॽ" DEVANAGARI LETTER GLOTTAL STOP
-         U+0970: "॰" DEVANAGARI ABBREVIATION SIGN
-         U+093D: "ऽ" DEVANAGARI SIGN AVAGRAHA -->
-    <Key
-        latin:keyLabel="&#x25CC;&#x093C;"
-        latin:code="0x093C"
-        latin:moreKeys="&#x25CC;&#x097D;|&#x097D;,&#x25CC;&#x0970;|&#x0970;,&#x25CC;&#x093D;|&#x093D;"
-        latin:keyLabelFlags="fontNormal|followKeyLetterRatio" />
-</merge>
diff --git a/java/res/xml/key_hindi3_shift_left.xml b/java/res/xml/key_hindi3_shift_left.xml
deleted file mode 100644
index c5e2f13..0000000
--- a/java/res/xml/key_hindi3_shift_left.xml
+++ /dev/null
@@ -1,36 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-/*
-**
-** Copyright 2013, 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.
-*/
--->
-
-<!-- The code point U+25CC for key label is needed because the font rendering system prior to
-     API version 16 can't automatically render dotted circle for incomplete combining letter
-     of Hindi. The files named res/xml/{key,keys}_hindi*.xml have this U+25CC hack, although the
-     counterpart files named res/xml-v16/{key,keys}_hindi*.xml don't have this hack. -->
-<merge
-    xmlns:latin="http://schemas.android.com/apk/res/com.android.inputmethod.latin"
->
-    <!-- U+25CC: "◌" DOTTED CIRCLE
-         U+0901: "ँ" DEVANAGARI SIGN CANDRABINDU
-         U+0945: "ॅ" DEVANAGARI VOWEL SIGN CANDRA E -->
-    <Key
-        latin:keyLabel="&#x25CC;&#x0901;"
-        latin:code="0x0901"
-        latin:moreKeys="&#x25CC;&#x0945;|&#x0945;"
-        latin:keyLabelFlags="fontNormal|followKeyLetterRatio" />
-</merge>
diff --git a/java/res/xml/key_hindi3_shift_right.xml b/java/res/xml/key_hindi3_shift_right.xml
deleted file mode 100644
index 0da116a..0000000
--- a/java/res/xml/key_hindi3_shift_right.xml
+++ /dev/null
@@ -1,36 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-/*
-**
-** Copyright 2013, 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.
-*/
--->
-
-<!-- The code point U+25CC for key label is needed because the font rendering system prior to
-     API version 16 can't automatically render dotted circle for incomplete combining letter
-     of Hindi. The files named res/xml/{key,keys}_hindi*.xml have this U+25CC hack, although the
-     counterpart files named res/xml-v16/{key,keys}_hindi*.xml don't have this hack. -->
-<merge
-    xmlns:latin="http://schemas.android.com/apk/res/com.android.inputmethod.latin"
->
-    <!-- U+25CC: "◌" DOTTED CIRCLE
-         U+0943: "ृ" DEVANAGARI VOWEL SIGN VOCALIC R
-         U+0944: "ॄ" DEVANAGARI VOWEL SIGN VOCALIC RR -->
-    <Key
-        latin:keyLabel="&#x25CC;&#x0943;"
-        latin:code="0x0943"
-        latin:moreKeys="&#x25CC;&#x0944;|&#x0944;"
-        latin:keyLabelFlags="fontNormal|followKeyLetterRatio" />
-</merge>
diff --git a/java/res/xml/key_nepali_traditional_period.xml b/java/res/xml/key_nepali_traditional_period.xml
new file mode 100644
index 0000000..0f575c5
--- /dev/null
+++ b/java/res/xml/key_nepali_traditional_period.xml
@@ -0,0 +1,49 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+**
+** Copyright 2013, 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.
+*/
+-->
+
+<!-- The code point U+25CC for key label is needed because the font rendering system prior to
+     API version 16 can't automatically render dotted circle for incomplete combining letter
+     of Hindi. The files named res/xml/{key,keys}_nepali*.xml have this U+25CC hack, although the
+     counterpart files named res/xml-v16/{key,keys}_nepali*.xml don't have this hack. -->
+<merge
+    xmlns:latin="http://schemas.android.com/apk/res/com.android.inputmethod.latin"
+>
+    <switch>
+        <case
+            latin:keyboardLayoutSetElement="alphabetManualShifted|alphabetShiftLocked|alphabetShiftLockShifted"
+        >
+            <Key
+                latin:keyLabel=","
+                latin:backgroundType="functional" />
+        </case>
+        <default>
+            <!-- Because the font rendering system prior to API version 16 can't automatically
+                 render dotted circle for incomplete combining letter of some scripts, different
+                 set of Key definitions are needed based on the API version. -->
+            <include
+                latin:keyboardLayout="@xml/keystyle_devanagari_sign_virama" />
+            <Key
+                latin:keyStyle="baseKeyDevanagariSignVirama"
+                latin:keyLabelFlags="hasPopupHint"
+                latin:moreKeys="!fixedColumnOrder!4,.,!text/more_keys_for_punctuation"
+                latin:backgroundType="functional" />
+        </default>
+    </switch>
+</merge>
diff --git a/java/res/xml-sw600dp/key_f2.xml b/java/res/xml/key_space_3kw.xml
similarity index 66%
rename from java/res/xml-sw600dp/key_f2.xml
rename to java/res/xml/key_space_3kw.xml
index ca3b30b..20ec882 100644
--- a/java/res/xml-sw600dp/key_f2.xml
+++ b/java/res/xml/key_space_3kw.xml
@@ -2,7 +2,7 @@
 <!--
 /*
 **
-** Copyright 2012, The Android Open Source Project
+** Copyright 2013, 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.
@@ -23,23 +23,19 @@
 >
     <switch>
         <case
-            latin:mode="email|url"
+            latin:languageSwitchKeyEnabled="true"
         >
             <Key
-                latin:keyStyle="comKeyStyle" />
-        </case>
-        <case
-            latin:imeAction="actionSearch"
-        >
+                latin:keyStyle="languageSwitchKeyStyle" />
             <Key
-                latin:keyLabel=":"
-                latin:keyHintLabel="+"
-                latin:moreKeys="+"
-                latin:keyStyle="hasShiftedLetterHintStyle" />
+                latin:keyStyle="spaceKeyStyle"
+                latin:keyWidth="20%p" />
         </case>
+        <!-- languageSwitchKeyEnabled="false" -->
         <default>
             <Key
-                latin:keyStyle="smileyKeyStyle" />
+                latin:keyStyle="spaceKeyStyle"
+                latin:keyWidth="30%p" />
         </default>
     </switch>
 </merge>
diff --git a/java/res/xml/key_space.xml b/java/res/xml/key_space_5kw.xml
similarity index 95%
rename from java/res/xml/key_space.xml
rename to java/res/xml/key_space_5kw.xml
index 02ee42f..b6d38fb 100644
--- a/java/res/xml/key_space.xml
+++ b/java/res/xml/key_space_5kw.xml
@@ -23,7 +23,7 @@
 >
     <switch>
         <case
-            latin:languageCode="fa"
+            latin:languageCode="fa|ne"
             latin:languageSwitchKeyEnabled="true"
         >
             <Key
@@ -35,7 +35,7 @@
                 latin:keyStyle="zwnjKeyStyle" />
         </case>
         <case
-            latin:languageCode="fa"
+            latin:languageCode="fa|ne"
             latin:languageSwitchKeyEnabled="false"
         >
             <Key
diff --git a/java/res/xml/kbd_10_10_7_symbols.xml b/java/res/xml/key_space_symbols.xml
similarity index 82%
rename from java/res/xml/kbd_10_10_7_symbols.xml
rename to java/res/xml/key_space_symbols.xml
index 4d9861b..1efc4ff 100644
--- a/java/res/xml/kbd_10_10_7_symbols.xml
+++ b/java/res/xml/key_space_symbols.xml
@@ -18,10 +18,9 @@
 */
 -->
 
-<Keyboard
+<merge
     xmlns:latin="http://schemas.android.com/apk/res/com.android.inputmethod.latin"
-    latin:touchPositionCorrectionData="@array/touch_position_correction_data_default"
 >
     <include
-        latin:keyboardLayout="@xml/rows_symbols" />
-</Keyboard>
+        latin:keyboardLayout="@xml/key_space_3kw" />
+</merge>
diff --git a/java/res/xml/key_styles_common.xml b/java/res/xml/key_styles_common.xml
index 355455e..67ed962 100644
--- a/java/res/xml/key_styles_common.xml
+++ b/java/res/xml/key_styles_common.xml
@@ -21,6 +21,20 @@
 <merge
     xmlns:latin="http://schemas.android.com/apk/res/com.android.inputmethod.latin"
 >
+    <switch>
+        <case
+            latin:keyboardLayoutSetElement="alphabetManualShifted|alphabetShiftLockShifted"
+        >
+            <key-style
+                latin:styleName="hasShiftedLetterHintStyle"
+                latin:keyLabelFlags="hasShiftedLetterHint|shiftedLetterActivated" />
+        </case>
+        <default>
+            <key-style
+                latin:styleName="hasShiftedLetterHintStyle"
+                latin:keyLabelFlags="hasShiftedLetterHint" />
+        </default>
+    </switch>
     <!-- Base key style for the key which may have settings or tab key as popup key. -->
     <include
         latin:keyboardLayout="@xml/key_styles_f1" />
@@ -67,30 +81,6 @@
         latin:backgroundType="functional" />
     <include
         latin:keyboardLayout="@xml/key_styles_enter" />
-    <switch>
-        <!-- Shift + Enter in textMultiLine field. -->
-        <case
-            latin:isMultiLine="true"
-            latin:keyboardLayoutSetElement="alphabetManualShifted|alphabetShiftLockShifted"
-        >
-            <key-style
-                latin:styleName="enterKeyStyle"
-                latin:parentStyle="shiftEnterKeyStyle" />
-        </case>
-        <!-- Smiley in textShortMessage field.
-             Overrides common enter key style. -->
-        <case
-            latin:mode="im"
-        >
-            <key-style
-                latin:styleName="enterKeyStyle"
-                latin:keyLabel=":-)"
-                latin:keyOutputText=":-) "
-                latin:keyLabelFlags="hasPopupHint"
-                latin:moreKeys="!text/more_keys_for_smiley"
-                latin:backgroundType="functional" />
-        </case>
-    </switch>
     <key-style
         latin:styleName="spaceKeyStyle"
         latin:code="!code/key_space"
@@ -129,7 +119,8 @@
         latin:styleName="emojiKeyStyle"
         latin:code="!code/key_emoji"
         latin:keyIcon="!icon/emoji_key"
-        latin:keyActionFlags="noKeyPreview" />
+        latin:keyActionFlags="noKeyPreview"
+        latin:backgroundType="functional" />
     <key-style
         latin:styleName="tabKeyStyle"
         latin:code="!code/key_tab"
@@ -193,4 +184,11 @@
         latin:keyLabelFlags="hasPopupHint"
         latin:moreKeys="!text/more_keys_for_punctuation"
         latin:backgroundType="functional" />
+    <key-style
+        latin:styleName="comKeyStyle"
+        latin:keyLabel="!text/keylabel_for_popular_domain"
+        latin:keyLabelFlags="autoXScale|fontNormal|hasPopupHint|preserveCase"
+        latin:keyOutputText="!text/keylabel_for_popular_domain"
+        latin:moreKeys="!text/more_keys_for_popular_domain"
+        latin:backgroundType="functional" />
 </merge>
diff --git a/java/res/xml/key_styles_currency.xml b/java/res/xml/key_styles_currency.xml
index 76fe0e6..84c2abc 100644
--- a/java/res/xml/key_styles_currency.xml
+++ b/java/res/xml/key_styles_currency.xml
@@ -95,22 +95,26 @@
         <!-- fa: Persian (Rial and Afgahni)
              hi: Hindi (Indian Rupee)
              iw: Hebrew (New Sheqel)
+             lo: Lao (Kip)
              mn: Mongolian (Tugrik)
+             ne: Nepali (Nepalese Rupee)
              th: Thai (Baht)
              uk: Ukrainian (Hryvnia)
              vi: Vietnamese (Dong)  -->
         <!-- TODO: The currency sign of Turkish Lira was created in 2012 and assigned U+20BA for
              its unicode, although there is no font glyph for it as of November 2012. -->
+        <!-- TODO: The currency sign of Armenian Dram was created in 2012 and assigned U+058F for
+             its unicode, although there is no font glyph for it as of September 2013. -->
         <case
-            latin:languageCode="fa|hi|iw|mn|th|uk|vi"
+            latin:languageCode="fa|hi|iw|lo|mn|ne|th|uk|vi"
         >
             <!-- U+00A3: "£" POUND SIGN
                  U+20AC: "€" EURO SIGN
                  U+00A2: "¢" CENT SIGN -->
             <key-style
                 latin:styleName="currencyKeyStyle"
-                latin:keyLabel="!text/keylabel_for_currency_generic"
-                latin:moreKeys="!text/more_keys_for_currency_generic" />
+                latin:keyLabel="!text/keylabel_for_currency"
+                latin:moreKeys="!text/more_keys_for_currency" />
             <key-style
                 latin:styleName="moreCurrency1KeyStyle"
                 latin:keyLabel="&#x00A3;" />
diff --git a/java/res/xml/key_symbols_period.xml b/java/res/xml/key_symbols_period.xml
new file mode 100644
index 0000000..6efc9de
--- /dev/null
+++ b/java/res/xml/key_symbols_period.xml
@@ -0,0 +1,47 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+**
+** Copyright 2013, 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.
+*/
+-->
+
+<merge
+    xmlns:latin="http://schemas.android.com/apk/res/com.android.inputmethod.latin"
+>
+    <!-- U+2105: "℅" CARE OF
+         U+2122: "™" TRADE MARK SIGN
+         U+00AE: "®" REGISTERED SIGN
+         U+00A9: "©" COPYRIGHT SIGN
+         U+00A7: "§" SECTION SIGN
+         U+00B6: "¶" PILCROW SIGN
+         U+002C: "," COMMA
+         U+2022: "•" BULLET -->
+    <!-- U+00B0: "°" DEGREE SIGN
+         U+2032: "′" PRIME
+         U+2033: "″" DOUBLE PRIME
+         U+2191: "↑" UPWARDS ARROW
+         U+2193: "↓" DOWNWARDS ARROW
+         U+2190: "←" LEFTWARDS ARROW
+         U+2192: "→" RIGHTWARDS ARROW
+         U+2026: "…" HORIZONTAL ELLIPSIS -->
+    <!-- U+0394: "Δ" GREEK CAPITAL LETTER DELTA
+         U+03A0: "Π" GREEK CAPITAL LETTER PI
+         U+03C0: "π" GREEK SMALL LETTER PI -->
+    <Key
+        latin:keyLabel="."
+        latin:keyLabelFlags="hasPopupHint"
+        latin:moreKeys="!fixedColumnOrder!8,&#x2105;,&#x2122;,&#x00AE;,&#x00A9;,&#x00A7;,&#x00B6;,\\,,&#x2022;,&#x00B0;,&#x2032;,&#x2033;,&#x2191;,&#x2193;,&#x2190;,&#x2192;,&#x2026;,!text/more_keys_for_bullet,&#x0394;,&#x03A0;,&#x03C0;" />
+</merge>
diff --git a/java/res/xml/keyboard_layout_set_armenian_phonetic.xml b/java/res/xml/keyboard_layout_set_armenian_phonetic.xml
new file mode 100644
index 0000000..35bd43f
--- /dev/null
+++ b/java/res/xml/keyboard_layout_set_armenian_phonetic.xml
@@ -0,0 +1,42 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+**
+** Copyright 2013, 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.
+*/
+-->
+
+<KeyboardLayoutSet
+    xmlns:latin="http://schemas.android.com/apk/res/com.android.inputmethod.latin">
+    <Element
+        latin:elementName="alphabet"
+        latin:elementKeyboard="@xml/kbd_armenian_phonetic"
+        latin:enableProximityCharsCorrection="true" />
+    <Element
+        latin:elementName="symbols"
+        latin:elementKeyboard="@xml/kbd_symbols" />
+    <Element
+        latin:elementName="symbolsShifted"
+        latin:elementKeyboard="@xml/kbd_symbols_shift" />
+    <Element
+        latin:elementName="phone"
+        latin:elementKeyboard="@xml/kbd_phone" />
+    <Element
+        latin:elementName="phoneSymbols"
+        latin:elementKeyboard="@xml/kbd_phone_symbols" />
+    <Element
+        latin:elementName="number"
+        latin:elementKeyboard="@xml/kbd_number" />
+</KeyboardLayoutSet>
diff --git a/java/res/xml/keyboard_layout_set_azerty.xml b/java/res/xml/keyboard_layout_set_azerty.xml
index 4d144ed..38797f9 100644
--- a/java/res/xml/keyboard_layout_set_azerty.xml
+++ b/java/res/xml/keyboard_layout_set_azerty.xml
@@ -26,10 +26,10 @@
         latin:enableProximityCharsCorrection="true" />
     <Element
         latin:elementName="symbols"
-        latin:elementKeyboard="@xml/kbd_10_10_7_symbols" />
+        latin:elementKeyboard="@xml/kbd_symbols" />
     <Element
         latin:elementName="symbolsShifted"
-        latin:elementKeyboard="@xml/kbd_10_10_7_symbols_shift" />
+        latin:elementKeyboard="@xml/kbd_symbols_shift" />
     <Element
         latin:elementName="phone"
         latin:elementKeyboard="@xml/kbd_phone" />
diff --git a/java/res/xml/keyboard_layout_set_colemak.xml b/java/res/xml/keyboard_layout_set_colemak.xml
index c18f132..3061872 100644
--- a/java/res/xml/keyboard_layout_set_colemak.xml
+++ b/java/res/xml/keyboard_layout_set_colemak.xml
@@ -26,10 +26,10 @@
         latin:enableProximityCharsCorrection="true" />
     <Element
         latin:elementName="symbols"
-        latin:elementKeyboard="@xml/kbd_10_10_7_symbols" />
+        latin:elementKeyboard="@xml/kbd_symbols" />
     <Element
         latin:elementName="symbolsShifted"
-        latin:elementKeyboard="@xml/kbd_10_10_7_symbols_shift" />
+        latin:elementKeyboard="@xml/kbd_symbols_shift" />
     <Element
         latin:elementName="phone"
         latin:elementKeyboard="@xml/kbd_phone" />
diff --git a/java/res/xml/keyboard_layout_set_dvorak.xml b/java/res/xml/keyboard_layout_set_dvorak.xml
index eb8e0c5..31aeec5 100644
--- a/java/res/xml/keyboard_layout_set_dvorak.xml
+++ b/java/res/xml/keyboard_layout_set_dvorak.xml
@@ -26,10 +26,10 @@
         latin:enableProximityCharsCorrection="true" />
     <Element
         latin:elementName="symbols"
-        latin:elementKeyboard="@xml/kbd_10_10_7_symbols" />
+        latin:elementKeyboard="@xml/kbd_symbols" />
     <Element
         latin:elementName="symbolsShifted"
-        latin:elementKeyboard="@xml/kbd_10_10_7_symbols_shift" />
+        latin:elementKeyboard="@xml/kbd_symbols_shift" />
     <Element
         latin:elementName="phone"
         latin:elementKeyboard="@xml/kbd_phone" />
diff --git a/java/res/xml/keyboard_layout_set_emoji.xml b/java/res/xml/keyboard_layout_set_emoji.xml
new file mode 100644
index 0000000..98e6b6b
--- /dev/null
+++ b/java/res/xml/keyboard_layout_set_emoji.xml
@@ -0,0 +1,44 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+**
+** Copyright 2013, 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.
+*/
+-->
+
+<KeyboardLayoutSet
+    xmlns:latin="http://schemas.android.com/apk/res/com.android.inputmethod.latin">
+    <Element
+        latin:elementName="emojiRecents"
+        latin:elementKeyboard="@xml/kbd_emoji_recents" />
+    <Element
+        latin:elementName="emojiCategory1"
+        latin:elementKeyboard="@xml/kbd_emoji_category1" />
+    <Element
+        latin:elementName="emojiCategory2"
+        latin:elementKeyboard="@xml/kbd_emoji_category2" />
+    <Element
+        latin:elementName="emojiCategory3"
+        latin:elementKeyboard="@xml/kbd_emoji_category3" />
+    <Element
+        latin:elementName="emojiCategory4"
+        latin:elementKeyboard="@xml/kbd_emoji_category4" />
+    <Element
+        latin:elementName="emojiCategory5"
+        latin:elementKeyboard="@xml/kbd_emoji_category5" />
+    <Element
+        latin:elementName="emojiCategory6"
+        latin:elementKeyboard="@xml/kbd_emoji_category6" />
+</KeyboardLayoutSet>
diff --git a/java/res/xml/keyboard_layout_set_hebrew.xml b/java/res/xml/keyboard_layout_set_hebrew.xml
index 212816d..d5b25b3 100644
--- a/java/res/xml/keyboard_layout_set_hebrew.xml
+++ b/java/res/xml/keyboard_layout_set_hebrew.xml
@@ -26,10 +26,10 @@
         latin:enableProximityCharsCorrection="true" />
     <Element
         latin:elementName="symbols"
-        latin:elementKeyboard="@xml/kbd_10_10_7_symbols" />
+        latin:elementKeyboard="@xml/kbd_symbols" />
     <Element
         latin:elementName="symbolsShifted"
-        latin:elementKeyboard="@xml/kbd_10_10_7_symbols_shift" />
+        latin:elementKeyboard="@xml/kbd_symbols_shift" />
     <Element
         latin:elementName="phone"
         latin:elementKeyboard="@xml/kbd_phone" />
diff --git a/java/res/xml/keyboard_layout_set_khmer.xml b/java/res/xml/keyboard_layout_set_khmer.xml
new file mode 100644
index 0000000..181f98b
--- /dev/null
+++ b/java/res/xml/keyboard_layout_set_khmer.xml
@@ -0,0 +1,58 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+**
+** Copyright 2013, 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.
+*/
+-->
+
+<KeyboardLayoutSet
+    xmlns:latin="http://schemas.android.com/apk/res/com.android.inputmethod.latin">
+    <Element
+        latin:elementName="alphabet"
+        latin:elementKeyboard="@xml/kbd_khmer"
+        latin:enableProximityCharsCorrection="true" />
+    <Element
+        latin:elementName="alphabetAutomaticShifted"
+        latin:elementKeyboard="@xml/kbd_khmer"
+        latin:enableProximityCharsCorrection="true" />
+    <!-- On these shifted alphabet layouts the proximity characters correction should be disabled
+         because the letters on these layouts aren't the ones in different case of the above
+         unshifted layouts. -->
+    <Element
+        latin:elementName="alphabetManualShifted"
+        latin:elementKeyboard="@xml/kbd_khmer" />
+    <Element
+        latin:elementName="alphabetShiftLocked"
+        latin:elementKeyboard="@xml/kbd_khmer" />
+    <Element
+        latin:elementName="alphabetShiftLockShifted"
+        latin:elementKeyboard="@xml/kbd_khmer" />
+    <Element
+        latin:elementName="symbols"
+        latin:elementKeyboard="@xml/kbd_symbols" />
+    <Element
+        latin:elementName="symbolsShifted"
+        latin:elementKeyboard="@xml/kbd_symbols_shift" />
+    <Element
+        latin:elementName="phone"
+        latin:elementKeyboard="@xml/kbd_phone" />
+    <Element
+        latin:elementName="phoneSymbols"
+        latin:elementKeyboard="@xml/kbd_phone_symbols" />
+    <Element
+        latin:elementName="number"
+        latin:elementKeyboard="@xml/kbd_number" />
+</KeyboardLayoutSet>
diff --git a/java/res/xml/keyboard_layout_set_lao.xml b/java/res/xml/keyboard_layout_set_lao.xml
new file mode 100644
index 0000000..2ffde45
--- /dev/null
+++ b/java/res/xml/keyboard_layout_set_lao.xml
@@ -0,0 +1,58 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+**
+** Copyright 2013, 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.
+*/
+-->
+
+<KeyboardLayoutSet
+    xmlns:latin="http://schemas.android.com/apk/res/com.android.inputmethod.latin">
+    <Element
+        latin:elementName="alphabet"
+        latin:elementKeyboard="@xml/kbd_lao"
+        latin:enableProximityCharsCorrection="true" />
+    <Element
+        latin:elementName="alphabetAutomaticShifted"
+        latin:elementKeyboard="@xml/kbd_lao"
+        latin:enableProximityCharsCorrection="true" />
+    <!-- On these shifted alphabet layouts the proximity characters correction should be disabled
+         because the letters on these layouts aren't the ones in different case of the above
+         unshifted layouts. -->
+    <Element
+        latin:elementName="alphabetManualShifted"
+        latin:elementKeyboard="@xml/kbd_lao" />
+    <Element
+        latin:elementName="alphabetShiftLocked"
+        latin:elementKeyboard="@xml/kbd_lao" />
+    <Element
+        latin:elementName="alphabetShiftLockShifted"
+        latin:elementKeyboard="@xml/kbd_lao" />
+    <Element
+        latin:elementName="symbols"
+        latin:elementKeyboard="@xml/kbd_symbols" />
+    <Element
+        latin:elementName="symbolsShifted"
+        latin:elementKeyboard="@xml/kbd_symbols_shift" />
+    <Element
+        latin:elementName="phone"
+        latin:elementKeyboard="@xml/kbd_phone" />
+    <Element
+        latin:elementName="phoneSymbols"
+        latin:elementKeyboard="@xml/kbd_phone_symbols" />
+    <Element
+        latin:elementName="number"
+        latin:elementKeyboard="@xml/kbd_number" />
+</KeyboardLayoutSet>
diff --git a/java/res/xml/keyboard_layout_set_nepali_romanized.xml b/java/res/xml/keyboard_layout_set_nepali_romanized.xml
new file mode 100644
index 0000000..fbbc6a5
--- /dev/null
+++ b/java/res/xml/keyboard_layout_set_nepali_romanized.xml
@@ -0,0 +1,58 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+**
+** Copyright 2013, 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.
+*/
+-->
+
+<KeyboardLayoutSet
+    xmlns:latin="http://schemas.android.com/apk/res/com.android.inputmethod.latin">
+    <Element
+        latin:elementName="alphabet"
+        latin:elementKeyboard="@xml/kbd_nepali_romanized"
+        latin:enableProximityCharsCorrection="true" />
+    <Element
+        latin:elementName="alphabetAutomaticShifted"
+        latin:elementKeyboard="@xml/kbd_nepali_romanized"
+        latin:enableProximityCharsCorrection="true" />
+    <!-- On these shifted alphabet layouts the proximity characters correction should be disabled
+         because the letters on these layouts aren't the ones in different case of the above
+         unshifted layouts. -->
+    <Element
+        latin:elementName="alphabetManualShifted"
+        latin:elementKeyboard="@xml/kbd_nepali_romanized" />
+    <Element
+        latin:elementName="alphabetShiftLocked"
+        latin:elementKeyboard="@xml/kbd_nepali_romanized" />
+    <Element
+        latin:elementName="alphabetShiftLockShifted"
+        latin:elementKeyboard="@xml/kbd_nepali_romanized" />
+    <Element
+        latin:elementName="symbols"
+        latin:elementKeyboard="@xml/kbd_symbols" />
+    <Element
+        latin:elementName="symbolsShifted"
+        latin:elementKeyboard="@xml/kbd_symbols_shift" />
+    <Element
+        latin:elementName="phone"
+        latin:elementKeyboard="@xml/kbd_phone" />
+    <Element
+        latin:elementName="phoneSymbols"
+        latin:elementKeyboard="@xml/kbd_phone_symbols" />
+    <Element
+        latin:elementName="number"
+        latin:elementKeyboard="@xml/kbd_number" />
+</KeyboardLayoutSet>
diff --git a/java/res/xml/keyboard_layout_set_nepali_traditional.xml b/java/res/xml/keyboard_layout_set_nepali_traditional.xml
new file mode 100644
index 0000000..4a3b601
--- /dev/null
+++ b/java/res/xml/keyboard_layout_set_nepali_traditional.xml
@@ -0,0 +1,58 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+**
+** Copyright 2013, 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.
+*/
+-->
+
+<KeyboardLayoutSet
+    xmlns:latin="http://schemas.android.com/apk/res/com.android.inputmethod.latin">
+    <Element
+        latin:elementName="alphabet"
+        latin:elementKeyboard="@xml/kbd_nepali_traditional"
+        latin:enableProximityCharsCorrection="true" />
+    <Element
+        latin:elementName="alphabetAutomaticShifted"
+        latin:elementKeyboard="@xml/kbd_nepali_traditional"
+        latin:enableProximityCharsCorrection="true" />
+    <!-- On these shifted alphabet layouts the proximity characters correction should be disabled
+         because the letters on these layouts aren't the ones in different case of the above
+         unshifted layouts. -->
+    <Element
+        latin:elementName="alphabetManualShifted"
+        latin:elementKeyboard="@xml/kbd_nepali_traditional" />
+    <Element
+        latin:elementName="alphabetShiftLocked"
+        latin:elementKeyboard="@xml/kbd_nepali_traditional" />
+    <Element
+        latin:elementName="alphabetShiftLockShifted"
+        latin:elementKeyboard="@xml/kbd_nepali_traditional" />
+    <Element
+        latin:elementName="symbols"
+        latin:elementKeyboard="@xml/kbd_symbols" />
+    <Element
+        latin:elementName="symbolsShifted"
+        latin:elementKeyboard="@xml/kbd_symbols_shift" />
+    <Element
+        latin:elementName="phone"
+        latin:elementKeyboard="@xml/kbd_phone" />
+    <Element
+        latin:elementName="phoneSymbols"
+        latin:elementKeyboard="@xml/kbd_phone_symbols" />
+    <Element
+        latin:elementName="number"
+        latin:elementKeyboard="@xml/kbd_number" />
+</KeyboardLayoutSet>
diff --git a/java/res/xml/keyboard_layout_set_pcqwerty.xml b/java/res/xml/keyboard_layout_set_pcqwerty.xml
index 9367ed0..67fbd91 100644
--- a/java/res/xml/keyboard_layout_set_pcqwerty.xml
+++ b/java/res/xml/keyboard_layout_set_pcqwerty.xml
@@ -25,12 +25,6 @@
         latin:elementKeyboard="@xml/kbd_pcqwerty"
         latin:enableProximityCharsCorrection="true" />
     <Element
-        latin:elementName="symbols"
-        latin:elementKeyboard="@xml/kbd_pcqwerty_symbols" />
-    <Element
-        latin:elementName="symbolsShifted"
-        latin:elementKeyboard="@xml/kbd_pcqwerty_symbols" />
-    <Element
         latin:elementName="phone"
         latin:elementKeyboard="@xml/kbd_phone" />
     <Element
diff --git a/java/res/xml/keyboard_layout_set_spanish.xml b/java/res/xml/keyboard_layout_set_spanish.xml
index 57cef52..c454de3 100644
--- a/java/res/xml/keyboard_layout_set_spanish.xml
+++ b/java/res/xml/keyboard_layout_set_spanish.xml
@@ -26,10 +26,10 @@
         latin:enableProximityCharsCorrection="true" />
     <Element
         latin:elementName="symbols"
-        latin:elementKeyboard="@xml/kbd_10_10_7_symbols" />
+        latin:elementKeyboard="@xml/kbd_symbols" />
     <Element
         latin:elementName="symbolsShifted"
-        latin:elementKeyboard="@xml/kbd_10_10_7_symbols_shift" />
+        latin:elementKeyboard="@xml/kbd_symbols_shift" />
     <Element
         latin:elementName="phone"
         latin:elementKeyboard="@xml/kbd_phone" />
diff --git a/java/res/xml/keyboard_layout_set_thai.xml b/java/res/xml/keyboard_layout_set_thai.xml
index 94713e3..b8f9997 100644
--- a/java/res/xml/keyboard_layout_set_thai.xml
+++ b/java/res/xml/keyboard_layout_set_thai.xml
@@ -42,10 +42,10 @@
         latin:elementKeyboard="@xml/kbd_thai" />
     <Element
         latin:elementName="symbols"
-        latin:elementKeyboard="@xml/kbd_thai_symbols" />
+        latin:elementKeyboard="@xml/kbd_symbols" />
     <Element
         latin:elementName="symbolsShifted"
-        latin:elementKeyboard="@xml/kbd_thai_symbols_shift" />
+        latin:elementKeyboard="@xml/kbd_symbols_shift" />
     <Element
         latin:elementName="phone"
         latin:elementKeyboard="@xml/kbd_phone" />
diff --git a/java/res/xml/keys_comma_period.xml b/java/res/xml/keys_comma_period.xml
new file mode 100644
index 0000000..02b46c2
--- /dev/null
+++ b/java/res/xml/keys_comma_period.xml
@@ -0,0 +1,103 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+**
+** Copyright 2012, 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.
+*/
+-->
+
+<merge
+    xmlns:latin="http://schemas.android.com/apk/res/com.android.inputmethod.latin"
+>
+    <switch>
+        <case
+            latin:mode="email|url"
+        >
+            <Key
+                latin:keyLabel="."
+                latin:keyHintLabel="_"
+                latin:moreKeys="_"
+                latin:backgroundType="functional"
+                latin:keyStyle="hasShiftedLetterHintStyle" />
+            <Key
+                latin:keyLabel=","
+                latin:keyHintLabel="-"
+                latin:moreKeys="-"
+                latin:backgroundType="functional"
+                latin:keyStyle="hasShiftedLetterHintStyle" />
+        </case>
+        <case
+            latin:languageCode="ar"
+        >
+            <Key
+                latin:keyLabel="!text/keylabel_for_apostrophe"
+                latin:keyHintLabel="!text/keyhintlabel_for_apostrophe"
+                latin:moreKeys="!text/more_keys_for_apostrophe"
+                latin:backgroundType="functional"
+                latin:keyStyle="hasShiftedLetterHintStyle" />
+            <Key
+                latin:keyLabel="."
+                latin:keyHintLabel="!text/keyhintlabel_for_arabic_diacritics"
+                latin:keyLabelFlags="hasPopupHint"
+                latin:moreKeys="!text/more_keys_for_arabic_diacritics"
+                latin:backgroundType="functional"
+                latin:keyStyle="hasShiftedLetterHintStyle" />
+        </case>
+        <case
+            latin:languageCode="fa"
+        >
+            <Key
+                latin:keyLabel="!text/keylabel_for_apostrophe"
+                latin:keyHintLabel="!text/keyhintlabel_for_apostrophe"
+                latin:keyLabelFlags="hasPopupHint"
+                latin:moreKeys="!text/more_keys_for_apostrophe"
+                latin:backgroundType="functional"
+                latin:keyStyle="hasShiftedLetterHintStyle" />
+            <Key
+                latin:keyLabel="."
+                latin:keyHintLabel="!text/keyhintlabel_for_arabic_diacritics"
+                latin:keyLabelFlags="hasPopupHint"
+                latin:moreKeys="!text/more_keys_for_arabic_diacritics"
+                latin:backgroundType="functional"
+                latin:keyStyle="hasShiftedLetterHintStyle" />
+        </case>
+        <case
+            latin:languageCode="hy"
+        >
+            <!-- U+0589: "։" ARMENIAN FULL STOP -->
+            <Key
+                latin:keyLabel="&#x0589;"
+                latin:keyLabelFlags="hasPopupHint"
+                latin:backgroundType="functional"
+                latin:moreKeys="!text/more_keys_for_punctuation" />
+            <!-- U+055D: "՝" ARMENIAN COMMA -->
+            <Key
+                latin:keyLabel="&#x055D;"
+                latin:backgroundType="functional" />
+        </case>
+        <default>
+            <Key
+                latin:keyLabel="."
+                latin:keyHintLabel="!text/keyhintlabel_for_tablet_period"
+                latin:backgroundType="functional"
+                latin:moreKeys="!text/more_keys_for_tablet_period" />
+            <Key
+                latin:keyLabel="!text/keylabel_for_tablet_comma"
+                latin:keyHintLabel="!text/keyhintlabel_for_tablet_comma"
+                latin:backgroundType="functional"
+                latin:moreKeys="!text/more_keys_for_tablet_comma" />
+        </default>
+    </switch>
+</merge>
diff --git a/java/res/xml/keys_hindi1_left5.xml b/java/res/xml/keys_hindi1_left5.xml
deleted file mode 100644
index 8757afe..0000000
--- a/java/res/xml/keys_hindi1_left5.xml
+++ /dev/null
@@ -1,85 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-/*
-**
-** Copyright 2013, 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.
-*/
--->
-
-<!-- The code point U+25CC for key label is needed because the font rendering system prior to
-     API version 16 can't automatically render dotted circle for incomplete combining letter
-     of Hindi. The files named res/xml/{key,keys}_hindi*.xml have this U+25CC hack, although the
-     counterpart files named res/xml-v16/{key,keys}_hindi*.xml don't have this hack. -->
-<merge
-    xmlns:latin="http://schemas.android.com/apk/res/com.android.inputmethod.latin"
->
-    <!-- U+25CC: "◌" DOTTED CIRCLE
-         U+094C: "ौ" DEVANAGARI VOWEL SIGN AU
-         U+094C/U+0902: "ौं" DEVANAGARI VOWEL SIGN AU/DEVANAGARI SIGN ANUSVARA
-         U+0967: "१" DEVANAGARI DIGIT ONE -->
-    <Key
-        latin:keyLabel="&#x25CC;&#x094C;"
-        latin:code="0x094C"
-        latin:moreKeys="&#x25CC;&#x094C;&#x0902;|&#x094C;&#x0902;,%"
-        latin:keyHintLabel="1"
-        latin:additionalMoreKeys="&#x0967;,1"
-        latin:keyLabelFlags="fontNormal|followKeyLetterRatio" />
-    <!-- U+25CC: "◌" DOTTED CIRCLE
-         U+0948: "ै" DEVANAGARI VOWEL SIGN AI
-         U+0948/U+0902: "ैं" DEVANAGARI VOWEL SIGN AI/DEVANAGARI SIGN ANUSVARA
-         U+0968: "२" DEVANAGARI DIGIT TWO -->
-    <Key
-        latin:keyLabel="&#x25CC;&#x0948;"
-        latin:code="0x0948"
-        latin:moreKeys="&#x25CC;&#x0948;&#x0902;|&#x0948;&#x0902;,%"
-        latin:keyHintLabel="2"
-        latin:additionalMoreKeys="&#x0968;,2"
-        latin:keyLabelFlags="fontNormal|followKeyLetterRatio" />
-    <!-- U+25CC: "◌" DOTTED CIRCLE
-         U+093E: "ा" DEVANAGARI VOWEL SIGN AA
-         U+093E/U+0902: "ां" DEVANAGARI VOWEL SIGN AA/DEVANAGARI SIGN ANUSVARA
-         U+093E/U+0901: "ाँ" DEVANAGARI VOWEL SIGN AA/DEVANAGARI SIGN CANDRABINDU
-         U+0969: "३" DEVANAGARI DIGIT THREE -->
-    <Key
-        latin:keyLabel="&#x25CC;&#x093E;"
-        latin:code="0x093E"
-        latin:moreKeys="&#x25CC;&#x093E;&#x0902;|&#x093E;&#x0902;,&#x25CC;&#x093E;&#x0901;|&#x093E;&#x0901;,%"
-        latin:keyHintLabel="3"
-        latin:additionalMoreKeys="&#x0969;,3"
-        latin:keyLabelFlags="fontNormal|followKeyLetterRatio" />
-    <!-- U+25CC: "◌" DOTTED CIRCLE
-         U+0940: "ी" DEVANAGARI VOWEL SIGN II
-         U+0940/U+0902: "ीं" DEVANAGARI VOWEL SIGN II/DEVANAGARI SIGN ANUSVARA
-         U+096A: "४" DEVANAGARI DIGIT FOUR -->
-    <Key
-        latin:keyLabel="&#x25CC;&#x0940;"
-        latin:code="0x0940"
-        latin:moreKeys="&#x25CC;&#x0940;&#x0902;|&#x0940;&#x0902;,%"
-        latin:keyHintLabel="4"
-        latin:additionalMoreKeys="&#x096A;,4"
-        latin:keyLabelFlags="fontNormal|followKeyLetterRatio" />
-    <!-- U+25CC: "◌" DOTTED CIRCLE
-         U+0942: "ू" DEVANAGARI VOWEL SIGN UU
-         U+0942/U+0902: "ूं" DEVANAGARI VOWEL SIGN UU/DEVANAGARI SIGN ANUSVARA
-         U+0942/U+0901: "ूँ" DEVANAGARI VOWEL SIGN UU/DEVANAGARI SIGN CANDRABINDU
-         U+096B: "५" DEVANAGARI DIGIT FIVE -->
-    <Key
-        latin:keyLabel="&#x25CC;&#x0942;"
-        latin:code="0x0942"
-        latin:moreKeys="&#x25CC;&#x0942;&#x0902;|&#x0942;&#x0902;,&#x25CC;&#x0942;&#x0901;|&#x0942;&#x0901;,%"
-        latin:keyHintLabel="5"
-        latin:additionalMoreKeys="&#x096B;,5"
-        latin:keyLabelFlags="fontNormal|followKeyLetterRatio" />
-</merge>
diff --git a/java/res/xml/keys_hindi2_left5.xml b/java/res/xml/keys_hindi2_left5.xml
deleted file mode 100644
index 4c3a5e0..0000000
--- a/java/res/xml/keys_hindi2_left5.xml
+++ /dev/null
@@ -1,69 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-/*
-**
-** Copyright 2013, 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.
-*/
--->
-
-<!-- The code point U+25CC for key label is needed because the font rendering system prior to
-     API version 16 can't automatically render dotted circle for incomplete combining letter
-     of Hindi. The files named res/xml/{key,keys}_hindi*.xml have this U+25CC hack, although the
-     counterpart files named res/xml-v16/{key,keys}_hindi*.xml don't have this hack. -->
-<merge
-    xmlns:latin="http://schemas.android.com/apk/res/com.android.inputmethod.latin"
->
-    <!-- U+25CC: "◌" DOTTED CIRCLE
-         U+094B: "ो" DEVANAGARI VOWEL SIGN O
-         U+094B/U+0902: "қं" DEVANAGARI VOWEL SIGN O/DEVANAGARI SIGN ANUSVARA
-         U+0949: "ॉ" DEVANAGARI VOWEL SIGN CANDRA O
-         U+094A: "ॊ" DEVANAGARI VOWEL SIGN SHORT O -->
-    <Key
-        latin:keyLabel="&#x25CC;&#x094B;"
-        latin:code="0x094B"
-        latin:moreKeys="&#x25CC;&#x094B;&#x0902;|&#x094B;&#x0902;,&#x25CC;&#x0949;,&#x094A;|&#x0949;,&#x094A;"
-        latin:keyLabelFlags="fontNormal|followKeyLetterRatio" />
-    <!-- U+25CC: "◌" DOTTED CIRCLE
-         U+0947: "े" DEVANAGARI VOWEL SIGN E
-         U+0947/U+0902: "ें" DEVANAGARI VOWEL SIGN E/DEVANAGARI SIGN ANUSVARA -->
-    <Key
-        latin:keyLabel="&#x25CC;&#x0947;"
-        latin:code="0x0947"
-        latin:moreKeys="&#x25CC;&#x0947;&#x0902;|&#x0947;&#x0902;"
-        latin:keyLabelFlags="fontNormal|followKeyLetterRatio" />
-    <!-- U+25CC: "◌" DOTTED CIRCLE
-         U+094D: "्" DEVANAGARI SIGN VIRAMA -->
-    <Key
-        latin:keyLabel="&#x25CC;&#x094D;"
-        latin:code="0x094D"
-        latin:keyLabelFlags="fontNormal|followKeyLetterRatio" />
-    <!-- U+25CC: "◌" DOTTED CIRCLE
-         U+093F: "ि" DEVANAGARI VOWEL SIGN I
-         U+093F/U+0902: "िं" DEVANAGARI VOWEL SIGN I/DEVANAGARI SIGN ANUSVARA -->
-    <Key
-        latin:keyLabel="&#x093F;&#x25CC;"
-        latin:code="0x093F"
-        latin:moreKeys="&#x093F;&#x25CC;&#x0902;|&#x093F;&#x0902;"
-        latin:keyLabelFlags="fontNormal|followKeyLetterRatio" />
-    <!-- U+25CC: "◌" DOTTED CIRCLE
-         U+0941: "ु" DEVANAGARI VOWEL SIGN U
-         U+0941/U+0902: "ुं" DEVANAGARI VOWEL SIGN U/DEVANAGARI SIGN ANUSVARA
-         U+0941/U+0901: "ुँ" DEVANAGARI VOWEL SIGN U/DEVANAGARI SIGN CANDRABINDU -->
-    <Key
-        latin:keyLabel="&#x25CC;&#x0941;"
-        latin:code="0x0941"
-        latin:moreKeys="&#x25CC;&#x0941;&#x0902;|&#x0941;&#x0902;,&#x25CC;&#x0941;&#x0901;|&#x0941;&#x0901;"
-        latin:keyLabelFlags="fontNormal|followKeyLetterRatio" />
-</merge>
diff --git a/java/res/xml/keys_hindi3_left2.xml b/java/res/xml/keys_hindi3_left2.xml
deleted file mode 100644
index 4f1ad16..0000000
--- a/java/res/xml/keys_hindi3_left2.xml
+++ /dev/null
@@ -1,40 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-/*
-**
-** Copyright 2013, 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.
-*/
--->
-
-<!-- The code point U+25CC for key label is needed because the font rendering system prior to
-     API version 16 can't automatically render dotted circle for incomplete combining letter
-     of Hindi. The files named res/xml/{key,keys}_hindi*.xml have this U+25CC hack, although the
-     counterpart files named res/xml-v16/{key,keys}_hindi*.xml don't have this hack. -->
-<merge
-    xmlns:latin="http://schemas.android.com/apk/res/com.android.inputmethod.latin"
->
-    <!-- U+25CC: "◌" DOTTED CIRCLE
-         U+0949: "ॉ" DEVANAGARI VOWEL SIGN CANDRA O -->
-    <Key
-        latin:keyLabel="&#x25CC;&#x0949;"
-        latin:code="0x0949"
-        latin:keyLabelFlags="fontNormal|followKeyLetterRatio" />
-    <!-- U+25CC: "◌" DOTTED CIRCLE
-         U+0902: "ं" DEVANAGARI SIGN ANUSVARA -->
-    <Key
-        latin:keyLabel="&#x25CC;&#x0902;"
-        latin:code="0x0902"
-        latin:keyLabelFlags="fontNormal|followKeyLetterRatio" />
-</merge>
diff --git a/java/res/xml/keys_less_greater.xml b/java/res/xml/keys_less_greater.xml
index bc9ecdf..56d0727 100644
--- a/java/res/xml/keys_less_greater.xml
+++ b/java/res/xml/keys_less_greater.xml
@@ -30,20 +30,24 @@
             <Key
                 latin:keyLabel="&#x00AB;"
                 latin:code="0x00BB"
+                latin:backgroundType="functional"
                 latin:moreKeys="!text/more_keys_for_less_than" />
             <Key
                 latin:keyLabel="&#x00BB;"
                 latin:code="0x00AB"
+                latin:backgroundType="functional"
                 latin:moreKeys="!text/more_keys_for_greater_than" />
         </case>
         <default>
             <Key
                 latin:keyLabel="&lt;"
                 latin:code="!code/key_less_than"
+                latin:backgroundType="functional"
                 latin:moreKeys="!text/more_keys_for_less_than" />
             <Key
                 latin:keyLabel="&gt;"
                 latin:code="!code/key_greater_than"
+                latin:backgroundType="functional"
                 latin:moreKeys="!text/more_keys_for_greater_than" />
         </default>
     </switch>
diff --git a/java/res/xml/keys_pcqwerty2_right3.xml b/java/res/xml/keys_pcqwerty2_right3.xml
index 2065e6b..6f86477 100644
--- a/java/res/xml/keys_pcqwerty2_right3.xml
+++ b/java/res/xml/keys_pcqwerty2_right3.xml
@@ -27,25 +27,22 @@
         >
             <Key
                 latin:keyLabel="["
-                latin:moreKeys="{" />
+                latin:additionalMoreKeys="{" />
             <Key
                 latin:keyLabel="]"
-                latin:moreKeys="}" />
-            <!-- U+00A6: "¦" BROKEN BAR -->
+                latin:additionalMoreKeys="}" />
             <Key
                 latin:keyLabel="\\"
-                latin:moreKeys="\\|,&#x00A6;" />
+                latin:additionalMoreKeys="\\|" />
         </case>
-        <!-- keyboardLayoutSetElement="alphabet*Shifted|symbols*" -->
+        <!-- keyboardLayoutSetElement="alphabetManualShifted|alphabetShiftLocked|alphabetShiftLockShifted" -->
         <default>
             <Key
                 latin:keyLabel="{" />
             <Key
                 latin:keyLabel="}" />
-            <!-- U+00A6: "¦" BROKEN BAR -->
             <Key
-                latin:keyLabel="|"
-                latin:moreKeys="&#x00A6;" />
+                latin:keyLabel="|" />
         </default>
     </switch>
 </merge>
diff --git a/java/res/xml/keys_pcqwerty3_right2.xml b/java/res/xml/keys_pcqwerty3_right2.xml
index aa150af..8da145b 100644
--- a/java/res/xml/keys_pcqwerty3_right2.xml
+++ b/java/res/xml/keys_pcqwerty3_right2.xml
@@ -27,12 +27,13 @@
         >
             <Key
                 latin:keyLabel=";"
-                latin:moreKeys=":" />
+                latin:additionalMoreKeys=":" />
             <Key
                 latin:keyLabel="\'"
-                latin:moreKeys="!fixedColumnOrder!4,!text/double_quotes,&quot;,!text/single_quotes" />
+                latin:additionalMoreKeys="&quot;"
+                latin:moreKeys="!fixedColumnOrder!4,!text/double_quotes,%,!text/single_quotes" />
         </case>
-        <!-- keyboardLayoutSetElement="alphabet*Shifted|symbols*" -->
+        <!-- keyboardLayoutSetElement="alphabetManualShifted|alphabetShiftLocked|alphabetShiftLockShifted" -->
         <default>
             <Key
                 latin:keyLabel=":" />
diff --git a/java/res/xml/keys_pcqwerty4_right3.xml b/java/res/xml/keys_pcqwerty4_right3.xml
index 7795b3d..e6084cb 100644
--- a/java/res/xml/keys_pcqwerty4_right3.xml
+++ b/java/res/xml/keys_pcqwerty4_right3.xml
@@ -27,16 +27,16 @@
         >
             <Key
                 latin:keyLabel=","
-                latin:moreKeys="&lt;" />
+                latin:additionalMoreKeys="&lt;" />
             <Key
                 latin:keyLabel="."
-                latin:moreKeys="&gt;" />
-            <!-- U+00BF: "¿" INVERTED QUESTION MARK -->
+                latin:additionalMoreKeys="&gt;" />
             <Key
                 latin:keyLabel="/"
-                latin:moreKeys="\?,&#x00BF;" />
+                latin:additionalMoreKeys="\?"
+                latin:moreKeys="!text/more_keys_for_symbols_question" />
         </case>
-        <!-- keyboardLayoutSetElement="alphabet*Shifted|symbols*" -->
+        <!-- keyboardLayoutSetElement="alphabetManualShifted|alphabetShiftLocked|alphabetShiftLockShifted" -->
         <default>
             <!-- U+2039: "‹" SINGLE LEFT-POINTING ANGLE QUOTATION MARK
                  U+203A: "›" SINGLE RIGHT-POINTING ANGLE QUOTATION MARK
@@ -50,10 +50,9 @@
             <Key
                 latin:keyLabel="&gt;"
                 latin:moreKeys="!fixedColumnOrder!3,&#x203A;,&#x2265;,&#x00BB;" />
-            <!-- U+00BF: "¿" INVERTED QUESTION MARK -->
             <Key
                 latin:keyLabel="\?"
-                latin:moreKeys="&#x00BF;" />
+                latin:moreKeys="!text/more_keys_for_symbols_question" />
         </default>
     </switch>
 </merge>
diff --git a/java/res/xml/keys_pcqwerty_symbols2.xml b/java/res/xml/keys_pcqwerty_symbols2.xml
deleted file mode 100644
index d0ea984..0000000
--- a/java/res/xml/keys_pcqwerty_symbols2.xml
+++ /dev/null
@@ -1,59 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-/*
-**
-** Copyright 2012, 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.
-*/
--->
-
-<merge
-    xmlns:latin="http://schemas.android.com/apk/res/com.android.inputmethod.latin"
->
-    <!-- U+2022: "•" BULLET -->
-    <Key
-        latin:keyLabel="&#x2022;"
-        latin:moreKeys="!text/more_keys_for_bullet" />
-    <!-- U+00B1: "±" PLUS-MINUS SIGN -->
-    <Key
-        latin:keyLabel="&#x00B1;" />
-    <!-- U+00AC: "¬" NOT SIGN -->
-    <Key
-        latin:keyLabel="&#x00AC;" />
-    <!-- U+00A6: "¦" BROKEN BAR -->
-    <Key
-        latin:keyLabel="&#x00A6;" />
-    <!-- U+221A: "√" SQUARE ROOT -->
-    <Key
-        latin:keyLabel="&#x221A;" />
-    <!-- U+03C0: "π" GREEK SMALL LETTER PI
-         U+03A0: "Π" GREEK CAPITAL LETTER PI -->
-    <Key
-        latin:keyLabel="&#x03C0;"
-        latin:moreKeys="&#x03A0;" />
-    <!-- U+03CC: "σ" GREEK SMALL LETTER SIGMA
-         U+03A3: "Σ" GREEK CAPITAL LETTER SIGMA -->
-    <Key
-        latin:keyLabel="&#x03C3;"
-        latin:moreKeys="&#x03A3;" />
-    <!-- U+00B5: "µ" MICRO SIGN -->
-    <Key
-        latin:keyLabel="&#x00B5;" />
-    <!-- U+00F7: "÷" DIVISION SIGN -->
-    <Key
-        latin:keyLabel="&#x00F7;" />
-    <!-- U+00D7: "×" MULTIPLICATION SIGN -->
-    <Key
-        latin:keyLabel="&#x00D7;" />
-</merge>
diff --git a/java/res/xml/keys_pcqwerty_symbols3.xml b/java/res/xml/keys_pcqwerty_symbols3.xml
deleted file mode 100644
index 35279de..0000000
--- a/java/res/xml/keys_pcqwerty_symbols3.xml
+++ /dev/null
@@ -1,54 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-/*
-**
-** Copyright 2012, 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.
-*/
--->
-
-<merge
-    xmlns:latin="http://schemas.android.com/apk/res/com.android.inputmethod.latin"
->
-    <!-- U+00A3: "£" POUND SIGN; -->
-    <Key
-        latin:keyLabel="&#x00A3;" />
-    <!-- U+00A2: "¢" CENT SIGN -->
-    <Key
-        latin:keyLabel="&#x00A2;" />
-    <!-- U+20AC: "€" EURO SIGN -->
-    <Key
-        latin:keyLabel="&#x20AC;" />
-    <!-- U+00A5: "¥" YEN SIGN -->
-    <Key
-        latin:keyLabel="&#x00A5;" />
-    <!-- U+00A4: "¤" CURRENCY SIGN -->
-    <Key
-        latin:keyLabel="&#x00A4;" />
-    <!-- U+00B0: "°" DEGREE SIGN
-         U+2032: "′" PRIME
-         U+2033: "″" DOUBLE PRIME -->
-    <Key
-        latin:keyLabel="&#x00B0;"
-        latin:moreKeys="&#x2032;,&#x2033;" />
-    <!-- U+2260: "≠" NOT EQUAL TO -->
-    <Key
-        latin:keyLabel="&#x2260;" />
-    <!-- U+2248: "≈" ALMOST EQUAL TO -->
-    <Key
-        latin:keyLabel="&#x2248;" />
-    <!-- U+221E: "∞" INFINITY -->
-    <Key
-        latin:keyLabel="&#x221E;" />
-</merge>
diff --git a/java/res/xml/keys_pcqwerty_symbols4.xml b/java/res/xml/keys_pcqwerty_symbols4.xml
deleted file mode 100644
index 3c628f0..0000000
--- a/java/res/xml/keys_pcqwerty_symbols4.xml
+++ /dev/null
@@ -1,49 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-/*
-**
-** Copyright 2012, 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.
-*/
--->
-
-<merge
-    xmlns:latin="http://schemas.android.com/apk/res/com.android.inputmethod.latin"
->
-    <!-- U+2122: "™" TRADE MARK SIGN -->
-    <Key
-        latin:keyLabel="&#x2122;" />
-    <!-- U+00AE: "®" REGISTERED SIGN -->
-    <Key
-        latin:keyLabel="&#x00AE;" />
-    <!-- U+00A9: "©" COPYRIGHT SIGN -->
-    <Key
-        latin:keyLabel="&#x00A9;" />
-    <!-- U+00B6: "¶" PILCROW SIGN -->
-    <Key
-        latin:keyLabel="&#x00B6;" />
-    <!-- U+00A7: "§" SECTION SIGN -->
-    <Key
-        latin:keyLabel="&#x00A7;" />
-    <!-- U+2191: "↑" UPWARDS ARROW
-         U+2193: "↓" DOWNWARDS ARROW
-         U+2190: "←" LEFTWARDS ARROW
-         U+2192: "→" RIGHTWARDS ARROW -->
-    <Key
-        latin:keyLabel="&#x2191;"
-        latin:moreKeys="&#x2193;" />
-    <Key
-        latin:keyLabel="&#x2190;"
-        latin:moreKeys="&#x2192;" />
-</merge>
diff --git a/java/res/xml/key_hindi1_shift.xml b/java/res/xml/keystyle_devanagari_sign_virama.xml
similarity index 73%
copy from java/res/xml/key_hindi1_shift.xml
copy to java/res/xml/keystyle_devanagari_sign_virama.xml
index 0db5ae9..b22fbe8 100644
--- a/java/res/xml/key_hindi1_shift.xml
+++ b/java/res/xml/keystyle_devanagari_sign_virama.xml
@@ -20,15 +20,16 @@
 
 <!-- The code point U+25CC for key label is needed because the font rendering system prior to
      API version 16 can't automatically render dotted circle for incomplete combining letter
-     of Hindi. The files named res/xml/{key,keys}_hindi*.xml have this U+25CC hack, although the
-     counterpart files named res/xml-v16/{key,keys}_hindi*.xml don't have this hack. -->
+     of some scripts. The files named res/xml/key_*.xml have this U+25CC hack, although the
+     counterpart files named res/xml-v16/key_*.xml don't have this hack. -->
 <merge
     xmlns:latin="http://schemas.android.com/apk/res/com.android.inputmethod.latin"
 >
     <!-- U+25CC: "◌" DOTTED CIRCLE
-         U+0903: "ः" DEVANAGARI SIGN VISARGA -->
-    <Key
-        latin:keyLabel="&#x25CC;&#x0903;"
-        latin:code="0x0903"
+         U+094D: "्" DEVANAGARI SIGN VIRAMA -->
+    <key-style
+        latin:styleName="baseKeyDevanagariSignVirama"
+        latin:keyLabel="&#x25CC;&#x094D;"
+        latin:code="0x094D"
         latin:keyLabelFlags="fontNormal|followKeyLetterRatio" />
-</merge>
+ </merge>
diff --git a/java/res/xml/key_hindi1_shift.xml b/java/res/xml/keystyle_devanagari_sign_visarga.xml
similarity index 82%
rename from java/res/xml/key_hindi1_shift.xml
rename to java/res/xml/keystyle_devanagari_sign_visarga.xml
index 0db5ae9..cb29495 100644
--- a/java/res/xml/key_hindi1_shift.xml
+++ b/java/res/xml/keystyle_devanagari_sign_visarga.xml
@@ -20,14 +20,15 @@
 
 <!-- The code point U+25CC for key label is needed because the font rendering system prior to
      API version 16 can't automatically render dotted circle for incomplete combining letter
-     of Hindi. The files named res/xml/{key,keys}_hindi*.xml have this U+25CC hack, although the
-     counterpart files named res/xml-v16/{key,keys}_hindi*.xml don't have this hack. -->
+     of some scripts. The files named res/xml/key_*.xml have this U+25CC hack, although the
+     counterpart files named res/xml-v16/key_*.xml don't have this hack. -->
 <merge
     xmlns:latin="http://schemas.android.com/apk/res/com.android.inputmethod.latin"
 >
     <!-- U+25CC: "◌" DOTTED CIRCLE
          U+0903: "ः" DEVANAGARI SIGN VISARGA -->
-    <Key
+    <key-style
+        latin:styleName="baseKeyDevanagariSignVisarga"
         latin:keyLabel="&#x25CC;&#x0903;"
         latin:code="0x0903"
         latin:keyLabelFlags="fontNormal|followKeyLetterRatio" />
diff --git a/java/res/xml/keystyle_devanagari_vowel_sign_aa.xml b/java/res/xml/keystyle_devanagari_vowel_sign_aa.xml
new file mode 100644
index 0000000..2e78c53
--- /dev/null
+++ b/java/res/xml/keystyle_devanagari_vowel_sign_aa.xml
@@ -0,0 +1,52 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+**
+** Copyright 2013, 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.
+*/
+-->
+
+<!-- The code point U+25CC for key label is needed because the font rendering system prior to
+     API version 16 can't automatically render dotted circle for incomplete combining letter
+     of some scripts. The files named res/xml/key_*.xml have this U+25CC hack, although the
+     counterpart files named res/xml-v16/key_*.xml don't have this hack. -->
+<merge
+    xmlns:latin="http://schemas.android.com/apk/res/com.android.inputmethod.latin"
+>
+    <switch>
+        <case
+            latin:keyboardLayoutSet="hindi"
+        >
+            <!-- U+25CC: "◌" DOTTED CIRCLE
+                 U+093E/U+0902: "ां" DEVANAGARI VOWEL SIGN AA/DEVANAGARI SIGN ANUSVARA
+                 U+093E/U+0901: "ाँ" DEVANAGARI VOWEL SIGN AA/DEVANAGARI SIGN CANDRABINDU -->
+            <key-style
+                latin:styleName="moreKeysDevanagariVowelSignAa"
+                latin:moreKeys="&#x25CC;&#x093E;&#x0902;|&#x093E;&#x0902;,&#x25CC;&#x093E;&#x0901;|&#x093E;&#x0901;,%" />
+        </case>
+        <default>
+            <key-style
+                latin:styleName="moreKeysDevanagariVowelSignAa" />
+        </default>
+    </switch>
+    <!-- U+25CC: "◌" DOTTED CIRCLE
+         U+093E: "ा" DEVANAGARI VOWEL SIGN AA -->
+    <key-style
+        latin:styleName="baseKeyDevanagariVowelSignAa"
+        latin:parentStyle="moreKeysDevanagariVowelSignAa"
+        latin:keyLabel="&#x25CC;&#x093E;"
+        latin:code="0x093E"
+        latin:keyLabelFlags="fontNormal|followKeyLetterRatio" />
+</merge>
diff --git a/java/res/xml/keystyle_devanagari_vowel_sign_ai.xml b/java/res/xml/keystyle_devanagari_vowel_sign_ai.xml
new file mode 100644
index 0000000..0554c0e
--- /dev/null
+++ b/java/res/xml/keystyle_devanagari_vowel_sign_ai.xml
@@ -0,0 +1,59 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+**
+** Copyright 2013, 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.
+*/
+-->
+
+<!-- The code point U+25CC for key label is needed because the font rendering system prior to
+     API version 16 can't automatically render dotted circle for incomplete combining letter
+     of some scripts. The files named res/xml/key_*.xml have this U+25CC hack, although the
+     counterpart files named res/xml-v16/key_*.xml don't have this hack. -->
+<merge
+    xmlns:latin="http://schemas.android.com/apk/res/com.android.inputmethod.latin"
+>
+    <switch>
+        <case
+            latin:keyboardLayoutSet="hindi"
+        >
+            <!-- U+25CC: "◌" DOTTED CIRCLE
+                 U+0948/U+0902: "ैं" DEVANAGARI VOWEL SIGN AI/DEVANAGARI SIGN ANUSVARA -->
+            <key-style
+                latin:styleName="moreKeysDevanagariVowelSignAi"
+                latin:moreKeys="&#x25CC;&#x0948;&#x0902;|&#x0948;&#x0902;,%" />
+        </case>
+        <case
+            latin:keyboardLayoutSet="nepali_traditional"
+        >
+            <!-- U+0936/U+094D/U+0930: "श्र" DEVANAGARI LETTER SHA/DEVANAGARI SIGN VIRAMA/DEVANAGARI LETTER RA -->
+            <key-style
+                latin:styleName="moreKeysDevanagariVowelSignAi"
+                latin:moreKeys="&#x0936;&#x094D;&#x0930;" />
+        </case>
+        <default>
+            <key-style
+                latin:styleName="moreKeysDevanagariVowelSignAi" />
+        </default>
+    </switch>
+    <!-- U+25CC: "◌" DOTTED CIRCLE
+         U+0948: "ै" DEVANAGARI VOWEL SIGN AI -->
+    <key-style
+        latin:styleName="baseKeyDevanagariVowelSignAi"
+        latin:parentStyle="moreKeysDevanagariVowelSignAi"
+        latin:keyLabel="&#x25CC;&#x0948;"
+        latin:code="0x0948"
+        latin:keyLabelFlags="fontNormal|followKeyLetterRatio" />
+</merge>
diff --git a/java/res/xml/keystyle_devanagari_vowel_sign_au.xml b/java/res/xml/keystyle_devanagari_vowel_sign_au.xml
new file mode 100644
index 0000000..29a11a8
--- /dev/null
+++ b/java/res/xml/keystyle_devanagari_vowel_sign_au.xml
@@ -0,0 +1,50 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+**
+** Copyright 2013, 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.
+*/
+-->
+
+<!-- The code point U+25CC for key label is needed because the font rendering system prior to
+     API version 16 can't automatically render dotted circle for incomplete combining letter
+     of some scripts. The files named res/xml/key_*.xml have this U+25CC hack, although the
+     counterpart files named res/xml-v16/key_*.xml don't have this hack. -->
+<merge
+    xmlns:latin="http://schemas.android.com/apk/res/com.android.inputmethod.latin"
+>
+    <switch>
+        <case
+            latin:keyboardLayoutSet="hindi"
+        >
+            <!-- U+25CC: "◌" DOTTED CIRCLE
+                U+094C/U+0902: "ौं" DEVANAGARI VOWEL SIGN AU/DEVANAGARI SIGN ANUSVARA -->
+            <key-style
+                latin:styleName="moreKeysDevanagariVowelSignAu"
+                latin:moreKeys="&#x25CC;&#x094C;&#x0902;|&#x094C;&#x0902;,%" />
+        </case>
+        <default>
+             <key-style
+                latin:styleName="moreKeysDevanagariVowelSignAu" />
+        </default>
+    </switch>
+    <!-- U+094C: "ौ" DEVANAGARI VOWEL SIGN AU -->
+    <key-style
+        latin:styleName="baseKeyDevanagariVowelSignAu"
+        latin:parentStyle="moreKeysDevanagariVowelSignAu"
+        latin:keyLabel="&#x25CC;&#x094C;"
+        latin:code="0x094C"
+        latin:keyLabelFlags="fontNormal|followKeyLetterRatio" />
+</merge>
diff --git a/java/res/xml/keystyle_devanagari_vowel_sign_e.xml b/java/res/xml/keystyle_devanagari_vowel_sign_e.xml
new file mode 100644
index 0000000..edd29c7
--- /dev/null
+++ b/java/res/xml/keystyle_devanagari_vowel_sign_e.xml
@@ -0,0 +1,59 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+**
+** Copyright 2013, 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.
+*/
+-->
+
+<!-- The code point U+25CC for key label is needed because the font rendering system prior to
+     API version 16 can't automatically render dotted circle for incomplete combining letter
+     of some scripts. The files named res/xml/key_*.xml have this U+25CC hack, although the
+     counterpart files named res/xml-v16/key_*.xml don't have this hack. -->
+<merge
+    xmlns:latin="http://schemas.android.com/apk/res/com.android.inputmethod.latin"
+>
+    <switch>
+        <case
+            latin:keyboardLayoutSet="hindi"
+        >
+            <!-- U+25CC: "◌" DOTTED CIRCLE
+                 U+0947/U+0902: "ें" DEVANAGARI VOWEL SIGN E/DEVANAGARI SIGN ANUSVARA -->
+            <key-style
+                latin:styleName="moreKeysDevanagariVowelSignE"
+                latin:moreKeys="&#x25CC;&#x0947;&#x0902;|&#x0947;&#x0902;" />
+        </case>
+        <case
+            latin:keyboardLayoutSet="nepali_traditional"
+        >
+            <!-- U+25CC: "◌" DOTTED CIRCLE
+                 U+0903: "ः‍" DEVANAGARI SIGN VISARGA
+                 U+093D: "ऽ" DEVANAGARI SIGN AVAGRAHA -->
+            <key-style
+                latin:styleName="moreKeysDevanagariVowelSignE"
+                latin:moreKeys="&#x25CC;&#x0903;|&#x0903;,&#x093D;" />
+        </case>
+        <default>
+             <key-style
+                latin:styleName="moreKeysDevanagariVowelSignE" />
+        </default>
+    </switch>
+    <key-style
+        latin:styleName="baseKeyDevanagariVowelSignE"
+        latin:parentStyle="moreKeysDevanagariVowelSignE"
+        latin:keyLabel="&#x25CC;&#x0947;"
+        latin:code="0x0947"
+        latin:keyLabelFlags="fontNormal|followKeyLetterRatio" />
+</merge>
diff --git a/java/res/xml/keystyle_devanagari_vowel_sign_i.xml b/java/res/xml/keystyle_devanagari_vowel_sign_i.xml
new file mode 100644
index 0000000..200fed2
--- /dev/null
+++ b/java/res/xml/keystyle_devanagari_vowel_sign_i.xml
@@ -0,0 +1,51 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+**
+** Copyright 2013, 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.
+*/
+-->
+
+<!-- The code point U+25CC for key label is needed because the font rendering system prior to
+     API version 16 can't automatically render dotted circle for incomplete combining letter
+     of some scripts. The files named res/xml/key_*.xml have this U+25CC hack, although the
+     counterpart files named res/xml-v16/key_*.xml don't have this hack. -->
+<merge
+    xmlns:latin="http://schemas.android.com/apk/res/com.android.inputmethod.latin"
+>
+    <switch>
+        <case
+            latin:keyboardLayoutSet="hindi"
+        >
+            <!-- U+25CC: "◌" DOTTED CIRCLE
+                 U+093F/U+0902: "िं" DEVANAGARI VOWEL SIGN I/DEVANAGARI SIGN ANUSVARA -->
+            <key-style
+                latin:styleName="moreKeysDevanagariVowelSignI"
+                latin:moreKeys="&#x093F;&#x25CC;&#x0902;|&#x093F;&#x0902;" />
+        </case>
+        <default>
+             <key-style
+                latin:styleName="moreKeysDevanagariVowelSignI" />
+        </default>
+    </switch>
+    <!-- U+25CC: "◌" DOTTED CIRCLE
+         U+093F: "ि" DEVANAGARI VOWEL SIGN I -->
+    <key-style
+        latin:styleName="baseKeyDevanagariVowelSignI"
+        latin:parentStyle="moreKeysDevanagariVowelSignI"
+        latin:keyLabel="&#x25CC;&#x093F;"
+        latin:code="0x093F"
+        latin:keyLabelFlags="fontNormal|followKeyLetterRatio" />
+</merge>
diff --git a/java/res/xml/keystyle_devanagari_vowel_sign_ii.xml b/java/res/xml/keystyle_devanagari_vowel_sign_ii.xml
new file mode 100644
index 0000000..6dc9951
--- /dev/null
+++ b/java/res/xml/keystyle_devanagari_vowel_sign_ii.xml
@@ -0,0 +1,51 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+**
+** Copyright 2013, 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.
+*/
+-->
+
+<!-- The code point U+25CC for key label is needed because the font rendering system prior to
+     API version 16 can't automatically render dotted circle for incomplete combining letter
+     of some scripts. The files named res/xml/key_*.xml have this U+25CC hack, although the
+     counterpart files named res/xml-v16/key_*.xml don't have this hack. -->
+<merge
+    xmlns:latin="http://schemas.android.com/apk/res/com.android.inputmethod.latin"
+>
+    <switch>
+        <case
+            latin:keyboardLayoutSet="hindi"
+        >
+            <!-- U+0940: "ी" DEVANAGARI VOWEL SIGN II
+                 U+0940/U+0902: "ीं" DEVANAGARI VOWEL SIGN II/DEVANAGARI SIGN ANUSVARA -->
+            <key-style
+                latin:styleName="moreKeysDevanagariVowelSignIi"
+                latin:moreKeys="&#x25CC;&#x0940;&#x0902;|&#x0940;&#x0902;,%" />
+        </case>
+        <default>
+             <key-style
+                latin:styleName="moreKeysDevanagariVowelSignIi" />
+        </default>
+    </switch>
+    <!-- U+25CC: "◌" DOTTED CIRCLE
+         U+0940: "ी" DEVANAGARI VOWEL SIGN II -->
+    <key-style
+        latin:styleName="baseKeyDevanagariVowelSignIi"
+        latin:parentStyle="moreKeysDevanagariVowelSignIi"
+        latin:keyLabel="&#x25CC;&#x0940;"
+        latin:code="0x0940"
+        latin:keyLabelFlags="fontNormal|followKeyLetterRatio" />
+</merge>
diff --git a/java/res/xml/keystyle_devanagari_vowel_sign_o.xml b/java/res/xml/keystyle_devanagari_vowel_sign_o.xml
new file mode 100644
index 0000000..233ac86
--- /dev/null
+++ b/java/res/xml/keystyle_devanagari_vowel_sign_o.xml
@@ -0,0 +1,53 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+**
+** Copyright 2013, 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.
+*/
+-->
+
+<!-- The code point U+25CC for key label is needed because the font rendering system prior to
+     API version 16 can't automatically render dotted circle for incomplete combining letter
+     of some scripts. The files named res/xml/key_*.xml have this U+25CC hack, although the
+     counterpart files named res/xml-v16/key_*.xml don't have this hack. -->
+<merge
+    xmlns:latin="http://schemas.android.com/apk/res/com.android.inputmethod.latin"
+>
+    <switch>
+        <case
+            latin:keyboardLayoutSet="hindi"
+        >
+            <!-- U+25CC: "◌" DOTTED CIRCLE
+                 U+094B/U+0902: "қं" DEVANAGARI VOWEL SIGN O/DEVANAGARI SIGN ANUSVARA
+                 U+0949: "ॉ" DEVANAGARI VOWEL SIGN CANDRA O
+                 U+094A: "ॊ" DEVANAGARI VOWEL SIGN SHORT O -->
+            <key-style
+                latin:styleName="moreKeysDevanagariVowelSignO"
+                latin:moreKeys="&#x25CC;&#x094B;&#x0902;|&#x094B;&#x0902;,&#x25CC;&#x0949;|&#x0949;,&#x25CC;&#x094A;|&#x094A;" />
+        </case>
+        <default>
+             <key-style
+                latin:styleName="moreKeysDevanagariVowelSignO" />
+        </default>
+    </switch>
+    <!-- U+25CC: "◌" DOTTED CIRCLE
+         U+094B: "ो" DEVANAGARI VOWEL SIGN O -->
+    <key-style
+        latin:styleName="baseKeyDevanagariVowelSignO"
+        latin:parentStyle="moreKeysDevanagariVowelSignO"
+        latin:keyLabel="&#x25CC;&#x094B;"
+        latin:code="0x094B"
+        latin:keyLabelFlags="fontNormal|followKeyLetterRatio" />
+</merge>
diff --git a/java/res/xml/keystyle_devanagari_vowel_sign_u.xml b/java/res/xml/keystyle_devanagari_vowel_sign_u.xml
new file mode 100644
index 0000000..7291b70
--- /dev/null
+++ b/java/res/xml/keystyle_devanagari_vowel_sign_u.xml
@@ -0,0 +1,52 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+**
+** Copyright 2013, 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.
+*/
+-->
+
+<!-- The code point U+25CC for key label is needed because the font rendering system prior to
+     API version 16 can't automatically render dotted circle for incomplete combining letter
+     of some scripts. The files named res/xml/key_*.xml have this U+25CC hack, although the
+     counterpart files named res/xml-v16/key_*.xml don't have this hack. -->
+<merge
+    xmlns:latin="http://schemas.android.com/apk/res/com.android.inputmethod.latin"
+>
+    <switch>
+        <case
+            latin:keyboardLayoutSet="hindi"
+        >
+            <!-- U+25CC: "◌" DOTTED CIRCLE
+                 U+0941/U+0902: "ुं" DEVANAGARI VOWEL SIGN U/DEVANAGARI SIGN ANUSVARA
+                 U+0941/U+0901: "ुँ" DEVANAGARI VOWEL SIGN U/DEVANAGARI SIGN CANDRABINDU -->
+            <key-style
+                latin:styleName="moreKeysDevanagariVowelSignU"
+                latin:moreKeys="&#x25CC;&#x0941;&#x0902;|&#x0941;&#x0902;,&#x25CC;&#x0941;&#x0901;|&#x0941;&#x0901;" />
+        </case>
+        <default>
+             <key-style
+                latin:styleName="moreKeysDevanagariVowelSignU" />
+        </default>
+    </switch>
+    <!-- U+25CC: "◌" DOTTED CIRCLE
+         U+0941: "ु" DEVANAGARI VOWEL SIGN U -->
+    <key-style
+        latin:styleName="baseKeyDevanagariVowelSignU"
+        latin:parentStyle="moreKeysDevanagariVowelSignU"
+        latin:keyLabel="&#x25CC;&#x0941;"
+        latin:code="0x0941"
+        latin:keyLabelFlags="fontNormal|followKeyLetterRatio" />
+</merge>
diff --git a/java/res/xml/keystyle_devanagari_vowel_sign_uu.xml b/java/res/xml/keystyle_devanagari_vowel_sign_uu.xml
new file mode 100644
index 0000000..a95ab82
--- /dev/null
+++ b/java/res/xml/keystyle_devanagari_vowel_sign_uu.xml
@@ -0,0 +1,52 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+**
+** Copyright 2013, 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.
+*/
+-->
+
+<!-- The code point U+25CC for key label is needed because the font rendering system prior to
+     API version 16 can't automatically render dotted circle for incomplete combining letter
+     of some scripts. The files named res/xml/key_*.xml have this U+25CC hack, although the
+     counterpart files named res/xml-v16/key_*.xml don't have this hack. -->
+<merge
+    xmlns:latin="http://schemas.android.com/apk/res/com.android.inputmethod.latin"
+>
+    <switch>
+        <case
+            latin:keyboardLayoutSet="hindi"
+        >
+            <!-- U+25CC: "◌" DOTTED CIRCLE
+                 U+0942/U+0902: "ूं" DEVANAGARI VOWEL SIGN UU/DEVANAGARI SIGN ANUSVARA
+                 U+0942/U+0901: "ूँ" DEVANAGARI VOWEL SIGN UU/DEVANAGARI SIGN CANDRABINDU -->
+            <key-style
+                latin:styleName="moreKeysDevanagariVowelSignUu"
+                latin:moreKeys="&#x25CC;&#x0942;&#x0902;|&#x0942;&#x0902;,&#x25CC;&#x0942;&#x0901;|&#x0942;&#x0901;,%" />
+        </case>
+        <default>
+             <key-style
+                latin:styleName="moreKeysDevanagariVowelSignUu" />
+        </default>
+    </switch>
+    <!-- U+25CC: "◌" DOTTED CIRCLE
+         U+0942: "ू" DEVANAGARI VOWEL SIGN UU -->
+    <key-style
+        latin:styleName="baseKeyDevanagariVowelSignUu"
+        latin:parentStyle="moreKeysDevanagariVowelSignUu"
+        latin:keyLabel="&#x25CC;&#x0942;"
+        latin:code="0x0942"
+        latin:keyLabelFlags="fontNormal|followKeyLetterRatio" />
+</merge>
diff --git a/java/res/xml/method.xml b/java/res/xml/method.xml
index 52d715a..f0e04c2 100644
--- a/java/res/xml/method.xml
+++ b/java/res/xml/method.xml
@@ -24,7 +24,7 @@
     keyboard_locale: script_name/keyboard_layout_set
     af: Afrikaans/qwerty
     ar: Arabic/arabic
-    (az: Azerbaijani/qwerty) # disabled temporarily. waiting for strnig resources.
+    az: Azerbaijani/qwerty
     be: Belarusian/east_slavic
     bg: Bulgarian/bulgarian
     bg: Bulgarian/bulgarian_bds
@@ -47,19 +47,24 @@
     hi: Hindi/hindi
     hr: Croatian/qwertz
     hu: Hungarian/qwertz
+    hy: Armenian Phonetic/armenian_phonetic
     in: Indonesian/qwerty    # "id" is official language code of Indonesian.
     is: Icelandic/qwerty
     it: Italian/qwerty
     iw: Hebrew/hebrew        # "he" is official language code of Hebrew.
     ka: Georgian/georgian
     (kk: Kazakh/east_slavic) # disabled temporarily. waiting for strnig resources.
+    km: Khmer/khmer
     ky: Kyrgyz/east_slavic
+    lo: Lao/lao
     lt: Lithuanian/qwerty
     lv: Latvian/qwerty
     mk: Macedonian/south_slavic
     mn: Mongolian/mongolian
     ms: Malay/qwerty
     nb: Norwegian Bokmål/nordic
+    ne: Nepali Romanized/nepali_romanized  # disabled temporarily
+    ne: Nepali Traditional/nepali_traditional  # disabled temporarily
     nl: Dutch/qwerty
     nl_BE: Dutch Belgium/azerty
     pl: Polish/qwerty
@@ -80,6 +85,7 @@
     vi: Vietnamese/qwerty
     zu: Zulu/qwerty
     zz: QWERTY/qwerty
+    (zz: Emoji/emoji)
     -->
 <!-- TODO: use <lang>_keyboard icon instead of a common keyboard icon. -->
 <!-- Note: SupportTouchPositionCorrection extra value is obsolete and maintained for backward
@@ -89,427 +95,474 @@
 <input-method xmlns:android="http://schemas.android.com/apk/res/android"
         android:settingsActivity="com.android.inputmethod.latin.settings.SettingsActivity"
         android:isDefault="@bool/im_is_default">
-    <subtype android:icon="@drawable/ic_subtype_keyboard"
+    <subtype android:icon="@drawable/ic_ime_switcher_dark"
             android:label="@string/subtype_en_US"
             android:subtypeId="0xc9194f98"
             android:imeSubtypeLocale="en_US"
             android:imeSubtypeMode="keyboard"
-            android:imeSubtypeExtraValue="TrySuppressingImeSwitcher,AsciiCapable,SupportTouchPositionCorrection"
+            android:imeSubtypeExtraValue="TrySuppressingImeSwitcher,AsciiCapable,SupportTouchPositionCorrection,EmojiCapable"
     />
-    <subtype android:icon="@drawable/ic_subtype_keyboard"
+    <subtype android:icon="@drawable/ic_ime_switcher_dark"
             android:label="@string/subtype_en_GB"
             android:subtypeId="0xb045e755"
             android:imeSubtypeLocale="en_GB"
             android:imeSubtypeMode="keyboard"
-            android:imeSubtypeExtraValue="TrySuppressingImeSwitcher,AsciiCapable,SupportTouchPositionCorrection"
+            android:imeSubtypeExtraValue="TrySuppressingImeSwitcher,AsciiCapable,SupportTouchPositionCorrection,EmojiCapable"
     />
-    <subtype android:icon="@drawable/ic_subtype_keyboard"
+    <subtype android:icon="@drawable/ic_ime_switcher_dark"
             android:label="@string/subtype_generic"
             android:subtypeId="0x6f972360"
             android:imeSubtypeLocale="af"
             android:imeSubtypeMode="keyboard"
-            android:imeSubtypeExtraValue="KeyboardLayoutSet=qwerty,AsciiCapable"
+            android:imeSubtypeExtraValue="KeyboardLayoutSet=qwerty,AsciiCapable,EmojiCapable"
     />
-    <subtype android:icon="@drawable/ic_subtype_keyboard"
+    <subtype android:icon="@drawable/ic_ime_switcher_dark"
             android:label="@string/subtype_generic"
             android:subtypeId="0x590dde40"
             android:imeSubtypeLocale="ar"
             android:imeSubtypeMode="keyboard"
-            android:imeSubtypeExtraValue="SupportTouchPositionCorrection"
+            android:imeSubtypeExtraValue="SupportTouchPositionCorrection,EmojiCapable"
     />
-    <!--
-    <subtype android:icon="@drawable/ic_subtype_keyboard"
+    <subtype android:icon="@drawable/ic_ime_switcher_dark"
             android:label="@string/subtype_generic"
             android:subtypeId="0x70b0f974"
             android:imeSubtypeLocale="az"
             android:imeSubtypeMode="keyboard"
-            android:imeSubtypeExtraValue="KeyboardLayoutSet=qwerty,AsciiCapable"
+            android:imeSubtypeExtraValue="KeyboardLayoutSet=qwerty,AsciiCapable,EmojiCapable"
     />
-    -->
-    <subtype android:icon="@drawable/ic_subtype_keyboard"
+    <subtype android:icon="@drawable/ic_ime_switcher_dark"
             android:label="@string/subtype_generic"
             android:subtypeId="0x1dc3a859"
             android:imeSubtypeLocale="be"
             android:imeSubtypeMode="keyboard"
-            android:imeSubtypeExtraValue="KeyboardLayoutSet=east_slavic"
+            android:imeSubtypeExtraValue="KeyboardLayoutSet=east_slavic,EmojiCapable"
     />
-    <subtype android:icon="@drawable/ic_subtype_keyboard"
+    <subtype android:icon="@drawable/ic_ime_switcher_dark"
             android:label="@string/subtype_generic"
             android:subtypeId="0x0ba9c0e8"
             android:imeSubtypeLocale="bg"
             android:imeSubtypeMode="keyboard"
-            android:imeSubtypeExtraValue="KeyboardLayoutSet=bulgarian"
+            android:imeSubtypeExtraValue="KeyboardLayoutSet=bulgarian,EmojiCapable"
     />
-    <subtype android:icon="@drawable/ic_subtype_keyboard"
+    <subtype android:icon="@drawable/ic_ime_switcher_dark"
             android:label="@string/subtype_bulgarian_bds"
             android:subtypeId="0x5f51ba9a"
             android:imeSubtypeLocale="bg"
             android:imeSubtypeMode="keyboard"
-            android:imeSubtypeExtraValue="KeyboardLayoutSet=bulgarian_bds"
+            android:imeSubtypeExtraValue="KeyboardLayoutSet=bulgarian_bds,EmojiCapable"
     />
-    <subtype android:icon="@drawable/ic_subtype_keyboard"
+    <subtype android:icon="@drawable/ic_ime_switcher_dark"
             android:label="@string/subtype_generic"
             android:subtypeId="0xd2e520d5"
             android:imeSubtypeLocale="ca"
             android:imeSubtypeMode="keyboard"
-            android:imeSubtypeExtraValue="KeyboardLayoutSet=spanish,AsciiCapable"
+            android:imeSubtypeExtraValue="KeyboardLayoutSet=spanish,AsciiCapable,EmojiCapable"
     />
-    <subtype android:icon="@drawable/ic_subtype_keyboard"
+    <subtype android:icon="@drawable/ic_ime_switcher_dark"
             android:label="@string/subtype_generic"
             android:subtypeId="0x2d3d2ed0"
             android:imeSubtypeLocale="cs"
             android:imeSubtypeMode="keyboard"
-            android:imeSubtypeExtraValue="AsciiCapable,SupportTouchPositionCorrection"
+            android:imeSubtypeExtraValue="AsciiCapable,SupportTouchPositionCorrection,EmojiCapable"
     />
-    <subtype android:icon="@drawable/ic_subtype_keyboard"
+    <subtype android:icon="@drawable/ic_ime_switcher_dark"
             android:label="@string/subtype_generic"
             android:subtypeId="0x2df4605d"
             android:imeSubtypeLocale="da"
             android:imeSubtypeMode="keyboard"
-            android:imeSubtypeExtraValue="AsciiCapable,SupportTouchPositionCorrection"
+            android:imeSubtypeExtraValue="AsciiCapable,SupportTouchPositionCorrection,EmojiCapable"
     />
-    <subtype android:icon="@drawable/ic_subtype_keyboard"
+    <subtype android:icon="@drawable/ic_ime_switcher_dark"
             android:label="@string/subtype_generic"
             android:subtypeId="0x2e2cbe61"
             android:imeSubtypeLocale="de"
             android:imeSubtypeMode="keyboard"
-            android:imeSubtypeExtraValue="AsciiCapable,SupportTouchPositionCorrection"
+            android:imeSubtypeExtraValue="AsciiCapable,SupportTouchPositionCorrection,EmojiCapable"
     />
-    <subtype android:icon="@drawable/ic_subtype_keyboard"
+    <subtype android:icon="@drawable/ic_ime_switcher_dark"
             android:label="@string/subtype_generic"
             android:subtypeId="0x0e7802d3"
             android:imeSubtypeLocale="el"
             android:imeSubtypeMode="keyboard"
-            android:imeSubtypeExtraValue="KeyboardLayoutSet=greek"
+            android:imeSubtypeExtraValue="KeyboardLayoutSet=greek,EmojiCapable"
     />
-    <subtype android:icon="@drawable/ic_subtype_keyboard"
+    <subtype android:icon="@drawable/ic_ime_switcher_dark"
             android:label="@string/subtype_generic"
             android:subtypeId="0x4090554a"
             android:imeSubtypeLocale="eo"
             android:imeSubtypeMode="keyboard"
-            android:imeSubtypeExtraValue="KeyboardLayoutSet=spanish"
+            android:imeSubtypeExtraValue="KeyboardLayoutSet=spanish,EmojiCapable"
     />
-    <subtype android:icon="@drawable/ic_subtype_keyboard"
+    <subtype android:icon="@drawable/ic_ime_switcher_dark"
             android:label="@string/subtype_generic"
             android:subtypeId="0x30a6e00e"
             android:imeSubtypeLocale="es"
             android:imeSubtypeMode="keyboard"
-            android:imeSubtypeExtraValue="AsciiCapable,SupportTouchPositionCorrection"
+            android:imeSubtypeExtraValue="AsciiCapable,SupportTouchPositionCorrection,EmojiCapable"
     />
-    <subtype android:icon="@drawable/ic_subtype_keyboard"
+    <subtype android:icon="@drawable/ic_ime_switcher_dark"
             android:label="@string/subtype_es_US"
             android:subtypeId="0x84d2efc6"
             android:imeSubtypeLocale="es_US"
             android:imeSubtypeMode="keyboard"
-            android:imeSubtypeExtraValue="KeyboardLayoutSet=spanish,AsciiCapable"
+            android:imeSubtypeExtraValue="KeyboardLayoutSet=spanish,AsciiCapable,EmojiCapable"
     />
     <!--
-    <subtype android:icon="@drawable/ic_subtype_keyboard"
+    <subtype android:icon="@drawable/ic_ime_switcher_dark"
             android:label="@string/subtype_generic"
             android:subtypeId="0x623f9286"
             android:imeSubtypeLocale="es_419"
             android:imeSubtypeMode="keyboard"
-            android:imeSubtypeExtraValue="KeyboardLayoutSet=spanish,AsciiCapable"
+            android:imeSubtypeExtraValue="KeyboardLayoutSet=spanish,AsciiCapable,EmojiCapable"
     />
     -->
-    <subtype android:icon="@drawable/ic_subtype_keyboard"
+    <subtype android:icon="@drawable/ic_ime_switcher_dark"
             android:label="@string/subtype_generic"
             android:subtypeId="0xec2d3955"
             android:imeSubtypeLocale="et"
             android:imeSubtypeMode="keyboard"
-            android:imeSubtypeExtraValue="KeyboardLayoutSet=nordic,AsciiCapable"
+            android:imeSubtypeExtraValue="KeyboardLayoutSet=nordic,AsciiCapable,EmojiCapable"
     />
-    <subtype android:icon="@drawable/ic_subtype_keyboard"
+    <subtype android:icon="@drawable/ic_ime_switcher_dark"
             android:label="@string/subtype_generic"
             android:subtypeId="0xbe66c254"
             android:imeSubtypeLocale="fa"
             android:imeSubtypeMode="keyboard"
-            android:imeSubtypeExtraValue="KeyboardLayoutSet=farsi"
+            android:imeSubtypeExtraValue="KeyboardLayoutSet=farsi,EmojiCapable"
     />
-    <subtype android:icon="@drawable/ic_subtype_keyboard"
+    <subtype android:icon="@drawable/ic_ime_switcher_dark"
             android:label="@string/subtype_generic"
             android:subtypeId="0x31cecda3"
             android:imeSubtypeLocale="fi"
             android:imeSubtypeMode="keyboard"
-            android:imeSubtypeExtraValue="AsciiCapable,SupportTouchPositionCorrection"
+            android:imeSubtypeExtraValue="AsciiCapable,SupportTouchPositionCorrection,EmojiCapable"
     />
-    <subtype android:icon="@drawable/ic_subtype_keyboard"
+    <subtype android:icon="@drawable/ic_ime_switcher_dark"
             android:label="@string/subtype_generic"
             android:subtypeId="0x324da12c"
             android:imeSubtypeLocale="fr"
             android:imeSubtypeMode="keyboard"
-            android:imeSubtypeExtraValue="AsciiCapable,SupportTouchPositionCorrection"
+            android:imeSubtypeExtraValue="AsciiCapable,SupportTouchPositionCorrection,EmojiCapable"
     />
-    <subtype android:icon="@drawable/ic_subtype_keyboard"
+    <subtype android:icon="@drawable/ic_ime_switcher_dark"
             android:label="@string/subtype_generic"
             android:subtypeId="0xeadbb691"
             android:imeSubtypeLocale="fr_CA"
             android:imeSubtypeMode="keyboard"
-            android:imeSubtypeExtraValue="AsciiCapable,SupportTouchPositionCorrection"
+            android:imeSubtypeExtraValue="AsciiCapable,SupportTouchPositionCorrection,EmojiCapable"
     />
-    <subtype android:icon="@drawable/ic_subtype_keyboard"
+    <subtype android:icon="@drawable/ic_ime_switcher_dark"
             android:label="@string/subtype_generic"
             android:subtypeId="0x39753b7f"
             android:imeSubtypeLocale="hi"
             android:imeSubtypeMode="keyboard"
-            android:imeSubtypeExtraValue="KeyboardLayoutSet=hindi"
+            android:imeSubtypeExtraValue="KeyboardLayoutSet=hindi,EmojiCapable"
     />
-    <subtype android:icon="@drawable/ic_subtype_keyboard"
+    <subtype android:icon="@drawable/ic_ime_switcher_dark"
             android:label="@string/subtype_generic"
             android:subtypeId="0x35b7526a"
             android:imeSubtypeLocale="hr"
             android:imeSubtypeMode="keyboard"
-            android:imeSubtypeExtraValue="AsciiCapable,SupportTouchPositionCorrection"
+            android:imeSubtypeExtraValue="AsciiCapable,SupportTouchPositionCorrection,EmojiCapable"
     />
-    <subtype android:icon="@drawable/ic_subtype_keyboard"
+    <subtype android:icon="@drawable/ic_ime_switcher_dark"
             android:label="@string/subtype_generic"
             android:subtypeId="0x35e198ed"
             android:imeSubtypeLocale="hu"
             android:imeSubtypeMode="keyboard"
-            android:imeSubtypeExtraValue="AsciiCapable,SupportTouchPositionCorrection"
+            android:imeSubtypeExtraValue="AsciiCapable,SupportTouchPositionCorrection,EmojiCapable"
+    />
+    <subtype android:icon="@drawable/ic_ime_switcher_dark"
+            android:label="@string/subtype_generic"
+            android:subtypeId="0xe39ac3ca"
+            android:imeSubtypeLocale="hy"
+            android:imeSubtypeMode="keyboard"
+            android:imeSubtypeExtraValue="KeyboardLayoutSet=armenian_phonetic,EmojiCapable"
     />
     <!-- Java uses the deprecated "in" code instead of the standard "id" code for Indonesian. -->
-    <subtype android:icon="@drawable/ic_subtype_keyboard"
+    <subtype android:icon="@drawable/ic_ime_switcher_dark"
             android:label="@string/subtype_generic"
             android:subtypeId="0x7daea460"
             android:imeSubtypeLocale="in"
             android:imeSubtypeMode="keyboard"
-            android:imeSubtypeExtraValue="KeyboardLayoutSet=qwerty,AsciiCapable"
+            android:imeSubtypeExtraValue="KeyboardLayoutSet=qwerty,AsciiCapable,EmojiCapable"
     />
-    <subtype android:icon="@drawable/ic_subtype_keyboard"
+    <subtype android:icon="@drawable/ic_ime_switcher_dark"
             android:label="@string/subtype_generic"
             android:subtypeId="0x7df519e5"
             android:imeSubtypeLocale="is"
             android:imeSubtypeMode="keyboard"
-            android:imeSubtypeExtraValue="KeyboardLayoutSet=qwerty,AsciiCapable"
+            android:imeSubtypeExtraValue="KeyboardLayoutSet=qwerty,AsciiCapable,EmojiCapable"
     />
-    <subtype android:icon="@drawable/ic_subtype_keyboard"
+    <subtype android:icon="@drawable/ic_ime_switcher_dark"
             android:label="@string/subtype_generic"
             android:subtypeId="0x37885a0b"
             android:imeSubtypeLocale="it"
             android:imeSubtypeMode="keyboard"
-            android:imeSubtypeExtraValue="AsciiCapable,SupportTouchPositionCorrection"
+            android:imeSubtypeExtraValue="AsciiCapable,SupportTouchPositionCorrection,EmojiCapable"
     />
     <!-- Java uses the deprecated "iw" code instead of the standard "he" code for Hebrew. -->
-    <subtype android:icon="@drawable/ic_subtype_keyboard"
+    <subtype android:icon="@drawable/ic_ime_switcher_dark"
             android:label="@string/subtype_generic"
             android:subtypeId="0x66fb18bd"
             android:imeSubtypeLocale="iw"
             android:imeSubtypeMode="keyboard"
-            android:imeSubtypeExtraValue="SupportTouchPositionCorrection"
+            android:imeSubtypeExtraValue="SupportTouchPositionCorrection,EmojiCapable"
     />
-    <subtype android:icon="@drawable/ic_subtype_keyboard"
+    <subtype android:icon="@drawable/ic_ime_switcher_dark"
             android:label="@string/subtype_generic"
             android:subtypeId="0x6e119e6a"
             android:imeSubtypeLocale="ka"
             android:imeSubtypeMode="keyboard"
-            android:imeSubtypeExtraValue="KeyboardLayoutSet=georgian"
+            android:imeSubtypeExtraValue="KeyboardLayoutSet=georgian,EmojiCapable"
     />
     <!--
-    <subtype android:icon="@drawable/ic_subtype_keyboard"
+    <subtype android:icon="@drawable/ic_ime_switcher_dark"
             android:label="@string/subtype_generic"
             android:subtypeId="0x2d73d2f6"
             android:imeSubtypeLocale="kk"
             android:imeSubtypeMode="keyboard"
-            android:imeSubtypeExtraValue="KeyboardLayoutSet=east_slavic"
+            android:imeSubtypeExtraValue="KeyboardLayoutSet=east_slavic,EmojiCapable"
     />
     -->
-    <subtype android:icon="@drawable/ic_subtype_keyboard"
+    <subtype android:icon="@drawable/ic_ime_switcher_dark"
+            android:label="@string/subtype_generic"
+            android:subtypeId="0x1365683a"
+            android:imeSubtypeLocale="km"
+            android:imeSubtypeMode="keyboard"
+            android:imeSubtypeExtraValue="KeyboardLayoutSet=khmer,EmojiCapable"
+    />
+            <!-- android:subtypeId="Need this for km" -->
+    <subtype android:icon="@drawable/ic_ime_switcher_dark"
             android:label="@string/subtype_generic"
             android:subtypeId="0x2e391c04"
             android:imeSubtypeLocale="ky"
             android:imeSubtypeMode="keyboard"
-            android:imeSubtypeExtraValue="KeyboardLayoutSet=east_slavic"
+            android:imeSubtypeExtraValue="KeyboardLayoutSet=east_slavic,EmojiCapable"
     />
-    <subtype android:icon="@drawable/ic_subtype_keyboard"
+    <subtype android:icon="@drawable/ic_ime_switcher_dark"
+            android:label="@string/subtype_generic"
+            android:subtypeId="0x8315772c"
+            android:imeSubtypeLocale="lo"
+            android:imeSubtypeMode="keyboard"
+            android:imeSubtypeExtraValue="KeyboardLayoutSet=lao,EmojiCapable"
+    />
+    <subtype android:icon="@drawable/ic_ime_switcher_dark"
             android:label="@string/subtype_generic"
             android:subtypeId="0x8321bb43"
             android:imeSubtypeLocale="lt"
             android:imeSubtypeMode="keyboard"
-            android:imeSubtypeExtraValue="KeyboardLayoutSet=qwerty,AsciiCapable"
+            android:imeSubtypeExtraValue="KeyboardLayoutSet=qwerty,AsciiCapable,EmojiCapable"
     />
-    <subtype android:icon="@drawable/ic_subtype_keyboard"
+    <subtype android:icon="@drawable/ic_ime_switcher_dark"
             android:label="@string/subtype_generic"
             android:subtypeId="0x833dea45"
             android:imeSubtypeLocale="lv"
             android:imeSubtypeMode="keyboard"
-            android:imeSubtypeExtraValue="KeyboardLayoutSet=qwerty,AsciiCapable"
+            android:imeSubtypeExtraValue="KeyboardLayoutSet=qwerty,AsciiCapable,EmojiCapable"
     />
-    <subtype android:icon="@drawable/ic_subtype_keyboard"
+    <subtype android:icon="@drawable/ic_ime_switcher_dark"
             android:label="@string/subtype_generic"
             android:subtypeId="0xaf50ab7c"
             android:imeSubtypeLocale="mk"
             android:imeSubtypeMode="keyboard"
-            android:imeSubtypeExtraValue="KeyboardLayoutSet=south_slavic"
+            android:imeSubtypeExtraValue="KeyboardLayoutSet=south_slavic,EmojiCapable"
     />
-    <subtype android:icon="@drawable/ic_subtype_keyboard"
+    <subtype android:icon="@drawable/ic_ime_switcher_dark"
             android:label="@string/subtype_generic"
             android:subtypeId="0xcdcfc3ab"
             android:imeSubtypeLocale="mn"
             android:imeSubtypeMode="keyboard"
-            android:imeSubtypeExtraValue="KeyboardLayoutSet=mongolian"
+            android:imeSubtypeExtraValue="KeyboardLayoutSet=mongolian,EmojiCapable"
     />
-    <subtype android:icon="@drawable/ic_subtype_keyboard"
+    <subtype android:icon="@drawable/ic_ime_switcher_dark"
             android:label="@string/subtype_generic"
             android:subtypeId="0x84c87c61"
             android:imeSubtypeLocale="ms"
             android:imeSubtypeMode="keyboard"
-            android:imeSubtypeExtraValue="KeyboardLayoutSet=qwerty,AsciiCapable"
+            android:imeSubtypeExtraValue="KeyboardLayoutSet=qwerty,AsciiCapable,EmojiCapable"
     />
-    <subtype android:icon="@drawable/ic_subtype_keyboard"
+    <subtype android:icon="@drawable/ic_ime_switcher_dark"
             android:label="@string/subtype_generic"
             android:subtypeId="0x3f12ee14"
             android:imeSubtypeLocale="nb"
             android:imeSubtypeMode="keyboard"
-            android:imeSubtypeExtraValue="AsciiCapable,SupportTouchPositionCorrection"
+            android:imeSubtypeExtraValue="AsciiCapable,SupportTouchPositionCorrection,EmojiCapable"
     />
-    <subtype android:icon="@drawable/ic_subtype_keyboard"
+    <!--
+    <subtype android:icon="@drawable/ic_ime_switcher_dark"
+            android:label="@string/subtype_generic"
+            android:subtypeId="0xd80a4cee"
+            android:imeSubtypeLocale="ne"
+            android:imeSubtypeMode="keyboard"
+            android:imeSubtypeExtraValue="KeyboardLayoutSet=nepali_romanized,EmojiCapable"
+    />
+    <subtype android:icon="@drawable/ic_ime_switcher_dark"
+            android:label="@string/subtype_nepali_traditional"
+            android:subtypeId="0x5fafea88"
+            android:imeSubtypeLocale="ne"
+            android:imeSubtypeMode="keyboard"
+            android:imeSubtypeExtraValue="KeyboardLayoutSet=nepali_traditional,EmojiCapable"
+    />
+    -->
+    <subtype android:icon="@drawable/ic_ime_switcher_dark"
             android:label="@string/subtype_generic"
             android:subtypeId="0x3f9fd91e"
             android:imeSubtypeLocale="nl"
             android:imeSubtypeMode="keyboard"
-            android:imeSubtypeExtraValue="AsciiCapable,SupportTouchPositionCorrection"
+            android:imeSubtypeExtraValue="AsciiCapable,SupportTouchPositionCorrection,EmojiCapable"
     />
-    <subtype android:icon="@drawable/ic_subtype_keyboard"
+    <subtype android:icon="@drawable/ic_ime_switcher_dark"
             android:label="@string/subtype_generic"
             android:subtypeId="0x500ca92c"
             android:imeSubtypeLocale="nl_BE"
             android:imeSubtypeMode="keyboard"
-            android:imeSubtypeExtraValue="KeyboardLayoutSet=azerty,AsciiCapable"
+            android:imeSubtypeExtraValue="KeyboardLayoutSet=azerty,AsciiCapable,EmojiCapable"
     />
-    <subtype android:icon="@drawable/ic_subtype_keyboard"
+    <subtype android:icon="@drawable/ic_ime_switcher_dark"
             android:label="@string/subtype_generic"
             android:subtypeId="0x43098a5c"
             android:imeSubtypeLocale="pl"
             android:imeSubtypeMode="keyboard"
-            android:imeSubtypeExtraValue="AsciiCapable,SupportTouchPositionCorrection"
+            android:imeSubtypeExtraValue="AsciiCapable,SupportTouchPositionCorrection,EmojiCapable"
     />
-    <subtype android:icon="@drawable/ic_subtype_keyboard"
+    <subtype android:icon="@drawable/ic_ime_switcher_dark"
             android:label="@string/subtype_generic"
             android:subtypeId="0xcafff4a6"
             android:imeSubtypeLocale="pt_BR"
             android:imeSubtypeMode="keyboard"
-            android:imeSubtypeExtraValue="KeyboardLayoutSet=qwerty,AsciiCapable"
+            android:imeSubtypeExtraValue="KeyboardLayoutSet=qwerty,AsciiCapable,EmojiCapable"
     />
-    <subtype android:icon="@drawable/ic_subtype_keyboard"
+    <subtype android:icon="@drawable/ic_ime_switcher_dark"
             android:label="@string/subtype_generic"
             android:subtypeId="0xe2fffc5a"
             android:imeSubtypeLocale="pt_PT"
             android:imeSubtypeMode="keyboard"
-            android:imeSubtypeExtraValue="KeyboardLayoutSet=qwerty,AsciiCapable"
+            android:imeSubtypeExtraValue="KeyboardLayoutSet=qwerty,AsciiCapable,EmojiCapable"
     />
-    <subtype android:icon="@drawable/ic_subtype_keyboard"
+    <subtype android:icon="@drawable/ic_ime_switcher_dark"
             android:label="@string/subtype_generic"
             android:subtypeId="0x8d185978"
             android:imeSubtypeLocale="ro"
             android:imeSubtypeMode="keyboard"
-            android:imeSubtypeExtraValue="KeyboardLayoutSet=qwerty,AsciiCapable"
+            android:imeSubtypeExtraValue="KeyboardLayoutSet=qwerty,AsciiCapable,EmojiCapable"
     />
-    <subtype android:icon="@drawable/ic_subtype_keyboard"
+    <subtype android:icon="@drawable/ic_ime_switcher_dark"
             android:label="@string/subtype_generic"
             android:subtypeId="0x763a8752"
             android:imeSubtypeLocale="ru"
             android:imeSubtypeMode="keyboard"
-            android:imeSubtypeExtraValue="SupportTouchPositionCorrection"
+            android:imeSubtypeExtraValue="SupportTouchPositionCorrection,EmojiCapable"
     />
-    <subtype android:icon="@drawable/ic_subtype_keyboard"
+    <subtype android:icon="@drawable/ic_ime_switcher_dark"
             android:label="@string/subtype_generic"
             android:subtypeId="0x8e94d413"
             android:imeSubtypeLocale="sk"
             android:imeSubtypeMode="keyboard"
-            android:imeSubtypeExtraValue="KeyboardLayoutSet=qwerty,AsciiCapable"
+            android:imeSubtypeExtraValue="KeyboardLayoutSet=qwerty,AsciiCapable,EmojiCapable"
     />
-    <subtype android:icon="@drawable/ic_subtype_keyboard"
+    <subtype android:icon="@drawable/ic_ime_switcher_dark"
             android:label="@string/subtype_generic"
             android:subtypeId="0x8ea2eb94"
             android:imeSubtypeLocale="sl"
             android:imeSubtypeMode="keyboard"
-            android:imeSubtypeExtraValue="KeyboardLayoutSet=qwerty,AsciiCapable"
+            android:imeSubtypeExtraValue="KeyboardLayoutSet=qwerty,AsciiCapable,EmojiCapable"
     />
-    <subtype android:icon="@drawable/ic_subtype_keyboard"
+    <subtype android:icon="@drawable/ic_ime_switcher_dark"
             android:label="@string/subtype_generic"
             android:subtypeId="0x77c5196e"
             android:imeSubtypeLocale="sr"
             android:imeSubtypeMode="keyboard"
-            android:imeSubtypeExtraValue="SupportTouchPositionCorrection"
+            android:imeSubtypeExtraValue="SupportTouchPositionCorrection,EmojiCapable"
     />
     <!-- TODO: Uncomment once we can handle IETF language tag with script name specified.
-    <subtype android:icon="@drawable/ic_subtype_keyboard"
+    <subtype android:icon="@drawable/ic_ime_switcher_dark"
             android:label="@string/subtype_serbian_cyrillic"
             android:subtypeId="0xXXXXXXXX"
             android:imeSubtypeLocale="sr"
             android:imeSubtypeMode="keyboard"
-            android:imeSubtypeExtraValue="SupportTouchPositionCorrection"
+            android:imeSubtypeExtraValue="SupportTouchPositionCorrection,EmojiCapable"
     />
-    <subtype android:icon="@drawable/ic_subtype_keyboard"
+    <subtype android:icon="@drawable/ic_ime_switcher_dark"
             android:label="@string/subtype_serbian_latin"
             android:subtypeId="0xXXXXXXXX"
             android:imeSubtypeLocale="sr-Latn"
             android:imeSubtypeMode="keyboard"
-            android:imeSubtypeExtraValue="KeyboardLayoutSet=qwerty,AsciiCapable"
+            android:imeSubtypeExtraValue="KeyboardLayoutSet=qwerty,AsciiCapable,EmojiCapable"
     />
     -->
-    <subtype android:icon="@drawable/ic_subtype_keyboard"
+    <subtype android:icon="@drawable/ic_ime_switcher_dark"
             android:label="@string/subtype_generic"
             android:subtypeId="0x48b4ff43"
             android:imeSubtypeLocale="sv"
             android:imeSubtypeMode="keyboard"
-            android:imeSubtypeExtraValue="AsciiCapable,SupportTouchPositionCorrection"
+            android:imeSubtypeExtraValue="AsciiCapable,SupportTouchPositionCorrection,EmojiCapable"
     />
-    <subtype android:icon="@drawable/ic_subtype_keyboard"
+    <subtype android:icon="@drawable/ic_ime_switcher_dark"
             android:label="@string/subtype_generic"
             android:subtypeId="0x8f3dee1f"
             android:imeSubtypeLocale="sw"
             android:imeSubtypeMode="keyboard"
-            android:imeSubtypeExtraValue="KeyboardLayoutSet=qwerty,AsciiCapable"
+            android:imeSubtypeExtraValue="KeyboardLayoutSet=qwerty,AsciiCapable,EmojiCapable"
     />
-    <subtype android:icon="@drawable/ic_subtype_keyboard"
+    <subtype android:icon="@drawable/ic_ime_switcher_dark"
             android:label="@string/subtype_generic"
             android:subtypeId="0x1f94d5d4"
             android:imeSubtypeLocale="th"
             android:imeSubtypeMode="keyboard"
-            android:imeSubtypeExtraValue="KeyboardLayoutSet=thai"
+            android:imeSubtypeExtraValue="KeyboardLayoutSet=thai,EmojiCapable"
     />
-    <subtype android:icon="@drawable/ic_subtype_keyboard"
+    <subtype android:icon="@drawable/ic_ime_switcher_dark"
             android:label="@string/subtype_generic"
             android:subtypeId="0xf08285ef"
             android:imeSubtypeLocale="tl"
             android:imeSubtypeMode="keyboard"
-            android:imeSubtypeExtraValue="KeyboardLayoutSet=spanish,AsciiCapable"
+            android:imeSubtypeExtraValue="KeyboardLayoutSet=spanish,AsciiCapable,EmojiCapable"
     />
-    <subtype android:icon="@drawable/ic_subtype_keyboard"
+    <subtype android:icon="@drawable/ic_ime_switcher_dark"
             android:label="@string/subtype_generic"
             android:subtypeId="0x4a3179de"
             android:imeSubtypeLocale="tr"
             android:imeSubtypeMode="keyboard"
-            android:imeSubtypeExtraValue="AsciiCapable,SupportTouchPositionCorrection"
+            android:imeSubtypeExtraValue="AsciiCapable,SupportTouchPositionCorrection,EmojiCapable"
     />
-    <subtype android:icon="@drawable/ic_subtype_keyboard"
+    <subtype android:icon="@drawable/ic_ime_switcher_dark"
             android:label="@string/subtype_generic"
             android:subtypeId="0x3e84492c"
             android:imeSubtypeLocale="uk"
             android:imeSubtypeMode="keyboard"
-            android:imeSubtypeExtraValue="KeyboardLayoutSet=east_slavic"
+            android:imeSubtypeExtraValue="KeyboardLayoutSet=east_slavic,EmojiCapable"
     />
-    <subtype android:icon="@drawable/ic_subtype_keyboard"
+    <subtype android:icon="@drawable/ic_ime_switcher_dark"
             android:label="@string/subtype_generic"
             android:subtypeId="0x93972eee"
             android:imeSubtypeLocale="vi"
             android:imeSubtypeMode="keyboard"
-            android:imeSubtypeExtraValue="KeyboardLayoutSet=qwerty,AsciiCapable"
+            android:imeSubtypeExtraValue="KeyboardLayoutSet=qwerty,AsciiCapable,EmojiCapable"
     />
-    <subtype android:icon="@drawable/ic_subtype_keyboard"
+    <subtype android:icon="@drawable/ic_ime_switcher_dark"
             android:label="@string/subtype_generic"
             android:subtypeId="0x9b13ab76"
             android:imeSubtypeLocale="zu"
             android:imeSubtypeMode="keyboard"
-            android:imeSubtypeExtraValue="KeyboardLayoutSet=qwerty,AsciiCapable"
+            android:imeSubtypeExtraValue="KeyboardLayoutSet=qwerty,AsciiCapable,EmojiCapable"
     />
-    <subtype android:icon="@drawable/ic_subtype_keyboard"
+    <subtype android:icon="@drawable/ic_ime_switcher_dark"
             android:label="@string/subtype_no_language_qwerty"
             android:subtypeId="0xa239ebad"
             android:imeSubtypeLocale="zz"
             android:imeSubtypeMode="keyboard"
-            android:imeSubtypeExtraValue="KeyboardLayoutSet=qwerty,AsciiCapable,EnabledWhenDefaultIsNotAsciiCapable"
+            android:imeSubtypeExtraValue="KeyboardLayoutSet=qwerty,AsciiCapable,EnabledWhenDefaultIsNotAsciiCapable,EmojiCapable"
     />
+    <!-- Emoji subtype has to be an addtional subtype added at boot time because ICS doesn't
+         support Emoji. -->
+    <!--
+    <subtype android:icon="@drawable/ic_ime_switcher_dark"
+            android:label="@string/subtype_emoji"
+            android:subtypeId="0xc14d88b2"
+            android:imeSubtypeLocale="zz"
+            android:imeSubtypeMode="keyboard"
+            android:imeSubtypeExtraValue="KeyboardLayoutSet=emoji,EmojiCapable"
+    />
+    -->
 </input-method>
diff --git a/java/res/xml/prefs.xml b/java/res/xml/prefs.xml
index 2a726c4..6c36b0e 100644
--- a/java/res/xml/prefs.xml
+++ b/java/res/xml/prefs.xml
@@ -42,13 +42,11 @@
             android:title="@string/popup_on_keypress"
             android:persistent="true"
             android:defaultValue="@bool/config_default_key_preview_popup" />
-        <ListPreference
-            android:key="voice_mode"
+        <CheckBoxPreference
+            android:key="pref_voice_input_key"
             android:title="@string/voice_input"
             android:persistent="true"
-            android:entryValues="@array/voice_input_modes_values"
-            android:entries="@array/voice_input_modes"
-            android:defaultValue="@string/voice_mode_main" />
+            android:defaultValue="true" />
     </PreferenceCategory>
     <PreferenceCategory
         android:title="@string/correction_category"
diff --git a/java/res/xml/row_dvorak4.xml b/java/res/xml/row_dvorak4.xml
index e6d487e..02a95ac 100644
--- a/java/res/xml/row_dvorak4.xml
+++ b/java/res/xml/row_dvorak4.xml
@@ -65,7 +65,7 @@
         </switch>
         <include
             latin:keyXPos="25%p"
-            latin:keyboardLayout="@xml/key_space" />
+            latin:keyboardLayout="@xml/key_space_5kw" />
         <Key
             latin:keyLabel="z"
             latin:keyLabelFlags="hasPopupHint"
diff --git a/java/res/xml/row_pcqwerty5.xml b/java/res/xml/row_pcqwerty5.xml
index a8940af..4ec908b 100644
--- a/java/res/xml/row_pcqwerty5.xml
+++ b/java/res/xml/row_pcqwerty5.xml
@@ -24,36 +24,23 @@
     <Row
         latin:keyWidth="7.692%p"
     >
-        <switch>
-            <case
-                latin:keyboardLayoutSetElement="symbols|symbolsShifted"
-            >
-                <Key
-                    latin:keyStyle="toAlphaKeyStyle"
-                    latin:keyWidth="11.538%p" />
-            </case>
-            <!-- keyboardLayoutSetElement="alphabet*" -->
-            <default>
-                <Key
-                    latin:keyStyle="toSymbolKeyStyle"
-                    latin:keyIcon="!icon/undefined"
-                    latin:keyLabel="!text/label_to_symbol_key_pcqwerty"
-                    latin:keyWidth="11.538%p" />
-            </default>
-        </switch>
+        <Spacer
+            latin:keyWidth="11.538%p" />
         <switch>
             <case
                 latin:shortcutKeyEnabled="true"
             >
                 <Key
-                    latin:keyStyle="shortcutKeyStyle" />
-            </case>
+                    latin:keyStyle="shortcutKeyStyle"
+                    latin:keyWidth="11.538%p" />
+                </case>
             <case
                 latin:clobberSettingsKey="false"
             >
                 <Key
-                    latin:keyStyle="settingsKeyStyle" />
-            </case>
+                    latin:keyStyle="settingsKeyStyle"
+                    latin:keyWidth="11.538%p" />
+                </case>
         </switch>
         <switch>
             <case
@@ -61,21 +48,33 @@
             >
                 <Key
                     latin:keyStyle="languageSwitchKeyStyle"
-                    latin:keyXPos="19.230%p" />
+                    latin:keyWidth="11.538%p" />
                 <Key
                     latin:keyStyle="spaceKeyStyle"
-                    latin:keyWidth="53.844%p" />
-            </case>
+                    latin:keyWidth="38.464%p" />
+                </case>
             <!-- languageSwitchKeyEnabled="false" -->
             <default>
                 <Key
                     latin:keyStyle="spaceKeyStyle"
-                    latin:keyXPos="19.230%p"
-                    latin:keyWidth="61.536%p" />
+                    latin:keyWidth="50.002%p" />
             </default>
         </switch>
         <Key
-            latin:keyStyle="enterKeyStyle"
-            latin:keyWidth="fillRight" />
+            latin:keyStyle="defaultEnterKeyStyle"
+            latin:keyWidth="15.384%p" />
+        <switch>
+            <case
+                latin:keyboardLayoutSetElement="alphabet|alphabetAutomaticShifted"
+            >
+                <Spacer />
+            </case>
+            <!-- keyboardLayoutSetElement="alphabetManualShifted|alphabetShiftLocked|alphabetShiftLockShifted" -->
+            <default>
+                <Key
+                    latin:keyStyle="emojiKeyStyle"
+                    latin:keyWidth="fillRight" />
+            </default>
+        </switch>
     </Row>
 </merge>
diff --git a/java/res/xml/row_qwerty4.xml b/java/res/xml/row_qwerty4.xml
index c29fbf2..578bc12 100644
--- a/java/res/xml/row_qwerty4.xml
+++ b/java/res/xml/row_qwerty4.xml
@@ -31,7 +31,7 @@
             latin:keyboardLayout="@xml/key_f1" />
         <include
             latin:keyXPos="25%p"
-            latin:keyboardLayout="@xml/key_space" />
+            latin:keyboardLayout="@xml/key_space_5kw" />
         <switch>
             <case
                 latin:languageCode="ar|fa"
@@ -42,6 +42,21 @@
                     latin:moreKeys="!text/more_keys_for_arabic_diacritics"
                     latin:keyStyle="punctuationKeyStyle" />
             </case>
+            <case
+                latin:languageCode="ne"
+                latin:keyboardLayoutSet="nepali_traditional"
+            >
+                <include
+                    latin:keyboardLayout="@xml/key_nepali_traditional_period" />
+            </case>
+            <case
+                latin:languageCode="hy"
+            >
+                <!-- U+0589: "։" ARMENIAN FULL STOP -->
+                <Key
+                    latin:keyLabel="&#x0589;"
+                    latin:keyStyle="punctuationKeyStyle" />
+            </case>
             <default>
                 <Key
                     latin:keyStyle="punctuationKeyStyle" />
diff --git a/java/res/xml/row_symbols4.xml b/java/res/xml/row_symbols4.xml
index 150ad48..0bf412f 100644
--- a/java/res/xml/row_symbols4.xml
+++ b/java/res/xml/row_symbols4.xml
@@ -18,38 +18,29 @@
 */
 -->
 
-<merge
-    xmlns:latin="http://schemas.android.com/apk/res/com.android.inputmethod.latin"
->
-    <Row
-        latin:keyWidth="10%p"
-    >
-        <Key
-            latin:keyStyle="toAlphaKeyStyle"
-            latin:keyWidth="15%p" />
-        <switch>
-            <case
-                latin:hasShortcutKey="true"
-            >
-                <Key
-                    latin:keyStyle="shortcutKeyStyle" />
-            </case>
-            <!-- latin:hasShortcutKey="false" -->
-            <default>
-                <Key
-                    latin:keyLabel="!text/keylabel_for_comma"
-                    latin:keyLabelFlags="hasPopupHint"
-                    latin:additionalMoreKeys="!text/more_keys_for_comma"
-                    latin:keyStyle="f1MoreKeysStyle" />
-            </default>
-        </switch>
-        <include
-            latin:keyXPos="25%p"
-            latin:keyboardLayout="@xml/key_space" />
-        <Key
-            latin:keyStyle="punctuationKeyStyle" />
-        <Key
-            latin:keyStyle="enterKeyStyle"
-            latin:keyWidth="fillRight" />
-    </Row>
+<merge xmlns:latin="http://schemas.android.com/apk/res/com.android.inputmethod.latin" >
+
+    <Key
+        latin:backgroundType="functional"
+        latin:keyLabel="_" />
+    <Key
+        latin:backgroundType="functional"
+        latin:keyLabel="/" />
+
+    <switch>
+        <case latin:hasShortcutKey="true" >
+            <Key latin:keyStyle="shortcutKeyStyle" />
+        </case>
+        <!-- latin:hasShortcutKey="false" -->
+        <default>
+        </default>
+    </switch>
+
+    <include latin:keyboardLayout="@xml/key_space_symbols" />
+    <include latin:keyboardLayout="@xml/keys_comma_period" />
+
+    <Key
+        latin:keyStyle="emojiKeyStyle"
+        latin:keyWidth="fillRight" />
+
 </merge>
diff --git a/java/res/xml/row_symbols_shift4.xml b/java/res/xml/row_symbols_shift4.xml
index 99a685c..72d24a3 100644
--- a/java/res/xml/row_symbols_shift4.xml
+++ b/java/res/xml/row_symbols_shift4.xml
@@ -17,31 +17,14 @@
 ** limitations under the License.
 */
 -->
+<merge xmlns:latin="http://schemas.android.com/apk/res/com.android.inputmethod.latin" >
 
-<merge
-    xmlns:latin="http://schemas.android.com/apk/res/com.android.inputmethod.latin"
->
-    <Row
-        latin:keyWidth="10%p"
-    >
-        <Key
-            latin:keyStyle="toAlphaKeyStyle"
-            latin:keyWidth="15%p" />
-        <!-- U+201A: "‚" SINGLE LOW-9 QUOTATION MARK
-             U+201E: "„" DOUBLE LOW-9 QUOTATION MARK -->
-        <Key
-            latin:keyLabel="&#x201E;"
-            latin:moreKeys="&#x201A;"
-            latin:backgroundType="functional" />
-        <include
-            latin:keyXPos="25%p"
-            latin:keyboardLayout="@xml/key_space" />
-        <!-- U+2026: "…" HORIZONTAL ELLIPSIS -->
-        <Key
-            latin:keyLabel="&#x2026;"
-            latin:backgroundType="functional" />
-        <Key
-            latin:keyStyle="enterKeyStyle"
-            latin:keyWidth="fillRight" />
-    </Row>
+    <include latin:keyboardLayout="@xml/keys_less_greater" />
+    <include
+        latin:keyboardLayout="@xml/key_space_symbols" />
+    <include latin:keyboardLayout="@xml/keys_comma_period" />
+
+    <include
+        latin:keyboardLayout="@xml/key_f2" />
+
 </merge>
diff --git a/java/res/xml/rowkeys_armenian_phonetic1.xml b/java/res/xml/rowkeys_armenian_phonetic1.xml
new file mode 100644
index 0000000..1984fae
--- /dev/null
+++ b/java/res/xml/rowkeys_armenian_phonetic1.xml
@@ -0,0 +1,84 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+**
+** Copyright 2013, 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.
+*/
+-->
+
+<merge
+    xmlns:latin="http://schemas.android.com/apk/res/com.android.inputmethod.latin"
+>
+    <!-- U+0567: "է" ARMENIAN SMALL LETTER EH -->
+    <Key
+        latin:keyLabel="&#x0567;"
+        latin:keyHintLabel="1"
+        latin:additionalMoreKeys="1"
+        latin:keyLabelFlags="fontNormal" />
+    <!-- U+0569: "թ" ARMENIAN SMALL LETTER TO -->
+    <Key
+        latin:keyLabel="&#x0569;"
+        latin:keyHintLabel="2"
+        latin:additionalMoreKeys="2"
+        latin:keyLabelFlags="fontNormal" />
+    <!-- U+0583: "փ" ARMENIAN SMALL LETTER PIWR -->
+    <Key
+        latin:keyLabel="&#x0583;"
+        latin:keyHintLabel="3"
+        latin:additionalMoreKeys="3"
+        latin:keyLabelFlags="fontNormal" />
+    <!-- U+0571: "ձ" ARMENIAN SMALL LETTER JA -->
+    <Key
+        latin:keyLabel="&#x0571;"
+        latin:keyHintLabel="4"
+        latin:additionalMoreKeys="4"
+        latin:keyLabelFlags="fontNormal" />
+    <!-- U+057B: "ջ" ARMENIAN SMALL LETTER JHEH -->
+    <Key
+        latin:keyLabel="&#x057B;"
+        latin:keyHintLabel="5"
+        latin:additionalMoreKeys="5"
+        latin:keyLabelFlags="fontNormal" />
+    <!-- U+0580: "ր" ARMENIAN SMALL LETTER REH -->
+    <Key
+        latin:keyLabel="&#x0580;"
+        latin:keyHintLabel="6"
+        latin:additionalMoreKeys="6"
+        latin:keyLabelFlags="fontNormal" />
+    <!-- U+0579: "չ" ARMENIAN SMALL LETTER CHA -->
+    <Key
+        latin:keyLabel="&#x0579;"
+        latin:keyHintLabel="7"
+        latin:additionalMoreKeys="7"
+        latin:keyLabelFlags="fontNormal" />
+    <!-- U+0573: "ճ" ARMENIAN SMALL LETTER CHEH -->
+    <Key
+        latin:keyLabel="&#x0573;"
+        latin:keyHintLabel="8"
+        latin:additionalMoreKeys="8"
+        latin:keyLabelFlags="fontNormal" />
+    <!-- U+056A: "ժ" ARMENIAN SMALL LETTER ZHE -->
+    <Key
+        latin:keyLabel="&#x056A;"
+        latin:keyHintLabel="9"
+        latin:additionalMoreKeys="9"
+        latin:keyLabelFlags="fontNormal" />
+    <!-- U+056E: "ծ" ARMENIAN SMALL LETTER CA -->
+    <Key
+        latin:keyLabel="&#x056E;"
+        latin:keyHintLabel="0"
+        latin:additionalMoreKeys="0"
+        latin:keyLabelFlags="fontNormal" />
+</merge>
diff --git a/java/res/xml/rowkeys_armenian_phonetic2.xml b/java/res/xml/rowkeys_armenian_phonetic2.xml
new file mode 100644
index 0000000..5dcabc3
--- /dev/null
+++ b/java/res/xml/rowkeys_armenian_phonetic2.xml
@@ -0,0 +1,66 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+**
+** Copyright 2013, 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.
+*/
+-->
+
+<merge
+    xmlns:latin="http://schemas.android.com/apk/res/com.android.inputmethod.latin"
+>
+    <!-- U+0584: "ք" ARMENIAN SMALL LETTER KEH -->
+    <Key
+        latin:keyLabel="&#x0584;"
+        latin:keyLabelFlags="fontNormal" />
+    <!-- U+0578: "ո" ARMENIAN SMALL LETTER VO -->
+    <Key
+        latin:keyLabel="&#x0578;"
+        latin:keyLabelFlags="fontNormal" />
+    <!-- U+0565: "ե" ARMENIAN SMALL LETTER ECH
+         U+0587: "և" ARMENIAN SMALL LIGATURE ECH YIWN -->
+    <Key
+        latin:keyLabel="&#x0565;"
+        latin:moreKeys="&#x0587;"
+        latin:keyLabelFlags="fontNormal" />
+    <!-- U+057C: "ռ" ARMENIAN SMALL LETTER RA -->
+    <Key
+        latin:keyLabel="&#x057C;"
+        latin:keyLabelFlags="fontNormal" />
+    <!-- U+057F: "տ" ARMENIAN SMALL LETTER TIWN -->
+    <Key
+        latin:keyLabel="&#x057F;"
+        latin:keyLabelFlags="fontNormal" />
+    <!-- U+0568: "ը" ARMENIAN SMALL LETTER ET -->
+    <Key
+        latin:keyLabel="&#x0568;"
+        latin:keyLabelFlags="fontNormal" />
+    <!-- U+0582: "ւ" ARMENIAN SMALL LETTER YIWN -->
+    <Key
+        latin:keyLabel="&#x0582;"
+        latin:keyLabelFlags="fontNormal" />
+    <!-- U+056B: "ի" ARMENIAN SMALL LETTER INI -->
+    <Key
+        latin:keyLabel="&#x056B;"
+        latin:keyLabelFlags="fontNormal" />
+    <!-- U+0585: "օ" ARMENIAN SMALL LETTER OH -->
+    <Key
+        latin:keyLabel="&#x0585;"
+        latin:keyLabelFlags="fontNormal" />
+    <!-- U+057A: "պ" ARMENIAN SMALL LETTER PEH -->
+    <Key
+        latin:keyLabel="&#x057A;"
+        latin:keyLabelFlags="fontNormal" />
+</merge>
diff --git a/java/res/xml/rowkeys_armenian_phonetic3.xml b/java/res/xml/rowkeys_armenian_phonetic3.xml
new file mode 100644
index 0000000..3116811
--- /dev/null
+++ b/java/res/xml/rowkeys_armenian_phonetic3.xml
@@ -0,0 +1,60 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+**
+** Copyright 2013, 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.
+*/
+-->
+
+<merge
+    xmlns:latin="http://schemas.android.com/apk/res/com.android.inputmethod.latin"
+>
+    <!-- U+0561: "ա" ARMENIAN SMALL LETTER AYB -->
+    <Key
+        latin:keyLabel="&#x0561;"
+        latin:keyLabelFlags="fontNormal" />
+    <!-- U+057D: "ս" ARMENIAN SMALL LETTER SEH -->
+    <Key
+        latin:keyLabel="&#x057D;"
+        latin:keyLabelFlags="fontNormal" />
+    <!-- U+0564: "դ" ARMENIAN SMALL LETTER DA -->
+    <Key
+        latin:keyLabel="&#x0564;"
+        latin:keyLabelFlags="fontNormal" />
+    <!-- U+0586: "ֆ" ARMENIAN SMALL LETTER FEH -->
+    <Key
+        latin:keyLabel="&#x0586;"
+        latin:keyLabelFlags="fontNormal" />
+    <!-- U+0563: "գ" ARMENIAN SMALL LETTER GIM -->
+    <Key
+        latin:keyLabel="&#x0563;"
+        latin:keyLabelFlags="fontNormal" />
+    <!-- U+0570: "հ" ARMENIAN SMALL LETTER HO -->
+    <Key
+        latin:keyLabel="&#x0570;"
+        latin:keyLabelFlags="fontNormal" />
+    <!-- U+0575: "յ" ARMENIAN SMALL LETTER YI -->
+    <Key
+        latin:keyLabel="&#x0575;"
+        latin:keyLabelFlags="fontNormal" />
+    <!-- U+056F: "կ" ARMENIAN SMALL LETTER KEN -->
+    <Key
+        latin:keyLabel="&#x056F;"
+        latin:keyLabelFlags="fontNormal" />
+    <!-- U+056C: "լ" ARMENIAN SMALL LETTER LIWN -->
+    <Key
+        latin:keyLabel="&#x056C;"
+        latin:keyLabelFlags="fontNormal" />
+</merge>
diff --git a/java/res/xml/rowkeys_armenian_phonetic4.xml b/java/res/xml/rowkeys_armenian_phonetic4.xml
new file mode 100644
index 0000000..922481a
--- /dev/null
+++ b/java/res/xml/rowkeys_armenian_phonetic4.xml
@@ -0,0 +1,52 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+**
+** Copyright 2013, 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.
+*/
+-->
+
+<merge
+    xmlns:latin="http://schemas.android.com/apk/res/com.android.inputmethod.latin"
+>
+    <!-- U+0566: "զ" ARMENIAN SMALL LETTER ZA -->
+    <Key
+        latin:keyLabel="&#x0566;"
+        latin:keyLabelFlags="fontNormal" />
+    <!-- U+0572: "ղ" ARMENIAN SMALL LETTER GHAD -->
+    <Key
+        latin:keyLabel="&#x0572;"
+        latin:keyLabelFlags="fontNormal" />
+    <!-- U+0581: "ց" ARMENIAN SMALL LETTER CO -->
+    <Key
+        latin:keyLabel="&#x0581;"
+        latin:keyLabelFlags="fontNormal" />
+    <!-- U+057E: "վ" ARMENIAN SMALL LETTER VEW -->
+    <Key
+        latin:keyLabel="&#x057E;"
+        latin:keyLabelFlags="fontNormal" />
+    <!-- U+0562: "բ" ARMENIAN SMALL LETTER BEN -->
+    <Key
+        latin:keyLabel="&#x0562;"
+        latin:keyLabelFlags="fontNormal" />
+    <!-- U+0576: "ն" ARMENIAN SMALL LETTER NOW -->
+    <Key
+        latin:keyLabel="&#x0576;"
+        latin:keyLabelFlags="fontNormal" />
+    <!-- U+0574: "մ" ARMENIAN SMALL LETTER MEN -->
+    <Key
+        latin:keyLabel="&#x0574;"
+        latin:keyLabelFlags="fontNormal" />
+</merge>
diff --git a/java/res/xml/rowkeys_hindi1.xml b/java/res/xml/rowkeys_hindi1.xml
index 7457476..c0b3cb9 100644
--- a/java/res/xml/rowkeys_hindi1.xml
+++ b/java/res/xml/rowkeys_hindi1.xml
@@ -62,10 +62,12 @@
                 latin:keyLabel="&#x092D;"
                 latin:keyLabelFlags="fontNormal" />
             <!-- Because the font rendering system prior to API version 16 can't automatically
-                 render dotted circle for incomplete combining letter of Hindi, different set of
-                 Key definitions are needed based on the API version. -->
+                 render dotted circle for incomplete combining letter of some scripts, different
+                 set of Key definitions are needed based on the API version. -->
             <include
-                latin:keyboardLayout="@xml/key_hindi1_shift" />
+                latin:keyboardLayout="@xml/keystyle_devanagari_sign_visarga" />
+            <Key
+                latin:keyStyle="baseKeyDevanagariSignVisarga" />
             <!-- U+0918: "घ" DEVANAGARI LETTER GHA -->
             <Key
                 latin:keyLabel="&#x0918;"
@@ -88,11 +90,57 @@
         </case>
         <default>
             <!-- Because the font rendering system prior to API version 16 can't automatically
-                 render dotted circle for incomplete combining letter of Hindi, different set of
-                 Key definitions are needed based on the API version. -->
+                 render dotted circle for incomplete combining letter of some scripts, different
+                 set of Key definitions are needed based on the API version. -->
+            <!-- U+0967: "१" DEVANAGARI DIGIT ONE -->
             <include
-                latin:keyboardLayout="@xml/keys_hindi1_left5" />
+                latin:keyboardLayout="@xml/keystyle_devanagari_vowel_sign_au" />
+            <Key
+                latin:keyStyle="baseKeyDevanagariVowelSignAu"
+                latin:keyHintLabel="1"
+                latin:additionalMoreKeys="&#x0967;,1" />
+            <!-- Because the font rendering system prior to API version 16 can't automatically
+                 render dotted circle for incomplete combining letter of some scripts, different
+                 set of Key definitions are needed based on the API version. -->
+            <!-- U+0968: "२" DEVANAGARI DIGIT TWO -->
+            <include
+                latin:keyboardLayout="@xml/keystyle_devanagari_vowel_sign_ai" />
+            <Key
+                latin:keyStyle="baseKeyDevanagariVowelSignAi"
+                latin:keyHintLabel="2"
+                latin:additionalMoreKeys="&#x0968;,2" />
+            <!-- Because the font rendering system prior to API version 16 can't automatically
+                 render dotted circle for incomplete combining letter of some scripts, different
+                 set of Key definitions are needed based on the API version. -->
+            <!-- U+0969: "३" DEVANAGARI DIGIT THREE -->
+            <include
+                latin:keyboardLayout="@xml/keystyle_devanagari_vowel_sign_aa" />
+            <Key
+                latin:keyStyle="baseKeyDevanagariVowelSignAa"
+                latin:keyHintLabel="3"
+                latin:additionalMoreKeys="&#x0969;,3" />
+            <!-- Because the font rendering system prior to API version 16 can't automatically
+                 render dotted circle for incomplete combining letter of some scripts, different
+                 set of Key definitions are needed based on the API version. -->
+            <!-- U+096A: "४" DEVANAGARI DIGIT FOUR -->
+            <include
+                latin:keyboardLayout="@xml/keystyle_devanagari_vowel_sign_ii" />
+            <Key
+                latin:keyStyle="baseKeyDevanagariVowelSignIi"
+                latin:keyHintLabel="4"
+                latin:additionalMoreKeys="&#x096A;,4" />
+            <!-- Because the font rendering system prior to API version 16 can't automatically
+                 render dotted circle for incomplete combining letter of some scripts, different
+                 set of Key definitions are needed based on the API version. -->
+            <!-- U+096B: "५" DEVANAGARI DIGIT FIVE -->
+            <include
+                latin:keyboardLayout="@xml/keystyle_devanagari_vowel_sign_uu" />
+            <Key
+                latin:keyStyle="baseKeyDevanagariVowelSignUu"
+                latin:keyHintLabel="5"
+                latin:additionalMoreKeys="&#x096B;,5" />
             <!-- U+092C: "ब" DEVANAGARI LETTER BA
+                 U+096C: "६" DEVANAGARI DIGIT SIX
                  U+092C/U+0952: "ब॒" DEVANAGARI LETTER BA/DEVANAGARI STRESS SIGN ANUDATTA -->
             <Key
                 latin:keyLabel="&#x092C;"
diff --git a/java/res/xml/rowkeys_hindi2.xml b/java/res/xml/rowkeys_hindi2.xml
index 9545b84..70ac66e 100644
--- a/java/res/xml/rowkeys_hindi2.xml
+++ b/java/res/xml/rowkeys_hindi2.xml
@@ -97,10 +97,40 @@
         </case>
         <default>
             <!-- Because the font rendering system prior to API version 16 can't automatically
-                 render dotted circle for incomplete combining letter of Hindi, different set of
-                 Key definitions are needed based on the API version. -->
+                 render dotted circle for incomplete combining letter of some scripts, different
+                 set of Key definitions are needed based on the API version. -->
             <include
-                latin:keyboardLayout="@xml/keys_hindi2_left5" />
+                latin:keyboardLayout="@xml/keystyle_devanagari_vowel_sign_o" />
+            <Key
+                latin:keyStyle="baseKeyDevanagariVowelSignO" />
+            <!-- Because the font rendering system prior to API version 16 can't automatically
+                 render dotted circle for incomplete combining letter of some scripts, different
+                 set of Key definitions are needed based on the API version. -->
+            <include
+                latin:keyboardLayout="@xml/keystyle_devanagari_vowel_sign_e" />
+            <Key
+                latin:keyStyle="baseKeyDevanagariVowelSignE" />
+            <!-- Because the font rendering system prior to API version 16 can't automatically
+                 render dotted circle for incomplete combining letter of some scripts, different
+                 set of Key definitions are needed based on the API version. -->
+            <include
+                latin:keyboardLayout="@xml/keystyle_devanagari_sign_virama" />
+            <Key
+                latin:keyStyle="baseKeyDevanagariSignVirama" />
+            <!-- Because the font rendering system prior to API version 16 can't automatically
+                 render dotted circle for incomplete combining letter of some scripts, different
+                 set of Key definitions are needed based on the API version. -->
+            <include
+                latin:keyboardLayout="@xml/keystyle_devanagari_vowel_sign_i" />
+            <Key
+                latin:keyStyle="baseKeyDevanagariVowelSignI" />
+            <!-- Because the font rendering system prior to API version 16 can't automatically
+                 render dotted circle for incomplete combining letter of some scripts, different
+                 set of Key definitions are needed based on the API version. -->
+            <include
+                latin:keyboardLayout="@xml/keystyle_devanagari_vowel_sign_u" />
+            <Key
+                latin:keyStyle="baseKeyDevanagariVowelSignU" />
             <!-- U+092A: "प" DEVANAGARI LETTER PA -->
             <Key
                 latin:keyLabel="&#x092A;"
diff --git a/java/res/xml/rowkeys_hindi3.xml b/java/res/xml/rowkeys_hindi3.xml
index 3014907..136bc5f 100644
--- a/java/res/xml/rowkeys_hindi3.xml
+++ b/java/res/xml/rowkeys_hindi3.xml
@@ -30,10 +30,10 @@
                 latin:keyLabel="&#x0911;"
                 latin:keyLabelFlags="fontNormal" />
             <!-- Because the font rendering system prior to API version 16 can't automatically
-                 render dotted circle for incomplete combining letter of Hindi, different set of
-                 Key definitions are needed based on the API version. -->
+                 render dotted circle for incomplete combining letter of some scripts, different
+                 set of Key definitions are needed based on the API version. -->
             <include
-                latin:keyboardLayout="@xml/key_hindi3_shift_left" />
+                latin:keyboardLayout="@xml/key_devanagari_sign_candrabindu" />
             <!-- U+0923: "ण" DEVANAGARI LETTER NNA -->
             <Key
                 latin:keyLabel="&#x0923;"
@@ -56,10 +56,10 @@
                 latin:keyLabel="&#x0937;"
                 latin:keyLabelFlags="fontNormal" />
             <!-- Because the font rendering system prior to API version 16 can't automatically
-                 render dotted circle for incomplete combining letter of Hindi, different set of
-                 Key definitions are needed based on the API version. -->
+                 render dotted circle for incomplete combining letter of some scripts, different
+                 set of Key definitions are needed based on the API version. -->
             <include
-                latin:keyboardLayout="@xml/key_hindi3_shift_right" />
+                latin:keyboardLayout="@xml/key_devanagari_vowel_sign_vocalic_r" />
             <!-- U+091E: "ञ" DEVANAGARI LETTER NYA -->
             <Key
                 latin:keyLabel="&#x091E;"
@@ -67,10 +67,12 @@
         </case>
         <default>
             <!-- Because the font rendering system prior to API version 16 can't automatically
-                 render dotted circle for incomplete combining letter of Hindi, different set of
-                 Key definitions are needed based on the API version. -->
+                 render dotted circle for incomplete combining letter of some scripts, different
+                 set of Key definitions are needed based on the API version. -->
             <include
-                latin:keyboardLayout="@xml/keys_hindi3_left2" />
+                latin:keyboardLayout="@xml/key_devanagari_vowel_sign_candra_o" />
+            <include
+                latin:keyboardLayout="@xml/key_devanagari_sign_anusvara" />
             <!-- U+092E: "म" DEVANAGARI LETTER MA
                  U+0950: "ॐ" DEVANAGARI OM -->
             <Key
@@ -107,10 +109,10 @@
                 latin:moreKeys="&#x095F;"
                 latin:keyLabelFlags="fontNormal" />
             <!-- Because the font rendering system prior to API version 16 can't automatically
-                 render dotted circle for incomplete combining letter of Hindi, different set of
-                 Key definitions are needed based on the API version. -->
+                 render dotted circle for incomplete combining letter of some scripts, different
+                 set of Key definitions are needed based on the API version. -->
             <include
-                latin:keyboardLayout="@xml/key_hindi3_right" />
+                latin:keyboardLayout="@xml/key_devanagari_sign_nukta" />
          </default>
     </switch>
 </merge>
diff --git a/java/res/xml/rowkeys_khmer1.xml b/java/res/xml/rowkeys_khmer1.xml
new file mode 100644
index 0000000..25da664
--- /dev/null
+++ b/java/res/xml/rowkeys_khmer1.xml
@@ -0,0 +1,194 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+**
+** Copyright 2013, 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.
+*/
+-->
+
+<merge
+    xmlns:latin="http://schemas.android.com/apk/res/com.android.inputmethod.latin"
+>
+    <switch>
+        <case
+            latin:keyboardLayoutSetElement="alphabetManualShifted|alphabetShiftLocked|alphabetShiftLockShifted"
+        >
+            <!-- U+200D: ZERO WIDTH JOINER -->
+            <Key
+                latin:keyLabel="!"
+                latin:moreKeys="!icon/zwj_key|&#x200D;" />
+            <!-- U+17D7: "ៗ" KHMER SIGN LEK TOO
+                 U+200C: ZERO WIDTH NON-JOINER -->
+            <Key
+                latin:keyLabel="&#x17D7;"
+                latin:moreKeys="!icon/zwnj_key|&#x200C;"
+                latin:keyLabelFlags="fontNormal" />
+            <!-- U+17D1: "៑" KHMER SIGN VIRIAM -->
+            <Key
+                latin:keyLabel="&quot;"
+                latin:keyHintLabel="&#x17D1;"
+                latin:moreKeys="&#x17D1;" />
+            <!-- U+17DB: "៛" KHMER CURRENCY SYMBOL RIEL
+                 U+20AC: "€" EURO SIGN -->
+            <Key
+                latin:keyLabel="&#x17DB;"
+                latin:keyHintLabel="$"
+                latin:moreKeys="$,&#x20AC;"
+                latin:keyLabelFlags="fontNormal" />
+            <!-- U+17D6: "៖" KHMER SIGN CAMNUC PII KUUH -->
+            <Key
+                latin:keyLabel="%"
+                latin:keyHintLabel="&#x17D6;"
+                latin:moreKeys="&#x17D6;" />
+            <!-- U+17CD: "៍" KHMER SIGN TOANDAKHIAT
+                 U+17D9: "៙" KHMER SIGN PHNAEK MUAN -->
+            <Key
+                latin:keyLabel="&#x17CD;"
+                latin:keyHintLabel="&#x17D9;"
+                latin:moreKeys="&#x17D9;"
+                latin:keyLabelFlags="fontNormal" />
+            <!-- U+17D0: "័" KHMER SIGN SAMYOK SANNYA
+                 U+17DA: "៚" KHMER SIGN KOOMUUT -->
+            <Key
+                latin:keyLabel="&#x17D0;"
+                latin:keyHintLabel="&#x17DA;"
+                latin:moreKeys="&#x17DA;"
+                latin:keyLabelFlags="fontNormal" />
+            <!-- U+17CF: "៏" KHMER SIGN AHSDA -->
+            <Key
+                latin:keyLabel="&#x17CF;"
+                latin:keyHintLabel="*"
+                latin:moreKeys="*"
+                latin:keyLabelFlags="fontNormal" />
+            <!-- U+00AB: "«" LEFT-POINTING DOUBLE ANGLE QUOTATION MARK -->
+            <Key
+                latin:keyLabel="("
+                latin:keyHintLabel="{"
+                latin:moreKeys="{,&#x00AB;" />
+            <!-- U+00BB: "»" RIGHT-POINTING DOUBLE ANGLE QUOTATION MARK -->
+            <Key
+                latin:keyLabel=")"
+                latin:keyHintLabel="}"
+                latin:moreKeys="},&#x00BB;" />
+            <!-- U+17CC: "៌" KHMER SIGN ROBAT
+                 U+00D7: "×" MULTIPLICATION SIGN -->
+            <Key
+                latin:keyLabel="&#x17CC;"
+                latin:keyHintLabel="&#x00D7;"
+                latin:moreKeys="&#x00D7;"
+                latin:keyLabelFlags="fontNormal" />
+            <!-- U+17CE: "៎" KHMER SIGN KAKABAT -->
+            <Key
+                latin:keyLabel="&#x17CE;"
+                latin:keyLabelFlags="fontNormal" />
+        </case>
+        <default>
+            <!-- U+17E1: "១" KHMER DIGIT ONE
+                 U+17F1: "៱" KHMER SYMBOL LEK ATTAK MUOY -->
+            <Key
+                latin:keyLabel="&#x17E1;"
+                latin:keyHintLabel="1"
+                latin:additionalMoreKeys="1"
+                latin:moreKeys="&#x17F1;"
+                latin:keyLabelFlags="fontNormal" />
+            <!-- U+17E2: "២" KHMER DIGIT TWO
+                 U+17F2: "៲" KHMER SYMBOL LEK ATTAK PII -->
+            <Key
+                latin:keyLabel="&#x17E2;"
+                latin:keyHintLabel="2"
+                latin:additionalMoreKeys="2"
+                latin:moreKeys="&#x17F2;"
+                latin:keyLabelFlags="fontNormal" />
+            <!-- U+17E3: "៣" KHMER DIGIT THREE
+                 U+17F3: "៳" KHMER SYMBOL LEK ATTAK BEI -->
+            <Key
+                latin:keyLabel="&#x17E3;"
+                latin:keyHintLabel="3"
+                latin:additionalMoreKeys="3"
+                latin:moreKeys="&#x17F3;"
+                latin:keyLabelFlags="fontNormal" />
+            <!-- U+17E4: "៤" KHMER DIGIT FOUR
+                 U+17F4: "៴" KHMER SYMBOL LEK ATTAK BUON -->
+            <Key
+                latin:keyLabel="&#x17E4;"
+                latin:keyHintLabel="4"
+                latin:additionalMoreKeys="4"
+                latin:moreKeys="&#x17F4;"
+                latin:keyLabelFlags="fontNormal" />
+            <!-- U+17E5: "៥" KHMER DIGIT FIVE
+                 U+17F5: "៵" KHMER SYMBOL LEK ATTAK PRAM -->
+            <Key
+                latin:keyLabel="&#x17E5;"
+                latin:keyHintLabel="5"
+                latin:additionalMoreKeys="5"
+                latin:moreKeys="&#x17F5;"
+                latin:keyLabelFlags="fontNormal" />
+            <!-- U+17E6: "៦" KHMER DIGIT SIX
+                 U+17F6: "៶" KHMER SYMBOL LEK ATTAK PRAM-MUOY -->
+            <Key
+                latin:keyLabel="&#x17E6;"
+                latin:keyHintLabel="6"
+                latin:additionalMoreKeys="6"
+                latin:moreKeys="&#x17F6;"
+                latin:keyLabelFlags="fontNormal" />
+            <!-- U+17E7: "៧" KHMER DIGIT SEVEN
+                 U+17F7: "៷" KHMER SYMBOL LEK ATTAK PRAM-PII -->
+            <Key
+                latin:keyLabel="&#x17E7;"
+                latin:keyHintLabel="7"
+                latin:additionalMoreKeys="7"
+                latin:moreKeys="&#x17F7;"
+                latin:keyLabelFlags="fontNormal" />
+            <!-- U+17E8: "៨" KHMER DIGIT EIGHT
+                 U+17F8: "៸" KHMER SYMBOL LEK ATTAK PRAM-BEI -->
+            <Key
+                latin:keyLabel="&#x17E8;"
+                latin:keyHintLabel="8"
+                latin:additionalMoreKeys="8"
+                latin:moreKeys="&#x17F8;"
+                latin:keyLabelFlags="fontNormal" />
+            <!-- U+17E9: "៩" KHMER DIGIT NINE
+                 U+17F9: "៹" KHMER SYMBOL LEK ATTAK PRAM-BUON -->
+            <Key
+                latin:keyLabel="&#x17E9;"
+                latin:keyHintLabel="9"
+                latin:additionalMoreKeys="9"
+                latin:moreKeys="&#x17F9;"
+                latin:keyLabelFlags="fontNormal" />
+            <!-- U+17E0: "០" KHMER DIGIT ZERO
+                 U+17F0: "៰" KHMER SYMBOL LEK ATTAK SON -->
+            <Key
+                latin:keyLabel="&#x17E0;"
+                latin:keyHintLabel="0"
+                latin:additionalMoreKeys="0"
+                latin:moreKeys="&#x17F0;"
+                latin:keyLabelFlags="fontNormal" />
+            <!-- U+17A5: "ឥ" KHMER INDEPENDENT VOWEL QI
+                 U+17A6: "ឦ" KHMER INDEPENDENT VOWEL QII -->
+            <Key
+                latin:keyLabel="&#x17A5;"
+                latin:keyHintLabel="&#x17A6;"
+                latin:moreKeys=",&#x17A6;"
+                latin:keyLabelFlags="fontNormal" />
+            <!-- U+17B2: "ឲ" KHMER INDEPENDENT VOWEL QOO TYPE TWO
+                 U+17B1: "ឱ" KHMER INDEPENDENT VOWEL QOO TYPE ONE -->
+            <Key
+                latin:keyLabel="&#x17B2;"
+                latin:keyHintLabel="&#x17B1;"
+                latin:moreKeys="&#x17B1;"
+                latin:keyLabelFlags="fontNormal" />
+        </default>
+    </switch>
+</merge>
diff --git a/java/res/xml/rowkeys_khmer2.xml b/java/res/xml/rowkeys_khmer2.xml
new file mode 100644
index 0000000..cba2d3b
--- /dev/null
+++ b/java/res/xml/rowkeys_khmer2.xml
@@ -0,0 +1,144 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+**
+** Copyright 2013, 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.
+*/
+-->
+
+<merge
+    xmlns:latin="http://schemas.android.com/apk/res/com.android.inputmethod.latin"
+>
+    <switch>
+        <case
+            latin:keyboardLayoutSetElement="alphabetManualShifted|alphabetShiftLocked|alphabetShiftLockShifted"
+        >
+            <!-- U+1788: "ឈ" KHMER LETTER CHO
+                 U+17DC: "ៜ" KHMER SIGN AVAKRAHASANYA -->
+            <Key
+                latin:keyLabel="&#x1788;"
+                latin:keyHintLabel="&#x17DC;"
+                latin:moreKeys="&#x17DC;"
+                latin:keyLabelFlags="fontNormal" />
+            <!-- U+17BA: "ឺ" KHMER VOWEL SIGN YY
+                 U+17DD: "៝" KHMER SIGN ATTHACAN -->
+            <Key
+                latin:keyLabel="&#x17BA;"
+                latin:keyHintLabel="&#x17DD;"
+                latin:moreKeys="&#x17DD;"
+                latin:keyLabelFlags="fontNormal" />
+            <!-- U+17C2: "ែ" KHMER VOWEL SIGN AE -->
+            <Key
+                latin:keyLabel="&#x17C2;"
+                latin:keyLabelFlags="fontNormal" />
+            <!-- U+17AC: "ឬ" KHMER INDEPENDENT VOWEL RYY
+                 U+17AB: "ឫ" KHMER INDEPENDENT VOWEL RY -->
+            <Key
+                latin:keyLabel="&#x17AC;"
+                latin:keyHintLabel="&#x17AB;"
+                latin:moreKeys="&#x17AB;"
+                latin:keyLabelFlags="fontNormal" />
+            <!-- U+1791: "ទ" KHMER LETTER TO -->
+            <Key
+                latin:keyLabel="&#x1791;"
+                latin:keyLabelFlags="fontNormal" />
+            <!-- U+17BD: "ួ" KHMER VOWEL SIGN UA -->
+            <Key
+                latin:keyLabel="&#x17BD;"
+                latin:keyLabelFlags="fontNormal" />
+            <!-- U+17BC: "ូ" KHMER VOWEL SIGN UU -->
+            <Key
+                latin:keyLabel="&#x17BC;"
+                latin:keyLabelFlags="fontNormal" />
+            <!-- U+17B8: "ី" KHMER VOWEL SIGN II -->
+            <Key
+                latin:keyLabel="&#x17B8;"
+                latin:keyLabelFlags="fontNormal" />
+            <!-- U+17C5: "ៅ" KHMER VOWEL SIGN AU -->
+            <Key
+                latin:keyLabel="&#x17C5;"
+                latin:keyLabelFlags="fontNormal" />
+            <!-- U+1797: "ភ" KHMER LETTER PHO -->
+            <Key
+                latin:keyLabel="&#x1797;"
+                latin:keyLabelFlags="fontNormal" />
+            <!-- U+17BF: "ឿ" KHMER VOWEL SIGN YA -->
+            <Key
+                latin:keyLabel="&#x17BF;"
+                latin:keyLabelFlags="fontNormal" />
+            <!-- U+17B0: "ឰ" KHMER INDEPENDENT VOWEL QAI -->
+            <Key
+                latin:keyLabel="&#x17B0;"
+                latin:keyLabelFlags="fontNormal" />
+        </case>
+        <default>
+            <!-- U+1786: "ឆ" KHMER LETTER CHA -->
+            <Key
+                latin:keyLabel="&#x1786;"
+                latin:keyLabelFlags="fontNormal" />
+            <!-- U+17B9: "ឹ" KHMER VOWEL SIGN Y -->
+            <Key
+                latin:keyLabel="&#x17B9;"
+                latin:keyLabelFlags="fontNormal" />
+            <!-- U+17C1: "េ" KHMER VOWEL SIGN E -->
+            <Key
+                latin:keyLabel="&#x17C1;"
+                latin:keyLabelFlags="fontNormal" />
+            <!-- U+179A: "រ" KHMER LETTER RO -->
+            <Key
+                latin:keyLabel="&#x179A;"
+                latin:keyLabelFlags="fontNormal" />
+            <!-- U+178F: "ត" KHMER LETTER TA -->
+            <Key
+                latin:keyLabel="&#x178F;"
+                latin:keyLabelFlags="fontNormal" />
+            <!-- U+1799: "យ" KHMER LETTER YO -->
+            <Key
+                latin:keyLabel="&#x1799;"
+                latin:keyLabelFlags="fontNormal" />
+            <!-- U+17BB: "ុ" KHMER VOWEL SIGN U -->
+            <Key
+                latin:keyLabel="&#x17BB;"
+                latin:keyLabelFlags="fontNormal" />
+            <!-- U+17B7: "ិ" KHMER VOWEL SIGN I -->
+            <Key
+                latin:keyLabel="&#x17B7;"
+                latin:keyLabelFlags="fontNormal" />
+            <!-- U+17C4: "ោ" KHMER VOWEL SIGN OO -->
+            <Key
+                latin:keyLabel="&#x17C4;"
+                latin:keyLabelFlags="fontNormal" />
+            <!-- U+1795: "ផ" KHMER LETTER PHA -->
+            <Key
+                latin:keyLabel="&#x1795;"
+                latin:keyLabelFlags="fontNormal" />
+            <!-- U+17C0: "ៀ" KHMER VOWEL SIGN IE -->
+            <Key
+                latin:keyLabel="&#x17C0;"
+                latin:keyLabelFlags="fontNormal" />
+            <!-- U+17AA: "ឪ" KHMER INDEPENDENT VOWEL QUUV
+                 U+17A7: "ឧ" KHMER INDEPENDENT VOWEL QU
+                 U+17B1: "ឱ" KHMER INDEPENDENT VOWEL QOO TYPE ONE
+                 U+17B3: "ឳ" KHMER INDEPENDENT VOWEL QAU
+                 U+17A9: "ឩ" KHMER INDEPENDENT VOWEL QUU
+                 U+17A8: "ឨ" KHMER INDEPENDENT VOWEL QUK -->
+            <Key
+                latin:keyLabel="&#x17AA;"
+                latin:keyHintLabel="&#x17A7;"
+                latin:moreKeys="&#x17A7;,&#x17B1;,&#x17B3;,&#x17A9;,&#x17A8;"
+                latin:keyLabelFlags="fontNormal" />
+        </default>
+    </switch>
+</merge>
diff --git a/java/res/xml/rowkeys_khmer3.xml b/java/res/xml/rowkeys_khmer3.xml
new file mode 100644
index 0000000..ff6c9ca
--- /dev/null
+++ b/java/res/xml/rowkeys_khmer3.xml
@@ -0,0 +1,138 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+**
+** Copyright 2013, 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.
+*/
+-->
+
+<merge
+    xmlns:latin="http://schemas.android.com/apk/res/com.android.inputmethod.latin"
+>
+    <switch>
+        <case
+            latin:keyboardLayoutSetElement="alphabetManualShifted|alphabetShiftLocked|alphabetShiftLockShifted"
+        >
+            <!-- U+17B6/U+17C6: "ាំ" KHMER VOWEL SIGN AA/KHMER SIGN NIKAHIT -->
+            <Key
+                latin:keyLabel="&#x17B6;&#x17C6;"
+                latin:keyLabelFlags="fontNormal|followKeyLetterRatio" />
+            <!-- U+17C3: "ៃ" KHMER VOWEL SIGN AI -->
+            <Key
+                latin:keyLabel="&#x17C3;"
+                latin:keyLabelFlags="fontNormal" />
+            <!-- U+178C: "ឌ" KHMER LETTER DO -->
+            <Key
+                latin:keyLabel="&#x178C;"
+                latin:keyLabelFlags="fontNormal" />
+            <!-- U+1792: "ធ" KHMER LETTER THO -->
+            <Key
+                latin:keyLabel="&#x1792;"
+                latin:keyLabelFlags="fontNormal" />
+            <!-- U+17A2: "អ" KHMER LETTER QA -->
+            <Key
+                latin:keyLabel="&#x17A2;"
+                latin:keyLabelFlags="fontNormal" />
+            <!-- U+17C7: "ះ" KHMER SIGN REAHMUK
+                 U+17C8: "ៈ" KHMER SIGN YUUKALEAPINTU;-->
+            <Key
+                latin:keyLabel="&#x17C7;"
+                latin:keyHintLabel="&#x17C8;"
+                latin:moreKeys="&#x17C8;"
+                latin:keyLabelFlags="fontNormal" />
+            <!-- U+1789: "ញ" KHMER LETTER NYO -->
+            <Key
+                latin:keyLabel="&#x1789;"
+                latin:keyLabelFlags="fontNormal" />
+            <!-- U+1782: "គ" KHMER LETTER KO
+                 U+179D: "ឝ" KHMER LETTER SHA -->
+            <Key
+                latin:keyLabel="&#x1782;"
+                latin:keyHintLabel="&#x179D;"
+                latin:moreKeys="&#x179D;"
+                latin:keyLabelFlags="fontNormal" />
+            <!-- U+17A1: "ឡ" KHMER LETTER LA -->
+            <Key
+                latin:keyLabel="&#x17A1;"
+                latin:keyLabelFlags="fontNormal" />
+            <!-- U+17C4/U+17C7: "ោះ" KHMER VOWEL SIGN OO/KHMER SIGN REAHMUK -->
+            <Key
+                latin:keyLabel="&#x17C4;&#x17C7;"
+                latin:keyLabelFlags="fontNormal" />
+            <!-- U+17C9: "៉" KHMER SIGN MUUSIKATOAN -->
+            <Key
+                latin:keyLabel="&#x17C9;"
+                latin:keyLabelFlags="fontNormal" />
+            <!-- U+17AF: "ឯ" KHMER INDEPENDENT VOWEL QE -->
+            <Key
+                latin:keyLabel="&#x17AF;"
+                latin:keyLabelFlags="fontNormal" />
+        </case>
+        <default>
+            <!-- U+17B6: "ា" KHMER VOWEL SIGN AA -->
+            <Key
+                latin:keyLabel="&#x17B6;"
+                latin:keyLabelFlags="fontNormal" />
+            <!-- U+179F: "ស" KHMER LETTER SA -->
+            <Key
+                latin:keyLabel="&#x179F;"
+                latin:keyLabelFlags="fontNormal" />
+            <!-- U+178A: "ដ" KHMER LETTER DA -->
+            <Key
+                latin:keyLabel="&#x178A;"
+                latin:keyLabelFlags="fontNormal" />
+            <!-- U+1790: "ថ" KHMER LETTER THA -->
+            <Key
+                latin:keyLabel="&#x1790;"
+                latin:keyLabelFlags="fontNormal" />
+            <!-- U+1784: "ង" KHMER LETTER NGO -->
+            <Key
+                latin:keyLabel="&#x1784;"
+                latin:keyLabelFlags="fontNormal" />
+            <!-- U+17A0: "ហ" KHMER LETTER HA -->
+            <Key
+                latin:keyLabel="&#x17A0;"
+                latin:keyLabelFlags="fontNormal" />
+            <!-- U+17D2: "្" KHMER SIGN COENG -->
+            <Key
+                latin:keyLabel="&#x17D2;"
+                latin:keyLabelFlags="fontNormal" />
+            <!-- U+1780: "ក" KHMER LETTER KA -->
+            <Key
+                latin:keyLabel="&#x1780;"
+                latin:keyLabelFlags="fontNormal" />
+            <!-- U+179B: "ល" KHMER LETTER LO -->
+            <Key
+                latin:keyLabel="&#x179B;"
+                latin:keyLabelFlags="fontNormal" />
+            <!-- U+17BE: "ើ" KHMER VOWEL SIGN OE -->
+            <Key
+                latin:keyLabel="&#x17BE;"
+                latin:keyLabelFlags="fontNormal" />
+            <!-- U+17CB: "់" KHMER SIGN BANTOC -->
+            <Key
+                latin:keyLabel="&#x17CB;"
+                latin:keyLabelFlags="fontNormal" />
+            <!-- U+17AE: "ឮ" KHMER INDEPENDENT VOWEL LYY
+                 U+17AD: "ឭ" KHMER INDEPENDENT VOWEL LY
+                 U+17B0: "ឰ" KHMER INDEPENDENT VOWEL QAI -->
+            <Key
+                latin:keyLabel="&#x17AE;"
+                latin:keyHintLabel="&#x17AD;"
+                latin:moreKeys="&#x17AD;,&#x17B0;"
+                latin:keyLabelFlags="fontNormal" />
+        </default>
+    </switch>
+</merge>
diff --git a/java/res/xml/rowkeys_khmer4.xml b/java/res/xml/rowkeys_khmer4.xml
new file mode 100644
index 0000000..fe6c591
--- /dev/null
+++ b/java/res/xml/rowkeys_khmer4.xml
@@ -0,0 +1,113 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+**
+** Copyright 2013, 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.
+*/
+-->
+
+<merge
+    xmlns:latin="http://schemas.android.com/apk/res/com.android.inputmethod.latin"
+>
+    <switch>
+        <case
+            latin:keyboardLayoutSetElement="alphabetManualShifted|alphabetShiftLocked|alphabetShiftLockShifted"
+        >
+            <!-- U+178D: "ឍ" KHMER LETTER TTHO -->
+            <Key
+                latin:keyLabel="&#x178D;"
+                latin:keyLabelFlags="fontNormal" />
+            <!-- U+1783: "ឃ" KHMER LETTER KHO -->
+            <Key
+                latin:keyLabel="&#x1783;"
+                latin:keyLabelFlags="fontNormal" />
+            <!-- U+1787: "ជ" KHMER LETTER CO -->
+            <Key
+                latin:keyLabel="&#x1787;"
+                latin:keyLabelFlags="fontNormal" />
+            <!-- U+17C1/U+17C7: "េះ" KHMER VOWEL SIGN E/KHMER SIGN REAHMUK -->
+            <Key
+                latin:keyLabel="&#x17C1;&#x17C7;"
+                latin:keyLabelFlags="fontNormal" />
+            <!-- U+1796: "ព" KHMER LETTER PO
+                 U+179E: "ឞ" KHMER LETTER SSO -->
+            <Key
+                latin:keyLabel="&#x1796;"
+                latin:keyHintLabel="&#x179E;"
+                latin:moreKeys="&#x179E;"
+                latin:keyLabelFlags="fontNormal" />
+            <!-- U+178E: "ណ" KHMER LETTER NNO -->
+            <Key
+                latin:keyLabel="&#x178E;"
+                latin:keyLabelFlags="fontNormal" />
+            <!-- U+17C6: "ំ" KHMER SIGN NIKAHIT -->
+            <Key
+                latin:keyLabel="&#x17C6;"
+                latin:keyLabelFlags="fontNormal" />
+            <!-- U+17BB/U+17C7: "ុះ" KHMER VOWEL SIGN U/KHMER SIGN REAHMUK -->
+            <Key
+                latin:keyLabel="&#x17BB;&#x17C7;"
+                latin:keyLabelFlags="fontNormal|followKeyLetterRatio" />
+            <!-- U+17D5: "៕" KHMER SIGN BARIYOOSAN -->
+            <Key
+                latin:keyLabel="&#x17D5;"
+                latin:keyLabelFlags="fontNormal" />
+            <Key
+                latin:keyLabel="\?" />
+        </case>
+        <default>
+            <!-- U+178B: "ឋ" KHMER LETTER TTHA -->
+            <Key
+                latin:keyLabel="&#x178B;"
+                latin:keyLabelFlags="fontNormal" />
+            <!-- U+1781: "ខ" KHMER LETTER KHA -->
+            <Key
+                latin:keyLabel="&#x1781;"
+                latin:keyLabelFlags="fontNormal" />
+            <!-- U+1785: "ច" KHMER LETTER CA -->
+            <Key
+                latin:keyLabel="&#x1785;"
+                latin:keyLabelFlags="fontNormal" />
+            <!-- U+179C: "វ" KHMER LETTER VO -->
+            <Key
+                latin:keyLabel="&#x179C;"
+                latin:keyLabelFlags="fontNormal" />
+            <!-- U+1794: "ប" KHMER LETTER BA -->
+            <Key
+                latin:keyLabel="&#x1794;"
+                latin:keyLabelFlags="fontNormal" />
+            <!-- U+1793: "ន" KHMER LETTER NO -->
+            <Key
+                latin:keyLabel="&#x1793;"
+                latin:keyLabelFlags="fontNormal" />
+            <!-- U+1798: "ម" KHMER LETTER MO -->
+            <Key
+                latin:keyLabel="&#x1798;"
+                latin:keyLabelFlags="fontNormal" />
+            <!-- U+17BB/U+17C6: "ុំ" KHMER VOWEL SIGN U/KHMER SIGN NIKAHIT -->
+            <Key
+                latin:keyLabel="&#x17BB;&#x17C6;"
+                latin:keyLabelFlags="fontNormal|followKeyLetterRatio" />
+            <!-- U+17D4: "។" KHMER SIGN KHAN -->
+            <Key
+                latin:keyLabel="&#x17D4;"
+                latin:keyLabelFlags="fontNormal" />
+            <!-- U+17CA: "៊" KHMER SIGN TRIISAP -->
+            <Key
+                latin:keyLabel="&#x17CA;"
+                latin:keyLabelFlags="fontNormal" />
+        </default>
+    </switch>
+</merge>
diff --git a/java/res/xml/rowkeys_lao1.xml b/java/res/xml/rowkeys_lao1.xml
new file mode 100644
index 0000000..fa1ad97
--- /dev/null
+++ b/java/res/xml/rowkeys_lao1.xml
@@ -0,0 +1,164 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+**
+** Copyright 2013, 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.
+*/
+-->
+
+<merge
+    xmlns:latin="http://schemas.android.com/apk/res/com.android.inputmethod.latin"
+>
+    <switch>
+        <case
+            latin:keyboardLayoutSetElement="alphabetManualShifted|alphabetShiftLocked|alphabetShiftLockShifted"
+        >
+            <!-- U+0ED1: "໑" LAO DIGIT ONE -->
+            <Key
+                latin:keyLabel="&#x0ED1;"
+                latin:keyLabelFlags="fontNormal" />
+            <!-- U+0ED2: "໒" LAO DIGIT TWO -->
+            <Key
+                latin:keyLabel="&#x0ED2;"
+                latin:keyLabelFlags="fontNormal" />
+            <!-- U+0ED3: "໓" LAO DIGIT THREE -->
+            <Key
+                latin:keyLabel="&#x0ED3;"
+                latin:keyLabelFlags="fontNormal" />
+            <!-- U+0ED4: "໔" LAO DIGIT FOUR -->
+            <Key
+                latin:keyLabel="&#x0ED4;"
+                latin:keyLabelFlags="fontNormal" />
+            <!-- U+0ECC: "໌" LAO CANCELLATION MARK -->
+            <Key
+                latin:keyLabel="&#x0ECC;"
+                latin:keyLabelFlags="fontNormal" />
+            <!-- U+0EBC: "ຼ" LAO SEMIVOWEL SIGN LO -->
+            <Key
+                latin:keyLabel="&#x0EBC;"
+                latin:keyLabelFlags="fontNormal" />
+            <!-- U+0ED5: "໕" LAO DIGIT FIVE -->
+            <Key
+                latin:keyLabel="&#x0ED5;"
+                latin:keyLabelFlags="fontNormal" />
+            <!-- U+0ED6: "໖" LAO DIGIT SIX -->
+            <Key
+                latin:keyLabel="&#x0ED6;"
+                latin:keyLabelFlags="fontNormal" />
+            <!-- U+0ED7: "໗" LAO DIGIT SEVEN -->
+            <Key
+                latin:keyLabel="&#x0ED7;"
+                latin:keyLabelFlags="fontNormal" />
+            <!-- U+0ED8: "໘" LAO DIGIT EIGHT -->
+            <Key
+                latin:keyLabel="&#x0ED8;"
+                latin:keyLabelFlags="fontNormal" />
+            <!-- U+0ED9: "໙" LAO DIGIT NINE -->
+            <Key
+                latin:keyLabel="&#x0ED9;"
+                latin:keyLabelFlags="fontNormal" />
+            <!-- U+0ECD/U+0EC8: "ໍ່" LAO NIGGAHITA/LAO TONE MAI EK -->
+            <Key
+                latin:keyLabel="&#x0ECD;&#x0EC8;"
+                latin:keyLabelFlags="fontNormal|followKeyLetterRatio" />
+        </case>
+        <default>
+            <!-- U+0EA2: "ຢ" LAO LETTER YO
+                 U+0ED1: "໑" LAO DIGIT ONE -->
+            <Key
+                latin:keyLabel="&#x0EA2;"
+                latin:keyHintLabel="1"
+                latin:additionalMoreKeys="1"
+                latin:moreKeys="&#x0ED1;"
+                latin:keyLabelFlags="fontNormal" />
+            <!-- U+0E9F: "ຟ" LAO LETTER FO SUNG
+                 U+0ED2: "໒" LAO DIGIT TWO -->
+            <Key
+                latin:keyLabel="&#x0E9F;"
+                latin:keyHintLabel="2"
+                latin:additionalMoreKeys="2"
+                latin:moreKeys="&#x0ED2;"
+                latin:keyLabelFlags="fontNormal" />
+            <!-- U+0EC2: "ໂ" LAO VOWEL SIGN O
+                 U+0ED3: "໓" LAO DIGIT THREE -->
+            <Key
+                latin:keyLabel="&#x0EC2;"
+                latin:keyHintLabel="3"
+                latin:additionalMoreKeys="3"
+                latin:moreKeys="&#x0ED3;"
+                latin:keyLabelFlags="fontNormal" />
+            <!-- U+0E96: "ຖ" LAO LETTER THO SUNG
+                 U+0ED4: "໔" LAO DIGIT FOUR -->
+            <Key
+                latin:keyLabel="&#x0E96;"
+                latin:keyHintLabel="4"
+                latin:additionalMoreKeys="4"
+                latin:moreKeys="&#x0ED4;"
+                latin:keyLabelFlags="fontNormal" />
+            <!-- U+0EB8: "ຸ" LAO VOWEL SIGN U -->
+            <Key
+                latin:keyLabel="&#x0EB8;"
+                latin:keyLabelFlags="fontNormal" />
+            <!-- U+0EB9: "ູ" LAO VOWEL SIGN UU -->
+            <Key
+                latin:keyLabel="&#x0EB9;"
+                latin:keyLabelFlags="fontNormal" />
+            <!-- U+0E84: "ຄ" LAO LETTER KHO TAM
+                 U+0ED5: "໕" LAO DIGIT FIVE -->
+            <Key
+                latin:keyLabel="&#x0E84;"
+                latin:keyHintLabel="5"
+                latin:additionalMoreKeys="5"
+                latin:moreKeys="&#x0ED5;"
+                latin:keyLabelFlags="fontNormal" />
+            <!-- U+0E95: "ຕ" LAO LETTER TO
+                 U+0ED6: "໖" LAO DIGIT SIX -->
+            <Key
+                latin:keyLabel="&#x0E95;"
+                latin:keyHintLabel="6"
+                latin:additionalMoreKeys="6"
+                latin:moreKeys="&#x0ED6;"
+                latin:keyLabelFlags="fontNormal" />
+            <!-- U+0E88: "ຈ" LAO LETTER CO
+                 U+0ED7: "໗" LAO DIGIT SEVEN -->
+            <Key
+                latin:keyLabel="&#x0E88;"
+                latin:keyHintLabel="7"
+                latin:additionalMoreKeys="7"
+                latin:moreKeys="&#x0ED7;"
+                latin:keyLabelFlags="fontNormal" />
+            <!-- U+0E82: "ຂ" LAO LETTER KHO SUNG
+                 U+0ED8: "໘" LAO DIGIT EIGHT -->
+            <Key
+                latin:keyLabel="&#x0E82;"
+                latin:keyHintLabel="8"
+                latin:additionalMoreKeys="8"
+                latin:moreKeys="&#x0ED8;"
+                latin:keyLabelFlags="fontNormal" />
+            <!-- U+0E8A: "ຊ" LAO LETTER SO TAM
+                 U+0ED9: "໙" LAO DIGIT NINE -->
+            <Key
+                latin:keyLabel="&#x0E8A;"
+                latin:keyHintLabel="9"
+                latin:additionalMoreKeys="9"
+                latin:moreKeys="&#x0ED9;"
+                latin:keyLabelFlags="fontNormal" />
+            <!-- U+0ECD: "ໍ" LAO NIGGAHITA -->
+            <Key
+                latin:keyLabel="&#x0ECD;"
+                latin:keyLabelFlags="fontNormal" />
+        </default>
+    </switch>
+</merge>
diff --git a/java/res/xml/rowkeys_lao2.xml b/java/res/xml/rowkeys_lao2.xml
new file mode 100644
index 0000000..fca58ac
--- /dev/null
+++ b/java/res/xml/rowkeys_lao2.xml
@@ -0,0 +1,127 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+**
+** Copyright 2013, 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.
+*/
+-->
+
+<merge
+    xmlns:latin="http://schemas.android.com/apk/res/com.android.inputmethod.latin"
+>
+    <switch>
+        <case
+            latin:keyboardLayoutSetElement="alphabetManualShifted|alphabetShiftLocked|alphabetShiftLockShifted"
+        >
+            <!-- U+0EBB/U+0EC9: "" LAO VOWEL SIGN MAI KON/LAO TONE MAI THO -->
+            <Key
+                latin:keyLabel="&#x0EBB;&#x0EC9;"
+                latin:keyLabelFlags="fontNormal|followKeyLetterRatio" />
+            <!-- U+0ED0: "໐" LAO DIGIT ZERO -->
+            <Key
+                latin:keyLabel="&#x0ED0;"
+                latin:keyLabelFlags="fontNormal" />
+            <!-- U+0EB3/U+0EC9: "ຳ້" LAO VOWEL SIGN AM/LAO TONE MAI THO -->
+            <Key
+                latin:keyLabel="&#x0EB3;&#x0EC9;"
+                latin:keyLabelFlags="fontNormal|followKeyLetterRatio" />
+            <Key
+                latin:keyLabel="_" />
+            <Key
+                latin:keyLabel="+" />
+            <!-- U+0EB4/U+0EC9: "ິ້" LAO VOWEL SIGN I/LAO TONE MAI THO -->
+            <Key
+                latin:keyLabel="&#x0EB4;&#x0EC9;"
+                latin:keyLabelFlags="fontNormal|followKeyLetterRatio" />
+            <!-- U+0EB5/U+0EC9: "ີ້" LAO VOWEL SIGN II/LAO TONE MAI THO -->
+            <Key
+                latin:keyLabel="&#x0EB5;&#x0EC9;"
+                latin:keyLabelFlags="fontNormal|followKeyLetterRatio" />
+            <!-- U+0EA3: "ຣ" LAO LETTER LO LING -->
+            <Key
+                latin:keyLabel="&#x0EA3;"
+                latin:keyLabelFlags="fontNormal" />
+            <!-- U+0EDC: "ໜ" LAO HO NO -->
+            <Key
+                latin:keyLabel="&#x0EDC;"
+                latin:keyLabelFlags="fontNormal" />
+            <!-- U+0EBD: "ຽ" LAO SEMIVOWEL SIGN NYO -->
+            <Key
+                latin:keyLabel="&#x0EBD;"
+                latin:keyLabelFlags="fontNormal" />
+            <!-- U+0EAB/U+0EBC: "" LAO LETTER HO SUNG/LAO SEMIVOWEL SIGN LO -->
+            <Key
+                latin:keyLabel="&#x0EAB;&#x0EBC;"
+                latin:keyLabelFlags="fontNormal|followKeyLetterRatio" />
+            <!-- U+201D: "”" RIGHT DOUBLE QUOTATION MARK -->
+            <Key
+                latin:keyLabel="&#x201D;" />
+        </case>
+        <default>
+            <!-- U+0EBB: "ົ" LAO VOWEL SIGN MAI KON -->
+            <Key
+                latin:keyLabel="&#x0EBB;"
+                latin:keyLabelFlags="fontNormal" />
+            <!-- U+0EC4: "ໄ" LAO VOWEL SIGN AI
+                 U+0ED0: "໐" LAO DIGIT ZERO -->
+            <Key
+                latin:keyLabel="&#x0EC4;"
+                latin:keyHintLabel="0"
+                latin:additionalMoreKeys="0"
+                latin:moreKeys="&#x0ED0;"
+                latin:keyLabelFlags="fontNormal" />
+            <!-- U+0EB3: "ຳ" LAO VOWEL SIGN AM -->
+            <Key
+                latin:keyLabel="&#x0EB3;"
+                latin:keyLabelFlags="fontNormal" />
+            <!-- U+0E9E: "ພ" LAO LETTER PHO TAM -->
+            <Key
+                latin:keyLabel="&#x0E9E;"
+                latin:keyLabelFlags="fontNormal" />
+            <!-- U+0EB0: "ະ" LAO VOWEL SIGN A -->
+            <Key
+                latin:keyLabel="&#x0EB0;"
+                latin:keyLabelFlags="fontNormal" />
+            <!-- U+0EB4: "ິ" LAO VOWEL SIGN I -->
+            <Key
+                latin:keyLabel="&#x0EB4;"
+                latin:keyLabelFlags="fontNormal" />
+            <!-- U+0EB5: "ີ" LAO VOWEL SIGN II -->
+            <Key
+                latin:keyLabel="&#x0EB5;"
+                latin:keyLabelFlags="fontNormal" />
+            <!-- U+0EAE: "ຮ" LAO LETTER HO TAM -->
+            <Key
+                latin:keyLabel="&#x0EAE;"
+                latin:keyLabelFlags="fontNormal" />
+            <!-- U+0E99: "ນ" LAO LETTER NO -->
+            <Key
+                latin:keyLabel="&#x0E99;"
+                latin:keyLabelFlags="fontNormal" />
+            <!-- U+0E8D: "ຍ" LAO LETTER NYO -->
+            <Key
+                latin:keyLabel="&#x0E8D;"
+                latin:keyLabelFlags="fontNormal" />
+            <!-- U+0E9A: "ບ" LAO LETTER BO -->
+            <Key
+                latin:keyLabel="&#x0E9A;"
+                latin:keyLabelFlags="fontNormal" />
+            <!-- U+0EA5: "ລ" LAO LETTER LO LOOT -->
+            <Key
+                latin:keyLabel="&#x0EA5;"
+                latin:keyLabelFlags="fontNormal" />
+        </default>
+    </switch>
+</merge>
diff --git a/java/res/xml/rowkeys_lao3.xml b/java/res/xml/rowkeys_lao3.xml
new file mode 100644
index 0000000..2a6c2d1
--- /dev/null
+++ b/java/res/xml/rowkeys_lao3.xml
@@ -0,0 +1,110 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+**
+** Copyright 2013, 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.
+*/
+-->
+
+<merge
+    xmlns:latin="http://schemas.android.com/apk/res/com.android.inputmethod.latin"
+>
+    <switch>
+        <case
+            latin:keyboardLayoutSetElement="alphabetManualShifted|alphabetShiftLocked|alphabetShiftLockShifted"
+        >
+            <!-- U+0EB1/U+0EC9: "ັ້" LAO VOWEL SIGN MAI KAN/LAO TONE MAI THO -->
+            <Key
+                latin:keyLabel="&#x0EB1;&#x0EC9;"
+                latin:keyLabelFlags="fontNormal|followKeyLetterRatio" />
+            <Key
+                latin:keyLabel=";" />
+            <Key
+                latin:keyLabel="." />
+            <Key
+                latin:keyLabel="," />
+            <Key
+                latin:keyLabel=":" />
+            <!-- U+0ECA: "໊" LAO TONE MAI TI -->
+            <Key
+                latin:keyLabel="&#x0ECA;"
+                latin:keyLabelFlags="fontNormal" />
+            <!-- U+0ECB: "໋" LAO TONE MAI CATAWA -->
+            <Key
+                latin:keyLabel="&#x0ECB;"
+                latin:keyLabelFlags="fontNormal" />
+            <Key
+                latin:keyLabel="!" />
+            <Key
+                latin:keyLabel="\?" />
+            <Key
+                latin:keyLabel="%" />
+            <Key
+                latin:keyLabel="=" />
+            <!-- U+201C: "“" LEFT DOUBLE QUOTATION MARK -->
+            <Key
+                latin:keyLabel="&#x201C;" />
+        </case>
+        <default>
+            <!-- U+0EB1: "ັ" LAO VOWEL SIGN MAI KAN -->
+            <Key
+                latin:keyLabel="&#x0EB1;"
+                latin:keyLabelFlags="fontNormal" />
+            <!-- U+0EAB: "ຫ" LAO LETTER HO SUNG -->
+            <Key
+                latin:keyLabel="&#x0EAB;"
+                latin:keyLabelFlags="fontNormal" />
+            <!-- U+0E81: "ກ" LAO LETTER KO -->
+            <Key
+                latin:keyLabel="&#x0E81;"
+                latin:keyLabelFlags="fontNormal" />
+            <!-- U+0E94: "ດ" LAO LETTER DO -->
+            <Key
+                latin:keyLabel="&#x0E94;"
+                latin:keyLabelFlags="fontNormal" />
+            <!-- U+0EC0: "ເ" LAO VOWEL SIGN E -->
+            <Key
+                latin:keyLabel="&#x0EC0;"
+                latin:keyLabelFlags="fontNormal" />
+            <!-- U+0EC9: "້" LAO TONE MAI THO -->
+            <Key
+                latin:keyLabel="&#x0EC9;"
+                latin:keyLabelFlags="fontNormal" />
+            <!-- U+0EC8: "່" LAO TONE MAI EK -->
+            <Key
+                latin:keyLabel="&#x0EC8;"
+                latin:keyLabelFlags="fontNormal" />
+            <!-- U+0EB2: "າ" LAO VOWEL SIGN AA -->
+            <Key
+                latin:keyLabel="&#x0EB2;"
+                latin:keyLabelFlags="fontNormal" />
+            <!-- U+0EAA: "ສ" LAO LETTER SO SUNG -->
+            <Key
+                latin:keyLabel="&#x0EAA;"
+                latin:keyLabelFlags="fontNormal" />
+            <!-- U+0EA7: "ວ" LAO LETTER WO -->
+            <Key
+                latin:keyLabel="&#x0EA7;"
+                latin:keyLabelFlags="fontNormal" />
+            <!-- U+0E87: "ງ" LAO LETTER NGO -->
+            <Key
+                latin:keyLabel="&#x0E87;"
+                latin:keyLabelFlags="fontNormal" />
+            <!-- U+201C: "“" LEFT DOUBLE QUOTATION MARK -->
+            <Key
+                latin:keyLabel="&#x201C;" />
+        </default>
+    </switch>
+</merge>
diff --git a/java/res/xml/rowkeys_lao4.xml b/java/res/xml/rowkeys_lao4.xml
new file mode 100644
index 0000000..fae9cc9
--- /dev/null
+++ b/java/res/xml/rowkeys_lao4.xml
@@ -0,0 +1,103 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+**
+** Copyright 2013, 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.
+*/
+-->
+
+<merge
+    xmlns:latin="http://schemas.android.com/apk/res/com.android.inputmethod.latin"
+>
+    <switch>
+        <case
+            latin:keyboardLayoutSetElement="alphabetManualShifted|alphabetShiftLocked|alphabetShiftLockShifted"
+        >
+            <!-- U+20AD: "₭" KIP SIGN -->
+            <Key
+                latin:keyLabel="&#x20AD;" />
+            <Key
+                latin:keyLabel="(" />
+            <!-- U+0EAF: "ຯ" LAO ELLIPSIS -->
+            <Key
+                latin:keyLabel="&#x0EAF;"
+                latin:keyLabelFlags="fontNormal" />
+            <Key
+                latin:keyLabel="\@" />
+            <!-- U+0EB6/U+0EC9: "ຶ້" LAO VOWEL SIGN Y/LAO TONE MAI THO -->
+            <Key
+                latin:keyLabel="&#x0EB6;&#x0EC9;"
+                latin:keyLabelFlags="fontNormal|followKeyLetterRatio" />
+            <!-- U+0EB7/U+0EC9: "ື້" LAO VOWEL SIGN YY/LAO TONE MAI THO -->
+            <Key
+                latin:keyLabel="&#x0EB7;&#x0EC9;"
+                latin:keyLabelFlags="fontNormal|followKeyLetterRatio" />
+            <!-- U+0EC6: "ໆ" LAO KO LA -->
+            <Key
+                latin:keyLabel="&#x0EC6;"
+                latin:keyLabelFlags="fontNormal" />
+            <!-- U+0EDD: "ໝ" LAO HO MO -->
+            <Key
+                latin:keyLabel="&#x0EDD;"
+                latin:keyLabelFlags="fontNormal" />
+            <Key
+                latin:keyLabel="$" />
+            <Key
+                latin:keyLabel=")" />
+        </case>
+        <default>
+            <!-- U+0E9C: "ຜ" LAO LETTER PHO SUNG -->
+            <Key
+                latin:keyLabel="&#x0E9C;"
+                latin:keyLabelFlags="fontNormal" />
+            <!-- U+0E9B: "ປ" LAO LETTER PO -->
+            <Key
+                latin:keyLabel="&#x0E9B;"
+                latin:keyLabelFlags="fontNormal" />
+            <!-- U+0EC1: "ແ" LAO VOWEL SIGN EI -->
+            <Key
+                latin:keyLabel="&#x0EC1;"
+                latin:keyLabelFlags="fontNormal" />
+            <!-- U+0EAD: "ອ" LAO LETTER O -->
+            <Key
+                latin:keyLabel="&#x0EAD;"
+                latin:keyLabelFlags="fontNormal" />
+            <!-- U+0EB6: "ຶ" LAO VOWEL SIGN Y -->
+            <Key
+                latin:keyLabel="&#x0EB6;"
+                latin:keyLabelFlags="fontNormal" />
+            <!-- U+0EB7: "ື" LAO VOWEL SIGN YY -->
+            <Key
+                latin:keyLabel="&#x0EB7;"
+                latin:keyLabelFlags="fontNormal" />
+            <!-- U+0E97: "ທ" LAO LETTER THO TAM -->
+            <Key
+                latin:keyLabel="&#x0E97;"
+                latin:keyLabelFlags="fontNormal" />
+            <!-- U+0EA1: "ມ" LAO LETTER MO -->
+            <Key
+                latin:keyLabel="&#x0EA1;"
+                latin:keyLabelFlags="fontNormal" />
+            <!-- U+0EC3: "ໃ" LAO VOWEL SIGN AY -->
+            <Key
+                latin:keyLabel="&#x0EC3;"
+                latin:keyLabelFlags="fontNormal" />
+            <!-- U+0E9D: "ຝ" LAO LETTER FO TAM -->
+            <Key
+                latin:keyLabel="&#x0E9D;"
+                latin:keyLabelFlags="fontNormal" />
+        </default>
+    </switch>
+</merge>
diff --git a/java/res/xml/rowkeys_nepali_romanized1.xml b/java/res/xml/rowkeys_nepali_romanized1.xml
new file mode 100644
index 0000000..408a966
--- /dev/null
+++ b/java/res/xml/rowkeys_nepali_romanized1.xml
@@ -0,0 +1,177 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+**
+** Copyright 2013, 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.
+*/
+-->
+
+<merge
+    xmlns:latin="http://schemas.android.com/apk/res/com.android.inputmethod.latin"
+>
+    <switch>
+        <case
+            latin:keyboardLayoutSetElement="alphabetManualShifted|alphabetShiftLocked|alphabetShiftLockShifted"
+        >
+            <!-- U+0920: "ठ" DEVANAGARI LETTER TTHA -->
+            <Key
+                latin:keyLabel="&#x0920;"
+                latin:keyLabelFlags="fontNormal" />
+            <!-- U+0914: "औ" DEVANAGARI LETTER AU -->
+            <Key
+                latin:keyLabel="&#x0914;"
+                latin:keyLabelFlags="fontNormal" />
+            <!-- Because the font rendering system prior to API version 16 can't automatically
+                 render dotted circle for incomplete combining letter of some scripts, different
+                 set of Key definitions are needed based on the API version. -->
+            <include
+                latin:keyboardLayout="@xml/keystyle_devanagari_vowel_sign_ai" />
+            <Key
+                latin:keyStyle="baseKeyDevanagariVowelSignAi" />
+            <!-- Because the font rendering system prior to API version 16 can't automatically
+                 render dotted circle for incomplete combining letter of some scripts, different
+                 set of Key definitions are needed based on the API version. -->
+            <include
+                latin:keyboardLayout="@xml/key_devanagari_vowel_sign_vocalic_r" />
+            <!-- U+0925: "थ" DEVANAGARI LETTER THA -->
+            <Key
+                latin:keyLabel="&#x0925;"
+                latin:keyLabelFlags="fontNormal" />
+            <!-- U+091E: "ञ" DEVANAGARI LETTER NYA -->
+            <Key
+                latin:keyLabel="&#x091E;"
+                latin:keyLabelFlags="fontNormal" />
+            <!-- Because the font rendering system prior to API version 16 can't automatically
+                 render dotted circle for incomplete combining letter of some scripts, different
+                 set of Key definitions are needed based on the API version. -->
+            <include
+                latin:keyboardLayout="@xml/keystyle_devanagari_vowel_sign_uu" />
+            <Key
+                latin:keyStyle="baseKeyDevanagariVowelSignUu" />
+            <!-- Because the font rendering system prior to API version 16 can't automatically
+                 render dotted circle for incomplete combining letter of some scripts, different
+                 set of Key definitions are needed based on the API version. -->
+            <include
+                latin:keyboardLayout="@xml/keystyle_devanagari_vowel_sign_ii" />
+            <Key
+                latin:keyStyle="baseKeyDevanagariVowelSignIi" />
+            <!-- U+0913: "ओ" DEVANAGARI LETTER O -->
+            <Key
+                latin:keyLabel="&#x0913;"
+                latin:keyLabelFlags="fontNormal" />
+            <!-- U+092B: "फ" DEVANAGARI LETTER PHA -->
+            <Key
+                latin:keyLabel="&#x092B;"
+                latin:keyLabelFlags="fontNormal" />
+            <!-- U+0908: "ई" DEVANAGARI LETTER II -->
+            <Key
+                latin:keyLabel="&#x0908;"
+                latin:keyLabelFlags="fontNormal" />
+        </case>
+        <default>
+            <!-- U+091F: "ट" DEVANAGARI LETTER TTA
+                 U+0967: "१" DEVANAGARI DIGIT ONE
+                 U+093C: "़" DEVANAGARI SIGN NUKTA -->
+            <Key
+                latin:keyLabel="&#x091F;"
+                latin:keyHintLabel="1"
+                latin:additionalMoreKeys="&#x0967;,1"
+                latin:moreKeys="&#x093C;"
+                latin:keyLabelFlags="fontNormal" />
+            <!-- Because the font rendering system prior to API version 16 can't automatically
+                 render dotted circle for incomplete combining letter of some scripts, different
+                 set of Key definitions are needed based on the API version. -->
+            <!-- U+0968: "२" DEVANAGARI DIGIT TWO -->
+            <include
+                latin:keyboardLayout="@xml/keystyle_devanagari_vowel_sign_au" />
+            <Key
+                latin:keyStyle="baseKeyDevanagariVowelSignAu"
+                latin:keyHintLabel="2"
+                latin:additionalMoreKeys="&#x0968;,2" />
+            <!-- Because the font rendering system prior to API version 16 can't automatically
+                 render dotted circle for incomplete combining letter of some scripts, different
+                 set of Key definitions are needed based on the API version. -->
+            <!-- U+0969: "३" DEVANAGARI DIGIT THREE -->
+            <include
+                latin:keyboardLayout="@xml/keystyle_devanagari_vowel_sign_e" />
+            <Key
+                latin:keyStyle="baseKeyDevanagariVowelSignE"
+                latin:keyHintLabel="3"
+                latin:additionalMoreKeys="&#x0969;,3" />
+            <!-- U+0930: "र" DEVANAGARI LETTER RA
+                 U+096A: "४" DEVANAGARI DIGIT FOUR -->
+            <Key
+                latin:keyLabel="&#x0930;"
+                latin:keyHintLabel="4"
+                latin:additionalMoreKeys="&#x096A;,4"
+                latin:keyLabelFlags="fontNormal" />
+            <!-- U+0924: "त" DEVANAGARI LETTER TA
+                 U+096B: "५" DEVANAGARI DIGIT FIVE -->
+            <Key
+                latin:keyLabel="&#x0924;"
+                latin:keyHintLabel="5"
+                latin:additionalMoreKeys="&#x096B;,5"
+                latin:keyLabelFlags="fontNormal" />
+            <!-- U+092F: "य" DEVANAGARI LETTER YA
+                 U+096C: "६" DEVANAGARI DIGIT SIX -->
+            <Key
+                latin:keyLabel="&#x092F;"
+                latin:keyHintLabel="6"
+                latin:additionalMoreKeys="&#x096C;,6"
+                latin:keyLabelFlags="fontNormal" />
+            <!-- Because the font rendering system prior to API version 16 can't automatically
+                 render dotted circle for incomplete combining letter of some scripts, different
+                 set of Key definitions are needed based on the API version. -->
+            <!-- U+096D: "७" DEVANAGARI DIGIT SEVEN -->
+            <include
+                latin:keyboardLayout="@xml/keystyle_devanagari_vowel_sign_u" />
+            <Key
+                latin:keyStyle="baseKeyDevanagariVowelSignU"
+                latin:keyHintLabel="7"
+                latin:additionalMoreKeys="&#x096D;,7" />
+            <!-- Because the font rendering system prior to API version 16 can't automatically
+                 render dotted circle for incomplete combining letter of some scripts, different
+                 set of Key definitions are needed based on the API version. -->
+            <!-- U+096E: "८" DEVANAGARI DIGIT EIGHT -->
+            <include
+                latin:keyboardLayout="@xml/keystyle_devanagari_vowel_sign_i" />
+            <Key
+                latin:keyStyle="baseKeyDevanagariVowelSignI"
+                latin:keyHintLabel="8"
+                latin:additionalMoreKeys="&#x096E;,8" />
+            <!-- Because the font rendering system prior to API version 16 can't automatically
+                 render dotted circle for incomplete combining letter of some scripts, different
+                 set of Key definitions are needed based on the API version. -->
+            <!-- U+096F: "९" DEVANAGARI DIGIT NINE -->
+            <include
+                latin:keyboardLayout="@xml/keystyle_devanagari_vowel_sign_o" />
+            <Key
+                latin:keyStyle="baseKeyDevanagariVowelSignO"
+                latin:keyHintLabel="9"
+                latin:additionalMoreKeys="&#x096F;,9" />
+            <!-- U+092A: "प" DEVANAGARI LETTER PA
+                 U+0966: "०" DEVANAGARI DIGIT ZERO -->
+            <Key
+                latin:keyLabel="&#x092A;"
+                latin:keyHintLabel="0"
+                latin:additionalMoreKeys="&#x0966;,0"
+                latin:keyLabelFlags="fontNormal" />
+            <!-- U+0907: "इ" DEVANAGARI LETTER I -->
+            <Key
+                latin:keyLabel="&#x0907;"
+                latin:keyLabelFlags="fontNormal" />
+         </default>
+    </switch>
+</merge>
diff --git a/java/res/xml/rowkeys_nepali_romanized2.xml b/java/res/xml/rowkeys_nepali_romanized2.xml
new file mode 100644
index 0000000..66359ff
--- /dev/null
+++ b/java/res/xml/rowkeys_nepali_romanized2.xml
@@ -0,0 +1,126 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+**
+** Copyright 2013, 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.
+*/
+-->
+
+<merge
+    xmlns:latin="http://schemas.android.com/apk/res/com.android.inputmethod.latin"
+>
+    <switch>
+        <case
+            latin:keyboardLayoutSetElement="alphabetManualShifted|alphabetShiftLocked|alphabetShiftLockShifted"
+        >
+            <!-- U+0906: "आ" DEVANAGARI LETTER AA -->
+            <Key
+                latin:keyLabel="&#x0906;"
+                latin:keyLabelFlags="fontNormal" />
+            <!-- U+0936: "श" DEVANAGARI LETTER SHA -->
+            <Key
+                latin:keyLabel="&#x0936;"
+                latin:keyLabelFlags="fontNormal" />
+            <!-- U+0927: "ध" DEVANAGARI LETTER DHA -->
+            <Key
+                latin:keyLabel="&#x0927;"
+                latin:keyLabelFlags="fontNormal" />
+            <!-- U+090A: "ऊ" DEVANAGARI LETTER UU -->
+            <Key
+                latin:keyLabel="&#x090A;"
+                latin:keyLabelFlags="fontNormal" />
+            <!-- U+0918: "घ" DEVANAGARI LETTER GHA -->
+            <Key
+                latin:keyLabel="&#x0918;"
+                latin:keyLabelFlags="fontNormal" />
+            <!-- U+0905: "अ" DEVANAGARI LETTER A -->
+            <Key
+                latin:keyLabel="&#x0905;"
+                latin:keyLabelFlags="fontNormal" />
+            <!-- U+091D: "झ" DEVANAGARI LETTER JHA -->
+            <Key
+                latin:keyLabel="&#x091D;"
+                latin:keyLabelFlags="fontNormal" />
+            <!-- U+0916: "ख" DEVANAGARI LETTER KHA -->
+            <Key
+                latin:keyLabel="&#x0916;"
+                latin:keyLabelFlags="fontNormal" />
+            <!-- U+0965: "॥" DEVANAGARI DOUBLE DANDA -->
+            <Key
+                latin:keyLabel="&#x0965;"
+                latin:keyLabelFlags="fontNormal" />
+            <!-- U+0910: "ऐ" DEVANAGARI LETTER AI -->
+            <Key
+                latin:keyLabel="&#x0910;"
+                latin:keyLabelFlags="fontNormal" />
+            <!-- Because the font rendering system prior to API version 16 can't automatically
+                 render dotted circle for incomplete combining letter of some scripts, different
+                 set of Key definitions are needed based on the API version. -->
+            <include
+                latin:keyboardLayout="@xml/keystyle_devanagari_sign_visarga" />
+            <Key
+                latin:keyStyle="baseKeyDevanagariSignVisarga" />
+        </case>
+        <default>
+            <!-- Because the font rendering system prior to API version 16 can't automatically
+                 render dotted circle for incomplete combining letter of some scripts, different
+                 set of Key definitions are needed based on the API version. -->
+            <include
+                latin:keyboardLayout="@xml/keystyle_devanagari_vowel_sign_aa" />
+            <Key
+                latin:keyStyle="baseKeyDevanagariVowelSignAa" />
+            <!-- U+0938: "स" DEVANAGARI LETTER SA -->
+            <Key
+                latin:keyLabel="&#x0938;"
+                latin:keyLabelFlags="fontNormal" />
+            <!-- U+0926: "द" DEVANAGARI LETTER DA -->
+            <Key
+                latin:keyLabel="&#x0926;"
+                latin:keyLabelFlags="fontNormal" />
+            <!-- U+0909: "उ" DEVANAGARI LETTER U -->
+            <Key
+                latin:keyLabel="&#x0909;"
+                latin:keyLabelFlags="fontNormal" />
+            <!-- U+0917: "ग" DEVANAGARI LETTER GA -->
+            <Key
+                latin:keyLabel="&#x0917;"
+                latin:keyLabelFlags="fontNormal" />
+            <!-- U+0939: "ह" DEVANAGARI LETTER HA -->
+            <Key
+                latin:keyLabel="&#x0939;"
+                latin:keyLabelFlags="fontNormal" />
+            <!-- U+091C: "ज" DEVANAGARI LETTER JA -->
+            <Key
+                latin:keyLabel="&#x091C;"
+                latin:keyLabelFlags="fontNormal" />
+            <!-- U+0915: "क" DEVANAGARI LETTER KA -->
+            <Key
+                latin:keyLabel="&#x0915;"
+                latin:keyLabelFlags="fontNormal" />
+            <!-- U+0932: "ल" DEVANAGARI LETTER LA -->
+            <Key
+                latin:keyLabel="&#x0932;"
+                latin:keyLabelFlags="fontNormal" />
+            <!-- U+090F: "ए" DEVANAGARI LETTER E -->
+            <Key
+                latin:keyLabel="&#x090F;"
+                latin:keyLabelFlags="fontNormal" />
+            <!-- U+0950: "ॐ" DEVANAGARI OM -->
+            <Key
+                latin:keyLabel="&#x0950;"
+                latin:keyLabelFlags="fontNormal" />
+         </default>
+    </switch>
+</merge>
diff --git a/java/res/xml/rowkeys_nepali_romanized3.xml b/java/res/xml/rowkeys_nepali_romanized3.xml
new file mode 100644
index 0000000..166d028
--- /dev/null
+++ b/java/res/xml/rowkeys_nepali_romanized3.xml
@@ -0,0 +1,114 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+**
+** Copyright 2013, 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.
+*/
+-->
+
+<merge
+    xmlns:latin="http://schemas.android.com/apk/res/com.android.inputmethod.latin"
+>
+    <switch>
+        <case
+            latin:keyboardLayoutSetElement="alphabetManualShifted|alphabetShiftLocked|alphabetShiftLockShifted"
+        >
+            <!-- U+090B: "ऋ" DEVANAGARI LETTER VOCALIC R -->
+            <Key
+                latin:keyLabel="&#x090B;"
+                latin:keyLabelFlags="fontNormal" />
+            <!-- U+0922: "ढ" DEVANAGARI LETTER DDHA -->
+            <Key
+                latin:keyLabel="&#x0922;"
+                latin:keyLabelFlags="fontNormal" />
+            <!-- U+091B: "छ" DEVANAGARI LETTER CHA -->
+            <Key
+                latin:keyLabel="&#x091B;"
+                latin:keyLabelFlags="fontNormal" />
+            <!-- Because the font rendering system prior to API version 16 can't automatically
+                 render dotted circle for incomplete combining letter of some scripts, different
+                 set of Key definitions are needed based on the API version. -->
+            <include
+                latin:keyboardLayout="@xml/key_devanagari_sign_candrabindu" />
+            <!-- U+092D: "भ" DEVANAGARI LETTER BHA -->
+            <Key
+                latin:keyLabel="&#x092D;"
+                latin:keyLabelFlags="fontNormal" />
+            <!-- U+0923: "ण" DEVANAGARI LETTER NNA -->
+            <Key
+                latin:keyLabel="&#x0923;"
+                latin:keyLabelFlags="fontNormal" />
+            <!-- Because the font rendering system prior to API version 16 can't automatically
+                 render dotted circle for incomplete combining letter of some scripts, different
+                 set of Key definitions are needed based on the API version. -->
+            <include
+                latin:keyboardLayout="@xml/key_devanagari_sign_anusvara" />
+            <!-- U+0919: "ङ" DEVANAGARI LETTER NGA -->
+            <Key
+                latin:keyLabel="&#x0919;"
+                latin:keyLabelFlags="fontNormal" />
+            <!-- Because the font rendering system prior to API version 16 can't automatically
+                 render dotted circle for incomplete combining letter of some scripts, different
+                 set of Key definitions are needed based on the API version. -->
+            <include
+                latin:keyboardLayout="@xml/keystyle_devanagari_sign_virama" />
+            <Key
+                latin:keyStyle="baseKeyDevanagariSignVirama" />
+        </case>
+        <default>
+            <!-- U+0937: "ष" DEVANAGARI LETTER SSA -->
+            <Key
+                latin:keyLabel="&#x0937;"
+                latin:keyLabelFlags="fontNormal" />
+            <!-- U+0921: "ड" DEVANAGARI LETTER DDA -->
+            <Key
+                latin:keyLabel="&#x0921;"
+                latin:keyLabelFlags="fontNormal" />
+            <!-- U+091A: "च" DEVANAGARI LETTER CA -->
+            <Key
+                latin:keyLabel="&#x091A;"
+                latin:keyLabelFlags="fontNormal" />
+            <!-- U+0935: "व" DEVANAGARI LETTER VA -->
+            <Key
+                latin:keyLabel="&#x0935;"
+                latin:keyLabelFlags="fontNormal" />
+            <!-- U+092C: "ब" DEVANAGARI LETTER BHA -->
+            <Key
+                latin:keyLabel="&#x092C;"
+                latin:keyLabelFlags="fontNormal" />
+            <!-- U+0928: "न" DEVANAGARI LETTER NA -->
+            <Key
+                latin:keyLabel="&#x0928;"
+                latin:keyLabelFlags="fontNormal" />
+            <!-- U+092E: "म" DEVANAGARI LETTER MA -->
+            <Key
+                latin:keyLabel="&#x092E;"
+                latin:keyLabelFlags="fontNormal" />
+            <!-- U+0964: "।" DEVANAGARI DANDA
+                 U+093D: "ऽ" DEVANAGARI SIGN AVAGRAHA -->
+            <Key
+                latin:keyLabel="&#x0964;"
+                latin:moreKeys="&#x093D;"
+                latin:keyLabelFlags="fontNormal" />
+            <!-- Because the font rendering system prior to API version 16 can't automatically
+                 render dotted circle for incomplete combining letter of some scripts, different
+                 set of Key definitions are needed based on the API version. -->
+            <include
+                latin:keyboardLayout="@xml/keystyle_devanagari_sign_virama" />
+            <Key
+                latin:keyStyle="baseKeyDevanagariSignVirama" />
+        </default>
+    </switch>
+</merge>
diff --git a/java/res/xml/rowkeys_nepali_traditional1.xml b/java/res/xml/rowkeys_nepali_traditional1.xml
new file mode 100644
index 0000000..c7883c7
--- /dev/null
+++ b/java/res/xml/rowkeys_nepali_traditional1.xml
@@ -0,0 +1,175 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+**
+** Copyright 2013, 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.
+*/
+-->
+
+<merge
+    xmlns:latin="http://schemas.android.com/apk/res/com.android.inputmethod.latin"
+>
+    <switch>
+        <case
+            latin:keyboardLayoutSetElement="alphabetManualShifted|alphabetShiftLocked|alphabetShiftLockShifted"
+        >
+            <!-- U+0924/U+094D/U+0924: "त्त" DEVANAGARI LETTER TA/DEVANAGARI SIGN VIRAMA/DEVANAGARI LETTER TA
+                 U+091E: "ञ" DEVANAGARI LETTER NYA
+                 U+091C/U+094D/U+091E: "ज्ञ" DEVANAGARI LETTER JA/DEVANAGARI SIGN VIRAMA/DEVANAGARI LETTER NYA
+                 U+0965: "॥" DEVANAGARI DOUBLE DANDA -->
+            <Key
+                latin:keyLabel="&#x0924;&#x094D;&#x0924;"
+                latin:moreKeys="&#x091E;,&#x091C;&#x094D;&#x091E;,&#x0965;"
+                latin:keyLabelFlags="fontNormal|followKeyLetterRatio" />
+            <!-- U+0921/U+094D/U+0922: "ड्ढ" DEVANAGARI LETTER DDA/DEVANAGARI SIGN VIRAMA/DEVANAGARI LETTER DDHA
+                 U+0908: "ई" DEVANAGARI LETTER II -->
+            <Key
+                latin:keyLabel="&#x0921;&#x094D;&#x0922;"
+                latin:moreKeys="&#x0908;"
+                latin:keyLabelFlags="fontNormal|followKeyLetterRatio" />
+            <!-- U+0910: "ऐ" DEVANAGARI LETTER AI
+                 U+0918: "घ" DEVANAGARI LETTER GHA -->
+            <Key
+                latin:keyLabel="&#x0910;"
+                latin:moreKeys="&#x0918;"
+                latin:keyLabelFlags="fontNormal" />
+            <!-- U+0926/U+094D/U+0935: "द्व" DEVANAGARI LETTER DA/DEVANAGARI SIGN VIRAMA/DEVANAGARI LETTER VA
+                 U+0926/U+094D/U+0927: "द्ध" DEVANAGARI LETTER DA/DEVANAGARI SIGN VIRAMA/DEVANAGARI LETTER DHA -->
+            <Key
+                latin:keyLabel="&#x0926;&#x094D;&#x0935;"
+                latin:moreKeys="&#x0926;&#x094D;&#x0927;"
+                latin:keyLabelFlags="fontNormal|followKeyLetterRatio" />
+            <!-- U+091F/U+094D/U+091F: "ट्ट" DEVANAGARI LETTER TTA/DEVANAGARI SIGN VIRAMA/DEVANAGARI LETTER TTA
+                 U+091B: "छ" DEVANAGARI LETTER CHA -->
+            <Key
+                latin:keyLabel="&#x091F;&#x094D;&#x091F;"
+                latin:moreKeys="&#x091B;"
+                latin:keyLabelFlags="fontNormal|followKeyLetterRatio" />
+            <!-- U+0920/U+094D/U+0920: "ठ्ठ" DEVANAGARI LETTER TTHA/DEVANAGARI SIGN VIRAMA/DEVANAGARI LETTER TTHA
+                 U+091F: "ट" DEVANAGARI LETTER TTA -->
+            <Key
+                latin:keyLabel="&#x0920;&#x094D;&#x0920;"
+                latin:moreKeys="&#x091F;"
+                latin:keyLabelFlags="fontNormal|followKeyLetterRatio" />
+            <!-- U+090A: "ऊ" DEVANAGARI LETTER UU
+                 U+0920: "ठ" DEVANAGARI LETTER TTHA -->
+            <Key
+                latin:keyLabel="&#x090A;"
+                latin:moreKeys="&#x0920;"
+                latin:keyLabelFlags="fontNormal" />
+            <!-- U+0915/U+094D/U+0937: "क्ष" DEVANAGARI LETTER KA/DEVANAGARI SIGN VIRAMA/DEVANAGARI LETTER SSA
+                 U+0921: "ड" DEVANAGARI LETTER DDA -->
+            <Key
+                latin:keyLabel="&#x0915;&#x094D;&#x0937;"
+                latin:moreKeys="&#x0921;"
+                latin:keyLabelFlags="fontNormal|followKeyLetterRatio" />
+            <!-- U+0907: "इ" DEVANAGARI LETTER I
+                 U+0922: "ढ" DEVANAGARI LETTER DDHA -->
+            <Key
+                latin:keyLabel="&#x0907;"
+                latin:moreKeys="&#x0922;"
+                latin:keyLabelFlags="fontNormal" />
+            <!-- U+090F: "ए" DEVANAGARI LETTER E
+                 U+0923: "ण" DEVANAGARI LETTER NNA -->
+            <Key
+                latin:keyLabel="&#x090F;"
+                latin:moreKeys="&#x0923;"
+                latin:keyLabelFlags="fontNormal" />
+            <!-- Because the font rendering system prior to API version 16 can't automatically
+                 render dotted circle for incomplete combining letter of some scripts, different
+                 set of Key definitions are needed based on the API version. -->
+            <include
+                latin:keyboardLayout="@xml/key_devanagari_vowel_sign_vocalic_r" />
+        </case>
+        <default>
+            <!-- U+091F: "ट" DEVANAGARI LETTER TTA
+                 U+0967: "१" DEVANAGARI DIGIT ONE -->
+            <Key
+                latin:keyLabel="&#x091F;"
+                latin:keyHintLabel="1"
+                latin:additionalMoreKeys="&#x0967;,1"
+                latin:keyLabelFlags="fontNormal" />
+            <!-- U+0927: "ध" DEVANAGARI LETTER DHA
+                 U+0968: "२" DEVANAGARI DIGIT TWO -->
+            <Key
+                latin:keyLabel="&#x0927;"
+                latin:keyHintLabel="2"
+                latin:additionalMoreKeys="&#x0968;,2"
+                latin:keyLabelFlags="fontNormal" />
+            <!-- U+092D: "भ" DEVANAGARI LETTER BHA
+                 U+0969: "३" DEVANAGARI DIGIT THREE -->
+            <Key
+                latin:keyLabel="&#x092D;"
+                latin:keyHintLabel="3"
+                latin:additionalMoreKeys="&#x0969;,3"
+                latin:keyLabelFlags="fontNormal" />
+            <!-- U+091A: "च" DEVANAGARI LETTER CA
+                 U+096A: "४" DEVANAGARI DIGIT FOUR -->
+            <Key
+                latin:keyLabel="&#x091A;"
+                latin:keyHintLabel="4"
+                latin:additionalMoreKeys="&#x096A;,4"
+                latin:keyLabelFlags="fontNormal" />
+            <!-- U+0924: "त" DEVANAGARI LETTER TA
+                 U+096B: "५" DEVANAGARI DIGIT FIVE -->
+            <Key
+                latin:keyLabel="&#x0924;"
+                latin:keyHintLabel="5"
+                latin:additionalMoreKeys="&#x096B;,5"
+                latin:keyLabelFlags="fontNormal" />
+            <!-- U+0925: "थ" DEVANAGARI LETTER THA
+                 U+096C: "६" DEVANAGARI DIGIT SIX -->
+            <Key
+                latin:keyLabel="&#x0925;"
+                latin:keyHintLabel="6"
+                latin:additionalMoreKeys="&#x096C;,6"
+                latin:keyLabelFlags="fontNormal" />
+            <!-- U+0917: "ग" DEVANAGARI LETTER G
+                 U+096D: "७" DEVANAGARI DIGIT SEVEN -->
+            <Key
+                latin:keyLabel="&#x0917;"
+                latin:keyHintLabel="7"
+                latin:additionalMoreKeys="&#x096D;,7"
+                latin:keyLabelFlags="fontNormal" />
+            <!-- U+0937: "ष" DEVANAGARI LETTER SSA
+                 U+096E: "८" DEVANAGARI DIGIT EIGHT -->
+            <Key
+                latin:keyLabel="&#x0937;"
+                latin:keyHintLabel="8"
+                latin:additionalMoreKeys="&#x096E;,8"
+                latin:keyLabelFlags="fontNormal" />
+            <!-- U+092F: "य" DEVANAGARI LETTER YA
+                 U+096F: "९" DEVANAGARI DIGIT NINE -->
+            <Key
+                latin:keyLabel="&#x092F;"
+                latin:keyHintLabel="9"
+                latin:additionalMoreKeys="&#x096F;,9"
+                latin:keyLabelFlags="fontNormal" />
+            <!-- U+0909: "उ" DEVANAGARI LETTER U
+                 U+0966: "०" DEVANAGARI DIGIT ZERO -->
+            <Key
+                latin:keyLabel="&#x0909;"
+                latin:keyHintLabel="0"
+                latin:additionalMoreKeys="&#x0966;,0"
+                latin:keyLabelFlags="fontNormal" />
+            <!-- U+0907: "इ" DEVANAGARI LETTER I
+                 U+0914: "औ" DEVANAGARI LETTER AU -->
+            <Key
+                latin:keyLabel="&#x0907;"
+                latin:moreKeys="&#x0914;"
+                latin:keyLabelFlags="fontNormal" />
+         </default>
+    </switch>
+</merge>
diff --git a/java/res/xml/rowkeys_nepali_traditional2.xml b/java/res/xml/rowkeys_nepali_traditional2.xml
new file mode 100644
index 0000000..45620a9
--- /dev/null
+++ b/java/res/xml/rowkeys_nepali_traditional2.xml
@@ -0,0 +1,139 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+**
+** Copyright 2013, 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.
+*/
+-->
+
+<merge
+    xmlns:latin="http://schemas.android.com/apk/res/com.android.inputmethod.latin"
+>
+    <switch>
+        <case
+            latin:keyboardLayoutSetElement="alphabetManualShifted|alphabetShiftLocked|alphabetShiftLockShifted"
+        >
+            <!-- U+0906: "आ" DEVANAGARI LETTER AA -->
+            <Key
+                latin:keyLabel="&#x0906;"
+                latin:keyLabelFlags="fontNormal" />
+            <!-- U+0919/U+094D: "ङ्" DEVANAGARI LETTER NGA/DEVANAGARI SIGN VIRAMA -->
+            <Key
+                latin:keyLabel="&#x0919;&#x094D;"
+                latin:keyLabelFlags="fontNormal|followKeyLetterRatio" />
+            <!-- U+0921/U+094D/U+0921: "ड्ड" DEVANAGARI LETTER DDA/DEVANAGARI SIGN VIRAMA/DEVANAGARI LETTER DDA -->
+            <Key
+                latin:keyLabel="&#x0921;&#x094D;&#x0921;"
+                latin:keyLabelFlags="fontNormal|followKeyLetterRatio" />
+            <!-- Because the font rendering system prior to API version 16 can't automatically
+                 render dotted circle for incomplete combining letter of some scripts, different
+                 set of Key definitions are needed based on the API version. -->
+            <include
+                latin:keyboardLayout="@xml/key_devanagari_sign_candrabindu" />
+            <!-- U+0926/U+094D/U+0926: "द्द" DEVANAGARI LETTER DA/DEVANAGARI SIGN VIRAMA/DEVANAGARI LETTER DA -->
+            <Key
+                latin:keyLabel="&#x0926;&#x094D;&#x0926;"
+                latin:keyLabelFlags="fontNormal|followKeyLetterRatio" />
+            <!-- U+091D: "झ" DEVANAGARI LETTER JHA -->
+            <Key
+                latin:keyLabel="&#x091D;"
+                latin:keyLabelFlags="fontNormal" />
+            <!-- Because the font rendering system prior to API version 16 can't automatically
+                 render dotted circle for incomplete combining letter of some scripts, different
+                 set of Key definitions are needed based on the API version. -->
+            <include
+                latin:keyboardLayout="@xml/keystyle_devanagari_vowel_sign_o" />
+            <Key
+                latin:keyStyle="baseKeyDevanagariVowelSignO" />
+            <!-- U+092B: "फ" DEVANAGARI LETTER PHA -->
+            <Key
+                latin:keyLabel="&#x092B;"
+                latin:keyLabelFlags="fontNormal" />
+            <!-- Because the font rendering system prior to API version 16 can't automatically
+                 render dotted circle for incomplete combining letter of some scripts, different
+                 set of Key definitions are needed based on the API version. -->
+            <include
+                latin:keyboardLayout="@xml/keystyle_devanagari_vowel_sign_ii" />
+            <Key
+                latin:keyStyle="baseKeyDevanagariVowelSignIi" />
+            <!-- U+091F/U+094D/U+0920: "ट्ठ" DEVANAGARI LETTER TTA/DEVANAGARI SIGN VIRAMA/DEVANAGARI LETTER TTHA -->
+            <Key
+                latin:keyLabel="&#x091F;&#x094D;&#x0920;"
+                latin:keyLabelFlags="fontNormal|followKeyLetterRatio" />
+            <!-- Because the font rendering system prior to API version 16 can't automatically
+                 render dotted circle for incomplete combining letter of some scripts, different
+                 set of Key definitions are needed based on the API version. -->
+            <include
+                latin:keyboardLayout="@xml/keystyle_devanagari_vowel_sign_uu" />
+            <Key
+                latin:keyStyle="baseKeyDevanagariVowelSignUu" />
+        </case>
+        <default>
+            <!-- U+092C: "ब" DEVANAGARI LETTER BA -->
+            <Key
+                latin:keyLabel="&#x092C;"
+                latin:keyLabelFlags="fontNormal" />
+            <!-- U+0915: "क" DEVANAGARI LETTER KA -->
+            <Key
+                latin:keyLabel="&#x0915;"
+                latin:keyLabelFlags="fontNormal" />
+            <!-- U+092E: "म" DEVANAGARI LETTER MA -->
+            <Key
+                latin:keyLabel="&#x092E;"
+                latin:keyLabelFlags="fontNormal" />
+            <!-- Because the font rendering system prior to API version 16 can't automatically
+                 render dotted circle for incomplete combining letter of some scripts, different
+                 set of Key definitions are needed based on the API version. -->
+            <include
+                latin:keyboardLayout="@xml/keystyle_devanagari_vowel_sign_aa" />
+            <Key
+                latin:keyStyle="baseKeyDevanagariVowelSignAa" />
+            <!-- U+0928: "न" DEVANAGARI LETTER NA -->
+            <Key
+                latin:keyLabel="&#x0928;"
+                latin:keyLabelFlags="fontNormal" />
+            <!-- U+091C: "ज" DEVANAGARI LETTER JA -->
+            <Key
+                latin:keyLabel="&#x091C;"
+                latin:keyLabelFlags="fontNormal" />
+            <!-- U+0935: "व" DEVANAGARI LETTER VA -->
+            <Key
+                latin:keyLabel="&#x0935;"
+                latin:keyLabelFlags="fontNormal" />
+            <!-- U+092A: "प" DEVANAGARI LETTER PA -->
+            <Key
+                latin:keyLabel="&#x092A;"
+                latin:keyLabelFlags="fontNormal" />
+            <!-- Because the font rendering system prior to API version 16 can't automatically
+                 render dotted circle for incomplete combining letter of some scripts, different
+                 set of Key definitions are needed based on the API version. -->
+            <include
+                latin:keyboardLayout="@xml/keystyle_devanagari_vowel_sign_i" />
+            <Key
+                latin:keyStyle="baseKeyDevanagariVowelSignI" />
+            <!-- U+0938: "स" DEVANAGARI LETTER SA -->
+            <Key
+                latin:keyLabel="&#x0938;"
+                latin:keyLabelFlags="fontNormal" />
+            <!-- Because the font rendering system prior to API version 16 can't automatically
+                 render dotted circle for incomplete combining letter of some scripts, different
+                 set of Key definitions are needed based on the API version. -->
+            <include
+                latin:keyboardLayout="@xml/keystyle_devanagari_vowel_sign_u" />
+            <Key
+                latin:keyStyle="baseKeyDevanagariVowelSignU" />
+         </default>
+    </switch>
+</merge>
diff --git a/java/res/xml/rowkeys_nepali_traditional3_left6.xml b/java/res/xml/rowkeys_nepali_traditional3_left6.xml
new file mode 100644
index 0000000..1cacced
--- /dev/null
+++ b/java/res/xml/rowkeys_nepali_traditional3_left6.xml
@@ -0,0 +1,83 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+**
+** Copyright 2013, 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.
+*/
+-->
+
+<merge
+    xmlns:latin="http://schemas.android.com/apk/res/com.android.inputmethod.latin"
+>
+    <switch>
+        <case
+            latin:keyboardLayoutSetElement="alphabetManualShifted|alphabetShiftLocked|alphabetShiftLockShifted"
+        >
+            <!-- U+0915/U+094D: "क्" DEVANAGARI LETTER KA/DEVANAGARI SIGN VIRAMA -->
+            <Key
+                latin:keyLabel="&#x0915;&#x094D;"
+                latin:keyLabelFlags="fontNormal|followKeyLetterRatio" />
+            <!-- U+0939/U+094D/U+092E: "ह्म" DEVANAGARI LETTER HA/DEVANAGARI SIGN VIRAMA/DEVANAGARI LETTER MA -->
+            <Key
+                latin:keyLabel="&#x0939;&#x094D;&#x092E;"
+                latin:keyLabelFlags="fontNormal|followKeyLetterRatio" />
+            <!-- U+090B: "ऋ" DEVANAGARI LETTER VOCALIC R -->
+            <Key
+                latin:keyLabel="&#x090B;"
+                latin:keyLabelFlags="fontNormal" />
+            <!-- U+0950: "ॐ" DEVANAGARI OM -->
+            <Key
+                latin:keyLabel="&#x0950;"
+                latin:keyLabelFlags="fontNormal" />
+            <!-- Because the font rendering system prior to API version 16 can't automatically
+                 render dotted circle for incomplete combining letter of some scripts, different
+                 set of Key definitions are needed based on the API version. -->
+            <include
+                latin:keyboardLayout="@xml/keystyle_devanagari_vowel_sign_au" />
+            <Key
+                latin:keyStyle="baseKeyDevanagariVowelSignAu" />
+            <!-- U+0926/U+094D/U+092F: "द्य" DEVANAGARI LETTER DA/DEVANAGARI SIGN VIRAMA/DEVANAGARI LETTER YA -->
+            <Key
+                latin:keyLabel="&#x0926;&#x094D;&#x092F;"
+                latin:keyLabelFlags="fontNormal|followKeyLetterRatio" />
+        </case>
+        <default>
+            <!-- U+0936: "श" DEVANAGARI LETTER SHA -->
+            <Key
+                latin:keyLabel="&#x0936;"
+                latin:keyLabelFlags="fontNormal" />
+            <!-- U+0939: "ह" DEVANAGARI LETTER HA -->
+            <Key
+                latin:keyLabel="&#x0939;"
+                latin:keyLabelFlags="fontNormal" />
+            <!-- U+0905: "अ" DEVANAGARI LETTER A -->
+            <Key
+                latin:keyLabel="&#x0905;"
+                latin:keyLabelFlags="fontNormal" />
+            <!-- U+0916: "ख" DEVANAGARI LETTER KHA -->
+            <Key
+                latin:keyLabel="&#x0916;"
+                latin:keyLabelFlags="fontNormal" />
+            <!-- U+0926: "द" DEVANAGARI LETTER DA -->
+            <Key
+                latin:keyLabel="&#x0926;"
+                latin:keyLabelFlags="fontNormal" />
+            <!-- U+0932: "ल" DEVANAGARI LETTER LA -->
+            <Key
+                latin:keyLabel="&#x0932;"
+                latin:keyLabelFlags="fontNormal" />
+        </default>
+    </switch>
+</merge>
diff --git a/java/res/xml/rowkeys_nepali_traditional3_right3.xml b/java/res/xml/rowkeys_nepali_traditional3_right3.xml
new file mode 100644
index 0000000..b2e01e4
--- /dev/null
+++ b/java/res/xml/rowkeys_nepali_traditional3_right3.xml
@@ -0,0 +1,65 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+**
+** Copyright 2013, 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.
+*/
+-->
+
+<merge
+    xmlns:latin="http://schemas.android.com/apk/res/com.android.inputmethod.latin"
+>
+    <switch>
+        <case
+            latin:keyboardLayoutSetElement="alphabetManualShifted|alphabetShiftLocked|alphabetShiftLockShifted"
+        >
+            <!-- Because the font rendering system prior to API version 16 can't automatically
+                 render dotted circle for incomplete combining letter of some scripts, different
+                 set of Key definitions are needed based on the API version. -->
+            <include
+                latin:keyboardLayout="@xml/key_devanagari_sign_anusvara" />
+            <!-- U+0919: "ङ" DEVANAGARI LETTER NGA -->
+            <Key
+                latin:keyLabel="&#x0919;"
+                latin:keyLabelFlags="fontNormal" />
+            <!-- Because the font rendering system prior to API version 16 can't automatically
+                 render dotted circle for incomplete combining letter of some scripts, different
+                 set of Key definitions are needed based on the API version. -->
+            <include
+                latin:keyboardLayout="@xml/keystyle_devanagari_vowel_sign_ai" />
+            <Key
+                latin:keyStyle="baseKeyDevanagariVowelSignAi" />
+        </case>
+        <default>
+            <!-- Because the font rendering system prior to API version 16 can't automatically
+                 render dotted circle for incomplete combining letter of some scripts, different
+                 set of Key definitions are needed based on the API version. -->
+            <include
+                latin:keyboardLayout="@xml/keystyle_devanagari_vowel_sign_e" />
+            <Key
+                latin:keyStyle="baseKeyDevanagariVowelSignE" />
+            <!-- U+0964: "।" DEVANAGARI DANDA -->
+            <Key
+                latin:keyLabel="&#x0964;"
+                latin:keyLabelFlags="fontNormal" />
+             <!-- U+0930: "र" DEVANAGARI LETTER RA
+                  U+0930/U+0941: "रु" DEVANAGARI LETTER RA/DEVANAGARI VOWEL SIGN U -->
+            <Key
+                latin:keyLabel="&#x0930;"
+                latin:moreKeys="&#x0930;&#x0941;"
+                latin:keyLabelFlags="fontNormal" />
+         </default>
+    </switch>
+</merge>
diff --git a/java/res/xml/rowkeys_nepali_traditional3_right5.xml b/java/res/xml/rowkeys_nepali_traditional3_right5.xml
new file mode 100644
index 0000000..87f0616
--- /dev/null
+++ b/java/res/xml/rowkeys_nepali_traditional3_right5.xml
@@ -0,0 +1,91 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+**
+** Copyright 2013, 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.
+*/
+-->
+
+<merge
+    xmlns:latin="http://schemas.android.com/apk/res/com.android.inputmethod.latin"
+>
+    <switch>
+        <case
+            latin:keyboardLayoutSetElement="alphabetManualShifted|alphabetShiftLocked|alphabetShiftLockShifted"
+        >
+            <!-- Because the font rendering system prior to API version 16 can't automatically
+                 render dotted circle for incomplete combining letter of some scripts, different
+                 set of Key definitions are needed based on the API version. -->
+            <include
+                latin:keyboardLayout="@xml/key_devanagari_sign_anusvara" />
+            <!-- U+0919: "ङ" DEVANAGARI LETTER NGA -->
+            <Key
+                latin:keyLabel="&#x0919;"
+                latin:keyLabelFlags="fontNormal" />
+            <!-- Because the font rendering system prior to API version 16 can't automatically
+                 render dotted circle for incomplete combining letter of some scripts, different
+                 set of Key definitions are needed based on the API version. -->
+            <include
+                latin:keyboardLayout="@xml/keystyle_devanagari_vowel_sign_ai" />
+            <Key
+                latin:keyStyle="baseKeyDevanagariVowelSignAi" />
+            <!-- U+0930/U+0941: "रु" DEVANAGARI LETTER RA/DEVANAGARI VOWEL SIGN U -->
+            <Key
+                latin:keyLabel="&#x0930;&#x0941;"
+                latin:moreKeys="!"
+                latin:keyLabelFlags="fontNormal|followKeyLetterRatio" />
+            <Key
+                latin:keyLabel="\?" />
+        </case>
+        <default>
+            <!-- Because the font rendering system prior to API version 16 can't automatically
+                 render dotted circle for incomplete combining letter of some scripts, different
+                 set of Key definitions are needed based on the API version. -->
+            <!-- U+093D: "ऽ" DEVANAGARI SIGN AVAGRAHA -->
+            <include
+                latin:keyboardLayout="@xml/keystyle_devanagari_sign_visarga" />
+            <Key
+                latin:keyStyle="baseKeyDevanagariSignVisarga"
+                latin:moreKeys="&#x093D;" />
+            <!-- Because the font rendering system prior to API version 16 can't automatically
+                 render dotted circle for incomplete combining letter of some scripts, different
+                 set of Key definitions are needed based on the API version. -->
+            <include
+                latin:keyboardLayout="@xml/keystyle_devanagari_vowel_sign_e" />
+            <!-- Override more keys with empty definition -->
+            <key-style
+                latin:styleName="moreKeysDevanagariVowelSignE" />
+            <Key
+                latin:keyStyle="baseKeyDevanagariVowelSignE" />
+            <!-- U+0964: "।" DEVANAGARI DANDA -->
+            <Key
+                latin:keyLabel="&#x0964;"
+                latin:keyLabelFlags="fontNormal" />
+            <!-- U+0930: "र" DEVANAGARI LETTER RA -->
+            <Key
+                latin:keyLabel="&#x0930;"
+                latin:moreKeys="!"
+                latin:keyLabelFlags="fontNormal" />
+            <!-- Because the font rendering system prior to API version 16 can't automatically
+                 render dotted circle for incomplete combining letter of some scripts, different
+                 set of Key definitions are needed based on the API version. -->
+            <include
+                latin:keyboardLayout="@xml/keystyle_devanagari_sign_virama" />
+            <Key
+                latin:keyStyle="baseKeyDevanagariSignVirama"
+                latin:moreKeys="\?" />
+        </default>
+    </switch>
+</merge>
diff --git a/java/res/xml/rowkeys_pcqwerty1.xml b/java/res/xml/rowkeys_pcqwerty1.xml
index b2d1d37..de548d0 100644
--- a/java/res/xml/rowkeys_pcqwerty1.xml
+++ b/java/res/xml/rowkeys_pcqwerty1.xml
@@ -21,67 +21,61 @@
 <merge
     xmlns:latin="http://schemas.android.com/apk/res/com.android.inputmethod.latin"
 >
-    <switch>
-        <case
-            latin:keyboardLayoutSetElement="alphabet|alphabetAutomaticShifted"
-        >
-            <!-- U+00AC: "¬" NOT SIGN -->
-            <Key
-                latin:keyLabel="`"
-                latin:moreKeys="~"
-                latin:additionalMoreKeys="&#x00AC;" />
-            <!-- U+00A1: "¡" NVERTED EXCLAMATION MARK -->
-            <Key
-                latin:keyLabel="1"
-                latin:additionalMoreKeys="!"
-                latin:moreKeys="&#x00A1;,!text/more_keys_for_symbols_1" />
-            <Key
-                latin:keyLabel="2"
-                latin:additionalMoreKeys="\@"
-                latin:moreKeys="!text/more_keys_for_symbols_2" />
-            <Key
-                latin:keyLabel="3"
-                latin:additionalMoreKeys="\#"
-                latin:moreKeys="!text/more_keys_for_symbols_3" />
-            <Key
-                latin:keyLabel="4"
-                latin:additionalMoreKeys="$"
-                latin:moreKeys="!text/more_keys_for_symbols_4" />
-            <Key
-                latin:keyLabel="5"
-                latin:additionalMoreKeys="\\%"
-                latin:moreKeys="!text/more_keys_for_symbols_5" />
-            <Key
-                latin:keyLabel="6"
-                latin:additionalMoreKeys="^"
-                latin:moreKeys="!text/more_keys_for_symbols_6" />
-            <Key
-                latin:keyLabel="7"
-                latin:additionalMoreKeys="&amp;"
-                latin:moreKeys="!text/more_keys_for_symbols_7" />
-            <Key
-                latin:keyLabel="8"
-                latin:additionalMoreKeys="*,%"
-                latin:moreKeys="!text/more_keys_for_symbols_8" />
-            <Key
-                latin:keyLabel="9"
-                latin:additionalMoreKeys="("
-                latin:moreKeys="!text/more_keys_for_symbols_9" />
-            <Key
-                latin:keyLabel="0"
-                latin:additionalMoreKeys=")"
-                latin:moreKeys="!text/more_keys_for_symbols_0" />
-            <Key
-                latin:keyLabel="-"
-                latin:moreKeys="_" />
-            <Key
-                latin:keyLabel="="
-                latin:moreKeys="+" />
-        </case>
-        <!-- keyboardLayoutSetElement="alphabet*Shifted|symbols*" -->
-        <default>
-            <include
-                latin:keyboardLayout="@xml/keys_pcqwerty_symbols1" />
-        </default>
-    </switch>
+    <Key
+        latin:keyLabel="`"
+        latin:additionalMoreKeys="~" />
+    <Key
+        latin:keyLabel="1"
+        latin:additionalMoreKeys="!,!text/more_keys_for_symbols_exclamation"
+        latin:moreKeys="!text/more_keys_for_symbols_1" />
+    <Key
+        latin:keyLabel="2"
+        latin:additionalMoreKeys="\@"
+        latin:moreKeys="!text/more_keys_for_symbols_2" />
+    <Key
+        latin:keyLabel="3"
+        latin:additionalMoreKeys="\#"
+        latin:moreKeys="!text/more_keys_for_symbols_3" />
+    <Key
+        latin:keyLabel="4"
+        latin:additionalMoreKeys="$"
+        latin:moreKeys="!text/more_keys_for_symbols_4" />
+    <Key
+        latin:keyLabel="5"
+        latin:additionalMoreKeys="\\%"
+        latin:moreKeys="!text/more_keys_for_symbols_5" />
+    <Key
+        latin:keyLabel="6"
+        latin:additionalMoreKeys="^"
+        latin:moreKeys="!text/more_keys_for_symbols_6" />
+    <Key
+        latin:keyLabel="7"
+        latin:additionalMoreKeys="&amp;"
+        latin:moreKeys="!text/more_keys_for_symbols_7" />
+    <Key
+        latin:keyLabel="8"
+        latin:additionalMoreKeys="*"
+        latin:moreKeys="!text/more_keys_for_symbols_8" />
+    <Key
+        latin:keyLabel="9"
+        latin:additionalMoreKeys="("
+        latin:moreKeys="!text/more_keys_for_symbols_9" />
+    <Key
+        latin:keyLabel="0"
+        latin:additionalMoreKeys=")"
+        latin:moreKeys="!text/more_keys_for_symbols_0" />
+    <!-- U+2013: "–" EN DASH
+         U+2014: "—" EM DASH
+         U+00B7: "·" MIDDLE DOT -->
+    <Key
+        latin:keyLabel="-"
+        latin:additionalMoreKeys="_"
+        latin:moreKeys="&#x2013;,&#x2014;,&#x00B7;" />
+    <!-- U+221E: "∞" INFINITY
+         U+2260: "≠" NOT EQUAL TO
+         U+2248: "≈" ALMOST EQUAL TO -->
+    <Key
+        latin:keyLabel="="
+        latin:additionalMoreKeys="+"
+        latin:moreKeys="!fixedColumnOrder!4,&#x221E;,&#x2260;,&#x2248;,%" />
 </merge>
diff --git a/java/res/xml/keys_pcqwerty_symbols1.xml b/java/res/xml/rowkeys_pcqwerty1_shift.xml
similarity index 64%
rename from java/res/xml/keys_pcqwerty_symbols1.xml
rename to java/res/xml/rowkeys_pcqwerty1_shift.xml
index 2364e10..bc39f94 100644
--- a/java/res/xml/keys_pcqwerty_symbols1.xml
+++ b/java/res/xml/rowkeys_pcqwerty1_shift.xml
@@ -21,37 +21,40 @@
 <merge
     xmlns:latin="http://schemas.android.com/apk/res/com.android.inputmethod.latin"
 >
-    <!-- U+00AC: "¬" NOT SIGN -->
     <Key
-        latin:keyLabel="~"
-        latin:moreKeys="&#x00AC;" />
-    <!-- U+00A1: "¡" NVERTED EXCLAMATION MARK -->
+        latin:keyLabel="~" />
     <Key
         latin:keyLabel="!"
-        latin:moreKeys="&#x00A1;" />
+        latin:additionalMoreKeys="!text/more_keys_for_symbols_exclamation" />
     <Key
         latin:keyLabel="\@" />
     <Key
         latin:keyLabel="\#" />
     <Key
-        latin:keyLabel="$" />
-    <!-- U+2030: "‰" PER MILLE SIGN -->
+        latin:keyLabel="$"
+        latin:additionalMoreKeys="!text/more_keys_for_currency_dollar" />
     <Key
         latin:keyLabel="%"
-        latin:moreKeys="&#x2030;" />
+        latin:additionalMoreKeys="!text/more_keys_for_symbols_percent" />
     <Key
         latin:keyLabel="^" />
     <Key
         latin:keyLabel="&amp;" />
     <Key
         latin:keyLabel="*"
-        latin:moreKeys="!text/more_keys_for_star" />
+        latin:additionalMoreKeys="!text/more_keys_for_star" />
     <Key
         latin:keyLabel="(" />
     <Key
         latin:keyLabel=")" />
     <Key
         latin:keyLabel="_" />
+    <!-- U+00B1: "±" PLUS-MINUS SIGN
+         U+00D7: "×" MULTIPLICATION SIGN
+         U+00F7: "÷" DIVISION SIGN
+         U+221A: "√" SQUARE ROOT -->
     <Key
-        latin:keyLabel="+" />
+        latin:keyLabel="+"
+        latin:additionalMoreKeys="!text/more_keys_for_plus"
+        latin:moreKeys="&#x00B1;,&#x00D7;,&#x00F7;,&#x221A;" />
 </merge>
diff --git a/java/res/xml/rowkeys_pcqwerty2.xml b/java/res/xml/rowkeys_pcqwerty2.xml
index cedf475..8db704d 100644
--- a/java/res/xml/rowkeys_pcqwerty2.xml
+++ b/java/res/xml/rowkeys_pcqwerty2.xml
@@ -21,21 +21,11 @@
 <merge
     xmlns:latin="http://schemas.android.com/apk/res/com.android.inputmethod.latin"
 >
-    <switch>
-        <case
-            latin:keyboardLayoutSetElement="symbols|symbolsShifted"
-        >
-            <include
-                latin:keyboardLayout="@xml/keys_pcqwerty_symbols2" />
-        </case>
-        <default>
-            <!-- The keys on this PC layout row2 consist of the letters of QWERTY layout row1 and
-                 some symbols keys. -->
-            <include
-                latin:keyboardLayout="@xml/rowkeys_qwerty1"
-                latin:keyLabelFlags="disableAdditionalMoreKeys|disableKeyHintLabel" />
-        </default>
-    </switch>
+    <!-- The keys on this PC layout row2 consist of the letters of QWERTY layout row1 and
+         some symbols keys. -->
+    <include
+        latin:keyboardLayout="@xml/rowkeys_qwerty1"
+        latin:keyLabelFlags="disableAdditionalMoreKeys|disableKeyHintLabel" />
     <include
         latin:keyboardLayout="@xml/keys_pcqwerty2_right3" />
 </merge>
diff --git a/java/res/xml/rowkeys_pcqwerty3.xml b/java/res/xml/rowkeys_pcqwerty3.xml
index 5044e5f..ad122d3 100644
--- a/java/res/xml/rowkeys_pcqwerty3.xml
+++ b/java/res/xml/rowkeys_pcqwerty3.xml
@@ -21,20 +21,10 @@
 <merge
     xmlns:latin="http://schemas.android.com/apk/res/com.android.inputmethod.latin"
 >
-    <switch>
-        <case
-            latin:keyboardLayoutSetElement="symbols|symbolsShifted"
-        >
-            <include
-                latin:keyboardLayout="@xml/keys_pcqwerty_symbols3" />
-        </case>
-        <default>
-            <!-- The keys on this PC layout row3 consist of the letters of QWERTY layout row2 and
-                 some symbols keys. -->
-            <include
-                latin:keyboardLayout="@xml/rowkeys_qwerty2" />
-        </default>
-    </switch>
+    <!-- The keys on this PC layout row3 consist of the letters of QWERTY layout row2 and
+         some symbols keys. -->
+    <include
+        latin:keyboardLayout="@xml/rowkeys_qwerty2" />
     <include
         latin:keyboardLayout="@xml/keys_pcqwerty3_right2" />
 </merge>
diff --git a/java/res/xml/rowkeys_pcqwerty4.xml b/java/res/xml/rowkeys_pcqwerty4.xml
index 4071e8c..b558f41 100644
--- a/java/res/xml/rowkeys_pcqwerty4.xml
+++ b/java/res/xml/rowkeys_pcqwerty4.xml
@@ -21,20 +21,10 @@
 <merge
     xmlns:latin="http://schemas.android.com/apk/res/com.android.inputmethod.latin"
 >
-    <switch>
-        <case
-            latin:keyboardLayoutSetElement="symbols|symbolsShifted"
-        >
-            <include
-                latin:keyboardLayout="@xml/keys_pcqwerty_symbols4" />
-        </case>
-        <default>
-            <!-- The keys on this PC layout row4 consist of the letters of QWERTY layout row3 and
-                 some symbols keys. -->
-            <include
-                latin:keyboardLayout="@xml/rowkeys_qwerty3" />
-        </default>
-    </switch>
+    <!-- The keys on this PC layout row4 consist of the letters of QWERTY layout row3 and
+         some symbols keys. -->
+    <include
+        latin:keyboardLayout="@xml/rowkeys_qwerty3" />
     <include
         latin:keyboardLayout="@xml/keys_pcqwerty4_right3" />
 </merge>
diff --git a/java/res/xml/rowkeys_symbols2.xml b/java/res/xml/rowkeys_symbols2.xml
index 3e27f15..76cbf62 100644
--- a/java/res/xml/rowkeys_symbols2.xml
+++ b/java/res/xml/rowkeys_symbols2.xml
@@ -50,9 +50,6 @@
         latin:moreKeys="!text/more_keys_for_symbols_percent" />
     <Key
         latin:keyLabel="&amp;" />
-    <Key
-        latin:keyLabel="*"
-        latin:moreKeys="!text/more_keys_for_star" />
     <!-- U+2013: "–" EN DASH
          U+2014: "—" EM DASH
          U+00B7: "·" MIDDLE DOT -->
diff --git a/java/res/xml/rowkeys_symbols3.xml b/java/res/xml/rowkeys_symbols3.xml
index 7722ca9..9f5e620 100644
--- a/java/res/xml/rowkeys_symbols3.xml
+++ b/java/res/xml/rowkeys_symbols3.xml
@@ -22,8 +22,8 @@
     xmlns:latin="http://schemas.android.com/apk/res/com.android.inputmethod.latin"
 >
     <Key
-        latin:keyLabel="!"
-        latin:moreKeys="!text/more_keys_for_symbols_exclamation" />
+        latin:keyLabel="*"
+        latin:moreKeys="!text/more_keys_for_star" />
     <switch>
         <case
             latin:languageCode="fa"
@@ -54,8 +54,9 @@
         latin:keyLabel="!text/keylabel_for_symbols_semicolon"
         latin:moreKeys="!text/more_keys_for_symbols_semicolon" />
     <Key
-        latin:keyLabel="/" />
-    <Key
         latin:keyLabel="!text/keylabel_for_symbols_question"
         latin:moreKeys="!text/more_keys_for_symbols_question" />
+    <Key
+        latin:keyLabel="!"
+        latin:moreKeys="!text/more_keys_for_symbols_exclamation" />
 </merge>
diff --git a/java/res/xml/rowkeys_symbols_shift1.xml b/java/res/xml/rowkeys_symbols_shift1.xml
index fea8ae3..6013493 100644
--- a/java/res/xml/rowkeys_symbols_shift1.xml
+++ b/java/res/xml/rowkeys_symbols_shift1.xml
@@ -34,17 +34,23 @@
     <!-- U+221A: "√" SQUARE ROOT -->
     <Key
         latin:keyLabel="&#x221A;" />
-    <!-- U+03C0: "π" GREEK SMALL LETTER PI
-         U+03A0: "Π" GREEK CAPITAL LETTER PI  -->
+    <!-- U+03A0: "Π" GREEK CAPITAL LETTER PI
+         U+03C0: "π" GREEK SMALL LETTER PI  -->
     <Key
-        latin:keyLabel="&#x03C0;"
-        latin:moreKeys="&#x03A0;" />
+        latin:keyLabel="&#x03A0;"
+        latin:moreKeys="&#x03C0;" />
     <!-- U+00F7: "÷" DIVISION SIGN -->
     <Key
         latin:keyLabel="&#x00F7;" />
     <!-- U+00D7: "×" MULTIPLICATION SIGN -->
     <Key
         latin:keyLabel="&#x00D7;" />
-    <include
-        latin:keyboardLayout="@xml/keys_curly_brackets" />
+    <!-- U+00B6: "¶" PILCROW SIGN
+         U+00A7: "§" SECTION SIGN -->
+    <Key
+        latin:keyLabel="&#x00B6;"
+        latin:moreKeys="&#x00A7;" />
+    <!-- U+2206: "∆" INCREMENT -->
+    <Key
+        latin:keyLabel="&#x2206;" />
 </merge>
diff --git a/java/res/xml/rowkeys_symbols_shift2.xml b/java/res/xml/rowkeys_symbols_shift2.xml
index 3fd8aac..36f9214 100644
--- a/java/res/xml/rowkeys_symbols_shift2.xml
+++ b/java/res/xml/rowkeys_symbols_shift2.xml
@@ -22,19 +22,13 @@
     xmlns:latin="http://schemas.android.com/apk/res/com.android.inputmethod.latin"
 >
     <Key
-        latin:keyStyle="nonSpecialBackgroundTabKeyStyle" />
-    <Key
         latin:keyStyle="moreCurrency1KeyStyle" />
     <Key
         latin:keyStyle="moreCurrency2KeyStyle" />
     <Key
         latin:keyStyle="moreCurrency3KeyStyle" />
-    <!-- U+00B0: "°" DEGREE SIGN
-         U+2032: "′" PRIME
-         U+2033: "″" DOUBLE PRIME -->
     <Key
-        latin:keyLabel="&#x00B0;"
-        latin:moreKeys="&#x2032;,&#x2033;" />
+        latin:keyStyle="moreCurrency4KeyStyle" />
     <!-- U+2191: "↑" UPWARDS ARROW
          U+2193: "↓" DOWNWARDS ARROW
          U+2190: "←" LEFTWARDS ARROW
@@ -42,8 +36,12 @@
     <Key
         latin:keyLabel="^"
         latin:moreKeys="&#x2191;,&#x2193;,&#x2190;,&#x2192;" />
+    <!-- U+00B0: "°" DEGREE SIGN
+         U+2032: "′" PRIME
+         U+2033: "″" DOUBLE PRIME -->
     <Key
-        latin:keyLabel="_" />
+        latin:keyLabel="&#x00B0;"
+        latin:moreKeys="&#x2032;,&#x2033;" />
     <!-- U+2260: "≠" NOT EQUAL TO
          U+2248: "≈" ALMOST EQUAL TO
          U+221E: "∞" INFINITY -->
@@ -51,5 +49,5 @@
         latin:keyLabel="="
         latin:moreKeys="&#x2260;,&#x2248;,&#x221E;" />
     <include
-        latin:keyboardLayout="@xml/keys_square_brackets" />
+        latin:keyboardLayout="@xml/keys_curly_brackets" />
 </merge>
diff --git a/java/res/xml/rowkeys_symbols_shift3.xml b/java/res/xml/rowkeys_symbols_shift3.xml
index a35af21..5fe1c74 100644
--- a/java/res/xml/rowkeys_symbols_shift3.xml
+++ b/java/res/xml/rowkeys_symbols_shift3.xml
@@ -21,22 +21,20 @@
 <merge
     xmlns:latin="http://schemas.android.com/apk/res/com.android.inputmethod.latin"
 >
-    <!-- U+2122: "™" TRADE MARK SIGN -->
     <Key
-        latin:keyLabel="&#x2122;" />
-    <!-- U+00AE: "®" REGISTERED SIGN -->
-    <Key
-        latin:keyLabel="&#x00AE;" />
+        latin:keyLabel="\\" />
     <!-- U+00A9: "©" COPYRIGHT SIGN -->
     <Key
         latin:keyLabel="&#x00A9;" />
-    <!-- U+00B6: "¶" PILCROW SIGN
-         U+00A7: "§" SECTION SIGN -->
+    <!-- U+00AE: "®" REGISTERED SIGN -->
     <Key
-        latin:keyLabel="&#x00B6;"
-        latin:moreKeys="&#x00A7;" />
+        latin:keyLabel="&#x00AE;" />
+    <!-- U+2122: "™" TRADE MARK SIGN -->
     <Key
-        latin:keyLabel="\\" />
+        latin:keyLabel="&#x2122;" />
+    <!-- U+2105: "℅" CARE OF -->
+    <Key
+        latin:keyLabel="&#x2105;" />
     <include
-        latin:keyboardLayout="@xml/keys_less_greater" />
+        latin:keyboardLayout="@xml/keys_square_brackets" />
 </merge>
diff --git a/java/res/xml/rows_armenian_phonetic.xml b/java/res/xml/rows_armenian_phonetic.xml
new file mode 100644
index 0000000..ea8870e
--- /dev/null
+++ b/java/res/xml/rows_armenian_phonetic.xml
@@ -0,0 +1,64 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+**
+** Copyright 2013, 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.
+*/
+-->
+
+<merge
+    xmlns:latin="http://schemas.android.com/apk/res/com.android.inputmethod.latin"
+>
+    <include
+        latin:keyboardLayout="@xml/key_styles_common" />
+    <include
+        latin:keyboardLayout="@xml/key_styles_currency" />
+    <Row
+        latin:keyWidth="10.0%p"
+    >
+        <include
+            latin:keyboardLayout="@xml/rowkeys_armenian_phonetic1" />
+    </Row>
+    <Row
+        latin:keyWidth="10.0%p"
+    >
+        <include
+            latin:keyboardLayout="@xml/rowkeys_armenian_phonetic2" />
+    </Row>
+    <Row
+        latin:keyWidth="10.0%p"
+    >
+        <include
+            latin:keyboardLayout="@xml/rowkeys_armenian_phonetic3" />
+        <include
+            latin:keyboardLayout="@xml/key_armenian_xeh" />
+    </Row>
+    <Row
+        latin:keyWidth="9.8000%p"
+    >
+        <Key
+            latin:keyStyle="shiftKeyStyle"
+            latin:keyWidth="10.8%p" />
+        <include
+            latin:keyboardLayout="@xml/rowkeys_armenian_phonetic4" />
+        <include
+            latin:keyboardLayout="@xml/key_armenian_sha" />
+        <Key
+            latin:keyStyle="deleteKeyStyle"
+            latin:keyWidth="fillRight" />
+    </Row>
+    <include
+        latin:keyboardLayout="@xml/row_qwerty4" />
+</merge>
diff --git a/java/res/xml/rows_khmer.xml b/java/res/xml/rows_khmer.xml
new file mode 100644
index 0000000..e399387
--- /dev/null
+++ b/java/res/xml/rows_khmer.xml
@@ -0,0 +1,56 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+**
+** Copyright 2013, 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.
+*/
+-->
+
+<merge
+    xmlns:latin="http://schemas.android.com/apk/res/com.android.inputmethod.latin"
+>
+    <include
+        latin:keyboardLayout="@xml/key_styles_common" />
+    <Row
+        latin:keyWidth="8.3333%p"
+    >
+        <include
+            latin:keyboardLayout="@xml/rowkeys_khmer1" />
+    </Row>
+    <Row
+        latin:keyWidth="8.3333%p"
+    >
+        <include
+            latin:keyboardLayout="@xml/rowkeys_khmer2" />
+    </Row>
+    <Row
+        latin:keyWidth="8.3333%p"
+    >
+        <include
+            latin:keyboardLayout="@xml/rowkeys_khmer3" />
+    </Row>
+    <Row
+        latin:keyWidth="8.3333%p"
+    >
+        <Key
+            latin:keyStyle="shiftKeyStyle" />
+        <include
+            latin:keyboardLayout="@xml/rowkeys_khmer4" />
+        <Key
+            latin:keyStyle="deleteKeyStyle" />
+    </Row>
+    <include
+        latin:keyboardLayout="@xml/row_qwerty4" />
+</merge>
diff --git a/java/res/xml/rows_lao.xml b/java/res/xml/rows_lao.xml
new file mode 100644
index 0000000..321f411
--- /dev/null
+++ b/java/res/xml/rows_lao.xml
@@ -0,0 +1,56 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+**
+** Copyright 2013, 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.
+*/
+-->
+
+<merge
+    xmlns:latin="http://schemas.android.com/apk/res/com.android.inputmethod.latin"
+>
+    <include
+        latin:keyboardLayout="@xml/key_styles_common" />
+    <Row
+        latin:keyWidth="8.3333%p"
+    >
+        <include
+            latin:keyboardLayout="@xml/rowkeys_lao1" />
+    </Row>
+    <Row
+        latin:keyWidth="8.3333%p"
+    >
+        <include
+            latin:keyboardLayout="@xml/rowkeys_lao2" />
+    </Row>
+    <Row
+        latin:keyWidth="8.3333%p"
+    >
+        <include
+            latin:keyboardLayout="@xml/rowkeys_lao3" />
+    </Row>
+    <Row
+        latin:keyWidth="8.3333%p"
+    >
+        <Key
+            latin:keyStyle="shiftKeyStyle" />
+        <include
+            latin:keyboardLayout="@xml/rowkeys_lao4" />
+        <Key
+            latin:keyStyle="deleteKeyStyle" />
+    </Row>
+    <include
+        latin:keyboardLayout="@xml/row_qwerty4" />
+</merge>
diff --git a/java/res/xml/rows_nepali_romanized.xml b/java/res/xml/rows_nepali_romanized.xml
new file mode 100644
index 0000000..6df09c8
--- /dev/null
+++ b/java/res/xml/rows_nepali_romanized.xml
@@ -0,0 +1,52 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+**
+** Copyright 2013, 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.
+*/
+-->
+
+<merge
+    xmlns:latin="http://schemas.android.com/apk/res/com.android.inputmethod.latin"
+>
+    <include
+        latin:keyboardLayout="@xml/key_styles_common" />
+    <Row
+        latin:keyWidth="9.091%p"
+    >
+        <include
+            latin:keyboardLayout="@xml/rowkeys_nepali_romanized1" />
+    </Row>
+    <Row
+            latin:keyWidth="9.091%p"
+    >
+        <include
+            latin:keyboardLayout="@xml/rowkeys_nepali_romanized2" />
+    </Row>
+    <Row
+        latin:keyWidth="8.711%p"
+    >
+        <Key
+            latin:keyStyle="shiftKeyStyle"
+            latin:keyWidth="10.8%p" />
+        <include
+            latin:keyboardLayout="@xml/rowkeys_nepali_romanized3" />
+        <Key
+            latin:keyStyle="deleteKeyStyle"
+            latin:keyWidth="fillRight" />
+    </Row>
+    <include
+        latin:keyboardLayout="@xml/row_qwerty4" />
+</merge>
diff --git a/java/res/xml-sw600dp/rows_10_10_7_symbols.xml b/java/res/xml/rows_nepali_traditional.xml
similarity index 62%
copy from java/res/xml-sw600dp/rows_10_10_7_symbols.xml
copy to java/res/xml/rows_nepali_traditional.xml
index 0e4710c..7789135 100644
--- a/java/res/xml-sw600dp/rows_10_10_7_symbols.xml
+++ b/java/res/xml/rows_nepali_traditional.xml
@@ -2,7 +2,7 @@
 <!--
 /*
 **
-** Copyright 2012, The Android Open Source Project
+** Copyright 2013, 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.
@@ -26,35 +26,31 @@
     <include
         latin:keyboardLayout="@xml/key_styles_currency" />
     <Row
-        latin:keyWidth="9.0%p"
+        latin:keyWidth="9.091%p"
     >
         <include
-            latin:keyboardLayout="@xml/rowkeys_symbols1" />
+            latin:keyboardLayout="@xml/rowkeys_nepali_traditional1" />
+    </Row>
+    <Row
+            latin:keyWidth="9.091%p"
+    >
+        <include
+            latin:keyboardLayout="@xml/rowkeys_nepali_traditional2" />
+    </Row>
+    <Row
+        latin:keyWidth="8.711%p"
+    >
+        <Key
+            latin:keyStyle="shiftKeyStyle"
+            latin:keyWidth="10.8%p" />
+        <include
+            latin:keyboardLayout="@xml/rowkeys_nepali_traditional3_left6" />
+        <include
+            latin:keyboardLayout="@xml/rowkeys_nepali_traditional3_right3" />
         <Key
             latin:keyStyle="deleteKeyStyle"
             latin:keyWidth="fillRight" />
     </Row>
-    <Row
-        latin:keyWidth="9.0%p"
-    >
-        <include
-            latin:keyboardLayout="@xml/rowkeys_symbols2" />
-        <Key
-            latin:keyStyle="enterKeyStyle"
-            latin:keyWidth="fillRight" />
-    </Row>
-    <Row
-        latin:keyWidth="9.0%p"
-    >
-        <Key
-            latin:keyStyle="toMoreSymbolKeyStyle"
-            latin:keyWidth="10.0%p" />
-        <include
-            latin:keyboardLayout="@xml/rowkeys_symbols3" />
-        <Key
-            latin:keyStyle="toMoreSymbolKeyStyle"
-            latin:keyWidth="fillRight" />
-    </Row>
     <include
-        latin:keyboardLayout="@xml/row_symbols4" />
+        latin:keyboardLayout="@xml/row_qwerty4" />
 </merge>
diff --git a/java/res/xml/rows_pcqwerty.xml b/java/res/xml/rows_pcqwerty.xml
index a5ed745..8846989 100644
--- a/java/res/xml/rows_pcqwerty.xml
+++ b/java/res/xml/rows_pcqwerty.xml
@@ -26,8 +26,19 @@
     <Row
         latin:keyWidth="7.692%p"
     >
-        <include
-            latin:keyboardLayout="@xml/rowkeys_pcqwerty1" />
+        <switch>
+            <case
+                latin:keyboardLayoutSetElement="alphabet|alphabetAutomaticShifted"
+            >
+                <include
+                    latin:keyboardLayout="@xml/rowkeys_pcqwerty1" />
+            </case>
+            <!-- keyboardLayoutSetElement="alphabetManualShifted|alphabetShiftLocked|alphabetShiftLockShifted" -->
+            <default>
+                <include
+                     latin:keyboardLayout="@xml/rowkeys_pcqwerty1_shift" />
+            </default>
+        </switch>
     </Row>
     <Row
         latin:keyWidth="7.692%p"
diff --git a/java/res/xml/rows_pcqwerty_symbols.xml b/java/res/xml/rows_pcqwerty_symbols.xml
deleted file mode 100644
index 107a4ad..0000000
--- a/java/res/xml/rows_pcqwerty_symbols.xml
+++ /dev/null
@@ -1,60 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-/*
-**
-** Copyright 2012, 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.
-*/
--->
-
-<merge
-    xmlns:latin="http://schemas.android.com/apk/res/com.android.inputmethod.latin"
->
-    <include
-        latin:keyboardLayout="@xml/key_styles_common" />
-    <include
-        latin:keyboardLayout="@xml/key_styles_currency" />
-    <Row
-        latin:keyWidth="7.692%p"
-    >
-        <include
-            latin:keyboardLayout="@xml/rowkeys_pcqwerty1" />
-    </Row>
-    <Row
-        latin:keyWidth="7.692%p"
-    >
-        <include
-            latin:keyboardLayout="@xml/rowkeys_pcqwerty2" />
-    </Row>
-    <Row
-        latin:keyWidth="7.692%p"
-    >
-        <include
-            latin:keyboardLayout="@xml/rowkeys_pcqwerty3"
-            latin:keyXPos="3.846%p" />
-        <Key
-            latin:keyStyle="deleteKeyStyle"
-            latin:keyWidth="fillRight"
-            latin:visualInsetsLeft="1%p" />
-    </Row>
-    <Row
-        latin:keyWidth="7.692%p"
-    >
-        <include
-            latin:keyboardLayout="@xml/rowkeys_pcqwerty4"
-            latin:keyXPos="11.538%p" />
-    </Row>
-    <include
-        latin:keyboardLayout="@xml/row_pcqwerty5" />
-</merge>
diff --git a/java/res/xml/rows_symbols.xml b/java/res/xml/rows_symbols.xml
index bd1a57e..3f102e2 100644
--- a/java/res/xml/rows_symbols.xml
+++ b/java/res/xml/rows_symbols.xml
@@ -35,7 +35,8 @@
         latin:keyWidth="10%p"
     >
         <include
-            latin:keyboardLayout="@xml/rowkeys_symbols2" />
+            latin:keyboardLayout="@xml/rowkeys_symbols2"
+            latin:keyXPos="5%p" />
     </Row>
     <Row
         latin:keyWidth="10%p"
@@ -51,6 +52,13 @@
             latin:keyWidth="fillRight"
             latin:visualInsetsLeft="1%p" />
     </Row>
-    <include
-        latin:keyboardLayout="@xml/row_symbols4" />
+    <Row
+        latin:keyWidth="10%p"
+    >
+        <Key
+            latin:keyStyle="toAlphaKeyStyle"
+            latin:keyWidth="15%p" />
+        <include
+            latin:keyboardLayout="@xml/row_symbols4" />
+    </Row>
 </merge>
diff --git a/java/res/xml/rows_symbols_shift.xml b/java/res/xml/rows_symbols_shift.xml
index 9c03d90..45ada2a 100644
--- a/java/res/xml/rows_symbols_shift.xml
+++ b/java/res/xml/rows_symbols_shift.xml
@@ -35,6 +35,7 @@
         latin:keyWidth="10%p"
     >
         <include
+            latin:keyXPos="5%p"
             latin:keyboardLayout="@xml/rowkeys_symbols_shift2" />
     </Row>
     <Row
@@ -51,6 +52,13 @@
             latin:keyWidth="fillRight"
             latin:visualInsetsLeft="1%p" />
     </Row>
-    <include
-        latin:keyboardLayout="@xml/row_symbols_shift4" />
+    <Row
+        latin:keyWidth="10%p"
+    >
+        <Key
+            latin:keyStyle="toAlphaKeyStyle"
+            latin:keyWidth="15%p" />
+        <include
+            latin:keyboardLayout="@xml/row_symbols_shift4" />
+    </Row>
 </merge>
diff --git a/java/src/com/android/inputmethod/accessibility/AccessibilityEntityProvider.java b/java/src/com/android/inputmethod/accessibility/AccessibilityEntityProvider.java
index 77f3234..7639432 100644
--- a/java/src/com/android/inputmethod/accessibility/AccessibilityEntityProvider.java
+++ b/java/src/com/android/inputmethod/accessibility/AccessibilityEntityProvider.java
@@ -157,7 +157,7 @@
 
             // Add the virtual children of the root View.
             final Keyboard keyboard = mKeyboardView.getKeyboard();
-            final Key[] keys = keyboard.mKeys;
+            final Key[] keys = keyboard.getKeys();
             for (Key key : keys) {
                 final int childVirtualViewId = generateVirtualViewIdForKey(key);
                 rootInfo.addChild(mKeyboardView, childVirtualViewId);
@@ -172,7 +172,7 @@
             return null;
         }
         final String keyDescription = getKeyDescription(key);
-        final Rect boundsInParent = key.mHitBox;
+        final Rect boundsInParent = key.getHitBox();
 
         // Calculate the key's in-screen bounds.
         mTempBoundsInScreen.set(boundsInParent);
@@ -208,8 +208,8 @@
      * @param key The key to press.
      */
     void simulateKeyPress(final Key key) {
-        final int x = key.mHitBox.centerX();
-        final int y = key.mHitBox.centerY();
+        final int x = key.getHitBox().centerX();
+        final int y = key.getHitBox().centerY();
         final long downTime = SystemClock.uptimeMillis();
         final MotionEvent downEvent = MotionEvent.obtain(
                 downTime, downTime, MotionEvent.ACTION_DOWN, x, y, 0);
@@ -300,7 +300,7 @@
         }
         mVirtualViewIdToKey.clear();
 
-        final Key[] keys = keyboard.mKeys;
+        final Key[] keys = keyboard.getKeys();
         for (Key key : keys) {
             final int virtualViewId = generateVirtualViewIdForKey(key);
             mVirtualViewIdToKey.put(virtualViewId, key);
@@ -325,6 +325,6 @@
         // The key x- and y-coordinates are stable between layout changes.
         // Generate an identifier by bit-shifting the x-coordinate to the
         // left-half of the integer and OR'ing with the y-coordinate.
-        return ((0xFFFF & key.mX) << (Integer.SIZE / 2)) | (0xFFFF & key.mY);
+        return ((0xFFFF & key.getX()) << (Integer.SIZE / 2)) | (0xFFFF & key.getY());
     }
 }
diff --git a/java/src/com/android/inputmethod/accessibility/KeyCodeDescriptionMapper.java b/java/src/com/android/inputmethod/accessibility/KeyCodeDescriptionMapper.java
index 41f5b9a..58624a2 100644
--- a/java/src/com/android/inputmethod/accessibility/KeyCodeDescriptionMapper.java
+++ b/java/src/com/android/inputmethod/accessibility/KeyCodeDescriptionMapper.java
@@ -97,7 +97,7 @@
      */
     public String getDescriptionForKey(final Context context, final Keyboard keyboard,
             final Key key, final boolean shouldObscure) {
-        final int code = key.mCode;
+        final int code = key.getCode();
 
         if (code == Constants.CODE_SWITCH_ALPHA_SYMBOL) {
             final String description = getDescriptionForSwitchAlphaSymbol(context, keyboard);
@@ -116,8 +116,8 @@
             return getDescriptionForActionKey(context, keyboard, key);
         }
 
-        if (!TextUtils.isEmpty(key.mLabel)) {
-            final String label = key.mLabel.toString().trim();
+        if (!TextUtils.isEmpty(key.getLabel())) {
+            final String label = key.getLabel().trim();
 
             // First, attempt to map the label to a pre-defined description.
             if (mKeyLabelMap.containsKey(label)) {
@@ -126,7 +126,7 @@
         }
 
         // Just attempt to speak the description.
-        if (key.mCode != Constants.CODE_UNSPECIFIED) {
+        if (key.getCode() != Constants.CODE_UNSPECIFIED) {
             return getDescriptionForKeyCode(context, keyboard, key, shouldObscure);
         }
         return null;
@@ -215,8 +215,8 @@
         final int resId;
 
         // Always use the label, if available.
-        if (!TextUtils.isEmpty(key.mLabel)) {
-            return key.mLabel.toString().trim();
+        if (!TextUtils.isEmpty(key.getLabel())) {
+            return key.getLabel().trim();
         }
 
         // Otherwise, use the action ID.
@@ -267,7 +267,7 @@
      */
     private String getDescriptionForKeyCode(final Context context, final Keyboard keyboard,
             final Key key, final boolean shouldObscure) {
-        final int code = key.mCode;
+        final int code = key.getCode();
 
         // If the key description should be obscured, now is the time to do it.
         final boolean isDefinedNonCtrl = Character.isDefined(code) && !Character.isISOControl(code);
@@ -280,8 +280,8 @@
         if (isDefinedNonCtrl) {
             return Character.toString((char) code);
         }
-        if (!TextUtils.isEmpty(key.mLabel)) {
-            return key.mLabel;
+        if (!TextUtils.isEmpty(key.getLabel())) {
+            return key.getLabel();
         }
         return context.getString(R.string.spoken_description_unknown, code);
     }
diff --git a/java/src/com/android/inputmethod/compat/ActivityManagerCompatUtils.java b/java/src/com/android/inputmethod/compat/ActivityManagerCompatUtils.java
new file mode 100644
index 0000000..385e3e0
--- /dev/null
+++ b/java/src/com/android/inputmethod/compat/ActivityManagerCompatUtils.java
@@ -0,0 +1,46 @@
+/*
+ * Copyright (C) 2013 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.compat;
+
+import android.app.ActivityManager;
+import android.content.Context;
+
+import java.lang.reflect.Method;
+
+public class ActivityManagerCompatUtils {
+    private static final Object LOCK = new Object();
+    private static volatile Boolean sBoolean = null;
+    private static final Method METHOD_isLowRamDevice = CompatUtils.getMethod(
+            ActivityManager.class, "isLowRamDevice");
+
+    private ActivityManagerCompatUtils() {
+        // Do not instantiate this class.
+    }
+
+    public static boolean isLowRamDevice(Context context) {
+        if (sBoolean == null) {
+            synchronized(LOCK) {
+                if (sBoolean == null) {
+                    final ActivityManager am =
+                            (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);
+                    sBoolean = (Boolean)CompatUtils.invoke(am, false, METHOD_isLowRamDevice);
+                }
+            }
+        }
+        return sBoolean;
+    }
+}
diff --git a/java/src/com/android/inputmethod/keyboard/EmojiCategoryPageIndicatorView.java b/java/src/com/android/inputmethod/keyboard/EmojiCategoryPageIndicatorView.java
new file mode 100644
index 0000000..fed134e
--- /dev/null
+++ b/java/src/com/android/inputmethod/keyboard/EmojiCategoryPageIndicatorView.java
@@ -0,0 +1,67 @@
+/*
+ * Copyright (C) 2013 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.Context;
+import android.graphics.Canvas;
+import android.graphics.Paint;
+import android.util.AttributeSet;
+import android.widget.LinearLayout;
+
+public class EmojiCategoryPageIndicatorView extends LinearLayout {
+    private static final float BOTTOM_MARGIN_RATIO = 0.66f;
+    private final Paint mPaint = new Paint();
+    private int mCategoryPageSize = 0;
+    private int mCurrentCategoryPageId = 0;
+    private float mOffset = 0.0f;
+
+    public EmojiCategoryPageIndicatorView(Context context) {
+        this(context, null /* attrs */);
+    }
+
+    public EmojiCategoryPageIndicatorView(Context context, AttributeSet attrs) {
+        super(context, attrs);
+        mPaint.setColor(context.getResources().getColor(
+                R.color.emoji_category_page_id_view_foreground));
+    }
+
+    public void setCategoryPageId(int size, int id, float offset) {
+        mCategoryPageSize = size;
+        mCurrentCategoryPageId = id;
+        mOffset = offset;
+        invalidate();
+    }
+
+    @Override
+    protected void onDraw(Canvas canvas) {
+        if (mCategoryPageSize == 0) {
+            // If the category is not set yet, just clear and return.
+            canvas.drawColor(0);
+            return;
+        }
+        final float height = getHeight();
+        final float width = getWidth();
+        final float unitWidth = width / mCategoryPageSize;
+        final float left = unitWidth * mCurrentCategoryPageId + mOffset * unitWidth;
+        final float top = 0.0f;
+        final float right = left + unitWidth;
+        final float bottom = height * BOTTOM_MARGIN_RATIO;
+        canvas.drawRect(left, top, right, bottom, mPaint);
+    }
+}
diff --git a/java/src/com/android/inputmethod/keyboard/EmojiKeyboardView.java b/java/src/com/android/inputmethod/keyboard/EmojiKeyboardView.java
new file mode 100644
index 0000000..4e61eda
--- /dev/null
+++ b/java/src/com/android/inputmethod/keyboard/EmojiKeyboardView.java
@@ -0,0 +1,766 @@
+/*
+ * Copyright (C) 2013 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 static com.android.inputmethod.latin.Constants.NOT_A_COORDINATE;
+
+import android.content.Context;
+import android.content.SharedPreferences;
+import android.content.res.ColorStateList;
+import android.content.res.Resources;
+import android.content.res.TypedArray;
+import android.graphics.Rect;
+import android.os.Build;
+import android.preference.PreferenceManager;
+import android.support.v4.view.PagerAdapter;
+import android.support.v4.view.ViewPager;
+import android.text.format.DateUtils;
+import android.util.AttributeSet;
+import android.util.Log;
+import android.util.Pair;
+import android.util.SparseArray;
+import android.view.LayoutInflater;
+import android.view.MotionEvent;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.ImageView;
+import android.widget.LinearLayout;
+import android.widget.TabHost;
+import android.widget.TabHost.OnTabChangeListener;
+import android.widget.TextView;
+
+import com.android.inputmethod.keyboard.internal.DynamicGridKeyboard;
+import com.android.inputmethod.keyboard.internal.ScrollKeyboardView;
+import com.android.inputmethod.keyboard.internal.ScrollViewWithNotifier;
+import com.android.inputmethod.latin.Constants;
+import com.android.inputmethod.latin.R;
+import com.android.inputmethod.latin.SubtypeSwitcher;
+import com.android.inputmethod.latin.settings.Settings;
+import com.android.inputmethod.latin.utils.CollectionUtils;
+import com.android.inputmethod.latin.utils.ResourceUtils;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Comparator;
+import java.util.HashMap;
+import java.util.concurrent.ConcurrentHashMap;
+
+/**
+ * View class to implement Emoji keyboards.
+ * The Emoji keyboard consists of group of views {@link R.layout#emoji_keyboard_view}.
+ * <ol>
+ * <li> Emoji category tabs.
+ * <li> Delete button.
+ * <li> Emoji keyboard pages that can be scrolled by swiping horizontally or by selecting a tab.
+ * <li> Back to main keyboard button and enter button.
+ * </ol>
+ * Because of the above reasons, this class doesn't extend {@link KeyboardView}.
+ */
+public final class EmojiKeyboardView extends LinearLayout implements OnTabChangeListener,
+        ViewPager.OnPageChangeListener, View.OnClickListener,
+        ScrollKeyboardView.OnKeyClickListener {
+    private static final String TAG = EmojiKeyboardView.class.getSimpleName();
+    private final int mKeyBackgroundId;
+    private final int mEmojiFunctionalKeyBackgroundId;
+    private final KeyboardLayoutSet mLayoutSet;
+    private final ColorStateList mTabLabelColor;
+    private final DeleteKeyOnTouchListener mDeleteKeyOnTouchListener;
+    private EmojiKeyboardAdapter mEmojiKeyboardAdapter;
+
+    private TabHost mTabHost;
+    private ViewPager mEmojiPager;
+    private EmojiCategoryPageIndicatorView mEmojiCategoryPageIndicatorView;
+
+    private KeyboardActionListener mKeyboardActionListener = KeyboardActionListener.EMPTY_LISTENER;
+
+    private static final int CATEGORY_ID_UNSPECIFIED = -1;
+    public static final int CATEGORY_ID_RECENTS = 0;
+    public static final int CATEGORY_ID_PEOPLE = 1;
+    public static final int CATEGORY_ID_OBJECTS = 2;
+    public static final int CATEGORY_ID_NATURE = 3;
+    public static final int CATEGORY_ID_PLACES = 4;
+    public static final int CATEGORY_ID_SYMBOLS = 5;
+    public static final int CATEGORY_ID_EMOTICONS = 6;
+
+    private static class CategoryProperties {
+        public int mCategoryId;
+        public int mPageCount;
+        public CategoryProperties(final int categoryId, final int pageCount) {
+            mCategoryId = categoryId;
+            mPageCount = pageCount;
+        }
+    }
+
+    private static class EmojiCategory {
+        private static final String[] sCategoryName = {
+                "recents",
+                "people",
+                "objects",
+                "nature",
+                "places",
+                "symbols",
+                "emoticons" };
+        private static final int[] sCategoryIcon = new int[] {
+                R.drawable.ic_emoji_recent_light,
+                R.drawable.ic_emoji_people_light,
+                R.drawable.ic_emoji_objects_light,
+                R.drawable.ic_emoji_nature_light,
+                R.drawable.ic_emoji_places_light,
+                R.drawable.ic_emoji_symbols_light,
+                0 };
+        private static final String[] sCategoryLabel =
+                { null, null, null, null, null, null, ":-)" };
+        private static final int[] sCategoryElementId = {
+                KeyboardId.ELEMENT_EMOJI_RECENTS,
+                KeyboardId.ELEMENT_EMOJI_CATEGORY1,
+                KeyboardId.ELEMENT_EMOJI_CATEGORY2,
+                KeyboardId.ELEMENT_EMOJI_CATEGORY3,
+                KeyboardId.ELEMENT_EMOJI_CATEGORY4,
+                KeyboardId.ELEMENT_EMOJI_CATEGORY5,
+                KeyboardId.ELEMENT_EMOJI_CATEGORY6 };
+        private final SharedPreferences mPrefs;
+        private final int mMaxPageKeyCount;
+        private final KeyboardLayoutSet mLayoutSet;
+        private final HashMap<String, Integer> mCategoryNameToIdMap = CollectionUtils.newHashMap();
+        private final ArrayList<CategoryProperties> mShownCategories =
+                CollectionUtils.newArrayList();
+        private final ConcurrentHashMap<Long, DynamicGridKeyboard>
+                mCategoryKeyboardMap = new ConcurrentHashMap<Long, DynamicGridKeyboard>();
+
+        private int mCurrentCategoryId = CATEGORY_ID_UNSPECIFIED;
+        private int mCurrentCategoryPageId = 0;
+
+        public EmojiCategory(final SharedPreferences prefs, final Resources res,
+                final KeyboardLayoutSet layoutSet) {
+            mPrefs = prefs;
+            mMaxPageKeyCount = res.getInteger(R.integer.emoji_keyboard_max_key_count);
+            mLayoutSet = layoutSet;
+            for (int i = 0; i < sCategoryName.length; ++i) {
+                mCategoryNameToIdMap.put(sCategoryName[i], i);
+            }
+            addShownCategoryId(CATEGORY_ID_RECENTS);
+            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR2) {
+                addShownCategoryId(CATEGORY_ID_PEOPLE);
+                addShownCategoryId(CATEGORY_ID_OBJECTS);
+                addShownCategoryId(CATEGORY_ID_NATURE);
+                addShownCategoryId(CATEGORY_ID_PLACES);
+                mCurrentCategoryId = CATEGORY_ID_PEOPLE;
+            } else {
+                mCurrentCategoryId = CATEGORY_ID_SYMBOLS;
+            }
+            addShownCategoryId(CATEGORY_ID_SYMBOLS);
+            addShownCategoryId(CATEGORY_ID_EMOTICONS);
+            getKeyboard(CATEGORY_ID_RECENTS, 0 /* cagetoryPageId */)
+                    .loadRecentKeys(mCategoryKeyboardMap.values());
+        }
+
+        private void addShownCategoryId(int categoryId) {
+            // Load a keyboard of categoryId
+            getKeyboard(categoryId, 0 /* cagetoryPageId */);
+            final CategoryProperties properties =
+                    new CategoryProperties(categoryId, getCategoryPageCount(categoryId));
+            mShownCategories.add(properties);
+        }
+
+        public String getCategoryName(int categoryId, int categoryPageId) {
+            return sCategoryName[categoryId] + "-" + categoryPageId;
+        }
+
+        public int getCategoryId(String name) {
+            final String[] strings = name.split("-");
+            return mCategoryNameToIdMap.get(strings[0]);
+        }
+
+        public int getCategoryIcon(int categoryId) {
+            return sCategoryIcon[categoryId];
+        }
+
+        public String getCategoryLabel(int categoryId) {
+            return sCategoryLabel[categoryId];
+        }
+
+        public ArrayList<CategoryProperties> getShownCategories() {
+            return mShownCategories;
+        }
+
+        public int getCurrentCategoryId() {
+            return mCurrentCategoryId;
+        }
+
+        public int getCurrentCategoryPageSize() {
+            return getCategoryPageSize(mCurrentCategoryId);
+        }
+
+        public int getCategoryPageSize(int categoryId) {
+            for (final CategoryProperties prop : mShownCategories) {
+                if (prop.mCategoryId == categoryId) {
+                    return prop.mPageCount;
+                }
+            }
+            Log.w(TAG, "Invalid category id: " + categoryId);
+            // Should not reach here.
+            return 0;
+        }
+
+        public void setCurrentCategoryId(int categoryId) {
+            mCurrentCategoryId = categoryId;
+        }
+
+        public void setCurrentCategoryPageId(int id) {
+            mCurrentCategoryPageId = id;
+        }
+
+        public int getCurrentCategoryPageId() {
+            return mCurrentCategoryPageId;
+        }
+
+        public void saveLastTypedCategoryPage() {
+            Settings.writeEmojiCategoryLastTypedId(
+                    mPrefs, mCurrentCategoryId, mCurrentCategoryPageId);
+        }
+
+        public boolean isInRecentTab() {
+            return mCurrentCategoryId == CATEGORY_ID_RECENTS;
+        }
+
+        public int getTabIdFromCategoryId(int categoryId) {
+            for (int i = 0; i < mShownCategories.size(); ++i) {
+                if (mShownCategories.get(i).mCategoryId == categoryId) {
+                    return i;
+                }
+            }
+            Log.w(TAG, "categoryId not found: " + categoryId);
+            return 0;
+        }
+
+        // Returns the view pager's page position for the categoryId
+        public int getPageIdFromCategoryId(int categoryId) {
+            final int lastSavedCategoryPageId =
+                    Settings.readEmojiCategoryLastTypedId(mPrefs, categoryId);
+            int sum = 0;
+            for (int i = 0; i < mShownCategories.size(); ++i) {
+                final CategoryProperties props = mShownCategories.get(i);
+                if (props.mCategoryId == categoryId) {
+                    return sum + lastSavedCategoryPageId;
+                }
+                sum += props.mPageCount;
+            }
+            Log.w(TAG, "categoryId not found: " + categoryId);
+            return 0;
+        }
+
+        public int getRecentTabId() {
+            return getTabIdFromCategoryId(CATEGORY_ID_RECENTS);
+        }
+
+        private int getCategoryPageCount(int categoryId) {
+            final Keyboard keyboard = mLayoutSet.getKeyboard(sCategoryElementId[categoryId]);
+            return (keyboard.getKeys().length - 1) / mMaxPageKeyCount + 1;
+        }
+
+        // Returns a pair of the category id and the category page id from the view pager's page
+        // position. The category page id is numbered in each category. And the view page position
+        // is the position of the current shown page in the view pager which contains all pages of
+        // all categories.
+        public Pair<Integer, Integer> getCategoryIdAndPageIdFromPagePosition(int position) {
+            int sum = 0;
+            for (CategoryProperties properties : mShownCategories) {
+                final int temp = sum;
+                sum += properties.mPageCount;
+                if (sum > position) {
+                    return new Pair<Integer, Integer>(properties.mCategoryId, position - temp);
+                }
+            }
+            return null;
+        }
+
+        // Returns a keyboard from the view pager's page position.
+        public DynamicGridKeyboard getKeyboardFromPagePosition(int position) {
+            final Pair<Integer, Integer> categoryAndId =
+                    getCategoryIdAndPageIdFromPagePosition(position);
+            if (categoryAndId != null) {
+                return getKeyboard(categoryAndId.first, categoryAndId.second);
+            }
+            return null;
+        }
+
+        public DynamicGridKeyboard getKeyboard(int categoryId, int id) {
+            synchronized(mCategoryKeyboardMap) {
+                final long key = (((long) categoryId) << Constants.MAX_INT_BIT_COUNT) | id;
+                final DynamicGridKeyboard kbd;
+                if (!mCategoryKeyboardMap.containsKey(key)) {
+                    if (categoryId != CATEGORY_ID_RECENTS) {
+                        final Keyboard keyboard =
+                                mLayoutSet.getKeyboard(sCategoryElementId[categoryId]);
+                        final Key[][] sortedKeys = sortKeys(keyboard.getKeys(), mMaxPageKeyCount);
+                        for (int i = 0; i < sortedKeys.length; ++i) {
+                            final DynamicGridKeyboard tempKbd = new DynamicGridKeyboard(mPrefs,
+                                    mLayoutSet.getKeyboard(KeyboardId.ELEMENT_EMOJI_RECENTS),
+                                    mMaxPageKeyCount, categoryId, i /* categoryPageId */);
+                            for (Key emojiKey : sortedKeys[i]) {
+                                if (emojiKey == null) {
+                                    break;
+                                }
+                                tempKbd.addKeyLast(emojiKey);
+                            }
+                            mCategoryKeyboardMap.put((((long) categoryId)
+                                    << Constants.MAX_INT_BIT_COUNT) | i, tempKbd);
+                        }
+                        kbd = mCategoryKeyboardMap.get(key);
+                    } else {
+                        kbd = new DynamicGridKeyboard(mPrefs,
+                                mLayoutSet.getKeyboard(KeyboardId.ELEMENT_EMOJI_RECENTS),
+                                mMaxPageKeyCount, categoryId, 0 /* categoryPageId */);
+                        mCategoryKeyboardMap.put(key, kbd);
+                    }
+                } else {
+                    kbd = mCategoryKeyboardMap.get(key);
+                }
+                return kbd;
+            }
+        }
+
+        public int getTotalPageCountOfAllCategories() {
+            int sum = 0;
+            for (CategoryProperties properties : mShownCategories) {
+                sum += properties.mPageCount;
+            }
+            return sum;
+        }
+
+        private Key[][] sortKeys(Key[] inKeys, int maxPageCount) {
+            Key[] keys = Arrays.copyOf(inKeys, inKeys.length);
+            Arrays.sort(keys, 0, keys.length, new Comparator<Key>() {
+                @Override
+                public int compare(Key lhs, Key rhs) {
+                    final Rect lHitBox = lhs.getHitBox();
+                    final Rect rHitBox = rhs.getHitBox();
+                    if (lHitBox.top < rHitBox.top) {
+                        return -1;
+                    } else if (lHitBox.top > rHitBox.top) {
+                        return 1;
+                    }
+                    if (lHitBox.left < rHitBox.left) {
+                        return -1;
+                    } else if (lHitBox.left > rHitBox.left) {
+                        return 1;
+                    }
+                    if (lhs.getCode() == rhs.getCode()) {
+                        return 0;
+                    }
+                    return lhs.getCode() < rhs.getCode() ? -1 : 1;
+                }
+            });
+            final int pageCount = (keys.length - 1) / maxPageCount + 1;
+            final Key[][] retval = new Key[pageCount][maxPageCount];
+            for (int i = 0; i < keys.length; ++i) {
+                retval[i / maxPageCount][i % maxPageCount] = keys[i];
+            }
+            return retval;
+        }
+    }
+
+    private final EmojiCategory mEmojiCategory;
+
+    public EmojiKeyboardView(final Context context, final AttributeSet attrs) {
+        this(context, attrs, R.attr.emojiKeyboardViewStyle);
+    }
+
+    public EmojiKeyboardView(final Context context, final AttributeSet attrs, final int defStyle) {
+        super(context, attrs, defStyle);
+        final TypedArray keyboardViewAttr = context.obtainStyledAttributes(attrs,
+                R.styleable.KeyboardView, defStyle, R.style.KeyboardView);
+        mKeyBackgroundId = keyboardViewAttr.getResourceId(
+                R.styleable.KeyboardView_keyBackground, 0);
+        mEmojiFunctionalKeyBackgroundId = keyboardViewAttr.getResourceId(
+                R.styleable.KeyboardView_keyBackgroundEmojiFunctional, 0);
+        keyboardViewAttr.recycle();
+        final TypedArray emojiKeyboardViewAttr = context.obtainStyledAttributes(attrs,
+                R.styleable.EmojiKeyboardView, defStyle, R.style.EmojiKeyboardView);
+        mTabLabelColor = emojiKeyboardViewAttr.getColorStateList(
+                R.styleable.EmojiKeyboardView_emojiTabLabelColor);
+        emojiKeyboardViewAttr.recycle();
+        final KeyboardLayoutSet.Builder builder = new KeyboardLayoutSet.Builder(
+                context, null /* editorInfo */);
+        final Resources res = context.getResources();
+        final EmojiLayoutParams emojiLp = new EmojiLayoutParams(res);
+        builder.setSubtype(SubtypeSwitcher.getInstance().getEmojiSubtype());
+        builder.setKeyboardGeometry(ResourceUtils.getDefaultKeyboardWidth(res),
+                emojiLp.mEmojiKeyboardHeight);
+        builder.setOptions(false, false, false /* lanuageSwitchKeyEnabled */);
+        mLayoutSet = builder.build();
+        mEmojiCategory = new EmojiCategory(PreferenceManager.getDefaultSharedPreferences(context),
+                context.getResources(), builder.build());
+        mDeleteKeyOnTouchListener = new DeleteKeyOnTouchListener(context);
+    }
+
+    @Override
+    protected void onMeasure(final int widthMeasureSpec, final int heightMeasureSpec) {
+        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
+        final Resources res = getContext().getResources();
+        // The main keyboard expands to the entire this {@link KeyboardView}.
+        final int width = ResourceUtils.getDefaultKeyboardWidth(res)
+                + getPaddingLeft() + getPaddingRight();
+        final int height = ResourceUtils.getDefaultKeyboardHeight(res)
+                + res.getDimensionPixelSize(R.dimen.suggestions_strip_height)
+                + getPaddingTop() + getPaddingBottom();
+        setMeasuredDimension(width, height);
+    }
+
+    private void addTab(final TabHost host, final int categoryId) {
+        final String tabId = mEmojiCategory.getCategoryName(categoryId, 0 /* categoryPageId */);
+        final TabHost.TabSpec tspec = host.newTabSpec(tabId);
+        tspec.setContent(R.id.emoji_keyboard_dummy);
+        if (mEmojiCategory.getCategoryIcon(categoryId) != 0) {
+            final ImageView iconView = (ImageView)LayoutInflater.from(getContext()).inflate(
+                    R.layout.emoji_keyboard_tab_icon, null);
+            iconView.setImageResource(mEmojiCategory.getCategoryIcon(categoryId));
+            tspec.setIndicator(iconView);
+        }
+        if (mEmojiCategory.getCategoryLabel(categoryId) != null) {
+            final TextView textView = (TextView)LayoutInflater.from(getContext()).inflate(
+                    R.layout.emoji_keyboard_tab_label, null);
+            textView.setText(mEmojiCategory.getCategoryLabel(categoryId));
+            textView.setTextColor(mTabLabelColor);
+            tspec.setIndicator(textView);
+        }
+        host.addTab(tspec);
+    }
+
+    @Override
+    protected void onFinishInflate() {
+        mTabHost = (TabHost)findViewById(R.id.emoji_category_tabhost);
+        mTabHost.setup();
+        for (final CategoryProperties properties : mEmojiCategory.getShownCategories()) {
+            addTab(mTabHost, properties.mCategoryId);
+        }
+        mTabHost.setOnTabChangedListener(this);
+        mTabHost.getTabWidget().setStripEnabled(true);
+
+        mEmojiKeyboardAdapter = new EmojiKeyboardAdapter(mEmojiCategory, mLayoutSet, this);
+
+        mEmojiPager = (ViewPager)findViewById(R.id.emoji_keyboard_pager);
+        mEmojiPager.setAdapter(mEmojiKeyboardAdapter);
+        mEmojiPager.setOnPageChangeListener(this);
+        mEmojiPager.setOffscreenPageLimit(0);
+        final Resources res = getResources();
+        final EmojiLayoutParams emojiLp = new EmojiLayoutParams(res);
+        emojiLp.setPagerProperties(mEmojiPager);
+
+        mEmojiCategoryPageIndicatorView =
+                (EmojiCategoryPageIndicatorView)findViewById(R.id.emoji_category_page_id_view);
+        emojiLp.setCategoryPageIdViewProperties(mEmojiCategoryPageIndicatorView);
+
+        setCurrentCategoryId(mEmojiCategory.getCurrentCategoryId(), true /* force */);
+
+        final LinearLayout actionBar = (LinearLayout)findViewById(R.id.emoji_action_bar);
+        emojiLp.setActionBarProperties(actionBar);
+
+        final ImageView deleteKey = (ImageView)findViewById(R.id.emoji_keyboard_delete);
+        deleteKey.setTag(Constants.CODE_DELETE);
+        deleteKey.setOnTouchListener(mDeleteKeyOnTouchListener);
+        final ImageView alphabetKey = (ImageView)findViewById(R.id.emoji_keyboard_alphabet);
+        alphabetKey.setBackgroundResource(mEmojiFunctionalKeyBackgroundId);
+        alphabetKey.setTag(Constants.CODE_SWITCH_ALPHA_SYMBOL);
+        alphabetKey.setOnClickListener(this);
+        final ImageView spaceKey = (ImageView)findViewById(R.id.emoji_keyboard_space);
+        spaceKey.setBackgroundResource(mKeyBackgroundId);
+        spaceKey.setTag(Constants.CODE_SPACE);
+        spaceKey.setOnClickListener(this);
+        emojiLp.setKeyProperties(spaceKey);
+        final ImageView sendKey = (ImageView)findViewById(R.id.emoji_keyboard_send);
+        sendKey.setBackgroundResource(mEmojiFunctionalKeyBackgroundId);
+        sendKey.setTag(Constants.CODE_ENTER);
+        sendKey.setOnClickListener(this);
+    }
+
+    @Override
+    public void onTabChanged(final String tabId) {
+        final int categoryId = mEmojiCategory.getCategoryId(tabId);
+        setCurrentCategoryId(categoryId, false /* force */);
+        updateEmojiCategoryPageIdView();
+    }
+
+
+    @Override
+    public void onPageSelected(final int position) {
+        final Pair<Integer, Integer> newPos =
+                mEmojiCategory.getCategoryIdAndPageIdFromPagePosition(position);
+        setCurrentCategoryId(newPos.first /* categoryId */, false /* force */);
+        mEmojiCategory.setCurrentCategoryPageId(newPos.second /* categoryPageId */);
+        updateEmojiCategoryPageIdView();
+    }
+
+    @Override
+    public void onPageScrollStateChanged(final int state) {
+        // Ignore this message. Only want the actual page selected.
+    }
+
+    @Override
+    public void onPageScrolled(final int position, final float positionOffset,
+            final int positionOffsetPixels) {
+        final Pair<Integer, Integer> newPos =
+                mEmojiCategory.getCategoryIdAndPageIdFromPagePosition(position);
+        final int newCategoryId = newPos.first;
+        final int newCategorySize = mEmojiCategory.getCategoryPageSize(newCategoryId);
+        final int currentCategoryId = mEmojiCategory.getCurrentCategoryId();
+        final int currentCategoryPageId = mEmojiCategory.getCurrentCategoryPageId();
+        final int currentCategorySize = mEmojiCategory.getCurrentCategoryPageSize();
+        if (newCategoryId == currentCategoryId) {
+            mEmojiCategoryPageIndicatorView.setCategoryPageId(
+                    newCategorySize, newPos.second, positionOffset);
+        } else if (newCategoryId > currentCategoryId) {
+            mEmojiCategoryPageIndicatorView.setCategoryPageId(
+                    currentCategorySize, currentCategoryPageId, positionOffset);
+        } else if (newCategoryId < currentCategoryId) {
+            mEmojiCategoryPageIndicatorView.setCategoryPageId(
+                    currentCategorySize, currentCategoryPageId, positionOffset - 1);
+        }
+    }
+
+    @Override
+    public void onClick(final View v) {
+        if (v.getTag() instanceof Integer) {
+            final int code = (Integer)v.getTag();
+            registerCode(code);
+            return;
+        }
+    }
+
+    private void registerCode(final int code) {
+        mKeyboardActionListener.onPressKey(code, 0 /* repeatCount */, true /* isSinglePointer */);
+        mKeyboardActionListener.onCodeInput(code, NOT_A_COORDINATE, NOT_A_COORDINATE);
+        mKeyboardActionListener.onReleaseKey(code, false /* withSliding */);
+    }
+
+    @Override
+    public void onKeyClick(final Key key) {
+        mEmojiKeyboardAdapter.addRecentKey(key);
+        mEmojiCategory.saveLastTypedCategoryPage();
+        final int code = key.getCode();
+        if (code == Constants.CODE_OUTPUT_TEXT) {
+            mKeyboardActionListener.onTextInput(key.getOutputText());
+            return;
+        }
+        registerCode(code);
+    }
+
+    public void setHardwareAcceleratedDrawingEnabled(final boolean enabled) {
+        // TODO:
+    }
+
+    public void setKeyboardActionListener(final KeyboardActionListener listener) {
+        mKeyboardActionListener = listener;
+        mDeleteKeyOnTouchListener.setKeyboardActionListener(mKeyboardActionListener);
+    }
+
+    private void updateEmojiCategoryPageIdView() {
+        if (mEmojiCategoryPageIndicatorView == null) {
+            return;
+        }
+        mEmojiCategoryPageIndicatorView.setCategoryPageId(
+                mEmojiCategory.getCurrentCategoryPageSize(),
+                mEmojiCategory.getCurrentCategoryPageId(), 0.0f /* offset */);
+    }
+
+    private void setCurrentCategoryId(final int categoryId, final boolean force) {
+        if (mEmojiCategory.getCurrentCategoryId() == categoryId && !force) {
+            return;
+        }
+
+        mEmojiCategory.setCurrentCategoryId(categoryId);
+        final int newTabId = mEmojiCategory.getTabIdFromCategoryId(categoryId);
+        final int newCategoryPageId = mEmojiCategory.getPageIdFromCategoryId(categoryId);
+        if (force || mEmojiCategory.getCategoryIdAndPageIdFromPagePosition(
+                mEmojiPager.getCurrentItem()).first != categoryId) {
+            mEmojiPager.setCurrentItem(newCategoryPageId, false /* smoothScroll */);
+        }
+        if (force || mTabHost.getCurrentTab() != newTabId) {
+            mTabHost.setCurrentTab(newTabId);
+        }
+    }
+
+    private static class EmojiKeyboardAdapter extends PagerAdapter {
+        private final ScrollKeyboardView.OnKeyClickListener mListener;
+        private final DynamicGridKeyboard mRecentsKeyboard;
+        private final SparseArray<ScrollKeyboardView> mActiveKeyboardView =
+                CollectionUtils.newSparseArray();
+        private final EmojiCategory mEmojiCategory;
+        private int mActivePosition = 0;
+
+        public EmojiKeyboardAdapter(final EmojiCategory emojiCategory,
+                final KeyboardLayoutSet layoutSet,
+                final ScrollKeyboardView.OnKeyClickListener listener) {
+            mEmojiCategory = emojiCategory;
+            mListener = listener;
+            mRecentsKeyboard = mEmojiCategory.getKeyboard(CATEGORY_ID_RECENTS, 0);
+        }
+
+        public void addRecentKey(final Key key) {
+            if (mEmojiCategory.isInRecentTab()) {
+                return;
+            }
+            mRecentsKeyboard.addKeyFirst(key);
+            final KeyboardView recentKeyboardView =
+                    mActiveKeyboardView.get(mEmojiCategory.getRecentTabId());
+            if (recentKeyboardView != null) {
+                recentKeyboardView.invalidateAllKeys();
+            }
+        }
+
+        @Override
+        public int getCount() {
+            return mEmojiCategory.getTotalPageCountOfAllCategories();
+        }
+
+        @Override
+        public void setPrimaryItem(final View container, final int position, final Object object) {
+            if (mActivePosition == position) {
+                return;
+            }
+            final ScrollKeyboardView oldKeyboardView = mActiveKeyboardView.get(mActivePosition);
+            if (oldKeyboardView != null) {
+                oldKeyboardView.releaseCurrentKey();
+                oldKeyboardView.deallocateMemory();
+            }
+            mActivePosition = position;
+        }
+
+        @Override
+        public Object instantiateItem(final ViewGroup container, final int position) {
+            final Keyboard keyboard =
+                    mEmojiCategory.getKeyboardFromPagePosition(position);
+            final LayoutInflater inflater = LayoutInflater.from(container.getContext());
+            final View view = inflater.inflate(
+                    R.layout.emoji_keyboard_page, container, false /* attachToRoot */);
+            final ScrollKeyboardView keyboardView = (ScrollKeyboardView)view.findViewById(
+                    R.id.emoji_keyboard_page);
+            keyboardView.setKeyboard(keyboard);
+            keyboardView.setOnKeyClickListener(mListener);
+            final ScrollViewWithNotifier scrollView = (ScrollViewWithNotifier)view.findViewById(
+                    R.id.emoji_keyboard_scroller);
+            keyboardView.setScrollView(scrollView);
+            container.addView(view);
+            mActiveKeyboardView.put(position, keyboardView);
+            return view;
+        }
+
+        @Override
+        public boolean isViewFromObject(final View view, final Object object) {
+            return view == object;
+        }
+
+        @Override
+        public void destroyItem(final ViewGroup container, final int position,
+                final Object object) {
+            final ScrollKeyboardView keyboardView = mActiveKeyboardView.get(position);
+            if (keyboardView != null) {
+                keyboardView.deallocateMemory();
+                mActiveKeyboardView.remove(position);
+            }
+            container.removeView(keyboardView);
+        }
+    }
+
+    // TODO: Do the same things done in PointerTracker
+    private static class DeleteKeyOnTouchListener implements OnTouchListener {
+        private static final long MAX_REPEAT_COUNT_TIME = 30 * DateUtils.SECOND_IN_MILLIS;
+        private final int mDeleteKeyPressedBackgroundColor;
+        private final long mKeyRepeatStartTimeout;
+        private final long mKeyRepeatInterval;
+
+        public DeleteKeyOnTouchListener(Context context) {
+            final Resources res = context.getResources();
+            mDeleteKeyPressedBackgroundColor =
+                    res.getColor(R.color.emoji_key_pressed_background_color);
+            mKeyRepeatStartTimeout = res.getInteger(R.integer.config_key_repeat_start_timeout);
+            mKeyRepeatInterval = res.getInteger(R.integer.config_key_repeat_interval);
+        }
+
+        private KeyboardActionListener mKeyboardActionListener =
+                KeyboardActionListener.EMPTY_LISTENER;
+        private DummyRepeatKeyRepeatTimer mTimer;
+
+        private synchronized void startRepeat() {
+            if (mTimer != null) {
+                abortRepeat();
+            }
+            mTimer = new DummyRepeatKeyRepeatTimer();
+            mTimer.start();
+        }
+
+        private synchronized void abortRepeat() {
+            mTimer.abort();
+            mTimer = null;
+        }
+
+        // TODO: Remove
+        // This function is mimicking the repeat code in PointerTracker.
+        // Specifically referring to PointerTracker#startRepeatKey and PointerTracker#onKeyRepeat.
+        private class DummyRepeatKeyRepeatTimer extends Thread {
+            public boolean mAborted = false;
+
+            @Override
+            public void run() {
+                int timeCount = 0;
+                while (timeCount < MAX_REPEAT_COUNT_TIME && !mAborted) {
+                    if (timeCount > mKeyRepeatStartTimeout) {
+                        pressDelete();
+                    }
+                    timeCount += mKeyRepeatInterval;
+                    try {
+                        Thread.sleep(mKeyRepeatInterval);
+                    } catch (InterruptedException e) {
+                    }
+                }
+            }
+
+            public void abort() {
+                mAborted = true;
+            }
+        }
+
+        public void pressDelete() {
+            mKeyboardActionListener.onPressKey(
+                    Constants.CODE_DELETE, 0 /* repeatCount */, true /* isSinglePointer */);
+            mKeyboardActionListener.onCodeInput(
+                    Constants.CODE_DELETE, NOT_A_COORDINATE, NOT_A_COORDINATE);
+            mKeyboardActionListener.onReleaseKey(
+                    Constants.CODE_DELETE, false /* withSliding */);
+        }
+
+        public void setKeyboardActionListener(KeyboardActionListener listener) {
+            mKeyboardActionListener = listener;
+        }
+
+        @Override
+        public boolean onTouch(View v, MotionEvent event) {
+            switch(event.getAction()) {
+                case MotionEvent.ACTION_DOWN:
+                    v.setBackgroundColor(mDeleteKeyPressedBackgroundColor);
+                    pressDelete();
+                    startRepeat();
+                    return true;
+                case MotionEvent.ACTION_UP:
+                    v.setBackgroundColor(0);
+                    abortRepeat();
+                    return true;
+            }
+            return false;
+        }
+    }
+}
diff --git a/java/src/com/android/inputmethod/keyboard/EmojiLayoutParams.java b/java/src/com/android/inputmethod/keyboard/EmojiLayoutParams.java
new file mode 100644
index 0000000..267fad5
--- /dev/null
+++ b/java/src/com/android/inputmethod/keyboard/EmojiLayoutParams.java
@@ -0,0 +1,90 @@
+/*
+ * Copyright (C) 2013 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 com.android.inputmethod.latin.utils.ResourceUtils;
+
+import android.content.res.Resources;
+import android.support.v4.view.ViewPager;
+import android.widget.ImageView;
+import android.widget.LinearLayout;
+
+public class EmojiLayoutParams {
+    private static final int DEFAULT_KEYBOARD_ROWS = 4;
+
+    public final int mEmojiPagerHeight;
+    private final int mEmojiPagerBottomMargin;
+    public final int mEmojiKeyboardHeight;
+    private final int mEmojiCategoryPageIdViewHeight;
+    public final int mEmojiActionBarHeight;
+    public final int mKeyVerticalGap;
+    private final int mKeyHorizontalGap;
+    private final int mBottomPadding;
+    private final int mTopPadding;
+
+    public EmojiLayoutParams(Resources res) {
+        final int defaultKeyboardHeight = ResourceUtils.getDefaultKeyboardHeight(res);
+        final int defaultKeyboardWidth = ResourceUtils.getDefaultKeyboardWidth(res);
+        mKeyVerticalGap = (int) res.getFraction(R.fraction.key_bottom_gap_ics,
+                (int) defaultKeyboardHeight, (int) defaultKeyboardHeight);
+        mBottomPadding = (int) res.getFraction(R.fraction.keyboard_bottom_padding_ics,
+                (int) defaultKeyboardHeight, (int) defaultKeyboardHeight);
+        mTopPadding = (int) res.getFraction(R.fraction.keyboard_top_padding_ics,
+                (int) defaultKeyboardHeight, (int) defaultKeyboardHeight);
+        mKeyHorizontalGap = (int) (res.getFraction(R.fraction.key_horizontal_gap_ics,
+                defaultKeyboardWidth, defaultKeyboardWidth));
+        mEmojiCategoryPageIdViewHeight =
+                (int) (res.getDimension(R.dimen.emoji_category_page_id_height));
+        final int baseheight = defaultKeyboardHeight - mBottomPadding - mTopPadding
+                + mKeyVerticalGap;
+        mEmojiActionBarHeight = ((int) baseheight) / DEFAULT_KEYBOARD_ROWS
+                - (mKeyVerticalGap - mBottomPadding) / 2;
+        mEmojiPagerHeight = defaultKeyboardHeight - mEmojiActionBarHeight
+                - mEmojiCategoryPageIdViewHeight;
+        mEmojiPagerBottomMargin = mKeyVerticalGap / 2;
+        mEmojiKeyboardHeight = mEmojiPagerHeight - mEmojiPagerBottomMargin - 1;
+    }
+
+    public void setPagerProperties(ViewPager vp) {
+        final LinearLayout.LayoutParams lp = (LinearLayout.LayoutParams) vp.getLayoutParams();
+        lp.height = mEmojiKeyboardHeight;
+        lp.bottomMargin = mEmojiPagerBottomMargin;
+        vp.setLayoutParams(lp);
+    }
+
+    public void setCategoryPageIdViewProperties(LinearLayout ll) {
+        final LinearLayout.LayoutParams lp = (LinearLayout.LayoutParams) ll.getLayoutParams();
+        lp.height = mEmojiCategoryPageIdViewHeight;
+        ll.setLayoutParams(lp);
+    }
+
+    public void setActionBarProperties(LinearLayout ll) {
+        final LinearLayout.LayoutParams lp = (LinearLayout.LayoutParams) ll.getLayoutParams();
+        lp.height = mEmojiActionBarHeight;
+        lp.topMargin = 0;
+        lp.bottomMargin = mBottomPadding;
+        ll.setLayoutParams(lp);
+    }
+
+    public void setKeyProperties(ImageView ib) {
+        final LinearLayout.LayoutParams lp = (LinearLayout.LayoutParams) ib.getLayoutParams();
+        lp.leftMargin = mKeyHorizontalGap / 2;
+        lp.rightMargin = mKeyHorizontalGap / 2;
+        ib.setLayoutParams(lp);
+    }
+}
diff --git a/java/src/com/android/inputmethod/keyboard/Key.java b/java/src/com/android/inputmethod/keyboard/Key.java
index 6180528..3ea6880 100644
--- a/java/src/com/android/inputmethod/keyboard/Key.java
+++ b/java/src/com/android/inputmethod/keyboard/Key.java
@@ -58,12 +58,12 @@
     /**
      * The key code (unicode or custom code) that this key generates.
      */
-    public final int mCode;
+    private final int mCode;
 
     /** Label to display */
-    public final String mLabel;
+    private final String mLabel;
     /** Hint label to display on the key in conjunction with the label */
-    public final String mHintLabel;
+    private final String mHintLabel;
     /** Flags of the label */
     private final int mLabelFlags;
     private static final int LABEL_FLAGS_ALIGN_LEFT = 0x01;
@@ -95,18 +95,18 @@
     private final int mIconId;
 
     /** Width of the key, not including the gap */
-    public final int mWidth;
+    private final int mWidth;
     /** Height of the key, not including the gap */
-    public final int mHeight;
+    private final int mHeight;
     /** X coordinate of the key in the keyboard layout */
-    public final int mX;
+    private final int mX;
     /** Y coordinate of the key in the keyboard layout */
-    public final int mY;
+    private final int mY;
     /** Hit bounding box of the key */
-    public final Rect mHitBox = new Rect();
+    private final Rect mHitBox = new Rect();
 
     /** More keys. It is guaranteed that this is null or an array of one or more elements */
-    public final MoreKeySpec[] mMoreKeys;
+    private final MoreKeySpec[] mMoreKeys;
     /** More keys column number and flags */
     private final int mMoreKeysColumnAndFlags;
     private static final int MORE_KEYS_COLUMN_MASK = 0x000000ff;
@@ -121,12 +121,13 @@
     private static final String MORE_KEYS_NO_PANEL_AUTO_MORE_KEY = "!noPanelAutoMoreKey!";
 
     /** Background type that represents different key background visual than normal one. */
-    public final int mBackgroundType;
-    public static final int BACKGROUND_TYPE_NORMAL = 0;
-    public static final int BACKGROUND_TYPE_FUNCTIONAL = 1;
-    public static final int BACKGROUND_TYPE_ACTION = 2;
-    public static final int BACKGROUND_TYPE_STICKY_OFF = 3;
-    public static final int BACKGROUND_TYPE_STICKY_ON = 4;
+    private final int mBackgroundType;
+    public static final int BACKGROUND_TYPE_EMPTY = 0;
+    public static final int BACKGROUND_TYPE_NORMAL = 1;
+    public static final int BACKGROUND_TYPE_FUNCTIONAL = 2;
+    public static final int BACKGROUND_TYPE_ACTION = 3;
+    public static final int BACKGROUND_TYPE_STICKY_OFF = 4;
+    public static final int BACKGROUND_TYPE_STICKY_ON = 5;
 
     private final int mActionFlags;
     private static final int ACTION_FLAGS_IS_REPEATABLE = 0x01;
@@ -134,7 +135,7 @@
     private static final int ACTION_FLAGS_ALT_CODE_WHILE_TYPING = 0x04;
     private static final int ACTION_FLAGS_ENABLE_LONG_PRESS = 0x08;
 
-    public final KeyVisualAttributes mKeyVisualAttributes;
+    private final KeyVisualAttributes mKeyVisualAttributes;
 
     private final OptionalAttributes mOptionalAttributes;
 
@@ -150,7 +151,7 @@
         public final int mVisualInsetsLeft;
         public final int mVisualInsetsRight;
 
-        public OptionalAttributes(final String outputText, final int altCode,
+        private OptionalAttributes(final String outputText, final int altCode,
                 final int disabledIconId, final int previewIconId,
                 final int visualInsetsLeft, final int visualInsetsRight) {
             mOutputText = outputText;
@@ -160,6 +161,18 @@
             mVisualInsetsLeft = visualInsetsLeft;
             mVisualInsetsRight = visualInsetsRight;
         }
+
+        public static OptionalAttributes newInstance(final String outputText, final int altCode,
+                final int disabledIconId, final int previewIconId,
+                final int visualInsetsLeft, final int visualInsetsRight) {
+            if (outputText == null && altCode == CODE_UNSPECIFIED
+                    && disabledIconId == ICON_UNDEFINED && previewIconId == ICON_UNDEFINED
+                    && visualInsetsLeft == 0 && visualInsetsRight == 0) {
+                return null;
+            }
+            return new OptionalAttributes(outputText, altCode, disabledIconId, previewIconId,
+                    visualInsetsLeft, visualInsetsRight);
+        }
     }
 
     private final int mHashCode;
@@ -175,7 +188,7 @@
     public Key(final KeyboardParams params, final MoreKeySpec moreKeySpec, final int x, final int y,
             final int width, final int height, final int labelFlags) {
         this(params, moreKeySpec.mLabel, null, moreKeySpec.mIconId, moreKeySpec.mCode,
-                moreKeySpec.mOutputText, x, y, width, height, labelFlags);
+                moreKeySpec.mOutputText, x, y, width, height, labelFlags, BACKGROUND_TYPE_NORMAL);
     }
 
     /**
@@ -183,22 +196,19 @@
      */
     public Key(final KeyboardParams params, final String label, final String hintLabel,
             final int iconId, final int code, final String outputText, final int x, final int y,
-            final int width, final int height, final int labelFlags) {
+            final int width, final int height, final int labelFlags, final int backgroundType) {
         mHeight = height - params.mVerticalGap;
         mWidth = width - params.mHorizontalGap;
         mHintLabel = hintLabel;
         mLabelFlags = labelFlags;
-        mBackgroundType = BACKGROUND_TYPE_NORMAL;
+        mBackgroundType = backgroundType;
         mActionFlags = 0;
         mMoreKeys = null;
         mMoreKeysColumnAndFlags = 0;
         mLabel = label;
-        if (outputText == null) {
-            mOptionalAttributes = null;
-        } else {
-            mOptionalAttributes = new OptionalAttributes(outputText, CODE_UNSPECIFIED,
-                    ICON_UNDEFINED, ICON_UNDEFINED, 0, 0);
-        }
+        mOptionalAttributes = OptionalAttributes.newInstance(outputText, CODE_UNSPECIFIED,
+                ICON_UNDEFINED, ICON_UNDEFINED,
+                0 /* visualInsetsLeft */, 0 /* visualInsetsRight */);
         mCode = code;
         mEnabled = (code != CODE_UNSPECIFIED);
         mIconId = iconId;
@@ -224,7 +234,7 @@
     public Key(final Resources res, final KeyboardParams params, final KeyboardRow row,
             final XmlPullParser parser) throws XmlPullParserException {
         final float horizontalGap = isSpacer() ? 0 : params.mHorizontalGap;
-        final int rowHeight = row.mRowHeight;
+        final int rowHeight = row.getRowHeight();
         mHeight = rowHeight - params.mVerticalGap;
 
         final TypedArray keyAttr = res.obtainAttributes(Xml.asAttributeSet(parser),
@@ -259,11 +269,11 @@
         final int previewIconId = KeySpecParser.getIconId(style.getString(keyAttr,
                 R.styleable.Keyboard_Key_keyIconPreview));
 
-        mLabelFlags = style.getFlag(keyAttr, R.styleable.Keyboard_Key_keyLabelFlags)
+        mLabelFlags = style.getFlags(keyAttr, R.styleable.Keyboard_Key_keyLabelFlags)
                 | row.getDefaultKeyLabelFlags();
         final boolean needsToUpperCase = needsToUpperCase(mLabelFlags, params.mId.mElementId);
         final Locale locale = params.mId.mLocale;
-        int actionFlags = style.getFlag(keyAttr, R.styleable.Keyboard_Key_keyActionFlags);
+        int actionFlags = style.getFlags(keyAttr, R.styleable.Keyboard_Key_keyActionFlags);
         String[] moreKeys = style.getStringArray(keyAttr, R.styleable.Keyboard_Key_moreKeys);
 
         int moreKeysColumn = style.getInt(keyAttr,
@@ -359,15 +369,8 @@
                 KeySpecParser.parseCode(style.getString(keyAttr,
                 R.styleable.Keyboard_Key_altCode), params.mCodesSet, CODE_UNSPECIFIED),
                 needsToUpperCase, locale);
-        if (outputText == null && altCode == CODE_UNSPECIFIED
-                && disabledIconId == ICON_UNDEFINED && previewIconId == ICON_UNDEFINED
-                && visualInsetsLeft == 0 && visualInsetsRight == 0) {
-            mOptionalAttributes = null;
-        } else {
-            mOptionalAttributes = new OptionalAttributes(outputText, altCode,
-                    disabledIconId, previewIconId,
-                    visualInsetsLeft, visualInsetsRight);
-        }
+        mOptionalAttributes = OptionalAttributes.newInstance(outputText, altCode,
+                disabledIconId, previewIconId, visualInsetsLeft, visualInsetsRight);
         mKeyVisualAttributes = KeyVisualAttributes.newInstance(keyAttr);
         keyAttr.recycle();
         mHashCode = computeHashCode(this);
@@ -376,6 +379,35 @@
         }
     }
 
+    /**
+     * Copy constructor.
+     *
+     * @param key the original key.
+     */
+    protected Key(final Key key) {
+        // Final attributes.
+        mCode = key.mCode;
+        mLabel = key.mLabel;
+        mHintLabel = key.mHintLabel;
+        mLabelFlags = key.mLabelFlags;
+        mIconId = key.mIconId;
+        mWidth = key.mWidth;
+        mHeight = key.mHeight;
+        mX = key.mX;
+        mY = key.mY;
+        mHitBox.set(key.mHitBox);
+        mMoreKeys = key.mMoreKeys;
+        mMoreKeysColumnAndFlags = key.mMoreKeysColumnAndFlags;
+        mBackgroundType = key.mBackgroundType;
+        mActionFlags = key.mActionFlags;
+        mKeyVisualAttributes = key.mKeyVisualAttributes;
+        mOptionalAttributes = key.mOptionalAttributes;
+        mHashCode = key.mHashCode;
+        // Key state.
+        mPressed = key.mPressed;
+        mEnabled = key.mEnabled;
+    }
+
     private static boolean needsToUpperCase(final int labelFlags, final int keyboardElementId) {
         if ((labelFlags & LABEL_FLAGS_PRESERVE_CASE) != 0) return false;
         switch (keyboardElementId) {
@@ -465,6 +497,7 @@
 
     private static String backgroundName(final int backgroundType) {
         switch (backgroundType) {
+        case BACKGROUND_TYPE_EMPTY: return "empty";
         case BACKGROUND_TYPE_NORMAL: return "normal";
         case BACKGROUND_TYPE_FUNCTIONAL: return "functional";
         case BACKGROUND_TYPE_ACTION: return "action";
@@ -474,6 +507,22 @@
         }
     }
 
+    public int getCode() {
+        return mCode;
+    }
+
+    public String getLabel() {
+        return mLabel;
+    }
+
+    public String getHintLabel() {
+        return mHintLabel;
+    }
+
+    public MoreKeySpec[] getMoreKeys() {
+        return mMoreKeys;
+    }
+
     public void markAsLeftEdge(final KeyboardParams params) {
         mHitBox.left = params.mLeftPadding;
     }
@@ -520,6 +569,10 @@
                 && (mLabelFlags & LABEL_FLAGS_SHIFTED_LETTER_ACTIVATED) == 0;
     }
 
+    public KeyVisualAttributes getVisualAttributes() {
+        return mKeyVisualAttributes;
+    }
+
     public final Typeface selectTypeface(final KeyDrawParams params) {
         // TODO: Handle "bold" here too?
         if ((mLabelFlags & LABEL_FLAGS_FONT_NORMAL) != 0) {
@@ -694,9 +747,26 @@
                 ? iconSet.getIconDrawable(previewIconId) : iconSet.getIconDrawable(mIconId);
     }
 
+    public int getWidth() {
+        return mWidth;
+    }
+
+    public int getHeight() {
+        return mHeight;
+    }
+
+    public int getX() {
+        return mX;
+    }
+
+    public int getY() {
+        return mY;
+    }
+
     public final int getDrawX() {
+        final int x = getX();
         final OptionalAttributes attrs = mOptionalAttributes;
-        return (attrs == null) ? mX : mX + attrs.mVisualInsetsLeft;
+        return (attrs == null) ? x : x + attrs.mVisualInsetsLeft;
     }
 
     public final int getDrawWidth() {
@@ -731,6 +801,10 @@
         mEnabled = enabled;
     }
 
+    public Rect getHitBox() {
+        return mHitBox;
+    }
+
     /**
      * Detects if a point falls on this key.
      * @param x the x-coordinate of the point
@@ -750,9 +824,9 @@
      * @return the square of the distance of the point from the nearest edge of the key
      */
     public int squaredDistanceToEdge(final int x, final int y) {
-        final int left = mX;
+        final int left = getX();
         final int right = left + mWidth;
-        final int top = mY;
+        final int top = getY();
         final int bottom = top + mHeight;
         final int edgeX = x < left ? left : (x > right ? right : x);
         final int edgeY = y < top ? top : (y > bottom ? bottom : y);
@@ -788,6 +862,10 @@
         android.R.attr.state_pressed
     };
 
+    private final static int[] KEY_STATE_EMPTY = {
+        android.R.attr.state_empty
+    };
+
     // functional normal state (with properties)
     private static final int[] KEY_STATE_FUNCTIONAL_NORMAL = {
             android.R.attr.state_single
@@ -825,6 +903,8 @@
             return mPressed ? KEY_STATE_PRESSED_HIGHLIGHT_OFF : KEY_STATE_NORMAL_HIGHLIGHT_OFF;
         case BACKGROUND_TYPE_STICKY_ON:
             return mPressed ? KEY_STATE_PRESSED_HIGHLIGHT_ON : KEY_STATE_NORMAL_HIGHLIGHT_ON;
+        case BACKGROUND_TYPE_EMPTY:
+            return mPressed ? KEY_STATE_PRESSED : KEY_STATE_EMPTY;
         default: /* BACKGROUND_TYPE_NORMAL */
             return mPressed ? KEY_STATE_PRESSED : KEY_STATE_NORMAL;
         }
@@ -842,7 +922,7 @@
         protected Spacer(final KeyboardParams params, final int x, final int y, final int width,
                 final int height) {
             super(params, null, null, ICON_UNDEFINED, CODE_UNSPECIFIED,
-                    null, x, y, width, height, 0);
+                    null, x, y, width, height, 0, BACKGROUND_TYPE_EMPTY);
         }
     }
 }
diff --git a/java/src/com/android/inputmethod/keyboard/KeyDetector.java b/java/src/com/android/inputmethod/keyboard/KeyDetector.java
index 17e707f..befb6fa 100644
--- a/java/src/com/android/inputmethod/keyboard/KeyDetector.java
+++ b/java/src/com/android/inputmethod/keyboard/KeyDetector.java
@@ -108,8 +108,9 @@
             if (distance > minDistance) {
                 continue;
             }
-            // To take care of hitbox overlaps, we compare mCode here too.
-            if (primaryKey == null || distance < minDistance || key.mCode > primaryKey.mCode) {
+            // To take care of hitbox overlaps, we compare key's code here too.
+            if (primaryKey == null || distance < minDistance
+                    || key.getCode() > primaryKey.getCode()) {
                 minDistance = distance;
                 primaryKey = key;
             }
@@ -118,7 +119,7 @@
     }
 
     public static String printableCode(Key key) {
-        return key != null ? Constants.printableCode(key.mCode) : "none";
+        return key != null ? Constants.printableCode(key.getCode()) : "none";
     }
 
     public static String printableCodes(int[] codes) {
diff --git a/java/src/com/android/inputmethod/keyboard/Keyboard.java b/java/src/com/android/inputmethod/keyboard/Keyboard.java
index fefac96..bc1383a 100644
--- a/java/src/com/android/inputmethod/keyboard/Keyboard.java
+++ b/java/src/com/android/inputmethod/keyboard/Keyboard.java
@@ -51,6 +51,11 @@
     /** Total width of the keyboard, including the padding and keys */
     public final int mOccupiedWidth;
 
+    /** Base height of the keyboard, used to calculate rows' height */
+    public final int mBaseHeight;
+    /** Base width of the keyboard, used to calculate keys' width */
+    public final int mBaseWidth;
+
     /** The padding above the keyboard */
     public final int mTopPadding;
     /** Default gap between rows */
@@ -69,7 +74,7 @@
     public final int mMaxMoreKeysKeyboardColumn;
 
     /** Array of keys and icons in this keyboard */
-    public final Key[] mKeys;
+    private final Key[] mKeys;
     public final Key[] mShiftKeys;
     public final Key[] mAltCodeKeysWhileTyping;
     public final KeyboardIconsSet mIconsSet;
@@ -84,6 +89,8 @@
         mThemeId = params.mThemeId;
         mOccupiedHeight = params.mOccupiedHeight;
         mOccupiedWidth = params.mOccupiedWidth;
+        mBaseHeight = params.mBaseHeight;
+        mBaseWidth = params.mBaseWidth;
         mMostCommonKeyHeight = params.mMostCommonKeyHeight;
         mMostCommonKeyWidth = params.mMostCommonKeyWidth;
         mMoreKeysTemplate = params.mMoreKeysTemplate;
@@ -104,6 +111,30 @@
         mProximityCharsCorrectionEnabled = params.mProximityCharsCorrectionEnabled;
     }
 
+    protected Keyboard(final Keyboard keyboard) {
+        mId = keyboard.mId;
+        mThemeId = keyboard.mThemeId;
+        mOccupiedHeight = keyboard.mOccupiedHeight;
+        mOccupiedWidth = keyboard.mOccupiedWidth;
+        mBaseHeight = keyboard.mBaseHeight;
+        mBaseWidth = keyboard.mBaseWidth;
+        mMostCommonKeyHeight = keyboard.mMostCommonKeyHeight;
+        mMostCommonKeyWidth = keyboard.mMostCommonKeyWidth;
+        mMoreKeysTemplate = keyboard.mMoreKeysTemplate;
+        mMaxMoreKeysKeyboardColumn = keyboard.mMaxMoreKeysKeyboardColumn;
+        mKeyVisualAttributes = keyboard.mKeyVisualAttributes;
+        mTopPadding = keyboard.mTopPadding;
+        mVerticalGap = keyboard.mVerticalGap;
+
+        mKeys = keyboard.mKeys;
+        mShiftKeys = keyboard.mShiftKeys;
+        mAltCodeKeysWhileTyping = keyboard.mAltCodeKeysWhileTyping;
+        mIconsSet = keyboard.mIconsSet;
+
+        mProximityInfo = keyboard.mProximityInfo;
+        mProximityCharsCorrectionEnabled = keyboard.mProximityCharsCorrectionEnabled;
+    }
+
     public boolean hasProximityCharsCorrection(final int code) {
         if (!mProximityCharsCorrectionEnabled) {
             return false;
@@ -120,6 +151,19 @@
         return mProximityInfo;
     }
 
+    public Key[] getKeys() {
+        return mKeys;
+    }
+
+    public Key getKeyFromOutputText(final String outputText) {
+        for (final Key key : getKeys()) {
+            if (outputText.equals(key.getOutputText())) {
+                return key;
+            }
+        }
+        return null;
+    }
+
     public Key getKey(final int code) {
         if (code == Constants.CODE_UNSPECIFIED) {
             return null;
@@ -130,8 +174,8 @@
                 return mKeyCache.valueAt(index);
             }
 
-            for (final Key key : mKeys) {
-                if (key.mCode == code) {
+            for (final Key key : getKeys()) {
+                if (key.getCode() == code) {
                     mKeyCache.put(code, key);
                     return key;
                 }
@@ -146,9 +190,9 @@
             return true;
         }
 
-        for (final Key key : mKeys) {
+        for (final Key key : getKeys()) {
             if (key == aKey) {
-                mKeyCache.put(key.mCode, key);
+                mKeyCache.put(key.getCode(), key);
                 return true;
             }
         }
diff --git a/java/src/com/android/inputmethod/keyboard/KeyboardActionListener.java b/java/src/com/android/inputmethod/keyboard/KeyboardActionListener.java
index b266986..dc760e6 100644
--- a/java/src/com/android/inputmethod/keyboard/KeyboardActionListener.java
+++ b/java/src/com/android/inputmethod/keyboard/KeyboardActionListener.java
@@ -26,10 +26,10 @@
      *
      * @param primaryCode the unicode of the key being pressed. If the touch is not on a valid key,
      *            the value will be zero.
-     * @param isRepeatKey true if pressing has occurred while key repeat input.
+     * @param repeatCount how many times the key was repeated. Zero if it is the first press.
      * @param isSinglePointer true if pressing has occurred while no other key is being pressed.
      */
-    public void onPressKey(int primaryCode, boolean isRepeatKey, boolean isSinglePointer);
+    public void onPressKey(int primaryCode, int repeatCount, boolean isSinglePointer);
 
     /**
      * Called when the user releases a key. This is sent after the {@link #onCodeInput} is called.
@@ -103,7 +103,7 @@
 
     public static class Adapter implements KeyboardActionListener {
         @Override
-        public void onPressKey(int primaryCode, boolean isRepeatKey, boolean isSinglePointer) {}
+        public void onPressKey(int primaryCode, int repeatCount, boolean isSinglePointer) {}
         @Override
         public void onReleaseKey(int primaryCode, boolean withSliding) {}
         @Override
diff --git a/java/src/com/android/inputmethod/keyboard/KeyboardId.java b/java/src/com/android/inputmethod/keyboard/KeyboardId.java
index 1dc3c6a..736f13e 100644
--- a/java/src/com/android/inputmethod/keyboard/KeyboardId.java
+++ b/java/src/com/android/inputmethod/keyboard/KeyboardId.java
@@ -54,6 +54,13 @@
     public static final int ELEMENT_PHONE = 7;
     public static final int ELEMENT_PHONE_SYMBOLS = 8;
     public static final int ELEMENT_NUMBER = 9;
+    public static final int ELEMENT_EMOJI_RECENTS = 10;
+    public static final int ELEMENT_EMOJI_CATEGORY1 = 11;
+    public static final int ELEMENT_EMOJI_CATEGORY2 = 12;
+    public static final int ELEMENT_EMOJI_CATEGORY3 = 13;
+    public static final int ELEMENT_EMOJI_CATEGORY4 = 14;
+    public static final int ELEMENT_EMOJI_CATEGORY5 = 15;
+    public static final int ELEMENT_EMOJI_CATEGORY6 = 16;
 
     public final InputMethodSubtype mSubtype;
     public final Locale mLocale;
@@ -217,6 +224,13 @@
         case ELEMENT_PHONE: return "phone";
         case ELEMENT_PHONE_SYMBOLS: return "phoneSymbols";
         case ELEMENT_NUMBER: return "number";
+        case ELEMENT_EMOJI_RECENTS: return "emojiRecents";
+        case ELEMENT_EMOJI_CATEGORY1: return "emojiCategory1";
+        case ELEMENT_EMOJI_CATEGORY2: return "emojiCategory2";
+        case ELEMENT_EMOJI_CATEGORY3: return "emojiCategory3";
+        case ELEMENT_EMOJI_CATEGORY4: return "emojiCategory4";
+        case ELEMENT_EMOJI_CATEGORY5: return "emojiCategory5";
+        case ELEMENT_EMOJI_CATEGORY6: return "emojiCategory6";
         default: return null;
         }
     }
diff --git a/java/src/com/android/inputmethod/keyboard/KeyboardLayoutSet.java b/java/src/com/android/inputmethod/keyboard/KeyboardLayoutSet.java
index e97f294..1eccdf3 100644
--- a/java/src/com/android/inputmethod/keyboard/KeyboardLayoutSet.java
+++ b/java/src/com/android/inputmethod/keyboard/KeyboardLayoutSet.java
@@ -106,6 +106,8 @@
         EditorInfo mEditorInfo;
         boolean mDisableTouchPositionCorrectionDataForTest;
         boolean mVoiceKeyEnabled;
+        // TODO: Remove mVoiceKeyOnMain when it's certainly confirmed that we no longer show
+        // the voice input key on the symbol layout
         boolean mVoiceKeyOnMain;
         boolean mNoSettingsKey;
         boolean mLanguageSwitchKeyEnabled;
@@ -162,7 +164,8 @@
         final KeyboardId id = new KeyboardId(keyboardLayoutSetElementId, mParams);
         try {
             return getKeyboard(elementParams, id);
-        } catch (RuntimeException e) {
+        } catch (final RuntimeException e) {
+            Log.e(TAG, "Can't create keyboard: " + id, e);
             throw new KeyboardLayoutSetException(e, id);
         }
     }
@@ -213,7 +216,6 @@
         private final Context mContext;
         private final String mPackageName;
         private final Resources mResources;
-        private final EditorInfo mEditorInfo;
 
         private final Params mParams = new Params();
 
@@ -223,13 +225,12 @@
             mContext = context;
             mPackageName = context.getPackageName();
             mResources = context.getResources();
-            mEditorInfo = editorInfo;
             final Params params = mParams;
 
             params.mMode = getKeyboardMode(editorInfo);
             params.mEditorInfo = (editorInfo != null) ? editorInfo : EMPTY_EDITOR_INFO;
             params.mNoSettingsKey = InputAttributes.inPrivateImeOptions(
-                    mPackageName, NO_SETTINGS_KEY, mEditorInfo);
+                    mPackageName, NO_SETTINGS_KEY, params.mEditorInfo);
         }
 
         public Builder setKeyboardGeometry(final int keyboardWidth, final int keyboardHeight) {
@@ -242,7 +243,7 @@
             final boolean asciiCapable = subtype.containsExtraValueKey(ASCII_CAPABLE);
             @SuppressWarnings("deprecation")
             final boolean deprecatedForceAscii = InputAttributes.inPrivateImeOptions(
-                    mPackageName, FORCE_ASCII, mEditorInfo);
+                    mPackageName, FORCE_ASCII, mParams.mEditorInfo);
             final boolean forceAscii = EditorInfoCompatUtils.hasFlagForceAscii(
                     mParams.mEditorInfo.imeOptions)
                     || deprecatedForceAscii;
@@ -260,13 +261,15 @@
             return this;
         }
 
+        // TODO: Remove mVoiceKeyOnMain when it's certainly confirmed that we no longer show
+        // the voice input key on the symbol layout
         public Builder setOptions(final boolean voiceKeyEnabled, final boolean voiceKeyOnMain,
                 final boolean languageSwitchKeyEnabled) {
             @SuppressWarnings("deprecation")
             final boolean deprecatedNoMicrophone = InputAttributes.inPrivateImeOptions(
-                    null, NO_MICROPHONE_COMPAT, mEditorInfo);
+                    null, NO_MICROPHONE_COMPAT, mParams.mEditorInfo);
             final boolean noMicrophone = InputAttributes.inPrivateImeOptions(
-                    mPackageName, NO_MICROPHONE, mEditorInfo)
+                    mPackageName, NO_MICROPHONE, mParams.mEditorInfo)
                     || deprecatedNoMicrophone;
             mParams.mVoiceKeyEnabled = voiceKeyEnabled && !noMicrophone;
             mParams.mVoiceKeyOnMain = voiceKeyOnMain;
diff --git a/java/src/com/android/inputmethod/keyboard/KeyboardSwitcher.java b/java/src/com/android/inputmethod/keyboard/KeyboardSwitcher.java
index 1ea0f8b..cc1ffd1 100644
--- a/java/src/com/android/inputmethod/keyboard/KeyboardSwitcher.java
+++ b/java/src/com/android/inputmethod/keyboard/KeyboardSwitcher.java
@@ -58,19 +58,17 @@
     }
 
     private static final KeyboardTheme[] KEYBOARD_THEMES = {
-        new KeyboardTheme(0, R.style.KeyboardTheme),
-        new KeyboardTheme(1, R.style.KeyboardTheme_HighContrast),
-        new KeyboardTheme(6, R.style.KeyboardTheme_Stone),
-        new KeyboardTheme(7, R.style.KeyboardTheme_Stone_Bold),
-        new KeyboardTheme(8, R.style.KeyboardTheme_Gingerbread),
-        new KeyboardTheme(5, R.style.KeyboardTheme_IceCreamSandwich),
+        new KeyboardTheme(0, R.style.KeyboardTheme_ICS),
+        new KeyboardTheme(1, R.style.KeyboardTheme_GB),
     };
 
     private SubtypeSwitcher mSubtypeSwitcher;
     private SharedPreferences mPrefs;
 
     private InputView mCurrentInputView;
+    private View mMainKeyboardFrame;
     private MainKeyboardView mKeyboardView;
+    private EmojiKeyboardView mEmojiKeyboardView;
     private LatinIME mLatinIME;
     private Resources mResources;
 
@@ -121,8 +119,9 @@
         } catch (NumberFormatException e) {
             // Format error, keyboard theme is default to 0.
         }
-        Log.w(TAG, "Illegal keyboard theme in preference: " + themeIndex + ", default to 0");
-        return KEYBOARD_THEMES[0];
+        Log.w(TAG, "Illegal keyboard theme in preference: " + themeIndex + ", default to "
+                + defaultIndex);
+        return KEYBOARD_THEMES[Integer.valueOf(defaultIndex)];
     }
 
     private void setContextThemeWrapper(final Context context, final KeyboardTheme keyboardTheme) {
@@ -143,7 +142,7 @@
         builder.setSubtype(mSubtypeSwitcher.getCurrentSubtype());
         builder.setOptions(
                 settingsValues.isVoiceKeyEnabled(editorInfo),
-                settingsValues.isVoiceKeyOnMain(),
+                true /* always show a voice key on the main keyboard */,
                 settingsValues.isLanguageSwitchKeyEnabled());
         mKeyboardLayoutSet = builder.build();
         try {
@@ -170,6 +169,8 @@
     }
 
     private void setKeyboard(final Keyboard keyboard) {
+        // Make {@link MainKeyboardView} visible and hide {@link EmojiKeyboardView}.
+        setMainKeyboardFrame();
         final MainKeyboardView keyboardView = mKeyboardView;
         final Keyboard oldKeyboard = keyboardView.getKeyboard();
         keyboardView.setKeyboard(keyboard);
@@ -256,6 +257,18 @@
         setKeyboard(mKeyboardLayoutSet.getKeyboard(KeyboardId.ELEMENT_SYMBOLS));
     }
 
+    private void setMainKeyboardFrame() {
+        mMainKeyboardFrame.setVisibility(View.VISIBLE);
+        mEmojiKeyboardView.setVisibility(View.GONE);
+    }
+
+    // Implements {@link KeyboardState.SwitchActions}.
+    @Override
+    public void setEmojiKeyboard() {
+        mMainKeyboardFrame.setVisibility(View.GONE);
+        mEmojiKeyboardView.setVisibility(View.VISIBLE);
+    }
+
     // Implements {@link KeyboardState.SwitchActions}.
     @Override
     public void setSymbolsShiftedKeyboard() {
@@ -301,6 +314,24 @@
         mState.onCodeInput(code, mLatinIME.getCurrentAutoCapsState());
     }
 
+    public boolean isShowingEmojiKeyboard() {
+        return mEmojiKeyboardView.getVisibility() == View.VISIBLE;
+    }
+
+    public boolean isShowingMoreKeysPanel() {
+        if (isShowingEmojiKeyboard()) {
+            return false;
+        }
+        return mKeyboardView.isShowingMoreKeysPanel();
+    }
+
+    public View getVisibleKeyboardView() {
+        if (isShowingEmojiKeyboard()) {
+            return mEmojiKeyboardView;
+        }
+        return mKeyboardView;
+    }
+
     public MainKeyboardView getMainKeyboardView() {
         return mKeyboardView;
     }
@@ -313,10 +344,16 @@
         setContextThemeWrapper(mLatinIME, mKeyboardTheme);
         mCurrentInputView = (InputView)LayoutInflater.from(mThemeContext).inflate(
                 R.layout.input_view, null);
+        mMainKeyboardFrame = mCurrentInputView.findViewById(R.id.main_keyboard_frame);
+        mEmojiKeyboardView = (EmojiKeyboardView)mCurrentInputView.findViewById(
+                R.id.emoji_keyboard_view);
 
         mKeyboardView = (MainKeyboardView) mCurrentInputView.findViewById(R.id.keyboard_view);
         mKeyboardView.setHardwareAcceleratedDrawingEnabled(isHardwareAcceleratedDrawingEnabled);
         mKeyboardView.setKeyboardActionListener(mLatinIME);
+        mEmojiKeyboardView.setHardwareAcceleratedDrawingEnabled(
+                isHardwareAcceleratedDrawingEnabled);
+        mEmojiKeyboardView.setKeyboardActionListener(mLatinIME);
 
         // This always needs to be set since the accessibility state can
         // potentially change without the input view being re-created.
diff --git a/java/src/com/android/inputmethod/keyboard/KeyboardView.java b/java/src/com/android/inputmethod/keyboard/KeyboardView.java
index 054c503..aeb9e67 100644
--- a/java/src/com/android/inputmethod/keyboard/KeyboardView.java
+++ b/java/src/com/android/inputmethod/keyboard/KeyboardView.java
@@ -26,6 +26,7 @@
 import android.graphics.PorterDuff;
 import android.graphics.Rect;
 import android.graphics.Region;
+import android.graphics.Typeface;
 import android.graphics.drawable.Drawable;
 import android.util.AttributeSet;
 import android.view.View;
@@ -196,13 +197,14 @@
 
     @Override
     protected void onMeasure(final int widthMeasureSpec, final int heightMeasureSpec) {
-        if (mKeyboard != null) {
-            // The main keyboard expands to the display width.
-            final int height = mKeyboard.mOccupiedHeight + getPaddingTop() + getPaddingBottom();
-            setMeasuredDimension(widthMeasureSpec, height);
-        } else {
+        if (mKeyboard == null) {
             super.onMeasure(widthMeasureSpec, heightMeasureSpec);
+            return;
         }
+        // The main keyboard expands to the entire this {@link KeyboardView}.
+        final int width = mKeyboard.mOccupiedWidth + getPaddingLeft() + getPaddingRight();
+        final int height = mKeyboard.mOccupiedHeight + getPaddingTop() + getPaddingBottom();
+        setMeasuredDimension(width, height);
     }
 
     @Override
@@ -264,9 +266,9 @@
             mClipRegion.setEmpty();
             for (final Key key : mInvalidatedKeys) {
                 if (mKeyboard.hasKey(key)) {
-                    final int x = key.mX + getPaddingLeft();
-                    final int y = key.mY + getPaddingTop();
-                    mWorkingRect.set(x, y, x + key.mWidth, y + key.mHeight);
+                    final int x = key.getX() + getPaddingLeft();
+                    final int y = key.getY() + getPaddingTop();
+                    mWorkingRect.set(x, y, x + key.getWidth(), y + key.getHeight());
                     mClipRegion.union(mWorkingRect);
                 }
             }
@@ -284,7 +286,7 @@
         // TODO: Confirm if it's really required to draw all keys when hardware acceleration is on.
         if (drawAllKeys || isHardwareAccelerated) {
             // Draw all keys.
-            for (final Key key : mKeyboard.mKeys) {
+            for (final Key key : mKeyboard.getKeys()) {
                 onDrawKey(key, canvas, paint);
             }
         } else {
@@ -309,11 +311,11 @@
 
     private void onDrawKey(final Key key, final Canvas canvas, final Paint paint) {
         final int keyDrawX = key.getDrawX() + getPaddingLeft();
-        final int keyDrawY = key.mY + getPaddingTop();
+        final int keyDrawY = key.getY() + getPaddingTop();
         canvas.translate(keyDrawX, keyDrawY);
 
         final int keyHeight = mKeyboard.mMostCommonKeyHeight - mKeyboard.mVerticalGap;
-        final KeyVisualAttributes attr = key.mKeyVisualAttributes;
+        final KeyVisualAttributes attr = key.getVisualAttributes();
         final KeyDrawParams params = mKeyDrawParams.mayCloneAndUpdateParams(keyHeight, attr);
         params.mAnimAlpha = Constants.Color.ALPHA_OPAQUE;
 
@@ -329,7 +331,7 @@
     protected void onDrawKeyBackground(final Key key, final Canvas canvas) {
         final Rect padding = mKeyBackgroundPadding;
         final int bgWidth = key.getDrawWidth() + padding.left + padding.right;
-        final int bgHeight = key.mHeight + padding.top + padding.bottom;
+        final int bgHeight = key.getHeight() + padding.top + padding.bottom;
         final int bgX = -padding.left;
         final int bgY = -padding.top;
         final int[] drawableState = key.getCurrentDrawableState();
@@ -351,7 +353,7 @@
     protected void onDrawKeyTopVisuals(final Key key, final Canvas canvas, final Paint paint,
             final KeyDrawParams params) {
         final int keyWidth = key.getDrawWidth();
-        final int keyHeight = key.mHeight;
+        final int keyHeight = key.getHeight();
         final float centerX = keyWidth * 0.5f;
         final float centerY = keyHeight * 0.5f;
 
@@ -362,8 +364,8 @@
         // Draw key label.
         final Drawable icon = key.getIcon(mKeyboard.mIconsSet, params.mAnimAlpha);
         float positionX = centerX;
-        if (key.mLabel != null) {
-            final String label = key.mLabel;
+        final String label = key.getLabel();
+        if (label != null) {
             paint.setTypeface(key.selectTypeface(params));
             paint.setTextSize(key.selectTextSize(params));
             final float labelCharHeight = TypefaceUtils.getCharHeight(
@@ -440,10 +442,12 @@
         }
 
         // Draw hint label.
-        if (key.mHintLabel != null) {
-            final String hintLabel = key.mHintLabel;
+        final String hintLabel = key.getHintLabel();
+        if (hintLabel != null) {
             paint.setTextSize(key.selectHintTextSize(params));
             paint.setColor(key.selectHintTextColor(params));
+            // TODO: Should add a way to specify type face for hint letters
+            paint.setTypeface(Typeface.DEFAULT_BOLD);
             blendAlpha(paint, params.mAnimAlpha);
             final float hintX, hintY;
             if (key.hasHintLabel()) {
@@ -464,9 +468,13 @@
                 paint.setTextAlign(Align.CENTER);
             } else { // key.hasHintLetter()
                 // The hint letter is placed at top-right corner of the key. Used mainly on phone.
+                final float keyNumericHintLabelReferenceCharWidth =
+                        TypefaceUtils.getCharWidth(KEY_NUMERIC_HINT_LABEL_REFERENCE_CHAR, paint);
+                final float keyHintLabelStringWidth =
+                        TypefaceUtils.getStringWidth(hintLabel, paint);
                 hintX = keyWidth - mKeyHintLetterPadding
-                        - TypefaceUtils.getCharWidth(KEY_NUMERIC_HINT_LABEL_REFERENCE_CHAR, paint)
-                        / 2.0f;
+                        - Math.max(keyNumericHintLabelReferenceCharWidth, keyHintLabelStringWidth)
+                                / 2.0f;
                 hintY = -paint.ascent();
                 paint.setTextAlign(Align.CENTER);
             }
@@ -480,7 +488,7 @@
         }
 
         // Draw key icon.
-        if (key.mLabel == null && icon != null) {
+        if (label == null && icon != null) {
             final int iconWidth = Math.min(icon.getIntrinsicWidth(), keyWidth);
             final int iconHeight = icon.getIntrinsicHeight();
             final int iconX, alignX;
@@ -504,7 +512,7 @@
             }
         }
 
-        if (key.hasPopupHint() && key.mMoreKeys != null) {
+        if (key.hasPopupHint() && key.getMoreKeys() != null) {
             drawKeyPopupHint(key, canvas, paint, params);
         }
     }
@@ -513,7 +521,7 @@
     protected void drawKeyPopupHint(final Key key, final Canvas canvas, final Paint paint,
             final KeyDrawParams params) {
         final int keyWidth = key.getDrawWidth();
-        final int keyHeight = key.mHeight;
+        final int keyHeight = key.getHeight();
 
         paint.setTypeface(params.mTypeface);
         paint.setTextSize(params.mHintLetterSize);
@@ -601,9 +609,9 @@
         if (mInvalidateAllKeys) return;
         if (key == null) return;
         mInvalidatedKeys.add(key);
-        final int x = key.mX + getPaddingLeft();
-        final int y = key.mY + getPaddingTop();
-        invalidate(x, y, x + key.mWidth, y + key.mHeight);
+        final int x = key.getX() + getPaddingLeft();
+        final int y = key.getY() + getPaddingTop();
+        invalidate(x, y, x + key.getWidth(), y + key.getHeight());
     }
 
     @Override
diff --git a/java/src/com/android/inputmethod/keyboard/MainKeyboardView.java b/java/src/com/android/inputmethod/keyboard/MainKeyboardView.java
index 526c2f1..13db470 100644
--- a/java/src/com/android/inputmethod/keyboard/MainKeyboardView.java
+++ b/java/src/com/android/inputmethod/keyboard/MainKeyboardView.java
@@ -155,7 +155,6 @@
     private final SlidingKeyInputPreview mSlidingKeyInputPreview;
 
     // Key preview
-    private static final int PREVIEW_ALPHA = 240;
     private final int mKeyPreviewLayoutId;
     private final int mKeyPreviewOffset;
     private final int mKeyPreviewHeight;
@@ -183,6 +182,7 @@
     private final NonDistinctMultitouchHelper mNonDistinctMultitouchHelper;
 
     private final KeyTimerHandler mKeyTimerHandler;
+    private final int mLanguageOnSpacebarHorizontalMargin;
 
     private static final class KeyTimerHandler extends StaticInnerHandlerWrapper<MainKeyboardView>
             implements TimerProxy {
@@ -217,7 +217,7 @@
                 startWhileTypingFadeinAnimation(keyboardView);
                 break;
             case MSG_REPEAT_KEY:
-                tracker.onKeyRepeat(msg.arg1);
+                tracker.onKeyRepeat(msg.arg1 /* code */, msg.arg2 /* repeatCount */);
                 break;
             case MSG_LONGPRESS_KEY:
                 keyboardView.onLongPress(tracker);
@@ -230,12 +230,14 @@
         }
 
         @Override
-        public void startKeyRepeatTimer(final PointerTracker tracker, final int delay) {
+        public void startKeyRepeatTimer(final PointerTracker tracker, final int repeatCount,
+                final int delay) {
             final Key key = tracker.getKey();
             if (key == null || delay == 0) {
                 return;
             }
-            sendMessageDelayed(obtainMessage(MSG_REPEAT_KEY, key.mCode, 0, tracker), delay);
+            sendMessageDelayed(
+                    obtainMessage(MSG_REPEAT_KEY, key.getCode(), repeatCount, tracker), delay);
         }
 
         public void cancelKeyRepeatTimer() {
@@ -296,7 +298,7 @@
             final MainKeyboardView keyboardView = getOuterInstance();
 
             // When user hits the space or the enter key, just cancel the while-typing timer.
-            final int typedCode = typedKey.mCode;
+            final int typedCode = typedKey.getCode();
             if (typedCode == Constants.CODE_SPACE || typedCode == Constants.CODE_ENTER) {
                 if (isTyping) {
                     startWhileTypingFadeinAnimation(keyboardView);
@@ -510,6 +512,9 @@
                 altCodeKeyWhileTypingFadeinAnimatorResId, this);
 
         mKeyboardActionListener = KeyboardActionListener.EMPTY_LISTENER;
+
+        mLanguageOnSpacebarHorizontalMargin =
+                (int) getResources().getDimension(R.dimen.language_on_spacebar_horizontal_margin);
     }
 
     @Override
@@ -636,7 +641,6 @@
         mKeyPreviewLingerTimeout = delay;
     }
 
-
     private void locatePreviewPlacerView() {
         if (mPreviewPlacerView.getParent() != null) {
             return;
@@ -805,13 +809,12 @@
         }
         // The key preview is placed vertically above the top edge of the parent key with an
         // arbitrary offset.
-        final int previewY = key.mY - previewHeight + mKeyPreviewOffset
+        final int previewY = key.getY() - previewHeight + mKeyPreviewOffset
                 + CoordinateUtils.y(mOriginCoords);
 
         if (background != null) {
-            final int hasMoreKeys = (key.mMoreKeys != null) ? STATE_HAS_MOREKEYS : STATE_NORMAL;
+            final int hasMoreKeys = (key.getMoreKeys() != null) ? STATE_HAS_MOREKEYS : STATE_NORMAL;
             background.setState(KEY_PREVIEW_BACKGROUND_STATE_TABLE[statePosition][hasMoreKeys]);
-            background.setAlpha(PREVIEW_ALPHA);
         }
         ViewLayoutUtils.placeViewAt(
                 previewText, previewX, previewY, previewWidth, previewHeight);
@@ -901,7 +904,7 @@
     }
 
     private MoreKeysPanel onCreateMoreKeysPanel(final Key key, final Context context) {
-        if (key.mMoreKeys == null) {
+        if (key.getMoreKeys() == null) {
             return null;
         }
         Keyboard moreKeysKeyboard = mMoreKeysKeyboardCache.get(key);
@@ -936,15 +939,15 @@
         }
         final KeyboardActionListener listener = mKeyboardActionListener;
         if (key.hasNoPanelAutoMoreKey()) {
-            final int moreKeyCode = key.mMoreKeys[0].mCode;
+            final int moreKeyCode = key.getMoreKeys()[0].mCode;
             tracker.onLongPressed();
-            listener.onPressKey(moreKeyCode, false /* isRepeatKey */, true /* isSinglePointer */);
+            listener.onPressKey(moreKeyCode, 0 /* repeatCount */, true /* isSinglePointer */);
             listener.onCodeInput(moreKeyCode,
                     Constants.NOT_A_COORDINATE, Constants.NOT_A_COORDINATE);
             listener.onReleaseKey(moreKeyCode, false /* withSliding */);
             return;
         }
-        final int code = key.mCode;
+        final int code = key.getCode();
         if (code == Constants.CODE_SPACE || code == Constants.CODE_LANGUAGE_SWITCH) {
             // Long pressing the space key invokes IME switcher dialog.
             if (listener.onCustomRequest(Constants.CUSTOM_CODE_SHOW_INPUT_METHOD_PICKER)) {
@@ -970,13 +973,13 @@
         // keys keyboard is placed at the touch point of the parent key.
         final int pointX = (mConfigShowMoreKeysKeyboardAtTouchedPoint && !keyPreviewEnabled)
                 ? CoordinateUtils.x(lastCoords)
-                : key.mX + key.mWidth / 2;
+                : key.getX() + key.getWidth() / 2;
         // The more keys keyboard is usually vertically aligned with the top edge of the parent key
         // (plus vertical gap). If the key preview is enabled, the more keys keyboard is vertically
         // aligned with the bottom edge of the visible part of the key preview.
         // {@code mPreviewVisibleOffset} has been set appropriately in
         // {@link KeyboardView#showKeyPreview(PointerTracker)}.
-        final int pointY = key.mY + mKeyPreviewDrawParams.mPreviewVisibleOffset;
+        final int pointY = key.getY() + mKeyPreviewDrawParams.mPreviewVisibleOffset;
         moreKeysPanel.showMoreKeysPanel(this, this, pointX, pointY, mKeyboardActionListener);
         tracker.onShowMoreKeysPanel(moreKeysPanel);
     }
@@ -1172,13 +1175,14 @@
         if (key.altCodeWhileTyping() && key.isEnabled()) {
             params.mAnimAlpha = mAltCodeKeyWhileTypingAnimAlpha;
         }
-        if (key.mCode == Constants.CODE_SPACE) {
+        final int code = key.getCode();
+        if (code == Constants.CODE_SPACE) {
             drawSpacebar(key, canvas, paint);
             // Whether space key needs to show the "..." popup hint for special purposes
             if (key.isLongPressEnabled() && mHasMultipleEnabledIMEsOrSubtypes) {
                 drawKeyPopupHint(key, canvas, paint, params);
             }
-        } else if (key.mCode == Constants.CODE_LANGUAGE_SWITCH) {
+        } else if (code == Constants.CODE_LANGUAGE_SWITCH) {
             super.onDrawKeyTopVisuals(key, canvas, paint, params);
             drawKeyPopupHint(key, canvas, paint, params);
         } else {
@@ -1186,26 +1190,27 @@
         }
     }
 
-    private static boolean fitsTextIntoWidth(final int width, final String text,
-            final Paint paint) {
+    private boolean fitsTextIntoWidth(final int width, final String text, final Paint paint) {
+        final int maxTextWidth = width - mLanguageOnSpacebarHorizontalMargin * 2;
         paint.setTextScaleX(1.0f);
         final float textWidth = TypefaceUtils.getLabelWidth(text, paint);
         if (textWidth < width) {
             return true;
         }
 
-        final float scaleX = width / textWidth;
+        final float scaleX = maxTextWidth / textWidth;
         if (scaleX < MINIMUM_XSCALE_OF_LANGUAGE_NAME) {
             return false;
         }
 
         paint.setTextScaleX(scaleX);
-        return TypefaceUtils.getLabelWidth(text, paint) < width;
+        return TypefaceUtils.getLabelWidth(text, paint) < maxTextWidth;
     }
 
     // Layout language name on spacebar.
-    private static String layoutLanguageOnSpacebar(final Paint paint,
+    private String layoutLanguageOnSpacebar(final Paint paint,
             final InputMethodSubtype subtype, final int width) {
+
         // Choose appropriate language name to fit into the width.
         final String fullText = SubtypeLocaleUtils.getFullDisplayName(subtype);
         if (fitsTextIntoWidth(width, fullText, paint)) {
@@ -1226,8 +1231,8 @@
     }
 
     private void drawSpacebar(final Key key, final Canvas canvas, final Paint paint) {
-        final int width = key.mWidth;
-        final int height = key.mHeight;
+        final int width = key.getWidth();
+        final int height = key.getHeight();
 
         // If input language are explicitly selected.
         if (mNeedsToDisplayLanguage) {
diff --git a/java/src/com/android/inputmethod/keyboard/MoreKeysDetector.java b/java/src/com/android/inputmethod/keyboard/MoreKeysDetector.java
index a2001cb..6b76e24 100644
--- a/java/src/com/android/inputmethod/keyboard/MoreKeysDetector.java
+++ b/java/src/com/android/inputmethod/keyboard/MoreKeysDetector.java
@@ -39,7 +39,7 @@
 
         Key nearestKey = null;
         int nearestDist = (y < 0) ? mSlideAllowanceSquareTop : mSlideAllowanceSquare;
-        for (final Key key : getKeyboard().mKeys) {
+        for (final Key key : getKeyboard().getKeys()) {
             final int dist = key.squaredDistanceToEdge(touchX, touchY);
             if (dist < nearestDist) {
                 nearestKey = key;
diff --git a/java/src/com/android/inputmethod/keyboard/MoreKeysKeyboard.java b/java/src/com/android/inputmethod/keyboard/MoreKeysKeyboard.java
index 3fd29dc..8256d46 100644
--- a/java/src/com/android/inputmethod/keyboard/MoreKeysKeyboard.java
+++ b/java/src/com/android/inputmethod/keyboard/MoreKeysKeyboard.java
@@ -276,6 +276,7 @@
             mParams.mVerticalGap = parentKeyboard.mVerticalGap / 2;
             mParentKey = parentKey;
 
+            final MoreKeySpec[] moreKeys = parentKey.getMoreKeys();
             final int width, height;
             // {@link KeyPreviewDrawParams#mPreviewVisibleWidth} should have been set at
             // {@link MainKeyboardView#showKeyPreview(PointerTracker}, though there may be
@@ -283,7 +284,7 @@
             // zero-division error at
             // {@link MoreKeysKeyboardParams#setParameters(int,int,int,int,int,int,boolean,int)}.
             final boolean singleMoreKeyWithPreview = parentKeyboardView.isKeyPreviewPopupEnabled()
-                    && !parentKey.noKeyPreview() && parentKey.mMoreKeys.length == 1
+                    && !parentKey.noKeyPreview() && moreKeys.length == 1
                     && keyPreviewDrawParams.mPreviewVisibleWidth > 0;
             if (singleMoreKeyWithPreview) {
                 // Use pre-computed width and height if this more keys keyboard has only one key to
@@ -312,8 +313,8 @@
                 mDivider = null;
                 dividerWidth = 0;
             }
-            mParams.setParameters(parentKey.mMoreKeys.length, parentKey.getMoreKeysColumn(),
-                    width, height, parentKey.mX + parentKey.mWidth / 2,
+            mParams.setParameters(moreKeys.length, parentKey.getMoreKeysColumn(),
+                    width, height, parentKey.getX() + parentKey.getWidth() / 2,
                     parentKeyboard.mId.mWidth, parentKey.isFixedColumnOrderMoreKeys(),
                     dividerWidth);
         }
@@ -321,7 +322,7 @@
         private static int getMaxKeyWidth(final Key parentKey, final int minKeyWidth,
                 final float padding, final Paint paint) {
             int maxWidth = minKeyWidth;
-            for (final MoreKeySpec spec : parentKey.mMoreKeys) {
+            for (final MoreKeySpec spec : parentKey.getMoreKeys()) {
                 final String label = spec.mLabel;
                 // If the label is single letter, minKeyWidth is enough to hold the label.
                 if (label != null && StringUtils.codePointCount(label) > 1) {
@@ -336,7 +337,7 @@
         public MoreKeysKeyboard build() {
             final MoreKeysKeyboardParams params = mParams;
             final int moreKeyFlags = mParentKey.getMoreKeyLabelFlags();
-            final MoreKeySpec[] moreKeys = mParentKey.mMoreKeys;
+            final MoreKeySpec[] moreKeys = mParentKey.getMoreKeys();
             for (int n = 0; n < moreKeys.length; n++) {
                 final MoreKeySpec moreKeySpec = moreKeys[n];
                 final int row = n / params.mNumColumns;
diff --git a/java/src/com/android/inputmethod/keyboard/MoreKeysKeyboardView.java b/java/src/com/android/inputmethod/keyboard/MoreKeysKeyboardView.java
index f00f5a9..973128d 100644
--- a/java/src/com/android/inputmethod/keyboard/MoreKeysKeyboardView.java
+++ b/java/src/com/android/inputmethod/keyboard/MoreKeysKeyboardView.java
@@ -127,7 +127,7 @@
     public void onUpEvent(final int x, final int y, final int pointerId, final long eventTime) {
         if (mCurrentKey != null && mActivePointerId == pointerId) {
             updateReleaseKeyGraphics(mCurrentKey);
-            onCodeInput(mCurrentKey.mCode, x, y);
+            onCodeInput(mCurrentKey.getCode(), x, y);
             mCurrentKey = null;
         }
     }
diff --git a/java/src/com/android/inputmethod/keyboard/PointerTracker.java b/java/src/com/android/inputmethod/keyboard/PointerTracker.java
index b66ee2a..d4d0d87 100644
--- a/java/src/com/android/inputmethod/keyboard/PointerTracker.java
+++ b/java/src/com/android/inputmethod/keyboard/PointerTracker.java
@@ -64,7 +64,7 @@
 
         /**
          * Get KeyboardActionListener object that is used to register key code and so on.
-         * @return the KeyboardActionListner for this PointerTracker
+         * @return the KeyboardActionListner for this PointerTracke
          */
         public KeyboardActionListener getKeyboardActionListener();
 
@@ -94,7 +94,7 @@
     public interface TimerProxy {
         public void startTypingStateTimer(Key typedKey);
         public boolean isTypingState();
-        public void startKeyRepeatTimer(PointerTracker tracker, int delay);
+        public void startKeyRepeatTimer(PointerTracker tracker, int repeatCount, int delay);
         public void startLongPressTimer(PointerTracker tracker, int delay);
         public void cancelLongPressTimer();
         public void startDoubleTapShiftKeyTimer();
@@ -111,7 +111,7 @@
             @Override
             public boolean isTypingState() { return false; }
             @Override
-            public void startKeyRepeatTimer(PointerTracker tracker, int delay) {}
+            public void startKeyRepeatTimer(PointerTracker tracker, int repeatCount, int delay) {}
             @Override
             public void startLongPressTimer(PointerTracker tracker, int delay) {}
             @Override
@@ -490,7 +490,7 @@
 
     // Returns true if keyboard has been changed by this callback.
     private boolean callListenerOnPressAndCheckKeyboardLayoutChange(final Key key,
-            final boolean isRepeatKey) {
+            final int repeatCount) {
         // While gesture input is going on, this method should be a no-operation. But when gesture
         // input has been canceled, <code>sInGesture</code> and <code>mIsDetectingGesture</code>
         // are set to false. To keep this method is a no-operation,
@@ -504,13 +504,13 @@
                     KeyDetector.printableCode(key),
                     ignoreModifierKey ? " ignoreModifier" : "",
                     key.isEnabled() ? "" : " disabled",
-                    isRepeatKey ? " repeat" : ""));
+                    repeatCount > 0 ? " repeatCount=" + repeatCount : ""));
         }
         if (ignoreModifierKey) {
             return false;
         }
         if (key.isEnabled()) {
-            mListener.onPressKey(key.mCode, isRepeatKey, getActivePointerTrackerCount() == 1);
+            mListener.onPressKey(key.getCode(), repeatCount, getActivePointerTrackerCount() == 1);
             final boolean keyboardLayoutHasBeenChanged = mKeyboardLayoutHasBeenChanged;
             mKeyboardLayoutHasBeenChanged = false;
             mTimerProxy.startTypingStateTimer(key);
@@ -776,7 +776,7 @@
         if (sInGesture || !mGestureStrokeWithPreviewPoints.isStartOfAGesture()) {
             return;
         }
-        if (key == null || !Character.isLetter(key.mCode)) {
+        if (key == null || !Character.isLetter(key.getCode())) {
             return;
         }
         if (DEBUG_LISTENER) {
@@ -967,7 +967,7 @@
             // This onPress call may have changed keyboard layout. Those cases are detected at
             // {@link #setKeyboard}. In those cases, we should update key according to the new
             // keyboard layout.
-            if (callListenerOnPressAndCheckKeyboardLayoutChange(key, false /* isRepeatKey */)) {
+            if (callListenerOnPressAndCheckKeyboardLayoutChange(key, 0 /* repeatCount */)) {
                 key = onDownKey(x, y, eventTime);
             }
 
@@ -1057,7 +1057,7 @@
         // at {@link #setKeyboard}. In those cases, we should update key according
         // to the new keyboard layout.
         Key key = newKey;
-        if (callListenerOnPressAndCheckKeyboardLayoutChange(key, false /* isRepeatKey */)) {
+        if (callListenerOnPressAndCheckKeyboardLayoutChange(key, 0 /* repeatCount */)) {
             key = onMoveKey(x, y);
         }
         onMoveToNewKey(key, x, y);
@@ -1075,8 +1075,8 @@
                     + " phantom sudden move event (distance=%d) is translated to "
                     + "up[%d,%d,%s]/down[%d,%d,%s] events", mPointerId,
                     getDistance(x, y, lastX, lastY),
-                    lastX, lastY, Constants.printableCode(oldKey.mCode),
-                    x, y, Constants.printableCode(key.mCode)));
+                    lastX, lastY, Constants.printableCode(oldKey.getCode()),
+                    x, y, Constants.printableCode(key.getCode())));
         }
         // TODO: This should be moved to outside of this nested if-clause?
         if (ProductionFlag.USES_DEVELOPMENT_ONLY_DIAGNOSTICS) {
@@ -1098,8 +1098,8 @@
                     + " bogus down-move-up event (raidus=%.2f key diagonal) is "
                     + " translated to up[%d,%d,%s]/down[%d,%d,%s] events",
                     mPointerId, radiusRatio,
-                    lastX, lastY, Constants.printableCode(oldKey.mCode),
-                    x, y, Constants.printableCode(key.mCode)));
+                    lastX, lastY, Constants.printableCode(oldKey.getCode()),
+                    x, y, Constants.printableCode(key.getCode())));
         }
         onUpEventInternal(x, y, eventTime);
         onDownEventInternal(x, y, eventTime);
@@ -1107,7 +1107,7 @@
 
     private void processSildeOutFromOldKey(final Key oldKey) {
         setReleasedKeyGraphics(oldKey);
-        callListenerOnRelease(oldKey, oldKey.mCode, true /* withSliding */);
+        callListenerOnRelease(oldKey, oldKey.getCode(), true /* withSliding */);
         startSlidingKeyInput(oldKey);
         mTimerProxy.cancelKeyTimers();
     }
@@ -1263,7 +1263,7 @@
 
         if (sInGesture) {
             if (currentKey != null) {
-                callListenerOnRelease(currentKey, currentKey.mCode, true /* withSliding */);
+                callListenerOnRelease(currentKey, currentKey.getCode(), true /* withSliding */);
             }
             mayEndBatchInput(eventTime);
             return;
@@ -1376,9 +1376,9 @@
         // doesn't have its more keys. (e.g. spacebar, globe key)
         // We always need to start the long press timer if the key has its more keys regardless of
         // whether or not we are in the sliding input mode.
-        if (mIsInSlidingKeyInput && key.mMoreKeys == null) return;
+        if (mIsInSlidingKeyInput && key.getMoreKeys() == null) return;
         final int delay;
-        switch (key.mCode) {
+        switch (key.getCode()) {
         case Constants.CODE_SHIFT:
             delay = sParams.mLongPressShiftLockTimeout;
             break;
@@ -1401,7 +1401,7 @@
             return;
         }
 
-        final int code = key.mCode;
+        final int code = key.getCode();
         callListenerOnCodeInput(key, code, x, y, eventTime);
         callListenerOnRelease(key, code, false /* withSliding */);
     }
@@ -1412,17 +1412,19 @@
         if (!key.isRepeatable()) return;
         // Don't start key repeat when we are in sliding input mode.
         if (mIsInSlidingKeyInput) return;
-        detectAndSendKey(key, key.mX, key.mY, SystemClock.uptimeMillis());
-        mTimerProxy.startKeyRepeatTimer(this, sParams.mKeyRepeatStartTimeout);
+        detectAndSendKey(key, key.getX(), key.getY(), SystemClock.uptimeMillis());
+        final int startRepeatCount = 1;
+        mTimerProxy.startKeyRepeatTimer(this, startRepeatCount, sParams.mKeyRepeatStartTimeout);
     }
 
-    public void onKeyRepeat(final int code) {
+    public void onKeyRepeat(final int code, final int repeatCount) {
         final Key key = getKey();
-        if (key == null || key.mCode != code) {
+        if (key == null || key.getCode() != code) {
             return;
         }
-        mTimerProxy.startKeyRepeatTimer(this, sParams.mKeyRepeatInterval);
-        callListenerOnPressAndCheckKeyboardLayoutChange(key, true /* isRepeatKey */);
+        final int nextRepeatCount = repeatCount + 1;
+        mTimerProxy.startKeyRepeatTimer(this, nextRepeatCount, sParams.mKeyRepeatInterval);
+        callListenerOnPressAndCheckKeyboardLayoutChange(key, repeatCount);
         callListenerOnCodeInput(key, code, mKeyX, mKeyY, SystemClock.uptimeMillis());
     }
 
diff --git a/java/src/com/android/inputmethod/keyboard/ProximityInfo.java b/java/src/com/android/inputmethod/keyboard/ProximityInfo.java
index 9b0a33c..a031669 100644
--- a/java/src/com/android/inputmethod/keyboard/ProximityInfo.java
+++ b/java/src/com/android/inputmethod/keyboard/ProximityInfo.java
@@ -96,7 +96,7 @@
 
     private static boolean needsProximityInfo(final Key key) {
         // Don't include special keys into ProximityInfo.
-        return key.mCode >= Constants.CODE_SPACE;
+        return key.getCode() >= Constants.CODE_SPACE;
     }
 
     private static int getProximityInfoKeysCount(final Key[] keys) {
@@ -122,7 +122,7 @@
                 if (!needsProximityInfo(neighborKey)) {
                     continue;
                 }
-                proximityCharsArray[infoIndex] = neighborKey.mCode;
+                proximityCharsArray[infoIndex] = neighborKey.getCode();
                 infoIndex++;
             }
         }
@@ -159,11 +159,11 @@
             if (!needsProximityInfo(key)) {
                 continue;
             }
-            keyXCoordinates[infoIndex] = key.mX;
-            keyYCoordinates[infoIndex] = key.mY;
-            keyWidths[infoIndex] = key.mWidth;
-            keyHeights[infoIndex] = key.mHeight;
-            keyCharCodes[infoIndex] = key.mCode;
+            keyXCoordinates[infoIndex] = key.getX();
+            keyYCoordinates[infoIndex] = key.getY();
+            keyWidths[infoIndex] = key.getWidth();
+            keyHeights[infoIndex] = key.getHeight();
+            keyCharCodes[infoIndex] = key.getCode();
             infoIndex++;
         }
 
@@ -183,7 +183,7 @@
                 if (!needsProximityInfo(key)) {
                     continue;
                 }
-                final Rect hitBox = key.mHitBox;
+                final Rect hitBox = key.getHitBox();
                 sweetSpotCenterXs[infoIndex] = hitBox.exactCenterX();
                 sweetSpotCenterYs[infoIndex] = hitBox.exactCenterY();
                 sweetSpotRadii[infoIndex] = defaultRadius;
@@ -204,7 +204,7 @@
                             "  [%2d] row=%d x/y/r=%7.2f/%7.2f/%5.2f %s code=%s", infoIndex, row,
                             sweetSpotCenterXs[infoIndex], sweetSpotCenterYs[infoIndex],
                             sweetSpotRadii[infoIndex], (row < rows ? "correct" : "default"),
-                            Constants.printableCode(key.mCode)));
+                            Constants.printableCode(key.getCode())));
                 }
                 infoIndex++;
             }
@@ -245,8 +245,8 @@
         final int threshold = (int) (defaultWidth * SEARCH_DISTANCE);
         final int thresholdSquared = threshold * threshold;
         // Round-up so we don't have any pixels outside the grid
-        final int fullGridWidth = mGridWidth * mCellWidth;
-        final int fullGridHeight = mGridHeight * mCellHeight;
+        final int lastPixelXCoordinate = mGridWidth * mCellWidth - 1;
+        final int lastPixelYCoordinate = mGridHeight * mCellHeight - 1;
 
         // For large layouts, 'neighborsFlatBuffer' is about 80k of memory: gridSize is usually 512,
         // keycount is about 40 and a pointer to a Key is 4 bytes. This contains, for each cell,
@@ -322,19 +322,21 @@
   have to align this on the center of the key. Hence, we don't need a separate value for
   bottomPixelWithinThreshold and call this yEnd right away.
 */
-            final int topPixelWithinThreshold = key.mY - threshold;
+            final int keyX = key.getX();
+            final int keyY = key.getY();
+            final int topPixelWithinThreshold = keyY - threshold;
             final int yDeltaToGrid = topPixelWithinThreshold % mCellHeight;
             final int yMiddleOfTopCell = topPixelWithinThreshold - yDeltaToGrid + halfCellHeight;
             final int yStart = Math.max(halfCellHeight,
                     yMiddleOfTopCell + (yDeltaToGrid <= halfCellHeight ? 0 : mCellHeight));
-            final int yEnd = Math.min(fullGridHeight, key.mY + key.mHeight + threshold);
+            final int yEnd = Math.min(lastPixelYCoordinate, keyY + key.getHeight() + threshold);
 
-            final int leftPixelWithinThreshold = key.mX - threshold;
+            final int leftPixelWithinThreshold = keyX - threshold;
             final int xDeltaToGrid = leftPixelWithinThreshold % mCellWidth;
             final int xMiddleOfLeftCell = leftPixelWithinThreshold - xDeltaToGrid + halfCellWidth;
             final int xStart = Math.max(halfCellWidth,
                     xMiddleOfLeftCell + (xDeltaToGrid <= halfCellWidth ? 0 : mCellWidth));
-            final int xEnd = Math.min(fullGridWidth, key.mX + key.mWidth + threshold);
+            final int xEnd = Math.min(lastPixelXCoordinate, keyX + key.getWidth() + threshold);
 
             int baseIndexOfCurrentRow = (yStart / mCellHeight) * mGridWidth + (xStart / mCellWidth);
             for (int centerY = yStart; centerY <= yEnd; centerY += mCellHeight) {
@@ -372,7 +374,7 @@
             if (index >= destLength) {
                 break;
             }
-            final int code = key.mCode;
+            final int code = key.getCode();
             if (code <= Constants.CODE_SPACE) {
                 break;
             }
diff --git a/java/src/com/android/inputmethod/keyboard/internal/CodesArrayParser.java b/java/src/com/android/inputmethod/keyboard/internal/CodesArrayParser.java
new file mode 100644
index 0000000..c10fdba
--- /dev/null
+++ b/java/src/com/android/inputmethod/keyboard/internal/CodesArrayParser.java
@@ -0,0 +1,85 @@
+/*
+ * Copyright (C) 2013 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.internal;
+
+import com.android.inputmethod.latin.Constants;
+
+/**
+ * The string parser of codesArray specification for <GridRows />. The attribute codesArray is an
+ * array of string.
+ * Each element of the array defines a key label by specifying a code point as a hexadecimal string.
+ * A key label may consist of multiple code points separated by comma.
+ * Each element of the array optionally can have an output text definition after vertical bar
+ * marker. An output text may consist of multiple code points separated by comma.
+ * The format of the codesArray element should be:
+ * <pre>
+ *   codePointInHex[,codePoint2InHex]*(|outputTextCodePointInHex[,outputTextCodePoint2InHex]*)?
+ * </pre>
+ */
+// TODO: Write unit tests for this class.
+public final class CodesArrayParser {
+    // Constants for parsing.
+    private static final char COMMA = ',';
+    private static final char VERTICAL_BAR = '|';
+    private static final String COMMA_STRING = ",";
+    private static final int BASE_HEX = 16;
+
+    private CodesArrayParser() {
+     // This utility class is not publicly instantiable.
+    }
+
+    private static String getLabelSpec(final String codesArraySpec) {
+        final int pos = codesArraySpec.indexOf(VERTICAL_BAR);
+        return (pos < 0) ? codesArraySpec : codesArraySpec.substring(0, pos);
+    }
+
+    public static String parseLabel(final String codesArraySpec) {
+        final String labelSpec = getLabelSpec(codesArraySpec);
+        final StringBuilder sb = new StringBuilder();
+        for (final String codeInHex : labelSpec.split(COMMA_STRING)) {
+            final int codePoint = Integer.parseInt(codeInHex, BASE_HEX);
+            sb.appendCodePoint(codePoint);
+        }
+        return sb.toString();
+    }
+
+    private static String getCodeSpec(final String codesArraySpec) {
+        final int pos = codesArraySpec.indexOf(VERTICAL_BAR);
+        return (pos < 0) ? codesArraySpec : codesArraySpec.substring(pos + 1);
+    }
+
+    public static int parseCode(final String codesArraySpec) {
+        final String codeSpec = getCodeSpec(codesArraySpec);
+        if (codeSpec.indexOf(COMMA) < 0) {
+            return Integer.parseInt(codeSpec, BASE_HEX);
+        }
+        return Constants.CODE_OUTPUT_TEXT;
+    }
+
+    public static String parseOutputText(final String codesArraySpec) {
+        final String codeSpec = getCodeSpec(codesArraySpec);
+        if (codeSpec.indexOf(COMMA) < 0) {
+            return null;
+        }
+        final StringBuilder sb = new StringBuilder();
+        for (final String codeInHex : codeSpec.split(COMMA_STRING)) {
+            final int codePoint = Integer.parseInt(codeInHex, BASE_HEX);
+            sb.appendCodePoint(codePoint);
+        }
+        return sb.toString();
+    }
+}
diff --git a/java/src/com/android/inputmethod/keyboard/internal/DynamicGridKeyboard.java b/java/src/com/android/inputmethod/keyboard/internal/DynamicGridKeyboard.java
new file mode 100644
index 0000000..0dd71e2
--- /dev/null
+++ b/java/src/com/android/inputmethod/keyboard/internal/DynamicGridKeyboard.java
@@ -0,0 +1,223 @@
+/*
+ * Copyright (C) 2013 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.internal;
+
+import android.content.SharedPreferences;
+import android.text.TextUtils;
+import android.util.Log;
+
+import com.android.inputmethod.keyboard.EmojiKeyboardView;
+import com.android.inputmethod.keyboard.Key;
+import com.android.inputmethod.keyboard.Keyboard;
+import com.android.inputmethod.latin.settings.Settings;
+import com.android.inputmethod.latin.utils.CollectionUtils;
+import com.android.inputmethod.latin.utils.StringUtils;
+
+import java.util.ArrayDeque;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+
+/**
+ * This is a Keyboard class where you can add keys dynamically shown in a grid layout
+ */
+public class DynamicGridKeyboard extends Keyboard {
+    private static final String TAG = DynamicGridKeyboard.class.getSimpleName();
+    private static final int TEMPLATE_KEY_CODE_0 = 0x30;
+    private static final int TEMPLATE_KEY_CODE_1 = 0x31;
+
+    private final SharedPreferences mPrefs;
+    private final int mLeftPadding;
+    private final int mHorizontalStep;
+    private final int mVerticalStep;
+    private final int mColumnsNum;
+    private final int mMaxKeyCount;
+    private final boolean mIsRecents;
+    private final ArrayDeque<GridKey> mGridKeys = CollectionUtils.newArrayDeque();
+
+    private Key[] mCachedGridKeys;
+
+    public DynamicGridKeyboard(final SharedPreferences prefs, final Keyboard templateKeyboard,
+            final int maxKeyCount, final int categoryId, final int categoryPageId) {
+        super(templateKeyboard);
+        final Key key0 = getTemplateKey(TEMPLATE_KEY_CODE_0);
+        final Key key1 = getTemplateKey(TEMPLATE_KEY_CODE_1);
+        mLeftPadding = key0.getX();
+        mHorizontalStep = Math.abs(key1.getX() - key0.getX());
+        mVerticalStep = key0.getHeight() + mVerticalGap;
+        mColumnsNum = mBaseWidth / mHorizontalStep;
+        mMaxKeyCount = maxKeyCount;
+        mIsRecents = categoryId == EmojiKeyboardView.CATEGORY_ID_RECENTS;
+        mPrefs = prefs;
+    }
+
+    private Key getTemplateKey(final int code) {
+        for (final Key key : super.getKeys()) {
+            if (key.getCode() == code) {
+                return key;
+            }
+        }
+        throw new RuntimeException("Can't find template key: code=" + code);
+    }
+
+    public void addKeyFirst(final Key usedKey) {
+        addKey(usedKey, true);
+        if (mIsRecents) {
+            saveRecentKeys();
+        }
+    }
+
+    public void addKeyLast(final Key usedKey) {
+        addKey(usedKey, false);
+    }
+
+    private void addKey(final Key usedKey, final boolean addFirst) {
+        if (usedKey == null) {
+            return;
+        }
+        synchronized (mGridKeys) {
+            mCachedGridKeys = null;
+            final GridKey key = new GridKey(usedKey);
+            while (mGridKeys.remove(key)) {
+                // Remove duplicate keys.
+            }
+            if (addFirst) {
+                mGridKeys.addFirst(key);
+            } else {
+                mGridKeys.addLast(key);
+            }
+            while (mGridKeys.size() > mMaxKeyCount) {
+                mGridKeys.removeLast();
+            }
+            int index = 0;
+            for (final GridKey gridKey : mGridKeys) {
+                final int keyX = getKeyX(index);
+                final int keyY = getKeyY(index);
+                gridKey.updateCorrdinates(keyX, keyY);
+                index++;
+            }
+        }
+    }
+
+    private void saveRecentKeys() {
+        final ArrayList<Object> keys = CollectionUtils.newArrayList();
+        for (final Key key : mGridKeys) {
+            if (key.getOutputText() != null) {
+                keys.add(key.getOutputText());
+            } else {
+                keys.add(key.getCode());
+            }
+        }
+        final String jsonStr = StringUtils.listToJsonStr(keys);
+        Settings.writeEmojiRecentKeys(mPrefs, jsonStr);
+    }
+
+    private static Key getKey(final Collection<DynamicGridKeyboard> keyboards, final Object o) {
+        for (final DynamicGridKeyboard kbd : keyboards) {
+            if (o instanceof Integer) {
+                final int code = (Integer) o;
+                final Key key = kbd.getKey(code);
+                if (key != null) {
+                    return key;
+                }
+            } else if (o instanceof String) {
+                final String outputText = (String) o;
+                final Key key = kbd.getKeyFromOutputText(outputText);
+                if (key != null) {
+                    return key;
+                }
+            } else {
+                Log.w(TAG, "Invalid object: " + o);
+            }
+        }
+        return null;
+    }
+
+    public void loadRecentKeys(Collection<DynamicGridKeyboard> keyboards) {
+        final String str = Settings.readEmojiRecentKeys(mPrefs);
+        final List<Object> keys = StringUtils.jsonStrToList(str);
+        for (final Object o : keys) {
+            addKeyLast(getKey(keyboards, o));
+        }
+    }
+
+    private int getKeyX(final int index) {
+        final int column = index % mColumnsNum;
+        return column * mHorizontalStep + mLeftPadding;
+    }
+
+    private int getKeyY(final int index) {
+        final int row = index / mColumnsNum;
+        return row * mVerticalStep + mTopPadding;
+    }
+
+    @Override
+    public Key[] getKeys() {
+        synchronized (mGridKeys) {
+            if (mCachedGridKeys != null) {
+                return mCachedGridKeys;
+            }
+            mCachedGridKeys = mGridKeys.toArray(new Key[mGridKeys.size()]);
+            return mCachedGridKeys;
+        }
+    }
+
+    @Override
+    public Key[] getNearestKeys(final int x, final int y) {
+        // TODO: Calculate the nearest key index in mGridKeys from x and y.
+        return getKeys();
+    }
+
+    static final class GridKey extends Key {
+        private int mCurrentX;
+        private int mCurrentY;
+
+        public GridKey(final Key originalKey) {
+            super(originalKey);
+        }
+
+        public void updateCorrdinates(final int x, final int y) {
+            mCurrentX = x;
+            mCurrentY = y;
+            getHitBox().set(x, y, x + getWidth(), y + getHeight());
+        }
+
+        @Override
+        public int getX() {
+            return mCurrentX;
+        }
+
+        @Override
+        public int getY() {
+            return mCurrentY;
+        }
+
+        @Override
+        public boolean equals(final Object o) {
+            if (!(o instanceof Key)) return false;
+            final Key key = (Key)o;
+            if (getCode() != key.getCode()) return false;
+            if (!TextUtils.equals(getLabel(), key.getLabel())) return false;
+            return TextUtils.equals(getOutputText(), key.getOutputText());
+        }
+
+        @Override
+        public String toString() {
+            return "GridKey: " + super.toString();
+        }
+    }
+}
diff --git a/java/src/com/android/inputmethod/keyboard/internal/KeyStyle.java b/java/src/com/android/inputmethod/keyboard/internal/KeyStyle.java
index f650569..e6a6743 100644
--- a/java/src/com/android/inputmethod/keyboard/internal/KeyStyle.java
+++ b/java/src/com/android/inputmethod/keyboard/internal/KeyStyle.java
@@ -24,7 +24,7 @@
     public abstract String[] getStringArray(TypedArray a, int index);
     public abstract String getString(TypedArray a, int index);
     public abstract int getInt(TypedArray a, int index, int defaultValue);
-    public abstract int getFlag(TypedArray a, int index);
+    public abstract int getFlags(TypedArray a, int index);
 
     protected KeyStyle(final KeyboardTextsSet textsSet) {
         mTextsSet = textsSet;
diff --git a/java/src/com/android/inputmethod/keyboard/internal/KeyStylesSet.java b/java/src/com/android/inputmethod/keyboard/internal/KeyStylesSet.java
index 6aab3e7..05d855e 100644
--- a/java/src/com/android/inputmethod/keyboard/internal/KeyStylesSet.java
+++ b/java/src/com/android/inputmethod/keyboard/internal/KeyStylesSet.java
@@ -66,7 +66,7 @@
         }
 
         @Override
-        public int getFlag(final TypedArray a, final int index) {
+        public int getFlags(final TypedArray a, final int index) {
             return a.getInt(index, 0);
         }
     }
@@ -123,14 +123,12 @@
         }
 
         @Override
-        public int getFlag(final TypedArray a, final int index) {
-            int flags = a.getInt(index, 0);
-            final Object value = mStyleAttributes.get(index);
-            if (value != null) {
-                flags |= (Integer)value;
-            }
-            final KeyStyle parentStyle = mStyles.get(mParentStyleName);
-            return flags | parentStyle.getFlag(a, index);
+        public int getFlags(final TypedArray a, final int index) {
+            final int parentFlags = mStyles.get(mParentStyleName).getFlags(a, index);
+            final Integer value = (Integer)mStyleAttributes.get(index);
+            final int styleFlags = (value != null) ? value : 0;
+            final int flags = a.getInt(index, 0);
+            return flags | styleFlags | parentFlags;
         }
 
         public void readKeyAttributes(final TypedArray keyAttr) {
@@ -142,13 +140,13 @@
             readString(keyAttr, R.styleable.Keyboard_Key_keyHintLabel);
             readStringArray(keyAttr, R.styleable.Keyboard_Key_moreKeys);
             readStringArray(keyAttr, R.styleable.Keyboard_Key_additionalMoreKeys);
-            readFlag(keyAttr, R.styleable.Keyboard_Key_keyLabelFlags);
+            readFlags(keyAttr, R.styleable.Keyboard_Key_keyLabelFlags);
             readString(keyAttr, R.styleable.Keyboard_Key_keyIcon);
             readString(keyAttr, R.styleable.Keyboard_Key_keyIconDisabled);
             readString(keyAttr, R.styleable.Keyboard_Key_keyIconPreview);
             readInt(keyAttr, R.styleable.Keyboard_Key_maxMoreKeysColumn);
             readInt(keyAttr, R.styleable.Keyboard_Key_backgroundType);
-            readFlag(keyAttr, R.styleable.Keyboard_Key_keyActionFlags);
+            readFlags(keyAttr, R.styleable.Keyboard_Key_keyActionFlags);
         }
 
         private void readString(final TypedArray a, final int index) {
@@ -163,10 +161,11 @@
             }
         }
 
-        private void readFlag(final TypedArray a, final int index) {
+        private void readFlags(final TypedArray a, final int index) {
             if (a.hasValue(index)) {
                 final Integer value = (Integer)mStyleAttributes.get(index);
-                mStyleAttributes.put(index, a.getInt(index, 0) | (value != null ? value : 0));
+                final int styleFlags = value != null ? value : 0;
+                mStyleAttributes.put(index, a.getInt(index, 0) | styleFlags);
             }
         }
 
diff --git a/java/src/com/android/inputmethod/keyboard/internal/KeyboardBuilder.java b/java/src/com/android/inputmethod/keyboard/internal/KeyboardBuilder.java
index b34d7c4..22f7a83 100644
--- a/java/src/com/android/inputmethod/keyboard/internal/KeyboardBuilder.java
+++ b/java/src/com/android/inputmethod/keyboard/internal/KeyboardBuilder.java
@@ -29,6 +29,7 @@
 import com.android.inputmethod.keyboard.Key;
 import com.android.inputmethod.keyboard.Keyboard;
 import com.android.inputmethod.keyboard.KeyboardId;
+import com.android.inputmethod.latin.Constants;
 import com.android.inputmethod.latin.R;
 import com.android.inputmethod.latin.utils.ResourceUtils;
 import com.android.inputmethod.latin.utils.RunInLocale;
@@ -113,6 +114,7 @@
  * </pre>
  */
 
+// TODO: Write unit tests for this class.
 public class KeyboardBuilder<KP extends KeyboardParams> {
     private static final String BUILDER_TAG = "Keyboard.Builder";
     private static final boolean DEBUG = false;
@@ -120,6 +122,7 @@
     // Keyboard XML Tags
     private static final String TAG_KEYBOARD = "Keyboard";
     private static final String TAG_ROW = "Row";
+    private static final String TAG_GRID_ROWS = "GridRows";
     private static final String TAG_KEY = "Key";
     private static final String TAG_SPACER = "Spacer";
     private static final String TAG_INCLUDE = "include";
@@ -218,20 +221,18 @@
                     parseKeyboardAttributes(parser);
                     startKeyboard();
                     parseKeyboardContent(parser, false);
-                    break;
-                } else {
-                    throw new XmlParseUtils.IllegalStartTag(parser, tag, TAG_KEYBOARD);
+                    return;
                 }
+                throw new XmlParseUtils.IllegalStartTag(parser, tag, TAG_KEYBOARD);
             }
         }
     }
 
     private void parseKeyboardAttributes(final XmlPullParser parser) {
+        final AttributeSet attr = Xml.asAttributeSet(parser);
         final TypedArray keyboardAttr = mContext.obtainStyledAttributes(
-                Xml.asAttributeSet(parser), R.styleable.Keyboard, R.attr.keyboardStyle,
-                R.style.Keyboard);
-        final TypedArray keyAttr = mResources.obtainAttributes(Xml.asAttributeSet(parser),
-                R.styleable.Keyboard_Key);
+                attr, R.styleable.Keyboard, R.attr.keyboardStyle, R.style.Keyboard);
+        final TypedArray keyAttr = mResources.obtainAttributes(attr, R.styleable.Keyboard_Key);
         try {
             final KeyboardParams params = mParams;
             final int height = params.mId.mHeight;
@@ -314,6 +315,9 @@
                         startRow(row);
                     }
                     parseRowContent(parser, row, skip);
+                } else if (TAG_GRID_ROWS.equals(tag)) {
+                    if (DEBUG) startTag("<%s>%s", TAG_GRID_ROWS, skip ? " skipped" : "");
+                    parseGridRows(parser, skip);
                 } else if (TAG_INCLUDE.equals(tag)) {
                     parseIncludeKeyboardContent(parser, skip);
                 } else if (TAG_SWITCH.equals(tag)) {
@@ -328,31 +332,30 @@
                 if (DEBUG) endTag("</%s>", tag);
                 if (TAG_KEYBOARD.equals(tag)) {
                     endKeyboard();
-                    break;
-                } else if (TAG_CASE.equals(tag) || TAG_DEFAULT.equals(tag)
-                        || TAG_MERGE.equals(tag)) {
-                    break;
-                } else {
-                    throw new XmlParseUtils.IllegalEndTag(parser, tag, TAG_ROW);
+                    return;
                 }
+                if (TAG_CASE.equals(tag) || TAG_DEFAULT.equals(tag) || TAG_MERGE.equals(tag)) {
+                    return;
+                }
+                throw new XmlParseUtils.IllegalEndTag(parser, tag, TAG_ROW);
             }
         }
     }
 
     private KeyboardRow parseRowAttributes(final XmlPullParser parser)
             throws XmlPullParserException {
-        final TypedArray a = mResources.obtainAttributes(Xml.asAttributeSet(parser),
-                R.styleable.Keyboard);
+        final AttributeSet attr = Xml.asAttributeSet(parser);
+        final TypedArray keyboardAttr = mResources.obtainAttributes(attr, R.styleable.Keyboard);
         try {
-            if (a.hasValue(R.styleable.Keyboard_horizontalGap)) {
+            if (keyboardAttr.hasValue(R.styleable.Keyboard_horizontalGap)) {
                 throw new XmlParseUtils.IllegalAttribute(parser, TAG_ROW, "horizontalGap");
             }
-            if (a.hasValue(R.styleable.Keyboard_verticalGap)) {
+            if (keyboardAttr.hasValue(R.styleable.Keyboard_verticalGap)) {
                 throw new XmlParseUtils.IllegalAttribute(parser, TAG_ROW, "verticalGap");
             }
             return new KeyboardRow(mResources, mParams, parser, mCurrentY);
         } finally {
-            a.recycle();
+            keyboardAttr.recycle();
         }
     }
 
@@ -382,34 +385,97 @@
                     if (!skip) {
                         endRow(row);
                     }
-                    break;
-                } else if (TAG_CASE.equals(tag) || TAG_DEFAULT.equals(tag)
-                        || TAG_MERGE.equals(tag)) {
-                    break;
-                } else {
-                    throw new XmlParseUtils.IllegalEndTag(parser, tag, TAG_ROW);
+                    return;
                 }
+                if (TAG_CASE.equals(tag) || TAG_DEFAULT.equals(tag) || TAG_MERGE.equals(tag)) {
+                    return;
+                }
+                throw new XmlParseUtils.IllegalEndTag(parser, tag, TAG_ROW);
             }
         }
     }
 
+    private void parseGridRows(final XmlPullParser parser, final boolean skip)
+            throws XmlPullParserException, IOException {
+        if (skip) {
+            XmlParseUtils.checkEndTag(TAG_GRID_ROWS, parser);
+            if (DEBUG) {
+                startEndTag("<%s /> skipped", TAG_GRID_ROWS);
+            }
+            return;
+        }
+        final KeyboardRow gridRows = new KeyboardRow(mResources, mParams, parser, mCurrentY);
+        final TypedArray gridRowAttr = mResources.obtainAttributes(
+                Xml.asAttributeSet(parser), R.styleable.Keyboard_GridRows);
+        final int codesArrayId = gridRowAttr.getResourceId(
+                R.styleable.Keyboard_GridRows_codesArray, 0);
+        final int textsArrayId = gridRowAttr.getResourceId(
+                R.styleable.Keyboard_GridRows_textsArray, 0);
+        gridRowAttr.recycle();
+        if (codesArrayId == 0 && textsArrayId == 0) {
+            throw new XmlParseUtils.ParseException(
+                    "Missing codesArray or textsArray attributes", parser);
+        }
+        if (codesArrayId != 0 && textsArrayId != 0) {
+            throw new XmlParseUtils.ParseException(
+                    "Both codesArray and textsArray attributes specifed", parser);
+        }
+        final String[] array = mResources.getStringArray(
+                codesArrayId != 0 ? codesArrayId : textsArrayId);
+        final int counts = array.length;
+        final float keyWidth = gridRows.getKeyWidth(null, 0.0f);
+        final int numColumns = (int)(mParams.mOccupiedWidth / keyWidth);
+        for (int index = 0; index < counts; index += numColumns) {
+            final KeyboardRow row = new KeyboardRow(mResources, mParams, parser, mCurrentY);
+            startRow(row);
+            for (int c = 0; c < numColumns; c++) {
+                final int i = index + c;
+                if (i >= counts) {
+                    break;
+                }
+                final String label;
+                final int code;
+                final String outputText;
+                if (codesArrayId != 0) {
+                    final String codeArraySpec = array[i];
+                    label = CodesArrayParser.parseLabel(codeArraySpec);
+                    code = CodesArrayParser.parseCode(codeArraySpec);
+                    outputText = CodesArrayParser.parseOutputText(codeArraySpec);
+                } else {
+                    final String textArraySpec = array[i];
+                    // TODO: Utilize KeySpecParser or write more generic TextsArrayParser.
+                    label = textArraySpec;
+                    code = Constants.CODE_OUTPUT_TEXT;
+                    outputText = textArraySpec + (char)Constants.CODE_SPACE;
+                }
+                final int x = (int)row.getKeyX(null);
+                final int y = row.getKeyY();
+                final Key key = new Key(mParams, label, null /* hintLabel */, 0 /* iconId */,
+                        code, outputText, x, y, (int)keyWidth, (int)row.getRowHeight(),
+                        row.getDefaultKeyLabelFlags(), row.getDefaultBackgroundType());
+                endKey(key);
+                row.advanceXPos(keyWidth);
+            }
+            endRow(row);
+        }
+
+        XmlParseUtils.checkEndTag(TAG_GRID_ROWS, parser);
+    }
+
     private void parseKey(final XmlPullParser parser, final KeyboardRow row, final boolean skip)
             throws XmlPullParserException, IOException {
         if (skip) {
             XmlParseUtils.checkEndTag(TAG_KEY, parser);
-            if (DEBUG) {
-                startEndTag("<%s /> skipped", TAG_KEY);
-            }
-        } else {
-            final Key key = new Key(mResources, mParams, row, parser);
-            if (DEBUG) {
-                startEndTag("<%s%s %s moreKeys=%s />", TAG_KEY,
-                        (key.isEnabled() ? "" : " disabled"), key,
-                        Arrays.toString(key.mMoreKeys));
-            }
-            XmlParseUtils.checkEndTag(TAG_KEY, parser);
-            endKey(key);
+            if (DEBUG) startEndTag("<%s /> skipped", TAG_KEY);
+            return;
         }
+        final Key key = new Key(mResources, mParams, row, parser);
+        if (DEBUG) {
+            startEndTag("<%s%s %s moreKeys=%s />", TAG_KEY, (key.isEnabled() ? "" : " disabled"),
+                    key, Arrays.toString(key.getMoreKeys()));
+        }
+        XmlParseUtils.checkEndTag(TAG_KEY, parser);
+        endKey(key);
     }
 
     private void parseSpacer(final XmlPullParser parser, final KeyboardRow row, final boolean skip)
@@ -417,12 +483,12 @@
         if (skip) {
             XmlParseUtils.checkEndTag(TAG_SPACER, parser);
             if (DEBUG) startEndTag("<%s /> skipped", TAG_SPACER);
-        } else {
-            final Key.Spacer spacer = new Key.Spacer(mResources, mParams, row, parser);
-            if (DEBUG) startEndTag("<%s />", TAG_SPACER);
-            XmlParseUtils.checkEndTag(TAG_SPACER, parser);
-            endKey(spacer);
+            return;
         }
+        final Key.Spacer spacer = new Key.Spacer(mResources, mParams, row, parser);
+        if (DEBUG) startEndTag("<%s />", TAG_SPACER);
+        XmlParseUtils.checkEndTag(TAG_SPACER, parser);
+        endKey(spacer);
     }
 
     private void parseIncludeKeyboardContent(final XmlPullParser parser, final boolean skip)
@@ -440,66 +506,44 @@
         if (skip) {
             XmlParseUtils.checkEndTag(TAG_INCLUDE, parser);
             if (DEBUG) startEndTag("</%s> skipped", TAG_INCLUDE);
-        } else {
-            final AttributeSet attr = Xml.asAttributeSet(parser);
-            final TypedArray keyboardAttr = mResources.obtainAttributes(attr,
-                    R.styleable.Keyboard_Include);
-            final TypedArray keyAttr = mResources.obtainAttributes(attr,
-                    R.styleable.Keyboard_Key);
-            int keyboardLayout = 0;
-            float savedDefaultKeyWidth = 0;
-            int savedDefaultKeyLabelFlags = 0;
-            int savedDefaultBackgroundType = Key.BACKGROUND_TYPE_NORMAL;
-            try {
-                XmlParseUtils.checkAttributeExists(keyboardAttr,
-                        R.styleable.Keyboard_Include_keyboardLayout, "keyboardLayout",
-                        TAG_INCLUDE, parser);
-                keyboardLayout = keyboardAttr.getResourceId(
-                        R.styleable.Keyboard_Include_keyboardLayout, 0);
-                if (row != null) {
-                    if (keyAttr.hasValue(R.styleable.Keyboard_Key_keyXPos)) {
-                        // Override current x coordinate.
-                        row.setXPos(row.getKeyX(keyAttr));
-                    }
-                    // TODO: Remove this if-clause and do the same as backgroundType below.
-                    savedDefaultKeyWidth = row.getDefaultKeyWidth();
-                    if (keyAttr.hasValue(R.styleable.Keyboard_Key_keyWidth)) {
-                        // Override default key width.
-                        row.setDefaultKeyWidth(row.getKeyWidth(keyAttr));
-                    }
-                    savedDefaultKeyLabelFlags = row.getDefaultKeyLabelFlags();
-                    // Bitwise-or default keyLabelFlag if exists.
-                    row.setDefaultKeyLabelFlags(keyAttr.getInt(
-                            R.styleable.Keyboard_Key_keyLabelFlags, 0)
-                            | savedDefaultKeyLabelFlags);
-                    savedDefaultBackgroundType = row.getDefaultBackgroundType();
-                    // Override default backgroundType if exists.
-                    row.setDefaultBackgroundType(keyAttr.getInt(
-                            R.styleable.Keyboard_Key_backgroundType,
-                            savedDefaultBackgroundType));
-                }
-            } finally {
-                keyboardAttr.recycle();
-                keyAttr.recycle();
+            return;
+        }
+        final AttributeSet attr = Xml.asAttributeSet(parser);
+        final TypedArray keyboardAttr = mResources.obtainAttributes(
+                attr, R.styleable.Keyboard_Include);
+        final TypedArray keyAttr = mResources.obtainAttributes(attr, R.styleable.Keyboard_Key);
+        int keyboardLayout = 0;
+        try {
+            XmlParseUtils.checkAttributeExists(
+                    keyboardAttr, R.styleable.Keyboard_Include_keyboardLayout, "keyboardLayout",
+                    TAG_INCLUDE, parser);
+            keyboardLayout = keyboardAttr.getResourceId(
+                    R.styleable.Keyboard_Include_keyboardLayout, 0);
+            if (row != null) {
+                // Override current x coordinate.
+                row.setXPos(row.getKeyX(keyAttr));
+                // Push current Row attributes and update with new attributes.
+                row.pushRowAttributes(keyAttr);
             }
+        } finally {
+            keyboardAttr.recycle();
+            keyAttr.recycle();
+        }
 
-            XmlParseUtils.checkEndTag(TAG_INCLUDE, parser);
-            if (DEBUG) {
-                startEndTag("<%s keyboardLayout=%s />",TAG_INCLUDE,
-                        mResources.getResourceEntryName(keyboardLayout));
+        XmlParseUtils.checkEndTag(TAG_INCLUDE, parser);
+        if (DEBUG) {
+            startEndTag("<%s keyboardLayout=%s />",TAG_INCLUDE,
+                    mResources.getResourceEntryName(keyboardLayout));
+        }
+        final XmlResourceParser parserForInclude = mResources.getXml(keyboardLayout);
+        try {
+            parseMerge(parserForInclude, row, skip);
+        } finally {
+            if (row != null) {
+                // Restore Row attributes.
+                row.popRowAttributes();
             }
-            final XmlResourceParser parserForInclude = mResources.getXml(keyboardLayout);
-            try {
-                parseMerge(parserForInclude, row, skip);
-            } finally {
-                if (row != null) {
-                    // Restore default keyWidth, keyLabelFlags, and backgroundType.
-                    row.setDefaultKeyWidth(savedDefaultKeyWidth);
-                    row.setDefaultKeyLabelFlags(savedDefaultKeyLabelFlags);
-                    row.setDefaultBackgroundType(savedDefaultBackgroundType);
-                }
-                parserForInclude.close();
-            }
+            parserForInclude.close();
         }
     }
 
@@ -516,11 +560,10 @@
                     } else {
                         parseRowContent(parser, row, skip);
                     }
-                    break;
-                } else {
-                    throw new XmlParseUtils.ParseException(
-                            "Included keyboard layout must have <merge> root element", parser);
+                    return;
                 }
+                throw new XmlParseUtils.ParseException(
+                        "Included keyboard layout must have <merge> root element", parser);
             }
         }
     }
@@ -554,10 +597,9 @@
                 final String tag = parser.getName();
                 if (TAG_SWITCH.equals(tag)) {
                     if (DEBUG) endTag("</%s>", TAG_SWITCH);
-                    break;
-                } else {
-                    throw new XmlParseUtils.IllegalEndTag(parser, tag, TAG_SWITCH);
+                    return;
                 }
+                throw new XmlParseUtils.IllegalEndTag(parser, tag, TAG_SWITCH);
             }
         }
     }
@@ -580,86 +622,92 @@
         if (id == null) {
             return true;
         }
-        final TypedArray a = mResources.obtainAttributes(Xml.asAttributeSet(parser),
-                R.styleable.Keyboard_Case);
+        final AttributeSet attr = Xml.asAttributeSet(parser);
+        final TypedArray caseAttr = mResources.obtainAttributes(attr, R.styleable.Keyboard_Case);
         try {
-            final boolean keyboardLayoutSetElementMatched = matchTypedValue(a,
+            final boolean keyboardLayoutSetMatched = matchString(caseAttr,
+                    R.styleable.Keyboard_Case_keyboardLayoutSet,
+                    SubtypeLocaleUtils.getKeyboardLayoutSetName(id.mSubtype));
+            final boolean keyboardLayoutSetElementMatched = matchTypedValue(caseAttr,
                     R.styleable.Keyboard_Case_keyboardLayoutSetElement, id.mElementId,
                     KeyboardId.elementIdToName(id.mElementId));
-            final boolean modeMatched = matchTypedValue(a,
+            final boolean modeMatched = matchTypedValue(caseAttr,
                     R.styleable.Keyboard_Case_mode, id.mMode, KeyboardId.modeName(id.mMode));
-            final boolean navigateNextMatched = matchBoolean(a,
+            final boolean navigateNextMatched = matchBoolean(caseAttr,
                     R.styleable.Keyboard_Case_navigateNext, id.navigateNext());
-            final boolean navigatePreviousMatched = matchBoolean(a,
+            final boolean navigatePreviousMatched = matchBoolean(caseAttr,
                     R.styleable.Keyboard_Case_navigatePrevious, id.navigatePrevious());
-            final boolean passwordInputMatched = matchBoolean(a,
+            final boolean passwordInputMatched = matchBoolean(caseAttr,
                     R.styleable.Keyboard_Case_passwordInput, id.passwordInput());
-            final boolean clobberSettingsKeyMatched = matchBoolean(a,
+            final boolean clobberSettingsKeyMatched = matchBoolean(caseAttr,
                     R.styleable.Keyboard_Case_clobberSettingsKey, id.mClobberSettingsKey);
-            final boolean shortcutKeyEnabledMatched = matchBoolean(a,
+            final boolean shortcutKeyEnabledMatched = matchBoolean(caseAttr,
                     R.styleable.Keyboard_Case_shortcutKeyEnabled, id.mShortcutKeyEnabled);
-            final boolean shortcutKeyOnSymbolsMatched = matchBoolean(a,
+            final boolean shortcutKeyOnSymbolsMatched = matchBoolean(caseAttr,
                     R.styleable.Keyboard_Case_shortcutKeyOnSymbols, id.mShortcutKeyOnSymbols);
-            final boolean hasShortcutKeyMatched = matchBoolean(a,
+            final boolean hasShortcutKeyMatched = matchBoolean(caseAttr,
                     R.styleable.Keyboard_Case_hasShortcutKey, id.mHasShortcutKey);
-            final boolean languageSwitchKeyEnabledMatched = matchBoolean(a,
+            final boolean languageSwitchKeyEnabledMatched = matchBoolean(caseAttr,
                     R.styleable.Keyboard_Case_languageSwitchKeyEnabled,
                     id.mLanguageSwitchKeyEnabled);
-            final boolean isMultiLineMatched = matchBoolean(a,
+            final boolean isMultiLineMatched = matchBoolean(caseAttr,
                     R.styleable.Keyboard_Case_isMultiLine, id.isMultiLine());
-            final boolean imeActionMatched = matchInteger(a,
+            final boolean imeActionMatched = matchInteger(caseAttr,
                     R.styleable.Keyboard_Case_imeAction, id.imeAction());
-            final boolean localeCodeMatched = matchString(a,
+            final boolean localeCodeMatched = matchString(caseAttr,
                     R.styleable.Keyboard_Case_localeCode, id.mLocale.toString());
-            final boolean languageCodeMatched = matchString(a,
+            final boolean languageCodeMatched = matchString(caseAttr,
                     R.styleable.Keyboard_Case_languageCode, id.mLocale.getLanguage());
-            final boolean countryCodeMatched = matchString(a,
+            final boolean countryCodeMatched = matchString(caseAttr,
                     R.styleable.Keyboard_Case_countryCode, id.mLocale.getCountry());
-            final boolean selected = keyboardLayoutSetElementMatched && modeMatched
-                    && navigateNextMatched && navigatePreviousMatched && passwordInputMatched
-                    && clobberSettingsKeyMatched && shortcutKeyEnabledMatched
-                    && shortcutKeyOnSymbolsMatched && hasShortcutKeyMatched
-                    && languageSwitchKeyEnabledMatched && isMultiLineMatched && imeActionMatched
-                    && localeCodeMatched && languageCodeMatched && countryCodeMatched;
+            final boolean selected = keyboardLayoutSetMatched && keyboardLayoutSetElementMatched
+                    && modeMatched && navigateNextMatched && navigatePreviousMatched
+                    && passwordInputMatched && clobberSettingsKeyMatched
+                    && shortcutKeyEnabledMatched && shortcutKeyOnSymbolsMatched
+                    && hasShortcutKeyMatched && languageSwitchKeyEnabledMatched
+                    && isMultiLineMatched && imeActionMatched && localeCodeMatched
+                    && languageCodeMatched && countryCodeMatched;
 
             if (DEBUG) {
-                startTag("<%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s>%s", TAG_CASE,
-                        textAttr(a.getString(
+                startTag("<%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s>%s", TAG_CASE,
+                        textAttr(caseAttr.getString(
+                                R.styleable.Keyboard_Case_keyboardLayoutSet), "keyboardLayoutSet"),
+                        textAttr(caseAttr.getString(
                                 R.styleable.Keyboard_Case_keyboardLayoutSetElement),
                                 "keyboardLayoutSetElement"),
-                        textAttr(a.getString(R.styleable.Keyboard_Case_mode), "mode"),
-                        textAttr(a.getString(R.styleable.Keyboard_Case_imeAction),
+                        textAttr(caseAttr.getString(R.styleable.Keyboard_Case_mode), "mode"),
+                        textAttr(caseAttr.getString(R.styleable.Keyboard_Case_imeAction),
                                 "imeAction"),
-                        booleanAttr(a, R.styleable.Keyboard_Case_navigateNext,
+                        booleanAttr(caseAttr, R.styleable.Keyboard_Case_navigateNext,
                                 "navigateNext"),
-                        booleanAttr(a, R.styleable.Keyboard_Case_navigatePrevious,
+                        booleanAttr(caseAttr, R.styleable.Keyboard_Case_navigatePrevious,
                                 "navigatePrevious"),
-                        booleanAttr(a, R.styleable.Keyboard_Case_clobberSettingsKey,
+                        booleanAttr(caseAttr, R.styleable.Keyboard_Case_clobberSettingsKey,
                                 "clobberSettingsKey"),
-                        booleanAttr(a, R.styleable.Keyboard_Case_passwordInput,
+                        booleanAttr(caseAttr, R.styleable.Keyboard_Case_passwordInput,
                                 "passwordInput"),
-                        booleanAttr(a, R.styleable.Keyboard_Case_shortcutKeyEnabled,
+                        booleanAttr(caseAttr, R.styleable.Keyboard_Case_shortcutKeyEnabled,
                                 "shortcutKeyEnabled"),
-                        booleanAttr(a, R.styleable.Keyboard_Case_shortcutKeyOnSymbols,
+                        booleanAttr(caseAttr, R.styleable.Keyboard_Case_shortcutKeyOnSymbols,
                                 "shortcutKeyOnSymbols"),
-                        booleanAttr(a, R.styleable.Keyboard_Case_hasShortcutKey,
+                        booleanAttr(caseAttr, R.styleable.Keyboard_Case_hasShortcutKey,
                                 "hasShortcutKey"),
-                        booleanAttr(a, R.styleable.Keyboard_Case_languageSwitchKeyEnabled,
+                        booleanAttr(caseAttr, R.styleable.Keyboard_Case_languageSwitchKeyEnabled,
                                 "languageSwitchKeyEnabled"),
-                        booleanAttr(a, R.styleable.Keyboard_Case_isMultiLine,
+                        booleanAttr(caseAttr, R.styleable.Keyboard_Case_isMultiLine,
                                 "isMultiLine"),
-                        textAttr(a.getString(R.styleable.Keyboard_Case_localeCode),
+                        textAttr(caseAttr.getString(R.styleable.Keyboard_Case_localeCode),
                                 "localeCode"),
-                        textAttr(a.getString(R.styleable.Keyboard_Case_languageCode),
+                        textAttr(caseAttr.getString(R.styleable.Keyboard_Case_languageCode),
                                 "languageCode"),
-                        textAttr(a.getString(R.styleable.Keyboard_Case_countryCode),
+                        textAttr(caseAttr.getString(R.styleable.Keyboard_Case_countryCode),
                                 "countryCode"),
                         selected ? "" : " skipped");
             }
 
             return selected;
         } finally {
-            a.recycle();
+            caseAttr.recycle();
         }
     }
 
@@ -692,7 +740,8 @@
         }
         if (ResourceUtils.isIntegerValue(v)) {
             return intValue == a.getInt(index, 0);
-        } else if (ResourceUtils.isStringValue(v)) {
+        }
+        if (ResourceUtils.isStringValue(v)) {
             return StringUtils.containsInArray(strValue, a.getString(index).split("\\|"));
         }
         return false;
@@ -711,10 +760,10 @@
 
     private void parseKeyStyle(final XmlPullParser parser, final boolean skip)
             throws XmlPullParserException, IOException {
-        TypedArray keyStyleAttr = mResources.obtainAttributes(Xml.asAttributeSet(parser),
-                R.styleable.Keyboard_KeyStyle);
-        TypedArray keyAttrs = mResources.obtainAttributes(Xml.asAttributeSet(parser),
-                R.styleable.Keyboard_Key);
+        final AttributeSet attr = Xml.asAttributeSet(parser);
+        final TypedArray keyStyleAttr = mResources.obtainAttributes(
+                attr, R.styleable.Keyboard_KeyStyle);
+        final TypedArray keyAttrs = mResources.obtainAttributes(attr, R.styleable.Keyboard_Key);
         try {
             if (!keyStyleAttr.hasValue(R.styleable.Keyboard_KeyStyle_styleName)) {
                 throw new XmlParseUtils.ParseException("<" + TAG_KEY_STYLE
@@ -756,7 +805,7 @@
             mRightEdgeKey = null;
         }
         addEdgeSpace(mParams.mRightPadding, row);
-        mCurrentY += row.mRowHeight;
+        mCurrentY += row.getRowHeight();
         mCurrentRow = null;
         mTopEdge = false;
     }
@@ -774,7 +823,10 @@
     }
 
     private void endKeyboard() {
-        // nothing to do here.
+        // {@link #parseGridRows(XmlPullParser,boolean)} may populate keyboard rows higher than
+        // previously expected.
+        final int actualHeight = mCurrentY - mParams.mVerticalGap + mParams.mBottomPadding;
+        mParams.mOccupiedHeight = Math.max(mParams.mOccupiedHeight, actualHeight);
     }
 
     private void addEdgeSpace(final float width, final KeyboardRow row) {
diff --git a/java/src/com/android/inputmethod/keyboard/internal/KeyboardParams.java b/java/src/com/android/inputmethod/keyboard/internal/KeyboardParams.java
index a57b83a..d32bb75 100644
--- a/java/src/com/android/inputmethod/keyboard/internal/KeyboardParams.java
+++ b/java/src/com/android/inputmethod/keyboard/internal/KeyboardParams.java
@@ -85,7 +85,7 @@
     public void onAddKey(final Key newKey) {
         final Key key = (mKeysCache != null) ? mKeysCache.get(newKey) : newKey;
         final boolean isSpacer = key.isSpacer();
-        if (isSpacer && key.mWidth == 0) {
+        if (isSpacer && key.getWidth() == 0) {
             // Ignore zero width {@link Spacer}.
             return;
         }
@@ -94,7 +94,7 @@
             return;
         }
         updateHistogram(key);
-        if (key.mCode == Constants.CODE_SHIFT) {
+        if (key.getCode() == Constants.CODE_SHIFT) {
             mShiftKeys.add(key);
         }
         if (key.altCodeWhileTyping()) {
@@ -125,14 +125,14 @@
     }
 
     private void updateHistogram(final Key key) {
-        final int height = key.mHeight + mVerticalGap;
+        final int height = key.getHeight() + mVerticalGap;
         final int heightCount = updateHistogramCounter(mHeightHistogram, height);
         if (heightCount > mMaxHeightCount) {
             mMaxHeightCount = heightCount;
             mMostCommonKeyHeight = height;
         }
 
-        final int width = key.mWidth + mHorizontalGap;
+        final int width = key.getWidth() + mHorizontalGap;
         final int widthCount = updateHistogramCounter(mWidthHistogram, width);
         if (widthCount > mMaxWidthCount) {
             mMaxWidthCount = widthCount;
diff --git a/java/src/com/android/inputmethod/keyboard/internal/KeyboardRow.java b/java/src/com/android/inputmethod/keyboard/internal/KeyboardRow.java
index 5fe84a7..0f9497c 100644
--- a/java/src/com/android/inputmethod/keyboard/internal/KeyboardRow.java
+++ b/java/src/com/android/inputmethod/keyboard/internal/KeyboardRow.java
@@ -23,10 +23,13 @@
 import com.android.inputmethod.keyboard.Key;
 import com.android.inputmethod.keyboard.Keyboard;
 import com.android.inputmethod.latin.R;
+import com.android.inputmethod.latin.utils.CollectionUtils;
 import com.android.inputmethod.latin.utils.ResourceUtils;
 
 import org.xmlpull.v1.XmlPullParser;
 
+import java.util.ArrayDeque;
+
 /**
  * Container for keys in the keyboard. All keys in a row are at the same Y-coordinate.
  * Some of the key size defaults can be overridden per row from what the {@link Keyboard}
@@ -38,64 +41,100 @@
     private static final int KEYWIDTH_FILL_RIGHT = -1;
 
     private final KeyboardParams mParams;
-    /** Default width of a key in this row. */
-    private float mDefaultKeyWidth;
-    /** Default height of a key in this row. */
-    public final int mRowHeight;
-    /** Default keyLabelFlags in this row. */
-    private int mDefaultKeyLabelFlags;
-    /** Default backgroundType for this row */
-    private int mDefaultBackgroundType;
+    /** The height of this row. */
+    private final int mRowHeight;
+
+    private final ArrayDeque<RowAttributes> mRowAttributesStack = CollectionUtils.newArrayDeque();
+
+    private static class RowAttributes {
+        /** Default width of a key in this row. */
+        public final float mDefaultKeyWidth;
+        /** Default keyLabelFlags in this row. */
+        public final int mDefaultKeyLabelFlags;
+        /** Default backgroundType for this row */
+        public final int mDefaultBackgroundType;
+
+        /**
+         * Parse and create key attributes. This constructor is used to parse Row tag.
+         *
+         * @param keyAttr an attributes array of Row tag.
+         * @param defaultKeyWidth a default key width.
+         * @param keyboardWidth the keyboard width that is required to calculate keyWidth attribute.
+         */
+        public RowAttributes(final TypedArray keyAttr, final float defaultKeyWidth,
+                final int keyboardWidth) {
+            mDefaultKeyWidth = keyAttr.getFraction(R.styleable.Keyboard_Key_keyWidth,
+                    keyboardWidth, keyboardWidth, defaultKeyWidth);
+            mDefaultKeyLabelFlags = keyAttr.getInt(R.styleable.Keyboard_Key_keyLabelFlags, 0);
+            mDefaultBackgroundType = keyAttr.getInt(R.styleable.Keyboard_Key_backgroundType,
+                    Key.BACKGROUND_TYPE_NORMAL);
+        }
+
+        /**
+         * Parse and update key attributes using default attributes. This constructor is used
+         * to parse include tag.
+         *
+         * @param keyAttr an attributes array of include tag.
+         * @param defaultRowAttr default Row attributes.
+         * @param keyboardWidth the keyboard width that is required to calculate keyWidth attribute.
+         */
+        public RowAttributes(final TypedArray keyAttr, final RowAttributes defaultRowAttr,
+                final int keyboardWidth) {
+            mDefaultKeyWidth = keyAttr.getFraction(R.styleable.Keyboard_Key_keyWidth,
+                    keyboardWidth, keyboardWidth, defaultRowAttr.mDefaultKeyWidth);
+            mDefaultKeyLabelFlags = keyAttr.getInt(R.styleable.Keyboard_Key_keyLabelFlags, 0)
+                    | defaultRowAttr.mDefaultKeyLabelFlags;
+            mDefaultBackgroundType = keyAttr.getInt(R.styleable.Keyboard_Key_backgroundType,
+                    defaultRowAttr.mDefaultBackgroundType);
+        }
+    }
 
     private final int mCurrentY;
     // Will be updated by {@link Key}'s constructor.
     private float mCurrentX;
 
-    public KeyboardRow(final Resources res, final KeyboardParams params, final XmlPullParser parser,
-            final int y) {
+    public KeyboardRow(final Resources res, final KeyboardParams params,
+            final XmlPullParser parser, final int y) {
         mParams = params;
         final TypedArray keyboardAttr = res.obtainAttributes(Xml.asAttributeSet(parser),
                 R.styleable.Keyboard);
         mRowHeight = (int)ResourceUtils.getDimensionOrFraction(keyboardAttr,
-                R.styleable.Keyboard_rowHeight,
-                params.mBaseHeight, params.mDefaultRowHeight);
+                R.styleable.Keyboard_rowHeight, params.mBaseHeight, params.mDefaultRowHeight);
         keyboardAttr.recycle();
         final TypedArray keyAttr = res.obtainAttributes(Xml.asAttributeSet(parser),
                 R.styleable.Keyboard_Key);
-        mDefaultKeyWidth = keyAttr.getFraction(R.styleable.Keyboard_Key_keyWidth,
-                params.mBaseWidth, params.mBaseWidth, params.mDefaultKeyWidth);
-        mDefaultBackgroundType = keyAttr.getInt(R.styleable.Keyboard_Key_backgroundType,
-                Key.BACKGROUND_TYPE_NORMAL);
+        mRowAttributesStack.push(new RowAttributes(
+                keyAttr, params.mDefaultKeyWidth, params.mBaseWidth));
         keyAttr.recycle();
 
-        // TODO: Initialize this with <Row> attribute as backgroundType is done.
-        mDefaultKeyLabelFlags = 0;
         mCurrentY = y;
         mCurrentX = 0.0f;
     }
 
-    public float getDefaultKeyWidth() {
-        return mDefaultKeyWidth;
+    public int getRowHeight() {
+        return mRowHeight;
     }
 
-    public void setDefaultKeyWidth(final float defaultKeyWidth) {
-        mDefaultKeyWidth = defaultKeyWidth;
+    public void pushRowAttributes(final TypedArray keyAttr) {
+        final RowAttributes newAttributes = new RowAttributes(
+                keyAttr, mRowAttributesStack.peek(), mParams.mBaseWidth);
+        mRowAttributesStack.push(newAttributes);
+    }
+
+    public void popRowAttributes() {
+        mRowAttributesStack.pop();
+    }
+
+    public float getDefaultKeyWidth() {
+        return mRowAttributesStack.peek().mDefaultKeyWidth;
     }
 
     public int getDefaultKeyLabelFlags() {
-        return mDefaultKeyLabelFlags;
-    }
-
-    public void setDefaultKeyLabelFlags(final int keyLabelFlags) {
-        mDefaultKeyLabelFlags = keyLabelFlags;
+        return mRowAttributesStack.peek().mDefaultKeyLabelFlags;
     }
 
     public int getDefaultBackgroundType() {
-        return mDefaultBackgroundType;
-    }
-
-    public void setDefaultBackgroundType(final int backgroundType) {
-        mDefaultBackgroundType = backgroundType;
+        return mRowAttributesStack.peek().mDefaultBackgroundType;
     }
 
     public void setXPos(final float keyXPos) {
@@ -111,29 +150,27 @@
     }
 
     public float getKeyX(final TypedArray keyAttr) {
-        if (keyAttr.hasValue(R.styleable.Keyboard_Key_keyXPos)) {
-            final float keyXPos = keyAttr.getFraction(R.styleable.Keyboard_Key_keyXPos,
-                    mParams.mBaseWidth, mParams.mBaseWidth, 0);
-            if (keyXPos < 0) {
-                // If keyXPos is negative, the actual x-coordinate will be
-                // keyboardWidth + keyXPos.
-                // keyXPos shouldn't be less than mCurrentX because drawable area for this
-                // key starts at mCurrentX. Or, this key will overlaps the adjacent key on
-                // its left hand side.
-                final int keyboardRightEdge = mParams.mOccupiedWidth - mParams.mRightPadding;
-                return Math.max(keyXPos + keyboardRightEdge, mCurrentX);
-            } else {
-                return keyXPos + mParams.mLeftPadding;
-            }
+        if (keyAttr == null || !keyAttr.hasValue(R.styleable.Keyboard_Key_keyXPos)) {
+            return mCurrentX;
         }
-        return mCurrentX;
-    }
-
-    public float getKeyWidth(final TypedArray keyAttr) {
-        return getKeyWidth(keyAttr, mCurrentX);
+        final float keyXPos = keyAttr.getFraction(R.styleable.Keyboard_Key_keyXPos,
+                mParams.mBaseWidth, mParams.mBaseWidth, 0);
+        if (keyXPos >= 0) {
+            return keyXPos + mParams.mLeftPadding;
+        }
+        // If keyXPos is negative, the actual x-coordinate will be
+        // keyboardWidth + keyXPos.
+        // keyXPos shouldn't be less than mCurrentX because drawable area for this
+        // key starts at mCurrentX. Or, this key will overlaps the adjacent key on
+        // its left hand side.
+        final int keyboardRightEdge = mParams.mOccupiedWidth - mParams.mRightPadding;
+        return Math.max(keyXPos + keyboardRightEdge, mCurrentX);
     }
 
     public float getKeyWidth(final TypedArray keyAttr, final float keyXPos) {
+        if (keyAttr == null) {
+            return getDefaultKeyWidth();
+        }
         final int widthType = ResourceUtils.getEnumValue(keyAttr,
                 R.styleable.Keyboard_Key_keyWidth, KEYWIDTH_NOT_ENUM);
         switch (widthType) {
@@ -144,7 +181,7 @@
             return keyboardRightEdge - keyXPos;
         default: // KEYWIDTH_NOT_ENUM
             return keyAttr.getFraction(R.styleable.Keyboard_Key_keyWidth,
-                    mParams.mBaseWidth, mParams.mBaseWidth, mDefaultKeyWidth);
+                    mParams.mBaseWidth, mParams.mBaseWidth, getDefaultKeyWidth());
         }
     }
 }
diff --git a/java/src/com/android/inputmethod/keyboard/internal/KeyboardState.java b/java/src/com/android/inputmethod/keyboard/internal/KeyboardState.java
index 164910d..b3491d8 100644
--- a/java/src/com/android/inputmethod/keyboard/internal/KeyboardState.java
+++ b/java/src/com/android/inputmethod/keyboard/internal/KeyboardState.java
@@ -45,6 +45,7 @@
         public void setAlphabetAutomaticShiftedKeyboard();
         public void setAlphabetShiftLockedKeyboard();
         public void setAlphabetShiftLockShiftedKeyboard();
+        public void setEmojiKeyboard();
         public void setSymbolsKeyboard();
         public void setSymbolsShiftedKeyboard();
 
@@ -74,7 +75,10 @@
     private static final int SWITCH_STATE_MOMENTARY_ALPHA_SHIFT = 5;
     private int mSwitchState = SWITCH_STATE_ALPHA;
 
+    // TODO: Consolidate these two mode booleans into one integer to distinguish between alphabet,
+    // symbols, and emoji mode.
     private boolean mIsAlphabetMode;
+    private boolean mIsEmojiMode;
     private AlphabetShiftState mAlphabetShiftState = new AlphabetShiftState();
     private boolean mIsSymbolShifted;
     private boolean mPrevMainKeyboardWasShiftLocked;
@@ -91,6 +95,7 @@
         public boolean mIsValid;
         public boolean mIsAlphabetMode;
         public boolean mIsAlphabetShiftLocked;
+        public boolean mIsEmojiMode;
         public int mShiftMode;
 
         @Override
@@ -99,6 +104,8 @@
             if (mIsAlphabetMode) {
                 if (mIsAlphabetShiftLocked) return "ALPHABET_SHIFT_LOCKED";
                 return "ALPHABET_" + shiftModeToString(mShiftMode);
+            } else if (mIsEmojiMode) {
+                return "EMOJI";
             } else {
                 return "SYMBOLS_" + shiftModeToString(mShiftMode);
             }
@@ -131,6 +138,7 @@
     public void onSaveKeyboardState() {
         final SavedKeyboardState state = mSavedKeyboardState;
         state.mIsAlphabetMode = mIsAlphabetMode;
+        state.mIsEmojiMode = mIsEmojiMode;
         if (mIsAlphabetMode) {
             state.mIsAlphabetShiftLocked = mAlphabetShiftState.isShiftLocked();
             state.mShiftMode = mAlphabetShiftState.isAutomaticShifted() ? AUTOMATIC_SHIFT
@@ -152,6 +160,8 @@
         }
         if (!state.mIsValid || state.mIsAlphabetMode) {
             setAlphabetKeyboard();
+        } else if (state.mIsEmojiMode) {
+            setEmojiKeyboard();
         } else {
             if (state.mShiftMode == MANUAL_SHIFT) {
                 setSymbolsShiftedKeyboard();
@@ -280,6 +290,7 @@
 
         mSwitchActions.setAlphabetKeyboard();
         mIsAlphabetMode = true;
+        mIsEmojiMode = false;
         mIsSymbolShifted = false;
         mRecapitalizeMode = RecapitalizeStatus.NOT_A_RECAPITALIZE_MODE;
         mSwitchState = SWITCH_STATE_ALPHA;
@@ -310,6 +321,15 @@
         mSwitchState = SWITCH_STATE_SYMBOL_BEGIN;
     }
 
+    private void setEmojiKeyboard() {
+        if (DEBUG_ACTION) {
+            Log.d(TAG, "setEmojiKeyboard");
+        }
+        mIsAlphabetMode = false;
+        mIsEmojiMode = true;
+        mSwitchActions.setEmojiKeyboard();
+    }
+
     public void onPressKey(final int code, final boolean isSinglePointer, final int autoCaps) {
         if (DEBUG_EVENT) {
             Log.d(TAG, "onPressKey: code=" + Constants.printableCode(code)
@@ -460,7 +480,8 @@
             } else {
                 if (mAlphabetShiftState.isShiftLocked()) {
                     // Shift key is pressed while shift locked state, we will treat this state as
-                    // shift lock shifted state and mark as if shift key pressed while normal state.
+                    // shift lock shifted state and mark as if shift key pressed while normal
+                    // state.
                     setShifted(SHIFT_LOCK_SHIFTED);
                     mShiftKeyState.onPress();
                 } else if (mAlphabetShiftState.isAutomaticShifted()) {
@@ -587,7 +608,8 @@
             break;
         case SWITCH_STATE_MOMENTARY_SYMBOL_AND_MORE:
             if (code == Constants.CODE_SHIFT) {
-                // Detected only the shift key has been pressed on symbol layout, and then released.
+                // Detected only the shift key has been pressed on symbol layout, and then
+                // released.
                 mSwitchState = SWITCH_STATE_SYMBOL_BEGIN;
             }
             break;
@@ -610,6 +632,8 @@
         // If the code is a letter, update keyboard shift state.
         if (Constants.isLetterCode(code)) {
             updateAlphabetShiftState(autoCaps, RecapitalizeStatus.NOT_A_RECAPITALIZE_MODE);
+        } else if (code == Constants.CODE_EMOJI) {
+            setEmojiKeyboard();
         }
     }
 
diff --git a/java/src/com/android/inputmethod/keyboard/internal/KeyboardTextsSet.java b/java/src/com/android/inputmethod/keyboard/internal/KeyboardTextsSet.java
index b55e19d..67553fb 100644
--- a/java/src/com/android/inputmethod/keyboard/internal/KeyboardTextsSet.java
+++ b/java/src/com/android/inputmethod/keyboard/internal/KeyboardTextsSet.java
@@ -27,18 +27,18 @@
 /**
  * !!!!! DO NOT EDIT THIS FILE !!!!!
  *
- * This file is generated by tools/maketext. The base template file is
- *   tools/maketext/res/com/android/inputmethod/keyboard/internal/KeyboardTextsSet.tmpl
+ * This file is generated by tools/make-keyboard-text. The base template file is
+ *   tools/make-keyboard-text/res/com/android/inputmethod/keyboard/internal/KeyboardTextsSet.tmpl
  *
  * This file must be updated when any text resources in keyboard layout files have been changed.
  * These text resources are referred as "!text/<resource_name>" in keyboard XML definitions,
  * and should be defined in
- *   tools/maketext/res/values-<locale>/donottranslate-more-keys.xml
+ *   tools/make-keyboard-text/res/values-<locale>/donottranslate-more-keys.xml
  *
  * To update this file, please run the following commands.
  *   $ cd $ANDROID_BUILD_TOP
- *   $ mmm packages/inputmethods/LatinIME/tools/maketext
- *   $ maketext -java packages/inputmethods/LatinIME/java/src
+ *   $ mmm packages/inputmethods/LatinIME/tools/make-keyboard-text
+ *   $ make-keyboard-text -java packages/inputmethods/LatinIME/java/src
  *
  * The updated source file will be generated to the following path (this file).
  *   packages/inputmethods/LatinIME/java/src/com/android/inputmethod/keyboard/internal/
@@ -153,8 +153,8 @@
         /* 48 */ "single_angle_quotes",
         /* 49 */ "double_angle_quotes",
         /* 50 */ "more_keys_for_currency_dollar",
-        /* 51 */ "keylabel_for_currency_generic",
-        /* 52 */ "more_keys_for_currency_generic",
+        /* 51 */ "keylabel_for_currency",
+        /* 52 */ "more_keys_for_currency",
         /* 53 */ "more_keys_for_punctuation",
         /* 54 */ "more_keys_for_star",
         /* 55 */ "more_keys_for_bullet",
@@ -233,25 +233,24 @@
         /* 128 */ "label_to_phone_symbols_key",
         /* 129 */ "label_time_am",
         /* 130 */ "label_time_pm",
-        /* 131 */ "label_to_symbol_key_pcqwerty",
-        /* 132 */ "keylabel_for_popular_domain",
-        /* 133 */ "more_keys_for_popular_domain",
-        /* 134 */ "more_keys_for_smiley",
-        /* 135 */ "single_laqm_raqm",
-        /* 136 */ "single_laqm_raqm_rtl",
-        /* 137 */ "single_raqm_laqm",
-        /* 138 */ "double_laqm_raqm",
-        /* 139 */ "double_laqm_raqm_rtl",
-        /* 140 */ "double_raqm_laqm",
-        /* 141 */ "single_lqm_rqm",
-        /* 142 */ "single_9qm_lqm",
-        /* 143 */ "single_9qm_rqm",
-        /* 144 */ "double_lqm_rqm",
-        /* 145 */ "double_9qm_lqm",
-        /* 146 */ "double_9qm_rqm",
-        /* 147 */ "more_keys_for_single_quote",
-        /* 148 */ "more_keys_for_double_quote",
-        /* 149 */ "more_keys_for_tablet_double_quote",
+        /* 131 */ "keylabel_for_popular_domain",
+        /* 132 */ "more_keys_for_popular_domain",
+        /* 133 */ "more_keys_for_smiley",
+        /* 134 */ "single_laqm_raqm",
+        /* 135 */ "single_laqm_raqm_rtl",
+        /* 136 */ "single_raqm_laqm",
+        /* 137 */ "double_laqm_raqm",
+        /* 138 */ "double_laqm_raqm_rtl",
+        /* 139 */ "double_raqm_laqm",
+        /* 140 */ "single_lqm_rqm",
+        /* 141 */ "single_9qm_lqm",
+        /* 142 */ "single_9qm_rqm",
+        /* 143 */ "double_lqm_rqm",
+        /* 144 */ "double_9qm_lqm",
+        /* 145 */ "double_9qm_rqm",
+        /* 146 */ "more_keys_for_single_quote",
+        /* 147 */ "more_keys_for_double_quote",
+        /* 148 */ "more_keys_for_tablet_double_quote",
     };
 
     private static final String EMPTY = "";
@@ -278,7 +277,7 @@
         /* 50 */ "\u00A2,\u00A3,\u20AC,\u00A5,\u20B1",
         /* 51 */ "$",
         /* 52 */ "$,\u00A2,\u20AC,\u00A3,\u00A5,\u20B1",
-        /* 53 */ "!fixedColumnOrder!8,\",\',#,-,:,!,\\,,?,@,&,\\%,+,;,/,(,)",
+        /* 53 */ "!fixedColumnOrder!3,!,\\,,?,:,;,@",
         // U+2020: "†" DAGGER
         // U+2021: "‡" DOUBLE DAGGER
         // U+2605: "★" BLACK STAR
@@ -361,10 +360,11 @@
         // U+2030: "‰" PER MILLE SIGN
         /* 103 */ "\u2030",
         /* 104 */ ",",
-        /* 105 */ "!",
-        /* 106 */ "!",
-        /* 107 */ "?",
-        /* 108 */ "?",
+        /* 105~ */
+        EMPTY, EMPTY, EMPTY,
+        /* ~107 */
+        // U+2026: "…" HORIZONTAL ELLIPSIS
+        /* 108 */ "\u2026",
         /* 109 */ "\'",
         /* 110 */ "\"",
         /* 111 */ "\"",
@@ -383,7 +383,7 @@
         // Label for "switch to more symbol" modifier key.  Must be short to fit on key!
         /* 124 */ "= \\ <",
         // Label for "switch to more symbol" modifier key on tablets.  Must be short to fit on key!
-        /* 125 */ "~ \\ {",
+        /* 125 */ "~ [ <",
         // Label for "Tab" key.  Must be short to fit on key!
         /* 126 */ "Tab",
         // Label for "switch to phone numeric" key.  Must be short to fit on key!
@@ -396,12 +396,10 @@
         /* 129 */ "AM",
         // Key label for "post meridiem"
         /* 130 */ "PM",
-        // Label for "switch to symbols" key on PC QWERTY layout
-        /* 131 */ "Sym",
-        /* 132 */ ".com",
+        /* 131 */ ".com",
         // popular web domains for the locale - most popular, displayed on the keyboard
-        /* 133 */ "!hasLabels!,.net,.org,.gov,.edu",
-        /* 134 */ "!fixedColumnOrder!5,!hasLabels!,=-O|=-O ,:-P|:-P ,;-)|;-) ,:-(|:-( ,:-)|:-) ,:-!|:-! ,:-$|:-$ ,B-)|B-) ,:O|:O ,:-*|:-* ,:-D|:-D ,:\'(|:\'( ,:-\\\\|:-\\\\ ,O:-)|O:-) ,:-[|:-[ ",
+        /* 132 */ "!hasLabels!,.net,.org,.gov,.edu",
+        /* 133 */ "!fixedColumnOrder!5,!hasLabels!,=-O|=-O ,:-P|:-P ,;-)|;-) ,:-(|:-( ,:-)|:-) ,:-!|:-! ,:-$|:-$ ,B-)|B-) ,:O|:O ,:-*|:-* ,:-D|:-D ,:\'(|:\'( ,:-\\\\|:-\\\\ ,O:-)|O:-) ,:-[|:-[ ",
         // U+2039: "‹" SINGLE LEFT-POINTING ANGLE QUOTATION MARK
         // U+203A: "›" SINGLE RIGHT-POINTING ANGLE QUOTATION MARK
         // U+00AB: "«" LEFT-POINTING DOUBLE ANGLE QUOTATION MARK
@@ -423,24 +421,24 @@
         // The following each quotation mark pair consist of
         // <opening quotation mark>, <closing quotation mark>
         // and is named after (single|double)_<opening quotation mark>_<closing quotation mark>.
-        /* 135 */ "\u2039,\u203A",
-        /* 136 */ "\u2039|\u203A,\u203A|\u2039",
-        /* 137 */ "\u203A,\u2039",
-        /* 138 */ "\u00AB,\u00BB",
-        /* 139 */ "\u00AB|\u00BB,\u00BB|\u00AB",
-        /* 140 */ "\u00BB,\u00AB",
+        /* 134 */ "\u2039,\u203A",
+        /* 135 */ "\u2039|\u203A,\u203A|\u2039",
+        /* 136 */ "\u203A,\u2039",
+        /* 137 */ "\u00AB,\u00BB",
+        /* 138 */ "\u00AB|\u00BB,\u00BB|\u00AB",
+        /* 139 */ "\u00BB,\u00AB",
         // The following each quotation mark triplet consists of
         // <another quotation mark>, <opening quotation mark>, <closing quotation mark>
         // and is named after (single|double)_<opening quotation mark>_<closing quotation mark>.
-        /* 141 */ "\u201A,\u2018,\u2019",
-        /* 142 */ "\u2019,\u201A,\u2018",
-        /* 143 */ "\u2018,\u201A,\u2019",
-        /* 144 */ "\u201E,\u201C,\u201D",
-        /* 145 */ "\u201D,\u201E,\u201C",
-        /* 146 */ "\u201C,\u201E,\u201D",
-        /* 147 */ "!fixedColumnOrder!5,!text/single_quotes,!text/single_angle_quotes",
-        /* 148 */ "!fixedColumnOrder!5,!text/double_quotes,!text/double_angle_quotes",
-        /* 149 */ "!fixedColumnOrder!6,!text/double_quotes,!text/single_quotes,!text/double_angle_quotes,!text/single_angle_quotes",
+        /* 140 */ "\u201A,\u2018,\u2019",
+        /* 141 */ "\u2019,\u201A,\u2018",
+        /* 142 */ "\u2018,\u201A,\u2019",
+        /* 143 */ "\u201E,\u201C,\u201D",
+        /* 144 */ "\u201D,\u201E,\u201C",
+        /* 145 */ "\u201C,\u201E,\u201D",
+        /* 146 */ "!fixedColumnOrder!5,!text/single_quotes,!text/single_angle_quotes",
+        /* 147 */ "!fixedColumnOrder!5,!text/double_quotes,!text/double_angle_quotes",
+        /* 148 */ "!fixedColumnOrder!6,!text/double_quotes,!text/single_quotes,!text/double_angle_quotes,!text/single_angle_quotes",
     };
 
     /* Language af: Afrikaans */
@@ -519,7 +517,7 @@
         // U+061F: "؟" ARABIC QUESTION MARK
         // U+060C: "،" ARABIC COMMA
         // U+061B: "؛" ARABIC SEMICOLON
-        /* 53 */ "!fixedColumnOrder!8,\",\',#,-,:,!,\u060C,\u061F,@,&,\\%,+,\u061B,/,(,)",
+        /* 53 */ "!fixedColumnOrder!8,\",\',#,-,:,!,\u060C,\u061F,@,&,\\%,+,\u061B,/,(|),)|(",
         // U+2605: "★" BLACK STAR
         // U+066D: "٭" ARABIC FIVE POINTED STAR
         /* 54 */ "\u2605,\u066D",
@@ -785,7 +783,7 @@
         null, null, null, null, null, null, null, null,
         /* ~52 */
         // U+00B7: "·" MIDDLE DOT
-        /* 53 */ "!fixedColumnOrder!9,\u00B7,\",\',#,-,:,!,\\,,?,@,&,\\%,+,;,/,(,)",
+        /* 53 */ "!fixedColumnOrder!4,\u00B7,!,\\,,?,:,;,@",
         /* 54~ */
         null, null, null, null, null, null, null, null, null, null, null, null, null, null, null,
         null, null, null, null, null, null, null, null, null, null, null, null, null, null, null,
@@ -1257,7 +1255,7 @@
         /* ~52 */
         // U+00A1: "¡" INVERTED EXCLAMATION MARK
         // U+00BF: "¿" INVERTED QUESTION MARK
-        /* 53 */ "!fixedColumnOrder!9,\u00A1,\",\',#,-,:,!,\\,,?,\u00BF,@,&,\\%,+,;,/,(,)",
+        /* 53 */ "!fixedColumnOrder!4,;,!,\\,,?,:,\u00A1,@,\u00BF",
         /* 54~ */
         null, null, null, null, null, null, null, null, null, null, null, null, null, null, null,
         null, null, null, null, null, null, null, null, null, null, null, null, null, null, null,
@@ -1402,13 +1400,14 @@
         /* 47 */ null,
         /* 48 */ "!text/single_laqm_raqm_rtl",
         /* 49 */ "!text/double_laqm_raqm_rtl",
-        /* 50~ */
-        null, null, null,
-        /* ~52 */
+        /* 50 */ null,
+        // U+FDFC: "﷼" RIAL SIGN
+        /* 51 */ "\uFDFC",
+        /* 52 */ null,
         // U+061F: "؟" ARABIC QUESTION MARK
         // U+060C: "،" ARABIC COMMA
         // U+061B: "؛" ARABIC SEMICOLON
-        /* 53 */ "!fixedColumnOrder!8,\",\',#,-,:,!,\u060C,\u061F,@,&,\\%,+,\u061B,/,(,)",
+        /* 53 */ "!fixedColumnOrder!8,\",\',#,-,:,!,\u060C,\u061F,@,&,\\%,+,\u061B,/,(|),)|(",
         // U+2605: "★" BLACK STAR
         // U+066D: "٭" ARABIC FIVE POINTED STAR
         /* 54 */ "\u2605,\u066D",
@@ -1762,6 +1761,37 @@
         /* 49 */ "!text/double_raqm_laqm",
     };
 
+    /* Language hy: Armenian */
+    private static final String[] LANGUAGE_hy = {
+        /* 0~ */
+        null, null, null, null, null, null, null, null, null, null, null, null, null, null, null,
+        null, null, null, null, null, null, null, null, null, null, null, null, null, null, null,
+        null, null, null, null, null, null, null, null, null, null, null, null, null, null, null,
+        null, null, null, null, null, null, null, null,
+        /* ~52 */
+        // U+058A: "֊" ARMENIAN HYPHEN
+        // U+055C: "՜" ARMENIAN EXCLAMATION MARK
+        // U+055D: "՝" ARMENIAN COMMA
+        // U+055E: "՞" ARMENIAN QUESTION MARK
+        // U+0559: "ՙ" ARMENIAN MODIFIER LETTER LEFT HALF RING
+        // U+055A: "՚" ARMENIAN APOSTROPHE
+        // U+055B: "՛" ARMENIAN EMPHASIS MARK
+        // U+055F: "՟" ARMENIAN ABBREVIATION MARK
+        /* 53 */ "!fixedColumnOrder!8,!,?,\\,,.,\u058A,\u055C,\u055D,\u055E,:,;,@,\u0559,\u055A,\u055B,\u055F",
+        /* 54~ */
+        null, null, null, null, null, null, null, null, null, null, null, null, null, null, null,
+        null, null, null, null, null, null, null, null, null, null, null, null, null, null, null,
+        null, null, null, null, null, null, null, null, null, null, null, null, null, null, null,
+        null,
+        /* ~99 */
+        // U+055C: "՜" ARMENIAN EXCLAMATION MARK
+        // U+00A1: "¡" INVERTED EXCLAMATION MARK
+        /* 100 */ "\u055C,\u00A1",
+        // U+055E: "՞" ARMENIAN QUESTION MARK
+        // U+00BF: "¿" INVERTED QUESTION MARK
+        /* 101 */ "\u055E,\u00BF",
+    };
+
     /* Language is: Icelandic */
     private static final String[] LANGUAGE_is = {
         // U+00E1: "á" LATIN SMALL LETTER A WITH ACUTE
@@ -1899,9 +1929,11 @@
         /* 47 */ "\u201C,\u201D,\u201E",
         /* 48 */ "!text/single_laqm_raqm_rtl",
         /* 49 */ "!text/double_laqm_raqm_rtl",
-        /* 50~ */
-        null, null, null, null,
-        /* ~53 */
+        /* 50 */ null,
+        // U+20AA: "₪" NEW SHEQEL SIGN
+        /* 51 */ "\u20AA",
+        /* 52 */ null,
+        /* 53 */ null,
         // U+2605: "★" BLACK STAR
         /* 54 */ "\u2605",
         /* 55 */ null,
@@ -1920,6 +1952,15 @@
         // U+203A: "›" SINGLE RIGHT-POINTING ANGLE QUOTATION MARK
         /* 59 */ "!fixedColumnOrder!3,\u2039|\u203A,\u2264|\u2265,\u00AB|\u00BB",
         /* 60 */ "!fixedColumnOrder!3,\u203A|\u2039,\u2265|\u2264,\u00BB|\u00AB",
+        /* 61~ */
+        null, null, null, null, null, null, null, null, null, null, null, null, null, null, null,
+        null, null, null, null, null, null, null, null, null, null, null, null, null, null, null,
+        null, null, null, null, null, null, null, null, null, null, null, null, null, null,
+        /* ~104 */
+        /* 105 */ "!",
+        /* 106 */ "!",
+        /* 107 */ "?",
+        /* 108 */ "?",
     };
 
     /* Language ka: Georgian */
@@ -1986,6 +2027,25 @@
         /* 45 */ "\u0410\u0411\u0412",
     };
 
+    /* Language km: Khmer */
+    private static final String[] LANGUAGE_km = {
+        /* 0~ */
+        null, null, null, null, null, null, null, null, null, null, null, null, null, null, null,
+        null, null, null, null, null, null, null, null, null, null, null, null, null, null, null,
+        null, null, null, null, null, null, null, null, null, null, null, null, null, null, null,
+        /* ~44 */
+        // Label for "switch to alphabetic" key.
+        // U+1780: "ក" KHMER LETTER KA
+        // U+1781: "ខ" KHMER LETTER KHA
+        // U+1782: "គ" KHMER LETTER KO
+        /* 45 */ "\u1780\u1781\u1782",
+        /* 46~ */
+        null, null, null, null,
+        /* ~49 */
+        // U+17DB: "៛" KHMER CURRENCY SYMBOL RIEL
+        /* 50 */ "\u17DB,\u00A2,\u00A3,\u20AC,\u00A5,\u20B1",
+    };
+
     /* Language ky: Kirghiz */
     private static final String[] LANGUAGE_ky = {
         /* 0~ */
@@ -2027,6 +2087,25 @@
         /* 45 */ "\u0410\u0411\u0412",
     };
 
+    /* Language lo: Lao */
+    private static final String[] LANGUAGE_lo = {
+        /* 0~ */
+        null, null, null, null, null, null, null, null, null, null, null, null, null, null, null,
+        null, null, null, null, null, null, null, null, null, null, null, null, null, null, null,
+        null, null, null, null, null, null, null, null, null, null, null, null, null, null, null,
+        /* ~44 */
+        // Label for "switch to alphabetic" key.
+        // U+0E81: "ກ" LAO LETTER KO
+        // U+0E82: "ຂ" LAO LETTER KHO SUNG
+        // U+0E84: "ຄ" LAO LETTER KHO TAM
+        /* 45 */ "\u0E81\u0E82\u0E84",
+        /* 46~ */
+        null, null, null, null, null,
+        /* ~50 */
+        // U+20AD: "₭" KIP SIGN
+        /* 51 */ "\u20AD",
+    };
+
     /* Language lt: Lithuanian */
     private static final String[] LANGUAGE_lt = {
         // U+0105: "ą" LATIN SMALL LETTER A WITH OGONEK
@@ -2318,6 +2397,63 @@
         /* 47 */ "!text/double_9qm_rqm",
     };
 
+    /* Language ne: Nepali */
+    private static final String[] LANGUAGE_ne = {
+        /* 0~ */
+        null, null, null, null, null, null, null, null, null, null, null, null, null, null, null,
+        null, null, null, null, null, null, null, null, null, null, null, null, null, null, null,
+        null, null, null, null, null, null, null, null, null, null, null, null, null, null, null,
+        /* ~44 */
+        // Label for "switch to alphabetic" key.
+        // U+0915: "क" DEVANAGARI LETTER KA
+        // U+0916: "ख" DEVANAGARI LETTER KHA
+        // U+0917: "ग" DEVANAGARI LETTER GA
+        /* 45 */ "\u0915\u0916\u0917",
+        /* 46~ */
+        null, null, null, null, null,
+        /* ~50 */
+        // U+0930/U+0941/U+002E "रु." NEPALESE RUPEE SIGN
+        /* 51 */ "\u0930\u0941.",
+        /* 52~ */
+        null, null, null, null, null, null, null, null, null, null, null,
+        /* ~62 */
+        // U+0967: "१" DEVANAGARI DIGIT ONE
+        /* 63 */ "\u0967",
+        // U+0968: "२" DEVANAGARI DIGIT TWO
+        /* 64 */ "\u0968",
+        // U+0969: "३" DEVANAGARI DIGIT THREE
+        /* 65 */ "\u0969",
+        // U+096A: "४" DEVANAGARI DIGIT FOUR
+        /* 66 */ "\u096A",
+        // U+096B: "५" DEVANAGARI DIGIT FIVE
+        /* 67 */ "\u096B",
+        // U+096C: "६" DEVANAGARI DIGIT SIX
+        /* 68 */ "\u096C",
+        // U+096D: "७" DEVANAGARI DIGIT SEVEN
+        /* 69 */ "\u096D",
+        // U+096E: "८" DEVANAGARI DIGIT EIGHT
+        /* 70 */ "\u096E",
+        // U+096F: "९" DEVANAGARI DIGIT NINE
+        /* 71 */ "\u096F",
+        // U+0966: "०" DEVANAGARI DIGIT ZERO
+        /* 72 */ "\u0966",
+        // Label for "switch to symbols" key.
+        /* 73 */ "?\u0967\u0968\u0969",
+        // Label for "switch to symbols with microphone" key. This string shouldn't include the "mic"
+        // part because it'll be appended by the code.
+        /* 74 */ "\u0967\u0968\u0969",
+        /* 75 */ "1",
+        /* 76 */ "2",
+        /* 77 */ "3",
+        /* 78 */ "4",
+        /* 79 */ "5",
+        /* 80 */ "6",
+        /* 81 */ "7",
+        /* 82 */ "8",
+        /* 83 */ "9",
+        /* 84 */ "0",
+    };
+
     /* Language nl: Dutch */
     private static final String[] LANGUAGE_nl = {
         // U+00E1: "á" LATIN SMALL LETTER A WITH ACUTE
@@ -3296,17 +3432,21 @@
         "hi", LANGUAGE_hi, /* Hindi */
         "hr", LANGUAGE_hr, /* Croatian */
         "hu", LANGUAGE_hu, /* Hungarian */
+        "hy", LANGUAGE_hy, /* Armenian */
         "is", LANGUAGE_is, /* Icelandic */
         "it", LANGUAGE_it, /* Italian */
         "iw", LANGUAGE_iw, /* Hebrew */
         "ka", LANGUAGE_ka, /* Georgian */
         "kk", LANGUAGE_kk, /* Kazakh */
+        "km", LANGUAGE_km, /* Khmer */
         "ky", LANGUAGE_ky, /* Kirghiz */
+        "lo", LANGUAGE_lo, /* Lao */
         "lt", LANGUAGE_lt, /* Lithuanian */
         "lv", LANGUAGE_lv, /* Latvian */
         "mk", LANGUAGE_mk, /* Macedonian */
         "mn", LANGUAGE_mn, /* Mongolian */
         "nb", LANGUAGE_nb, /* Norwegian Bokmål */
+        "ne", LANGUAGE_ne, /* Nepali */
         "nl", LANGUAGE_nl, /* Dutch */
         "pl", LANGUAGE_pl, /* Polish */
         "pt", LANGUAGE_pt, /* Portuguese */
diff --git a/java/src/com/android/inputmethod/keyboard/internal/ScrollKeyboardView.java b/java/src/com/android/inputmethod/keyboard/internal/ScrollKeyboardView.java
new file mode 100644
index 0000000..b8ee976
--- /dev/null
+++ b/java/src/com/android/inputmethod/keyboard/internal/ScrollKeyboardView.java
@@ -0,0 +1,212 @@
+/*
+ * Copyright (C) 2013 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.internal;
+
+import android.content.Context;
+import android.util.AttributeSet;
+import android.view.GestureDetector;
+import android.view.MotionEvent;
+import android.widget.ScrollView;
+import android.widget.Scroller;
+
+import com.android.inputmethod.keyboard.Key;
+import com.android.inputmethod.keyboard.KeyDetector;
+import com.android.inputmethod.keyboard.Keyboard;
+import com.android.inputmethod.keyboard.KeyboardView;
+import com.android.inputmethod.latin.R;
+
+/**
+ * This is an extended {@link KeyboardView} class that hosts a scroll keyboard.
+ * Multi-touch unsupported. No {@link PointerTracker}s. No gesture support.
+ */
+// TODO: Implement key popup preview.
+public final class ScrollKeyboardView extends KeyboardView implements
+        ScrollViewWithNotifier.ScrollListener, GestureDetector.OnGestureListener {
+    private static final boolean PAGINATION = false;
+
+    public interface OnKeyClickListener {
+        public void onKeyClick(Key key);
+    }
+
+    private static final OnKeyClickListener EMPTY_LISTENER = new OnKeyClickListener() {
+        @Override
+        public void onKeyClick(final Key key) {}
+    };
+
+    private OnKeyClickListener mListener = EMPTY_LISTENER;
+    private final KeyDetector mKeyDetector = new KeyDetector(0.0f /*keyHysteresisDistance */);
+    private final GestureDetector mGestureDetector;
+
+    private final Scroller mScroller;
+    private ScrollViewWithNotifier mScrollView;
+
+    public ScrollKeyboardView(final Context context, final AttributeSet attrs) {
+        this(context, attrs, R.attr.keyboardViewStyle);
+    }
+
+    public ScrollKeyboardView(final Context context, final AttributeSet attrs, final int defStyle) {
+        super(context, attrs, defStyle);
+        mGestureDetector = new GestureDetector(context, this);
+        mGestureDetector.setIsLongpressEnabled(false /* isLongpressEnabled */);
+        mScroller = new Scroller(context);
+    }
+
+    public void setScrollView(final ScrollViewWithNotifier scrollView) {
+        mScrollView = scrollView;
+        scrollView.setScrollListener(this);
+    }
+
+    private final Runnable mScrollTask = new Runnable() {
+        @Override
+        public void run() {
+            final Scroller scroller = mScroller;
+            final ScrollView scrollView = mScrollView;
+            scroller.computeScrollOffset();
+            scrollView.scrollTo(0, scroller.getCurrY());
+            if (!scroller.isFinished()) {
+                scrollView.post(this);
+            }
+        }
+    };
+
+    // {@link ScrollViewWithNotified#ScrollListener} methods.
+    @Override
+    public void notifyScrollChanged(final int scrollX, final int scrollY, final int oldX,
+            final int oldY) {
+        if (PAGINATION) {
+            mScroller.forceFinished(true /* finished */);
+            mScrollView.removeCallbacks(mScrollTask);
+            final int currentTop = mScrollView.getScrollY();
+            final int pageHeight = getKeyboard().mBaseHeight;
+            final int lastPageNo = currentTop / pageHeight;
+            final int lastPageTop = lastPageNo * pageHeight;
+            final int nextPageNo = lastPageNo + 1;
+            final int nextPageTop = Math.min(nextPageNo * pageHeight, getHeight() - pageHeight);
+            final int scrollTo = (currentTop - lastPageTop) < (nextPageTop - currentTop)
+                    ? lastPageTop : nextPageTop;
+            final int deltaY = scrollTo - currentTop;
+            mScroller.startScroll(0, currentTop, 0, deltaY, 300);
+            mScrollView.post(mScrollTask);
+        }
+    }
+
+    @Override
+    public void notifyOverScrolled(final int scrollX, final int scrollY, final boolean clampedX,
+            final boolean clampedY) {
+        releaseCurrentKey();
+    }
+
+    public void setOnKeyClickListener(final OnKeyClickListener listener) {
+        mListener = listener;
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public void setKeyboard(final Keyboard keyboard) {
+        super.setKeyboard(keyboard);
+        mKeyDetector.setKeyboard(keyboard, 0 /* correctionX */, 0 /* correctionY */);
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public boolean onTouchEvent(final MotionEvent e) {
+        if (mGestureDetector.onTouchEvent(e)) {
+            return true;
+        }
+        final Key key = getKey(e);
+        if (key != null && key != mCurrentKey) {
+            releaseCurrentKey();
+        }
+        return true;
+    }
+
+    // {@link GestureDetector#OnGestureListener} methods.
+    private Key mCurrentKey;
+
+    private Key getKey(final MotionEvent e) {
+        final int index = e.getActionIndex();
+        final int x = (int)e.getX(index);
+        final int y = (int)e.getY(index);
+        return mKeyDetector.detectHitKey(x, y);
+    }
+
+    public void releaseCurrentKey() {
+        final Key currentKey = mCurrentKey;
+        if (currentKey == null) {
+            return;
+        }
+        currentKey.onReleased();
+        invalidateKey(currentKey);
+        mCurrentKey = null;
+    }
+
+    @Override
+    public boolean onDown(final MotionEvent e) {
+        final Key key = getKey(e);
+        releaseCurrentKey();
+        mCurrentKey = key;
+        if (key == null) {
+            return false;
+        }
+        // TODO: May call {@link KeyboardActionListener#onPressKey(int,int,boolean)}.
+        key.onPressed();
+        invalidateKey(key);
+        return false;
+    }
+
+    @Override
+    public void onShowPress(final MotionEvent e) {
+        // User feedback is done at {@link #onDown(MotionEvent)}.
+    }
+
+    @Override
+    public boolean onSingleTapUp(final MotionEvent e) {
+        final Key key = getKey(e);
+        releaseCurrentKey();
+        if (key == null) {
+            return false;
+        }
+        // TODO: May call {@link KeyboardActionListener#onReleaseKey(int,boolean)}.
+        key.onReleased();
+        invalidateKey(key);
+        mListener.onKeyClick(key);
+        return true;
+    }
+
+    @Override
+    public boolean onScroll(final MotionEvent e1, final MotionEvent e2, final float distanceX,
+           final float distanceY) {
+        releaseCurrentKey();
+        return false;
+    }
+
+    @Override
+    public boolean onFling(final MotionEvent e1, final MotionEvent e2, final float velocityX,
+            final float velocityY) {
+        releaseCurrentKey();
+        return false;
+    }
+
+    @Override
+    public void onLongPress(final MotionEvent e) {
+        // Long press detection of {@link #mGestureDetector} is disabled and not used.
+    }
+}
diff --git a/java/src/com/android/inputmethod/keyboard/internal/ScrollViewWithNotifier.java b/java/src/com/android/inputmethod/keyboard/internal/ScrollViewWithNotifier.java
new file mode 100644
index 0000000..d1ccdc7
--- /dev/null
+++ b/java/src/com/android/inputmethod/keyboard/internal/ScrollViewWithNotifier.java
@@ -0,0 +1,66 @@
+/*
+ * Copyright (C) 2013 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.internal;
+
+import android.content.Context;
+import android.util.AttributeSet;
+import android.widget.ScrollView;
+
+/**
+ * This is an extended {@link ScrollView} that can notify
+ * {@link ScrollView#onScrollChanged(int,int,int,int} and
+ * {@link ScrollView#onOverScrolled(int,int,int,int)} to a content view.
+ */
+public class ScrollViewWithNotifier extends ScrollView {
+    private ScrollListener mScrollListener = EMPTY_LISTER;
+
+    public interface ScrollListener {
+        public void notifyScrollChanged(int scrollX, int scrollY, int oldX, int oldY);
+        public void notifyOverScrolled(int scrollX, int scrollY, boolean clampedX,
+                boolean clampedY);
+    }
+
+    private static final ScrollListener EMPTY_LISTER = new ScrollListener() {
+        @Override
+        public void notifyScrollChanged(int scrollX, int scrollY, int oldX, int oldY) {}
+        @Override
+        public void notifyOverScrolled(int scrollX, int scrollY, boolean clampedX,
+                boolean clampedY) {}
+    };
+
+    public ScrollViewWithNotifier(final Context context, final AttributeSet attrs) {
+        super(context, attrs);
+    }
+
+    @Override
+    protected void onScrollChanged(final int scrollX, final int scrollY, final int oldX,
+            final int oldY) {
+        super.onScrollChanged(scrollX, scrollY, oldX, oldY);
+        mScrollListener.notifyScrollChanged(scrollX, scrollY, oldX, oldY);
+    }
+
+    @Override
+    protected void onOverScrolled(final int scrollX, final int scrollY, final boolean clampedX,
+            final boolean clampedY) {
+        super.onOverScrolled(scrollX, scrollY, clampedX, clampedY);
+        mScrollListener.notifyOverScrolled(scrollX, scrollY, clampedX, clampedY);
+    }
+
+    public void setScrollListener(final ScrollListener listener) {
+        mScrollListener = listener;
+    }
+}
diff --git a/java/src/com/android/inputmethod/latin/AbstractDictionaryWriter.java b/java/src/com/android/inputmethod/latin/AbstractDictionaryWriter.java
index ebbcedc..845a9b9 100644
--- a/java/src/com/android/inputmethod/latin/AbstractDictionaryWriter.java
+++ b/java/src/com/android/inputmethod/latin/AbstractDictionaryWriter.java
@@ -19,10 +19,11 @@
 import android.content.Context;
 import android.util.Log;
 
+import com.android.inputmethod.latin.makedict.DictEncoder;
 import com.android.inputmethod.latin.makedict.UnsupportedFormatException;
+import com.android.inputmethod.latin.makedict.Ver3DictEncoder;
 
 import java.io.File;
-import java.io.FileOutputStream;
 import java.io.IOException;
 
 // TODO: Quit extending Dictionary after implementing dynamic binary dictionary.
@@ -42,37 +43,28 @@
     abstract public void addUnigramWord(final String word, final String shortcutTarget,
             final int frequency, final boolean isNotAWord);
 
+    // TODO: Remove lastModifiedTime after making binary dictionary support forgetting curve.
     abstract public void addBigramWords(final String word0, final String word1,
-            final int frequency, final boolean isValid);
+            final int frequency, final boolean isValid,
+            final long lastModifiedTime);
 
     abstract public void removeBigramWords(final String word0, final String word1);
 
-    abstract protected void writeBinaryDictionary(final FileOutputStream out)
+    abstract protected void writeDictionary(final DictEncoder dictEncoder)
             throws IOException, UnsupportedFormatException;
 
     public void write(final String fileName) {
         final String tempFileName = fileName + ".temp";
         final File file = new File(mContext.getFilesDir(), fileName);
         final File tempFile = new File(mContext.getFilesDir(), tempFileName);
-        FileOutputStream out = null;
         try {
-            out = new FileOutputStream(tempFile);
-            writeBinaryDictionary(out);
-            out.flush();
-            out.close();
+            final DictEncoder dictEncoder = new Ver3DictEncoder(tempFile);
+            writeDictionary(dictEncoder);
             tempFile.renameTo(file);
         } catch (IOException e) {
             Log.e(TAG, "IO exception while writing file", e);
         } catch (UnsupportedFormatException e) {
             Log.e(TAG, "Unsupported format", e);
-        } finally {
-            if (out != null) {
-                try {
-                    out.close();
-                } catch (IOException e) {
-                    // ignore
-                }
-            }
         }
     }
 }
diff --git a/java/src/com/android/inputmethod/latin/AudioAndHapticFeedbackManager.java b/java/src/com/android/inputmethod/latin/AudioAndHapticFeedbackManager.java
index 42c5794..54bc295 100644
--- a/java/src/com/android/inputmethod/latin/AudioAndHapticFeedbackManager.java
+++ b/java/src/com/android/inputmethod/latin/AudioAndHapticFeedbackManager.java
@@ -57,10 +57,10 @@
         mVibrator = (Vibrator) context.getSystemService(Context.VIBRATOR_SERVICE);
     }
 
-    public void hapticAndAudioFeedback(final int primaryCode,
+    public void performHapticAndAudioFeedback(final int code,
             final View viewToPerformHapticFeedbackOn) {
-        vibrateInternal(viewToPerformHapticFeedbackOn);
-        playKeyClick(primaryCode);
+        performHapticFeedback(viewToPerformHapticFeedbackOn);
+        performAudioFeedback(code);
     }
 
     public boolean hasVibrator() {
@@ -81,14 +81,14 @@
         return mAudioManager.getRingerMode() == AudioManager.RINGER_MODE_NORMAL;
     }
 
-    private void playKeyClick(final int primaryCode) {
+    public void performAudioFeedback(final int code) {
         // if mAudioManager is null, we can't play a sound anyway, so return
         if (mAudioManager == null) {
             return;
         }
         if (mSoundOn) {
             final int sound;
-            switch (primaryCode) {
+            switch (code) {
             case Constants.CODE_DELETE:
                 sound = AudioManager.FX_KEYPRESS_DELETE;
                 break;
@@ -106,7 +106,7 @@
         }
     }
 
-    private void vibrateInternal(final View viewToPerformHapticFeedbackOn) {
+    public void performHapticFeedback(final View viewToPerformHapticFeedbackOn) {
         if (!mSettingsValues.mVibrateOn) {
             return;
         }
diff --git a/java/src/com/android/inputmethod/latin/BinaryDictionary.java b/java/src/com/android/inputmethod/latin/BinaryDictionary.java
index d181bf6..61ccfcf 100644
--- a/java/src/com/android/inputmethod/latin/BinaryDictionary.java
+++ b/java/src/com/android/inputmethod/latin/BinaryDictionary.java
@@ -19,14 +19,15 @@
 import android.text.TextUtils;
 import android.util.SparseArray;
 
+import com.android.inputmethod.annotations.UsedForTesting;
 import com.android.inputmethod.keyboard.ProximityInfo;
 import com.android.inputmethod.latin.SuggestedWords.SuggestedWordInfo;
-import com.android.inputmethod.latin.settings.AdditionalFeaturesSettingUtils;
 import com.android.inputmethod.latin.settings.NativeSuggestOptions;
 import com.android.inputmethod.latin.utils.CollectionUtils;
 import com.android.inputmethod.latin.utils.JniUtils;
 import com.android.inputmethod.latin.utils.StringUtils;
 
+import java.io.File;
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Locale;
@@ -41,14 +42,20 @@
     private static final int MAX_WORD_LENGTH = Constants.DICTIONARY_MAX_WORD_LENGTH;
     // Must be equal to MAX_RESULTS in native/jni/src/defines.h
     private static final int MAX_RESULTS = 18;
+    // Required space count for auto commit.
+    // TODO: Remove this heuristic.
+    private static final int SPACE_COUNT_FOR_AUTO_COMMIT = 3;
 
     private long mNativeDict;
     private final Locale mLocale;
+    private final long mDictSize;
+    private final String mDictFilePath;
     private final int[] mInputCodePoints = new int[MAX_WORD_LENGTH];
     private final int[] mOutputCodePoints = new int[MAX_WORD_LENGTH * MAX_RESULTS];
     private final int[] mSpaceIndices = new int[MAX_RESULTS];
     private final int[] mOutputScores = new int[MAX_RESULTS];
     private final int[] mOutputTypes = new int[MAX_RESULTS];
+    private final int[] mOutputAutoCommitFirstWordConfidence = new int[MAX_RESULTS];
 
     private final NativeSuggestOptions mNativeSuggestOptions = new NativeSuggestOptions();
 
@@ -63,7 +70,7 @@
             if (traverseSession == null) {
                 traverseSession = mDicTraverseSessions.get(traverseSessionId);
                 if (traverseSession == null) {
-                    traverseSession = new DicTraverseSession(mLocale, mNativeDict);
+                    traverseSession = new DicTraverseSession(mLocale, mNativeDict, mDictSize);
                     mDicTraverseSessions.put(traverseSessionId, traverseSession);
                 }
             }
@@ -86,6 +93,8 @@
             final boolean isUpdatable) {
         super(dictType);
         mLocale = locale;
+        mDictSize = length;
+        mDictFilePath = filename;
         mNativeSuggestOptions.setUseFullEditDistance(useFullEditDistance);
         loadDictionary(filename, offset, length, isUpdatable);
     }
@@ -96,20 +105,26 @@
 
     private static native long openNative(String sourceDir, long dictOffset, long dictSize,
             boolean isUpdatable);
+    private static native void flushNative(long dict, String filePath);
+    private static native boolean needsToRunGCNative(long dict);
+    private static native void flushWithGCNative(long dict, String filePath);
     private static native void closeNative(long dict);
     private static native int getProbabilityNative(long dict, int[] word);
-    private static native boolean isValidBigramNative(long dict, int[] word0, int[] word1);
+    private static native int getBigramProbabilityNative(long dict, int[] word0, int[] word1);
     private static native int getSuggestionsNative(long dict, long proximityInfo,
             long traverseSession, int[] xCoordinates, int[] yCoordinates, int[] times,
             int[] pointerIds, int[] inputCodePoints, int inputSize, int commitPoint,
             int[] suggestOptions, int[] prevWordCodePointArray,
-            int[] outputCodePoints, int[] outputScores, int[] outputIndices, int[] outputTypes);
+            int[] outputCodePoints, int[] outputScores, int[] outputIndices, int[] outputTypes,
+            int[] outputAutoCommitFirstWordConfidence);
     private static native float calcNormalizedScoreNative(int[] before, int[] after, int score);
     private static native int editDistanceNative(int[] before, int[] after);
     private static native void addUnigramWordNative(long dict, int[] word, int probability);
     private static native void addBigramWordsNative(long dict, int[] word0, int[] word1,
             int probability);
     private static native void removeBigramWordsNative(long dict, int[] word0, int[] word1);
+    private static native int calculateProbabilityNative(long dict, int unigramProbability,
+            int bigramProbability);
 
     // TODO: Move native dict into session
     private final void loadDictionary(final String path, final long startOffset,
@@ -120,15 +135,16 @@
     @Override
     public ArrayList<SuggestedWordInfo> getSuggestions(final WordComposer composer,
             final String prevWord, final ProximityInfo proximityInfo,
-            final boolean blockOffensiveWords) {
+            final boolean blockOffensiveWords, final int[] additionalFeaturesOptions) {
         return getSuggestionsWithSessionId(composer, prevWord, proximityInfo, blockOffensiveWords,
-                0 /* sessionId */);
+                additionalFeaturesOptions, 0 /* sessionId */);
     }
 
     @Override
     public ArrayList<SuggestedWordInfo> getSuggestionsWithSessionId(final WordComposer composer,
             final String prevWord, final ProximityInfo proximityInfo,
-            final boolean blockOffensiveWords, final int sessionId) {
+            final boolean blockOffensiveWords, final int[] additionalFeaturesOptions,
+            final int sessionId) {
         if (!isValidDictionary()) return null;
 
         Arrays.fill(mInputCodePoints, Constants.NOT_A_CODE);
@@ -148,15 +164,14 @@
         final InputPointers ips = composer.getInputPointers();
         final int inputSize = isGesture ? ips.getPointerSize() : composerSize;
         mNativeSuggestOptions.setIsGesture(isGesture);
-        mNativeSuggestOptions.setAdditionalFeaturesOptions(
-                AdditionalFeaturesSettingUtils.getAdditionalNativeSuggestOptions());
+        mNativeSuggestOptions.setAdditionalFeaturesOptions(additionalFeaturesOptions);
         // proximityInfo and/or prevWordForBigrams may not be null.
         final int count = getSuggestionsNative(mNativeDict, proximityInfo.getNativeProximityInfo(),
                 getTraverseSession(sessionId).getSession(), ips.getXCoordinates(),
                 ips.getYCoordinates(), ips.getTimes(), ips.getPointerIds(), mInputCodePoints,
                 inputSize, 0 /* commitPoint */, mNativeSuggestOptions.getOptions(),
                 prevWordCodePointArray, mOutputCodePoints, mOutputScores, mSpaceIndices,
-                mOutputTypes);
+                mOutputTypes, mOutputAutoCommitFirstWordConfidence);
         final ArrayList<SuggestedWordInfo> suggestions = CollectionUtils.newArrayList();
         for (int j = 0; j < count; ++j) {
             final int start = j * MAX_WORD_LENGTH;
@@ -179,7 +194,9 @@
                 // TODO: check that all users of the `kind' parameter are ready to accept
                 // flags too and pass mOutputTypes[j] instead of kind
                 suggestions.add(new SuggestedWordInfo(new String(mOutputCodePoints, start, len),
-                        score, kind, mDictType));
+                        score, kind, this /* sourceDict */,
+                        mSpaceIndices[j] /* indexOfTouchPointOfSecondWord */,
+                        mOutputAutoCommitFirstWordConfidence[0]));
             }
         }
         return suggestions;
@@ -205,12 +222,12 @@
 
     @Override
     public boolean isValidWord(final String word) {
-        return getFrequency(word) >= 0;
+        return getFrequency(word) != NOT_A_PROBABILITY;
     }
 
     @Override
     public int getFrequency(final String word) {
-        if (word == null) return -1;
+        if (word == null) return NOT_A_PROBABILITY;
         int[] codePoints = StringUtils.toCodePointArray(word);
         return getProbabilityNative(mNativeDict, codePoints);
     }
@@ -218,10 +235,20 @@
     // TODO: Add a batch process version (isValidBigramMultiple?) to avoid excessive numbers of jni
     // calls when checking for changes in an entire dictionary.
     public boolean isValidBigram(final String word0, final String word1) {
-        if (TextUtils.isEmpty(word0) || TextUtils.isEmpty(word1)) return false;
+        return getBigramProbability(word0, word1) != NOT_A_PROBABILITY;
+    }
+
+    public int getBigramProbability(final String word0, final String word1) {
+        if (TextUtils.isEmpty(word0) || TextUtils.isEmpty(word1)) return NOT_A_PROBABILITY;
         final int[] codePoints0 = StringUtils.toCodePointArray(word0);
         final int[] codePoints1 = StringUtils.toCodePointArray(word1);
-        return isValidBigramNative(mNativeDict, codePoints0, codePoints1);
+        return getBigramProbabilityNative(mNativeDict, codePoints0, codePoints1);
+    }
+
+    private void runGCIfRequired() {
+        if (needsToRunGCNative(mNativeDict)) {
+            flushWithGC();
+        }
     }
 
     // Add a unigram entry to binary dictionary in native code.
@@ -229,6 +256,7 @@
         if (TextUtils.isEmpty(word)) {
             return;
         }
+        runGCIfRequired();
         final int[] codePoints = StringUtils.toCodePointArray(word);
         addUnigramWordNative(mNativeDict, codePoints, probability);
     }
@@ -238,6 +266,7 @@
         if (TextUtils.isEmpty(word0) || TextUtils.isEmpty(word1)) {
             return;
         }
+        runGCIfRequired();
         final int[] codePoints0 = StringUtils.toCodePointArray(word0);
         final int[] codePoints1 = StringUtils.toCodePointArray(word1);
         addBigramWordsNative(mNativeDict, codePoints0, codePoints1, probability);
@@ -248,11 +277,57 @@
         if (TextUtils.isEmpty(word0) || TextUtils.isEmpty(word1)) {
             return;
         }
+        runGCIfRequired();
         final int[] codePoints0 = StringUtils.toCodePointArray(word0);
         final int[] codePoints1 = StringUtils.toCodePointArray(word1);
         removeBigramWordsNative(mNativeDict, codePoints0, codePoints1);
     }
 
+    public void flush() {
+        if (!isValidDictionary()) return;
+        flushNative(mNativeDict, mDictFilePath);
+        closeNative(mNativeDict);
+        final File dictFile = new File(mDictFilePath);
+        mNativeDict = openNative(dictFile.getAbsolutePath(), 0 /* startOffset */,
+                dictFile.length(), true /* isUpdatable */);
+    }
+
+    public void flushWithGC() {
+        if (!isValidDictionary()) return;
+        flushWithGCNative(mNativeDict, mDictFilePath);
+        closeNative(mNativeDict);
+        final File dictFile = new File(mDictFilePath);
+        mNativeDict = openNative(dictFile.getAbsolutePath(), 0 /* startOffset */,
+                dictFile.length(), true /* isUpdatable */);
+    }
+
+    public boolean needsToRunGC() {
+        if (!isValidDictionary()) return false;
+        return needsToRunGCNative(mNativeDict);
+    }
+
+    @UsedForTesting
+    public int calculateProbability(final int unigramProbability, final int bigramProbability) {
+        if (!isValidDictionary()) return NOT_A_PROBABILITY;
+        return calculateProbabilityNative(mNativeDict, unigramProbability, bigramProbability);
+    }
+
+    @Override
+    public boolean shouldAutoCommit(final SuggestedWordInfo candidate) {
+        // TODO: actually use the confidence rather than use this completely broken heuristic
+        final String word = candidate.mWord;
+        final int length = word.length();
+        int remainingSpaces = SPACE_COUNT_FOR_AUTO_COMMIT;
+        for (int i = 0; i < length; ++i) {
+            // This is okay because no low-surrogate and no high-surrogate can ever match the
+            // space character, so we don't need to take care of iterating on code points.
+            if (Constants.CODE_SPACE == word.charAt(i)) {
+                if (0 >= --remainingSpaces) return true;
+            }
+        }
+        return false;
+    }
+
     @Override
     public void close() {
         synchronized (mDicTraverseSessions) {
diff --git a/java/src/com/android/inputmethod/latin/BinaryDictionaryGetter.java b/java/src/com/android/inputmethod/latin/BinaryDictionaryGetter.java
index fa301b5..181ad17 100644
--- a/java/src/com/android/inputmethod/latin/BinaryDictionaryGetter.java
+++ b/java/src/com/android/inputmethod/latin/BinaryDictionaryGetter.java
@@ -21,17 +21,17 @@
 import android.content.res.AssetFileDescriptor;
 import android.util.Log;
 
-import com.android.inputmethod.latin.makedict.BinaryDictInputOutput;
+import com.android.inputmethod.latin.makedict.DictDecoder;
 import com.android.inputmethod.latin.makedict.FormatSpec;
+import com.android.inputmethod.latin.makedict.FormatSpec.FileHeader;
+import com.android.inputmethod.latin.makedict.UnsupportedFormatException;
 import com.android.inputmethod.latin.utils.CollectionUtils;
 import com.android.inputmethod.latin.utils.DictionaryInfoUtils;
 import com.android.inputmethod.latin.utils.LocaleUtils;
 
 import java.io.File;
-import java.io.FileInputStream;
 import java.io.IOException;
 import java.nio.BufferUnderflowException;
-import java.nio.channels.FileChannel;
 import java.util.ArrayList;
 import java.util.HashMap;
 import java.util.Locale;
@@ -227,23 +227,12 @@
     // those do not include whitelist entries, the new code with an old version of the dictionary
     // would lose whitelist functionality.
     private static boolean hackCanUseDictionaryFile(final Locale locale, final File f) {
-        FileInputStream inStream = null;
         try {
             // Read the version of the file
-            inStream = new FileInputStream(f);
-            final BinaryDictInputOutput.ByteBufferWrapper buffer =
-                    new BinaryDictInputOutput.ByteBufferWrapper(inStream.getChannel().map(
-                            FileChannel.MapMode.READ_ONLY, 0, f.length()));
-            final int magic = buffer.readInt();
-            if (magic != FormatSpec.VERSION_2_MAGIC_NUMBER) {
-                return false;
-            }
-            final int formatVersion = buffer.readInt();
-            final int headerSize = buffer.readInt();
-            final HashMap<String, String> options = CollectionUtils.newHashMap();
-            BinaryDictInputOutput.populateOptions(buffer, headerSize, options);
+            final DictDecoder dictDecoder = FormatSpec.getDictDecoder(f);
+            final FileHeader header = dictDecoder.readHeader();
 
-            final String version = options.get(VERSION_KEY);
+            final String version = header.mDictionaryOptions.mAttributes.get(VERSION_KEY);
             if (null == version) {
                 // No version in the options : the format is unexpected
                 return false;
@@ -259,14 +248,8 @@
             return false;
         } catch (BufferUnderflowException e) {
             return false;
-        } finally {
-            if (inStream != null) {
-                try {
-                    inStream.close();
-                } catch (IOException e) {
-                    // do nothing
-                }
-            }
+        } catch (UnsupportedFormatException e) {
+            return false;
         }
     }
 
@@ -303,7 +286,8 @@
             }
             if (!dictPackSettings.isWordListActive(wordListId)) continue;
             if (canUse) {
-                fileList.add(AssetFileAddress.makeFromFileName(f.getPath()));
+                final AssetFileAddress afa = AssetFileAddress.makeFromFileName(f.getPath());
+                if (null != afa) fileList.add(afa);
             } else {
                 Log.e(TAG, "Found a cached dictionary file but cannot read or use it");
             }
diff --git a/java/src/com/android/inputmethod/latin/Constants.java b/java/src/com/android/inputmethod/latin/Constants.java
index 8aec03f..c4f9601 100644
--- a/java/src/com/android/inputmethod/latin/Constants.java
+++ b/java/src/com/android/inputmethod/latin/Constants.java
@@ -76,6 +76,11 @@
             public static final String ASCII_CAPABLE = "AsciiCapable";
 
             /**
+             * The subtype extra value used to indicate that the subtype keyboard layout is capable
+             * for typing EMOJI characters.
+             */
+            public static final String EMOJI_CAPABLE = "EmojiCapable";
+            /**
              * The subtype extra value used to indicate that the subtype require network connection
              * to work.
              */
@@ -133,6 +138,9 @@
     public static final int SPELL_CHECKER_COORDINATE = -3;
     public static final int EXTERNAL_KEYBOARD_COORDINATE = -4;
 
+    // A hint on how many characters to cache from the TextView. A good value of this is given by
+    // how many characters we need to be able to almost always find the caps mode.
+    public static final int EDITOR_CONTENTS_CACHE_SIZE = 1024;
 
     // Must be equal to MAX_WORD_LENGTH in native/jni/src/defines.h
     public static final int DICTIONARY_MAX_WORD_LENGTH = 48;
@@ -157,6 +165,7 @@
     public static final int CODE_TAB = '\t';
     public static final int CODE_SPACE = ' ';
     public static final int CODE_PERIOD = '.';
+    public static final int CODE_ARMENIAN_PERIOD = 0x0589;
     public static final int CODE_DASH = '-';
     public static final int CODE_SINGLE_QUOTE = '\'';
     public static final int CODE_DOUBLE_QUOTE = '"';
@@ -164,9 +173,7 @@
     public static final int CODE_EXCLAMATION_MARK = '!';
     public static final int CODE_SLASH = '/';
     public static final int CODE_COMMERCIAL_AT = '@';
-    // TODO: Check how this should work for right-to-left languages. It seems to stand
-    // that for rtl languages, a closing parenthesis is a left parenthesis. Is this
-    // managed by the font? Or is it a different char?
+    public static final int CODE_PLUS = '+';
     public static final int CODE_CLOSING_PARENTHESIS = ')';
     public static final int CODE_CLOSING_SQUARE_BRACKET = ']';
     public static final int CODE_CLOSING_CURLY_BRACKET = '}';
@@ -220,7 +227,10 @@
         }
     }
 
+    public static final int MAX_INT_BIT_COUNT = 32;
+
     private Constants() {
         // This utility class is not publicly instantiable.
     }
+
 }
diff --git a/java/src/com/android/inputmethod/latin/ContactsBinaryDictionary.java b/java/src/com/android/inputmethod/latin/ContactsBinaryDictionary.java
index c99d0e2..ffeb927 100644
--- a/java/src/com/android/inputmethod/latin/ContactsBinaryDictionary.java
+++ b/java/src/com/android/inputmethod/latin/ContactsBinaryDictionary.java
@@ -22,6 +22,7 @@
 import android.content.Context;
 import android.database.ContentObserver;
 import android.database.Cursor;
+import android.database.sqlite.SQLiteException;
 import android.net.Uri;
 import android.os.SystemClock;
 import android.provider.BaseColumns;
@@ -70,7 +71,8 @@
     private final boolean mUseFirstLastBigrams;
 
     public ContactsBinaryDictionary(final Context context, final Locale locale) {
-        super(context, getFilenameWithLocale(NAME, locale.toString()), Dictionary.TYPE_CONTACTS);
+        super(context, getFilenameWithLocale(NAME, locale.toString()), Dictionary.TYPE_CONTACTS,
+                false /* isUpdatable */);
         mLocale = locale;
         mUseFirstLastBigrams = useFirstLastBigramsForLocale(locale);
         registerObserver(context);
@@ -144,8 +146,10 @@
                     cursor.close();
                 }
             }
-        } catch (IllegalStateException e) {
-            Log.e(TAG, "Contacts DB is having problems");
+        } catch (final SQLiteException e) {
+            Log.e(TAG, "SQLiteException in the remote Contacts process.", e);
+        } catch (final IllegalStateException e) {
+            Log.e(TAG, "Contacts DB is having problems", e);
         }
     }
 
@@ -172,14 +176,18 @@
     private int getContactCount() {
         // TODO: consider switching to a rawQuery("select count(*)...") on the database if
         // performance is a bottleneck.
-        final Cursor cursor = mContext.getContentResolver().query(
-                Contacts.CONTENT_URI, PROJECTION_ID_ONLY, null, null, null);
-        if (cursor != null) {
-            try {
-                return cursor.getCount();
-            } finally {
-                cursor.close();
+        try {
+            final Cursor cursor = mContext.getContentResolver().query(
+                    Contacts.CONTENT_URI, PROJECTION_ID_ONLY, null, null, null);
+            if (cursor != null) {
+                try {
+                    return cursor.getCount();
+                } finally {
+                    cursor.close();
+                }
             }
+        } catch (final SQLiteException e) {
+            Log.e(TAG, "SQLiteException in the remote Contacts process.", e);
         }
         return 0;
     }
@@ -208,7 +216,8 @@
                             false /* isNotAWord */);
                     if (!TextUtils.isEmpty(prevWord)) {
                         if (mUseFirstLastBigrams) {
-                            super.setBigram(prevWord, word, FREQUENCY_FOR_CONTACTS_BIGRAM);
+                            super.addBigram(prevWord, word, FREQUENCY_FOR_CONTACTS_BIGRAM,
+                                    0 /* lastModifiedTime */);
                         }
                     }
                     prevWord = word;
diff --git a/java/src/com/android/inputmethod/latin/DicTraverseSession.java b/java/src/com/android/inputmethod/latin/DicTraverseSession.java
index 45b2813..8d295ad 100644
--- a/java/src/com/android/inputmethod/latin/DicTraverseSession.java
+++ b/java/src/com/android/inputmethod/latin/DicTraverseSession.java
@@ -25,16 +25,16 @@
         JniUtils.loadNativeLibrary();
     }
 
-    private static native long setDicTraverseSessionNative(String locale);
+    private static native long setDicTraverseSessionNative(String locale, long dictSize);
     private static native void initDicTraverseSessionNative(long nativeDicTraverseSession,
             long dictionary, int[] previousWord, int previousWordLength);
     private static native void releaseDicTraverseSessionNative(long nativeDicTraverseSession);
 
     private long mNativeDicTraverseSession;
 
-    public DicTraverseSession(Locale locale, long dictionary) {
+    public DicTraverseSession(Locale locale, long dictionary, long dictSize) {
         mNativeDicTraverseSession = createNativeDicTraverseSession(
-                locale != null ? locale.toString() : "");
+                locale != null ? locale.toString() : "", dictSize);
         initSession(dictionary);
     }
 
@@ -51,8 +51,8 @@
                 mNativeDicTraverseSession, dictionary, previousWord, previousWordLength);
     }
 
-    private final long createNativeDicTraverseSession(String locale) {
-        return setDicTraverseSessionNative(locale);
+    private final long createNativeDicTraverseSession(String locale, long dictSize) {
+        return setDicTraverseSessionNative(locale, dictSize);
     }
 
     private void closeInternal() {
diff --git a/java/src/com/android/inputmethod/latin/Dictionary.java b/java/src/com/android/inputmethod/latin/Dictionary.java
index 7c3e4a7..fa79f5a 100644
--- a/java/src/com/android/inputmethod/latin/Dictionary.java
+++ b/java/src/com/android/inputmethod/latin/Dictionary.java
@@ -28,9 +28,26 @@
 public abstract class Dictionary {
     public static final int NOT_A_PROBABILITY = -1;
 
+    // The following types do not actually come from real dictionary instances, so we create
+    // corresponding instances.
     public static final String TYPE_USER_TYPED = "user_typed";
+    public static final Dictionary DICTIONARY_USER_TYPED = new PhonyDictionary(TYPE_USER_TYPED);
+
     public static final String TYPE_APPLICATION_DEFINED = "application_defined";
+    public static final Dictionary DICTIONARY_APPLICATION_DEFINED =
+            new PhonyDictionary(TYPE_APPLICATION_DEFINED);
+
     public static final String TYPE_HARDCODED = "hardcoded"; // punctuation signs and such
+    public static final Dictionary DICTIONARY_HARDCODED =
+            new PhonyDictionary(TYPE_HARDCODED);
+
+    // Spawned by resuming suggestions. Comes from a span that was in the TextView.
+    public static final String TYPE_RESUMED = "resumed";
+    public static final Dictionary DICTIONARY_RESUMED =
+            new PhonyDictionary(TYPE_RESUMED);
+
+    // The following types of dictionary have actual functional instances. We don't need final
+    // phony dictionary instances for them.
     public static final String TYPE_MAIN = "main";
     public static final String TYPE_CONTACTS = "contacts";
     // User dictionary, the system-managed one.
@@ -42,9 +59,7 @@
     // Personalization prediction dictionary internal to LatinIME's Java code.
     public static final String TYPE_PERSONALIZATION_PREDICTION_IN_JAVA =
             "personalization_prediction_in_java";
-    // Spawned by resuming suggestions. Comes from a span that was in the TextView.
-    public static final String TYPE_RESUMED = "resumed";
-    protected final String mDictType;
+    public final String mDictType;
 
     public Dictionary(final String dictType) {
         mDictType = dictType;
@@ -57,20 +72,23 @@
      * @param prevWord the previous word, or null if none
      * @param proximityInfo the object for key proximity. May be ignored by some implementations.
      * @param blockOffensiveWords whether to block potentially offensive words
+     * @param additionalFeaturesOptions options about additional features used for the suggestion.
      * @return the list of suggestions (possibly null if none)
      */
     // TODO: pass more context than just the previous word, to enable better suggestions (n-gram
     // and more)
     abstract public ArrayList<SuggestedWordInfo> getSuggestions(final WordComposer composer,
             final String prevWord, final ProximityInfo proximityInfo,
-            final boolean blockOffensiveWords);
+            final boolean blockOffensiveWords, final int[] additionalFeaturesOptions);
 
     // The default implementation of this method ignores sessionId.
     // Subclasses that want to use sessionId need to override this method.
     public ArrayList<SuggestedWordInfo> getSuggestionsWithSessionId(final WordComposer composer,
             final String prevWord, final ProximityInfo proximityInfo,
-            final boolean blockOffensiveWords, final int sessionId) {
-        return getSuggestions(composer, prevWord, proximityInfo, blockOffensiveWords);
+            final boolean blockOffensiveWords, final int[] additionalFeaturesOptions,
+            final int sessionId) {
+        return getSuggestions(composer, prevWord, proximityInfo, blockOffensiveWords,
+                additionalFeaturesOptions);
     }
 
     /**
@@ -114,8 +132,43 @@
     /**
      * Subclasses may override to indicate that this Dictionary is not yet properly initialized.
      */
-
     public boolean isInitialized() {
         return true;
     }
+
+    /**
+     * Whether we think this suggestion should trigger an auto-commit. prevWord is the word
+     * before the suggestion, so that we can use n-gram frequencies.
+     * @param candidate The candidate suggestion, in whole (not only the first part).
+     * @return whether we should auto-commit or not.
+     */
+    public boolean shouldAutoCommit(final SuggestedWordInfo candidate) {
+        // If we don't have support for auto-commit, or if we don't know, we return false to
+        // avoid auto-committing stuff. Implementations of the Dictionary class that know to
+        // determine whether we should auto-commit will override this.
+        return false;
+    }
+
+    /**
+     * Not a true dictionary. A placeholder used to indicate suggestions that don't come from any
+     * real dictionary.
+     */
+    private static class PhonyDictionary extends Dictionary {
+        // This class is not publicly instantiable.
+        private PhonyDictionary(final String type) {
+            super(type);
+        }
+
+        @Override
+        public ArrayList<SuggestedWordInfo> getSuggestions(final WordComposer composer,
+                final String prevWord, final ProximityInfo proximityInfo,
+                final boolean blockOffensiveWords, final int[] additionalFeaturesOptions) {
+            return null;
+        }
+
+        @Override
+        public boolean isValidWord(String word) {
+            return false;
+        }
+    }
 }
diff --git a/java/src/com/android/inputmethod/latin/DictionaryCollection.java b/java/src/com/android/inputmethod/latin/DictionaryCollection.java
index d05bb1e..bf07514 100644
--- a/java/src/com/android/inputmethod/latin/DictionaryCollection.java
+++ b/java/src/com/android/inputmethod/latin/DictionaryCollection.java
@@ -58,18 +58,18 @@
     @Override
     public ArrayList<SuggestedWordInfo> getSuggestions(final WordComposer composer,
             final String prevWord, final ProximityInfo proximityInfo,
-            final boolean blockOffensiveWords) {
+            final boolean blockOffensiveWords, final int[] additionalFeaturesOptions) {
         final CopyOnWriteArrayList<Dictionary> dictionaries = mDictionaries;
         if (dictionaries.isEmpty()) return null;
         // To avoid creating unnecessary objects, we get the list out of the first
         // dictionary and add the rest to it if not null, hence the get(0)
         ArrayList<SuggestedWordInfo> suggestions = dictionaries.get(0).getSuggestions(composer,
-                prevWord, proximityInfo, blockOffensiveWords);
+                prevWord, proximityInfo, blockOffensiveWords, additionalFeaturesOptions);
         if (null == suggestions) suggestions = CollectionUtils.newArrayList();
         final int length = dictionaries.size();
         for (int i = 1; i < length; ++ i) {
             final ArrayList<SuggestedWordInfo> sugg = dictionaries.get(i).getSuggestions(composer,
-                    prevWord, proximityInfo, blockOffensiveWords);
+                    prevWord, proximityInfo, blockOffensiveWords, additionalFeaturesOptions);
             if (null != sugg) suggestions.addAll(sugg);
         }
         return suggestions;
diff --git a/java/src/com/android/inputmethod/latin/DictionaryWriter.java b/java/src/com/android/inputmethod/latin/DictionaryWriter.java
index 47151bf..5a453dd 100644
--- a/java/src/com/android/inputmethod/latin/DictionaryWriter.java
+++ b/java/src/com/android/inputmethod/latin/DictionaryWriter.java
@@ -20,15 +20,14 @@
 
 import com.android.inputmethod.keyboard.ProximityInfo;
 import com.android.inputmethod.latin.SuggestedWords.SuggestedWordInfo;
-import com.android.inputmethod.latin.makedict.BinaryDictInputOutput;
+import com.android.inputmethod.latin.makedict.DictEncoder;
 import com.android.inputmethod.latin.makedict.FormatSpec;
 import com.android.inputmethod.latin.makedict.FusionDictionary;
-import com.android.inputmethod.latin.makedict.FusionDictionary.Node;
+import com.android.inputmethod.latin.makedict.FusionDictionary.PtNodeArray;
 import com.android.inputmethod.latin.makedict.FusionDictionary.WeightedString;
 import com.android.inputmethod.latin.makedict.UnsupportedFormatException;
 import com.android.inputmethod.latin.utils.CollectionUtils;
 
-import java.io.FileOutputStream;
 import java.io.IOException;
 import java.util.ArrayList;
 import java.util.HashMap;
@@ -51,7 +50,7 @@
     @Override
     public void clear() {
         final HashMap<String, String> attributes = CollectionUtils.newHashMap();
-        mFusionDictionary = new FusionDictionary(new Node(),
+        mFusionDictionary = new FusionDictionary(new PtNodeArray(),
                 new FusionDictionary.DictionaryOptions(attributes, false, false));
     }
 
@@ -75,7 +74,7 @@
 
     @Override
     public void addBigramWords(final String word0, final String word1, final int frequency,
-            final boolean isValid) {
+            final boolean isValid, final long lastModifiedTime) {
         mFusionDictionary.setBigram(word0, word1, frequency);
     }
 
@@ -85,15 +84,15 @@
     }
 
     @Override
-    protected void writeBinaryDictionary(final FileOutputStream out)
+    protected void writeDictionary(final DictEncoder dictEncoder)
             throws IOException, UnsupportedFormatException {
-        BinaryDictInputOutput.writeDictionaryBinary(out, mFusionDictionary, FORMAT_OPTIONS);
+        dictEncoder.writeDictionary(mFusionDictionary, FORMAT_OPTIONS);
     }
 
     @Override
     public ArrayList<SuggestedWordInfo> getSuggestions(final WordComposer composer,
             final String prevWord, final ProximityInfo proximityInfo,
-            boolean blockOffensiveWords) {
+            boolean blockOffensiveWords, final int[] additionalFeaturesOptions) {
         // This class doesn't support suggestion.
         return null;
     }
diff --git a/java/src/com/android/inputmethod/latin/ExpandableBinaryDictionary.java b/java/src/com/android/inputmethod/latin/ExpandableBinaryDictionary.java
index 3f11391..0774ce2 100644
--- a/java/src/com/android/inputmethod/latin/ExpandableBinaryDictionary.java
+++ b/java/src/com/android/inputmethod/latin/ExpandableBinaryDictionary.java
@@ -20,14 +20,26 @@
 import android.os.SystemClock;
 import android.util.Log;
 
+import com.android.inputmethod.annotations.UsedForTesting;
 import com.android.inputmethod.keyboard.ProximityInfo;
+import com.android.inputmethod.latin.makedict.DictEncoder;
+import com.android.inputmethod.latin.makedict.FormatSpec;
+import com.android.inputmethod.latin.makedict.FusionDictionary;
+import com.android.inputmethod.latin.makedict.FusionDictionary.PtNodeArray;
+import com.android.inputmethod.latin.makedict.UnsupportedFormatException;
+import com.android.inputmethod.latin.makedict.Ver3DictEncoder;
+import com.android.inputmethod.latin.personalization.DynamicPersonalizationDictionaryWriter;
 import com.android.inputmethod.latin.SuggestedWords.SuggestedWordInfo;
+import com.android.inputmethod.latin.utils.AsyncResultHolder;
 import com.android.inputmethod.latin.utils.CollectionUtils;
+import com.android.inputmethod.latin.utils.PrioritizedSerialExecutor;
 
 import java.io.File;
+import java.io.IOException;
 import java.util.ArrayList;
 import java.util.HashMap;
-import java.util.concurrent.locks.ReentrantReadWriteLock;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.atomic.AtomicReference;
 
 /**
  * Abstract base class for an expandable dictionary that can be created and updated dynamically
@@ -45,19 +57,30 @@
     /** Whether to print debug output to log */
     private static boolean DEBUG = false;
 
+    // TODO: Remove.
+    /** Whether to call binary dictionary dynamically updating methods. */
+    public static boolean ENABLE_BINARY_DICTIONARY_DYNAMIC_UPDATE = true;
+
+    private static final int TIMEOUT_FOR_READ_OPS_IN_MILLISECONDS = 100;
+
     /**
      * The maximum length of a word in this dictionary.
      */
     protected static final int MAX_WORD_LENGTH = Constants.DICTIONARY_MAX_WORD_LENGTH;
 
+    private static final FormatSpec.FormatOptions FORMAT_OPTIONS =
+            new FormatSpec.FormatOptions(3 /* version */, true /* supportsDynamicUpdate */);
+
     /**
-     * A static map of locks, each of which controls access to a single binary dictionary file. They
-     * ensure that only one instance can update the same dictionary at the same time. The key for
-     * this map is the filename and the value is the shared dictionary controller associated with
-     * that filename.
+     * A static map of time recorders, each of which records the time of accesses to a single binary
+     * dictionary file. The key for this map is the filename and the value is the shared dictionary
+     * time recorder associated with that filename.
      */
-    private static final HashMap<String, DictionaryController> sSharedDictionaryControllers =
-            CollectionUtils.newHashMap();
+    private static volatile ConcurrentHashMap<String, DictionaryTimeRecorder>
+            sFilenameDictionaryTimeRecorderMap = CollectionUtils.newConcurrentHashMap();
+
+    private static volatile ConcurrentHashMap<String, PrioritizedSerialExecutor>
+            sFilenameExecutorMap = CollectionUtils.newConcurrentHashMap();
 
     /** The application context. */
     protected final Context mContext;
@@ -68,21 +91,34 @@
      */
     private BinaryDictionary mBinaryDictionary;
 
+    // TODO: Remove and handle dictionaries in native code.
     /** The in-memory dictionary used to generate the binary dictionary. */
-    private AbstractDictionaryWriter mDictionaryWriter;
+    protected AbstractDictionaryWriter mDictionaryWriter;
 
     /**
      * The name of this dictionary, used as the filename for storing the binary dictionary. Multiple
      * dictionary instances with the same filename is supported, with access controlled by
-     * DictionaryController.
+     * DictionaryTimeRecorder.
      */
     private final String mFilename;
 
-    /** Controls access to the shared binary dictionary file across multiple instances. */
-    private final DictionaryController mSharedDictionaryController;
+    /** Whether to support dynamically updating the dictionary */
+    private final boolean mIsUpdatable;
 
-    /** Controls access to the local binary dictionary for this instance. */
-    private final DictionaryController mLocalDictionaryController = new DictionaryController();
+    // TODO: remove, once dynamic operations is serialized
+    /** Records access to the shared binary dictionary file across multiple instances. */
+    private final DictionaryTimeRecorder mFilenameDictionaryTimeRecorder;
+
+    // TODO: remove, once dynamic operations is serialized
+    /** Records access to the local binary dictionary for this instance. */
+    private final DictionaryTimeRecorder mPerInstanceDictionaryTimeRecorder =
+            new DictionaryTimeRecorder();
+
+    /* A extension for a binary dictionary file. */
+    public static final String DICT_FILE_EXTENSION = ".dict";
+
+    private final AtomicReference<Runnable> mUnfinishedFlushingTask =
+            new AtomicReference<Runnable>();
 
     /**
      * Abstract method for loading the unigrams and bigrams of a given dictionary in a background
@@ -98,16 +134,45 @@
     protected abstract boolean hasContentChanged();
 
     /**
-     * Gets the shared dictionary controller for the given filename.
+     * Gets the dictionary time recorder for the given filename.
      */
-    private static synchronized DictionaryController getSharedDictionaryController(
+    private static DictionaryTimeRecorder getDictionaryTimeRecorder(
             String filename) {
-        DictionaryController controller = sSharedDictionaryControllers.get(filename);
-        if (controller == null) {
-            controller = new DictionaryController();
-            sSharedDictionaryControllers.put(filename, controller);
+        DictionaryTimeRecorder recorder = sFilenameDictionaryTimeRecorderMap.get(filename);
+        if (recorder == null) {
+            synchronized(sFilenameDictionaryTimeRecorderMap) {
+                recorder = new DictionaryTimeRecorder();
+                sFilenameDictionaryTimeRecorderMap.put(filename, recorder);
+            }
         }
-        return controller;
+        return recorder;
+    }
+
+    /**
+     * Gets the executor for the given filename.
+     */
+    private static PrioritizedSerialExecutor getExecutor(final String filename) {
+        PrioritizedSerialExecutor executor = sFilenameExecutorMap.get(filename);
+        if (executor == null) {
+            synchronized(sFilenameExecutorMap) {
+                executor = new PrioritizedSerialExecutor();
+                sFilenameExecutorMap.put(filename, executor);
+            }
+        }
+        return executor;
+    }
+
+    private static AbstractDictionaryWriter getDictionaryWriter(final Context context,
+            final String dictType, final boolean isDynamicPersonalizationDictionary) {
+        if (isDynamicPersonalizationDictionary) {
+            if (ENABLE_BINARY_DICTIONARY_DYNAMIC_UPDATE) {
+                return null;
+            } else {
+                return new DynamicPersonalizationDictionaryWriter(context, dictType);
+            }
+        } else {
+            return new DictionaryWriter(context, dictType);
+        }
     }
 
     /**
@@ -117,19 +182,23 @@
      * @param filename The filename for this binary dictionary. Multiple dictionaries with the same
      *        filename is supported.
      * @param dictType the dictionary type, as a human-readable string
+     * @param isUpdatable whether to support dynamically updating the dictionary. Please note that
+     *        dynamic dictionary has negative effects on memory space and computation time.
      */
-    public ExpandableBinaryDictionary(
-            final Context context, final String filename, final String dictType) {
+    public ExpandableBinaryDictionary(final Context context, final String filename,
+            final String dictType, final boolean isUpdatable) {
         super(dictType);
         mFilename = filename;
         mContext = context;
+        mIsUpdatable = isUpdatable;
         mBinaryDictionary = null;
-        mSharedDictionaryController = getSharedDictionaryController(filename);
-        mDictionaryWriter = new DictionaryWriter(context, dictType);
+        mFilenameDictionaryTimeRecorder = getDictionaryTimeRecorder(filename);
+        // Currently, only dynamic personalization dictionary is updatable.
+        mDictionaryWriter = getDictionaryWriter(context, dictType, isUpdatable);
     }
 
     protected static String getFilenameWithLocale(final String name, final String localeStr) {
-        return name + "." + localeStr + ".dict";
+        return name + "." + localeStr + DICT_FILE_EXTENSION;
     }
 
     /**
@@ -137,17 +206,56 @@
      */
     @Override
     public void close() {
-        // Ensure that no other threads are accessing the local binary dictionary.
-        mLocalDictionaryController.writeLock().lock();
-        try {
-            if (mBinaryDictionary != null) {
-                mBinaryDictionary.close();
-                mBinaryDictionary = null;
+        getExecutor(mFilename).execute(new Runnable() {
+            @Override
+            public void run() {
+                if (mBinaryDictionary!= null) {
+                    mBinaryDictionary.close();
+                    mBinaryDictionary = null;
+                }
+                if (mDictionaryWriter != null) {
+                    mDictionaryWriter.close();
+                }
             }
-            mDictionaryWriter.close();
-        } finally {
-            mLocalDictionaryController.writeLock().unlock();
-        }
+        });
+    }
+
+    protected void closeBinaryDictionary() {
+        // Ensure that no other threads are accessing the local binary dictionary.
+        getExecutor(mFilename).execute(new Runnable() {
+            @Override
+            public void run() {
+                if (mBinaryDictionary != null) {
+                    mBinaryDictionary.close();
+                    mBinaryDictionary = null;
+                }
+            }
+        });
+    }
+
+    protected void clear() {
+        getExecutor(mFilename).execute(new Runnable() {
+            @Override
+            public void run() {
+                if (ENABLE_BINARY_DICTIONARY_DYNAMIC_UPDATE && mDictionaryWriter == null) {
+                    mBinaryDictionary.close();
+                    final File file = new File(mContext.getFilesDir(), mFilename);
+                    final FusionDictionary dict = new FusionDictionary(new PtNodeArray(),
+                            new FusionDictionary.DictionaryOptions(new HashMap<String,String>(),
+                                    false, false));
+                    final DictEncoder dictEncoder = new Ver3DictEncoder(file);
+                    try {
+                        dictEncoder.writeDictionary(dict, FORMAT_OPTIONS);
+                    } catch (IOException e) {
+                        Log.e(TAG, "Exception in creating new dictionary file.", e);
+                    } catch (UnsupportedFormatException e) {
+                        Log.e(TAG, "Exception in creating new dictionary file.", e);
+                    }
+                } else {
+                    mDictionaryWriter.clear();
+                }
+            }
+        });
     }
 
     /**
@@ -159,86 +267,159 @@
     }
 
     /**
-     * Sets a word bigram in the dictionary. Used for loading a dictionary.
+     * Adds a word bigram in the dictionary. Used for loading a dictionary.
      */
-    protected void setBigram(final String prevWord, final String word, final int frequency) {
-        mDictionaryWriter.addBigramWords(prevWord, word, frequency, true /* isValid */);
+    protected void addBigram(final String prevWord, final String word, final int frequency,
+            final long lastModifiedTime) {
+        mDictionaryWriter.addBigramWords(prevWord, word, frequency, true /* isValid */,
+                lastModifiedTime);
     }
 
     /**
-     * Dynamically adds a word unigram to the dictionary.
+     * Dynamically adds a word unigram to the dictionary. May overwrite an existing entry.
      */
     protected void addWordDynamically(final String word, final String shortcutTarget,
             final int frequency, final boolean isNotAWord) {
-        mLocalDictionaryController.writeLock().lock();
-        try {
-            mDictionaryWriter.addUnigramWord(word, shortcutTarget, frequency, isNotAWord);
-        } finally {
-            mLocalDictionaryController.writeLock().unlock();
+        if (!mIsUpdatable) {
+            Log.w(TAG, "addWordDynamically is called for non-updatable dictionary: " + mFilename);
+            return;
         }
+
+        getExecutor(mFilename).execute(new Runnable() {
+            @Override
+            public void run() {
+                if (ENABLE_BINARY_DICTIONARY_DYNAMIC_UPDATE) {
+                    mBinaryDictionary.addUnigramWord(word, frequency);
+                } else {
+                    // TODO: Remove.
+                    mDictionaryWriter.addUnigramWord(word, shortcutTarget, frequency, isNotAWord);
+                }
+            }
+        });
     }
 
     /**
-     * Dynamically sets a word bigram in the dictionary.
+     * Dynamically adds a word bigram in the dictionary. May overwrite an existing entry.
      */
-    protected void setBigramDynamically(final String prevWord, final String word,
-            final int frequency) {
-        mLocalDictionaryController.writeLock().lock();
-        try {
-            mDictionaryWriter.addBigramWords(prevWord, word, frequency, true /* isValid */);
-        } finally {
-            mLocalDictionaryController.writeLock().unlock();
+    protected void addBigramDynamically(final String word0, final String word1,
+            final int frequency, final boolean isValid) {
+        if (!mIsUpdatable) {
+            Log.w(TAG, "addBigramDynamically is called for non-updatable dictionary: "
+                    + mFilename);
+            return;
         }
+
+        getExecutor(mFilename).execute(new Runnable() {
+            @Override
+            public void run() {
+                if (ENABLE_BINARY_DICTIONARY_DYNAMIC_UPDATE) {
+                    mBinaryDictionary.addBigramWords(word0, word1, frequency);
+                } else {
+                    // TODO: Remove.
+                    mDictionaryWriter.addBigramWords(word0, word1, frequency, isValid,
+                            0 /* lastTouchedTime */);
+                }
+            }
+        });
+    }
+
+    /**
+     * Dynamically remove a word bigram in the dictionary.
+     */
+    protected void removeBigramDynamically(final String word0, final String word1) {
+        if (!mIsUpdatable) {
+            Log.w(TAG, "removeBigramDynamically is called for non-updatable dictionary: "
+                    + mFilename);
+            return;
+        }
+
+        getExecutor(mFilename).execute(new Runnable() {
+            @Override
+            public void run() {
+                if (ENABLE_BINARY_DICTIONARY_DYNAMIC_UPDATE) {
+                    mBinaryDictionary.removeBigramWords(word0, word1);
+                } else {
+                    // TODO: Remove.
+                    mDictionaryWriter.removeBigramWords(word0, word1);
+                }
+            }
+        });
+    }
+
+    @Override
+    public ArrayList<SuggestedWordInfo> getSuggestionsWithSessionId(final WordComposer composer,
+            final String prevWord, final ProximityInfo proximityInfo,
+            final boolean blockOffensiveWords, final int[] additionalFeaturesOptions,
+            final int sessionId) {
+        reloadDictionaryIfRequired();
+        final ArrayList<SuggestedWordInfo> suggestions = CollectionUtils.newArrayList();
+        final AsyncResultHolder<ArrayList<SuggestedWordInfo>> holder =
+                new AsyncResultHolder<ArrayList<SuggestedWordInfo>>();
+        getExecutor(mFilename).executePrioritized(new Runnable() {
+            @Override
+            public void run() {
+                if (ENABLE_BINARY_DICTIONARY_DYNAMIC_UPDATE) {
+                    if (mBinaryDictionary == null) {
+                        holder.set(null);
+                        return;
+                    }
+                    final ArrayList<SuggestedWordInfo> binarySuggestion =
+                            mBinaryDictionary.getSuggestionsWithSessionId(composer, prevWord,
+                                    proximityInfo, blockOffensiveWords, additionalFeaturesOptions,
+                                    sessionId);
+                    holder.set(binarySuggestion);
+                } else {
+                    final ArrayList<SuggestedWordInfo> inMemDictSuggestion =
+                            composer.isBatchMode() ? null :
+                                    mDictionaryWriter.getSuggestionsWithSessionId(composer,
+                                            prevWord, proximityInfo, blockOffensiveWords,
+                                            additionalFeaturesOptions, sessionId);
+                    // TODO: Remove checking mIsUpdatable and use native suggestion.
+                    if (mBinaryDictionary != null && !mIsUpdatable) {
+                        final ArrayList<SuggestedWordInfo> binarySuggestion =
+                                mBinaryDictionary.getSuggestionsWithSessionId(composer, prevWord,
+                                        proximityInfo, blockOffensiveWords,
+                                        additionalFeaturesOptions, sessionId);
+                        if (inMemDictSuggestion == null) {
+                            holder.set(binarySuggestion);
+                        } else if (binarySuggestion == null) {
+                            holder.set(inMemDictSuggestion);
+                        } else {
+                            binarySuggestion.addAll(inMemDictSuggestion);
+                            holder.set(binarySuggestion);
+                        }
+                    } else {
+                        holder.set(inMemDictSuggestion);
+                    }
+                }
+            }
+        });
+        return holder.get(null, TIMEOUT_FOR_READ_OPS_IN_MILLISECONDS);
     }
 
     @Override
     public ArrayList<SuggestedWordInfo> getSuggestions(final WordComposer composer,
             final String prevWord, final ProximityInfo proximityInfo,
-            final boolean blockOffensiveWords) {
-        asyncReloadDictionaryIfRequired();
-        // Write lock because getSuggestions in native updates session status.
-        if (mLocalDictionaryController.writeLock().tryLock()) {
-            try {
-                final ArrayList<SuggestedWordInfo> inMemDictSuggestion =
-                        mDictionaryWriter.getSuggestions(composer, prevWord, proximityInfo,
-                                blockOffensiveWords);
-                if (mBinaryDictionary != null) {
-                    final ArrayList<SuggestedWordInfo> binarySuggestion =
-                            mBinaryDictionary.getSuggestions(composer, prevWord, proximityInfo,
-                                    blockOffensiveWords);
-                    if (inMemDictSuggestion == null) {
-                        return binarySuggestion;
-                    } else if (binarySuggestion == null) {
-                        return inMemDictSuggestion;
-                    } else {
-                        binarySuggestion.addAll(binarySuggestion);
-                        return binarySuggestion;
-                    }
-                } else {
-                    return inMemDictSuggestion;
-                }
-            } finally {
-                mLocalDictionaryController.writeLock().unlock();
-            }
-        }
-        return null;
+            final boolean blockOffensiveWords, final int[] additionalFeaturesOptions) {
+        return getSuggestionsWithSessionId(composer, prevWord, proximityInfo, blockOffensiveWords,
+                additionalFeaturesOptions, 0 /* sessionId */);
     }
 
     @Override
     public boolean isValidWord(final String word) {
-        asyncReloadDictionaryIfRequired();
+        reloadDictionaryIfRequired();
         return isValidWordInner(word);
     }
 
     protected boolean isValidWordInner(final String word) {
-        if (mLocalDictionaryController.readLock().tryLock()) {
-            try {
-                return isValidWordLocked(word);
-            } finally {
-                mLocalDictionaryController.readLock().unlock();
+        final AsyncResultHolder<Boolean> holder = new AsyncResultHolder<Boolean>();
+        getExecutor(mFilename).executePrioritized(new Runnable() {
+            @Override
+            public void run() {
+                holder.set(isValidWordLocked(word));
             }
-        }
-        return false;
+        });
+        return holder.get(false, TIMEOUT_FOR_READ_OPS_IN_MILLISECONDS);
     }
 
     protected boolean isValidWordLocked(final String word) {
@@ -256,8 +437,8 @@
      * dictionary exists, this method will generate one.
      */
     protected void loadDictionary() {
-        mLocalDictionaryController.mLastUpdateRequestTime = SystemClock.uptimeMillis();
-        asyncReloadDictionaryIfRequired();
+        mPerInstanceDictionaryTimeRecorder.mLastUpdateRequestTime = SystemClock.uptimeMillis();
+        reloadDictionaryIfRequired();
     }
 
     /**
@@ -267,8 +448,8 @@
     private void loadBinaryDictionary() {
         if (DEBUG) {
             Log.d(TAG, "Loading binary dictionary: " + mFilename + " request="
-                    + mSharedDictionaryController.mLastUpdateRequestTime + " update="
-                    + mSharedDictionaryController.mLastUpdateTime);
+                    + mFilenameDictionaryTimeRecorder.mLastUpdateRequestTime + " update="
+                    + mFilenameDictionaryTimeRecorder.mLastUpdateTime);
         }
 
         final File file = new File(mContext.getFilesDir(), mFilename);
@@ -277,22 +458,21 @@
 
         // Build the new binary dictionary
         final BinaryDictionary newBinaryDictionary = new BinaryDictionary(filename, 0, length,
-                true /* useFullEditDistance */, null, mDictType, false /* isUpdatable */);
+                true /* useFullEditDistance */, null, mDictType, mIsUpdatable);
 
-        if (mBinaryDictionary != null) {
-            // Ensure all threads accessing the current dictionary have finished before swapping in
-            // the new one.
-            final BinaryDictionary oldBinaryDictionary = mBinaryDictionary;
-            mLocalDictionaryController.writeLock().lock();
-            try {
+        // Ensure all threads accessing the current dictionary have finished before
+        // swapping in the new one.
+        // TODO: Ensure multi-thread assignment of mBinaryDictionary.
+        final BinaryDictionary oldBinaryDictionary = mBinaryDictionary;
+        getExecutor(mFilename).executePrioritized(new Runnable() {
+            @Override
+            public void run() {
                 mBinaryDictionary = newBinaryDictionary;
-            } finally {
-                mLocalDictionaryController.writeLock().unlock();
+                if (oldBinaryDictionary != null) {
+                    oldBinaryDictionary.close();
+                }
             }
-            oldBinaryDictionary.close();
-        } else {
-            mBinaryDictionary = newBinaryDictionary;
-        }
+        });
     }
 
     /**
@@ -302,19 +482,44 @@
     abstract protected boolean needsToReloadBeforeWriting();
 
     /**
-     * Generates and writes a new binary dictionary based on the contents of the fusion dictionary.
+     * Writes a new binary dictionary based on the contents of the fusion dictionary.
      */
-    private void generateBinaryDictionary() {
+    private void writeBinaryDictionary() {
         if (DEBUG) {
             Log.d(TAG, "Generating binary dictionary: " + mFilename + " request="
-                    + mSharedDictionaryController.mLastUpdateRequestTime + " update="
-                    + mSharedDictionaryController.mLastUpdateTime);
+                    + mFilenameDictionaryTimeRecorder.mLastUpdateRequestTime + " update="
+                    + mFilenameDictionaryTimeRecorder.mLastUpdateTime);
         }
         if (needsToReloadBeforeWriting()) {
             mDictionaryWriter.clear();
             loadDictionaryAsync();
+            mDictionaryWriter.write(mFilename);
+        } else {
+            if (ENABLE_BINARY_DICTIONARY_DYNAMIC_UPDATE) {
+                if (mBinaryDictionary == null || !mBinaryDictionary.isValidDictionary()) {
+                    final File file = new File(mContext.getFilesDir(), mFilename);
+                    final FusionDictionary dict = new FusionDictionary(new PtNodeArray(),
+                            new FusionDictionary.DictionaryOptions(new HashMap<String,String>(),
+                                    false, false));
+                    final DictEncoder dictEncoder = new Ver3DictEncoder(file);
+                    try {
+                        dictEncoder.writeDictionary(dict, FORMAT_OPTIONS);
+                    } catch (IOException e) {
+                        Log.e(TAG, "Exception in creating new dictionary file.", e);
+                    } catch (UnsupportedFormatException e) {
+                        Log.e(TAG, "Exception in creating new dictionary file.", e);
+                    }
+                } else {
+                    if (mBinaryDictionary.needsToRunGC()) {
+                        mBinaryDictionary.flushWithGC();
+                    } else {
+                        mBinaryDictionary.flush();
+                    }
+                }
+            } else {
+                mDictionaryWriter.write(mFilename);
+            }
         }
-        mDictionaryWriter.write(mFilename);
     }
 
     /**
@@ -326,83 +531,74 @@
      */
     protected void setRequiresReload(final boolean requiresRebuild) {
         final long time = SystemClock.uptimeMillis();
-        mLocalDictionaryController.mLastUpdateRequestTime = time;
-        mSharedDictionaryController.mLastUpdateRequestTime = time;
+        mPerInstanceDictionaryTimeRecorder.mLastUpdateRequestTime = time;
+        mFilenameDictionaryTimeRecorder.mLastUpdateRequestTime = time;
         if (DEBUG) {
             Log.d(TAG, "Reload request: " + mFilename + ": request=" + time + " update="
-                    + mSharedDictionaryController.mLastUpdateTime);
+                    + mFilenameDictionaryTimeRecorder.mLastUpdateTime);
         }
     }
 
     /**
-     * Reloads the dictionary if required. Reload will occur asynchronously in a separate thread.
-     */
-    void asyncReloadDictionaryIfRequired() {
-        if (!isReloadRequired()) return;
-        if (DEBUG) {
-            Log.d(TAG, "Starting AsyncReloadDictionaryTask: " + mFilename);
-        }
-        new AsyncReloadDictionaryTask().start();
-    }
-
-    /**
      * Reloads the dictionary if required.
      */
-    protected final void syncReloadDictionaryIfRequired() {
+    public final void reloadDictionaryIfRequired() {
         if (!isReloadRequired()) return;
-        syncReloadDictionaryInternal();
+        reloadDictionary();
     }
 
     /**
      * Returns whether a dictionary reload is required.
      */
     private boolean isReloadRequired() {
-        return mBinaryDictionary == null || mLocalDictionaryController.isOutOfDate();
+        return mBinaryDictionary == null || mPerInstanceDictionaryTimeRecorder.isOutOfDate();
     }
 
     /**
      * Reloads the dictionary. Access is controlled on a per dictionary file basis and supports
      * concurrent calls from multiple instances that share the same dictionary file.
      */
-    private final void syncReloadDictionaryInternal() {
+    private final void reloadDictionary() {
         // Ensure that only one thread attempts to read or write to the shared binary dictionary
         // file at the same time.
-        mSharedDictionaryController.writeLock().lock();
-        try {
-            final long time = SystemClock.uptimeMillis();
-            final boolean dictionaryFileExists = dictionaryFileExists();
-            if (mSharedDictionaryController.isOutOfDate() || !dictionaryFileExists) {
-                // If the shared dictionary file does not exist or is out of date, the first
-                // instance that acquires the lock will generate a new one.
-                if (hasContentChanged() || !dictionaryFileExists) {
-                    // If the source content has changed or the dictionary does not exist, rebuild
-                    // the binary dictionary. Empty dictionaries are supported (in the case where
-                    // loadDictionaryAsync() adds nothing) in order to provide a uniform framework.
-                    mSharedDictionaryController.mLastUpdateTime = time;
-                    generateBinaryDictionary();
+        getExecutor(mFilename).execute(new Runnable() {
+            @Override
+            public void run() {
+                final long time = SystemClock.uptimeMillis();
+                final boolean dictionaryFileExists = dictionaryFileExists();
+                if (mFilenameDictionaryTimeRecorder.isOutOfDate() || !dictionaryFileExists) {
+                    // If the shared dictionary file does not exist or is out of date, the first
+                    // instance that acquires the lock will generate a new one.
+                    if (hasContentChanged() || !dictionaryFileExists) {
+                        // If the source content has changed or the dictionary does not exist,
+                        // rebuild the binary dictionary. Empty dictionaries are supported (in the
+                        // case where loadDictionaryAsync() adds nothing) in order to provide a
+                        // uniform framework.
+                        mFilenameDictionaryTimeRecorder.mLastUpdateTime = time;
+                        writeBinaryDictionary();
+                        loadBinaryDictionary();
+                    } else {
+                        // If not, the reload request was unnecessary so revert
+                        // LastUpdateRequestTime to LastUpdateTime.
+                        mFilenameDictionaryTimeRecorder.mLastUpdateRequestTime =
+                                mFilenameDictionaryTimeRecorder.mLastUpdateTime;
+                    }
+                } else if (mBinaryDictionary == null ||
+                        mPerInstanceDictionaryTimeRecorder.mLastUpdateTime
+                                < mFilenameDictionaryTimeRecorder.mLastUpdateTime) {
+                    // Otherwise, if the local dictionary is older than the shared dictionary, load
+                    // the shared dictionary.
                     loadBinaryDictionary();
-                } else {
-                    // If not, the reload request was unnecessary so revert LastUpdateRequestTime
-                    // to LastUpdateTime.
-                    mSharedDictionaryController.mLastUpdateRequestTime =
-                            mSharedDictionaryController.mLastUpdateTime;
                 }
-            } else if (mBinaryDictionary == null || mLocalDictionaryController.mLastUpdateTime
-                    < mSharedDictionaryController.mLastUpdateTime) {
-                // Otherwise, if the local dictionary is older than the shared dictionary, load the
-                // shared dictionary.
-                loadBinaryDictionary();
+                if (mBinaryDictionary != null && !mBinaryDictionary.isValidDictionary()) {
+                    // Binary dictionary is not valid. Regenerate the dictionary file.
+                    mFilenameDictionaryTimeRecorder.mLastUpdateTime = time;
+                    writeBinaryDictionary();
+                    loadBinaryDictionary();
+                }
+                mPerInstanceDictionaryTimeRecorder.mLastUpdateTime = time;
             }
-            if (mBinaryDictionary != null && !mBinaryDictionary.isValidDictionary()) {
-                // Binary dictionary is not valid. Regenerate the dictionary file.
-                mSharedDictionaryController.mLastUpdateTime = time;
-                generateBinaryDictionary();
-                loadBinaryDictionary();
-            }
-            mLocalDictionaryController.mLastUpdateTime = time;
-        } finally {
-            mSharedDictionaryController.writeLock().unlock();
-        }
+        });
     }
 
     // TODO: cache the file's existence so that we avoid doing a disk access each time.
@@ -412,21 +608,38 @@
     }
 
     /**
-     * Thread class for asynchronously reloading and rewriting the binary dictionary.
+     * Load the dictionary to memory.
      */
-    private class AsyncReloadDictionaryTask extends Thread {
-        @Override
-        public void run() {
-            syncReloadDictionaryInternal();
-        }
+    protected void asyncLoadDictionaryToMemory() {
+        getExecutor(mFilename).executePrioritized(new Runnable() {
+            @Override
+            public void run() {
+                if (!ENABLE_BINARY_DICTIONARY_DYNAMIC_UPDATE) {
+                    loadDictionaryAsync();
+                }
+            }
+        });
     }
 
     /**
-     * Lock for controlling access to a given binary dictionary and for tracking whether the
-     * dictionary is out of date. Can be shared across multiple dictionary instances that access the
-     * same filename.
+     * Generate binary dictionary using DictionaryWriter.
      */
-    private static class DictionaryController extends ReentrantReadWriteLock {
+    protected void asyncFlashAllBinaryDictionary() {
+        final Runnable newTask = new Runnable() {
+            @Override
+            public void run() {
+                writeBinaryDictionary();
+            }
+        };
+        final Runnable oldTask = mUnfinishedFlushingTask.getAndSet(newTask);
+        getExecutor(mFilename).replaceAndExecute(oldTask, newTask);
+    }
+
+    /**
+     * Time recorder for tracking whether the dictionary is out of date.
+     * Can be shared across multiple dictionary instances that access the same filename.
+     */
+    private static class DictionaryTimeRecorder {
         private volatile long mLastUpdateTime = 0;
         private volatile long mLastUpdateRequestTime = 0;
 
@@ -434,4 +647,75 @@
             return (mLastUpdateRequestTime > mLastUpdateTime);
         }
     }
+
+    /**
+     * Dynamically adds a word unigram to the dictionary for testing with blocking-lock.
+     */
+    @UsedForTesting
+    protected void addWordDynamicallyForTests(final String word, final String shortcutTarget,
+            final int frequency, final boolean isNotAWord) {
+        getExecutor(mFilename).executePrioritized(new Runnable() {
+            @Override
+            public void run() {
+                addWordDynamically(word, shortcutTarget, frequency, isNotAWord);
+            }
+        });
+    }
+
+    /**
+     * Dynamically adds a word bigram in the dictionary for testing with blocking-lock.
+     */
+    @UsedForTesting
+    protected void addBigramDynamicallyForTests(final String word0, final String word1,
+            final int frequency, final boolean isValid) {
+        getExecutor(mFilename).executePrioritized(new Runnable() {
+            @Override
+            public void run() {
+                addBigramDynamically(word0, word1, frequency, isValid);
+            }
+        });
+    }
+
+    /**
+     * Dynamically remove a word bigram in the dictionary for testing with blocking-lock.
+     */
+    @UsedForTesting
+    protected void removeBigramDynamicallyForTests(final String word0, final String word1) {
+        getExecutor(mFilename).executePrioritized(new Runnable() {
+            @Override
+            public void run() {
+                removeBigramDynamically(word0, word1);
+            }
+        });
+    }
+
+    // TODO: Implement native binary methods once the dynamic dictionary implementation is done.
+    @UsedForTesting
+    public boolean isInDictionaryForTests(final String word) {
+        final AsyncResultHolder<Boolean> holder = new AsyncResultHolder<Boolean>();
+        getExecutor(mFilename).executePrioritized(new Runnable() {
+            @Override
+            public void run() {
+                if (mDictType == Dictionary.TYPE_USER_HISTORY) {
+                    if (ENABLE_BINARY_DICTIONARY_DYNAMIC_UPDATE) {
+                        holder.set(mBinaryDictionary.isValidWord(word));
+                    } else {
+                        holder.set(((DynamicPersonalizationDictionaryWriter) mDictionaryWriter)
+                                .isInDictionaryForTests(word));
+                    }
+                }
+            }
+        });
+        return holder.get(false, TIMEOUT_FOR_READ_OPS_IN_MILLISECONDS);
+    }
+
+    @UsedForTesting
+    public void shutdownExecutorForTests() {
+        getExecutor(mFilename).shutdown();
+    }
+
+    @UsedForTesting
+    public boolean isTerminatedForTests() {
+        return getExecutor(mFilename).isTerminated();
+    }
 }
diff --git a/java/src/com/android/inputmethod/latin/ExpandableDictionary.java b/java/src/com/android/inputmethod/latin/ExpandableDictionary.java
index bd2d703..d491f98 100644
--- a/java/src/com/android/inputmethod/latin/ExpandableDictionary.java
+++ b/java/src/com/android/inputmethod/latin/ExpandableDictionary.java
@@ -16,10 +16,10 @@
 
 package com.android.inputmethod.latin;
 
-import android.content.Context;
 import android.text.TextUtils;
 import android.util.Log;
 
+import com.android.inputmethod.annotations.UsedForTesting;
 import com.android.inputmethod.keyboard.ProximityInfo;
 import com.android.inputmethod.latin.SuggestedWords.SuggestedWordInfo;
 import com.android.inputmethod.latin.utils.CollectionUtils;
@@ -29,9 +29,10 @@
 import java.util.LinkedList;
 
 /**
- * Base class for an in-memory dictionary that can grow dynamically and can
+ * Class for an in-memory dictionary that can grow dynamically and can
  * be searched for suggestions and valid words.
  */
+// TODO: Remove after binary dictionary supports dynamic update.
 public class ExpandableDictionary extends Dictionary {
     private static final String TAG = ExpandableDictionary.class.getSimpleName();
     /**
@@ -39,23 +40,11 @@
      */
     private static final int FULL_WORD_SCORE_MULTIPLIER = 2;
 
-    // Bigram frequency is a fixed point number with 1 meaning 1.2 and 255 meaning 1.8.
-    protected static final int BIGRAM_MAX_FREQUENCY = 255;
-
-    private Context mContext;
     private char[] mWordBuilder = new char[Constants.DICTIONARY_MAX_WORD_LENGTH];
     private int mMaxDepth;
     private int mInputLength;
 
-    private boolean mRequiresReload;
-
-    private boolean mUpdatingDictionary;
-
-    // Use this lock before touching mUpdatingDictionary & mRequiresDownload
-    private Object mUpdatingLock = new Object();
-
     private static final class Node {
-        Node() {}
         char mCode;
         int mFrequency;
         boolean mTerminal;
@@ -157,46 +146,12 @@
 
     private int[][] mCodes;
 
-    public ExpandableDictionary(final Context context, final String dictType) {
+    public ExpandableDictionary(final String dictType) {
         super(dictType);
-        mContext = context;
         clearDictionary();
         mCodes = new int[Constants.DICTIONARY_MAX_WORD_LENGTH][];
     }
 
-    public void loadDictionary() {
-        synchronized (mUpdatingLock) {
-            startDictionaryLoadingTaskLocked();
-        }
-    }
-
-    public void startDictionaryLoadingTaskLocked() {
-        if (!mUpdatingDictionary) {
-            mUpdatingDictionary = true;
-            mRequiresReload = false;
-            new LoadDictionaryTask().start();
-        }
-    }
-
-    public void setRequiresReload(final boolean reload) {
-        synchronized (mUpdatingLock) {
-            mRequiresReload = reload;
-        }
-    }
-
-    public boolean getRequiresReload() {
-        return mRequiresReload;
-    }
-
-    /** Override to load your dictionary here, on a background thread. */
-    public void loadDictionaryAsync() {
-        // empty base implementation
-    }
-
-    public Context getContext() {
-        return mContext;
-    }
-
     public int getMaxWordLength() {
         return Constants.DICTIONARY_MAX_WORD_LENGTH;
     }
@@ -231,7 +186,7 @@
             childNode.mShortcutOnly = isShortcutOnly;
             children.add(childNode);
         }
-        if (wordLength == depth + 1 && shortcutTarget != null) {
+        if (wordLength == depth + 1) {
             // Terminate this word
             childNode.mTerminal = true;
             if (isShortcutOnly) {
@@ -255,8 +210,7 @@
     @Override
     public ArrayList<SuggestedWordInfo> getSuggestions(final WordComposer composer,
             final String prevWord, final ProximityInfo proximityInfo,
-            final boolean blockOffensiveWords) {
-        if (reloadDictionaryIfRequired()) return null;
+            final boolean blockOffensiveWords, final int[] additionalFeaturesOptions) {
         if (composer.size() > 1) {
             if (composer.size() >= Constants.DICTIONARY_MAX_WORD_LENGTH) {
                 return null;
@@ -272,17 +226,7 @@
         }
     }
 
-    // This reloads the dictionary if required, and returns whether it's currently updating its
-    // contents or not.
-    private boolean reloadDictionaryIfRequired() {
-        synchronized (mUpdatingLock) {
-            // If we need to update, start off a background task
-            if (mRequiresReload) startDictionaryLoadingTaskLocked();
-            return mUpdatingDictionary;
-        }
-    }
-
-    protected ArrayList<SuggestedWordInfo> getWordsInner(final WordComposer codes,
+    private ArrayList<SuggestedWordInfo> getWordsInner(final WordComposer codes,
             final String prevWordForBigrams, final ProximityInfo proximityInfo) {
         final ArrayList<SuggestedWordInfo> suggestions = CollectionUtils.newArrayList();
         mInputLength = codes.size();
@@ -312,11 +256,6 @@
 
     @Override
     public synchronized boolean isValidWord(final String word) {
-        synchronized (mUpdatingLock) {
-            // If we need to update, start off a background task
-            if (mRequiresReload) startDictionaryLoadingTaskLocked();
-            if (mUpdatingDictionary) return false;
-        }
         final Node node = searchNode(mRoots, word, 0, word.length());
         // If node is null, we didn't find the word, so it's not valid.
         // If node.mShortcutOnly is true, then it exists as a shortcut but not as a word,
@@ -326,10 +265,10 @@
         return (node == null) ? false : !node.mShortcutOnly;
     }
 
-    protected boolean removeBigram(final String word1, final String word2) {
+    public boolean removeBigram(final String word0, final String word1) {
         // Refer to addOrSetBigram() about word1.toLowerCase()
-        final Node firstWord = searchWord(mRoots, word1.toLowerCase(), 0, null);
-        final Node secondWord = searchWord(mRoots, word2, 0, null);
+        final Node firstWord = searchWord(mRoots, word0.toLowerCase(), 0, null);
+        final Node secondWord = searchWord(mRoots, word1, 0, null);
         LinkedList<NextWord> bigrams = firstWord.mNGrams;
         NextWord bigramNode = null;
         if (bigrams == null || bigrams.size() == 0) {
@@ -351,16 +290,17 @@
     /**
      * Returns the word's frequency or -1 if not found
      */
-    protected int getWordFrequency(final String word) {
+    @UsedForTesting
+    public int getWordFrequency(final String word) {
         // Case-sensitive search
         final Node node = searchNode(mRoots, word, 0, word.length());
         return (node == null) ? -1 : node.mFrequency;
     }
 
-    protected NextWord getBigramWord(final String word1, final String word2) {
-        // Refer to addOrSetBigram() about word1.toLowerCase()
-        final Node firstWord = searchWord(mRoots, word1.toLowerCase(), 0, null);
-        final Node secondWord = searchWord(mRoots, word2, 0, null);
+    public NextWord getBigramWord(final String word0, final String word1) {
+        // Refer to addOrSetBigram() about word0.toLowerCase()
+        final Node firstWord = searchWord(mRoots, word0.toLowerCase(), 0, null);
+        final Node secondWord = searchWord(mRoots, word1, 0, null);
         LinkedList<NextWord> bigrams = firstWord.mNGrams;
         if (bigrams == null || bigrams.size() == 0) {
             return null;
@@ -403,7 +343,9 @@
             // the respective size of the typed word and the suggestion if it matters sometime
             // in the future.
             suggestions.add(new SuggestedWordInfo(new String(word, 0, depth + 1), finalFreq,
-                    SuggestedWordInfo.KIND_CORRECTION, mDictType));
+                    SuggestedWordInfo.KIND_CORRECTION, this /* sourceDict */,
+                    SuggestedWordInfo.NOT_AN_INDEX /* indexOfTouchPointOfSecondWord */,
+                    SuggestedWordInfo.NOT_A_CONFIDENCE /* autoCommitFirstWordConfidence */));
             if (suggestions.size() >= Suggest.MAX_SUGGESTIONS) return false;
         }
         if (null != node.mShortcutTargets) {
@@ -411,7 +353,9 @@
             for (int shortcutIndex = 0; shortcutIndex < length; ++shortcutIndex) {
                 final char[] shortcut = node.mShortcutTargets.get(shortcutIndex);
                 suggestions.add(new SuggestedWordInfo(new String(shortcut, 0, shortcut.length),
-                        finalFreq, SuggestedWordInfo.KIND_SHORTCUT, mDictType));
+                        finalFreq, SuggestedWordInfo.KIND_SHORTCUT, this /* sourceDict */,
+                        SuggestedWordInfo.NOT_AN_INDEX /* indexOfTouchPointOfSecondWord */,
+                        SuggestedWordInfo.NOT_A_CONFIDENCE /* autoCommitFirstWordConfidence */));
                 if (suggestions.size() > Suggest.MAX_SUGGESTIONS) return false;
             }
         }
@@ -438,7 +382,7 @@
      * @param suggestions the list in which to add suggestions
      */
     // TODO: Share this routine with the native code for BinaryDictionary
-    protected void getWordsRec(final NodeArray roots, final WordComposer codes, final char[] word,
+    private void getWordsRec(final NodeArray roots, final WordComposer codes, final char[] word,
             final int depth, final boolean completion, final int snr, final int inputIndex,
             final int skipPos, final ArrayList<SuggestedWordInfo> suggestions) {
         final int count = roots.mLength;
@@ -531,37 +475,41 @@
         }
     }
 
-    public int setBigramAndGetFrequency(final String word1, final String word2,
+    public int setBigramAndGetFrequency(final String word0, final String word1,
             final int frequency) {
-        return setBigramAndGetFrequency(word1, word2, frequency, null /* unused */);
+        return setBigramAndGetFrequency(word0, word1, frequency, null /* unused */);
     }
 
-    public int setBigramAndGetFrequency(final String word1, final String word2,
+    public int setBigramAndGetFrequency(final String word0, final String word1,
             final ForgettingCurveParams fcp) {
-        return setBigramAndGetFrequency(word1, word2, 0 /* unused */, fcp);
+        return setBigramAndGetFrequency(word0, word1, 0 /* unused */, fcp);
     }
 
     /**
      * Adds bigrams to the in-memory trie structure that is being used to retrieve any word
-     * @param word1 the first word of this bigram
-     * @param word2 the second word of this bigram
+     * @param word0 the first word of this bigram
+     * @param word1 the second word of this bigram
      * @param frequency frequency for this bigram
      * @param fcp an instance of ForgettingCurveParams to use for decay policy
      * @return returns the final bigram frequency
      */
-    private int setBigramAndGetFrequency(final String word1, final String word2,
+    private int setBigramAndGetFrequency(final String word0, final String word1,
             final int frequency, final ForgettingCurveParams fcp) {
+        if (TextUtils.isEmpty(word0)) {
+            Log.e(TAG, "Invalid bigram previous word: " + word0);
+            return frequency;
+        }
         // We don't want results to be different according to case of the looked up left hand side
         // word. We do want however to return the correct case for the right hand side.
         // So we want to squash the case of the left hand side, and preserve that of the right
         // hand side word.
-        final String word1Lower = word1.toLowerCase();
-        if (TextUtils.isEmpty(word1Lower) || TextUtils.isEmpty(word2)) {
-            Log.e(TAG, "Invalid bigram pair: " + word1 + ", " + word1Lower + ", " + word2);
+        final String word0Lower = word0.toLowerCase();
+        if (TextUtils.isEmpty(word0Lower) || TextUtils.isEmpty(word1)) {
+            Log.e(TAG, "Invalid bigram pair: " + word0 + ", " + word0Lower + ", " + word1);
             return frequency;
         }
-        final Node firstWord = searchWord(mRoots, word1Lower, 0, null);
-        final Node secondWord = searchWord(mRoots, word2, 0, null);
+        final Node firstWord = searchWord(mRoots, word0Lower, 0, null);
+        final Node secondWord = searchWord(mRoots, word1, 0, null);
         LinkedList<NextWord> bigrams = firstWord.mNGrams;
         if (bigrams == null || bigrams.size() == 0) {
             firstWord.mNGrams = CollectionUtils.newLinkedList();
@@ -657,7 +605,9 @@
             if (freq >= 0 && node == null) {
                 suggestions.add(new SuggestedWordInfo(new String(mLookedUpString, index,
                         Constants.DICTIONARY_MAX_WORD_LENGTH - index),
-                        freq, SuggestedWordInfo.KIND_CORRECTION, mDictType));
+                        freq, SuggestedWordInfo.KIND_CORRECTION, this /* sourceDict */,
+                        SuggestedWordInfo.NOT_AN_INDEX /* indexOfTouchPointOfSecondWord */,
+                        SuggestedWordInfo.NOT_A_CONFIDENCE /* autoCommitFirstWordConfidence */));
             }
         }
     }
@@ -695,21 +645,10 @@
         return null;
     }
 
-    protected void clearDictionary() {
+    public void clearDictionary() {
         mRoots = new NodeArray();
     }
 
-    private final class LoadDictionaryTask extends Thread {
-        LoadDictionaryTask() {}
-        @Override
-        public void run() {
-            loadDictionaryAsync();
-            synchronized (mUpdatingLock) {
-                mUpdatingDictionary = false;
-            }
-        }
-    }
-
     private static char toLowerCase(final char c) {
         char baseChar = c;
         if (c < BASE_CHARS.length) {
diff --git a/java/src/com/android/inputmethod/latin/InputPointers.java b/java/src/com/android/inputmethod/latin/InputPointers.java
index e96a46e..2e638aa 100644
--- a/java/src/com/android/inputmethod/latin/InputPointers.java
+++ b/java/src/com/android/inputmethod/latin/InputPointers.java
@@ -105,6 +105,17 @@
         mTimes.append(times, startPos, length);
     }
 
+    /**
+     * Shift to the left by elementCount, discarding elementCount pointers at the start.
+     * @param elementCount how many elements to shift.
+     */
+    public void shift(final int elementCount) {
+        mXCoordinates.shift(elementCount);
+        mYCoordinates.shift(elementCount);
+        mPointerIds.shift(elementCount);
+        mTimes.shift(elementCount);
+    }
+
     public void reset() {
         final int defaultCapacity = mDefaultCapacity;
         mXCoordinates.reset(defaultCapacity);
diff --git a/java/src/com/android/inputmethod/latin/LatinIME.java b/java/src/com/android/inputmethod/latin/LatinIME.java
index 5c5b7b7..270dc4c 100644
--- a/java/src/com/android/inputmethod/latin/LatinIME.java
+++ b/java/src/com/android/inputmethod/latin/LatinIME.java
@@ -46,6 +46,7 @@
 import android.text.TextUtils;
 import android.text.style.SuggestionSpan;
 import android.util.Log;
+import android.util.Pair;
 import android.util.PrintWriterPrinter;
 import android.util.Printer;
 import android.view.KeyCharacterMap;
@@ -73,9 +74,12 @@
 import com.android.inputmethod.keyboard.KeyboardId;
 import com.android.inputmethod.keyboard.KeyboardSwitcher;
 import com.android.inputmethod.keyboard.MainKeyboardView;
+import com.android.inputmethod.latin.Suggest.OnGetSuggestedWordsCallback;
 import com.android.inputmethod.latin.SuggestedWords.SuggestedWordInfo;
 import com.android.inputmethod.latin.define.ProductionFlag;
-import com.android.inputmethod.latin.personalization.PersonalizationDictionaryHelper;
+import com.android.inputmethod.latin.personalization.PersonalizationDictionary;
+import com.android.inputmethod.latin.personalization.PersonalizationDictionarySessionRegister;
+import com.android.inputmethod.latin.personalization.PersonalizationHelper;
 import com.android.inputmethod.latin.personalization.PersonalizationPredictionDictionary;
 import com.android.inputmethod.latin.personalization.UserHistoryPredictionDictionary;
 import com.android.inputmethod.latin.settings.Settings;
@@ -83,6 +87,7 @@
 import com.android.inputmethod.latin.settings.SettingsValues;
 import com.android.inputmethod.latin.suggestions.SuggestionStripView;
 import com.android.inputmethod.latin.utils.ApplicationUtils;
+import com.android.inputmethod.latin.utils.AsyncResultHolder;
 import com.android.inputmethod.latin.utils.AutoCorrectionUtils;
 import com.android.inputmethod.latin.utils.CapsModeUtils;
 import com.android.inputmethod.latin.utils.CollectionUtils;
@@ -91,11 +96,11 @@
 import com.android.inputmethod.latin.utils.IntentUtils;
 import com.android.inputmethod.latin.utils.JniUtils;
 import com.android.inputmethod.latin.utils.LatinImeLoggerUtils;
-import com.android.inputmethod.latin.utils.PositionalInfoForUserDictPendingAddition;
 import com.android.inputmethod.latin.utils.RecapitalizeStatus;
 import com.android.inputmethod.latin.utils.StaticInnerHandlerWrapper;
 import com.android.inputmethod.latin.utils.TargetPackageInfoGetterTask;
 import com.android.inputmethod.latin.utils.TextRange;
+import com.android.inputmethod.latin.utils.UserHistoryForgettingCurveUtils;
 import com.android.inputmethod.research.ResearchLogger;
 
 import java.io.FileDescriptor;
@@ -123,6 +128,11 @@
 
     private static final int PENDING_IMS_CALLBACK_DURATION = 800;
 
+    private static final int PERIOD_FOR_AUDIO_AND_HAPTIC_FEEDBACK_IN_KEY_REPEAT = 2;
+
+    // TODO: Set this value appropriately.
+    private static final int GET_SUGGESTED_WORDS_TIMEOUT = 200;
+
     /**
      * The name of the scheme used by the Package Manager to warn of a new package installation,
      * replacement or removal.
@@ -171,11 +181,10 @@
     private UserBinaryDictionary mUserDictionary;
     private UserHistoryPredictionDictionary mUserHistoryPredictionDictionary;
     private PersonalizationPredictionDictionary mPersonalizationPredictionDictionary;
+    private PersonalizationDictionary mPersonalizationDictionary;
     private boolean mIsUserDictionaryAvailable;
 
     private LastComposedWord mLastComposedWord = LastComposedWord.NOT_A_COMPOSED_WORD;
-    private PositionalInfoForUserDictPendingAddition
-            mPositionalInfoForUserDictPendingAddition = null;
     private final WordComposer mWordComposer = new WordComposer();
     private final RichInputConnection mConnection = new RichInputConnection(this);
     private final RecapitalizeStatus mRecapitalizeStatus = new RecapitalizeStatus();
@@ -190,7 +199,10 @@
     private boolean mExpectingUpdateSelection;
     private int mDeleteCount;
     private long mLastKeyTime;
-    private TreeSet<Long> mCurrentlyPressedHardwareKeys = CollectionUtils.newTreeSet();
+    private final TreeSet<Long> mCurrentlyPressedHardwareKeys = CollectionUtils.newTreeSet();
+    // Personalization debugging params
+    private boolean mUseOnlyPersonalizationDictionaryForDebug = false;
+    private boolean mBoostPersonalizationDictionaryForDebug = false;
 
     // Member variables for remembering the current device orientation.
     private int mDisplayOrientation;
@@ -211,6 +223,7 @@
     private final boolean mIsHardwareAcceleratedDrawingEnabled;
 
     public final UIHandler mHandler = new UIHandler(this);
+    private InputUpdater mInputUpdater;
 
     public static final class UIHandler extends StaticInnerHandlerWrapper<LatinIME> {
         private static final int MSG_UPDATE_SHIFT_STATE = 0;
@@ -219,8 +232,14 @@
         private static final int MSG_SHOW_GESTURE_PREVIEW_AND_SUGGESTION_STRIP = 3;
         private static final int MSG_RESUME_SUGGESTIONS = 4;
         private static final int MSG_REOPEN_DICTIONARIES = 5;
+        private static final int MSG_ON_END_BATCH_INPUT = 6;
+        private static final int MSG_RESET_CACHES = 7;
 
+        private static final int ARG1_NOT_GESTURE_INPUT = 0;
         private static final int ARG1_DISMISS_GESTURE_FLOATING_PREVIEW_TEXT = 1;
+        private static final int ARG1_SHOW_GESTURE_FLOATING_PREVIEW_TEXT = 2;
+        private static final int ARG2_WITHOUT_TYPED_WORD = 0;
+        private static final int ARG2_WITH_TYPED_WORD = 1;
 
         private int mDelayUpdateSuggestions;
         private int mDelayUpdateShiftState;
@@ -253,8 +272,18 @@
                 switcher.updateShiftState();
                 break;
             case MSG_SHOW_GESTURE_PREVIEW_AND_SUGGESTION_STRIP:
-                latinIme.showGesturePreviewAndSuggestionStrip((SuggestedWords)msg.obj,
-                        msg.arg1 == ARG1_DISMISS_GESTURE_FLOATING_PREVIEW_TEXT);
+                if (msg.arg1 == ARG1_NOT_GESTURE_INPUT) {
+                    if (msg.arg2 == ARG2_WITH_TYPED_WORD) {
+                        final Pair<SuggestedWords, String> p =
+                                (Pair<SuggestedWords, String>) msg.obj;
+                        latinIme.showSuggestionStripWithTypedWord(p.first, p.second);
+                    } else {
+                        latinIme.showSuggestionStrip((SuggestedWords) msg.obj);
+                    }
+                } else {
+                    latinIme.showGesturePreviewAndSuggestionStrip((SuggestedWords) msg.obj,
+                            msg.arg1 == ARG1_DISMISS_GESTURE_FLOATING_PREVIEW_TEXT);
+                }
                 break;
             case MSG_RESUME_SUGGESTIONS:
                 latinIme.restartSuggestionsOnWordTouchedByCursor();
@@ -266,6 +295,13 @@
                 // get any suggestions. Wait one frame.
                 postUpdateSuggestionStrip();
                 break;
+            case MSG_ON_END_BATCH_INPUT:
+                latinIme.onEndBatchInputAsyncInternal((SuggestedWords) msg.obj);
+                break;
+            case MSG_RESET_CACHES:
+                latinIme.retryResetCaches(msg.arg1 == 1 /* tryResumeSuggestions */,
+                        msg.arg2 /* remainingTries */);
+                break;
             }
         }
 
@@ -282,6 +318,12 @@
             sendMessageDelayed(obtainMessage(MSG_RESUME_SUGGESTIONS), mDelayUpdateSuggestions);
         }
 
+        public void postResetCaches(final boolean tryResumeSuggestions, final int remainingTries) {
+            removeMessages(MSG_RESET_CACHES);
+            sendMessage(obtainMessage(MSG_RESET_CACHES, tryResumeSuggestions ? 1 : 0,
+                    remainingTries, null));
+        }
+
         public void cancelUpdateSuggestionStrip() {
             removeMessages(MSG_UPDATE_SUGGESTION_STRIP);
         }
@@ -307,9 +349,29 @@
                 final boolean dismissGestureFloatingPreviewText) {
             removeMessages(MSG_SHOW_GESTURE_PREVIEW_AND_SUGGESTION_STRIP);
             final int arg1 = dismissGestureFloatingPreviewText
-                    ? ARG1_DISMISS_GESTURE_FLOATING_PREVIEW_TEXT : 0;
-            obtainMessage(MSG_SHOW_GESTURE_PREVIEW_AND_SUGGESTION_STRIP, arg1, 0, suggestedWords)
-                    .sendToTarget();
+                    ? ARG1_DISMISS_GESTURE_FLOATING_PREVIEW_TEXT
+                    : ARG1_SHOW_GESTURE_FLOATING_PREVIEW_TEXT;
+            obtainMessage(MSG_SHOW_GESTURE_PREVIEW_AND_SUGGESTION_STRIP, arg1,
+                    ARG2_WITHOUT_TYPED_WORD, suggestedWords).sendToTarget();
+        }
+
+        public void showSuggestionStrip(final SuggestedWords suggestedWords) {
+            removeMessages(MSG_SHOW_GESTURE_PREVIEW_AND_SUGGESTION_STRIP);
+            obtainMessage(MSG_SHOW_GESTURE_PREVIEW_AND_SUGGESTION_STRIP,
+                    ARG1_NOT_GESTURE_INPUT, ARG2_WITHOUT_TYPED_WORD, suggestedWords).sendToTarget();
+        }
+
+        // TODO: Remove this method.
+        public void showSuggestionStripWithTypedWord(final SuggestedWords suggestedWords,
+                final String typedWord) {
+            removeMessages(MSG_SHOW_GESTURE_PREVIEW_AND_SUGGESTION_STRIP);
+            obtainMessage(MSG_SHOW_GESTURE_PREVIEW_AND_SUGGESTION_STRIP, ARG1_NOT_GESTURE_INPUT,
+                    ARG2_WITH_TYPED_WORD,
+                    new Pair<SuggestedWords, String>(suggestedWords, typedWord)).sendToTarget();
+        }
+
+        public void onEndBatchInput(final SuggestedWords suggestedWords) {
+            obtainMessage(MSG_ON_END_BATCH_INPUT, suggestedWords).sendToTarget();
         }
 
         public void startDoubleSpacePeriodTimer() {
@@ -472,6 +534,7 @@
         KeyboardSwitcher.init(this);
         AudioAndHapticFeedbackManager.init(this);
         AccessibilityUtils.init(this);
+        PersonalizationDictionarySessionRegister.init(this);
 
         super.onCreate();
 
@@ -503,6 +566,8 @@
         final IntentFilter newDictFilter = new IntentFilter();
         newDictFilter.addAction(DictionaryPackConstants.NEW_DICTIONARY_INTENT_ACTION);
         registerReceiver(mDictionaryPackInstallReceiver, newDictFilter);
+
+        mInputUpdater = new InputUpdater(this);
     }
 
     // Has to be package-visible for unit tests
@@ -558,10 +623,13 @@
 
         final SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(this);
 
-        mUserHistoryPredictionDictionary = PersonalizationDictionaryHelper
+        mUserHistoryPredictionDictionary = PersonalizationHelper
                 .getUserHistoryPredictionDictionary(this, localeStr, prefs);
         newSuggest.setUserHistoryPredictionDictionary(mUserHistoryPredictionDictionary);
-        mPersonalizationPredictionDictionary = PersonalizationDictionaryHelper
+        mPersonalizationDictionary = PersonalizationHelper
+                .getPersonalizationDictionary(this, localeStr, prefs);
+        newSuggest.setPersonalizationDictionary(mPersonalizationDictionary);
+        mPersonalizationPredictionDictionary = PersonalizationHelper
                 .getPersonalizationPredictionDictionary(this, localeStr, prefs);
         newSuggest.setPersonalizationPredictionDictionary(mPersonalizationPredictionDictionary);
 
@@ -633,8 +701,13 @@
             ResearchLogger.getInstance().onDestroy();
         }
         unregisterReceiver(mDictionaryPackInstallReceiver);
+        PersonalizationDictionarySessionRegister.onDestroy(this);
         LatinImeLogger.commit();
         LatinImeLogger.onDestroy();
+        if (mInputUpdater != null) {
+            mInputUpdater.onDestroy();
+            mInputUpdater = null;
+        }
         super.onDestroy();
     }
 
@@ -652,6 +725,7 @@
                 mOptionsDialog.dismiss();
             }
         }
+        PersonalizationDictionarySessionRegister.onConfigurationChanged(this, conf);
         super.onConfigurationChanged(conf);
     }
 
@@ -789,7 +863,6 @@
         // span, so we should reset our state unconditionally, even if restarting is true.
         mEnteredText = null;
         resetComposingState(true /* alsoResetLastComposedWord */);
-        if (isDifferentTextField) mHandler.postResumeSuggestions();
         mDeleteCount = 0;
         mSpaceState = SPACE_STATE_NONE;
         mRecapitalizeStatus.deactivate();
@@ -808,8 +881,16 @@
         }
         mSuggestedWords = SuggestedWords.EMPTY;
 
-        mConnection.resetCachesUponCursorMove(editorInfo.initialSelStart,
-                false /* shouldFinishComposition */);
+        // Sometimes, while rotating, for some reason the framework tells the app we are not
+        // connected to it and that means we can't refresh the cache. In this case, schedule a
+        // refresh later.
+        if (!mConnection.resetCachesUponCursorMoveAndReturnSuccess(editorInfo.initialSelStart,
+                false /* shouldFinishComposition */)) {
+            // We try resetting the caches up to 5 times before giving up.
+            mHandler.postResetCaches(isDifferentTextField, 5 /* remainingTries */);
+        } else {
+            if (isDifferentTextField) mHandler.postResumeSuggestions();
+        }
 
         if (isDifferentTextField) {
             mainKeyboardView.closing();
@@ -836,6 +917,9 @@
 
         mLastSelectionStart = editorInfo.initialSelStart;
         mLastSelectionEnd = editorInfo.initialSelEnd;
+        // In some cases (namely, after rotation of the device) editorInfo.initialSelStart is lying
+        // so we try using some heuristics to find out about these and fix them.
+        tryFixLyingCursorPosition();
 
         mHandler.cancelUpdateSuggestionStrip();
         mHandler.cancelDoubleSpacePeriodTimer();
@@ -850,22 +934,64 @@
                 currentSettingsValues.mGestureTrailEnabled,
                 currentSettingsValues.mGestureFloatingPreviewTextEnabled);
 
-        // If we have a user dictionary addition in progress, we should check now if we should
-        // replace the previously committed string with the word that has actually been added
-        // to the user dictionary.
-        if (null != mPositionalInfoForUserDictPendingAddition
-                && mPositionalInfoForUserDictPendingAddition.tryReplaceWithActualWord(
-                        mConnection, editorInfo, mLastSelectionEnd, currentLocale)) {
-            mPositionalInfoForUserDictPendingAddition = null;
-        }
-        // If tryReplaceWithActualWord returns false, we don't know what word was
-        // added to the user dictionary yet, so we keep the data and defer processing. The word will
-        // be replaced when the user dictionary reports back with the actual word, which ends
-        // up calling #onWordAddedToUserDictionary() in this class.
+        initPersonalizationDebugSettings(currentSettingsValues);
 
         if (TRACE) Debug.startMethodTracing("/data/trace/latinime");
     }
 
+    /**
+     * Try to get the text from the editor to expose lies the framework may have been
+     * telling us. Concretely, when the device rotates, the frameworks tells us about where the
+     * cursor used to be initially in the editor at the time it first received the focus; this
+     * may be completely different from the place it is upon rotation. Since we don't have any
+     * means to get the real value, try at least to ask the text view for some characters and
+     * detect the most damaging cases: when the cursor position is declared to be much smaller
+     * than it really is.
+     */
+    private void tryFixLyingCursorPosition() {
+        final CharSequence textBeforeCursor =
+                mConnection.getTextBeforeCursor(Constants.EDITOR_CONTENTS_CACHE_SIZE, 0);
+        if (null == textBeforeCursor) {
+            mLastSelectionStart = mLastSelectionEnd = NOT_A_CURSOR_POSITION;
+        } else {
+            final int textLength = textBeforeCursor.length();
+            if (textLength > mLastSelectionStart
+                    || (textLength < Constants.EDITOR_CONTENTS_CACHE_SIZE
+                            && mLastSelectionStart < Constants.EDITOR_CONTENTS_CACHE_SIZE)) {
+                mLastSelectionStart = textLength;
+                // We can't figure out the value of mLastSelectionEnd :(
+                // But at least if it's smaller than mLastSelectionStart something is wrong
+                if (mLastSelectionStart > mLastSelectionEnd) {
+                    mLastSelectionEnd = mLastSelectionStart;
+                }
+            }
+        }
+    }
+
+    // Initialization of personalization debug settings. This must be called inside
+    // onStartInputView.
+    private void initPersonalizationDebugSettings(SettingsValues currentSettingsValues) {
+        if (mUseOnlyPersonalizationDictionaryForDebug
+                != currentSettingsValues.mUseOnlyPersonalizationDictionaryForDebug) {
+            // Only for debug
+            initSuggest();
+            mUseOnlyPersonalizationDictionaryForDebug =
+                    currentSettingsValues.mUseOnlyPersonalizationDictionaryForDebug;
+        }
+
+        if (mBoostPersonalizationDictionaryForDebug !=
+                currentSettingsValues.mBoostPersonalizationDictionaryForDebug) {
+            // Only for debug
+            mBoostPersonalizationDictionaryForDebug =
+                    currentSettingsValues.mBoostPersonalizationDictionaryForDebug;
+            if (mBoostPersonalizationDictionaryForDebug) {
+                UserHistoryForgettingCurveUtils.boostMaxFreqForDebug();
+            } else {
+                UserHistoryForgettingCurveUtils.resetMaxFreqForDebug();
+            }
+        }
+    }
+
     // Callback for the TargetPackageInfoGetterTask
     @Override
     public void onTargetPackageInfoKnown(final PackageInfo info) {
@@ -996,7 +1122,7 @@
                 // argument as true. But in all cases where we don't reset the entire input state,
                 // we still want to tell the rich input connection about the new cursor position so
                 // that it can update its caches.
-                mConnection.resetCachesUponCursorMove(newSelStart,
+                mConnection.resetCachesUponCursorMoveAndReturnSuccess(newSelStart,
                         false /* shouldFinishComposition */);
             }
 
@@ -1135,11 +1261,13 @@
             return currentHeight;
         }
 
-        final MainKeyboardView mainKeyboardView = mKeyboardSwitcher.getMainKeyboardView();
-        if (mainKeyboardView == null) {
+        final View visibleKeyboardView = mKeyboardSwitcher.getVisibleKeyboardView();
+        if (visibleKeyboardView == null) {
             return 0;
         }
-        final int keyboardHeight = mainKeyboardView.getHeight();
+        // TODO: !!!!!!!!!!!!!!!!!!!! Handle different backing view heights between the main   !!!
+        // keyboard and the emoji keyboard. !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
+        final int keyboardHeight = visibleKeyboardView.getHeight();
         final int suggestionsHeight = mSuggestionStripView.getHeight();
         final int displayHeight = getResources().getDisplayMetrics().heightPixels;
         final Rect rect = new Rect();
@@ -1157,8 +1285,8 @@
     @Override
     public void onComputeInsets(final InputMethodService.Insets outInsets) {
         super.onComputeInsets(outInsets);
-        final MainKeyboardView mainKeyboardView = mKeyboardSwitcher.getMainKeyboardView();
-        if (mainKeyboardView == null || mSuggestionStripView == null) {
+        final View visibleKeyboardView = mKeyboardSwitcher.getVisibleKeyboardView();
+        if (visibleKeyboardView == null || mSuggestionStripView == null) {
             return;
         }
         final int adjustedBackingHeight = getAdjustedBackingViewHeight();
@@ -1173,13 +1301,16 @@
         final int extraHeight = extractHeight + backingHeight + suggestionsHeight;
         int visibleTopY = extraHeight;
         // Need to set touchable region only if input view is being shown
-        if (mainKeyboardView.isShown()) {
-            if (mSuggestionStripView.getVisibility() == View.VISIBLE) {
+        if (visibleKeyboardView.isShown()) {
+            // Note that the height of Emoji layout is the same as the height of the main keyboard
+            // and the suggestion strip
+            if (mKeyboardSwitcher.isShowingEmojiKeyboard()
+                    || mSuggestionStripView.getVisibility() == View.VISIBLE) {
                 visibleTopY -= suggestionsHeight;
             }
-            final int touchY = mainKeyboardView.isShowingMoreKeysPanel() ? 0 : visibleTopY;
-            final int touchWidth = mainKeyboardView.getWidth();
-            final int touchHeight = mainKeyboardView.getHeight() + extraHeight
+            final int touchY = mKeyboardSwitcher.isShowingMoreKeysPanel() ? 0 : visibleTopY;
+            final int touchWidth = visibleKeyboardView.getWidth();
+            final int touchHeight = visibleKeyboardView.getHeight() + extraHeight
                     // Extend touchable region below the keyboard.
                     + EXTENDED_TOUCHABLE_REGION_HEIGHT;
             outInsets.touchableInsets = InputMethodService.Insets.TOUCHABLE_INSETS_REGION;
@@ -1227,7 +1358,8 @@
         } else {
             setSuggestedWords(settingsValues.mSuggestPuncList, false);
         }
-        mConnection.resetCachesUponCursorMove(newCursorPosition, shouldFinishComposition);
+        mConnection.resetCachesUponCursorMoveAndReturnSuccess(newCursorPosition,
+                shouldFinishComposition);
     }
 
     private void resetComposingState(final boolean alsoResetLastComposedWord) {
@@ -1331,7 +1463,8 @@
                 || codePoint == Constants.CODE_CLOSING_PARENTHESIS
                 || codePoint == Constants.CODE_CLOSING_SQUARE_BRACKET
                 || codePoint == Constants.CODE_CLOSING_CURLY_BRACKET
-                || codePoint == Constants.CODE_CLOSING_ANGLE_BRACKET;
+                || codePoint == Constants.CODE_CLOSING_ANGLE_BRACKET
+                || codePoint == Constants.CODE_PLUS;
     }
 
     // Callback for the {@link SuggestionStripView}, to call when the "add to dictionary" hint is
@@ -1340,7 +1473,6 @@
     public void addWordToUserDictionary(final String word) {
         if (TextUtils.isEmpty(word)) {
             // Probably never supposed to happen, but just in case.
-            mPositionalInfoForUserDictPendingAddition = null;
             return;
         }
         final String wordToEdit;
@@ -1352,26 +1484,6 @@
         mUserDictionary.addWordToUserDictionary(wordToEdit);
     }
 
-    public void onWordAddedToUserDictionary(final String newSpelling) {
-        // If word was added but not by us, bail out
-        if (null == mPositionalInfoForUserDictPendingAddition) return;
-        if (mWordComposer.isComposingWord()) {
-            // We are late... give up and return
-            mPositionalInfoForUserDictPendingAddition = null;
-            return;
-        }
-        mPositionalInfoForUserDictPendingAddition.setActualWordBeingAdded(newSpelling);
-        if (mPositionalInfoForUserDictPendingAddition.tryReplaceWithActualWord(
-                mConnection, getCurrentInputEditorInfo(), mLastSelectionEnd,
-                mSubtypeSwitcher.getCurrentSubtypeLocale())) {
-            mPositionalInfoForUserDictPendingAddition = null;
-        }
-    }
-
-    private static boolean isAlphabet(final int code) {
-        return Character.isLetter(code);
-    }
-
     private void onSettingsKeyPressed() {
         if (isShowingOptionDialog()) return;
         showSubtypeSelectorAndSettings();
@@ -1479,7 +1591,7 @@
             break;
         case Constants.CODE_SHIFT:
             // Note: Calling back to the keyboard on Shift key is handled in
-            // {@link #onPressKey(int,boolean)} and {@link #onReleaseKey(int,boolean)}.
+            // {@link #onPressKey(int,int,boolean)} and {@link #onReleaseKey(int,boolean)}.
             final Keyboard currentKeyboard = switcher.getKeyboard();
             if (null != currentKeyboard && currentKeyboard.mId.isAlphabetKeyboard()) {
                 // TODO: Instead of checking for alphabetic keyboard here, separate keycodes for
@@ -1493,7 +1605,7 @@
             break;
         case Constants.CODE_SWITCH_ALPHA_SYMBOL:
             // Note: Calling back to the keyboard on symbol key is handled in
-            // {@link #onPressKey(int,boolean)} and {@link #onReleaseKey(int,boolean)}.
+            // {@link #onPressKey(int,int,boolean)} and {@link #onReleaseKey(int,boolean)}.
             break;
         case Constants.CODE_SETTINGS:
             onSettingsKeyPressed();
@@ -1511,7 +1623,8 @@
             handleLanguageSwitchKey();
             break;
         case Constants.CODE_EMOJI:
-            // TODO: Implement emoji keyboard switch.
+            // Note: Switching emoji keyboard is being handled in
+            // {@link KeyboardState#onCodeInput(int,int)}.
             break;
         case Constants.CODE_ENTER:
             final EditorInfo editorInfo = getCurrentInputEditorInfo();
@@ -1627,7 +1740,7 @@
 
     @Override
     public void onStartBatchInput() {
-        BatchInputUpdater.getInstance().onStartBatchInput(this);
+        mInputUpdater.onStartBatchInput();
         mHandler.cancelUpdateSuggestionStrip();
         mConnection.beginBatchEdit();
         final SettingsValues settingsValues = mSettings.getCurrent();
@@ -1667,46 +1780,41 @@
         mWordComposer.setCapitalizedModeAtStartComposingTime(getActualCapsMode());
     }
 
-    private static final class BatchInputUpdater implements Handler.Callback {
+    private static final class InputUpdater implements Handler.Callback {
         private final Handler mHandler;
-        private LatinIME mLatinIme;
+        private final LatinIME mLatinIme;
         private final Object mLock = new Object();
         private boolean mInBatchInput; // synchronized using {@link #mLock}.
 
-        private BatchInputUpdater() {
+        private InputUpdater(final LatinIME latinIme) {
             final HandlerThread handlerThread = new HandlerThread(
-                    BatchInputUpdater.class.getSimpleName());
+                    InputUpdater.class.getSimpleName());
             handlerThread.start();
             mHandler = new Handler(handlerThread.getLooper(), this);
-        }
-
-        // Initialization-on-demand holder
-        private static final class OnDemandInitializationHolder {
-            public static final BatchInputUpdater sInstance = new BatchInputUpdater();
-        }
-
-        public static BatchInputUpdater getInstance() {
-            return OnDemandInitializationHolder.sInstance;
+            mLatinIme = latinIme;
         }
 
         private static final int MSG_UPDATE_GESTURE_PREVIEW_AND_SUGGESTION_STRIP = 1;
+        private static final int MSG_GET_SUGGESTED_WORDS = 2;
 
         @Override
         public boolean handleMessage(final Message msg) {
             switch (msg.what) {
-            case MSG_UPDATE_GESTURE_PREVIEW_AND_SUGGESTION_STRIP:
-                updateBatchInput((InputPointers)msg.obj);
-                break;
+                case MSG_UPDATE_GESTURE_PREVIEW_AND_SUGGESTION_STRIP:
+                    updateBatchInput((InputPointers)msg.obj);
+                    break;
+                case MSG_GET_SUGGESTED_WORDS:
+                    mLatinIme.getSuggestedWords(msg.arg1, (OnGetSuggestedWordsCallback) msg.obj);
+                    break;
             }
             return true;
         }
 
         // Run in the UI thread.
-        public void onStartBatchInput(final LatinIME latinIme) {
+        public void onStartBatchInput() {
             synchronized (mLock) {
                 mHandler.removeMessages(MSG_UPDATE_GESTURE_PREVIEW_AND_SUGGESTION_STRIP);
                 mInBatchInput = true;
-                mLatinIme = latinIme;
                 mLatinIme.mHandler.showGesturePreviewAndSuggestionStrip(
                         SuggestedWords.EMPTY, false /* dismissGestureFloatingPreviewText */);
             }
@@ -1719,9 +1827,14 @@
                     // Batch input has ended or canceled while the message was being delivered.
                     return;
                 }
-                final SuggestedWords suggestedWords = getSuggestedWordsGestureLocked(batchPointers);
-                mLatinIme.mHandler.showGesturePreviewAndSuggestionStrip(
-                        suggestedWords, false /* dismissGestureFloatingPreviewText */);
+
+                getSuggestedWordsGestureLocked(batchPointers, new OnGetSuggestedWordsCallback() {
+                    @Override
+                    public void onGetSuggestedWords(final SuggestedWords suggestedWords) {
+                        mLatinIme.mHandler.showGesturePreviewAndSuggestionStrip(
+                                suggestedWords, false /* dismissGestureFloatingPreviewText */);
+                    }
+                });
             }
         }
 
@@ -1744,35 +1857,57 @@
         }
 
         // Run in the UI thread.
-        public SuggestedWords onEndBatchInput(final InputPointers batchPointers) {
-            synchronized (mLock) {
-                mInBatchInput = false;
-                final SuggestedWords suggestedWords = getSuggestedWordsGestureLocked(batchPointers);
-                mLatinIme.mHandler.showGesturePreviewAndSuggestionStrip(
-                        suggestedWords, true /* dismissGestureFloatingPreviewText */);
-                return suggestedWords;
+        public void onEndBatchInput(final InputPointers batchPointers) {
+            synchronized(mLock) {
+                getSuggestedWordsGestureLocked(batchPointers, new OnGetSuggestedWordsCallback() {
+                    @Override
+                    public void onGetSuggestedWords(final SuggestedWords suggestedWords) {
+                        mInBatchInput = false;
+                        mLatinIme.mHandler.showGesturePreviewAndSuggestionStrip(suggestedWords,
+                                true /* dismissGestureFloatingPreviewText */);
+                        mLatinIme.mHandler.onEndBatchInput(suggestedWords);
+                    }
+                });
             }
         }
 
         // {@link LatinIME#getSuggestedWords(int)} method calls with same session id have to
         // be synchronized.
-        private SuggestedWords getSuggestedWordsGestureLocked(final InputPointers batchPointers) {
+        private void getSuggestedWordsGestureLocked(final InputPointers batchPointers,
+                final OnGetSuggestedWordsCallback callback) {
             mLatinIme.mWordComposer.setBatchInputPointers(batchPointers);
-            final SuggestedWords suggestedWords =
-                    mLatinIme.getSuggestedWordsOrOlderSuggestions(Suggest.SESSION_GESTURE);
-            final int suggestionCount = suggestedWords.size();
-            if (suggestionCount <= 1) {
-                final String mostProbableSuggestion = (suggestionCount == 0) ? null
-                        : suggestedWords.getWord(0);
-                return mLatinIme.getOlderSuggestions(mostProbableSuggestion);
-            }
-            return suggestedWords;
+            mLatinIme.getSuggestedWordsOrOlderSuggestionsAsync(Suggest.SESSION_GESTURE,
+                    new OnGetSuggestedWordsCallback() {
+                @Override
+                public void onGetSuggestedWords(SuggestedWords suggestedWords) {
+                    final int suggestionCount = suggestedWords.size();
+                    if (suggestionCount <= 1) {
+                        final String mostProbableSuggestion = (suggestionCount == 0) ? null
+                                : suggestedWords.getWord(0);
+                        callback.onGetSuggestedWords(
+                                mLatinIme.getOlderSuggestions(mostProbableSuggestion));
+                    }
+                    callback.onGetSuggestedWords(suggestedWords);
+                }
+            });
+        }
+
+        public void getSuggestedWords(final int sessionId,
+                final OnGetSuggestedWordsCallback callback) {
+            mHandler.obtainMessage(MSG_GET_SUGGESTED_WORDS, sessionId, 0, callback).sendToTarget();
+        }
+
+        private void onDestroy() {
+            mHandler.removeMessages(MSG_GET_SUGGESTED_WORDS);
+            mHandler.removeMessages(MSG_UPDATE_GESTURE_PREVIEW_AND_SUGGESTION_STRIP);
+            mHandler.getLooper().quit();
         }
     }
 
+    // This method must run in UI Thread.
     private void showGesturePreviewAndSuggestionStrip(final SuggestedWords suggestedWords,
             final boolean dismissGestureFloatingPreviewText) {
-        showSuggestionStrip(suggestedWords, null);
+        showSuggestionStrip(suggestedWords);
         final MainKeyboardView mainKeyboardView = mKeyboardSwitcher.getMainKeyboardView();
         mainKeyboardView.showGestureFloatingPreviewText(suggestedWords);
         if (dismissGestureFloatingPreviewText) {
@@ -1782,24 +1917,48 @@
 
     @Override
     public void onUpdateBatchInput(final InputPointers batchPointers) {
-        BatchInputUpdater.getInstance().onUpdateBatchInput(batchPointers);
+        if (mSettings.getCurrent().mPhraseGestureEnabled) {
+            final SuggestedWordInfo candidate = mSuggestedWords.getAutoCommitCandidate();
+            if (null != candidate) {
+                if (candidate.mSourceDict.shouldAutoCommit(candidate)) {
+                    final String[] commitParts = candidate.mWord.split(" ", 2);
+                    batchPointers.shift(candidate.mIndexOfTouchPointOfSecondWord);
+                    promotePhantomSpace();
+                    mConnection.commitText(commitParts[0], 0);
+                    mSpaceState = SPACE_STATE_PHANTOM;
+                    mKeyboardSwitcher.updateShiftState();
+                    mWordComposer.setCapitalizedModeAtStartComposingTime(getActualCapsMode());
+                }
+            }
+        }
+        mInputUpdater.onUpdateBatchInput(batchPointers);
     }
 
-    @Override
-    public void onEndBatchInput(final InputPointers batchPointers) {
-        final SuggestedWords suggestedWords = BatchInputUpdater.getInstance().onEndBatchInput(
-                batchPointers);
+    // This method must run in UI Thread.
+    public void onEndBatchInputAsyncInternal(final SuggestedWords suggestedWords) {
         final String batchInputText = suggestedWords.isEmpty()
                 ? null : suggestedWords.getWord(0);
         if (TextUtils.isEmpty(batchInputText)) {
             return;
         }
-        mWordComposer.setBatchInputWord(batchInputText);
         mConnection.beginBatchEdit();
         if (SPACE_STATE_PHANTOM == mSpaceState) {
             promotePhantomSpace();
         }
-        mConnection.setComposingText(batchInputText, 1);
+        if (mSettings.getCurrent().mPhraseGestureEnabled) {
+            // Find the last space
+            final int indexOfLastSpace = batchInputText.lastIndexOf(Constants.CODE_SPACE) + 1;
+            if (0 != indexOfLastSpace) {
+                mConnection.commitText(batchInputText.substring(0, indexOfLastSpace), 1);
+                showSuggestionStrip(suggestedWords.getSuggestedWordsForLastWordOfPhraseGesture());
+            }
+            final String lastWord = batchInputText.substring(indexOfLastSpace);
+            mWordComposer.setBatchInputWord(lastWord);
+            mConnection.setComposingText(lastWord, 1);
+        } else {
+            mWordComposer.setBatchInputWord(batchInputText);
+            mConnection.setComposingText(batchInputText, 1);
+        }
         mExpectingUpdateSelection = true;
         mConnection.endBatchEdit();
         if (ProductionFlag.USES_DEVELOPMENT_ONLY_DIAGNOSTICS) {
@@ -1810,6 +1969,11 @@
         mKeyboardSwitcher.updateShiftState();
     }
 
+    @Override
+    public void onEndBatchInput(final InputPointers batchPointers) {
+        mInputUpdater.onEndBatchInput(batchPointers);
+    }
+
     private String specificTldProcessingOnTextInput(final String text) {
         if (text.length() <= 1 || text.charAt(0) != Constants.CODE_PERIOD
                 || !Character.isLetter(text.charAt(1))) {
@@ -1845,7 +2009,7 @@
 
     @Override
     public void onCancelBatchInput() {
-        BatchInputUpdater.getInstance().onCancelBatchInput();
+        mInputUpdater.onCancelBatchInput();
     }
 
     private void handleBackspace(final int spaceState) {
@@ -1861,23 +2025,23 @@
             // When we exit this if-clause, mWordComposer.isComposingWord() will return false.
         }
         if (mWordComposer.isComposingWord()) {
-            final int length = mWordComposer.size();
-            if (length > 0) {
-                if (mWordComposer.isBatchMode()) {
-                    if (ProductionFlag.USES_DEVELOPMENT_ONLY_DIAGNOSTICS) {
-                        final String word = mWordComposer.getTypedWord();
-                        ResearchLogger.latinIME_handleBackspace_batch(word, 1);
-                    }
-                    final String rejectedSuggestion = mWordComposer.getTypedWord();
-                    mWordComposer.reset();
-                    mWordComposer.setRejectedBatchModeSuggestion(rejectedSuggestion);
-                } else {
-                    mWordComposer.deleteLast();
+            if (mWordComposer.isBatchMode()) {
+                if (ProductionFlag.USES_DEVELOPMENT_ONLY_DIAGNOSTICS) {
+                    final String word = mWordComposer.getTypedWord();
+                    ResearchLogger.latinIME_handleBackspace_batch(word, 1);
                 }
-                mConnection.setComposingText(getTextWithUnderline(mWordComposer.getTypedWord()), 1);
-                mHandler.postUpdateSuggestionStrip();
+                final String rejectedSuggestion = mWordComposer.getTypedWord();
+                mWordComposer.reset();
+                mWordComposer.setRejectedBatchModeSuggestion(rejectedSuggestion);
             } else {
-                mConnection.deleteSurroundingText(1, 0);
+                mWordComposer.deleteLast();
+            }
+            mConnection.setComposingText(getTextWithUnderline(mWordComposer.getTypedWord()), 1);
+            mHandler.postUpdateSuggestionStrip();
+            if (!mWordComposer.isComposingWord()) {
+                // If we just removed the last character, auto-caps mode may have changed so we
+                // need to re-evaluate.
+                mKeyboardSwitcher.updateShiftState();
             }
         } else {
             final SettingsValues currentSettings = mSettings.getCurrent();
@@ -1892,8 +2056,7 @@
                 // Cancel multi-character input: remove the text we just entered.
                 // This is triggered on backspace after a key that inputs multiple characters,
                 // like the smiley key or the .com key.
-                final int length = mEnteredText.length();
-                mConnection.deleteSurroundingText(length, 0);
+                mConnection.deleteSurroundingText(mEnteredText.length(), 0);
                 if (ProductionFlag.USES_DEVELOPMENT_ONLY_DIAGNOSTICS) {
                     ResearchLogger.latinIME_handleBackspace_cancelTextInput(mEnteredText);
                 }
@@ -1939,6 +2102,8 @@
                     // This should never happen.
                     Log.e(TAG, "Backspace when we don't know the selection position");
                 }
+                final int lengthToDelete = Character.isSupplementaryCodePoint(
+                        mConnection.getCodePointBeforeCursor()) ? 2 : 1;
                 if (mAppWorkAroundsUtils.isBeforeJellyBean()) {
                     // Backward compatibility mode. Before Jelly bean, the keyboard would simulate
                     // a hardware keyboard event on pressing enter or delete. This is bad for many
@@ -1946,22 +2111,28 @@
                     // relying on this behavior so we continue to support it for older apps.
                     sendDownUpKeyEventForBackwardCompatibility(KeyEvent.KEYCODE_DEL);
                 } else {
-                    mConnection.deleteSurroundingText(1, 0);
+                    mConnection.deleteSurroundingText(lengthToDelete, 0);
                 }
                 if (ProductionFlag.USES_DEVELOPMENT_ONLY_DIAGNOSTICS) {
-                    ResearchLogger.latinIME_handleBackspace(1, true /* shouldUncommitLogUnit */);
+                    ResearchLogger.latinIME_handleBackspace(lengthToDelete,
+                            true /* shouldUncommitLogUnit */);
                 }
                 if (mDeleteCount > DELETE_ACCELERATE_AT) {
-                    mConnection.deleteSurroundingText(1, 0);
+                    final int lengthToDeleteAgain = Character.isSupplementaryCodePoint(
+                            mConnection.getCodePointBeforeCursor()) ? 2 : 1;
+                    mConnection.deleteSurroundingText(lengthToDeleteAgain, 0);
                     if (ProductionFlag.USES_DEVELOPMENT_ONLY_DIAGNOSTICS) {
-                        ResearchLogger.latinIME_handleBackspace(1,
+                        ResearchLogger.latinIME_handleBackspace(lengthToDeleteAgain,
                                 true /* shouldUncommitLogUnit */);
                     }
                 }
             }
-            if (currentSettings.isSuggestionsRequested(mDisplayOrientation)) {
+            if (currentSettings.isSuggestionsRequested(mDisplayOrientation)
+                    && currentSettings.mCurrentLanguageHasSpaces) {
                 restartSuggestionsOnWordBeforeCursorIfAtEndOfWord();
             }
+            // We just removed a character. We need to update the auto-caps state.
+            mKeyboardSwitcher.updateShiftState();
         }
     }
 
@@ -1986,6 +2157,9 @@
 
     private void handleCharacter(final int primaryCode, final int x,
             final int y, final int spaceState) {
+        // TODO: refactor this method to stop flipping isComposingWord around all the time, and
+        // make it shorter (possibly cut into several pieces). Also factor handleNonSpecialCharacter
+        // which has the same name as other handle* methods but is not the same.
         boolean isComposingWord = mWordComposer.isComposingWord();
 
         // TODO: remove isWordConnector() and use isUsuallyFollowedBySpace() instead.
@@ -2005,18 +2179,26 @@
             resetEntireInputState(mLastSelectionStart);
             isComposingWord = false;
         }
-        // NOTE: isCursorTouchingWord() is a blocking IPC call, so it often takes several
-        // dozen milliseconds. Avoid calling it as much as possible, since we are on the UI
-        // thread here.
-        if (!isComposingWord && (isAlphabet(primaryCode)
-                || currentSettings.isWordConnector(primaryCode))
+        // We want to find out whether to start composing a new word with this character. If so,
+        // we need to reset the composing state and switch isComposingWord. The order of the
+        // tests is important for good performance.
+        // We only start composing if we're not already composing.
+        if (!isComposingWord
+        // We only start composing if this is a word code point. Essentially that means it's a
+        // a letter or a word connector.
+                && currentSettings.isWordCodePoint(primaryCode)
+        // We never go into composing state if suggestions are not requested.
                 && currentSettings.isSuggestionsRequested(mDisplayOrientation) &&
-                !mConnection.isCursorTouchingWord(currentSettings)) {
+        // In languages with spaces, we only start composing a word when we are not already
+        // touching a word. In languages without spaces, the above conditions are sufficient.
+                (!mConnection.isCursorTouchingWord(currentSettings)
+                        || !currentSettings.mCurrentLanguageHasSpaces)) {
             // Reset entirely the composing state anyway, then start composing a new word unless
-            // the character is a single quote. The idea here is, single quote is not a
-            // separator and it should be treated as a normal character, except in the first
-            // position where it should not start composing a word.
-            isComposingWord = (Constants.CODE_SINGLE_QUOTE != primaryCode);
+            // the character is a single quote or a dash. The idea here is, single quote and dash
+            // are not separators and they should be treated as normal characters, except in the
+            // first position where they should not start composing a word.
+            isComposingWord = (Constants.CODE_SINGLE_QUOTE != primaryCode
+                    && Constants.CODE_DASH != primaryCode);
             // Here we don't need to reset the last composed word. It will be reset
             // when we commit this one, if we ever do; if on the other hand we backspace
             // it entirely and resume suggestions on the previous word, we'd like to still
@@ -2095,20 +2277,24 @@
         mKeyboardSwitcher.updateShiftState();
     }
 
-    // Returns true if we did an autocorrection, false otherwise.
+    // Returns true if we do an autocorrection, false otherwise.
     private boolean handleSeparator(final int primaryCode, final int x, final int y,
             final int spaceState) {
         boolean didAutoCorrect = false;
+        final SettingsValues currentSettings = mSettings.getCurrent();
+        // We avoid sending spaces in languages without spaces if we were composing.
+        final boolean shouldAvoidSendingCode = Constants.CODE_SPACE == primaryCode
+                && !currentSettings.mCurrentLanguageHasSpaces && mWordComposer.isComposingWord();
         if (mWordComposer.isCursorFrontOrMiddleOfComposingWord()) {
             // If we are in the middle of a recorrection, we need to commit the recorrection
             // first so that we can insert the separator at the current cursor position.
             resetEntireInputState(mLastSelectionStart);
         }
-        final SettingsValues currentSettings = mSettings.getCurrent();
-        if (mWordComposer.isComposingWord()) {
+        if (mWordComposer.isComposingWord()) { // May have changed since we stored wasComposing
             if (currentSettings.mCorrectionEnabled) {
-                // TODO: maybe cache Strings in an <String> sparse array or something
-                commitCurrentAutoCorrection(new String(new int[]{primaryCode}, 0, 1));
+                final String separator = shouldAvoidSendingCode ? LastComposedWord.NOT_A_SEPARATOR
+                        : new String(new int[] { primaryCode }, 0, 1);
+                commitCurrentAutoCorrection(separator);
                 didAutoCorrect = true;
             } else {
                 commitTyped(new String(new int[]{primaryCode}, 0, 1));
@@ -2125,7 +2311,10 @@
         if (ProductionFlag.USES_DEVELOPMENT_ONLY_DIAGNOSTICS) {
             ResearchLogger.latinIME_handleSeparator(primaryCode, mWordComposer.isComposingWord());
         }
-        sendKeyCodePoint(primaryCode);
+
+        if (!shouldAvoidSendingCode) {
+            sendKeyCodePoint(primaryCode);
+        }
 
         if (Constants.CODE_SPACE == primaryCode) {
             if (currentSettings.isSuggestionsRequested(mDisplayOrientation)) {
@@ -2255,34 +2444,60 @@
             return;
         }
 
-        final SuggestedWords suggestedWords =
-                getSuggestedWordsOrOlderSuggestions(Suggest.SESSION_TYPING);
-        final String typedWord = mWordComposer.getTypedWord();
-        showSuggestionStrip(suggestedWords, typedWord);
+        final AsyncResultHolder<SuggestedWords> holder = new AsyncResultHolder<SuggestedWords>();
+        getSuggestedWordsOrOlderSuggestionsAsync(Suggest.SESSION_TYPING,
+                new OnGetSuggestedWordsCallback() {
+                    @Override
+                    public void onGetSuggestedWords(final SuggestedWords suggestedWords) {
+                        holder.set(suggestedWords);
+                    }
+                }
+        );
+
+        // This line may cause the current thread to wait.
+        final SuggestedWords suggestedWords = holder.get(null, GET_SUGGESTED_WORDS_TIMEOUT);
+        if (suggestedWords != null) {
+            showSuggestionStrip(suggestedWords);
+        }
     }
 
-    private SuggestedWords getSuggestedWords(final int sessionId) {
+    private void getSuggestedWords(final int sessionId,
+            final OnGetSuggestedWordsCallback callback) {
         final Keyboard keyboard = mKeyboardSwitcher.getKeyboard();
         final Suggest suggest = mSuggest;
         if (keyboard == null || suggest == null) {
-            return SuggestedWords.EMPTY;
+            callback.onGetSuggestedWords(SuggestedWords.EMPTY);
+            return;
         }
         // Get the word on which we should search the bigrams. If we are composing a word, it's
         // whatever is *before* the half-committed word in the buffer, hence 2; if we aren't, we
         // should just skip whitespace if any, so 1.
-        // TODO: this is slow (2-way IPC) - we should probably cache this instead.
         final SettingsValues currentSettings = mSettings.getCurrent();
-        final String prevWord =
-                mConnection.getNthPreviousWord(currentSettings.mWordSeparators,
-                mWordComposer.isComposingWord() ? 2 : 1);
-        return suggest.getSuggestedWords(mWordComposer, prevWord, keyboard.getProximityInfo(),
-                currentSettings.mBlockPotentiallyOffensive,
-                currentSettings.mCorrectionEnabled, sessionId);
+        final int[] additionalFeaturesOptions = currentSettings.mAdditionalFeaturesSettingValues;
+        final String prevWord;
+        if (currentSettings.mCurrentLanguageHasSpaces) {
+            // If we are typing in a language with spaces we can just look up the previous
+            // word from textview.
+            prevWord = mConnection.getNthPreviousWord(currentSettings.mWordSeparators,
+                    mWordComposer.isComposingWord() ? 2 : 1);
+        } else {
+            prevWord = LastComposedWord.NOT_A_COMPOSED_WORD == mLastComposedWord ? null
+                    : mLastComposedWord.mCommittedWord;
+        }
+        suggest.getSuggestedWords(mWordComposer, prevWord, keyboard.getProximityInfo(),
+                currentSettings.mBlockPotentiallyOffensive, currentSettings.mCorrectionEnabled,
+                additionalFeaturesOptions, sessionId, callback);
     }
 
-    private SuggestedWords getSuggestedWordsOrOlderSuggestions(final int sessionId) {
-        return maybeRetrieveOlderSuggestions(mWordComposer.getTypedWord(),
-                getSuggestedWords(sessionId));
+    private void getSuggestedWordsOrOlderSuggestionsAsync(final int sessionId,
+            final OnGetSuggestedWordsCallback callback) {
+        mInputUpdater.getSuggestedWords(sessionId, new OnGetSuggestedWordsCallback() {
+            @Override
+            public void onGetSuggestedWords(SuggestedWords suggestedWords) {
+                callback.onGetSuggestedWords(maybeRetrieveOlderSuggestions(
+                        mWordComposer.getTypedWord(), suggestedWords));
+            }
+        });
     }
 
     private SuggestedWords maybeRetrieveOlderSuggestions(final String typedWord,
@@ -2322,25 +2537,42 @@
                 false /* isPrediction */);
     }
 
-    private void showSuggestionStrip(final SuggestedWords suggestedWords, final String typedWord) {
+    private void setAutoCorrection(final SuggestedWords suggestedWords, final String typedWord) {
+        if (suggestedWords.isEmpty()) return;
+        final String autoCorrection;
+        if (suggestedWords.mWillAutoCorrect) {
+            autoCorrection = suggestedWords.getWord(SuggestedWords.INDEX_OF_AUTO_CORRECTION);
+        } else {
+            // We can't use suggestedWords.getWord(SuggestedWords.INDEX_OF_TYPED_WORD)
+            // because it may differ from mWordComposer.mTypedWord.
+            autoCorrection = typedWord;
+        }
+        mWordComposer.setAutoCorrection(autoCorrection);
+    }
+
+    private void showSuggestionStripWithTypedWord(final SuggestedWords suggestedWords,
+            final String typedWord) {
+      if (suggestedWords.isEmpty()) {
+          clearSuggestionStrip();
+          return;
+      }
+      setAutoCorrection(suggestedWords, typedWord);
+      final boolean isAutoCorrection = suggestedWords.willAutoCorrect();
+      setSuggestedWords(suggestedWords, isAutoCorrection);
+      setAutoCorrectionIndicator(isAutoCorrection);
+      setSuggestionStripShown(isSuggestionsStripVisible());
+    }
+
+    private void showSuggestionStrip(final SuggestedWords suggestedWords) {
         if (suggestedWords.isEmpty()) {
             clearSuggestionStrip();
             return;
         }
-        final String autoCorrection;
-        if (suggestedWords.mWillAutoCorrect) {
-            autoCorrection = suggestedWords.getWord(1);
-        } else {
-            autoCorrection = typedWord;
-        }
-        mWordComposer.setAutoCorrection(autoCorrection);
-        final boolean isAutoCorrection = suggestedWords.willAutoCorrect();
-        setSuggestedWords(suggestedWords, isAutoCorrection);
-        setAutoCorrectionIndicator(isAutoCorrection);
-        setSuggestionStripShown(isSuggestionsStripVisible());
+        showSuggestionStripWithTypedWord(suggestedWords,
+            suggestedWords.getWord(SuggestedWords.INDEX_OF_TYPED_WORD));
     }
 
-    private void commitCurrentAutoCorrection(final String separatorString) {
+    private void commitCurrentAutoCorrection(final String separator) {
         // Complete any pending suggestions query first
         if (mHandler.hasPendingUpdateSuggestions()) {
             updateSuggestionStrip();
@@ -2356,16 +2588,16 @@
             }
             if (mSettings.isInternal()) {
                 LatinImeLoggerUtils.onAutoCorrection(
-                        typedWord, autoCorrection, separatorString, mWordComposer);
+                        typedWord, autoCorrection, separator, mWordComposer);
             }
             if (ProductionFlag.USES_DEVELOPMENT_ONLY_DIAGNOSTICS) {
                 final SuggestedWords suggestedWords = mSuggestedWords;
                 ResearchLogger.latinIme_commitCurrentAutoCorrection(typedWord, autoCorrection,
-                        separatorString, mWordComposer.isBatchMode(), suggestedWords);
+                        separator, mWordComposer.isBatchMode(), suggestedWords);
             }
             mExpectingUpdateSelection = true;
             commitChosenWord(autoCorrection, LastComposedWord.COMMIT_TYPE_DECIDED_WORD,
-                    separatorString);
+                    separator);
             if (!typedWord.equals(autoCorrection)) {
                 // This will make the correction flash for a short while as a visual clue
                 // to the user that auto-correction happened. It has no other effect; in particular
@@ -2440,7 +2672,7 @@
         if (ProductionFlag.USES_DEVELOPMENT_ONLY_DIAGNOSTICS) {
             ResearchLogger.latinIME_pickSuggestionManually(replacedWord, index, suggestion,
                     mWordComposer.isBatchMode(), suggestionInfo.mScore, suggestionInfo.mKind,
-                    suggestionInfo.mSourceDict);
+                    suggestionInfo.mSourceDict.mDictType);
         }
         mConnection.endBatchEdit();
         // Don't allow cancellation of manual pick
@@ -2535,6 +2767,13 @@
         return prevWord;
     }
 
+    private boolean isResumableWord(final String word, final SettingsValues settings) {
+        final int firstCodePoint = word.codePointAt(0);
+        return settings.isWordCodePoint(firstCodePoint)
+                && Constants.CODE_SINGLE_QUOTE != firstCodePoint
+                && Constants.CODE_DASH != firstCodePoint;
+    }
+
     /**
      * Check if the cursor is touching a word. If so, restart suggestions on this word, else
      * do nothing.
@@ -2544,6 +2783,11 @@
         // recorrection. This is a temporary, stopgap measure that will be removed later.
         // TODO: remove this.
         if (mAppWorkAroundsUtils.isBrokenByRecorrection()) return;
+        // A simple way to test for support from the TextView.
+        if (!isSuggestionsStripVisible()) return;
+        // Recorrection is not supported in languages without spaces because we don't know
+        // how to segment them yet.
+        if (!mSettings.getCurrent().mCurrentLanguageHasSpaces) return;
         // If the cursor is not touching a word, or if there is a selection, return right away.
         if (mLastSelectionStart != mLastSelectionEnd) return;
         // If we don't know the cursor location, return.
@@ -2553,12 +2797,14 @@
         final TextRange range = mConnection.getWordRangeAtCursor(currentSettings.mWordSeparators,
                 0 /* additionalPrecedingWordsCount */);
         if (null == range) return; // Happens if we don't have an input connection at all
+        if (range.length() <= 0) return; // Race condition. No text to resume on, so bail out.
         // If for some strange reason (editor bug or so) we measure the text before the cursor as
         // longer than what the entire text is supposed to be, the safe thing to do is bail out.
         final int numberOfCharsInWordBeforeCursor = range.getNumberOfCharsInWordBeforeCursor();
         if (numberOfCharsInWordBeforeCursor > mLastSelectionStart) return;
         final ArrayList<SuggestedWordInfo> suggestions = CollectionUtils.newArrayList();
         final String typedWord = range.mWord.toString();
+        if (!isResumableWord(typedWord, currentSettings)) return;
         int i = 0;
         for (final SuggestionSpan span : range.getSuggestionSpansAtWord()) {
             for (final String s : span.getSuggestions()) {
@@ -2566,51 +2812,69 @@
                 if (!TextUtils.equals(s, typedWord)) {
                     suggestions.add(new SuggestedWordInfo(s,
                             SuggestionStripView.MAX_SUGGESTIONS - i,
-                            SuggestedWordInfo.KIND_RESUMED, Dictionary.TYPE_RESUMED));
+                            SuggestedWordInfo.KIND_RESUMED, Dictionary.DICTIONARY_RESUMED,
+                            SuggestedWordInfo.NOT_AN_INDEX /* indexOfTouchPointOfSecondWord */,
+                            SuggestedWordInfo.NOT_A_CONFIDENCE
+                                    /* autoCommitFirstWordConfidence */));
                 }
             }
         }
         mWordComposer.setComposingWord(typedWord, mKeyboardSwitcher.getKeyboard());
-        // TODO: this is in chars but the callee expects code points!
-        mWordComposer.setCursorPositionWithinWord(numberOfCharsInWordBeforeCursor);
+        mWordComposer.setCursorPositionWithinWord(
+                typedWord.codePointCount(0, numberOfCharsInWordBeforeCursor));
         mConnection.setComposingRegion(
                 mLastSelectionStart - numberOfCharsInWordBeforeCursor,
                 mLastSelectionEnd + range.getNumberOfCharsInWordAfterCursor());
-        final SuggestedWords suggestedWords;
         if (suggestions.isEmpty()) {
             // We come here if there weren't any suggestion spans on this word. We will try to
             // compute suggestions for it instead.
-            final SuggestedWords suggestedWordsIncludingTypedWord =
-                    getSuggestedWords(Suggest.SESSION_TYPING);
-            if (suggestedWordsIncludingTypedWord.size() > 1) {
-                // We were able to compute new suggestions for this word.
-                // Remove the typed word, since we don't want to display it in this case.
-                // The #getSuggestedWordsExcludingTypedWord() method sets willAutoCorrect to false.
-                suggestedWords =
-                        suggestedWordsIncludingTypedWord.getSuggestedWordsExcludingTypedWord();
-            } else {
-                // No saved suggestions, and we were unable to compute any good one either.
-                // Rather than displaying an empty suggestion strip, we'll display the original
-                // word alone in the middle.
-                // Since there is only one word, willAutoCorrect is false.
-                suggestedWords = suggestedWordsIncludingTypedWord;
-            }
+            mInputUpdater.getSuggestedWords(Suggest.SESSION_TYPING,
+                    new OnGetSuggestedWordsCallback() {
+                @Override
+                public void onGetSuggestedWords(
+                        final SuggestedWords suggestedWordsIncludingTypedWord) {
+                    final SuggestedWords suggestedWords;
+                    if (suggestedWordsIncludingTypedWord.size() > 1) {
+                        // We were able to compute new suggestions for this word.
+                        // Remove the typed word, since we don't want to display it in this case.
+                        // The #getSuggestedWordsExcludingTypedWord() method sets willAutoCorrect to
+                        // false.
+                        suggestedWords = suggestedWordsIncludingTypedWord
+                                .getSuggestedWordsExcludingTypedWord();
+                    } else {
+                        // No saved suggestions, and we were unable to compute any good one either.
+                        // Rather than displaying an empty suggestion strip, we'll display the
+                        // original word alone in the middle.
+                        // Since there is only one word, willAutoCorrect is false.
+                        suggestedWords = suggestedWordsIncludingTypedWord;
+                    }
+                    // We need to pass typedWord because mWordComposer.mTypedWord may differ from
+                    // typedWord.
+                    unsetIsAutoCorrectionIndicatorOnAndCallShowSuggestionStrip(suggestedWords,
+                        typedWord);
+                }});
         } else {
             // We found suggestion spans in the word. We'll create the SuggestedWords out of
             // them, and make willAutoCorrect false.
-            suggestedWords = new SuggestedWords(suggestions,
+            final SuggestedWords suggestedWords = new SuggestedWords(suggestions,
                     true /* typedWordValid */, false /* willAutoCorrect */,
                     false /* isPunctuationSuggestions */, false /* isObsoleteSuggestions */,
                     false /* isPrediction */);
+            // We need to pass typedWord because mWordComposer.mTypedWord may differ from typedWord.
+            unsetIsAutoCorrectionIndicatorOnAndCallShowSuggestionStrip(suggestedWords, typedWord);
         }
+    }
 
+    public void unsetIsAutoCorrectionIndicatorOnAndCallShowSuggestionStrip(
+            final SuggestedWords suggestedWords, final String typedWord) {
         // Note that it's very important here that suggestedWords.mWillAutoCorrect is false.
-        // We never want to auto-correct on a resumed suggestion. Please refer to the three
-        // places above where suggestedWords is affected. We also need to reset
-        // mIsAutoCorrectionIndicatorOn to avoid showSuggestionStrip touching the text to adapt it.
-        // TODO: remove mIsAutoCorrectionIndicator on (see comment on definition)
+        // We never want to auto-correct on a resumed suggestion. Please refer to the three places
+        // above in restartSuggestionsOnWordTouchedByCursor() where suggestedWords is affected.
+        // We also need to unset mIsAutoCorrectionIndicatorOn to avoid showSuggestionStrip touching
+        // the text to adapt it.
+        // TODO: remove mIsAutoCorrectionIndicatorOn (see comment on definition)
         mIsAutoCorrectionIndicatorOn = false;
-        showSuggestionStrip(suggestedWords, typedWord);
+        mHandler.showSuggestionStripWithTypedWord(suggestedWords, typedWord);
     }
 
     /**
@@ -2640,6 +2904,27 @@
         mHandler.postUpdateSuggestionStrip();
     }
 
+    /**
+     * Retry resetting caches in the rich input connection.
+     *
+     * When the editor can't be accessed we can't reset the caches, so we schedule a retry.
+     * This method handles the retry, and re-schedules a new retry if we still can't access.
+     * We only retry up to 5 times before giving up.
+     *
+     * @param tryResumeSuggestions Whether we should resume suggestions or not.
+     * @param remainingTries How many times we may try again before giving up.
+     */
+    private void retryResetCaches(final boolean tryResumeSuggestions, final int remainingTries) {
+        if (!mConnection.resetCachesUponCursorMoveAndReturnSuccess(mLastSelectionStart, false)) {
+            if (0 < remainingTries) {
+                mHandler.postResetCaches(tryResumeSuggestions, remainingTries - 1);
+            }
+            return;
+        }
+        tryFixLyingCursorPosition();
+        if (tryResumeSuggestions) mHandler.postResumeSuggestions();
+    }
+
     private void revertCommit() {
         final String previousWord = mLastComposedWord.mPrevWord;
         final String originallyTypedWord = mLastComposedWord.mTypedWord;
@@ -2666,7 +2951,18 @@
         if (!TextUtils.isEmpty(previousWord) && !TextUtils.isEmpty(committedWord)) {
             mUserHistoryPredictionDictionary.cancelAddingUserHistory(previousWord, committedWord);
         }
-        mConnection.commitText(originallyTypedWord + mLastComposedWord.mSeparatorString, 1);
+        final String stringToCommit = originallyTypedWord + mLastComposedWord.mSeparatorString;
+        if (mSettings.getCurrent().mCurrentLanguageHasSpaces) {
+            // For languages with spaces, we revert to the typed string, but the cursor is still
+            // after the separator so we don't resume suggestions. If the user wants to correct
+            // the word, they have to press backspace again.
+            mConnection.commitText(stringToCommit, 1);
+        } else {
+            // For languages without spaces, we revert the typed string but the cursor is flush
+            // with the typed word, so we need to resume suggestions right away.
+            mWordComposer.setComposingWord(stringToCommit, mKeyboardSwitcher.getKeyboard());
+            mConnection.setComposingText(stringToCommit, 1);
+        }
         if (mSettings.isInternal()) {
             LatinImeLoggerUtils.onSeparator(mLastComposedWord.mSeparatorString,
                     Constants.NOT_A_COORDINATE, Constants.NOT_A_COORDINATE);
@@ -2684,7 +2980,9 @@
 
     // This essentially inserts a space, and that's it.
     public void promotePhantomSpace() {
-        if (mSettings.getCurrent().shouldInsertSpacesAutomatically()
+        final SettingsValues currentSettings = mSettings.getCurrent();
+        if (currentSettings.shouldInsertSpacesAutomatically()
+                && currentSettings.mCurrentLanguageHasSpaces
                 && !mConnection.textBeforeCursorLooksLikeURL()) {
             if (ProductionFlag.USES_DEVELOPMENT_ONLY_DIAGNOSTICS) {
                 ResearchLogger.latinIME_promotePhantomSpace();
@@ -2710,30 +3008,43 @@
         }
     }
 
-    private void hapticAndAudioFeedback(final int code, final boolean isRepeatKey) {
+    private void hapticAndAudioFeedback(final int code, final int repeatCount) {
         final MainKeyboardView keyboardView = mKeyboardSwitcher.getMainKeyboardView();
         if (keyboardView != null && keyboardView.isInSlidingKeyInput()) {
             // No need to feedback while sliding input.
             return;
         }
-        if (isRepeatKey && code == Constants.CODE_DELETE && !mConnection.canDeleteCharacters()) {
-            // No need to feedback when repeating delete key will have no effect.
-            return;
+        if (repeatCount > 0) {
+            if (code == Constants.CODE_DELETE && !mConnection.canDeleteCharacters()) {
+                // No need to feedback when repeat delete key will have no effect.
+                return;
+            }
+            // TODO: Use event time that the last feedback has been generated instead of relying on
+            // a repeat count to thin out feedback.
+            if (repeatCount % PERIOD_FOR_AUDIO_AND_HAPTIC_FEEDBACK_IN_KEY_REPEAT == 0) {
+                return;
+            }
         }
-        AudioAndHapticFeedbackManager.getInstance().hapticAndAudioFeedback(code, keyboardView);
+        final AudioAndHapticFeedbackManager feedbackManager =
+                AudioAndHapticFeedbackManager.getInstance();
+        if (repeatCount == 0) {
+            // TODO: Reconsider how to perform haptic feedback when repeating key.
+            feedbackManager.performHapticFeedback(keyboardView);
+        }
+        feedbackManager.performAudioFeedback(code);
     }
 
     // Callback of the {@link KeyboardActionListener}. This is called when a key is depressed;
     // release matching call is {@link #onReleaseKey(int,boolean)} below.
     @Override
-    public void onPressKey(final int primaryCode, final boolean isRepeatKey,
+    public void onPressKey(final int primaryCode, final int repeatCount,
             final boolean isSinglePointer) {
         mKeyboardSwitcher.onPressKey(primaryCode, isSinglePointer);
-        hapticAndAudioFeedback(primaryCode, isRepeatKey);
+        hapticAndAudioFeedback(primaryCode, repeatCount);
     }
 
     // Callback of the {@link KeyboardActionListener}. This is called when a key is released;
-    // press matching call is {@link #onPressKey(int,boolean,boolean)} above.
+    // press matching call is {@link #onPressKey(int,int,boolean)} above.
     @Override
     public void onReleaseKey(final int primaryCode, final boolean withSliding) {
         mKeyboardSwitcher.onReleaseKey(primaryCode, withSliding);
@@ -2749,17 +3060,6 @@
                 break;
             }
         }
-
-        if (Constants.CODE_DELETE == primaryCode) {
-            // This is a stopgap solution to avoid leaving a high surrogate alone in a text view.
-            // In the future, we need to deprecate deteleSurroundingText() and have a surrogate
-            // pair-friendly way of deleting characters in InputConnection.
-            // TODO: use getCodePointBeforeCursor instead to improve performance
-            final CharSequence lastChar = mConnection.getTextBeforeCursor(1, 0);
-            if (!TextUtils.isEmpty(lastChar) && Character.isHighSurrogate(lastChar.charAt(0))) {
-                mConnection.deleteSurroundingText(1, 0);
-            }
-        }
     }
 
     // Hooks for hardware keyboard
@@ -2895,6 +3195,12 @@
         return mSuggest.hasMainDictionary();
     }
 
+    // DO NOT USE THIS for any other purpose than testing. This can break the keyboard badly.
+    @UsedForTesting
+    /* package for test */ void replaceMainDictionaryForTest(final Locale locale) {
+        mSuggest.resetMainDict(this, locale, null);
+    }
+
     public void debugDumpStateAndCrashWithException(final String context) {
         final StringBuilder s = new StringBuilder(mAppWorkAroundsUtils.toString());
         s.append("\nAttributes : ").append(mSettings.getCurrent().mInputAttributes)
diff --git a/java/src/com/android/inputmethod/latin/RichInputConnection.java b/java/src/com/android/inputmethod/latin/RichInputConnection.java
index 35920f8..925381b 100644
--- a/java/src/com/android/inputmethod/latin/RichInputConnection.java
+++ b/java/src/com/android/inputmethod/latin/RichInputConnection.java
@@ -73,9 +73,6 @@
      * This contains the currently composing text, as LatinIME thinks the TextView is seeing it.
      */
     private final StringBuilder mComposingText = new StringBuilder();
-    // A hint on how many characters to cache from the TextView. A good value of this is given by
-    // how many characters we need to be able to almost always find the caps mode.
-    private static final int DEFAULT_TEXT_CACHE_SIZE = 100;
 
     private final InputMethodService mParent;
     InputConnection mIC;
@@ -93,7 +90,8 @@
         r.token = 1;
         r.flags = 0;
         final ExtractedText et = mIC.getExtractedText(r, 0);
-        final CharSequence beforeCursor = getTextBeforeCursor(DEFAULT_TEXT_CACHE_SIZE, 0);
+        final CharSequence beforeCursor = getTextBeforeCursor(Constants.EDITOR_CONTENTS_CACHE_SIZE,
+                0);
         final StringBuilder internal = new StringBuilder().append(mCommittedTextBeforeComposingText)
                 .append(mComposingText);
         if (null == et || null == beforeCursor) return;
@@ -142,19 +140,56 @@
         if (DEBUG_PREVIOUS_TEXT) checkConsistencyForDebug();
     }
 
-    public void resetCachesUponCursorMove(final int newCursorPosition,
+    /**
+     * Reset the cached text and retrieve it again from the editor.
+     *
+     * This should be called when the cursor moved. It's possible that we can't connect to
+     * the application when doing this; notably, this happens sometimes during rotation, probably
+     * because of a race condition in the framework. In this case, we just can't retrieve the
+     * data, so we empty the cache and note that we don't know the new cursor position, and we
+     * return false so that the caller knows about this and can retry later.
+     *
+     * @param newCursorPosition The new position of the cursor, as received from the system.
+     * @param shouldFinishComposition Whether we should finish the composition in progress.
+     * @return true if we were able to connect to the editor successfully, false otherwise. When
+     *   this method returns false, the caches could not be correctly refreshed so they were only
+     *   reset: the caller should try again later to return to normal operation.
+     */
+    public boolean resetCachesUponCursorMoveAndReturnSuccess(final int newCursorPosition,
             final boolean shouldFinishComposition) {
         mExpectedCursorPosition = newCursorPosition;
         mComposingText.setLength(0);
         mCommittedTextBeforeComposingText.setLength(0);
-        final CharSequence textBeforeCursor = getTextBeforeCursor(DEFAULT_TEXT_CACHE_SIZE, 0);
-        if (null != textBeforeCursor) mCommittedTextBeforeComposingText.append(textBeforeCursor);
+        mIC = mParent.getCurrentInputConnection();
+        // Call upon the inputconnection directly since our own method is using the cache, and
+        // we want to refresh it.
+        final CharSequence textBeforeCursor = null == mIC ? null :
+                mIC.getTextBeforeCursor(Constants.EDITOR_CONTENTS_CACHE_SIZE, 0);
+        if (null == textBeforeCursor) {
+            // For some reason the app thinks we are not connected to it. This looks like a
+            // framework bug... Fall back to ground state and return false.
+            mExpectedCursorPosition = INVALID_CURSOR_POSITION;
+            Log.e(TAG, "Unable to connect to the editor to retrieve text... will retry later");
+            return false;
+        }
+        mCommittedTextBeforeComposingText.append(textBeforeCursor);
+        final int lengthOfTextBeforeCursor = textBeforeCursor.length();
+        if (lengthOfTextBeforeCursor > newCursorPosition
+                || (lengthOfTextBeforeCursor < Constants.EDITOR_CONTENTS_CACHE_SIZE
+                        && newCursorPosition < Constants.EDITOR_CONTENTS_CACHE_SIZE)) {
+            // newCursorPosition may be lying -- when rotating the device (probably a framework
+            // bug). If we have less chars than we asked for, then we know how many chars we have,
+            // and if we got more than newCursorPosition says, then we know it was lying. In both
+            // cases the length is more reliable
+            mExpectedCursorPosition = lengthOfTextBeforeCursor;
+        }
         if (null != mIC && shouldFinishComposition) {
             mIC.finishComposingText();
             if (ProductionFlag.USES_DEVELOPMENT_ONLY_DIAGNOSTICS) {
                 ResearchLogger.richInputConnection_finishComposingText();
             }
         }
+        return true;
     }
 
     private void checkBatchEdit() {
@@ -169,7 +204,6 @@
         if (DEBUG_BATCH_NESTING) checkBatchEdit();
         if (DEBUG_PREVIOUS_TEXT) checkConsistencyForDebug();
         mCommittedTextBeforeComposingText.append(mComposingText);
-        mExpectedCursorPosition += mComposingText.length();
         mComposingText.setLength(0);
         if (null != mIC) {
             mIC.finishComposingText();
@@ -234,8 +268,11 @@
         // getCapsMode should be updated to be able to return a "not enough info" result so that
         // we can get more context only when needed.
         if (TextUtils.isEmpty(mCommittedTextBeforeComposingText) && 0 != mExpectedCursorPosition) {
-            mCommittedTextBeforeComposingText.append(
-                    getTextBeforeCursor(DEFAULT_TEXT_CACHE_SIZE, 0));
+            final CharSequence textBeforeCursor = getTextBeforeCursor(
+                    Constants.EDITOR_CONTENTS_CACHE_SIZE, 0);
+            if (!TextUtils.isEmpty(textBeforeCursor)) {
+                mCommittedTextBeforeComposingText.append(textBeforeCursor);
+            }
         }
         // This never calls InputConnection#getCapsMode - in fact, it's a static method that
         // never blocks or initiates IPC.
@@ -363,7 +400,7 @@
         if (DEBUG_BATCH_NESTING) checkBatchEdit();
         if (DEBUG_PREVIOUS_TEXT) checkConsistencyForDebug();
         final CharSequence textBeforeCursor =
-                getTextBeforeCursor(DEFAULT_TEXT_CACHE_SIZE + (end - start), 0);
+                getTextBeforeCursor(Constants.EDITOR_CONTENTS_CACHE_SIZE + (end - start), 0);
         mCommittedTextBeforeComposingText.setLength(0);
         if (!TextUtils.isEmpty(textBeforeCursor)) {
             final int indexOfStartOfComposingText =
@@ -405,7 +442,8 @@
         }
         mExpectedCursorPosition = start;
         mCommittedTextBeforeComposingText.setLength(0);
-        mCommittedTextBeforeComposingText.append(getTextBeforeCursor(DEFAULT_TEXT_CACHE_SIZE, 0));
+        mCommittedTextBeforeComposingText.append(
+                getTextBeforeCursor(Constants.EDITOR_CONTENTS_CACHE_SIZE, 0));
     }
 
     public void commitCorrection(final CorrectionInfo correctionInfo) {
@@ -524,9 +562,9 @@
         if (mIC == null || sep == null) {
             return null;
         }
-        final CharSequence before = mIC.getTextBeforeCursor(1000,
+        final CharSequence before = mIC.getTextBeforeCursor(Constants.EDITOR_CONTENTS_CACHE_SIZE,
                 InputConnection.GET_TEXT_WITH_STYLES);
-        final CharSequence after = mIC.getTextAfterCursor(1000,
+        final CharSequence after = mIC.getTextAfterCursor(Constants.EDITOR_CONTENTS_CACHE_SIZE,
                 InputConnection.GET_TEXT_WITH_STYLES);
         if (before == null || after == null) {
             return null;
@@ -569,8 +607,11 @@
             }
         }
 
-        return new TextRange(TextUtils.concat(before, after), startIndexInBefore,
-                before.length() + endIndexInAfter, before.length());
+        // We don't use TextUtils#concat because it copies all spans without respect to their
+        // nature. If the text includes a PARAGRAPH span and it has been split, then
+        // TextUtils#concat will crash when it tries to concat both sides of it.
+        return new TextRange(StringUtils.concatWithNonParagraphSuggestionSpansOnly(before, after),
+                startIndexInBefore, before.length() + endIndexInAfter, before.length());
     }
 
     public boolean isCursorTouchingWord(final SettingsValues settingsValues) {
diff --git a/java/src/com/android/inputmethod/latin/SubtypeSwitcher.java b/java/src/com/android/inputmethod/latin/SubtypeSwitcher.java
index be03d4a..cd9c89f 100644
--- a/java/src/com/android/inputmethod/latin/SubtypeSwitcher.java
+++ b/java/src/com/android/inputmethod/latin/SubtypeSwitcher.java
@@ -53,12 +53,25 @@
     private InputMethodInfo mShortcutInputMethodInfo;
     private InputMethodSubtype mShortcutSubtype;
     private InputMethodSubtype mNoLanguageSubtype;
+    private InputMethodSubtype mEmojiSubtype;
     private boolean mIsNetworkConnected;
 
     // Dummy no language QWERTY subtype. See {@link R.xml.method}.
     private static final InputMethodSubtype DUMMY_NO_LANGUAGE_SUBTYPE = new InputMethodSubtype(
-            R.string.subtype_no_language_qwerty, R.drawable.ic_subtype_keyboard, "zz", "keyboard",
-            "KeyboardLayoutSet=qwerty,AsciiCapable,EnabledWhenDefaultIsNotAsciiCapable",
+            R.string.subtype_no_language_qwerty, R.drawable.ic_ime_switcher_dark,
+            SubtypeLocaleUtils.NO_LANGUAGE, "keyboard", "KeyboardLayoutSet="
+                    + SubtypeLocaleUtils.QWERTY
+                    + "," + Constants.Subtype.ExtraValue.ASCII_CAPABLE
+                    + ",EnabledWhenDefaultIsNotAsciiCapable,"
+                    + Constants.Subtype.ExtraValue.EMOJI_CAPABLE,
+            false /* isAuxiliary */, false /* overridesImplicitlyEnabledSubtype */);
+    // Caveat: We probably should remove this when we add an Emoji subtype in {@link R.xml.method}.
+    // Dummy Emoji subtype. See {@link R.xml.method}.
+    private static final InputMethodSubtype DUMMY_EMOJI_SUBTYPE = new InputMethodSubtype(
+            R.string.subtype_emoji, R.drawable.ic_ime_switcher_dark,
+            SubtypeLocaleUtils.NO_LANGUAGE, "keyboard", "KeyboardLayoutSet="
+                    + SubtypeLocaleUtils.EMOJI + ","
+                    + Constants.Subtype.ExtraValue.EMOJI_CAPABLE,
             false /* isAuxiliary */, false /* overridesImplicitlyEnabledSubtype */);
 
     static final class NeedsToDisplayLanguage {
@@ -271,4 +284,17 @@
                 + DUMMY_NO_LANGUAGE_SUBTYPE);
         return DUMMY_NO_LANGUAGE_SUBTYPE;
     }
+
+    public InputMethodSubtype getEmojiSubtype() {
+        if (mEmojiSubtype == null) {
+            mEmojiSubtype = mRichImm.findSubtypeByLocaleAndKeyboardLayoutSet(
+                    SubtypeLocaleUtils.NO_LANGUAGE, SubtypeLocaleUtils.EMOJI);
+        }
+        if (mEmojiSubtype != null) {
+            return mEmojiSubtype;
+        }
+        Log.w(TAG, "Can't find Emoji subtype");
+        Log.w(TAG, "No input method subtype found; return dummy subtype: " + DUMMY_EMOJI_SUBTYPE);
+        return DUMMY_EMOJI_SUBTYPE;
+    }
 }
diff --git a/java/src/com/android/inputmethod/latin/Suggest.java b/java/src/com/android/inputmethod/latin/Suggest.java
index c2fdcb5..6c18c94 100644
--- a/java/src/com/android/inputmethod/latin/Suggest.java
+++ b/java/src/com/android/inputmethod/latin/Suggest.java
@@ -24,6 +24,7 @@
 import com.android.inputmethod.annotations.UsedForTesting;
 import com.android.inputmethod.keyboard.ProximityInfo;
 import com.android.inputmethod.latin.SuggestedWords.SuggestedWordInfo;
+import com.android.inputmethod.latin.personalization.PersonalizationDictionary;
 import com.android.inputmethod.latin.personalization.PersonalizationPredictionDictionary;
 import com.android.inputmethod.latin.personalization.UserHistoryPredictionDictionary;
 import com.android.inputmethod.latin.settings.Settings;
@@ -47,8 +48,9 @@
 
     // Session id for
     // {@link #getSuggestedWords(WordComposer,String,ProximityInfo,boolean,int)}.
+    // We are sharing the same ID between typing and gesture to save RAM footprint.
     public static final int SESSION_TYPING = 0;
-    public static final int SESSION_GESTURE = 1;
+    public static final int SESSION_GESTURE = 0;
 
     // TODO: rename this to CORRECTION_OFF
     public static final int CORRECTION_NONE = 0;
@@ -107,7 +109,7 @@
     }
 
     private void addOrReplaceDictionaryInternal(final String key, final Dictionary dict) {
-        if (mOnlyDictionarySetForDebug != null && mOnlyDictionarySetForDebug.contains(key)) {
+        if (mOnlyDictionarySetForDebug != null && !mOnlyDictionarySetForDebug.contains(key)) {
             Log.w(TAG, "Ignore add " + key + " dictionary for debug.");
             return;
         }
@@ -200,28 +202,41 @@
                 personalizationPredictionDictionary);
     }
 
+    public void setPersonalizationDictionary(
+            final PersonalizationDictionary personalizationDictionary) {
+        addOrReplaceDictionaryInternal(Dictionary.TYPE_PERSONALIZATION,
+                personalizationDictionary);
+    }
+
     public void setAutoCorrectionThreshold(float threshold) {
         mAutoCorrectionThreshold = threshold;
     }
 
-    public SuggestedWords getSuggestedWords(final WordComposer wordComposer,
+    public interface OnGetSuggestedWordsCallback {
+        public void onGetSuggestedWords(final SuggestedWords suggestedWords);
+    }
+
+    public void getSuggestedWords(final WordComposer wordComposer,
             final String prevWordForBigram, final ProximityInfo proximityInfo,
             final boolean blockOffensiveWords, final boolean isCorrectionEnabled,
-            final int sessionId) {
+            final int[] additionalFeaturesOptions, final int sessionId,
+            final OnGetSuggestedWordsCallback callback) {
         LatinImeLogger.onStartSuggestion(prevWordForBigram);
         if (wordComposer.isBatchMode()) {
-            return getSuggestedWordsForBatchInput(
-                    wordComposer, prevWordForBigram, proximityInfo, blockOffensiveWords, sessionId);
+            getSuggestedWordsForBatchInput(wordComposer, prevWordForBigram, proximityInfo,
+                    blockOffensiveWords, additionalFeaturesOptions, sessionId, callback);
         } else {
-            return getSuggestedWordsForTypingInput(wordComposer, prevWordForBigram, proximityInfo,
-                    blockOffensiveWords, isCorrectionEnabled);
+            getSuggestedWordsForTypingInput(wordComposer, prevWordForBigram, proximityInfo,
+                    blockOffensiveWords, isCorrectionEnabled, additionalFeaturesOptions, callback);
         }
     }
 
-    // Retrieves suggestions for the typing input.
-    private SuggestedWords getSuggestedWordsForTypingInput(final WordComposer wordComposer,
+    // Retrieves suggestions for the typing input
+    // and calls the callback function with the suggestions.
+    private void getSuggestedWordsForTypingInput(final WordComposer wordComposer,
             final String prevWordForBigram, final ProximityInfo proximityInfo,
-            final boolean blockOffensiveWords, final boolean isCorrectionEnabled) {
+            final boolean blockOffensiveWords, final boolean isCorrectionEnabled,
+            final int[] additionalFeaturesOptions, final OnGetSuggestedWordsCallback callback) {
         final int trailingSingleQuotesCount = wordComposer.trailingSingleQuotesCount();
         final BoundedTreeSet suggestionsSet = new BoundedTreeSet(sSuggestedWordInfoComparator,
                 MAX_SUGGESTIONS);
@@ -244,8 +259,9 @@
 
         for (final String key : mDictionaries.keySet()) {
             final Dictionary dictionary = mDictionaries.get(key);
-            suggestionsSet.addAll(dictionary.getSuggestions(
-                    wordComposerForLookup, prevWordForBigram, proximityInfo, blockOffensiveWords));
+            suggestionsSet.addAll(dictionary.getSuggestions(wordComposerForLookup,
+                    prevWordForBigram, proximityInfo, blockOffensiveWords,
+                    additionalFeaturesOptions));
         }
 
         final String whitelistedWord;
@@ -303,13 +319,16 @@
 
         for (int i = 0; i < suggestionsCount; ++i) {
             final SuggestedWordInfo wordInfo = suggestionsContainer.get(i);
-            LatinImeLogger.onAddSuggestedWord(wordInfo.mWord.toString(), wordInfo.mSourceDict);
+            LatinImeLogger.onAddSuggestedWord(wordInfo.mWord.toString(),
+                    wordInfo.mSourceDict.mDictType);
         }
 
         if (!TextUtils.isEmpty(typedWord)) {
             suggestionsContainer.add(0, new SuggestedWordInfo(typedWord,
                     SuggestedWordInfo.MAX_SCORE, SuggestedWordInfo.KIND_TYPED,
-                    Dictionary.TYPE_USER_TYPED));
+                    Dictionary.DICTIONARY_USER_TYPED,
+                    SuggestedWordInfo.NOT_AN_INDEX /* indexOfTouchPointOfSecondWord */,
+                    SuggestedWordInfo.NOT_A_CONFIDENCE /* autoCommitFirstWordConfidence */));
         }
         SuggestedWordInfo.removeDups(suggestionsContainer);
 
@@ -320,7 +339,7 @@
             suggestionsList = suggestionsContainer;
         }
 
-        return new SuggestedWords(suggestionsList,
+        callback.onGetSuggestedWords(new SuggestedWords(suggestionsList,
                 // TODO: this first argument is lying. If this is a whitelisted word which is an
                 // actual word, it says typedWordValid = false, which looks wrong. We should either
                 // rename the attribute or change the value.
@@ -328,31 +347,28 @@
                 hasAutoCorrection, /* willAutoCorrect */
                 false /* isPunctuationSuggestions */,
                 false /* isObsoleteSuggestions */,
-                !wordComposer.isComposingWord() /* isPrediction */);
+                !wordComposer.isComposingWord() /* isPrediction */));
     }
 
-    // Retrieves suggestions for the batch input.
-    private SuggestedWords getSuggestedWordsForBatchInput(final WordComposer wordComposer,
+    // Retrieves suggestions for the batch input
+    // and calls the callback function with the suggestions.
+    private void getSuggestedWordsForBatchInput(final WordComposer wordComposer,
             final String prevWordForBigram, final ProximityInfo proximityInfo,
-            final boolean blockOffensiveWords, final int sessionId) {
+            final boolean blockOffensiveWords, final int[] additionalFeaturesOptions,
+            final int sessionId, final OnGetSuggestedWordsCallback callback) {
         final BoundedTreeSet suggestionsSet = new BoundedTreeSet(sSuggestedWordInfoComparator,
                 MAX_SUGGESTIONS);
 
         // At second character typed, search the unigrams (scores being affected by bigrams)
         for (final String key : mDictionaries.keySet()) {
-            // Skip User history dictionary for lookup
-            // TODO: The user history dictionary should just override getSuggestionsWithSessionId
-            // to make sure it doesn't return anything and we should remove this test
-            if (key.equals(Dictionary.TYPE_USER_HISTORY)) {
-                continue;
-            }
             final Dictionary dictionary = mDictionaries.get(key);
             suggestionsSet.addAll(dictionary.getSuggestionsWithSessionId(wordComposer,
-                    prevWordForBigram, proximityInfo, blockOffensiveWords, sessionId));
+                    prevWordForBigram, proximityInfo, blockOffensiveWords,
+                    additionalFeaturesOptions, sessionId));
         }
 
         for (SuggestedWordInfo wordInfo : suggestionsSet) {
-            LatinImeLogger.onAddSuggestedWord(wordInfo.mWord, wordInfo.mSourceDict);
+            LatinImeLogger.onAddSuggestedWord(wordInfo.mWord, wordInfo.mSourceDict.mDictType);
         }
 
         final ArrayList<SuggestedWordInfo> suggestionsContainer =
@@ -387,12 +403,12 @@
 
         // In the batch input mode, the most relevant suggested word should act as a "typed word"
         // (typedWordValid=true), not as an "auto correct word" (willAutoCorrect=false).
-        return new SuggestedWords(suggestionsContainer,
+        callback.onGetSuggestedWords(new SuggestedWords(suggestionsContainer,
                 true /* typedWordValid */,
                 false /* willAutoCorrect */,
                 false /* isPunctuationSuggestions */,
                 false /* isObsoleteSuggestions */,
-                false /* isPrediction */);
+                false /* isPrediction */));
     }
 
     private static ArrayList<SuggestedWordInfo> getSuggestionsInfoListWithDebugInfo(
@@ -438,7 +454,7 @@
     private static final SuggestedWordInfoComparator sSuggestedWordInfoComparator =
             new SuggestedWordInfoComparator();
 
-    private static SuggestedWordInfo getTransformedSuggestedWordInfo(
+    /* package for test */ static SuggestedWordInfo getTransformedSuggestedWordInfo(
             final SuggestedWordInfo wordInfo, final Locale locale, final boolean isAllUpperCase,
             final boolean isFirstCharCapitalized, final int trailingSingleQuotesCount) {
         final StringBuilder sb = new StringBuilder(wordInfo.mWord.length());
@@ -449,11 +465,17 @@
         } else {
             sb.append(wordInfo.mWord);
         }
-        for (int i = trailingSingleQuotesCount - 1; i >= 0; --i) {
+        // Appending quotes is here to help people quote words. However, it's not helpful
+        // when they type words with quotes toward the end like "it's" or "didn't", where
+        // it's more likely the user missed the last character (or didn't type it yet).
+        final int quotesToAppend = trailingSingleQuotesCount
+                - (-1 == wordInfo.mWord.indexOf(Constants.CODE_SINGLE_QUOTE) ? 0 : 1);
+        for (int i = quotesToAppend - 1; i >= 0; --i) {
             sb.appendCodePoint(Constants.CODE_SINGLE_QUOTE);
         }
         return new SuggestedWordInfo(sb.toString(), wordInfo.mScore, wordInfo.mKind,
-                wordInfo.mSourceDict);
+                wordInfo.mSourceDict, wordInfo.mIndexOfTouchPointOfSecondWord,
+                SuggestedWordInfo.NOT_A_CONFIDENCE /* autoCommitFirstWordConfidence */);
     }
 
     public void close() {
diff --git a/java/src/com/android/inputmethod/latin/SuggestedWords.java b/java/src/com/android/inputmethod/latin/SuggestedWords.java
index 22beaef..fed4cdb 100644
--- a/java/src/com/android/inputmethod/latin/SuggestedWords.java
+++ b/java/src/com/android/inputmethod/latin/SuggestedWords.java
@@ -113,7 +113,9 @@
             if (null == text) continue;
             final SuggestedWordInfo suggestedWordInfo = new SuggestedWordInfo(text.toString(),
                     SuggestedWordInfo.MAX_SCORE, SuggestedWordInfo.KIND_APP_DEFINED,
-                    Dictionary.TYPE_APPLICATION_DEFINED);
+                    Dictionary.DICTIONARY_APPLICATION_DEFINED,
+                    SuggestedWordInfo.NOT_AN_INDEX /* indexOfTouchPointOfSecondWord */,
+                    SuggestedWordInfo.NOT_A_CONFIDENCE /* autoCommitFirstWordConfidence */);
             result.add(suggestedWordInfo);
         }
         return result;
@@ -126,7 +128,9 @@
         final ArrayList<SuggestedWordInfo> suggestionsList = CollectionUtils.newArrayList();
         final HashSet<String> alreadySeen = CollectionUtils.newHashSet();
         suggestionsList.add(new SuggestedWordInfo(typedWord, SuggestedWordInfo.MAX_SCORE,
-                SuggestedWordInfo.KIND_TYPED, Dictionary.TYPE_USER_TYPED));
+                SuggestedWordInfo.KIND_TYPED, Dictionary.DICTIONARY_USER_TYPED,
+                SuggestedWordInfo.NOT_AN_INDEX /* indexOfTouchPointOfSecondWord */,
+                SuggestedWordInfo.NOT_A_CONFIDENCE /* autoCommitFirstWordConfidence */));
         alreadySeen.add(typedWord.toString());
         final int previousSize = previousSuggestions.size();
         for (int index = 1; index < previousSize; index++) {
@@ -141,7 +145,15 @@
         return suggestionsList;
     }
 
+    public SuggestedWordInfo getAutoCommitCandidate() {
+        if (mSuggestedWordInfoList.size() <= 0) return null;
+        final SuggestedWordInfo candidate = mSuggestedWordInfoList.get(0);
+        return candidate.isEligibleForAutoCommit() ? candidate : null;
+    }
+
     public static final class SuggestedWordInfo {
+        public static final int NOT_AN_INDEX = -1;
+        public static final int NOT_A_CONFIDENCE = -1;
         public static final int MAX_SCORE = Integer.MAX_VALUE;
         public static final int KIND_MASK_KIND = 0xFF; // Mask to get only the kind
         public static final int KIND_TYPED = 0; // What user typed
@@ -166,18 +178,40 @@
         public final int mScore;
         public final int mKind; // one of the KIND_* constants above
         public final int mCodePointCount;
-        public final String mSourceDict;
+        public final Dictionary mSourceDict;
+        // For auto-commit. This keeps track of the index inside the touch coordinates array
+        // passed to native code to get suggestions for a gesture that corresponds to the first
+        // letter of the second word.
+        public final int mIndexOfTouchPointOfSecondWord;
+        // For auto-commit. This is a measure of how confident we are that we can commit the
+        // first word of this suggestion.
+        public final int mAutoCommitFirstWordConfidence;
         private String mDebugString = "";
 
+        /**
+         * Create a new suggested word info.
+         * @param word The string to suggest.
+         * @param score A measure of how likely this suggestion is.
+         * @param kind The kind of suggestion, as one of the above KIND_* constants.
+         * @param sourceDict What instance of Dictionary produced this suggestion.
+         * @param indexOfTouchPointOfSecondWord See mIndexOfTouchPointOfSecondWord.
+         * @param autoCommitFirstWordConfidence See mAutoCommitFirstWordConfidence.
+         */
         public SuggestedWordInfo(final String word, final int score, final int kind,
-                final String sourceDict) {
+                final Dictionary sourceDict, final int indexOfTouchPointOfSecondWord,
+                final int autoCommitFirstWordConfidence) {
             mWord = word;
             mScore = score;
             mKind = kind;
             mSourceDict = sourceDict;
             mCodePointCount = StringUtils.codePointCount(mWord);
+            mIndexOfTouchPointOfSecondWord = indexOfTouchPointOfSecondWord;
+            mAutoCommitFirstWordConfidence = autoCommitFirstWordConfidence;
         }
 
+        public boolean isEligibleForAutoCommit() {
+            return (KIND_CORRECTION == mKind && NOT_AN_INDEX != mIndexOfTouchPointOfSecondWord);
+        }
 
         public void setDebugString(final String str) {
             if (null == str) throw new NullPointerException("Debug info is null");
@@ -242,4 +276,24 @@
                 false /* willAutoCorrect */, mIsPunctuationSuggestions, mIsObsoleteSuggestions,
                 mIsPrediction);
     }
+
+    // Creates a new SuggestedWordInfo from the currently suggested words that removes all but the
+    // last word of all suggestions, separated by a space. This is necessary because when we commit
+    // a multiple-word suggestion, the IME only retains the last word as the composing word, and
+    // we should only suggest replacements for this last word.
+    // TODO: make this work with languages without spaces.
+    public SuggestedWords getSuggestedWordsForLastWordOfPhraseGesture() {
+        final ArrayList<SuggestedWordInfo> newSuggestions = CollectionUtils.newArrayList();
+        for (int i = 0; i < mSuggestedWordInfoList.size(); ++i) {
+            final SuggestedWordInfo info = mSuggestedWordInfoList.get(i);
+            final int indexOfLastSpace = info.mWord.lastIndexOf(Constants.CODE_SPACE) + 1;
+            final String lastWord = info.mWord.substring(indexOfLastSpace);
+            newSuggestions.add(new SuggestedWordInfo(lastWord, info.mScore, info.mKind,
+                    info.mSourceDict, SuggestedWordInfo.NOT_AN_INDEX,
+                    SuggestedWordInfo.NOT_A_CONFIDENCE));
+        }
+        return new SuggestedWords(newSuggestions, mTypedWordValid,
+                mWillAutoCorrect, mIsPunctuationSuggestions, mIsObsoleteSuggestions,
+                mIsPrediction);
+    }
 }
diff --git a/java/src/com/android/inputmethod/latin/SynchronouslyLoadedContactsBinaryDictionary.java b/java/src/com/android/inputmethod/latin/SynchronouslyLoadedContactsBinaryDictionary.java
index 92f96c0..3213c92 100644
--- a/java/src/com/android/inputmethod/latin/SynchronouslyLoadedContactsBinaryDictionary.java
+++ b/java/src/com/android/inputmethod/latin/SynchronouslyLoadedContactsBinaryDictionary.java
@@ -34,14 +34,15 @@
     @Override
     public synchronized ArrayList<SuggestedWordInfo> getSuggestions(final WordComposer codes,
             final String prevWordForBigrams, final ProximityInfo proximityInfo,
-            final boolean blockOffensiveWords) {
-        syncReloadDictionaryIfRequired();
-        return super.getSuggestions(codes, prevWordForBigrams, proximityInfo, blockOffensiveWords);
+            final boolean blockOffensiveWords, final int[] additionalFeaturesOptions) {
+        reloadDictionaryIfRequired();
+        return super.getSuggestions(codes, prevWordForBigrams, proximityInfo, blockOffensiveWords,
+                additionalFeaturesOptions);
     }
 
     @Override
     public synchronized boolean isValidWord(final String word) {
-        syncReloadDictionaryIfRequired();
+        reloadDictionaryIfRequired();
         return isValidWordInner(word);
     }
 
diff --git a/java/src/com/android/inputmethod/latin/SynchronouslyLoadedUserBinaryDictionary.java b/java/src/com/android/inputmethod/latin/SynchronouslyLoadedUserBinaryDictionary.java
index 33fe896..6405b5e 100644
--- a/java/src/com/android/inputmethod/latin/SynchronouslyLoadedUserBinaryDictionary.java
+++ b/java/src/com/android/inputmethod/latin/SynchronouslyLoadedUserBinaryDictionary.java
@@ -37,14 +37,15 @@
     @Override
     public synchronized ArrayList<SuggestedWordInfo> getSuggestions(final WordComposer codes,
             final String prevWordForBigrams, final ProximityInfo proximityInfo,
-            final boolean blockOffensiveWords) {
-        syncReloadDictionaryIfRequired();
-        return super.getSuggestions(codes, prevWordForBigrams, proximityInfo, blockOffensiveWords);
+            final boolean blockOffensiveWords, final int[] additionalFeaturesOptions) {
+        reloadDictionaryIfRequired();
+        return super.getSuggestions(codes, prevWordForBigrams, proximityInfo, blockOffensiveWords,
+                additionalFeaturesOptions);
     }
 
     @Override
     public synchronized boolean isValidWord(final String word) {
-        syncReloadDictionaryIfRequired();
+        reloadDictionaryIfRequired();
         return isValidWordInner(word);
     }
 }
diff --git a/java/src/com/android/inputmethod/latin/UserBinaryDictionary.java b/java/src/com/android/inputmethod/latin/UserBinaryDictionary.java
index ed6fefa..864a173 100644
--- a/java/src/com/android/inputmethod/latin/UserBinaryDictionary.java
+++ b/java/src/com/android/inputmethod/latin/UserBinaryDictionary.java
@@ -22,10 +22,12 @@
 import android.content.Context;
 import android.database.ContentObserver;
 import android.database.Cursor;
+import android.database.sqlite.SQLiteException;
 import android.net.Uri;
 import android.os.Build;
 import android.provider.UserDictionary.Words;
 import android.text.TextUtils;
+import android.util.Log;
 
 import com.android.inputmethod.compat.UserDictionaryCompatUtils;
 import com.android.inputmethod.latin.utils.LocaleUtils;
@@ -39,6 +41,7 @@
  * dictionary file to use it from native code.
  */
 public class UserBinaryDictionary extends ExpandableBinaryDictionary {
+    private static final String TAG = ExpandableBinaryDictionary.class.getSimpleName();
 
     // The user dictionary provider uses an empty string to mean "all languages".
     private static final String USER_DICTIONARY_ALL_LANGUAGES = "";
@@ -75,7 +78,8 @@
 
     public UserBinaryDictionary(final Context context, final String locale,
             final boolean alsoUseMoreRestrictiveLocales) {
-        super(context, getFilenameWithLocale(NAME, locale), Dictionary.TYPE_USER);
+        super(context, getFilenameWithLocale(NAME, locale), Dictionary.TYPE_USER,
+                false /* isUpdatable */);
         if (null == locale) throw new NullPointerException(); // Catch the error earlier
         if (SubtypeLocaleUtils.NO_LANGUAGE.equals(locale)) {
             // If we don't have a locale, insert into the "all locales" user dictionary.
@@ -102,14 +106,6 @@
             @Override
             public void onChange(final boolean self, final Uri uri) {
                 setRequiresReload(true);
-                // We want to report back to Latin IME in case the user just entered the word.
-                // If the user changed the word in the dialog box, then we want to replace
-                // what was entered in the text field.
-                if (null == uri || !(context instanceof LatinIME)) return;
-                final long changedRowId = ContentUris.parseId(uri);
-                if (-1 == changedRowId) return; // Unknown content... Not sure why we're here
-                final String changedWord = getChangedWordForUri(uri);
-                ((LatinIME)context).onWordAddedToUserDictionary(changedWord);
             }
         };
         cres.registerContentObserver(Words.CONTENT_URI, true, mObserver);
@@ -117,19 +113,6 @@
         loadDictionary();
     }
 
-    private String getChangedWordForUri(final Uri uri) {
-        final Cursor cursor = mContext.getContentResolver().query(uri,
-                PROJECTION_QUERY, null, null, null);
-        if (cursor == null) return null;
-        try {
-            if (!cursor.moveToFirst()) return null;
-            final int indexWord = cursor.getColumnIndex(Words.WORD);
-            return cursor.getString(indexWord);
-        } finally {
-            cursor.close();
-        }
-    }
-
     @Override
     public synchronized void close() {
         if (mObserver != null) {
@@ -188,12 +171,19 @@
         } else {
             requestArguments = localeElements;
         }
-        final Cursor cursor = mContext.getContentResolver().query(
-                Words.CONTENT_URI, PROJECTION_QUERY, request.toString(), requestArguments, null);
+        Cursor cursor = null;
         try {
+            cursor = mContext.getContentResolver().query(
+                Words.CONTENT_URI, PROJECTION_QUERY, request.toString(), requestArguments, null);
             addWords(cursor);
+        } catch (final SQLiteException e) {
+            Log.e(TAG, "SQLiteException in the remote User dictionary process.", e);
         } finally {
-            if (null != cursor) cursor.close();
+            try {
+                if (null != cursor) cursor.close();
+            } catch (final SQLiteException e) {
+                Log.e(TAG, "SQLiteException in the remote User dictionary process.", e);
+            }
         }
     }
 
diff --git a/java/src/com/android/inputmethod/latin/WordComposer.java b/java/src/com/android/inputmethod/latin/WordComposer.java
index a09ca60..039dadc 100644
--- a/java/src/com/android/inputmethod/latin/WordComposer.java
+++ b/java/src/com/android/inputmethod/latin/WordComposer.java
@@ -272,8 +272,8 @@
         final int x, y;
         final Key key;
         if (keyboard != null && (key = keyboard.getKey(codePoint)) != null) {
-            x = key.mX + key.mWidth / 2;
-            y = key.mY + key.mHeight / 2;
+            x = key.getX() + key.getWidth() / 2;
+            y = key.getY() + key.getHeight() / 2;
         } else {
             x = Constants.NOT_A_COORDINATE;
             y = Constants.NOT_A_COORDINATE;
diff --git a/java/src/com/android/inputmethod/latin/makedict/BinaryDictDecoderUtils.java b/java/src/com/android/inputmethod/latin/makedict/BinaryDictDecoderUtils.java
new file mode 100644
index 0000000..665c7a2
--- /dev/null
+++ b/java/src/com/android/inputmethod/latin/makedict/BinaryDictDecoderUtils.java
@@ -0,0 +1,624 @@
+/*
+ * Copyright (C) 2013 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.latin.makedict;
+
+import com.android.inputmethod.annotations.UsedForTesting;
+import com.android.inputmethod.latin.makedict.FormatSpec.FileHeader;
+import com.android.inputmethod.latin.makedict.FormatSpec.FormatOptions;
+import com.android.inputmethod.latin.makedict.FusionDictionary.PtNode;
+import com.android.inputmethod.latin.makedict.FusionDictionary.PtNodeArray;
+import com.android.inputmethod.latin.makedict.FusionDictionary.WeightedString;
+
+import java.io.ByteArrayOutputStream;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.nio.ByteBuffer;
+import java.nio.channels.FileChannel;
+import java.util.ArrayList;
+import java.util.Map;
+import java.util.TreeMap;
+
+/**
+ * Decodes binary files for a FusionDictionary.
+ *
+ * All the methods in this class are static.
+ *
+ * TODO: Remove calls from classes except Ver3DictDecoder
+ * TODO: Move this file to makedict/internal.
+ * TODO: Rename this class to DictDecoderUtils.
+ */
+public final class BinaryDictDecoderUtils {
+
+    private static final boolean DBG = MakedictLog.DBG;
+
+    private BinaryDictDecoderUtils() {
+        // This utility class is not publicly instantiable.
+    }
+
+    private static final int MAX_JUMPS = 12;
+
+    @UsedForTesting
+    public interface DictBuffer {
+        public int readUnsignedByte();
+        public int readUnsignedShort();
+        public int readUnsignedInt24();
+        public int readInt();
+        public int position();
+        public void position(int newPosition);
+        public void put(final byte b);
+        public int limit();
+        @UsedForTesting
+        public int capacity();
+    }
+
+    public static final class ByteBufferDictBuffer implements DictBuffer {
+        private ByteBuffer mBuffer;
+
+        public ByteBufferDictBuffer(final ByteBuffer buffer) {
+            mBuffer = buffer;
+        }
+
+        @Override
+        public int readUnsignedByte() {
+            return mBuffer.get() & 0xFF;
+        }
+
+        @Override
+        public int readUnsignedShort() {
+            return mBuffer.getShort() & 0xFFFF;
+        }
+
+        @Override
+        public int readUnsignedInt24() {
+            final int retval = readUnsignedByte();
+            return (retval << 16) + readUnsignedShort();
+        }
+
+        @Override
+        public int readInt() {
+            return mBuffer.getInt();
+        }
+
+        @Override
+        public int position() {
+            return mBuffer.position();
+        }
+
+        @Override
+        public void position(int newPos) {
+            mBuffer.position(newPos);
+        }
+
+        @Override
+        public void put(final byte b) {
+            mBuffer.put(b);
+        }
+
+        @Override
+        public int limit() {
+            return mBuffer.limit();
+        }
+
+        @Override
+        public int capacity() {
+            return mBuffer.capacity();
+        }
+    }
+
+    /**
+     * A class grouping utility function for our specific character encoding.
+     */
+    static final class CharEncoding {
+        private static final int MINIMAL_ONE_BYTE_CHARACTER_VALUE = 0x20;
+        private static final int MAXIMAL_ONE_BYTE_CHARACTER_VALUE = 0xFF;
+
+        /**
+         * Helper method to find out whether this code fits on one byte
+         */
+        private static boolean fitsOnOneByte(final int character) {
+            return character >= MINIMAL_ONE_BYTE_CHARACTER_VALUE
+                    && character <= MAXIMAL_ONE_BYTE_CHARACTER_VALUE;
+        }
+
+        /**
+         * Compute the size of a character given its character code.
+         *
+         * Char format is:
+         * 1 byte = bbbbbbbb match
+         * case 000xxxxx: xxxxx << 16 + next byte << 8 + next byte
+         * else: if 00011111 (= 0x1F) : this is the terminator. This is a relevant choice because
+         *       unicode code points range from 0 to 0x10FFFF, so any 3-byte value starting with
+         *       00011111 would be outside unicode.
+         * else: iso-latin-1 code
+         * This allows for the whole unicode range to be encoded, including chars outside of
+         * the BMP. Also everything in the iso-latin-1 charset is only 1 byte, except control
+         * characters which should never happen anyway (and still work, but take 3 bytes).
+         *
+         * @param character the character code.
+         * @return the size in binary encoded-form, either 1 or 3 bytes.
+         */
+        static int getCharSize(final int character) {
+            // See char encoding in FusionDictionary.java
+            if (fitsOnOneByte(character)) return 1;
+            if (FormatSpec.INVALID_CHARACTER == character) return 1;
+            return 3;
+        }
+
+        /**
+         * Compute the byte size of a character array.
+         */
+        static int getCharArraySize(final int[] chars) {
+            int size = 0;
+            for (int character : chars) size += getCharSize(character);
+            return size;
+        }
+
+        /**
+         * Writes a char array to a byte buffer.
+         *
+         * @param codePoints the code point array to write.
+         * @param buffer the byte buffer to write to.
+         * @param index the index in buffer to write the character array to.
+         * @return the index after the last character.
+         */
+        static int writeCharArray(final int[] codePoints, final byte[] buffer, int index) {
+            for (int codePoint : codePoints) {
+                if (1 == getCharSize(codePoint)) {
+                    buffer[index++] = (byte)codePoint;
+                } else {
+                    buffer[index++] = (byte)(0xFF & (codePoint >> 16));
+                    buffer[index++] = (byte)(0xFF & (codePoint >> 8));
+                    buffer[index++] = (byte)(0xFF & codePoint);
+                }
+            }
+            return index;
+        }
+
+        /**
+         * Writes a string with our character format to a byte buffer.
+         *
+         * This will also write the terminator byte.
+         *
+         * @param buffer the byte buffer to write to.
+         * @param origin the offset to write from.
+         * @param word the string to write.
+         * @return the size written, in bytes.
+         */
+        static int writeString(final byte[] buffer, final int origin,
+                final String word) {
+            final int length = word.length();
+            int index = origin;
+            for (int i = 0; i < length; i = word.offsetByCodePoints(i, 1)) {
+                final int codePoint = word.codePointAt(i);
+                if (1 == getCharSize(codePoint)) {
+                    buffer[index++] = (byte)codePoint;
+                } else {
+                    buffer[index++] = (byte)(0xFF & (codePoint >> 16));
+                    buffer[index++] = (byte)(0xFF & (codePoint >> 8));
+                    buffer[index++] = (byte)(0xFF & codePoint);
+                }
+            }
+            buffer[index++] = FormatSpec.PTNODE_CHARACTERS_TERMINATOR;
+            return index - origin;
+        }
+
+        /**
+         * Writes a string with our character format to a ByteArrayOutputStream.
+         *
+         * This will also write the terminator byte.
+         *
+         * @param buffer the ByteArrayOutputStream to write to.
+         * @param word the string to write.
+         */
+        static void writeString(final ByteArrayOutputStream buffer, final String word) {
+            final int length = word.length();
+            for (int i = 0; i < length; i = word.offsetByCodePoints(i, 1)) {
+                final int codePoint = word.codePointAt(i);
+                if (1 == getCharSize(codePoint)) {
+                    buffer.write((byte) codePoint);
+                } else {
+                    buffer.write((byte) (0xFF & (codePoint >> 16)));
+                    buffer.write((byte) (0xFF & (codePoint >> 8)));
+                    buffer.write((byte) (0xFF & codePoint));
+                }
+            }
+            buffer.write(FormatSpec.PTNODE_CHARACTERS_TERMINATOR);
+        }
+
+        /**
+         * Reads a string from a DictBuffer. This is the converse of the above method.
+         */
+        static String readString(final DictBuffer dictBuffer) {
+            final StringBuilder s = new StringBuilder();
+            int character = readChar(dictBuffer);
+            while (character != FormatSpec.INVALID_CHARACTER) {
+                s.appendCodePoint(character);
+                character = readChar(dictBuffer);
+            }
+            return s.toString();
+        }
+
+        /**
+         * Reads a character from the buffer.
+         *
+         * This follows the character format documented earlier in this source file.
+         *
+         * @param dictBuffer the buffer, positioned over an encoded character.
+         * @return the character code.
+         */
+        static int readChar(final DictBuffer dictBuffer) {
+            int character = dictBuffer.readUnsignedByte();
+            if (!fitsOnOneByte(character)) {
+                if (FormatSpec.PTNODE_CHARACTERS_TERMINATOR == character) {
+                    return FormatSpec.INVALID_CHARACTER;
+                }
+                character <<= 16;
+                character += dictBuffer.readUnsignedShort();
+            }
+            return character;
+        }
+    }
+
+    // Input methods: Read a binary dictionary to memory.
+    // readDictionaryBinary is the public entry point for them.
+
+    static int readSInt24(final DictBuffer dictBuffer) {
+        final int retval = dictBuffer.readUnsignedInt24();
+        final int sign = ((retval & FormatSpec.MSB24) != 0) ? -1 : 1;
+        return sign * (retval & FormatSpec.SINT24_MAX);
+    }
+
+    static int readChildrenAddress(final DictBuffer dictBuffer,
+            final int optionFlags, final FormatOptions options) {
+        if (options.mSupportsDynamicUpdate) {
+            final int address = dictBuffer.readUnsignedInt24();
+            if (address == 0) return FormatSpec.NO_CHILDREN_ADDRESS;
+            if ((address & FormatSpec.MSB24) != 0) {
+                return -(address & FormatSpec.SINT24_MAX);
+            } else {
+                return address;
+            }
+        }
+        int address;
+        switch (optionFlags & FormatSpec.MASK_CHILDREN_ADDRESS_TYPE) {
+            case FormatSpec.FLAG_CHILDREN_ADDRESS_TYPE_ONEBYTE:
+                return dictBuffer.readUnsignedByte();
+            case FormatSpec.FLAG_CHILDREN_ADDRESS_TYPE_TWOBYTES:
+                return dictBuffer.readUnsignedShort();
+            case FormatSpec.FLAG_CHILDREN_ADDRESS_TYPE_THREEBYTES:
+                return dictBuffer.readUnsignedInt24();
+            case FormatSpec.FLAG_CHILDREN_ADDRESS_TYPE_NOADDRESS:
+            default:
+                return FormatSpec.NO_CHILDREN_ADDRESS;
+        }
+    }
+
+    static int readParentAddress(final DictBuffer dictBuffer,
+            final FormatOptions formatOptions) {
+        if (BinaryDictIOUtils.supportsDynamicUpdate(formatOptions)) {
+            final int parentAddress = dictBuffer.readUnsignedInt24();
+            final int sign = ((parentAddress & FormatSpec.MSB24) != 0) ? -1 : 1;
+            return sign * (parentAddress & FormatSpec.SINT24_MAX);
+        } else {
+            return FormatSpec.NO_PARENT_ADDRESS;
+        }
+    }
+
+    /**
+     * Reads and returns the PtNode count out of a buffer and forwards the pointer.
+     */
+    /* package */ static int readPtNodeCount(final DictBuffer dictBuffer) {
+        final int msb = dictBuffer.readUnsignedByte();
+        if (FormatSpec.MAX_PTNODES_FOR_ONE_BYTE_PTNODE_COUNT >= msb) {
+            return msb;
+        } else {
+            return ((FormatSpec.MAX_PTNODES_FOR_ONE_BYTE_PTNODE_COUNT & msb) << 8)
+                    + dictBuffer.readUnsignedByte();
+        }
+    }
+
+    /**
+     * Finds, as a string, the word at the position passed as an argument.
+     *
+     * @param dictDecoder the dict decoder.
+     * @param headerSize the size of the header.
+     * @param pos the position to seek.
+     * @param formatOptions file format options.
+     * @return the word with its frequency, as a weighted string.
+     */
+    /* package for tests */ static WeightedString getWordAtPosition(final DictDecoder dictDecoder,
+            final int headerSize, final int pos, final FormatOptions formatOptions) {
+        final WeightedString result;
+        final int originalPos = dictDecoder.getPosition();
+        dictDecoder.setPosition(pos);
+
+        if (BinaryDictIOUtils.supportsDynamicUpdate(formatOptions)) {
+            result = getWordAtPositionWithParentAddress(dictDecoder, pos, formatOptions);
+        } else {
+            result = getWordAtPositionWithoutParentAddress(dictDecoder, headerSize, pos,
+                    formatOptions);
+        }
+
+        dictDecoder.setPosition(originalPos);
+        return result;
+    }
+
+    @SuppressWarnings("unused")
+    private static WeightedString getWordAtPositionWithParentAddress(final DictDecoder dictDecoder,
+            final int pos, final FormatOptions options) {
+        int currentPos = pos;
+        int frequency = Integer.MIN_VALUE;
+        final StringBuilder builder = new StringBuilder();
+        // the length of the path from the root to the leaf is limited by MAX_WORD_LENGTH
+        for (int count = 0; count < FormatSpec.MAX_WORD_LENGTH; ++count) {
+            PtNodeInfo currentInfo;
+            int loopCounter = 0;
+            do {
+                dictDecoder.setPosition(currentPos);
+                currentInfo = dictDecoder.readPtNode(currentPos, options);
+                if (BinaryDictIOUtils.isMovedPtNode(currentInfo.mFlags, options)) {
+                    currentPos = currentInfo.mParentAddress + currentInfo.mOriginalAddress;
+                }
+                if (DBG && loopCounter++ > MAX_JUMPS) {
+                    MakedictLog.d("Too many jumps - probably a bug");
+                }
+            } while (BinaryDictIOUtils.isMovedPtNode(currentInfo.mFlags, options));
+            if (Integer.MIN_VALUE == frequency) frequency = currentInfo.mFrequency;
+            builder.insert(0,
+                    new String(currentInfo.mCharacters, 0, currentInfo.mCharacters.length));
+            if (currentInfo.mParentAddress == FormatSpec.NO_PARENT_ADDRESS) break;
+            currentPos = currentInfo.mParentAddress + currentInfo.mOriginalAddress;
+        }
+        return new WeightedString(builder.toString(), frequency);
+    }
+
+    private static WeightedString getWordAtPositionWithoutParentAddress(
+            final DictDecoder dictDecoder, final int headerSize, final int pos,
+            final FormatOptions options) {
+        dictDecoder.setPosition(headerSize);
+        final int count = dictDecoder.readPtNodeCount();
+        int groupPos = headerSize + BinaryDictIOUtils.getPtNodeCountSize(count);
+        final StringBuilder builder = new StringBuilder();
+        WeightedString result = null;
+
+        PtNodeInfo last = null;
+        for (int i = count - 1; i >= 0; --i) {
+            PtNodeInfo info = dictDecoder.readPtNode(groupPos, options);
+            groupPos = info.mEndAddress;
+            if (info.mOriginalAddress == pos) {
+                builder.append(new String(info.mCharacters, 0, info.mCharacters.length));
+                result = new WeightedString(builder.toString(), info.mFrequency);
+                break; // and return
+            }
+            if (BinaryDictIOUtils.hasChildrenAddress(info.mChildrenAddress)) {
+                if (info.mChildrenAddress > pos) {
+                    if (null == last) continue;
+                    builder.append(new String(last.mCharacters, 0, last.mCharacters.length));
+                    dictDecoder.setPosition(last.mChildrenAddress);
+                    i = dictDecoder.readPtNodeCount();
+                    groupPos = last.mChildrenAddress + BinaryDictIOUtils.getPtNodeCountSize(i);
+                    last = null;
+                    continue;
+                }
+                last = info;
+            }
+            if (0 == i && BinaryDictIOUtils.hasChildrenAddress(last.mChildrenAddress)) {
+                builder.append(new String(last.mCharacters, 0, last.mCharacters.length));
+                dictDecoder.setPosition(last.mChildrenAddress);
+                i = dictDecoder.readPtNodeCount();
+                groupPos = last.mChildrenAddress + BinaryDictIOUtils.getPtNodeCountSize(i);
+                last = null;
+                continue;
+            }
+        }
+        return result;
+    }
+
+    /**
+     * Reads a single node array from a buffer.
+     *
+     * This methods reads the file at the current position. A node array is fully expected to start
+     * at the current position.
+     * This will recursively read other node arrays into the structure, populating the reverse
+     * maps on the fly and using them to keep track of already read nodes.
+     *
+     * @param dictDecoder the dict decoder, correctly positioned at the start of a node array.
+     * @param headerSize the size, in bytes, of the file header.
+     * @param reverseNodeArrayMap a mapping from addresses to already read node arrays.
+     * @param reversePtNodeMap a mapping from addresses to already read PtNodes.
+     * @param options file format options.
+     * @return the read node array with all his children already read.
+     */
+    private static PtNodeArray readNodeArray(final DictDecoder dictDecoder,
+            final int headerSize, final Map<Integer, PtNodeArray> reverseNodeArrayMap,
+            final Map<Integer, PtNode> reversePtNodeMap, final FormatOptions options)
+            throws IOException {
+        final ArrayList<PtNode> nodeArrayContents = new ArrayList<PtNode>();
+        final int nodeArrayOriginPos = dictDecoder.getPosition();
+
+        do { // Scan the linked-list node.
+            final int nodeArrayHeadPos = dictDecoder.getPosition();
+            final int count = dictDecoder.readPtNodeCount();
+            int groupOffsetPos = nodeArrayHeadPos + BinaryDictIOUtils.getPtNodeCountSize(count);
+            for (int i = count; i > 0; --i) { // Scan the array of PtNode.
+                PtNodeInfo info = dictDecoder.readPtNode(groupOffsetPos, options);
+                if (BinaryDictIOUtils.isMovedPtNode(info.mFlags, options)) continue;
+                ArrayList<WeightedString> shortcutTargets = info.mShortcutTargets;
+                ArrayList<WeightedString> bigrams = null;
+                if (null != info.mBigrams) {
+                    bigrams = new ArrayList<WeightedString>();
+                    for (PendingAttribute bigram : info.mBigrams) {
+                        final WeightedString word = getWordAtPosition(dictDecoder, headerSize,
+                                bigram.mAddress, options);
+                        final int reconstructedFrequency =
+                                BinaryDictIOUtils.reconstructBigramFrequency(word.mFrequency,
+                                        bigram.mFrequency);
+                        bigrams.add(new WeightedString(word.mWord, reconstructedFrequency));
+                    }
+                }
+                if (BinaryDictIOUtils.hasChildrenAddress(info.mChildrenAddress)) {
+                    PtNodeArray children = reverseNodeArrayMap.get(info.mChildrenAddress);
+                    if (null == children) {
+                        final int currentPosition = dictDecoder.getPosition();
+                        dictDecoder.setPosition(info.mChildrenAddress);
+                        children = readNodeArray(dictDecoder, headerSize, reverseNodeArrayMap,
+                                reversePtNodeMap, options);
+                        dictDecoder.setPosition(currentPosition);
+                    }
+                    nodeArrayContents.add(
+                            new PtNode(info.mCharacters, shortcutTargets, bigrams,
+                                    info.mFrequency,
+                                    0 != (info.mFlags & FormatSpec.FLAG_IS_NOT_A_WORD),
+                                    0 != (info.mFlags & FormatSpec.FLAG_IS_BLACKLISTED), children));
+                } else {
+                    nodeArrayContents.add(
+                            new PtNode(info.mCharacters, shortcutTargets, bigrams,
+                                    info.mFrequency,
+                                    0 != (info.mFlags & FormatSpec.FLAG_IS_NOT_A_WORD),
+                                    0 != (info.mFlags & FormatSpec.FLAG_IS_BLACKLISTED)));
+                }
+                groupOffsetPos = info.mEndAddress;
+            }
+
+            // reach the end of the array.
+            if (options.mSupportsDynamicUpdate) {
+                final boolean hasValidForwardLink = dictDecoder.readAndFollowForwardLink();
+                if (!hasValidForwardLink) break;
+            }
+        } while (options.mSupportsDynamicUpdate && dictDecoder.hasNextPtNodeArray());
+
+        final PtNodeArray nodeArray = new PtNodeArray(nodeArrayContents);
+        nodeArray.mCachedAddressBeforeUpdate = nodeArrayOriginPos;
+        nodeArray.mCachedAddressAfterUpdate = nodeArrayOriginPos;
+        reverseNodeArrayMap.put(nodeArray.mCachedAddressAfterUpdate, nodeArray);
+        return nodeArray;
+    }
+
+    /**
+     * Helper function to get the binary format version from the header.
+     * @throws IOException
+     */
+    private static int getFormatVersion(final DictBuffer dictBuffer)
+            throws IOException {
+        final int magic = dictBuffer.readInt();
+        if (FormatSpec.MAGIC_NUMBER == magic) return dictBuffer.readUnsignedShort();
+        return FormatSpec.NOT_A_VERSION_NUMBER;
+    }
+
+    /**
+     * Helper function to get and validate the binary format version.
+     * @throws UnsupportedFormatException
+     * @throws IOException
+     */
+    static int checkFormatVersion(final DictBuffer dictBuffer)
+            throws IOException, UnsupportedFormatException {
+        final int version = getFormatVersion(dictBuffer);
+        if (version < FormatSpec.MINIMUM_SUPPORTED_VERSION
+                || version > FormatSpec.MAXIMUM_SUPPORTED_VERSION) {
+            throw new UnsupportedFormatException("This file has version " + version
+                    + ", but this implementation does not support versions above "
+                    + FormatSpec.MAXIMUM_SUPPORTED_VERSION);
+        }
+        return version;
+    }
+
+    /**
+     * Reads a buffer and returns the memory representation of the dictionary.
+     *
+     * This high-level method takes a buffer and reads its contents, populating a
+     * FusionDictionary structure. The optional dict argument is an existing dictionary to
+     * which words from the buffer should be added. If it is null, a new dictionary is created.
+     *
+     * @param dictDecoder the dict decoder.
+     * @param dict an optional dictionary to add words to, or null.
+     * @return the created (or merged) dictionary.
+     */
+    @UsedForTesting
+    /* package */ static FusionDictionary readDictionaryBinary(final DictDecoder dictDecoder,
+            final FusionDictionary dict) throws IOException, UnsupportedFormatException {
+        // Read header
+        final FileHeader fileHeader = dictDecoder.readHeader();
+
+        Map<Integer, PtNodeArray> reverseNodeArrayMapping = new TreeMap<Integer, PtNodeArray>();
+        Map<Integer, PtNode> reversePtNodeMapping = new TreeMap<Integer, PtNode>();
+        final PtNodeArray root = readNodeArray(dictDecoder, fileHeader.mHeaderSize,
+                reverseNodeArrayMapping, reversePtNodeMapping, fileHeader.mFormatOptions);
+
+        FusionDictionary newDict = new FusionDictionary(root, fileHeader.mDictionaryOptions);
+        if (null != dict) {
+            for (final Word w : dict) {
+                if (w.mIsBlacklistEntry) {
+                    newDict.addBlacklistEntry(w.mWord, w.mShortcutTargets, w.mIsNotAWord);
+                } else {
+                    newDict.add(w.mWord, w.mFrequency, w.mShortcutTargets, w.mIsNotAWord);
+                }
+            }
+            for (final Word w : dict) {
+                // By construction a binary dictionary may not have bigrams pointing to
+                // words that are not also registered as unigrams so we don't have to avoid
+                // them explicitly here.
+                for (final WeightedString bigram : w.mBigrams) {
+                    newDict.setBigram(w.mWord, bigram.mWord, bigram.mFrequency);
+                }
+            }
+        }
+
+        return newDict;
+    }
+
+    /**
+     * Helper method to pass a file name instead of a File object to isBinaryDictionary.
+     */
+    public static boolean isBinaryDictionary(final String filename) {
+        final File file = new File(filename);
+        return isBinaryDictionary(file);
+    }
+
+    /**
+     * Basic test to find out whether the file is a binary dictionary or not.
+     *
+     * Concretely this only tests the magic number.
+     *
+     * @param file The file to test.
+     * @return true if it's a binary dictionary, false otherwise
+     */
+    public static boolean isBinaryDictionary(final File file) {
+        FileInputStream inStream = null;
+        try {
+            inStream = new FileInputStream(file);
+            final ByteBuffer buffer = inStream.getChannel().map(
+                    FileChannel.MapMode.READ_ONLY, 0, file.length());
+            final int version = getFormatVersion(new ByteBufferDictBuffer(buffer));
+            return (version >= FormatSpec.MINIMUM_SUPPORTED_VERSION
+                    && version <= FormatSpec.MAXIMUM_SUPPORTED_VERSION);
+        } catch (FileNotFoundException e) {
+            return false;
+        } catch (IOException e) {
+            return false;
+        } finally {
+            if (inStream != null) {
+                try {
+                    inStream.close();
+                } catch (IOException e) {
+                    // do nothing
+                }
+            }
+        }
+    }
+}
diff --git a/java/src/com/android/inputmethod/latin/makedict/BinaryDictEncoderUtils.java b/java/src/com/android/inputmethod/latin/makedict/BinaryDictEncoderUtils.java
new file mode 100644
index 0000000..4dba8e5
--- /dev/null
+++ b/java/src/com/android/inputmethod/latin/makedict/BinaryDictEncoderUtils.java
@@ -0,0 +1,936 @@
+/*
+ * Copyright (C) 2013 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.latin.makedict;
+
+import com.android.inputmethod.latin.makedict.BinaryDictDecoderUtils.CharEncoding;
+import com.android.inputmethod.latin.makedict.FormatSpec.FormatOptions;
+import com.android.inputmethod.latin.makedict.FusionDictionary.PtNode;
+import com.android.inputmethod.latin.makedict.FusionDictionary.DictionaryOptions;
+import com.android.inputmethod.latin.makedict.FusionDictionary.PtNodeArray;
+import com.android.inputmethod.latin.makedict.FusionDictionary.WeightedString;
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.OutputStream;
+import java.util.ArrayList;
+
+/**
+ * Encodes binary files for a FusionDictionary.
+ *
+ * All the methods in this class are static.
+ *
+ * TODO: Rename this class to DictEncoderUtils.
+ */
+public class BinaryDictEncoderUtils {
+
+    private static final boolean DBG = MakedictLog.DBG;
+
+    private BinaryDictEncoderUtils() {
+        // This utility class is not publicly instantiable.
+    }
+
+    // Arbitrary limit to how much passes we consider address size compression should
+    // terminate in. At the time of this writing, our largest dictionary completes
+    // compression in five passes.
+    // If the number of passes exceeds this number, makedict bails with an exception on
+    // suspicion that a bug might be causing an infinite loop.
+    private static final int MAX_PASSES = 24;
+
+    /**
+     * Compute the binary size of the character array.
+     *
+     * If only one character, this is the size of this character. If many, it's the sum of their
+     * sizes + 1 byte for the terminator.
+     *
+     * @param characters the character array
+     * @return the size of the char array, including the terminator if any
+     */
+    static int getPtNodeCharactersSize(final int[] characters) {
+        int size = CharEncoding.getCharArraySize(characters);
+        if (characters.length > 1) size += FormatSpec.PTNODE_TERMINATOR_SIZE;
+        return size;
+    }
+
+    /**
+     * Compute the binary size of the character array in a PtNode
+     *
+     * If only one character, this is the size of this character. If many, it's the sum of their
+     * sizes + 1 byte for the terminator.
+     *
+     * @param ptNode the PtNode
+     * @return the size of the char array, including the terminator if any
+     */
+    private static int getPtNodeCharactersSize(final PtNode ptNode) {
+        return getPtNodeCharactersSize(ptNode.mChars);
+    }
+
+    /**
+     * Compute the binary size of the PtNode count for a node array.
+     * @param nodeArray the nodeArray
+     * @return the size of the PtNode count, either 1 or 2 bytes.
+     */
+    private static int getPtNodeCountSize(final PtNodeArray nodeArray) {
+        return BinaryDictIOUtils.getPtNodeCountSize(nodeArray.mData.size());
+    }
+
+    /**
+     * Compute the size of a shortcut in bytes.
+     */
+    private static int getShortcutSize(final WeightedString shortcut) {
+        int size = FormatSpec.PTNODE_ATTRIBUTE_FLAGS_SIZE;
+        final String word = shortcut.mWord;
+        final int length = word.length();
+        for (int i = 0; i < length; i = word.offsetByCodePoints(i, 1)) {
+            final int codePoint = word.codePointAt(i);
+            size += CharEncoding.getCharSize(codePoint);
+        }
+        size += FormatSpec.PTNODE_TERMINATOR_SIZE;
+        return size;
+    }
+
+    /**
+     * Compute the size of a shortcut list in bytes.
+     *
+     * This is known in advance and does not change according to position in the file
+     * like address lists do.
+     */
+    static int getShortcutListSize(final ArrayList<WeightedString> shortcutList) {
+        if (null == shortcutList || shortcutList.isEmpty()) return 0;
+        int size = FormatSpec.PTNODE_SHORTCUT_LIST_SIZE_SIZE;
+        for (final WeightedString shortcut : shortcutList) {
+            size += getShortcutSize(shortcut);
+        }
+        return size;
+    }
+
+    /**
+     * Compute the maximum size of a PtNode, assuming 3-byte addresses for everything.
+     *
+     * @param ptNode the PtNode to compute the size of.
+     * @param options file format options.
+     * @return the maximum size of the PtNode.
+     */
+    private static int getPtNodeMaximumSize(final PtNode ptNode, final FormatOptions options) {
+        int size = getNodeHeaderSize(ptNode, options);
+        if (ptNode.isTerminal()) {
+            // If terminal, one byte for the frequency or four bytes for the terminal id.
+            if (options.mHasTerminalId) {
+                size += FormatSpec.PTNODE_TERMINAL_ID_SIZE;
+            } else {
+                size += FormatSpec.PTNODE_FREQUENCY_SIZE;
+            }
+        }
+        size += FormatSpec.PTNODE_MAX_ADDRESS_SIZE; // For children address
+        size += getShortcutListSize(ptNode.mShortcutTargets);
+        if (null != ptNode.mBigrams) {
+            size += (FormatSpec.PTNODE_ATTRIBUTE_FLAGS_SIZE
+                    + FormatSpec.PTNODE_ATTRIBUTE_MAX_ADDRESS_SIZE)
+                    * ptNode.mBigrams.size();
+        }
+        return size;
+    }
+
+    /**
+     * Compute the maximum size of each PtNode of a PtNode array, assuming 3-byte addresses for
+     * everything, and caches it in the `mCachedSize' member of the nodes; deduce the size of
+     * the containing node array, and cache it it its 'mCachedSize' member.
+     *
+     * @param ptNodeArray the node array to compute the maximum size of.
+     * @param options file format options.
+     */
+    private static void calculatePtNodeArrayMaximumSize(final PtNodeArray ptNodeArray,
+            final FormatOptions options) {
+        int size = getPtNodeCountSize(ptNodeArray);
+        for (PtNode node : ptNodeArray.mData) {
+            final int nodeSize = getPtNodeMaximumSize(node, options);
+            node.mCachedSize = nodeSize;
+            size += nodeSize;
+        }
+        if (options.mSupportsDynamicUpdate) {
+            size += FormatSpec.FORWARD_LINK_ADDRESS_SIZE;
+        }
+        ptNodeArray.mCachedSize = size;
+    }
+
+    /**
+     * Compute the size of the header (flag + [parent address] + characters size) of a PtNode.
+     *
+     * @param ptNode the PtNode of which to compute the size of the header
+     * @param options file format options.
+     */
+    private static int getNodeHeaderSize(final PtNode ptNode, final FormatOptions options) {
+        if (BinaryDictIOUtils.supportsDynamicUpdate(options)) {
+            return FormatSpec.PTNODE_FLAGS_SIZE + FormatSpec.PARENT_ADDRESS_SIZE
+                    + getPtNodeCharactersSize(ptNode);
+        } else {
+            return FormatSpec.PTNODE_FLAGS_SIZE + getPtNodeCharactersSize(ptNode);
+        }
+    }
+
+    /**
+     * Compute the size, in bytes, that an address will occupy.
+     *
+     * This can be used either for children addresses (which are always positive) or for
+     * attribute, which may be positive or negative but
+     * store their sign bit separately.
+     *
+     * @param address the address
+     * @return the byte size.
+     */
+    static int getByteSize(final int address) {
+        assert(address <= FormatSpec.UINT24_MAX);
+        if (!BinaryDictIOUtils.hasChildrenAddress(address)) {
+            return 0;
+        } else if (Math.abs(address) <= FormatSpec.UINT8_MAX) {
+            return 1;
+        } else if (Math.abs(address) <= FormatSpec.UINT16_MAX) {
+            return 2;
+        } else {
+            return 3;
+        }
+    }
+
+    static int writeUIntToBuffer(final byte[] buffer, int position, final int value,
+            final int size) {
+        switch(size) {
+            case 4:
+                buffer[position++] = (byte) ((value >> 24) & 0xFF);
+                /* fall through */
+            case 3:
+                buffer[position++] = (byte) ((value >> 16) & 0xFF);
+                /* fall through */
+            case 2:
+                buffer[position++] = (byte) ((value >> 8) & 0xFF);
+                /* fall through */
+            case 1:
+                buffer[position++] = (byte) (value & 0xFF);
+                break;
+            default:
+                /* nop */
+        }
+        return position;
+    }
+
+    // End utility methods
+
+    // This method is responsible for finding a nice ordering of the nodes that favors run-time
+    // cache performance and dictionary size.
+    /* package for tests */ static ArrayList<PtNodeArray> flattenTree(
+            final PtNodeArray rootNodeArray) {
+        final int treeSize = FusionDictionary.countPtNodes(rootNodeArray);
+        MakedictLog.i("Counted nodes : " + treeSize);
+        final ArrayList<PtNodeArray> flatTree = new ArrayList<PtNodeArray>(treeSize);
+        return flattenTreeInner(flatTree, rootNodeArray);
+    }
+
+    private static ArrayList<PtNodeArray> flattenTreeInner(final ArrayList<PtNodeArray> list,
+            final PtNodeArray ptNodeArray) {
+        // Removing the node is necessary if the tails are merged, because we would then
+        // add the same node several times when we only want it once. A number of places in
+        // the code also depends on any node being only once in the list.
+        // Merging tails can only be done if there are no attributes. Searching for attributes
+        // in LatinIME code depends on a total breadth-first ordering, which merging tails
+        // breaks. If there are no attributes, it should be fine (and reduce the file size)
+        // to merge tails, and removing the node from the list would be necessary. However,
+        // we don't merge tails because breaking the breadth-first ordering would result in
+        // extreme overhead at bigram lookup time (it would make the search function O(n) instead
+        // of the current O(log(n)), where n=number of nodes in the dictionary which is pretty
+        // high).
+        // If no nodes are ever merged, we can't have the same node twice in the list, hence
+        // searching for duplicates in unnecessary. It is also very performance consuming,
+        // since `list' is an ArrayList so it's an O(n) operation that runs on all nodes, making
+        // this simple list.remove operation O(n*n) overall. On Android this overhead is very
+        // high.
+        // For future reference, the code to remove duplicate is a simple : list.remove(node);
+        list.add(ptNodeArray);
+        final ArrayList<PtNode> branches = ptNodeArray.mData;
+        final int nodeSize = branches.size();
+        for (PtNode ptNode : branches) {
+            if (null != ptNode.mChildren) flattenTreeInner(list, ptNode.mChildren);
+        }
+        return list;
+    }
+
+    /**
+     * Get the offset from a position inside a current node array to a target node array, during
+     * update.
+     *
+     * If the current node array is before the target node array, the target node array has not
+     * been updated yet, so we should return the offset from the old position of the current node
+     * array to the old position of the target node array. If on the other hand the target is
+     * before the current node array, it already has been updated, so we should return the offset
+     * from the new position in the current node array to the new position in the target node
+     * array.
+     *
+     * @param currentNodeArray node array containing the PtNode where the offset will be written
+     * @param offsetFromStartOfCurrentNodeArray offset, in bytes, from the start of currentNodeArray
+     * @param targetNodeArray the target node array to get the offset to
+     * @return the offset to the target node array
+     */
+    private static int getOffsetToTargetNodeArrayDuringUpdate(final PtNodeArray currentNodeArray,
+            final int offsetFromStartOfCurrentNodeArray, final PtNodeArray targetNodeArray) {
+        final boolean isTargetBeforeCurrent = (targetNodeArray.mCachedAddressBeforeUpdate
+                < currentNodeArray.mCachedAddressBeforeUpdate);
+        if (isTargetBeforeCurrent) {
+            return targetNodeArray.mCachedAddressAfterUpdate
+                    - (currentNodeArray.mCachedAddressAfterUpdate
+                            + offsetFromStartOfCurrentNodeArray);
+        } else {
+            return targetNodeArray.mCachedAddressBeforeUpdate
+                    - (currentNodeArray.mCachedAddressBeforeUpdate
+                            + offsetFromStartOfCurrentNodeArray);
+        }
+    }
+
+    /**
+     * Get the offset from a position inside a current node array to a target PtNode, during
+     * update.
+     *
+     * @param currentNodeArray node array containing the PtNode where the offset will be written
+     * @param offsetFromStartOfCurrentNodeArray offset, in bytes, from the start of currentNodeArray
+     * @param targetPtNode the target PtNode to get the offset to
+     * @return the offset to the target PtNode
+     */
+    // TODO: is there any way to factorize this method with the one above?
+    private static int getOffsetToTargetPtNodeDuringUpdate(final PtNodeArray currentNodeArray,
+            final int offsetFromStartOfCurrentNodeArray, final PtNode targetPtNode) {
+        final int oldOffsetBasePoint = currentNodeArray.mCachedAddressBeforeUpdate
+                + offsetFromStartOfCurrentNodeArray;
+        final boolean isTargetBeforeCurrent = (targetPtNode.mCachedAddressBeforeUpdate
+                < oldOffsetBasePoint);
+        // If the target is before the current node array, then its address has already been
+        // updated. We can use the AfterUpdate member, and compare it to our own member after
+        // update. Otherwise, the AfterUpdate member is not updated yet, so we need to use the
+        // BeforeUpdate member, and of course we have to compare this to our own address before
+        // update.
+        if (isTargetBeforeCurrent) {
+            final int newOffsetBasePoint = currentNodeArray.mCachedAddressAfterUpdate
+                    + offsetFromStartOfCurrentNodeArray;
+            return targetPtNode.mCachedAddressAfterUpdate - newOffsetBasePoint;
+        } else {
+            return targetPtNode.mCachedAddressBeforeUpdate - oldOffsetBasePoint;
+        }
+    }
+
+    /**
+     * Computes the actual node array size, based on the cached addresses of the children nodes.
+     *
+     * Each node array stores its tentative address. During dictionary address computing, these
+     * are not final, but they can be used to compute the node array size (the node array size
+     * depends on the address of the children because the number of bytes necessary to store an
+     * address depends on its numeric value. The return value indicates whether the node array
+     * contents (as in, any of the addresses stored in the cache fields) have changed with
+     * respect to their previous value.
+     *
+     * @param ptNodeArray the node array to compute the size of.
+     * @param dict the dictionary in which the word/attributes are to be found.
+     * @param formatOptions file format options.
+     * @return false if none of the cached addresses inside the node array changed, true otherwise.
+     */
+    private static boolean computeActualPtNodeArraySize(final PtNodeArray ptNodeArray,
+            final FusionDictionary dict, final FormatOptions formatOptions) {
+        boolean changed = false;
+        int size = getPtNodeCountSize(ptNodeArray);
+        for (PtNode ptNode : ptNodeArray.mData) {
+            ptNode.mCachedAddressAfterUpdate = ptNodeArray.mCachedAddressAfterUpdate + size;
+            if (ptNode.mCachedAddressAfterUpdate != ptNode.mCachedAddressBeforeUpdate) {
+                changed = true;
+            }
+            int nodeSize = getNodeHeaderSize(ptNode, formatOptions);
+            if (ptNode.isTerminal()) {
+                if (formatOptions.mHasTerminalId) {
+                    nodeSize += FormatSpec.PTNODE_TERMINAL_ID_SIZE;
+                } else {
+                    nodeSize += FormatSpec.PTNODE_FREQUENCY_SIZE;
+                }
+            }
+            if (formatOptions.mSupportsDynamicUpdate) {
+                nodeSize += FormatSpec.SIGNED_CHILDREN_ADDRESS_SIZE;
+            } else if (null != ptNode.mChildren) {
+                nodeSize += getByteSize(getOffsetToTargetNodeArrayDuringUpdate(ptNodeArray,
+                        nodeSize + size, ptNode.mChildren));
+            }
+            nodeSize += getShortcutListSize(ptNode.mShortcutTargets);
+            if (null != ptNode.mBigrams) {
+                for (WeightedString bigram : ptNode.mBigrams) {
+                    final int offset = getOffsetToTargetPtNodeDuringUpdate(ptNodeArray,
+                            nodeSize + size + FormatSpec.PTNODE_ATTRIBUTE_FLAGS_SIZE,
+                            FusionDictionary.findWordInTree(dict.mRootNodeArray, bigram.mWord));
+                    nodeSize += getByteSize(offset) + FormatSpec.PTNODE_ATTRIBUTE_FLAGS_SIZE;
+                }
+            }
+            ptNode.mCachedSize = nodeSize;
+            size += nodeSize;
+        }
+        if (formatOptions.mSupportsDynamicUpdate) {
+            size += FormatSpec.FORWARD_LINK_ADDRESS_SIZE;
+        }
+        if (ptNodeArray.mCachedSize != size) {
+            ptNodeArray.mCachedSize = size;
+            changed = true;
+        }
+        return changed;
+    }
+
+    /**
+     * Initializes the cached addresses of node arrays and their containing nodes from their size.
+     *
+     * @param flatNodes the list of node arrays.
+     * @param formatOptions file format options.
+     * @return the byte size of the entire stack.
+     */
+    private static int initializePtNodeArraysCachedAddresses(final ArrayList<PtNodeArray> flatNodes,
+            final FormatOptions formatOptions) {
+        int nodeArrayOffset = 0;
+        for (final PtNodeArray nodeArray : flatNodes) {
+            nodeArray.mCachedAddressBeforeUpdate = nodeArrayOffset;
+            int nodeCountSize = getPtNodeCountSize(nodeArray);
+            int nodeffset = 0;
+            for (final PtNode ptNode : nodeArray.mData) {
+                ptNode.mCachedAddressBeforeUpdate = ptNode.mCachedAddressAfterUpdate =
+                        nodeCountSize + nodeArrayOffset + nodeffset;
+                nodeffset += ptNode.mCachedSize;
+            }
+            final int nodeSize = nodeCountSize + nodeffset
+                    + (formatOptions.mSupportsDynamicUpdate
+                            ? FormatSpec.FORWARD_LINK_ADDRESS_SIZE : 0);
+            nodeArrayOffset += nodeArray.mCachedSize;
+        }
+        return nodeArrayOffset;
+    }
+
+    /**
+     * Updates the cached addresses of node arrays after recomputing their new positions.
+     *
+     * @param flatNodes the list of node arrays.
+     */
+    private static void updatePtNodeArraysCachedAddresses(final ArrayList<PtNodeArray> flatNodes) {
+        for (final PtNodeArray nodeArray : flatNodes) {
+            nodeArray.mCachedAddressBeforeUpdate = nodeArray.mCachedAddressAfterUpdate;
+            for (final PtNode ptNode : nodeArray.mData) {
+                ptNode.mCachedAddressBeforeUpdate = ptNode.mCachedAddressAfterUpdate;
+            }
+        }
+    }
+
+    /**
+     * Compute the cached parent addresses after all has been updated.
+     *
+     * The parent addresses are used by some binary formats at write-to-disk time. Not all formats
+     * need them. In particular, version 2 does not need them, and version 3 does.
+     *
+     * @param flatNodes the flat array of node arrays to fill in
+     */
+    private static void computeParentAddresses(final ArrayList<PtNodeArray> flatNodes) {
+        for (final PtNodeArray nodeArray : flatNodes) {
+            for (final PtNode ptNode : nodeArray.mData) {
+                if (null != ptNode.mChildren) {
+                    // Assign my address to children's parent address
+                    // Here BeforeUpdate and AfterUpdate addresses have the same value, so it
+                    // does not matter which we use.
+                    ptNode.mChildren.mCachedParentAddress = ptNode.mCachedAddressAfterUpdate
+                            - ptNode.mChildren.mCachedAddressAfterUpdate;
+                }
+            }
+        }
+    }
+
+    /**
+     * Compute the addresses and sizes of an ordered list of PtNode arrays.
+     *
+     * This method takes a list of PtNode arrays and will update their cached address and size
+     * values so that they can be written into a file. It determines the smallest size each of the
+     * PtNode arrays can be given the addresses of its children and attributes, and store that into
+     * each PtNode.
+     * The order of the PtNode is given by the order of the array. This method makes no effort
+     * to find a good order; it only mechanically computes the size this order results in.
+     *
+     * @param dict the dictionary
+     * @param flatNodes the ordered list of PtNode arrays
+     * @param formatOptions file format options.
+     * @return the same array it was passed. The nodes have been updated for address and size.
+     */
+    /* package */ static ArrayList<PtNodeArray> computeAddresses(final FusionDictionary dict,
+            final ArrayList<PtNodeArray> flatNodes, final FormatOptions formatOptions) {
+        // First get the worst possible sizes and offsets
+        for (final PtNodeArray n : flatNodes) calculatePtNodeArrayMaximumSize(n, formatOptions);
+        final int offset = initializePtNodeArraysCachedAddresses(flatNodes, formatOptions);
+
+        MakedictLog.i("Compressing the array addresses. Original size : " + offset);
+        MakedictLog.i("(Recursively seen size : " + offset + ")");
+
+        int passes = 0;
+        boolean changesDone = false;
+        do {
+            changesDone = false;
+            int ptNodeArrayStartOffset = 0;
+            for (final PtNodeArray ptNodeArray : flatNodes) {
+                ptNodeArray.mCachedAddressAfterUpdate = ptNodeArrayStartOffset;
+                final int oldNodeArraySize = ptNodeArray.mCachedSize;
+                final boolean changed =
+                        computeActualPtNodeArraySize(ptNodeArray, dict, formatOptions);
+                final int newNodeArraySize = ptNodeArray.mCachedSize;
+                if (oldNodeArraySize < newNodeArraySize) {
+                    throw new RuntimeException("Increased size ?!");
+                }
+                ptNodeArrayStartOffset += newNodeArraySize;
+                changesDone |= changed;
+            }
+            updatePtNodeArraysCachedAddresses(flatNodes);
+            ++passes;
+            if (passes > MAX_PASSES) throw new RuntimeException("Too many passes - probably a bug");
+        } while (changesDone);
+
+        if (formatOptions.mSupportsDynamicUpdate) {
+            computeParentAddresses(flatNodes);
+        }
+        final PtNodeArray lastPtNodeArray = flatNodes.get(flatNodes.size() - 1);
+        MakedictLog.i("Compression complete in " + passes + " passes.");
+        MakedictLog.i("After address compression : "
+                + (lastPtNodeArray.mCachedAddressAfterUpdate + lastPtNodeArray.mCachedSize));
+
+        return flatNodes;
+    }
+
+    /**
+     * Sanity-checking method.
+     *
+     * This method checks a list of PtNode arrays for juxtaposition, that is, it will do
+     * nothing if each node array's cached address is actually the previous node array's address
+     * plus the previous node's size.
+     * If this is not the case, it will throw an exception.
+     *
+     * @param arrays the list of node arrays to check
+     */
+    /* package */ static void checkFlatPtNodeArrayList(final ArrayList<PtNodeArray> arrays) {
+        int offset = 0;
+        int index = 0;
+        for (final PtNodeArray ptNodeArray : arrays) {
+            // BeforeUpdate and AfterUpdate addresses are the same here, so it does not matter
+            // which we use.
+            if (ptNodeArray.mCachedAddressAfterUpdate != offset) {
+                throw new RuntimeException("Wrong address for node " + index
+                        + " : expected " + offset + ", got " +
+                        ptNodeArray.mCachedAddressAfterUpdate);
+            }
+            ++index;
+            offset += ptNodeArray.mCachedSize;
+        }
+    }
+
+    /**
+     * Helper method to write a children position to a file.
+     *
+     * @param buffer the buffer to write to.
+     * @param index the index in the buffer to write the address to.
+     * @param position the position to write.
+     * @return the size in bytes the address actually took.
+     */
+    /* package */ static int writeChildrenPosition(final byte[] buffer, int index,
+            final int position) {
+        switch (getByteSize(position)) {
+        case 1:
+            buffer[index++] = (byte)position;
+            return 1;
+        case 2:
+            buffer[index++] = (byte)(0xFF & (position >> 8));
+            buffer[index++] = (byte)(0xFF & position);
+            return 2;
+        case 3:
+            buffer[index++] = (byte)(0xFF & (position >> 16));
+            buffer[index++] = (byte)(0xFF & (position >> 8));
+            buffer[index++] = (byte)(0xFF & position);
+            return 3;
+        case 0:
+            return 0;
+        default:
+            throw new RuntimeException("Position " + position + " has a strange size");
+        }
+    }
+
+    /**
+     * Helper method to write a signed children position to a file.
+     *
+     * @param buffer the buffer to write to.
+     * @param index the index in the buffer to write the address to.
+     * @param position the position to write.
+     * @return the size in bytes the address actually took.
+     */
+    /* package */ static int writeSignedChildrenPosition(final byte[] buffer, int index,
+            final int position) {
+        if (!BinaryDictIOUtils.hasChildrenAddress(position)) {
+            buffer[index] = buffer[index + 1] = buffer[index + 2] = 0;
+        } else {
+            final int absPosition = Math.abs(position);
+            buffer[index++] =
+                    (byte)((position < 0 ? FormatSpec.MSB8 : 0) | (0xFF & (absPosition >> 16)));
+            buffer[index++] = (byte)(0xFF & (absPosition >> 8));
+            buffer[index++] = (byte)(0xFF & absPosition);
+        }
+        return 3;
+    }
+
+    /**
+     * Makes the flag value for a PtNode.
+     *
+     * @param hasMultipleChars whether the PtNode has multiple chars.
+     * @param isTerminal whether the PtNode is terminal.
+     * @param childrenAddressSize the size of a children address.
+     * @param hasShortcuts whether the PtNode has shortcuts.
+     * @param hasBigrams whether the PtNode has bigrams.
+     * @param isNotAWord whether the PtNode is not a word.
+     * @param isBlackListEntry whether the PtNode is a blacklist entry.
+     * @param formatOptions file format options.
+     * @return the flags
+     */
+    static int makePtNodeFlags(final boolean hasMultipleChars, final boolean isTerminal,
+            final int childrenAddressSize, final boolean hasShortcuts, final boolean hasBigrams,
+            final boolean isNotAWord, final boolean isBlackListEntry,
+            final FormatOptions formatOptions) {
+        byte flags = 0;
+        if (hasMultipleChars) flags |= FormatSpec.FLAG_HAS_MULTIPLE_CHARS;
+        if (isTerminal) flags |= FormatSpec.FLAG_IS_TERMINAL;
+        if (formatOptions.mSupportsDynamicUpdate) {
+            flags |= FormatSpec.FLAG_IS_NOT_MOVED;
+        } else if (true) {
+            switch (childrenAddressSize) {
+                case 1:
+                    flags |= FormatSpec.FLAG_CHILDREN_ADDRESS_TYPE_ONEBYTE;
+                    break;
+                case 2:
+                    flags |= FormatSpec.FLAG_CHILDREN_ADDRESS_TYPE_TWOBYTES;
+                    break;
+                case 3:
+                    flags |= FormatSpec.FLAG_CHILDREN_ADDRESS_TYPE_THREEBYTES;
+                    break;
+                case 0:
+                    flags |= FormatSpec.FLAG_CHILDREN_ADDRESS_TYPE_NOADDRESS;
+                    break;
+                default:
+                    throw new RuntimeException("Node with a strange address");
+            }
+        }
+        if (hasShortcuts) flags |= FormatSpec.FLAG_HAS_SHORTCUT_TARGETS;
+        if (hasBigrams) flags |= FormatSpec.FLAG_HAS_BIGRAMS;
+        if (isNotAWord) flags |= FormatSpec.FLAG_IS_NOT_A_WORD;
+        if (isBlackListEntry) flags |= FormatSpec.FLAG_IS_BLACKLISTED;
+        return flags;
+    }
+
+    /* package */ static byte makePtNodeFlags(final PtNode node, final int ptNodeAddress,
+            final int childrenOffset, final FormatOptions formatOptions) {
+        return (byte) makePtNodeFlags(node.mChars.length > 1, node.mFrequency >= 0,
+                getByteSize(childrenOffset),
+                node.mShortcutTargets != null && !node.mShortcutTargets.isEmpty(),
+                node.mBigrams != null, node.mIsNotAWord, node.mIsBlacklistEntry, formatOptions);
+    }
+
+    /**
+     * Makes the flag value for a bigram.
+     *
+     * @param more whether there are more bigrams after this one.
+     * @param offset the offset of the bigram.
+     * @param bigramFrequency the frequency of the bigram, 0..255.
+     * @param unigramFrequency the unigram frequency of the same word, 0..255.
+     * @param word the second bigram, for debugging purposes
+     * @return the flags
+     */
+    /* package */ static final int makeBigramFlags(final boolean more, final int offset,
+            int bigramFrequency, final int unigramFrequency, final String word) {
+        int bigramFlags = (more ? FormatSpec.FLAG_BIGRAM_SHORTCUT_ATTR_HAS_NEXT : 0)
+                + (offset < 0 ? FormatSpec.FLAG_BIGRAM_ATTR_OFFSET_NEGATIVE : 0);
+        switch (getByteSize(offset)) {
+        case 1:
+            bigramFlags |= FormatSpec.FLAG_BIGRAM_ATTR_ADDRESS_TYPE_ONEBYTE;
+            break;
+        case 2:
+            bigramFlags |= FormatSpec.FLAG_BIGRAM_ATTR_ADDRESS_TYPE_TWOBYTES;
+            break;
+        case 3:
+            bigramFlags |= FormatSpec.FLAG_BIGRAM_ATTR_ADDRESS_TYPE_THREEBYTES;
+            break;
+        default:
+            throw new RuntimeException("Strange offset size");
+        }
+        if (unigramFrequency > bigramFrequency) {
+            MakedictLog.e("Unigram freq is superior to bigram freq for \"" + word
+                    + "\". Bigram freq is " + bigramFrequency + ", unigram freq for "
+                    + word + " is " + unigramFrequency);
+            bigramFrequency = unigramFrequency;
+        }
+        // We compute the difference between 255 (which means probability = 1) and the
+        // unigram score. We split this into a number of discrete steps.
+        // Now, the steps are numbered 0~15; 0 represents an increase of 1 step while 15
+        // represents an increase of 16 steps: a value of 15 will be interpreted as the median
+        // value of the 16th step. In all justice, if the bigram frequency is low enough to be
+        // rounded below the first step (which means it is less than half a step higher than the
+        // unigram frequency) then the unigram frequency itself is the best approximation of the
+        // bigram freq that we could possibly supply, hence we should *not* include this bigram
+        // in the file at all.
+        // until this is done, we'll write 0 and slightly overestimate this case.
+        // In other words, 0 means "between 0.5 step and 1.5 step", 1 means "between 1.5 step
+        // and 2.5 steps", and 15 means "between 15.5 steps and 16.5 steps". So we want to
+        // divide our range [unigramFreq..MAX_TERMINAL_FREQUENCY] in 16.5 steps to get the
+        // step size. Then we compute the start of the first step (the one where value 0 starts)
+        // by adding half-a-step to the unigramFrequency. From there, we compute the integer
+        // number of steps to the bigramFrequency. One last thing: we want our steps to include
+        // their lower bound and exclude their higher bound so we need to have the first step
+        // start at exactly 1 unit higher than floor(unigramFreq + half a step).
+        // Note : to reconstruct the score, the dictionary reader will need to divide
+        // MAX_TERMINAL_FREQUENCY - unigramFreq by 16.5 likewise to get the value of the step,
+        // and add (discretizedFrequency + 0.5 + 0.5) times this value to get the best
+        // approximation. (0.5 to get the first step start, and 0.5 to get the middle of the
+        // step pointed by the discretized frequency.
+        final float stepSize =
+                (FormatSpec.MAX_TERMINAL_FREQUENCY - unigramFrequency)
+                / (1.5f + FormatSpec.MAX_BIGRAM_FREQUENCY);
+        final float firstStepStart = 1 + unigramFrequency + (stepSize / 2.0f);
+        final int discretizedFrequency = (int)((bigramFrequency - firstStepStart) / stepSize);
+        // If the bigram freq is less than half-a-step higher than the unigram freq, we get -1
+        // here. The best approximation would be the unigram freq itself, so we should not
+        // include this bigram in the dictionary. For now, register as 0, and live with the
+        // small over-estimation that we get in this case. TODO: actually remove this bigram
+        // if discretizedFrequency < 0.
+        final int finalBigramFrequency = discretizedFrequency > 0 ? discretizedFrequency : 0;
+        bigramFlags += finalBigramFrequency & FormatSpec.FLAG_BIGRAM_SHORTCUT_ATTR_FREQUENCY;
+        return bigramFlags;
+    }
+
+    /**
+     * Makes the 2-byte value for options flags.
+     */
+    private static final int makeOptionsValue(final FusionDictionary dictionary,
+            final FormatOptions formatOptions) {
+        final DictionaryOptions options = dictionary.mOptions;
+        final boolean hasBigrams = dictionary.hasBigrams();
+        return (options.mFrenchLigatureProcessing ? FormatSpec.FRENCH_LIGATURE_PROCESSING_FLAG : 0)
+                + (options.mGermanUmlautProcessing ? FormatSpec.GERMAN_UMLAUT_PROCESSING_FLAG : 0)
+                + (hasBigrams ? FormatSpec.CONTAINS_BIGRAMS_FLAG : 0)
+                + (formatOptions.mSupportsDynamicUpdate ? FormatSpec.SUPPORTS_DYNAMIC_UPDATE : 0);
+    }
+
+    /**
+     * Makes the flag value for a shortcut.
+     *
+     * @param more whether there are more attributes after this one.
+     * @param frequency the frequency of the attribute, 0..15
+     * @return the flags
+     */
+    static final int makeShortcutFlags(final boolean more, final int frequency) {
+        return (more ? FormatSpec.FLAG_BIGRAM_SHORTCUT_ATTR_HAS_NEXT : 0)
+                + (frequency & FormatSpec.FLAG_BIGRAM_SHORTCUT_ATTR_FREQUENCY);
+    }
+
+    /* package */ static final int writeParentAddress(final byte[] buffer, final int index,
+            final int address, final FormatOptions formatOptions) {
+        if (BinaryDictIOUtils.supportsDynamicUpdate(formatOptions)) {
+            if (address == FormatSpec.NO_PARENT_ADDRESS) {
+                buffer[index] = buffer[index + 1] = buffer[index + 2] = 0;
+            } else {
+                final int absAddress = Math.abs(address);
+                assert(absAddress <= FormatSpec.SINT24_MAX);
+                buffer[index] = (byte)((address < 0 ? FormatSpec.MSB8 : 0)
+                        | ((absAddress >> 16) & 0xFF));
+                buffer[index + 1] = (byte)((absAddress >> 8) & 0xFF);
+                buffer[index + 2] = (byte)(absAddress & 0xFF);
+            }
+            return index + 3;
+        } else {
+            return index;
+        }
+    }
+
+    /* package */ static final int getChildrenPosition(final PtNode ptNode,
+            final FormatOptions formatOptions) {
+        int positionOfChildrenPosField = ptNode.mCachedAddressAfterUpdate
+                + getNodeHeaderSize(ptNode, formatOptions);
+        if (ptNode.isTerminal()) {
+            // A terminal node has either the terminal id or the frequency.
+            // If positionOfChildrenPosField is incorrect, we may crash when jumping to the children
+            // position.
+            if (formatOptions.mHasTerminalId) {
+                positionOfChildrenPosField += FormatSpec.PTNODE_TERMINAL_ID_SIZE;
+            } else {
+                positionOfChildrenPosField += FormatSpec.PTNODE_FREQUENCY_SIZE;
+            }
+        }
+        return null == ptNode.mChildren ? FormatSpec.NO_CHILDREN_ADDRESS
+                : ptNode.mChildren.mCachedAddressAfterUpdate - positionOfChildrenPosField;
+    }
+
+    /**
+     * Write a PtNodeArray. The PtNodeArray is expected to have its final position cached.
+     *
+     * @param dict the dictionary the node array is a part of (for relative offsets).
+     * @param dictEncoder the dictionary encoder.
+     * @param ptNodeArray the node array to write.
+     * @param formatOptions file format options.
+     */
+    @SuppressWarnings("unused")
+    /* package */ static void writePlacedPtNodeArray(final FusionDictionary dict,
+            final DictEncoder dictEncoder, final PtNodeArray ptNodeArray,
+            final FormatOptions formatOptions) {
+        // TODO: Make the code in common with BinaryDictIOUtils#writePtNode
+        dictEncoder.setPosition(ptNodeArray.mCachedAddressAfterUpdate);
+
+        final int ptNodeCount = ptNodeArray.mData.size();
+        dictEncoder.writePtNodeCount(ptNodeCount);
+        final int parentPosition =
+                (ptNodeArray.mCachedParentAddress == FormatSpec.NO_PARENT_ADDRESS)
+                ? FormatSpec.NO_PARENT_ADDRESS
+                : ptNodeArray.mCachedParentAddress + ptNodeArray.mCachedAddressAfterUpdate;
+        for (int i = 0; i < ptNodeCount; ++i) {
+            final PtNode ptNode = ptNodeArray.mData.get(i);
+            if (dictEncoder.getPosition() != ptNode.mCachedAddressAfterUpdate) {
+                throw new RuntimeException("Bug: write index is not the same as the cached address "
+                        + "of the node : " + dictEncoder.getPosition() + " <> "
+                        + ptNode.mCachedAddressAfterUpdate);
+            }
+            // Sanity checks.
+            if (DBG && ptNode.mFrequency > FormatSpec.MAX_TERMINAL_FREQUENCY) {
+                throw new RuntimeException("A node has a frequency > "
+                        + FormatSpec.MAX_TERMINAL_FREQUENCY
+                        + " : " + ptNode.mFrequency);
+            }
+            dictEncoder.writePtNode(ptNode, parentPosition, formatOptions, dict);
+        }
+        if (formatOptions.mSupportsDynamicUpdate) {
+            dictEncoder.writeForwardLinkAddress(FormatSpec.NO_FORWARD_LINK_ADDRESS);
+        }
+        if (dictEncoder.getPosition() != ptNodeArray.mCachedAddressAfterUpdate
+                + ptNodeArray.mCachedSize) {
+            throw new RuntimeException("Not the same size : written "
+                     + (dictEncoder.getPosition() - ptNodeArray.mCachedAddressAfterUpdate)
+                     + " bytes from a node that should have " + ptNodeArray.mCachedSize + " bytes");
+        }
+    }
+
+    /**
+     * Dumps a collection of useful statistics about a list of PtNode arrays.
+     *
+     * This prints purely informative stuff, like the total estimated file size, the
+     * number of PtNode arrays, of PtNodes, the repartition of each address size, etc
+     *
+     * @param ptNodeArrays the list of PtNode arrays.
+     */
+    /* package */ static void showStatistics(ArrayList<PtNodeArray> ptNodeArrays) {
+        int firstTerminalAddress = Integer.MAX_VALUE;
+        int lastTerminalAddress = Integer.MIN_VALUE;
+        int size = 0;
+        int ptNodes = 0;
+        int maxNodes = 0;
+        int maxRuns = 0;
+        for (final PtNodeArray ptNodeArray : ptNodeArrays) {
+            if (maxNodes < ptNodeArray.mData.size()) maxNodes = ptNodeArray.mData.size();
+            for (final PtNode ptNode : ptNodeArray.mData) {
+                ++ptNodes;
+                if (ptNode.mChars.length > maxRuns) maxRuns = ptNode.mChars.length;
+                if (ptNode.mFrequency >= 0) {
+                    if (ptNodeArray.mCachedAddressAfterUpdate < firstTerminalAddress)
+                        firstTerminalAddress = ptNodeArray.mCachedAddressAfterUpdate;
+                    if (ptNodeArray.mCachedAddressAfterUpdate > lastTerminalAddress)
+                        lastTerminalAddress = ptNodeArray.mCachedAddressAfterUpdate;
+                }
+            }
+            if (ptNodeArray.mCachedAddressAfterUpdate + ptNodeArray.mCachedSize > size) {
+                size = ptNodeArray.mCachedAddressAfterUpdate + ptNodeArray.mCachedSize;
+            }
+        }
+        final int[] ptNodeCounts = new int[maxNodes + 1];
+        final int[] runCounts = new int[maxRuns + 1];
+        for (final PtNodeArray ptNodeArray : ptNodeArrays) {
+            ++ptNodeCounts[ptNodeArray.mData.size()];
+            for (final PtNode ptNode : ptNodeArray.mData) {
+                ++runCounts[ptNode.mChars.length];
+            }
+        }
+
+        MakedictLog.i("Statistics:\n"
+                + "  total file size " + size + "\n"
+                + "  " + ptNodeArrays.size() + " node arrays\n"
+                + "  " + ptNodes + " PtNodes (" + ((float)ptNodes / ptNodeArrays.size())
+                        + " PtNodes per node)\n"
+                + "  first terminal at " + firstTerminalAddress + "\n"
+                + "  last terminal at " + lastTerminalAddress + "\n"
+                + "  PtNode stats : max = " + maxNodes);
+        for (int i = 0; i < ptNodeCounts.length; ++i) {
+            MakedictLog.i("    " + i + " : " + ptNodeCounts[i]);
+        }
+        MakedictLog.i("  Character run stats : max = " + maxRuns);
+        for (int i = 0; i < runCounts.length; ++i) {
+            MakedictLog.i("    " + i + " : " + runCounts[i]);
+        }
+    }
+
+    /**
+     * Writes a file header to an output stream.
+     *
+     * @param destination the stream to write the file header to.
+     * @param dict the dictionary to write.
+     * @param formatOptions file format options.
+     */
+    /* package */ static void writeDictionaryHeader(final OutputStream destination,
+            final FusionDictionary dict, final FormatOptions formatOptions)
+                    throws IOException, UnsupportedFormatException {
+        final int version = formatOptions.mVersion;
+        if (version < FormatSpec.MINIMUM_SUPPORTED_VERSION
+                || version > FormatSpec.MAXIMUM_SUPPORTED_VERSION) {
+            throw new UnsupportedFormatException("Requested file format version " + version
+                    + ", but this implementation only supports versions "
+                    + FormatSpec.MINIMUM_SUPPORTED_VERSION + " through "
+                    + FormatSpec.MAXIMUM_SUPPORTED_VERSION);
+        }
+
+        ByteArrayOutputStream headerBuffer = new ByteArrayOutputStream(256);
+
+        // The magic number in big-endian order.
+        // Magic number for all versions.
+        headerBuffer.write((byte) (0xFF & (FormatSpec.MAGIC_NUMBER >> 24)));
+        headerBuffer.write((byte) (0xFF & (FormatSpec.MAGIC_NUMBER >> 16)));
+        headerBuffer.write((byte) (0xFF & (FormatSpec.MAGIC_NUMBER >> 8)));
+        headerBuffer.write((byte) (0xFF & FormatSpec.MAGIC_NUMBER));
+        // Dictionary version.
+        headerBuffer.write((byte) (0xFF & (version >> 8)));
+        headerBuffer.write((byte) (0xFF & version));
+
+        // Options flags
+        final int options = makeOptionsValue(dict, formatOptions);
+        headerBuffer.write((byte) (0xFF & (options >> 8)));
+        headerBuffer.write((byte) (0xFF & options));
+        final int headerSizeOffset = headerBuffer.size();
+        // Placeholder to be written later with header size.
+        for (int i = 0; i < 4; ++i) {
+            headerBuffer.write(0);
+        }
+        // Write out the options.
+        for (final String key : dict.mOptions.mAttributes.keySet()) {
+            final String value = dict.mOptions.mAttributes.get(key);
+            CharEncoding.writeString(headerBuffer, key);
+            CharEncoding.writeString(headerBuffer, value);
+        }
+        final int size = headerBuffer.size();
+        final byte[] bytes = headerBuffer.toByteArray();
+        // Write out the header size.
+        bytes[headerSizeOffset] = (byte) (0xFF & (size >> 24));
+        bytes[headerSizeOffset + 1] = (byte) (0xFF & (size >> 16));
+        bytes[headerSizeOffset + 2] = (byte) (0xFF & (size >> 8));
+        bytes[headerSizeOffset + 3] = (byte) (0xFF & (size >> 0));
+        destination.write(bytes);
+
+        headerBuffer.close();
+    }
+}
diff --git a/java/src/com/android/inputmethod/latin/makedict/BinaryDictIOUtils.java b/java/src/com/android/inputmethod/latin/makedict/BinaryDictIOUtils.java
index 167c691..a282f59 100644
--- a/java/src/com/android/inputmethod/latin/makedict/BinaryDictIOUtils.java
+++ b/java/src/com/android/inputmethod/latin/makedict/BinaryDictIOUtils.java
@@ -18,56 +18,52 @@
 
 import com.android.inputmethod.annotations.UsedForTesting;
 import com.android.inputmethod.latin.Constants;
-import com.android.inputmethod.latin.makedict.BinaryDictInputOutput.CharEncoding;
-import com.android.inputmethod.latin.makedict.BinaryDictInputOutput.FusionDictionaryBufferInterface;
+import com.android.inputmethod.latin.makedict.BinaryDictDecoderUtils.CharEncoding;
+import com.android.inputmethod.latin.makedict.BinaryDictDecoderUtils.DictBuffer;
 import com.android.inputmethod.latin.makedict.FormatSpec.FileHeader;
 import com.android.inputmethod.latin.makedict.FormatSpec.FormatOptions;
-import com.android.inputmethod.latin.makedict.FusionDictionary.CharGroup;
+import com.android.inputmethod.latin.makedict.FusionDictionary.PtNode;
 import com.android.inputmethod.latin.makedict.FusionDictionary.WeightedString;
+import com.android.inputmethod.latin.utils.ByteArrayDictBuffer;
 
 import java.io.File;
 import java.io.FileInputStream;
 import java.io.FileNotFoundException;
 import java.io.IOException;
 import java.io.OutputStream;
-import java.nio.channels.FileChannel;
 import java.util.ArrayList;
-import java.util.Arrays;
 import java.util.Iterator;
 import java.util.Map;
 import java.util.Stack;
 
 public final class BinaryDictIOUtils {
     private static final boolean DBG = false;
-    private static final int MSB24 = 0x800000;
-    private static final int SINT24_MAX = 0x7FFFFF;
-    private static final int MAX_JUMPS = 10000;
 
     private BinaryDictIOUtils() {
         // This utility class is not publicly instantiable.
     }
 
     private static final class Position {
-        public static final int NOT_READ_GROUPCOUNT = -1;
+        public static final int NOT_READ_PTNODE_COUNT = -1;
 
         public int mAddress;
-        public int mNumOfCharGroup;
+        public int mNumOfPtNode;
         public int mPosition;
         public int mLength;
 
         public Position(int address, int length) {
             mAddress = address;
             mLength = length;
-            mNumOfCharGroup = NOT_READ_GROUPCOUNT;
+            mNumOfPtNode = NOT_READ_PTNODE_COUNT;
         }
     }
 
     /**
-     * Tours all node without recursive call.
+     * Retrieves all node arrays without recursive call.
      */
-    private static void readUnigramsAndBigramsBinaryInner(
-            final FusionDictionaryBufferInterface buffer, final int headerSize,
-            final Map<Integer, String> words, final Map<Integer, Integer> frequencies,
+    private static void readUnigramsAndBigramsBinaryInner(final DictDecoder dictDecoder,
+            final int headerSize, final Map<Integer, String> words,
+            final Map<Integer, Integer> frequencies,
             final Map<Integer, ArrayList<PendingAttribute>> bigrams,
             final FormatOptions formatOptions) {
         int[] pushedChars = new int[FormatSpec.MAX_WORD_LENGTH + 1];
@@ -82,47 +78,47 @@
             Position p = stack.peek();
 
             if (DBG) {
-                MakedictLog.d("read: address=" + p.mAddress + ", numOfCharGroup=" +
-                        p.mNumOfCharGroup + ", position=" + p.mPosition + ", length=" + p.mLength);
+                MakedictLog.d("read: address=" + p.mAddress + ", numOfPtNode=" +
+                        p.mNumOfPtNode + ", position=" + p.mPosition + ", length=" + p.mLength);
             }
 
-            if (buffer.position() != p.mAddress) buffer.position(p.mAddress);
+            if (dictDecoder.getPosition() != p.mAddress) dictDecoder.setPosition(p.mAddress);
             if (index != p.mLength) index = p.mLength;
 
-            if (p.mNumOfCharGroup == Position.NOT_READ_GROUPCOUNT) {
-                p.mNumOfCharGroup = BinaryDictInputOutput.readCharGroupCount(buffer);
-                p.mAddress += BinaryDictInputOutput.getGroupCountSize(p.mNumOfCharGroup);
+            if (p.mNumOfPtNode == Position.NOT_READ_PTNODE_COUNT) {
+                p.mNumOfPtNode = dictDecoder.readPtNodeCount();
+                p.mAddress += getPtNodeCountSize(p.mNumOfPtNode);
                 p.mPosition = 0;
             }
-            if (p.mNumOfCharGroup == 0) {
+            if (p.mNumOfPtNode == 0) {
                 stack.pop();
                 continue;
             }
-            CharGroupInfo info = BinaryDictInputOutput.readCharGroup(buffer,
-                    p.mAddress - headerSize, formatOptions);
+            PtNodeInfo info = dictDecoder.readPtNode(p.mAddress, formatOptions);
             for (int i = 0; i < info.mCharacters.length; ++i) {
                 pushedChars[index++] = info.mCharacters[i];
             }
             p.mPosition++;
 
-            final boolean isMovedGroup = BinaryDictInputOutput.isMovedGroup(info.mFlags,
+            final boolean isMovedPtNode = isMovedPtNode(info.mFlags,
                     formatOptions);
-            final boolean isDeletedGroup = BinaryDictInputOutput.isDeletedGroup(info.mFlags,
+            final boolean isDeletedPtNode = isDeletedPtNode(info.mFlags,
                     formatOptions);
-            if (!isMovedGroup && !isDeletedGroup
-                    && info.mFrequency != FusionDictionary.CharGroup.NOT_A_TERMINAL) {// found word
+            if (!isMovedPtNode && !isDeletedPtNode
+                    && info.mFrequency != FusionDictionary.PtNode.NOT_A_TERMINAL) {// found word
                 words.put(info.mOriginalAddress, new String(pushedChars, 0, index));
                 frequencies.put(info.mOriginalAddress, info.mFrequency);
                 if (info.mBigrams != null) bigrams.put(info.mOriginalAddress, info.mBigrams);
             }
 
-            if (p.mPosition == p.mNumOfCharGroup) {
+            if (p.mPosition == p.mNumOfPtNode) {
                 if (formatOptions.mSupportsDynamicUpdate) {
-                    final int forwardLinkAddress = buffer.readUnsignedInt24();
-                    if (forwardLinkAddress != FormatSpec.NO_FORWARD_LINK_ADDRESS) {
-                        // the node has a forward link.
-                        p.mNumOfCharGroup = Position.NOT_READ_GROUPCOUNT;
-                        p.mAddress = forwardLinkAddress;
+                    final boolean hasValidForwardLinkAddress =
+                            dictDecoder.readAndFollowForwardLink();
+                    if (hasValidForwardLinkAddress && dictDecoder.hasNextPtNodeArray()) {
+                        // The node array has a forward link.
+                        p.mNumOfPtNode = Position.NOT_READ_PTNODE_COUNT;
+                        p.mAddress = dictDecoder.getPosition();
                     } else {
                         stack.pop();
                     }
@@ -130,12 +126,12 @@
                     stack.pop();
                 }
             } else {
-                // the node has more groups.
-                p.mAddress = buffer.position();
+                // The Ptnode array has more PtNodes.
+                p.mAddress = dictDecoder.getPosition();
             }
 
-            if (!isMovedGroup && BinaryDictInputOutput.hasChildrenAddress(info.mChildrenAddress)) {
-                Position childrenPos = new Position(info.mChildrenAddress + headerSize, index);
+            if (!isMovedPtNode && hasChildrenAddress(info.mChildrenAddress)) {
+                final Position childrenPos = new Position(info.mChildrenAddress, index);
                 stack.push(childrenPos);
             }
         }
@@ -143,61 +139,59 @@
 
     /**
      * Reads unigrams and bigrams from the binary file.
-     * Doesn't make the memory representation of the dictionary.
+     * Doesn't store a full memory representation of the dictionary.
      *
-     * @param buffer the buffer to read.
+     * @param dictDecoder the dict decoder.
      * @param words the map to store the address as a key and the word as a value.
      * @param frequencies the map to store the address as a key and the frequency as a value.
      * @param bigrams the map to store the address as a key and the list of address as a value.
-     * @throws IOException
-     * @throws UnsupportedFormatException
+     * @throws IOException if the file can't be read.
+     * @throws UnsupportedFormatException if the format of the file is not recognized.
      */
-    public static void readUnigramsAndBigramsBinary(final FusionDictionaryBufferInterface buffer,
+    /* package */ static void readUnigramsAndBigramsBinary(final DictDecoder dictDecoder,
             final Map<Integer, String> words, final Map<Integer, Integer> frequencies,
             final Map<Integer, ArrayList<PendingAttribute>> bigrams) throws IOException,
             UnsupportedFormatException {
         // Read header
-        final FileHeader header = BinaryDictInputOutput.readHeader(buffer);
-        readUnigramsAndBigramsBinaryInner(buffer, header.mHeaderSize, words, frequencies, bigrams,
-                header.mFormatOptions);
+        final FileHeader header = dictDecoder.readHeader();
+        readUnigramsAndBigramsBinaryInner(dictDecoder, header.mHeaderSize, words,
+                frequencies, bigrams, header.mFormatOptions);
     }
 
     /**
-     * Gets the address of the last CharGroup of the exact matching word in the dictionary.
+     * Gets the address of the last PtNode of the exact matching word in the dictionary.
      * If no match is found, returns NOT_VALID_WORD.
      *
-     * @param buffer the buffer to read.
+     * @param dictDecoder the dict decoder.
      * @param word the word we search for.
      * @return the address of the terminal node.
-     * @throws IOException
-     * @throws UnsupportedFormatException
+     * @throws IOException if the file can't be read.
+     * @throws UnsupportedFormatException if the format of the file is not recognized.
      */
     @UsedForTesting
-    public static int getTerminalPosition(final FusionDictionaryBufferInterface buffer,
+    /* package */ static int getTerminalPosition(final DictDecoder dictDecoder,
             final String word) throws IOException, UnsupportedFormatException {
         if (word == null) return FormatSpec.NOT_VALID_WORD;
-        if (buffer.position() != 0) buffer.position(0);
+        dictDecoder.setPosition(0);
 
-        final FileHeader header = BinaryDictInputOutput.readHeader(buffer);
+        final FileHeader header = dictDecoder.readHeader();
         int wordPos = 0;
         final int wordLen = word.codePointCount(0, word.length());
         for (int depth = 0; depth < Constants.DICTIONARY_MAX_WORD_LENGTH; ++depth) {
             if (wordPos >= wordLen) return FormatSpec.NOT_VALID_WORD;
 
             do {
-                final int charGroupCount = BinaryDictInputOutput.readCharGroupCount(buffer);
-                boolean foundNextCharGroup = false;
-                for (int i = 0; i < charGroupCount; ++i) {
-                    final int charGroupPos = buffer.position();
-                    final CharGroupInfo currentInfo = BinaryDictInputOutput.readCharGroup(buffer,
-                            buffer.position(), header.mFormatOptions);
-                    final boolean isMovedGroup =
-                            BinaryDictInputOutput.isMovedGroup(currentInfo.mFlags,
-                                    header.mFormatOptions);
-                    final boolean isDeletedGroup =
-                            BinaryDictInputOutput.isDeletedGroup(currentInfo.mFlags,
-                                    header.mFormatOptions);
-                    if (isMovedGroup) continue;
+                final int ptNodeCount = dictDecoder.readPtNodeCount();
+                boolean foundNextPtNode = false;
+                for (int i = 0; i < ptNodeCount; ++i) {
+                    final int ptNodePos = dictDecoder.getPosition();
+                    final PtNodeInfo currentInfo = dictDecoder.readPtNode(ptNodePos,
+                            header.mFormatOptions);
+                    final boolean isMovedNode = isMovedPtNode(currentInfo.mFlags,
+                            header.mFormatOptions);
+                    final boolean isDeletedNode = isDeletedPtNode(currentInfo.mFlags,
+                            header.mFormatOptions);
+                    if (isMovedNode) continue;
                     boolean same = true;
                     for (int p = 0, j = word.offsetByCodePoints(0, wordPos);
                             p < currentInfo.mCharacters.length;
@@ -210,86 +204,60 @@
                     }
 
                     if (same) {
-                        // found the group matches the word.
+                        // found the PtNode matches the word.
                         if (wordPos + currentInfo.mCharacters.length == wordLen) {
-                            if (currentInfo.mFrequency == CharGroup.NOT_A_TERMINAL
-                                    || isDeletedGroup) {
+                            if (currentInfo.mFrequency == PtNode.NOT_A_TERMINAL
+                                    || isDeletedNode) {
                                 return FormatSpec.NOT_VALID_WORD;
                             } else {
-                                return charGroupPos;
+                                return ptNodePos;
                             }
                         }
                         wordPos += currentInfo.mCharacters.length;
                         if (currentInfo.mChildrenAddress == FormatSpec.NO_CHILDREN_ADDRESS) {
                             return FormatSpec.NOT_VALID_WORD;
                         }
-                        foundNextCharGroup = true;
-                        buffer.position(currentInfo.mChildrenAddress);
+                        foundNextPtNode = true;
+                        dictDecoder.setPosition(currentInfo.mChildrenAddress);
                         break;
                     }
                 }
 
-                // If we found the next char group, it is under the file pointer.
-                // But if not, we are at the end of this node so we expect to have
+                // If we found the next PtNode, it is under the file pointer.
+                // But if not, we are at the end of this node array so we expect to have
                 // a forward link address that we need to consult and possibly resume
-                // search on the next node in the linked list.
-                if (foundNextCharGroup) break;
+                // search on the next node array in the linked list.
+                if (foundNextPtNode) break;
                 if (!header.mFormatOptions.mSupportsDynamicUpdate) {
                     return FormatSpec.NOT_VALID_WORD;
                 }
 
-                final int forwardLinkAddress = buffer.readUnsignedInt24();
-                if (forwardLinkAddress == FormatSpec.NO_FORWARD_LINK_ADDRESS) {
+                final boolean hasValidForwardLinkAddress =
+                        dictDecoder.readAndFollowForwardLink();
+                if (!hasValidForwardLinkAddress || !dictDecoder.hasNextPtNodeArray()) {
                     return FormatSpec.NOT_VALID_WORD;
                 }
-                buffer.position(forwardLinkAddress);
             } while(true);
         }
         return FormatSpec.NOT_VALID_WORD;
     }
 
-    private static int markAsDeleted(final int flags) {
-        return (flags & (~FormatSpec.MASK_GROUP_ADDRESS_TYPE)) | FormatSpec.FLAG_IS_DELETED;
-    }
-
-    /**
-     * Delete the word from the binary file.
-     *
-     * @param buffer the buffer to write.
-     * @param word the word we delete
-     * @throws IOException
-     * @throws UnsupportedFormatException
-     */
-    @UsedForTesting
-    public static void deleteWord(final FusionDictionaryBufferInterface buffer,
-            final String word) throws IOException, UnsupportedFormatException {
-        buffer.position(0);
-        final FileHeader header = BinaryDictInputOutput.readHeader(buffer);
-        final int wordPosition = getTerminalPosition(buffer, word);
-        if (wordPosition == FormatSpec.NOT_VALID_WORD) return;
-
-        buffer.position(wordPosition);
-        final int flags = buffer.readUnsignedByte();
-        buffer.position(wordPosition);
-        buffer.put((byte)markAsDeleted(flags));
-    }
-
     /**
      * @return the size written, in bytes. Always 3 bytes.
      */
-    private static int writeSInt24ToBuffer(final FusionDictionaryBufferInterface buffer,
+    static int writeSInt24ToBuffer(final DictBuffer dictBuffer,
             final int value) {
         final int absValue = Math.abs(value);
-        buffer.put((byte)(((value < 0 ? 0x80 : 0) | (absValue >> 16)) & 0xFF));
-        buffer.put((byte)((absValue >> 8) & 0xFF));
-        buffer.put((byte)(absValue & 0xFF));
+        dictBuffer.put((byte)(((value < 0 ? 0x80 : 0) | (absValue >> 16)) & 0xFF));
+        dictBuffer.put((byte)((absValue >> 8) & 0xFF));
+        dictBuffer.put((byte)(absValue & 0xFF));
         return 3;
     }
 
     /**
      * @return the size written, in bytes. Always 3 bytes.
      */
-    private static int writeSInt24ToStream(final OutputStream destination, final int value)
+    static int writeSInt24ToStream(final OutputStream destination, final int value)
             throws IOException {
         final int absValue = Math.abs(value);
         destination.write((byte)(((value < 0 ? 0x80 : 0) | (absValue >> 16)) & 0xFF));
@@ -303,7 +271,7 @@
      */
     private static int writeVariableAddress(final OutputStream destination, final int value)
             throws IOException {
-        switch (BinaryDictInputOutput.getByteSize(value)) {
+        switch (BinaryDictEncoderUtils.getByteSize(value)) {
         case 1:
             destination.write((byte)value);
             break;
@@ -317,111 +285,52 @@
             destination.write((byte)(0xFF & value));
             break;
         }
-        return BinaryDictInputOutput.getByteSize(value);
+        return BinaryDictEncoderUtils.getByteSize(value);
     }
 
-    /**
-     * Update a parent address in a CharGroup that is referred to by groupOriginAddress.
-     *
-     * @param buffer the buffer to write.
-     * @param groupOriginAddress the address of the group.
-     * @param newParentAddress the absolute address of the parent.
-     * @param formatOptions file format options.
-     */
-    public static void updateParentAddress(final FusionDictionaryBufferInterface buffer,
-            final int groupOriginAddress, final int newParentAddress,
-            final FormatOptions formatOptions) {
-        final int originalPosition = buffer.position();
-        buffer.position(groupOriginAddress);
-        if (!formatOptions.mSupportsDynamicUpdate) {
-            throw new RuntimeException("this file format does not support parent addresses");
-        }
-        final int flags = buffer.readUnsignedByte();
-        if (BinaryDictInputOutput.isMovedGroup(flags, formatOptions)) {
-            // if the group is moved, the parent address is stored in the destination group.
-            // We are guaranteed to process the destination group later, so there is no need to
-            // update anything here.
-            buffer.position(originalPosition);
-            return;
-        }
-        if (DBG) {
-            MakedictLog.d("update parent address flags=" + flags + ", " + groupOriginAddress);
-        }
-        final int parentOffset = newParentAddress - groupOriginAddress;
-        writeSInt24ToBuffer(buffer, parentOffset);
-        buffer.position(originalPosition);
-    }
-
-    private static void skipCharGroup(final FusionDictionaryBufferInterface buffer,
-            final FormatOptions formatOptions) {
-        final int flags = buffer.readUnsignedByte();
-        BinaryDictInputOutput.readParentAddress(buffer, formatOptions);
-        skipString(buffer, (flags & FormatSpec.FLAG_HAS_MULTIPLE_CHARS) != 0);
-        BinaryDictInputOutput.readChildrenAddress(buffer, flags, formatOptions);
-        if ((flags & FormatSpec.FLAG_IS_TERMINAL) != 0) buffer.readUnsignedByte();
+    static void skipPtNode(final DictBuffer dictBuffer, final FormatOptions formatOptions) {
+        final int flags = dictBuffer.readUnsignedByte();
+        BinaryDictDecoderUtils.readParentAddress(dictBuffer, formatOptions);
+        skipString(dictBuffer, (flags & FormatSpec.FLAG_HAS_MULTIPLE_CHARS) != 0);
+        BinaryDictDecoderUtils.readChildrenAddress(dictBuffer, flags, formatOptions);
+        if ((flags & FormatSpec.FLAG_IS_TERMINAL) != 0) dictBuffer.readUnsignedByte();
         if ((flags & FormatSpec.FLAG_HAS_SHORTCUT_TARGETS) != 0) {
-            final int shortcutsSize = buffer.readUnsignedShort();
-            buffer.position(buffer.position() + shortcutsSize
-                    - FormatSpec.GROUP_SHORTCUT_LIST_SIZE_SIZE);
+            final int shortcutsSize = dictBuffer.readUnsignedShort();
+            dictBuffer.position(dictBuffer.position() + shortcutsSize
+                    - FormatSpec.PTNODE_SHORTCUT_LIST_SIZE_SIZE);
         }
         if ((flags & FormatSpec.FLAG_HAS_BIGRAMS) != 0) {
             int bigramCount = 0;
-            while (bigramCount++ < FormatSpec.MAX_BIGRAMS_IN_A_GROUP) {
-                final int bigramFlags = buffer.readUnsignedByte();
-                switch (bigramFlags & FormatSpec.MASK_ATTRIBUTE_ADDRESS_TYPE) {
-                    case FormatSpec.FLAG_ATTRIBUTE_ADDRESS_TYPE_ONEBYTE:
-                        buffer.readUnsignedByte();
+            while (bigramCount++ < FormatSpec.MAX_BIGRAMS_IN_A_PTNODE) {
+                final int bigramFlags = dictBuffer.readUnsignedByte();
+                switch (bigramFlags & FormatSpec.MASK_BIGRAM_ATTR_ADDRESS_TYPE) {
+                    case FormatSpec.FLAG_BIGRAM_ATTR_ADDRESS_TYPE_ONEBYTE:
+                        dictBuffer.readUnsignedByte();
                         break;
-                    case FormatSpec.FLAG_ATTRIBUTE_ADDRESS_TYPE_TWOBYTES:
-                        buffer.readUnsignedShort();
+                    case FormatSpec.FLAG_BIGRAM_ATTR_ADDRESS_TYPE_TWOBYTES:
+                        dictBuffer.readUnsignedShort();
                         break;
-                    case FormatSpec.FLAG_ATTRIBUTE_ADDRESS_TYPE_THREEBYTES:
-                        buffer.readUnsignedInt24();
+                    case FormatSpec.FLAG_BIGRAM_ATTR_ADDRESS_TYPE_THREEBYTES:
+                        dictBuffer.readUnsignedInt24();
                         break;
                 }
-                if ((bigramFlags & FormatSpec.FLAG_ATTRIBUTE_HAS_NEXT) == 0) break;
+                if ((bigramFlags & FormatSpec.FLAG_BIGRAM_SHORTCUT_ATTR_HAS_NEXT) == 0) break;
             }
-            if (bigramCount >= FormatSpec.MAX_BIGRAMS_IN_A_GROUP) {
-                throw new RuntimeException("Too many bigrams in a group.");
+            if (bigramCount >= FormatSpec.MAX_BIGRAMS_IN_A_PTNODE) {
+                throw new RuntimeException("Too many bigrams in a PtNode.");
             }
         }
     }
 
-    /**
-     * Update parent addresses in a Node that is referred to by nodeOriginAddress.
-     *
-     * @param buffer the buffer to be modified.
-     * @param nodeOriginAddress the address of a modified Node.
-     * @param newParentAddress the address to be written.
-     * @param formatOptions file format options.
-     */
-    public static void updateParentAddresses(final FusionDictionaryBufferInterface buffer,
-            final int nodeOriginAddress, final int newParentAddress,
-            final FormatOptions formatOptions) {
-        final int originalPosition = buffer.position();
-        buffer.position(nodeOriginAddress);
-        do {
-            final int count = BinaryDictInputOutput.readCharGroupCount(buffer);
-            for (int i = 0; i < count; ++i) {
-                updateParentAddress(buffer, buffer.position(), newParentAddress, formatOptions);
-                skipCharGroup(buffer, formatOptions);
-            }
-            final int forwardLinkAddress = buffer.readUnsignedInt24();
-            buffer.position(forwardLinkAddress);
-        } while (formatOptions.mSupportsDynamicUpdate
-                && buffer.position() != FormatSpec.NO_FORWARD_LINK_ADDRESS);
-        buffer.position(originalPosition);
-    }
-
-    private static void skipString(final FusionDictionaryBufferInterface buffer,
+    static void skipString(final DictBuffer dictBuffer,
             final boolean hasMultipleChars) {
         if (hasMultipleChars) {
-            int character = CharEncoding.readChar(buffer);
+            int character = CharEncoding.readChar(dictBuffer);
             while (character != FormatSpec.INVALID_CHARACTER) {
-                character = CharEncoding.readChar(buffer);
+                character = CharEncoding.readChar(dictBuffer);
             }
         } else {
-            CharEncoding.readChar(buffer);
+            CharEncoding.readChar(dictBuffer);
         }
     }
 
@@ -449,46 +358,24 @@
                 size += 3;
             }
         }
-        destination.write((byte)FormatSpec.GROUP_CHARACTERS_TERMINATOR);
-        size += FormatSpec.GROUP_TERMINATOR_SIZE;
+        destination.write((byte)FormatSpec.PTNODE_CHARACTERS_TERMINATOR);
+        size += FormatSpec.PTNODE_TERMINATOR_SIZE;
         return size;
     }
 
     /**
-     * Update a children address in a CharGroup that is addressed by groupOriginAddress.
-     *
-     * @param buffer the buffer to write.
-     * @param groupOriginAddress the address of the group.
-     * @param newChildrenAddress the absolute address of the child.
-     * @param formatOptions file format options.
-     */
-    public static void updateChildrenAddress(final FusionDictionaryBufferInterface buffer,
-            final int groupOriginAddress, final int newChildrenAddress,
-            final FormatOptions formatOptions) {
-        final int originalPosition = buffer.position();
-        buffer.position(groupOriginAddress);
-        final int flags = buffer.readUnsignedByte();
-        final int parentAddress = BinaryDictInputOutput.readParentAddress(buffer, formatOptions);
-        skipString(buffer, (flags & FormatSpec.FLAG_HAS_MULTIPLE_CHARS) != 0);
-        if ((flags & FormatSpec.FLAG_IS_TERMINAL) != 0) buffer.readUnsignedByte();
-        final int childrenOffset = newChildrenAddress == FormatSpec.NO_CHILDREN_ADDRESS
-                ? FormatSpec.NO_CHILDREN_ADDRESS : newChildrenAddress - buffer.position();
-        writeSInt24ToBuffer(buffer, childrenOffset);
-        buffer.position(originalPosition);
-    }
-
-    /**
-     * Write a char group to an output stream.
-     * A char group is an in-memory representation of a node in trie.
-     * A char group info is an on-disk representation of a node.
+     * Write a PtNode to an output stream from a PtNodeInfo.
+     * A PtNode is an in-memory representation of a node in the patricia trie.
+     * A PtNode info is a container for low-level information about how the
+     * PtNode is stored in the binary format.
      *
      * @param destination the stream to write.
-     * @param info the char group info to be written.
+     * @param info the PtNode info to be written.
      * @return the size written, in bytes.
      */
-    public static int writeCharGroup(final OutputStream destination, final CharGroupInfo info)
+    private static int writePtNode(final OutputStream destination, final PtNodeInfo info)
             throws IOException {
-        int size = FormatSpec.GROUP_FLAGS_SIZE;
+        int size = FormatSpec.PTNODE_FLAGS_SIZE;
         destination.write((byte)info.mFlags);
         final int parentOffset = info.mParentAddress == FormatSpec.NO_PARENT_ADDRESS ?
                 FormatSpec.NO_PARENT_ADDRESS : info.mParentAddress - info.mOriginalAddress;
@@ -503,7 +390,7 @@
             }
         }
         if (info.mCharacters.length > 1) {
-            destination.write((byte)FormatSpec.GROUP_CHARACTERS_TERMINATOR);
+            destination.write((byte)FormatSpec.PTNODE_CHARACTERS_TERMINATOR);
             size++;
         }
 
@@ -513,7 +400,7 @@
         }
 
         if (DBG) {
-            MakedictLog.d("writeCharGroup origin=" + info.mOriginalAddress + ", size=" + size
+            MakedictLog.d("writePtNode origin=" + info.mOriginalAddress + ", size=" + size
                     + ", child=" + info.mChildrenAddress + ", characters ="
                     + new String(info.mCharacters, 0, info.mCharacters.length));
         }
@@ -524,14 +411,14 @@
 
         if (info.mShortcutTargets != null && info.mShortcutTargets.size() > 0) {
             final int shortcutListSize =
-                    BinaryDictInputOutput.getShortcutListSize(info.mShortcutTargets);
+                    BinaryDictEncoderUtils.getShortcutListSize(info.mShortcutTargets);
             destination.write((byte)(shortcutListSize >> 8));
             destination.write((byte)(shortcutListSize & 0xFF));
             size += 2;
             final Iterator<WeightedString> shortcutIterator = info.mShortcutTargets.iterator();
             while (shortcutIterator.hasNext()) {
                 final WeightedString target = shortcutIterator.next();
-                destination.write((byte)BinaryDictInputOutput.makeShortcutFlags(
+                destination.write((byte)BinaryDictEncoderUtils.makeShortcutFlags(
                         shortcutIterator.hasNext(), target.mFrequency));
                 size++;
                 size += writeString(destination, target.mWord);
@@ -540,28 +427,28 @@
 
         if (info.mBigrams != null) {
             // TODO: Consolidate this code with the code that computes the size of the bigram list
-            //        in BinaryDictionaryInputOutput#computeActualNodeSize
+            //        in BinaryDictEncoderUtils#computeActualNodeArraySize
             for (int i = 0; i < info.mBigrams.size(); ++i) {
 
                 final int bigramFrequency = info.mBigrams.get(i).mFrequency;
                 int bigramFlags = (i < info.mBigrams.size() - 1)
-                        ? FormatSpec.FLAG_ATTRIBUTE_HAS_NEXT : 0;
+                        ? FormatSpec.FLAG_BIGRAM_SHORTCUT_ATTR_HAS_NEXT : 0;
                 size++;
                 final int bigramOffset = info.mBigrams.get(i).mAddress - (info.mOriginalAddress
                         + size);
-                bigramFlags |= (bigramOffset < 0) ? FormatSpec.FLAG_ATTRIBUTE_OFFSET_NEGATIVE : 0;
-                switch (BinaryDictInputOutput.getByteSize(bigramOffset)) {
+                bigramFlags |= (bigramOffset < 0) ? FormatSpec.FLAG_BIGRAM_ATTR_OFFSET_NEGATIVE : 0;
+                switch (BinaryDictEncoderUtils.getByteSize(bigramOffset)) {
                 case 1:
-                    bigramFlags |= FormatSpec.FLAG_ATTRIBUTE_ADDRESS_TYPE_ONEBYTE;
+                    bigramFlags |= FormatSpec.FLAG_BIGRAM_ATTR_ADDRESS_TYPE_ONEBYTE;
                     break;
                 case 2:
-                    bigramFlags |= FormatSpec.FLAG_ATTRIBUTE_ADDRESS_TYPE_TWOBYTES;
+                    bigramFlags |= FormatSpec.FLAG_BIGRAM_ATTR_ADDRESS_TYPE_TWOBYTES;
                     break;
                 case 3:
-                    bigramFlags |= FormatSpec.FLAG_ATTRIBUTE_ADDRESS_TYPE_THREEBYTES;
+                    bigramFlags |= FormatSpec.FLAG_BIGRAM_ATTR_ADDRESS_TYPE_THREEBYTES;
                     break;
                 }
-                bigramFlags |= bigramFrequency & FormatSpec.FLAG_ATTRIBUTE_FREQUENCY;
+                bigramFlags |= bigramFrequency & FormatSpec.FLAG_BIGRAM_SHORTCUT_ATTR_FREQUENCY;
                 destination.write((byte)bigramFlags);
                 size += writeVariableAddress(destination, Math.abs(bigramOffset));
             }
@@ -569,82 +456,40 @@
         return size;
     }
 
-    @SuppressWarnings("unused")
-    private static void updateForwardLink(final FusionDictionaryBufferInterface buffer,
-            final int nodeOriginAddress, final int newNodeAddress,
-            final FormatOptions formatOptions) {
-        buffer.position(nodeOriginAddress);
-        int jumpCount = 0;
-        while (jumpCount++ < MAX_JUMPS) {
-            final int count = BinaryDictInputOutput.readCharGroupCount(buffer);
-            for (int i = 0; i < count; ++i) skipCharGroup(buffer, formatOptions);
-            final int forwardLinkAddress = buffer.readUnsignedInt24();
-            if (forwardLinkAddress == FormatSpec.NO_FORWARD_LINK_ADDRESS) {
-                buffer.position(buffer.position() - FormatSpec.FORWARD_LINK_ADDRESS_SIZE);
-                writeSInt24ToBuffer(buffer, newNodeAddress);
-                return;
-            }
-            buffer.position(forwardLinkAddress);
-        }
-        if (DBG && jumpCount >= MAX_JUMPS) {
-            throw new RuntimeException("too many jumps, probably a bug.");
-        }
-    }
-
     /**
-     * Helper method to move a char group to the tail of the file.
+     * Compute the size of the PtNode.
      */
-    private static int moveCharGroup(final OutputStream destination,
-            final FusionDictionaryBufferInterface buffer, final CharGroupInfo info,
-            final int nodeOriginAddress, final int oldGroupAddress,
-            final FormatOptions formatOptions) throws IOException {
-        updateParentAddress(buffer, oldGroupAddress, buffer.limit() + 1, formatOptions);
-        buffer.position(oldGroupAddress);
-        final int currentFlags = buffer.readUnsignedByte();
-        buffer.position(oldGroupAddress);
-        buffer.put((byte)(FormatSpec.FLAG_IS_MOVED | (currentFlags
-                & (~FormatSpec.MASK_MOVE_AND_DELETE_FLAG))));
-        int size = FormatSpec.GROUP_FLAGS_SIZE;
-        updateForwardLink(buffer, nodeOriginAddress, buffer.limit(), formatOptions);
-        size += writeNode(destination, new CharGroupInfo[] { info });
-        return size;
-    }
-
-    /**
-     * Compute the size of the char group.
-     */
-    private static int computeGroupSize(final CharGroupInfo info,
-            final FormatOptions formatOptions) {
-        int size = FormatSpec.GROUP_FLAGS_SIZE + FormatSpec.PARENT_ADDRESS_SIZE
-                + BinaryDictInputOutput.getGroupCharactersSize(info.mCharacters)
-                + BinaryDictInputOutput.getChildrenAddressSize(info.mFlags, formatOptions);
+    static int computePtNodeSize(final PtNodeInfo info, final FormatOptions formatOptions) {
+        int size = FormatSpec.PTNODE_FLAGS_SIZE + FormatSpec.PARENT_ADDRESS_SIZE
+                + BinaryDictEncoderUtils.getPtNodeCharactersSize(info.mCharacters)
+                + getChildrenAddressSize(info.mFlags, formatOptions);
         if ((info.mFlags & FormatSpec.FLAG_IS_TERMINAL) != 0) {
-            size += FormatSpec.GROUP_FREQUENCY_SIZE;
+            size += FormatSpec.PTNODE_FREQUENCY_SIZE;
         }
         if (info.mShortcutTargets != null && !info.mShortcutTargets.isEmpty()) {
-            size += BinaryDictInputOutput.getShortcutListSize(info.mShortcutTargets);
+            size += BinaryDictEncoderUtils.getShortcutListSize(info.mShortcutTargets);
         }
         if (info.mBigrams != null) {
             for (final PendingAttribute attr : info.mBigrams) {
-                size += FormatSpec.GROUP_FLAGS_SIZE;
-                size += BinaryDictInputOutput.getByteSize(attr.mAddress);
+                size += FormatSpec.PTNODE_FLAGS_SIZE;
+                size += BinaryDictEncoderUtils.getByteSize(attr.mAddress);
             }
         }
         return size;
     }
 
     /**
-     * Write a node to the stream.
+     * Write a node array to the stream.
      *
      * @param destination the stream to write.
-     * @param infos groups to be written.
+     * @param infos an array of PtNodeInfo to be written.
      * @return the size written, in bytes.
      * @throws IOException
      */
-    private static int writeNode(final OutputStream destination, final CharGroupInfo[] infos)
+    static int writeNodes(final OutputStream destination, final PtNodeInfo[] infos)
             throws IOException {
-        int size = BinaryDictInputOutput.getGroupCountSize(infos.length);
-        switch (BinaryDictInputOutput.getGroupCountSize(infos.length)) {
+        int size = getPtNodeCountSize(infos.length);
+        switch (getPtNodeCountSize(infos.length)) {
             case 1:
                 destination.write((byte)infos.length);
                 break;
@@ -653,335 +498,13 @@
                 destination.write((byte)(infos.length & 0xFF));
                 break;
             default:
-                throw new RuntimeException("Invalid group count size.");
+                throw new RuntimeException("Invalid node count size.");
         }
-        for (final CharGroupInfo info : infos) size += writeCharGroup(destination, info);
+        for (final PtNodeInfo info : infos) size += writePtNode(destination, info);
         writeSInt24ToStream(destination, FormatSpec.NO_FORWARD_LINK_ADDRESS);
         return size + FormatSpec.FORWARD_LINK_ADDRESS_SIZE;
     }
 
-    /**
-     * Move a group that is referred to by oldGroupOrigin to the tail of the file.
-     * And set the children address to the byte after the group.
-     *
-     * @param nodeOrigin the address of the tail of the file.
-     * @param characters
-     * @param length
-     * @param flags
-     * @param frequency
-     * @param parentAddress
-     * @param shortcutTargets
-     * @param bigrams
-     * @param destination the stream representing the tail of the file.
-     * @param buffer the buffer representing the (constant-size) body of the file.
-     * @param oldNodeOrigin
-     * @param oldGroupOrigin
-     * @param formatOptions
-     * @return the size written, in bytes.
-     * @throws IOException
-     */
-    private static int moveGroup(final int nodeOrigin, final int[] characters, final int length,
-            final int flags, final int frequency, final int parentAddress,
-            final ArrayList<WeightedString> shortcutTargets,
-            final ArrayList<PendingAttribute> bigrams, final OutputStream destination,
-            final FusionDictionaryBufferInterface buffer, final int oldNodeOrigin,
-            final int oldGroupOrigin, final FormatOptions formatOptions) throws IOException {
-        int size = 0;
-        final int newGroupOrigin = nodeOrigin + 1;
-        final int[] writtenCharacters = Arrays.copyOfRange(characters, 0, length);
-        final CharGroupInfo tmpInfo = new CharGroupInfo(newGroupOrigin, -1 /* endAddress */,
-                flags, writtenCharacters, frequency, parentAddress, FormatSpec.NO_CHILDREN_ADDRESS,
-                shortcutTargets, bigrams);
-        size = computeGroupSize(tmpInfo, formatOptions);
-        final CharGroupInfo newInfo = new CharGroupInfo(newGroupOrigin, newGroupOrigin + size,
-                flags, writtenCharacters, frequency, parentAddress,
-                nodeOrigin + 1 + size + FormatSpec.FORWARD_LINK_ADDRESS_SIZE, shortcutTargets,
-                bigrams);
-        moveCharGroup(destination, buffer, newInfo, oldNodeOrigin, oldGroupOrigin, formatOptions);
-        return 1 + size + FormatSpec.FORWARD_LINK_ADDRESS_SIZE;
-    }
-
-    /**
-     * Insert a word into a binary dictionary.
-     *
-     * @param buffer
-     * @param destination
-     * @param word
-     * @param frequency
-     * @param bigramStrings
-     * @param shortcuts
-     * @throws IOException
-     * @throws UnsupportedFormatException
-     */
-    // TODO: Support batch insertion.
-    // TODO: Remove @UsedForTesting once UserHistoryDictionary is implemented by BinaryDictionary.
-    @UsedForTesting
-    public static void insertWord(final FusionDictionaryBufferInterface buffer,
-            final OutputStream destination, final String word, final int frequency,
-            final ArrayList<WeightedString> bigramStrings,
-            final ArrayList<WeightedString> shortcuts, final boolean isNotAWord,
-            final boolean isBlackListEntry)
-                    throws IOException, UnsupportedFormatException {
-        final ArrayList<PendingAttribute> bigrams = new ArrayList<PendingAttribute>();
-        if (bigramStrings != null) {
-            for (final WeightedString bigram : bigramStrings) {
-                int position = getTerminalPosition(buffer, bigram.mWord);
-                if (position == FormatSpec.NOT_VALID_WORD) {
-                    // TODO: figure out what is the correct thing to do here.
-                } else {
-                    bigrams.add(new PendingAttribute(bigram.mFrequency, position));
-                }
-            }
-        }
-
-        final boolean isTerminal = true;
-        final boolean hasBigrams = !bigrams.isEmpty();
-        final boolean hasShortcuts = shortcuts != null && !shortcuts.isEmpty();
-
-        // find the insert position of the word.
-        if (buffer.position() != 0) buffer.position(0);
-        final FileHeader header = BinaryDictInputOutput.readHeader(buffer);
-
-        int wordPos = 0, address = buffer.position(), nodeOriginAddress = buffer.position();
-        final int[] codePoints = FusionDictionary.getCodePoints(word);
-        final int wordLen = codePoints.length;
-
-        for (int depth = 0; depth < Constants.DICTIONARY_MAX_WORD_LENGTH; ++depth) {
-            if (wordPos >= wordLen) break;
-            nodeOriginAddress = buffer.position();
-            int nodeParentAddress = -1;
-            final int charGroupCount = BinaryDictInputOutput.readCharGroupCount(buffer);
-            boolean foundNextGroup = false;
-
-            for (int i = 0; i < charGroupCount; ++i) {
-                address = buffer.position();
-                final CharGroupInfo currentInfo = BinaryDictInputOutput.readCharGroup(buffer,
-                        buffer.position(), header.mFormatOptions);
-                final boolean isMovedGroup = BinaryDictInputOutput.isMovedGroup(currentInfo.mFlags,
-                        header.mFormatOptions);
-                if (isMovedGroup) continue;
-                nodeParentAddress = (currentInfo.mParentAddress == FormatSpec.NO_PARENT_ADDRESS)
-                        ? FormatSpec.NO_PARENT_ADDRESS : currentInfo.mParentAddress + address;
-                boolean matched = true;
-                for (int p = 0; p < currentInfo.mCharacters.length; ++p) {
-                    if (wordPos + p >= wordLen) {
-                        /*
-                         * splitting
-                         * before
-                         *  abcd - ef
-                         *
-                         * insert "abc"
-                         *
-                         * after
-                         *  abc - d - ef
-                         */
-                        final int newNodeAddress = buffer.limit();
-                        final int flags = BinaryDictInputOutput.makeCharGroupFlags(p > 1,
-                                isTerminal, 0, hasShortcuts, hasBigrams, false /* isNotAWord */,
-                                false /* isBlackListEntry */, header.mFormatOptions);
-                        int written = moveGroup(newNodeAddress, currentInfo.mCharacters, p, flags,
-                                frequency, nodeParentAddress, shortcuts, bigrams, destination,
-                                buffer, nodeOriginAddress, address, header.mFormatOptions);
-
-                        final int[] characters2 = Arrays.copyOfRange(currentInfo.mCharacters, p,
-                                currentInfo.mCharacters.length);
-                        if (currentInfo.mChildrenAddress != FormatSpec.NO_CHILDREN_ADDRESS) {
-                            updateParentAddresses(buffer, currentInfo.mChildrenAddress,
-                                    newNodeAddress + written + 1, header.mFormatOptions);
-                        }
-                        final CharGroupInfo newInfo2 = new CharGroupInfo(
-                                newNodeAddress + written + 1, -1 /* endAddress */,
-                                currentInfo.mFlags, characters2, currentInfo.mFrequency,
-                                newNodeAddress + 1, currentInfo.mChildrenAddress,
-                                currentInfo.mShortcutTargets, currentInfo.mBigrams);
-                        writeNode(destination, new CharGroupInfo[] { newInfo2 });
-                        return;
-                    } else if (codePoints[wordPos + p] != currentInfo.mCharacters[p]) {
-                        if (p > 0) {
-                            /*
-                             * splitting
-                             * before
-                             *   ab - cd
-                             *
-                             * insert "ac"
-                             *
-                             * after
-                             *   a - b - cd
-                             *     |
-                             *     - c
-                             */
-
-                            final int newNodeAddress = buffer.limit();
-                            final int childrenAddress = currentInfo.mChildrenAddress;
-
-                            // move prefix
-                            final int prefixFlags = BinaryDictInputOutput.makeCharGroupFlags(p > 1,
-                                    false /* isTerminal */, 0 /* childrenAddressSize*/,
-                                    false /* hasShortcut */, false /* hasBigrams */,
-                                    false /* isNotAWord */, false /* isBlackListEntry */,
-                                    header.mFormatOptions);
-                            int written = moveGroup(newNodeAddress, currentInfo.mCharacters, p,
-                                    prefixFlags, -1 /* frequency */, nodeParentAddress, null, null,
-                                    destination, buffer, nodeOriginAddress, address,
-                                    header.mFormatOptions);
-
-                            final int[] suffixCharacters = Arrays.copyOfRange(
-                                    currentInfo.mCharacters, p, currentInfo.mCharacters.length);
-                            if (currentInfo.mChildrenAddress != FormatSpec.NO_CHILDREN_ADDRESS) {
-                                updateParentAddresses(buffer, currentInfo.mChildrenAddress,
-                                        newNodeAddress + written + 1, header.mFormatOptions);
-                            }
-                            final int suffixFlags = BinaryDictInputOutput.makeCharGroupFlags(
-                                    suffixCharacters.length > 1,
-                                    (currentInfo.mFlags & FormatSpec.FLAG_IS_TERMINAL) != 0,
-                                    0 /* childrenAddressSize */,
-                                    (currentInfo.mFlags & FormatSpec.FLAG_HAS_SHORTCUT_TARGETS)
-                                            != 0,
-                                    (currentInfo.mFlags & FormatSpec.FLAG_HAS_BIGRAMS) != 0,
-                                    isNotAWord, isBlackListEntry, header.mFormatOptions);
-                            final CharGroupInfo suffixInfo = new CharGroupInfo(
-                                    newNodeAddress + written + 1, -1 /* endAddress */, suffixFlags,
-                                    suffixCharacters, currentInfo.mFrequency, newNodeAddress + 1,
-                                    currentInfo.mChildrenAddress, currentInfo.mShortcutTargets,
-                                    currentInfo.mBigrams);
-                            written += computeGroupSize(suffixInfo, header.mFormatOptions) + 1;
-
-                            final int[] newCharacters = Arrays.copyOfRange(codePoints, wordPos + p,
-                                    codePoints.length);
-                            final int flags = BinaryDictInputOutput.makeCharGroupFlags(
-                                    newCharacters.length > 1, isTerminal,
-                                    0 /* childrenAddressSize */, hasShortcuts, hasBigrams,
-                                    isNotAWord, isBlackListEntry, header.mFormatOptions);
-                            final CharGroupInfo newInfo = new CharGroupInfo(
-                                    newNodeAddress + written, -1 /* endAddress */, flags,
-                                    newCharacters, frequency, newNodeAddress + 1,
-                                    FormatSpec.NO_CHILDREN_ADDRESS, shortcuts, bigrams);
-                            writeNode(destination, new CharGroupInfo[] { suffixInfo, newInfo });
-                            return;
-                        }
-                        matched = false;
-                        break;
-                    }
-                }
-
-                if (matched) {
-                    if (wordPos + currentInfo.mCharacters.length == wordLen) {
-                        // the word exists in the dictionary.
-                        // only update group.
-                        final int newNodeAddress = buffer.limit();
-                        final boolean hasMultipleChars = currentInfo.mCharacters.length > 1;
-                        final int flags = BinaryDictInputOutput.makeCharGroupFlags(hasMultipleChars,
-                                isTerminal, 0 /* childrenAddressSize */, hasShortcuts, hasBigrams,
-                                isNotAWord, isBlackListEntry, header.mFormatOptions);
-                        final CharGroupInfo newInfo = new CharGroupInfo(newNodeAddress + 1,
-                                -1 /* endAddress */, flags, currentInfo.mCharacters, frequency,
-                                nodeParentAddress, currentInfo.mChildrenAddress, shortcuts,
-                                bigrams);
-                        moveCharGroup(destination, buffer, newInfo, nodeOriginAddress, address,
-                                header.mFormatOptions);
-                        return;
-                    }
-                    wordPos += currentInfo.mCharacters.length;
-                    if (currentInfo.mChildrenAddress == FormatSpec.NO_CHILDREN_ADDRESS) {
-                        /*
-                         * found the prefix of the word.
-                         * make new node and link to the node from this group.
-                         *
-                         * before
-                         * ab - cd
-                         *
-                         * insert "abcde"
-                         *
-                         * after
-                         * ab - cd - e
-                         */
-                        final int newNodeAddress = buffer.limit();
-                        updateChildrenAddress(buffer, address, newNodeAddress,
-                                header.mFormatOptions);
-                        final int newGroupAddress = newNodeAddress + 1;
-                        final boolean hasMultipleChars = (wordLen - wordPos) > 1;
-                        final int flags = BinaryDictInputOutput.makeCharGroupFlags(hasMultipleChars,
-                                isTerminal, 0 /* childrenAddressSize */, hasShortcuts, hasBigrams,
-                                isNotAWord, isBlackListEntry, header.mFormatOptions);
-                        final int[] characters = Arrays.copyOfRange(codePoints, wordPos, wordLen);
-                        final CharGroupInfo newInfo = new CharGroupInfo(newGroupAddress, -1, flags,
-                                characters, frequency, address, FormatSpec.NO_CHILDREN_ADDRESS,
-                                shortcuts, bigrams);
-                        writeNode(destination, new CharGroupInfo[] { newInfo });
-                        return;
-                    }
-                    buffer.position(currentInfo.mChildrenAddress);
-                    foundNextGroup = true;
-                    break;
-                }
-            }
-
-            if (foundNextGroup) continue;
-
-            // reached the end of the array.
-            final int linkAddressPosition = buffer.position();
-            int nextLink = buffer.readUnsignedInt24();
-            if ((nextLink & MSB24) != 0) {
-                nextLink = -(nextLink & SINT24_MAX);
-            }
-            if (nextLink == FormatSpec.NO_FORWARD_LINK_ADDRESS) {
-                /*
-                 * expand this node.
-                 *
-                 * before
-                 * ab - cd
-                 *
-                 * insert "abef"
-                 *
-                 * after
-                 * ab - cd
-                 *    |
-                 *    - ef
-                 */
-
-                // change the forward link address.
-                final int newNodeAddress = buffer.limit();
-                buffer.position(linkAddressPosition);
-                writeSInt24ToBuffer(buffer, newNodeAddress);
-
-                final int[] characters = Arrays.copyOfRange(codePoints, wordPos, wordLen);
-                final int flags = BinaryDictInputOutput.makeCharGroupFlags(characters.length > 1,
-                        isTerminal, 0 /* childrenAddressSize */, hasShortcuts, hasBigrams,
-                        isNotAWord, isBlackListEntry, header.mFormatOptions);
-                final CharGroupInfo newInfo = new CharGroupInfo(newNodeAddress + 1,
-                        -1 /* endAddress */, flags, characters, frequency, nodeParentAddress,
-                        FormatSpec.NO_CHILDREN_ADDRESS, shortcuts, bigrams);
-                writeNode(destination, new CharGroupInfo[]{ newInfo });
-                return;
-            } else {
-                depth--;
-                buffer.position(nextLink);
-            }
-        }
-    }
-
-    /**
-     * Find a word from the buffer.
-     *
-     * @param buffer the buffer representing the body of the dictionary file.
-     * @param word the word searched
-     * @return the found group
-     * @throws IOException
-     * @throws UnsupportedFormatException
-     */
-    @UsedForTesting
-    public static CharGroupInfo findWordFromBuffer(final FusionDictionaryBufferInterface buffer,
-            final String word) throws IOException, UnsupportedFormatException {
-        int position = getTerminalPosition(buffer, word);
-        if (position != FormatSpec.NOT_VALID_WORD) {
-            buffer.position(0);
-            final FileHeader header = BinaryDictInputOutput.readHeader(buffer);
-            buffer.position(position);
-            return BinaryDictInputOutput.readCharGroup(buffer, position, header.mFormatOptions);
-        }
-        return null;
-    }
-
     private static final int HEADER_READING_BUFFER_SIZE = 16384;
     /**
      * Convenience method to read the header of a binary file.
@@ -992,20 +515,27 @@
      * @param offset The offset in the file where to start reading the data.
      * @param length The length of the data file.
      */
-    public static FileHeader getDictionaryFileHeader(
+    private static FileHeader getDictionaryFileHeader(
             final File file, final long offset, final long length)
             throws FileNotFoundException, IOException, UnsupportedFormatException {
         final byte[] buffer = new byte[HEADER_READING_BUFFER_SIZE];
-        final FileInputStream inStream = new FileInputStream(file);
-        try {
-            inStream.read(buffer);
-            final BinaryDictInputOutput.ByteBufferWrapper wrapper =
-                    new BinaryDictInputOutput.ByteBufferWrapper(inStream.getChannel().map(
-                            FileChannel.MapMode.READ_ONLY, offset, length));
-            return BinaryDictInputOutput.readHeader(wrapper);
-        } finally {
-            inStream.close();
-        }
+        final DictDecoder dictDecoder = FormatSpec.getDictDecoder(file,
+                new DictDecoder.DictionaryBufferFactory() {
+                    @Override
+                    public DictBuffer getDictionaryBuffer(File file)
+                            throws FileNotFoundException, IOException {
+                        final FileInputStream inStream = new FileInputStream(file);
+                        try {
+                            inStream.skip(offset);
+                            inStream.read(buffer);
+                            return new ByteArrayDictBuffer(buffer);
+                        } finally {
+                            inStream.close();
+                        }
+                    }
+                }
+        );
+        return dictDecoder.readHeader();
     }
 
     public static FileHeader getDictionaryFileHeaderOrNull(final File file, final long offset,
@@ -1019,4 +549,83 @@
             return null;
         }
     }
+
+    /**
+     * Helper method to hide the actual value of the no children address.
+     */
+    public static boolean hasChildrenAddress(final int address) {
+        return FormatSpec.NO_CHILDREN_ADDRESS != address;
+    }
+
+    /**
+     * Helper method to check whether the node is moved.
+     */
+    public static boolean isMovedPtNode(final int flags, final FormatOptions options) {
+        return options.mSupportsDynamicUpdate
+                && ((flags & FormatSpec.MASK_CHILDREN_ADDRESS_TYPE) == FormatSpec.FLAG_IS_MOVED);
+    }
+
+    /**
+     * Helper method to check whether the dictionary can be updated dynamically.
+     */
+    public static boolean supportsDynamicUpdate(final FormatOptions options) {
+        return options.mVersion >= FormatSpec.FIRST_VERSION_WITH_DYNAMIC_UPDATE
+                && options.mSupportsDynamicUpdate;
+    }
+
+    /**
+     * Helper method to check whether the node is deleted.
+     */
+    public static boolean isDeletedPtNode(final int flags, final FormatOptions formatOptions) {
+        return formatOptions.mSupportsDynamicUpdate
+                && ((flags & FormatSpec.MASK_CHILDREN_ADDRESS_TYPE) == FormatSpec.FLAG_IS_DELETED);
+    }
+
+    /**
+     * Compute the binary size of the node count
+     * @param count the node count
+     * @return the size of the node count, either 1 or 2 bytes.
+     */
+    public static int getPtNodeCountSize(final int count) {
+        if (FormatSpec.MAX_PTNODES_FOR_ONE_BYTE_PTNODE_COUNT >= count) {
+            return 1;
+        } else if (FormatSpec.MAX_PTNODES_IN_A_PT_NODE_ARRAY >= count) {
+            return 2;
+        } else {
+            throw new RuntimeException("Can't have more than "
+                    + FormatSpec.MAX_PTNODES_IN_A_PT_NODE_ARRAY + " PtNode in a PtNodeArray (found "
+                    + count + ")");
+        }
+    }
+
+    static int getChildrenAddressSize(final int optionFlags,
+            final FormatOptions formatOptions) {
+        if (formatOptions.mSupportsDynamicUpdate) return FormatSpec.SIGNED_CHILDREN_ADDRESS_SIZE;
+        switch (optionFlags & FormatSpec.MASK_CHILDREN_ADDRESS_TYPE) {
+            case FormatSpec.FLAG_CHILDREN_ADDRESS_TYPE_ONEBYTE:
+                return 1;
+            case FormatSpec.FLAG_CHILDREN_ADDRESS_TYPE_TWOBYTES:
+                return 2;
+            case FormatSpec.FLAG_CHILDREN_ADDRESS_TYPE_THREEBYTES:
+                return 3;
+            case FormatSpec.FLAG_CHILDREN_ADDRESS_TYPE_NOADDRESS:
+            default:
+                return 0;
+        }
+    }
+
+    /**
+     * Calculate bigram frequency from compressed value
+     *
+     * @param unigramFrequency
+     * @param bigramFrequency compressed frequency
+     * @return approximate bigram frequency
+     */
+    public static int reconstructBigramFrequency(final int unigramFrequency,
+            final int bigramFrequency) {
+        final float stepSize = (FormatSpec.MAX_TERMINAL_FREQUENCY - unigramFrequency)
+                / (1.5f + FormatSpec.MAX_BIGRAM_FREQUENCY);
+        final float resultFreqFloat = unigramFrequency + stepSize * (bigramFrequency + 1.0f);
+        return (int)resultFreqFloat;
+    }
 }
diff --git a/java/src/com/android/inputmethod/latin/makedict/BinaryDictInputOutput.java b/java/src/com/android/inputmethod/latin/makedict/BinaryDictInputOutput.java
deleted file mode 100644
index 1b187d8..0000000
--- a/java/src/com/android/inputmethod/latin/makedict/BinaryDictInputOutput.java
+++ /dev/null
@@ -1,1838 +0,0 @@
-/*
- * Copyright (C) 2011 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.inputmethod.latin.makedict;
-
-import com.android.inputmethod.annotations.UsedForTesting;
-import com.android.inputmethod.latin.makedict.FormatSpec.FileHeader;
-import com.android.inputmethod.latin.makedict.FormatSpec.FormatOptions;
-import com.android.inputmethod.latin.makedict.FusionDictionary.CharGroup;
-import com.android.inputmethod.latin.makedict.FusionDictionary.DictionaryOptions;
-import com.android.inputmethod.latin.makedict.FusionDictionary.Node;
-import com.android.inputmethod.latin.makedict.FusionDictionary.WeightedString;
-
-import java.io.ByteArrayOutputStream;
-import java.io.File;
-import java.io.FileInputStream;
-import java.io.FileNotFoundException;
-import java.io.IOException;
-import java.io.OutputStream;
-import java.nio.ByteBuffer;
-import java.nio.channels.FileChannel;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.HashMap;
-import java.util.Iterator;
-import java.util.Map;
-import java.util.TreeMap;
-
-/**
- * Reads and writes XML files for a FusionDictionary.
- *
- * All the methods in this class are static.
- */
-public final class BinaryDictInputOutput {
-
-    private static final boolean DBG = MakedictLog.DBG;
-
-    // Arbitrary limit to how much passes we consider address size compression should
-    // terminate in. At the time of this writing, our largest dictionary completes
-    // compression in five passes.
-    // If the number of passes exceeds this number, makedict bails with an exception on
-    // suspicion that a bug might be causing an infinite loop.
-    private static final int MAX_PASSES = 24;
-    private static final int MAX_JUMPS = 12;
-
-    @UsedForTesting
-    public interface FusionDictionaryBufferInterface {
-        public int readUnsignedByte();
-        public int readUnsignedShort();
-        public int readUnsignedInt24();
-        public int readInt();
-        public int position();
-        public void position(int newPosition);
-        public void put(final byte b);
-        public int limit();
-        public int capacity();
-    }
-
-    public static final class ByteBufferWrapper implements FusionDictionaryBufferInterface {
-        private ByteBuffer mBuffer;
-
-        public ByteBufferWrapper(final ByteBuffer buffer) {
-            mBuffer = buffer;
-        }
-
-        @Override
-        public int readUnsignedByte() {
-            return mBuffer.get() & 0xFF;
-        }
-
-        @Override
-        public int readUnsignedShort() {
-            return mBuffer.getShort() & 0xFFFF;
-        }
-
-        @Override
-        public int readUnsignedInt24() {
-            final int retval = readUnsignedByte();
-            return (retval << 16) + readUnsignedShort();
-        }
-
-        @Override
-        public int readInt() {
-            return mBuffer.getInt();
-        }
-
-        @Override
-        public int position() {
-            return mBuffer.position();
-        }
-
-        @Override
-        public void position(int newPos) {
-            mBuffer.position(newPos);
-        }
-
-        @Override
-        public void put(final byte b) {
-            mBuffer.put(b);
-        }
-
-        @Override
-        public int limit() {
-            return mBuffer.limit();
-        }
-
-        @Override
-        public int capacity() {
-            return mBuffer.capacity();
-        }
-    }
-
-    /**
-     * A class grouping utility function for our specific character encoding.
-     */
-    static final class CharEncoding {
-        private static final int MINIMAL_ONE_BYTE_CHARACTER_VALUE = 0x20;
-        private static final int MAXIMAL_ONE_BYTE_CHARACTER_VALUE = 0xFF;
-
-        /**
-         * Helper method to find out whether this code fits on one byte
-         */
-        private static boolean fitsOnOneByte(final int character) {
-            return character >= MINIMAL_ONE_BYTE_CHARACTER_VALUE
-                    && character <= MAXIMAL_ONE_BYTE_CHARACTER_VALUE;
-        }
-
-        /**
-         * Compute the size of a character given its character code.
-         *
-         * Char format is:
-         * 1 byte = bbbbbbbb match
-         * case 000xxxxx: xxxxx << 16 + next byte << 8 + next byte
-         * else: if 00011111 (= 0x1F) : this is the terminator. This is a relevant choice because
-         *       unicode code points range from 0 to 0x10FFFF, so any 3-byte value starting with
-         *       00011111 would be outside unicode.
-         * else: iso-latin-1 code
-         * This allows for the whole unicode range to be encoded, including chars outside of
-         * the BMP. Also everything in the iso-latin-1 charset is only 1 byte, except control
-         * characters which should never happen anyway (and still work, but take 3 bytes).
-         *
-         * @param character the character code.
-         * @return the size in binary encoded-form, either 1 or 3 bytes.
-         */
-        static int getCharSize(final int character) {
-            // See char encoding in FusionDictionary.java
-            if (fitsOnOneByte(character)) return 1;
-            if (FormatSpec.INVALID_CHARACTER == character) return 1;
-            return 3;
-        }
-
-        /**
-         * Compute the byte size of a character array.
-         */
-        private static int getCharArraySize(final int[] chars) {
-            int size = 0;
-            for (int character : chars) size += getCharSize(character);
-            return size;
-        }
-
-        /**
-         * Writes a char array to a byte buffer.
-         *
-         * @param codePoints the code point array to write.
-         * @param buffer the byte buffer to write to.
-         * @param index the index in buffer to write the character array to.
-         * @return the index after the last character.
-         */
-        private static int writeCharArray(final int[] codePoints, final byte[] buffer, int index) {
-            for (int codePoint : codePoints) {
-                if (1 == getCharSize(codePoint)) {
-                    buffer[index++] = (byte)codePoint;
-                } else {
-                    buffer[index++] = (byte)(0xFF & (codePoint >> 16));
-                    buffer[index++] = (byte)(0xFF & (codePoint >> 8));
-                    buffer[index++] = (byte)(0xFF & codePoint);
-                }
-            }
-            return index;
-        }
-
-        /**
-         * Writes a string with our character format to a byte buffer.
-         *
-         * This will also write the terminator byte.
-         *
-         * @param buffer the byte buffer to write to.
-         * @param origin the offset to write from.
-         * @param word the string to write.
-         * @return the size written, in bytes.
-         */
-        private static int writeString(final byte[] buffer, final int origin,
-                final String word) {
-            final int length = word.length();
-            int index = origin;
-            for (int i = 0; i < length; i = word.offsetByCodePoints(i, 1)) {
-                final int codePoint = word.codePointAt(i);
-                if (1 == getCharSize(codePoint)) {
-                    buffer[index++] = (byte)codePoint;
-                } else {
-                    buffer[index++] = (byte)(0xFF & (codePoint >> 16));
-                    buffer[index++] = (byte)(0xFF & (codePoint >> 8));
-                    buffer[index++] = (byte)(0xFF & codePoint);
-                }
-            }
-            buffer[index++] = FormatSpec.GROUP_CHARACTERS_TERMINATOR;
-            return index - origin;
-        }
-
-        /**
-         * Writes a string with our character format to a ByteArrayOutputStream.
-         *
-         * This will also write the terminator byte.
-         *
-         * @param buffer the ByteArrayOutputStream to write to.
-         * @param word the string to write.
-         */
-        private static void writeString(final ByteArrayOutputStream buffer, final String word) {
-            final int length = word.length();
-            for (int i = 0; i < length; i = word.offsetByCodePoints(i, 1)) {
-                final int codePoint = word.codePointAt(i);
-                if (1 == getCharSize(codePoint)) {
-                    buffer.write((byte) codePoint);
-                } else {
-                    buffer.write((byte) (0xFF & (codePoint >> 16)));
-                    buffer.write((byte) (0xFF & (codePoint >> 8)));
-                    buffer.write((byte) (0xFF & codePoint));
-                }
-            }
-            buffer.write(FormatSpec.GROUP_CHARACTERS_TERMINATOR);
-        }
-
-        /**
-         * Reads a string from a buffer. This is the converse of the above method.
-         */
-        private static String readString(final FusionDictionaryBufferInterface buffer) {
-            final StringBuilder s = new StringBuilder();
-            int character = readChar(buffer);
-            while (character != FormatSpec.INVALID_CHARACTER) {
-                s.appendCodePoint(character);
-                character = readChar(buffer);
-            }
-            return s.toString();
-        }
-
-        /**
-         * Reads a character from the buffer.
-         *
-         * This follows the character format documented earlier in this source file.
-         *
-         * @param buffer the buffer, positioned over an encoded character.
-         * @return the character code.
-         */
-        static int readChar(final FusionDictionaryBufferInterface buffer) {
-            int character = buffer.readUnsignedByte();
-            if (!fitsOnOneByte(character)) {
-                if (FormatSpec.GROUP_CHARACTERS_TERMINATOR == character) {
-                    return FormatSpec.INVALID_CHARACTER;
-                }
-                character <<= 16;
-                character += buffer.readUnsignedShort();
-            }
-            return character;
-        }
-    }
-
-    /**
-     * Compute the binary size of the character array.
-     *
-     * If only one character, this is the size of this character. If many, it's the sum of their
-     * sizes + 1 byte for the terminator.
-     *
-     * @param characters the character array
-     * @return the size of the char array, including the terminator if any
-     */
-    static int getGroupCharactersSize(final int[] characters) {
-        int size = CharEncoding.getCharArraySize(characters);
-        if (characters.length > 1) size += FormatSpec.GROUP_TERMINATOR_SIZE;
-        return size;
-    }
-
-    /**
-     * Compute the binary size of the character array in a group
-     *
-     * If only one character, this is the size of this character. If many, it's the sum of their
-     * sizes + 1 byte for the terminator.
-     *
-     * @param group the group
-     * @return the size of the char array, including the terminator if any
-     */
-    private static int getGroupCharactersSize(final CharGroup group) {
-        return getGroupCharactersSize(group.mChars);
-    }
-
-    /**
-     * Compute the binary size of the group count
-     * @param count the group count
-     * @return the size of the group count, either 1 or 2 bytes.
-     */
-    public static int getGroupCountSize(final int count) {
-        if (FormatSpec.MAX_CHARGROUPS_FOR_ONE_BYTE_CHARGROUP_COUNT >= count) {
-            return 1;
-        } else if (FormatSpec.MAX_CHARGROUPS_IN_A_NODE >= count) {
-            return 2;
-        } else {
-            throw new RuntimeException("Can't have more than "
-                    + FormatSpec.MAX_CHARGROUPS_IN_A_NODE + " groups in a node (found " + count
-                    + ")");
-        }
-    }
-
-    /**
-     * Compute the binary size of the group count for a node
-     * @param node the node
-     * @return the size of the group count, either 1 or 2 bytes.
-     */
-    private static int getGroupCountSize(final Node node) {
-        return getGroupCountSize(node.mData.size());
-    }
-
-    /**
-     * Compute the size of a shortcut in bytes.
-     */
-    private static int getShortcutSize(final WeightedString shortcut) {
-        int size = FormatSpec.GROUP_ATTRIBUTE_FLAGS_SIZE;
-        final String word = shortcut.mWord;
-        final int length = word.length();
-        for (int i = 0; i < length; i = word.offsetByCodePoints(i, 1)) {
-            final int codePoint = word.codePointAt(i);
-            size += CharEncoding.getCharSize(codePoint);
-        }
-        size += FormatSpec.GROUP_TERMINATOR_SIZE;
-        return size;
-    }
-
-    /**
-     * Compute the size of a shortcut list in bytes.
-     *
-     * This is known in advance and does not change according to position in the file
-     * like address lists do.
-     */
-    static int getShortcutListSize(final ArrayList<WeightedString> shortcutList) {
-        if (null == shortcutList) return 0;
-        int size = FormatSpec.GROUP_SHORTCUT_LIST_SIZE_SIZE;
-        for (final WeightedString shortcut : shortcutList) {
-            size += getShortcutSize(shortcut);
-        }
-        return size;
-    }
-
-    /**
-     * Compute the maximum size of a CharGroup, assuming 3-byte addresses for everything.
-     *
-     * @param group the CharGroup to compute the size of.
-     * @param options file format options.
-     * @return the maximum size of the group.
-     */
-    private static int getCharGroupMaximumSize(final CharGroup group, final FormatOptions options) {
-        int size = getGroupHeaderSize(group, options);
-        // If terminal, one byte for the frequency
-        if (group.isTerminal()) size += FormatSpec.GROUP_FREQUENCY_SIZE;
-        size += FormatSpec.GROUP_MAX_ADDRESS_SIZE; // For children address
-        size += getShortcutListSize(group.mShortcutTargets);
-        if (null != group.mBigrams) {
-            size += (FormatSpec.GROUP_ATTRIBUTE_FLAGS_SIZE
-                    + FormatSpec.GROUP_ATTRIBUTE_MAX_ADDRESS_SIZE)
-                    * group.mBigrams.size();
-        }
-        return size;
-    }
-
-    /**
-     * Compute the maximum size of a node, assuming 3-byte addresses for everything, and caches
-     * it in the 'actualSize' member of the node.
-     *
-     * @param node the node to compute the maximum size of.
-     * @param options file format options.
-     */
-    private static void calculateNodeMaximumSize(final Node node, final FormatOptions options) {
-        int size = getGroupCountSize(node);
-        for (CharGroup g : node.mData) {
-            final int groupSize = getCharGroupMaximumSize(g, options);
-            g.mCachedSize = groupSize;
-            size += groupSize;
-        }
-        if (options.mSupportsDynamicUpdate) {
-            size += FormatSpec.FORWARD_LINK_ADDRESS_SIZE;
-        }
-        node.mCachedSize = size;
-    }
-
-    /**
-     * Helper method to hide the actual value of the no children address.
-     */
-    public static boolean hasChildrenAddress(final int address) {
-        return FormatSpec.NO_CHILDREN_ADDRESS != address;
-    }
-
-    /**
-     * Helper method to check whether the group is moved.
-     */
-    public static boolean isMovedGroup(final int flags, final FormatOptions options) {
-        return options.mSupportsDynamicUpdate
-                && ((flags & FormatSpec.MASK_GROUP_ADDRESS_TYPE) == FormatSpec.FLAG_IS_MOVED);
-    }
-
-    /**
-     * Helper method to check whether the group is deleted.
-     */
-    public static boolean isDeletedGroup(final int flags, final FormatOptions formatOptions) {
-        return formatOptions.mSupportsDynamicUpdate
-                && ((flags & FormatSpec.MASK_GROUP_ADDRESS_TYPE) == FormatSpec.FLAG_IS_DELETED);
-    }
-
-    /**
-     * Helper method to check whether the dictionary can be updated dynamically.
-     */
-    public static boolean supportsDynamicUpdate(final FormatOptions options) {
-        return options.mVersion >= FormatSpec.FIRST_VERSION_WITH_DYNAMIC_UPDATE
-                && options.mSupportsDynamicUpdate;
-    }
-
-    /**
-     * Compute the size of the header (flag + [parent address] + characters size) of a CharGroup.
-     *
-     * @param group the group of which to compute the size of the header
-     * @param options file format options.
-     */
-    private static int getGroupHeaderSize(final CharGroup group, final FormatOptions options) {
-        if (supportsDynamicUpdate(options)) {
-            return FormatSpec.GROUP_FLAGS_SIZE + FormatSpec.PARENT_ADDRESS_SIZE
-                    + getGroupCharactersSize(group);
-        } else {
-            return FormatSpec.GROUP_FLAGS_SIZE + getGroupCharactersSize(group);
-        }
-    }
-
-    private static final int UINT8_MAX = 0xFF;
-    private static final int UINT16_MAX = 0xFFFF;
-    private static final int UINT24_MAX = 0xFFFFFF;
-
-    /**
-     * Compute the size, in bytes, that an address will occupy.
-     *
-     * This can be used either for children addresses (which are always positive) or for
-     * attribute, which may be positive or negative but
-     * store their sign bit separately.
-     *
-     * @param address the address
-     * @return the byte size.
-     */
-    static int getByteSize(final int address) {
-        assert(address <= UINT24_MAX);
-        if (!hasChildrenAddress(address)) {
-            return 0;
-        } else if (Math.abs(address) <= UINT8_MAX) {
-            return 1;
-        } else if (Math.abs(address) <= UINT16_MAX) {
-            return 2;
-        } else {
-            return 3;
-        }
-    }
-
-    private static final int SINT24_MAX = 0x7FFFFF;
-    private static final int MSB8 = 0x80;
-    private static final int MSB24 = 0x800000;
-
-    // End utility methods.
-
-    // This method is responsible for finding a nice ordering of the nodes that favors run-time
-    // cache performance and dictionary size.
-    /* package for tests */ static ArrayList<Node> flattenTree(final Node root) {
-        final int treeSize = FusionDictionary.countCharGroups(root);
-        MakedictLog.i("Counted nodes : " + treeSize);
-        final ArrayList<Node> flatTree = new ArrayList<Node>(treeSize);
-        return flattenTreeInner(flatTree, root);
-    }
-
-    private static ArrayList<Node> flattenTreeInner(final ArrayList<Node> list, final Node node) {
-        // Removing the node is necessary if the tails are merged, because we would then
-        // add the same node several times when we only want it once. A number of places in
-        // the code also depends on any node being only once in the list.
-        // Merging tails can only be done if there are no attributes. Searching for attributes
-        // in LatinIME code depends on a total breadth-first ordering, which merging tails
-        // breaks. If there are no attributes, it should be fine (and reduce the file size)
-        // to merge tails, and removing the node from the list would be necessary. However,
-        // we don't merge tails because breaking the breadth-first ordering would result in
-        // extreme overhead at bigram lookup time (it would make the search function O(n) instead
-        // of the current O(log(n)), where n=number of nodes in the dictionary which is pretty
-        // high).
-        // If no nodes are ever merged, we can't have the same node twice in the list, hence
-        // searching for duplicates in unnecessary. It is also very performance consuming,
-        // since `list' is an ArrayList so it's an O(n) operation that runs on all nodes, making
-        // this simple list.remove operation O(n*n) overall. On Android this overhead is very
-        // high.
-        // For future reference, the code to remove duplicate is a simple : list.remove(node);
-        list.add(node);
-        final ArrayList<CharGroup> branches = node.mData;
-        final int nodeSize = branches.size();
-        for (CharGroup group : branches) {
-            if (null != group.mChildren) flattenTreeInner(list, group.mChildren);
-        }
-        return list;
-    }
-
-    /**
-     * Get the offset from a position inside a current node to a target node, during update.
-     *
-     * If the current node is before the target node, the target node has not been updated yet,
-     * so we should return the offset from the old position of the current node to the old position
-     * of the target node. If on the other hand the target is before the current node, it already
-     * has been updated, so we should return the offset from the new position in the current node
-     * to the new position in the target node.
-     * @param currentNode the node containing the CharGroup where the offset will be written
-     * @param offsetFromStartOfCurrentNode the offset, in bytes, from the start of currentNode
-     * @param targetNode the target node to get the offset to
-     * @return the offset to the target node
-     */
-    private static int getOffsetToTargetNodeDuringUpdate(final Node currentNode,
-            final int offsetFromStartOfCurrentNode, final Node targetNode) {
-        final boolean isTargetBeforeCurrent = (targetNode.mCachedAddressBeforeUpdate
-                < currentNode.mCachedAddressBeforeUpdate);
-        if (isTargetBeforeCurrent) {
-            return targetNode.mCachedAddressAfterUpdate
-                    - (currentNode.mCachedAddressAfterUpdate + offsetFromStartOfCurrentNode);
-        } else {
-            return targetNode.mCachedAddressBeforeUpdate
-                    - (currentNode.mCachedAddressBeforeUpdate + offsetFromStartOfCurrentNode);
-        }
-    }
-
-    /**
-     * Get the offset from a position inside a current node to a target CharGroup, during update.
-     * @param currentNode the node containing the CharGroup where the offset will be written
-     * @param offsetFromStartOfCurrentNode the offset, in bytes, from the start of currentNode
-     * @param targetCharGroup the target CharGroup to get the offset to
-     * @return the offset to the target CharGroup
-     */
-    // TODO: is there any way to factorize this method with the one above?
-    private static int getOffsetToTargetCharGroupDuringUpdate(final Node currentNode,
-            final int offsetFromStartOfCurrentNode, final CharGroup targetCharGroup) {
-        final int oldOffsetBasePoint = currentNode.mCachedAddressBeforeUpdate
-                + offsetFromStartOfCurrentNode;
-        final boolean isTargetBeforeCurrent = (targetCharGroup.mCachedAddressBeforeUpdate
-                < oldOffsetBasePoint);
-        // If the target is before the current node, then its address has already been updated.
-        // We can use the AfterUpdate member, and compare it to our own member after update.
-        // Otherwise, the AfterUpdate member is not updated yet, so we need to use the BeforeUpdate
-        // member, and of course we have to compare this to our own address before update.
-        if (isTargetBeforeCurrent) {
-            final int newOffsetBasePoint = currentNode.mCachedAddressAfterUpdate
-                    + offsetFromStartOfCurrentNode;
-            return targetCharGroup.mCachedAddressAfterUpdate - newOffsetBasePoint;
-        } else {
-            return targetCharGroup.mCachedAddressBeforeUpdate - oldOffsetBasePoint;
-        }
-    }
-
-    /**
-     * Computes the actual node size, based on the cached addresses of the children nodes.
-     *
-     * Each node stores its tentative address. During dictionary address computing, these
-     * are not final, but they can be used to compute the node size (the node size depends
-     * on the address of the children because the number of bytes necessary to store an
-     * address depends on its numeric value. The return value indicates whether the node
-     * contents (as in, any of the addresses stored in the cache fields) have changed with
-     * respect to their previous value.
-     *
-     * @param node the node to compute the size of.
-     * @param dict the dictionary in which the word/attributes are to be found.
-     * @param formatOptions file format options.
-     * @return false if none of the cached addresses inside the node changed, true otherwise.
-     */
-    private static boolean computeActualNodeSize(final Node node, final FusionDictionary dict,
-            final FormatOptions formatOptions) {
-        boolean changed = false;
-        int size = getGroupCountSize(node);
-        for (CharGroup group : node.mData) {
-            group.mCachedAddressAfterUpdate = node.mCachedAddressAfterUpdate + size;
-            if (group.mCachedAddressAfterUpdate != group.mCachedAddressBeforeUpdate) {
-                changed = true;
-            }
-            int groupSize = getGroupHeaderSize(group, formatOptions);
-            if (group.isTerminal()) groupSize += FormatSpec.GROUP_FREQUENCY_SIZE;
-            if (null == group.mChildren && formatOptions.mSupportsDynamicUpdate) {
-                groupSize += FormatSpec.SIGNED_CHILDREN_ADDRESS_SIZE;
-            } else if (null != group.mChildren) {
-                if (formatOptions.mSupportsDynamicUpdate) {
-                    groupSize += FormatSpec.SIGNED_CHILDREN_ADDRESS_SIZE;
-                } else {
-                    groupSize += getByteSize(getOffsetToTargetNodeDuringUpdate(node,
-                            groupSize + size, group.mChildren));
-                }
-            }
-            groupSize += getShortcutListSize(group.mShortcutTargets);
-            if (null != group.mBigrams) {
-                for (WeightedString bigram : group.mBigrams) {
-                    final int offset = getOffsetToTargetCharGroupDuringUpdate(node,
-                            groupSize + size + FormatSpec.GROUP_FLAGS_SIZE,
-                            FusionDictionary.findWordInTree(dict.mRoot, bigram.mWord));
-                    groupSize += getByteSize(offset) + FormatSpec.GROUP_FLAGS_SIZE;
-                }
-            }
-            group.mCachedSize = groupSize;
-            size += groupSize;
-        }
-        if (formatOptions.mSupportsDynamicUpdate) {
-            size += FormatSpec.FORWARD_LINK_ADDRESS_SIZE;
-        }
-        if (node.mCachedSize != size) {
-            node.mCachedSize = size;
-            changed = true;
-        }
-        return changed;
-    }
-
-    /**
-     * Initializes the cached addresses of nodes from their size.
-     *
-     * @param flatNodes the array of nodes.
-     * @param formatOptions file format options.
-     * @return the byte size of the entire stack.
-     */
-    private static int initializeNodesCachedAddresses(final ArrayList<Node> flatNodes,
-            final FormatOptions formatOptions) {
-        int nodeOffset = 0;
-        for (final Node n : flatNodes) {
-            n.mCachedAddressBeforeUpdate = nodeOffset;
-            int groupCountSize = getGroupCountSize(n);
-            int groupOffset = 0;
-            for (final CharGroup g : n.mData) {
-                g.mCachedAddressBeforeUpdate = g.mCachedAddressAfterUpdate =
-                        groupCountSize + nodeOffset + groupOffset;
-                groupOffset += g.mCachedSize;
-            }
-            final int nodeSize = groupCountSize + groupOffset
-                    + (formatOptions.mSupportsDynamicUpdate
-                            ? FormatSpec.FORWARD_LINK_ADDRESS_SIZE : 0);
-            nodeOffset += n.mCachedSize;
-        }
-        return nodeOffset;
-    }
-
-    /**
-     * Updates the cached addresses of nodes after recomputing their new positions.
-     *
-     * @param flatNodes the array of nodes.
-     */
-    private static void updateNodeCachedAddresses(final ArrayList<Node> flatNodes) {
-        for (final Node n : flatNodes) {
-            n.mCachedAddressBeforeUpdate = n.mCachedAddressAfterUpdate;
-            for (final CharGroup g : n.mData) {
-                g.mCachedAddressBeforeUpdate = g.mCachedAddressAfterUpdate;
-            }
-        }
-    }
-
-    /**
-     * Compute the cached parent addresses after all has been updated.
-     *
-     * The parent addresses are used by some binary formats at write-to-disk time. Not all formats
-     * need them. In particular, version 2 does not need them, and version 3 does.
-     *
-     * @param flatNodes the flat array of nodes to fill in
-     */
-    private static void computeParentAddresses(final ArrayList<Node> flatNodes) {
-        for (final Node node : flatNodes) {
-            for (final CharGroup group : node.mData) {
-                if (null != group.mChildren) {
-                    // Assign my address to children's parent address
-                    // Here BeforeUpdate and AfterUpdate addresses have the same value, so it
-                    // does not matter which we use.
-                    group.mChildren.mCachedParentAddress = group.mCachedAddressAfterUpdate
-                            - group.mChildren.mCachedAddressAfterUpdate;
-                }
-            }
-        }
-    }
-
-    /**
-     * Compute the addresses and sizes of an ordered node array.
-     *
-     * This method takes a node array and will update its cached address and size values
-     * so that they can be written into a file. It determines the smallest size each of the
-     * nodes can be given the addresses of its children and attributes, and store that into
-     * each node.
-     * The order of the node is given by the order of the array. This method makes no effort
-     * to find a good order; it only mechanically computes the size this order results in.
-     *
-     * @param dict the dictionary
-     * @param flatNodes the ordered array of nodes
-     * @param formatOptions file format options.
-     * @return the same array it was passed. The nodes have been updated for address and size.
-     */
-    private static ArrayList<Node> computeAddresses(final FusionDictionary dict,
-            final ArrayList<Node> flatNodes, final FormatOptions formatOptions) {
-        // First get the worst possible sizes and offsets
-        for (final Node n : flatNodes) calculateNodeMaximumSize(n, formatOptions);
-        final int offset = initializeNodesCachedAddresses(flatNodes, formatOptions);
-
-        MakedictLog.i("Compressing the array addresses. Original size : " + offset);
-        MakedictLog.i("(Recursively seen size : " + offset + ")");
-
-        int passes = 0;
-        boolean changesDone = false;
-        do {
-            changesDone = false;
-            int nodeStartOffset = 0;
-            for (final Node n : flatNodes) {
-                n.mCachedAddressAfterUpdate = nodeStartOffset;
-                final int oldNodeSize = n.mCachedSize;
-                final boolean changed = computeActualNodeSize(n, dict, formatOptions);
-                final int newNodeSize = n.mCachedSize;
-                if (oldNodeSize < newNodeSize) throw new RuntimeException("Increased size ?!");
-                nodeStartOffset += newNodeSize;
-                changesDone |= changed;
-            }
-            updateNodeCachedAddresses(flatNodes);
-            ++passes;
-            if (passes > MAX_PASSES) throw new RuntimeException("Too many passes - probably a bug");
-        } while (changesDone);
-
-        if (formatOptions.mSupportsDynamicUpdate) {
-            computeParentAddresses(flatNodes);
-        }
-        final Node lastNode = flatNodes.get(flatNodes.size() - 1);
-        MakedictLog.i("Compression complete in " + passes + " passes.");
-        MakedictLog.i("After address compression : "
-                + (lastNode.mCachedAddressAfterUpdate + lastNode.mCachedSize));
-
-        return flatNodes;
-    }
-
-    /**
-     * Sanity-checking method.
-     *
-     * This method checks an array of node for juxtaposition, that is, it will do
-     * nothing if each node's cached address is actually the previous node's address
-     * plus the previous node's size.
-     * If this is not the case, it will throw an exception.
-     *
-     * @param array the array node to check
-     */
-    private static void checkFlatNodeArray(final ArrayList<Node> array) {
-        int offset = 0;
-        int index = 0;
-        for (final Node n : array) {
-            // BeforeUpdate and AfterUpdate addresses are the same here, so it does not matter
-            // which we use.
-            if (n.mCachedAddressAfterUpdate != offset) {
-                throw new RuntimeException("Wrong address for node " + index
-                        + " : expected " + offset + ", got " + n.mCachedAddressAfterUpdate);
-            }
-            ++index;
-            offset += n.mCachedSize;
-        }
-    }
-
-    /**
-     * Helper method to write a variable-size address to a file.
-     *
-     * @param buffer the buffer to write to.
-     * @param index the index in the buffer to write the address to.
-     * @param address the address to write.
-     * @return the size in bytes the address actually took.
-     */
-    private static int writeVariableAddress(final byte[] buffer, int index, final int address) {
-        switch (getByteSize(address)) {
-        case 1:
-            buffer[index++] = (byte)address;
-            return 1;
-        case 2:
-            buffer[index++] = (byte)(0xFF & (address >> 8));
-            buffer[index++] = (byte)(0xFF & address);
-            return 2;
-        case 3:
-            buffer[index++] = (byte)(0xFF & (address >> 16));
-            buffer[index++] = (byte)(0xFF & (address >> 8));
-            buffer[index++] = (byte)(0xFF & address);
-            return 3;
-        case 0:
-            return 0;
-        default:
-            throw new RuntimeException("Address " + address + " has a strange size");
-        }
-    }
-
-    /**
-     * Helper method to write a variable-size signed address to a file.
-     *
-     * @param buffer the buffer to write to.
-     * @param index the index in the buffer to write the address to.
-     * @param address the address to write.
-     * @return the size in bytes the address actually took.
-     */
-    private static int writeVariableSignedAddress(final byte[] buffer, int index,
-            final int address) {
-        if (!hasChildrenAddress(address)) {
-            buffer[index] = buffer[index + 1] = buffer[index + 2] = 0;
-        } else {
-            final int absAddress = Math.abs(address);
-            buffer[index++] = (byte)((address < 0 ? MSB8 : 0) | (0xFF & (absAddress >> 16)));
-            buffer[index++] = (byte)(0xFF & (absAddress >> 8));
-            buffer[index++] = (byte)(0xFF & absAddress);
-        }
-        return 3;
-    }
-
-    /**
-     * Makes the flag value for a char group.
-     *
-     * @param hasMultipleChars whether the group has multiple chars.
-     * @param isTerminal whether the group is terminal.
-     * @param childrenAddressSize the size of a children address.
-     * @param hasShortcuts whether the group has shortcuts.
-     * @param hasBigrams whether the group has bigrams.
-     * @param isNotAWord whether the group is not a word.
-     * @param isBlackListEntry whether the group is a blacklist entry.
-     * @param formatOptions file format options.
-     * @return the flags
-     */
-    static int makeCharGroupFlags(final boolean hasMultipleChars, final boolean isTerminal,
-            final int childrenAddressSize, final boolean hasShortcuts, final boolean hasBigrams,
-            final boolean isNotAWord, final boolean isBlackListEntry,
-            final FormatOptions formatOptions) {
-        byte flags = 0;
-        if (hasMultipleChars) flags |= FormatSpec.FLAG_HAS_MULTIPLE_CHARS;
-        if (isTerminal) flags |= FormatSpec.FLAG_IS_TERMINAL;
-        if (formatOptions.mSupportsDynamicUpdate) {
-            flags |= FormatSpec.FLAG_IS_NOT_MOVED;
-        } else if (true) {
-            switch (childrenAddressSize) {
-                case 1:
-                    flags |= FormatSpec.FLAG_GROUP_ADDRESS_TYPE_ONEBYTE;
-                    break;
-                case 2:
-                    flags |= FormatSpec.FLAG_GROUP_ADDRESS_TYPE_TWOBYTES;
-                    break;
-                case 3:
-                    flags |= FormatSpec.FLAG_GROUP_ADDRESS_TYPE_THREEBYTES;
-                    break;
-                case 0:
-                    flags |= FormatSpec.FLAG_GROUP_ADDRESS_TYPE_NOADDRESS;
-                    break;
-                default:
-                    throw new RuntimeException("Node with a strange address");
-            }
-        }
-        if (hasShortcuts) flags |= FormatSpec.FLAG_HAS_SHORTCUT_TARGETS;
-        if (hasBigrams) flags |= FormatSpec.FLAG_HAS_BIGRAMS;
-        if (isNotAWord) flags |= FormatSpec.FLAG_IS_NOT_A_WORD;
-        if (isBlackListEntry) flags |= FormatSpec.FLAG_IS_BLACKLISTED;
-        return flags;
-    }
-
-    private static byte makeCharGroupFlags(final CharGroup group, final int groupAddress,
-            final int childrenOffset, final FormatOptions formatOptions) {
-        return (byte) makeCharGroupFlags(group.mChars.length > 1, group.mFrequency >= 0,
-                getByteSize(childrenOffset), group.mShortcutTargets != null, group.mBigrams != null,
-                group.mIsNotAWord, group.mIsBlacklistEntry, formatOptions);
-    }
-
-    /**
-     * Makes the flag value for a bigram.
-     *
-     * @param more whether there are more bigrams after this one.
-     * @param offset the offset of the bigram.
-     * @param bigramFrequency the frequency of the bigram, 0..255.
-     * @param unigramFrequency the unigram frequency of the same word, 0..255.
-     * @param word the second bigram, for debugging purposes
-     * @return the flags
-     */
-    private static final int makeBigramFlags(final boolean more, final int offset,
-            int bigramFrequency, final int unigramFrequency, final String word) {
-        int bigramFlags = (more ? FormatSpec.FLAG_ATTRIBUTE_HAS_NEXT : 0)
-                + (offset < 0 ? FormatSpec.FLAG_ATTRIBUTE_OFFSET_NEGATIVE : 0);
-        switch (getByteSize(offset)) {
-        case 1:
-            bigramFlags |= FormatSpec.FLAG_ATTRIBUTE_ADDRESS_TYPE_ONEBYTE;
-            break;
-        case 2:
-            bigramFlags |= FormatSpec.FLAG_ATTRIBUTE_ADDRESS_TYPE_TWOBYTES;
-            break;
-        case 3:
-            bigramFlags |= FormatSpec.FLAG_ATTRIBUTE_ADDRESS_TYPE_THREEBYTES;
-            break;
-        default:
-            throw new RuntimeException("Strange offset size");
-        }
-        if (unigramFrequency > bigramFrequency) {
-            MakedictLog.e("Unigram freq is superior to bigram freq for \"" + word
-                    + "\". Bigram freq is " + bigramFrequency + ", unigram freq for "
-                    + word + " is " + unigramFrequency);
-            bigramFrequency = unigramFrequency;
-        }
-        // We compute the difference between 255 (which means probability = 1) and the
-        // unigram score. We split this into a number of discrete steps.
-        // Now, the steps are numbered 0~15; 0 represents an increase of 1 step while 15
-        // represents an increase of 16 steps: a value of 15 will be interpreted as the median
-        // value of the 16th step. In all justice, if the bigram frequency is low enough to be
-        // rounded below the first step (which means it is less than half a step higher than the
-        // unigram frequency) then the unigram frequency itself is the best approximation of the
-        // bigram freq that we could possibly supply, hence we should *not* include this bigram
-        // in the file at all.
-        // until this is done, we'll write 0 and slightly overestimate this case.
-        // In other words, 0 means "between 0.5 step and 1.5 step", 1 means "between 1.5 step
-        // and 2.5 steps", and 15 means "between 15.5 steps and 16.5 steps". So we want to
-        // divide our range [unigramFreq..MAX_TERMINAL_FREQUENCY] in 16.5 steps to get the
-        // step size. Then we compute the start of the first step (the one where value 0 starts)
-        // by adding half-a-step to the unigramFrequency. From there, we compute the integer
-        // number of steps to the bigramFrequency. One last thing: we want our steps to include
-        // their lower bound and exclude their higher bound so we need to have the first step
-        // start at exactly 1 unit higher than floor(unigramFreq + half a step).
-        // Note : to reconstruct the score, the dictionary reader will need to divide
-        // MAX_TERMINAL_FREQUENCY - unigramFreq by 16.5 likewise to get the value of the step,
-        // and add (discretizedFrequency + 0.5 + 0.5) times this value to get the best
-        // approximation. (0.5 to get the first step start, and 0.5 to get the middle of the
-        // step pointed by the discretized frequency.
-        final float stepSize =
-                (FormatSpec.MAX_TERMINAL_FREQUENCY - unigramFrequency)
-                / (1.5f + FormatSpec.MAX_BIGRAM_FREQUENCY);
-        final float firstStepStart = 1 + unigramFrequency + (stepSize / 2.0f);
-        final int discretizedFrequency = (int)((bigramFrequency - firstStepStart) / stepSize);
-        // If the bigram freq is less than half-a-step higher than the unigram freq, we get -1
-        // here. The best approximation would be the unigram freq itself, so we should not
-        // include this bigram in the dictionary. For now, register as 0, and live with the
-        // small over-estimation that we get in this case. TODO: actually remove this bigram
-        // if discretizedFrequency < 0.
-        final int finalBigramFrequency = discretizedFrequency > 0 ? discretizedFrequency : 0;
-        bigramFlags += finalBigramFrequency & FormatSpec.FLAG_ATTRIBUTE_FREQUENCY;
-        return bigramFlags;
-    }
-
-    /**
-     * Makes the 2-byte value for options flags.
-     */
-    private static final int makeOptionsValue(final FusionDictionary dictionary,
-            final FormatOptions formatOptions) {
-        final DictionaryOptions options = dictionary.mOptions;
-        final boolean hasBigrams = dictionary.hasBigrams();
-        return (options.mFrenchLigatureProcessing ? FormatSpec.FRENCH_LIGATURE_PROCESSING_FLAG : 0)
-                + (options.mGermanUmlautProcessing ? FormatSpec.GERMAN_UMLAUT_PROCESSING_FLAG : 0)
-                + (hasBigrams ? FormatSpec.CONTAINS_BIGRAMS_FLAG : 0)
-                + (formatOptions.mSupportsDynamicUpdate ? FormatSpec.SUPPORTS_DYNAMIC_UPDATE : 0);
-    }
-
-    /**
-     * Makes the flag value for a shortcut.
-     *
-     * @param more whether there are more attributes after this one.
-     * @param frequency the frequency of the attribute, 0..15
-     * @return the flags
-     */
-    static final int makeShortcutFlags(final boolean more, final int frequency) {
-        return (more ? FormatSpec.FLAG_ATTRIBUTE_HAS_NEXT : 0)
-                + (frequency & FormatSpec.FLAG_ATTRIBUTE_FREQUENCY);
-    }
-
-    private static final int writeParentAddress(final byte[] buffer, final int index,
-            final int address, final FormatOptions formatOptions) {
-        if (supportsDynamicUpdate(formatOptions)) {
-            if (address == FormatSpec.NO_PARENT_ADDRESS) {
-                buffer[index] = buffer[index + 1] = buffer[index + 2] = 0;
-            } else {
-                final int absAddress = Math.abs(address);
-                assert(absAddress <= SINT24_MAX);
-                buffer[index] = (byte)((address < 0 ? MSB8 : 0)
-                        | ((absAddress >> 16) & 0xFF));
-                buffer[index + 1] = (byte)((absAddress >> 8) & 0xFF);
-                buffer[index + 2] = (byte)(absAddress & 0xFF);
-            }
-            return index + 3;
-        } else {
-            return index;
-        }
-    }
-
-    /**
-     * Write a node to memory. The node is expected to have its final position cached.
-     *
-     * This can be an empty map, but the more is inside the faster the lookups will be. It can
-     * be carried on as long as nodes do not move.
-     *
-     * @param dict the dictionary the node is a part of (for relative offsets).
-     * @param buffer the memory buffer to write to.
-     * @param node the node to write.
-     * @param formatOptions file format options.
-     * @return the address of the END of the node.
-     */
-    @SuppressWarnings("unused")
-    private static int writePlacedNode(final FusionDictionary dict, byte[] buffer,
-            final Node node, final FormatOptions formatOptions) {
-        // TODO: Make the code in common with BinaryDictIOUtils#writeCharGroup
-        int index = node.mCachedAddressAfterUpdate;
-
-        final int groupCount = node.mData.size();
-        final int countSize = getGroupCountSize(node);
-        final int parentAddress = node.mCachedParentAddress;
-        if (1 == countSize) {
-            buffer[index++] = (byte)groupCount;
-        } else if (2 == countSize) {
-            // We need to signal 2-byte size by setting the top bit of the MSB to 1, so
-            // we | 0x80 to do this.
-            buffer[index++] = (byte)((groupCount >> 8) | 0x80);
-            buffer[index++] = (byte)(groupCount & 0xFF);
-        } else {
-            throw new RuntimeException("Strange size from getGroupCountSize : " + countSize);
-        }
-        int groupAddress = index;
-        for (int i = 0; i < groupCount; ++i) {
-            final CharGroup group = node.mData.get(i);
-            if (index != group.mCachedAddressAfterUpdate) {
-                throw new RuntimeException("Bug: write index is not the same as the cached address "
-                        + "of the group : " + index + " <> " + group.mCachedAddressAfterUpdate);
-            }
-            groupAddress += getGroupHeaderSize(group, formatOptions);
-            // Sanity checks.
-            if (DBG && group.mFrequency > FormatSpec.MAX_TERMINAL_FREQUENCY) {
-                throw new RuntimeException("A node has a frequency > "
-                        + FormatSpec.MAX_TERMINAL_FREQUENCY
-                        + " : " + group.mFrequency);
-            }
-            if (group.mFrequency >= 0) groupAddress += FormatSpec.GROUP_FREQUENCY_SIZE;
-            final int childrenOffset = null == group.mChildren
-                    ? FormatSpec.NO_CHILDREN_ADDRESS
-                            : group.mChildren.mCachedAddressAfterUpdate - groupAddress;
-            buffer[index++] =
-                    makeCharGroupFlags(group, groupAddress, childrenOffset, formatOptions);
-
-            if (parentAddress == FormatSpec.NO_PARENT_ADDRESS) {
-                index = writeParentAddress(buffer, index, parentAddress, formatOptions);
-            } else {
-                index = writeParentAddress(buffer, index, parentAddress
-                        + (node.mCachedAddressAfterUpdate - group.mCachedAddressAfterUpdate),
-                        formatOptions);
-            }
-
-            index = CharEncoding.writeCharArray(group.mChars, buffer, index);
-            if (group.hasSeveralChars()) {
-                buffer[index++] = FormatSpec.GROUP_CHARACTERS_TERMINATOR;
-            }
-            if (group.mFrequency >= 0) {
-                buffer[index++] = (byte) group.mFrequency;
-            }
-
-            final int shift;
-            if (formatOptions.mSupportsDynamicUpdate) {
-                shift = writeVariableSignedAddress(buffer, index, childrenOffset);
-            } else {
-                shift = writeVariableAddress(buffer, index, childrenOffset);
-            }
-            index += shift;
-            groupAddress += shift;
-
-            // Write shortcuts
-            if (null != group.mShortcutTargets) {
-                final int indexOfShortcutByteSize = index;
-                index += FormatSpec.GROUP_SHORTCUT_LIST_SIZE_SIZE;
-                groupAddress += FormatSpec.GROUP_SHORTCUT_LIST_SIZE_SIZE;
-                final Iterator<WeightedString> shortcutIterator = group.mShortcutTargets.iterator();
-                while (shortcutIterator.hasNext()) {
-                    final WeightedString target = shortcutIterator.next();
-                    ++groupAddress;
-                    int shortcutFlags = makeShortcutFlags(shortcutIterator.hasNext(),
-                            target.mFrequency);
-                    buffer[index++] = (byte)shortcutFlags;
-                    final int shortcutShift = CharEncoding.writeString(buffer, index, target.mWord);
-                    index += shortcutShift;
-                    groupAddress += shortcutShift;
-                }
-                final int shortcutByteSize = index - indexOfShortcutByteSize;
-                if (shortcutByteSize > 0xFFFF) {
-                    throw new RuntimeException("Shortcut list too large");
-                }
-                buffer[indexOfShortcutByteSize] = (byte)(shortcutByteSize >> 8);
-                buffer[indexOfShortcutByteSize + 1] = (byte)(shortcutByteSize & 0xFF);
-            }
-            // Write bigrams
-            if (null != group.mBigrams) {
-                final Iterator<WeightedString> bigramIterator = group.mBigrams.iterator();
-                while (bigramIterator.hasNext()) {
-                    final WeightedString bigram = bigramIterator.next();
-                    final CharGroup target =
-                            FusionDictionary.findWordInTree(dict.mRoot, bigram.mWord);
-                    final int addressOfBigram = target.mCachedAddressAfterUpdate;
-                    final int unigramFrequencyForThisWord = target.mFrequency;
-                    ++groupAddress;
-                    final int offset = addressOfBigram - groupAddress;
-                    int bigramFlags = makeBigramFlags(bigramIterator.hasNext(), offset,
-                            bigram.mFrequency, unigramFrequencyForThisWord, bigram.mWord);
-                    buffer[index++] = (byte)bigramFlags;
-                    final int bigramShift = writeVariableAddress(buffer, index, Math.abs(offset));
-                    index += bigramShift;
-                    groupAddress += bigramShift;
-                }
-            }
-
-        }
-        if (formatOptions.mSupportsDynamicUpdate) {
-            buffer[index] = buffer[index + 1] = buffer[index + 2]
-                    = FormatSpec.NO_FORWARD_LINK_ADDRESS;
-            index += FormatSpec.FORWARD_LINK_ADDRESS_SIZE;
-        }
-        if (index != node.mCachedAddressAfterUpdate + node.mCachedSize) throw new RuntimeException(
-                "Not the same size : written "
-                + (index - node.mCachedAddressAfterUpdate) + " bytes from a node that should have "
-                + node.mCachedSize + " bytes");
-        return index;
-    }
-
-    /**
-     * Dumps a collection of useful statistics about a node array.
-     *
-     * This prints purely informative stuff, like the total estimated file size, the
-     * number of nodes, of character groups, the repartition of each address size, etc
-     *
-     * @param nodes the node array.
-     */
-    private static void showStatistics(ArrayList<Node> nodes) {
-        int firstTerminalAddress = Integer.MAX_VALUE;
-        int lastTerminalAddress = Integer.MIN_VALUE;
-        int size = 0;
-        int charGroups = 0;
-        int maxGroups = 0;
-        int maxRuns = 0;
-        for (final Node n : nodes) {
-            if (maxGroups < n.mData.size()) maxGroups = n.mData.size();
-            for (final CharGroup cg : n.mData) {
-                ++charGroups;
-                if (cg.mChars.length > maxRuns) maxRuns = cg.mChars.length;
-                if (cg.mFrequency >= 0) {
-                    if (n.mCachedAddressAfterUpdate < firstTerminalAddress)
-                        firstTerminalAddress = n.mCachedAddressAfterUpdate;
-                    if (n.mCachedAddressAfterUpdate > lastTerminalAddress)
-                        lastTerminalAddress = n.mCachedAddressAfterUpdate;
-                }
-            }
-            if (n.mCachedAddressAfterUpdate + n.mCachedSize > size) {
-                size = n.mCachedAddressAfterUpdate + n.mCachedSize;
-            }
-        }
-        final int[] groupCounts = new int[maxGroups + 1];
-        final int[] runCounts = new int[maxRuns + 1];
-        for (final Node n : nodes) {
-            ++groupCounts[n.mData.size()];
-            for (final CharGroup cg : n.mData) {
-                ++runCounts[cg.mChars.length];
-            }
-        }
-
-        MakedictLog.i("Statistics:\n"
-                + "  total file size " + size + "\n"
-                + "  " + nodes.size() + " nodes\n"
-                + "  " + charGroups + " groups (" + ((float)charGroups / nodes.size())
-                        + " groups per node)\n"
-                + "  first terminal at " + firstTerminalAddress + "\n"
-                + "  last terminal at " + lastTerminalAddress + "\n"
-                + "  Group stats : max = " + maxGroups);
-        for (int i = 0; i < groupCounts.length; ++i) {
-            MakedictLog.i("    " + i + " : " + groupCounts[i]);
-        }
-        MakedictLog.i("  Character run stats : max = " + maxRuns);
-        for (int i = 0; i < runCounts.length; ++i) {
-            MakedictLog.i("    " + i + " : " + runCounts[i]);
-        }
-    }
-
-    /**
-     * Dumps a FusionDictionary to a file.
-     *
-     * This is the public entry point to write a dictionary to a file.
-     *
-     * @param destination the stream to write the binary data to.
-     * @param dict the dictionary to write.
-     * @param formatOptions file format options.
-     */
-    public static void writeDictionaryBinary(final OutputStream destination,
-            final FusionDictionary dict, final FormatOptions formatOptions)
-            throws IOException, UnsupportedFormatException {
-
-        // Addresses are limited to 3 bytes, but since addresses can be relative to each node, the
-        // structure itself is not limited to 16MB. However, if it is over 16MB deciding the order
-        // of the nodes becomes a quite complicated problem, because though the dictionary itself
-        // does not have a size limit, each node must still be within 16MB of all its children and
-        // parents. As long as this is ensured, the dictionary file may grow to any size.
-
-        final int version = formatOptions.mVersion;
-        if (version < FormatSpec.MINIMUM_SUPPORTED_VERSION
-                || version > FormatSpec.MAXIMUM_SUPPORTED_VERSION) {
-            throw new UnsupportedFormatException("Requested file format version " + version
-                    + ", but this implementation only supports versions "
-                    + FormatSpec.MINIMUM_SUPPORTED_VERSION + " through "
-                    + FormatSpec.MAXIMUM_SUPPORTED_VERSION);
-        }
-
-        ByteArrayOutputStream headerBuffer = new ByteArrayOutputStream(256);
-
-        // The magic number in big-endian order.
-        if (version >= FormatSpec.FIRST_VERSION_WITH_HEADER_SIZE) {
-            // Magic number for version 2+.
-            headerBuffer.write((byte) (0xFF & (FormatSpec.VERSION_2_MAGIC_NUMBER >> 24)));
-            headerBuffer.write((byte) (0xFF & (FormatSpec.VERSION_2_MAGIC_NUMBER >> 16)));
-            headerBuffer.write((byte) (0xFF & (FormatSpec.VERSION_2_MAGIC_NUMBER >> 8)));
-            headerBuffer.write((byte) (0xFF & FormatSpec.VERSION_2_MAGIC_NUMBER));
-            // Dictionary version.
-            headerBuffer.write((byte) (0xFF & (version >> 8)));
-            headerBuffer.write((byte) (0xFF & version));
-        } else {
-            // Magic number for version 1.
-            headerBuffer.write((byte) (0xFF & (FormatSpec.VERSION_1_MAGIC_NUMBER >> 8)));
-            headerBuffer.write((byte) (0xFF & FormatSpec.VERSION_1_MAGIC_NUMBER));
-            // Dictionary version.
-            headerBuffer.write((byte) (0xFF & version));
-        }
-        // Options flags
-        final int options = makeOptionsValue(dict, formatOptions);
-        headerBuffer.write((byte) (0xFF & (options >> 8)));
-        headerBuffer.write((byte) (0xFF & options));
-        if (version >= FormatSpec.FIRST_VERSION_WITH_HEADER_SIZE) {
-            final int headerSizeOffset = headerBuffer.size();
-            // Placeholder to be written later with header size.
-            for (int i = 0; i < 4; ++i) {
-                headerBuffer.write(0);
-            }
-            // Write out the options.
-            for (final String key : dict.mOptions.mAttributes.keySet()) {
-                final String value = dict.mOptions.mAttributes.get(key);
-                CharEncoding.writeString(headerBuffer, key);
-                CharEncoding.writeString(headerBuffer, value);
-            }
-            final int size = headerBuffer.size();
-            final byte[] bytes = headerBuffer.toByteArray();
-            // Write out the header size.
-            bytes[headerSizeOffset] = (byte) (0xFF & (size >> 24));
-            bytes[headerSizeOffset + 1] = (byte) (0xFF & (size >> 16));
-            bytes[headerSizeOffset + 2] = (byte) (0xFF & (size >> 8));
-            bytes[headerSizeOffset + 3] = (byte) (0xFF & (size >> 0));
-            destination.write(bytes);
-        } else {
-            headerBuffer.writeTo(destination);
-        }
-
-        headerBuffer.close();
-
-        // Leave the choice of the optimal node order to the flattenTree function.
-        MakedictLog.i("Flattening the tree...");
-        ArrayList<Node> flatNodes = flattenTree(dict.mRoot);
-
-        MakedictLog.i("Computing addresses...");
-        computeAddresses(dict, flatNodes, formatOptions);
-        MakedictLog.i("Checking array...");
-        if (DBG) checkFlatNodeArray(flatNodes);
-
-        // Create a buffer that matches the final dictionary size.
-        final Node lastNode = flatNodes.get(flatNodes.size() - 1);
-        final int bufferSize = lastNode.mCachedAddressAfterUpdate + lastNode.mCachedSize;
-        final byte[] buffer = new byte[bufferSize];
-        int index = 0;
-
-        MakedictLog.i("Writing file...");
-        int dataEndOffset = 0;
-        for (Node n : flatNodes) {
-            dataEndOffset = writePlacedNode(dict, buffer, n, formatOptions);
-        }
-
-        if (DBG) showStatistics(flatNodes);
-
-        destination.write(buffer, 0, dataEndOffset);
-
-        destination.close();
-        MakedictLog.i("Done");
-    }
-
-
-    // Input methods: Read a binary dictionary to memory.
-    // readDictionaryBinary is the public entry point for them.
-
-    static int getChildrenAddressSize(final int optionFlags,
-            final FormatOptions formatOptions) {
-        if (formatOptions.mSupportsDynamicUpdate) return FormatSpec.SIGNED_CHILDREN_ADDRESS_SIZE;
-        switch (optionFlags & FormatSpec.MASK_GROUP_ADDRESS_TYPE) {
-            case FormatSpec.FLAG_GROUP_ADDRESS_TYPE_ONEBYTE:
-                return 1;
-            case FormatSpec.FLAG_GROUP_ADDRESS_TYPE_TWOBYTES:
-                return 2;
-            case FormatSpec.FLAG_GROUP_ADDRESS_TYPE_THREEBYTES:
-                return 3;
-            case FormatSpec.FLAG_GROUP_ADDRESS_TYPE_NOADDRESS:
-            default:
-                return 0;
-        }
-    }
-
-    static int readChildrenAddress(final FusionDictionaryBufferInterface buffer,
-            final int optionFlags, final FormatOptions options) {
-        if (options.mSupportsDynamicUpdate) {
-            final int address = buffer.readUnsignedInt24();
-            if (address == 0) return FormatSpec.NO_CHILDREN_ADDRESS;
-            if ((address & MSB24) != 0) {
-                return -(address & SINT24_MAX);
-            } else {
-                return address;
-            }
-        }
-        int address;
-        switch (optionFlags & FormatSpec.MASK_GROUP_ADDRESS_TYPE) {
-            case FormatSpec.FLAG_GROUP_ADDRESS_TYPE_ONEBYTE:
-                return buffer.readUnsignedByte();
-            case FormatSpec.FLAG_GROUP_ADDRESS_TYPE_TWOBYTES:
-                return buffer.readUnsignedShort();
-            case FormatSpec.FLAG_GROUP_ADDRESS_TYPE_THREEBYTES:
-                return buffer.readUnsignedInt24();
-            case FormatSpec.FLAG_GROUP_ADDRESS_TYPE_NOADDRESS:
-            default:
-                return FormatSpec.NO_CHILDREN_ADDRESS;
-        }
-    }
-
-    static int readParentAddress(final FusionDictionaryBufferInterface buffer,
-            final FormatOptions formatOptions) {
-        if (supportsDynamicUpdate(formatOptions)) {
-            final int parentAddress = buffer.readUnsignedInt24();
-            final int sign = ((parentAddress & MSB24) != 0) ? -1 : 1;
-            return sign * (parentAddress & SINT24_MAX);
-        } else {
-            return FormatSpec.NO_PARENT_ADDRESS;
-        }
-    }
-
-    private static final int[] CHARACTER_BUFFER = new int[FormatSpec.MAX_WORD_LENGTH];
-    public static CharGroupInfo readCharGroup(final FusionDictionaryBufferInterface buffer,
-            final int originalGroupAddress, final FormatOptions options) {
-        int addressPointer = originalGroupAddress;
-        final int flags = buffer.readUnsignedByte();
-        ++addressPointer;
-
-        final int parentAddress = readParentAddress(buffer, options);
-        if (supportsDynamicUpdate(options)) {
-            addressPointer += 3;
-        }
-
-        final int characters[];
-        if (0 != (flags & FormatSpec.FLAG_HAS_MULTIPLE_CHARS)) {
-            int index = 0;
-            int character = CharEncoding.readChar(buffer);
-            addressPointer += CharEncoding.getCharSize(character);
-            while (-1 != character) {
-                // FusionDictionary is making sure that the length of the word is smaller than
-                // MAX_WORD_LENGTH.
-                // So we'll never write past the end of CHARACTER_BUFFER.
-                CHARACTER_BUFFER[index++] = character;
-                character = CharEncoding.readChar(buffer);
-                addressPointer += CharEncoding.getCharSize(character);
-            }
-            characters = Arrays.copyOfRange(CHARACTER_BUFFER, 0, index);
-        } else {
-            final int character = CharEncoding.readChar(buffer);
-            addressPointer += CharEncoding.getCharSize(character);
-            characters = new int[] { character };
-        }
-        final int frequency;
-        if (0 != (FormatSpec.FLAG_IS_TERMINAL & flags)) {
-            ++addressPointer;
-            frequency = buffer.readUnsignedByte();
-        } else {
-            frequency = CharGroup.NOT_A_TERMINAL;
-        }
-        int childrenAddress = readChildrenAddress(buffer, flags, options);
-        if (childrenAddress != FormatSpec.NO_CHILDREN_ADDRESS) {
-            childrenAddress += addressPointer;
-        }
-        addressPointer += getChildrenAddressSize(flags, options);
-        ArrayList<WeightedString> shortcutTargets = null;
-        if (0 != (flags & FormatSpec.FLAG_HAS_SHORTCUT_TARGETS)) {
-            final int pointerBefore = buffer.position();
-            shortcutTargets = new ArrayList<WeightedString>();
-            buffer.readUnsignedShort(); // Skip the size
-            while (true) {
-                final int targetFlags = buffer.readUnsignedByte();
-                final String word = CharEncoding.readString(buffer);
-                shortcutTargets.add(new WeightedString(word,
-                        targetFlags & FormatSpec.FLAG_ATTRIBUTE_FREQUENCY));
-                if (0 == (targetFlags & FormatSpec.FLAG_ATTRIBUTE_HAS_NEXT)) break;
-            }
-            addressPointer += buffer.position() - pointerBefore;
-        }
-        ArrayList<PendingAttribute> bigrams = null;
-        if (0 != (flags & FormatSpec.FLAG_HAS_BIGRAMS)) {
-            bigrams = new ArrayList<PendingAttribute>();
-            int bigramCount = 0;
-            while (bigramCount++ < FormatSpec.MAX_BIGRAMS_IN_A_GROUP) {
-                final int bigramFlags = buffer.readUnsignedByte();
-                ++addressPointer;
-                final int sign = 0 == (bigramFlags & FormatSpec.FLAG_ATTRIBUTE_OFFSET_NEGATIVE)
-                        ? 1 : -1;
-                int bigramAddress = addressPointer;
-                switch (bigramFlags & FormatSpec.MASK_ATTRIBUTE_ADDRESS_TYPE) {
-                case FormatSpec.FLAG_ATTRIBUTE_ADDRESS_TYPE_ONEBYTE:
-                    bigramAddress += sign * buffer.readUnsignedByte();
-                    addressPointer += 1;
-                    break;
-                case FormatSpec.FLAG_ATTRIBUTE_ADDRESS_TYPE_TWOBYTES:
-                    bigramAddress += sign * buffer.readUnsignedShort();
-                    addressPointer += 2;
-                    break;
-                case FormatSpec.FLAG_ATTRIBUTE_ADDRESS_TYPE_THREEBYTES:
-                    final int offset = (buffer.readUnsignedByte() << 16)
-                            + buffer.readUnsignedShort();
-                    bigramAddress += sign * offset;
-                    addressPointer += 3;
-                    break;
-                default:
-                    throw new RuntimeException("Has bigrams with no address");
-                }
-                bigrams.add(new PendingAttribute(bigramFlags & FormatSpec.FLAG_ATTRIBUTE_FREQUENCY,
-                        bigramAddress));
-                if (0 == (bigramFlags & FormatSpec.FLAG_ATTRIBUTE_HAS_NEXT)) break;
-            }
-            if (bigramCount >= FormatSpec.MAX_BIGRAMS_IN_A_GROUP) {
-                MakedictLog.d("too many bigrams in a group.");
-            }
-        }
-        return new CharGroupInfo(originalGroupAddress, addressPointer, flags, characters, frequency,
-                parentAddress, childrenAddress, shortcutTargets, bigrams);
-    }
-
-    /**
-     * Reads and returns the char group count out of a buffer and forwards the pointer.
-     */
-    public static int readCharGroupCount(final FusionDictionaryBufferInterface buffer) {
-        final int msb = buffer.readUnsignedByte();
-        if (FormatSpec.MAX_CHARGROUPS_FOR_ONE_BYTE_CHARGROUP_COUNT >= msb) {
-            return msb;
-        } else {
-            return ((FormatSpec.MAX_CHARGROUPS_FOR_ONE_BYTE_CHARGROUP_COUNT & msb) << 8)
-                    + buffer.readUnsignedByte();
-        }
-    }
-
-    // The word cache here is a stopgap bandaid to help the catastrophic performance
-    // of this method. Since it performs direct, unbuffered random access to the file and
-    // may be called hundreds of thousands of times, the resulting performance is not
-    // reasonable without some kind of cache. Thus:
-    private static TreeMap<Integer, WeightedString> wordCache =
-            new TreeMap<Integer, WeightedString>();
-    /**
-     * Finds, as a string, the word at the address passed as an argument.
-     *
-     * @param buffer the buffer to read from.
-     * @param headerSize the size of the header.
-     * @param address the address to seek.
-     * @param formatOptions file format options.
-     * @return the word with its frequency, as a weighted string.
-     */
-    /* package for tests */ static WeightedString getWordAtAddress(
-            final FusionDictionaryBufferInterface buffer, final int headerSize, final int address,
-            final FormatOptions formatOptions) {
-        final WeightedString cachedString = wordCache.get(address);
-        if (null != cachedString) return cachedString;
-
-        final WeightedString result;
-        final int originalPointer = buffer.position();
-        buffer.position(address);
-
-        if (supportsDynamicUpdate(formatOptions)) {
-            result = getWordAtAddressWithParentAddress(buffer, headerSize, address, formatOptions);
-        } else {
-            result = getWordAtAddressWithoutParentAddress(buffer, headerSize, address,
-                    formatOptions);
-        }
-
-        wordCache.put(address, result);
-        buffer.position(originalPointer);
-        return result;
-    }
-
-    // TODO: static!? This will behave erratically when used in multi-threaded code.
-    // We need to fix this
-    private static int[] sGetWordBuffer = new int[FormatSpec.MAX_WORD_LENGTH];
-    @SuppressWarnings("unused")
-    private static WeightedString getWordAtAddressWithParentAddress(
-            final FusionDictionaryBufferInterface buffer, final int headerSize, final int address,
-            final FormatOptions options) {
-        int currentAddress = address;
-        int index = FormatSpec.MAX_WORD_LENGTH - 1;
-        int frequency = Integer.MIN_VALUE;
-        // the length of the path from the root to the leaf is limited by MAX_WORD_LENGTH
-        for (int count = 0; count < FormatSpec.MAX_WORD_LENGTH; ++count) {
-            CharGroupInfo currentInfo;
-            int loopCounter = 0;
-            do {
-                buffer.position(currentAddress + headerSize);
-                currentInfo = readCharGroup(buffer, currentAddress, options);
-                if (isMovedGroup(currentInfo.mFlags, options)) {
-                    currentAddress = currentInfo.mParentAddress + currentInfo.mOriginalAddress;
-                }
-                if (DBG && loopCounter++ > MAX_JUMPS) {
-                    MakedictLog.d("Too many jumps - probably a bug");
-                }
-            } while (isMovedGroup(currentInfo.mFlags, options));
-            if (Integer.MIN_VALUE == frequency) frequency = currentInfo.mFrequency;
-            for (int i = 0; i < currentInfo.mCharacters.length; ++i) {
-                sGetWordBuffer[index--] =
-                        currentInfo.mCharacters[currentInfo.mCharacters.length - i - 1];
-            }
-            if (currentInfo.mParentAddress == FormatSpec.NO_PARENT_ADDRESS) break;
-            currentAddress = currentInfo.mParentAddress + currentInfo.mOriginalAddress;
-        }
-
-        return new WeightedString(
-                new String(sGetWordBuffer, index + 1, FormatSpec.MAX_WORD_LENGTH - index - 1),
-                        frequency);
-    }
-
-    private static WeightedString getWordAtAddressWithoutParentAddress(
-            final FusionDictionaryBufferInterface buffer, final int headerSize, final int address,
-            final FormatOptions options) {
-        buffer.position(headerSize);
-        final int count = readCharGroupCount(buffer);
-        int groupOffset = getGroupCountSize(count);
-        final StringBuilder builder = new StringBuilder();
-        WeightedString result = null;
-
-        CharGroupInfo last = null;
-        for (int i = count - 1; i >= 0; --i) {
-            CharGroupInfo info = readCharGroup(buffer, groupOffset, options);
-            groupOffset = info.mEndAddress;
-            if (info.mOriginalAddress == address) {
-                builder.append(new String(info.mCharacters, 0, info.mCharacters.length));
-                result = new WeightedString(builder.toString(), info.mFrequency);
-                break; // and return
-            }
-            if (hasChildrenAddress(info.mChildrenAddress)) {
-                if (info.mChildrenAddress > address) {
-                    if (null == last) continue;
-                    builder.append(new String(last.mCharacters, 0, last.mCharacters.length));
-                    buffer.position(last.mChildrenAddress + headerSize);
-                    i = readCharGroupCount(buffer);
-                    groupOffset = last.mChildrenAddress + getGroupCountSize(i);
-                    last = null;
-                    continue;
-                }
-                last = info;
-            }
-            if (0 == i && hasChildrenAddress(last.mChildrenAddress)) {
-                builder.append(new String(last.mCharacters, 0, last.mCharacters.length));
-                buffer.position(last.mChildrenAddress + headerSize);
-                i = readCharGroupCount(buffer);
-                groupOffset = last.mChildrenAddress + getGroupCountSize(i);
-                last = null;
-                continue;
-            }
-        }
-        return result;
-    }
-
-    /**
-     * Reads a single node from a buffer.
-     *
-     * This methods reads the file at the current position. A node is fully expected to start at
-     * the current position.
-     * This will recursively read other nodes into the structure, populating the reverse
-     * maps on the fly and using them to keep track of already read nodes.
-     *
-     * @param buffer the buffer, correctly positioned at the start of a node.
-     * @param headerSize the size, in bytes, of the file header.
-     * @param reverseNodeMap a mapping from addresses to already read nodes.
-     * @param reverseGroupMap a mapping from addresses to already read character groups.
-     * @param options file format options.
-     * @return the read node with all his children already read.
-     */
-    private static Node readNode(final FusionDictionaryBufferInterface buffer, final int headerSize,
-            final Map<Integer, Node> reverseNodeMap, final Map<Integer, CharGroup> reverseGroupMap,
-            final FormatOptions options)
-            throws IOException {
-        final ArrayList<CharGroup> nodeContents = new ArrayList<CharGroup>();
-        final int nodeOrigin = buffer.position() - headerSize;
-
-        do { // Scan the linked-list node.
-            final int nodeHeadPosition = buffer.position() - headerSize;
-            final int count = readCharGroupCount(buffer);
-            int groupOffset = nodeHeadPosition + getGroupCountSize(count);
-            for (int i = count; i > 0; --i) { // Scan the array of CharGroup.
-                CharGroupInfo info = readCharGroup(buffer, groupOffset, options);
-                if (isMovedGroup(info.mFlags, options)) continue;
-                ArrayList<WeightedString> shortcutTargets = info.mShortcutTargets;
-                ArrayList<WeightedString> bigrams = null;
-                if (null != info.mBigrams) {
-                    bigrams = new ArrayList<WeightedString>();
-                    for (PendingAttribute bigram : info.mBigrams) {
-                        final WeightedString word = getWordAtAddress(
-                                buffer, headerSize, bigram.mAddress, options);
-                        final int reconstructedFrequency =
-                                reconstructBigramFrequency(word.mFrequency, bigram.mFrequency);
-                        bigrams.add(new WeightedString(word.mWord, reconstructedFrequency));
-                    }
-                }
-                if (hasChildrenAddress(info.mChildrenAddress)) {
-                    Node children = reverseNodeMap.get(info.mChildrenAddress);
-                    if (null == children) {
-                        final int currentPosition = buffer.position();
-                        buffer.position(info.mChildrenAddress + headerSize);
-                        children = readNode(
-                                buffer, headerSize, reverseNodeMap, reverseGroupMap, options);
-                        buffer.position(currentPosition);
-                    }
-                    nodeContents.add(
-                            new CharGroup(info.mCharacters, shortcutTargets, bigrams,
-                                    info.mFrequency,
-                                    0 != (info.mFlags & FormatSpec.FLAG_IS_NOT_A_WORD),
-                                    0 != (info.mFlags & FormatSpec.FLAG_IS_BLACKLISTED), children));
-                } else {
-                    nodeContents.add(
-                            new CharGroup(info.mCharacters, shortcutTargets, bigrams,
-                                    info.mFrequency,
-                                    0 != (info.mFlags & FormatSpec.FLAG_IS_NOT_A_WORD),
-                                    0 != (info.mFlags & FormatSpec.FLAG_IS_BLACKLISTED)));
-                }
-                groupOffset = info.mEndAddress;
-            }
-
-            // reach the end of the array.
-            if (options.mSupportsDynamicUpdate) {
-                final int nextAddress = buffer.readUnsignedInt24();
-                if (nextAddress >= 0 && nextAddress < buffer.limit()) {
-                    buffer.position(nextAddress);
-                } else {
-                    break;
-                }
-            }
-        } while (options.mSupportsDynamicUpdate &&
-                buffer.position() != FormatSpec.NO_FORWARD_LINK_ADDRESS);
-
-        final Node node = new Node(nodeContents);
-        node.mCachedAddressBeforeUpdate = nodeOrigin;
-        node.mCachedAddressAfterUpdate = nodeOrigin;
-        reverseNodeMap.put(node.mCachedAddressAfterUpdate, node);
-        return node;
-    }
-
-    /**
-     * Helper function to get the binary format version from the header.
-     * @throws IOException
-     */
-    private static int getFormatVersion(final FusionDictionaryBufferInterface buffer)
-            throws IOException {
-        final int magic_v1 = buffer.readUnsignedShort();
-        if (FormatSpec.VERSION_1_MAGIC_NUMBER == magic_v1) return buffer.readUnsignedByte();
-        final int magic_v2 = (magic_v1 << 16) + buffer.readUnsignedShort();
-        if (FormatSpec.VERSION_2_MAGIC_NUMBER == magic_v2) return buffer.readUnsignedShort();
-        return FormatSpec.NOT_A_VERSION_NUMBER;
-    }
-
-    /**
-     * Helper function to get and validate the binary format version.
-     * @throws UnsupportedFormatException
-     * @throws IOException
-     */
-    private static int checkFormatVersion(final FusionDictionaryBufferInterface buffer)
-            throws IOException, UnsupportedFormatException {
-        final int version = getFormatVersion(buffer);
-        if (version < FormatSpec.MINIMUM_SUPPORTED_VERSION
-                || version > FormatSpec.MAXIMUM_SUPPORTED_VERSION) {
-            throw new UnsupportedFormatException("This file has version " + version
-                    + ", but this implementation does not support versions above "
-                    + FormatSpec.MAXIMUM_SUPPORTED_VERSION);
-        }
-        return version;
-    }
-
-    /**
-     * Reads a header from a buffer.
-     * @param buffer the buffer to read.
-     * @throws IOException
-     * @throws UnsupportedFormatException
-     */
-    public static FileHeader readHeader(final FusionDictionaryBufferInterface buffer)
-            throws IOException, UnsupportedFormatException {
-        final int version = checkFormatVersion(buffer);
-        final int optionsFlags = buffer.readUnsignedShort();
-
-        final HashMap<String, String> attributes = new HashMap<String, String>();
-        final int headerSize;
-        if (version < FormatSpec.FIRST_VERSION_WITH_HEADER_SIZE) {
-            headerSize = buffer.position();
-        } else {
-            headerSize = buffer.readInt();
-            populateOptions(buffer, headerSize, attributes);
-            buffer.position(headerSize);
-        }
-
-        if (headerSize < 0) {
-            throw new UnsupportedFormatException("header size can't be negative.");
-        }
-
-        final FileHeader header = new FileHeader(headerSize,
-                new FusionDictionary.DictionaryOptions(attributes,
-                        0 != (optionsFlags & FormatSpec.GERMAN_UMLAUT_PROCESSING_FLAG),
-                        0 != (optionsFlags & FormatSpec.FRENCH_LIGATURE_PROCESSING_FLAG)),
-                new FormatOptions(version,
-                        0 != (optionsFlags & FormatSpec.SUPPORTS_DYNAMIC_UPDATE)));
-        return header;
-    }
-
-    /**
-     * Reads options from a buffer and populate a map with their contents.
-     *
-     * The buffer is read at the current position, so the caller must take care the pointer
-     * is in the right place before calling this.
-     */
-    public static void populateOptions(final FusionDictionaryBufferInterface buffer,
-            final int headerSize, final HashMap<String, String> options) {
-        while (buffer.position() < headerSize) {
-            final String key = CharEncoding.readString(buffer);
-            final String value = CharEncoding.readString(buffer);
-            options.put(key, value);
-        }
-    }
-
-    /**
-     * Reads a buffer and returns the memory representation of the dictionary.
-     *
-     * This high-level method takes a buffer and reads its contents, populating a
-     * FusionDictionary structure. The optional dict argument is an existing dictionary to
-     * which words from the buffer should be added. If it is null, a new dictionary is created.
-     *
-     * @param buffer the buffer to read.
-     * @param dict an optional dictionary to add words to, or null.
-     * @return the created (or merged) dictionary.
-     */
-    @UsedForTesting
-    public static FusionDictionary readDictionaryBinary(
-            final FusionDictionaryBufferInterface buffer, final FusionDictionary dict)
-                    throws IOException, UnsupportedFormatException {
-        // clear cache
-        wordCache.clear();
-
-        // Read header
-        final FileHeader header = readHeader(buffer);
-
-        Map<Integer, Node> reverseNodeMapping = new TreeMap<Integer, Node>();
-        Map<Integer, CharGroup> reverseGroupMapping = new TreeMap<Integer, CharGroup>();
-        final Node root = readNode(buffer, header.mHeaderSize, reverseNodeMapping,
-                reverseGroupMapping, header.mFormatOptions);
-
-        FusionDictionary newDict = new FusionDictionary(root, header.mDictionaryOptions);
-        if (null != dict) {
-            for (final Word w : dict) {
-                if (w.mIsBlacklistEntry) {
-                    newDict.addBlacklistEntry(w.mWord, w.mShortcutTargets, w.mIsNotAWord);
-                } else {
-                    newDict.add(w.mWord, w.mFrequency, w.mShortcutTargets, w.mIsNotAWord);
-                }
-            }
-            for (final Word w : dict) {
-                // By construction a binary dictionary may not have bigrams pointing to
-                // words that are not also registered as unigrams so we don't have to avoid
-                // them explicitly here.
-                for (final WeightedString bigram : w.mBigrams) {
-                    newDict.setBigram(w.mWord, bigram.mWord, bigram.mFrequency);
-                }
-            }
-        }
-
-        return newDict;
-    }
-
-    /**
-     * Helper method to pass a file name instead of a File object to isBinaryDictionary.
-     */
-    public static boolean isBinaryDictionary(final String filename) {
-        final File file = new File(filename);
-        return isBinaryDictionary(file);
-    }
-
-    /**
-     * Basic test to find out whether the file is a binary dictionary or not.
-     *
-     * Concretely this only tests the magic number.
-     *
-     * @param file The file to test.
-     * @return true if it's a binary dictionary, false otherwise
-     */
-    public static boolean isBinaryDictionary(final File file) {
-        FileInputStream inStream = null;
-        try {
-            inStream = new FileInputStream(file);
-            final ByteBuffer buffer = inStream.getChannel().map(
-                    FileChannel.MapMode.READ_ONLY, 0, file.length());
-            final int version = getFormatVersion(new ByteBufferWrapper(buffer));
-            return (version >= FormatSpec.MINIMUM_SUPPORTED_VERSION
-                    && version <= FormatSpec.MAXIMUM_SUPPORTED_VERSION);
-        } catch (FileNotFoundException e) {
-            return false;
-        } catch (IOException e) {
-            return false;
-        } finally {
-            if (inStream != null) {
-                try {
-                    inStream.close();
-                } catch (IOException e) {
-                    // do nothing
-                }
-            }
-        }
-    }
-
-    /**
-     * Calculate bigram frequency from compressed value
-     *
-     * @see #makeBigramFlags
-     *
-     * @param unigramFrequency
-     * @param bigramFrequency compressed frequency
-     * @return approximate bigram frequency
-     */
-    public static int reconstructBigramFrequency(final int unigramFrequency,
-            final int bigramFrequency) {
-        final float stepSize = (FormatSpec.MAX_TERMINAL_FREQUENCY - unigramFrequency)
-                / (1.5f + FormatSpec.MAX_BIGRAM_FREQUENCY);
-        final float resultFreqFloat = unigramFrequency + stepSize * (bigramFrequency + 1.0f);
-        return (int)resultFreqFloat;
-    }
-}
diff --git a/java/src/com/android/inputmethod/latin/makedict/DictDecoder.java b/java/src/com/android/inputmethod/latin/makedict/DictDecoder.java
new file mode 100644
index 0000000..3796a46
--- /dev/null
+++ b/java/src/com/android/inputmethod/latin/makedict/DictDecoder.java
@@ -0,0 +1,394 @@
+/*
+ * Copyright (C) 2013 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.latin.makedict;
+
+import com.android.inputmethod.annotations.UsedForTesting;
+import com.android.inputmethod.latin.makedict.BinaryDictDecoderUtils.CharEncoding;
+import com.android.inputmethod.latin.makedict.BinaryDictDecoderUtils.DictBuffer;
+import com.android.inputmethod.latin.makedict.FormatSpec.FileHeader;
+import com.android.inputmethod.latin.makedict.FormatSpec.FormatOptions;
+import com.android.inputmethod.latin.makedict.FusionDictionary.WeightedString;
+import com.android.inputmethod.latin.utils.ByteArrayDictBuffer;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.io.RandomAccessFile;
+import java.nio.ByteBuffer;
+import java.nio.channels.FileChannel;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.TreeMap;
+
+/**
+ * The base class of binary dictionary decoders.
+ */
+public abstract class DictDecoder {
+
+    protected FileHeader readHeader(final DictBuffer dictBuffer)
+            throws IOException, UnsupportedFormatException {
+        if (dictBuffer == null) {
+            openDictBuffer();
+        }
+
+        final int version = HeaderReader.readVersion(dictBuffer);
+        if (version < FormatSpec.MINIMUM_SUPPORTED_VERSION
+                || version > FormatSpec.MAXIMUM_SUPPORTED_VERSION) {
+            throw new UnsupportedFormatException("Unsupported version : " + version);
+        }
+        // TODO: Remove this field.
+        final int optionsFlags = HeaderReader.readOptionFlags(dictBuffer);
+
+        final int headerSize = HeaderReader.readHeaderSize(dictBuffer);
+
+        if (headerSize < 0) {
+            throw new UnsupportedFormatException("header size can't be negative.");
+        }
+
+        final HashMap<String, String> attributes = HeaderReader.readAttributes(dictBuffer,
+                headerSize);
+
+        final FileHeader header = new FileHeader(headerSize,
+                new FusionDictionary.DictionaryOptions(attributes,
+                        0 != (optionsFlags & FormatSpec.GERMAN_UMLAUT_PROCESSING_FLAG),
+                        0 != (optionsFlags & FormatSpec.FRENCH_LIGATURE_PROCESSING_FLAG)),
+                        new FormatOptions(version,
+                                0 != (optionsFlags & FormatSpec.SUPPORTS_DYNAMIC_UPDATE)));
+        return header;
+    }
+
+    /**
+     * Reads and returns the file header.
+     */
+    public abstract FileHeader readHeader() throws IOException, UnsupportedFormatException;
+
+    /**
+     * Reads PtNode from nodeAddress.
+     * @param ptNodePos the position of PtNode.
+     * @param formatOptions the format options.
+     * @return PtNodeInfo.
+     */
+    public abstract PtNodeInfo readPtNode(final int ptNodePos, final FormatOptions formatOptions);
+
+    /**
+     * Reads a buffer and returns the memory representation of the dictionary.
+     *
+     * This high-level method takes a buffer and reads its contents, populating a
+     * FusionDictionary structure. The optional dict argument is an existing dictionary to
+     * which words from the buffer should be added. If it is null, a new dictionary is created.
+     *
+     * @param dict an optional dictionary to add words to, or null.
+     * @param deleteDictIfBroken a flag indicating whether this method should remove the broken
+     * dictionary or not.
+     * @return the created (or merged) dictionary.
+     */
+    @UsedForTesting
+    public abstract FusionDictionary readDictionaryBinary(final FusionDictionary dict,
+            final boolean deleteDictIfBroken)
+                    throws FileNotFoundException, IOException, UnsupportedFormatException;
+
+    /**
+     * Gets the address of the last PtNode of the exact matching word in the dictionary.
+     * If no match is found, returns NOT_VALID_WORD.
+     *
+     * @param word the word we search for.
+     * @return the address of the terminal node.
+     * @throws IOException if the file can't be read.
+     * @throws UnsupportedFormatException if the format of the file is not recognized.
+     */
+    @UsedForTesting
+    public int getTerminalPosition(final String word)
+            throws IOException, UnsupportedFormatException {
+        if (!isDictBufferOpen()) {
+            openDictBuffer();
+        }
+        return BinaryDictIOUtils.getTerminalPosition(this, word);
+    }
+
+    /**
+     * Reads unigrams and bigrams from the binary file.
+     * Doesn't store a full memory representation of the dictionary.
+     *
+     * @param words the map to store the address as a key and the word as a value.
+     * @param frequencies the map to store the address as a key and the frequency as a value.
+     * @param bigrams the map to store the address as a key and the list of address as a value.
+     * @throws IOException if the file can't be read.
+     * @throws UnsupportedFormatException if the format of the file is not recognized.
+     */
+    @UsedForTesting
+    public void readUnigramsAndBigramsBinary(final TreeMap<Integer, String> words,
+            final TreeMap<Integer, Integer> frequencies,
+            final TreeMap<Integer, ArrayList<PendingAttribute>> bigrams)
+            throws IOException, UnsupportedFormatException {
+        if (!isDictBufferOpen()) {
+            openDictBuffer();
+        }
+        BinaryDictIOUtils.readUnigramsAndBigramsBinary(this, words, frequencies, bigrams);
+    }
+
+    /**
+     * Sets the position of the buffer to the given value.
+     *
+     * @param newPos the new position
+     */
+    public abstract void setPosition(final int newPos);
+
+    /**
+     * Gets the position of the buffer.
+     *
+     * @return the position
+     */
+    public abstract int getPosition();
+
+    /**
+     * Reads and returns the PtNode count out of a buffer and forwards the pointer.
+     */
+    public abstract int readPtNodeCount();
+
+    /**
+     * Reads the forward link and advances the position.
+     *
+     * @return true if this method moves the file pointer, false otherwise.
+     */
+    public abstract boolean readAndFollowForwardLink();
+    public abstract boolean hasNextPtNodeArray();
+
+    /**
+     * Opens the dictionary file and makes DictBuffer.
+     */
+    @UsedForTesting
+    public abstract void openDictBuffer() throws FileNotFoundException, IOException;
+    @UsedForTesting
+    public abstract boolean isDictBufferOpen();
+
+    // Constants for DictionaryBufferFactory.
+    public static final int USE_READONLY_BYTEBUFFER = 0x01000000;
+    public static final int USE_BYTEARRAY = 0x02000000;
+    public static final int USE_WRITABLE_BYTEBUFFER = 0x03000000;
+    public static final int MASK_DICTBUFFER = 0x0F000000;
+
+    public interface DictionaryBufferFactory {
+        public DictBuffer getDictionaryBuffer(final File file)
+                throws FileNotFoundException, IOException;
+    }
+
+    /**
+     * Creates DictionaryBuffer using a ByteBuffer
+     *
+     * This class uses less memory than DictionaryBufferFromByteArrayFactory,
+     * but doesn't perform as fast.
+     * When operating on a big dictionary, this class is preferred.
+     */
+    public static final class DictionaryBufferFromReadOnlyByteBufferFactory
+            implements DictionaryBufferFactory {
+        @Override
+        public DictBuffer getDictionaryBuffer(final File file)
+                throws FileNotFoundException, IOException {
+            FileInputStream inStream = null;
+            ByteBuffer buffer = null;
+            try {
+                inStream = new FileInputStream(file);
+                buffer = inStream.getChannel().map(FileChannel.MapMode.READ_ONLY,
+                        0, file.length());
+            } finally {
+                if (inStream != null) {
+                    inStream.close();
+                }
+            }
+            if (buffer != null) {
+                return new BinaryDictDecoderUtils.ByteBufferDictBuffer(buffer);
+            }
+            return null;
+        }
+    }
+
+    /**
+     * Creates DictionaryBuffer using a byte array
+     *
+     * This class performs faster than other classes, but consumes more memory.
+     * When operating on a small dictionary, this class is preferred.
+     */
+    public static final class DictionaryBufferFromByteArrayFactory
+            implements DictionaryBufferFactory {
+        @Override
+        public DictBuffer getDictionaryBuffer(final File file)
+                throws FileNotFoundException, IOException {
+            FileInputStream inStream = null;
+            try {
+                inStream = new FileInputStream(file);
+                final byte[] array = new byte[(int) file.length()];
+                inStream.read(array);
+                return new ByteArrayDictBuffer(array);
+            } finally {
+                if (inStream != null) {
+                    inStream.close();
+                }
+            }
+        }
+    }
+
+    /**
+     * Creates DictionaryBuffer using a writable ByteBuffer and a RandomAccessFile.
+     *
+     * This class doesn't perform as fast as other classes,
+     * but this class is the only option available for destructive operations (insert or delete)
+     * on a dictionary.
+     */
+    @UsedForTesting
+    public static final class DictionaryBufferFromWritableByteBufferFactory
+            implements DictionaryBufferFactory {
+        @Override
+        public DictBuffer getDictionaryBuffer(final File file)
+                throws FileNotFoundException, IOException {
+            RandomAccessFile raFile = null;
+            ByteBuffer buffer = null;
+            try {
+                raFile = new RandomAccessFile(file, "rw");
+                buffer = raFile.getChannel().map(FileChannel.MapMode.READ_WRITE, 0, file.length());
+            } finally {
+                if (raFile != null) {
+                    raFile.close();
+                }
+            }
+            if (buffer != null) {
+                return new BinaryDictDecoderUtils.ByteBufferDictBuffer(buffer);
+            }
+            return null;
+        }
+    }
+
+    /**
+     * A utility class for reading a file header.
+     */
+    protected static class HeaderReader {
+        protected static int readVersion(final DictBuffer dictBuffer)
+                throws IOException, UnsupportedFormatException {
+            return BinaryDictDecoderUtils.checkFormatVersion(dictBuffer);
+        }
+
+        protected static int readOptionFlags(final DictBuffer dictBuffer) {
+            return dictBuffer.readUnsignedShort();
+        }
+
+        protected static int readHeaderSize(final DictBuffer dictBuffer) {
+            return dictBuffer.readInt();
+        }
+
+        protected static HashMap<String, String> readAttributes(final DictBuffer dictBuffer,
+                final int headerSize) {
+            final HashMap<String, String> attributes = new HashMap<String, String>();
+            while (dictBuffer.position() < headerSize) {
+                // We can avoid an infinite loop here since dictBuffer.position() is always
+                // increased by calling CharEncoding.readString.
+                final String key = CharEncoding.readString(dictBuffer);
+                final String value = CharEncoding.readString(dictBuffer);
+                attributes.put(key, value);
+            }
+            dictBuffer.position(headerSize);
+            return attributes;
+        }
+    }
+
+    /**
+     * A utility class for reading a PtNode.
+     */
+    protected static class PtNodeReader {
+        protected static int readPtNodeOptionFlags(final DictBuffer dictBuffer) {
+            return dictBuffer.readUnsignedByte();
+        }
+
+        protected static int readParentAddress(final DictBuffer dictBuffer,
+                final FormatOptions formatOptions) {
+            if (BinaryDictIOUtils.supportsDynamicUpdate(formatOptions)) {
+                return BinaryDictDecoderUtils.readSInt24(dictBuffer);
+            } else {
+                return FormatSpec.NO_PARENT_ADDRESS;
+            }
+        }
+
+        protected static int readChildrenAddress(final DictBuffer dictBuffer, final int optionFlags,
+                final FormatOptions formatOptions) {
+            if (BinaryDictIOUtils.supportsDynamicUpdate(formatOptions)) {
+                final int address = BinaryDictDecoderUtils.readSInt24(dictBuffer);
+                if (address == 0) return FormatSpec.NO_CHILDREN_ADDRESS;
+                return address;
+            } else {
+                switch (optionFlags & FormatSpec.MASK_CHILDREN_ADDRESS_TYPE) {
+                    case FormatSpec.FLAG_CHILDREN_ADDRESS_TYPE_ONEBYTE:
+                        return dictBuffer.readUnsignedByte();
+                    case FormatSpec.FLAG_CHILDREN_ADDRESS_TYPE_TWOBYTES:
+                        return dictBuffer.readUnsignedShort();
+                    case FormatSpec.FLAG_CHILDREN_ADDRESS_TYPE_THREEBYTES:
+                        return dictBuffer.readUnsignedInt24();
+                    case FormatSpec.FLAG_CHILDREN_ADDRESS_TYPE_NOADDRESS:
+                    default:
+                        return FormatSpec.NO_CHILDREN_ADDRESS;
+                }
+            }
+        }
+
+        // Reads shortcuts and returns the read length.
+        protected static int readShortcut(final DictBuffer dictBuffer,
+                final ArrayList<WeightedString> shortcutTargets) {
+            final int pointerBefore = dictBuffer.position();
+            dictBuffer.readUnsignedShort(); // skip the size
+            while (true) {
+                final int targetFlags = dictBuffer.readUnsignedByte();
+                final String word = CharEncoding.readString(dictBuffer);
+                shortcutTargets.add(new WeightedString(word,
+                        targetFlags & FormatSpec.FLAG_BIGRAM_SHORTCUT_ATTR_FREQUENCY));
+                if (0 == (targetFlags & FormatSpec.FLAG_BIGRAM_SHORTCUT_ATTR_HAS_NEXT)) break;
+            }
+            return dictBuffer.position() - pointerBefore;
+        }
+
+        protected static int readBigramAddresses(final DictBuffer dictBuffer,
+                final ArrayList<PendingAttribute> bigrams, final int baseAddress) {
+            int readLength = 0;
+            int bigramCount = 0;
+            while (bigramCount++ < FormatSpec.MAX_BIGRAMS_IN_A_PTNODE) {
+                final int bigramFlags = dictBuffer.readUnsignedByte();
+                ++readLength;
+                final int sign = 0 == (bigramFlags & FormatSpec.FLAG_BIGRAM_ATTR_OFFSET_NEGATIVE)
+                        ? 1 : -1;
+                int bigramAddress = baseAddress + readLength;
+                switch (bigramFlags & FormatSpec.MASK_BIGRAM_ATTR_ADDRESS_TYPE) {
+                    case FormatSpec.FLAG_BIGRAM_ATTR_ADDRESS_TYPE_ONEBYTE:
+                        bigramAddress += sign * dictBuffer.readUnsignedByte();
+                        readLength += 1;
+                        break;
+                    case FormatSpec.FLAG_BIGRAM_ATTR_ADDRESS_TYPE_TWOBYTES:
+                        bigramAddress += sign * dictBuffer.readUnsignedShort();
+                        readLength += 2;
+                        break;
+                    case FormatSpec.FLAG_BIGRAM_ATTR_ADDRESS_TYPE_THREEBYTES:
+                        bigramAddress += sign * dictBuffer.readUnsignedInt24();
+                        readLength += 3;
+                        break;
+                    default:
+                        throw new RuntimeException("Has bigrams with no address");
+                }
+                bigrams.add(new PendingAttribute(
+                        bigramFlags & FormatSpec.FLAG_BIGRAM_SHORTCUT_ATTR_FREQUENCY,
+                        bigramAddress));
+                if (0 == (bigramFlags & FormatSpec.FLAG_BIGRAM_SHORTCUT_ATTR_HAS_NEXT)) break;
+            }
+            return readLength;
+        }
+    }
+}
diff --git a/java/src/com/android/inputmethod/latin/makedict/DictEncoder.java b/java/src/com/android/inputmethod/latin/makedict/DictEncoder.java
new file mode 100644
index 0000000..ea5d492
--- /dev/null
+++ b/java/src/com/android/inputmethod/latin/makedict/DictEncoder.java
@@ -0,0 +1,38 @@
+/*
+ * Copyright (C) 2013 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.latin.makedict;
+
+import com.android.inputmethod.latin.makedict.FormatSpec.FormatOptions;
+import com.android.inputmethod.latin.makedict.FusionDictionary.PtNode;
+
+import java.io.IOException;
+
+/**
+ * An interface of binary dictionary encoder.
+ */
+public interface DictEncoder {
+    public void writeDictionary(final FusionDictionary dict, final FormatOptions formatOptions)
+            throws IOException, UnsupportedFormatException;
+
+    public void setPosition(final int position);
+    public int getPosition();
+    public void writePtNodeCount(final int ptNodeCount);
+    public void writeForwardLinkAddress(final int forwardLinkAddress);
+
+    public void writePtNode(final PtNode ptNode, final int parentPosition,
+            final FormatOptions formatOptions, final FusionDictionary dict);
+}
diff --git a/java/src/com/android/inputmethod/latin/makedict/DynamicBinaryDictIOUtils.java b/java/src/com/android/inputmethod/latin/makedict/DynamicBinaryDictIOUtils.java
new file mode 100644
index 0000000..bf3d191
--- /dev/null
+++ b/java/src/com/android/inputmethod/latin/makedict/DynamicBinaryDictIOUtils.java
@@ -0,0 +1,502 @@
+/*
+ * Copyright (C) 2013 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.latin.makedict;
+
+import com.android.inputmethod.annotations.UsedForTesting;
+import com.android.inputmethod.latin.Constants;
+import com.android.inputmethod.latin.makedict.BinaryDictDecoderUtils.DictBuffer;
+import com.android.inputmethod.latin.makedict.FormatSpec.FileHeader;
+import com.android.inputmethod.latin.makedict.FormatSpec.FormatOptions;
+import com.android.inputmethod.latin.makedict.FusionDictionary.WeightedString;
+
+import java.io.IOException;
+import java.io.OutputStream;
+import java.util.ArrayList;
+import java.util.Arrays;
+
+/**
+ * The utility class to help dynamic updates on the binary dictionary.
+ *
+ * All the methods in this class are static.
+ */
+@UsedForTesting
+public final class DynamicBinaryDictIOUtils {
+    private static final boolean DBG = false;
+    private static final int MAX_JUMPS = 10000;
+
+    private DynamicBinaryDictIOUtils() {
+        // This utility class is not publicly instantiable.
+    }
+
+    private static int markAsDeleted(final int flags) {
+        return (flags & (~FormatSpec.MASK_CHILDREN_ADDRESS_TYPE)) | FormatSpec.FLAG_IS_DELETED;
+    }
+
+    /**
+     * Delete the word from the binary file.
+     *
+     * @param dictDecoder the dict decoder.
+     * @param word the word we delete
+     * @throws IOException
+     * @throws UnsupportedFormatException
+     */
+    @UsedForTesting
+    public static void deleteWord(final Ver3DictDecoder dictDecoder, final String word)
+            throws IOException, UnsupportedFormatException {
+        final DictBuffer dictBuffer = dictDecoder.getDictBuffer();
+        dictBuffer.position(0);
+        final FileHeader header = dictDecoder.readHeader();
+        final int wordPosition = dictDecoder.getTerminalPosition(word);
+        if (wordPosition == FormatSpec.NOT_VALID_WORD) return;
+
+        dictBuffer.position(wordPosition);
+        final int flags = dictBuffer.readUnsignedByte();
+        dictBuffer.position(wordPosition);
+        dictBuffer.put((byte)markAsDeleted(flags));
+    }
+
+    /**
+     * Update a parent address in a PtNode that is referred to by ptNodeOriginAddress.
+     *
+     * @param dictBuffer the DictBuffer to write.
+     * @param ptNodeOriginAddress the address of the PtNode.
+     * @param newParentAddress the absolute address of the parent.
+     * @param formatOptions file format options.
+     */
+    public static void updateParentAddress(final DictBuffer dictBuffer,
+            final int ptNodeOriginAddress, final int newParentAddress,
+            final FormatOptions formatOptions) {
+        final int originalPosition = dictBuffer.position();
+        dictBuffer.position(ptNodeOriginAddress);
+        if (!formatOptions.mSupportsDynamicUpdate) {
+            throw new RuntimeException("this file format does not support parent addresses");
+        }
+        final int flags = dictBuffer.readUnsignedByte();
+        if (BinaryDictIOUtils.isMovedPtNode(flags, formatOptions)) {
+            // If the node is moved, the parent address is stored in the destination node.
+            // We are guaranteed to process the destination node later, so there is no need to
+            // update anything here.
+            dictBuffer.position(originalPosition);
+            return;
+        }
+        if (DBG) {
+            MakedictLog.d("update parent address flags=" + flags + ", " + ptNodeOriginAddress);
+        }
+        final int parentOffset = newParentAddress - ptNodeOriginAddress;
+        BinaryDictIOUtils.writeSInt24ToBuffer(dictBuffer, parentOffset);
+        dictBuffer.position(originalPosition);
+    }
+
+    /**
+     * Update parent addresses in a node array stored at ptNodeOriginAddress.
+     *
+     * @param dictBuffer the DictBuffer to be modified.
+     * @param ptNodeOriginAddress the address of the node array to update.
+     * @param newParentAddress the address to be written.
+     * @param formatOptions file format options.
+     */
+    public static void updateParentAddresses(final DictBuffer dictBuffer,
+            final int ptNodeOriginAddress, final int newParentAddress,
+            final FormatOptions formatOptions) {
+        final int originalPosition = dictBuffer.position();
+        dictBuffer.position(ptNodeOriginAddress);
+        do {
+            final int count = BinaryDictDecoderUtils.readPtNodeCount(dictBuffer);
+            for (int i = 0; i < count; ++i) {
+                updateParentAddress(dictBuffer, dictBuffer.position(), newParentAddress,
+                        formatOptions);
+                BinaryDictIOUtils.skipPtNode(dictBuffer, formatOptions);
+            }
+            final int forwardLinkAddress = dictBuffer.readUnsignedInt24();
+            dictBuffer.position(forwardLinkAddress);
+        } while (formatOptions.mSupportsDynamicUpdate
+                && dictBuffer.position() != FormatSpec.NO_FORWARD_LINK_ADDRESS);
+        dictBuffer.position(originalPosition);
+    }
+
+    /**
+     * Update a children address in a PtNode that is addressed by ptNodeOriginAddress.
+     *
+     * @param dictBuffer the DictBuffer to write.
+     * @param ptNodeOriginAddress the address of the PtNode.
+     * @param newChildrenAddress the absolute address of the child.
+     * @param formatOptions file format options.
+     */
+    public static void updateChildrenAddress(final DictBuffer dictBuffer,
+            final int ptNodeOriginAddress, final int newChildrenAddress,
+            final FormatOptions formatOptions) {
+        final int originalPosition = dictBuffer.position();
+        dictBuffer.position(ptNodeOriginAddress);
+        final int flags = dictBuffer.readUnsignedByte();
+        final int parentAddress = BinaryDictDecoderUtils.readParentAddress(dictBuffer,
+                formatOptions);
+        BinaryDictIOUtils.skipString(dictBuffer, (flags & FormatSpec.FLAG_HAS_MULTIPLE_CHARS) != 0);
+        if ((flags & FormatSpec.FLAG_IS_TERMINAL) != 0) dictBuffer.readUnsignedByte();
+        final int childrenOffset = newChildrenAddress == FormatSpec.NO_CHILDREN_ADDRESS
+                ? FormatSpec.NO_CHILDREN_ADDRESS : newChildrenAddress - dictBuffer.position();
+        BinaryDictIOUtils.writeSInt24ToBuffer(dictBuffer, childrenOffset);
+        dictBuffer.position(originalPosition);
+    }
+
+    /**
+     * Helper method to move a PtNode to the tail of the file.
+     */
+    private static int movePtNode(final OutputStream destination,
+            final DictBuffer dictBuffer, final PtNodeInfo info,
+            final int nodeArrayOriginAddress, final int oldNodeAddress,
+            final FormatOptions formatOptions) throws IOException {
+        updateParentAddress(dictBuffer, oldNodeAddress, dictBuffer.limit() + 1, formatOptions);
+        dictBuffer.position(oldNodeAddress);
+        final int currentFlags = dictBuffer.readUnsignedByte();
+        dictBuffer.position(oldNodeAddress);
+        dictBuffer.put((byte)(FormatSpec.FLAG_IS_MOVED | (currentFlags
+                & (~FormatSpec.MASK_MOVE_AND_DELETE_FLAG))));
+        int size = FormatSpec.PTNODE_FLAGS_SIZE;
+        updateForwardLink(dictBuffer, nodeArrayOriginAddress, dictBuffer.limit(), formatOptions);
+        size += BinaryDictIOUtils.writeNodes(destination, new PtNodeInfo[] { info });
+        return size;
+    }
+
+    @SuppressWarnings("unused")
+    private static void updateForwardLink(final DictBuffer dictBuffer,
+            final int nodeArrayOriginAddress, final int newNodeArrayAddress,
+            final FormatOptions formatOptions) {
+        dictBuffer.position(nodeArrayOriginAddress);
+        int jumpCount = 0;
+        while (jumpCount++ < MAX_JUMPS) {
+            final int count = BinaryDictDecoderUtils.readPtNodeCount(dictBuffer);
+            for (int i = 0; i < count; ++i) {
+                BinaryDictIOUtils.skipPtNode(dictBuffer, formatOptions);
+            }
+            final int forwardLinkAddress = dictBuffer.readUnsignedInt24();
+            if (forwardLinkAddress == FormatSpec.NO_FORWARD_LINK_ADDRESS) {
+                dictBuffer.position(dictBuffer.position() - FormatSpec.FORWARD_LINK_ADDRESS_SIZE);
+                BinaryDictIOUtils.writeSInt24ToBuffer(dictBuffer, newNodeArrayAddress);
+                return;
+            }
+            dictBuffer.position(forwardLinkAddress);
+        }
+        if (DBG && jumpCount >= MAX_JUMPS) {
+            throw new RuntimeException("too many jumps, probably a bug.");
+        }
+    }
+
+    /**
+     * Move a PtNode that is referred to by oldPtNodeOrigin to the tail of the file, and set the
+     * children address to the byte after the PtNode.
+     *
+     * @param fileEndAddress the address of the tail of the file.
+     * @param codePoints the characters to put inside the PtNode.
+     * @param length how many code points to read from codePoints.
+     * @param flags the flags for this PtNode.
+     * @param frequency the frequency of this terminal.
+     * @param parentAddress the address of the parent PtNode of this PtNode.
+     * @param shortcutTargets the shortcut targets for this PtNode.
+     * @param bigrams the bigrams for this PtNode.
+     * @param destination the stream representing the tail of the file.
+     * @param dictBuffer the DictBuffer representing the (constant-size) body of the file.
+     * @param oldPtNodeArrayOrigin the origin of the old PtNode array this PtNode was a part of.
+     * @param oldPtNodeOrigin the old origin where this PtNode used to be stored.
+     * @param formatOptions format options for this dictionary.
+     * @return the size written, in bytes.
+     * @throws IOException if the file can't be accessed
+     */
+    private static int movePtNode(final int fileEndAddress, final int[] codePoints,
+            final int length, final int flags, final int frequency, final int parentAddress,
+            final ArrayList<WeightedString> shortcutTargets,
+            final ArrayList<PendingAttribute> bigrams, final OutputStream destination,
+            final DictBuffer dictBuffer, final int oldPtNodeArrayOrigin,
+            final int oldPtNodeOrigin, final FormatOptions formatOptions) throws IOException {
+        int size = 0;
+        final int newPtNodeOrigin = fileEndAddress + 1;
+        final int[] writtenCharacters = Arrays.copyOfRange(codePoints, 0, length);
+        final PtNodeInfo tmpInfo = new PtNodeInfo(newPtNodeOrigin, -1 /* endAddress */,
+                flags, writtenCharacters, frequency, parentAddress, FormatSpec.NO_CHILDREN_ADDRESS,
+                shortcutTargets, bigrams);
+        size = BinaryDictIOUtils.computePtNodeSize(tmpInfo, formatOptions);
+        final PtNodeInfo newInfo = new PtNodeInfo(newPtNodeOrigin, newPtNodeOrigin + size,
+                flags, writtenCharacters, frequency, parentAddress,
+                fileEndAddress + 1 + size + FormatSpec.FORWARD_LINK_ADDRESS_SIZE, shortcutTargets,
+                bigrams);
+        movePtNode(destination, dictBuffer, newInfo, oldPtNodeArrayOrigin, oldPtNodeOrigin,
+                formatOptions);
+        return 1 + size + FormatSpec.FORWARD_LINK_ADDRESS_SIZE;
+    }
+
+    /**
+     * Insert a word into a binary dictionary.
+     *
+     * @param dictDecoder the dict decoder.
+     * @param destination a stream to the underlying file, with the pointer at the end of the file.
+     * @param word the word to insert.
+     * @param frequency the frequency of the new word.
+     * @param bigramStrings bigram list, or null if none.
+     * @param shortcuts shortcut list, or null if none.
+     * @param isBlackListEntry whether this should be a blacklist entry.
+     * @throws IOException if the file can't be accessed.
+     * @throws UnsupportedFormatException if the existing dictionary is in an unexpected format.
+     */
+    // TODO: Support batch insertion.
+    // TODO: Remove @UsedForTesting once UserHistoryDictionary is implemented by BinaryDictionary.
+    @UsedForTesting
+    public static void insertWord(final Ver3DictDecoder dictDecoder,
+            final OutputStream destination, final String word, final int frequency,
+            final ArrayList<WeightedString> bigramStrings,
+            final ArrayList<WeightedString> shortcuts, final boolean isNotAWord,
+            final boolean isBlackListEntry)
+                    throws IOException, UnsupportedFormatException {
+        final ArrayList<PendingAttribute> bigrams = new ArrayList<PendingAttribute>();
+        final DictBuffer dictBuffer = dictDecoder.getDictBuffer();
+        if (bigramStrings != null) {
+            for (final WeightedString bigram : bigramStrings) {
+                int position = dictDecoder.getTerminalPosition(bigram.mWord);
+                if (position == FormatSpec.NOT_VALID_WORD) {
+                    // TODO: figure out what is the correct thing to do here.
+                } else {
+                    bigrams.add(new PendingAttribute(bigram.mFrequency, position));
+                }
+            }
+        }
+
+        final boolean isTerminal = true;
+        final boolean hasBigrams = !bigrams.isEmpty();
+        final boolean hasShortcuts = shortcuts != null && !shortcuts.isEmpty();
+
+        // find the insert position of the word.
+        if (dictBuffer.position() != 0) dictBuffer.position(0);
+        final FileHeader fileHeader = dictDecoder.readHeader();
+
+        int wordPos = 0, address = dictBuffer.position(), nodeOriginAddress = dictBuffer.position();
+        final int[] codePoints = FusionDictionary.getCodePoints(word);
+        final int wordLen = codePoints.length;
+
+        for (int depth = 0; depth < Constants.DICTIONARY_MAX_WORD_LENGTH; ++depth) {
+            if (wordPos >= wordLen) break;
+            nodeOriginAddress = dictBuffer.position();
+            int nodeParentAddress = -1;
+            final int ptNodeCount = BinaryDictDecoderUtils.readPtNodeCount(dictBuffer);
+            boolean foundNextNode = false;
+
+            for (int i = 0; i < ptNodeCount; ++i) {
+                address = dictBuffer.position();
+                final PtNodeInfo currentInfo = dictDecoder.readPtNode(address,
+                        fileHeader.mFormatOptions);
+                final boolean isMovedNode = BinaryDictIOUtils.isMovedPtNode(currentInfo.mFlags,
+                        fileHeader.mFormatOptions);
+                if (isMovedNode) continue;
+                nodeParentAddress = (currentInfo.mParentAddress == FormatSpec.NO_PARENT_ADDRESS)
+                        ? FormatSpec.NO_PARENT_ADDRESS : currentInfo.mParentAddress + address;
+                boolean matched = true;
+                for (int p = 0; p < currentInfo.mCharacters.length; ++p) {
+                    if (wordPos + p >= wordLen) {
+                        /*
+                         * splitting
+                         * before
+                         *  abcd - ef
+                         *
+                         * insert "abc"
+                         *
+                         * after
+                         *  abc - d - ef
+                         */
+                        final int newNodeAddress = dictBuffer.limit();
+                        final int flags = BinaryDictEncoderUtils.makePtNodeFlags(p > 1,
+                                isTerminal, 0, hasShortcuts, hasBigrams, false /* isNotAWord */,
+                                false /* isBlackListEntry */, fileHeader.mFormatOptions);
+                        int written = movePtNode(newNodeAddress, currentInfo.mCharacters, p, flags,
+                                frequency, nodeParentAddress, shortcuts, bigrams, destination,
+                                dictBuffer, nodeOriginAddress, address, fileHeader.mFormatOptions);
+
+                        final int[] characters2 = Arrays.copyOfRange(currentInfo.mCharacters, p,
+                                currentInfo.mCharacters.length);
+                        if (currentInfo.mChildrenAddress != FormatSpec.NO_CHILDREN_ADDRESS) {
+                            updateParentAddresses(dictBuffer, currentInfo.mChildrenAddress,
+                                    newNodeAddress + written + 1, fileHeader.mFormatOptions);
+                        }
+                        final PtNodeInfo newInfo2 = new PtNodeInfo(
+                                newNodeAddress + written + 1, -1 /* endAddress */,
+                                currentInfo.mFlags, characters2, currentInfo.mFrequency,
+                                newNodeAddress + 1, currentInfo.mChildrenAddress,
+                                currentInfo.mShortcutTargets, currentInfo.mBigrams);
+                        BinaryDictIOUtils.writeNodes(destination, new PtNodeInfo[] { newInfo2 });
+                        return;
+                    } else if (codePoints[wordPos + p] != currentInfo.mCharacters[p]) {
+                        if (p > 0) {
+                            /*
+                             * splitting
+                             * before
+                             *   ab - cd
+                             *
+                             * insert "ac"
+                             *
+                             * after
+                             *   a - b - cd
+                             *     |
+                             *     - c
+                             */
+
+                            final int newNodeAddress = dictBuffer.limit();
+                            final int childrenAddress = currentInfo.mChildrenAddress;
+
+                            // move prefix
+                            final int prefixFlags = BinaryDictEncoderUtils.makePtNodeFlags(p > 1,
+                                    false /* isTerminal */, 0 /* childrenAddressSize*/,
+                                    false /* hasShortcut */, false /* hasBigrams */,
+                                    false /* isNotAWord */, false /* isBlackListEntry */,
+                                    fileHeader.mFormatOptions);
+                            int written = movePtNode(newNodeAddress, currentInfo.mCharacters, p,
+                                    prefixFlags, -1 /* frequency */, nodeParentAddress, null, null,
+                                    destination, dictBuffer, nodeOriginAddress, address,
+                                    fileHeader.mFormatOptions);
+
+                            final int[] suffixCharacters = Arrays.copyOfRange(
+                                    currentInfo.mCharacters, p, currentInfo.mCharacters.length);
+                            if (currentInfo.mChildrenAddress != FormatSpec.NO_CHILDREN_ADDRESS) {
+                                updateParentAddresses(dictBuffer, currentInfo.mChildrenAddress,
+                                        newNodeAddress + written + 1, fileHeader.mFormatOptions);
+                            }
+                            final int suffixFlags = BinaryDictEncoderUtils.makePtNodeFlags(
+                                    suffixCharacters.length > 1,
+                                    (currentInfo.mFlags & FormatSpec.FLAG_IS_TERMINAL) != 0,
+                                    0 /* childrenAddressSize */,
+                                    (currentInfo.mFlags & FormatSpec.FLAG_HAS_SHORTCUT_TARGETS)
+                                            != 0,
+                                    (currentInfo.mFlags & FormatSpec.FLAG_HAS_BIGRAMS) != 0,
+                                    isNotAWord, isBlackListEntry, fileHeader.mFormatOptions);
+                            final PtNodeInfo suffixInfo = new PtNodeInfo(
+                                    newNodeAddress + written + 1, -1 /* endAddress */, suffixFlags,
+                                    suffixCharacters, currentInfo.mFrequency, newNodeAddress + 1,
+                                    currentInfo.mChildrenAddress, currentInfo.mShortcutTargets,
+                                    currentInfo.mBigrams);
+                            written += BinaryDictIOUtils.computePtNodeSize(suffixInfo,
+                                    fileHeader.mFormatOptions) + 1;
+
+                            final int[] newCharacters = Arrays.copyOfRange(codePoints, wordPos + p,
+                                    codePoints.length);
+                            final int flags = BinaryDictEncoderUtils.makePtNodeFlags(
+                                    newCharacters.length > 1, isTerminal,
+                                    0 /* childrenAddressSize */, hasShortcuts, hasBigrams,
+                                    isNotAWord, isBlackListEntry, fileHeader.mFormatOptions);
+                            final PtNodeInfo newInfo = new PtNodeInfo(
+                                    newNodeAddress + written, -1 /* endAddress */, flags,
+                                    newCharacters, frequency, newNodeAddress + 1,
+                                    FormatSpec.NO_CHILDREN_ADDRESS, shortcuts, bigrams);
+                            BinaryDictIOUtils.writeNodes(destination,
+                                    new PtNodeInfo[] { suffixInfo, newInfo });
+                            return;
+                        }
+                        matched = false;
+                        break;
+                    }
+                }
+
+                if (matched) {
+                    if (wordPos + currentInfo.mCharacters.length == wordLen) {
+                        // the word exists in the dictionary.
+                        // only update the PtNode.
+                        final int newNodeAddress = dictBuffer.limit();
+                        final boolean hasMultipleChars = currentInfo.mCharacters.length > 1;
+                        final int flags = BinaryDictEncoderUtils.makePtNodeFlags(hasMultipleChars,
+                                isTerminal, 0 /* childrenAddressSize */, hasShortcuts, hasBigrams,
+                                isNotAWord, isBlackListEntry, fileHeader.mFormatOptions);
+                        final PtNodeInfo newInfo = new PtNodeInfo(newNodeAddress + 1,
+                                -1 /* endAddress */, flags, currentInfo.mCharacters, frequency,
+                                nodeParentAddress, currentInfo.mChildrenAddress, shortcuts,
+                                bigrams);
+                        movePtNode(destination, dictBuffer, newInfo, nodeOriginAddress, address,
+                                fileHeader.mFormatOptions);
+                        return;
+                    }
+                    wordPos += currentInfo.mCharacters.length;
+                    if (currentInfo.mChildrenAddress == FormatSpec.NO_CHILDREN_ADDRESS) {
+                        /*
+                         * found the prefix of the word.
+                         * make new PtNode and link to the PtNode from this PtNode.
+                         *
+                         * before
+                         * ab - cd
+                         *
+                         * insert "abcde"
+                         *
+                         * after
+                         * ab - cd - e
+                         */
+                        final int newNodeArrayAddress = dictBuffer.limit();
+                        updateChildrenAddress(dictBuffer, address, newNodeArrayAddress,
+                                fileHeader.mFormatOptions);
+                        final int newNodeAddress = newNodeArrayAddress + 1;
+                        final boolean hasMultipleChars = (wordLen - wordPos) > 1;
+                        final int flags = BinaryDictEncoderUtils.makePtNodeFlags(hasMultipleChars,
+                                isTerminal, 0 /* childrenAddressSize */, hasShortcuts, hasBigrams,
+                                isNotAWord, isBlackListEntry, fileHeader.mFormatOptions);
+                        final int[] characters = Arrays.copyOfRange(codePoints, wordPos, wordLen);
+                        final PtNodeInfo newInfo = new PtNodeInfo(newNodeAddress, -1, flags,
+                                characters, frequency, address, FormatSpec.NO_CHILDREN_ADDRESS,
+                                shortcuts, bigrams);
+                        BinaryDictIOUtils.writeNodes(destination, new PtNodeInfo[] { newInfo });
+                        return;
+                    }
+                    dictBuffer.position(currentInfo.mChildrenAddress);
+                    foundNextNode = true;
+                    break;
+                }
+            }
+
+            if (foundNextNode) continue;
+
+            // reached the end of the array.
+            final int linkAddressPosition = dictBuffer.position();
+            int nextLink = dictBuffer.readUnsignedInt24();
+            if ((nextLink & FormatSpec.MSB24) != 0) {
+                nextLink = -(nextLink & FormatSpec.SINT24_MAX);
+            }
+            if (nextLink == FormatSpec.NO_FORWARD_LINK_ADDRESS) {
+                /*
+                 * expand this node.
+                 *
+                 * before
+                 * ab - cd
+                 *
+                 * insert "abef"
+                 *
+                 * after
+                 * ab - cd
+                 *    |
+                 *    - ef
+                 */
+
+                // change the forward link address.
+                final int newNodeAddress = dictBuffer.limit();
+                dictBuffer.position(linkAddressPosition);
+                BinaryDictIOUtils.writeSInt24ToBuffer(dictBuffer, newNodeAddress);
+
+                final int[] characters = Arrays.copyOfRange(codePoints, wordPos, wordLen);
+                final int flags = BinaryDictEncoderUtils.makePtNodeFlags(characters.length > 1,
+                        isTerminal, 0 /* childrenAddressSize */, hasShortcuts, hasBigrams,
+                        isNotAWord, isBlackListEntry, fileHeader.mFormatOptions);
+                final PtNodeInfo newInfo = new PtNodeInfo(newNodeAddress + 1,
+                        -1 /* endAddress */, flags, characters, frequency, nodeParentAddress,
+                        FormatSpec.NO_CHILDREN_ADDRESS, shortcuts, bigrams);
+                BinaryDictIOUtils.writeNodes(destination, new PtNodeInfo[]{ newInfo });
+                return;
+            } else {
+                depth--;
+                dictBuffer.position(nextLink);
+            }
+        }
+    }
+}
diff --git a/java/src/com/android/inputmethod/latin/makedict/FormatSpec.java b/java/src/com/android/inputmethod/latin/makedict/FormatSpec.java
index 46266aa..51b89a0 100644
--- a/java/src/com/android/inputmethod/latin/makedict/FormatSpec.java
+++ b/java/src/com/android/inputmethod/latin/makedict/FormatSpec.java
@@ -18,29 +18,66 @@
 
 import com.android.inputmethod.annotations.UsedForTesting;
 import com.android.inputmethod.latin.Constants;
+import com.android.inputmethod.latin.makedict.DictDecoder.DictionaryBufferFactory;
 import com.android.inputmethod.latin.makedict.FusionDictionary.DictionaryOptions;
 
+import java.io.File;
+
 /**
  * Dictionary File Format Specification.
  */
 public final class FormatSpec {
 
     /*
-     * Array of Node(FusionDictionary.Node) layout is as follows:
+     * File header layout is as follows:
      *
-     * g |
-     * r | the number of groups, 1 or 2 bytes.
-     * o | 1 byte = bbbbbbbb match
-     * u |   case 1xxxxxxx => xxxxxxx << 8 + next byte
-     * p |   otherwise => bbbbbbbb
-     * c |
-     * ount
+     * v |
+     * e | MAGIC_NUMBER + version of the file format, 2 bytes.
+     * r |
+     * sion
      *
-     * g |
-     * r | sequence of groups,
-     * o | the layout of each group is described below.
-     * u |
-     * ps
+     * o |
+     * p | not used                                4 bits
+     * t | has bigrams ?                           1 bit, 1 = yes, 0 = no : CONTAINS_BIGRAMS_FLAG
+     * i | FRENCH_LIGATURE_PROCESSING_FLAG
+     * o | supports dynamic updates ?              1 bit, 1 = yes, 0 = no : SUPPORTS_DYNAMIC_UPDATE
+     * n | GERMAN_UMLAUT_PROCESSING_FLAG
+     * f |
+     * lags
+     *
+     * h |
+     * e | size of the file header, 4bytes
+     * a |   including the size of the magic number, the option flags and the header size
+     * d |
+     * ersize
+     *
+     *   | attributes list
+     *
+     * attributes list is:
+     * <key>   = | string of characters at the char format described below, with the terminator used
+     *           | to signal the end of the string.
+     * <value> = | string of characters at the char format described below, with the terminator used
+     *           | to signal the end of the string.
+     * if the size of already read < headersize, goto key.
+     *
+     */
+
+    /*
+     * Node array (FusionDictionary.PtNodeArray) layout is as follows:
+     *
+     * n |
+     * o | the number of PtNodes, 1 or 2 bytes.
+     * d | 1 byte = bbbbbbbb match
+     * e |   case 1xxxxxxx => xxxxxxx << 8 + next byte
+     * c |   otherwise => bbbbbbbb
+     * o |
+     * unt
+     *
+     * n |
+     * o | sequence of PtNodes,
+     * d | the layout of each PtNode is described below.
+     * e |
+     * s
      *
      * f |
      * o | IF SUPPORTS_DYNAMIC_UPDATE (defined in the file header)
@@ -52,19 +89,19 @@
      * linkaddress
      */
 
-    /* Node(CharGroup) layout is as follows:
+    /* Node (FusionDictionary.PtNode) layout is as follows:
      *   | IF !SUPPORTS_DYNAMIC_UPDATE
-     *   |   addressType                         xx     : mask with MASK_GROUP_ADDRESS_TYPE
-     *   |                           2 bits, 00 = no children : FLAG_GROUP_ADDRESS_TYPE_NOADDRESS
-     * f |                                   01 = 1 byte      : FLAG_GROUP_ADDRESS_TYPE_ONEBYTE
-     * l |                                   10 = 2 bytes     : FLAG_GROUP_ADDRESS_TYPE_TWOBYTES
-     * a |                                   11 = 3 bytes     : FLAG_GROUP_ADDRESS_TYPE_THREEBYTES
+     *   |   addressType                    xx               : mask with MASK_CHILDREN_ADDRESS_TYPE
+     *   |                          2 bits, 00 = no children : FLAG_CHILDREN_ADDRESS_TYPE_NOADDRESS
+     * f |                                  01 = 1 byte      : FLAG_CHILDREN_ADDRESS_TYPE_ONEBYTE
+     * l |                                  10 = 2 bytes     : FLAG_CHILDREN_ADDRESS_TYPE_TWOBYTES
+     * a |                                  11 = 3 bytes     : FLAG_CHILDREN_ADDRESS_TYPE_THREEBYTES
      * g | ELSE
-     * s |   is moved ?              2 bits, 11 = no          : FLAG_IS_NOT_MOVED
-     *   |                              This must be the same as FLAG_GROUP_ADDRESS_TYPE_THREEBYTES
-     *   |                                   01 = yes         : FLAG_IS_MOVED
+     * s |   is moved ?             2 bits, 11 = no          : FLAG_IS_NOT_MOVED
+     *   |                            This must be the same as FLAG_CHILDREN_ADDRESS_TYPE_THREEBYTES
+     *   |                                  01 = yes         : FLAG_IS_MOVED
      *   |                        the new address is stored in the same place as the parent address
-     *   |   is deleted?                     10 = yes         : FLAG_IS_DELETED
+     *   |   is deleted?                    10 = yes         : FLAG_IS_DELETED
      *   | has several chars ?         1 bit, 1 = yes, 0 = no   : FLAG_HAS_MULTIPLE_CHARS
      *   | has a terminal ?            1 bit, 1 = yes, 0 = no   : FLAG_IS_TERMINAL
      *   | has shortcut targets ?      1 bit, 1 = yes, 0 = no   : FLAG_HAS_SHORTCUT_TARGETS
@@ -78,11 +115,13 @@
      * e | 1 byte = bbbbbbbb match
      * n |   case 1xxxxxxx => -((0xxxxxxx << 16) + (next byte << 8) + next byte)
      * t |   otherwise => (bbbbbbbb << 16) + (next byte << 8) + next byte
-     * a |
-     * ddress
+     * a | This address is relative to the head of the PtNode.
+     * d | If the node doesn't have a parent, this field is set to 0.
+     * d |
+     * ress
      *
      * c | IF FLAG_HAS_MULTIPLE_CHARS
-     * h |   char, char, char, char    n * (1 or 3 bytes) : use CharGroupInfo for i/o helpers
+     * h |   char, char, char, char    n * (1 or 3 bytes) : use PtNodeInfo for i/o helpers
      * a |   end                       1 byte, = 0
      * r | ELSE
      * s |   char                      1 or 3 bytes
@@ -93,17 +132,23 @@
      * e |   frequency                 1 byte
      * q |
      *
-     * c | IF 00 = FLAG_GROUP_ADDRESS_TYPE_NOADDRESS = addressType
-     * h |   // nothing
-     * i | ELSIF 01 = FLAG_GROUP_ADDRESS_TYPE_ONEBYTE == addressType
-     * l |   children address, 1 byte
-     * d | ELSIF 10 = FLAG_GROUP_ADDRESS_TYPE_TWOBYTES == addressType
-     * r |   children address, 2 bytes
-     * e | ELSE // 11 = FLAG_GROUP_ADDRESS_TYPE_THREEBYTES = addressType
-     * n |   children address, 3 bytes
-     * A | END
-     * d
-     * dress
+     * c | IF SUPPORTS_DYNAMIC_UPDATE
+     * h |   children address, 3 bytes
+     * i |   1 byte = bbbbbbbb match
+     * l |     case 1xxxxxxx => -((0xxxxxxx << 16) + (next byte << 8) + next byte)
+     * d |     otherwise => (bbbbbbbb<<16) + (next byte << 8) + next byte
+     * r |   if this node doesn't have children, this field is set to 0.
+     * e |     (see BinaryDictEncoderUtils#writeVariableSignedAddress)
+     * n | ELSIF 00 = FLAG_CHILDREN_ADDRESS_TYPE_NOADDRESS == addressType
+     * a |   // nothing
+     * d | ELSIF 01 = FLAG_CHILDREN_ADDRESS_TYPE_ONEBYTE == addressType
+     * d |   children address, 1 byte
+     * r | ELSIF 10 = FLAG_CHILDREN_ADDRESS_TYPE_TWOBYTES == addressType
+     * e |   children address, 2 bytes
+     * s | ELSE // 11 = FLAG_CHILDREN_ADDRESS_TYPE_THREEBYTES = addressType
+     * s |   children address, 3 bytes
+     *   | END
+     *   | This address is relative to the position of this field.
      *
      *   | IF FLAG_IS_TERMINAL && FLAG_HAS_SHORTCUT_TARGETS
      *   | shortcut string list
@@ -122,42 +167,43 @@
      * characters which should never happen anyway (and still work, but take 3 bytes).
      *
      * bigram address list is:
-     * <flags> = | hasNext = 1 bit, 1 = yes, 0 = no     : FLAG_ATTRIBUTE_HAS_NEXT
-     *           | addressSign = 1 bit,                 : FLAG_ATTRIBUTE_OFFSET_NEGATIVE
+     * <flags> = | hasNext = 1 bit, 1 = yes, 0 = no     : FLAG_BIGRAM_SHORTCUT_ATTR_HAS_NEXT
+     *           | addressSign = 1 bit,                 : FLAG_BIGRAM_ATTR_OFFSET_NEGATIVE
      *           |                      1 = must take -address, 0 = must take +address
-     *           |                         xx : mask with MASK_ATTRIBUTE_ADDRESS_TYPE
-     *           | addressFormat = 2 bits, 00 = unused  : FLAG_ATTRIBUTE_ADDRESS_TYPE_ONEBYTE
-     *           |                         01 = 1 byte  : FLAG_ATTRIBUTE_ADDRESS_TYPE_ONEBYTE
-     *           |                         10 = 2 bytes : FLAG_ATTRIBUTE_ADDRESS_TYPE_TWOBYTES
-     *           |                         11 = 3 bytes : FLAG_ATTRIBUTE_ADDRESS_TYPE_THREEBYTES
-     *           | 4 bits : frequency         : mask with FLAG_ATTRIBUTE_FREQUENCY
-     * <address> | IF (01 == FLAG_ATTRIBUTE_ADDRESS_TYPE_ONEBYTE == addressFormat)
+     *           |                         xx : mask with MASK_BIGRAM_ATTR_ADDRESS_TYPE
+     *           | addressFormat = 2 bits, 00 = unused  : FLAG_BIGRAM_ATTR_ADDRESS_TYPE_ONEBYTE
+     *           |                         01 = 1 byte  : FLAG_BIGRAM_ATTR_ADDRESS_TYPE_ONEBYTE
+     *           |                         10 = 2 bytes : FLAG_BIGRAM_ATTR_ADDRESS_TYPE_TWOBYTES
+     *           |                         11 = 3 bytes : FLAG_BIGRAM_ATTR_ADDRESS_TYPE_THREEBYTES
+     *           | 4 bits : frequency         : mask with FLAG_BIGRAM_SHORTCUT_ATTR_FREQUENCY
+     * <address> | IF (01 == FLAG_BIGRAM_ATTR_ADDRESS_TYPE_ONEBYTE == addressFormat)
      *           |   read 1 byte, add top 4 bits
-     *           | ELSIF (10 == FLAG_ATTRIBUTE_ADDRESS_TYPE_TWOBYTES == addressFormat)
+     *           | ELSIF (10 == FLAG_BIGRAM_ATTR_ADDRESS_TYPE_TWOBYTES == addressFormat)
      *           |   read 2 bytes, add top 4 bits
-     *           | ELSE // 11 == FLAG_ATTRIBUTE_ADDRESS_TYPE_THREEBYTES == addressFormat
+     *           | ELSE // 11 == FLAG_BIGRAM_ATTR_ADDRESS_TYPE_THREEBYTES == addressFormat
      *           |   read 3 bytes, add top 4 bits
      *           | END
-     *           | if (FLAG_ATTRIBUTE_OFFSET_NEGATIVE) then address = -address
-     * if (FLAG_ATTRIBUTE_HAS_NEXT) goto bigram_and_shortcut_address_list_is
+     *           | if (FLAG_BIGRAM_ATTR_OFFSET_NEGATIVE) then address = -address
+     * if (FLAG_BIGRAM_SHORTCUT_ATTR_HAS_NEXT) goto bigram_and_shortcut_address_list_is
      *
      * shortcut string list is:
-     * <byte size> = GROUP_SHORTCUT_LIST_SIZE_SIZE bytes, big-endian: size of the list, in bytes.
-     * <flags>     = | hasNext = 1 bit, 1 = yes, 0 = no : FLAG_ATTRIBUTE_HAS_NEXT
+     * <byte size> = PTNODE_SHORTCUT_LIST_SIZE_SIZE bytes, big-endian: size of the list, in bytes.
+     * <flags>     = | hasNext = 1 bit, 1 = yes, 0 = no : FLAG_BIGRAM_SHORTCUT_ATTR_HAS_NEXT
      *               | reserved = 3 bits, must be 0
-     *               | 4 bits : frequency : mask with FLAG_ATTRIBUTE_FREQUENCY
+     *               | 4 bits : frequency : mask with FLAG_BIGRAM_SHORTCUT_ATTR_FREQUENCY
      * <shortcut>  = | string of characters at the char format described above, with the terminator
      *               | used to signal the end of the string.
-     * if (FLAG_ATTRIBUTE_HAS_NEXT goto flags
+     * if (FLAG_BIGRAM_SHORTCUT_ATTR_HAS_NEXT goto flags
      */
 
-    static final int VERSION_1_MAGIC_NUMBER = 0x78B1;
-    public static final int VERSION_2_MAGIC_NUMBER = 0x9BC13AFE;
-    static final int MINIMUM_SUPPORTED_VERSION = 1;
-    static final int MAXIMUM_SUPPORTED_VERSION = 3;
+    public static final int MAGIC_NUMBER = 0x9BC13AFE;
+    static final int MINIMUM_SUPPORTED_VERSION = 2;
+    static final int MAXIMUM_SUPPORTED_VERSION = 4;
     static final int NOT_A_VERSION_NUMBER = -1;
-    static final int FIRST_VERSION_WITH_HEADER_SIZE = 2;
     static final int FIRST_VERSION_WITH_DYNAMIC_UPDATE = 3;
+    static final int FIRST_VERSION_WITH_TERMINAL_ID = 4;
+    static final int VERSION3 = 3;
+    static final int VERSION4 = 4;
 
     // These options need to be the same numeric values as the one in the native reading code.
     static final int GERMAN_UMLAUT_PROCESSING_FLAG = 0x1;
@@ -174,11 +220,11 @@
     static final int FORWARD_LINK_ADDRESS_SIZE = 3;
 
     // These flags are used only in the static dictionary.
-    static final int MASK_GROUP_ADDRESS_TYPE = 0xC0;
-    static final int FLAG_GROUP_ADDRESS_TYPE_NOADDRESS = 0x00;
-    static final int FLAG_GROUP_ADDRESS_TYPE_ONEBYTE = 0x40;
-    static final int FLAG_GROUP_ADDRESS_TYPE_TWOBYTES = 0x80;
-    static final int FLAG_GROUP_ADDRESS_TYPE_THREEBYTES = 0xC0;
+    static final int MASK_CHILDREN_ADDRESS_TYPE = 0xC0;
+    static final int FLAG_CHILDREN_ADDRESS_TYPE_NOADDRESS = 0x00;
+    static final int FLAG_CHILDREN_ADDRESS_TYPE_ONEBYTE = 0x40;
+    static final int FLAG_CHILDREN_ADDRESS_TYPE_TWOBYTES = 0x80;
+    static final int FLAG_CHILDREN_ADDRESS_TYPE_THREEBYTES = 0xC0;
 
     static final int FLAG_HAS_MULTIPLE_CHARS = 0x20;
 
@@ -195,32 +241,39 @@
     static final int FLAG_IS_NOT_MOVED = 0x80 | FIXED_BIT_OF_DYNAMIC_UPDATE_MOVE;
     static final int FLAG_IS_DELETED = 0x80;
 
-    static final int FLAG_ATTRIBUTE_HAS_NEXT = 0x80;
-    static final int FLAG_ATTRIBUTE_OFFSET_NEGATIVE = 0x40;
-    static final int MASK_ATTRIBUTE_ADDRESS_TYPE = 0x30;
-    static final int FLAG_ATTRIBUTE_ADDRESS_TYPE_ONEBYTE = 0x10;
-    static final int FLAG_ATTRIBUTE_ADDRESS_TYPE_TWOBYTES = 0x20;
-    static final int FLAG_ATTRIBUTE_ADDRESS_TYPE_THREEBYTES = 0x30;
-    static final int FLAG_ATTRIBUTE_FREQUENCY = 0x0F;
+    static final int FLAG_BIGRAM_SHORTCUT_ATTR_HAS_NEXT = 0x80;
+    static final int FLAG_BIGRAM_ATTR_OFFSET_NEGATIVE = 0x40;
+    static final int MASK_BIGRAM_ATTR_ADDRESS_TYPE = 0x30;
+    static final int FLAG_BIGRAM_ATTR_ADDRESS_TYPE_ONEBYTE = 0x10;
+    static final int FLAG_BIGRAM_ATTR_ADDRESS_TYPE_TWOBYTES = 0x20;
+    static final int FLAG_BIGRAM_ATTR_ADDRESS_TYPE_THREEBYTES = 0x30;
+    static final int FLAG_BIGRAM_SHORTCUT_ATTR_FREQUENCY = 0x0F;
 
-    static final int GROUP_CHARACTERS_TERMINATOR = 0x1F;
+    static final int PTNODE_CHARACTERS_TERMINATOR = 0x1F;
 
-    static final int GROUP_TERMINATOR_SIZE = 1;
-    static final int GROUP_FLAGS_SIZE = 1;
-    static final int GROUP_FREQUENCY_SIZE = 1;
-    static final int GROUP_MAX_ADDRESS_SIZE = 3;
-    static final int GROUP_ATTRIBUTE_FLAGS_SIZE = 1;
-    static final int GROUP_ATTRIBUTE_MAX_ADDRESS_SIZE = 3;
-    static final int GROUP_SHORTCUT_LIST_SIZE_SIZE = 2;
+    static final int PTNODE_TERMINATOR_SIZE = 1;
+    static final int PTNODE_FLAGS_SIZE = 1;
+    static final int PTNODE_FREQUENCY_SIZE = 1;
+    static final int PTNODE_TERMINAL_ID_SIZE = 4;
+    static final int PTNODE_MAX_ADDRESS_SIZE = 3;
+    static final int PTNODE_ATTRIBUTE_FLAGS_SIZE = 1;
+    static final int PTNODE_ATTRIBUTE_MAX_ADDRESS_SIZE = 3;
+    static final int PTNODE_SHORTCUT_LIST_SIZE_SIZE = 2;
+
+    // These values are used only by version 4 or later.
+    static final String TRIE_FILE_EXTENSION = ".trie";
+    static final String FREQ_FILE_EXTENSION = ".freq";
+    static final int FREQUENCY_AND_FLAGS_SIZE = 2;
 
     static final int NO_CHILDREN_ADDRESS = Integer.MIN_VALUE;
     static final int NO_PARENT_ADDRESS = 0;
     static final int NO_FORWARD_LINK_ADDRESS = 0;
     static final int INVALID_CHARACTER = -1;
 
-    static final int MAX_CHARGROUPS_FOR_ONE_BYTE_CHARGROUP_COUNT = 0x7F; // 127
-    static final int MAX_CHARGROUPS_IN_A_NODE = 0x7FFF; // 32767
-    static final int MAX_BIGRAMS_IN_A_GROUP = 10000;
+    static final int MAX_PTNODES_FOR_ONE_BYTE_PTNODE_COUNT = 0x7F; // 127
+    static final int MAX_PTNODES_IN_A_PT_NODE_ARRAY = 0x7FFF; // 32767
+    static final int MAX_BIGRAMS_IN_A_PTNODE = 10000;
+    static final int MAX_SHORTCUT_LIST_SIZE_IN_A_PTNODE = 0xFFFF;
 
     static final int MAX_TERMINAL_FREQUENCY = 255;
     static final int MAX_BIGRAM_FREQUENCY = 15;
@@ -231,12 +284,20 @@
     static final int NOT_VALID_WORD = -99;
     static final int SIGNED_CHILDREN_ADDRESS_SIZE = 3;
 
+    static final int UINT8_MAX = 0xFF;
+    static final int UINT16_MAX = 0xFFFF;
+    static final int UINT24_MAX = 0xFFFFFF;
+    static final int SINT24_MAX = 0x7FFFFF;
+    static final int MSB8 = 0x80;
+    static final int MSB24 = 0x800000;
+
     /**
      * Options about file format.
      */
     public static final class FormatOptions {
         public final int mVersion;
         public final boolean mSupportsDynamicUpdate;
+        public final boolean mHasTerminalId;
         @UsedForTesting
         public FormatOptions(final int version) {
             this(version, false);
@@ -250,6 +311,7 @@
                         + FIRST_VERSION_WITH_DYNAMIC_UPDATE + " and ulterior.");
             }
             mSupportsDynamicUpdate = supportsDynamicUpdate;
+            mHasTerminalId = (version >= FIRST_VERSION_WITH_TERMINAL_ID);
         }
     }
 
@@ -294,6 +356,36 @@
         }
     }
 
+    /**
+     * Returns new dictionary decoder.
+     *
+     * @param dictFile the dictionary file.
+     * @param bufferType The type of buffer, as one of USE_* in DictDecoder.
+     * @return new dictionary decoder if the dictionary file exists, otherwise null.
+     */
+    public static DictDecoder getDictDecoder(final File dictFile, final int bufferType) {
+        if (dictFile.isDirectory()) {
+            return new Ver4DictDecoder(dictFile, bufferType);
+        } else if (dictFile.isFile()) {
+            return new Ver3DictDecoder(dictFile, bufferType);
+        }
+        return null;
+    }
+
+    public static DictDecoder getDictDecoder(final File dictFile,
+            final DictionaryBufferFactory factory) {
+        if (dictFile.isDirectory()) {
+            return new Ver4DictDecoder(dictFile, factory);
+        } else if (dictFile.isFile()) {
+            return new Ver3DictDecoder(dictFile, factory);
+        }
+        return null;
+    }
+
+    public static DictDecoder getDictDecoder(final File dictFile) {
+        return getDictDecoder(dictFile, DictDecoder.USE_READONLY_BYTEBUFFER);
+    }
+
     private FormatSpec() {
         // This utility class is not publicly instantiable.
     }
diff --git a/java/src/com/android/inputmethod/latin/makedict/FusionDictionary.java b/java/src/com/android/inputmethod/latin/makedict/FusionDictionary.java
index 118dc22..be653fe 100644
--- a/java/src/com/android/inputmethod/latin/makedict/FusionDictionary.java
+++ b/java/src/com/android/inputmethod/latin/makedict/FusionDictionary.java
@@ -37,15 +37,15 @@
     private static int CHARACTER_NOT_FOUND_INDEX = -1;
 
     /**
-     * A node of the dictionary, containing several CharGroups.
+     * A node array of the dictionary, containing several PtNodes.
      *
-     * A node is but an ordered array of CharGroups, which essentially contain all the
+     * A PtNodeArray is but an ordered array of PtNodes, which essentially contain all the
      * real information.
      * This class also contains fields to cache size and address, to help with binary
      * generation.
      */
-    public static final class Node {
-        ArrayList<CharGroup> mData;
+    public static final class PtNodeArray {
+        ArrayList<PtNode> mData;
         // To help with binary generation
         int mCachedSize = Integer.MIN_VALUE;
         // mCachedAddressBefore/AfterUpdate are helpers for binary dictionary generation. They
@@ -57,10 +57,10 @@
         int mCachedAddressAfterUpdate = Integer.MIN_VALUE;
         int mCachedParentAddress = 0;
 
-        public Node() {
-            mData = new ArrayList<CharGroup>();
+        public PtNodeArray() {
+            mData = new ArrayList<PtNode>();
         }
-        public Node(ArrayList<CharGroup> data) {
+        public PtNodeArray(ArrayList<PtNode> data) {
             mData = data;
         }
     }
@@ -93,24 +93,26 @@
     }
 
     /**
-     * A group of characters, with a frequency, shortcut targets, bigrams, and children.
+     * PtNode is a group of characters, with a frequency, shortcut targets, bigrams, and children
+     * (Pt means Patricia Trie).
      *
-     * This is the central class of the in-memory representation. A CharGroup is what can
+     * This is the central class of the in-memory representation. A PtNode is what can
      * be seen as a traditional "trie node", except it can hold several characters at the
-     * same time. A CharGroup essentially represents one or several characters in the middle
-     * of the trie trie; as such, it can be a terminal, and it can have children.
-     * In this in-memory representation, whether the CharGroup is a terminal or not is represented
+     * same time. A PtNode essentially represents one or several characters in the middle
+     * of the trie tree; as such, it can be a terminal, and it can have children.
+     * In this in-memory representation, whether the PtNode is a terminal or not is represented
      * in the frequency, where NOT_A_TERMINAL (= -1) means this is not a terminal and any other
      * value is the frequency of this terminal. A terminal may have non-null shortcuts and/or
      * bigrams, but a non-terminal may not. Moreover, children, if present, are null.
      */
-    public static final class CharGroup {
+    public static final class PtNode {
         public static final int NOT_A_TERMINAL = -1;
         final int mChars[];
         ArrayList<WeightedString> mShortcutTargets;
         ArrayList<WeightedString> mBigrams;
         int mFrequency; // NOT_A_TERMINAL == mFrequency indicates this is not a terminal.
-        Node mChildren;
+        int mTerminalId; // NOT_A_TERMINAL == mTerminalId indicates this is not a terminal.
+        PtNodeArray mChildren;
         boolean mIsNotAWord; // Only a shortcut
         boolean mIsBlacklistEntry;
         // mCachedSize and mCachedAddressBefore/AfterUpdate are helpers for binary dictionary
@@ -119,15 +121,16 @@
         // same time. Updating will update the AfterUpdate value, and the code will move them
         // to BeforeUpdate before the next update pass.
         // The update process does not need two versions of mCachedSize.
-        int mCachedSize; // The size, in bytes, of this char group.
-        int mCachedAddressBeforeUpdate; // The address of this char group (before update)
-        int mCachedAddressAfterUpdate; // The address of this char group (after update)
+        int mCachedSize; // The size, in bytes, of this PtNode.
+        int mCachedAddressBeforeUpdate; // The address of this PtNode (before update)
+        int mCachedAddressAfterUpdate; // The address of this PtNode (after update)
 
-        public CharGroup(final int[] chars, final ArrayList<WeightedString> shortcutTargets,
+        public PtNode(final int[] chars, final ArrayList<WeightedString> shortcutTargets,
                 final ArrayList<WeightedString> bigrams, final int frequency,
                 final boolean isNotAWord, final boolean isBlacklistEntry) {
             mChars = chars;
             mFrequency = frequency;
+            mTerminalId = frequency;
             mShortcutTargets = shortcutTargets;
             mBigrams = bigrams;
             mChildren = null;
@@ -135,9 +138,10 @@
             mIsBlacklistEntry = isBlacklistEntry;
         }
 
-        public CharGroup(final int[] chars, final ArrayList<WeightedString> shortcutTargets,
+        public PtNode(final int[] chars, final ArrayList<WeightedString> shortcutTargets,
                 final ArrayList<WeightedString> bigrams, final int frequency,
-                final boolean isNotAWord, final boolean isBlacklistEntry, final Node children) {
+                final boolean isNotAWord, final boolean isBlacklistEntry,
+                final PtNodeArray children) {
             mChars = chars;
             mFrequency = frequency;
             mShortcutTargets = shortcutTargets;
@@ -147,13 +151,17 @@
             mIsBlacklistEntry = isBlacklistEntry;
         }
 
-        public void addChild(CharGroup n) {
+        public void addChild(PtNode n) {
             if (null == mChildren) {
-                mChildren = new Node();
+                mChildren = new PtNodeArray();
             }
             mChildren.mData.add(n);
         }
 
+        public int getTerminalId() {
+            return mTerminalId;
+        }
+
         public boolean isTerminal() {
             return NOT_A_TERMINAL != mFrequency;
         }
@@ -244,7 +252,7 @@
         }
 
         /**
-         * Updates the CharGroup with the given properties. Adds the shortcut and bigram lists to
+         * Updates the PtNode with the given properties. Adds the shortcut and bigram lists to
          * the existing ones if any. Note: unigram, bigram, and shortcut frequencies are only
          * updated if they are higher than the existing ones.
          */
@@ -344,10 +352,10 @@
     }
 
     public final DictionaryOptions mOptions;
-    public final Node mRoot;
+    public final PtNodeArray mRootNodeArray;
 
-    public FusionDictionary(final Node root, final DictionaryOptions options) {
-        mRoot = root;
+    public FusionDictionary(final PtNodeArray rootNodeArray, final DictionaryOptions options) {
+        mRootNodeArray = rootNodeArray;
         mOptions = options;
     }
 
@@ -406,13 +414,13 @@
     }
 
     /**
-     * Sanity check for a node.
+     * Sanity check for a PtNode array.
      *
-     * This method checks that all CharGroups in a node are ordered as expected.
+     * This method checks that all PtNodes in a node array are ordered as expected.
      * If they are, nothing happens. If they aren't, an exception is thrown.
      */
-    private void checkStack(Node node) {
-        ArrayList<CharGroup> stack = node.mData;
+    private void checkStack(PtNodeArray ptNodeArray) {
+        ArrayList<PtNode> stack = ptNodeArray.mData;
         int lastValue = -1;
         for (int i = 0; i < stack.size(); ++i) {
             int currentValue = stack.get(i).mChars[0];
@@ -431,18 +439,18 @@
      * @param frequency the bigram frequency
      */
     public void setBigram(final String word1, final String word2, final int frequency) {
-        CharGroup charGroup = findWordInTree(mRoot, word1);
-        if (charGroup != null) {
-            final CharGroup charGroup2 = findWordInTree(mRoot, word2);
-            if (charGroup2 == null) {
+        PtNode ptNode = findWordInTree(mRootNodeArray, word1);
+        if (ptNode != null) {
+            final PtNode ptNode2 = findWordInTree(mRootNodeArray, word2);
+            if (ptNode2 == null) {
                 add(getCodePoints(word2), 0, null, false /* isNotAWord */,
                         false /* isBlacklistEntry */);
-                // The chargroup for the first word may have moved by the above insertion,
+                // The PtNode for the first word may have moved by the above insertion,
                 // if word1 and word2 share a common stem that happens not to have been
-                // a cutting point until now. In this case, we need to refresh charGroup.
-                charGroup = findWordInTree(mRoot, word1);
+                // a cutting point until now. In this case, we need to refresh ptNode.
+                ptNode = findWordInTree(mRootNodeArray, word1);
             }
-            charGroup.addBigram(word2, frequency);
+            ptNode.addBigram(word2, frequency);
         } else {
             throw new RuntimeException("First word of bigram not found");
         }
@@ -469,92 +477,91 @@
             return;
         }
 
-        Node currentNode = mRoot;
+        PtNodeArray currentNodeArray = mRootNodeArray;
         int charIndex = 0;
 
-        CharGroup currentGroup = null;
+        PtNode currentPtNode = null;
         int differentCharIndex = 0; // Set by the loop to the index of the char that differs
-        int nodeIndex = findIndexOfChar(mRoot, word[charIndex]);
+        int nodeIndex = findIndexOfChar(mRootNodeArray, word[charIndex]);
         while (CHARACTER_NOT_FOUND_INDEX != nodeIndex) {
-            currentGroup = currentNode.mData.get(nodeIndex);
-            differentCharIndex = compareArrays(currentGroup.mChars, word, charIndex);
+            currentPtNode = currentNodeArray.mData.get(nodeIndex);
+            differentCharIndex = compareCharArrays(currentPtNode.mChars, word, charIndex);
             if (ARRAYS_ARE_EQUAL != differentCharIndex
-                    && differentCharIndex < currentGroup.mChars.length) break;
-            if (null == currentGroup.mChildren) break;
-            charIndex += currentGroup.mChars.length;
+                    && differentCharIndex < currentPtNode.mChars.length) break;
+            if (null == currentPtNode.mChildren) break;
+            charIndex += currentPtNode.mChars.length;
             if (charIndex >= word.length) break;
-            currentNode = currentGroup.mChildren;
-            nodeIndex = findIndexOfChar(currentNode, word[charIndex]);
+            currentNodeArray = currentPtNode.mChildren;
+            nodeIndex = findIndexOfChar(currentNodeArray, word[charIndex]);
         }
 
         if (CHARACTER_NOT_FOUND_INDEX == nodeIndex) {
             // No node at this point to accept the word. Create one.
-            final int insertionIndex = findInsertionIndex(currentNode, word[charIndex]);
-            final CharGroup newGroup = new CharGroup(
-                    Arrays.copyOfRange(word, charIndex, word.length),
+            final int insertionIndex = findInsertionIndex(currentNodeArray, word[charIndex]);
+            final PtNode newPtNode = new PtNode(Arrays.copyOfRange(word, charIndex, word.length),
                     shortcutTargets, null /* bigrams */, frequency, isNotAWord, isBlacklistEntry);
-            currentNode.mData.add(insertionIndex, newGroup);
-            if (DBG) checkStack(currentNode);
+            currentNodeArray.mData.add(insertionIndex, newPtNode);
+            if (DBG) checkStack(currentNodeArray);
         } else {
             // There is a word with a common prefix.
-            if (differentCharIndex == currentGroup.mChars.length) {
+            if (differentCharIndex == currentPtNode.mChars.length) {
                 if (charIndex + differentCharIndex >= word.length) {
                     // The new word is a prefix of an existing word, but the node on which it
-                    // should end already exists as is. Since the old CharNode was not a terminal,
+                    // should end already exists as is. Since the old PtNode was not a terminal,
                     // make it one by filling in its frequency and other attributes
-                    currentGroup.update(frequency, shortcutTargets, null, isNotAWord,
+                    currentPtNode.update(frequency, shortcutTargets, null, isNotAWord,
                             isBlacklistEntry);
                 } else {
                     // The new word matches the full old word and extends past it.
                     // We only have to create a new node and add it to the end of this.
-                    final CharGroup newNode = new CharGroup(
+                    final PtNode newNode = new PtNode(
                             Arrays.copyOfRange(word, charIndex + differentCharIndex, word.length),
                                     shortcutTargets, null /* bigrams */, frequency, isNotAWord,
                                     isBlacklistEntry);
-                    currentGroup.mChildren = new Node();
-                    currentGroup.mChildren.mData.add(newNode);
+                    currentPtNode.mChildren = new PtNodeArray();
+                    currentPtNode.mChildren.mData.add(newNode);
                 }
             } else {
                 if (0 == differentCharIndex) {
                     // Exact same word. Update the frequency if higher. This will also add the
                     // new shortcuts to the existing shortcut list if it already exists.
-                    currentGroup.update(frequency, shortcutTargets, null,
-                            currentGroup.mIsNotAWord && isNotAWord,
-                            currentGroup.mIsBlacklistEntry || isBlacklistEntry);
+                    currentPtNode.update(frequency, shortcutTargets, null,
+                            currentPtNode.mIsNotAWord && isNotAWord,
+                            currentPtNode.mIsBlacklistEntry || isBlacklistEntry);
                 } else {
                     // Partial prefix match only. We have to replace the current node with a node
                     // containing the current prefix and create two new ones for the tails.
-                    Node newChildren = new Node();
-                    final CharGroup newOldWord = new CharGroup(
-                            Arrays.copyOfRange(currentGroup.mChars, differentCharIndex,
-                                    currentGroup.mChars.length), currentGroup.mShortcutTargets,
-                            currentGroup.mBigrams, currentGroup.mFrequency,
-                            currentGroup.mIsNotAWord, currentGroup.mIsBlacklistEntry,
-                            currentGroup.mChildren);
+                    PtNodeArray newChildren = new PtNodeArray();
+                    final PtNode newOldWord = new PtNode(
+                            Arrays.copyOfRange(currentPtNode.mChars, differentCharIndex,
+                                    currentPtNode.mChars.length), currentPtNode.mShortcutTargets,
+                            currentPtNode.mBigrams, currentPtNode.mFrequency,
+                            currentPtNode.mIsNotAWord, currentPtNode.mIsBlacklistEntry,
+                            currentPtNode.mChildren);
                     newChildren.mData.add(newOldWord);
 
-                    final CharGroup newParent;
+                    final PtNode newParent;
                     if (charIndex + differentCharIndex >= word.length) {
-                        newParent = new CharGroup(
-                                Arrays.copyOfRange(currentGroup.mChars, 0, differentCharIndex),
+                        newParent = new PtNode(
+                                Arrays.copyOfRange(currentPtNode.mChars, 0, differentCharIndex),
                                 shortcutTargets, null /* bigrams */, frequency,
                                 isNotAWord, isBlacklistEntry, newChildren);
                     } else {
-                        newParent = new CharGroup(
-                                Arrays.copyOfRange(currentGroup.mChars, 0, differentCharIndex),
+                        newParent = new PtNode(
+                                Arrays.copyOfRange(currentPtNode.mChars, 0, differentCharIndex),
                                 null /* shortcutTargets */, null /* bigrams */, -1,
                                 false /* isNotAWord */, false /* isBlacklistEntry */, newChildren);
-                        final CharGroup newWord = new CharGroup(Arrays.copyOfRange(word,
+                        final PtNode newWord = new PtNode(Arrays.copyOfRange(word,
                                 charIndex + differentCharIndex, word.length),
                                 shortcutTargets, null /* bigrams */, frequency,
                                 isNotAWord, isBlacklistEntry);
                         final int addIndex = word[charIndex + differentCharIndex]
-                                > currentGroup.mChars[differentCharIndex] ? 1 : 0;
+                                > currentPtNode.mChars[differentCharIndex] ? 1 : 0;
                         newChildren.mData.add(addIndex, newWord);
                     }
-                    currentNode.mData.set(nodeIndex, newParent);
+                    currentNodeArray.mData.set(nodeIndex, newParent);
                 }
-                if (DBG) checkStack(currentNode);
+                if (DBG) checkStack(currentNodeArray);
             }
         }
     }
@@ -576,7 +583,7 @@
      * @param dstOffset the offset in the right-hand side string.
      * @return the index at which the strings differ, or ARRAYS_ARE_EQUAL = 0 if they don't.
      */
-    private static int compareArrays(final int[] src, final int[] dst, int dstOffset) {
+    private static int compareCharArrays(final int[] src, final int[] dst, int dstOffset) {
         // We do NOT test the first char, because we come from a method that already
         // tested it.
         for (int i = 1; i < src.length; ++i) {
@@ -588,43 +595,43 @@
     }
 
     /**
-     * Helper class that compares and sorts two chargroups according to their
+     * Helper class that compares and sorts two PtNodes according to their
      * first element only. I repeat: ONLY the first element is considered, the rest
      * is ignored.
      * This comparator imposes orderings that are inconsistent with equals.
      */
-    static private final class CharGroupComparator implements java.util.Comparator<CharGroup> {
+    static private final class PtNodeComparator implements java.util.Comparator<PtNode> {
         @Override
-        public int compare(CharGroup c1, CharGroup c2) {
-            if (c1.mChars[0] == c2.mChars[0]) return 0;
-            return c1.mChars[0] < c2.mChars[0] ? -1 : 1;
+        public int compare(PtNode p1, PtNode p2) {
+            if (p1.mChars[0] == p2.mChars[0]) return 0;
+            return p1.mChars[0] < p2.mChars[0] ? -1 : 1;
         }
     }
-    final static private CharGroupComparator CHARGROUP_COMPARATOR = new CharGroupComparator();
+    final static private PtNodeComparator PTNODE_COMPARATOR = new PtNodeComparator();
 
     /**
-     * Finds the insertion index of a character within a node.
+     * Finds the insertion index of a character within a node array.
      */
-    private static int findInsertionIndex(final Node node, int character) {
-        final ArrayList<CharGroup> data = node.mData;
-        final CharGroup reference = new CharGroup(new int[] { character },
+    private static int findInsertionIndex(final PtNodeArray nodeArray, int character) {
+        final ArrayList<PtNode> data = nodeArray.mData;
+        final PtNode reference = new PtNode(new int[] { character },
                 null /* shortcutTargets */, null /* bigrams */, 0, false /* isNotAWord */,
                 false /* isBlacklistEntry */);
-        int result = Collections.binarySearch(data, reference, CHARGROUP_COMPARATOR);
+        int result = Collections.binarySearch(data, reference, PTNODE_COMPARATOR);
         return result >= 0 ? result : -result - 1;
     }
 
     /**
-     * Find the index of a char in a node, if it exists.
+     * Find the index of a char in a node array, if it exists.
      *
-     * @param node the node to search in.
+     * @param nodeArray the node array to search in.
      * @param character the character to search for.
      * @return the position of the character if it's there, or CHARACTER_NOT_FOUND_INDEX = -1 else.
      */
-    private static int findIndexOfChar(final Node node, int character) {
-        final int insertionIndex = findInsertionIndex(node, character);
-        if (node.mData.size() <= insertionIndex) return CHARACTER_NOT_FOUND_INDEX;
-        return character == node.mData.get(insertionIndex).mChars[0] ? insertionIndex
+    private static int findIndexOfChar(final PtNodeArray nodeArray, int character) {
+        final int insertionIndex = findInsertionIndex(nodeArray, character);
+        if (nodeArray.mData.size() <= insertionIndex) return CHARACTER_NOT_FOUND_INDEX;
+        return character == nodeArray.mData.get(insertionIndex).mChars[0] ? insertionIndex
                 : CHARACTER_NOT_FOUND_INDEX;
     }
 
@@ -632,35 +639,37 @@
      * Helper method to find a word in a given branch.
      */
     @SuppressWarnings("unused")
-    public static CharGroup findWordInTree(Node node, final String string) {
+    public static PtNode findWordInTree(PtNodeArray nodeArray, final String string) {
         int index = 0;
         final StringBuilder checker = DBG ? new StringBuilder() : null;
         final int[] codePoints = getCodePoints(string);
 
-        CharGroup currentGroup;
+        PtNode currentPtNode;
         do {
-            int indexOfGroup = findIndexOfChar(node, codePoints[index]);
+            int indexOfGroup = findIndexOfChar(nodeArray, codePoints[index]);
             if (CHARACTER_NOT_FOUND_INDEX == indexOfGroup) return null;
-            currentGroup = node.mData.get(indexOfGroup);
+            currentPtNode = nodeArray.mData.get(indexOfGroup);
 
-            if (codePoints.length - index < currentGroup.mChars.length) return null;
+            if (codePoints.length - index < currentPtNode.mChars.length) return null;
             int newIndex = index;
-            while (newIndex < codePoints.length && newIndex - index < currentGroup.mChars.length) {
-                if (currentGroup.mChars[newIndex - index] != codePoints[newIndex]) return null;
+            while (newIndex < codePoints.length && newIndex - index < currentPtNode.mChars.length) {
+                if (currentPtNode.mChars[newIndex - index] != codePoints[newIndex]) return null;
                 newIndex++;
             }
             index = newIndex;
 
-            if (DBG) checker.append(new String(currentGroup.mChars, 0, currentGroup.mChars.length));
-            if (index < codePoints.length) {
-                node = currentGroup.mChildren;
+            if (DBG) {
+                checker.append(new String(currentPtNode.mChars, 0, currentPtNode.mChars.length));
             }
-        } while (null != node && index < codePoints.length);
+            if (index < codePoints.length) {
+                nodeArray = currentPtNode.mChildren;
+            }
+        } while (null != nodeArray && index < codePoints.length);
 
         if (index < codePoints.length) return null;
-        if (!currentGroup.isTerminal()) return null;
+        if (!currentPtNode.isTerminal()) return null;
         if (DBG && !string.equals(checker.toString())) return null;
-        return currentGroup;
+        return currentPtNode;
     }
 
     /**
@@ -670,22 +679,22 @@
         if (null == s || "".equals(s)) {
             throw new RuntimeException("Can't search for a null or empty string");
         }
-        return null != findWordInTree(mRoot, s);
+        return null != findWordInTree(mRootNodeArray, s);
     }
 
     /**
-     * Recursively count the number of character groups in a given branch of the trie.
+     * Recursively count the number of PtNodes in a given branch of the trie.
      *
-     * @param node the parent node.
-     * @return the number of char groups in all the branch under this node.
+     * @param nodeArray the parent node.
+     * @return the number of PtNodes in all the branch under this node.
      */
-    public static int countCharGroups(final Node node) {
-        final int nodeSize = node.mData.size();
+    public static int countPtNodes(final PtNodeArray nodeArray) {
+        final int nodeSize = nodeArray.mData.size();
         int size = nodeSize;
         for (int i = nodeSize - 1; i >= 0; --i) {
-            CharGroup group = node.mData.get(i);
-            if (null != group.mChildren)
-                size += countCharGroups(group.mChildren);
+            PtNode ptNode = nodeArray.mData.get(i);
+            if (null != ptNode.mChildren)
+                size += countPtNodes(ptNode.mChildren);
         }
         return size;
     }
@@ -693,15 +702,15 @@
     /**
      * Recursively count the number of nodes in a given branch of the trie.
      *
-     * @param node the node to count.
+     * @param nodeArray the node array to count.
      * @return the number of nodes in this branch.
      */
-    public static int countNodes(final Node node) {
+    public static int countNodeArrays(final PtNodeArray nodeArray) {
         int size = 1;
-        for (int i = node.mData.size() - 1; i >= 0; --i) {
-            CharGroup group = node.mData.get(i);
-            if (null != group.mChildren)
-                size += countNodes(group.mChildren);
+        for (int i = nodeArray.mData.size() - 1; i >= 0; --i) {
+            PtNode ptNode = nodeArray.mData.get(i);
+            if (null != ptNode.mChildren)
+                size += countNodeArrays(ptNode.mChildren);
         }
         return size;
     }
@@ -709,12 +718,12 @@
     // Recursively find out whether there are any bigrams.
     // This can be pretty expensive especially if there aren't any (we return as soon
     // as we find one, so it's much cheaper if there are bigrams)
-    private static boolean hasBigramsInternal(final Node node) {
-        if (null == node) return false;
-        for (int i = node.mData.size() - 1; i >= 0; --i) {
-            CharGroup group = node.mData.get(i);
-            if (null != group.mBigrams) return true;
-            if (hasBigramsInternal(group.mChildren)) return true;
+    private static boolean hasBigramsInternal(final PtNodeArray nodeArray) {
+        if (null == nodeArray) return false;
+        for (int i = nodeArray.mData.size() - 1; i >= 0; --i) {
+            PtNode ptNode = nodeArray.mData.get(i);
+            if (null != ptNode.mBigrams) return true;
+            if (hasBigramsInternal(ptNode.mChildren)) return true;
         }
         return false;
     }
@@ -729,7 +738,7 @@
     // find a more efficient way of doing this, without compromising too much on memory
     // and ease of use.
     public boolean hasBigrams() {
-        return hasBigramsInternal(mRoot);
+        return hasBigramsInternal(mRootNodeArray);
     }
 
     // Historically, the tails of the words were going to be merged to save space.
@@ -747,16 +756,16 @@
         MakedictLog.i("Do not merge tails");
         return;
 
-//        MakedictLog.i("Merging nodes. Number of nodes : " + countNodes(root));
-//        MakedictLog.i("Number of groups : " + countCharGroups(root));
+//        MakedictLog.i("Merging PtNodes. Number of PtNodes : " + countPtNodes(root));
+//        MakedictLog.i("Number of PtNodes : " + countPtNodes(root));
 //
-//        final HashMap<String, ArrayList<Node>> repository =
-//                  new HashMap<String, ArrayList<Node>>();
+//        final HashMap<String, ArrayList<PtNodeArray>> repository =
+//                  new HashMap<String, ArrayList<PtNodeArray>>();
 //        mergeTailsInner(repository, root);
 //
 //        MakedictLog.i("Number of different pseudohashes : " + repository.size());
 //        int size = 0;
-//        for (ArrayList<Node> a : repository.values()) {
+//        for (ArrayList<PtNodeArray> a : repository.values()) {
 //            size += a.size();
 //        }
 //        MakedictLog.i("Number of nodes after merge : " + (1 + size));
@@ -764,58 +773,58 @@
     }
 
     // The following methods are used by the deactivated mergeTails()
-//   private static boolean isEqual(Node a, Node b) {
+//   private static boolean isEqual(PtNodeArray a, PtNodeArray b) {
 //       if (null == a && null == b) return true;
 //       if (null == a || null == b) return false;
 //       if (a.data.size() != b.data.size()) return false;
 //       final int size = a.data.size();
 //       for (int i = size - 1; i >= 0; --i) {
-//           CharGroup aGroup = a.data.get(i);
-//           CharGroup bGroup = b.data.get(i);
-//           if (aGroup.frequency != bGroup.frequency) return false;
-//           if (aGroup.alternates == null && bGroup.alternates != null) return false;
-//           if (aGroup.alternates != null && !aGroup.equals(bGroup.alternates)) return false;
-//           if (!Arrays.equals(aGroup.chars, bGroup.chars)) return false;
-//           if (!isEqual(aGroup.children, bGroup.children)) return false;
+//           PtNode aPtNode = a.data.get(i);
+//           PtNode bPtNode = b.data.get(i);
+//           if (aPtNode.frequency != bPtNode.frequency) return false;
+//           if (aPtNode.alternates == null && bPtNode.alternates != null) return false;
+//           if (aPtNode.alternates != null && !aPtNode.equals(bPtNode.alternates)) return false;
+//           if (!Arrays.equals(aPtNode.chars, bPtNode.chars)) return false;
+//           if (!isEqual(aPtNode.children, bPtNode.children)) return false;
 //       }
 //       return true;
 //   }
 
-//   static private HashMap<String, ArrayList<Node>> mergeTailsInner(
-//           final HashMap<String, ArrayList<Node>> map, final Node node) {
-//       final ArrayList<CharGroup> branches = node.data;
+//   static private HashMap<String, ArrayList<PtNodeArray>> mergeTailsInner(
+//           final HashMap<String, ArrayList<PtNodeArray>> map, final PtNodeArray nodeArray) {
+//       final ArrayList<PtNode> branches = nodeArray.data;
 //       final int nodeSize = branches.size();
 //       for (int i = 0; i < nodeSize; ++i) {
-//           CharGroup group = branches.get(i);
-//           if (null != group.children) {
-//               String pseudoHash = getPseudoHash(group.children);
-//               ArrayList<Node> similarList = map.get(pseudoHash);
+//           PtNode ptNode = branches.get(i);
+//           if (null != ptNode.children) {
+//               String pseudoHash = getPseudoHash(ptNode.children);
+//               ArrayList<PtNodeArray> similarList = map.get(pseudoHash);
 //               if (null == similarList) {
-//                   similarList = new ArrayList<Node>();
+//                   similarList = new ArrayList<PtNodeArray>();
 //                   map.put(pseudoHash, similarList);
 //               }
 //               boolean merged = false;
-//               for (Node similar : similarList) {
-//                   if (isEqual(group.children, similar)) {
-//                       group.children = similar;
+//               for (PtNodeArray similar : similarList) {
+//                   if (isEqual(ptNode.children, similar)) {
+//                       ptNode.children = similar;
 //                       merged = true;
 //                       break;
 //                   }
 //               }
 //               if (!merged) {
-//                   similarList.add(group.children);
+//                   similarList.add(ptNode.children);
 //               }
-//               mergeTailsInner(map, group.children);
+//               mergeTailsInner(map, ptNode.children);
 //           }
 //       }
 //       return map;
 //   }
 
-//  private static String getPseudoHash(final Node node) {
+//  private static String getPseudoHash(final PtNodeArray nodeArray) {
 //      StringBuilder s = new StringBuilder();
-//      for (CharGroup g : node.data) {
-//          s.append(g.frequency);
-//          for (int ch : g.chars) {
+//      for (PtNode ptNode : nodeArray.data) {
+//          s.append(ptNode.frequency);
+//          for (int ch : ptNode.chars) {
 //              s.append(Character.toChars(ch));
 //          }
 //      }
@@ -829,20 +838,20 @@
      */
     public static final class DictionaryIterator implements Iterator<Word> {
         private static final class Position {
-            public Iterator<CharGroup> pos;
+            public Iterator<PtNode> pos;
             public int length;
-            public Position(ArrayList<CharGroup> groups) {
-                pos = groups.iterator();
+            public Position(ArrayList<PtNode> ptNodes) {
+                pos = ptNodes.iterator();
                 length = 0;
             }
         }
         final StringBuilder mCurrentString;
         final LinkedList<Position> mPositions;
 
-        public DictionaryIterator(ArrayList<CharGroup> root) {
+        public DictionaryIterator(ArrayList<PtNode> ptRoot) {
             mCurrentString = new StringBuilder();
             mPositions = new LinkedList<Position>();
-            final Position rootPos = new Position(root);
+            final Position rootPos = new Position(ptRoot);
             mPositions.add(rootPos);
         }
 
@@ -863,20 +872,20 @@
 
             do {
                 if (currentPos.pos.hasNext()) {
-                    final CharGroup currentGroup = currentPos.pos.next();
+                    final PtNode currentPtNode = currentPos.pos.next();
                     currentPos.length = mCurrentString.length();
-                    for (int i : currentGroup.mChars) {
+                    for (int i : currentPtNode.mChars) {
                         mCurrentString.append(Character.toChars(i));
                     }
-                    if (null != currentGroup.mChildren) {
-                        currentPos = new Position(currentGroup.mChildren.mData);
+                    if (null != currentPtNode.mChildren) {
+                        currentPos = new Position(currentPtNode.mChildren.mData);
                         currentPos.length = mCurrentString.length();
                         mPositions.addLast(currentPos);
                     }
-                    if (currentGroup.mFrequency >= 0) {
-                        return new Word(mCurrentString.toString(), currentGroup.mFrequency,
-                                currentGroup.mShortcutTargets, currentGroup.mBigrams,
-                                currentGroup.mIsNotAWord, currentGroup.mIsBlacklistEntry);
+                    if (currentPtNode.mFrequency >= 0) {
+                        return new Word(mCurrentString.toString(), currentPtNode.mFrequency,
+                                currentPtNode.mShortcutTargets, currentPtNode.mBigrams,
+                                currentPtNode.mIsNotAWord, currentPtNode.mIsBlacklistEntry);
                     }
                 } else {
                     mPositions.removeLast();
@@ -901,6 +910,6 @@
      */
     @Override
     public Iterator<Word> iterator() {
-        return new DictionaryIterator(mRoot.mData);
+        return new DictionaryIterator(mRootNodeArray.mData);
     }
 }
diff --git a/java/src/com/android/inputmethod/latin/makedict/CharGroupInfo.java b/java/src/com/android/inputmethod/latin/makedict/PtNodeInfo.java
similarity index 88%
rename from java/src/com/android/inputmethod/latin/makedict/CharGroupInfo.java
rename to java/src/com/android/inputmethod/latin/makedict/PtNodeInfo.java
index b361744..188de7a 100644
--- a/java/src/com/android/inputmethod/latin/makedict/CharGroupInfo.java
+++ b/java/src/com/android/inputmethod/latin/makedict/PtNodeInfo.java
@@ -21,9 +21,9 @@
 import java.util.ArrayList;
 
 /**
- * Raw char group info straight out of a file. This will contain numbers for addresses.
+ * Raw PtNode info straight out of a file. This will contain numbers for addresses.
  */
-public final class CharGroupInfo {
+public final class PtNodeInfo {
 
     public final int mOriginalAddress;
     public final int mEndAddress;
@@ -35,7 +35,7 @@
     public final ArrayList<WeightedString> mShortcutTargets;
     public final ArrayList<PendingAttribute> mBigrams;
 
-    public CharGroupInfo(final int originalAddress, final int endAddress, final int flags,
+    public PtNodeInfo(final int originalAddress, final int endAddress, final int flags,
             final int[] characters, final int frequency, final int parentAddress,
             final int childrenAddress, final ArrayList<WeightedString> shortcutTargets,
             final ArrayList<PendingAttribute> bigrams) {
diff --git a/java/src/com/android/inputmethod/latin/makedict/Ver3DictDecoder.java b/java/src/com/android/inputmethod/latin/makedict/Ver3DictDecoder.java
new file mode 100644
index 0000000..848277c
--- /dev/null
+++ b/java/src/com/android/inputmethod/latin/makedict/Ver3DictDecoder.java
@@ -0,0 +1,234 @@
+/*
+ * Copyright (C) 2013 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.latin.makedict;
+
+import com.android.inputmethod.annotations.UsedForTesting;
+import com.android.inputmethod.latin.makedict.BinaryDictDecoderUtils.CharEncoding;
+import com.android.inputmethod.latin.makedict.BinaryDictDecoderUtils.DictBuffer;
+import com.android.inputmethod.latin.makedict.FormatSpec.FileHeader;
+import com.android.inputmethod.latin.makedict.FormatSpec.FormatOptions;
+import com.android.inputmethod.latin.makedict.FusionDictionary.PtNode;
+import com.android.inputmethod.latin.makedict.FusionDictionary.WeightedString;
+import com.android.inputmethod.latin.utils.JniUtils;
+
+import android.util.Log;
+
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Arrays;
+
+/**
+ * An implementation of DictDecoder for version 3 binary dictionary.
+ */
+@UsedForTesting
+public class Ver3DictDecoder extends DictDecoder {
+    private static final String TAG = Ver3DictDecoder.class.getSimpleName();
+
+    static {
+        JniUtils.loadNativeLibrary();
+    }
+
+    // TODO: implement something sensical instead of just a phony method
+    private static native int doNothing();
+
+    protected static class PtNodeReader extends DictDecoder.PtNodeReader {
+        private static int readFrequency(final DictBuffer dictBuffer) {
+            return dictBuffer.readUnsignedByte();
+        }
+    }
+
+    private final File mDictionaryBinaryFile;
+    private final DictionaryBufferFactory mBufferFactory;
+    private DictBuffer mDictBuffer;
+
+    /* package */ Ver3DictDecoder(final File file, final int factoryFlag) {
+        mDictionaryBinaryFile = file;
+        mDictBuffer = null;
+
+        if ((factoryFlag & MASK_DICTBUFFER) == USE_READONLY_BYTEBUFFER) {
+            mBufferFactory = new DictionaryBufferFromReadOnlyByteBufferFactory();
+        } else if ((factoryFlag  & MASK_DICTBUFFER) == USE_BYTEARRAY) {
+            mBufferFactory = new DictionaryBufferFromByteArrayFactory();
+        } else if ((factoryFlag & MASK_DICTBUFFER) == USE_WRITABLE_BYTEBUFFER) {
+            mBufferFactory = new DictionaryBufferFromWritableByteBufferFactory();
+        } else {
+            mBufferFactory = new DictionaryBufferFromReadOnlyByteBufferFactory();
+        }
+    }
+
+    /* package */ Ver3DictDecoder(final File file, final DictionaryBufferFactory factory) {
+        mDictionaryBinaryFile = file;
+        mBufferFactory = factory;
+    }
+
+    @Override
+    public void openDictBuffer() throws FileNotFoundException, IOException {
+        mDictBuffer = mBufferFactory.getDictionaryBuffer(mDictionaryBinaryFile);
+    }
+
+    @Override
+    public boolean isDictBufferOpen() {
+        return mDictBuffer != null;
+    }
+
+    /* package */ DictBuffer getDictBuffer() {
+        return mDictBuffer;
+    }
+
+    @UsedForTesting
+    /* package */ DictBuffer openAndGetDictBuffer() throws FileNotFoundException, IOException {
+        openDictBuffer();
+        return getDictBuffer();
+    }
+
+    @Override
+    public FileHeader readHeader() throws IOException, UnsupportedFormatException {
+        if (mDictBuffer == null) {
+            openDictBuffer();
+        }
+        final FileHeader header = super.readHeader(mDictBuffer);
+        final int version = header.mFormatOptions.mVersion;
+        if (!(version >= 2 && version <= 3)) {
+          throw new UnsupportedFormatException("File header has a wrong version : " + version);
+        }
+        return header;
+    }
+
+    // TODO: Make this buffer multi thread safe.
+    private final int[] mCharacterBuffer = new int[FormatSpec.MAX_WORD_LENGTH];
+    @Override
+    public PtNodeInfo readPtNode(final int ptNodePos, final FormatOptions options) {
+        int addressPointer = ptNodePos;
+        final int flags = PtNodeReader.readPtNodeOptionFlags(mDictBuffer);
+        addressPointer += FormatSpec.PTNODE_FLAGS_SIZE;
+
+        final int parentAddress = PtNodeReader.readParentAddress(mDictBuffer, options);
+        if (BinaryDictIOUtils.supportsDynamicUpdate(options)) {
+            addressPointer += FormatSpec.PARENT_ADDRESS_SIZE;
+        }
+
+        final int characters[];
+        if (0 != (flags & FormatSpec.FLAG_HAS_MULTIPLE_CHARS)) {
+            int index = 0;
+            int character = CharEncoding.readChar(mDictBuffer);
+            addressPointer += CharEncoding.getCharSize(character);
+            while (FormatSpec.INVALID_CHARACTER != character) {
+                // FusionDictionary is making sure that the length of the word is smaller than
+                // MAX_WORD_LENGTH.
+                // So we'll never write past the end of mCharacterBuffer.
+                mCharacterBuffer[index++] = character;
+                character = CharEncoding.readChar(mDictBuffer);
+                addressPointer += CharEncoding.getCharSize(character);
+            }
+            characters = Arrays.copyOfRange(mCharacterBuffer, 0, index);
+        } else {
+            final int character = CharEncoding.readChar(mDictBuffer);
+            addressPointer += CharEncoding.getCharSize(character);
+            characters = new int[] { character };
+        }
+        final int frequency;
+        if (0 != (FormatSpec.FLAG_IS_TERMINAL & flags)) {
+            frequency = PtNodeReader.readFrequency(mDictBuffer);
+            addressPointer += FormatSpec.PTNODE_FREQUENCY_SIZE;
+        } else {
+            frequency = PtNode.NOT_A_TERMINAL;
+        }
+        int childrenAddress = PtNodeReader.readChildrenAddress(mDictBuffer, flags, options);
+        if (childrenAddress != FormatSpec.NO_CHILDREN_ADDRESS) {
+            childrenAddress += addressPointer;
+        }
+        addressPointer += BinaryDictIOUtils.getChildrenAddressSize(flags, options);
+        final ArrayList<WeightedString> shortcutTargets;
+        if (0 != (flags & FormatSpec.FLAG_HAS_SHORTCUT_TARGETS)) {
+            // readShortcut will add shortcuts to shortcutTargets.
+            shortcutTargets = new ArrayList<WeightedString>();
+            addressPointer += PtNodeReader.readShortcut(mDictBuffer, shortcutTargets);
+        } else {
+            shortcutTargets = null;
+        }
+
+        final ArrayList<PendingAttribute> bigrams;
+        if (0 != (flags & FormatSpec.FLAG_HAS_BIGRAMS)) {
+            bigrams = new ArrayList<PendingAttribute>();
+            addressPointer += PtNodeReader.readBigramAddresses(mDictBuffer, bigrams, 
+                    addressPointer);
+            if (bigrams.size() >= FormatSpec.MAX_BIGRAMS_IN_A_PTNODE) {
+                MakedictLog.d("too many bigrams in a PtNode.");
+            }
+        } else {
+            bigrams = null;
+        }
+        return new PtNodeInfo(ptNodePos, addressPointer, flags, characters, frequency,
+                parentAddress, childrenAddress, shortcutTargets, bigrams);
+    }
+
+    @Override
+    public FusionDictionary readDictionaryBinary(final FusionDictionary dict,
+            final boolean deleteDictIfBroken)
+            throws FileNotFoundException, IOException, UnsupportedFormatException {
+        if (mDictBuffer == null) {
+            openDictBuffer();
+        }
+        try {
+            return BinaryDictDecoderUtils.readDictionaryBinary(this, dict);
+        } catch (IOException e) {
+            Log.e(TAG, "The dictionary " + mDictionaryBinaryFile.getName() + " is broken.", e);
+            if (deleteDictIfBroken && !mDictionaryBinaryFile.delete()) {
+                Log.e(TAG, "Failed to delete the broken dictionary.");
+            }
+            throw e;
+        } catch (UnsupportedFormatException e) {
+            Log.e(TAG, "The dictionary " + mDictionaryBinaryFile.getName() + " is broken.", e);
+            if (deleteDictIfBroken && !mDictionaryBinaryFile.delete()) {
+                Log.e(TAG, "Failed to delete the broken dictionary.");
+            }
+            throw e;
+        }
+    }
+
+    @Override
+    public void setPosition(int newPos) {
+        mDictBuffer.position(newPos);
+    }
+
+    @Override
+    public int getPosition() {
+        return mDictBuffer.position();
+    }
+
+    @Override
+    public int readPtNodeCount() {
+        return BinaryDictDecoderUtils.readPtNodeCount(mDictBuffer);
+    }
+
+    @Override
+    public boolean readAndFollowForwardLink() {
+        final int nextAddress = mDictBuffer.readUnsignedInt24();
+        if (nextAddress >= 0 && nextAddress < mDictBuffer.limit()) {
+            mDictBuffer.position(nextAddress);
+            return true;
+        }
+        return false;
+    }
+
+    @Override
+    public boolean hasNextPtNodeArray() {
+        return mDictBuffer.position() != FormatSpec.NO_FORWARD_LINK_ADDRESS;
+    }
+}
diff --git a/java/src/com/android/inputmethod/latin/makedict/Ver3DictEncoder.java b/java/src/com/android/inputmethod/latin/makedict/Ver3DictEncoder.java
new file mode 100644
index 0000000..76f0f40
--- /dev/null
+++ b/java/src/com/android/inputmethod/latin/makedict/Ver3DictEncoder.java
@@ -0,0 +1,255 @@
+/*
+ * Copyright (C) 2013 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.latin.makedict;
+
+import com.android.inputmethod.latin.makedict.BinaryDictDecoderUtils.CharEncoding;
+import com.android.inputmethod.latin.makedict.FormatSpec.FormatOptions;
+import com.android.inputmethod.latin.makedict.FusionDictionary.PtNode;
+import com.android.inputmethod.latin.makedict.FusionDictionary.PtNodeArray;
+import com.android.inputmethod.latin.makedict.FusionDictionary.WeightedString;
+
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.OutputStream;
+import java.util.ArrayList;
+import java.util.Iterator;
+
+/**
+ * An implementation of DictEncoder for version 3 binary dictionary.
+ */
+public class Ver3DictEncoder implements DictEncoder {
+
+    private final File mDictFile;
+    private OutputStream mOutStream;
+    private byte[] mBuffer;
+    private int mPosition;
+
+    public Ver3DictEncoder(final File dictFile) {
+        mDictFile = dictFile;
+        mOutStream = null;
+        mBuffer = null;
+    }
+
+    // This constructor is used only by BinaryDictOffdeviceUtilsTests.
+    // If you want to use this in the production code, you should consider keeping consistency of
+    // the interface of Ver3DictDecoder by using factory.
+    public Ver3DictEncoder(final OutputStream outStream) {
+        mDictFile = null;
+        mOutStream = outStream;
+    }
+
+    private void openStream() throws FileNotFoundException {
+        mOutStream = new FileOutputStream(mDictFile);
+    }
+
+    private void close() throws IOException {
+        if (mOutStream != null) {
+            mOutStream.close();
+            mOutStream = null;
+        }
+    }
+
+    @Override
+    public void writeDictionary(final FusionDictionary dict, final FormatOptions formatOptions)
+            throws IOException, UnsupportedFormatException {
+        if (formatOptions.mVersion > FormatSpec.VERSION3) {
+            throw new UnsupportedFormatException(
+                    "The given format options has wrong version number : "
+                    + formatOptions.mVersion);
+        }
+
+        if (mOutStream == null) {
+            openStream();
+        }
+        BinaryDictEncoderUtils.writeDictionaryHeader(mOutStream, dict, formatOptions);
+
+        // Addresses are limited to 3 bytes, but since addresses can be relative to each node
+        // array, the structure itself is not limited to 16MB. However, if it is over 16MB deciding
+        // the order of the PtNode arrays becomes a quite complicated problem, because though the
+        // dictionary itself does not have a size limit, each node array must still be within 16MB
+        // of all its children and parents. As long as this is ensured, the dictionary file may
+        // grow to any size.
+
+        // Leave the choice of the optimal node order to the flattenTree function.
+        MakedictLog.i("Flattening the tree...");
+        ArrayList<PtNodeArray> flatNodes = BinaryDictEncoderUtils.flattenTree(dict.mRootNodeArray);
+
+        MakedictLog.i("Computing addresses...");
+        BinaryDictEncoderUtils.computeAddresses(dict, flatNodes, formatOptions);
+        MakedictLog.i("Checking PtNode array...");
+        if (MakedictLog.DBG) BinaryDictEncoderUtils.checkFlatPtNodeArrayList(flatNodes);
+
+        // Create a buffer that matches the final dictionary size.
+        final PtNodeArray lastNodeArray = flatNodes.get(flatNodes.size() - 1);
+        final int bufferSize = lastNodeArray.mCachedAddressAfterUpdate + lastNodeArray.mCachedSize;
+        mBuffer = new byte[bufferSize];
+
+        MakedictLog.i("Writing file...");
+
+        for (PtNodeArray nodeArray : flatNodes) {
+            BinaryDictEncoderUtils.writePlacedPtNodeArray(dict, this, nodeArray, formatOptions);
+        }
+        if (MakedictLog.DBG) BinaryDictEncoderUtils.showStatistics(flatNodes);
+        mOutStream.write(mBuffer, 0, mPosition);
+
+        MakedictLog.i("Done");
+        close();
+    }
+
+    @Override
+    public void setPosition(final int position) {
+        if (mBuffer == null || position < 0 || position >= mBuffer.length) return;
+        mPosition = position;
+    }
+
+    @Override
+    public int getPosition() {
+        return mPosition;
+    }
+
+    @Override
+    public void writePtNodeCount(final int ptNodeCount) {
+        final int countSize = BinaryDictIOUtils.getPtNodeCountSize(ptNodeCount);
+        if (countSize != 1 && countSize != 2) {
+            throw new RuntimeException("Strange size from getGroupCountSize : " + countSize);
+        }
+        mPosition = BinaryDictEncoderUtils.writeUIntToBuffer(mBuffer, mPosition, ptNodeCount,
+                countSize);
+    }
+
+    private void writePtNodeFlags(final PtNode ptNode, final int parentAddress,
+            final FormatOptions formatOptions) {
+        final int childrenPos = BinaryDictEncoderUtils.getChildrenPosition(ptNode, formatOptions);
+        mPosition = BinaryDictEncoderUtils.writeUIntToBuffer(mBuffer, mPosition,
+                BinaryDictEncoderUtils.makePtNodeFlags(ptNode, mPosition, childrenPos,
+                        formatOptions),
+                FormatSpec.PTNODE_FLAGS_SIZE);
+    }
+
+    private void writeParentPosition(final int parentPosition, final PtNode ptNode,
+            final FormatOptions formatOptions) {
+        if (parentPosition == FormatSpec.NO_PARENT_ADDRESS) {
+            mPosition = BinaryDictEncoderUtils.writeParentAddress(mBuffer, mPosition,
+                    parentPosition, formatOptions);
+        } else {
+            mPosition = BinaryDictEncoderUtils.writeParentAddress(mBuffer, mPosition,
+                    parentPosition - ptNode.mCachedAddressAfterUpdate, formatOptions);
+        }
+    }
+
+    private void writeCharacters(final int[] codePoints, final boolean hasSeveralChars) {
+        mPosition = CharEncoding.writeCharArray(codePoints, mBuffer, mPosition);
+        if (hasSeveralChars) {
+            mBuffer[mPosition++] = FormatSpec.PTNODE_CHARACTERS_TERMINATOR;
+        }
+    }
+
+    private void writeFrequency(final int frequency) {
+        if (frequency >= 0) {
+            mPosition = BinaryDictEncoderUtils.writeUIntToBuffer(mBuffer, mPosition, frequency,
+                    FormatSpec.PTNODE_FREQUENCY_SIZE);
+        }
+    }
+
+    private void writeChildrenPosition(final PtNode ptNode, final FormatOptions formatOptions) {
+        final int childrenPos = BinaryDictEncoderUtils.getChildrenPosition(ptNode, formatOptions);
+        if (formatOptions.mSupportsDynamicUpdate) {
+            mPosition += BinaryDictEncoderUtils.writeSignedChildrenPosition(mBuffer, mPosition,
+                    childrenPos);
+        } else {
+            mPosition += BinaryDictEncoderUtils.writeChildrenPosition(mBuffer, mPosition,
+                    childrenPos);
+        }
+    }
+
+    /**
+     * Write a shortcut attributes list to mBuffer.
+     *
+     * @param shortcuts the shortcut attributes list.
+     */
+    private void writeShortcuts(final ArrayList<WeightedString> shortcuts) {
+        if (null == shortcuts || shortcuts.isEmpty()) return;
+
+        final int indexOfShortcutByteSize = mPosition;
+        mPosition += FormatSpec.PTNODE_SHORTCUT_LIST_SIZE_SIZE;
+        final Iterator<WeightedString> shortcutIterator = shortcuts.iterator();
+        while (shortcutIterator.hasNext()) {
+            final WeightedString target = shortcutIterator.next();
+            final int shortcutFlags = BinaryDictEncoderUtils.makeShortcutFlags(
+                    shortcutIterator.hasNext(),
+                    target.mFrequency);
+            mPosition = BinaryDictEncoderUtils.writeUIntToBuffer(mBuffer, mPosition, shortcutFlags,
+                    FormatSpec.PTNODE_ATTRIBUTE_FLAGS_SIZE);
+            final int shortcutShift = CharEncoding.writeString(mBuffer, mPosition, target.mWord);
+            mPosition += shortcutShift;
+        }
+        final int shortcutByteSize = mPosition - indexOfShortcutByteSize;
+        if (shortcutByteSize > FormatSpec.MAX_SHORTCUT_LIST_SIZE_IN_A_PTNODE) {
+            throw new RuntimeException("Shortcut list too large");
+        }
+        BinaryDictEncoderUtils.writeUIntToBuffer(mBuffer, indexOfShortcutByteSize, shortcutByteSize,
+                FormatSpec.PTNODE_SHORTCUT_LIST_SIZE_SIZE);
+    }
+
+    /**
+     * Write a bigram attributes list to mBuffer.
+     *
+     * @param bigrams the bigram attributes list.
+     * @param dict the dictionary the node array is a part of (for relative offsets).
+     */
+    private void writeBigrams(final ArrayList<WeightedString> bigrams,
+            final FusionDictionary dict) {
+        if (bigrams == null) return;
+
+        final Iterator<WeightedString> bigramIterator = bigrams.iterator();
+        while (bigramIterator.hasNext()) {
+            final WeightedString bigram = bigramIterator.next();
+            final PtNode target =
+                    FusionDictionary.findWordInTree(dict.mRootNodeArray, bigram.mWord);
+            final int addressOfBigram = target.mCachedAddressAfterUpdate;
+            final int unigramFrequencyForThisWord = target.mFrequency;
+            final int offset = addressOfBigram
+                    - (mPosition + FormatSpec.PTNODE_ATTRIBUTE_FLAGS_SIZE);
+            final int bigramFlags = BinaryDictEncoderUtils.makeBigramFlags(bigramIterator.hasNext(),
+                    offset, bigram.mFrequency, unigramFrequencyForThisWord, bigram.mWord);
+            mPosition = BinaryDictEncoderUtils.writeUIntToBuffer(mBuffer, mPosition, bigramFlags,
+                    FormatSpec.PTNODE_ATTRIBUTE_FLAGS_SIZE);
+            mPosition += BinaryDictEncoderUtils.writeChildrenPosition(mBuffer, mPosition,
+                    Math.abs(offset));
+        }
+    }
+
+    @Override
+    public void writeForwardLinkAddress(final int forwardLinkAddress) {
+        mPosition = BinaryDictEncoderUtils.writeUIntToBuffer(mBuffer, mPosition, forwardLinkAddress,
+                FormatSpec.FORWARD_LINK_ADDRESS_SIZE);
+    }
+
+    @Override
+    public void writePtNode(final PtNode ptNode, final int parentPosition,
+            final FormatOptions formatOptions, final FusionDictionary dict) {
+        writePtNodeFlags(ptNode, parentPosition, formatOptions);
+        writeParentPosition(parentPosition, ptNode, formatOptions);
+        writeCharacters(ptNode.mChars, ptNode.hasSeveralChars());
+        writeFrequency(ptNode.mFrequency);
+        writeChildrenPosition(ptNode, formatOptions);
+        writeShortcuts(ptNode.mShortcutTargets);
+        writeBigrams(ptNode.mBigrams, dict);
+    }
+}
diff --git a/java/src/com/android/inputmethod/latin/makedict/Ver4DictDecoder.java b/java/src/com/android/inputmethod/latin/makedict/Ver4DictDecoder.java
new file mode 100644
index 0000000..36c5a27
--- /dev/null
+++ b/java/src/com/android/inputmethod/latin/makedict/Ver4DictDecoder.java
@@ -0,0 +1,259 @@
+/*
+ * Copyright (C) 2013 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.latin.makedict;
+
+import com.android.inputmethod.annotations.UsedForTesting;
+import com.android.inputmethod.latin.makedict.BinaryDictDecoderUtils.CharEncoding;
+import com.android.inputmethod.latin.makedict.BinaryDictDecoderUtils.DictBuffer;
+import com.android.inputmethod.latin.makedict.FormatSpec.FileHeader;
+import com.android.inputmethod.latin.makedict.FormatSpec.FormatOptions;
+import com.android.inputmethod.latin.makedict.FusionDictionary.PtNode;
+import com.android.inputmethod.latin.makedict.FusionDictionary.WeightedString;
+
+import android.util.Log;
+
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Arrays;
+
+/**
+ * An implementation of binary dictionary decoder for version 4 binary dictionary.
+ */
+@UsedForTesting
+public class Ver4DictDecoder extends DictDecoder {
+    private static final String TAG = Ver4DictDecoder.class.getSimpleName();
+
+    private static final int FILETYPE_TRIE = 1;
+    private static final int FILETYPE_FREQUENCY = 2;
+
+    private final File mDictDirectory;
+    private final DictionaryBufferFactory mBufferFactory;
+    private DictBuffer mDictBuffer;
+    private DictBuffer mFrequencyBuffer;
+
+    @UsedForTesting
+    /* package */ Ver4DictDecoder(final File dictDirectory, final int factoryFlag) {
+        mDictDirectory = dictDirectory;
+        mDictBuffer = mFrequencyBuffer = null;
+
+        if ((factoryFlag & MASK_DICTBUFFER) == USE_READONLY_BYTEBUFFER) {
+            mBufferFactory = new DictionaryBufferFromReadOnlyByteBufferFactory();
+        } else if ((factoryFlag  & MASK_DICTBUFFER) == USE_BYTEARRAY) {
+            mBufferFactory = new DictionaryBufferFromByteArrayFactory();
+        } else if ((factoryFlag & MASK_DICTBUFFER) == USE_WRITABLE_BYTEBUFFER) {
+            mBufferFactory = new DictionaryBufferFromWritableByteBufferFactory();
+        } else {
+            mBufferFactory = new DictionaryBufferFromReadOnlyByteBufferFactory();
+        }
+    }
+
+    @UsedForTesting
+    /* package */ Ver4DictDecoder(final File dictDirectory, final DictionaryBufferFactory factory) {
+        mDictDirectory = dictDirectory;
+        mBufferFactory = factory;
+        mDictBuffer = mFrequencyBuffer = null;
+    }
+
+    private File getFile(final int fileType) {
+        if (fileType == FILETYPE_TRIE) {
+            return new File(mDictDirectory,
+                    mDictDirectory.getName() + FormatSpec.TRIE_FILE_EXTENSION);
+        } else if (fileType == FILETYPE_FREQUENCY) {
+            return new File(mDictDirectory,
+                    mDictDirectory.getName() + FormatSpec.FREQ_FILE_EXTENSION);
+        } else {
+            throw new RuntimeException("Unsupported kind of file : " + fileType);
+        }
+    }
+
+    @Override
+    public void openDictBuffer() throws FileNotFoundException, IOException {
+        final String filename = mDictDirectory.getName();
+        mDictBuffer = mBufferFactory.getDictionaryBuffer(getFile(FILETYPE_TRIE));
+        mFrequencyBuffer = mBufferFactory.getDictionaryBuffer(getFile(FILETYPE_FREQUENCY));
+    }
+
+    @Override
+    public boolean isDictBufferOpen() {
+        return mDictBuffer != null;
+    }
+
+    /* package */ DictBuffer getDictBuffer() {
+        return mDictBuffer;
+    }
+
+    @Override
+    public FileHeader readHeader() throws IOException, UnsupportedFormatException {
+        if (mDictBuffer == null) {
+            openDictBuffer();
+        }
+        final FileHeader header = super.readHeader(mDictBuffer);
+        final int version = header.mFormatOptions.mVersion;
+        if (version != 4) {
+            throw new UnsupportedFormatException("File header has a wrong version : " + version);
+        }
+        return header;
+    }
+
+    protected static class PtNodeReader extends DictDecoder.PtNodeReader {
+        protected static int readFrequency(final DictBuffer frequencyBuffer, final int terminalId) {
+            frequencyBuffer.position(terminalId * FormatSpec.FREQUENCY_AND_FLAGS_SIZE + 1);
+            return frequencyBuffer.readUnsignedByte();
+        }
+
+        protected static int readTerminalId(final DictBuffer dictBuffer) {
+            return dictBuffer.readInt();
+        }
+    }
+
+    // TODO: Make this buffer thread safe.
+    // TODO: Support words longer than FormatSpec.MAX_WORD_LENGTH.
+    private final int[] mCharacterBuffer = new int[FormatSpec.MAX_WORD_LENGTH];
+    @Override
+    public PtNodeInfo readPtNode(int ptNodePos, FormatOptions options) {
+        int addressPointer = ptNodePos;
+        final int flags = PtNodeReader.readPtNodeOptionFlags(mDictBuffer);
+        addressPointer += FormatSpec.PTNODE_FLAGS_SIZE;
+
+        final int parentAddress = PtNodeReader.readParentAddress(mDictBuffer, options);
+        if (BinaryDictIOUtils.supportsDynamicUpdate(options)) {
+            addressPointer += FormatSpec.PARENT_ADDRESS_SIZE;
+        }
+
+        final int characters[];
+        if (0 != (flags & FormatSpec.FLAG_HAS_MULTIPLE_CHARS)) {
+            int index = 0;
+            int character = CharEncoding.readChar(mDictBuffer);
+            addressPointer += CharEncoding.getCharSize(character);
+            while (FormatSpec.INVALID_CHARACTER != character
+                    && index < FormatSpec.MAX_WORD_LENGTH) {
+                mCharacterBuffer[index++] = character;
+                character = CharEncoding.readChar(mDictBuffer);
+                addressPointer += CharEncoding.getCharSize(character);
+            }
+            characters = Arrays.copyOfRange(mCharacterBuffer, 0, index);
+        } else {
+            final int character = CharEncoding.readChar(mDictBuffer);
+            addressPointer += CharEncoding.getCharSize(character);
+            characters = new int[] { character };
+        }
+        final int terminalId;
+        if (0 != (FormatSpec.FLAG_IS_TERMINAL & flags)) {
+            terminalId = PtNodeReader.readTerminalId(mDictBuffer);
+            addressPointer += FormatSpec.PTNODE_TERMINAL_ID_SIZE;
+        } else {
+            terminalId = PtNode.NOT_A_TERMINAL;
+        }
+
+        final int frequency;
+        if (0 != (FormatSpec.FLAG_IS_TERMINAL & flags)) {
+            frequency = PtNodeReader.readFrequency(mFrequencyBuffer, terminalId);
+        } else {
+            frequency = PtNode.NOT_A_TERMINAL;
+        }
+        int childrenAddress = PtNodeReader.readChildrenAddress(mDictBuffer, flags, options);
+        if (childrenAddress != FormatSpec.NO_CHILDREN_ADDRESS) {
+            childrenAddress += addressPointer;
+        }
+        addressPointer += BinaryDictIOUtils.getChildrenAddressSize(flags, options);
+        final ArrayList<WeightedString> shortcutTargets;
+        if (0 != (flags & FormatSpec.FLAG_HAS_SHORTCUT_TARGETS)) {
+            // readShortcut will add shortcuts to shortcutTargets.
+            shortcutTargets = new ArrayList<WeightedString>();
+            addressPointer += PtNodeReader.readShortcut(mDictBuffer, shortcutTargets);
+        } else {
+            shortcutTargets = null;
+        }
+
+        final ArrayList<PendingAttribute> bigrams;
+        if (0 != (flags & FormatSpec.FLAG_HAS_BIGRAMS)) {
+            bigrams = new ArrayList<PendingAttribute>();
+            addressPointer += PtNodeReader.readBigramAddresses(mDictBuffer, bigrams,
+                    addressPointer);
+            if (bigrams.size() >= FormatSpec.MAX_BIGRAMS_IN_A_PTNODE) {
+                MakedictLog.d("too many bigrams in a node.");
+            }
+        } else {
+            bigrams = null;
+        }
+        return new PtNodeInfo(ptNodePos, addressPointer, flags, characters, frequency,
+                parentAddress, childrenAddress, shortcutTargets, bigrams);
+    }
+
+    private void deleteDictFiles() {
+        final File[] files = mDictDirectory.listFiles();
+        for (int i = 0; i < files.length; ++i) {
+            files[i].delete();
+        }
+    }
+
+    @Override
+    public FusionDictionary readDictionaryBinary(final FusionDictionary dict,
+            final boolean deleteDictIfBroken)
+            throws FileNotFoundException, IOException, UnsupportedFormatException {
+        if (mDictBuffer == null) {
+            openDictBuffer();
+        }
+        try {
+            return BinaryDictDecoderUtils.readDictionaryBinary(this, dict);
+        } catch (IOException e) {
+            Log.e(TAG, "The dictionary " + mDictDirectory.getName() + " is broken.", e);
+            if (deleteDictIfBroken) {
+                deleteDictFiles();
+            }
+            throw e;
+        } catch (UnsupportedFormatException e) {
+            Log.e(TAG, "The dictionary " + mDictDirectory.getName() + " is broken.", e);
+            if (deleteDictIfBroken) {
+                deleteDictFiles();
+            }
+            throw e;
+        }
+    }
+
+    @Override
+    public void setPosition(int newPos) {
+        mDictBuffer.position(newPos);
+    }
+
+    @Override
+    public int getPosition() {
+        return mDictBuffer.position();
+    }
+
+    @Override
+    public int readPtNodeCount() {
+        return BinaryDictDecoderUtils.readPtNodeCount(mDictBuffer);
+    }
+
+    @Override
+    public boolean readAndFollowForwardLink() {
+        final int nextAddress = mDictBuffer.readUnsignedInt24();
+        if (nextAddress >= 0 && nextAddress < mDictBuffer.limit()) {
+            mDictBuffer.position(nextAddress);
+            return true;
+        }
+        return false;
+    }
+
+    @Override
+    public boolean hasNextPtNodeArray() {
+        return mDictBuffer.position() != FormatSpec.NO_FORWARD_LINK_ADDRESS;
+    }
+}
diff --git a/java/src/com/android/inputmethod/latin/makedict/Ver4DictEncoder.java b/java/src/com/android/inputmethod/latin/makedict/Ver4DictEncoder.java
new file mode 100644
index 0000000..75b75ae
--- /dev/null
+++ b/java/src/com/android/inputmethod/latin/makedict/Ver4DictEncoder.java
@@ -0,0 +1,269 @@
+/*
+/*
+ * Copyright (C) 2013 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.latin.makedict;
+
+import com.android.inputmethod.annotations.UsedForTesting;
+import com.android.inputmethod.latin.makedict.BinaryDictDecoderUtils.CharEncoding;
+import com.android.inputmethod.latin.makedict.FormatSpec.FileHeader;
+import com.android.inputmethod.latin.makedict.FormatSpec.FormatOptions;
+import com.android.inputmethod.latin.makedict.FusionDictionary.DictionaryOptions;
+import com.android.inputmethod.latin.makedict.FusionDictionary.PtNode;
+import com.android.inputmethod.latin.makedict.FusionDictionary.PtNodeArray;
+import com.android.inputmethod.latin.makedict.FusionDictionary.WeightedString;
+
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.OutputStream;
+import java.util.ArrayList;
+import java.util.Iterator;
+
+/**
+ * An implementation of DictEncoder for version 4 binary dictionary.
+ */
+@UsedForTesting
+public class Ver4DictEncoder implements DictEncoder {
+    private final File mDictPlacedDir;
+    private byte[] mTrieBuf;
+    private byte[] mFreqBuf;
+    private int mTriePos;
+    private OutputStream mTrieOutStream;
+    private OutputStream mFreqOutStream;
+
+    @UsedForTesting
+    public Ver4DictEncoder(final File dictPlacedDir) {
+        mDictPlacedDir = dictPlacedDir;
+    }
+
+    private void openStreams(final FormatOptions formatOptions, final DictionaryOptions dictOptions)
+            throws FileNotFoundException, IOException {
+        final FileHeader header = new FileHeader(0, dictOptions, formatOptions);
+        final String filename = header.getId() + "." + header.getVersion();
+        final File mDictDir = new File(mDictPlacedDir, filename);
+        final File trieFile = new File(mDictDir, filename + FormatSpec.TRIE_FILE_EXTENSION);
+        final File freqFile = new File(mDictDir, filename + FormatSpec.FREQ_FILE_EXTENSION);
+        if (!mDictDir.isDirectory()) {
+            if (mDictDir.exists()) mDictDir.delete();
+            mDictDir.mkdirs();
+        }
+        if (!trieFile.exists()) trieFile.createNewFile();
+        if (!freqFile.exists()) freqFile.createNewFile();
+        mTrieOutStream = new FileOutputStream(trieFile);
+        mFreqOutStream = new FileOutputStream(freqFile);
+    }
+
+    private void close() throws IOException {
+        try {
+            if (mTrieOutStream != null) {
+                mTrieOutStream.close();
+            }
+            if (mFreqOutStream != null) {
+                mFreqOutStream.close();
+            }
+        } finally {
+            mTrieOutStream = null;
+            mFreqOutStream = null;
+        }
+    }
+
+    @Override
+    public void writeDictionary(final FusionDictionary dict, final FormatOptions formatOptions)
+            throws IOException, UnsupportedFormatException {
+        if (formatOptions.mVersion != FormatSpec.VERSION4) {
+            throw new UnsupportedFormatException("File header has a wrong version number : "
+                    + formatOptions.mVersion);
+        }
+        if (!mDictPlacedDir.isDirectory()) {
+            throw new UnsupportedFormatException("Given path is not a directory.");
+        }
+
+        if (mTrieOutStream == null) {
+            openStreams(formatOptions, dict.mOptions);
+        }
+
+        BinaryDictEncoderUtils.writeDictionaryHeader(mTrieOutStream, dict, formatOptions);
+
+        MakedictLog.i("Flattening the tree...");
+        ArrayList<PtNodeArray> flatNodes = BinaryDictEncoderUtils.flattenTree(dict.mRootNodeArray);
+        int terminalCount = 0;
+        for (final PtNodeArray array : flatNodes) {
+            for (final PtNode node : array.mData) {
+                if (node.isTerminal()) node.mTerminalId = terminalCount++;
+            }
+        }
+
+        MakedictLog.i("Computing addresses...");
+        BinaryDictEncoderUtils.computeAddresses(dict, flatNodes, formatOptions);
+        if (MakedictLog.DBG) BinaryDictEncoderUtils.checkFlatPtNodeArrayList(flatNodes);
+
+        final PtNodeArray lastNodeArray = flatNodes.get(flatNodes.size() - 1);
+        final int bufferSize = lastNodeArray.mCachedAddressAfterUpdate + lastNodeArray.mCachedSize;
+        mTrieBuf = new byte[bufferSize];
+        mFreqBuf = new byte[terminalCount * FormatSpec.FREQUENCY_AND_FLAGS_SIZE];
+
+        MakedictLog.i("Writing file...");
+        for (PtNodeArray nodeArray : flatNodes) {
+            BinaryDictEncoderUtils.writePlacedPtNodeArray(dict, this, nodeArray, formatOptions);
+        }
+        if (MakedictLog.DBG) {
+            BinaryDictEncoderUtils.showStatistics(flatNodes);
+            MakedictLog.i("has " + terminalCount + " terminals.");
+        }
+        mTrieOutStream.write(mTrieBuf);
+        mFreqOutStream.write(mFreqBuf);
+
+        MakedictLog.i("Done");
+        close();
+    }
+
+    @Override
+    public void setPosition(int position) {
+        if (mTrieBuf == null || position < 0 || position >- mTrieBuf.length) return;
+        mTriePos = position;
+    }
+
+    @Override
+    public int getPosition() {
+        return mTriePos;
+    }
+
+    @Override
+    public void writePtNodeCount(int ptNodeCount) {
+        final int countSize = BinaryDictIOUtils.getPtNodeCountSize(ptNodeCount);
+        // ptNodeCount must fit on one byte or two bytes.
+        // Please see comments in FormatSpec
+        if (countSize != 1 && countSize != 2) {
+            throw new RuntimeException("Strange size from getPtNodeCountSize : " + countSize);
+        }
+        mTriePos = BinaryDictEncoderUtils.writeUIntToBuffer(mTrieBuf, mTriePos, ptNodeCount,
+                countSize);
+    }
+
+    private void writePtNodeFlags(final PtNode ptNode, final int parentAddress,
+            final FormatOptions formatOptions) {
+        final int childrenPos = BinaryDictEncoderUtils.getChildrenPosition(ptNode, formatOptions);
+        mTriePos = BinaryDictEncoderUtils.writeUIntToBuffer(mTrieBuf, mTriePos,
+                BinaryDictEncoderUtils.makePtNodeFlags(ptNode, mTriePos, childrenPos,
+                        formatOptions),
+                FormatSpec.PTNODE_FLAGS_SIZE);
+    }
+
+    private void writeParentPosition(int parentPos, final PtNode ptNode,
+            final FormatOptions formatOptions) {
+        if (parentPos != FormatSpec.NO_PARENT_ADDRESS) {
+            parentPos -= ptNode.mCachedAddressAfterUpdate;
+        }
+        mTriePos = BinaryDictEncoderUtils.writeParentAddress(mTrieBuf, mTriePos, parentPos,
+                formatOptions);
+    }
+
+    private void writeCharacters(final int[] characters, final boolean hasSeveralChars) {
+        mTriePos = CharEncoding.writeCharArray(characters, mTrieBuf, mTriePos);
+        if (hasSeveralChars) {
+            mTrieBuf[mTriePos++] = FormatSpec.PTNODE_CHARACTERS_TERMINATOR;
+        }
+    }
+
+    private void writeTerminalId(final int terminalId) {
+        mTriePos = BinaryDictEncoderUtils.writeUIntToBuffer(mTrieBuf, mTriePos, terminalId,
+                FormatSpec.PTNODE_TERMINAL_ID_SIZE);
+    }
+
+    private void writeFrequency(final int frequency, final int terminalId) {
+        final int freqPos = terminalId * FormatSpec.FREQUENCY_AND_FLAGS_SIZE;
+        BinaryDictEncoderUtils.writeUIntToBuffer(mFreqBuf, freqPos, frequency,
+                FormatSpec.FREQUENCY_AND_FLAGS_SIZE);
+    }
+
+    private void writeChildrenPosition(PtNode ptNode, FormatOptions formatOptions) {
+        final int childrenPos = BinaryDictEncoderUtils.getChildrenPosition(ptNode, formatOptions);
+        if (formatOptions.mSupportsDynamicUpdate) {
+            mTriePos += BinaryDictEncoderUtils.writeSignedChildrenPosition(mTrieBuf,
+                    mTriePos, childrenPos);
+        } else {
+            mTriePos += BinaryDictEncoderUtils.writeChildrenPosition(mTrieBuf,
+                    mTriePos, childrenPos);
+        }
+    }
+
+    private void writeShortcuts(ArrayList<WeightedString> shortcuts) {
+        if (null == shortcuts || shortcuts.isEmpty()) return;
+
+        final int indexOfShortcutByteSize = mTriePos;
+        mTriePos += FormatSpec.PTNODE_SHORTCUT_LIST_SIZE_SIZE;
+        final Iterator<WeightedString> shortcutIterator = shortcuts.iterator();
+        while (shortcutIterator.hasNext()) {
+            final WeightedString target = shortcutIterator.next();
+            final int shortcutFlags = BinaryDictEncoderUtils.makeShortcutFlags(
+                    shortcutIterator.hasNext(),
+                    target.mFrequency);
+            mTrieBuf[mTriePos++] = (byte)shortcutFlags;
+            final int shortcutShift = CharEncoding.writeString(mTrieBuf, mTriePos,
+                    target.mWord);
+            mTriePos += shortcutShift;
+        }
+        final int shortcutByteSize = mTriePos - indexOfShortcutByteSize;
+        if (shortcutByteSize > FormatSpec.MAX_SHORTCUT_LIST_SIZE_IN_A_PTNODE) {
+            throw new RuntimeException("Shortcut list too large : " + shortcutByteSize);
+        }
+        BinaryDictEncoderUtils.writeUIntToBuffer(mTrieBuf, indexOfShortcutByteSize,
+                shortcutByteSize, FormatSpec.PTNODE_SHORTCUT_LIST_SIZE_SIZE);
+    }
+
+    private void writeBigrams(ArrayList<WeightedString> bigrams, FusionDictionary dict) {
+        if (bigrams == null) return;
+
+        final Iterator<WeightedString> bigramIterator = bigrams.iterator();
+        while (bigramIterator.hasNext()) {
+            final WeightedString bigram = bigramIterator.next();
+            final PtNode target =
+                    FusionDictionary.findWordInTree(dict.mRootNodeArray, bigram.mWord);
+            final int addressOfBigram = target.mCachedAddressAfterUpdate;
+            final int unigramFrequencyForThisWord = target.mFrequency;
+            final int offset = addressOfBigram
+                    - (mTriePos + FormatSpec.PTNODE_ATTRIBUTE_FLAGS_SIZE);
+            int bigramFlags = BinaryDictEncoderUtils.makeBigramFlags(bigramIterator.hasNext(),
+                    offset, bigram.mFrequency, unigramFrequencyForThisWord, bigram.mWord);
+            mTrieBuf[mTriePos++] = (byte) bigramFlags;
+            mTriePos += BinaryDictEncoderUtils.writeChildrenPosition(mTrieBuf,
+                    mTriePos, Math.abs(offset));
+        }
+    }
+
+    @Override
+    public void writeForwardLinkAddress(int forwardLinkAddress) {
+        mTriePos = BinaryDictEncoderUtils.writeUIntToBuffer(mTrieBuf, mTriePos,
+                forwardLinkAddress, FormatSpec.FORWARD_LINK_ADDRESS_SIZE);
+    }
+
+    @Override
+    public void writePtNode(final PtNode ptNode, final int parentPosition,
+            final FormatOptions formatOptions, final FusionDictionary dict) {
+        writePtNodeFlags(ptNode, parentPosition, formatOptions);
+        writeParentPosition(parentPosition, ptNode, formatOptions);
+        writeCharacters(ptNode.mChars, ptNode.hasSeveralChars());
+        if (ptNode.isTerminal()) {
+            writeTerminalId(ptNode.mTerminalId);
+            writeFrequency(ptNode.mFrequency, ptNode.mTerminalId);
+        }
+        writeChildrenPosition(ptNode, formatOptions);
+        writeShortcuts(ptNode.mShortcutTargets);
+        writeBigrams(ptNode.mBigrams, dict);
+    }
+}
diff --git a/java/src/com/android/inputmethod/latin/personalization/DynamicPersonalizationDictionaryWriter.java b/java/src/com/android/inputmethod/latin/personalization/DynamicPersonalizationDictionaryWriter.java
new file mode 100644
index 0000000..0af028a
--- /dev/null
+++ b/java/src/com/android/inputmethod/latin/personalization/DynamicPersonalizationDictionaryWriter.java
@@ -0,0 +1,183 @@
+/*
+ * Copyright (C) 2013 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.latin.personalization;
+
+import android.content.Context;
+
+import com.android.inputmethod.annotations.UsedForTesting;
+import com.android.inputmethod.compat.ActivityManagerCompatUtils;
+import com.android.inputmethod.keyboard.ProximityInfo;
+import com.android.inputmethod.latin.AbstractDictionaryWriter;
+import com.android.inputmethod.latin.ExpandableDictionary;
+import com.android.inputmethod.latin.WordComposer;
+import com.android.inputmethod.latin.ExpandableDictionary.NextWord;
+import com.android.inputmethod.latin.SuggestedWords.SuggestedWordInfo;
+import com.android.inputmethod.latin.makedict.DictEncoder;
+import com.android.inputmethod.latin.makedict.FormatSpec;
+import com.android.inputmethod.latin.makedict.UnsupportedFormatException;
+import com.android.inputmethod.latin.utils.UserHistoryDictIOUtils;
+import com.android.inputmethod.latin.utils.UserHistoryDictIOUtils.BigramDictionaryInterface;
+import com.android.inputmethod.latin.utils.UserHistoryForgettingCurveUtils;
+import com.android.inputmethod.latin.utils.UserHistoryForgettingCurveUtils.ForgettingCurveParams;
+
+import java.io.IOException;
+import java.util.ArrayList;
+
+// Currently this class is used to implement dynamic prodiction dictionary.
+// TODO: Move to native code.
+public class DynamicPersonalizationDictionaryWriter extends AbstractDictionaryWriter {
+    private static final String TAG = DynamicPersonalizationDictionaryWriter.class.getSimpleName();
+    /** Maximum number of pairs. Pruning will start when databases goes above this number. */
+    public static final int DEFAULT_MAX_HISTORY_BIGRAMS = 10000;
+    public static final int LOW_MEMORY_MAX_HISTORY_BIGRAMS = 2000;
+
+    /** Any pair being typed or picked */
+    private static final int FREQUENCY_FOR_TYPED = 2;
+
+    private static final int BINARY_DICT_VERSION = 3;
+    private static final FormatSpec.FormatOptions FORMAT_OPTIONS =
+            new FormatSpec.FormatOptions(BINARY_DICT_VERSION, true /* supportsDynamicUpdate */);
+
+    private final UserHistoryDictionaryBigramList mBigramList =
+            new UserHistoryDictionaryBigramList();
+    private final ExpandableDictionary mExpandableDictionary;
+    private final int mMaxHistoryBigrams;
+
+    public DynamicPersonalizationDictionaryWriter(final Context context, final String dictType) {
+        super(context, dictType);
+        mExpandableDictionary = new ExpandableDictionary(dictType);
+        final boolean isLowRamDevice = ActivityManagerCompatUtils.isLowRamDevice(context);
+        mMaxHistoryBigrams = isLowRamDevice ?
+                LOW_MEMORY_MAX_HISTORY_BIGRAMS : DEFAULT_MAX_HISTORY_BIGRAMS;
+    }
+
+    @Override
+    public void clear() {
+        mBigramList.evictAll();
+        mExpandableDictionary.clearDictionary();
+    }
+
+    /**
+     * Adds a word unigram to the fusion dictionary. Call updateBinaryDictionary when all changes
+     * are done to update the binary dictionary.
+     */
+    @Override
+    public void addUnigramWord(final String word, final String shortcutTarget, final int frequency,
+            final boolean isNotAWord) {
+        if (mBigramList.size() > mMaxHistoryBigrams * 2) {
+            // Too many entries: just stop adding new vocabrary and wait next refresh.
+            return;
+        }
+        mExpandableDictionary.addWord(word, shortcutTarget, frequency);
+        mBigramList.addBigram(null, word, (byte)frequency);
+    }
+
+    @Override
+    public void addBigramWords(final String word0, final String word1, final int frequency,
+            final boolean isValid, final long lastModifiedTime) {
+        if (mBigramList.size() > mMaxHistoryBigrams * 2) {
+            // Too many entries: just stop adding new vocabrary and wait next refresh.
+            return;
+        }
+        if (lastModifiedTime > 0) {
+            mExpandableDictionary.setBigramAndGetFrequency(word0, word1,
+                    new ForgettingCurveParams(frequency, System.currentTimeMillis(),
+                            lastModifiedTime));
+            mBigramList.addBigram(word0, word1, (byte)frequency);
+        } else {
+            mExpandableDictionary.setBigramAndGetFrequency(word0, word1,
+                    new ForgettingCurveParams(isValid));
+            mBigramList.addBigram(word0, word1, (byte)frequency);
+        }
+    }
+
+    @Override
+    public void removeBigramWords(final String word0, final String word1) {
+        if (mBigramList.removeBigram(word0, word1)) {
+            mExpandableDictionary.removeBigram(word0, word1);
+        }
+    }
+
+    @Override
+    protected void writeDictionary(final DictEncoder dictEncoder)
+            throws IOException, UnsupportedFormatException {
+        UserHistoryDictIOUtils.writeDictionary(dictEncoder,
+                new FrequencyProvider(mBigramList, mExpandableDictionary, mMaxHistoryBigrams),
+                mBigramList, FORMAT_OPTIONS);
+    }
+
+    private static class FrequencyProvider implements BigramDictionaryInterface {
+        private final UserHistoryDictionaryBigramList mBigramList;
+        private final ExpandableDictionary mExpandableDictionary;
+        private final int mMaxHistoryBigrams;
+
+        public FrequencyProvider(final UserHistoryDictionaryBigramList bigramList,
+                final ExpandableDictionary expandableDictionary, final int maxHistoryBigrams) {
+            mBigramList = bigramList;
+            mExpandableDictionary = expandableDictionary;
+            mMaxHistoryBigrams = maxHistoryBigrams;
+        }
+
+        @Override
+        public int getFrequency(final String word0, final String word1) {
+            final int freq;
+            if (word0 == null) { // unigram
+                freq = FREQUENCY_FOR_TYPED;
+            } else { // bigram
+                final NextWord nw = mExpandableDictionary.getBigramWord(word0, word1);
+                if (nw != null) {
+                    final ForgettingCurveParams forgettingCurveParams = nw.getFcParams();
+                    final byte prevFc = mBigramList.getBigrams(word0).get(word1);
+                    final byte fc = forgettingCurveParams.getFc();
+                    final boolean isValid = forgettingCurveParams.isValid();
+                    if (prevFc > 0 && prevFc == fc) {
+                        freq = fc & 0xFF;
+                    } else if (UserHistoryForgettingCurveUtils.
+                            needsToSave(fc, isValid, mBigramList.size() <= mMaxHistoryBigrams)) {
+                        freq = fc & 0xFF;
+                    } else {
+                        // Delete this entry
+                        freq = -1;
+                    }
+                } else {
+                    // Delete this entry
+                    freq = -1;
+                }
+            }
+            return freq;
+        }
+    }
+
+    @Override
+    public ArrayList<SuggestedWordInfo> getSuggestions(final WordComposer composer,
+            final String prevWord, final ProximityInfo proximityInfo,
+            boolean blockOffensiveWords, final int[] additionalFeaturesOptions) {
+        return mExpandableDictionary.getSuggestions(composer, prevWord, proximityInfo,
+                blockOffensiveWords, additionalFeaturesOptions);
+    }
+
+    @Override
+    public boolean isValidWord(final String word) {
+        return mExpandableDictionary.isValidWord(word);
+    }
+
+    @UsedForTesting
+    public boolean isInDictionaryForTests(final String word) {
+        // TODO: Use native method to determine whether the word is in dictionary or not
+        return mBigramList.containsKey(word);
+    }
+}
diff --git a/java/src/com/android/inputmethod/latin/personalization/DynamicPredictionDictionaryBase.java b/java/src/com/android/inputmethod/latin/personalization/DynamicPredictionDictionaryBase.java
index bb6ec6b..075d7e3 100644
--- a/java/src/com/android/inputmethod/latin/personalization/DynamicPredictionDictionaryBase.java
+++ b/java/src/com/android/inputmethod/latin/personalization/DynamicPredictionDictionaryBase.java
@@ -18,59 +18,40 @@
 
 import android.content.Context;
 import android.content.SharedPreferences;
-import android.os.AsyncTask;
 import android.util.Log;
 
 import com.android.inputmethod.annotations.UsedForTesting;
-import com.android.inputmethod.keyboard.ProximityInfo;
 import com.android.inputmethod.latin.Constants;
-import com.android.inputmethod.latin.ExpandableDictionary;
+import com.android.inputmethod.latin.ExpandableBinaryDictionary;
 import com.android.inputmethod.latin.LatinImeLogger;
-import com.android.inputmethod.latin.SuggestedWords.SuggestedWordInfo;
-import com.android.inputmethod.latin.WordComposer;
-import com.android.inputmethod.latin.makedict.FormatSpec.FormatOptions;
+import com.android.inputmethod.latin.makedict.DictDecoder;
+import com.android.inputmethod.latin.makedict.FormatSpec;
 import com.android.inputmethod.latin.settings.Settings;
-import com.android.inputmethod.latin.utils.ByteArrayWrapper;
 import com.android.inputmethod.latin.utils.CollectionUtils;
 import com.android.inputmethod.latin.utils.UserHistoryDictIOUtils;
-import com.android.inputmethod.latin.utils.UserHistoryDictIOUtils.BigramDictionaryInterface;
 import com.android.inputmethod.latin.utils.UserHistoryDictIOUtils.OnAddWordListener;
-import com.android.inputmethod.latin.utils.UserHistoryForgettingCurveUtils;
-import com.android.inputmethod.latin.utils.UserHistoryForgettingCurveUtils.ForgettingCurveParams;
 
 import java.io.File;
-import java.io.FileInputStream;
-import java.io.FileNotFoundException;
-import java.io.FileOutputStream;
 import java.io.IOException;
 import java.util.ArrayList;
-import java.util.concurrent.locks.ReentrantLock;
 
 /**
  * This class is a base class of a dictionary for the personalized prediction language model.
  */
-public abstract class DynamicPredictionDictionaryBase extends ExpandableDictionary {
-
+public abstract class DynamicPredictionDictionaryBase extends ExpandableBinaryDictionary {
     private static final String TAG = DynamicPredictionDictionaryBase.class.getSimpleName();
     public static final boolean DBG_SAVE_RESTORE = false;
     private static final boolean DBG_STRESS_TEST = false;
     private static final boolean PROFILE_SAVE_RESTORE = LatinImeLogger.sDBG;
 
-    private static final FormatOptions VERSION3 = new FormatOptions(3,
-            true /* supportsDynamicUpdate */);
-
     /** Any pair being typed or picked */
-    private static final int FREQUENCY_FOR_TYPED = 2;
-
-    /** Maximum number of pairs. Pruning will start when databases goes above this number. */
-    private static final int MAX_HISTORY_BIGRAMS = 10000;
+    public static final int FREQUENCY_FOR_TYPED = 2;
 
     /** Locale for which this user history dictionary is storing words */
     private final String mLocale;
 
-    private final UserHistoryDictionaryBigramList mBigramList =
-            new UserHistoryDictionaryBigramList();
-    private final ReentrantLock mBigramListLock = new ReentrantLock();
+    private final String mFileName;
+
     private final SharedPreferences mPrefs;
 
     private final ArrayList<PersonalizationDictionaryUpdateSession> mSessions =
@@ -80,41 +61,44 @@
     @UsedForTesting boolean mIsTest = false;
 
     /* package */ DynamicPredictionDictionaryBase(final Context context, final String locale,
-            final SharedPreferences sp, final String dictionaryType) {
-        super(context, dictionaryType);
+            final SharedPreferences sp, final String dictionaryType, final String fileName) {
+        super(context, fileName, dictionaryType, true);
         mLocale = locale;
+        mFileName = fileName;
         mPrefs = sp;
         if (mLocale != null && mLocale.length() > 1) {
-            loadDictionary();
+            asyncLoadDictionaryToMemory();
+            reloadDictionaryIfRequired();
         }
     }
 
     @Override
     public void close() {
-        flushPendingWrites();
-        // Don't close the database as locale changes will require it to be reopened anyway
-        // Also, the database is written to somewhat frequently, so it needs to be kept alive
-        // throughout the life of the process.
-        // mOpenHelper.close();
-        // Ignore close because we cache PersonalizationPredictionDictionary for each language.
-        // See getInstance() above.
-        // super.close();
+        if (!ExpandableBinaryDictionary.ENABLE_BINARY_DICTIONARY_DYNAMIC_UPDATE) {
+            closeBinaryDictionary();
+        }
+        // Flush pending writes.
+        // TODO: Remove after this class become to use a dynamic binary dictionary.
+        asyncFlashAllBinaryDictionary();
+        Settings.writeLastUserHistoryWriteTime(mPrefs, mLocale);
     }
 
     @Override
-    protected ArrayList<SuggestedWordInfo> getWordsInner(final WordComposer composer,
-            final String prevWord, final ProximityInfo proximityInfo) {
-        // Inhibit suggestions (not predictions) for user history for now. Removing this method
-        // is enough to use it through the standard ExpandableDictionary way.
-        return null;
+    protected boolean hasContentChanged() {
+        return false;
+    }
+
+    @Override
+    protected boolean needsToReloadBeforeWriting() {
+        return false;
     }
 
     /**
      * Return whether the passed charsequence is in the dictionary.
      */
     @Override
-    public synchronized boolean isValidWord(final String word) {
-        // TODO: figure out what is the correct thing to do here.
+    public boolean isValidWord(final String word) {
+     // Words included only in the user history should be treated as not in dictionary words.
         return false;
     }
 
@@ -126,70 +110,29 @@
      * context, as in beginning of a sentence for example.
      * The second word may not be null (a NullPointerException would be thrown).
      */
-    public int addToPersonalizationPredictionDictionary(
-            final String word1, final String word2, final boolean isValid) {
-        if (word2.length() >= Constants.DICTIONARY_MAX_WORD_LENGTH ||
-                (word1 != null && word1.length() >= Constants.DICTIONARY_MAX_WORD_LENGTH)) {
-            return -1;
+    public void addToPersonalizationPredictionDictionary(
+            final String word0, final String word1, final boolean isValid) {
+        if (word1.length() >= Constants.DICTIONARY_MAX_WORD_LENGTH ||
+                (word0 != null && word0.length() >= Constants.DICTIONARY_MAX_WORD_LENGTH)) {
+            return;
         }
-        if (mBigramListLock.tryLock()) {
-            try {
-                super.addWord(
-                        word2, null /* the "shortcut" parameter is null */, FREQUENCY_FOR_TYPED);
-                mBigramList.addBigram(null, word2, (byte)FREQUENCY_FOR_TYPED);
-                // Do not insert a word as a bigram of itself
-                if (word2.equals(word1)) {
-                    return 0;
-                }
-                final int freq;
-                if (null == word1) {
-                    freq = FREQUENCY_FOR_TYPED;
-                } else {
-                    freq = super.setBigramAndGetFrequency(
-                            word1, word2, new ForgettingCurveParams(isValid));
-                }
-                mBigramList.addBigram(word1, word2);
-                return freq;
-            } finally {
-                mBigramListLock.unlock();
-            }
+        addWordDynamically(word1, null /* the "shortcut" parameter is null */, FREQUENCY_FOR_TYPED,
+                false /* isNotAWord */);
+        // Do not insert a word as a bigram of itself
+        if (word1.equals(word0)) {
+            return;
         }
-        return -1;
+        if (null != word0) {
+            addBigramDynamically(word0, word1, FREQUENCY_FOR_TYPED, isValid);
+        }
     }
 
-    public boolean cancelAddingUserHistory(final String word1, final String word2) {
-        if (mBigramListLock.tryLock()) {
-            try {
-                if (mBigramList.removeBigram(word1, word2)) {
-                    return super.removeBigram(word1, word2);
-                }
-            } finally {
-                mBigramListLock.unlock();
-            }
-        }
-        return false;
-    }
-
-    /**
-     * Schedules a background thread to write any pending words to the database.
-     */
-    private void flushPendingWrites() {
-        // Create a background thread to write the pending entries
-        new UpdateBinaryTask(mBigramList, mLocale, this, mPrefs, getContext()).execute();
+    public void cancelAddingUserHistory(final String word0, final String word1) {
+        removeBigramDynamically(word0, word1);
     }
 
     @Override
-    public final void loadDictionaryAsync() {
-        // This must be run on non-main thread
-        mBigramListLock.lock();
-        try {
-            loadDictionaryAsyncLocked();
-        } finally {
-            mBigramListLock.unlock();
-        }
-    }
-
-    private void loadDictionaryAsyncLocked() {
+    protected void loadDictionaryAsync() {
         final int[] profTotalCount = { 0 };
         final String locale = getLocale();
         if (DBG_STRESS_TEST) {
@@ -201,10 +144,8 @@
             }
         }
         final long last = Settings.readLastUserHistoryWriteTime(mPrefs, locale);
-        final boolean initializing = last == 0;
         final long now = System.currentTimeMillis();
-        final String fileName = getDictionaryFileName();
-        final ExpandableDictionary dictionary = this;
+        final ExpandableBinaryDictionary dictionary = this;
         final OnAddWordListener listener = new OnAddWordListener() {
             @Override
             public void setUnigram(final String word, final String shortcutTarget,
@@ -212,49 +153,39 @@
                 if (DBG_SAVE_RESTORE) {
                     Log.d(TAG, "load unigram: " + word + "," + frequency);
                 }
-                dictionary.addWord(word, shortcutTarget, frequency);
+                addWord(word, shortcutTarget, frequency, false /* isNotAWord */);
                 ++profTotalCount[0];
-                addToBigramListLocked(null, word, (byte)frequency);
             }
 
             @Override
-            public void setBigram(final String word1, final String word2, final int frequency) {
-                if (word1.length() < Constants.DICTIONARY_MAX_WORD_LENGTH
-                        && word2.length() < Constants.DICTIONARY_MAX_WORD_LENGTH) {
+            public void setBigram(final String word0, final String word1, final int frequency) {
+                if (word0.length() < Constants.DICTIONARY_MAX_WORD_LENGTH
+                        && word1.length() < Constants.DICTIONARY_MAX_WORD_LENGTH) {
                     if (DBG_SAVE_RESTORE) {
-                        Log.d(TAG, "load bigram: " + word1 + "," + word2 + "," + frequency);
+                        Log.d(TAG, "load bigram: " + word0 + "," + word1 + "," + frequency);
                     }
                     ++profTotalCount[0];
-                    dictionary.setBigramAndGetFrequency(
-                            word1, word2, initializing ? new ForgettingCurveParams(true)
-                            : new ForgettingCurveParams(frequency, now, last));
+                    addBigram(word0, word1, frequency, last);
                 }
-                addToBigramListLocked(word1, word2, (byte)frequency);
             }
         };
 
         // Load the dictionary from binary file
-        FileInputStream inStream = null;
-        try {
-            final File file = new File(getContext().getFilesDir(), fileName);
-            final byte[] buffer = new byte[(int)file.length()];
-            inStream = new FileInputStream(file);
-            inStream.read(buffer);
-            UserHistoryDictIOUtils.readDictionaryBinary(
-                    new ByteArrayWrapper(buffer), listener);
-        } catch (FileNotFoundException e) {
+        final File dictFile = new File(mContext.getFilesDir(), mFileName);
+        final DictDecoder dictDecoder = FormatSpec.getDictDecoder(dictFile,
+                DictDecoder.USE_BYTEARRAY);
+        if (dictDecoder == null) {
             // This is an expected condition: we don't have a user history dictionary for this
             // language yet. It will be created sometime later.
+            return;
+        }
+
+        try {
+            dictDecoder.openDictBuffer();
+            UserHistoryDictIOUtils.readDictionaryBinary(dictDecoder, listener);
         } catch (IOException e) {
-            Log.e(TAG, "IOException on opening a bytebuffer", e);
+            Log.d(TAG, "IOException on opening a bytebuffer", e);
         } finally {
-            if (inStream != null) {
-                try {
-                    inStream.close();
-                } catch (IOException e) {
-                    // do nothing
-                }
-            }
             if (PROFILE_SAVE_RESTORE) {
                 final long diff = System.currentTimeMillis() - now;
                 Log.d(TAG, "PROF: Load UserHistoryDictionary: "
@@ -263,146 +194,24 @@
         }
     }
 
-    protected abstract String getDictionaryFileName();
-
     protected String getLocale() {
         return mLocale;
     }
 
-    private void addToBigramListLocked(String word0, String word1, byte fcValue) {
-        mBigramList.addBigram(word0, word1, fcValue);
-    }
-
-    /**
-     * Async task to write pending words to the binarydicts.
-     */
-    private static final class UpdateBinaryTask extends AsyncTask<Void, Void, Void>
-            implements BigramDictionaryInterface {
-        private final UserHistoryDictionaryBigramList mBigramList;
-        private final boolean mAddLevel0Bigrams;
-        private final String mLocale;
-        private final DynamicPredictionDictionaryBase mDynamicPredictionDictionary;
-        private final SharedPreferences mPrefs;
-        private final Context mContext;
-
-        public UpdateBinaryTask(final UserHistoryDictionaryBigramList pendingWrites,
-                final String locale, final DynamicPredictionDictionaryBase dict,
-                final SharedPreferences prefs, final Context context) {
-            mBigramList = pendingWrites;
-            mLocale = locale;
-            mDynamicPredictionDictionary = dict;
-            mPrefs = prefs;
-            mContext = context;
-            mAddLevel0Bigrams = mBigramList.size() <= MAX_HISTORY_BIGRAMS;
-        }
-
-        @Override
-        protected Void doInBackground(final Void... v) {
-            if (mDynamicPredictionDictionary.mIsTest) {
-                // If mIsTest == true, wait until the lock is released.
-                mDynamicPredictionDictionary.mBigramListLock.lock();
-                try {
-                    doWriteTaskLocked();
-                } finally {
-                    mDynamicPredictionDictionary.mBigramListLock.unlock();
-                }
-            } else if (mDynamicPredictionDictionary.mBigramListLock.tryLock()) {
-                try {
-                    doWriteTaskLocked();
-                } finally {
-                    mDynamicPredictionDictionary.mBigramListLock.unlock();
-                }
-            }
-            return null;
-        }
-
-        private void doWriteTaskLocked() {
-            if (DBG_STRESS_TEST) {
-                try {
-                    Log.w(TAG, "Start stress in closing: " + mLocale);
-                    Thread.sleep(15000);
-                    Log.w(TAG, "End stress in closing");
-                } catch (InterruptedException e) {
-                    Log.e(TAG, "In stress test", e);
-                }
-            }
-
-            final long now = PROFILE_SAVE_RESTORE ? System.currentTimeMillis() : 0;
-            final String fileName =
-                    mDynamicPredictionDictionary.getDictionaryFileName();
-            final File file = new File(mContext.getFilesDir(), fileName);
-            FileOutputStream out = null;
-
-            try {
-                out = new FileOutputStream(file);
-                UserHistoryDictIOUtils.writeDictionaryBinary(out, this, mBigramList, VERSION3);
-                out.flush();
-                out.close();
-            } catch (IOException e) {
-                Log.e(TAG, "IO Exception while writing file", e);
-            } finally {
-                if (out != null) {
-                    try {
-                        out.close();
-                    } catch (IOException e) {
-                        // ignore
-                    }
-                }
-            }
-
-            // Save the timestamp after we finish writing the binary dictionary.
-            Settings.writeLastUserHistoryWriteTime(mPrefs, mLocale);
-            if (PROFILE_SAVE_RESTORE) {
-                final long diff = System.currentTimeMillis() - now;
-                Log.w(TAG, "PROF: Write User HistoryDictionary: " + mLocale + ", " + diff + "ms.");
-            }
-        }
-
-        @Override
-        public int getFrequency(final String word1, final String word2) {
-            final int freq;
-            if (word1 == null) { // unigram
-                freq = FREQUENCY_FOR_TYPED;
-                final byte prevFc = mBigramList.getBigrams(word1).get(word2);
-            } else { // bigram
-                final NextWord nw =
-                        mDynamicPredictionDictionary.getBigramWord(word1, word2);
-                if (nw != null) {
-                    final ForgettingCurveParams fcp = nw.getFcParams();
-                    final byte prevFc = mBigramList.getBigrams(word1).get(word2);
-                    final byte fc = fcp.getFc();
-                    final boolean isValid = fcp.isValid();
-                    if (prevFc > 0 && prevFc == fc) {
-                        freq = fc & 0xFF;
-                    } else if (UserHistoryForgettingCurveUtils.
-                            needsToSave(fc, isValid, mAddLevel0Bigrams)) {
-                        freq = fc & 0xFF;
-                    } else {
-                        // Delete this entry
-                        freq = -1;
-                    }
-                } else {
-                    // Delete this entry
-                    freq = -1;
-                }
-            }
-            return freq;
-        }
-    }
-
-    @UsedForTesting
-    /* package for test */ void forceAddWordForTest(
-            final String word1, final String word2, final boolean isValid) {
-        mBigramListLock.lock();
-        try {
-            addToPersonalizationPredictionDictionary(word1, word2, isValid);
-        } finally {
-            mBigramListLock.unlock();
-        }
-    }
-
     public void registerUpdateSession(PersonalizationDictionaryUpdateSession session) {
-        session.setDictionary(this);
+        session.setPredictionDictionary(this);
         mSessions.add(session);
+        session.onDictionaryReady();
+    }
+
+    public void unRegisterUpdateSession(PersonalizationDictionaryUpdateSession session) {
+        mSessions.remove(session);
+    }
+
+    public void clearAndFlushDictionary() {
+        // Clear the node structure on memory
+        clear();
+        // Then flush the cleared state of the dictionary on disk.
+        asyncFlashAllBinaryDictionary();
     }
 }
diff --git a/java/src/com/android/inputmethod/latin/personalization/PersonalizationDictionary.java b/java/src/com/android/inputmethod/latin/personalization/PersonalizationDictionary.java
index e38a235..f257165 100644
--- a/java/src/com/android/inputmethod/latin/personalization/PersonalizationDictionary.java
+++ b/java/src/com/android/inputmethod/latin/personalization/PersonalizationDictionary.java
@@ -18,26 +18,32 @@
 
 import com.android.inputmethod.latin.Dictionary;
 import com.android.inputmethod.latin.ExpandableBinaryDictionary;
+import com.android.inputmethod.latin.utils.CollectionUtils;
 
 import android.content.Context;
+import android.content.SharedPreferences;
+
+import java.util.ArrayList;
 
 /**
  * This class is a dictionary for the personalized language model that uses binary dictionary.
  */
 public class PersonalizationDictionary extends ExpandableBinaryDictionary {
     private static final String NAME = "personalization";
-
-    public static void registerUpdateListener(PersonalizationDictionaryUpdateSession listener) {
-        // TODO: Implement
-    }
+    private final ArrayList<PersonalizationDictionaryUpdateSession> mSessions =
+            CollectionUtils.newArrayList();
 
     /** Locale for which this user history dictionary is storing words */
     private final String mLocale;
 
-    // Singleton
-    private PersonalizationDictionary(final Context context, final String locale) {
-        super(context, getFilenameWithLocale(NAME, locale), Dictionary.TYPE_PERSONALIZATION);
+    public PersonalizationDictionary(final Context context, final String locale,
+            final SharedPreferences prefs) {
+        // TODO: Make isUpdatable true.
+        super(context, getFilenameWithLocale(NAME, locale), Dictionary.TYPE_PERSONALIZATION,
+                false /* isUpdatable */);
         mLocale = locale;
+        // TODO: Restore last updated time
+        loadDictionary();
     }
 
     @Override
@@ -47,15 +53,21 @@
 
     @Override
     protected boolean hasContentChanged() {
-        // TODO: Implement
         return false;
     }
 
     @Override
     protected boolean needsToReloadBeforeWriting() {
-        // TODO: Implement
         return false;
     }
 
-    // TODO: Implement
+    public void registerUpdateSession(PersonalizationDictionaryUpdateSession session) {
+        session.setDictionary(this);
+        mSessions.add(session);
+        session.onDictionaryReady();
+    }
+
+    public void unRegisterUpdateSession(PersonalizationDictionaryUpdateSession session) {
+        mSessions.remove(session);
+    }
 }
diff --git a/java/src/com/android/inputmethod/latin/personalization/PersonalizationDictionarySessionRegister.java b/java/src/com/android/inputmethod/latin/personalization/PersonalizationDictionarySessionRegister.java
new file mode 100644
index 0000000..c1833ff
--- /dev/null
+++ b/java/src/com/android/inputmethod/latin/personalization/PersonalizationDictionarySessionRegister.java
@@ -0,0 +1,37 @@
+/*
+ * Copyright (C) 2013 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.latin.personalization;
+
+import android.content.Context;
+import android.content.res.Configuration;
+
+public class PersonalizationDictionarySessionRegister {
+    public static void init(Context context) {
+    }
+
+    public static void onConfigurationChanged(final Context context, final Configuration conf) {
+    }
+
+    public static void onUpdateData(Context context, String type) {
+    }
+
+    public static void onRemoveData(Context context, String type) {
+    }
+
+    public static void onDestroy(Context context) {
+    }
+}
diff --git a/java/src/com/android/inputmethod/latin/personalization/PersonalizationDictionaryUpdateSession.java b/java/src/com/android/inputmethod/latin/personalization/PersonalizationDictionaryUpdateSession.java
index d62aec1..ab3de80 100644
--- a/java/src/com/android/inputmethod/latin/personalization/PersonalizationDictionaryUpdateSession.java
+++ b/java/src/com/android/inputmethod/latin/personalization/PersonalizationDictionaryUpdateSession.java
@@ -16,6 +16,8 @@
 
 package com.android.inputmethod.latin.personalization;
 
+import android.content.Context;
+
 import java.lang.ref.WeakReference;
 import java.util.ArrayList;
 
@@ -43,18 +45,78 @@
     }
 
     // TODO: Use a dynamic binary dictionary instead
-    public WeakReference<DynamicPredictionDictionaryBase> mDictionary;
+    public WeakReference<PersonalizationDictionary> mDictionary;
+    public WeakReference<DynamicPredictionDictionaryBase> mPredictionDictionary;
+    public final String mSystemLocale;
+    public PersonalizationDictionaryUpdateSession(String locale) {
+        mSystemLocale = locale;
+    }
 
     public abstract void onDictionaryReady();
 
-    public void setDictionary(DynamicPredictionDictionaryBase dictionary) {
-        mDictionary = new WeakReference<DynamicPredictionDictionaryBase>(dictionary);
+    public abstract void onDictionaryClosed(Context context);
+
+    public void setDictionary(PersonalizationDictionary dictionary) {
+        mDictionary = new WeakReference<PersonalizationDictionary>(dictionary);
     }
 
-    public void addToPersonalizationDictionary(
+    public void setPredictionDictionary(DynamicPredictionDictionaryBase dictionary) {
+        mPredictionDictionary = new WeakReference<DynamicPredictionDictionaryBase>(dictionary);
+    }
+
+    protected PersonalizationDictionary getDictionary() {
+        return mDictionary == null ? null : mDictionary.get();
+    }
+
+    protected DynamicPredictionDictionaryBase getPredictionDictionary() {
+        return mPredictionDictionary == null ? null : mPredictionDictionary.get();
+    }
+
+    private void unsetDictionary() {
+        final PersonalizationDictionary dictionary = getDictionary();
+        if (dictionary == null) {
+            return;
+        }
+        dictionary.unRegisterUpdateSession(this);
+    }
+
+    private void unsetPredictionDictionary() {
+        final DynamicPredictionDictionaryBase dictionary = getPredictionDictionary();
+        if (dictionary == null) {
+            return;
+        }
+        dictionary.unRegisterUpdateSession(this);
+    }
+
+    public void clearAndFlushPredictionDictionary(Context context) {
+        final DynamicPredictionDictionaryBase dictionary = getPredictionDictionary();
+        if (dictionary == null) {
+            return;
+        }
+        dictionary.clearAndFlushDictionary();
+    }
+
+    public void closeSession(Context context) {
+        unsetDictionary();
+        unsetPredictionDictionary();
+        onDictionaryClosed(context);
+    }
+
+    // TODO: Support multi locale to add bigram
+    public void addBigramToPersonalizationDictionary(String word0, String word1, boolean isValid,
+            int frequency) {
+        final DynamicPredictionDictionaryBase dictionary = getPredictionDictionary();
+        if (dictionary == null) {
+            return;
+        }
+        dictionary.addToPersonalizationPredictionDictionary(word0, word1, isValid);
+    }
+
+    // Bulk import
+    // TODO: Support multi locale to add bigram
+    public void addBigramsToPersonalizationDictionary(
             final ArrayList<PersonalizationLanguageModelParam> lmParams) {
-        final DynamicPredictionDictionaryBase dictionary = mDictionary == null
-                ? null : mDictionary.get();
+        final DynamicPredictionDictionaryBase dictionary = getPredictionDictionary();
         if (dictionary == null) {
             return;
         }
diff --git a/java/src/com/android/inputmethod/latin/personalization/PersonalizationDictionaryHelper.java b/java/src/com/android/inputmethod/latin/personalization/PersonalizationHelper.java
similarity index 63%
rename from java/src/com/android/inputmethod/latin/personalization/PersonalizationDictionaryHelper.java
rename to java/src/com/android/inputmethod/latin/personalization/PersonalizationHelper.java
index da256f8..5f702ee 100644
--- a/java/src/com/android/inputmethod/latin/personalization/PersonalizationDictionaryHelper.java
+++ b/java/src/com/android/inputmethod/latin/personalization/PersonalizationHelper.java
@@ -26,16 +26,20 @@
 import java.lang.ref.SoftReference;
 import java.util.concurrent.ConcurrentHashMap;
 
-public class PersonalizationDictionaryHelper {
-    private static final String TAG = PersonalizationDictionaryHelper.class.getSimpleName();
+public class PersonalizationHelper {
+    private static final String TAG = PersonalizationHelper.class.getSimpleName();
     private static final boolean DEBUG = false;
 
     private static final ConcurrentHashMap<String, SoftReference<UserHistoryPredictionDictionary>>
             sLangUserHistoryDictCache = CollectionUtils.newConcurrentHashMap();
 
+    private static final ConcurrentHashMap<String, SoftReference<PersonalizationDictionary>>
+            sLangPersonalizationDictCache = CollectionUtils.newConcurrentHashMap();
+
     private static final ConcurrentHashMap<String,
             SoftReference<PersonalizationPredictionDictionary>>
-                    sLangPersonalizationDictCache = CollectionUtils.newConcurrentHashMap();
+                    sLangPersonalizationPredictionDictCache =
+                            CollectionUtils.newConcurrentHashMap();
 
     public static UserHistoryPredictionDictionary getUserHistoryPredictionDictionary(
             final Context context, final String locale, final SharedPreferences sp) {
@@ -48,6 +52,7 @@
                     if (DEBUG) {
                         Log.w(TAG, "Use cached UserHistoryPredictionDictionary for " + locale);
                     }
+                    dict.reloadDictionaryIfRequired();
                     return dict;
                 }
             }
@@ -59,22 +64,46 @@
         }
     }
 
-    public static void
-            registerPersonalizationDictionaryUpdateSession(final Context context,
-                    final PersonalizationDictionaryUpdateSession session) {
-        final PersonalizationPredictionDictionary dictionary =
-                getPersonalizationPredictionDictionary(context,
-                        context.getResources().getConfiguration().locale.toString(),
+    public static void registerPersonalizationDictionaryUpdateSession(final Context context,
+            final PersonalizationDictionaryUpdateSession session, String locale) {
+        final PersonalizationPredictionDictionary predictionDictionary =
+                getPersonalizationPredictionDictionary(context, locale,
+                        PreferenceManager.getDefaultSharedPreferences(context));
+        predictionDictionary.registerUpdateSession(session);
+        final PersonalizationDictionary dictionary =
+                getPersonalizationDictionary(context, locale,
                         PreferenceManager.getDefaultSharedPreferences(context));
         dictionary.registerUpdateSession(session);
     }
 
-    public static PersonalizationPredictionDictionary getPersonalizationPredictionDictionary(
+    public static PersonalizationDictionary getPersonalizationDictionary(
             final Context context, final String locale, final SharedPreferences sp) {
         synchronized (sLangPersonalizationDictCache) {
             if (sLangPersonalizationDictCache.containsKey(locale)) {
-                final SoftReference<PersonalizationPredictionDictionary> ref =
+                final SoftReference<PersonalizationDictionary> ref =
                         sLangPersonalizationDictCache.get(locale);
+                final PersonalizationDictionary dict = ref == null ? null : ref.get();
+                if (dict != null) {
+                    if (DEBUG) {
+                        Log.w(TAG, "Use cached PersonalizationDictCache for " + locale);
+                    }
+                    return dict;
+                }
+            }
+            final PersonalizationDictionary dict =
+                    new PersonalizationDictionary(context, locale, sp);
+            sLangPersonalizationDictCache.put(
+                    locale, new SoftReference<PersonalizationDictionary>(dict));
+            return dict;
+        }
+    }
+
+    public static PersonalizationPredictionDictionary getPersonalizationPredictionDictionary(
+            final Context context, final String locale, final SharedPreferences sp) {
+        synchronized (sLangPersonalizationPredictionDictCache) {
+            if (sLangPersonalizationPredictionDictCache.containsKey(locale)) {
+                final SoftReference<PersonalizationPredictionDictionary> ref =
+                        sLangPersonalizationPredictionDictCache.get(locale);
                 final PersonalizationPredictionDictionary dict = ref == null ? null : ref.get();
                 if (dict != null) {
                     if (DEBUG) {
@@ -85,7 +114,7 @@
             }
             final PersonalizationPredictionDictionary dict =
                     new PersonalizationPredictionDictionary(context, locale, sp);
-            sLangPersonalizationDictCache.put(
+            sLangPersonalizationPredictionDictCache.put(
                     locale, new SoftReference<PersonalizationPredictionDictionary>(dict));
             return dict;
         }
diff --git a/java/src/com/android/inputmethod/latin/personalization/PersonalizationPredictionDictionary.java b/java/src/com/android/inputmethod/latin/personalization/PersonalizationPredictionDictionary.java
index 955bd27..e80953c 100644
--- a/java/src/com/android/inputmethod/latin/personalization/PersonalizationPredictionDictionary.java
+++ b/java/src/com/android/inputmethod/latin/personalization/PersonalizationPredictionDictionary.java
@@ -17,6 +17,7 @@
 package com.android.inputmethod.latin.personalization;
 
 import com.android.inputmethod.latin.Dictionary;
+import com.android.inputmethod.latin.ExpandableBinaryDictionary;
 
 import android.content.Context;
 import android.content.SharedPreferences;
@@ -26,11 +27,11 @@
 
     /* package */ PersonalizationPredictionDictionary(final Context context, final String locale,
             final SharedPreferences sp) {
-        super(context, locale, sp, Dictionary.TYPE_PERSONALIZATION_PREDICTION_IN_JAVA);
+        super(context, locale, sp, Dictionary.TYPE_PERSONALIZATION_PREDICTION_IN_JAVA,
+                getDictionaryFileName(locale));
     }
 
-    @Override
-    protected String getDictionaryFileName() {
-        return NAME + "." + getLocale() + ".dict";
+    private static String getDictionaryFileName(final String locale) {
+        return NAME + "." + locale + ExpandableBinaryDictionary.DICT_FILE_EXTENSION;
     }
 }
diff --git a/java/src/com/android/inputmethod/latin/personalization/UserHistoryDictionaryBigramList.java b/java/src/com/android/inputmethod/latin/personalization/UserHistoryDictionaryBigramList.java
index f21db25..4c1803b 100644
--- a/java/src/com/android/inputmethod/latin/personalization/UserHistoryDictionaryBigramList.java
+++ b/java/src/com/android/inputmethod/latin/personalization/UserHistoryDictionaryBigramList.java
@@ -45,6 +45,7 @@
     /**
      * Called when the user typed a word.
      */
+    @UsedForTesting
     public void addBigram(String word1, String word2) {
         addBigram(word1, word2, FORGETTING_CURVE_INITIAL_VALUE);
     }
@@ -53,7 +54,7 @@
      * Called when loaded from the SQL DB.
      */
     public void addBigram(String word1, String word2, byte fcValue) {
-        if (UserHistoryPredictionDictionary.DBG_SAVE_RESTORE) {
+        if (DynamicPredictionDictionaryBase.DBG_SAVE_RESTORE) {
             Log.d(TAG, "--- add bigram: " + word1 + ", " + word2 + ", " + fcValue);
         }
         final HashMap<String, Byte> map;
@@ -73,7 +74,7 @@
      * Called when inserted to the SQL DB.
      */
     public void updateBigram(String word1, String word2, byte fcValue) {
-        if (UserHistoryPredictionDictionary.DBG_SAVE_RESTORE) {
+        if (DynamicPredictionDictionaryBase.DBG_SAVE_RESTORE) {
             Log.d(TAG, "--- update bigram: " + word1 + ", " + word2 + ", " + fcValue);
         }
         final HashMap<String, Byte> map;
@@ -96,6 +97,10 @@
         return mBigramMap.isEmpty();
     }
 
+    public boolean containsKey(String word) {
+        return mBigramMap.containsKey(word);
+    }
+
     public Set<String> keySet() {
         return mBigramMap.keySet();
     }
diff --git a/java/src/com/android/inputmethod/latin/personalization/UserHistoryPredictionDictionary.java b/java/src/com/android/inputmethod/latin/personalization/UserHistoryPredictionDictionary.java
index d117844..b140c91 100644
--- a/java/src/com/android/inputmethod/latin/personalization/UserHistoryPredictionDictionary.java
+++ b/java/src/com/android/inputmethod/latin/personalization/UserHistoryPredictionDictionary.java
@@ -17,6 +17,7 @@
 package com.android.inputmethod.latin.personalization;
 
 import com.android.inputmethod.latin.Dictionary;
+import com.android.inputmethod.latin.ExpandableBinaryDictionary;
 
 import android.content.Context;
 import android.content.SharedPreferences;
@@ -26,14 +27,14 @@
  * cancellation or manual picks. This allows the keyboard to adapt to the typist over time.
  */
 public class UserHistoryPredictionDictionary extends DynamicPredictionDictionaryBase {
-    private static final String NAME = UserHistoryPredictionDictionary.class.getSimpleName();
+    /* package for tests */ static final String NAME =
+            UserHistoryPredictionDictionary.class.getSimpleName();
     /* package */ UserHistoryPredictionDictionary(final Context context, final String locale,
             final SharedPreferences sp) {
-        super(context, locale, sp, Dictionary.TYPE_USER_HISTORY);
+        super(context, locale, sp, Dictionary.TYPE_USER_HISTORY, getDictionaryFileName(locale));
     }
 
-    @Override
-    protected String getDictionaryFileName() {
-        return NAME + "." + getLocale() + ".dict";
+    private static String getDictionaryFileName(final String locale) {
+        return NAME + "." + locale + ExpandableBinaryDictionary.DICT_FILE_EXTENSION;
     }
 }
diff --git a/java/src/com/android/inputmethod/latin/settings/AdditionalFeaturesSettingUtils.java b/java/src/com/android/inputmethod/latin/settings/AdditionalFeaturesSettingUtils.java
index 139f5e2..6543003 100644
--- a/java/src/com/android/inputmethod/latin/settings/AdditionalFeaturesSettingUtils.java
+++ b/java/src/com/android/inputmethod/latin/settings/AdditionalFeaturesSettingUtils.java
@@ -40,8 +40,4 @@
             final SharedPreferences prefs, final int[] additionalFeaturesPreferences) {
         // do nothing.
     }
-
-    public static int[] getAdditionalNativeSuggestOptions() {
-        return Settings.getInstance().getCurrent().mAdditionalFeaturesSettingValues;
-    }
 }
diff --git a/java/src/com/android/inputmethod/latin/settings/DebugSettings.java b/java/src/com/android/inputmethod/latin/settings/DebugSettings.java
index b1cd887..1b592b5 100644
--- a/java/src/com/android/inputmethod/latin/settings/DebugSettings.java
+++ b/java/src/com/android/inputmethod/latin/settings/DebugSettings.java
@@ -39,6 +39,8 @@
     public static final String PREF_STATISTICS_LOGGING = "enable_logging";
     public static final String PREF_USE_ONLY_PERSONALIZATION_DICTIONARY_FOR_DEBUG =
             "use_only_personalization_dictionary_for_debug";
+    public static final String PREF_BOOST_PERSONALIZATION_DICTIONARY_FOR_DEBUG =
+            "boost_personalization_dictionary_for_debug";
     private static final String PREF_READ_EXTERNAL_DICTIONARY = "read_external_dictionary";
     private static final boolean SHOW_STATISTICS_LOGGING = false;
 
diff --git a/java/src/com/android/inputmethod/latin/settings/NativeSuggestOptions.java b/java/src/com/android/inputmethod/latin/settings/NativeSuggestOptions.java
index 878c505..cd726c9 100644
--- a/java/src/com/android/inputmethod/latin/settings/NativeSuggestOptions.java
+++ b/java/src/com/android/inputmethod/latin/settings/NativeSuggestOptions.java
@@ -34,6 +34,9 @@
     }
 
     public void setAdditionalFeaturesOptions(final int[] additionalOptions) {
+        if (additionalOptions == null) {
+            return;
+        }
         for (int i = 0; i < additionalOptions.length; i++) {
             setIntegerOption(OPTIONS_SIZE + i, additionalOptions[i]);
         }
diff --git a/java/src/com/android/inputmethod/latin/settings/Settings.java b/java/src/com/android/inputmethod/latin/settings/Settings.java
index d432087..1a0fecc 100644
--- a/java/src/com/android/inputmethod/latin/settings/Settings.java
+++ b/java/src/com/android/inputmethod/latin/settings/Settings.java
@@ -27,10 +27,10 @@
 import com.android.inputmethod.latin.InputAttributes;
 import com.android.inputmethod.latin.R;
 import com.android.inputmethod.latin.utils.AdditionalSubtypeUtils;
-import com.android.inputmethod.latin.utils.DebugLogUtils;
 import com.android.inputmethod.latin.utils.LocaleUtils;
 import com.android.inputmethod.latin.utils.ResourceUtils;
 import com.android.inputmethod.latin.utils.RunInLocale;
+import com.android.inputmethod.latin.utils.StringUtils;
 
 import java.util.HashMap;
 import java.util.Locale;
@@ -44,7 +44,9 @@
     public static final String PREF_VIBRATE_ON = "vibrate_on";
     public static final String PREF_SOUND_ON = "sound_on";
     public static final String PREF_POPUP_ON = "popup_on";
-    public static final String PREF_VOICE_MODE = "voice_mode";
+    // PREF_VOICE_MODE_OBSOLETE is obsolete. Use PREF_VOICE_INPUT_KEY instead.
+    public static final String PREF_VOICE_MODE_OBSOLETE = "voice_mode";
+    public static final String PREF_VOICE_INPUT_KEY = "pref_voice_input_key";
     public static final String PREF_CORRECTION_SETTINGS = "correction_settings";
     public static final String PREF_EDIT_PERSONAL_DICTIONARY = "edit_personal_dictionary";
     public static final String PREF_CONFIGURE_DICTIONARIES_KEY = "configure_dictionaries_key";
@@ -79,6 +81,7 @@
     public static final String PREF_GESTURE_FLOATING_PREVIEW_TEXT =
             "pref_gesture_floating_preview_text";
     public static final String PREF_SHOW_SETUP_WIZARD_ICON = "pref_show_setup_wizard_icon";
+    public static final String PREF_PHRASE_GESTURE_ENABLED = "pref_gesture_space_aware";
 
     public static final String PREF_INPUT_LANGUAGE = "input_language";
     public static final String PREF_SELECTED_LANGUAGES = "selected_languages";
@@ -90,9 +93,15 @@
     private static final String PREF_SUPPRESS_LANGUAGE_SWITCH_KEY =
             "pref_suppress_language_switch_key";
 
+    private static final String PREF_LAST_USED_PERSONALIZATION_TOKEN =
+            "pref_last_used_personalization_token";
     public static final String PREF_SEND_FEEDBACK = "send_feedback";
     public static final String PREF_ABOUT_KEYBOARD = "about_keyboard";
 
+    // Emoji
+    public static final String PREF_EMOJI_RECENT_KEYS = "emoji_recent_keys";
+    public static final String PREF_EMOJI_CATEGORY_LAST_TYPED_ID = "emoji_category_last_typed_id";
+
     private Resources mRes;
     private SharedPreferences mPrefs;
     private SettingsValues mSettingsValues;
@@ -212,6 +221,12 @@
                 && prefs.getBoolean(Settings.PREF_GESTURE_INPUT, true);
     }
 
+    public static boolean readPhraseGestureEnabled(final SharedPreferences prefs,
+            final Resources res) {
+        return prefs.getBoolean(Settings.PREF_PHRASE_GESTURE_ENABLED,
+                res.getBoolean(R.bool.config_default_phrase_gesture_enabled));
+    }
+
     public static boolean readFromBuildConfigIfToShowKeyPreviewPopupSettingsOption(
             final Resources res) {
         return res.getBoolean(R.bool.config_enable_show_option_of_key_preview_popup);
@@ -343,4 +358,40 @@
         return prefs.getBoolean(
                 DebugSettings.PREF_USE_ONLY_PERSONALIZATION_DICTIONARY_FOR_DEBUG, false);
     }
+
+    public static boolean readBoostPersonalizationDictionaryForDebug(
+            final SharedPreferences prefs) {
+        return prefs.getBoolean(
+                DebugSettings.PREF_BOOST_PERSONALIZATION_DICTIONARY_FOR_DEBUG, false);
+    }
+
+    public void writeLastUsedPersonalizationToken(byte[] token) {
+        final String tokenStr = StringUtils.byteArrayToHexString(token);
+        mPrefs.edit().putString(PREF_LAST_USED_PERSONALIZATION_TOKEN, tokenStr).apply();
+    }
+
+    public byte[] readLastUsedPersonalizationToken() {
+        final String tokenStr = mPrefs.getString(PREF_LAST_USED_PERSONALIZATION_TOKEN, null);
+        return StringUtils.hexStringToByteArray(tokenStr);
+    }
+
+    public static void writeEmojiRecentKeys(final SharedPreferences prefs, String str) {
+        prefs.edit().putString(PREF_EMOJI_RECENT_KEYS, str).apply();
+    }
+
+    public static String readEmojiRecentKeys(final SharedPreferences prefs) {
+        return prefs.getString(PREF_EMOJI_RECENT_KEYS, "");
+    }
+
+    public static void writeEmojiCategoryLastTypedId(
+            final SharedPreferences prefs, final int category, final int id) {
+        final String key = PREF_EMOJI_CATEGORY_LAST_TYPED_ID + category;
+        prefs.edit().putInt(key, id).apply();
+    }
+
+    public static int readEmojiCategoryLastTypedId(
+            final SharedPreferences prefs, final int category) {
+        final String key = PREF_EMOJI_CATEGORY_LAST_TYPED_ID + category;
+        return prefs.getInt(key, 0);
+    }
 }
diff --git a/java/src/com/android/inputmethod/latin/settings/SettingsFragment.java b/java/src/com/android/inputmethod/latin/settings/SettingsFragment.java
index 4467777..cb7dda6 100644
--- a/java/src/com/android/inputmethod/latin/settings/SettingsFragment.java
+++ b/java/src/com/android/inputmethod/latin/settings/SettingsFragment.java
@@ -61,7 +61,7 @@
             DBG_USE_INTERNAL_PERSONAL_DICTIONARY_SETTINGS
                     || Build.VERSION.SDK_INT <= 18 /* Build.VERSION.JELLY_BEAN_MR2 */;
 
-    private ListPreference mVoicePreference;
+    private CheckBoxPreference mVoiceInputKeyPreference;
     private ListPreference mShowCorrectionSuggestionsPreference;
     private ListPreference mAutoCorrectionThresholdPreference;
     private ListPreference mKeyPreviewPopupDismissDelay;
@@ -107,7 +107,8 @@
         SubtypeLocaleUtils.init(context);
         AudioAndHapticFeedbackManager.init(context);
 
-        mVoicePreference = (ListPreference) findPreference(Settings.PREF_VOICE_MODE);
+        mVoiceInputKeyPreference =
+                (CheckBoxPreference) findPreference(Settings.PREF_VOICE_INPUT_KEY);
         mShowCorrectionSuggestionsPreference =
                 (ListPreference) findPreference(Settings.PREF_SHOW_SUGGESTIONS_SETTING);
         final SharedPreferences prefs = getPreferenceManager().getSharedPreferences();
@@ -166,7 +167,7 @@
         final boolean showVoiceKeyOption = res.getBoolean(
                 R.bool.config_enable_show_voice_key_option);
         if (!showVoiceKeyOption) {
-            generalSettings.removePreference(mVoicePreference);
+            generalSettings.removePreference(mVoiceInputKeyPreference);
         }
 
         final PreferenceGroup advancedSettings =
@@ -229,10 +230,10 @@
 
         if (!Settings.readFromBuildConfigIfGestureInputEnabled(res)) {
             removePreference(Settings.PREF_GESTURE_SETTINGS, getPreferenceScreen());
-        } else {
-            AdditionalFeaturesSettingUtils.addAdditionalFeaturesPreferences(context, this);
         }
 
+        AdditionalFeaturesSettingUtils.addAdditionalFeaturesPreferences(context, this);
+
         setupKeyLongpressTimeoutSettings(prefs, res);
         setupKeypressVibrationDurationSettings(prefs, res);
         setupKeypressSoundVolumeSettings(prefs, res);
@@ -243,10 +244,8 @@
     public void onResume() {
         super.onResume();
         final boolean isShortcutImeEnabled = SubtypeSwitcher.getInstance().isShortcutImeEnabled();
-        if (isShortcutImeEnabled) {
-            updateVoiceModeSummary();
-        } else {
-            getPreferenceScreen().removePreference(mVoicePreference);
+        if (!isShortcutImeEnabled) {
+            getPreferenceScreen().removePreference(mVoiceInputKeyPreference);
         }
         final SharedPreferences prefs = getPreferenceManager().getSharedPreferences();
         final CheckBoxPreference showSetupWizardIcon =
@@ -287,7 +286,6 @@
             LauncherIconVisibilityManager.updateSetupWizardIconVisibility(getActivity());
         }
         ensureConsistencyOfAutoCorrectionSettings();
-        updateVoiceModeSummary();
         updateShowCorrectionSuggestionsSummary();
         updateKeyPreviewPopupDelaySummary();
         refreshEnablingsOfKeypressSoundAndVibrationSettings(prefs, getResources());
@@ -330,12 +328,6 @@
         lp.setSummary(entries[lp.findIndexOfValue(lp.getValue())]);
     }
 
-    private void updateVoiceModeSummary() {
-        mVoicePreference.setSummary(
-                getResources().getStringArray(R.array.voice_input_modes_summary)
-                        [mVoicePreference.findIndexOfValue(mVoicePreference.getValue())]);
-    }
-
     private void refreshEnablingsOfKeypressSoundAndVibrationSettings(
             final SharedPreferences sp, final Resources res) {
         setPreferenceEnabled(Settings.PREF_VIBRATION_DURATION_SETTINGS,
diff --git a/java/src/com/android/inputmethod/latin/settings/SettingsValues.java b/java/src/com/android/inputmethod/latin/settings/SettingsValues.java
index a25cf62..ee322e9 100644
--- a/java/src/com/android/inputmethod/latin/settings/SettingsValues.java
+++ b/java/src/com/android/inputmethod/latin/settings/SettingsValues.java
@@ -22,6 +22,7 @@
 import android.util.Log;
 import android.view.inputmethod.EditorInfo;
 
+import com.android.inputmethod.annotations.UsedForTesting;
 import com.android.inputmethod.keyboard.internal.KeySpecParser;
 import com.android.inputmethod.latin.Dictionary;
 import com.android.inputmethod.latin.InputAttributes;
@@ -56,13 +57,14 @@
     public final SuggestedWords mSuggestPuncList;
     public final String mWordSeparators;
     public final CharSequence mHintToSaveText;
+    public final boolean mCurrentLanguageHasSpaces;
 
     // From preferences, in the same order as xml/prefs.xml:
     public final boolean mAutoCap;
     public final boolean mVibrateOn;
     public final boolean mSoundOn;
     public final boolean mKeyPreviewPopupOn;
-    private final String mVoiceMode;
+    private final boolean mShowsVoiceInputKey;
     public final boolean mIncludesOtherImesInLanguageSwitchList;
     public final boolean mShowsLanguageSwitchKey;
     public final boolean mUseContactsDict;
@@ -74,6 +76,7 @@
     public final boolean mGestureTrailEnabled;
     public final boolean mGestureFloatingPreviewTextEnabled;
     public final boolean mSlidingKeyInputPreviewEnabled;
+    public final boolean mPhraseGestureEnabled;
     public final int mKeyLongpressTimeout;
     public final Locale mLocale;
 
@@ -88,8 +91,8 @@
     public final float mAutoCorrectionThreshold;
     public final boolean mCorrectionEnabled;
     public final int mSuggestionVisibility;
-    private final boolean mVoiceKeyEnabled;
-    private final boolean mVoiceKeyOnMain;
+    public final boolean mBoostPersonalizationDictionaryForDebug;
+    public final boolean mUseOnlyPersonalizationDictionaryForDebug;
 
     // Setting values for additional features
     public final int[] mAdditionalFeaturesSettingValues =
@@ -117,6 +120,7 @@
         mSuggestPuncList = createSuggestPuncList(suggestPuncsSpec);
         mWordSeparators = res.getString(R.string.symbols_word_separators);
         mHintToSaveText = res.getText(R.string.hint_add_to_dictionary);
+        mCurrentLanguageHasSpaces = res.getBoolean(R.bool.current_language_has_spaces);
 
         // Store the input attributes
         if (null == inputAttributes) {
@@ -132,9 +136,7 @@
         mKeyPreviewPopupOn = Settings.readKeyPreviewPopupEnabled(prefs, res);
         mSlidingKeyInputPreviewEnabled = prefs.getBoolean(
                 Settings.PREF_SLIDING_KEY_INPUT_PREVIEW, true);
-        final String voiceModeMain = res.getString(R.string.voice_mode_main);
-        final String voiceModeOff = res.getString(R.string.voice_mode_off);
-        mVoiceMode = prefs.getString(Settings.PREF_VOICE_MODE, voiceModeMain);
+        mShowsVoiceInputKey = needsToShowVoiceInputKey(prefs, res);
         final String autoCorrectionThresholdRawValue = prefs.getString(
                 Settings.PREF_AUTO_CORRECTION_THRESHOLD,
                 res.getString(R.string.auto_correction_threshold_mode_index_modest));
@@ -154,12 +156,11 @@
         mKeyPreviewPopupDismissDelay = Settings.readKeyPreviewPopupDismissDelay(prefs, res);
         mAutoCorrectionThreshold = readAutoCorrectionThreshold(res,
                 autoCorrectionThresholdRawValue);
-        mVoiceKeyEnabled = mVoiceMode != null && !mVoiceMode.equals(voiceModeOff);
-        mVoiceKeyOnMain = mVoiceMode != null && mVoiceMode.equals(voiceModeMain);
         mGestureInputEnabled = Settings.readGestureInputEnabled(prefs, res);
         mGestureTrailEnabled = prefs.getBoolean(Settings.PREF_GESTURE_PREVIEW_TRAIL, true);
         mGestureFloatingPreviewTextEnabled = prefs.getBoolean(
                 Settings.PREF_GESTURE_FLOATING_PREVIEW_TEXT, true);
+        mPhraseGestureEnabled = Settings.readPhraseGestureEnabled(prefs, res);
         mCorrectionEnabled = mAutoCorrectEnabled && !mInputAttributes.mInputTypeNoAutoCorrect;
         final String showSuggestionsSetting = prefs.getString(
                 Settings.PREF_SHOW_SUGGESTIONS_SETTING,
@@ -168,6 +169,61 @@
         AdditionalFeaturesSettingUtils.readAdditionalFeaturesPreferencesIntoArray(
                 prefs, mAdditionalFeaturesSettingValues);
         mIsInternal = Settings.isInternal(prefs);
+        mBoostPersonalizationDictionaryForDebug =
+                Settings.readBoostPersonalizationDictionaryForDebug(prefs);
+        mUseOnlyPersonalizationDictionaryForDebug =
+                Settings.readUseOnlyPersonalizationDictionaryForDebug(prefs);
+    }
+
+    // Only for tests
+    private SettingsValues(final Locale locale) {
+        // TODO: locale is saved, but not used yet. May have to change this if tests require.
+        mLocale = locale;
+        mDelayUpdateOldSuggestions = 0;
+        mSymbolsPrecededBySpace = new int[] { '(', '[', '{', '&' };
+        Arrays.sort(mSymbolsPrecededBySpace);
+        mSymbolsFollowedBySpace = new int[] { '.', ',', ';', ':', '!', '?', ')', ']', '}', '&' };
+        Arrays.sort(mSymbolsFollowedBySpace);
+        mWordConnectors = new int[] { '\'', '-' };
+        Arrays.sort(mWordConnectors);
+        final String[] suggestPuncsSpec = new String[] { "!", "?", ",", ":", ";" };
+        mSuggestPuncList = createSuggestPuncList(suggestPuncsSpec);
+        mWordSeparators = "&\t \n()[]{}*&<>+=|.,;:!?/_\"";
+        mHintToSaveText = "Touch again to save";
+        mCurrentLanguageHasSpaces = true;
+        mInputAttributes = new InputAttributes(null, false /* isFullscreenMode */);
+        mAutoCap = true;
+        mVibrateOn = true;
+        mSoundOn = true;
+        mKeyPreviewPopupOn = true;
+        mSlidingKeyInputPreviewEnabled = true;
+        mShowsVoiceInputKey = true;
+        mIncludesOtherImesInLanguageSwitchList = false;
+        mShowsLanguageSwitchKey = true;
+        mUseContactsDict = true;
+        mUseDoubleSpacePeriod = true;
+        mBlockPotentiallyOffensive = true;
+        mAutoCorrectEnabled = true;
+        mBigramPredictionEnabled = true;
+        mKeyLongpressTimeout = 300;
+        mKeypressVibrationDuration = 5;
+        mKeypressSoundVolume = 1;
+        mKeyPreviewPopupDismissDelay = 70;
+        mAutoCorrectionThreshold = 1;
+        mGestureInputEnabled = true;
+        mGestureTrailEnabled = true;
+        mGestureFloatingPreviewTextEnabled = true;
+        mPhraseGestureEnabled = true;
+        mCorrectionEnabled = mAutoCorrectEnabled && !mInputAttributes.mInputTypeNoAutoCorrect;
+        mSuggestionVisibility = 0;
+        mIsInternal = false;
+        mBoostPersonalizationDictionaryForDebug = false;
+        mUseOnlyPersonalizationDictionaryForDebug = false;
+    }
+
+    @UsedForTesting
+    public static SettingsValues makeDummySettingsValuesForTest(final Locale locale) {
+        return new SettingsValues(locale);
     }
 
     public boolean isApplicationSpecifiedCompletionsOn() {
@@ -194,6 +250,10 @@
         return Arrays.binarySearch(mWordConnectors, code) >= 0;
     }
 
+    public boolean isWordCodePoint(final int code) {
+        return Character.isLetter(code) || isWordConnector(code);
+    }
+
     public boolean isUsuallyPrecededBySpace(final int code) {
         return Arrays.binarySearch(mSymbolsPrecededBySpace, code) >= 0;
     }
@@ -209,14 +269,10 @@
     public boolean isVoiceKeyEnabled(final EditorInfo editorInfo) {
         final boolean shortcutImeEnabled = SubtypeSwitcher.getInstance().isShortcutImeEnabled();
         final int inputType = (editorInfo != null) ? editorInfo.inputType : 0;
-        return shortcutImeEnabled && mVoiceKeyEnabled
+        return shortcutImeEnabled && mShowsVoiceInputKey
                 && !InputTypeUtils.isPasswordInputType(inputType);
     }
 
-    public boolean isVoiceKeyOnMain() {
-        return mVoiceKeyOnMain;
-    }
-
     public boolean isLanguageSwitchKeyEnabled() {
         if (!mShowsLanguageSwitchKey) {
             return false;
@@ -241,7 +297,9 @@
                 // TODO: Stop using KeySpceParser.getLabel().
                 puncList.add(new SuggestedWordInfo(KeySpecParser.getLabel(puncSpec),
                         SuggestedWordInfo.MAX_SCORE, SuggestedWordInfo.KIND_HARDCODED,
-                        Dictionary.TYPE_HARDCODED));
+                        Dictionary.DICTIONARY_HARDCODED,
+                        SuggestedWordInfo.NOT_AN_INDEX /* indexOfTouchPointOfSecondWord */,
+                        SuggestedWordInfo.NOT_A_CONFIDENCE /* autoCommitFirstWordConfidence */));
             }
         }
         return new SuggestedWords(puncList,
@@ -306,4 +364,18 @@
         }
         return autoCorrectionThreshold;
     }
+
+    private static boolean needsToShowVoiceInputKey(SharedPreferences prefs, Resources res) {
+        final String voiceModeMain = res.getString(R.string.voice_mode_main);
+        final String voiceMode = prefs.getString(Settings.PREF_VOICE_MODE_OBSOLETE, voiceModeMain);
+        final boolean showsVoiceInputKey = voiceMode == null || voiceMode.equals(voiceModeMain);
+        if (!showsVoiceInputKey) {
+            // Migrate settings from PREF_VOICE_MODE_OBSOLETE to PREF_VOICE_INPUT_KEY
+            // Set voiceModeMain as a value of obsolete voice mode settings.
+            prefs.edit().putString(Settings.PREF_VOICE_MODE_OBSOLETE, voiceModeMain).apply();
+            // Disable voice input key.
+            prefs.edit().putBoolean(Settings.PREF_VOICE_INPUT_KEY, false).apply();
+        }
+        return prefs.getBoolean(Settings.PREF_VOICE_INPUT_KEY, true);
+    }
 }
diff --git a/java/src/com/android/inputmethod/latin/spellcheck/AndroidWordLevelSpellCheckerSession.java b/java/src/com/android/inputmethod/latin/spellcheck/AndroidWordLevelSpellCheckerSession.java
index 6719e98..69f9a46 100644
--- a/java/src/com/android/inputmethod/latin/spellcheck/AndroidWordLevelSpellCheckerSession.java
+++ b/java/src/com/android/inputmethod/latin/spellcheck/AndroidWordLevelSpellCheckerSession.java
@@ -301,12 +301,14 @@
                 // TODO: make a spell checker option to block offensive words or not
                 final ArrayList<SuggestedWordInfo> suggestions =
                         dictInfo.mDictionary.getSuggestions(composer, prevWord,
-                                dictInfo.getProximityInfo(),
-                                true /* blockOffensiveWords */);
-                for (final SuggestedWordInfo suggestion : suggestions) {
-                    final String suggestionStr = suggestion.mWord;
-                    suggestionsGatherer.addWord(suggestionStr.toCharArray(), null, 0,
-                            suggestionStr.length(), suggestion.mScore);
+                                dictInfo.getProximityInfo(), true /* blockOffensiveWords */,
+                                null /* additionalFeaturesOptions */);
+                if (suggestions != null) {
+                    for (final SuggestedWordInfo suggestion : suggestions) {
+                        final String suggestionStr = suggestion.mWord;
+                        suggestionsGatherer.addWord(suggestionStr.toCharArray(), null, 0,
+                                suggestionStr.length(), suggestion.mScore);
+                    }
                 }
                 isInDict = isInDictForAnyCapitalization(dictInfo.mDictionary, text, capitalizeType);
             } finally {
diff --git a/java/src/com/android/inputmethod/latin/spellcheck/DictionaryPool.java b/java/src/com/android/inputmethod/latin/spellcheck/DictionaryPool.java
index ac8f687..a0aed28 100644
--- a/java/src/com/android/inputmethod/latin/spellcheck/DictionaryPool.java
+++ b/java/src/com/android/inputmethod/latin/spellcheck/DictionaryPool.java
@@ -52,7 +52,7 @@
                 @Override
                 public ArrayList<SuggestedWordInfo> getSuggestions(final WordComposer composer,
                         final String prevWord, final ProximityInfo proximityInfo,
-                        final boolean blockOffensiveWords) {
+                        final boolean blockOffensiveWords, final int[] additionalFeaturesOptions) {
                     return noSuggestions;
                 }
                 @Override
diff --git a/java/src/com/android/inputmethod/latin/suggestions/MoreSuggestions.java b/java/src/com/android/inputmethod/latin/suggestions/MoreSuggestions.java
index e97069d..acd4745 100644
--- a/java/src/com/android/inputmethod/latin/suggestions/MoreSuggestions.java
+++ b/java/src/com/android/inputmethod/latin/suggestions/MoreSuggestions.java
@@ -210,7 +210,8 @@
                 final int indexInMoreSuggestions = index + SUGGESTION_CODE_BASE;
                 final Key key = new Key(
                         params, word, info, KeyboardIconsSet.ICON_UNDEFINED, indexInMoreSuggestions,
-                        null, x, y, width, params.mDefaultRowHeight, 0);
+                        null /* outputText */, x, y, width, params.mDefaultRowHeight,
+                        0 /* labelFlags */, Key.BACKGROUND_TYPE_NORMAL);
                 params.markAsEdgeKey(key, index);
                 params.onAddKey(key);
                 final int columnNumber = params.getColumnNumber(index);
diff --git a/java/src/com/android/inputmethod/latin/suggestions/MoreSuggestionsView.java b/java/src/com/android/inputmethod/latin/suggestions/MoreSuggestionsView.java
index d585b5c..0ebe377 100644
--- a/java/src/com/android/inputmethod/latin/suggestions/MoreSuggestionsView.java
+++ b/java/src/com/android/inputmethod/latin/suggestions/MoreSuggestionsView.java
@@ -34,7 +34,7 @@
     private static final String TAG = MoreSuggestionsView.class.getSimpleName();
 
     public MoreSuggestionsView(final Context context, final AttributeSet attrs) {
-        this(context, attrs, R.attr.moreSuggestionsViewStyle);
+        this(context, attrs, R.attr.moreKeysKeyboardViewStyle);
     }
 
     public MoreSuggestionsView(final Context context, final AttributeSet attrs,
diff --git a/java/src/com/android/inputmethod/latin/suggestions/SuggestionStripLayoutHelper.java b/java/src/com/android/inputmethod/latin/suggestions/SuggestionStripLayoutHelper.java
index 1dd04fc..8d2689a 100644
--- a/java/src/com/android/inputmethod/latin/suggestions/SuggestionStripLayoutHelper.java
+++ b/java/src/com/android/inputmethod/latin/suggestions/SuggestionStripLayoutHelper.java
@@ -122,27 +122,15 @@
         mSuggestionsStripHeight = res.getDimensionPixelSize(R.dimen.suggestions_strip_height);
 
         final TypedArray a = context.obtainStyledAttributes(attrs,
-                R.styleable.SuggestionStripView, defStyle, R.style.SuggestionStripViewStyle);
+                R.styleable.SuggestionStripView, defStyle, R.style.SuggestionStripView);
         mSuggestionStripOption = a.getInt(
                 R.styleable.SuggestionStripView_suggestionStripOption, 0);
-        final float alphaValidTypedWord = ResourceUtils.getFraction(a,
-                R.styleable.SuggestionStripView_alphaValidTypedWord, 1.0f);
-        final float alphaTypedWord = ResourceUtils.getFraction(a,
-                R.styleable.SuggestionStripView_alphaTypedWord, 1.0f);
-        final float alphaAutoCorrect = ResourceUtils.getFraction(a,
-                R.styleable.SuggestionStripView_alphaAutoCorrect, 1.0f);
-        final float alphaSuggested = ResourceUtils.getFraction(a,
-                R.styleable.SuggestionStripView_alphaSuggested, 1.0f);
         mAlphaObsoleted = ResourceUtils.getFraction(a,
-                R.styleable.SuggestionStripView_alphaSuggested, 1.0f);
-        mColorValidTypedWord = applyAlpha(a.getColor(
-                R.styleable.SuggestionStripView_colorValidTypedWord, 0), alphaValidTypedWord);
-        mColorTypedWord = applyAlpha(a.getColor(
-                R.styleable.SuggestionStripView_colorTypedWord, 0), alphaTypedWord);
-        mColorAutoCorrect = applyAlpha(a.getColor(
-                R.styleable.SuggestionStripView_colorAutoCorrect, 0), alphaAutoCorrect);
-        mColorSuggested = applyAlpha(a.getColor(
-                R.styleable.SuggestionStripView_colorSuggested, 0), alphaSuggested);
+                R.styleable.SuggestionStripView_alphaObsoleted, 1.0f);
+        mColorValidTypedWord = a.getColor(R.styleable.SuggestionStripView_colorValidTypedWord, 0);
+        mColorTypedWord = a.getColor(R.styleable.SuggestionStripView_colorTypedWord, 0);
+        mColorAutoCorrect = a.getColor(R.styleable.SuggestionStripView_colorAutoCorrect, 0);
+        mColorSuggested = a.getColor(R.styleable.SuggestionStripView_colorSuggested, 0);
         mSuggestionsCountInStrip = a.getInt(
                 R.styleable.SuggestionStripView_suggestionsCountInStrip,
                 DEFAULT_SUGGESTIONS_COUNT_IN_STRIP);
diff --git a/java/src/com/android/inputmethod/latin/suggestions/SuggestionStripView.java b/java/src/com/android/inputmethod/latin/suggestions/SuggestionStripView.java
index a8a14a8..75f17c5 100644
--- a/java/src/com/android/inputmethod/latin/suggestions/SuggestionStripView.java
+++ b/java/src/com/android/inputmethod/latin/suggestions/SuggestionStripView.java
@@ -198,7 +198,7 @@
 
     @Override
     public boolean onLongClick(final View view) {
-        AudioAndHapticFeedbackManager.getInstance().hapticAndAudioFeedback(
+        AudioAndHapticFeedbackManager.getInstance().performHapticAndAudioFeedback(
                 Constants.NOT_A_CODE, this);
         return showMoreSuggestions();
     }
diff --git a/java/src/com/android/inputmethod/latin/utils/AdditionalSubtypeUtils.java b/java/src/com/android/inputmethod/latin/utils/AdditionalSubtypeUtils.java
index 215faa0..44b2016 100644
--- a/java/src/com/android/inputmethod/latin/utils/AdditionalSubtypeUtils.java
+++ b/java/src/com/android/inputmethod/latin/utils/AdditionalSubtypeUtils.java
@@ -25,6 +25,7 @@
 import android.text.TextUtils;
 import android.view.inputmethod.InputMethodSubtype;
 
+import com.android.inputmethod.latin.Constants;
 import com.android.inputmethod.latin.R;
 
 import java.util.ArrayList;
@@ -60,9 +61,10 @@
                 StringUtils.appendToCommaSplittableTextIfNotExists(
                         IS_ADDITIONAL_SUBTYPE, layoutDisplayNameExtraValue);
         final int nameId = SubtypeLocaleUtils.getSubtypeNameId(localeString, keyboardLayoutSetName);
-        return new InputMethodSubtype(nameId, R.drawable.ic_subtype_keyboard,
-                localeString, KEYBOARD_MODE,
-                layoutExtraValue + "," + additionalSubtypeExtraValue, false, false);
+        return new InputMethodSubtype(nameId, R.drawable.ic_ime_switcher_dark,
+                localeString, KEYBOARD_MODE, layoutExtraValue + "," + additionalSubtypeExtraValue
+                        + "," + Constants.Subtype.ExtraValue.ASCII_CAPABLE
+                        + "," + Constants.Subtype.ExtraValue.EMOJI_CAPABLE, false, false);
     }
 
     public static String getPrefSubtype(final InputMethodSubtype subtype) {
diff --git a/java/src/com/android/inputmethod/latin/utils/AsyncResultHolder.java b/java/src/com/android/inputmethod/latin/utils/AsyncResultHolder.java
new file mode 100644
index 0000000..c2e97a3
--- /dev/null
+++ b/java/src/com/android/inputmethod/latin/utils/AsyncResultHolder.java
@@ -0,0 +1,71 @@
+/*
+ * Copyright (C) 2013 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.latin.utils;
+
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+
+/**
+ * This class is a holder of a result of asynchronous computation.
+ *
+ * @param <E> the type of the result.
+ */
+public class AsyncResultHolder<E> {
+
+    private final Object mLock = new Object();
+
+    private E mResult;
+    private final CountDownLatch mLatch;
+
+    public AsyncResultHolder() {
+        mLatch = new CountDownLatch(1);
+    }
+
+    /**
+     * Sets the result value to this holder.
+     *
+     * @param result the value which is set.
+     */
+    public void set(final E result) {
+        synchronized(mLock) {
+            if (mLatch.getCount() > 0) {
+                mResult = result;
+                mLatch.countDown();
+            }
+        }
+    }
+
+    /**
+     * Gets the result value held in this holder.
+     * Causes the current thread to wait unless the value is set or the specified time is elapsed.
+     *
+     * @param defaultValue the default value.
+     * @param timeOut the time to wait.
+     * @return if the result is set until the time limit then the result, otherwise defaultValue.
+     */
+    public E get(final E defaultValue, final long timeOut) {
+        try {
+            if(mLatch.await(timeOut, TimeUnit.MILLISECONDS)) {
+                return mResult;
+            } else {
+                return defaultValue;
+            }
+        } catch (InterruptedException e) {
+            return defaultValue;
+        }
+    }
+}
diff --git a/java/src/com/android/inputmethod/latin/utils/ByteArrayWrapper.java b/java/src/com/android/inputmethod/latin/utils/ByteArrayDictBuffer.java
similarity index 89%
rename from java/src/com/android/inputmethod/latin/utils/ByteArrayWrapper.java
rename to java/src/com/android/inputmethod/latin/utils/ByteArrayDictBuffer.java
index 1bb27aa..2028298 100644
--- a/java/src/com/android/inputmethod/latin/utils/ByteArrayWrapper.java
+++ b/java/src/com/android/inputmethod/latin/utils/ByteArrayDictBuffer.java
@@ -16,17 +16,17 @@
 
 package com.android.inputmethod.latin.utils;
 
-import com.android.inputmethod.latin.makedict.BinaryDictInputOutput.FusionDictionaryBufferInterface;
+import com.android.inputmethod.latin.makedict.BinaryDictDecoderUtils.DictBuffer;
 
 /**
  * This class provides an implementation for the FusionDictionary buffer interface that is backed
  * by a simpled byte array. It allows to create a binary dictionary in memory.
  */
-public final class ByteArrayWrapper implements FusionDictionaryBufferInterface {
+public final class ByteArrayDictBuffer implements DictBuffer {
     private byte[] mBuffer;
     private int mPosition;
 
-    public ByteArrayWrapper(final byte[] buffer) {
+    public ByteArrayDictBuffer(final byte[] buffer) {
         mBuffer = buffer;
         mPosition = 0;
     }
diff --git a/java/src/com/android/inputmethod/latin/utils/CapsModeUtils.java b/java/src/com/android/inputmethod/latin/utils/CapsModeUtils.java
index 2f91c57..60b24d5 100644
--- a/java/src/com/android/inputmethod/latin/utils/CapsModeUtils.java
+++ b/java/src/com/android/inputmethod/latin/utils/CapsModeUtils.java
@@ -60,6 +60,11 @@
                 || WordComposer.CAPS_MODE_AUTO_SHIFT_LOCKED == mode;
     }
 
+    private static boolean isPeriod(final int codePoint) {
+        // TODO: make this a resource.
+        return codePoint == Constants.CODE_PERIOD || codePoint == Constants.CODE_ARMENIAN_PERIOD;
+    }
+
     /**
      * Determine what caps mode should be in effect at the current offset in
      * the text. Only the mode bits set in <var>reqModes</var> will be
@@ -190,7 +195,7 @@
         if (c == Constants.CODE_QUESTION_MARK || c == Constants.CODE_EXCLAMATION_MARK) {
             return (TextUtils.CAP_MODE_CHARACTERS | TextUtils.CAP_MODE_SENTENCES) & reqModes;
         }
-        if (c != Constants.CODE_PERIOD || j <= 0) {
+        if (!isPeriod(c) || j <= 0) {
             return (TextUtils.CAP_MODE_CHARACTERS | TextUtils.CAP_MODE_WORDS) & reqModes;
         }
 
@@ -240,7 +245,7 @@
             case WORD:
                 if (Character.isLetter(c)) {
                     state = WORD;
-                } else if (c == Constants.CODE_PERIOD) {
+                } else if (isPeriod(c)) {
                     state = PERIOD;
                 } else {
                     return caps;
@@ -256,7 +261,7 @@
             case LETTER:
                 if (Character.isLetter(c)) {
                     state = LETTER;
-                } else if (c == Constants.CODE_PERIOD) {
+                } else if (isPeriod(c)) {
                     state = PERIOD;
                 } else {
                     return noCaps;
diff --git a/java/src/com/android/inputmethod/latin/utils/CollectionUtils.java b/java/src/com/android/inputmethod/latin/utils/CollectionUtils.java
index 98f0d8b..cc25102 100644
--- a/java/src/com/android/inputmethod/latin/utils/CollectionUtils.java
+++ b/java/src/com/android/inputmethod/latin/utils/CollectionUtils.java
@@ -18,6 +18,7 @@
 
 import android.util.SparseArray;
 
+import java.util.ArrayDeque;
 import java.util.ArrayList;
 import java.util.Collection;
 import java.util.Collections;
@@ -94,6 +95,10 @@
         return new CopyOnWriteArrayList<E>(array);
     }
 
+    public static <E> ArrayDeque<E> newArrayDeque() {
+        return new ArrayDeque<E>();
+    }
+
     public static <E> SparseArray<E> newSparseArray() {
         return new SparseArray<E>();
     }
diff --git a/java/src/com/android/inputmethod/latin/utils/DebugLogUtils.java b/java/src/com/android/inputmethod/latin/utils/DebugLogUtils.java
index c4ead0a..ac654fa 100644
--- a/java/src/com/android/inputmethod/latin/utils/DebugLogUtils.java
+++ b/java/src/com/android/inputmethod/latin/utils/DebugLogUtils.java
@@ -65,12 +65,12 @@
 
     /**
      * Get the stack trace contained in an exception as a human-readable string.
-     * @param e the exception
+     * @param t the throwable
      * @return the human-readable stack trace
      */
-    public static String getStackTrace(final Exception e) {
+    public static String getStackTrace(final Throwable t) {
         final StringBuilder sb = new StringBuilder();
-        final StackTraceElement[] frames = e.getStackTrace();
+        final StackTraceElement[] frames = t.getStackTrace();
         for (int j = 0; j < frames.length; ++j) {
             sb.append(frames[j].toString() + "\n");
         }
diff --git a/java/src/com/android/inputmethod/latin/utils/DictionaryInfoUtils.java b/java/src/com/android/inputmethod/latin/utils/DictionaryInfoUtils.java
index 34eccd6..021bf08 100644
--- a/java/src/com/android/inputmethod/latin/utils/DictionaryInfoUtils.java
+++ b/java/src/com/android/inputmethod/latin/utils/DictionaryInfoUtils.java
@@ -27,10 +27,8 @@
 import com.android.inputmethod.latin.R;
 import com.android.inputmethod.latin.makedict.BinaryDictIOUtils;
 import com.android.inputmethod.latin.makedict.FormatSpec.FileHeader;
-import com.android.inputmethod.latin.makedict.UnsupportedFormatException;
 
 import java.io.File;
-import java.io.IOException;
 import java.util.ArrayList;
 import java.util.Iterator;
 import java.util.Locale;
@@ -281,13 +279,7 @@
     }
 
     public static FileHeader getDictionaryFileHeaderOrNull(final File file) {
-        try {
-            return BinaryDictIOUtils.getDictionaryFileHeader(file, 0, file.length());
-        } catch (UnsupportedFormatException e) {
-            return null;
-        } catch (IOException e) {
-            return null;
-        }
+        return BinaryDictIOUtils.getDictionaryFileHeaderOrNull(file, 0, file.length());
     }
 
     private static DictionaryInfo createDictionaryInfoFromFileAddress(
diff --git a/java/src/com/android/inputmethod/latin/utils/PositionalInfoForUserDictPendingAddition.java b/java/src/com/android/inputmethod/latin/utils/PositionalInfoForUserDictPendingAddition.java
deleted file mode 100644
index 1fc7ecc..0000000
--- a/java/src/com/android/inputmethod/latin/utils/PositionalInfoForUserDictPendingAddition.java
+++ /dev/null
@@ -1,108 +0,0 @@
-/*
- * Copyright (C) 2012 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.latin.utils;
-
-import android.view.inputmethod.EditorInfo;
-
-import com.android.inputmethod.latin.RichInputConnection;
-
-import java.util.Locale;
-
-/**
- * Holder class for data about a word already committed but that may still be edited.
- *
- * When the user chooses to add a word to the user dictionary by pressing the appropriate
- * suggestion, a dialog is presented to give a chance to edit the word before it is actually
- * registered as a user dictionary word. If the word is actually modified, the IME needs to
- * go back and replace the word that was committed with the amended version.
- * The word we need to replace with will only be known after it's actually committed, so
- * the IME needs to take a note of what it has to replace and where it is.
- * This class encapsulates this data.
- */
-public final class PositionalInfoForUserDictPendingAddition {
-    final private String mOriginalWord;
-    final private int mCursorPos; // Position of the cursor after the word
-    final private EditorInfo mEditorInfo; // On what binding this has been added
-    final private int mCapitalizedMode;
-    private String mActualWordBeingAdded;
-
-    public PositionalInfoForUserDictPendingAddition(final String word, final int cursorPos,
-            final EditorInfo editorInfo, final int capitalizedMode) {
-        mOriginalWord = word;
-        mCursorPos = cursorPos;
-        mEditorInfo = editorInfo;
-        mCapitalizedMode = capitalizedMode;
-    }
-
-    public void setActualWordBeingAdded(final String actualWordBeingAdded) {
-        mActualWordBeingAdded = actualWordBeingAdded;
-    }
-
-    /**
-     * Try to replace the string at the remembered position with the actual word being added.
-     *
-     * After the user validated the word being added, the IME has to replace the old version
-     * (which has been committed in the text view) with the amended version if it's different.
-     * This method tries to do that, but may fail because the IME is not yet ready to do so -
-     * for example, it is still waiting for the new string, or it is waiting to return to the text
-     * view in which the amendment should be made. In these cases, we should keep the data
-     * and wait until all conditions are met.
-     * This method returns true if the replacement has been successfully made and this data
-     * can be forgotten; it returns false if the replacement can't be made yet and we need to
-     * keep this until a later time.
-     * The IME knows about the actual word being added through a callback called by the
-     * user dictionary facility of the device. When this callback comes, the keyboard may still
-     * be connected to the edition dialog, or it may have already returned to the original text
-     * field. Replacement has to work in both cases.
-     * Accordingly, this method is called at two different points in time : upon getting the
-     * event that a new word was added to the user dictionary, and upon starting up in a
-     * new text field.
-     * @param connection The RichInputConnection through which to contact the editor.
-     * @param editorInfo Information pertaining to the editor we are currently in.
-     * @param currentCursorPosition The current cursor position, for checking purposes.
-     * @param locale The locale for changing case, if necessary
-     * @return true if the edit has been successfully made, false if we need to try again later
-     */
-    public boolean tryReplaceWithActualWord(final RichInputConnection connection,
-            final EditorInfo editorInfo, final int currentCursorPosition, final Locale locale) {
-        // If we still don't know the actual word being added, we need to try again later.
-        if (null == mActualWordBeingAdded) return false;
-        // The entered text and the registered text were the same anyway : we can
-        // return success right away even if focus has not returned yet to the text field we
-        // want to amend.
-        if (mActualWordBeingAdded.equals(mOriginalWord)) return true;
-        // Not the same text field : we need to try again later. This happens when the addition
-        // is reported by the user dictionary provider before the focus has moved back to the
-        // original text view, so the IME is still in the text view of the dialog and has no way to
-        // edit the original text view at this time.
-        if (!mEditorInfo.packageName.equals(editorInfo.packageName)
-                || mEditorInfo.fieldId != editorInfo.fieldId) {
-            return false;
-        }
-        // Same text field, but not the same cursor position : we give up, so we return success
-        // so that it won't be tried again
-        if (currentCursorPosition != mCursorPos) return true;
-        // We have made all the checks : do the replacement and report success
-        // If this was auto-capitalized, we need to restore the case before committing
-        final String wordWithCaseFixed = CapsModeUtils.applyAutoCapsMode(mActualWordBeingAdded,
-                mCapitalizedMode, locale);
-        connection.setComposingRegion(currentCursorPosition - mOriginalWord.length(),
-                currentCursorPosition);
-        connection.commitText(wordWithCaseFixed, wordWithCaseFixed.length());
-        return true;
-    }
-}
diff --git a/java/src/com/android/inputmethod/latin/utils/PrioritizedSerialExecutor.java b/java/src/com/android/inputmethod/latin/utils/PrioritizedSerialExecutor.java
new file mode 100644
index 0000000..5dc0b58
--- /dev/null
+++ b/java/src/com/android/inputmethod/latin/utils/PrioritizedSerialExecutor.java
@@ -0,0 +1,147 @@
+/*
+ * Copyright (C) 2013 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.latin.utils;
+
+import java.util.ArrayDeque;
+import java.util.Queue;
+
+/**
+ * An object that executes submitted tasks using a thread.
+ */
+public class PrioritizedSerialExecutor {
+    public static final String TAG = PrioritizedSerialExecutor.class.getSimpleName();
+
+    private final Object mLock = new Object();
+
+    // The default value of capacities of task queues.
+    private static final int TASK_QUEUE_CAPACITY = 1000;
+    private final Queue<Runnable> mTasks;
+    private final Queue<Runnable> mPrioritizedTasks;
+    private boolean mIsShutdown;
+
+    // The task which is running now.
+    private Runnable mActive;
+
+    public PrioritizedSerialExecutor() {
+        mTasks = new ArrayDeque<Runnable>(TASK_QUEUE_CAPACITY);
+        mPrioritizedTasks = new ArrayDeque<Runnable>(TASK_QUEUE_CAPACITY);
+        mIsShutdown = false;
+    }
+
+    /**
+     * Clears all queued tasks.
+     */
+    public void clearAllTasks() {
+        synchronized(mLock) {
+            mTasks.clear();
+            mPrioritizedTasks.clear();
+        }
+    }
+
+    /**
+     * Enqueues the given task into the task queue.
+     * @param r the enqueued task
+     */
+    public void execute(final Runnable r) {
+        synchronized(mLock) {
+            if (!mIsShutdown) {
+                mTasks.offer(r);
+                if (mActive == null) {
+                    scheduleNext();
+                }
+            }
+        }
+    }
+
+    /**
+     * Enqueues the given task into the prioritized task queue.
+     * @param r the enqueued task
+     */
+    public void executePrioritized(final Runnable r) {
+        synchronized(mLock) {
+            if (!mIsShutdown) {
+                mPrioritizedTasks.offer(r);
+                if (mActive ==  null) {
+                    scheduleNext();
+                }
+            }
+        }
+    }
+
+    private boolean fetchNextTasks() {
+        synchronized(mLock) {
+            mActive = mPrioritizedTasks.poll();
+            if (mActive == null) {
+                mActive = mTasks.poll();
+            }
+            return mActive != null;
+        }
+    }
+
+    private void scheduleNext() {
+        synchronized(mLock) {
+            if (!fetchNextTasks()) {
+                return;
+            }
+            new Thread(new Runnable() {
+                @Override
+                public void run() {
+                    try {
+                        do {
+                            synchronized(mLock) {
+                                if (mActive != null) {
+                                    mActive.run();
+                                }
+                            }
+                        } while (fetchNextTasks());
+                    } finally {
+                        scheduleNext();
+                    }
+                }
+            }).start();
+        }
+    }
+
+    public void remove(final Runnable r) {
+        synchronized(mLock) {
+            mTasks.remove(r);
+            mPrioritizedTasks.remove(r);
+        }
+    }
+
+    public void replaceAndExecute(final Runnable oldTask, final Runnable newTask) {
+        synchronized(mLock) {
+            if (oldTask != null) remove(oldTask);
+            execute(newTask);
+        }
+    }
+
+    public void shutdown() {
+        synchronized(mLock) {
+            mIsShutdown = true;
+        }
+    }
+
+    public boolean isTerminated() {
+        synchronized(mLock) {
+            if (!mIsShutdown) {
+                return false;
+            }
+            return mPrioritizedTasks.isEmpty() && mTasks.isEmpty() && mActive == null;
+        }
+    }
+}
diff --git a/java/src/com/android/inputmethod/latin/utils/ResizableIntArray.java b/java/src/com/android/inputmethod/latin/utils/ResizableIntArray.java
index 4c7739a..7c6fe93 100644
--- a/java/src/com/android/inputmethod/latin/utils/ResizableIntArray.java
+++ b/java/src/com/android/inputmethod/latin/utils/ResizableIntArray.java
@@ -132,6 +132,15 @@
         }
     }
 
+    /**
+     * Shift to the left by elementCount, discarding elementCount pointers at the start.
+     * @param elementCount how many elements to shift.
+     */
+    public void shift(final int elementCount) {
+        System.arraycopy(mArray, elementCount, mArray, 0, mLength - elementCount);
+        mLength -= elementCount;
+    }
+
     @Override
     public String toString() {
         final StringBuilder sb = new StringBuilder();
diff --git a/java/src/com/android/inputmethod/latin/utils/StringUtils.java b/java/src/com/android/inputmethod/latin/utils/StringUtils.java
index 7406d85..327780a 100644
--- a/java/src/com/android/inputmethod/latin/utils/StringUtils.java
+++ b/java/src/com/android/inputmethod/latin/utils/StringUtils.java
@@ -16,14 +16,30 @@
 
 package com.android.inputmethod.latin.utils;
 
-import android.text.TextUtils;
-
+import com.android.inputmethod.annotations.UsedForTesting;
 import com.android.inputmethod.latin.Constants;
+import com.android.inputmethod.latin.settings.SettingsValues;
 
+import android.text.Spannable;
+import android.text.SpannableString;
+import android.text.Spanned;
+import android.text.SpannedString;
+import android.text.TextUtils;
+import android.text.style.SuggestionSpan;
+import android.util.JsonReader;
+import android.util.JsonWriter;
+import android.util.Log;
+
+import java.io.IOException;
+import java.io.StringReader;
+import java.io.StringWriter;
 import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
 import java.util.Locale;
 
 public final class StringUtils {
+    private static final String TAG = StringUtils.class.getSimpleName();
     public static final int CAPITALIZE_NONE = 0;  // No caps, or mixed case
     public static final int CAPITALIZE_FIRST = 1; // First only
     public static final int CAPITALIZE_ALL = 2;   // All caps
@@ -193,27 +209,56 @@
     }
 
     public static boolean isIdenticalAfterUpcase(final String text) {
-        final int len = text.length();
-        for (int i = 0; i < len; i = text.offsetByCodePoints(i, 1)) {
+        final int length = text.length();
+        int i = 0;
+        while (i < length) {
             final int codePoint = text.codePointAt(i);
             if (Character.isLetter(codePoint) && !Character.isUpperCase(codePoint)) {
                 return false;
             }
+            i += Character.charCount(codePoint);
         }
         return true;
     }
 
     public static boolean isIdenticalAfterDowncase(final String text) {
-        final int len = text.length();
-        for (int i = 0; i < len; i = text.offsetByCodePoints(i, 1)) {
+        final int length = text.length();
+        int i = 0;
+        while (i < length) {
             final int codePoint = text.codePointAt(i);
             if (Character.isLetter(codePoint) && !Character.isLowerCase(codePoint)) {
                 return false;
             }
+            i += Character.charCount(codePoint);
         }
         return true;
     }
 
+    @UsedForTesting
+    public static boolean looksValidForDictionaryInsertion(final CharSequence text,
+            final SettingsValues settings) {
+        if (TextUtils.isEmpty(text)) return false;
+        final int length = text.length();
+        int i = 0;
+        int digitCount = 0;
+        while (i < length) {
+            final int codePoint = Character.codePointAt(text, i);
+            final int charCount = Character.charCount(codePoint);
+            i += charCount;
+            if (Character.isDigit(codePoint)) {
+                // Count digits: see below
+                digitCount += charCount;
+                continue;
+            }
+            if (!settings.isWordCodePoint(codePoint)) return false;
+        }
+        // We reject strings entirely comprised of digits to avoid using PIN codes or credit
+        // card numbers. It would come in handy for word prediction though; a good example is
+        // when writing one's address where the street number is usually quite discriminative,
+        // as well as the postal code.
+        return digitCount < length;
+    }
+
     public static boolean isIdenticalAfterCapitalizeEachWord(final String text,
             final String separators) {
         boolean needCapsNext = true;
@@ -316,4 +361,194 @@
         // Otherwise, it doesn't look like an URL.
         return false;
     }
+
+    public static boolean isEmptyStringOrWhiteSpaces(String s) {
+        final int N = codePointCount(s);
+        for (int i = 0; i < N; ++i) {
+            if (!Character.isWhitespace(s.codePointAt(i))) {
+                return false;
+            }
+        }
+        return true;
+    }
+
+    @UsedForTesting
+    public static String byteArrayToHexString(byte[] bytes) {
+        if (bytes == null || bytes.length == 0) {
+            return "";
+        }
+        final StringBuilder sb = new StringBuilder();
+        for (byte b : bytes) {
+            sb.append(String.format("%02x", b & 0xff));
+        }
+        return sb.toString();
+    }
+
+    /**
+     * Convert hex string to byte array. The string length must be an even number.
+     */
+    @UsedForTesting
+    public static byte[] hexStringToByteArray(String hexString) {
+        if (TextUtils.isEmpty(hexString)) {
+            return null;
+        }
+        final int N = hexString.length();
+        if (N % 2 != 0) {
+            throw new NumberFormatException("Input hex string length must be an even number."
+                    + " Length = " + N);
+        }
+        final byte[] bytes = new byte[N / 2];
+        for (int i = 0; i < N; i += 2) {
+            bytes[i / 2] = (byte) ((Character.digit(hexString.charAt(i), 16) << 4)
+                    + Character.digit(hexString.charAt(i + 1), 16));
+        }
+        return bytes;
+    }
+
+    public static List<Object> jsonStrToList(String s) {
+        final ArrayList<Object> retval = CollectionUtils.newArrayList();
+        final JsonReader reader = new JsonReader(new StringReader(s));
+        try {
+            reader.beginArray();
+            while(reader.hasNext()) {
+                reader.beginObject();
+                while (reader.hasNext()) {
+                    final String name = reader.nextName();
+                    if (name.equals(Integer.class.getSimpleName())) {
+                        retval.add(reader.nextInt());
+                    } else if (name.equals(String.class.getSimpleName())) {
+                        retval.add(reader.nextString());
+                    } else {
+                        Log.w(TAG, "Invalid name: " + name);
+                        reader.skipValue();
+                    }
+                }
+                reader.endObject();
+            }
+            reader.endArray();
+            return retval;
+        } catch (IOException e) {
+        } finally {
+            try {
+                reader.close();
+            } catch (IOException e) {
+            }
+        }
+        return Collections.<Object>emptyList();
+    }
+
+    public static String listToJsonStr(List<Object> list) {
+        if (list == null || list.isEmpty()) {
+            return "";
+        }
+        final StringWriter sw = new StringWriter();
+        final JsonWriter writer = new JsonWriter(sw);
+        try {
+            writer.beginArray();
+            for (final Object o : list) {
+                writer.beginObject();
+                if (o instanceof Integer) {
+                    writer.name(Integer.class.getSimpleName()).value((Integer)o);
+                } else if (o instanceof String) {
+                    writer.name(String.class.getSimpleName()).value((String)o);
+                }
+                writer.endObject();
+            }
+            writer.endArray();
+            return sw.toString();
+        } catch (IOException e) {
+        } finally {
+            try {
+                if (writer != null) {
+                    writer.close();
+                }
+            } catch (IOException e) {
+            }
+        }
+        return "";
+    }
+
+    /**
+     * Copies the spans from the region <code>start...end</code> in
+     * <code>source</code> to the region
+     * <code>destoff...destoff+end-start</code> in <code>dest</code>.
+     * Spans in <code>source</code> that begin before <code>start</code>
+     * or end after <code>end</code> but overlap this range are trimmed
+     * as if they began at <code>start</code> or ended at <code>end</code>.
+     * Only SuggestionSpans that don't have the SPAN_PARAGRAPH span are copied.
+     *
+     * This code is almost entirely taken from {@link TextUtils#copySpansFrom}, except for the
+     * kind of span that is copied.
+     *
+     * @throws IndexOutOfBoundsException if any of the copied spans
+     * are out of range in <code>dest</code>.
+     */
+    public static void copyNonParagraphSuggestionSpansFrom(Spanned source, int start, int end,
+                                     Spannable dest, int destoff) {
+        Object[] spans = source.getSpans(start, end, SuggestionSpan.class);
+
+        for (int i = 0; i < spans.length; i++) {
+            int fl = source.getSpanFlags(spans[i]);
+            if (0 != (fl & Spannable.SPAN_PARAGRAPH)) continue;
+
+            int st = source.getSpanStart(spans[i]);
+            int en = source.getSpanEnd(spans[i]);
+
+            if (st < start)
+                st = start;
+            if (en > end)
+                en = end;
+
+            dest.setSpan(spans[i], st - start + destoff, en - start + destoff,
+                         fl);
+        }
+    }
+
+    /**
+     * Returns a CharSequence concatenating the specified CharSequences, retaining their
+     * SuggestionSpans that don't have the PARAGRAPH flag, but not other spans.
+     *
+     * This code is almost entirely taken from {@link TextUtils#concat(CharSequence...)}, except
+     * it calls copyNonParagraphSuggestionSpansFrom instead of {@link TextUtils#copySpansFrom}.
+     */
+    public static CharSequence concatWithNonParagraphSuggestionSpansOnly(CharSequence... text) {
+        if (text.length == 0) {
+            return "";
+        }
+
+        if (text.length == 1) {
+            return text[0];
+        }
+
+        boolean spanned = false;
+        for (int i = 0; i < text.length; i++) {
+            if (text[i] instanceof Spanned) {
+                spanned = true;
+                break;
+            }
+        }
+
+        StringBuilder sb = new StringBuilder();
+        for (int i = 0; i < text.length; i++) {
+            sb.append(text[i]);
+        }
+
+        if (!spanned) {
+            return sb.toString();
+        }
+
+        SpannableString ss = new SpannableString(sb);
+        int off = 0;
+        for (int i = 0; i < text.length; i++) {
+            int len = text[i].length();
+
+            if (text[i] instanceof Spanned) {
+                copyNonParagraphSuggestionSpansFrom((Spanned) text[i], 0, len, ss, off);
+            }
+
+            off += len;
+        }
+
+        return new SpannedString(ss);
+    }
 }
diff --git a/java/src/com/android/inputmethod/latin/utils/SubtypeLocaleUtils.java b/java/src/com/android/inputmethod/latin/utils/SubtypeLocaleUtils.java
index 1672809..102a41b 100644
--- a/java/src/com/android/inputmethod/latin/utils/SubtypeLocaleUtils.java
+++ b/java/src/com/android/inputmethod/latin/utils/SubtypeLocaleUtils.java
@@ -40,6 +40,7 @@
     // Special language code to represent "no language".
     public static final String NO_LANGUAGE = "zz";
     public static final String QWERTY = "qwerty";
+    public static final String EMOJI = "emoji";
     public static final int UNKNOWN_KEYBOARD_LAYOUT = R.string.subtype_generic;
 
     private static boolean sInitialized = false;
diff --git a/java/src/com/android/inputmethod/latin/utils/TextRange.java b/java/src/com/android/inputmethod/latin/utils/TextRange.java
index 5793e41..48b443d 100644
--- a/java/src/com/android/inputmethod/latin/utils/TextRange.java
+++ b/java/src/com/android/inputmethod/latin/utils/TextRange.java
@@ -40,6 +40,10 @@
         return mWordAtCursorEndIndex - mCursorIndex;
     }
 
+    public int length() {
+        return mWord.length();
+    }
+
     /**
      * Gets the suggestion spans that are put squarely on the word, with the exact start
      * and end of the span matching the boundaries of the word.
diff --git a/java/src/com/android/inputmethod/latin/utils/TypefaceUtils.java b/java/src/com/android/inputmethod/latin/utils/TypefaceUtils.java
index 544e4d2..47ea1ea 100644
--- a/java/src/com/android/inputmethod/latin/utils/TypefaceUtils.java
+++ b/java/src/com/android/inputmethod/latin/utils/TypefaceUtils.java
@@ -66,6 +66,11 @@
         }
     }
 
+    public static float getStringWidth(final String string, final Paint paint) {
+        paint.getTextBounds(string, 0, string.length(), sTextWidthBounds);
+        return sTextWidthBounds.width();
+    }
+
     private static int getCharGeometryCacheKey(final char referenceChar, final Paint paint) {
         final int labelSize = (int)paint.getTextSize();
         final Typeface face = paint.getTypeface();
diff --git a/java/src/com/android/inputmethod/latin/utils/UserHistoryDictIOUtils.java b/java/src/com/android/inputmethod/latin/utils/UserHistoryDictIOUtils.java
index d02f718..ea32a74 100644
--- a/java/src/com/android/inputmethod/latin/utils/UserHistoryDictIOUtils.java
+++ b/java/src/com/android/inputmethod/latin/utils/UserHistoryDictIOUtils.java
@@ -20,20 +20,21 @@
 
 import com.android.inputmethod.annotations.UsedForTesting;
 import com.android.inputmethod.latin.makedict.BinaryDictIOUtils;
-import com.android.inputmethod.latin.makedict.BinaryDictInputOutput;
-import com.android.inputmethod.latin.makedict.BinaryDictInputOutput.FusionDictionaryBufferInterface;
+import com.android.inputmethod.latin.makedict.DictDecoder;
+import com.android.inputmethod.latin.makedict.DictEncoder;
 import com.android.inputmethod.latin.makedict.FormatSpec.FormatOptions;
 import com.android.inputmethod.latin.makedict.FusionDictionary;
-import com.android.inputmethod.latin.makedict.FusionDictionary.Node;
+import com.android.inputmethod.latin.makedict.FusionDictionary.PtNodeArray;
 import com.android.inputmethod.latin.makedict.PendingAttribute;
 import com.android.inputmethod.latin.makedict.UnsupportedFormatException;
 import com.android.inputmethod.latin.personalization.UserHistoryDictionaryBigramList;
 
 import java.io.IOException;
-import java.io.OutputStream;
 import java.util.ArrayList;
 import java.util.HashMap;
-import java.util.Map;
+import java.util.Map.Entry;
+import java.util.TreeMap;
+import java.util.concurrent.TimeUnit;
 
 /**
  * Reads and writes Binary files for a UserHistoryDictionary.
@@ -43,6 +44,9 @@
 public final class UserHistoryDictIOUtils {
     private static final String TAG = UserHistoryDictIOUtils.class.getSimpleName();
     private static final boolean DEBUG = false;
+    private static final String USES_FORGETTING_CURVE_KEY = "USES_FORGETTING_CURVE";
+    private static final String USES_FORGETTING_CURVE_VALUE = "1";
+    private static final String LAST_UPDATED_TIME_KEY = "date";
 
     public interface OnAddWordListener {
         public void setUnigram(final String word, final String shortcutTarget, final int frequency);
@@ -57,12 +61,15 @@
     /**
      * Writes dictionary to file.
      */
-    public static void writeDictionaryBinary(final OutputStream destination,
+    public static void writeDictionary(final DictEncoder dictEncoder,
             final BigramDictionaryInterface dict, final UserHistoryDictionaryBigramList bigrams,
             final FormatOptions formatOptions) {
         final FusionDictionary fusionDict = constructFusionDictionary(dict, bigrams);
+        fusionDict.addOptionAttribute(USES_FORGETTING_CURVE_KEY, USES_FORGETTING_CURVE_VALUE);
+        fusionDict.addOptionAttribute(LAST_UPDATED_TIME_KEY,
+                String.valueOf(TimeUnit.MILLISECONDS.toSeconds(System.currentTimeMillis())));
         try {
-            BinaryDictInputOutput.writeDictionaryBinary(destination, fusionDict, formatOptions);
+            dictEncoder.writeDictionary(fusionDict, formatOptions);
             Log.d(TAG, "end writing");
         } catch (IOException e) {
             Log.e(TAG, "IO exception while writing file", e);
@@ -77,7 +84,7 @@
     @UsedForTesting
     static FusionDictionary constructFusionDictionary(
             final BigramDictionaryInterface dict, final UserHistoryDictionaryBigramList bigrams) {
-        final FusionDictionary fusionDict = new FusionDictionary(new Node(),
+        final FusionDictionary fusionDict = new FusionDictionary(new PtNodeArray(),
                 new FusionDictionary.DictionaryOptions(new HashMap<String, String>(), false,
                         false));
         int profTotal = 0;
@@ -101,7 +108,7 @@
                 if (word1 == null) { // unigram
                     fusionDict.add(word2, freq, null, false /* isNotAWord */);
                 } else { // bigram
-                    if (FusionDictionary.findWordInTree(fusionDict.mRoot, word1) == null) {
+                    if (FusionDictionary.findWordInTree(fusionDict.mRootNodeArray, word1) == null) {
                         fusionDict.add(word1, 2, null, false /* isNotAWord */);
                     }
                     fusionDict.setBigram(word1, word2, freq);
@@ -118,14 +125,13 @@
     /**
      * Reads dictionary from file.
      */
-    public static void readDictionaryBinary(final FusionDictionaryBufferInterface buffer,
+    public static void readDictionaryBinary(final DictDecoder dictDecoder,
             final OnAddWordListener dict) {
-        final Map<Integer, String> unigrams = CollectionUtils.newTreeMap();
-        final Map<Integer, Integer> frequencies = CollectionUtils.newTreeMap();
-        final Map<Integer, ArrayList<PendingAttribute>> bigrams = CollectionUtils.newTreeMap();
+        final TreeMap<Integer, String> unigrams = CollectionUtils.newTreeMap();
+        final TreeMap<Integer, Integer> frequencies = CollectionUtils.newTreeMap();
+        final TreeMap<Integer, ArrayList<PendingAttribute>> bigrams = CollectionUtils.newTreeMap();
         try {
-            BinaryDictIOUtils.readUnigramsAndBigramsBinary(buffer, unigrams, frequencies,
-                    bigrams);
+            dictDecoder.readUnigramsAndBigramsBinary(unigrams, frequencies, bigrams);
         } catch (IOException e) {
             Log.e(TAG, "IO exception while reading file", e);
         } catch (UnsupportedFormatException e) {
@@ -140,10 +146,11 @@
      * Adds all unigrams and bigrams in maps to OnAddWordListener.
      */
     @UsedForTesting
-    static void addWordsFromWordMap(final Map<Integer, String> unigrams,
-            final Map<Integer, Integer> frequencies,
-            final Map<Integer, ArrayList<PendingAttribute>> bigrams, final OnAddWordListener to) {
-        for (Map.Entry<Integer, String> entry : unigrams.entrySet()) {
+    static void addWordsFromWordMap(final TreeMap<Integer, String> unigrams,
+            final TreeMap<Integer, Integer> frequencies,
+            final TreeMap<Integer, ArrayList<PendingAttribute>> bigrams,
+            final OnAddWordListener to) {
+        for (Entry<Integer, String> entry : unigrams.entrySet()) {
             final String word1 = entry.getValue();
             final int unigramFrequency = frequencies.get(entry.getKey());
             to.setUnigram(word1, null, unigramFrequency);
@@ -156,7 +163,7 @@
                         continue;
                     }
                     to.setBigram(word1, word2,
-                            BinaryDictInputOutput.reconstructBigramFrequency(unigramFrequency,
+                            BinaryDictIOUtils.reconstructBigramFrequency(unigramFrequency,
                                     attr.mFrequency));
                 }
             }
diff --git a/java/src/com/android/inputmethod/latin/utils/UserHistoryForgettingCurveUtils.java b/java/src/com/android/inputmethod/latin/utils/UserHistoryForgettingCurveUtils.java
index 713a45b..1992b2f 100644
--- a/java/src/com/android/inputmethod/latin/utils/UserHistoryForgettingCurveUtils.java
+++ b/java/src/com/android/inputmethod/latin/utils/UserHistoryForgettingCurveUtils.java
@@ -23,7 +23,9 @@
 public final class UserHistoryForgettingCurveUtils {
     private static final String TAG = UserHistoryForgettingCurveUtils.class.getSimpleName();
     private static final boolean DEBUG = false;
-    private static final int FC_FREQ_MAX = 127;
+    private static final int DEFAULT_FC_FREQ = 127;
+    private static final int BOOSTED_FC_FREQ = 200;
+    private static int FC_FREQ_MAX = DEFAULT_FC_FREQ;
     /* package */ static final int COUNT_MAX = 3;
     private static final int FC_LEVEL_MAX = 3;
     /* package */ static final int ELAPSED_TIME_MAX = 15;
@@ -33,6 +35,14 @@
     private static final int HALF_LIFE_HOURS = 48;
     private static final int MAX_PUSH_ELAPSED = (FC_LEVEL_MAX + 1) * (ELAPSED_TIME_MAX + 1);
 
+    public static void boostMaxFreqForDebug() {
+        FC_FREQ_MAX = BOOSTED_FC_FREQ;
+    }
+
+    public static void resetMaxFreqForDebug() {
+        FC_FREQ_MAX = DEFAULT_FC_FREQ;
+    }
+
     private UserHistoryForgettingCurveUtils() {
         // This utility class is not publicly instantiable.
     }
diff --git a/java/src/com/android/inputmethod/latin/utils/UserLogRingCharBuffer.java b/java/src/com/android/inputmethod/latin/utils/UserLogRingCharBuffer.java
index 161386e..a75d353 100644
--- a/java/src/com/android/inputmethod/latin/utils/UserLogRingCharBuffer.java
+++ b/java/src/com/android/inputmethod/latin/utils/UserLogRingCharBuffer.java
@@ -19,6 +19,7 @@
 import android.inputmethodservice.InputMethodService;
 
 import com.android.inputmethod.annotations.UsedForTesting;
+import com.android.inputmethod.latin.LatinImeLogger;
 import com.android.inputmethod.latin.settings.Settings;
 
 public final class UserLogRingCharBuffer {
@@ -64,6 +65,9 @@
         if (!mEnabled) {
             return;
         }
+        if (LatinImeLogger.sUsabilityStudy) {
+            UsabilityStudyLogUtils.getInstance().writeChar(c, x, y);
+        }
         mCharBuf[mEnd] = c;
         mXBuf[mEnd] = x;
         mYBuf[mEnd] = y;
diff --git a/java/src/com/android/inputmethod/research/JsonUtils.java b/java/src/com/android/inputmethod/research/JsonUtils.java
index 63d524d..2beebdf 100644
--- a/java/src/com/android/inputmethod/research/JsonUtils.java
+++ b/java/src/com/android/inputmethod/research/JsonUtils.java
@@ -75,12 +75,12 @@
 
     private static void writeJson(final Key key, final JsonWriter jsonWriter) throws IOException {
         jsonWriter.beginObject();
-        jsonWriter.name("code").value(key.mCode);
+        jsonWriter.name("code").value(key.getCode());
         jsonWriter.name("altCode").value(key.getAltCode());
-        jsonWriter.name("x").value(key.mX);
-        jsonWriter.name("y").value(key.mY);
-        jsonWriter.name("w").value(key.mWidth);
-        jsonWriter.name("h").value(key.mHeight);
+        jsonWriter.name("x").value(key.getX());
+        jsonWriter.name("y").value(key.getY());
+        jsonWriter.name("w").value(key.getWidth());
+        jsonWriter.name("h").value(key.getHeight());
         jsonWriter.endObject();
     }
 
@@ -103,7 +103,7 @@
             jsonWriter.name("word").value(wordInfo.toString());
             jsonWriter.name("score").value(wordInfo.mScore);
             jsonWriter.name("kind").value(wordInfo.mKind);
-            jsonWriter.name("sourceDict").value(wordInfo.mSourceDict);
+            jsonWriter.name("sourceDict").value(wordInfo.mSourceDict.mDictType);
             jsonWriter.endObject();
         }
         jsonWriter.endArray();
diff --git a/java/src/com/android/inputmethod/research/ResearchLogger.java b/java/src/com/android/inputmethod/research/ResearchLogger.java
index 3a34082..da9c611 100644
--- a/java/src/com/android/inputmethod/research/ResearchLogger.java
+++ b/java/src/com/android/inputmethod/research/ResearchLogger.java
@@ -1427,7 +1427,7 @@
                 kid.navigateNext(), kid.navigatePrevious(), kid.mClobberSettingsKey,
                 isPasswordView, kid.mShortcutKeyEnabled, kid.mHasShortcutKey,
                 kid.mLanguageSwitchKeyEnabled, kid.isMultiLine(), keyboard.mOccupiedWidth,
-                keyboard.mOccupiedHeight, keyboard.mKeys);
+                keyboard.mOccupiedHeight, keyboard.getKeys());
     }
 
     /**
diff --git a/native/jni/Android.mk b/native/jni/Android.mk
index e14cf5a..c207032 100644
--- a/native/jni/Android.mk
+++ b/native/jni/Android.mk
@@ -43,6 +43,7 @@
     com_android_inputmethod_keyboard_ProximityInfo.cpp \
     com_android_inputmethod_latin_BinaryDictionary.cpp \
     com_android_inputmethod_latin_DicTraverseSession.cpp \
+    com_android_inputmethod_latin_makedict_Ver3DictDecoder.cpp \
     jni_common.cpp
 
 LATIN_IME_CORE_SRC_FILES := \
@@ -53,12 +54,7 @@
         dic_nodes_cache.cpp) \
     $(addprefix suggest/core/dictionary/, \
         bigram_dictionary.cpp \
-        binary_dictionary_format_utils.cpp \
-        binary_dictionary_header.cpp \
-        binary_dictionary_header_reading_utils.cpp \
-        binary_dictionary_terminal_attributes_reading_utils.cpp \
         bloom_filter.cpp \
-        byte_array_utils.cpp \
         dictionary.cpp \
         digraph_utils.cpp \
         multi_bigram_map.cpp) \
@@ -71,11 +67,25 @@
     suggest/core/policy/weighting.cpp \
     suggest/core/session/dic_traverse_session.cpp \
     $(addprefix suggest/policyimpl/dictionary/, \
+        bigram/bigram_list_read_write_utils.cpp \
+        bigram/dynamic_bigram_list_policy.cpp \
+        header/header_policy.cpp \
+        header/header_read_write_utils.cpp \
+        shortcut/shortcut_list_reading_utils.cpp \
+        dictionary_structure_with_buffer_policy_factory.cpp \
+        dynamic_patricia_trie_gc_event_listeners.cpp \
         dynamic_patricia_trie_node_reader.cpp \
         dynamic_patricia_trie_policy.cpp \
+        dynamic_patricia_trie_reading_helper.cpp \
         dynamic_patricia_trie_reading_utils.cpp \
+        dynamic_patricia_trie_writing_helper.cpp \
+        dynamic_patricia_trie_writing_utils.cpp \
         patricia_trie_policy.cpp \
         patricia_trie_reading_utils.cpp) \
+    $(addprefix suggest/policyimpl/dictionary/utils/, \
+        buffer_with_extendable_buffer.cpp \
+        byte_array_utils.cpp \
+        format_utils.cpp) \
     suggest/policyimpl/gesture/gesture_suggest_policy_factory.cpp \
     $(addprefix suggest/policyimpl/typing/, \
         scoring_params.cpp \
diff --git a/native/jni/com_android_inputmethod_latin_BinaryDictionary.cpp b/native/jni/com_android_inputmethod_latin_BinaryDictionary.cpp
index 8b46c26..7f47493 100644
--- a/native/jni/com_android_inputmethod_latin_BinaryDictionary.cpp
+++ b/native/jni/com_android_inputmethod_latin_BinaryDictionary.cpp
@@ -18,37 +18,20 @@
 
 #include "com_android_inputmethod_latin_BinaryDictionary.h"
 
-#include <cerrno>
 #include <cstring> // for memset()
-#include <fcntl.h>
-#include <sys/mman.h>
-#include <unistd.h>
 
 #include "defines.h"
 #include "jni.h"
 #include "jni_common.h"
-#include "suggest/core/dictionary/binary_dictionary_format_utils.h"
-#include "suggest/core/dictionary/binary_dictionary_info.h"
 #include "suggest/core/dictionary/dictionary.h"
 #include "suggest/core/suggest_options.h"
+#include "suggest/policyimpl/dictionary/dictionary_structure_with_buffer_policy_factory.h"
 #include "utils/autocorrection_threshold_utils.h"
 
 namespace latinime {
 
 class ProximityInfo;
 
-// Helper method
-static void releaseDictBuf(const void *dictBuf, const size_t length, const int fd) {
-    int ret = munmap(const_cast<void *>(dictBuf), length);
-    if (ret != 0) {
-        AKLOGE("DICT: Failure in munmap. ret=%d errno=%d", ret, errno);
-    }
-    ret = close(fd);
-    if (ret != 0) {
-        AKLOGE("DICT: Failure in close. ret=%d errno=%d", ret, errno);
-    }
-}
-
 static jlong latinime_BinaryDictionary_open(JNIEnv *env, jclass clazz, jstring sourceDir,
         jlong dictOffset, jlong dictSize, jboolean isUpdatable) {
     PROF_OPEN;
@@ -61,56 +44,52 @@
     char sourceDirChars[sourceDirUtf8Length + 1];
     env->GetStringUTFRegion(sourceDir, 0, env->GetStringLength(sourceDir), sourceDirChars);
     sourceDirChars[sourceDirUtf8Length] = '\0';
-    int fd = 0;
-    void *dictBuf = 0;
-    int offset = 0;
-    const bool updatableMmap = (isUpdatable == JNI_TRUE);
-    const int openMode = updatableMmap ? O_RDWR : O_RDONLY;
-    fd = open(sourceDirChars, openMode);
-    if (fd < 0) {
-        AKLOGE("DICT: Can't open sourceDir. sourceDirChars=%s errno=%d", sourceDirChars, errno);
+    DictionaryStructureWithBufferPolicy *const dictionaryStructureWithBufferPolicy =
+            DictionaryStructureWithBufferPolicyFactory::newDictionaryStructureWithBufferPolicy(
+                    sourceDirChars, static_cast<int>(dictOffset), static_cast<int>(dictSize),
+                    isUpdatable == JNI_TRUE);
+    if (!dictionaryStructureWithBufferPolicy) {
         return 0;
     }
-    int pagesize = getpagesize();
-    offset = static_cast<int>(dictOffset) % pagesize;
-    int adjDictOffset = static_cast<int>(dictOffset) - offset;
-    int adjDictSize = static_cast<int>(dictSize) + offset;
-    const int protMode = updatableMmap ? PROT_READ | PROT_WRITE : PROT_READ;
-    dictBuf = mmap(0, adjDictSize, protMode, MAP_PRIVATE, fd, adjDictOffset);
-    if (dictBuf == MAP_FAILED) {
-        AKLOGE("DICT: Can't mmap dictionary. errno=%d", errno);
-        return 0;
-    }
-    dictBuf = static_cast<char *>(dictBuf) + offset;
-    if (!dictBuf) {
-        AKLOGE("DICT: dictBuf is null");
-        return 0;
-    }
-    Dictionary *dictionary = 0;
-    if (BinaryDictionaryFormatUtils::UNKNOWN_VERSION
-            == BinaryDictionaryFormatUtils::detectFormatVersion(static_cast<uint8_t *>(dictBuf),
-                    static_cast<int>(dictSize))) {
-        AKLOGE("DICT: dictionary format is unknown, bad magic number");
-        releaseDictBuf(static_cast<const char *>(dictBuf) - offset, adjDictSize, fd);
-    } else {
-        dictionary = new Dictionary(env, dictBuf, static_cast<int>(dictSize), fd, offset,
-                updatableMmap);
-    }
+
+    Dictionary *const dictionary = new Dictionary(env, dictionaryStructureWithBufferPolicy);
     PROF_END(66);
     PROF_CLOSE;
     return reinterpret_cast<jlong>(dictionary);
 }
 
+static void latinime_BinaryDictionary_flush(JNIEnv *env, jclass clazz, jlong dict,
+        jstring filePath) {
+    Dictionary *dictionary = reinterpret_cast<Dictionary *>(dict);
+    if (!dictionary) return;
+    const jsize filePathUtf8Length = env->GetStringUTFLength(filePath);
+    char filePathChars[filePathUtf8Length + 1];
+    env->GetStringUTFRegion(filePath, 0, env->GetStringLength(filePath), filePathChars);
+    filePathChars[filePathUtf8Length] = '\0';
+    dictionary->flush(filePathChars);
+}
+
+static bool latinime_BinaryDictionary_needsToRunGC(JNIEnv *env, jclass clazz,
+        jlong dict) {
+    Dictionary *dictionary = reinterpret_cast<Dictionary *>(dict);
+    if (!dictionary) return false;
+    return dictionary->needsToRunGC();
+}
+
+static void latinime_BinaryDictionary_flushWithGC(JNIEnv *env, jclass clazz, jlong dict,
+        jstring filePath) {
+    Dictionary *dictionary = reinterpret_cast<Dictionary *>(dict);
+    if (!dictionary) return;
+    const jsize filePathUtf8Length = env->GetStringUTFLength(filePath);
+    char filePathChars[filePathUtf8Length + 1];
+    env->GetStringUTFRegion(filePath, 0, env->GetStringLength(filePath), filePathChars);
+    filePathChars[filePathUtf8Length] = '\0';
+    dictionary->flushWithGC(filePathChars);
+}
+
 static void latinime_BinaryDictionary_close(JNIEnv *env, jclass clazz, jlong dict) {
     Dictionary *dictionary = reinterpret_cast<Dictionary *>(dict);
     if (!dictionary) return;
-    const BinaryDictionaryInfo *const binaryDictionaryInfo = dictionary->getBinaryDictionaryInfo();
-    const int dictBufOffset = binaryDictionaryInfo->getDictBufOffset();
-    const void *dictBuf = binaryDictionaryInfo->getDictBuf();
-    if (!dictBuf) return;
-    releaseDictBuf(static_cast<const char *>(dictBuf) - dictBufOffset,
-            binaryDictionaryInfo->getDictSize() + dictBufOffset,
-            binaryDictionaryInfo->getMmapFd());
     delete dictionary;
 }
 
@@ -119,7 +98,8 @@
         jintArray yCoordinatesArray, jintArray timesArray, jintArray pointerIdsArray,
         jintArray inputCodePointsArray, jint inputSize, jint commitPoint, jintArray suggestOptions,
         jintArray prevWordCodePointsForBigrams, jintArray outputCodePointsArray,
-        jintArray scoresArray, jintArray spaceIndicesArray, jintArray outputTypesArray) {
+        jintArray scoresArray, jintArray spaceIndicesArray, jintArray outputTypesArray,
+        jintArray outputAutoCommitFirstWordConfidence) {
     Dictionary *dictionary = reinterpret_cast<Dictionary *>(dict);
     if (!dictionary) return 0;
     ProximityInfo *pInfo = reinterpret_cast<ProximityInfo *>(proximityInfo);
@@ -208,8 +188,8 @@
     return dictionary->getProbability(codePoints, wordLength);
 }
 
-static jboolean latinime_BinaryDictionary_isValidBigram(JNIEnv *env, jclass clazz, jlong dict,
-        jintArray word0, jintArray word1) {
+static jint latinime_BinaryDictionary_getBigramProbability(JNIEnv *env, jclass clazz,
+        jlong dict, jintArray word0, jintArray word1) {
     Dictionary *dictionary = reinterpret_cast<Dictionary *>(dict);
     if (!dictionary) return JNI_FALSE;
     const jsize word0Length = env->GetArrayLength(word0);
@@ -218,7 +198,8 @@
     int word1CodePoints[word1Length];
     env->GetIntArrayRegion(word0, 0, word0Length, word0CodePoints);
     env->GetIntArrayRegion(word1, 0, word1Length, word1CodePoints);
-    return dictionary->isValidBigram(word0CodePoints, word0Length, word1CodePoints, word1Length);
+    return dictionary->getBigramProbability(word0CodePoints, word0Length, word1CodePoints,
+            word1Length);
 }
 
 static jfloat latinime_BinaryDictionary_calcNormalizedScore(JNIEnv *env, jclass clazz,
@@ -253,6 +234,7 @@
     }
     jsize wordLength = env->GetArrayLength(word);
     int codePoints[wordLength];
+    env->GetIntArrayRegion(word, 0, wordLength, codePoints);
     dictionary->addUnigramWord(codePoints, wordLength, probability);
 }
 
@@ -264,8 +246,10 @@
     }
     jsize word0Length = env->GetArrayLength(word0);
     int word0CodePoints[word0Length];
+    env->GetIntArrayRegion(word0, 0, word0Length, word0CodePoints);
     jsize word1Length = env->GetArrayLength(word1);
     int word1CodePoints[word1Length];
+    env->GetIntArrayRegion(word1, 0, word1Length, word1CodePoints);
     dictionary->addBigramWords(word0CodePoints, word0Length, word1CodePoints,
             word1Length, probability);
 }
@@ -278,12 +262,24 @@
     }
     jsize word0Length = env->GetArrayLength(word0);
     int word0CodePoints[word0Length];
+    env->GetIntArrayRegion(word0, 0, word0Length, word0CodePoints);
     jsize word1Length = env->GetArrayLength(word1);
     int word1CodePoints[word1Length];
+    env->GetIntArrayRegion(word1, 0, word1Length, word1CodePoints);
     dictionary->removeBigramWords(word0CodePoints, word0Length, word1CodePoints,
             word1Length);
 }
 
+static int latinime_BinaryDictionary_calculateProbabilityNative(JNIEnv *env, jclass clazz,
+        jlong dict, jint unigramProbability, jint bigramProbability) {
+    Dictionary *dictionary = reinterpret_cast<Dictionary *>(dict);
+    if (!dictionary) {
+        return NOT_A_PROBABILITY;
+    }
+    return dictionary->getDictionaryStructurePolicy()->getProbability(unigramProbability,
+            bigramProbability);
+}
+
 static const JNINativeMethod sMethods[] = {
     {
         const_cast<char *>("openNative"),
@@ -296,8 +292,23 @@
         reinterpret_cast<void *>(latinime_BinaryDictionary_close)
     },
     {
+        const_cast<char *>("flushNative"),
+        const_cast<char *>("(JLjava/lang/String;)V"),
+        reinterpret_cast<void *>(latinime_BinaryDictionary_flush)
+    },
+    {
+        const_cast<char *>("needsToRunGCNative"),
+        const_cast<char *>("(J)Z"),
+        reinterpret_cast<void *>(latinime_BinaryDictionary_needsToRunGC)
+    },
+    {
+        const_cast<char *>("flushWithGCNative"),
+        const_cast<char *>("(JLjava/lang/String;)V"),
+        reinterpret_cast<void *>(latinime_BinaryDictionary_flushWithGC)
+    },
+    {
         const_cast<char *>("getSuggestionsNative"),
-        const_cast<char *>("(JJJ[I[I[I[I[III[I[I[I[I[I[I)I"),
+        const_cast<char *>("(JJJ[I[I[I[I[III[I[I[I[I[I[I[I)I"),
         reinterpret_cast<void *>(latinime_BinaryDictionary_getSuggestions)
     },
     {
@@ -306,9 +317,9 @@
         reinterpret_cast<void *>(latinime_BinaryDictionary_getProbability)
     },
     {
-        const_cast<char *>("isValidBigramNative"),
-        const_cast<char *>("(J[I[I)Z"),
-        reinterpret_cast<void *>(latinime_BinaryDictionary_isValidBigram)
+        const_cast<char *>("getBigramProbabilityNative"),
+        const_cast<char *>("(J[I[I)I"),
+        reinterpret_cast<void *>(latinime_BinaryDictionary_getBigramProbability)
     },
     {
         const_cast<char *>("calcNormalizedScoreNative"),
@@ -334,6 +345,11 @@
         const_cast<char *>("removeBigramWordsNative"),
         const_cast<char *>("(J[I[I)V"),
         reinterpret_cast<void *>(latinime_BinaryDictionary_removeBigramWords)
+    },
+    {
+        const_cast<char *>("calculateProbabilityNative"),
+        const_cast<char *>("(JII)I"),
+        reinterpret_cast<void *>(latinime_BinaryDictionary_calculateProbabilityNative)
     }
 };
 
diff --git a/native/jni/com_android_inputmethod_latin_DicTraverseSession.cpp b/native/jni/com_android_inputmethod_latin_DicTraverseSession.cpp
index 72e6258..3866433 100644
--- a/native/jni/com_android_inputmethod_latin_DicTraverseSession.cpp
+++ b/native/jni/com_android_inputmethod_latin_DicTraverseSession.cpp
@@ -25,8 +25,9 @@
 
 namespace latinime {
 class Dictionary;
-static jlong latinime_setDicTraverseSession(JNIEnv *env, jclass clazz, jstring localeJStr) {
-    void *traverseSession = DicTraverseSession::getSessionInstance(env, localeJStr);
+static jlong latinime_setDicTraverseSession(JNIEnv *env, jclass clazz, jstring localeJStr,
+        jlong dictSize) {
+    void *traverseSession = DicTraverseSession::getSessionInstance(env, localeJStr, dictSize);
     return reinterpret_cast<jlong>(traverseSession);
 }
 
@@ -53,7 +54,7 @@
 static const JNINativeMethod sMethods[] = {
     {
         const_cast<char *>("setDicTraverseSessionNative"),
-        const_cast<char *>("(Ljava/lang/String;)J"),
+        const_cast<char *>("(Ljava/lang/String;J)J"),
         reinterpret_cast<void *>(latinime_setDicTraverseSession)
     },
     {
diff --git a/native/jni/com_android_inputmethod_latin_makedict_Ver3DictDecoder.cpp b/native/jni/com_android_inputmethod_latin_makedict_Ver3DictDecoder.cpp
new file mode 100644
index 0000000..15088b6
--- /dev/null
+++ b/native/jni/com_android_inputmethod_latin_makedict_Ver3DictDecoder.cpp
@@ -0,0 +1,47 @@
+/*
+ * Copyright (C) 2013 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.
+ */
+
+#define LOG_TAG "LatinIME: jni: Ver3DictDecoder"
+
+#include "com_android_inputmethod_latin_makedict_Ver3DictDecoder.h"
+
+#include "defines.h"
+#include "jni.h"
+#include "jni_common.h"
+
+namespace latinime {
+static int latinime_Ver3DictDecoder_doNothing(JNIEnv *env, jclass clazz) {
+    // This is a phony method for test - it does nothing. It just returns some value
+    // unlikely to be in memory by chance for testing purposes.
+    // TODO: remove this method.
+    return 2097;
+}
+
+static const JNINativeMethod sMethods[] = {
+    {
+        // TODO: remove this entry when we have one useful method in here
+        const_cast<char *>("doNothing"),
+        const_cast<char *>("()I"),
+        reinterpret_cast<void *>(latinime_Ver3DictDecoder_doNothing)
+    },
+};
+
+int register_Ver3DictDecoder(JNIEnv *env) {
+    const char *const kClassPathName =
+            "com/android/inputmethod/latin/makedict/Ver3DictDecoder";
+    return registerNativeMethods(env, kClassPathName, sMethods, NELEMS(sMethods));
+}
+} // namespace latinime
diff --git a/native/jni/src/suggest/core/dictionary/byte_array_utils.cpp b/native/jni/com_android_inputmethod_latin_makedict_Ver3DictDecoder.h
similarity index 71%
copy from native/jni/src/suggest/core/dictionary/byte_array_utils.cpp
copy to native/jni/com_android_inputmethod_latin_makedict_Ver3DictDecoder.h
index 68b1d5d..07e80f1 100644
--- a/native/jni/src/suggest/core/dictionary/byte_array_utils.cpp
+++ b/native/jni/com_android_inputmethod_latin_makedict_Ver3DictDecoder.h
@@ -14,11 +14,12 @@
  * limitations under the License.
  */
 
-#include "suggest/core/dictionary/byte_array_utils.h"
+#ifndef _COM_ANDROID_INPUTMETHOD_LATIN_MAKEDICT_VER3DICTDECODER_H
+#define _COM_ANDROID_INPUTMETHOD_LATIN_MAKEDICT_VER3DICTDECODER_H
+
+#include "jni.h"
 
 namespace latinime {
-
-const uint8_t ByteArrayUtils::MINIMAL_ONE_BYTE_CHARACTER_VALUE = 0x20;
-const uint8_t ByteArrayUtils::CHARACTER_ARRAY_TERMINATOR = 0x1F;
-
+int register_Ver3DictDecoder(JNIEnv *env);
 } // namespace latinime
+#endif // _COM_ANDROID_INPUTMETHOD_LATIN_MAKEDICT_VER3DICTDECODER_H
diff --git a/native/jni/jni_common.cpp b/native/jni/jni_common.cpp
index f2867d7..3a8f436 100644
--- a/native/jni/jni_common.cpp
+++ b/native/jni/jni_common.cpp
@@ -18,9 +18,12 @@
 
 #include "jni_common.h"
 
+#ifndef HOST_TOOL
 #include "com_android_inputmethod_keyboard_ProximityInfo.h"
 #include "com_android_inputmethod_latin_BinaryDictionary.h"
 #include "com_android_inputmethod_latin_DicTraverseSession.h"
+#endif
+#include "com_android_inputmethod_latin_makedict_Ver3DictDecoder.h"
 #include "defines.h"
 
 /*
@@ -38,6 +41,7 @@
         AKLOGE("ERROR: JNIEnv is invalid");
         return -1;
     }
+#ifndef HOST_TOOL
     if (!latinime::register_BinaryDictionary(env)) {
         AKLOGE("ERROR: BinaryDictionary native registration failed");
         return -1;
@@ -50,6 +54,11 @@
         AKLOGE("ERROR: ProximityInfo native registration failed");
         return -1;
     }
+#endif
+    if (!latinime::register_Ver3DictDecoder(env)) {
+        AKLOGE("ERROR: Ver3DictDecoder native registration failed");
+        return -1;
+    }
     /* success -- return valid version number */
     return JNI_VERSION_1_6;
 }
diff --git a/native/jni/src/defines.h b/native/jni/src/defines.h
index 34a646f..89dfa39 100644
--- a/native/jni/src/defines.h
+++ b/native/jni/src/defines.h
@@ -292,7 +292,6 @@
 // of the binary dictionary where a {key,value} string pair scheme is used.
 #define LARGEST_INT_DIGIT_COUNT 11
 
-#define NOT_A_VALID_WORD_POS (-99)
 #define NOT_A_CODE_POINT (-1)
 #define NOT_A_DISTANCE (-1)
 #define NOT_A_COORDINATE (-1)
@@ -322,13 +321,6 @@
 #define MAX_POINTER_COUNT 1
 #define MAX_POINTER_COUNT_G 2
 
-// Queue IDs and size for DicNodesCache
-#define DIC_NODES_CACHE_INITIAL_QUEUE_ID_ACTIVE 0
-#define DIC_NODES_CACHE_INITIAL_QUEUE_ID_NEXT_ACTIVE 1
-#define DIC_NODES_CACHE_INITIAL_QUEUE_ID_TERMINAL 2
-#define DIC_NODES_CACHE_INITIAL_QUEUE_ID_CACHE_FOR_CONTINUOUS_SUGGESTION 3
-#define DIC_NODES_CACHE_PRIORITY_QUEUES_SIZE 4
-
 template<typename T> AK_FORCE_INLINE const T &min(const T &a, const T &b) { return a < b ? a : b; }
 template<typename T> AK_FORCE_INLINE const T &max(const T &a, const T &b) { return a > b ? a : b; }
 
diff --git a/native/jni/src/suggest/core/dicnode/dic_node.h b/native/jni/src/suggest/core/dicnode/dic_node.h
index cdd9f59..41ef9d2 100644
--- a/native/jni/src/suggest/core/dicnode/dic_node.h
+++ b/native/jni/src/suggest/core/dicnode/dic_node.h
@@ -112,7 +112,7 @@
         mIsUsed = true;
         mIsCachedForNextSuggestion = false;
         mDicNodeProperties.init(
-                NOT_A_VALID_WORD_POS /* pos */, rootGroupPos, NOT_A_CODE_POINT /* nodeCodePoint */,
+                NOT_A_DICT_POS /* pos */, rootGroupPos, NOT_A_CODE_POINT /* nodeCodePoint */,
                 NOT_A_PROBABILITY /* probability */, false /* isTerminal */,
                 true /* hasChildren */, false /* isBlacklistedOrNotAWord */, 0 /* depth */,
                 0 /* terminalDepth */);
@@ -125,7 +125,7 @@
         mIsUsed = true;
         mIsCachedForNextSuggestion = dicNode->mIsCachedForNextSuggestion;
         mDicNodeProperties.init(
-                NOT_A_VALID_WORD_POS /* pos */, rootGroupPos, NOT_A_CODE_POINT /* nodeCodePoint */,
+                NOT_A_DICT_POS /* pos */, rootGroupPos, NOT_A_CODE_POINT /* nodeCodePoint */,
                 NOT_A_PROBABILITY /* probability */, false /* isTerminal */,
                 true /* hasChildren */, false /* isBlacklistedOrNotAWord */,  0 /* depth */,
                 0 /* terminalDepth */);
@@ -143,7 +143,7 @@
                 dicNode->mDicNodeState.mDicNodeStatePrevWord.getPrevWordLength(),
                 dicNode->getOutputWordBuf(),
                 dicNode->mDicNodeProperties.getDepth(),
-                dicNode->mDicNodeState.mDicNodeStatePrevWord.mPrevSpacePositions,
+                dicNode->mDicNodeState.mDicNodeStatePrevWord.getSecondWordFirstInputIndex(),
                 mDicNodeState.mDicNodeStateInput.getInputIndex(0) /* lastInputIndex */);
         PROF_NODE_COPY(&dicNode->mProfiler, mProfiler);
     }
@@ -234,7 +234,7 @@
     }
 
     bool isFirstWord() const {
-        return mDicNodeState.mDicNodeStatePrevWord.getPrevWordNodePos() == NOT_A_VALID_WORD_POS;
+        return mDicNodeState.mDicNodeStatePrevWord.getPrevWordNodePos() == NOT_A_DICT_POS;
     }
 
     bool isCompletion(const int inputSize) const {
@@ -321,8 +321,13 @@
         DUMP_WORD_AND_SCORE("OUTPUT");
     }
 
-    void outputSpacePositionsResult(int *spaceIndices) const {
-        mDicNodeState.mDicNodeStatePrevWord.outputSpacePositions(spaceIndices);
+    int getSecondWordFirstInputIndex(const ProximityInfoState *const pInfoState) const {
+        const int inputIndex = mDicNodeState.mDicNodeStatePrevWord.getSecondWordFirstInputIndex();
+        if (inputIndex == NOT_AN_INDEX) {
+            return NOT_AN_INDEX;
+        } else {
+            return pInfoState->getInputIndexOfSampledPoint(inputIndex);
+        }
     }
 
     bool hasMultipleWords() const {
@@ -573,7 +578,11 @@
         }
     }
 
-    AK_FORCE_INLINE void updateInputIndexG(DicNode_InputStateG *inputStateG) {
+    AK_FORCE_INLINE void updateInputIndexG(const DicNode_InputStateG *const inputStateG) {
+        if (mDicNodeState.mDicNodeStatePrevWord.getPrevWordCount() == 1 && isFirstLetter()) {
+            mDicNodeState.mDicNodeStatePrevWord.setSecondWordFirstInputIndex(
+                    inputStateG->mInputIndex);
+        }
         mDicNodeState.mDicNodeStateInput.updateInputIndexG(inputStateG->mPointerId,
                 inputStateG->mInputIndex, inputStateG->mPrevCodePoint,
                 inputStateG->mTerminalDiffCost, inputStateG->mRawLength);
diff --git a/native/jni/src/suggest/core/dicnode/dic_node_priority_queue.h b/native/jni/src/suggest/core/dicnode/dic_node_priority_queue.h
index 2a486b8..7461f0c 100644
--- a/native/jni/src/suggest/core/dicnode/dic_node_priority_queue.h
+++ b/native/jni/src/suggest/core/dicnode/dic_node_priority_queue.h
@@ -24,20 +24,16 @@
 #include "suggest/core/dicnode/dic_node.h"
 #include "suggest/core/dicnode/dic_node_release_listener.h"
 
-// The biggest value among MAX_CACHE_DIC_NODE_SIZE, MAX_CACHE_DIC_NODE_SIZE_FOR_SINGLE_POINT, ...
-#define MAX_DIC_NODE_PRIORITY_QUEUE_CAPACITY 310
-
 namespace latinime {
 
 class DicNodePriorityQueue : public DicNodeReleaseListener {
  public:
-    AK_FORCE_INLINE DicNodePriorityQueue()
-            : MAX_CAPACITY(MAX_DIC_NODE_PRIORITY_QUEUE_CAPACITY),
-              mMaxSize(MAX_DIC_NODE_PRIORITY_QUEUE_CAPACITY), mDicNodesBuf(), mUnusedNodeIndices(),
-              mNextUnusedNodeId(0), mDicNodesQueue() {
-        mDicNodesBuf.resize(MAX_CAPACITY + 1);
-        mUnusedNodeIndices.resize(MAX_CAPACITY + 1);
-        reset();
+    AK_FORCE_INLINE explicit DicNodePriorityQueue(const int capacity)
+            : mCapacity(capacity), mMaxSize(capacity), mDicNodesBuf(),
+              mUnusedNodeIndices(), mNextUnusedNodeId(0), mDicNodesQueue() {
+        mDicNodesBuf.resize(mCapacity + 1);
+        mUnusedNodeIndices.resize(mCapacity + 1);
+        clearAndResizeToCapacity();
     }
 
     // Non virtual inline destructor -- never inherit this class
@@ -52,11 +48,12 @@
     }
 
     AK_FORCE_INLINE void setMaxSize(const int maxSize) {
-        mMaxSize = min(maxSize, MAX_CAPACITY);
+        ASSERT(maxSize <= mCapacity);
+        mMaxSize = min(maxSize, mCapacity);
     }
 
-    AK_FORCE_INLINE void reset() {
-        clearAndResize(MAX_CAPACITY);
+    AK_FORCE_INLINE void clearAndResizeToCapacity() {
+        clearAndResize(mCapacity);
     }
 
     AK_FORCE_INLINE void clear() {
@@ -64,27 +61,19 @@
     }
 
     AK_FORCE_INLINE void clearAndResize(const int maxSize) {
+        ASSERT(maxSize <= mCapacity);
         while (!mDicNodesQueue.empty()) {
             mDicNodesQueue.pop();
         }
         setMaxSize(maxSize);
-        for (int i = 0; i < MAX_CAPACITY + 1; ++i) {
+        for (int i = 0; i < mCapacity + 1; ++i) {
             mDicNodesBuf[i].remove();
             mDicNodesBuf[i].setReleaseListener(this);
-            mUnusedNodeIndices[i] = i == MAX_CAPACITY ? NOT_A_NODE_ID : static_cast<int>(i) + 1;
+            mUnusedNodeIndices[i] = i == mCapacity ? NOT_A_NODE_ID : static_cast<int>(i) + 1;
         }
         mNextUnusedNodeId = 0;
     }
 
-    AK_FORCE_INLINE DicNode *newDicNode(DicNode *dicNode) {
-        DicNode *newNode = searchEmptyDicNode();
-        if (newNode) {
-            DicNodeUtils::initByCopy(dicNode, newNode);
-            return newNode;
-        }
-        return 0;
-    }
-
     // Copy
     AK_FORCE_INLINE DicNode *copyPush(DicNode *dicNode) {
         return copyPush(dicNode, mMaxSize);
@@ -111,12 +100,12 @@
         }
         mUnusedNodeIndices[index] = mNextUnusedNodeId;
         mNextUnusedNodeId = index;
-        ASSERT(index >= 0 && index < (MAX_CAPACITY + 1));
+        ASSERT(index >= 0 && index < (mCapacity + 1));
     }
 
     AK_FORCE_INLINE void dump() const {
         AKLOGI("\n\n\n\n\n===========================");
-        for (int i = 0; i < MAX_CAPACITY + 1; ++i) {
+        for (int i = 0; i < mCapacity + 1; ++i) {
             if (mDicNodesBuf[i].isUsed()) {
                 mDicNodesBuf[i].dump("QUEUE: ");
             }
@@ -125,7 +114,7 @@
     }
 
  private:
-    DISALLOW_COPY_AND_ASSIGN(DicNodePriorityQueue);
+    DISALLOW_IMPLICIT_CONSTRUCTORS(DicNodePriorityQueue);
     static const int NOT_A_NODE_ID = -1;
 
     AK_FORCE_INLINE static bool compareDicNode(DicNode *left, DicNode *right) {
@@ -139,7 +128,7 @@
     };
 
     typedef std::priority_queue<DicNode *, std::vector<DicNode *>, DicNodeComparator> DicNodesQueue;
-    const int MAX_CAPACITY;
+    const int mCapacity;
     int mMaxSize;
     std::vector<DicNode> mDicNodesBuf; // of each element of mDicNodesBuf respectively
     std::vector<int> mUnusedNodeIndices;
@@ -163,13 +152,12 @@
     }
 
     AK_FORCE_INLINE DicNode *searchEmptyDicNode() {
-        // TODO: Currently O(n) but should be improved to O(1)
-        if (MAX_CAPACITY == 0) {
+        if (mCapacity == 0) {
             return 0;
         }
         if (mNextUnusedNodeId == NOT_A_NODE_ID) {
             AKLOGI("No unused node found.");
-            for (int i = 0; i < MAX_CAPACITY + 1; ++i) {
+            for (int i = 0; i < mCapacity + 1; ++i) {
                 AKLOGI("Dump node availability, %d, %d, %d",
                         i, mDicNodesBuf[i].isUsed(), mUnusedNodeIndices[i]);
             }
@@ -185,7 +173,7 @@
         const int index = static_cast<int>(dicNode - &mDicNodesBuf[0]);
         mNextUnusedNodeId = mUnusedNodeIndices[index];
         mUnusedNodeIndices[index] = NOT_A_NODE_ID;
-        ASSERT(index >= 0 && index < (MAX_CAPACITY + 1));
+        ASSERT(index >= 0 && index < (mCapacity + 1));
     }
 
     AK_FORCE_INLINE DicNode *pushPoolNodeWithMaxSize(DicNode *dicNode, const int maxSize) {
@@ -209,6 +197,15 @@
     AK_FORCE_INLINE DicNode *copyPush(DicNode *dicNode, const int maxSize) {
         return pushPoolNodeWithMaxSize(newDicNode(dicNode), maxSize);
     }
+
+    AK_FORCE_INLINE DicNode *newDicNode(DicNode *dicNode) {
+        DicNode *newNode = searchEmptyDicNode();
+        if (newNode) {
+            DicNodeUtils::initByCopy(dicNode, newNode);
+        }
+        return newNode;
+    }
+
 };
 } // namespace latinime
 #endif // LATINIME_DIC_NODE_PRIORITY_QUEUE_H
diff --git a/native/jni/src/suggest/core/dicnode/dic_node_proximity_filter.h b/native/jni/src/suggest/core/dicnode/dic_node_proximity_filter.h
deleted file mode 100644
index 1a39f2e..0000000
--- a/native/jni/src/suggest/core/dicnode/dic_node_proximity_filter.h
+++ /dev/null
@@ -1,58 +0,0 @@
-/*
- * Copyright (C) 2013 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.
- */
-
-#ifndef LATINIME_DIC_NODE_PROXIMITY_FILTER_H
-#define LATINIME_DIC_NODE_PROXIMITY_FILTER_H
-
-#include "defines.h"
-#include "suggest/core/layout/proximity_info_state.h"
-#include "suggest/core/layout/proximity_info_utils.h"
-#include "suggest/core/policy/dictionary_structure_policy.h"
-
-namespace latinime {
-
-class DicNodeProximityFilter : public DictionaryStructurePolicy::NodeFilter {
- public:
-    DicNodeProximityFilter(const ProximityInfoState *const pInfoState,
-            const int pointIndex, const bool exactOnly)
-            : mProximityInfoState(pInfoState), mPointIndex(pointIndex), mExactOnly(exactOnly) {}
-
-    bool isFilteredOut(const int codePoint) const {
-        return !isProximityCodePoint(codePoint);
-    }
-
- private:
-    DISALLOW_IMPLICIT_CONSTRUCTORS(DicNodeProximityFilter);
-
-    const ProximityInfoState *const mProximityInfoState;
-    const int mPointIndex;
-    const bool mExactOnly;
-
-    // TODO: Move to proximity info state
-    bool isProximityCodePoint(const int codePoint) const {
-        if (!mProximityInfoState) {
-            return true;
-        }
-        if (mExactOnly) {
-            return mProximityInfoState->getPrimaryCodePointAt(mPointIndex) == codePoint;
-        }
-        const ProximityType matchedId = mProximityInfoState->getProximityType(
-                mPointIndex, codePoint, true /* checkProximityChars */);
-        return ProximityInfoUtils::isMatchOrProximityChar(matchedId);
-    }
-};
-} // namespace latinime
-#endif // LATINIME_DIC_NODE_PROXIMITY_FILTER_H
diff --git a/native/jni/src/suggest/core/dicnode/dic_node_utils.cpp b/native/jni/src/suggest/core/dicnode/dic_node_utils.cpp
index 6b4ef2f..ec65114 100644
--- a/native/jni/src/suggest/core/dicnode/dic_node_utils.cpp
+++ b/native/jni/src/suggest/core/dicnode/dic_node_utils.cpp
@@ -19,12 +19,9 @@
 #include <cstring>
 
 #include "suggest/core/dicnode/dic_node.h"
-#include "suggest/core/dicnode/dic_node_proximity_filter.h"
 #include "suggest/core/dicnode/dic_node_vector.h"
-#include "suggest/core/dictionary/binary_dictionary_info.h"
 #include "suggest/core/dictionary/multi_bigram_map.h"
-#include "suggest/core/dictionary/probability_utils.h"
-#include "suggest/core/policy/dictionary_structure_policy.h"
+#include "suggest/core/policy/dictionary_structure_with_buffer_policy.h"
 #include "utils/char_utils.h"
 
 namespace latinime {
@@ -33,17 +30,17 @@
 // Node initialization utils //
 ///////////////////////////////
 
-/* static */ void DicNodeUtils::initAsRoot(const BinaryDictionaryInfo *const binaryDictionaryInfo,
+/* static */ void DicNodeUtils::initAsRoot(
+        const DictionaryStructureWithBufferPolicy *const dictionaryStructurePolicy,
         const int prevWordNodePos, DicNode *const newRootNode) {
-    newRootNode->initAsRoot(binaryDictionaryInfo->getStructurePolicy()->getRootPosition(),
-            prevWordNodePos);
+    newRootNode->initAsRoot(dictionaryStructurePolicy->getRootPosition(), prevWordNodePos);
 }
 
 /*static */ void DicNodeUtils::initAsRootWithPreviousWord(
-        const BinaryDictionaryInfo *const binaryDictionaryInfo,
+        const DictionaryStructureWithBufferPolicy *const dictionaryStructurePolicy,
         DicNode *const prevWordLastNode, DicNode *const newRootNode) {
     newRootNode->initAsRootWithPreviousWord(
-            prevWordLastNode, binaryDictionaryInfo->getStructurePolicy()->getRootPosition());
+            prevWordLastNode, dictionaryStructurePolicy->getRootPosition());
 }
 
 /* static */ void DicNodeUtils::initByCopy(DicNode *srcNode, DicNode *destNode) {
@@ -53,37 +50,16 @@
 ///////////////////////////////////
 // Traverse node expansion utils //
 ///////////////////////////////////
-
-/* static */ void DicNodeUtils::createAndGetPassingChildNode(DicNode *dicNode,
-        const DicNodeProximityFilter *const childrenFilter,
-        DicNodeVector *childDicNodes) {
-    // Passing multiple chars node. No need to traverse child
-    const int codePoint = dicNode->getNodeTypedCodePoint();
-    const int baseLowerCaseCodePoint = CharUtils::toBaseLowerCase(codePoint);
-    if (!childrenFilter->isFilteredOut(codePoint)
-            || CharUtils::isIntentionalOmissionCodePoint(baseLowerCaseCodePoint)) {
-        childDicNodes->pushPassingChild(dicNode);
-    }
-}
-
 /* static */ void DicNodeUtils::getAllChildDicNodes(DicNode *dicNode,
-        const BinaryDictionaryInfo *const binaryDictionaryInfo, DicNodeVector *childDicNodes) {
-    getProximityChildDicNodes(dicNode, binaryDictionaryInfo, 0, 0, false, childDicNodes);
-}
-
-/* static */ void DicNodeUtils::getProximityChildDicNodes(DicNode *dicNode,
-        const BinaryDictionaryInfo *const binaryDictionaryInfo,
-        const ProximityInfoState *pInfoState, const int pointIndex, bool exactOnly,
+        const DictionaryStructureWithBufferPolicy *const dictionaryStructurePolicy,
         DicNodeVector *childDicNodes) {
     if (dicNode->isTotalInputSizeExceedingLimit()) {
         return;
     }
-    const DicNodeProximityFilter childrenFilter(pInfoState, pointIndex, exactOnly);
     if (!dicNode->isLeavingNode()) {
-        DicNodeUtils::createAndGetPassingChildNode(dicNode, &childrenFilter, childDicNodes);
+        childDicNodes->pushPassingChild(dicNode);
     } else {
-        binaryDictionaryInfo->getStructurePolicy()->createAndGetAllChildNodes(dicNode,
-                binaryDictionaryInfo, &childrenFilter, childDicNodes);
+        dictionaryStructurePolicy->createAndGetAllChildNodes(dicNode, childDicNodes);
     }
 }
 
@@ -94,12 +70,13 @@
  * Computes the combined bigram / unigram cost for the given dicNode.
  */
 /* static */ float DicNodeUtils::getBigramNodeImprobability(
-        const BinaryDictionaryInfo *const binaryDictionaryInfo,
+        const DictionaryStructureWithBufferPolicy *const dictionaryStructurePolicy,
         const DicNode *const node, MultiBigramMap *multiBigramMap) {
     if (node->hasMultipleWords() && !node->isValidMultipleWordSuggestion()) {
         return static_cast<float>(MAX_VALUE_FOR_WEIGHTING);
     }
-    const int probability = getBigramNodeProbability(binaryDictionaryInfo, node, multiBigramMap);
+    const int probability = getBigramNodeProbability(dictionaryStructurePolicy, node,
+            multiBigramMap);
     // TODO: This equation to calculate the improbability looks unreasonable.  Investigate this.
     const float cost = static_cast<float>(MAX_PROBABILITY - probability)
             / static_cast<float>(MAX_PROBABILITY);
@@ -107,21 +84,23 @@
 }
 
 /* static */ int DicNodeUtils::getBigramNodeProbability(
-        const BinaryDictionaryInfo *const binaryDictionaryInfo,
+        const DictionaryStructureWithBufferPolicy *const dictionaryStructurePolicy,
         const DicNode *const node, MultiBigramMap *multiBigramMap) {
     const int unigramProbability = node->getProbability();
     const int wordPos = node->getPos();
     const int prevWordPos = node->getPrevWordPos();
-    if (NOT_A_VALID_WORD_POS == wordPos || NOT_A_VALID_WORD_POS == prevWordPos) {
+    if (NOT_A_DICT_POS == wordPos || NOT_A_DICT_POS == prevWordPos) {
         // Note: Normally wordPos comes from the dictionary and should never equal
         // NOT_A_VALID_WORD_POS.
-        return ProbabilityUtils::backoff(unigramProbability);
+        return dictionaryStructurePolicy->getProbability(unigramProbability,
+                NOT_A_PROBABILITY);
     }
     if (multiBigramMap) {
-        return multiBigramMap->getBigramProbability(
-                binaryDictionaryInfo, prevWordPos, wordPos, unigramProbability);
+        return multiBigramMap->getBigramProbability(dictionaryStructurePolicy, prevWordPos,
+                wordPos, unigramProbability);
     }
-    return ProbabilityUtils::backoff(unigramProbability);
+    return dictionaryStructurePolicy->getProbability(unigramProbability,
+            NOT_A_PROBABILITY);
 }
 
 ////////////////
diff --git a/native/jni/src/suggest/core/dicnode/dic_node_utils.h b/native/jni/src/suggest/core/dicnode/dic_node_utils.h
index 4f12b29..3fb351a 100644
--- a/native/jni/src/suggest/core/dicnode/dic_node_utils.h
+++ b/native/jni/src/suggest/core/dicnode/dic_node_utils.h
@@ -23,41 +23,37 @@
 
 namespace latinime {
 
-class BinaryDictionaryInfo;
 class DicNode;
-class DicNodeProximityFilter;
 class DicNodeVector;
-class ProximityInfoState;
+class DictionaryStructureWithBufferPolicy;
 class MultiBigramMap;
 
 class DicNodeUtils {
  public:
     static int appendTwoWords(const int *src0, const int16_t length0, const int *src1,
             const int16_t length1, int *dest);
-    static void initAsRoot(const BinaryDictionaryInfo *const binaryDictionaryInfo,
+    static void initAsRoot(
+            const DictionaryStructureWithBufferPolicy *const dictionaryStructurePolicy,
             const int prevWordNodePos, DicNode *newRootNode);
-    static void initAsRootWithPreviousWord(const BinaryDictionaryInfo *const binaryDictionaryInfo,
+    static void initAsRootWithPreviousWord(
+            const DictionaryStructureWithBufferPolicy *const dictionaryStructurePolicy,
             DicNode *prevWordLastNode, DicNode *newRootNode);
     static void initByCopy(DicNode *srcNode, DicNode *destNode);
     static void getAllChildDicNodes(DicNode *dicNode,
-            const BinaryDictionaryInfo *const binaryDictionaryInfo, DicNodeVector *childDicNodes);
-    static float getBigramNodeImprobability(const BinaryDictionaryInfo *const binaryDictionaryInfo,
-            const DicNode *const node, MultiBigramMap *const multiBigramMap);
-    // TODO: Move to private
-    static void getProximityChildDicNodes(DicNode *dicNode,
-            const BinaryDictionaryInfo *const binaryDictionaryInfo,
-            const ProximityInfoState *pInfoState, const int pointIndex, bool exactOnly,
+            const DictionaryStructureWithBufferPolicy *const dictionaryStructurePolicy,
             DicNodeVector *childDicNodes);
+    static float getBigramNodeImprobability(
+            const DictionaryStructureWithBufferPolicy *const dictionaryStructurePolicy,
+            const DicNode *const node, MultiBigramMap *const multiBigramMap);
 
  private:
     DISALLOW_IMPLICIT_CONSTRUCTORS(DicNodeUtils);
     // Max number of bigrams to look up
     static const int MAX_BIGRAMS_CONSIDERED_PER_CONTEXT = 500;
 
-    static int getBigramNodeProbability(const BinaryDictionaryInfo *const binaryDictionaryInfo,
+    static int getBigramNodeProbability(
+            const DictionaryStructureWithBufferPolicy *const dictionaryStructurePolicy,
             const DicNode *const node, MultiBigramMap *multiBigramMap);
-    static void createAndGetPassingChildNode(DicNode *dicNode,
-            const DicNodeProximityFilter *const childrenFilter, DicNodeVector *childDicNodes);
 };
 } // namespace latinime
 #endif // LATINIME_DIC_NODE_UTILS_H
diff --git a/native/jni/src/suggest/core/dicnode/dic_nodes_cache.cpp b/native/jni/src/suggest/core/dicnode/dic_nodes_cache.cpp
index c3d2a2e..b6be47e 100644
--- a/native/jni/src/suggest/core/dicnode/dic_nodes_cache.cpp
+++ b/native/jni/src/suggest/core/dicnode/dic_nodes_cache.cpp
@@ -23,6 +23,11 @@
 
 namespace latinime {
 
+// The biggest value among MAX_CACHE_DIC_NODE_SIZE, MAX_CACHE_DIC_NODE_SIZE_FOR_SINGLE_POINT, ...
+const int DicNodesCache::LARGE_PRIORITY_QUEUE_CAPACITY = 310;
+// Capacity for reducing memory footprint.
+const int DicNodesCache::SMALL_PRIORITY_QUEUE_CAPACITY = 100;
+
 /**
  * Truncates all of the dicNodes so that they start at the given commit point.
  * Only called for multi-word typing input.
diff --git a/native/jni/src/suggest/core/dicnode/dic_nodes_cache.h b/native/jni/src/suggest/core/dicnode/dic_nodes_cache.h
index 7aab090..8493b6a 100644
--- a/native/jni/src/suggest/core/dicnode/dic_nodes_cache.h
+++ b/native/jni/src/suggest/core/dicnode/dic_nodes_cache.h
@@ -31,25 +31,32 @@
  */
 class DicNodesCache {
  public:
-    AK_FORCE_INLINE DicNodesCache()
-            : mActiveDicNodes(&mDicNodePriorityQueues[DIC_NODES_CACHE_INITIAL_QUEUE_ID_ACTIVE]),
-              mNextActiveDicNodes(&mDicNodePriorityQueues[
-                      DIC_NODES_CACHE_INITIAL_QUEUE_ID_NEXT_ACTIVE]),
-              mTerminalDicNodes(&mDicNodePriorityQueues[DIC_NODES_CACHE_INITIAL_QUEUE_ID_TERMINAL]),
-              mCachedDicNodesForContinuousSuggestion(&mDicNodePriorityQueues[
-                      DIC_NODES_CACHE_INITIAL_QUEUE_ID_CACHE_FOR_CONTINUOUS_SUGGESTION]),
-              mInputIndex(0), mLastCachedInputIndex(0) {
-    }
+    AK_FORCE_INLINE explicit DicNodesCache(const bool usesLargeCapacityCache)
+            : mUsesLargeCapacityCache(usesLargeCapacityCache),
+              mDicNodePriorityQueue0(getCacheCapacity()),
+              mDicNodePriorityQueue1(getCacheCapacity()),
+              mDicNodePriorityQueue2(getCacheCapacity()),
+              mDicNodePriorityQueueForTerminal(MAX_RESULTS),
+              mActiveDicNodes(&mDicNodePriorityQueue0),
+              mNextActiveDicNodes(&mDicNodePriorityQueue1),
+              mCachedDicNodesForContinuousSuggestion(&mDicNodePriorityQueue2),
+              mTerminalDicNodes(&mDicNodePriorityQueueForTerminal),
+              mInputIndex(0), mLastCachedInputIndex(0) {}
 
     AK_FORCE_INLINE virtual ~DicNodesCache() {}
 
     AK_FORCE_INLINE void reset(const int nextActiveSize, const int terminalSize) {
         mInputIndex = 0;
         mLastCachedInputIndex = 0;
-        mActiveDicNodes->reset();
-        mNextActiveDicNodes->clearAndResize(nextActiveSize);
+        // We want to use the max capacity for the current active dic node queue.
+        mActiveDicNodes->clearAndResizeToCapacity();
+        // nextActiveSize is used to limit the next iteration's active dic node size.
+        const int nextActiveSizeFittingToTheCapacity = min(nextActiveSize, getCacheCapacity());
+        mNextActiveDicNodes->clearAndResize(nextActiveSizeFittingToTheCapacity);
         mTerminalDicNodes->clearAndResize(terminalSize);
-        mCachedDicNodesForContinuousSuggestion->reset();
+        // We want to use the max capacity for the cached dic nodes that will be used for the
+        // continuous suggestion.
+        mCachedDicNodesForContinuousSuggestion->clearAndResizeToCapacity();
     }
 
     AK_FORCE_INLINE void continueSearch() {
@@ -157,21 +164,35 @@
         return tmp;
     }
 
+    AK_FORCE_INLINE int getCacheCapacity() const {
+        return mUsesLargeCapacityCache ?
+                LARGE_PRIORITY_QUEUE_CAPACITY : SMALL_PRIORITY_QUEUE_CAPACITY;
+    }
+
     AK_FORCE_INLINE void resetTemporaryCaches() {
         mActiveDicNodes->clear();
         mNextActiveDicNodes->clear();
         mTerminalDicNodes->clear();
     }
 
-    DicNodePriorityQueue mDicNodePriorityQueues[DIC_NODES_CACHE_PRIORITY_QUEUES_SIZE];
+    static const int LARGE_PRIORITY_QUEUE_CAPACITY;
+    static const int SMALL_PRIORITY_QUEUE_CAPACITY;
+
+    const bool mUsesLargeCapacityCache;
+    // Instances
+    DicNodePriorityQueue mDicNodePriorityQueue0;
+    DicNodePriorityQueue mDicNodePriorityQueue1;
+    DicNodePriorityQueue mDicNodePriorityQueue2;
+    DicNodePriorityQueue mDicNodePriorityQueueForTerminal;
+
     // Active dicNodes currently being expanded.
     DicNodePriorityQueue *mActiveDicNodes;
     // Next dicNodes to be expanded.
     DicNodePriorityQueue *mNextActiveDicNodes;
-    // Current top terminal dicNodes.
-    DicNodePriorityQueue *mTerminalDicNodes;
     // Cached dicNodes used for continuous suggestion.
     DicNodePriorityQueue *mCachedDicNodesForContinuousSuggestion;
+    // Current top terminal dicNodes.
+    DicNodePriorityQueue *mTerminalDicNodes;
     int mInputIndex;
     int mLastCachedInputIndex;
 };
diff --git a/native/jni/src/suggest/core/dicnode/internal/dic_node_state_output.h b/native/jni/src/suggest/core/dicnode/internal/dic_node_state_output.h
index 45c7f5c..74eb5df 100644
--- a/native/jni/src/suggest/core/dicnode/internal/dic_node_state_output.h
+++ b/native/jni/src/suggest/core/dicnode/internal/dic_node_state_output.h
@@ -49,8 +49,10 @@
     void addMergedNodeCodePoints(const uint16_t mergedNodeCodePointCount,
             const int *const mergedNodeCodePoints) {
         if (mergedNodeCodePoints) {
+            const int additionalCodePointCount = min(static_cast<int>(mergedNodeCodePointCount),
+                    MAX_WORD_LENGTH - mOutputtedCodePointCount);
             memcpy(&mCodePointsBuf[mOutputtedCodePointCount], mergedNodeCodePoints,
-                    mergedNodeCodePointCount * sizeof(mCodePointsBuf[0]));
+                    additionalCodePointCount * sizeof(mCodePointsBuf[0]));
             mOutputtedCodePointCount = static_cast<uint16_t>(
                     mOutputtedCodePointCount + mergedNodeCodePointCount);
             if (mOutputtedCodePointCount < MAX_WORD_LENGTH) {
diff --git a/native/jni/src/suggest/core/dicnode/internal/dic_node_state_prevword.h b/native/jni/src/suggest/core/dicnode/internal/dic_node_state_prevword.h
index 5854f4f..b898620 100644
--- a/native/jni/src/suggest/core/dicnode/internal/dic_node_state_prevword.h
+++ b/native/jni/src/suggest/core/dicnode/internal/dic_node_state_prevword.h
@@ -22,6 +22,7 @@
 
 #include "defines.h"
 #include "suggest/core/dicnode/dic_node_utils.h"
+#include "suggest/core/layout/proximity_info_state.h"
 
 namespace latinime {
 
@@ -29,9 +30,8 @@
  public:
     AK_FORCE_INLINE DicNodeStatePrevWord()
             : mPrevWordCount(0), mPrevWordLength(0), mPrevWordStart(0), mPrevWordProbability(0),
-              mPrevWordNodePos(NOT_A_VALID_WORD_POS) {
+              mPrevWordNodePos(NOT_A_DICT_POS), mSecondWordFirstInputIndex(NOT_AN_INDEX) {
         memset(mPrevWord, 0, sizeof(mPrevWord));
-        memset(mPrevSpacePositions, 0, sizeof(mPrevSpacePositions));
     }
 
     virtual ~DicNodeStatePrevWord() {}
@@ -41,8 +41,8 @@
         mPrevWordCount = 0;
         mPrevWordStart = 0;
         mPrevWordProbability = -1;
-        mPrevWordNodePos = NOT_A_VALID_WORD_POS;
-        memset(mPrevSpacePositions, 0, sizeof(mPrevSpacePositions));
+        mPrevWordNodePos = NOT_A_DICT_POS;
+        mSecondWordFirstInputIndex = NOT_AN_INDEX;
     }
 
     void init(const int prevWordNodePos) {
@@ -51,7 +51,7 @@
         mPrevWordStart = 0;
         mPrevWordProbability = -1;
         mPrevWordNodePos = prevWordNodePos;
-        memset(mPrevSpacePositions, 0, sizeof(mPrevSpacePositions));
+        mSecondWordFirstInputIndex = NOT_AN_INDEX;
     }
 
     // Init by copy
@@ -61,24 +61,26 @@
         mPrevWordStart = prevWord->mPrevWordStart;
         mPrevWordProbability = prevWord->mPrevWordProbability;
         mPrevWordNodePos = prevWord->mPrevWordNodePos;
+        mSecondWordFirstInputIndex = prevWord->mSecondWordFirstInputIndex;
         memcpy(mPrevWord, prevWord->mPrevWord, prevWord->mPrevWordLength * sizeof(mPrevWord[0]));
-        memcpy(mPrevSpacePositions, prevWord->mPrevSpacePositions, sizeof(mPrevSpacePositions));
     }
 
     void init(const int16_t prevWordCount, const int16_t prevWordProbability,
             const int prevWordNodePos, const int *const src0, const int16_t length0,
-            const int *const src1, const int16_t length1, const int *const prevSpacePositions,
-            const int lastInputIndex) {
-        mPrevWordCount = prevWordCount;
+            const int *const src1, const int16_t length1,
+            const int prevWordSecondWordFirstInputIndex, const int lastInputIndex) {
+        mPrevWordCount = min(prevWordCount, static_cast<int16_t>(MAX_RESULTS));
         mPrevWordProbability = prevWordProbability;
         mPrevWordNodePos = prevWordNodePos;
-        const int twoWordsLen =
+        int twoWordsLen =
                 DicNodeUtils::appendTwoWords(src0, length0, src1, length1, mPrevWord);
+        if (twoWordsLen >= MAX_WORD_LENGTH) {
+            twoWordsLen = MAX_WORD_LENGTH - 1;
+        }
         mPrevWord[twoWordsLen] = KEYCODE_SPACE;
         mPrevWordStart = length0;
         mPrevWordLength = static_cast<int16_t>(twoWordsLen + 1);
-        memcpy(mPrevSpacePositions, prevSpacePositions, sizeof(mPrevSpacePositions));
-        mPrevSpacePositions[mPrevWordCount - 1] = lastInputIndex;
+        mSecondWordFirstInputIndex = prevWordSecondWordFirstInputIndex;
     }
 
     void truncate(const int offset) {
@@ -93,11 +95,12 @@
         mPrevWordLength = newPrevWordLength;
     }
 
-    void outputSpacePositions(int *spaceIndices) const {
-        // Convert uint16_t to int
-        for (int i = 0; i < MAX_RESULTS; i++) {
-            spaceIndices[i] = mPrevSpacePositions[i];
-        }
+    void setSecondWordFirstInputIndex(const int inputIndex) {
+        mSecondWordFirstInputIndex = inputIndex;
+    }
+
+    int getSecondWordFirstInputIndex() const {
+        return mSecondWordFirstInputIndex;
     }
 
     // TODO: remove
@@ -113,10 +116,6 @@
         return mPrevWordStart;
     }
 
-    int16_t getPrevWordProbability() const {
-        return mPrevWordProbability;
-    }
-
     int getPrevWordNodePos() const {
         return mPrevWordNodePos;
     }
@@ -139,8 +138,6 @@
 
     // TODO: Move to private
     int mPrevWord[MAX_WORD_LENGTH];
-    // TODO: Move to private
-    int mPrevSpacePositions[MAX_RESULTS];
 
  private:
     // Caution!!!
@@ -151,6 +148,7 @@
     int16_t mPrevWordStart;
     int16_t mPrevWordProbability;
     int mPrevWordNodePos;
+    int mSecondWordFirstInputIndex;
 };
 } // namespace latinime
 #endif // LATINIME_DIC_NODE_STATE_PREVWORD_H
diff --git a/native/jni/src/suggest/core/dictionary/bigram_dictionary.cpp b/native/jni/src/suggest/core/dictionary/bigram_dictionary.cpp
index 3751ae5..71f4ef6 100644
--- a/native/jni/src/suggest/core/dictionary/bigram_dictionary.cpp
+++ b/native/jni/src/suggest/core/dictionary/bigram_dictionary.cpp
@@ -22,15 +22,15 @@
 
 #include "defines.h"
 #include "suggest/core/dictionary/binary_dictionary_bigrams_iterator.h"
-#include "suggest/core/dictionary/binary_dictionary_info.h"
 #include "suggest/core/dictionary/dictionary.h"
-#include "suggest/core/dictionary/probability_utils.h"
+#include "suggest/core/policy/dictionary_structure_with_buffer_policy.h"
 #include "utils/char_utils.h"
 
 namespace latinime {
 
-BigramDictionary::BigramDictionary(const BinaryDictionaryInfo *const binaryDictionaryInfo)
-        : mBinaryDictionaryInfo(binaryDictionaryInfo) {
+BigramDictionary::BigramDictionary(
+        const DictionaryStructureWithBufferPolicy *const dictionaryStructurePolicy)
+        : mDictionaryStructurePolicy(dictionaryStructurePolicy) {
     if (DEBUG_DICT) {
         AKLOGI("BigramDictionary - constructor");
     }
@@ -112,22 +112,28 @@
     int bigramCount = 0;
     int unigramProbability = 0;
     int bigramBuffer[MAX_WORD_LENGTH];
-    BinaryDictionaryBigramsIterator bigramsIt(mBinaryDictionaryInfo, pos);
+    BinaryDictionaryBigramsIterator bigramsIt(
+            mDictionaryStructurePolicy->getBigramsStructurePolicy(), pos);
     while (bigramsIt.hasNext()) {
         bigramsIt.next();
-        const int length = mBinaryDictionaryInfo->getStructurePolicy()->
-                getCodePointsAndProbabilityAndReturnCodePointCount(
-                        mBinaryDictionaryInfo, bigramsIt.getBigramPos(), MAX_WORD_LENGTH,
-                        bigramBuffer, &unigramProbability);
+        if (bigramsIt.getBigramPos() == NOT_A_DICT_POS) {
+            continue;
+        }
+        const int codePointCount = mDictionaryStructurePolicy->
+                getCodePointsAndProbabilityAndReturnCodePointCount(bigramsIt.getBigramPos(),
+                        MAX_WORD_LENGTH, bigramBuffer, &unigramProbability);
+        if (codePointCount <= 0) {
+            continue;
+        }
         // Due to space constraints, the probability for bigrams is approximate - the lower the
         // unigram probability, the worse the precision. The theoritical maximum error in
         // resulting probability is 8 - although in the practice it's never bigger than 3 or 4
         // in very bad cases. This means that sometimes, we'll see some bigrams interverted
         // here, but it can't get too bad.
-        const int probability = ProbabilityUtils::computeProbabilityForBigram(
+        const int probability = mDictionaryStructurePolicy->getProbability(
                 unigramProbability, bigramsIt.getProbability());
-        addWordBigram(bigramBuffer, length, probability, outBigramProbability, outBigramCodePoints,
-                outputTypes);
+        addWordBigram(bigramBuffer, codePointCount, probability, outBigramProbability,
+                outBigramCodePoints, outputTypes);
         ++bigramCount;
     }
     return min(bigramCount, MAX_RESULTS);
@@ -138,30 +144,32 @@
 int BigramDictionary::getBigramListPositionForWord(const int *prevWord, const int prevWordLength,
         const bool forceLowerCaseSearch) const {
     if (0 >= prevWordLength) return NOT_A_DICT_POS;
-    int pos = mBinaryDictionaryInfo->getStructurePolicy()->getTerminalNodePositionOfWord(
-            mBinaryDictionaryInfo, prevWord, prevWordLength, forceLowerCaseSearch);
-    if (NOT_A_VALID_WORD_POS == pos) return NOT_A_DICT_POS;
-    return mBinaryDictionaryInfo->getStructurePolicy()->getBigramsPositionOfNode(
-            mBinaryDictionaryInfo, pos);
+    int pos = mDictionaryStructurePolicy->getTerminalNodePositionOfWord(prevWord, prevWordLength,
+            forceLowerCaseSearch);
+    if (NOT_A_DICT_POS == pos) return NOT_A_DICT_POS;
+    return mDictionaryStructurePolicy->getBigramsPositionOfPtNode(pos);
 }
 
-bool BigramDictionary::isValidBigram(const int *word0, int length0, const int *word1,
+int BigramDictionary::getBigramProbability(const int *word0, int length0, const int *word1,
         int length1) const {
     int pos = getBigramListPositionForWord(word0, length0, false /* forceLowerCaseSearch */);
     // getBigramListPositionForWord returns 0 if this word isn't in the dictionary or has no bigrams
-    if (NOT_A_DICT_POS == pos) return false;
-    int nextWordPos = mBinaryDictionaryInfo->getStructurePolicy()->getTerminalNodePositionOfWord(
-            mBinaryDictionaryInfo, word1, length1, false /* forceLowerCaseSearch */);
-    if (NOT_A_VALID_WORD_POS == nextWordPos) return false;
+    if (NOT_A_DICT_POS == pos) return NOT_A_PROBABILITY;
+    int nextWordPos = mDictionaryStructurePolicy->getTerminalNodePositionOfWord(word1, length1,
+            false /* forceLowerCaseSearch */);
+    if (NOT_A_DICT_POS == nextWordPos) return NOT_A_PROBABILITY;
 
-    BinaryDictionaryBigramsIterator bigramsIt(mBinaryDictionaryInfo, pos);
+    BinaryDictionaryBigramsIterator bigramsIt(
+            mDictionaryStructurePolicy->getBigramsStructurePolicy(), pos);
     while (bigramsIt.hasNext()) {
         bigramsIt.next();
         if (bigramsIt.getBigramPos() == nextWordPos) {
-            return true;
+            return mDictionaryStructurePolicy->getProbability(
+                    mDictionaryStructurePolicy->getUnigramProbabilityOfPtNode(nextWordPos),
+                    bigramsIt.getProbability());
         }
     }
-    return false;
+    return NOT_A_PROBABILITY;
 }
 
 // TODO: Move functions related to bigram to here
diff --git a/native/jni/src/suggest/core/dictionary/bigram_dictionary.h b/native/jni/src/suggest/core/dictionary/bigram_dictionary.h
index 438c34c..8af7ee7 100644
--- a/native/jni/src/suggest/core/dictionary/bigram_dictionary.h
+++ b/native/jni/src/suggest/core/dictionary/bigram_dictionary.h
@@ -21,15 +21,15 @@
 
 namespace latinime {
 
-class BinaryDictionaryInfo;
+class DictionaryStructureWithBufferPolicy;
 
 class BigramDictionary {
  public:
-    BigramDictionary(const BinaryDictionaryInfo *const binaryDictionaryInfo);
+    BigramDictionary(const DictionaryStructureWithBufferPolicy *const dictionaryStructurePolicy);
 
     int getPredictions(const int *word, int length, int *outBigramCodePoints,
             int *outBigramProbability, int *outputTypes) const;
-    bool isValidBigram(const int *word1, int length1, const int *word2, int length2) const;
+    int getBigramProbability(const int *word1, int length1, const int *word2, int length2) const;
     ~BigramDictionary();
 
  private:
@@ -40,7 +40,7 @@
     int getBigramListPositionForWord(const int *prevWord, const int prevWordLength,
             const bool forceLowerCaseSearch) const;
 
-    const BinaryDictionaryInfo *const mBinaryDictionaryInfo;
+    const DictionaryStructureWithBufferPolicy *const mDictionaryStructurePolicy;
 };
 } // namespace latinime
 #endif // LATINIME_BIGRAM_DICTIONARY_H
diff --git a/native/jni/src/suggest/core/dictionary/binary_dictionary_bigrams_iterator.h b/native/jni/src/suggest/core/dictionary/binary_dictionary_bigrams_iterator.h
index 8cbb129..d16ac47 100644
--- a/native/jni/src/suggest/core/dictionary/binary_dictionary_bigrams_iterator.h
+++ b/native/jni/src/suggest/core/dictionary/binary_dictionary_bigrams_iterator.h
@@ -18,51 +18,41 @@
 #define LATINIME_BINARY_DICTIONARY_BIGRAMS_ITERATOR_H
 
 #include "defines.h"
-#include "suggest/core/dictionary/binary_dictionary_info.h"
-#include "suggest/core/dictionary/binary_dictionary_terminal_attributes_reading_utils.h"
+#include "suggest/core/policy/dictionary_bigrams_structure_policy.h"
 
 namespace latinime {
 
 class BinaryDictionaryBigramsIterator {
  public:
     BinaryDictionaryBigramsIterator(
-            const BinaryDictionaryInfo *const binaryDictionaryInfo, const int pos)
-            : mBinaryDictionaryInfo(binaryDictionaryInfo), mPos(pos), mBigramFlags(0),
-              mBigramPos(NOT_A_DICT_POS), mHasNext(pos != NOT_A_DICT_POS) {}
+            const DictionaryBigramsStructurePolicy *const bigramsStructurePolicy, const int pos)
+            : mBigramsStructurePolicy(bigramsStructurePolicy), mPos(pos),
+              mBigramPos(NOT_A_DICT_POS), mProbability(NOT_A_PROBABILITY),
+              mHasNext(pos != NOT_A_DICT_POS) {}
 
     AK_FORCE_INLINE bool hasNext() const {
         return mHasNext;
     }
 
     AK_FORCE_INLINE void next() {
-        mBigramFlags = BinaryDictionaryTerminalAttributesReadingUtils::getFlagsAndForwardPointer(
-                mBinaryDictionaryInfo, &mPos);
-        mBigramPos =
-                BinaryDictionaryTerminalAttributesReadingUtils::getBigramAddressAndForwardPointer(
-                        mBinaryDictionaryInfo, mBigramFlags, &mPos);
-        mHasNext = BinaryDictionaryTerminalAttributesReadingUtils::hasNext(mBigramFlags);
+        mBigramsStructurePolicy->getNextBigram(&mBigramPos, &mProbability, &mHasNext, &mPos);
     }
 
     AK_FORCE_INLINE int getProbability() const {
-        return BinaryDictionaryTerminalAttributesReadingUtils::getProbabilityFromFlags(
-                mBigramFlags);
+        return mProbability;
     }
 
     AK_FORCE_INLINE int getBigramPos() const {
         return mBigramPos;
     }
 
-    AK_FORCE_INLINE int getFlags() const {
-        return mBigramFlags;
-    }
-
  private:
     DISALLOW_COPY_AND_ASSIGN(BinaryDictionaryBigramsIterator);
 
-    const BinaryDictionaryInfo *const mBinaryDictionaryInfo;
+    const DictionaryBigramsStructurePolicy *const mBigramsStructurePolicy;
     int mPos;
-    BinaryDictionaryTerminalAttributesReadingUtils::BigramFlags mBigramFlags;
     int mBigramPos;
+    int mProbability;
     bool mHasNext;
 };
 } // namespace latinime
diff --git a/native/jni/src/suggest/core/dictionary/binary_dictionary_format_utils.cpp b/native/jni/src/suggest/core/dictionary/binary_dictionary_format_utils.cpp
deleted file mode 100644
index 0e8d72f..0000000
--- a/native/jni/src/suggest/core/dictionary/binary_dictionary_format_utils.cpp
+++ /dev/null
@@ -1,73 +0,0 @@
-/*
- * Copyright (C) 2013 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.
- */
-
-#include "suggest/core/dictionary/binary_dictionary_format_utils.h"
-
-namespace latinime {
-
-/**
- * Dictionary size
- */
-// Any file smaller than this is not a dictionary.
-const int BinaryDictionaryFormatUtils::DICTIONARY_MINIMUM_SIZE = 4;
-
-/**
- * Format versions
- */
-
-// The versions of Latin IME that only handle format version 1 only test for the magic
-// number, so we had to change it so that version 2 files would be rejected by older
-// implementations. On this occasion, we made the magic number 32 bits long.
-const uint32_t BinaryDictionaryFormatUtils::HEADER_VERSION_2_MAGIC_NUMBER = 0x9BC13AFE;
-// Magic number (4 bytes), version (2 bytes), options (2 bytes), header size (4 bytes) = 12
-const int BinaryDictionaryFormatUtils::HEADER_VERSION_2_MINIMUM_SIZE = 12;
-
-/* static */ BinaryDictionaryFormatUtils::FORMAT_VERSION
-        BinaryDictionaryFormatUtils::detectFormatVersion(const uint8_t *const dict,
-                const int dictSize) {
-    // The magic number is stored big-endian.
-    // If the dictionary is less than 4 bytes, we can't even read the magic number, so we don't
-    // understand this format.
-    if (dictSize < DICTIONARY_MINIMUM_SIZE) {
-        return UNKNOWN_VERSION;
-    }
-    const uint32_t magicNumber = ByteArrayUtils::readUint32(dict, 0);
-    switch (magicNumber) {
-        case HEADER_VERSION_2_MAGIC_NUMBER:
-            // Version 2 header are at least 12 bytes long.
-            // If this header has the version 2 magic number but is less than 12 bytes long,
-            // then it's an unknown format and we need to avoid confidently reading the next bytes.
-            if (dictSize < HEADER_VERSION_2_MINIMUM_SIZE) {
-                return UNKNOWN_VERSION;
-            }
-            // Version 2 header is as follows:
-            // Magic number (4 bytes) 0x9B 0xC1 0x3A 0xFE
-            // Version number (2 bytes)
-            // Options (2 bytes)
-            // Header size (4 bytes) : integer, big endian
-            if (ByteArrayUtils::readUint16(dict, 4) == 2) {
-                return VERSION_2;
-            } else if (ByteArrayUtils::readUint16(dict, 4) == 3) {
-                return VERSION_3;
-            } else {
-                return UNKNOWN_VERSION;
-            }
-        default:
-            return UNKNOWN_VERSION;
-    }
-}
-
-} // namespace latinime
diff --git a/native/jni/src/suggest/core/dictionary/binary_dictionary_header.cpp b/native/jni/src/suggest/core/dictionary/binary_dictionary_header.cpp
deleted file mode 100644
index 91c643a..0000000
--- a/native/jni/src/suggest/core/dictionary/binary_dictionary_header.cpp
+++ /dev/null
@@ -1,49 +0,0 @@
-/*
- * Copyright (C) 2013, 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.
- */
-
-#include "suggest/core/dictionary/binary_dictionary_header.h"
-
-#include "defines.h"
-#include "suggest/core/dictionary/binary_dictionary_info.h"
-
-namespace latinime {
-
-const char *const BinaryDictionaryHeader::MULTIPLE_WORDS_DEMOTION_RATE_KEY =
-        "MULTIPLE_WORDS_DEMOTION_RATE";
-const float BinaryDictionaryHeader::DEFAULT_MULTI_WORD_COST_MULTIPLIER = 1.0f;
-const float BinaryDictionaryHeader::MULTI_WORD_COST_MULTIPLIER_SCALE = 100.0f;
-
-BinaryDictionaryHeader::BinaryDictionaryHeader(
-        const BinaryDictionaryInfo *const binaryDictionaryInfo)
-        : mBinaryDictionaryInfo(binaryDictionaryInfo),
-          mDictionaryFlags(BinaryDictionaryHeaderReadingUtils::getFlags(binaryDictionaryInfo)),
-          mSize(BinaryDictionaryHeaderReadingUtils::getHeaderSize(binaryDictionaryInfo)),
-          mMultiWordCostMultiplier(readMultiWordCostMultiplier()) {}
-
-float BinaryDictionaryHeader::readMultiWordCostMultiplier() const {
-    const int headerValue = BinaryDictionaryHeaderReadingUtils::readHeaderValueInt(
-            mBinaryDictionaryInfo, MULTIPLE_WORDS_DEMOTION_RATE_KEY);
-    if (headerValue == S_INT_MIN) {
-        // not found
-        return DEFAULT_MULTI_WORD_COST_MULTIPLIER;
-    }
-    if (headerValue <= 0) {
-        return static_cast<float>(MAX_VALUE_FOR_WEIGHTING);
-    }
-    return MULTI_WORD_COST_MULTIPLIER_SCALE / static_cast<float>(headerValue);
-}
-
-} // namespace latinime
diff --git a/native/jni/src/suggest/core/dictionary/binary_dictionary_header.h b/native/jni/src/suggest/core/dictionary/binary_dictionary_header.h
deleted file mode 100644
index 240512b..0000000
--- a/native/jni/src/suggest/core/dictionary/binary_dictionary_header.h
+++ /dev/null
@@ -1,85 +0,0 @@
-/*
- * Copyright (C) 2013, 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.
- */
-
-#ifndef LATINIME_BINARY_DICTIONARY_HEADER_H
-#define LATINIME_BINARY_DICTIONARY_HEADER_H
-
-#include "defines.h"
-#include "suggest/core/dictionary/binary_dictionary_header_reading_utils.h"
-
-namespace latinime {
-
-class BinaryDictionaryInfo;
-
-/**
- * This class abstracts dictionary header structures and provide interface to access dictionary
- * header information.
- */
-class BinaryDictionaryHeader {
- public:
-    explicit BinaryDictionaryHeader(const BinaryDictionaryInfo *const binaryDictionaryInfo);
-
-    AK_FORCE_INLINE int getSize() const {
-        return mSize;
-    }
-
-    AK_FORCE_INLINE bool supportsDynamicUpdate() const {
-        return BinaryDictionaryHeaderReadingUtils::supportsDynamicUpdate(mDictionaryFlags);
-    }
-
-    AK_FORCE_INLINE bool requiresGermanUmlautProcessing() const {
-        return BinaryDictionaryHeaderReadingUtils::requiresGermanUmlautProcessing(mDictionaryFlags);
-    }
-
-    AK_FORCE_INLINE bool requiresFrenchLigatureProcessing() const {
-        return BinaryDictionaryHeaderReadingUtils::requiresFrenchLigatureProcessing(
-                mDictionaryFlags);
-    }
-
-    AK_FORCE_INLINE float getMultiWordCostMultiplier() const {
-        return mMultiWordCostMultiplier;
-    }
-
-    AK_FORCE_INLINE void readHeaderValueOrQuestionMark(const char *const key,
-            int *outValue, int outValueSize) const {
-        if (outValueSize <= 0) return;
-        if (outValueSize == 1) {
-            outValue[0] = '\0';
-            return;
-        }
-        if (!BinaryDictionaryHeaderReadingUtils::readHeaderValue(mBinaryDictionaryInfo,
-                key, outValue, outValueSize)) {
-            outValue[0] = '?';
-            outValue[1] = '\0';
-        }
-    }
-
- private:
-    DISALLOW_COPY_AND_ASSIGN(BinaryDictionaryHeader);
-
-    static const char *const MULTIPLE_WORDS_DEMOTION_RATE_KEY;
-    static const float DEFAULT_MULTI_WORD_COST_MULTIPLIER;
-    static const float MULTI_WORD_COST_MULTIPLIER_SCALE;
-
-    const BinaryDictionaryInfo *const mBinaryDictionaryInfo;
-    const BinaryDictionaryHeaderReadingUtils::DictionaryFlags mDictionaryFlags;
-    const int mSize;
-    const float mMultiWordCostMultiplier;
-
-    float readMultiWordCostMultiplier() const;
-};
-} // namespace latinime
-#endif // LATINIME_BINARY_DICTIONARY_HEADER_H
diff --git a/native/jni/src/suggest/core/dictionary/binary_dictionary_header_reading_utils.cpp b/native/jni/src/suggest/core/dictionary/binary_dictionary_header_reading_utils.cpp
deleted file mode 100644
index a57b0f8..0000000
--- a/native/jni/src/suggest/core/dictionary/binary_dictionary_header_reading_utils.cpp
+++ /dev/null
@@ -1,123 +0,0 @@
-/*
- * Copyright (C) 2013, 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.
- */
-
-#include "suggest/core/dictionary/binary_dictionary_header_reading_utils.h"
-
-#include <cctype>
-#include <cstdlib>
-
-#include "defines.h"
-#include "suggest/core/dictionary/binary_dictionary_info.h"
-
-namespace latinime {
-
-const int BinaryDictionaryHeaderReadingUtils::MAX_OPTION_KEY_LENGTH = 256;
-
-const int BinaryDictionaryHeaderReadingUtils::VERSION_2_HEADER_MAGIC_NUMBER_SIZE = 4;
-const int BinaryDictionaryHeaderReadingUtils::VERSION_2_HEADER_DICTIONARY_VERSION_SIZE = 2;
-const int BinaryDictionaryHeaderReadingUtils::VERSION_2_HEADER_FLAG_SIZE = 2;
-const int BinaryDictionaryHeaderReadingUtils::VERSION_2_HEADER_SIZE_FIELD_SIZE = 4;
-
-const BinaryDictionaryHeaderReadingUtils::DictionaryFlags
-        BinaryDictionaryHeaderReadingUtils::NO_FLAGS = 0;
-// Flags for special processing
-// Those *must* match the flags in makedict (BinaryDictInputOutput#*_PROCESSING_FLAG) or
-// something very bad (like, the apocalypse) will happen. Please update both at the same time.
-const BinaryDictionaryHeaderReadingUtils::DictionaryFlags
-        BinaryDictionaryHeaderReadingUtils::GERMAN_UMLAUT_PROCESSING_FLAG = 0x1;
-const BinaryDictionaryHeaderReadingUtils::DictionaryFlags
-        BinaryDictionaryHeaderReadingUtils::SUPPORTS_DYNAMIC_UPDATE_FLAG = 0x2;
-const BinaryDictionaryHeaderReadingUtils::DictionaryFlags
-        BinaryDictionaryHeaderReadingUtils::FRENCH_LIGATURE_PROCESSING_FLAG = 0x4;
-
-/* static */ int BinaryDictionaryHeaderReadingUtils::getHeaderSize(
-        const BinaryDictionaryInfo *const binaryDictionaryInfo) {
-    switch (getHeaderVersion(binaryDictionaryInfo->getFormat())) {
-        case HEADER_VERSION_2:
-            // See the format of the header in the comment in
-            // BinaryDictionaryFormatUtils::detectFormatVersion()
-            return ByteArrayUtils::readUint32(binaryDictionaryInfo->getDictBuf(),
-                    VERSION_2_HEADER_MAGIC_NUMBER_SIZE + VERSION_2_HEADER_DICTIONARY_VERSION_SIZE
-                            + VERSION_2_HEADER_FLAG_SIZE);
-        default:
-            return S_INT_MAX;
-    }
-}
-
-/* static */ BinaryDictionaryHeaderReadingUtils::DictionaryFlags
-        BinaryDictionaryHeaderReadingUtils::getFlags(
-                const BinaryDictionaryInfo *const binaryDictionaryInfo) {
-    switch (getHeaderVersion(binaryDictionaryInfo->getFormat())) {
-        case HEADER_VERSION_2:
-            return ByteArrayUtils::readUint16(binaryDictionaryInfo->getDictBuf(),
-                    VERSION_2_HEADER_MAGIC_NUMBER_SIZE + VERSION_2_HEADER_DICTIONARY_VERSION_SIZE);
-        default:
-            return NO_FLAGS;
-    }
-}
-
-// Returns if the key is found or not and reads the found value into outValue.
-/* static */ bool BinaryDictionaryHeaderReadingUtils::readHeaderValue(
-        const BinaryDictionaryInfo *const binaryDictionaryInfo,
-        const char *const key, int *outValue, const int outValueSize) {
-    if (outValueSize <= 0) {
-        return false;
-    }
-    const int headerSize = getHeaderSize(binaryDictionaryInfo);
-    int pos = getHeaderOptionsPosition(binaryDictionaryInfo->getFormat());
-    if (pos == NOT_A_DICT_POS) {
-        // The header doesn't have header options.
-        return false;
-    }
-    while (pos < headerSize) {
-        if(ByteArrayUtils::compareStringInBufferWithCharArray(
-                binaryDictionaryInfo->getDictBuf(), key, headerSize - pos, &pos) == 0) {
-            // The key was found.
-            const int length = ByteArrayUtils::readStringAndAdvancePosition(
-                    binaryDictionaryInfo->getDictBuf(), outValueSize, outValue, &pos);
-            // Add a 0 terminator to the string.
-            outValue[length < outValueSize ? length : outValueSize - 1] = '\0';
-            return true;
-        }
-        ByteArrayUtils::advancePositionToBehindString(
-                binaryDictionaryInfo->getDictBuf(), headerSize - pos, &pos);
-    }
-    // The key was not found.
-    return false;
-}
-
-/* static */ int BinaryDictionaryHeaderReadingUtils::readHeaderValueInt(
-        const BinaryDictionaryInfo *const binaryDictionaryInfo, const char *const key) {
-    const int bufferSize = LARGEST_INT_DIGIT_COUNT;
-    int intBuffer[bufferSize];
-    char charBuffer[bufferSize];
-    if (!readHeaderValue(binaryDictionaryInfo, key, intBuffer, bufferSize)) {
-        return S_INT_MIN;
-    }
-    for (int i = 0; i < bufferSize; ++i) {
-        charBuffer[i] = intBuffer[i];
-        if (charBuffer[i] == '0') {
-            break;
-        }
-        if (!isdigit(charBuffer[i])) {
-            // If not a number, return S_INT_MIN
-            return S_INT_MIN;
-        }
-    }
-    return atoi(charBuffer);
-}
-
-} // namespace latinime
diff --git a/native/jni/src/suggest/core/dictionary/binary_dictionary_header_reading_utils.h b/native/jni/src/suggest/core/dictionary/binary_dictionary_header_reading_utils.h
deleted file mode 100644
index 6174822..0000000
--- a/native/jni/src/suggest/core/dictionary/binary_dictionary_header_reading_utils.h
+++ /dev/null
@@ -1,105 +0,0 @@
-/*
- * Copyright (C) 2013, 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.
- */
-
-#ifndef LATINIME_DICTIONARY_HEADER_READING_UTILS_H
-#define LATINIME_DICTIONARY_HEADER_READING_UTILS_H
-
-#include <stdint.h>
-
-#include "defines.h"
-#include "suggest/core/dictionary/binary_dictionary_format_utils.h"
-
-namespace latinime {
-
-class BinaryDictionaryInfo;
-
-class BinaryDictionaryHeaderReadingUtils {
- public:
-    typedef uint16_t DictionaryFlags;
-
-    static const int MAX_OPTION_KEY_LENGTH;
-
-    static int getHeaderSize(const BinaryDictionaryInfo *const binaryDictionaryInfo);
-
-    static DictionaryFlags getFlags(const BinaryDictionaryInfo *const binaryDictionaryInfo);
-
-    static AK_FORCE_INLINE bool supportsDynamicUpdate(const DictionaryFlags flags) {
-        return (flags & SUPPORTS_DYNAMIC_UPDATE_FLAG) != 0;
-    }
-
-    static AK_FORCE_INLINE bool requiresGermanUmlautProcessing(const DictionaryFlags flags) {
-        return (flags & GERMAN_UMLAUT_PROCESSING_FLAG) != 0;
-    }
-
-    static AK_FORCE_INLINE bool requiresFrenchLigatureProcessing(const DictionaryFlags flags) {
-        return (flags & FRENCH_LIGATURE_PROCESSING_FLAG) != 0;
-    }
-
-    static AK_FORCE_INLINE int getHeaderOptionsPosition(
-            const BinaryDictionaryFormatUtils::FORMAT_VERSION dictionaryFormat) {
-        switch (getHeaderVersion(dictionaryFormat)) {
-        case HEADER_VERSION_2:
-            return VERSION_2_HEADER_MAGIC_NUMBER_SIZE + VERSION_2_HEADER_DICTIONARY_VERSION_SIZE
-                    + VERSION_2_HEADER_FLAG_SIZE + VERSION_2_HEADER_SIZE_FIELD_SIZE;
-            break;
-        default:
-            return NOT_A_DICT_POS;
-        }
-    }
-
-    static bool readHeaderValue(
-            const BinaryDictionaryInfo *const binaryDictionaryInfo,
-            const char *const key, int *outValue, const int outValueSize);
-
-    static int readHeaderValueInt(
-            const BinaryDictionaryInfo *const binaryDictionaryInfo, const char *const key);
-
- private:
-    DISALLOW_IMPLICIT_CONSTRUCTORS(BinaryDictionaryHeaderReadingUtils);
-
-    enum HEADER_VERSION {
-        HEADER_VERSION_2,
-        UNKNOWN_HEADER_VERSION
-    };
-
-    static const int VERSION_2_HEADER_MAGIC_NUMBER_SIZE;
-    static const int VERSION_2_HEADER_DICTIONARY_VERSION_SIZE;
-    static const int VERSION_2_HEADER_FLAG_SIZE;
-    static const int VERSION_2_HEADER_SIZE_FIELD_SIZE;
-
-    static const DictionaryFlags NO_FLAGS;
-    // Flags for special processing
-    // Those *must* match the flags in makedict (FormatSpec#*_PROCESSING_FLAGS) or
-    // something very bad (like, the apocalypse) will happen. Please update both at the same time.
-    static const DictionaryFlags GERMAN_UMLAUT_PROCESSING_FLAG;
-    static const DictionaryFlags SUPPORTS_DYNAMIC_UPDATE_FLAG;
-    static const DictionaryFlags FRENCH_LIGATURE_PROCESSING_FLAG;
-    static const DictionaryFlags CONTAINS_BIGRAMS_FLAG;
-
-    static HEADER_VERSION getHeaderVersion(
-            const BinaryDictionaryFormatUtils::FORMAT_VERSION formatVersion) {
-        switch(formatVersion) {
-            case BinaryDictionaryFormatUtils::VERSION_2:
-                // Fall through
-            case BinaryDictionaryFormatUtils::VERSION_3:
-                return HEADER_VERSION_2;
-            default:
-                return UNKNOWN_HEADER_VERSION;
-        }
-    }
-};
-}
-#endif /* LATINIME_DICTIONARY_HEADER_READING_UTILS_H */
diff --git a/native/jni/src/suggest/core/dictionary/binary_dictionary_info.h b/native/jni/src/suggest/core/dictionary/binary_dictionary_info.h
deleted file mode 100644
index cbea18f..0000000
--- a/native/jni/src/suggest/core/dictionary/binary_dictionary_info.h
+++ /dev/null
@@ -1,124 +0,0 @@
-/*
- * Copyright (C) 2013, 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.
- */
-
-#ifndef LATINIME_BINARY_DICTIONARY_INFO_H
-#define LATINIME_BINARY_DICTIONARY_INFO_H
-
-#include <stdint.h>
-
-#include "defines.h"
-#include "jni.h"
-#include "suggest/core/dictionary/binary_dictionary_format_utils.h"
-#include "suggest/core/dictionary/binary_dictionary_header.h"
-#include "suggest/policyimpl/dictionary/dictionary_structure_policy_factory.h"
-#include "utils/log_utils.h"
-
-namespace latinime {
-
-class BinaryDictionaryInfo {
- public:
-     AK_FORCE_INLINE BinaryDictionaryInfo(JNIEnv *env, const uint8_t *const dictBuf,
-            const int dictSize, const int mmapFd, const int dictBufOffset, const bool isUpdatable)
-            : mDictBuf(dictBuf), mDictSize(dictSize), mMmapFd(mmapFd),
-              mDictBufOffset(dictBufOffset), mIsUpdatable(isUpdatable),
-              mDictionaryFormat(BinaryDictionaryFormatUtils::detectFormatVersion(
-                      mDictBuf, mDictSize)),
-              mDictionaryHeader(this), mDictRoot(mDictBuf + mDictionaryHeader.getSize()),
-              mStructurePolicy(DictionaryStructurePolicyFactory::getDictionaryStructurePolicy(
-                      mDictionaryFormat)) {
-        logDictionaryInfo(env);
-    }
-
-    AK_FORCE_INLINE const uint8_t *getDictBuf() const {
-        return mDictBuf;
-    }
-
-    AK_FORCE_INLINE int getDictSize() const {
-        return mDictSize;
-    }
-
-    AK_FORCE_INLINE int getMmapFd() const {
-        return mMmapFd;
-    }
-
-    AK_FORCE_INLINE int getDictBufOffset() const {
-        return mDictBufOffset;
-    }
-
-    AK_FORCE_INLINE const uint8_t *getDictRoot() const {
-        return mDictRoot;
-    }
-
-    AK_FORCE_INLINE BinaryDictionaryFormatUtils::FORMAT_VERSION getFormat() const {
-        return mDictionaryFormat;
-    }
-
-    AK_FORCE_INLINE const BinaryDictionaryHeader *getHeader() const {
-        return &mDictionaryHeader;
-    }
-
-    AK_FORCE_INLINE bool isDynamicallyUpdatable() const {
-        // TODO: Support dynamic dictionary formats.
-        const bool isUpdatableDictionaryFormat = false;
-        return mIsUpdatable && isUpdatableDictionaryFormat;
-    }
-
-    AK_FORCE_INLINE const DictionaryStructurePolicy *getStructurePolicy() const {
-        return mStructurePolicy;
-    }
-
- private:
-    DISALLOW_COPY_AND_ASSIGN(BinaryDictionaryInfo);
-
-    const uint8_t *const mDictBuf;
-    const int mDictSize;
-    const int mMmapFd;
-    const int mDictBufOffset;
-    const bool mIsUpdatable;
-    const BinaryDictionaryFormatUtils::FORMAT_VERSION mDictionaryFormat;
-    const BinaryDictionaryHeader mDictionaryHeader;
-    const uint8_t *const mDictRoot;
-    const DictionaryStructurePolicy *const mStructurePolicy;
-
-    AK_FORCE_INLINE void logDictionaryInfo(JNIEnv *const env) const {
-        const int BUFFER_SIZE = 16;
-        int dictionaryIdCodePointBuffer[BUFFER_SIZE];
-        int versionStringCodePointBuffer[BUFFER_SIZE];
-        int dateStringCodePointBuffer[BUFFER_SIZE];
-        mDictionaryHeader.readHeaderValueOrQuestionMark("dictionary",
-                dictionaryIdCodePointBuffer, BUFFER_SIZE);
-        mDictionaryHeader.readHeaderValueOrQuestionMark("version",
-                versionStringCodePointBuffer, BUFFER_SIZE);
-        mDictionaryHeader.readHeaderValueOrQuestionMark("date",
-                dateStringCodePointBuffer, BUFFER_SIZE);
-
-        char dictionaryIdCharBuffer[BUFFER_SIZE];
-        char versionStringCharBuffer[BUFFER_SIZE];
-        char dateStringCharBuffer[BUFFER_SIZE];
-        intArrayToCharArray(dictionaryIdCodePointBuffer, BUFFER_SIZE,
-                dictionaryIdCharBuffer, BUFFER_SIZE);
-        intArrayToCharArray(versionStringCodePointBuffer, BUFFER_SIZE,
-                versionStringCharBuffer, BUFFER_SIZE);
-        intArrayToCharArray(dateStringCodePointBuffer, BUFFER_SIZE,
-                dateStringCharBuffer, BUFFER_SIZE);
-
-        LogUtils::logToJava(env,
-                "Dictionary info: dictionary = %s ; version = %s ; date = %s ; filesize = %i",
-                dictionaryIdCharBuffer, versionStringCharBuffer, dateStringCharBuffer, mDictSize);
-    }
-};
-}
-#endif /* LATINIME_BINARY_DICTIONARY_INFO_H */
diff --git a/native/jni/src/suggest/core/dictionary/binary_dictionary_shortcut_iterator.h b/native/jni/src/suggest/core/dictionary/binary_dictionary_shortcut_iterator.h
new file mode 100644
index 0000000..558e0a5
--- /dev/null
+++ b/native/jni/src/suggest/core/dictionary/binary_dictionary_shortcut_iterator.h
@@ -0,0 +1,55 @@
+/*
+ * Copyright (C) 2012 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.
+ */
+
+#ifndef LATINIME_BINARY_DICTIONARY_SHORTCUT_ITERATOR_H
+#define LATINIME_BINARY_DICTIONARY_SHORTCUT_ITERATOR_H
+
+#include "defines.h"
+#include "suggest/core/policy/dictionary_shortcuts_structure_policy.h"
+
+namespace latinime {
+
+class BinaryDictionaryShortcutIterator {
+ public:
+    BinaryDictionaryShortcutIterator(
+            const DictionaryShortcutsStructurePolicy *const shortcutStructurePolicy,
+            const int shortcutPos)
+            : mShortcutStructurePolicy(shortcutStructurePolicy),
+              mPos(shortcutStructurePolicy->getStartPos(shortcutPos)),
+              mHasNextShortcutTarget(shortcutPos != NOT_A_DICT_POS) {}
+
+    AK_FORCE_INLINE bool hasNextShortcutTarget() const {
+        return mHasNextShortcutTarget;
+    }
+
+    // Gets the shortcut target itself as an int string and put it to outTarget, put its length
+    // to outTargetLength, put whether it is whitelist to outIsWhitelist.
+    AK_FORCE_INLINE void nextShortcutTarget(
+            const int maxDepth, int *const outTarget, int *const outTargetLength,
+            bool *const outIsWhitelist) {
+        mShortcutStructurePolicy->getNextShortcut(maxDepth, outTarget, outTargetLength,
+                outIsWhitelist, &mHasNextShortcutTarget, &mPos);
+    }
+
+ private:
+    DISALLOW_IMPLICIT_CONSTRUCTORS(BinaryDictionaryShortcutIterator);
+
+    const DictionaryShortcutsStructurePolicy *const mShortcutStructurePolicy;
+    int mPos;
+    bool mHasNextShortcutTarget;
+};
+} // namespace latinime
+#endif // LATINIME_BINARY_DICTIONARY_SHORTCUT_ITERATOR_H
diff --git a/native/jni/src/suggest/core/dictionary/binary_dictionary_terminal_attributes_reading_utils.cpp b/native/jni/src/suggest/core/dictionary/binary_dictionary_terminal_attributes_reading_utils.cpp
deleted file mode 100644
index 20b77b3..0000000
--- a/native/jni/src/suggest/core/dictionary/binary_dictionary_terminal_attributes_reading_utils.cpp
+++ /dev/null
@@ -1,66 +0,0 @@
-/*
- * Copyright (C) 2013 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.
- */
-
-#include "suggest/core/dictionary/binary_dictionary_terminal_attributes_reading_utils.h"
-
-#include "suggest/core/dictionary/binary_dictionary_info.h"
-#include "suggest/core/dictionary/byte_array_utils.h"
-
-namespace latinime {
-
-typedef BinaryDictionaryTerminalAttributesReadingUtils TaUtils;
-
-const TaUtils::TerminalAttributeFlags TaUtils::MASK_ATTRIBUTE_ADDRESS_TYPE = 0x30;
-const TaUtils::TerminalAttributeFlags TaUtils::FLAG_ATTRIBUTE_ADDRESS_TYPE_ONEBYTE = 0x10;
-const TaUtils::TerminalAttributeFlags TaUtils::FLAG_ATTRIBUTE_ADDRESS_TYPE_TWOBYTES = 0x20;
-const TaUtils::TerminalAttributeFlags TaUtils::FLAG_ATTRIBUTE_ADDRESS_TYPE_THREEBYTES = 0x30;
-const TaUtils::TerminalAttributeFlags TaUtils::FLAG_ATTRIBUTE_OFFSET_NEGATIVE = 0x40;
-// Flag for presence of more attributes
-const TaUtils::TerminalAttributeFlags TaUtils::FLAG_ATTRIBUTE_HAS_NEXT = 0x80;
-// Mask for attribute probability, stored on 4 bits inside the flags byte.
-const TaUtils::TerminalAttributeFlags TaUtils::MASK_ATTRIBUTE_PROBABILITY = 0x0F;
-const int TaUtils::ATTRIBUTE_ADDRESS_SHIFT = 4;
-const int TaUtils::SHORTCUT_LIST_SIZE_FIELD_SIZE = 2;
-// The numeric value of the shortcut probability that means 'whitelist'.
-const int TaUtils::WHITELIST_SHORTCUT_PROBABILITY = 15;
-
-/* static */ int TaUtils::getBigramAddressAndForwardPointer(
-        const BinaryDictionaryInfo *const binaryDictionaryInfo, const TerminalAttributeFlags flags,
-        int *const pos) {
-    int offset = 0;
-    const int origin = *pos;
-    switch (MASK_ATTRIBUTE_ADDRESS_TYPE & flags) {
-        case FLAG_ATTRIBUTE_ADDRESS_TYPE_ONEBYTE:
-            offset = ByteArrayUtils::readUint8AndAdvancePosition(
-                    binaryDictionaryInfo->getDictRoot(), pos);
-            break;
-        case FLAG_ATTRIBUTE_ADDRESS_TYPE_TWOBYTES:
-            offset = ByteArrayUtils::readUint16AndAdvancePosition(
-                    binaryDictionaryInfo->getDictRoot(), pos);
-            break;
-        case FLAG_ATTRIBUTE_ADDRESS_TYPE_THREEBYTES:
-            offset = ByteArrayUtils::readUint24AndAdvancePosition(
-                    binaryDictionaryInfo->getDictRoot(), pos);
-            break;
-    }
-    if (isOffsetNegative(flags)) {
-        return origin - offset;
-    } else {
-        return origin + offset;
-    }
-}
-
-} // namespace latinime
diff --git a/native/jni/src/suggest/core/dictionary/binary_dictionary_terminal_attributes_reading_utils.h b/native/jni/src/suggest/core/dictionary/binary_dictionary_terminal_attributes_reading_utils.h
deleted file mode 100644
index 375fc7d..0000000
--- a/native/jni/src/suggest/core/dictionary/binary_dictionary_terminal_attributes_reading_utils.h
+++ /dev/null
@@ -1,123 +0,0 @@
-/*
- * Copyright (C) 2013 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.
- */
-
-#ifndef LATINIME_BINARY_DICTIONARY_TERMINAL_ATTRIBUTES_READING_UTILS_H
-#define LATINIME_BINARY_DICTIONARY_TERMINAL_ATTRIBUTES_READING_UTILS_H
-
-#include <stdint.h>
-
-#include "defines.h"
-#include "suggest/core/dictionary/binary_dictionary_info.h"
-#include "suggest/core/dictionary/byte_array_utils.h"
-
-namespace latinime {
-
-class BinaryDictionaryTerminalAttributesReadingUtils {
- public:
-    typedef uint8_t TerminalAttributeFlags;
-    typedef TerminalAttributeFlags BigramFlags;
-    typedef TerminalAttributeFlags ShortcutFlags;
-
-    static AK_FORCE_INLINE TerminalAttributeFlags getFlagsAndForwardPointer(
-            const BinaryDictionaryInfo *const binaryDictionaryInfo, int *const pos) {
-        return ByteArrayUtils::readUint8AndAdvancePosition(
-                binaryDictionaryInfo->getDictRoot(), pos);
-    }
-
-    static AK_FORCE_INLINE int getProbabilityFromFlags(const TerminalAttributeFlags flags) {
-        return flags & MASK_ATTRIBUTE_PROBABILITY;
-    }
-
-    static AK_FORCE_INLINE bool hasNext(const TerminalAttributeFlags flags) {
-        return (flags & FLAG_ATTRIBUTE_HAS_NEXT) != 0;
-    }
-
-    // Bigrams reading methods
-    static AK_FORCE_INLINE void skipExistingBigrams(
-            const BinaryDictionaryInfo *const binaryDictionaryInfo, int *const pos) {
-        BigramFlags flags = getFlagsAndForwardPointer(binaryDictionaryInfo, pos);
-        while (hasNext(flags)) {
-            *pos += attributeAddressSize(flags);
-            flags = getFlagsAndForwardPointer(binaryDictionaryInfo, pos);
-        }
-        *pos += attributeAddressSize(flags);
-    }
-
-    static int getBigramAddressAndForwardPointer(
-            const BinaryDictionaryInfo *const binaryDictionaryInfo, const BigramFlags flags,
-                    int *const pos);
-
-    // Shortcuts reading methods
-    // This method returns the size of the shortcut list region excluding the shortcut list size
-    // field at the beginning.
-    static AK_FORCE_INLINE int getShortcutListSizeAndForwardPointer(
-            const BinaryDictionaryInfo *const binaryDictionaryInfo, int *const pos) {
-        // readUint16andAdvancePosition() returns an offset *including* the uint16 field itself.
-        return ByteArrayUtils::readUint16AndAdvancePosition(
-                binaryDictionaryInfo->getDictRoot(), pos) - SHORTCUT_LIST_SIZE_FIELD_SIZE;
-    }
-
-    static AK_FORCE_INLINE void skipShortcuts(
-            const BinaryDictionaryInfo *const binaryDictionaryInfo, int *const pos) {
-        const int shortcutListSize = getShortcutListSizeAndForwardPointer(
-                binaryDictionaryInfo, pos);
-        *pos += shortcutListSize;
-    }
-
-    static AK_FORCE_INLINE bool isWhitelist(const ShortcutFlags flags) {
-        return getProbabilityFromFlags(flags) == WHITELIST_SHORTCUT_PROBABILITY;
-    }
-
-    static AK_FORCE_INLINE int readShortcutTarget(
-            const BinaryDictionaryInfo *const binaryDictionaryInfo, const int maxLength,
-            int *const outWord, int *const pos) {
-        return ByteArrayUtils::readStringAndAdvancePosition(
-                binaryDictionaryInfo->getDictRoot(), maxLength, outWord, pos);
-    }
-
- private:
-    DISALLOW_IMPLICIT_CONSTRUCTORS(BinaryDictionaryTerminalAttributesReadingUtils);
-
-    static const TerminalAttributeFlags MASK_ATTRIBUTE_ADDRESS_TYPE;
-    static const TerminalAttributeFlags FLAG_ATTRIBUTE_ADDRESS_TYPE_ONEBYTE;
-    static const TerminalAttributeFlags FLAG_ATTRIBUTE_ADDRESS_TYPE_TWOBYTES;
-    static const TerminalAttributeFlags FLAG_ATTRIBUTE_ADDRESS_TYPE_THREEBYTES;
-    static const TerminalAttributeFlags FLAG_ATTRIBUTE_OFFSET_NEGATIVE;
-    static const TerminalAttributeFlags FLAG_ATTRIBUTE_HAS_NEXT;
-    static const TerminalAttributeFlags MASK_ATTRIBUTE_PROBABILITY;
-    static const int ATTRIBUTE_ADDRESS_SHIFT;
-    static const int SHORTCUT_LIST_SIZE_FIELD_SIZE;
-    static const int WHITELIST_SHORTCUT_PROBABILITY;
-
-    static AK_FORCE_INLINE bool isOffsetNegative(const TerminalAttributeFlags flags) {
-        return (flags & FLAG_ATTRIBUTE_OFFSET_NEGATIVE) != 0;
-    }
-
-    static AK_FORCE_INLINE int attributeAddressSize(const TerminalAttributeFlags flags) {
-        return (flags & MASK_ATTRIBUTE_ADDRESS_TYPE) >> ATTRIBUTE_ADDRESS_SHIFT;
-        /* Note: this is a value-dependant optimization of what may probably be
-           more readably written this way:
-           switch (flags * BinaryFormat::MASK_ATTRIBUTE_ADDRESS_TYPE) {
-           case FLAG_ATTRIBUTE_ADDRESS_TYPE_ONEBYTE: return 1;
-           case FLAG_ATTRIBUTE_ADDRESS_TYPE_TWOBYTES: return 2;
-           case FLAG_ATTRIBUTE_ADDRESS_TYPE_THREEBYTE: return 3;
-           default: return 0;
-           }
-        */
-    }
-};
-}
-#endif /* LATINIME_BINARY_DICTIONARY_TERMINAL_ATTRIBUTES_READING_UTILS_H */
diff --git a/native/jni/src/suggest/core/dictionary/byte_array_utils.h b/native/jni/src/suggest/core/dictionary/byte_array_utils.h
deleted file mode 100644
index 75ccfc7..0000000
--- a/native/jni/src/suggest/core/dictionary/byte_array_utils.h
+++ /dev/null
@@ -1,192 +0,0 @@
-/*
- * Copyright (C) 2013, 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.
- */
-
-#ifndef LATINIME_BYTE_ARRAY_UTILS_H
-#define LATINIME_BYTE_ARRAY_UTILS_H
-
-#include <stdint.h>
-
-#include "defines.h"
-
-namespace latinime {
-
-/**
- * Utility methods for reading byte arrays.
- */
-class ByteArrayUtils {
- public:
-    /**
-     * Integer
-     *
-     * Each method read a corresponding size integer in a big endian manner.
-     */
-    static AK_FORCE_INLINE uint32_t readUint32(const uint8_t *const buffer, const int pos) {
-        return (buffer[pos] << 24) ^ (buffer[pos + 1] << 16)
-                ^ (buffer[pos + 2] << 8) ^ buffer[pos + 3];
-    }
-
-    static AK_FORCE_INLINE uint32_t readUint24(const uint8_t *const buffer, const int pos) {
-        return (buffer[pos] << 16) ^ (buffer[pos + 1] << 8) ^ buffer[pos + 2];
-    }
-
-    static AK_FORCE_INLINE uint16_t readUint16(const uint8_t *const buffer, const int pos) {
-        return (buffer[pos] << 8) ^ buffer[pos + 1];
-    }
-
-    static AK_FORCE_INLINE uint8_t readUint8(const uint8_t *const buffer, const int pos) {
-        return buffer[pos];
-    }
-
-    static AK_FORCE_INLINE uint32_t readUint32AndAdvancePosition(
-            const uint8_t *const buffer, int *const pos) {
-        const uint32_t value = readUint32(buffer, *pos);
-        *pos += 4;
-        return value;
-    }
-
-    static AK_FORCE_INLINE int readSint24AndAdvancePosition(
-            const uint8_t *const buffer, int *const pos) {
-        const uint8_t value = readUint8(buffer, *pos);
-        if (value < 0x80) {
-            return readUint24AndAdvancePosition(buffer, pos);
-        } else {
-            (*pos)++;
-            return -(((value & 0x7F) << 16) ^ readUint16AndAdvancePosition(buffer, pos));
-        }
-    }
-
-    static AK_FORCE_INLINE uint32_t readUint24AndAdvancePosition(
-            const uint8_t *const buffer, int *const pos) {
-        const uint32_t value = readUint24(buffer, *pos);
-        *pos += 3;
-        return value;
-    }
-
-    static AK_FORCE_INLINE uint16_t readUint16AndAdvancePosition(
-            const uint8_t *const buffer, int *const pos) {
-        const uint16_t value = readUint16(buffer, *pos);
-        *pos += 2;
-        return value;
-    }
-
-    static AK_FORCE_INLINE uint8_t readUint8AndAdvancePosition(
-            const uint8_t *const buffer, int *const pos) {
-        return buffer[(*pos)++];
-    }
-
-    /**
-     * Code Point
-     *
-     * 1 byte = bbbbbbbb match
-     * case 000xxxxx: xxxxx << 16 + next byte << 8 + next byte
-     * else: if 00011111 (= 0x1F) : this is the terminator. This is a relevant choice because
-     *       unicode code points range from 0 to 0x10FFFF, so any 3-byte value starting with
-     *       00011111 would be outside unicode.
-     * else: iso-latin-1 code
-     * This allows for the whole unicode range to be encoded, including chars outside of
-     * the BMP. Also everything in the iso-latin-1 charset is only 1 byte, except control
-     * characters which should never happen anyway (and still work, but take 3 bytes).
-     */
-    static AK_FORCE_INLINE int readCodePoint(const uint8_t *const buffer, const int pos) {
-        int p = pos;
-        return readCodePointAndAdvancePosition(buffer, &p);
-    }
-
-    static AK_FORCE_INLINE int readCodePointAndAdvancePosition(
-            const uint8_t *const buffer, int *const pos) {
-        const uint8_t firstByte = readUint8(buffer, *pos);
-        if (firstByte < MINIMAL_ONE_BYTE_CHARACTER_VALUE) {
-            if (firstByte == CHARACTER_ARRAY_TERMINATOR) {
-                *pos += 1;
-                return NOT_A_CODE_POINT;
-            } else {
-                return readUint24AndAdvancePosition(buffer, pos);
-            }
-        } else {
-            *pos += 1;
-            return firstByte;
-        }
-    }
-
-    /**
-     * String (array of code points)
-     *
-     * Reads code points until the terminator is found.
-     */
-    // Returns the length of the string.
-    static int readStringAndAdvancePosition(const uint8_t *const buffer,
-            const int maxLength, int *const outBuffer, int *const pos) {
-        int length = 0;
-        int codePoint = readCodePointAndAdvancePosition(buffer, pos);
-        while (NOT_A_CODE_POINT != codePoint && length < maxLength) {
-            outBuffer[length++] = codePoint;
-            codePoint = readCodePointAndAdvancePosition(buffer, pos);
-        }
-        return length;
-    }
-
-    // Advances the position and returns the length of the string.
-    static int advancePositionToBehindString(
-            const uint8_t *const buffer, const int maxLength, int *const pos) {
-        int length = 0;
-        int codePoint = readCodePointAndAdvancePosition(buffer, pos);
-        while (NOT_A_CODE_POINT != codePoint && length < maxLength) {
-            codePoint = readCodePointAndAdvancePosition(buffer, pos);
-        }
-        return length;
-    }
-
-    // Returns an integer less than, equal to, or greater than zero when string starting from pos
-    // in buffer is less than, match, or is greater than charArray.
-    static AK_FORCE_INLINE int compareStringInBufferWithCharArray(const uint8_t *const buffer,
-            const char *const charArray, const int maxLength, int *const pos) {
-        int index = 0;
-        int codePoint = readCodePointAndAdvancePosition(buffer, pos);
-        const uint8_t *const uint8CharArrayForComparison =
-                reinterpret_cast<const uint8_t *>(charArray);
-        while (NOT_A_CODE_POINT != codePoint
-                && '\0' != uint8CharArrayForComparison[index] && index < maxLength) {
-            if (codePoint != uint8CharArrayForComparison[index]) {
-                // Different character is found.
-                // Skip the rest of the string in the buffer.
-                advancePositionToBehindString(buffer, maxLength - index, pos);
-                return codePoint - uint8CharArrayForComparison[index];
-            }
-            // Advance
-            codePoint = readCodePointAndAdvancePosition(buffer, pos);
-            ++index;
-        }
-        if (NOT_A_CODE_POINT != codePoint && index < maxLength) {
-            // Skip the rest of the string in the buffer.
-            advancePositionToBehindString(buffer, maxLength - index, pos);
-        }
-        if (NOT_A_CODE_POINT == codePoint && '\0' == uint8CharArrayForComparison[index]) {
-            // When both of the last characters are terminals, we consider the string in the buffer
-            // matches the given char array
-            return 0;
-        } else {
-            return codePoint - uint8CharArrayForComparison[index];
-        }
-    }
-
- private:
-    DISALLOW_IMPLICIT_CONSTRUCTORS(ByteArrayUtils);
-
-    static const uint8_t MINIMAL_ONE_BYTE_CHARACTER_VALUE;
-    static const uint8_t CHARACTER_ARRAY_TERMINATOR;
-};
-} // namespace latinime
-#endif /* LATINIME_BYTE_ARRAY_UTILS_H */
diff --git a/native/jni/src/suggest/core/dictionary/dictionary.cpp b/native/jni/src/suggest/core/dictionary/dictionary.cpp
index 4a9e38f..ec1b63a 100644
--- a/native/jni/src/suggest/core/dictionary/dictionary.cpp
+++ b/native/jni/src/suggest/core/dictionary/dictionary.cpp
@@ -18,33 +18,35 @@
 
 #include "suggest/core/dictionary/dictionary.h"
 
-#include <map> // TODO: remove
 #include <stdint.h>
 
 #include "defines.h"
-#include "jni.h"
 #include "suggest/core/dictionary/bigram_dictionary.h"
+#include "suggest/core/policy/dictionary_header_structure_policy.h"
+#include "suggest/core/policy/dictionary_structure_with_buffer_policy.h"
 #include "suggest/core/session/dic_traverse_session.h"
 #include "suggest/core/suggest.h"
 #include "suggest/core/suggest_options.h"
 #include "suggest/policyimpl/gesture/gesture_suggest_policy_factory.h"
 #include "suggest/policyimpl/typing/typing_suggest_policy_factory.h"
+#include "utils/log_utils.h"
 
 namespace latinime {
 
-Dictionary::Dictionary(JNIEnv *env, void *dict, int dictSize, int mmapFd,
-        int dictBufOffset, bool isUpdatable)
-        : mBinaryDictionaryInfo(env, static_cast<const uint8_t *>(dict), dictSize, mmapFd,
-                dictBufOffset, isUpdatable),
-          mBigramDictionary(new BigramDictionary(&mBinaryDictionaryInfo)),
+Dictionary::Dictionary(JNIEnv *env,
+        DictionaryStructureWithBufferPolicy *const dictionaryStructureWithBufferPolicy)
+        : mDictionaryStructureWithBufferPolicy(dictionaryStructureWithBufferPolicy),
+          mBigramDictionary(new BigramDictionary(mDictionaryStructureWithBufferPolicy)),
           mGestureSuggest(new Suggest(GestureSuggestPolicyFactory::getGestureSuggestPolicy())),
           mTypingSuggest(new Suggest(TypingSuggestPolicyFactory::getTypingSuggestPolicy())) {
+    logDictionaryInfo(env);
 }
 
 Dictionary::~Dictionary() {
     delete mBigramDictionary;
     delete mGestureSuggest;
     delete mTypingSuggest;
+    delete mDictionaryStructureWithBufferPolicy;
 }
 
 int Dictionary::getSuggestions(ProximityInfo *proximityInfo, DicTraverseSession *traverseSession,
@@ -83,47 +85,72 @@
 }
 
 int Dictionary::getProbability(const int *word, int length) const {
-    const DictionaryStructurePolicy *const structurePolicy =
-            mBinaryDictionaryInfo.getStructurePolicy();
-    int pos = structurePolicy->getTerminalNodePositionOfWord(&mBinaryDictionaryInfo, word, length,
+    int pos = getDictionaryStructurePolicy()->getTerminalNodePositionOfWord(word, length,
             false /* forceLowerCaseSearch */);
-    if (NOT_A_VALID_WORD_POS == pos) {
+    if (NOT_A_DICT_POS == pos) {
         return NOT_A_PROBABILITY;
     }
-    return structurePolicy->getUnigramProbability(&mBinaryDictionaryInfo, pos);
+    return getDictionaryStructurePolicy()->getUnigramProbabilityOfPtNode(pos);
 }
 
-bool Dictionary::isValidBigram(const int *word0, int length0, const int *word1, int length1) const {
-    return mBigramDictionary->isValidBigram(word0, length0, word1, length1);
+int Dictionary::getBigramProbability(const int *word0, int length0, const int *word1,
+        int length1) const {
+    return mBigramDictionary->getBigramProbability(word0, length0, word1, length1);
 }
 
 void Dictionary::addUnigramWord(const int *const word, const int length, const int probability) {
-    if (!mBinaryDictionaryInfo.isDynamicallyUpdatable()) {
-        // This method should not be called for non-updatable dictionary.
-        AKLOGI("Warning: Dictionary::addUnigramWord() is called for non-updatable dictionary.");
-        return;
-    }
-    // TODO: Support dynamic update
+    mDictionaryStructureWithBufferPolicy->addUnigramWord(word, length, probability);
 }
 
 void Dictionary::addBigramWords(const int *const word0, const int length0, const int *const word1,
         const int length1, const int probability) {
-    if (!mBinaryDictionaryInfo.isDynamicallyUpdatable()) {
-        // This method should not be called for non-updatable dictionary.
-        AKLOGI("Warning: Dictionary::addBigramWords() is called for non-updatable dictionary.");
-        return;
-    }
-    // TODO: Support dynamic update
+    mDictionaryStructureWithBufferPolicy->addBigramWords(word0, length0, word1, length1,
+            probability);
 }
 
 void Dictionary::removeBigramWords(const int *const word0, const int length0,
         const int *const word1, const int length1) {
-    if (!mBinaryDictionaryInfo.isDynamicallyUpdatable()) {
-        // This method should not be called for non-updatable dictionary.
-        AKLOGI("Warning: Dictionary::removeBigramWords() is called for non-updatable dictionary.");
-        return;
-    }
-    // TODO: Support dynamic update
+    mDictionaryStructureWithBufferPolicy->removeBigramWords(word0, length0, word1, length1);
+}
+
+void Dictionary::flush(const char *const filePath) {
+    mDictionaryStructureWithBufferPolicy->flush(filePath);
+}
+
+void Dictionary::flushWithGC(const char *const filePath) {
+    mDictionaryStructureWithBufferPolicy->flushWithGC(filePath);
+}
+
+bool Dictionary::needsToRunGC() {
+    return mDictionaryStructureWithBufferPolicy->needsToRunGC();
+}
+
+void Dictionary::logDictionaryInfo(JNIEnv *const env) const {
+    const int BUFFER_SIZE = 16;
+    int dictionaryIdCodePointBuffer[BUFFER_SIZE];
+    int versionStringCodePointBuffer[BUFFER_SIZE];
+    int dateStringCodePointBuffer[BUFFER_SIZE];
+    const DictionaryHeaderStructurePolicy *const headerPolicy =
+            getDictionaryStructurePolicy()->getHeaderStructurePolicy();
+    headerPolicy->readHeaderValueOrQuestionMark("dictionary", dictionaryIdCodePointBuffer,
+            BUFFER_SIZE);
+    headerPolicy->readHeaderValueOrQuestionMark("version", versionStringCodePointBuffer,
+            BUFFER_SIZE);
+    headerPolicy->readHeaderValueOrQuestionMark("date", dateStringCodePointBuffer, BUFFER_SIZE);
+
+    char dictionaryIdCharBuffer[BUFFER_SIZE];
+    char versionStringCharBuffer[BUFFER_SIZE];
+    char dateStringCharBuffer[BUFFER_SIZE];
+    intArrayToCharArray(dictionaryIdCodePointBuffer, BUFFER_SIZE,
+            dictionaryIdCharBuffer, BUFFER_SIZE);
+    intArrayToCharArray(versionStringCodePointBuffer, BUFFER_SIZE,
+            versionStringCharBuffer, BUFFER_SIZE);
+    intArrayToCharArray(dateStringCodePointBuffer, BUFFER_SIZE,
+            dateStringCharBuffer, BUFFER_SIZE);
+
+    LogUtils::logToJava(env,
+            "Dictionary info: dictionary = %s ; version = %s ; date = %s",
+            dictionaryIdCharBuffer, versionStringCharBuffer, dateStringCharBuffer);
 }
 
 } // namespace latinime
diff --git a/native/jni/src/suggest/core/dictionary/dictionary.h b/native/jni/src/suggest/core/dictionary/dictionary.h
index 9f1e072..9744474 100644
--- a/native/jni/src/suggest/core/dictionary/dictionary.h
+++ b/native/jni/src/suggest/core/dictionary/dictionary.h
@@ -21,11 +21,11 @@
 
 #include "defines.h"
 #include "jni.h"
-#include "suggest/core/dictionary/binary_dictionary_info.h"
 
 namespace latinime {
 
 class BigramDictionary;
+class DictionaryStructureWithBufferPolicy;
 class DicTraverseSession;
 class ProximityInfo;
 class SuggestInterface;
@@ -53,8 +53,8 @@
     static const int KIND_FLAG_POSSIBLY_OFFENSIVE = 0x80000000;
     static const int KIND_FLAG_EXACT_MATCH = 0x40000000;
 
-    Dictionary(JNIEnv *env, void *dict, int dictSize, int mmapFd, int dictBufOffset,
-            bool isUpdatable);
+    Dictionary(JNIEnv *env,
+            DictionaryStructureWithBufferPolicy *const dictionaryStructureWithBufferPoilcy);
 
     int getSuggestions(ProximityInfo *proximityInfo, DicTraverseSession *traverseSession,
             int *xcoordinates, int *ycoordinates, int *times, int *pointerIds, int *inputCodePoints,
@@ -67,7 +67,7 @@
 
     int getProbability(const int *word, int length) const;
 
-    bool isValidBigram(const int *word0, int length0, const int *word1, int length1) const;
+    int getBigramProbability(const int *word0, int length0, const int *word1, int length1) const;
 
     void addUnigramWord(const int *const word, const int length, const int probability);
 
@@ -77,8 +77,14 @@
     void removeBigramWords(const int *const word0, const int length0, const int *const word1,
             const int length1);
 
-    const BinaryDictionaryInfo *getBinaryDictionaryInfo() const {
-        return &mBinaryDictionaryInfo;
+    void flush(const char *const filePath);
+
+    void flushWithGC(const char *const filePath);
+
+    bool needsToRunGC();
+
+    const DictionaryStructureWithBufferPolicy *getDictionaryStructurePolicy() const {
+        return mDictionaryStructureWithBufferPolicy;
     }
 
     virtual ~Dictionary();
@@ -86,10 +92,12 @@
  private:
     DISALLOW_IMPLICIT_CONSTRUCTORS(Dictionary);
 
-    const BinaryDictionaryInfo mBinaryDictionaryInfo;
-    const BigramDictionary *mBigramDictionary;
-    SuggestInterface *mGestureSuggest;
-    SuggestInterface *mTypingSuggest;
+    DictionaryStructureWithBufferPolicy *const mDictionaryStructureWithBufferPolicy;
+    const BigramDictionary *const mBigramDictionary;
+    const SuggestInterface *const mGestureSuggest;
+    const SuggestInterface *const mTypingSuggest;
+
+    void logDictionaryInfo(JNIEnv *const env) const;
 };
 } // namespace latinime
 #endif // LATINIME_DICTIONARY_H
diff --git a/native/jni/src/suggest/core/dictionary/digraph_utils.cpp b/native/jni/src/suggest/core/dictionary/digraph_utils.cpp
index af378b1..3271c1b 100644
--- a/native/jni/src/suggest/core/dictionary/digraph_utils.cpp
+++ b/native/jni/src/suggest/core/dictionary/digraph_utils.cpp
@@ -19,7 +19,7 @@
 #include <cstdlib>
 
 #include "defines.h"
-#include "suggest/core/dictionary/binary_dictionary_header.h"
+#include "suggest/core/policy/dictionary_header_structure_policy.h"
 #include "utils/char_utils.h"
 
 namespace latinime {
@@ -35,8 +35,9 @@
         { DIGRAPH_TYPE_GERMAN_UMLAUT, DIGRAPH_TYPE_FRENCH_LIGATURES };
 
 /* static */ bool DigraphUtils::hasDigraphForCodePoint(
-        const BinaryDictionaryHeader *const header, const int compositeGlyphCodePoint) {
-    const DigraphUtils::DigraphType digraphType = getDigraphTypeForDictionary(header);
+        const DictionaryHeaderStructurePolicy *const headerPolicy,
+        const int compositeGlyphCodePoint) {
+    const DigraphUtils::DigraphType digraphType = getDigraphTypeForDictionary(headerPolicy);
     if (DigraphUtils::getDigraphForDigraphTypeAndCodePoint(digraphType, compositeGlyphCodePoint)) {
         return true;
     }
@@ -45,11 +46,11 @@
 
 // Returns the digraph type associated with the given dictionary.
 /* static */ DigraphUtils::DigraphType DigraphUtils::getDigraphTypeForDictionary(
-        const BinaryDictionaryHeader *const header) {
-    if (header->requiresGermanUmlautProcessing()) {
+        const DictionaryHeaderStructurePolicy *const headerPolicy) {
+    if (headerPolicy->requiresGermanUmlautProcessing()) {
         return DIGRAPH_TYPE_GERMAN_UMLAUT;
     }
-    if (header->requiresFrenchLigatureProcessing()) {
+    if (headerPolicy->requiresFrenchLigatureProcessing()) {
         return DIGRAPH_TYPE_FRENCH_LIGATURES;
     }
     return DIGRAPH_TYPE_NONE;
diff --git a/native/jni/src/suggest/core/dictionary/digraph_utils.h b/native/jni/src/suggest/core/dictionary/digraph_utils.h
index 9d74fe3..6ae16e3 100644
--- a/native/jni/src/suggest/core/dictionary/digraph_utils.h
+++ b/native/jni/src/suggest/core/dictionary/digraph_utils.h
@@ -21,7 +21,7 @@
 
 namespace latinime {
 
-class BinaryDictionaryHeader;
+class DictionaryHeaderStructurePolicy;
 
 class DigraphUtils {
  public:
@@ -39,14 +39,15 @@
 
     typedef struct { int first; int second; int compositeGlyph; } digraph_t;
 
-    static bool hasDigraphForCodePoint(
-            const BinaryDictionaryHeader *const header, const int compositeGlyphCodePoint);
+    static bool hasDigraphForCodePoint(const DictionaryHeaderStructurePolicy *const headerPolicy,
+            const int compositeGlyphCodePoint);
     static int getDigraphCodePointForIndex(const int compositeGlyphCodePoint,
             const DigraphCodePointIndex digraphCodePointIndex);
 
  private:
     DISALLOW_IMPLICIT_CONSTRUCTORS(DigraphUtils);
-    static DigraphType getDigraphTypeForDictionary(const BinaryDictionaryHeader *const header);
+    static DigraphType getDigraphTypeForDictionary(
+            const DictionaryHeaderStructurePolicy *const headerPolicy);
     static int getAllDigraphsForDigraphTypeAndReturnSize(
             const DigraphType digraphType, const digraph_t **const digraphs);
     static const digraph_t *getDigraphForCodePoint(const int compositeGlyphCodePoint);
diff --git a/native/jni/src/suggest/core/dictionary/multi_bigram_map.h b/native/jni/src/suggest/core/dictionary/multi_bigram_map.h
index d5eafe1..4633c07 100644
--- a/native/jni/src/suggest/core/dictionary/multi_bigram_map.h
+++ b/native/jni/src/suggest/core/dictionary/multi_bigram_map.h
@@ -21,9 +21,8 @@
 
 #include "defines.h"
 #include "suggest/core/dictionary/binary_dictionary_bigrams_iterator.h"
-#include "suggest/core/dictionary/binary_dictionary_info.h"
 #include "suggest/core/dictionary/bloom_filter.h"
-#include "suggest/core/dictionary/probability_utils.h"
+#include "suggest/core/policy/dictionary_structure_with_buffer_policy.h"
 #include "utils/hash_map_compat.h"
 
 namespace latinime {
@@ -38,20 +37,21 @@
 
     // Look up the bigram probability for the given word pair from the cached bigram maps.
     // Also caches the bigrams if there is space remaining and they have not been cached already.
-    int getBigramProbability(const BinaryDictionaryInfo *const binaryDictionaryInfo,
+    int getBigramProbability(const DictionaryStructureWithBufferPolicy *const structurePolicy,
             const int wordPosition, const int nextWordPosition, const int unigramProbability) {
         hash_map_compat<int, BigramMap>::const_iterator mapPosition =
                 mBigramMaps.find(wordPosition);
         if (mapPosition != mBigramMaps.end()) {
-            return mapPosition->second.getBigramProbability(nextWordPosition, unigramProbability);
+            return mapPosition->second.getBigramProbability(structurePolicy, nextWordPosition,
+                    unigramProbability);
         }
         if (mBigramMaps.size() < MAX_CACHED_PREV_WORDS_IN_BIGRAM_MAP) {
-            addBigramsForWordPosition(binaryDictionaryInfo, wordPosition);
-            return mBigramMaps[wordPosition].getBigramProbability(
+            addBigramsForWordPosition(structurePolicy, wordPosition);
+            return mBigramMaps[wordPosition].getBigramProbability(structurePolicy,
                     nextWordPosition, unigramProbability);
         }
-        return readBigramProbabilityFromBinaryDictionary(binaryDictionaryInfo,
-                wordPosition, nextWordPosition, unigramProbability);
+        return readBigramProbabilityFromBinaryDictionary(structurePolicy, wordPosition,
+                nextWordPosition, unigramProbability);
     }
 
     void clear() {
@@ -66,29 +66,33 @@
         BigramMap() : mBigramMap(DEFAULT_HASH_MAP_SIZE_FOR_EACH_BIGRAM_MAP), mBloomFilter() {}
         ~BigramMap() {}
 
-        void init(const BinaryDictionaryInfo *const binaryDictionaryInfo, const int nodePos) {
-            const int bigramsListPos = binaryDictionaryInfo->getStructurePolicy()->
-                    getBigramsPositionOfNode(binaryDictionaryInfo, nodePos);
-            BinaryDictionaryBigramsIterator bigramsIt(binaryDictionaryInfo, bigramsListPos);
+        void init(const DictionaryStructureWithBufferPolicy *const structurePolicy,
+                const int nodePos) {
+            const int bigramsListPos = structurePolicy->getBigramsPositionOfPtNode(nodePos);
+            BinaryDictionaryBigramsIterator bigramsIt(structurePolicy->getBigramsStructurePolicy(),
+                    bigramsListPos);
             while (bigramsIt.hasNext()) {
                 bigramsIt.next();
+                if (bigramsIt.getBigramPos() == NOT_A_DICT_POS) {
+                    continue;
+                }
                 mBigramMap[bigramsIt.getBigramPos()] = bigramsIt.getProbability();
                 mBloomFilter.setInFilter(bigramsIt.getBigramPos());
             }
         }
 
         AK_FORCE_INLINE int getBigramProbability(
+                const DictionaryStructureWithBufferPolicy *const structurePolicy,
                 const int nextWordPosition, const int unigramProbability) const {
+            int bigramProbability = NOT_A_PROBABILITY;
             if (mBloomFilter.isInFilter(nextWordPosition)) {
                 const hash_map_compat<int, int>::const_iterator bigramProbabilityIt =
                         mBigramMap.find(nextWordPosition);
                 if (bigramProbabilityIt != mBigramMap.end()) {
-                    const int bigramProbability = bigramProbabilityIt->second;
-                    return ProbabilityUtils::computeProbabilityForBigram(
-                            unigramProbability, bigramProbability);
+                    bigramProbability = bigramProbabilityIt->second;
                 }
             }
-            return ProbabilityUtils::backoff(unigramProbability);
+            return structurePolicy->getProbability(unigramProbability, bigramProbability);
         }
 
      private:
@@ -100,24 +104,25 @@
     };
 
     AK_FORCE_INLINE void addBigramsForWordPosition(
-            const BinaryDictionaryInfo *const binaryDictionaryInfo, const int position) {
-        mBigramMaps[position].init(binaryDictionaryInfo, position);
+            const DictionaryStructureWithBufferPolicy *const structurePolicy, const int position) {
+        mBigramMaps[position].init(structurePolicy, position);
     }
 
     AK_FORCE_INLINE int readBigramProbabilityFromBinaryDictionary(
-            const BinaryDictionaryInfo *const binaryDictionaryInfo, const int nodePos,
+            const DictionaryStructureWithBufferPolicy *const structurePolicy, const int nodePos,
             const int nextWordPosition, const int unigramProbability) {
-        const int bigramsListPos = binaryDictionaryInfo->getStructurePolicy()->
-                getBigramsPositionOfNode(binaryDictionaryInfo, nodePos);
-        BinaryDictionaryBigramsIterator bigramsIt(binaryDictionaryInfo, bigramsListPos);
+        int bigramProbability = NOT_A_PROBABILITY;
+        const int bigramsListPos = structurePolicy->getBigramsPositionOfPtNode(nodePos);
+        BinaryDictionaryBigramsIterator bigramsIt(structurePolicy->getBigramsStructurePolicy(),
+                bigramsListPos);
         while (bigramsIt.hasNext()) {
             bigramsIt.next();
             if (bigramsIt.getBigramPos() == nextWordPosition) {
-                return ProbabilityUtils::computeProbabilityForBigram(
-                        unigramProbability, bigramsIt.getProbability());
+                bigramProbability = bigramsIt.getProbability();
+                break;
             }
         }
-        return ProbabilityUtils::backoff(unigramProbability);
+        return structurePolicy->getProbability(unigramProbability, bigramProbability);
     }
 
     static const size_t MAX_CACHED_PREV_WORDS_IN_BIGRAM_MAP;
diff --git a/native/jni/src/suggest/core/dictionary/shortcut_utils.h b/native/jni/src/suggest/core/dictionary/shortcut_utils.h
index 3c21809..461d7b4 100644
--- a/native/jni/src/suggest/core/dictionary/shortcut_utils.h
+++ b/native/jni/src/suggest/core/dictionary/shortcut_utils.h
@@ -19,21 +19,20 @@
 
 #include "defines.h"
 #include "suggest/core/dicnode/dic_node_utils.h"
-#include "suggest/core/dictionary/terminal_attributes.h"
+#include "suggest/core/dictionary/binary_dictionary_shortcut_iterator.h"
 
 namespace latinime {
 
 class ShortcutUtils {
  public:
-    static int outputShortcuts(const TerminalAttributes *const terminalAttributes,
+    static int outputShortcuts(BinaryDictionaryShortcutIterator *const shortcutIt,
             int outputWordIndex, const int finalScore, int *const outputCodePoints,
             int *const frequencies, int *const outputTypes, const bool sameAsTyped) {
-        TerminalAttributes::ShortcutIterator iterator = terminalAttributes->getShortcutIterator();
         int shortcutTarget[MAX_WORD_LENGTH];
-        while (iterator.hasNextShortcutTarget() && outputWordIndex < MAX_RESULTS) {
+        while (shortcutIt->hasNextShortcutTarget() && outputWordIndex < MAX_RESULTS) {
             bool isWhilelist;
             int shortcutTargetStringLength;
-            iterator.nextShortcutTarget(MAX_WORD_LENGTH, shortcutTarget,
+            shortcutIt->nextShortcutTarget(MAX_WORD_LENGTH, shortcutTarget,
                     &shortcutTargetStringLength, &isWhilelist);
             int shortcutScore;
             int kind;
diff --git a/native/jni/src/suggest/core/dictionary/terminal_attributes.h b/native/jni/src/suggest/core/dictionary/terminal_attributes.h
deleted file mode 100644
index 0da6504..0000000
--- a/native/jni/src/suggest/core/dictionary/terminal_attributes.h
+++ /dev/null
@@ -1,93 +0,0 @@
-/*
- * Copyright (C) 2012 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.
- */
-
-#ifndef LATINIME_TERMINAL_ATTRIBUTES_H
-#define LATINIME_TERMINAL_ATTRIBUTES_H
-
-#include <stdint.h>
-
-#include "suggest/core/dictionary/binary_dictionary_info.h"
-#include "suggest/core/dictionary/binary_dictionary_terminal_attributes_reading_utils.h"
-
-namespace latinime {
-
-/**
- * This class encapsulates information about a terminal that allows to
- * retrieve local node attributes like the list of shortcuts without
- * exposing the format structure to the client.
- */
-class TerminalAttributes {
- public:
-    class ShortcutIterator {
-     public:
-        ShortcutIterator(const BinaryDictionaryInfo *const binaryDictionaryInfo,
-                const int shortcutPos, const bool hasShortcutList)
-                : mBinaryDictionaryInfo(binaryDictionaryInfo), mPos(shortcutPos),
-                  mHasNextShortcutTarget(hasShortcutList) {}
-
-        inline bool hasNextShortcutTarget() const {
-            return mHasNextShortcutTarget;
-        }
-
-        // Gets the shortcut target itself as an int string and put it to outTarget, put its length
-        // to outTargetLength, put whether it is whitelist to outIsWhitelist.
-        AK_FORCE_INLINE void nextShortcutTarget(
-                const int maxDepth, int *const outTarget, int *const outTargetLength,
-                bool *const outIsWhitelist) {
-            const BinaryDictionaryTerminalAttributesReadingUtils::ShortcutFlags flags =
-                    BinaryDictionaryTerminalAttributesReadingUtils::getFlagsAndForwardPointer(
-                            mBinaryDictionaryInfo, &mPos);
-            mHasNextShortcutTarget =
-                    BinaryDictionaryTerminalAttributesReadingUtils::hasNext(flags);
-            if (outIsWhitelist) {
-                *outIsWhitelist =
-                        BinaryDictionaryTerminalAttributesReadingUtils::isWhitelist(flags);
-            }
-            if (outTargetLength) {
-                *outTargetLength =
-                        BinaryDictionaryTerminalAttributesReadingUtils::readShortcutTarget(
-                                mBinaryDictionaryInfo, maxDepth, outTarget, &mPos);
-            }
-        }
-
-     private:
-        const BinaryDictionaryInfo *const mBinaryDictionaryInfo;
-        int mPos;
-        bool mHasNextShortcutTarget;
-    };
-
-    TerminalAttributes(const BinaryDictionaryInfo *const binaryDictionaryInfo,
-            const int shortcutPos)
-            : mBinaryDictionaryInfo(binaryDictionaryInfo), mShortcutListSizePos(shortcutPos) {}
-
-    inline ShortcutIterator getShortcutIterator() const {
-        int shortcutPos = mShortcutListSizePos;
-        const bool hasShortcutList = shortcutPos != NOT_A_DICT_POS;
-        if (hasShortcutList) {
-            BinaryDictionaryTerminalAttributesReadingUtils::getShortcutListSizeAndForwardPointer(
-                    mBinaryDictionaryInfo, &shortcutPos);
-        }
-        // shortcutPos is never used if hasShortcutList is false.
-        return ShortcutIterator(mBinaryDictionaryInfo, shortcutPos, hasShortcutList);
-    }
-
- private:
-    DISALLOW_IMPLICIT_CONSTRUCTORS(TerminalAttributes);
-    const BinaryDictionaryInfo *const mBinaryDictionaryInfo;
-    const int mShortcutListSizePos;
-};
-} // namespace latinime
-#endif // LATINIME_TERMINAL_ATTRIBUTES_H
diff --git a/native/jni/src/suggest/core/layout/proximity_info_state.cpp b/native/jni/src/suggest/core/layout/proximity_info_state.cpp
index 7780efd..fbabd92 100644
--- a/native/jni/src/suggest/core/layout/proximity_info_state.cpp
+++ b/native/jni/src/suggest/core/layout/proximity_info_state.cpp
@@ -36,8 +36,8 @@
         const int *const xCoordinates, const int *const yCoordinates, const int *const times,
         const int *const pointerIds, const bool isGeometric) {
     ASSERT(isGeometric || (inputSize < MAX_WORD_LENGTH));
-    mIsContinuousSuggestionPossible =
-            ProximityInfoStateUtils::checkAndReturnIsContinuousSuggestionPossible(
+    mIsContinuousSuggestionPossible = (mHasBeenUpdatedByGeometricInput != isGeometric) ?
+            false : ProximityInfoStateUtils::checkAndReturnIsContinuousSuggestionPossible(
                     inputSize, xCoordinates, yCoordinates, times, mSampledInputSize,
                     &mSampledInputXs, &mSampledInputYs, &mSampledTimes, &mSampledInputIndice);
     if (DEBUG_DICT) {
@@ -155,6 +155,7 @@
     if (DEBUG_GEO_FULL) {
         AKLOGI("ProximityState init finished: %d points out of %d", mSampledInputSize, inputSize);
     }
+    mHasBeenUpdatedByGeometricInput = isGeometric;
 }
 
 // This function basically converts from a length to an edit distance. Accordingly, it's obviously
diff --git a/native/jni/src/suggest/core/layout/proximity_info_state.h b/native/jni/src/suggest/core/layout/proximity_info_state.h
index dbcd544..c94060f 100644
--- a/native/jni/src/suggest/core/layout/proximity_info_state.h
+++ b/native/jni/src/suggest/core/layout/proximity_info_state.h
@@ -46,10 +46,11 @@
             : mProximityInfo(0), mMaxPointToKeyLength(0.0f), mAverageSpeed(0.0f),
               mHasTouchPositionCorrectionData(false), mMostCommonKeyWidthSquare(0),
               mKeyCount(0), mCellHeight(0), mCellWidth(0), mGridHeight(0), mGridWidth(0),
-              mIsContinuousSuggestionPossible(false), mSampledInputXs(), mSampledInputYs(),
-              mSampledTimes(), mSampledInputIndice(), mSampledLengthCache(),
-              mBeelineSpeedPercentiles(), mSampledNormalizedSquaredLengthCache(), mSpeedRates(),
-              mDirections(), mCharProbabilities(), mSampledNearKeySets(), mSampledSearchKeySets(),
+              mIsContinuousSuggestionPossible(false), mHasBeenUpdatedByGeometricInput(false),
+              mSampledInputXs(), mSampledInputYs(), mSampledTimes(), mSampledInputIndice(),
+              mSampledLengthCache(), mBeelineSpeedPercentiles(),
+              mSampledNormalizedSquaredLengthCache(), mSpeedRates(), mDirections(),
+              mCharProbabilities(), mSampledNearKeySets(), mSampledSearchKeySets(),
               mSampledSearchKeyVectors(), mTouchPositionCorrectionEnabled(false),
               mSampledInputSize(0), mMostProbableStringProbability(0.0f) {
         memset(mInputProximities, 0, sizeof(mInputProximities));
@@ -129,6 +130,10 @@
         return mSampledInputYs[index];
     }
 
+    int getInputIndexOfSampledPoint(const int sampledIndex) const {
+        return mSampledInputIndice[sampledIndex];
+    }
+
     bool hasSpaceProximity(const int index) const;
 
     int getLengthCache(const int index) const {
@@ -204,6 +209,7 @@
     int mGridHeight;
     int mGridWidth;
     bool mIsContinuousSuggestionPossible;
+    bool mHasBeenUpdatedByGeometricInput;
 
     std::vector<int> mSampledInputXs;
     std::vector<int> mSampledInputYs;
diff --git a/native/jni/src/suggest/core/policy/dictionary_bigrams_structure_policy.h b/native/jni/src/suggest/core/policy/dictionary_bigrams_structure_policy.h
new file mode 100644
index 0000000..661ef1b
--- /dev/null
+++ b/native/jni/src/suggest/core/policy/dictionary_bigrams_structure_policy.h
@@ -0,0 +1,42 @@
+/*
+ * Copyright (C) 2013, 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.
+ */
+
+#ifndef LATINIME_DICTIONARY_BIGRAMS_STRUCTURE_POLICY_H
+#define LATINIME_DICTIONARY_BIGRAMS_STRUCTURE_POLICY_H
+
+#include "defines.h"
+
+namespace latinime {
+
+/*
+ * This class abstracts structure of bigrams.
+ */
+class DictionaryBigramsStructurePolicy {
+ public:
+    virtual ~DictionaryBigramsStructurePolicy() {}
+
+    virtual void getNextBigram(int *const outBigramPos, int *const outProbability,
+            bool *const outHasNext, int *const pos) const = 0;
+    virtual void skipAllBigrams(int *const pos) const = 0;
+
+ protected:
+    DictionaryBigramsStructurePolicy() {}
+
+ private:
+    DISALLOW_COPY_AND_ASSIGN(DictionaryBigramsStructurePolicy);
+};
+} // namespace latinime
+#endif /* LATINIME_DICTIONARY_BIGRAMS_STRUCTURE_POLICY_H */
diff --git a/native/jni/src/suggest/core/policy/dictionary_header_structure_policy.h b/native/jni/src/suggest/core/policy/dictionary_header_structure_policy.h
new file mode 100644
index 0000000..a6829b4
--- /dev/null
+++ b/native/jni/src/suggest/core/policy/dictionary_header_structure_policy.h
@@ -0,0 +1,50 @@
+/*
+ * Copyright (C) 2013, 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.
+ */
+
+#ifndef LATINIME_DICTIONARY_HEADER_STRUCTURE_POLICY_H
+#define LATINIME_DICTIONARY_HEADER_STRUCTURE_POLICY_H
+
+#include "defines.h"
+
+namespace latinime {
+
+/*
+ * This class abstracts structure of dictionaries.
+ * Implement this policy to support additional dictionaries.
+ */
+class DictionaryHeaderStructurePolicy {
+ public:
+    virtual ~DictionaryHeaderStructurePolicy() {}
+
+    virtual bool supportsDynamicUpdate() const = 0;
+
+    virtual bool requiresGermanUmlautProcessing() const = 0;
+
+    virtual bool requiresFrenchLigatureProcessing() const = 0;
+
+    virtual float getMultiWordCostMultiplier() const = 0;
+
+    virtual void readHeaderValueOrQuestionMark(const char *const key, int *outValue,
+            int outValueSize) const = 0;
+
+ protected:
+    DictionaryHeaderStructurePolicy() {}
+
+ private:
+    DISALLOW_COPY_AND_ASSIGN(DictionaryHeaderStructurePolicy);
+};
+} // namespace latinime
+#endif /* LATINIME_DICTIONARY_HEADER_STRUCTURE_POLICY_H */
diff --git a/native/jni/src/suggest/core/policy/dictionary_shortcuts_structure_policy.h b/native/jni/src/suggest/core/policy/dictionary_shortcuts_structure_policy.h
new file mode 100644
index 0000000..40b6c2d
--- /dev/null
+++ b/native/jni/src/suggest/core/policy/dictionary_shortcuts_structure_policy.h
@@ -0,0 +1,46 @@
+/*
+ * Copyright (C) 2013, 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.
+ */
+
+#ifndef LATINIME_DICTIONARY_SHORTCUTS_STRUCTURE_POLICY_H
+#define LATINIME_DICTIONARY_SHORTCUTS_STRUCTURE_POLICY_H
+
+#include "defines.h"
+
+namespace latinime {
+
+/*
+ * This class abstracts structure of shortcuts.
+ */
+class DictionaryShortcutsStructurePolicy {
+ public:
+    virtual ~DictionaryShortcutsStructurePolicy() {}
+
+    virtual int getStartPos(const int pos) const = 0;
+
+    virtual void getNextShortcut(const int maxCodePointCount, int *const outCodePoint,
+            int *const outCodePointCount, bool *const outIsWhitelist, bool *const outHasNext,
+            int *const pos) const = 0;
+
+    virtual void skipAllShortcuts(int *const pos) const = 0;
+
+ protected:
+    DictionaryShortcutsStructurePolicy() {}
+
+ private:
+    DISALLOW_COPY_AND_ASSIGN(DictionaryShortcutsStructurePolicy);
+};
+} // namespace latinime
+#endif /* LATINIME_DICTIONARY_SHORTCUTS_STRUCTURE_POLICY_H */
diff --git a/native/jni/src/suggest/core/policy/dictionary_structure_policy.h b/native/jni/src/suggest/core/policy/dictionary_structure_policy.h
deleted file mode 100644
index cc14c98..0000000
--- a/native/jni/src/suggest/core/policy/dictionary_structure_policy.h
+++ /dev/null
@@ -1,79 +0,0 @@
-/*
- * Copyright (C) 2013, 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.
- */
-
-#ifndef LATINIME_DICTIONARY_STRUCTURE_POLICY_H
-#define LATINIME_DICTIONARY_STRUCTURE_POLICY_H
-
-#include "defines.h"
-
-namespace latinime {
-
-class BinaryDictionaryInfo;
-class DicNode;
-class DicNodeVector;
-
-/*
- * This class abstracts structure of dictionaries.
- * Implement this policy to support additional dictionaries.
- */
-class DictionaryStructurePolicy {
- public:
-    // This provides a filtering method for filtering new node.
-    class NodeFilter {
-     public:
-        virtual bool isFilteredOut(const int codePoint) const = 0;
-
-     protected:
-        NodeFilter() {}
-        virtual ~NodeFilter() {}
-
-     private:
-        DISALLOW_COPY_AND_ASSIGN(NodeFilter);
-    };
-
-    virtual int getRootPosition() const = 0;
-
-    virtual void createAndGetAllChildNodes(const DicNode *const dicNode,
-            const BinaryDictionaryInfo *const binaryDictionaryInfo,
-            const NodeFilter *const nodeFilter, DicNodeVector *const childDicNodes) const = 0;
-
-    virtual int getCodePointsAndProbabilityAndReturnCodePointCount(
-            const BinaryDictionaryInfo *const binaryDictionaryInfo,
-            const int nodePos, const int maxCodePointCount, int *const outCodePoints,
-            int *const outUnigramProbability) const = 0;
-
-    virtual int getTerminalNodePositionOfWord(
-            const BinaryDictionaryInfo *const binaryDictionaryInfo, const int *const inWord,
-            const int length, const bool forceLowerCaseSearch) const = 0;
-
-    virtual int getUnigramProbability(const BinaryDictionaryInfo *const binaryDictionaryInfo,
-            const int nodePos) const = 0;
-
-    virtual int getShortcutPositionOfNode(const BinaryDictionaryInfo *const binaryDictionaryInfo,
-            const int nodePos) const = 0;
-
-    virtual int getBigramsPositionOfNode(const BinaryDictionaryInfo *const binaryDictionaryInfo,
-            const int nodePos) const = 0;
-
- protected:
-    DictionaryStructurePolicy() {}
-    virtual ~DictionaryStructurePolicy() {}
-
- private:
-    DISALLOW_COPY_AND_ASSIGN(DictionaryStructurePolicy);
-};
-} // namespace latinime
-#endif /* LATINIME_DICTIONARY_STRUCTURE_POLICY_H */
diff --git a/native/jni/src/suggest/core/policy/dictionary_structure_with_buffer_policy.h b/native/jni/src/suggest/core/policy/dictionary_structure_with_buffer_policy.h
new file mode 100644
index 0000000..b95488e
--- /dev/null
+++ b/native/jni/src/suggest/core/policy/dictionary_structure_with_buffer_policy.h
@@ -0,0 +1,90 @@
+/*
+ * Copyright (C) 2013, 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.
+ */
+
+#ifndef LATINIME_DICTIONARY_STRUCTURE_POLICY_H
+#define LATINIME_DICTIONARY_STRUCTURE_POLICY_H
+
+#include "defines.h"
+
+namespace latinime {
+
+class DicNode;
+class DicNodeVector;
+class DictionaryBigramsStructurePolicy;
+class DictionaryHeaderStructurePolicy;
+class DictionaryShortcutsStructurePolicy;
+
+/*
+ * This class abstracts structure of dictionaries.
+ * Implement this policy to support additional dictionaries.
+ */
+class DictionaryStructureWithBufferPolicy {
+ public:
+    virtual ~DictionaryStructureWithBufferPolicy() {}
+
+    virtual int getRootPosition() const = 0;
+
+    virtual void createAndGetAllChildNodes(const DicNode *const dicNode,
+            DicNodeVector *const childDicNodes) const = 0;
+
+    virtual int getCodePointsAndProbabilityAndReturnCodePointCount(
+            const int nodePos, const int maxCodePointCount, int *const outCodePoints,
+            int *const outUnigramProbability) const = 0;
+
+    virtual int getTerminalNodePositionOfWord(const int *const inWord,
+            const int length, const bool forceLowerCaseSearch) const = 0;
+
+    virtual int getProbability(const int unigramProbability,
+            const int bigramProbability) const = 0;
+
+    virtual int getUnigramProbabilityOfPtNode(const int nodePos) const = 0;
+
+    virtual int getShortcutPositionOfPtNode(const int nodePos) const = 0;
+
+    virtual int getBigramsPositionOfPtNode(const int nodePos) const = 0;
+
+    virtual const DictionaryHeaderStructurePolicy *getHeaderStructurePolicy() const = 0;
+
+    virtual const DictionaryBigramsStructurePolicy *getBigramsStructurePolicy() const = 0;
+
+    virtual const DictionaryShortcutsStructurePolicy *getShortcutsStructurePolicy() const = 0;
+
+    // Returns whether the update was success or not.
+    virtual bool addUnigramWord(const int *const word, const int length,
+            const int probability) = 0;
+
+    // Returns whether the update was success or not.
+    virtual bool addBigramWords(const int *const word0, const int length0, const int *const word1,
+            const int length1, const int probability) = 0;
+
+    // Returns whether the update was success or not.
+    virtual bool removeBigramWords(const int *const word0, const int length0,
+            const int *const word1, const int length1) = 0;
+
+    virtual void flush(const char *const filePath) = 0;
+
+    virtual void flushWithGC(const char *const filePath) = 0;
+
+    virtual bool needsToRunGC() const = 0;
+
+ protected:
+    DictionaryStructureWithBufferPolicy() {}
+
+ private:
+    DISALLOW_COPY_AND_ASSIGN(DictionaryStructureWithBufferPolicy);
+};
+} // namespace latinime
+#endif /* LATINIME_DICTIONARY_STRUCTURE_POLICY_H */
diff --git a/native/jni/src/suggest/core/policy/weighting.cpp b/native/jni/src/suggest/core/policy/weighting.cpp
index 5872922..f9b777d 100644
--- a/native/jni/src/suggest/core/policy/weighting.cpp
+++ b/native/jni/src/suggest/core/policy/weighting.cpp
@@ -148,7 +148,7 @@
     case CT_TERMINAL: {
         const float languageImprobability =
                 DicNodeUtils::getBigramNodeImprobability(
-                        traverseSession->getBinaryDictionaryInfo(), dicNode, multiBigramMap);
+                        traverseSession->getDictionaryStructurePolicy(), dicNode, multiBigramMap);
         return weighting->getTerminalLanguageCost(traverseSession, dicNode, languageImprobability);
     }
     case CT_TERMINAL_INSERTION:
diff --git a/native/jni/src/suggest/core/session/dic_traverse_session.cpp b/native/jni/src/suggest/core/session/dic_traverse_session.cpp
index 7651b19..50f2bbd 100644
--- a/native/jni/src/suggest/core/session/dic_traverse_session.cpp
+++ b/native/jni/src/suggest/core/session/dic_traverse_session.cpp
@@ -17,32 +17,35 @@
 #include "suggest/core/session/dic_traverse_session.h"
 
 #include "defines.h"
-#include "jni.h"
-#include "suggest/core/dictionary/binary_dictionary_header.h"
-#include "suggest/core/dictionary/binary_dictionary_info.h"
 #include "suggest/core/dictionary/dictionary.h"
+#include "suggest/core/policy/dictionary_header_structure_policy.h"
+#include "suggest/core/policy/dictionary_structure_with_buffer_policy.h"
 
 namespace latinime {
 
+// 256K bytes threshold is heuristically used to distinguish dictionaries containing many unigrams
+// (e.g. main dictionary) from small dictionaries (e.g. contacts...)
+const int DicTraverseSession::DICTIONARY_SIZE_THRESHOLD_TO_USE_LARGE_CACHE_FOR_SUGGESTION =
+        256 * 1024;
+
 void DicTraverseSession::init(const Dictionary *const dictionary, const int *prevWord,
         int prevWordLength, const SuggestOptions *const suggestOptions) {
     mDictionary = dictionary;
-    const BinaryDictionaryInfo *const binaryDictionaryInfo =
-            mDictionary->getBinaryDictionaryInfo();
-    mMultiWordCostMultiplier = binaryDictionaryInfo->getHeader()->getMultiWordCostMultiplier();
+    mMultiWordCostMultiplier = getDictionaryStructurePolicy()->getHeaderStructurePolicy()
+            ->getMultiWordCostMultiplier();
     mSuggestOptions = suggestOptions;
     if (!prevWord) {
-        mPrevWordPos = NOT_A_VALID_WORD_POS;
+        mPrevWordPos = NOT_A_DICT_POS;
         return;
     }
     // TODO: merge following similar calls to getTerminalPosition into one case-insensitive call.
-    mPrevWordPos = binaryDictionaryInfo->getStructurePolicy()->getTerminalNodePositionOfWord(
-            binaryDictionaryInfo, prevWord, prevWordLength, false /* forceLowerCaseSearch */);
-    if (mPrevWordPos == NOT_A_VALID_WORD_POS) {
+    mPrevWordPos = getDictionaryStructurePolicy()->getTerminalNodePositionOfWord(
+            prevWord, prevWordLength, false /* forceLowerCaseSearch */);
+    if (mPrevWordPos == NOT_A_DICT_POS) {
         // Check bigrams for lower-cased previous word if original was not found. Useful for
         // auto-capitalized words like "The [current_word]".
-        mPrevWordPos = binaryDictionaryInfo->getStructurePolicy()->getTerminalNodePositionOfWord(
-                binaryDictionaryInfo, prevWord, prevWordLength, true /* forceLowerCaseSearch */);
+        mPrevWordPos = getDictionaryStructurePolicy()->getTerminalNodePositionOfWord(
+                prevWord, prevWordLength, true /* forceLowerCaseSearch */);
     }
 }
 
@@ -56,12 +59,14 @@
             maxSpatialDistance, maxPointerCount);
 }
 
-const BinaryDictionaryInfo *DicTraverseSession::getBinaryDictionaryInfo() const {
-    return mDictionary->getBinaryDictionaryInfo();
+const DictionaryStructureWithBufferPolicy *DicTraverseSession::getDictionaryStructurePolicy()
+        const {
+    return mDictionary->getDictionaryStructurePolicy();
 }
 
-void DicTraverseSession::resetCache(const int nextActiveCacheSize, const int maxWords) {
-    mDicNodesCache.reset(nextActiveCacheSize, maxWords);
+void DicTraverseSession::resetCache(const int thresholdForNextActiveDicNodes, const int maxWords) {
+    mDicNodesCache.reset(thresholdForNextActiveDicNodes /* nextActiveSize */,
+            maxWords /* terminalSize */);
     mMultiBigramMap.clear();
     mPartiallyCommited = false;
 }
diff --git a/native/jni/src/suggest/core/session/dic_traverse_session.h b/native/jni/src/suggest/core/session/dic_traverse_session.h
index de57e04..e0b1c67 100644
--- a/native/jni/src/suggest/core/session/dic_traverse_session.h
+++ b/native/jni/src/suggest/core/session/dic_traverse_session.h
@@ -28,8 +28,8 @@
 
 namespace latinime {
 
-class BinaryDictionaryInfo;
 class Dictionary;
+class DictionaryStructureWithBufferPolicy;
 class ProximityInfo;
 class SuggestOptions;
 
@@ -37,8 +37,12 @@
  public:
 
     // A factory method for DicTraverseSession
-    static AK_FORCE_INLINE void *getSessionInstance(JNIEnv *env, jstring localeStr) {
-        return new DicTraverseSession(env, localeStr);
+    static AK_FORCE_INLINE void *getSessionInstance(JNIEnv *env, jstring localeStr,
+            jlong dictSize) {
+        // To deal with the trade-off between accuracy and memory space, large cache is used for
+        // dictionaries larger that the threshold
+        return new DicTraverseSession(env, localeStr,
+                dictSize >= DICTIONARY_SIZE_THRESHOLD_TO_USE_LARGE_CACHE_FOR_SUGGESTION);
     }
 
     static AK_FORCE_INLINE void initSessionInstance(DicTraverseSession *traverseSession,
@@ -54,10 +58,10 @@
         delete traverseSession;
     }
 
-    AK_FORCE_INLINE DicTraverseSession(JNIEnv *env, jstring localeStr)
-            : mPrevWordPos(NOT_A_VALID_WORD_POS), mProximityInfo(0),
-              mDictionary(0), mSuggestOptions(0), mDicNodesCache(), mMultiBigramMap(),
-              mInputSize(0), mPartiallyCommited(false), mMaxPointerCount(1),
+    AK_FORCE_INLINE DicTraverseSession(JNIEnv *env, jstring localeStr, bool usesLargeCache)
+            : mPrevWordPos(NOT_A_DICT_POS), mProximityInfo(0),
+              mDictionary(0), mSuggestOptions(0), mDicNodesCache(usesLargeCache),
+              mMultiBigramMap(), mInputSize(0), mPartiallyCommited(false), mMaxPointerCount(1),
               mMultiWordCostMultiplier(1.0f) {
         // NOTE: mProximityInfoStates is an array of instances.
         // No need to initialize it explicitly here.
@@ -73,10 +77,9 @@
             const int inputSize, const int *const inputXs, const int *const inputYs,
             const int *const times, const int *const pointerIds, const float maxSpatialDistance,
             const int maxPointerCount);
-    void resetCache(const int nextActiveCacheSize, const int maxWords);
+    void resetCache(const int thresholdForNextActiveDicNodes, const int maxWords);
 
-    // TODO: Remove
-    const BinaryDictionaryInfo *getBinaryDictionaryInfo() const;
+    const DictionaryStructureWithBufferPolicy *getDictionaryStructurePolicy() const;
 
     //--------------------
     // getters and setters
@@ -110,7 +113,9 @@
         if (usedPointerCount != 1) {
             return false;
         }
-        *pointerId = usedPointerId;
+        if (pointerId) {
+            *pointerId = usedPointerId;
+        }
         return true;
     }
 
@@ -182,6 +187,7 @@
     DISALLOW_IMPLICIT_CONSTRUCTORS(DicTraverseSession);
     // threshold to start caching
     static const int CACHE_START_INPUT_LENGTH_THRESHOLD;
+    static const int DICTIONARY_SIZE_THRESHOLD_TO_USE_LARGE_CACHE_FOR_SUGGESTION;
     void initializeProximityInfoStates(const int *const inputCodePoints, const int *const inputXs,
             const int *const inputYs, const int *const times, const int *const pointerIds,
             const int inputSize, const float maxSpatialDistance, const int maxPointerCount);
diff --git a/native/jni/src/suggest/core/suggest.cpp b/native/jni/src/suggest/core/suggest.cpp
index 9376d7b..b1340e1 100644
--- a/native/jni/src/suggest/core/suggest.cpp
+++ b/native/jni/src/suggest/core/suggest.cpp
@@ -19,12 +19,12 @@
 #include "suggest/core/dicnode/dic_node.h"
 #include "suggest/core/dicnode/dic_node_priority_queue.h"
 #include "suggest/core/dicnode/dic_node_vector.h"
-#include "suggest/core/dictionary/binary_dictionary_info.h"
+#include "suggest/core/dictionary/binary_dictionary_shortcut_iterator.h"
 #include "suggest/core/dictionary/dictionary.h"
 #include "suggest/core/dictionary/digraph_utils.h"
 #include "suggest/core/dictionary/shortcut_utils.h"
-#include "suggest/core/dictionary/terminal_attributes.h"
 #include "suggest/core/layout/proximity_info.h"
+#include "suggest/core/policy/dictionary_structure_with_buffer_policy.h"
 #include "suggest/core/policy/scoring.h"
 #include "suggest/core/policy/traversal.h"
 #include "suggest/core/policy/weighting.h"
@@ -107,7 +107,7 @@
                 MAX_RESULTS);
         // Create a new dic node here
         DicNode rootNode;
-        DicNodeUtils::initAsRoot(traverseSession->getBinaryDictionaryInfo(),
+        DicNodeUtils::initAsRoot(traverseSession->getDictionaryStructurePolicy(),
                 traverseSession->getPrevWordPos(), &rootNode);
         traverseSession->getDicTraverseCache()->copyPushActive(&rootNode);
     }
@@ -117,7 +117,7 @@
  * Outputs the final list of suggestions (i.e., terminal nodes).
  */
 int Suggest::outputSuggestions(DicTraverseSession *traverseSession, int *frequencies,
-        int *outputCodePoints, int *spaceIndices, int *outputTypes) const {
+        int *outputCodePoints, int *outputIndicesToPartialCommit, int *outputTypes) const {
 #if DEBUG_EVALUATE_MOST_PROBABLE_STRING
     const int terminalSize = 0;
 #else
@@ -139,6 +139,7 @@
             SCORING->getMostProbableString(traverseSession, terminalSize, languageWeight,
                     &outputCodePoints[0], &outputTypes[0], &frequencies[0]);
     if (hasMostProbableString) {
+        outputIndicesToPartialCommit[outputWordIndex] = NOT_AN_INDEX;
         ++outputWordIndex;
     }
 
@@ -160,6 +161,9 @@
                             || (traverseSession->getInputSize()
                                     >= MIN_LEN_FOR_MULTI_WORD_AUTOCORRECT
                                             && terminals[0].hasMultipleWords())) : false;
+    // TODO: have partial commit work even with multiple pointers.
+    const bool outputSecondWordFirstLetterInputIndex =
+            traverseSession->isOnlyOnePointerUsed(0 /* pointerId */);
     // Output suggestion results here
     for (int terminalIndex = 0; terminalIndex < terminalSize && outputWordIndex < MAX_RESULTS;
             ++terminalIndex) {
@@ -171,7 +175,9 @@
                 terminalIndex, doubleLetterTerminalIndex, doubleLetterLevel);
         const float compoundDistance = terminalDicNode->getCompoundDistance(languageWeight)
                 + doubleLetterCost;
-        const bool isPossiblyOffensiveWord = terminalDicNode->getProbability() <= 0;
+        const bool isPossiblyOffensiveWord =
+                traverseSession->getDictionaryStructurePolicy()->getProbability(
+                        terminalDicNode->getProbability(), NOT_A_PROBABILITY) <= 0;
         const bool isExactMatch = terminalDicNode->isExactMatch();
         const bool isFirstCharUppercase = terminalDicNode->isFirstCharUppercase();
         // Heuristic: We exclude freq=0 first-char-uppercase words from exact match.
@@ -192,18 +198,21 @@
                 terminalDicNode->isExactMatch()
                         || (forceCommitMultiWords && terminalDicNode->hasMultipleWords())
                                 || (isValidWord && SCORING->doesAutoCorrectValidWord()));
-        maxScore = max(maxScore, finalScore);
-
-        // TODO: Implement a smarter auto-commit method for handling multi-word suggestions.
-        // Index for top typing suggestion should be 0.
-        if (isValidWord && outputWordIndex == 0) {
-            terminalDicNode->outputSpacePositionsResult(spaceIndices);
+        if (maxScore < finalScore && isValidWord) {
+            maxScore = finalScore;
         }
 
         // Don't output invalid words. However, we still need to submit their shortcuts if any.
         if (isValidWord) {
             outputTypes[outputWordIndex] = Dictionary::KIND_CORRECTION | outputTypeFlags;
             frequencies[outputWordIndex] = finalScore;
+            if (outputSecondWordFirstLetterInputIndex) {
+                outputIndicesToPartialCommit[outputWordIndex] =
+                        terminalDicNode->getSecondWordFirstInputIndex(
+                                traverseSession->getProximityInfoState(0));
+            } else {
+                outputIndicesToPartialCommit[outputWordIndex] = NOT_AN_INDEX;
+            }
             // Populate the outputChars array with the suggested word.
             const int startIndex = outputWordIndex * MAX_WORD_LENGTH;
             terminalDicNode->outputResult(&outputCodePoints[startIndex]);
@@ -211,16 +220,26 @@
         }
 
         if (!terminalDicNode->hasMultipleWords()) {
-            const BinaryDictionaryInfo *const binaryDictionaryInfo =
-                    traverseSession->getBinaryDictionaryInfo();
-            const TerminalAttributes terminalAttributes(traverseSession->getBinaryDictionaryInfo(),
-                    binaryDictionaryInfo->getStructurePolicy()->getShortcutPositionOfNode(
-                            binaryDictionaryInfo, terminalDicNode->getPos()));
+            BinaryDictionaryShortcutIterator shortcutIt(
+                    traverseSession->getDictionaryStructurePolicy()->getShortcutsStructurePolicy(),
+                    traverseSession->getDictionaryStructurePolicy()
+                            ->getShortcutPositionOfPtNode(terminalDicNode->getPos()));
             // Shortcut is not supported for multiple words suggestions.
             // TODO: Check shortcuts during traversal for multiple words suggestions.
             const bool sameAsTyped = TRAVERSAL->sameAsTyped(traverseSession, terminalDicNode);
-            outputWordIndex = ShortcutUtils::outputShortcuts(&terminalAttributes, outputWordIndex,
-                    finalScore, outputCodePoints, frequencies, outputTypes, sameAsTyped);
+            const int updatedOutputWordIndex = ShortcutUtils::outputShortcuts(&shortcutIt,
+                    outputWordIndex,  finalScore, outputCodePoints, frequencies, outputTypes,
+                    sameAsTyped);
+            const int secondWordFirstInputIndex = terminalDicNode->getSecondWordFirstInputIndex(
+                    traverseSession->getProximityInfoState(0));
+            for (int i = outputWordIndex; i < updatedOutputWordIndex; ++i) {
+                if (outputSecondWordFirstLetterInputIndex) {
+                    outputIndicesToPartialCommit[i] = secondWordFirstInputIndex;
+                } else {
+                    outputIndicesToPartialCommit[i] = NOT_AN_INDEX;
+                }
+            }
+            outputWordIndex = updatedOutputWordIndex;
         }
         DicNode::managedDelete(terminalDicNode);
     }
@@ -298,7 +317,7 @@
             }
 
             DicNodeUtils::getAllChildDicNodes(
-                    &dicNode, traverseSession->getBinaryDictionaryInfo(), &childDicNodes);
+                    &dicNode, traverseSession->getDictionaryStructurePolicy(), &childDicNodes);
 
             const int childDicNodesSize = childDicNodes.getSizeAndLock();
             for (int i = 0; i < childDicNodesSize; ++i) {
@@ -309,7 +328,8 @@
                     continue;
                 }
                 if (DigraphUtils::hasDigraphForCodePoint(
-                        traverseSession->getBinaryDictionaryInfo()->getHeader(),
+                        traverseSession->getDictionaryStructurePolicy()
+                                ->getHeaderStructurePolicy(),
                         childDicNode->getNodeCodePoint())) {
                     correctionDicNode.initByCopy(childDicNode);
                     correctionDicNode.advanceDigraphIndex();
@@ -447,7 +467,7 @@
         DicTraverseSession *traverseSession, DicNode *dicNode) const {
     DicNodeVector childDicNodes;
     DicNodeUtils::getAllChildDicNodes(
-            dicNode, traverseSession->getBinaryDictionaryInfo(), &childDicNodes);
+            dicNode, traverseSession->getDictionaryStructurePolicy(), &childDicNodes);
 
     const int size = childDicNodes.getSizeAndLock();
     for (int i = 0; i < size; i++) {
@@ -456,7 +476,6 @@
         Weighting::addCostAndForwardInputIndex(WEIGHTING, CT_OMISSION, traverseSession,
                 dicNode, childDicNode, 0 /* multiBigramMap */);
         weightChildNode(traverseSession, childDicNode);
-
         if (!TRAVERSAL->isPossibleOmissionChildNode(traverseSession, dicNode, childDicNode)) {
             continue;
         }
@@ -472,10 +491,14 @@
         DicNode *dicNode) const {
     const int16_t pointIndex = dicNode->getInputIndex(0);
     DicNodeVector childDicNodes;
-    DicNodeUtils::getProximityChildDicNodes(dicNode, traverseSession->getBinaryDictionaryInfo(),
-            traverseSession->getProximityInfoState(0), pointIndex + 1, true, &childDicNodes);
+    DicNodeUtils::getAllChildDicNodes(dicNode, traverseSession->getDictionaryStructurePolicy(),
+            &childDicNodes);
     const int size = childDicNodes.getSizeAndLock();
     for (int i = 0; i < size; i++) {
+        if (traverseSession->getProximityInfoState(0)->getPrimaryCodePointAt(pointIndex + 1)
+                != childDicNodes[i]->getNodeCodePoint()) {
+            continue;
+        }
         DicNode *const childDicNode = childDicNodes[i];
         Weighting::addCostAndForwardInputIndex(WEIGHTING, CT_INSERTION, traverseSession,
                 dicNode, childDicNode, 0 /* multiBigramMap */);
@@ -490,18 +513,29 @@
         DicNode *dicNode) const {
     const int16_t pointIndex = dicNode->getInputIndex(0);
     DicNodeVector childDicNodes1;
-    DicNodeUtils::getProximityChildDicNodes(dicNode, traverseSession->getBinaryDictionaryInfo(),
-            traverseSession->getProximityInfoState(0), pointIndex + 1, false, &childDicNodes1);
+    DicNodeUtils::getAllChildDicNodes(dicNode, traverseSession->getDictionaryStructurePolicy(),
+            &childDicNodes1);
     const int childSize1 = childDicNodes1.getSizeAndLock();
     for (int i = 0; i < childSize1; i++) {
+        const ProximityType matchedId1 = traverseSession->getProximityInfoState(0)
+                ->getProximityType(pointIndex + 1, childDicNodes1[i]->getNodeCodePoint(),
+                        true /* checkProximityChars */);
+        if (!ProximityInfoUtils::isMatchOrProximityChar(matchedId1)) {
+            continue;
+        }
         if (childDicNodes1[i]->hasChildren()) {
             DicNodeVector childDicNodes2;
-            DicNodeUtils::getProximityChildDicNodes(
-                    childDicNodes1[i], traverseSession->getBinaryDictionaryInfo(),
-                    traverseSession->getProximityInfoState(0), pointIndex, false, &childDicNodes2);
+            DicNodeUtils::getAllChildDicNodes(childDicNodes1[i],
+                    traverseSession->getDictionaryStructurePolicy(), &childDicNodes2);
             const int childSize2 = childDicNodes2.getSizeAndLock();
             for (int j = 0; j < childSize2; j++) {
                 DicNode *const childDicNode2 = childDicNodes2[j];
+                const ProximityType matchedId2 = traverseSession->getProximityInfoState(0)
+                        ->getProximityType(pointIndex, childDicNode2->getNodeCodePoint(),
+                                true /* checkProximityChars */);
+                if (!ProximityInfoUtils::isMatchOrProximityChar(matchedId2)) {
+                    continue;
+                }
                 Weighting::addCostAndForwardInputIndex(WEIGHTING, CT_TRANSPOSITION,
                         traverseSession, childDicNodes1[i], childDicNode2, 0 /* multiBigramMap */);
                 processExpandedDicNode(traverseSession, childDicNode2);
@@ -538,7 +572,7 @@
     // Create a non-cached node here.
     DicNode newDicNode;
     DicNodeUtils::initAsRootWithPreviousWord(
-            traverseSession->getBinaryDictionaryInfo(), dicNode, &newDicNode);
+            traverseSession->getDictionaryStructurePolicy(), dicNode, &newDicNode);
     const CorrectionType correctionType = spaceSubstitution ?
             CT_NEW_WORD_SPACE_SUBSTITUTION : CT_NEW_WORD_SPACE_OMITTION;
     Weighting::addCostAndForwardInputIndex(WEIGHTING, correctionType, traverseSession, dicNode,
diff --git a/native/jni/src/suggest/core/suggest.h b/native/jni/src/suggest/core/suggest.h
index 875cbe4..b240196 100644
--- a/native/jni/src/suggest/core/suggest.h
+++ b/native/jni/src/suggest/core/suggest.h
@@ -55,7 +55,7 @@
     void createNextWordDicNode(DicTraverseSession *traverseSession, DicNode *dicNode,
             const bool spaceSubstitution) const;
     int outputSuggestions(DicTraverseSession *traverseSession, int *frequencies,
-            int *outputCodePoints, int *outputIndices, int *outputTypes) const;
+            int *outputCodePoints, int *outputIndicesToPartialCommit, int *outputTypes) const;
     void initializeSearch(DicTraverseSession *traverseSession, int commitPoint) const;
     void expandCurrentDicNodes(DicTraverseSession *traverseSession) const;
     void processTerminalDicNode(DicTraverseSession *traverseSession, DicNode *dicNode) const;
diff --git a/native/jni/src/suggest/policyimpl/dictionary/bigram/bigram_list_policy.h b/native/jni/src/suggest/policyimpl/dictionary/bigram/bigram_list_policy.h
new file mode 100644
index 0000000..6ff95ca
--- /dev/null
+++ b/native/jni/src/suggest/policyimpl/dictionary/bigram/bigram_list_policy.h
@@ -0,0 +1,53 @@
+/*
+ * Copyright (C) 2013 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.
+ */
+
+#ifndef LATINIME_BIGRAM_LIST_POLICY_H
+#define LATINIME_BIGRAM_LIST_POLICY_H
+
+#include <stdint.h>
+
+#include "defines.h"
+#include "suggest/core/policy/dictionary_bigrams_structure_policy.h"
+#include "suggest/policyimpl/dictionary/bigram/bigram_list_read_write_utils.h"
+
+namespace latinime {
+
+class BigramListPolicy : public DictionaryBigramsStructurePolicy {
+ public:
+    explicit BigramListPolicy(const uint8_t *const bigramsBuf) : mBigramsBuf(bigramsBuf) {}
+
+    ~BigramListPolicy() {}
+
+    void getNextBigram(int *const outBigramPos, int *const outProbability, bool *const outHasNext,
+            int *const pos) const {
+        BigramListReadWriteUtils::BigramFlags flags;
+        BigramListReadWriteUtils::getBigramEntryPropertiesAndAdvancePosition(mBigramsBuf, &flags,
+                outBigramPos, pos);
+        *outProbability = BigramListReadWriteUtils::getProbabilityFromFlags(flags);
+        *outHasNext = BigramListReadWriteUtils::hasNext(flags);
+    }
+
+    void skipAllBigrams(int *const pos) const {
+        BigramListReadWriteUtils::skipExistingBigrams(mBigramsBuf, pos);
+    }
+
+ private:
+    DISALLOW_IMPLICIT_CONSTRUCTORS(BigramListPolicy);
+
+    const uint8_t *const mBigramsBuf;
+};
+} // namespace latinime
+#endif // LATINIME_BIGRAM_LIST_POLICY_H
diff --git a/native/jni/src/suggest/policyimpl/dictionary/bigram/bigram_list_read_write_utils.cpp b/native/jni/src/suggest/policyimpl/dictionary/bigram/bigram_list_read_write_utils.cpp
new file mode 100644
index 0000000..1926b98
--- /dev/null
+++ b/native/jni/src/suggest/policyimpl/dictionary/bigram/bigram_list_read_write_utils.cpp
@@ -0,0 +1,182 @@
+/*
+ * Copyright (C) 2013 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.
+ */
+
+#include "suggest/policyimpl/dictionary/bigram/bigram_list_read_write_utils.h"
+
+#include "suggest/policyimpl/dictionary/dynamic_patricia_trie_reading_utils.h"
+#include "suggest/policyimpl/dictionary/utils/byte_array_utils.h"
+#include "suggest/policyimpl/dictionary/utils/buffer_with_extendable_buffer.h"
+
+namespace latinime {
+
+const BigramListReadWriteUtils::BigramFlags BigramListReadWriteUtils::MASK_ATTRIBUTE_ADDRESS_TYPE =
+        0x30;
+const BigramListReadWriteUtils::BigramFlags
+        BigramListReadWriteUtils::FLAG_ATTRIBUTE_ADDRESS_TYPE_ONEBYTE = 0x10;
+const BigramListReadWriteUtils::BigramFlags
+        BigramListReadWriteUtils::FLAG_ATTRIBUTE_ADDRESS_TYPE_TWOBYTES = 0x20;
+const BigramListReadWriteUtils::BigramFlags
+        BigramListReadWriteUtils::FLAG_ATTRIBUTE_ADDRESS_TYPE_THREEBYTES = 0x30;
+const BigramListReadWriteUtils::BigramFlags
+        BigramListReadWriteUtils::FLAG_ATTRIBUTE_OFFSET_NEGATIVE = 0x40;
+// Flag for presence of more attributes
+const BigramListReadWriteUtils::BigramFlags BigramListReadWriteUtils::FLAG_ATTRIBUTE_HAS_NEXT =
+        0x80;
+// Mask for attribute probability, stored on 4 bits inside the flags byte.
+const BigramListReadWriteUtils::BigramFlags
+        BigramListReadWriteUtils::MASK_ATTRIBUTE_PROBABILITY = 0x0F;
+const int BigramListReadWriteUtils::ATTRIBUTE_ADDRESS_SHIFT = 4;
+
+/* static */ void BigramListReadWriteUtils::getBigramEntryPropertiesAndAdvancePosition(
+        const uint8_t *const bigramsBuf, BigramFlags *const outBigramFlags,
+        int *const outTargetPtNodePos, int *const bigramEntryPos) {
+    const BigramFlags bigramFlags = ByteArrayUtils::readUint8AndAdvancePosition(bigramsBuf,
+            bigramEntryPos);
+    if (outBigramFlags) {
+        *outBigramFlags = bigramFlags;
+    }
+    const int targetPos = getBigramAddressAndAdvancePosition(bigramsBuf, bigramFlags,
+            bigramEntryPos);
+    if (outTargetPtNodePos) {
+        *outTargetPtNodePos = targetPos;
+    }
+}
+
+/* static */ void BigramListReadWriteUtils::skipExistingBigrams(const uint8_t *const bigramsBuf,
+        int *const bigramListPos) {
+    BigramFlags flags;
+    do {
+        getBigramEntryPropertiesAndAdvancePosition(bigramsBuf, &flags, 0 /* outTargetPtNodePos */,
+                bigramListPos);
+    } while(hasNext(flags));
+}
+
+/* static */ int BigramListReadWriteUtils::getBigramAddressAndAdvancePosition(
+        const uint8_t *const bigramsBuf, const BigramFlags flags, int *const pos) {
+    int offset = 0;
+    const int origin = *pos;
+    switch (MASK_ATTRIBUTE_ADDRESS_TYPE & flags) {
+        case FLAG_ATTRIBUTE_ADDRESS_TYPE_ONEBYTE:
+            offset = ByteArrayUtils::readUint8AndAdvancePosition(bigramsBuf, pos);
+            break;
+        case FLAG_ATTRIBUTE_ADDRESS_TYPE_TWOBYTES:
+            offset = ByteArrayUtils::readUint16AndAdvancePosition(bigramsBuf, pos);
+            break;
+        case FLAG_ATTRIBUTE_ADDRESS_TYPE_THREEBYTES:
+            offset = ByteArrayUtils::readUint24AndAdvancePosition(bigramsBuf, pos);
+            break;
+    }
+    if (offset == DynamicPatriciaTrieReadingUtils::DICT_OFFSET_INVALID) {
+        return NOT_A_DICT_POS;
+    } else if (offset == DynamicPatriciaTrieReadingUtils::DICT_OFFSET_ZERO_OFFSET) {
+        return origin;
+    }
+    if (isOffsetNegative(flags)) {
+        return origin - offset;
+    } else {
+        return origin + offset;
+    }
+}
+
+/* static */ bool BigramListReadWriteUtils::setHasNextFlag(
+        BufferWithExtendableBuffer *const buffer, const bool hasNext, const int entryPos) {
+    const bool usesAdditionalBuffer = buffer->isInAdditionalBuffer(entryPos);
+    int readingPos = entryPos;
+    if (usesAdditionalBuffer) {
+        readingPos -= buffer->getOriginalBufferSize();
+    }
+    BigramFlags bigramFlags = ByteArrayUtils::readUint8AndAdvancePosition(
+            buffer->getBuffer(usesAdditionalBuffer), &readingPos);
+    if (hasNext) {
+        bigramFlags = bigramFlags | FLAG_ATTRIBUTE_HAS_NEXT;
+    } else {
+        bigramFlags = bigramFlags & (~FLAG_ATTRIBUTE_HAS_NEXT);
+    }
+    int writingPos = entryPos;
+    return buffer->writeUintAndAdvancePosition(bigramFlags, 1 /* size */, &writingPos);
+}
+
+/* static */ bool BigramListReadWriteUtils::createAndWriteBigramEntry(
+        BufferWithExtendableBuffer *const buffer, const int targetPos, const int probability,
+        const bool hasNext, int *const writingPos) {
+    BigramFlags flags;
+    if (!createAndGetBigramFlags(*writingPos, targetPos, probability, hasNext, &flags)) {
+        return false;
+    }
+    return writeBigramEntry(buffer, flags, targetPos, writingPos);
+}
+
+/* static */ bool BigramListReadWriteUtils::writeBigramEntry(
+        BufferWithExtendableBuffer *const bufferToWrite, const BigramFlags flags,
+        const int targetPtNodePos, int *const writingPos) {
+    const int offset = getBigramTargetOffset(targetPtNodePos, *writingPos);
+    const BigramFlags flagsToWrite = (offset < 0) ?
+            (flags | FLAG_ATTRIBUTE_OFFSET_NEGATIVE) : (flags & ~FLAG_ATTRIBUTE_OFFSET_NEGATIVE);
+    if (!bufferToWrite->writeUintAndAdvancePosition(flagsToWrite, 1 /* size */, writingPos)) {
+        return false;
+    }
+    const uint32_t absOffest = abs(offset);
+    const int bigramTargetFieldSize = attributeAddressSize(flags);
+    return bufferToWrite->writeUintAndAdvancePosition(absOffest, bigramTargetFieldSize,
+            writingPos);
+}
+
+// Returns true if the bigram entry is valid and put entry flags into out*.
+/* static */ bool BigramListReadWriteUtils::createAndGetBigramFlags(const int entryPos,
+        const int targetPtNodePos, const int probability, const bool hasNext,
+        BigramFlags *const outBigramFlags) {
+    BigramFlags flags = probability & MASK_ATTRIBUTE_PROBABILITY;
+    if (hasNext) {
+        flags |= FLAG_ATTRIBUTE_HAS_NEXT;
+    }
+    const int offset = getBigramTargetOffset(targetPtNodePos, entryPos);
+    if (offset < 0) {
+        flags |= FLAG_ATTRIBUTE_OFFSET_NEGATIVE;
+    }
+    const uint32_t absOffest = abs(offset);
+    if ((absOffest >> 24) != 0) {
+        // Offset is too large.
+        return false;
+    } else if ((absOffest >> 16) != 0) {
+        flags |= FLAG_ATTRIBUTE_ADDRESS_TYPE_THREEBYTES;
+    } else if ((absOffest >> 8) != 0) {
+        flags |= FLAG_ATTRIBUTE_ADDRESS_TYPE_TWOBYTES;
+    } else {
+        flags |= FLAG_ATTRIBUTE_ADDRESS_TYPE_ONEBYTE;
+    }
+    // Currently, all newly written bigram position fields are 3 bytes to simplify dictionary
+    // writing.
+    // TODO: Remove following 2 lines and optimize memory space.
+    flags = (flags & (~MASK_ATTRIBUTE_ADDRESS_TYPE)) | FLAG_ATTRIBUTE_ADDRESS_TYPE_THREEBYTES;
+    *outBigramFlags = flags;
+    return true;
+}
+
+/* static */ int BigramListReadWriteUtils::getBigramTargetOffset(const int targetPtNodePos,
+        const int entryPos) {
+    if (targetPtNodePos == NOT_A_DICT_POS) {
+        return DynamicPatriciaTrieReadingUtils::DICT_OFFSET_INVALID;
+    } else {
+        const int offset = targetPtNodePos - (entryPos + 1 /* bigramFlagsField */);
+        if (offset == 0) {
+            return DynamicPatriciaTrieReadingUtils::DICT_OFFSET_ZERO_OFFSET;
+        } else {
+            return offset;
+        }
+    }
+}
+
+} // namespace latinime
diff --git a/native/jni/src/suggest/policyimpl/dictionary/bigram/bigram_list_read_write_utils.h b/native/jni/src/suggest/policyimpl/dictionary/bigram/bigram_list_read_write_utils.h
new file mode 100644
index 0000000..eabe4e0
--- /dev/null
+++ b/native/jni/src/suggest/policyimpl/dictionary/bigram/bigram_list_read_write_utils.h
@@ -0,0 +1,102 @@
+/*
+ * Copyright (C) 2013 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.
+ */
+
+#ifndef LATINIME_BIGRAM_LIST_READ_WRITE_UTILS_H
+#define LATINIME_BIGRAM_LIST_READ_WRITE_UTILS_H
+
+#include <cstdlib>
+#include <stdint.h>
+
+#include "defines.h"
+
+namespace latinime {
+
+class BufferWithExtendableBuffer;
+
+class BigramListReadWriteUtils {
+public:
+   typedef uint8_t BigramFlags;
+
+   static void getBigramEntryPropertiesAndAdvancePosition(const uint8_t *const bigramsBuf,
+           BigramFlags *const outBigramFlags, int *const outTargetPtNodePos,
+           int *const bigramEntryPos);
+
+   static AK_FORCE_INLINE int getProbabilityFromFlags(const BigramFlags flags) {
+       return flags & MASK_ATTRIBUTE_PROBABILITY;
+   }
+
+   static AK_FORCE_INLINE bool hasNext(const BigramFlags flags) {
+       return (flags & FLAG_ATTRIBUTE_HAS_NEXT) != 0;
+   }
+
+   // Bigrams reading methods
+   static void skipExistingBigrams(const uint8_t *const bigramsBuf, int *const bigramListPos);
+
+   // Returns the size of the bigram position field that is stored in bigram flags.
+   static AK_FORCE_INLINE int attributeAddressSize(const BigramFlags flags) {
+       return (flags & MASK_ATTRIBUTE_ADDRESS_TYPE) >> ATTRIBUTE_ADDRESS_SHIFT;
+       /* Note: this is a value-dependant optimization of what may probably be
+          more readably written this way:
+          switch (flags * BinaryFormat::MASK_ATTRIBUTE_ADDRESS_TYPE) {
+          case FLAG_ATTRIBUTE_ADDRESS_TYPE_ONEBYTE: return 1;
+          case FLAG_ATTRIBUTE_ADDRESS_TYPE_TWOBYTES: return 2;
+          case FLAG_ATTRIBUTE_ADDRESS_TYPE_THREEBYTE: return 3;
+          default: return 0;
+          }
+       */
+   }
+
+   static bool setHasNextFlag(BufferWithExtendableBuffer *const buffer,
+           const bool hasNext, const int entryPos);
+
+   static AK_FORCE_INLINE BigramFlags setProbabilityInFlags(const BigramFlags flags,
+           const int probability) {
+       return (flags & (~MASK_ATTRIBUTE_PROBABILITY)) | (probability & MASK_ATTRIBUTE_PROBABILITY);
+   }
+
+   static bool createAndWriteBigramEntry(BufferWithExtendableBuffer *const buffer,
+           const int targetPos, const int probability, const bool hasNext, int *const writingPos);
+
+   static bool writeBigramEntry(BufferWithExtendableBuffer *const buffer, const BigramFlags flags,
+           const int targetOffset, int *const writingPos);
+
+private:
+   DISALLOW_IMPLICIT_CONSTRUCTORS(BigramListReadWriteUtils);
+
+   static const BigramFlags MASK_ATTRIBUTE_ADDRESS_TYPE;
+   static const BigramFlags FLAG_ATTRIBUTE_ADDRESS_TYPE_ONEBYTE;
+   static const BigramFlags FLAG_ATTRIBUTE_ADDRESS_TYPE_TWOBYTES;
+   static const BigramFlags FLAG_ATTRIBUTE_ADDRESS_TYPE_THREEBYTES;
+   static const BigramFlags FLAG_ATTRIBUTE_OFFSET_NEGATIVE;
+   static const BigramFlags FLAG_ATTRIBUTE_HAS_NEXT;
+   static const BigramFlags MASK_ATTRIBUTE_PROBABILITY;
+   static const int ATTRIBUTE_ADDRESS_SHIFT;
+
+   // Returns true if the bigram entry is valid and put entry flags into out*.
+   static bool createAndGetBigramFlags(const int entryPos, const int targetPos,
+           const int probability, const bool hasNext, BigramFlags *const outBigramFlags);
+
+   static AK_FORCE_INLINE bool isOffsetNegative(const BigramFlags flags) {
+       return (flags & FLAG_ATTRIBUTE_OFFSET_NEGATIVE) != 0;
+   }
+
+   static int getBigramAddressAndAdvancePosition(const uint8_t *const bigramsBuf,
+           const BigramFlags flags, int *const pos);
+
+   static int getBigramTargetOffset(const int targetPtNodePos, const int entryPos);
+};
+} // namespace latinime
+#endif // LATINIME_BIGRAM_LIST_READ_WRITE_UTILS_H
diff --git a/native/jni/src/suggest/policyimpl/dictionary/bigram/dynamic_bigram_list_policy.cpp b/native/jni/src/suggest/policyimpl/dictionary/bigram/dynamic_bigram_list_policy.cpp
new file mode 100644
index 0000000..bc2f5ee
--- /dev/null
+++ b/native/jni/src/suggest/policyimpl/dictionary/bigram/dynamic_bigram_list_policy.cpp
@@ -0,0 +1,336 @@
+/*
+ * Copyright (C) 2013 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.
+ */
+
+#include "suggest/policyimpl/dictionary/bigram/dynamic_bigram_list_policy.h"
+
+#include "suggest/core/policy/dictionary_shortcuts_structure_policy.h"
+#include "suggest/policyimpl/dictionary/bigram/bigram_list_read_write_utils.h"
+#include "suggest/policyimpl/dictionary/dynamic_patricia_trie_node_reader.h"
+#include "suggest/policyimpl/dictionary/dynamic_patricia_trie_writing_helper.h"
+#include "suggest/policyimpl/dictionary/utils/buffer_with_extendable_buffer.h"
+
+namespace latinime {
+
+const int DynamicBigramListPolicy::CONTINUING_BIGRAM_LINK_COUNT_LIMIT = 10000;
+const int DynamicBigramListPolicy::BIGRAM_ENTRY_COUNT_IN_A_BIGRAM_LIST_LIMIT = 100000;
+
+void DynamicBigramListPolicy::getNextBigram(int *const outBigramPos, int *const outProbability,
+        bool *const outHasNext, int *const bigramEntryPos) const {
+    const bool usesAdditionalBuffer = mBuffer->isInAdditionalBuffer(*bigramEntryPos);
+    const uint8_t *const buffer = mBuffer->getBuffer(usesAdditionalBuffer);
+    if (usesAdditionalBuffer) {
+        *bigramEntryPos -= mBuffer->getOriginalBufferSize();
+    }
+    BigramListReadWriteUtils::BigramFlags bigramFlags;
+    int originalBigramPos;
+    BigramListReadWriteUtils::getBigramEntryPropertiesAndAdvancePosition(buffer, &bigramFlags,
+            &originalBigramPos, bigramEntryPos);
+    if (usesAdditionalBuffer && originalBigramPos != NOT_A_DICT_POS) {
+        originalBigramPos += mBuffer->getOriginalBufferSize();
+    }
+    *outBigramPos = followBigramLinkAndGetCurrentBigramPtNodePos(originalBigramPos);
+    *outProbability = BigramListReadWriteUtils::getProbabilityFromFlags(bigramFlags);
+    *outHasNext = BigramListReadWriteUtils::hasNext(bigramFlags);
+    if (usesAdditionalBuffer) {
+        *bigramEntryPos += mBuffer->getOriginalBufferSize();
+    }
+}
+
+void DynamicBigramListPolicy::skipAllBigrams(int *const bigramListPos) const {
+    const bool usesAdditionalBuffer = mBuffer->isInAdditionalBuffer(*bigramListPos);
+    const uint8_t *const buffer = mBuffer->getBuffer(usesAdditionalBuffer);
+    if (usesAdditionalBuffer) {
+        *bigramListPos -= mBuffer->getOriginalBufferSize();
+    }
+    BigramListReadWriteUtils::skipExistingBigrams(buffer, bigramListPos);
+    if (usesAdditionalBuffer) {
+        *bigramListPos += mBuffer->getOriginalBufferSize();
+    }
+}
+
+bool DynamicBigramListPolicy::copyAllBigrams(BufferWithExtendableBuffer *const bufferToWrite,
+        int *const fromPos, int *const toPos, int *const outBigramsCount) const {
+    const bool usesAdditionalBuffer = mBuffer->isInAdditionalBuffer(*fromPos);
+    if (usesAdditionalBuffer) {
+        *fromPos -= mBuffer->getOriginalBufferSize();
+    }
+    *outBigramsCount = 0;
+    BigramListReadWriteUtils::BigramFlags bigramFlags;
+    int bigramEntryCount = 0;
+    int lastWrittenEntryPos = NOT_A_DICT_POS;
+    do {
+        if (++bigramEntryCount > BIGRAM_ENTRY_COUNT_IN_A_BIGRAM_LIST_LIMIT) {
+            AKLOGE("Too many bigram entries. Entry count: %d, Limit: %d",
+                    bigramEntryCount, BIGRAM_ENTRY_COUNT_IN_A_BIGRAM_LIST_LIMIT);
+            ASSERT(false);
+            return false;
+        }
+        // The buffer address can be changed after calling buffer writing methods.
+        int originalBigramPos;
+        BigramListReadWriteUtils::getBigramEntryPropertiesAndAdvancePosition(
+                mBuffer->getBuffer(usesAdditionalBuffer), &bigramFlags, &originalBigramPos,
+                fromPos);
+        if (originalBigramPos == NOT_A_DICT_POS) {
+            // skip invalid bigram entry.
+            continue;
+        }
+        if (usesAdditionalBuffer) {
+            originalBigramPos += mBuffer->getOriginalBufferSize();
+        }
+        const int bigramPos = followBigramLinkAndGetCurrentBigramPtNodePos(originalBigramPos);
+        if (bigramPos == NOT_A_DICT_POS) {
+            // Target PtNode has been invalidated.
+            continue;
+        }
+        lastWrittenEntryPos = *toPos;
+        if (!BigramListReadWriteUtils::createAndWriteBigramEntry(bufferToWrite, bigramPos,
+                BigramListReadWriteUtils::getProbabilityFromFlags(bigramFlags),
+                BigramListReadWriteUtils::hasNext(bigramFlags), toPos)) {
+            return false;
+        }
+        (*outBigramsCount)++;
+    } while(BigramListReadWriteUtils::hasNext(bigramFlags));
+    // Makes the last entry the terminal of the list. Updates the flags.
+    if (lastWrittenEntryPos != NOT_A_DICT_POS) {
+        if (!BigramListReadWriteUtils::setHasNextFlag(bufferToWrite, false /* hasNext */,
+                lastWrittenEntryPos)) {
+            return false;
+        }
+    }
+    if (usesAdditionalBuffer) {
+        *fromPos += mBuffer->getOriginalBufferSize();
+    }
+    return true;
+}
+
+// Finding useless bigram entries and remove them. Bigram entry is useless when the target PtNode
+// has been deleted or is not a valid terminal.
+bool DynamicBigramListPolicy::updateAllBigramEntriesAndDeleteUselessEntries(
+        int *const bigramListPos) {
+    const bool usesAdditionalBuffer = mBuffer->isInAdditionalBuffer(*bigramListPos);
+    if (usesAdditionalBuffer) {
+        *bigramListPos -= mBuffer->getOriginalBufferSize();
+    }
+    DynamicPatriciaTrieNodeReader nodeReader(mBuffer, this /* bigramsPolicy */, mShortcutPolicy);
+    BigramListReadWriteUtils::BigramFlags bigramFlags;
+    int bigramEntryCount = 0;
+    do {
+        if (++bigramEntryCount > BIGRAM_ENTRY_COUNT_IN_A_BIGRAM_LIST_LIMIT) {
+            AKLOGE("Too many bigram entries. Entry count: %d, Limit: %d",
+                    bigramEntryCount, BIGRAM_ENTRY_COUNT_IN_A_BIGRAM_LIST_LIMIT);
+            ASSERT(false);
+            return false;
+        }
+        int bigramEntryPos = *bigramListPos;
+        int originalBigramPos;
+        // The buffer address can be changed after calling buffer writing methods.
+        BigramListReadWriteUtils::getBigramEntryPropertiesAndAdvancePosition(
+                mBuffer->getBuffer(usesAdditionalBuffer), &bigramFlags, &originalBigramPos,
+                bigramListPos);
+        if (usesAdditionalBuffer) {
+            bigramEntryPos += mBuffer->getOriginalBufferSize();
+        }
+        if (originalBigramPos == NOT_A_DICT_POS) {
+            // This entry has already been removed.
+            continue;
+        }
+        if (usesAdditionalBuffer) {
+            originalBigramPos += mBuffer->getOriginalBufferSize();
+        }
+        const int bigramTargetNodePos =
+                followBigramLinkAndGetCurrentBigramPtNodePos(originalBigramPos);
+        nodeReader.fetchNodeInfoInBufferFromPtNodePos(bigramTargetNodePos);
+        // TODO: Update probability for supporting probability decaying.
+        if (nodeReader.isDeleted() || !nodeReader.isTerminal()
+                || bigramTargetNodePos == NOT_A_DICT_POS) {
+            // The target is no longer valid terminal. Invalidate the current bigram entry.
+            if (!BigramListReadWriteUtils::writeBigramEntry(mBuffer, bigramFlags,
+                    NOT_A_DICT_POS /* targetOffset */, &bigramEntryPos)) {
+                return false;
+            }
+        }
+    } while(BigramListReadWriteUtils::hasNext(bigramFlags));
+    return true;
+}
+
+// Updates bigram target PtNode positions in the list after the placing step in GC.
+bool DynamicBigramListPolicy::updateAllBigramTargetPtNodePositions(int *const bigramListPos,
+        const DynamicPatriciaTrieWritingHelper::PtNodePositionRelocationMap *const
+                ptNodePositionRelocationMap) {
+    const bool usesAdditionalBuffer = mBuffer->isInAdditionalBuffer(*bigramListPos);
+    if (usesAdditionalBuffer) {
+        *bigramListPos -= mBuffer->getOriginalBufferSize();
+    }
+    BigramListReadWriteUtils::BigramFlags bigramFlags;
+    int bigramEntryCount = 0;
+    do {
+        if (++bigramEntryCount > BIGRAM_ENTRY_COUNT_IN_A_BIGRAM_LIST_LIMIT) {
+            AKLOGE("Too many bigram entries. Entry count: %d, Limit: %d",
+                    bigramEntryCount, BIGRAM_ENTRY_COUNT_IN_A_BIGRAM_LIST_LIMIT);
+            ASSERT(false);
+            return false;
+        }
+        int bigramEntryPos = *bigramListPos;
+        if (usesAdditionalBuffer) {
+            bigramEntryPos += mBuffer->getOriginalBufferSize();
+        }
+        int bigramTargetPtNodePos;
+        // The buffer address can be changed after calling buffer writing methods.
+        BigramListReadWriteUtils::getBigramEntryPropertiesAndAdvancePosition(
+                mBuffer->getBuffer(usesAdditionalBuffer), &bigramFlags, &bigramTargetPtNodePos,
+                bigramListPos);
+        if (bigramTargetPtNodePos == NOT_A_DICT_POS) {
+            continue;
+        }
+        if (usesAdditionalBuffer) {
+            bigramTargetPtNodePos += mBuffer->getOriginalBufferSize();
+        }
+
+        DynamicPatriciaTrieWritingHelper::PtNodePositionRelocationMap::const_iterator it =
+                ptNodePositionRelocationMap->find(bigramTargetPtNodePos);
+        if (it != ptNodePositionRelocationMap->end()) {
+            bigramTargetPtNodePos = it->second;
+        } else {
+            bigramTargetPtNodePos = NOT_A_DICT_POS;
+        }
+        if (!BigramListReadWriteUtils::writeBigramEntry(mBuffer, bigramFlags,
+                bigramTargetPtNodePos, &bigramEntryPos)) {
+            return false;
+        }
+    } while(BigramListReadWriteUtils::hasNext(bigramFlags));
+    return true;
+}
+
+bool DynamicBigramListPolicy::addNewBigramEntryToBigramList(const int bigramTargetPos,
+        const int probability, int *const bigramListPos) {
+    const bool usesAdditionalBuffer = mBuffer->isInAdditionalBuffer(*bigramListPos);
+    if (usesAdditionalBuffer) {
+        *bigramListPos -= mBuffer->getOriginalBufferSize();
+    }
+    BigramListReadWriteUtils::BigramFlags bigramFlags;
+    int bigramEntryCount = 0;
+    do {
+        if (++bigramEntryCount > BIGRAM_ENTRY_COUNT_IN_A_BIGRAM_LIST_LIMIT) {
+            AKLOGE("Too many bigram entries. Entry count: %d, Limit: %d",
+                    bigramEntryCount, BIGRAM_ENTRY_COUNT_IN_A_BIGRAM_LIST_LIMIT);
+            ASSERT(false);
+            return false;
+        }
+        int entryPos = *bigramListPos;
+        if (usesAdditionalBuffer) {
+            entryPos += mBuffer->getOriginalBufferSize();
+        }
+        int originalBigramPos;
+        // The buffer address can be changed after calling buffer writing methods.
+        BigramListReadWriteUtils::getBigramEntryPropertiesAndAdvancePosition(
+                mBuffer->getBuffer(usesAdditionalBuffer), &bigramFlags, &originalBigramPos,
+                bigramListPos);
+        if (usesAdditionalBuffer && originalBigramPos != NOT_A_DICT_POS) {
+            originalBigramPos += mBuffer->getOriginalBufferSize();
+        }
+        if (followBigramLinkAndGetCurrentBigramPtNodePos(originalBigramPos) == bigramTargetPos) {
+            // Update this bigram entry.
+            const BigramListReadWriteUtils::BigramFlags updatedFlags =
+                    BigramListReadWriteUtils::setProbabilityInFlags(bigramFlags, probability);
+            return BigramListReadWriteUtils::writeBigramEntry(mBuffer, updatedFlags,
+                    originalBigramPos, &entryPos);
+        }
+        if (BigramListReadWriteUtils::hasNext(bigramFlags)) {
+            continue;
+        }
+        // The current last entry is found.
+        // First, update the flags of the last entry.
+        if (!BigramListReadWriteUtils::setHasNextFlag(mBuffer, true /* hasNext */, entryPos)) {
+            return false;
+        }
+        if (usesAdditionalBuffer) {
+            *bigramListPos += mBuffer->getOriginalBufferSize();
+        }
+        // Then, add a new entry after the last entry.
+        return writeNewBigramEntry(bigramTargetPos, probability, bigramListPos);
+    } while(BigramListReadWriteUtils::hasNext(bigramFlags));
+    // We return directly from the while loop.
+    ASSERT(false);
+    return false;
+}
+
+bool DynamicBigramListPolicy::writeNewBigramEntry(const int bigramTargetPos, const int probability,
+        int *const writingPos) {
+    // hasNext is false because we are adding a new bigram entry at the end of the bigram list.
+    return BigramListReadWriteUtils::createAndWriteBigramEntry(mBuffer, bigramTargetPos,
+            probability, false /* hasNext */, writingPos);
+}
+
+bool DynamicBigramListPolicy::removeBigram(const int bigramListPos, const int bigramTargetPos) {
+    const bool usesAdditionalBuffer = mBuffer->isInAdditionalBuffer(bigramListPos);
+    int pos = bigramListPos;
+    if (usesAdditionalBuffer) {
+        pos -= mBuffer->getOriginalBufferSize();
+    }
+    BigramListReadWriteUtils::BigramFlags bigramFlags;
+    int bigramEntryCount = 0;
+    do {
+        if (++bigramEntryCount > BIGRAM_ENTRY_COUNT_IN_A_BIGRAM_LIST_LIMIT) {
+            AKLOGE("Too many bigram entries. Entry count: %d, Limit: %d",
+                    bigramEntryCount, BIGRAM_ENTRY_COUNT_IN_A_BIGRAM_LIST_LIMIT);
+            ASSERT(false);
+            return false;
+        }
+        int bigramEntryPos = pos;
+        int originalBigramPos;
+        // The buffer address can be changed after calling buffer writing methods.
+        BigramListReadWriteUtils::getBigramEntryPropertiesAndAdvancePosition(
+                mBuffer->getBuffer(usesAdditionalBuffer), &bigramFlags, &originalBigramPos, &pos);
+        if (usesAdditionalBuffer) {
+            bigramEntryPos += mBuffer->getOriginalBufferSize();
+        }
+        if (usesAdditionalBuffer && originalBigramPos != NOT_A_DICT_POS) {
+            originalBigramPos += mBuffer->getOriginalBufferSize();
+        }
+        const int bigramPos = followBigramLinkAndGetCurrentBigramPtNodePos(originalBigramPos);
+        if (bigramPos != bigramTargetPos) {
+            continue;
+        }
+        // Target entry is found. Write an invalid target position to mark the bigram invalid.
+        return BigramListReadWriteUtils::writeBigramEntry(mBuffer, bigramFlags,
+                NOT_A_DICT_POS /* targetOffset */, &bigramEntryPos);
+    } while(BigramListReadWriteUtils::hasNext(bigramFlags));
+    return false;
+}
+
+int DynamicBigramListPolicy::followBigramLinkAndGetCurrentBigramPtNodePos(
+        const int originalBigramPos) const {
+    if (originalBigramPos == NOT_A_DICT_POS) {
+        return NOT_A_DICT_POS;
+    }
+    int currentPos = originalBigramPos;
+    DynamicPatriciaTrieNodeReader nodeReader(mBuffer, this /* bigramsPolicy */, mShortcutPolicy);
+    nodeReader.fetchNodeInfoInBufferFromPtNodePos(currentPos);
+    int bigramLinkCount = 0;
+    while (nodeReader.getBigramLinkedNodePos() != NOT_A_DICT_POS) {
+        currentPos = nodeReader.getBigramLinkedNodePos();
+        nodeReader.fetchNodeInfoInBufferFromPtNodePos(currentPos);
+        bigramLinkCount++;
+        if (bigramLinkCount > CONTINUING_BIGRAM_LINK_COUNT_LIMIT) {
+            AKLOGE("Bigram link is invalid. start position: %d", bigramPos);
+            ASSERT(false);
+            return NOT_A_DICT_POS;
+        }
+    }
+    return currentPos;
+}
+
+} // namespace latinime
diff --git a/native/jni/src/suggest/policyimpl/dictionary/bigram/dynamic_bigram_list_policy.h b/native/jni/src/suggest/policyimpl/dictionary/bigram/dynamic_bigram_list_policy.h
new file mode 100644
index 0000000..8ea318a
--- /dev/null
+++ b/native/jni/src/suggest/policyimpl/dictionary/bigram/dynamic_bigram_list_policy.h
@@ -0,0 +1,81 @@
+/*
+ * Copyright (C) 2013 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.
+ */
+
+#ifndef LATINIME_DYNAMIC_BIGRAM_LIST_POLICY_H
+#define LATINIME_DYNAMIC_BIGRAM_LIST_POLICY_H
+
+#include <stdint.h>
+
+#include "defines.h"
+#include "suggest/core/policy/dictionary_bigrams_structure_policy.h"
+#include "suggest/policyimpl/dictionary/dynamic_patricia_trie_writing_helper.h"
+
+namespace latinime {
+
+class BufferWithExtendableBuffer;
+class DictionaryShortcutsStructurePolicy;
+
+/*
+ * This is a dynamic version of BigramListPolicy and supports an additional buffer.
+ */
+class DynamicBigramListPolicy : public DictionaryBigramsStructurePolicy {
+ public:
+    DynamicBigramListPolicy(BufferWithExtendableBuffer *const buffer,
+            const DictionaryShortcutsStructurePolicy *const shortcutPolicy)
+            : mBuffer(buffer), mShortcutPolicy(shortcutPolicy) {}
+
+    ~DynamicBigramListPolicy() {}
+
+    void getNextBigram(int *const outBigramPos, int *const outProbability, bool *const outHasNext,
+            int *const bigramEntryPos) const;
+
+    void skipAllBigrams(int *const bigramListPos) const;
+
+    // Copy bigrams from the bigram list that starts at fromPos in mBuffer to toPos in
+    // bufferToWrite and advance these positions after bigram lists. This method skips invalid
+    // bigram entries and write the valid bigram entry count to outBigramsCount.
+    bool copyAllBigrams(BufferWithExtendableBuffer *const bufferToWrite, int *const fromPos,
+            int *const toPos, int *const outBigramsCount) const;
+
+    bool updateAllBigramEntriesAndDeleteUselessEntries(int *const bigramListPos);
+
+    bool updateAllBigramTargetPtNodePositions(int *const bigramListPos,
+            const DynamicPatriciaTrieWritingHelper::PtNodePositionRelocationMap *const
+                    ptNodePositionRelocationMap);
+
+    bool addNewBigramEntryToBigramList(const int bigramTargetPos, const int probability,
+            int *const bigramListPos);
+
+    bool writeNewBigramEntry(const int bigramTargetPos, const int probability,
+            int *const writingPos);
+
+    // Return if targetBigramPos is found or not.
+    bool removeBigram(const int bigramListPos, const int bigramTargetPos);
+
+ private:
+    DISALLOW_IMPLICIT_CONSTRUCTORS(DynamicBigramListPolicy);
+
+    static const int CONTINUING_BIGRAM_LINK_COUNT_LIMIT;
+    static const int BIGRAM_ENTRY_COUNT_IN_A_BIGRAM_LIST_LIMIT;
+
+    BufferWithExtendableBuffer *const mBuffer;
+    const DictionaryShortcutsStructurePolicy *const mShortcutPolicy;
+
+    // Follow bigram link and return the position of bigram target PtNode that is currently valid.
+    int followBigramLinkAndGetCurrentBigramPtNodePos(const int originalBigramPos) const;
+};
+} // namespace latinime
+#endif // LATINIME_DYNAMIC_BIGRAM_LIST_POLICY_H
diff --git a/native/jni/src/suggest/policyimpl/dictionary/binary_format.h b/native/jni/src/suggest/policyimpl/dictionary/binary_format.h
deleted file mode 100644
index 23f4c7f..0000000
--- a/native/jni/src/suggest/policyimpl/dictionary/binary_format.h
+++ /dev/null
@@ -1,470 +0,0 @@
-/*
- * Copyright (C) 2011 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef LATINIME_BINARY_FORMAT_H
-#define LATINIME_BINARY_FORMAT_H
-
-#include <stdint.h>
-
-#include "suggest/core/dictionary/probability_utils.h"
-#include "utils/char_utils.h"
-
-namespace latinime {
-
-class BinaryFormat {
- public:
-    // Mask and flags for children address type selection.
-    static const int MASK_GROUP_ADDRESS_TYPE = 0xC0;
-
-    // Flag for single/multiple char group
-    static const int FLAG_HAS_MULTIPLE_CHARS = 0x20;
-
-    // Flag for terminal groups
-    static const int FLAG_IS_TERMINAL = 0x10;
-
-    // Flag for shortcut targets presence
-    static const int FLAG_HAS_SHORTCUT_TARGETS = 0x08;
-    // Flag for bigram presence
-    static const int FLAG_HAS_BIGRAMS = 0x04;
-    // Flag for non-words (typically, shortcut only entries)
-    static const int FLAG_IS_NOT_A_WORD = 0x02;
-    // Flag for blacklist
-    static const int FLAG_IS_BLACKLISTED = 0x01;
-
-    // Attribute (bigram/shortcut) related flags:
-    // Flag for presence of more attributes
-    static const int FLAG_ATTRIBUTE_HAS_NEXT = 0x80;
-    // Flag for sign of offset. If this flag is set, the offset value must be negated.
-    static const int FLAG_ATTRIBUTE_OFFSET_NEGATIVE = 0x40;
-
-    // Mask for attribute probability, stored on 4 bits inside the flags byte.
-    static const int MASK_ATTRIBUTE_PROBABILITY = 0x0F;
-
-    // Mask and flags for attribute address type selection.
-    static const int MASK_ATTRIBUTE_ADDRESS_TYPE = 0x30;
-
-    static int getGroupCountAndForwardPointer(const uint8_t *const dict, int *pos);
-    static uint8_t getFlagsAndForwardPointer(const uint8_t *const dict, int *pos);
-    static int getCodePointAndForwardPointer(const uint8_t *const dict, int *pos);
-    static int readProbabilityWithoutMovingPointer(const uint8_t *const dict, const int pos);
-    static int skipOtherCharacters(const uint8_t *const dict, const int pos);
-    static int skipChildrenPosition(const uint8_t flags, const int pos);
-    static int skipProbability(const uint8_t flags, const int pos);
-    static int skipShortcuts(const uint8_t *const dict, const uint8_t flags, const int pos);
-    static int skipChildrenPosAndAttributes(const uint8_t *const dict, const uint8_t flags,
-            const int pos);
-    static int readChildrenPosition(const uint8_t *const dict, const uint8_t flags, const int pos);
-    static bool hasChildrenInFlags(const uint8_t flags);
-    static int getTerminalPosition(const uint8_t *const root, const int *const inWord,
-            const int length, const bool forceLowerCaseSearch);
-    static int getCodePointsAndProbabilityAndReturnCodePointCount(
-            const uint8_t *const root, const int nodePos, const int maxCodePointCount,
-            int *const outCodePoints, int *const outUnigramProbability);
-
- private:
-    DISALLOW_IMPLICIT_CONSTRUCTORS(BinaryFormat);
-
-    static const int FLAG_GROUP_ADDRESS_TYPE_NOADDRESS = 0x00;
-    static const int FLAG_GROUP_ADDRESS_TYPE_ONEBYTE = 0x40;
-    static const int FLAG_GROUP_ADDRESS_TYPE_TWOBYTES = 0x80;
-    static const int FLAG_GROUP_ADDRESS_TYPE_THREEBYTES = 0xC0;
-    static const int FLAG_ATTRIBUTE_ADDRESS_TYPE_ONEBYTE = 0x10;
-    static const int FLAG_ATTRIBUTE_ADDRESS_TYPE_TWOBYTES = 0x20;
-    static const int FLAG_ATTRIBUTE_ADDRESS_TYPE_THREEBYTES = 0x30;
-
-    static const int CHARACTER_ARRAY_TERMINATOR_SIZE = 1;
-    static const int MINIMAL_ONE_BYTE_CHARACTER_VALUE = 0x20;
-    static const int CHARACTER_ARRAY_TERMINATOR = 0x1F;
-    static const int MULTIPLE_BYTE_CHARACTER_ADDITIONAL_SIZE = 2;
-    static const int NO_FLAGS = 0;
-    static int skipAllAttributes(const uint8_t *const dict, const uint8_t flags, const int pos);
-    static int skipBigrams(const uint8_t *const dict, const uint8_t flags, const int pos);
-};
-
-AK_FORCE_INLINE int BinaryFormat::getGroupCountAndForwardPointer(const uint8_t *const dict,
-        int *pos) {
-    const int msb = dict[(*pos)++];
-    if (msb < 0x80) return msb;
-    return ((msb & 0x7F) << 8) | dict[(*pos)++];
-}
-
-inline uint8_t BinaryFormat::getFlagsAndForwardPointer(const uint8_t *const dict, int *pos) {
-    return dict[(*pos)++];
-}
-
-AK_FORCE_INLINE int BinaryFormat::getCodePointAndForwardPointer(const uint8_t *const dict,
-        int *pos) {
-    const int origin = *pos;
-    const int codePoint = dict[origin];
-    if (codePoint < MINIMAL_ONE_BYTE_CHARACTER_VALUE) {
-        if (codePoint == CHARACTER_ARRAY_TERMINATOR) {
-            *pos = origin + 1;
-            return NOT_A_CODE_POINT;
-        } else {
-            *pos = origin + 3;
-            const int char_1 = codePoint << 16;
-            const int char_2 = char_1 + (dict[origin + 1] << 8);
-            return char_2 + dict[origin + 2];
-        }
-    } else {
-        *pos = origin + 1;
-        return codePoint;
-    }
-}
-
-inline int BinaryFormat::readProbabilityWithoutMovingPointer(const uint8_t *const dict,
-        const int pos) {
-    return dict[pos];
-}
-
-AK_FORCE_INLINE int BinaryFormat::skipOtherCharacters(const uint8_t *const dict, const int pos) {
-    int currentPos = pos;
-    int character = dict[currentPos++];
-    while (CHARACTER_ARRAY_TERMINATOR != character) {
-        if (character < MINIMAL_ONE_BYTE_CHARACTER_VALUE) {
-            currentPos += MULTIPLE_BYTE_CHARACTER_ADDITIONAL_SIZE;
-        }
-        character = dict[currentPos++];
-    }
-    return currentPos;
-}
-
-static inline int attributeAddressSize(const uint8_t flags) {
-    static const int ATTRIBUTE_ADDRESS_SHIFT = 4;
-    return (flags & BinaryFormat::MASK_ATTRIBUTE_ADDRESS_TYPE) >> ATTRIBUTE_ADDRESS_SHIFT;
-    /* Note: this is a value-dependant optimization of what may probably be
-       more readably written this way:
-       switch (flags * BinaryFormat::MASK_ATTRIBUTE_ADDRESS_TYPE) {
-       case FLAG_ATTRIBUTE_ADDRESS_TYPE_ONEBYTE: return 1;
-       case FLAG_ATTRIBUTE_ADDRESS_TYPE_TWOBYTES: return 2;
-       case FLAG_ATTRIBUTE_ADDRESS_TYPE_THREEBYTE: return 3;
-       default: return 0;
-       }
-    */
-}
-
-static AK_FORCE_INLINE int skipExistingBigrams(const uint8_t *const dict, const int pos) {
-    int currentPos = pos;
-    uint8_t flags = BinaryFormat::getFlagsAndForwardPointer(dict, &currentPos);
-    while (flags & BinaryFormat::FLAG_ATTRIBUTE_HAS_NEXT) {
-        currentPos += attributeAddressSize(flags);
-        flags = BinaryFormat::getFlagsAndForwardPointer(dict, &currentPos);
-    }
-    currentPos += attributeAddressSize(flags);
-    return currentPos;
-}
-
-static inline int childrenAddressSize(const uint8_t flags) {
-    static const int CHILDREN_ADDRESS_SHIFT = 6;
-    return (BinaryFormat::MASK_GROUP_ADDRESS_TYPE & flags) >> CHILDREN_ADDRESS_SHIFT;
-    /* See the note in attributeAddressSize. The same applies here */
-}
-
-static AK_FORCE_INLINE int shortcutByteSize(const uint8_t *const dict, const int pos) {
-    return (static_cast<int>(dict[pos] << 8)) + (dict[pos + 1]);
-}
-
-inline int BinaryFormat::skipChildrenPosition(const uint8_t flags, const int pos) {
-    return pos + childrenAddressSize(flags);
-}
-
-inline int BinaryFormat::skipProbability(const uint8_t flags, const int pos) {
-    return FLAG_IS_TERMINAL & flags ? pos + 1 : pos;
-}
-
-AK_FORCE_INLINE int BinaryFormat::skipShortcuts(const uint8_t *const dict, const uint8_t flags,
-        const int pos) {
-    if (FLAG_HAS_SHORTCUT_TARGETS & flags) {
-        return pos + shortcutByteSize(dict, pos);
-    } else {
-        return pos;
-    }
-}
-
-AK_FORCE_INLINE int BinaryFormat::skipBigrams(const uint8_t *const dict, const uint8_t flags,
-        const int pos) {
-    if (FLAG_HAS_BIGRAMS & flags) {
-        return skipExistingBigrams(dict, pos);
-    } else {
-        return pos;
-    }
-}
-
-AK_FORCE_INLINE int BinaryFormat::skipAllAttributes(const uint8_t *const dict, const uint8_t flags,
-        const int pos) {
-    // This function skips all attributes: shortcuts and bigrams.
-    int newPos = pos;
-    newPos = skipShortcuts(dict, flags, newPos);
-    newPos = skipBigrams(dict, flags, newPos);
-    return newPos;
-}
-
-AK_FORCE_INLINE int BinaryFormat::skipChildrenPosAndAttributes(const uint8_t *const dict,
-        const uint8_t flags, const int pos) {
-    int currentPos = pos;
-    currentPos = skipChildrenPosition(flags, currentPos);
-    currentPos = skipAllAttributes(dict, flags, currentPos);
-    return currentPos;
-}
-
-AK_FORCE_INLINE int BinaryFormat::readChildrenPosition(const uint8_t *const dict,
-        const uint8_t flags, const int pos) {
-    int offset = 0;
-    switch (MASK_GROUP_ADDRESS_TYPE & flags) {
-        case FLAG_GROUP_ADDRESS_TYPE_ONEBYTE:
-            offset = dict[pos];
-            break;
-        case FLAG_GROUP_ADDRESS_TYPE_TWOBYTES:
-            offset = dict[pos] << 8;
-            offset += dict[pos + 1];
-            break;
-        case FLAG_GROUP_ADDRESS_TYPE_THREEBYTES:
-            offset = dict[pos] << 16;
-            offset += dict[pos + 1] << 8;
-            offset += dict[pos + 2];
-            break;
-        default:
-            // If we come here, it means we asked for the children of a word with
-            // no children.
-            return -1;
-    }
-    return pos + offset;
-}
-
-inline bool BinaryFormat::hasChildrenInFlags(const uint8_t flags) {
-    return (FLAG_GROUP_ADDRESS_TYPE_NOADDRESS != (MASK_GROUP_ADDRESS_TYPE & flags));
-}
-
-// This function gets the byte position of the last chargroup of the exact matching word in the
-// dictionary. If no match is found, it returns NOT_A_VALID_WORD_POS.
-AK_FORCE_INLINE int BinaryFormat::getTerminalPosition(const uint8_t *const root,
-        const int *const inWord, const int length, const bool forceLowerCaseSearch) {
-    int pos = 0;
-    int wordPos = 0;
-
-    while (true) {
-        // If we already traversed the tree further than the word is long, there means
-        // there was no match (or we would have found it).
-        if (wordPos >= length) return NOT_A_VALID_WORD_POS;
-        int charGroupCount = BinaryFormat::getGroupCountAndForwardPointer(root, &pos);
-        const int wChar = forceLowerCaseSearch
-                ? CharUtils::toLowerCase(inWord[wordPos]) : inWord[wordPos];
-        while (true) {
-            // If there are no more character groups in this node, it means we could not
-            // find a matching character for this depth, therefore there is no match.
-            if (0 >= charGroupCount) return NOT_A_VALID_WORD_POS;
-            const int charGroupPos = pos;
-            const uint8_t flags = BinaryFormat::getFlagsAndForwardPointer(root, &pos);
-            int character = BinaryFormat::getCodePointAndForwardPointer(root, &pos);
-            if (character == wChar) {
-                // This is the correct node. Only one character group may start with the same
-                // char within a node, so either we found our match in this node, or there is
-                // no match and we can return NOT_A_VALID_WORD_POS. So we will check all the
-                // characters in this character group indeed does match.
-                if (FLAG_HAS_MULTIPLE_CHARS & flags) {
-                    character = BinaryFormat::getCodePointAndForwardPointer(root, &pos);
-                    while (NOT_A_CODE_POINT != character) {
-                        ++wordPos;
-                        // If we shoot the length of the word we search for, or if we find a single
-                        // character that does not match, as explained above, it means the word is
-                        // not in the dictionary (by virtue of this chargroup being the only one to
-                        // match the word on the first character, but not matching the whole word).
-                        if (wordPos >= length) return NOT_A_VALID_WORD_POS;
-                        if (inWord[wordPos] != character) return NOT_A_VALID_WORD_POS;
-                        character = BinaryFormat::getCodePointAndForwardPointer(root, &pos);
-                    }
-                }
-                // If we come here we know that so far, we do match. Either we are on a terminal
-                // and we match the length, in which case we found it, or we traverse children.
-                // If we don't match the length AND don't have children, then a word in the
-                // dictionary fully matches a prefix of the searched word but not the full word.
-                ++wordPos;
-                if (FLAG_IS_TERMINAL & flags) {
-                    if (wordPos == length) {
-                        return charGroupPos;
-                    }
-                    pos = BinaryFormat::skipProbability(FLAG_IS_TERMINAL, pos);
-                }
-                if (FLAG_GROUP_ADDRESS_TYPE_NOADDRESS == (MASK_GROUP_ADDRESS_TYPE & flags)) {
-                    return NOT_A_VALID_WORD_POS;
-                }
-                // We have children and we are still shorter than the word we are searching for, so
-                // we need to traverse children. Put the pointer on the children position, and
-                // break
-                pos = BinaryFormat::readChildrenPosition(root, flags, pos);
-                break;
-            } else {
-                // This chargroup does not match, so skip the remaining part and go to the next.
-                if (FLAG_HAS_MULTIPLE_CHARS & flags) {
-                    pos = BinaryFormat::skipOtherCharacters(root, pos);
-                }
-                pos = BinaryFormat::skipProbability(flags, pos);
-                pos = BinaryFormat::skipChildrenPosAndAttributes(root, flags, pos);
-            }
-            --charGroupCount;
-        }
-    }
-}
-
-// This function searches for a terminal in the dictionary by its address.
-// Due to the fact that words are ordered in the dictionary in a strict breadth-first order,
-// it is possible to check for this with advantageous complexity. For each node, we search
-// for groups with children and compare the children address with the address we look for.
-// When we shoot the address we look for, it means the word we look for is in the children
-// of the previous group. The only tricky part is the fact that if we arrive at the end of a
-// node with the last group's children address still less than what we are searching for, we
-// must descend the last group's children (for example, if the word we are searching for starts
-// with a z, it's the last group of the root node, so all children addresses will be smaller
-// than the address we look for, and we have to descend the z node).
-/* Parameters :
- * root: the dictionary buffer
- * address: the byte position of the last chargroup of the word we are searching for (this is
- *   what is stored as the "bigram address" in each bigram)
- * outword: an array to write the found word, with MAX_WORD_LENGTH size.
- * outUnigramProbability: a pointer to an int to write the probability into.
- * Return value : the length of the word, of 0 if the word was not found.
- */
-AK_FORCE_INLINE int BinaryFormat::getCodePointsAndProbabilityAndReturnCodePointCount(
-        const uint8_t *const root, const int nodePos, const int maxCodePointCount,
-        int *const outCodePoints, int *const outUnigramProbability) {
-    int pos = 0;
-    int wordPos = 0;
-
-    // One iteration of the outer loop iterates through nodes. As stated above, we will only
-    // traverse nodes that are actually a part of the terminal we are searching, so each time
-    // we enter this loop we are one depth level further than last time.
-    // The only reason we count nodes is because we want to reduce the probability of infinite
-    // looping in case there is a bug. Since we know there is an upper bound to the depth we are
-    // supposed to traverse, it does not hurt to count iterations.
-    for (int loopCount = maxCodePointCount; loopCount > 0; --loopCount) {
-        int lastCandidateGroupPos = 0;
-        // Let's loop through char groups in this node searching for either the terminal
-        // or one of its ascendants.
-        for (int charGroupCount = getGroupCountAndForwardPointer(root, &pos); charGroupCount > 0;
-                 --charGroupCount) {
-            const int startPos = pos;
-            const uint8_t flags = getFlagsAndForwardPointer(root, &pos);
-            const int character = getCodePointAndForwardPointer(root, &pos);
-            if (nodePos == startPos) {
-                // We found the address. Copy the rest of the word in the buffer and return
-                // the length.
-                outCodePoints[wordPos] = character;
-                if (FLAG_HAS_MULTIPLE_CHARS & flags) {
-                    int nextChar = getCodePointAndForwardPointer(root, &pos);
-                    // We count chars in order to avoid infinite loops if the file is broken or
-                    // if there is some other bug
-                    int charCount = maxCodePointCount;
-                    while (NOT_A_CODE_POINT != nextChar && --charCount > 0) {
-                        outCodePoints[++wordPos] = nextChar;
-                        nextChar = getCodePointAndForwardPointer(root, &pos);
-                    }
-                }
-                *outUnigramProbability = readProbabilityWithoutMovingPointer(root, pos);
-                return ++wordPos;
-            }
-            // We need to skip past this char group, so skip any remaining chars after the
-            // first and possibly the probability.
-            if (FLAG_HAS_MULTIPLE_CHARS & flags) {
-                pos = skipOtherCharacters(root, pos);
-            }
-            pos = skipProbability(flags, pos);
-
-            // The fact that this group has children is very important. Since we already know
-            // that this group does not match, if it has no children we know it is irrelevant
-            // to what we are searching for.
-            const bool hasChildren = (FLAG_GROUP_ADDRESS_TYPE_NOADDRESS !=
-                    (MASK_GROUP_ADDRESS_TYPE & flags));
-            // We will write in `found' whether we have passed the children address we are
-            // searching for. For example if we search for "beer", the children of b are less
-            // than the address we are searching for and the children of c are greater. When we
-            // come here for c, we realize this is too big, and that we should descend b.
-            bool found;
-            if (hasChildren) {
-                // Here comes the tricky part. First, read the children position.
-                const int childrenPos = readChildrenPosition(root, flags, pos);
-                if (childrenPos > nodePos) {
-                    // If the children pos is greater than address, it means the previous chargroup,
-                    // which address is stored in lastCandidateGroupPos, was the right one.
-                    found = true;
-                } else if (1 >= charGroupCount) {
-                    // However if we are on the LAST group of this node, and we have NOT shot the
-                    // address we should descend THIS node. So we trick the lastCandidateGroupPos
-                    // so that we will descend this node, not the previous one.
-                    lastCandidateGroupPos = startPos;
-                    found = true;
-                } else {
-                    // Else, we should continue looking.
-                    found = false;
-                }
-            } else {
-                // Even if we don't have children here, we could still be on the last group of this
-                // node. If this is the case, we should descend the last group that had children,
-                // and their address is already in lastCandidateGroup.
-                found = (1 >= charGroupCount);
-            }
-
-            if (found) {
-                // Okay, we found the group we should descend. Its address is in
-                // the lastCandidateGroupPos variable, so we just re-read it.
-                if (0 != lastCandidateGroupPos) {
-                    const uint8_t lastFlags =
-                            getFlagsAndForwardPointer(root, &lastCandidateGroupPos);
-                    const int lastChar =
-                            getCodePointAndForwardPointer(root, &lastCandidateGroupPos);
-                    // We copy all the characters in this group to the buffer
-                    outCodePoints[wordPos] = lastChar;
-                    if (FLAG_HAS_MULTIPLE_CHARS & lastFlags) {
-                        int nextChar = getCodePointAndForwardPointer(root, &lastCandidateGroupPos);
-                        int charCount = maxCodePointCount;
-                        while (-1 != nextChar && --charCount > 0) {
-                            outCodePoints[++wordPos] = nextChar;
-                            nextChar = getCodePointAndForwardPointer(root, &lastCandidateGroupPos);
-                        }
-                    }
-                    ++wordPos;
-                    // Now we only need to branch to the children address. Skip the probability if
-                    // it's there, read pos, and break to resume the search at pos.
-                    lastCandidateGroupPos = skipProbability(lastFlags, lastCandidateGroupPos);
-                    pos = readChildrenPosition(root, lastFlags, lastCandidateGroupPos);
-                    break;
-                } else {
-                    // Here is a little tricky part: we come here if we found out that all children
-                    // addresses in this group are bigger than the address we are searching for.
-                    // Should we conclude the word is not in the dictionary? No! It could still be
-                    // one of the remaining chargroups in this node, so we have to keep looking in
-                    // this node until we find it (or we realize it's not there either, in which
-                    // case it's actually not in the dictionary). Pass the end of this group, ready
-                    // to start the next one.
-                    pos = skipChildrenPosAndAttributes(root, flags, pos);
-                }
-            } else {
-                // If we did not find it, we should record the last children address for the next
-                // iteration.
-                if (hasChildren) lastCandidateGroupPos = startPos;
-                // Now skip the end of this group (children pos and the attributes if any) so that
-                // our pos is after the end of this char group, at the start of the next one.
-                pos = skipChildrenPosAndAttributes(root, flags, pos);
-            }
-
-        }
-    }
-    // If we have looked through all the chargroups and found no match, the address is
-    // not the address of a terminal in this dictionary.
-    return 0;
-}
-
-} // namespace latinime
-#endif // LATINIME_BINARY_FORMAT_H
diff --git a/native/jni/src/suggest/policyimpl/dictionary/dictionary_structure_policy_factory.h b/native/jni/src/suggest/policyimpl/dictionary/dictionary_structure_policy_factory.h
deleted file mode 100644
index c0df89f..0000000
--- a/native/jni/src/suggest/policyimpl/dictionary/dictionary_structure_policy_factory.h
+++ /dev/null
@@ -1,48 +0,0 @@
-/*
- * Copyright (C) 2013 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.
- */
-
-#ifndef LATINIME_DICTIONARY_STRUCTURE_POLICY_FACTORY_H
-#define LATINIME_DICTIONARY_STRUCTURE_POLICY_FACTORY_H
-
-#include "defines.h"
-#include "suggest/core/dictionary/binary_dictionary_format_utils.h"
-#include "suggest/policyimpl/dictionary/dynamic_patricia_trie_policy.h"
-#include "suggest/policyimpl/dictionary/patricia_trie_policy.h"
-
-namespace latinime {
-
-class DictionaryStructurePolicy;
-
-class DictionaryStructurePolicyFactory {
- public:
-    static const DictionaryStructurePolicy *getDictionaryStructurePolicy(
-            const BinaryDictionaryFormatUtils::FORMAT_VERSION dictionaryFormat) {
-        switch (dictionaryFormat) {
-            case BinaryDictionaryFormatUtils::VERSION_2:
-                return PatriciaTriePolicy::getInstance();
-            case BinaryDictionaryFormatUtils::VERSION_3:
-                return DynamicPatriciaTriePolicy::getInstance();
-            default:
-                ASSERT(false);
-                return 0;
-        }
-    }
-
- private:
-    DISALLOW_IMPLICIT_CONSTRUCTORS(DictionaryStructurePolicyFactory);
-};
-} // namespace latinime
-#endif // LATINIME_DICTIONARY_STRUCTURE_POLICY_FACTORY_H
diff --git a/native/jni/src/suggest/policyimpl/dictionary/dictionary_structure_with_buffer_policy_factory.cpp b/native/jni/src/suggest/policyimpl/dictionary/dictionary_structure_with_buffer_policy_factory.cpp
new file mode 100644
index 0000000..ff80dd2
--- /dev/null
+++ b/native/jni/src/suggest/policyimpl/dictionary/dictionary_structure_with_buffer_policy_factory.cpp
@@ -0,0 +1,53 @@
+/*
+ * Copyright (C) 2013 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.
+ */
+
+#include "suggest/policyimpl/dictionary/dictionary_structure_with_buffer_policy_factory.h"
+
+#include <stdint.h>
+
+#include "defines.h"
+#include "suggest/policyimpl/dictionary/dynamic_patricia_trie_policy.h"
+#include "suggest/policyimpl/dictionary/patricia_trie_policy.h"
+#include "suggest/policyimpl/dictionary/utils/format_utils.h"
+#include "suggest/policyimpl/dictionary/utils/mmapped_buffer.h"
+
+namespace latinime {
+
+/* static */ DictionaryStructureWithBufferPolicy *DictionaryStructureWithBufferPolicyFactory
+        ::newDictionaryStructureWithBufferPolicy(const char *const path, const int bufOffset,
+                const int size, const bool isUpdatable) {
+    // Allocated buffer in MmapedBuffer::openBuffer() will be freed in the destructor of
+    // impl classes of DictionaryStructureWithBufferPolicy.
+    const MmappedBuffer *const mmapedBuffer = MmappedBuffer::openBuffer(path, bufOffset, size,
+            isUpdatable);
+    if (!mmapedBuffer) {
+        return 0;
+    }
+    switch (FormatUtils::detectFormatVersion(mmapedBuffer->getBuffer(),
+            mmapedBuffer->getBufferSize())) {
+        case FormatUtils::VERSION_2:
+            return new PatriciaTriePolicy(mmapedBuffer);
+        case FormatUtils::VERSION_3:
+            return new DynamicPatriciaTriePolicy(mmapedBuffer);
+        default:
+            AKLOGE("DICT: dictionary format is unknown, bad magic number");
+            delete mmapedBuffer;
+            ASSERT(false);
+            return 0;
+    }
+}
+
+} // namespace latinime
diff --git a/native/jni/src/suggest/policyimpl/dictionary/dictionary_structure_with_buffer_policy_factory.h b/native/jni/src/suggest/policyimpl/dictionary/dictionary_structure_with_buffer_policy_factory.h
new file mode 100644
index 0000000..8cebc3b
--- /dev/null
+++ b/native/jni/src/suggest/policyimpl/dictionary/dictionary_structure_with_buffer_policy_factory.h
@@ -0,0 +1,36 @@
+/*
+ * Copyright (C) 2013 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.
+ */
+
+#ifndef LATINIME_DICTIONARY_STRUCTURE_WITH_BUFFER_POLICY_FACTORY_H
+#define LATINIME_DICTIONARY_STRUCTURE_WITH_BUFFER_POLICY_FACTORY_H
+
+#include <stdint.h>
+
+#include "defines.h"
+#include "suggest/core/policy/dictionary_structure_with_buffer_policy.h"
+
+namespace latinime {
+
+class DictionaryStructureWithBufferPolicyFactory {
+ public:
+    static DictionaryStructureWithBufferPolicy *newDictionaryStructureWithBufferPolicy(
+            const char *const path, const int bufOffset, const int size, const bool isUpdatable);
+
+ private:
+    DISALLOW_IMPLICIT_CONSTRUCTORS(DictionaryStructureWithBufferPolicyFactory);
+};
+} // namespace latinime
+#endif // LATINIME_DICTIONARY_STRUCTURE_WITH_BUFFER_POLICY_FACTORY_H
diff --git a/native/jni/src/suggest/policyimpl/dictionary/dynamic_patricia_trie_gc_event_listeners.cpp b/native/jni/src/suggest/policyimpl/dictionary/dynamic_patricia_trie_gc_event_listeners.cpp
new file mode 100644
index 0000000..c60e458
--- /dev/null
+++ b/native/jni/src/suggest/policyimpl/dictionary/dynamic_patricia_trie_gc_event_listeners.cpp
@@ -0,0 +1,149 @@
+/*
+ * Copyright (C) 2013 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.
+ */
+
+#include "suggest/policyimpl/dictionary/dynamic_patricia_trie_gc_event_listeners.h"
+
+namespace latinime {
+
+bool DynamicPatriciaTrieGcEventListeners
+        ::TraversePolicyToUpdateUnigramProbabilityAndMarkUselessPtNodesAsDeleted
+                ::onVisitingPtNode(const DynamicPatriciaTrieNodeReader *const node,
+                        const int *const nodeCodePoints) {
+    // PtNode is useless when the PtNode is not a terminal and doesn't have any not useless
+    // children.
+    bool isUselessPtNode = !node->isTerminal();
+    if (mChildrenValue > 0) {
+        isUselessPtNode = false;
+    } else if (node->isTerminal()) {
+        // Remove children as all children are useless.
+        int writingPos = node->getChildrenPosFieldPos();
+        if (!DynamicPatriciaTrieWritingUtils::writeChildrenPositionAndAdvancePosition(
+                mBuffer, NOT_A_DICT_POS /* childrenPosition */, &writingPos)) {
+            return false;
+        }
+    }
+    if (isUselessPtNode) {
+        // Current PtNode is no longer needed. Mark it as deleted.
+        if (!mWritingHelper->markNodeAsDeleted(node)) {
+            return false;
+        }
+    } else {
+        valueStack.back() += 1;
+    }
+    return true;
+}
+
+// Writes dummy PtNode array size when the head of PtNode array is read.
+bool DynamicPatriciaTrieGcEventListeners::TraversePolicyToPlaceAndWriteValidPtNodesToBuffer
+        ::onDescend(const int ptNodeArrayPos) {
+    mValidPtNodeCount = 0;
+    int writingPos = mBufferToWrite->getTailPosition();
+    mDictPositionRelocationMap->mPtNodeArrayPositionRelocationMap.insert(
+            DynamicPatriciaTrieWritingHelper::PtNodeArrayPositionRelocationMap::value_type(
+                    ptNodeArrayPos, writingPos));
+    // Writes dummy PtNode array size because arrays can have a forward link or needles PtNodes.
+    // This field will be updated later in onReadingPtNodeArrayTail() with actual PtNode count.
+    mPtNodeArraySizeFieldPos = writingPos;
+    return DynamicPatriciaTrieWritingUtils::writePtNodeArraySizeAndAdvancePosition(
+            mBufferToWrite, 0 /* arraySize */, &writingPos);
+}
+
+// Write PtNode array terminal and actual PtNode array size.
+bool DynamicPatriciaTrieGcEventListeners::TraversePolicyToPlaceAndWriteValidPtNodesToBuffer
+        ::onReadingPtNodeArrayTail() {
+    int writingPos = mBufferToWrite->getTailPosition();
+    // Write PtNode array terminal.
+    if (!DynamicPatriciaTrieWritingUtils::writeForwardLinkPositionAndAdvancePosition(
+            mBufferToWrite, NOT_A_DICT_POS /* forwardLinkPos */, &writingPos)) {
+        return false;
+    }
+    // Write actual PtNode array size.
+    if (!DynamicPatriciaTrieWritingUtils::writePtNodeArraySizeAndAdvancePosition(
+            mBufferToWrite, mValidPtNodeCount, &mPtNodeArraySizeFieldPos)) {
+        return false;
+    }
+    return true;
+}
+
+// Write valid PtNode to buffer and memorize mapping from the old position to the new position.
+bool DynamicPatriciaTrieGcEventListeners::TraversePolicyToPlaceAndWriteValidPtNodesToBuffer
+        ::onVisitingPtNode(const DynamicPatriciaTrieNodeReader *const node,
+                const int *const nodeCodePoints) {
+    if (node->isDeleted()) {
+        // Current PtNode is not written in new buffer because it has been deleted.
+        mDictPositionRelocationMap->mPtNodePositionRelocationMap.insert(
+                DynamicPatriciaTrieWritingHelper::PtNodePositionRelocationMap::value_type(
+                        node->getHeadPos(), NOT_A_DICT_POS));
+        return true;
+    }
+    int writingPos = mBufferToWrite->getTailPosition();
+    mDictPositionRelocationMap->mPtNodePositionRelocationMap.insert(
+            DynamicPatriciaTrieWritingHelper::PtNodePositionRelocationMap::value_type(
+                    node->getHeadPos(), writingPos));
+    mValidPtNodeCount++;
+    // Writes current PtNode.
+    return mWritingHelper->writePtNodeToBufferByCopyingPtNodeInfo(mBufferToWrite, node,
+            node->getParentPos(), nodeCodePoints, node->getCodePointCount(),
+            node->getProbability(), &writingPos);
+}
+
+bool DynamicPatriciaTrieGcEventListeners::TraversePolicyToUpdateAllPositionFields
+        ::onVisitingPtNode(const DynamicPatriciaTrieNodeReader *const node,
+                const int *const nodeCodePoints) {
+    // Updates parent position.
+    int parentPos = node->getParentPos();
+    if (parentPos != NOT_A_DICT_POS) {
+        DynamicPatriciaTrieWritingHelper::PtNodePositionRelocationMap::const_iterator it =
+                mDictPositionRelocationMap->mPtNodePositionRelocationMap.find(parentPos);
+        if (it != mDictPositionRelocationMap->mPtNodePositionRelocationMap.end()) {
+            parentPos = it->second;
+        }
+    }
+    int writingPos = node->getHeadPos() + DynamicPatriciaTrieWritingUtils::NODE_FLAG_FIELD_SIZE;
+    // Write updated parent offset.
+    if (!DynamicPatriciaTrieWritingUtils::writeParentPosOffsetAndAdvancePosition(mBufferToWrite,
+            parentPos, node->getHeadPos(), &writingPos)) {
+        return false;
+    }
+
+    // Updates children position.
+    int childrenPos = node->getChildrenPos();
+    if (childrenPos != NOT_A_DICT_POS) {
+        DynamicPatriciaTrieWritingHelper::PtNodeArrayPositionRelocationMap::const_iterator it =
+                mDictPositionRelocationMap->mPtNodeArrayPositionRelocationMap.find(childrenPos);
+        if (it != mDictPositionRelocationMap->mPtNodeArrayPositionRelocationMap.end()) {
+            childrenPos = it->second;
+        }
+    }
+    writingPos = node->getChildrenPosFieldPos();
+    if (!DynamicPatriciaTrieWritingUtils::writeChildrenPositionAndAdvancePosition(mBufferToWrite,
+            childrenPos, &writingPos)) {
+        return false;
+    }
+
+    // Updates bigram target PtNode positions in the bigram list.
+    int bigramsPos = node->getBigramsPos();
+    if (bigramsPos != NOT_A_DICT_POS) {
+        if (!mBigramPolicy->updateAllBigramTargetPtNodePositions(&bigramsPos,
+                &mDictPositionRelocationMap->mPtNodePositionRelocationMap)) {
+            return false;
+        }
+    }
+
+    return true;
+}
+
+} // namespace latinime
diff --git a/native/jni/src/suggest/policyimpl/dictionary/dynamic_patricia_trie_gc_event_listeners.h b/native/jni/src/suggest/policyimpl/dictionary/dynamic_patricia_trie_gc_event_listeners.h
new file mode 100644
index 0000000..4256f22
--- /dev/null
+++ b/native/jni/src/suggest/policyimpl/dictionary/dynamic_patricia_trie_gc_event_listeners.h
@@ -0,0 +1,178 @@
+/*
+ * Copyright (C) 2013, 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.
+ */
+
+#ifndef LATINIME_DYNAMIC_PATRICIA_TRIE_GC_EVENT_LISTENERS_H
+#define LATINIME_DYNAMIC_PATRICIA_TRIE_GC_EVENT_LISTENERS_H
+
+#include <vector>
+
+#include "defines.h"
+#include "suggest/policyimpl/dictionary/bigram/dynamic_bigram_list_policy.h"
+#include "suggest/policyimpl/dictionary/dynamic_patricia_trie_reading_helper.h"
+#include "suggest/policyimpl/dictionary/dynamic_patricia_trie_writing_helper.h"
+#include "suggest/policyimpl/dictionary/dynamic_patricia_trie_writing_utils.h"
+#include "suggest/policyimpl/dictionary/utils/buffer_with_extendable_buffer.h"
+#include "utils/hash_map_compat.h"
+
+namespace latinime {
+
+class DynamicPatriciaTrieGcEventListeners {
+ public:
+    // Updates all PtNodes that can be reached from the root. Checks if each PtNode is useless or
+    // not and marks useless PtNodes as deleted. Such deleted PtNodes will be discarded in the GC.
+    // TODO: Concatenate non-terminal PtNodes.
+    class TraversePolicyToUpdateUnigramProbabilityAndMarkUselessPtNodesAsDeleted
+        : public DynamicPatriciaTrieReadingHelper::TraversingEventListener {
+     public:
+        TraversePolicyToUpdateUnigramProbabilityAndMarkUselessPtNodesAsDeleted(
+                DynamicPatriciaTrieWritingHelper *const writingHelper,
+                BufferWithExtendableBuffer *const buffer)
+                : mWritingHelper(writingHelper), mBuffer(buffer), valueStack(),
+                  mChildrenValue(0) {}
+
+        ~TraversePolicyToUpdateUnigramProbabilityAndMarkUselessPtNodesAsDeleted() {};
+
+        bool onAscend() {
+            if (valueStack.empty()) {
+                return false;
+            }
+            mChildrenValue = valueStack.back();
+            valueStack.pop_back();
+            return true;
+        }
+
+        bool onDescend(const int ptNodeArrayPos) {
+            valueStack.push_back(0);
+            return true;
+        }
+
+        bool onReadingPtNodeArrayTail() { return true; }
+
+        bool onVisitingPtNode(const DynamicPatriciaTrieNodeReader *const node,
+                const int *const nodeCodePoints);
+
+     private:
+        DISALLOW_IMPLICIT_CONSTRUCTORS(
+                TraversePolicyToUpdateUnigramProbabilityAndMarkUselessPtNodesAsDeleted);
+
+        DynamicPatriciaTrieWritingHelper *const mWritingHelper;
+        BufferWithExtendableBuffer *const mBuffer;
+        std::vector<int> valueStack;
+        int mChildrenValue;
+    };
+
+    // Updates all bigram entries that are held by valid PtNodes. This removes useless bigram
+    // entries.
+    class TraversePolicyToUpdateBigramProbability
+            : public DynamicPatriciaTrieReadingHelper::TraversingEventListener {
+     public:
+        TraversePolicyToUpdateBigramProbability(DynamicBigramListPolicy *const bigramPolicy)
+                : mBigramPolicy(bigramPolicy) {}
+
+        bool onAscend() { return true; }
+
+        bool onDescend(const int ptNodeArrayPos) { return true; }
+
+        bool onReadingPtNodeArrayTail() { return true; }
+
+        bool onVisitingPtNode(const DynamicPatriciaTrieNodeReader *const node,
+                const int *const nodeCodePoints) {
+            if (!node->isDeleted()) {
+                int pos = node->getBigramsPos();
+                if (pos != NOT_A_DICT_POS) {
+                    if (!mBigramPolicy->updateAllBigramEntriesAndDeleteUselessEntries(&pos)) {
+                        return false;
+                    }
+                }
+            }
+            return true;
+        }
+
+     private:
+        DISALLOW_IMPLICIT_CONSTRUCTORS(TraversePolicyToUpdateBigramProbability);
+
+        DynamicBigramListPolicy *const mBigramPolicy;
+    };
+
+    class TraversePolicyToPlaceAndWriteValidPtNodesToBuffer
+            : public DynamicPatriciaTrieReadingHelper::TraversingEventListener {
+     public:
+        TraversePolicyToPlaceAndWriteValidPtNodesToBuffer(
+                DynamicPatriciaTrieWritingHelper *const writingHelper,
+                BufferWithExtendableBuffer *const bufferToWrite,
+                DynamicPatriciaTrieWritingHelper::DictPositionRelocationMap *const
+                        dictPositionRelocationMap)
+                : mWritingHelper(writingHelper), mBufferToWrite(bufferToWrite),
+                  mDictPositionRelocationMap(dictPositionRelocationMap), mValidPtNodeCount(0),
+                  mPtNodeArraySizeFieldPos(NOT_A_DICT_POS) {};
+
+        bool onAscend() { return true; }
+
+        bool onDescend(const int ptNodeArrayPos);
+
+        bool onReadingPtNodeArrayTail();
+
+        bool onVisitingPtNode(const DynamicPatriciaTrieNodeReader *const node,
+                const int *const nodeCodePoints);
+
+     private:
+        DISALLOW_IMPLICIT_CONSTRUCTORS(TraversePolicyToPlaceAndWriteValidPtNodesToBuffer);
+
+        DynamicPatriciaTrieWritingHelper *const mWritingHelper;
+        BufferWithExtendableBuffer *const mBufferToWrite;
+        DynamicPatriciaTrieWritingHelper::DictPositionRelocationMap *const
+                mDictPositionRelocationMap;
+        int mValidPtNodeCount;
+        int mPtNodeArraySizeFieldPos;
+    };
+
+    class TraversePolicyToUpdateAllPositionFields
+            : public DynamicPatriciaTrieReadingHelper::TraversingEventListener {
+     public:
+        TraversePolicyToUpdateAllPositionFields(
+                DynamicPatriciaTrieWritingHelper *const writingHelper,
+                DynamicBigramListPolicy *const bigramPolicy,
+                BufferWithExtendableBuffer *const bufferToWrite,
+                const DynamicPatriciaTrieWritingHelper::DictPositionRelocationMap *const
+                        dictPositionRelocationMap)
+                : mWritingHelper(writingHelper), mBigramPolicy(bigramPolicy),
+                  mBufferToWrite(bufferToWrite),
+                  mDictPositionRelocationMap(dictPositionRelocationMap) {};
+
+        bool onAscend() { return true; }
+
+        bool onDescend(const int ptNodeArrayPos) { return true; }
+
+        bool onReadingPtNodeArrayTail() { return true; }
+
+        bool onVisitingPtNode(const DynamicPatriciaTrieNodeReader *const node,
+                const int *const nodeCodePoints);
+
+     private:
+        DISALLOW_IMPLICIT_CONSTRUCTORS(TraversePolicyToUpdateAllPositionFields);
+
+        DynamicPatriciaTrieWritingHelper *const mWritingHelper;
+        DynamicBigramListPolicy *const mBigramPolicy;
+        BufferWithExtendableBuffer *const mBufferToWrite;
+        const DynamicPatriciaTrieWritingHelper::DictPositionRelocationMap *const
+                mDictPositionRelocationMap;
+    };
+
+ private:
+    DISALLOW_IMPLICIT_CONSTRUCTORS(DynamicPatriciaTrieGcEventListeners);
+};
+} // namespace latinime
+#endif /* LATINIME_DYNAMIC_PATRICIA_TRIE_GC_EVENT_LISTENERS_H */
diff --git a/native/jni/src/suggest/policyimpl/dictionary/dynamic_patricia_trie_node_reader.cpp b/native/jni/src/suggest/policyimpl/dictionary/dynamic_patricia_trie_node_reader.cpp
index 7ac635a..456352c 100644
--- a/native/jni/src/suggest/policyimpl/dictionary/dynamic_patricia_trie_node_reader.cpp
+++ b/native/jni/src/suggest/policyimpl/dictionary/dynamic_patricia_trie_node_reader.cpp
@@ -16,61 +16,108 @@
 
 #include "suggest/policyimpl/dictionary/dynamic_patricia_trie_node_reader.h"
 
-#include "suggest/core/dictionary/binary_dictionary_info.h"
-#include "suggest/core/dictionary/binary_dictionary_terminal_attributes_reading_utils.h"
+#include "suggest/core/policy/dictionary_bigrams_structure_policy.h"
+#include "suggest/core/policy/dictionary_shortcuts_structure_policy.h"
 #include "suggest/policyimpl/dictionary/dynamic_patricia_trie_reading_utils.h"
+#include "suggest/policyimpl/dictionary/utils/buffer_with_extendable_buffer.h"
 
 namespace latinime {
 
-void DynamicPatriciaTrieNodeReader::fetchNodeInfoFromBufferAndProcessMovedNode(const int nodePos,
-        const int maxCodePointCount, int *const outCodePoints) {
-    const uint8_t *const dictRoot = mBinaryDictionaryInfo->getDictRoot();
-    int pos = nodePos;
-    mFlags = PatriciaTrieReadingUtils::getFlagsAndAdvancePosition(dictRoot, &pos);
-    const int parentPos =
-            DynamicPatriciaTrieReadingUtils::getParentPosAndAdvancePosition(dictRoot, &pos);
-    mParentPos = (parentPos != 0) ? mNodePos + parentPos : NOT_A_DICT_POS;
+void DynamicPatriciaTrieNodeReader::fetchPtNodeInfoFromBufferAndProcessMovedPtNode(
+        const int ptNodePos, const int maxCodePointCount, int *const outCodePoints) {
+    if (ptNodePos < 0 || ptNodePos >= mBuffer->getTailPosition()) {
+        AKLOGE("Fetching PtNode info form invalid dictionary position: %d, dictionary size: %d",
+                ptNodePos, mBuffer->getTailPosition());
+        ASSERT(false);
+        invalidatePtNodeInfo();
+        return;
+    }
+    const bool usesAdditionalBuffer = mBuffer->isInAdditionalBuffer(ptNodePos);
+    const uint8_t *const dictBuf = mBuffer->getBuffer(usesAdditionalBuffer);
+    int pos = ptNodePos;
+    mHeadPos = ptNodePos;
+    if (usesAdditionalBuffer) {
+        pos -= mBuffer->getOriginalBufferSize();
+    }
+    mFlags = PatriciaTrieReadingUtils::getFlagsAndAdvancePosition(dictBuf, &pos);
+    const int parentPosOffset =
+            DynamicPatriciaTrieReadingUtils::getParentPtNodePosOffsetAndAdvancePosition(dictBuf,
+                    &pos);
+    mParentPos = DynamicPatriciaTrieReadingUtils::getParentPtNodePos(parentPosOffset, mHeadPos);
     if (outCodePoints != 0) {
         mCodePointCount = PatriciaTrieReadingUtils::getCharsAndAdvancePosition(
-                dictRoot, mFlags, maxCodePointCount, outCodePoints, &pos);
+                dictBuf, mFlags, maxCodePointCount, outCodePoints, &pos);
     } else {
         mCodePointCount = PatriciaTrieReadingUtils::skipCharacters(
-                dictRoot, mFlags, MAX_WORD_LENGTH, &pos);
+                dictBuf, mFlags, MAX_WORD_LENGTH, &pos);
     }
     if (isTerminal()) {
-        mProbability = PatriciaTrieReadingUtils::readProbabilityAndAdvancePosition(dictRoot, &pos);
+        mProbabilityFieldPos = pos;
+        if (usesAdditionalBuffer) {
+            mProbabilityFieldPos += mBuffer->getOriginalBufferSize();
+        }
+        mProbability = PatriciaTrieReadingUtils::readProbabilityAndAdvancePosition(dictBuf, &pos);
     } else {
+        mProbabilityFieldPos = NOT_A_DICT_POS;
         mProbability = NOT_A_PROBABILITY;
     }
-    if (hasChildren()) {
-        mChildrenPos = DynamicPatriciaTrieReadingUtils::readChildrenPositionAndAdvancePosition(
-                dictRoot, mFlags, &pos);
-    } else {
-        mChildrenPos = NOT_A_DICT_POS;
+    mChildrenPosFieldPos = pos;
+    if (usesAdditionalBuffer) {
+        mChildrenPosFieldPos += mBuffer->getOriginalBufferSize();
+    }
+    mChildrenPos = DynamicPatriciaTrieReadingUtils::readChildrenPositionAndAdvancePosition(
+            dictBuf, &pos);
+    if (usesAdditionalBuffer && mChildrenPos != NOT_A_DICT_POS) {
+        mChildrenPos += mBuffer->getOriginalBufferSize();
+    }
+    if (mSiblingPos == NOT_A_DICT_POS) {
+        if (DynamicPatriciaTrieReadingUtils::isMoved(mFlags)) {
+            mBigramLinkedNodePos = mChildrenPos;
+        } else {
+            mBigramLinkedNodePos = NOT_A_DICT_POS;
+        }
+    }
+    if (usesAdditionalBuffer) {
+        pos += mBuffer->getOriginalBufferSize();
     }
     if (PatriciaTrieReadingUtils::hasShortcutTargets(mFlags)) {
         mShortcutPos = pos;
-        BinaryDictionaryTerminalAttributesReadingUtils::skipShortcuts(mBinaryDictionaryInfo, &pos);
+        mShortcutsPolicy->skipAllShortcuts(&pos);
     } else {
         mShortcutPos = NOT_A_DICT_POS;
     }
     if (PatriciaTrieReadingUtils::hasBigrams(mFlags)) {
         mBigramPos = pos;
-        BinaryDictionaryTerminalAttributesReadingUtils::skipExistingBigrams(
-                mBinaryDictionaryInfo, &pos);
+        mBigramsPolicy->skipAllBigrams(&pos);
     } else {
         mBigramPos = NOT_A_DICT_POS;
     }
     // Update siblingPos if needed.
-    if (mSiblingPos == NOT_A_VALID_WORD_POS) {
+    if (mSiblingPos == NOT_A_DICT_POS) {
         // Sibling position is the tail position of current node.
         mSiblingPos = pos;
     }
     // Read destination node if the read node is a moved node.
     if (DynamicPatriciaTrieReadingUtils::isMoved(mFlags)) {
         // The destination position is stored at the same place as the parent position.
-        fetchNodeInfoFromBufferAndProcessMovedNode(mParentPos, maxCodePointCount, outCodePoints);
+        fetchPtNodeInfoFromBufferAndProcessMovedPtNode(mParentPos, maxCodePointCount,
+                outCodePoints);
     }
 }
 
+void DynamicPatriciaTrieNodeReader::invalidatePtNodeInfo() {
+    mHeadPos = NOT_A_DICT_POS;
+    mFlags = 0;
+    mParentPos = NOT_A_DICT_POS;
+    mCodePointCount = 0;
+    mProbabilityFieldPos = NOT_A_DICT_POS;
+    mProbability = NOT_A_PROBABILITY;
+    mChildrenPosFieldPos = NOT_A_DICT_POS;
+    mChildrenPos = NOT_A_DICT_POS;
+    mBigramLinkedNodePos = NOT_A_DICT_POS;
+    mShortcutPos = NOT_A_DICT_POS;
+    mBigramPos = NOT_A_DICT_POS;
+    mSiblingPos = NOT_A_DICT_POS;
+}
+
 }
diff --git a/native/jni/src/suggest/policyimpl/dictionary/dynamic_patricia_trie_node_reader.h b/native/jni/src/suggest/policyimpl/dictionary/dynamic_patricia_trie_node_reader.h
index 71558ed..3b36d42 100644
--- a/native/jni/src/suggest/policyimpl/dictionary/dynamic_patricia_trie_node_reader.h
+++ b/native/jni/src/suggest/policyimpl/dictionary/dynamic_patricia_trie_node_reader.h
@@ -17,13 +17,17 @@
 #ifndef LATINIME_DYNAMIC_PATRICIA_TRIE_NODE_READER_H
 #define LATINIME_DYNAMIC_PATRICIA_TRIE_NODE_READER_H
 
+#include <stdint.h>
+
 #include "defines.h"
 #include "suggest/policyimpl/dictionary/dynamic_patricia_trie_reading_utils.h"
 #include "suggest/policyimpl/dictionary/patricia_trie_reading_utils.h"
 
 namespace latinime {
 
-class BinaryDictionaryInfo;
+class BufferWithExtendableBuffer;
+class DictionaryBigramsStructurePolicy;
+class DictionaryShortcutsStructurePolicy;
 
 /*
  * This class is used for helping to read nodes of dynamic patricia trie. This class handles moved
@@ -31,30 +35,35 @@
  */
 class DynamicPatriciaTrieNodeReader {
  public:
-    explicit DynamicPatriciaTrieNodeReader(const BinaryDictionaryInfo *const binaryDictionaryInfo)
-            : mBinaryDictionaryInfo(binaryDictionaryInfo), mNodePos(NOT_A_VALID_WORD_POS),
-              mFlags(0), mParentPos(NOT_A_DICT_POS), mCodePointCount(0),
-              mProbability(NOT_A_PROBABILITY), mChildrenPos(NOT_A_DICT_POS),
-              mShortcutPos(NOT_A_DICT_POS), mBigramPos(NOT_A_DICT_POS),
-              mSiblingPos(NOT_A_VALID_WORD_POS) {}
+    DynamicPatriciaTrieNodeReader(const BufferWithExtendableBuffer *const buffer,
+            const DictionaryBigramsStructurePolicy *const bigramsPolicy,
+            const DictionaryShortcutsStructurePolicy *const shortcutsPolicy)
+            : mBuffer(buffer), mBigramsPolicy(bigramsPolicy),
+              mShortcutsPolicy(shortcutsPolicy), mHeadPos(NOT_A_DICT_POS), mFlags(0),
+              mParentPos(NOT_A_DICT_POS), mCodePointCount(0), mProbabilityFieldPos(NOT_A_DICT_POS),
+              mProbability(NOT_A_PROBABILITY), mChildrenPosFieldPos(NOT_A_DICT_POS),
+              mChildrenPos(NOT_A_DICT_POS), mBigramLinkedNodePos(NOT_A_DICT_POS),
+              mShortcutPos(NOT_A_DICT_POS),  mBigramPos(NOT_A_DICT_POS),
+              mSiblingPos(NOT_A_DICT_POS) {}
 
     ~DynamicPatriciaTrieNodeReader() {}
 
-    // Reads node information from dictionary buffer and updates members with the information.
-    AK_FORCE_INLINE void fetchNodeInfoFromBuffer(const int nodePos) {
-        fetchNodeInfoFromBufferAndGetNodeCodePoints(nodePos , 0 /* maxCodePointCount */,
-                0 /* outCodePoints */);
+    // Reads PtNode information from dictionary buffer and updates members with the information.
+    AK_FORCE_INLINE void fetchNodeInfoInBufferFromPtNodePos(const int ptNodePos) {
+        fetchNodeInfoInBufferFromPtNodePosAndGetNodeCodePoints(ptNodePos ,
+                0 /* maxCodePointCount */, 0 /* outCodePoints */);
     }
 
-    AK_FORCE_INLINE void fetchNodeInfoFromBufferAndGetNodeCodePoints(const int nodePos,
-            const int maxCodePointCount, int *const outCodePoints) {
-        mNodePos = nodePos;
-        mSiblingPos = NOT_A_VALID_WORD_POS;
-        fetchNodeInfoFromBufferAndProcessMovedNode(mNodePos, maxCodePointCount, outCodePoints);
+    AK_FORCE_INLINE void fetchNodeInfoInBufferFromPtNodePosAndGetNodeCodePoints(
+            const int ptNodePos, const int maxCodePointCount, int *const outCodePoints) {
+        mSiblingPos = NOT_A_DICT_POS;
+        mBigramLinkedNodePos = NOT_A_DICT_POS;
+        fetchPtNodeInfoFromBufferAndProcessMovedPtNode(ptNodePos, maxCodePointCount, outCodePoints);
     }
 
-    AK_FORCE_INLINE int getNodePos() const {
-        return mNodePos;
+    // HeadPos is different from NodePos when the current PtNode is a moved PtNode.
+    AK_FORCE_INLINE int getHeadPos() const {
+        return mHeadPos;
     }
 
     // Flags
@@ -63,7 +72,7 @@
     }
 
     AK_FORCE_INLINE bool hasChildren() const {
-        return PatriciaTrieReadingUtils::hasChildrenInFlags(mFlags);
+        return mChildrenPos != NOT_A_DICT_POS;
     }
 
     AK_FORCE_INLINE bool isTerminal() const {
@@ -89,15 +98,28 @@
     }
 
     // Probability
+    AK_FORCE_INLINE int getProbabilityFieldPos() const {
+        return mProbabilityFieldPos;
+    }
+
     AK_FORCE_INLINE int getProbability() const {
         return mProbability;
     }
 
-    // Children node group position
+    // Children PtNode array position
+    AK_FORCE_INLINE int getChildrenPosFieldPos() const {
+        return mChildrenPosFieldPos;
+    }
+
     AK_FORCE_INLINE int getChildrenPos() const {
         return mChildrenPos;
     }
 
+    // Bigram linked node position.
+    AK_FORCE_INLINE int getBigramLinkedNodePos() const {
+        return mBigramLinkedNodePos;
+    }
+
     // Shortcutlist position
     AK_FORCE_INLINE int getShortcutPos() const {
         return mShortcutPos;
@@ -116,19 +138,26 @@
  private:
     DISALLOW_COPY_AND_ASSIGN(DynamicPatriciaTrieNodeReader);
 
-    const BinaryDictionaryInfo *const mBinaryDictionaryInfo;
-    int mNodePos;
+    const BufferWithExtendableBuffer *const mBuffer;
+    const DictionaryBigramsStructurePolicy *const mBigramsPolicy;
+    const DictionaryShortcutsStructurePolicy *const mShortcutsPolicy;
+    int mHeadPos;
     DynamicPatriciaTrieReadingUtils::NodeFlags mFlags;
     int mParentPos;
     uint8_t mCodePointCount;
+    int mProbabilityFieldPos;
     int mProbability;
+    int mChildrenPosFieldPos;
     int mChildrenPos;
+    int mBigramLinkedNodePos;
     int mShortcutPos;
     int mBigramPos;
     int mSiblingPos;
 
-    void fetchNodeInfoFromBufferAndProcessMovedNode(const int nodePos, const int maxCodePointCount,
-            int *const outCodePoints);
+    void fetchPtNodeInfoFromBufferAndProcessMovedPtNode(const int ptNodePos,
+            const int maxCodePointCount, int *const outCodePoints);
+
+    void invalidatePtNodeInfo();
 };
 } // namespace latinime
 #endif /* LATINIME_DYNAMIC_PATRICIA_TRIE_NODE_READER_H */
diff --git a/native/jni/src/suggest/policyimpl/dictionary/dynamic_patricia_trie_policy.cpp b/native/jni/src/suggest/policyimpl/dictionary/dynamic_patricia_trie_policy.cpp
index 3df5056..42397c1 100644
--- a/native/jni/src/suggest/policyimpl/dictionary/dynamic_patricia_trie_policy.cpp
+++ b/native/jni/src/suggest/policyimpl/dictionary/dynamic_patricia_trie_policy.cpp
@@ -19,221 +19,257 @@
 #include "defines.h"
 #include "suggest/core/dicnode/dic_node.h"
 #include "suggest/core/dicnode/dic_node_vector.h"
-#include "suggest/core/dictionary/binary_dictionary_info.h"
 #include "suggest/policyimpl/dictionary/dynamic_patricia_trie_node_reader.h"
+#include "suggest/policyimpl/dictionary/dynamic_patricia_trie_reading_helper.h"
 #include "suggest/policyimpl/dictionary/dynamic_patricia_trie_reading_utils.h"
+#include "suggest/policyimpl/dictionary/dynamic_patricia_trie_writing_helper.h"
 #include "suggest/policyimpl/dictionary/patricia_trie_reading_utils.h"
+#include "suggest/policyimpl/dictionary/utils/probability_utils.h"
 
 namespace latinime {
 
-const DynamicPatriciaTriePolicy DynamicPatriciaTriePolicy::sInstance;
-// To avoid infinite loop caused by invalid or malicious forward links.
-const int DynamicPatriciaTriePolicy::MAX_CHILD_COUNT_TO_AVOID_INFINITE_LOOP = 100000;
-
 void DynamicPatriciaTriePolicy::createAndGetAllChildNodes(const DicNode *const dicNode,
-        const BinaryDictionaryInfo *const binaryDictionaryInfo,
-        const NodeFilter *const nodeFilter, DicNodeVector *const childDicNodes) const {
+        DicNodeVector *const childDicNodes) const {
     if (!dicNode->hasChildren()) {
         return;
     }
-    DynamicPatriciaTrieNodeReader nodeReader(binaryDictionaryInfo);
-    int mergedNodeCodePoints[MAX_WORD_LENGTH];
-    int nextPos = dicNode->getChildrenPos();
-    int totalChildCount = 0;
-    do {
-        const int childCount = PatriciaTrieReadingUtils::getGroupCountAndAdvancePosition(
-                binaryDictionaryInfo->getDictRoot(), &nextPos);
-        totalChildCount += childCount;
-        if (childCount <= 0 || totalChildCount > MAX_CHILD_COUNT_TO_AVOID_INFINITE_LOOP) {
-            // Invalid dictionary.
-            AKLOGI("Invalid dictionary. childCount: %d, totalChildCount: %d, MAX: %d",
-                    childCount, totalChildCount, MAX_CHILD_COUNT_TO_AVOID_INFINITE_LOOP);
-            ASSERT(false);
-            return;
-        }
-        for (int i = 0; i < childCount; i++) {
-            nodeReader.fetchNodeInfoFromBufferAndGetNodeCodePoints(nextPos, MAX_WORD_LENGTH,
-                    mergedNodeCodePoints);
-            if (!nodeReader.isDeleted() && !nodeFilter->isFilteredOut(mergedNodeCodePoints[0])) {
-                // Push child node when the node is not deleted and not filtered out.
-                childDicNodes->pushLeavingChild(dicNode, nodeReader.getNodePos(),
-                        nodeReader.getChildrenPos(), nodeReader.getProbability(),
-                        nodeReader.isTerminal(), nodeReader.hasChildren(),
-                        nodeReader.isBlacklisted() || nodeReader.isNotAWord(),
-                        nodeReader.getCodePointCount(), mergedNodeCodePoints);
-            }
-            nextPos = nodeReader.getSiblingNodePos();
-        }
-        nextPos = DynamicPatriciaTrieReadingUtils::getForwardLinkPosition(
-                binaryDictionaryInfo->getDictRoot(), nextPos);
-    } while (DynamicPatriciaTrieReadingUtils::isValidForwardLinkPosition(nextPos));
+    DynamicPatriciaTrieReadingHelper readingHelper(&mBufferWithExtendableBuffer,
+            getBigramsStructurePolicy(), getShortcutsStructurePolicy());
+    readingHelper.initWithPtNodeArrayPos(dicNode->getChildrenPos());
+    const DynamicPatriciaTrieNodeReader *const nodeReader = readingHelper.getNodeReader();
+    while (!readingHelper.isEnd()) {
+        childDicNodes->pushLeavingChild(dicNode, nodeReader->getHeadPos(),
+                nodeReader->getChildrenPos(), nodeReader->getProbability(),
+                nodeReader->isTerminal() && !nodeReader->isDeleted(),
+                nodeReader->hasChildren(), nodeReader->isBlacklisted() || nodeReader->isNotAWord(),
+                nodeReader->getCodePointCount(), readingHelper.getMergedNodeCodePoints());
+        readingHelper.readNextSiblingNode();
+    }
 }
 
 int DynamicPatriciaTriePolicy::getCodePointsAndProbabilityAndReturnCodePointCount(
-        const BinaryDictionaryInfo *const binaryDictionaryInfo,
-        const int nodePos, const int maxCodePointCount, int *const outCodePoints,
+        const int ptNodePos, const int maxCodePointCount, int *const outCodePoints,
         int *const outUnigramProbability) const {
-    if (nodePos == NOT_A_VALID_WORD_POS) {
-        *outUnigramProbability = NOT_A_PROBABILITY;
-        return 0;
-    }
     // This method traverses parent nodes from the terminal by following parent pointers; thus,
     // node code points are stored in the buffer in the reverse order.
     int reverseCodePoints[maxCodePointCount];
-    int mergedNodeCodePoints[maxCodePointCount];
-    int codePointCount = 0;
-
-    DynamicPatriciaTrieNodeReader nodeReader(binaryDictionaryInfo);
-    // First, read terminal node and get its probability.
-    nodeReader.fetchNodeInfoFromBufferAndGetNodeCodePoints(nodePos, maxCodePointCount,
-            mergedNodeCodePoints);
-    // Store terminal node probability.
-    *outUnigramProbability = nodeReader.getProbability();
-    // Store terminal node code points to buffer in the reverse order.
-    for (int i = nodeReader.getCodePointCount() - 1; i >= 0; --i) {
-        reverseCodePoints[codePointCount++] = mergedNodeCodePoints[i];
+    DynamicPatriciaTrieReadingHelper readingHelper(&mBufferWithExtendableBuffer,
+            getBigramsStructurePolicy(), getShortcutsStructurePolicy());
+    // First, read the terminal node and get its probability.
+    readingHelper.initWithPtNodePos(ptNodePos);
+    if (!readingHelper.isValidTerminalNode()) {
+        // Node at the ptNodePos is not a valid terminal node.
+        *outUnigramProbability = NOT_A_PROBABILITY;
+        return 0;
     }
-    // Then, follow parent pos toward the root node.
-    while (nodeReader.getParentPos() != NOT_A_DICT_POS) {
-        // codePointCount must be incremented at least once in each iteration to ensure preventing
-        // infinite loop.
-        if (nodeReader.isDeleted() || codePointCount > maxCodePointCount
-                || nodeReader.getCodePointCount() <= 0) {
-            // The nodePos is not a valid terminal node position in the dictionary.
+    // Store terminal node probability.
+    *outUnigramProbability = readingHelper.getNodeReader()->getProbability();
+    // Then, following parent node link to the dictionary root and fetch node code points.
+    while (!readingHelper.isEnd()) {
+        if (readingHelper.getTotalCodePointCount() > maxCodePointCount) {
+            // The ptNodePos is not a valid terminal node position in the dictionary.
             *outUnigramProbability = NOT_A_PROBABILITY;
             return 0;
         }
-        // Read parent node.
-        nodeReader.fetchNodeInfoFromBufferAndGetNodeCodePoints(nodeReader.getParentPos(),
-                maxCodePointCount, mergedNodeCodePoints);
         // Store node code points to buffer in the reverse order.
-        for (int i = nodeReader.getCodePointCount() - 1; i >= 0; --i) {
-            reverseCodePoints[codePointCount++] = mergedNodeCodePoints[i];
-        }
+        readingHelper.fetchMergedNodeCodePointsInReverseOrder(
+                readingHelper.getPrevTotalCodePointCount(), reverseCodePoints);
+        // Follow parent node toward the root node.
+        readingHelper.readParentNode();
+    }
+    if (readingHelper.isError()) {
+        // The node position or the dictionary is invalid.
+        *outUnigramProbability = NOT_A_PROBABILITY;
+        return 0;
     }
     // Reverse the stored code points to output them.
+    const int codePointCount = readingHelper.getTotalCodePointCount();
     for (int i = 0; i < codePointCount; ++i) {
         outCodePoints[i] = reverseCodePoints[codePointCount - i - 1];
     }
     return codePointCount;
 }
 
-int DynamicPatriciaTriePolicy::getTerminalNodePositionOfWord(
-        const BinaryDictionaryInfo *const binaryDictionaryInfo, const int *const inWord,
+int DynamicPatriciaTriePolicy::getTerminalNodePositionOfWord(const int *const inWord,
         const int length, const bool forceLowerCaseSearch) const {
     int searchCodePoints[length];
     for (int i = 0; i < length; ++i) {
         searchCodePoints[i] = forceLowerCaseSearch ? CharUtils::toLowerCase(inWord[i]) : inWord[i];
     }
-    int mergedNodeCodePoints[MAX_WORD_LENGTH];
-    int currentLength = 0;
-    int pos = getRootPosition();
-    DynamicPatriciaTrieNodeReader nodeReader(binaryDictionaryInfo);
-    while (currentLength <= length) {
-        // When foundMatchedNode becomes true, currentLength is increased at least once.
-        bool foundMatchedNode = false;
-        int totalChildCount = 0;
-        do {
-            const int childCount = PatriciaTrieReadingUtils::getGroupCountAndAdvancePosition(
-                    binaryDictionaryInfo->getDictRoot(), &pos);
-            totalChildCount += childCount;
-            if (childCount <= 0 || totalChildCount > MAX_CHILD_COUNT_TO_AVOID_INFINITE_LOOP) {
-                // Invalid dictionary.
-                AKLOGI("Invalid dictionary. childCount: %d, totalChildCount: %d, MAX: %d",
-                        childCount, totalChildCount, MAX_CHILD_COUNT_TO_AVOID_INFINITE_LOOP);
-                ASSERT(false);
-                return NOT_A_VALID_WORD_POS;
-            }
-            for (int i = 0; i < childCount; i++) {
-                nodeReader.fetchNodeInfoFromBufferAndGetNodeCodePoints(pos, MAX_WORD_LENGTH,
-                        mergedNodeCodePoints);
-                if (nodeReader.isDeleted() || nodeReader.getCodePointCount() <= 0) {
-                    // Skip deleted or empty node.
-                    pos = nodeReader.getSiblingNodePos();
-                    continue;
-                }
-                bool matched = true;
-                for (int j = 0; j < nodeReader.getCodePointCount(); ++j) {
-                    if (mergedNodeCodePoints[j] != searchCodePoints[currentLength + j]) {
-                        // Different code point is found.
-                        matched = false;
-                        break;
-                    }
-                }
-                if (matched) {
-                    currentLength += nodeReader.getCodePointCount();
-                    if (length == currentLength) {
-                        // Terminal position is found.
-                        return nodeReader.getNodePos();
-                    }
-                    if (!nodeReader.hasChildren()) {
-                        return NOT_A_VALID_WORD_POS;
-                    }
-                    foundMatchedNode = true;
-                    // Advance to the children nodes.
-                    pos = nodeReader.getChildrenPos();
-                    break;
-                }
-                // Try next sibling node.
-                pos = nodeReader.getSiblingNodePos();
-            }
-            if (foundMatchedNode) {
-                break;
-            }
-            // If the matched node is not found in the current node group, try to follow the
-            // forward link.
-            pos = DynamicPatriciaTrieReadingUtils::getForwardLinkPosition(
-                    binaryDictionaryInfo->getDictRoot(), pos);
-        } while (DynamicPatriciaTrieReadingUtils::isValidForwardLinkPosition(pos));
-        if (!foundMatchedNode) {
-            // Matched node is not found.
-            return NOT_A_VALID_WORD_POS;
+    DynamicPatriciaTrieReadingHelper readingHelper(&mBufferWithExtendableBuffer,
+            getBigramsStructurePolicy(), getShortcutsStructurePolicy());
+    readingHelper.initWithPtNodeArrayPos(getRootPosition());
+    const DynamicPatriciaTrieNodeReader *const nodeReader = readingHelper.getNodeReader();
+    while (!readingHelper.isEnd()) {
+        const int matchedCodePointCount = readingHelper.getPrevTotalCodePointCount();
+        if (readingHelper.getTotalCodePointCount() > length
+                || !readingHelper.isMatchedCodePoint(0 /* index */,
+                        searchCodePoints[matchedCodePointCount])) {
+            // Current node has too many code points or its first code point is different from
+            // target code point. Skip this node and read the next sibling node.
+            readingHelper.readNextSiblingNode();
+            continue;
         }
+        // Check following merged node code points.
+        const int nodeCodePointCount = nodeReader->getCodePointCount();
+        for (int j = 1; j < nodeCodePointCount; ++j) {
+            if (!readingHelper.isMatchedCodePoint(
+                    j, searchCodePoints[matchedCodePointCount + j])) {
+                // Different code point is found. The given word is not included in the dictionary.
+                return NOT_A_DICT_POS;
+            }
+        }
+        // All characters are matched.
+        if (length == readingHelper.getTotalCodePointCount()) {
+            // Terminal position is found.
+            return nodeReader->getHeadPos();
+        }
+        if (!nodeReader->hasChildren()) {
+            return NOT_A_DICT_POS;
+        }
+        // Advance to the children nodes.
+        readingHelper.readChildNode();
     }
     // If we already traversed the tree further than the word is long, there means
     // there was no match (or we would have found it).
-    return NOT_A_VALID_WORD_POS;
+    return NOT_A_DICT_POS;
 }
 
-int DynamicPatriciaTriePolicy::getUnigramProbability(
-        const BinaryDictionaryInfo *const binaryDictionaryInfo, const int nodePos) const {
-    if (nodePos == NOT_A_VALID_WORD_POS) {
+int DynamicPatriciaTriePolicy::getProbability(const int unigramProbability,
+        const int bigramProbability) const {
+    // TODO: check mHeaderPolicy.usesForgettingCurve();
+    if (unigramProbability == NOT_A_PROBABILITY) {
+        return NOT_A_PROBABILITY;
+    } else if (bigramProbability == NOT_A_PROBABILITY) {
+        return ProbabilityUtils::backoff(unigramProbability);
+    } else {
+        return ProbabilityUtils::computeProbabilityForBigram(unigramProbability,
+                bigramProbability);
+    }
+}
+
+int DynamicPatriciaTriePolicy::getUnigramProbabilityOfPtNode(const int ptNodePos) const {
+    if (ptNodePos == NOT_A_DICT_POS) {
         return NOT_A_PROBABILITY;
     }
-    DynamicPatriciaTrieNodeReader nodeReader(binaryDictionaryInfo);
-    nodeReader.fetchNodeInfoFromBuffer(nodePos);
+    DynamicPatriciaTrieNodeReader nodeReader(&mBufferWithExtendableBuffer,
+            getBigramsStructurePolicy(), getShortcutsStructurePolicy());
+    nodeReader.fetchNodeInfoInBufferFromPtNodePos(ptNodePos);
     if (nodeReader.isDeleted() || nodeReader.isBlacklisted() || nodeReader.isNotAWord()) {
         return NOT_A_PROBABILITY;
     }
-    return nodeReader.getProbability();
+    return getProbability(nodeReader.getProbability(), NOT_A_PROBABILITY);
 }
 
-int DynamicPatriciaTriePolicy::getShortcutPositionOfNode(
-        const BinaryDictionaryInfo *const binaryDictionaryInfo,
-        const int nodePos) const {
-    if (nodePos == NOT_A_VALID_WORD_POS) {
+int DynamicPatriciaTriePolicy::getShortcutPositionOfPtNode(const int ptNodePos) const {
+    if (ptNodePos == NOT_A_DICT_POS) {
         return NOT_A_DICT_POS;
     }
-    DynamicPatriciaTrieNodeReader nodeReader(binaryDictionaryInfo);
-    nodeReader.fetchNodeInfoFromBuffer(nodePos);
+    DynamicPatriciaTrieNodeReader nodeReader(&mBufferWithExtendableBuffer,
+            getBigramsStructurePolicy(), getShortcutsStructurePolicy());
+    nodeReader.fetchNodeInfoInBufferFromPtNodePos(ptNodePos);
     if (nodeReader.isDeleted()) {
         return NOT_A_DICT_POS;
     }
     return nodeReader.getShortcutPos();
 }
 
-int DynamicPatriciaTriePolicy::getBigramsPositionOfNode(
-        const BinaryDictionaryInfo *const binaryDictionaryInfo,
-        const int nodePos) const {
-    if (nodePos == NOT_A_VALID_WORD_POS) {
+int DynamicPatriciaTriePolicy::getBigramsPositionOfPtNode(const int ptNodePos) const {
+    if (ptNodePos == NOT_A_DICT_POS) {
         return NOT_A_DICT_POS;
     }
-    DynamicPatriciaTrieNodeReader nodeReader(binaryDictionaryInfo);
-    nodeReader.fetchNodeInfoFromBuffer(nodePos);
+    DynamicPatriciaTrieNodeReader nodeReader(&mBufferWithExtendableBuffer,
+            getBigramsStructurePolicy(), getShortcutsStructurePolicy());
+    nodeReader.fetchNodeInfoInBufferFromPtNodePos(ptNodePos);
     if (nodeReader.isDeleted()) {
         return NOT_A_DICT_POS;
     }
     return nodeReader.getBigramsPos();
 }
 
+bool DynamicPatriciaTriePolicy::addUnigramWord(const int *const word, const int length,
+        const int probability) {
+    if (!mBuffer->isUpdatable()) {
+        AKLOGI("Warning: addUnigramWord() is called for non-updatable dictionary.");
+        return false;
+    }
+    DynamicPatriciaTrieReadingHelper readingHelper(&mBufferWithExtendableBuffer,
+            getBigramsStructurePolicy(), getShortcutsStructurePolicy());
+    readingHelper.initWithPtNodeArrayPos(getRootPosition());
+    DynamicPatriciaTrieWritingHelper writingHelper(&mBufferWithExtendableBuffer,
+            &mBigramListPolicy, &mShortcutListPolicy);
+    return writingHelper.addUnigramWord(&readingHelper, word, length, probability);
+}
+
+bool DynamicPatriciaTriePolicy::addBigramWords(const int *const word0, const int length0,
+        const int *const word1, const int length1, const int probability) {
+    if (!mBuffer->isUpdatable()) {
+        AKLOGI("Warning: addBigramWords() is called for non-updatable dictionary.");
+        return false;
+    }
+    const int word0Pos = getTerminalNodePositionOfWord(word0, length0,
+            false /* forceLowerCaseSearch */);
+    if (word0Pos == NOT_A_DICT_POS) {
+        return false;
+    }
+    const int word1Pos = getTerminalNodePositionOfWord(word1, length1,
+            false /* forceLowerCaseSearch */);
+    if (word1Pos == NOT_A_DICT_POS) {
+        return false;
+    }
+    DynamicPatriciaTrieWritingHelper writingHelper(&mBufferWithExtendableBuffer,
+            &mBigramListPolicy, &mShortcutListPolicy);
+    return writingHelper.addBigramWords(word0Pos, word1Pos, probability);
+}
+
+bool DynamicPatriciaTriePolicy::removeBigramWords(const int *const word0, const int length0,
+        const int *const word1, const int length1) {
+    if (!mBuffer->isUpdatable()) {
+        AKLOGI("Warning: removeBigramWords() is called for non-updatable dictionary.");
+        return false;
+    }
+    const int word0Pos = getTerminalNodePositionOfWord(word0, length0,
+            false /* forceLowerCaseSearch */);
+    if (word0Pos == NOT_A_DICT_POS) {
+        return false;
+    }
+    const int word1Pos = getTerminalNodePositionOfWord(word1, length1,
+            false /* forceLowerCaseSearch */);
+    if (word1Pos == NOT_A_DICT_POS) {
+        return false;
+    }
+    DynamicPatriciaTrieWritingHelper writingHelper(&mBufferWithExtendableBuffer,
+            &mBigramListPolicy, &mShortcutListPolicy);
+    return writingHelper.removeBigramWords(word0Pos, word1Pos);
+}
+
+void DynamicPatriciaTriePolicy::flush(const char *const filePath) {
+    if (!mBuffer->isUpdatable()) {
+        AKLOGI("Warning: flush() is called for non-updatable dictionary.");
+        return;
+    }
+    DynamicPatriciaTrieWritingHelper writingHelper(&mBufferWithExtendableBuffer,
+            &mBigramListPolicy, &mShortcutListPolicy);
+    writingHelper.writeToDictFile(filePath, &mHeaderPolicy);
+}
+
+void DynamicPatriciaTriePolicy::flushWithGC(const char *const filePath) {
+    if (!mBuffer->isUpdatable()) {
+        AKLOGI("Warning: flushWithGC() is called for non-updatable dictionary.");
+        return;
+    }
+    DynamicPatriciaTrieWritingHelper writingHelper(&mBufferWithExtendableBuffer,
+            &mBigramListPolicy, &mShortcutListPolicy);
+    writingHelper.writeToDictFileWithGC(getRootPosition(), filePath, &mHeaderPolicy);
+}
+
+bool DynamicPatriciaTriePolicy::needsToRunGC() const {
+    if (!mBuffer->isUpdatable()) {
+        AKLOGI("Warning: needsToRunGC() is called for non-updatable dictionary.");
+        return false;
+    }
+    // TODO: Implement more properly.
+    return mBufferWithExtendableBuffer.isNearSizeLimit();
+}
+
 } // namespace latinime
diff --git a/native/jni/src/suggest/policyimpl/dictionary/dynamic_patricia_trie_policy.h b/native/jni/src/suggest/policyimpl/dictionary/dynamic_patricia_trie_policy.h
index 6a79771..06d8095 100644
--- a/native/jni/src/suggest/policyimpl/dictionary/dynamic_patricia_trie_policy.h
+++ b/native/jni/src/suggest/policyimpl/dictionary/dynamic_patricia_trie_policy.h
@@ -18,18 +18,29 @@
 #define LATINIME_DYNAMIC_PATRICIA_TRIE_POLICY_H
 
 #include "defines.h"
-#include "suggest/core/policy/dictionary_structure_policy.h"
+#include "suggest/core/policy/dictionary_structure_with_buffer_policy.h"
+#include "suggest/policyimpl/dictionary/bigram/dynamic_bigram_list_policy.h"
+#include "suggest/policyimpl/dictionary/header/header_policy.h"
+#include "suggest/policyimpl/dictionary/shortcut/dynamic_shortcut_list_policy.h"
+#include "suggest/policyimpl/dictionary/utils/buffer_with_extendable_buffer.h"
+#include "suggest/policyimpl/dictionary/utils/mmapped_buffer.h"
 
 namespace latinime {
 
-class BinaryDictionaryInfo;
 class DicNode;
 class DicNodeVector;
 
-class DynamicPatriciaTriePolicy : public DictionaryStructurePolicy {
+class DynamicPatriciaTriePolicy : public DictionaryStructureWithBufferPolicy {
  public:
-    static AK_FORCE_INLINE const DynamicPatriciaTriePolicy *getInstance() {
-        return &sInstance;
+    DynamicPatriciaTriePolicy(const MmappedBuffer *const buffer)
+            : mBuffer(buffer), mHeaderPolicy(mBuffer->getBuffer(), buffer->getBufferSize()),
+              mBufferWithExtendableBuffer(mBuffer->getBuffer() + mHeaderPolicy.getSize(),
+                      mBuffer->getBufferSize() - mHeaderPolicy.getSize()),
+              mShortcutListPolicy(&mBufferWithExtendableBuffer),
+              mBigramListPolicy(&mBufferWithExtendableBuffer, &mShortcutListPolicy) {}
+
+    ~DynamicPatriciaTriePolicy() {
+        delete mBuffer;
     }
 
     AK_FORCE_INLINE int getRootPosition() const {
@@ -37,34 +48,57 @@
     }
 
     void createAndGetAllChildNodes(const DicNode *const dicNode,
-            const BinaryDictionaryInfo *const binaryDictionaryInfo,
-            const NodeFilter *const nodeFilter, DicNodeVector *const childDicNodes) const;
+            DicNodeVector *const childDicNodes) const;
 
     int getCodePointsAndProbabilityAndReturnCodePointCount(
-            const BinaryDictionaryInfo *const binaryDictionaryInfo,
-            const int terminalNodePos, const int maxCodePointCount, int *const outCodePoints,
+            const int terminalPtNodePos, const int maxCodePointCount, int *const outCodePoints,
             int *const outUnigramProbability) const;
 
-    int getTerminalNodePositionOfWord(
-            const BinaryDictionaryInfo *const binaryDictionaryInfo, const int *const inWord,
+    int getTerminalNodePositionOfWord(const int *const inWord,
             const int length, const bool forceLowerCaseSearch) const;
 
-    int getUnigramProbability(const BinaryDictionaryInfo *const binaryDictionaryInfo,
-            const int nodePos) const;
+    int getProbability(const int unigramProbability, const int bigramProbability) const;
 
-    int getShortcutPositionOfNode(const BinaryDictionaryInfo *const binaryDictionaryInfo,
-            const int nodePos) const;
+    int getUnigramProbabilityOfPtNode(const int ptNodePos) const;
 
-    int getBigramsPositionOfNode(const BinaryDictionaryInfo *const binaryDictionaryInfo,
-            const int nodePos) const;
+    int getShortcutPositionOfPtNode(const int ptNodePos) const;
+
+    int getBigramsPositionOfPtNode(const int ptNodePos) const;
+
+    const DictionaryHeaderStructurePolicy *getHeaderStructurePolicy() const {
+        return &mHeaderPolicy;
+    }
+
+    const DictionaryBigramsStructurePolicy *getBigramsStructurePolicy() const {
+        return &mBigramListPolicy;
+    }
+
+    const DictionaryShortcutsStructurePolicy *getShortcutsStructurePolicy() const {
+        return &mShortcutListPolicy;
+    }
+
+    bool addUnigramWord(const int *const word, const int length, const int probability);
+
+    bool addBigramWords(const int *const word0, const int length0, const int *const word1,
+            const int length1, const int probability);
+
+    bool removeBigramWords(const int *const word0, const int length0, const int *const word1,
+            const int length1);
+
+    void flush(const char *const filePath);
+
+    void flushWithGC(const char *const filePath);
+
+    bool needsToRunGC() const;
 
  private:
-    DISALLOW_COPY_AND_ASSIGN(DynamicPatriciaTriePolicy);
-    static const DynamicPatriciaTriePolicy sInstance;
-    static const int MAX_CHILD_COUNT_TO_AVOID_INFINITE_LOOP;
+    DISALLOW_IMPLICIT_CONSTRUCTORS(DynamicPatriciaTriePolicy);
 
-    DynamicPatriciaTriePolicy() {}
-    ~DynamicPatriciaTriePolicy() {}
+    const MmappedBuffer *const mBuffer;
+    const HeaderPolicy mHeaderPolicy;
+    BufferWithExtendableBuffer mBufferWithExtendableBuffer;
+    DynamicShortcutListPolicy mShortcutListPolicy;
+    DynamicBigramListPolicy mBigramListPolicy;
 };
 } // namespace latinime
 #endif // LATINIME_DYNAMIC_PATRICIA_TRIE_POLICY_H
diff --git a/native/jni/src/suggest/policyimpl/dictionary/dynamic_patricia_trie_reading_helper.cpp b/native/jni/src/suggest/policyimpl/dictionary/dynamic_patricia_trie_reading_helper.cpp
new file mode 100644
index 0000000..f4a2ef3
--- /dev/null
+++ b/native/jni/src/suggest/policyimpl/dictionary/dynamic_patricia_trie_reading_helper.cpp
@@ -0,0 +1,215 @@
+/*
+ * Copyright (C) 2013, 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.
+ */
+
+#include "suggest/policyimpl/dictionary/dynamic_patricia_trie_reading_helper.h"
+
+#include "suggest/policyimpl/dictionary/utils/buffer_with_extendable_buffer.h"
+
+namespace latinime {
+
+// To avoid infinite loop caused by invalid or malicious forward links.
+const int DynamicPatriciaTrieReadingHelper::MAX_CHILD_COUNT_TO_AVOID_INFINITE_LOOP = 100000;
+const int DynamicPatriciaTrieReadingHelper::MAX_NODE_ARRAY_COUNT_TO_AVOID_INFINITE_LOOP = 100000;
+const size_t DynamicPatriciaTrieReadingHelper::MAX_READING_STATE_STACK_SIZE = MAX_WORD_LENGTH;
+
+// Visits all PtNodes in post-order depth first manner.
+// For example, visits c -> b -> y -> x -> a for the following dictionary:
+// a _ b _ c
+//   \ x _ y
+bool DynamicPatriciaTrieReadingHelper::traverseAllPtNodesInPostorderDepthFirstManner(
+        TraversingEventListener *const listener) {
+    bool alreadyVisitedChildren = false;
+    // Descend from the root to the root PtNode array.
+    if (!listener->onDescend(getPosOfLastPtNodeArrayHead())) {
+        return false;
+    }
+    while (!isEnd()) {
+        if (!alreadyVisitedChildren) {
+            if (mNodeReader.hasChildren()) {
+                // Move to the first child.
+                if (!listener->onDescend(mNodeReader.getChildrenPos())) {
+                    return false;
+                }
+                pushReadingStateToStack();
+                readChildNode();
+            } else {
+                alreadyVisitedChildren = true;
+            }
+        } else {
+            if (!listener->onVisitingPtNode(&mNodeReader, mMergedNodeCodePoints)) {
+                return false;
+            }
+            readNextSiblingNode();
+            if (isEnd()) {
+                // All PtNodes in current linked PtNode arrays have been visited.
+                // Return to the parent.
+                if (!listener->onReadingPtNodeArrayTail()) {
+                    return false;
+                }
+                if (mReadingStateStack.size() <= 0) {
+                    break;
+                }
+                if (!listener->onAscend()) {
+                    return false;
+                }
+                popReadingStateFromStack();
+                alreadyVisitedChildren = true;
+            } else {
+                // Process sibling PtNode.
+                alreadyVisitedChildren = false;
+            }
+        }
+    }
+    // Ascend from the root PtNode array to the root.
+    if (!listener->onAscend()) {
+        return false;
+    }
+    return !isError();
+}
+
+// Visits all PtNodes in PtNode array level pre-order depth first manner, which is the same order
+// that PtNodes are written in the dictionary buffer.
+// For example, visits a -> b -> x -> c -> y for the following dictionary:
+// a _ b _ c
+//   \ x _ y
+bool DynamicPatriciaTrieReadingHelper::traverseAllPtNodesInPtNodeArrayLevelPreorderDepthFirstManner(
+        TraversingEventListener *const listener) {
+    bool alreadyVisitedAllPtNodesInArray = false;
+    bool alreadyVisitedChildren = false;
+    // Descend from the root to the root PtNode array.
+    if (!listener->onDescend(getPosOfLastPtNodeArrayHead())) {
+        return false;
+    }
+    pushReadingStateToStack();
+    while (!isEnd()) {
+        if (alreadyVisitedAllPtNodesInArray) {
+            if (alreadyVisitedChildren) {
+                // Move to next sibling PtNode's children.
+                readNextSiblingNode();
+                if (isEnd()) {
+                    // Return to the parent PTNode.
+                    if (!listener->onAscend()) {
+                        return false;
+                    }
+                    if (mReadingStateStack.size() <= 0) {
+                        break;
+                    }
+                    popReadingStateFromStack();
+                    alreadyVisitedChildren = true;
+                    alreadyVisitedAllPtNodesInArray = true;
+                } else {
+                    alreadyVisitedChildren = false;
+                }
+            } else {
+                if (mNodeReader.hasChildren()) {
+                    // Move to the first child.
+                    if (!listener->onDescend(mNodeReader.getChildrenPos())) {
+                        return false;
+                    }
+                    pushReadingStateToStack();
+                    readChildNode();
+                    // Push state to return the head of PtNode array.
+                    pushReadingStateToStack();
+                    alreadyVisitedAllPtNodesInArray = false;
+                    alreadyVisitedChildren = false;
+                } else {
+                    alreadyVisitedChildren = true;
+                }
+            }
+        } else {
+            if (!listener->onVisitingPtNode(&mNodeReader, mMergedNodeCodePoints)) {
+                return false;
+            }
+            readNextSiblingNode();
+            if (isEnd()) {
+                if (!listener->onReadingPtNodeArrayTail()) {
+                    return false;
+                }
+                // Return to the head of current PtNode array.
+                popReadingStateFromStack();
+                alreadyVisitedAllPtNodesInArray = true;
+            }
+        }
+    }
+    popReadingStateFromStack();
+    // Ascend from the root PtNode array to the root.
+    if (!listener->onAscend()) {
+        return false;
+    }
+    return !isError();
+}
+
+// Read node array size and process empty node arrays. Nodes and arrays are counted up in this
+// method to avoid an infinite loop.
+void DynamicPatriciaTrieReadingHelper::nextPtNodeArray() {
+    mReadingState.mPosOfLastPtNodeArrayHead = mReadingState.mPos;
+    const bool usesAdditionalBuffer = mBuffer->isInAdditionalBuffer(mReadingState.mPos);
+    const uint8_t *const dictBuf = mBuffer->getBuffer(usesAdditionalBuffer);
+    if (usesAdditionalBuffer) {
+        mReadingState.mPos -= mBuffer->getOriginalBufferSize();
+    }
+    mReadingState.mNodeCount = PatriciaTrieReadingUtils::getPtNodeArraySizeAndAdvancePosition(
+            dictBuf, &mReadingState.mPos);
+    if (usesAdditionalBuffer) {
+        mReadingState.mPos += mBuffer->getOriginalBufferSize();
+    }
+    // Count up nodes and node arrays to avoid infinite loop.
+    mReadingState.mTotalNodeCount += mReadingState.mNodeCount;
+    mReadingState.mNodeArrayCount++;
+    if (mReadingState.mNodeCount < 0
+            || mReadingState.mTotalNodeCount > MAX_CHILD_COUNT_TO_AVOID_INFINITE_LOOP
+            || mReadingState.mNodeArrayCount > MAX_NODE_ARRAY_COUNT_TO_AVOID_INFINITE_LOOP) {
+        // Invalid dictionary.
+        AKLOGI("Invalid dictionary. nodeCount: %d, totalNodeCount: %d, MAX_CHILD_COUNT: %d"
+                "nodeArrayCount: %d, MAX_NODE_ARRAY_COUNT: %d",
+                mReadingState.mNodeCount, mReadingState.mTotalNodeCount,
+                MAX_CHILD_COUNT_TO_AVOID_INFINITE_LOOP, mReadingState.mNodeArrayCount,
+                MAX_NODE_ARRAY_COUNT_TO_AVOID_INFINITE_LOOP);
+        ASSERT(false);
+        mIsError = true;
+        mReadingState.mPos = NOT_A_DICT_POS;
+        return;
+    }
+    if (mReadingState.mNodeCount == 0) {
+        // Empty node array. Try following forward link.
+        followForwardLink();
+    }
+}
+
+// Follow the forward link and read the next node array if exists.
+void DynamicPatriciaTrieReadingHelper::followForwardLink() {
+    const bool usesAdditionalBuffer = mBuffer->isInAdditionalBuffer(mReadingState.mPos);
+    const uint8_t *const dictBuf = mBuffer->getBuffer(usesAdditionalBuffer);
+    if (usesAdditionalBuffer) {
+        mReadingState.mPos -= mBuffer->getOriginalBufferSize();
+    }
+    const int forwardLinkPosition =
+            DynamicPatriciaTrieReadingUtils::getForwardLinkPosition(dictBuf, mReadingState.mPos);
+    if (usesAdditionalBuffer) {
+        mReadingState.mPos += mBuffer->getOriginalBufferSize();
+    }
+    mReadingState.mPosOfLastForwardLinkField = mReadingState.mPos;
+    if (DynamicPatriciaTrieReadingUtils::isValidForwardLinkPosition(forwardLinkPosition)) {
+        // Follow the forward link.
+        mReadingState.mPos += forwardLinkPosition;
+        nextPtNodeArray();
+    } else {
+        // All node arrays have been read.
+        mReadingState.mPos = NOT_A_DICT_POS;
+    }
+}
+
+} // namespace latinime
diff --git a/native/jni/src/suggest/policyimpl/dictionary/dynamic_patricia_trie_reading_helper.h b/native/jni/src/suggest/policyimpl/dictionary/dynamic_patricia_trie_reading_helper.h
new file mode 100644
index 0000000..b033eee
--- /dev/null
+++ b/native/jni/src/suggest/policyimpl/dictionary/dynamic_patricia_trie_reading_helper.h
@@ -0,0 +1,286 @@
+/*
+ * Copyright (C) 2013, 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.
+ */
+
+#ifndef LATINIME_DYNAMIC_PATRICIA_TRIE_READING_HELPER_H
+#define LATINIME_DYNAMIC_PATRICIA_TRIE_READING_HELPER_H
+
+#include <cstddef>
+#include <vector>
+
+#include "defines.h"
+#include "suggest/policyimpl/dictionary/dynamic_patricia_trie_node_reader.h"
+#include "suggest/policyimpl/dictionary/dynamic_patricia_trie_reading_utils.h"
+#include "suggest/policyimpl/dictionary/patricia_trie_reading_utils.h"
+
+namespace latinime {
+
+class BufferWithExtendableBuffer;
+class DictionaryBigramsStructurePolicy;
+class DictionaryShortcutsStructurePolicy;
+
+/*
+ * This class is used for traversing dynamic patricia trie. This class supports iterating nodes and
+ * dealing with additional buffer. This class counts nodes and node arrays to avoid infinite loop.
+ */
+class DynamicPatriciaTrieReadingHelper {
+ public:
+    class TraversingEventListener {
+     public:
+        virtual ~TraversingEventListener() {};
+
+        // Returns whether the event handling was succeeded or not.
+        virtual bool onAscend() = 0;
+
+        // Returns whether the event handling was succeeded or not.
+        virtual bool onDescend(const int ptNodeArrayPos) = 0;
+
+        // Returns whether the event handling was succeeded or not.
+        virtual bool onReadingPtNodeArrayTail() = 0;
+
+        // Returns whether the event handling was succeeded or not.
+        virtual bool onVisitingPtNode(const DynamicPatriciaTrieNodeReader *const node,
+                const int *const nodeCodePoints) = 0;
+
+     protected:
+        TraversingEventListener() {};
+
+     private:
+        DISALLOW_COPY_AND_ASSIGN(TraversingEventListener);
+    };
+
+    DynamicPatriciaTrieReadingHelper(const BufferWithExtendableBuffer *const buffer,
+            const DictionaryBigramsStructurePolicy *const bigramsPolicy,
+            const DictionaryShortcutsStructurePolicy *const shortcutsPolicy)
+            : mIsError(false), mReadingState(), mBuffer(buffer),
+              mNodeReader(mBuffer, bigramsPolicy, shortcutsPolicy), mReadingStateStack() {}
+
+    ~DynamicPatriciaTrieReadingHelper() {}
+
+    AK_FORCE_INLINE bool isError() const {
+        return mIsError;
+    }
+
+    AK_FORCE_INLINE bool isEnd() const {
+        return mReadingState.mPos == NOT_A_DICT_POS;
+    }
+
+    // Initialize reading state with the head position of a PtNode array.
+    AK_FORCE_INLINE void initWithPtNodeArrayPos(const int ptNodeArrayPos) {
+        if (ptNodeArrayPos == NOT_A_DICT_POS) {
+            mReadingState.mPos = NOT_A_DICT_POS;
+        } else {
+            mIsError = false;
+            mReadingState.mPos = ptNodeArrayPos;
+            mReadingState.mPrevTotalCodePointCount = 0;
+            mReadingState.mTotalNodeCount = 0;
+            mReadingState.mNodeArrayCount = 0;
+            mReadingState.mPosOfLastForwardLinkField = NOT_A_DICT_POS;
+            mReadingStateStack.clear();
+            nextPtNodeArray();
+            if (!isEnd()) {
+                fetchPtNodeInfo();
+            }
+        }
+    }
+
+    // Initialize reading state with the head position of a node.
+    AK_FORCE_INLINE void initWithPtNodePos(const int ptNodePos) {
+        if (ptNodePos == NOT_A_DICT_POS) {
+            mReadingState.mPos = NOT_A_DICT_POS;
+        } else {
+            mIsError = false;
+            mReadingState.mPos = ptNodePos;
+            mReadingState.mNodeCount = 1;
+            mReadingState.mPrevTotalCodePointCount = 0;
+            mReadingState.mTotalNodeCount = 1;
+            mReadingState.mNodeArrayCount = 1;
+            mReadingState.mPosOfLastForwardLinkField = NOT_A_DICT_POS;
+            mReadingState.mPosOfLastPtNodeArrayHead = NOT_A_DICT_POS;
+            mReadingStateStack.clear();
+            fetchPtNodeInfo();
+        }
+    }
+
+    AK_FORCE_INLINE const DynamicPatriciaTrieNodeReader* getNodeReader() const {
+        return &mNodeReader;
+    }
+
+    AK_FORCE_INLINE bool isValidTerminalNode() const {
+        return !isEnd() && !mNodeReader.isDeleted() && mNodeReader.isTerminal();
+    }
+
+    AK_FORCE_INLINE bool isMatchedCodePoint(const int index, const int codePoint) const {
+        return mMergedNodeCodePoints[index] == codePoint;
+    }
+
+    // Return code point count exclude the last read node's code points.
+    AK_FORCE_INLINE int getPrevTotalCodePointCount() const {
+        return mReadingState.mPrevTotalCodePointCount;
+    }
+
+    // Return code point count include the last read node's code points.
+    AK_FORCE_INLINE int getTotalCodePointCount() const {
+        return mReadingState.mPrevTotalCodePointCount + mNodeReader.getCodePointCount();
+    }
+
+    AK_FORCE_INLINE void fetchMergedNodeCodePointsInReverseOrder(
+            const int index, int *const outCodePoints) const {
+        const int nodeCodePointCount = mNodeReader.getCodePointCount();
+        for (int i =  0; i < nodeCodePointCount; ++i) {
+            outCodePoints[index + i] = mMergedNodeCodePoints[nodeCodePointCount - 1 - i];
+        }
+    }
+
+    AK_FORCE_INLINE const int *getMergedNodeCodePoints() const {
+        return mMergedNodeCodePoints;
+    }
+
+    AK_FORCE_INLINE void readNextSiblingNode() {
+        mReadingState.mNodeCount -= 1;
+        mReadingState.mPos = mNodeReader.getSiblingNodePos();
+        if (mReadingState.mNodeCount <= 0) {
+            // All nodes in the current node array have been read.
+            followForwardLink();
+            if (!isEnd()) {
+                fetchPtNodeInfo();
+            }
+        } else {
+            fetchPtNodeInfo();
+        }
+    }
+
+    // Read the first child node of the current node.
+    AK_FORCE_INLINE void readChildNode() {
+        if (mNodeReader.hasChildren()) {
+            mReadingState.mPrevTotalCodePointCount += mNodeReader.getCodePointCount();
+            mReadingState.mTotalNodeCount = 0;
+            mReadingState.mNodeArrayCount = 0;
+            mReadingState.mPos = mNodeReader.getChildrenPos();
+            mReadingState.mPosOfLastForwardLinkField = NOT_A_DICT_POS;
+            // Read children node array.
+            nextPtNodeArray();
+            if (!isEnd()) {
+                fetchPtNodeInfo();
+            }
+        } else {
+            mReadingState.mPos = NOT_A_DICT_POS;
+        }
+    }
+
+    // Read the parent node of the current node.
+    AK_FORCE_INLINE void readParentNode() {
+        if (mNodeReader.getParentPos() != NOT_A_DICT_POS) {
+            mReadingState.mPrevTotalCodePointCount += mNodeReader.getCodePointCount();
+            mReadingState.mTotalNodeCount = 1;
+            mReadingState.mNodeArrayCount = 1;
+            mReadingState.mNodeCount = 1;
+            mReadingState.mPos = mNodeReader.getParentPos();
+            mReadingState.mPosOfLastForwardLinkField = NOT_A_DICT_POS;
+            mReadingState.mPosOfLastPtNodeArrayHead = NOT_A_DICT_POS;
+            fetchPtNodeInfo();
+        } else {
+            mReadingState.mPos = NOT_A_DICT_POS;
+        }
+    }
+
+    AK_FORCE_INLINE int getPosOfLastForwardLinkField() const {
+        return mReadingState.mPosOfLastForwardLinkField;
+    }
+
+    AK_FORCE_INLINE int getPosOfLastPtNodeArrayHead() const {
+        return mReadingState.mPosOfLastPtNodeArrayHead;
+    }
+
+    AK_FORCE_INLINE void reloadCurrentPtNodeInfo() {
+        if (!isEnd()) {
+            fetchPtNodeInfo();
+        }
+    }
+
+    bool traverseAllPtNodesInPostorderDepthFirstManner(TraversingEventListener *const listener);
+
+    bool traverseAllPtNodesInPtNodeArrayLevelPreorderDepthFirstManner(
+            TraversingEventListener *const listener);
+
+ private:
+    DISALLOW_COPY_AND_ASSIGN(DynamicPatriciaTrieReadingHelper);
+
+    class ReadingState {
+     public:
+        // Note that copy constructor and assignment operator are used for this class to use
+        // std::vector.
+        ReadingState() : mPos(NOT_A_DICT_POS), mNodeCount(0), mPrevTotalCodePointCount(0),
+                mTotalNodeCount(0), mNodeArrayCount(0), mPosOfLastForwardLinkField(NOT_A_DICT_POS),
+                mPosOfLastPtNodeArrayHead(NOT_A_DICT_POS) {}
+
+        int mPos;
+        // Node count of a node array.
+        int mNodeCount;
+        int mPrevTotalCodePointCount;
+        int mTotalNodeCount;
+        int mNodeArrayCount;
+        int mPosOfLastForwardLinkField;
+        int mPosOfLastPtNodeArrayHead;
+    };
+
+    static const int MAX_CHILD_COUNT_TO_AVOID_INFINITE_LOOP;
+    static const int MAX_NODE_ARRAY_COUNT_TO_AVOID_INFINITE_LOOP;
+    static const size_t MAX_READING_STATE_STACK_SIZE;
+
+    bool mIsError;
+    ReadingState mReadingState;
+    const BufferWithExtendableBuffer *const mBuffer;
+    DynamicPatriciaTrieNodeReader mNodeReader;
+    int mMergedNodeCodePoints[MAX_WORD_LENGTH];
+    std::vector<ReadingState> mReadingStateStack;
+
+    void nextPtNodeArray();
+
+    void followForwardLink();
+
+    AK_FORCE_INLINE void fetchPtNodeInfo() {
+        mNodeReader.fetchNodeInfoInBufferFromPtNodePosAndGetNodeCodePoints(mReadingState.mPos,
+                MAX_WORD_LENGTH, mMergedNodeCodePoints);
+        if (mNodeReader.getCodePointCount() <= 0) {
+            // Empty node is not allowed.
+            mIsError = true;
+            mReadingState.mPos = NOT_A_DICT_POS;
+        }
+    }
+
+    AK_FORCE_INLINE void pushReadingStateToStack() {
+        if (mReadingStateStack.size() > MAX_READING_STATE_STACK_SIZE) {
+            AKLOGI("Reading state stack overflow. Max size: %d", MAX_READING_STATE_STACK_SIZE);
+            ASSERT(false);
+            mIsError = true;
+            mReadingState.mPos = NOT_A_DICT_POS;
+        } else {
+            mReadingStateStack.push_back(mReadingState);
+        }
+    }
+
+    AK_FORCE_INLINE void popReadingStateFromStack() {
+        if (mReadingStateStack.empty()) {
+            mReadingState.mPos = NOT_A_DICT_POS;
+        } else {
+            mReadingState = mReadingStateStack.back();
+            mReadingStateStack.pop_back();
+            fetchPtNodeInfo();
+        }
+    }
+};
+} // namespace latinime
+#endif /* LATINIME_DYNAMIC_PATRICIA_TRIE_READING_HELPER_H */
diff --git a/native/jni/src/suggest/policyimpl/dictionary/dynamic_patricia_trie_reading_utils.cpp b/native/jni/src/suggest/policyimpl/dictionary/dynamic_patricia_trie_reading_utils.cpp
index 0de6341..d68446d 100644
--- a/native/jni/src/suggest/policyimpl/dictionary/dynamic_patricia_trie_reading_utils.cpp
+++ b/native/jni/src/suggest/policyimpl/dictionary/dynamic_patricia_trie_reading_utils.cpp
@@ -17,7 +17,7 @@
 #include "suggest/policyimpl/dictionary/dynamic_patricia_trie_reading_utils.h"
 
 #include "defines.h"
-#include "suggest/core/dictionary/byte_array_utils.h"
+#include "suggest/policyimpl/dictionary/utils/byte_array_utils.h"
 
 namespace latinime {
 
@@ -28,13 +28,44 @@
 const DptReadingUtils::NodeFlags DptReadingUtils::FLAG_IS_MOVED = 0x40;
 const DptReadingUtils::NodeFlags DptReadingUtils::FLAG_IS_DELETED = 0x80;
 
-/* static */ int DptReadingUtils::readChildrenPositionAndAdvancePosition(
-        const uint8_t *const buffer, const NodeFlags flags, int *const pos) {
-    if ((flags & MASK_MOVED) == FLAG_IS_NOT_MOVED) {
-        const int base = *pos;
-        return base + ByteArrayUtils::readSint24AndAdvancePosition(buffer, pos);
-    } else {
+// TODO: Make DICT_OFFSET_ZERO_OFFSET = 0.
+// Currently, DICT_OFFSET_INVALID is 0 in Java side but offset can be 0 during GC. So, the maximum
+// value of offsets, which is 0x7FFFFF is used to represent 0 offset.
+const int DptReadingUtils::DICT_OFFSET_INVALID = 0;
+const int DptReadingUtils::DICT_OFFSET_ZERO_OFFSET = 0x7FFFFF;
+
+/* static */ int DptReadingUtils::getForwardLinkPosition(const uint8_t *const buffer,
+        const int pos) {
+    int linkAddressPos = pos;
+    return ByteArrayUtils::readSint24AndAdvancePosition(buffer, &linkAddressPos);
+}
+
+/* static */ int DptReadingUtils::getParentPtNodePosOffsetAndAdvancePosition(
+        const uint8_t *const buffer, int *const pos) {
+    return ByteArrayUtils::readSint24AndAdvancePosition(buffer, pos);
+}
+
+/* static */ int DptReadingUtils::getParentPtNodePos(const int parentOffset, const int ptNodePos) {
+    if (parentOffset == DICT_OFFSET_INVALID) {
         return NOT_A_DICT_POS;
+    } else if (parentOffset == DICT_OFFSET_ZERO_OFFSET) {
+        return ptNodePos;
+    } else {
+        return parentOffset + ptNodePos;
+    }
+}
+
+/* static */ int DptReadingUtils::readChildrenPositionAndAdvancePosition(
+        const uint8_t *const buffer, int *const pos) {
+    const int base = *pos;
+    const int offset = ByteArrayUtils::readSint24AndAdvancePosition(buffer, pos);
+    if (offset == DICT_OFFSET_INVALID) {
+        // The PtNode does not have children.
+        return NOT_A_DICT_POS;
+    } else if (offset == DICT_OFFSET_ZERO_OFFSET) {
+        return base;
+    } else {
+        return base + offset;
     }
 }
 
diff --git a/native/jni/src/suggest/policyimpl/dictionary/dynamic_patricia_trie_reading_utils.h b/native/jni/src/suggest/policyimpl/dictionary/dynamic_patricia_trie_reading_utils.h
index 5398d7e..67c3cc5 100644
--- a/native/jni/src/suggest/policyimpl/dictionary/dynamic_patricia_trie_reading_utils.h
+++ b/native/jni/src/suggest/policyimpl/dictionary/dynamic_patricia_trie_reading_utils.h
@@ -20,7 +20,6 @@
 #include <stdint.h>
 
 #include "defines.h"
-#include "suggest/core/dictionary/byte_array_utils.h"
 
 namespace latinime {
 
@@ -28,22 +27,21 @@
  public:
     typedef uint8_t NodeFlags;
 
-    static AK_FORCE_INLINE int getForwardLinkPosition(const uint8_t *const buffer, const int pos) {
-        int linkAddressPos = pos;
-        return ByteArrayUtils::readSint24AndAdvancePosition(buffer, &linkAddressPos);
-    }
+    static const int DICT_OFFSET_INVALID;
+    static const int DICT_OFFSET_ZERO_OFFSET;
+
+    static int getForwardLinkPosition(const uint8_t *const buffer, const int pos);
 
     static AK_FORCE_INLINE bool isValidForwardLinkPosition(const int forwardLinkAddress) {
         return forwardLinkAddress != 0;
     }
 
-    static AK_FORCE_INLINE int getParentPosAndAdvancePosition(const uint8_t *const buffer,
-            int *const pos) {
-        return ByteArrayUtils::readSint24AndAdvancePosition(buffer, pos);
-    }
+    static int getParentPtNodePosOffsetAndAdvancePosition(const uint8_t *const buffer,
+            int *const pos);
 
-    static int readChildrenPositionAndAdvancePosition(const uint8_t *const buffer,
-            const NodeFlags flags, int *const pos);
+    static int getParentPtNodePos(const int parentOffset, const int ptNodePos);
+
+    static int readChildrenPositionAndAdvancePosition(const uint8_t *const buffer, int *const pos);
 
     /**
      * Node Flags
@@ -56,6 +54,15 @@
         return FLAG_IS_DELETED == (MASK_MOVED & flags);
     }
 
+    static AK_FORCE_INLINE NodeFlags updateAndGetFlags(const NodeFlags originalFlags,
+            const bool isMoved, const bool isDeleted) {
+        NodeFlags flags = originalFlags;
+        flags = isMoved ? ((flags & (~MASK_MOVED)) | FLAG_IS_MOVED) : flags;
+        flags = isDeleted ? ((flags & (~MASK_MOVED)) | FLAG_IS_DELETED) : flags;
+        flags = (!isMoved && !isDeleted) ? ((flags & (~MASK_MOVED)) | FLAG_IS_NOT_MOVED) : flags;
+        return flags;
+    }
+
  private:
     DISALLOW_IMPLICIT_CONSTRUCTORS(DynamicPatriciaTrieReadingUtils);
 
diff --git a/native/jni/src/suggest/policyimpl/dictionary/dynamic_patricia_trie_writing_helper.cpp b/native/jni/src/suggest/policyimpl/dictionary/dynamic_patricia_trie_writing_helper.cpp
new file mode 100644
index 0000000..a51ae5e
--- /dev/null
+++ b/native/jni/src/suggest/policyimpl/dictionary/dynamic_patricia_trie_writing_helper.cpp
@@ -0,0 +1,569 @@
+/*
+ * Copyright (C) 2013, 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.
+ */
+
+#include "suggest/policyimpl/dictionary/dynamic_patricia_trie_writing_helper.h"
+
+#include <cstdio>
+#include <cstring>
+
+#include "suggest/policyimpl/dictionary/bigram/dynamic_bigram_list_policy.h"
+#include "suggest/policyimpl/dictionary/dynamic_patricia_trie_gc_event_listeners.h"
+#include "suggest/policyimpl/dictionary/dynamic_patricia_trie_node_reader.h"
+#include "suggest/policyimpl/dictionary/dynamic_patricia_trie_reading_helper.h"
+#include "suggest/policyimpl/dictionary/dynamic_patricia_trie_reading_utils.h"
+#include "suggest/policyimpl/dictionary/dynamic_patricia_trie_writing_utils.h"
+#include "suggest/policyimpl/dictionary/header/header_policy.h"
+#include "suggest/policyimpl/dictionary/patricia_trie_reading_utils.h"
+#include "suggest/policyimpl/dictionary/shortcut/dynamic_shortcut_list_policy.h"
+#include "utils/hash_map_compat.h"
+
+namespace latinime {
+
+const int DynamicPatriciaTrieWritingHelper::CHILDREN_POSITION_FIELD_SIZE = 3;
+const char *const DynamicPatriciaTrieWritingHelper::TEMP_FILE_SUFFIX_FOR_WRITING_DICT_FILE =
+        ".tmp";
+// TODO: Make MAX_DICTIONARY_SIZE 8MB.
+const size_t DynamicPatriciaTrieWritingHelper::MAX_DICTIONARY_SIZE = 2 * 1024 * 1024;
+
+bool DynamicPatriciaTrieWritingHelper::addUnigramWord(
+        DynamicPatriciaTrieReadingHelper *const readingHelper,
+        const int *const wordCodePoints, const int codePointCount, const int probability) {
+    int parentPos = NOT_A_DICT_POS;
+    while (!readingHelper->isEnd()) {
+        const int matchedCodePointCount = readingHelper->getPrevTotalCodePointCount();
+        if (!readingHelper->isMatchedCodePoint(0 /* index */,
+                wordCodePoints[matchedCodePointCount])) {
+            // The first code point is different from target code point. Skip this node and read
+            // the next sibling node.
+            readingHelper->readNextSiblingNode();
+            continue;
+        }
+        // Check following merged node code points.
+        const DynamicPatriciaTrieNodeReader *const nodeReader = readingHelper->getNodeReader();
+        const int nodeCodePointCount = nodeReader->getCodePointCount();
+        for (int j = 1; j < nodeCodePointCount; ++j) {
+            const int nextIndex = matchedCodePointCount + j;
+            if (nextIndex >= codePointCount || !readingHelper->isMatchedCodePoint(j,
+                    wordCodePoints[matchedCodePointCount + j])) {
+                return reallocatePtNodeAndAddNewPtNodes(nodeReader,
+                        readingHelper->getMergedNodeCodePoints(), j, probability,
+                        wordCodePoints + matchedCodePointCount,
+                        codePointCount - matchedCodePointCount);
+            }
+        }
+        // All characters are matched.
+        if (codePointCount == readingHelper->getTotalCodePointCount()) {
+            return setPtNodeProbability(nodeReader, probability,
+                    readingHelper->getMergedNodeCodePoints());
+        }
+        if (!nodeReader->hasChildren()) {
+            return createChildrenPtNodeArrayAndAChildPtNode(nodeReader, probability,
+                    wordCodePoints + readingHelper->getTotalCodePointCount(),
+                    codePointCount - readingHelper->getTotalCodePointCount());
+        }
+        // Advance to the children nodes.
+        parentPos = nodeReader->getHeadPos();
+        readingHelper->readChildNode();
+    }
+    if (readingHelper->isError()) {
+        // The dictionary is invalid.
+        return false;
+    }
+    int pos = readingHelper->getPosOfLastForwardLinkField();
+    return createAndInsertNodeIntoPtNodeArray(parentPos,
+            wordCodePoints + readingHelper->getPrevTotalCodePointCount(),
+            codePointCount - readingHelper->getPrevTotalCodePointCount(),
+            probability, &pos);
+}
+
+bool DynamicPatriciaTrieWritingHelper::addBigramWords(const int word0Pos, const int word1Pos,
+        const int probability) {
+    int mMergedNodeCodePoints[MAX_WORD_LENGTH];
+    DynamicPatriciaTrieNodeReader nodeReader(mBuffer, mBigramPolicy, mShortcutPolicy);
+    nodeReader.fetchNodeInfoInBufferFromPtNodePosAndGetNodeCodePoints(word0Pos, MAX_WORD_LENGTH,
+            mMergedNodeCodePoints);
+    // Move node to add bigram entry.
+    const int newNodePos = mBuffer->getTailPosition();
+    if (!markNodeAsMovedAndSetPosition(&nodeReader, newNodePos, newNodePos)) {
+        return false;
+    }
+    int writingPos = newNodePos;
+    // Write a new PtNode using original PtNode's info to the tail of the dictionary in mBuffer.
+    if (!writePtNodeToBufferByCopyingPtNodeInfo(mBuffer, &nodeReader, nodeReader.getParentPos(),
+            mMergedNodeCodePoints, nodeReader.getCodePointCount(), nodeReader.getProbability(),
+            &writingPos)) {
+        return false;
+    }
+    nodeReader.fetchNodeInfoInBufferFromPtNodePos(newNodePos);
+    if (nodeReader.getBigramsPos() != NOT_A_DICT_POS) {
+        // Insert a new bigram entry into the existing bigram list.
+        int bigramListPos = nodeReader.getBigramsPos();
+        return mBigramPolicy->addNewBigramEntryToBigramList(word1Pos, probability, &bigramListPos);
+    } else {
+        // The PtNode doesn't have a bigram list.
+        // First, Write a bigram entry at the tail position of the PtNode.
+        if (!mBigramPolicy->writeNewBigramEntry(word1Pos, probability, &writingPos)) {
+            return false;
+        }
+        // Then, Mark as the PtNode having bigram list in the flags.
+        const PatriciaTrieReadingUtils::NodeFlags updatedFlags =
+                PatriciaTrieReadingUtils::createAndGetFlags(nodeReader.isBlacklisted(),
+                        nodeReader.isNotAWord(), nodeReader.getProbability() != NOT_A_PROBABILITY,
+                        nodeReader.getShortcutPos() != NOT_A_DICT_POS, true /* hasBigrams */,
+                        nodeReader.getCodePointCount() > 1, CHILDREN_POSITION_FIELD_SIZE);
+        writingPos = newNodePos;
+        // Write updated flags into the moved PtNode's flags field.
+        return DynamicPatriciaTrieWritingUtils::writeFlagsAndAdvancePosition(mBuffer, updatedFlags,
+                &writingPos);
+    }
+}
+
+// Remove a bigram relation from word0Pos to word1Pos.
+bool DynamicPatriciaTrieWritingHelper::removeBigramWords(const int word0Pos, const int word1Pos) {
+    DynamicPatriciaTrieNodeReader nodeReader(mBuffer, mBigramPolicy, mShortcutPolicy);
+    nodeReader.fetchNodeInfoInBufferFromPtNodePos(word0Pos);
+    if (nodeReader.getBigramsPos() == NOT_A_DICT_POS) {
+        return false;
+    }
+    return mBigramPolicy->removeBigram(nodeReader.getBigramsPos(), word1Pos);
+}
+
+void DynamicPatriciaTrieWritingHelper::writeToDictFile(const char *const fileName,
+        const HeaderPolicy *const headerPolicy) {
+    BufferWithExtendableBuffer headerBuffer(0 /* originalBuffer */, 0 /* originalBufferSize */);
+    if (!headerPolicy->writeHeaderToBuffer(&headerBuffer, false /* updatesLastUpdatedTime */)) {
+        return;
+    }
+    flushAllToFile(fileName, &headerBuffer, mBuffer);
+}
+
+void DynamicPatriciaTrieWritingHelper::writeToDictFileWithGC(const int rootPtNodeArrayPos,
+        const char *const fileName, const HeaderPolicy *const headerPolicy) {
+    BufferWithExtendableBuffer headerBuffer(0 /* originalBuffer */, 0 /* originalBufferSize */);
+    if (!headerPolicy->writeHeaderToBuffer(&headerBuffer, true /* updatesLastUpdatedTime */)) {
+        return;
+    }
+    BufferWithExtendableBuffer newDictBuffer(0 /* originalBuffer */, 0 /* originalBufferSize */,
+            MAX_DICTIONARY_SIZE);
+    if (!runGC(rootPtNodeArrayPos, &newDictBuffer)) {
+        return;
+    }
+    flushAllToFile(fileName, &headerBuffer, &newDictBuffer);
+}
+
+bool DynamicPatriciaTrieWritingHelper::markNodeAsDeleted(
+        const DynamicPatriciaTrieNodeReader *const nodeToUpdate) {
+    int pos = nodeToUpdate->getHeadPos();
+    const bool usesAdditionalBuffer = mBuffer->isInAdditionalBuffer(pos);
+    const uint8_t *const dictBuf = mBuffer->getBuffer(usesAdditionalBuffer);
+    if (usesAdditionalBuffer) {
+        pos -= mBuffer->getOriginalBufferSize();
+    }
+    // Read original flags
+    const PatriciaTrieReadingUtils::NodeFlags originalFlags =
+            PatriciaTrieReadingUtils::getFlagsAndAdvancePosition(dictBuf, &pos);
+    const PatriciaTrieReadingUtils::NodeFlags updatedFlags =
+            DynamicPatriciaTrieReadingUtils::updateAndGetFlags(originalFlags, false /* isMoved */,
+                    true /* isDeleted */);
+    int writingPos = nodeToUpdate->getHeadPos();
+    // Update flags.
+    return DynamicPatriciaTrieWritingUtils::writeFlagsAndAdvancePosition(mBuffer, updatedFlags,
+            &writingPos);
+}
+
+bool DynamicPatriciaTrieWritingHelper::markNodeAsMovedAndSetPosition(
+        const DynamicPatriciaTrieNodeReader *const originalNode, const int movedPos,
+        const int bigramLinkedNodePos) {
+    int pos = originalNode->getHeadPos();
+    const bool usesAdditionalBuffer = mBuffer->isInAdditionalBuffer(pos);
+    const uint8_t *const dictBuf = mBuffer->getBuffer(usesAdditionalBuffer);
+    if (usesAdditionalBuffer) {
+        pos -= mBuffer->getOriginalBufferSize();
+    }
+    // Read original flags
+    const PatriciaTrieReadingUtils::NodeFlags originalFlags =
+            PatriciaTrieReadingUtils::getFlagsAndAdvancePosition(dictBuf, &pos);
+    const PatriciaTrieReadingUtils::NodeFlags updatedFlags =
+            DynamicPatriciaTrieReadingUtils::updateAndGetFlags(originalFlags, true /* isMoved */,
+                    false /* isDeleted */);
+    int writingPos = originalNode->getHeadPos();
+    // Update flags.
+    if (!DynamicPatriciaTrieWritingUtils::writeFlagsAndAdvancePosition(mBuffer, updatedFlags,
+            &writingPos)) {
+        return false;
+    }
+    // Update moved position, which is stored in the parent offset field.
+    if (!DynamicPatriciaTrieWritingUtils::writeParentPosOffsetAndAdvancePosition(
+            mBuffer, movedPos, originalNode->getHeadPos(), &writingPos)) {
+        return false;
+    }
+    // Update bigram linked node position, which is stored in the children position field.
+    int childrenPosFieldPos = originalNode->getChildrenPosFieldPos();
+    if (!DynamicPatriciaTrieWritingUtils::writeChildrenPositionAndAdvancePosition(
+            mBuffer, bigramLinkedNodePos, &childrenPosFieldPos)) {
+        return false;
+    }
+    if (originalNode->hasChildren()) {
+        // Update children's parent position.
+        DynamicPatriciaTrieReadingHelper readingHelper(mBuffer, mBigramPolicy, mShortcutPolicy);
+        const DynamicPatriciaTrieNodeReader *const nodeReader = readingHelper.getNodeReader();
+        readingHelper.initWithPtNodeArrayPos(originalNode->getChildrenPos());
+        while (!readingHelper.isEnd()) {
+            int parentOffsetFieldPos = nodeReader->getHeadPos()
+                    + DynamicPatriciaTrieWritingUtils::NODE_FLAG_FIELD_SIZE;
+            if (!DynamicPatriciaTrieWritingUtils::writeParentPosOffsetAndAdvancePosition(
+                    mBuffer, movedPos, nodeReader->getHeadPos(), &parentOffsetFieldPos)) {
+                // Parent offset cannot be written because of a bug or a broken dictionary; thus,
+                // we give up to update dictionary.
+                return false;
+            }
+            readingHelper.readNextSiblingNode();
+        }
+    }
+    return true;
+}
+
+// Write new PtNode at writingPos.
+bool DynamicPatriciaTrieWritingHelper::writePtNodeWithFullInfoToBuffer(
+        BufferWithExtendableBuffer *const bufferToWrite, const bool isBlacklisted,
+        const bool isNotAWord, const int parentPos, const int *const codePoints,
+        const int codePointCount, const int probability, const int childrenPos,
+        const int originalBigramListPos, const int originalShortcutListPos,
+        int *const writingPos) {
+    const int nodePos = *writingPos;
+    // Write dummy flags. The Node flags are updated with appropriate flags at the last step of the
+    // PtNode writing.
+    if (!DynamicPatriciaTrieWritingUtils::writeFlagsAndAdvancePosition(bufferToWrite,
+            0 /* nodeFlags */, writingPos)) {
+        return false;
+    }
+    // Calculate a parent offset and write the offset.
+    if (!DynamicPatriciaTrieWritingUtils::writeParentPosOffsetAndAdvancePosition(bufferToWrite,
+            parentPos, nodePos, writingPos)) {
+        return false;
+    }
+    // Write code points
+    if (!DynamicPatriciaTrieWritingUtils::writeCodePointsAndAdvancePosition(bufferToWrite,
+            codePoints, codePointCount, writingPos)) {
+        return false;
+    }
+    // Write probability when the probability is a valid probability, which means this node is
+    // terminal.
+    if (probability != NOT_A_PROBABILITY) {
+        if (!DynamicPatriciaTrieWritingUtils::writeProbabilityAndAdvancePosition(bufferToWrite,
+                probability, writingPos)) {
+            return false;
+        }
+    }
+    // Write children position
+    if (!DynamicPatriciaTrieWritingUtils::writeChildrenPositionAndAdvancePosition(bufferToWrite,
+            childrenPos, writingPos)) {
+        return false;
+    }
+    // Copy shortcut list when the originalShortcutListPos is valid dictionary position.
+    if (originalShortcutListPos != NOT_A_DICT_POS) {
+        int fromPos = originalShortcutListPos;
+        if (!mShortcutPolicy->copyAllShortcutsAndReturnIfSucceededOrNot(bufferToWrite, &fromPos,
+                writingPos)) {
+            return false;
+        }
+    }
+    // Copy bigram list when the originalBigramListPos is valid dictionary position.
+    int bigramCount = 0;
+    if (originalBigramListPos != NOT_A_DICT_POS) {
+        int fromPos = originalBigramListPos;
+        if (!mBigramPolicy->copyAllBigrams(bufferToWrite, &fromPos, writingPos, &bigramCount)) {
+            return false;
+        }
+    }
+    // Create node flags and write them.
+    PatriciaTrieReadingUtils::NodeFlags nodeFlags =
+            PatriciaTrieReadingUtils::createAndGetFlags(isBlacklisted, isNotAWord,
+                    probability != NOT_A_PROBABILITY /* isTerminal */,
+                    originalShortcutListPos != NOT_A_DICT_POS /* hasShortcutTargets */,
+                    bigramCount > 0 /* hasBigrams */, codePointCount > 1 /* hasMultipleChars */,
+                    CHILDREN_POSITION_FIELD_SIZE);
+    int flagsFieldPos = nodePos;
+    if (!DynamicPatriciaTrieWritingUtils::writeFlagsAndAdvancePosition(bufferToWrite, nodeFlags,
+            &flagsFieldPos)) {
+        return false;
+    }
+    return true;
+}
+
+bool DynamicPatriciaTrieWritingHelper::writePtNodeToBuffer(
+        BufferWithExtendableBuffer *const bufferToWrite, const int parentPos,
+        const int *const codePoints, const int codePointCount, const int probability,
+        int *const writingPos) {
+    return writePtNodeWithFullInfoToBuffer(bufferToWrite, false /* isBlacklisted */,
+            false /* isNotAWord */, parentPos, codePoints, codePointCount, probability,
+            NOT_A_DICT_POS /* childrenPos */, NOT_A_DICT_POS /* originalBigramsPos */,
+            NOT_A_DICT_POS /* originalShortcutPos */, writingPos);
+}
+
+bool DynamicPatriciaTrieWritingHelper::writePtNodeToBufferByCopyingPtNodeInfo(
+        BufferWithExtendableBuffer *const bufferToWrite,
+        const DynamicPatriciaTrieNodeReader *const originalNode, const int parentPos,
+        const int *const codePoints, const int codePointCount, const int probability,
+        int *const writingPos) {
+    return writePtNodeWithFullInfoToBuffer(bufferToWrite, originalNode->isBlacklisted(),
+            originalNode->isNotAWord(), parentPos, codePoints, codePointCount, probability,
+            originalNode->getChildrenPos(), originalNode->getBigramsPos(),
+            originalNode->getShortcutPos(), writingPos);
+}
+
+bool DynamicPatriciaTrieWritingHelper::createAndInsertNodeIntoPtNodeArray(const int parentPos,
+        const int *const nodeCodePoints, const int nodeCodePointCount, const int probability,
+        int *const forwardLinkFieldPos) {
+    const int newPtNodeArrayPos = mBuffer->getTailPosition();
+    if (!DynamicPatriciaTrieWritingUtils::writeForwardLinkPositionAndAdvancePosition(mBuffer,
+            newPtNodeArrayPos, forwardLinkFieldPos)) {
+        return false;
+    }
+    return createNewPtNodeArrayWithAChildPtNode(parentPos, nodeCodePoints, nodeCodePointCount,
+            probability);
+}
+
+bool DynamicPatriciaTrieWritingHelper::setPtNodeProbability(
+        const DynamicPatriciaTrieNodeReader *const originalPtNode, const int probability,
+        const int *const codePoints) {
+    if (originalPtNode->isTerminal()) {
+        // Overwrites the probability.
+        int probabilityFieldPos = originalPtNode->getProbabilityFieldPos();
+        if (!DynamicPatriciaTrieWritingUtils::writeProbabilityAndAdvancePosition(mBuffer,
+                probability, &probabilityFieldPos)) {
+            return false;
+        }
+    } else {
+        // Make the node terminal and write the probability.
+        int movedPos = mBuffer->getTailPosition();
+        if (!markNodeAsMovedAndSetPosition(originalPtNode, movedPos, movedPos)) {
+            return false;
+        }
+        if (!writePtNodeToBufferByCopyingPtNodeInfo(mBuffer, originalPtNode,
+                originalPtNode->getParentPos(), codePoints, originalPtNode->getCodePointCount(),
+                probability, &movedPos)) {
+            return false;
+        }
+    }
+    return true;
+}
+
+bool DynamicPatriciaTrieWritingHelper::createChildrenPtNodeArrayAndAChildPtNode(
+        const DynamicPatriciaTrieNodeReader *const parentNode, const int probability,
+        const int *const codePoints, const int codePointCount) {
+    const int newPtNodeArrayPos = mBuffer->getTailPosition();
+    int childrenPosFieldPos = parentNode->getChildrenPosFieldPos();
+    if (!DynamicPatriciaTrieWritingUtils::writeChildrenPositionAndAdvancePosition(mBuffer,
+            newPtNodeArrayPos, &childrenPosFieldPos)) {
+        return false;
+    }
+    return createNewPtNodeArrayWithAChildPtNode(parentNode->getHeadPos(), codePoints,
+            codePointCount, probability);
+}
+
+bool DynamicPatriciaTrieWritingHelper::createNewPtNodeArrayWithAChildPtNode(
+        const int parentPtNodePos, const int *const nodeCodePoints, const int nodeCodePointCount,
+        const int probability) {
+    int writingPos = mBuffer->getTailPosition();
+    if (!DynamicPatriciaTrieWritingUtils::writePtNodeArraySizeAndAdvancePosition(mBuffer,
+            1 /* arraySize */, &writingPos)) {
+        return false;
+    }
+    if (!writePtNodeToBuffer(mBuffer, parentPtNodePos, nodeCodePoints, nodeCodePointCount,
+            probability, &writingPos)) {
+        return false;
+    }
+    if (!DynamicPatriciaTrieWritingUtils::writeForwardLinkPositionAndAdvancePosition(mBuffer,
+            NOT_A_DICT_POS /* forwardLinkPos */, &writingPos)) {
+        return false;
+    }
+    return true;
+}
+
+// Returns whether the dictionary updating was succeeded or not.
+bool DynamicPatriciaTrieWritingHelper::reallocatePtNodeAndAddNewPtNodes(
+        const DynamicPatriciaTrieNodeReader *const reallocatingPtNode,
+        const int *const reallocatingPtNodeCodePoints, const int overlappingCodePointCount,
+        const int probabilityOfNewPtNode, const int *const newNodeCodePoints,
+        const int newNodeCodePointCount) {
+    // When addsExtraChild is true, split the reallocating PtNode and add new child.
+    // Reallocating PtNode: abcde, newNode: abcxy.
+    // abc (1st, not terminal) __ de (2nd)
+    //                         \_ xy (extra child, terminal)
+    // Otherwise, this method makes 1st part terminal and write probabilityOfNewPtNode.
+    // Reallocating PtNode: abcde, newNode: abc.
+    // abc (1st, terminal) __ de (2nd)
+    const bool addsExtraChild = newNodeCodePointCount > overlappingCodePointCount;
+    const int firstPartOfReallocatedPtNodePos = mBuffer->getTailPosition();
+    int writingPos = firstPartOfReallocatedPtNodePos;
+    // Write the 1st part of the reallocating node. The children position will be updated later
+    // with actual children position.
+    const int newProbability = addsExtraChild ? NOT_A_PROBABILITY : probabilityOfNewPtNode;
+    if (!writePtNodeToBuffer(mBuffer, reallocatingPtNode->getParentPos(),
+            reallocatingPtNodeCodePoints, overlappingCodePointCount, newProbability,
+            &writingPos)) {
+        return false;
+    }
+    const int actualChildrenPos = writingPos;
+    // Create new children PtNode array.
+    const size_t newPtNodeCount = addsExtraChild ? 2 : 1;
+    if (!DynamicPatriciaTrieWritingUtils::writePtNodeArraySizeAndAdvancePosition(mBuffer,
+            newPtNodeCount, &writingPos)) {
+        return false;
+    }
+    // Write the 2nd part of the reallocating node.
+    const int secondPartOfReallocatedPtNodePos = writingPos;
+    if (!writePtNodeToBufferByCopyingPtNodeInfo(mBuffer, reallocatingPtNode,
+            firstPartOfReallocatedPtNodePos,
+            reallocatingPtNodeCodePoints + overlappingCodePointCount,
+            reallocatingPtNode->getCodePointCount() - overlappingCodePointCount,
+            reallocatingPtNode->getProbability(), &writingPos)) {
+        return false;
+    }
+    if (addsExtraChild) {
+        if (!writePtNodeToBuffer(mBuffer, firstPartOfReallocatedPtNodePos,
+                newNodeCodePoints + overlappingCodePointCount,
+                newNodeCodePointCount - overlappingCodePointCount, probabilityOfNewPtNode,
+                &writingPos)) {
+            return false;
+        }
+    }
+    if (!DynamicPatriciaTrieWritingUtils::writeForwardLinkPositionAndAdvancePosition(mBuffer,
+            NOT_A_DICT_POS /* forwardLinkPos */, &writingPos)) {
+        return false;
+    }
+    // Update original reallocatingPtNode as moved.
+    if (!markNodeAsMovedAndSetPosition(reallocatingPtNode, firstPartOfReallocatedPtNodePos,
+            secondPartOfReallocatedPtNodePos)) {
+        return false;
+    }
+    // Load node info. Information of the 1st part will be fetched.
+    DynamicPatriciaTrieNodeReader nodeReader(mBuffer, mBigramPolicy, mShortcutPolicy);
+    nodeReader.fetchNodeInfoInBufferFromPtNodePos(firstPartOfReallocatedPtNodePos);
+    // Update children position.
+    int childrenPosFieldPos = nodeReader.getChildrenPosFieldPos();
+    if (!DynamicPatriciaTrieWritingUtils::writeChildrenPositionAndAdvancePosition(mBuffer,
+            actualChildrenPos, &childrenPosFieldPos)) {
+        return false;
+    }
+    return true;
+}
+
+// TODO: Create a struct which contains header, body and etc... and use here as an argument.
+void DynamicPatriciaTrieWritingHelper::flushAllToFile(const char *const fileName,
+        BufferWithExtendableBuffer *const dictHeader,
+        BufferWithExtendableBuffer *const dictBody) const {
+    const int tmpFileNameBufSize = strlen(fileName)
+            + strlen(TEMP_FILE_SUFFIX_FOR_WRITING_DICT_FILE) + 1 /* terminator */;
+    // Name of a temporary file used for writing that is a connected string of original name and
+    // TEMP_FILE_SUFFIX_FOR_WRITING_DICT_FILE.
+    char tmpFileName[tmpFileNameBufSize];
+    snprintf(tmpFileName, tmpFileNameBufSize, "%s%s", fileName,
+            TEMP_FILE_SUFFIX_FOR_WRITING_DICT_FILE);
+    FILE *const file = fopen(tmpFileName, "wb");
+    if (!file) {
+        AKLOGI("Dictionary file %s cannnot be opened.", tmpFileName);
+        ASSERT(false);
+        return;
+    }
+    // Write the dictionary header.
+    if (!writeBufferToFilePointer(file, dictHeader)) {
+        remove(tmpFileName);
+        AKLOGI("Dictionary header cannnot be written. size: %d", dictHeader->getTailPosition());
+        ASSERT(false);
+        return;
+    }
+    // Write the dictionary body.
+    if (!writeBufferToFilePointer(file, dictBody)) {
+        remove(tmpFileName);
+        AKLOGI("Dictionary body cannnot be written. size: %d", dictBody->getTailPosition());
+        ASSERT(false);
+        return;
+    }
+    fclose(file);
+    rename(tmpFileName, fileName);
+}
+
+// This closes file pointer when an error is caused and returns whether the writing was succeeded
+// or not.
+bool DynamicPatriciaTrieWritingHelper::writeBufferToFilePointer(FILE *const file,
+        const BufferWithExtendableBuffer *const buffer) const {
+    const int originalBufSize = buffer->getOriginalBufferSize();
+    if (originalBufSize > 0 && fwrite(buffer->getBuffer(false /* usesAdditionalBuffer */),
+            originalBufSize, 1, file) < 1) {
+        fclose(file);
+        return false;
+    }
+    const int additionalBufSize = buffer->getTailPosition() - buffer->getOriginalBufferSize();
+    if (additionalBufSize > 0 && fwrite(buffer->getBuffer(true /* usesAdditionalBuffer */),
+            additionalBufSize, 1, file) < 1) {
+        fclose(file);
+        return false;
+    }
+    return true;
+}
+
+bool DynamicPatriciaTrieWritingHelper::runGC(const int rootPtNodeArrayPos,
+        BufferWithExtendableBuffer *const bufferToWrite) {
+    DynamicPatriciaTrieReadingHelper readingHelper(mBuffer, mBigramPolicy, mShortcutPolicy);
+    readingHelper.initWithPtNodeArrayPos(rootPtNodeArrayPos);
+    DynamicPatriciaTrieGcEventListeners
+            ::TraversePolicyToUpdateUnigramProbabilityAndMarkUselessPtNodesAsDeleted
+                    traversePolicyToUpdateUnigramProbabilityAndMarkUselessPtNodesAsDeleted(
+                            this, mBuffer);
+    if (!readingHelper.traverseAllPtNodesInPostorderDepthFirstManner(
+            &traversePolicyToUpdateUnigramProbabilityAndMarkUselessPtNodesAsDeleted)) {
+        return false;
+    }
+
+    readingHelper.initWithPtNodeArrayPos(rootPtNodeArrayPos);
+    DynamicPatriciaTrieGcEventListeners::TraversePolicyToUpdateBigramProbability
+            traversePolicyToUpdateBigramProbability(mBigramPolicy);
+    if (!readingHelper.traverseAllPtNodesInPostorderDepthFirstManner(
+            &traversePolicyToUpdateBigramProbability)) {
+        return false;
+    }
+
+    // Mapping from positions in mBuffer to positions in bufferToWrite.
+    DictPositionRelocationMap dictPositionRelocationMap;
+    readingHelper.initWithPtNodeArrayPos(rootPtNodeArrayPos);
+    DynamicPatriciaTrieGcEventListeners::TraversePolicyToPlaceAndWriteValidPtNodesToBuffer
+            traversePolicyToPlaceAndWriteValidPtNodesToBuffer(this, bufferToWrite,
+                    &dictPositionRelocationMap);
+    if (!readingHelper.traverseAllPtNodesInPtNodeArrayLevelPreorderDepthFirstManner(
+            &traversePolicyToPlaceAndWriteValidPtNodesToBuffer)) {
+        return false;
+    }
+
+    // Create policy instance for the GCed dictionary.
+    DynamicShortcutListPolicy newDictShortcutPolicy(bufferToWrite);
+    DynamicBigramListPolicy newDictBigramPolicy(bufferToWrite, &newDictShortcutPolicy);
+    // Create reading helper for the GCed dictionary.
+    DynamicPatriciaTrieReadingHelper newDictReadingHelper(bufferToWrite, &newDictBigramPolicy,
+            &newDictShortcutPolicy);
+    newDictReadingHelper.initWithPtNodeArrayPos(rootPtNodeArrayPos);
+    DynamicPatriciaTrieGcEventListeners::TraversePolicyToUpdateAllPositionFields
+            traversePolicyToUpdateAllPositionFields(this, &newDictBigramPolicy, bufferToWrite,
+                    &dictPositionRelocationMap);
+    if (!newDictReadingHelper.traverseAllPtNodesInPtNodeArrayLevelPreorderDepthFirstManner(
+            &traversePolicyToUpdateAllPositionFields)) {
+        return false;
+    }
+    return true;
+}
+
+} // namespace latinime
diff --git a/native/jni/src/suggest/policyimpl/dictionary/dynamic_patricia_trie_writing_helper.h b/native/jni/src/suggest/policyimpl/dictionary/dynamic_patricia_trie_writing_helper.h
new file mode 100644
index 0000000..028fa60
--- /dev/null
+++ b/native/jni/src/suggest/policyimpl/dictionary/dynamic_patricia_trie_writing_helper.h
@@ -0,0 +1,137 @@
+/*
+ * Copyright (C) 2013, 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.
+ */
+
+#ifndef LATINIME_DYNAMIC_PATRICIA_TRIE_WRITING_HELPER_H
+#define LATINIME_DYNAMIC_PATRICIA_TRIE_WRITING_HELPER_H
+
+#include <cstdio>
+#include <stdint.h>
+
+#include "defines.h"
+#include "utils/hash_map_compat.h"
+
+namespace latinime {
+
+class BufferWithExtendableBuffer;
+class DynamicBigramListPolicy;
+class DynamicPatriciaTrieNodeReader;
+class DynamicPatriciaTrieReadingHelper;
+class DynamicShortcutListPolicy;
+class HeaderPolicy;
+
+class DynamicPatriciaTrieWritingHelper {
+ public:
+    typedef hash_map_compat<int, int> PtNodeArrayPositionRelocationMap;
+    typedef hash_map_compat<int, int> PtNodePositionRelocationMap;
+    struct DictPositionRelocationMap {
+     public:
+        DictPositionRelocationMap()
+                : mPtNodeArrayPositionRelocationMap(), mPtNodePositionRelocationMap() {}
+
+        PtNodeArrayPositionRelocationMap mPtNodeArrayPositionRelocationMap;
+        PtNodePositionRelocationMap mPtNodePositionRelocationMap;
+
+     private:
+        DISALLOW_COPY_AND_ASSIGN(DictPositionRelocationMap);
+    };
+
+    DynamicPatriciaTrieWritingHelper(BufferWithExtendableBuffer *const buffer,
+            DynamicBigramListPolicy *const bigramPolicy,
+            DynamicShortcutListPolicy *const shortcutPolicy)
+            : mBuffer(buffer), mBigramPolicy(bigramPolicy), mShortcutPolicy(shortcutPolicy) {}
+
+    ~DynamicPatriciaTrieWritingHelper() {}
+
+    // Add a word to the dictionary. If the word already exists, update the probability.
+    bool addUnigramWord(DynamicPatriciaTrieReadingHelper *const readingHelper,
+            const int *const wordCodePoints, const int codePointCount, const int probability);
+
+    // Add a bigram relation from word0Pos to word1Pos.
+    bool addBigramWords(const int word0Pos, const int word1Pos, const int probability);
+
+    // Remove a bigram relation from word0Pos to word1Pos.
+    bool removeBigramWords(const int word0Pos, const int word1Pos);
+
+    void writeToDictFile(const char *const fileName, const HeaderPolicy *const headerPolicy);
+
+    void writeToDictFileWithGC(const int rootPtNodeArrayPos, const char *const fileName,
+            const HeaderPolicy *const headerPolicy);
+
+    // CAVEAT: This method must be called only from inner classes of
+    // DynamicPatriciaTrieGcEventListeners.
+    bool markNodeAsDeleted(const DynamicPatriciaTrieNodeReader *const nodeToUpdate);
+
+    // CAVEAT: This method must be called only from this class or inner classes of
+    // DynamicPatriciaTrieGcEventListeners.
+    bool writePtNodeToBufferByCopyingPtNodeInfo(BufferWithExtendableBuffer *const bufferToWrite,
+            const DynamicPatriciaTrieNodeReader *const originalNode, const int parentPos,
+            const int *const codePoints, const int codePointCount, const int probability,
+            int *const writingPos);
+
+ private:
+    DISALLOW_IMPLICIT_CONSTRUCTORS(DynamicPatriciaTrieWritingHelper);
+
+    static const int CHILDREN_POSITION_FIELD_SIZE;
+    static const char *const TEMP_FILE_SUFFIX_FOR_WRITING_DICT_FILE;
+    static const size_t MAX_DICTIONARY_SIZE;
+
+    BufferWithExtendableBuffer *const mBuffer;
+    DynamicBigramListPolicy *const mBigramPolicy;
+    DynamicShortcutListPolicy *const mShortcutPolicy;
+
+    bool markNodeAsMovedAndSetPosition(const DynamicPatriciaTrieNodeReader *const nodeToUpdate,
+            const int movedPos, const int bigramLinkedNodePos);
+
+    bool writePtNodeWithFullInfoToBuffer(BufferWithExtendableBuffer *const bufferToWrite,
+            const bool isBlacklisted, const bool isNotAWord,
+            const int parentPos,  const int *const codePoints, const int codePointCount,
+            const int probability, const int childrenPos, const int originalBigramListPos,
+            const int originalShortcutListPos, int *const writingPos);
+
+    bool writePtNodeToBuffer(BufferWithExtendableBuffer *const bufferToWrite,
+            const int parentPos, const int *const codePoints, const int codePointCount,
+            const int probability, int *const writingPos);
+
+    bool createAndInsertNodeIntoPtNodeArray(const int parentPos, const int *const nodeCodePoints,
+            const int nodeCodePointCount, const int probability, int *const forwardLinkFieldPos);
+
+    bool setPtNodeProbability(const DynamicPatriciaTrieNodeReader *const originalNode,
+            const int probability, const int *const codePoints);
+
+    bool createChildrenPtNodeArrayAndAChildPtNode(
+            const DynamicPatriciaTrieNodeReader *const parentNode, const int probability,
+            const int *const codePoints, const int codePointCount);
+
+    bool createNewPtNodeArrayWithAChildPtNode(const int parentPos, const int *const nodeCodePoints,
+            const int nodeCodePointCount, const int probability);
+
+    bool reallocatePtNodeAndAddNewPtNodes(
+            const DynamicPatriciaTrieNodeReader *const reallocatingPtNode,
+            const int *const reallocatingPtNodeCodePoints, const int overlappingCodePointCount,
+            const int probabilityOfNewPtNode, const int *const newNodeCodePoints,
+            const int newNodeCodePointCount);
+
+    void flushAllToFile(const char *const fileName,
+            BufferWithExtendableBuffer *const dictHeader,
+            BufferWithExtendableBuffer *const dictBody) const;
+
+    bool writeBufferToFilePointer(FILE *const file,
+            const BufferWithExtendableBuffer *const buffer) const;
+
+    bool runGC(const int rootPtNodeArrayPos, BufferWithExtendableBuffer *const bufferToWrite);
+};
+} // namespace latinime
+#endif /* LATINIME_DYNAMIC_PATRICIA_TRIE_WRITING_HELPER_H */
diff --git a/native/jni/src/suggest/policyimpl/dictionary/dynamic_patricia_trie_writing_utils.cpp b/native/jni/src/suggest/policyimpl/dictionary/dynamic_patricia_trie_writing_utils.cpp
new file mode 100644
index 0000000..5a39837
--- /dev/null
+++ b/native/jni/src/suggest/policyimpl/dictionary/dynamic_patricia_trie_writing_utils.cpp
@@ -0,0 +1,137 @@
+/*
+ * Copyright (C) 2013, 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.
+ */
+
+#include "suggest/policyimpl/dictionary/dynamic_patricia_trie_writing_utils.h"
+
+#include <cstddef>
+#include <cstdlib>
+#include <stdint.h>
+
+#include "suggest/policyimpl/dictionary/utils/buffer_with_extendable_buffer.h"
+
+namespace latinime {
+
+const size_t DynamicPatriciaTrieWritingUtils::MAX_PTNODE_ARRAY_SIZE_TO_USE_SMALL_SIZE_FIELD = 0x7F;
+const size_t DynamicPatriciaTrieWritingUtils::MAX_PTNODE_ARRAY_SIZE = 0x7FFF;
+const int DynamicPatriciaTrieWritingUtils::SMALL_PTNODE_ARRAY_SIZE_FIELD_SIZE = 1;
+const int DynamicPatriciaTrieWritingUtils::LARGE_PTNODE_ARRAY_SIZE_FIELD_SIZE = 2;
+const int DynamicPatriciaTrieWritingUtils::LARGE_PTNODE_ARRAY_SIZE_FIELD_SIZE_FLAG = 0x8000;
+const int DynamicPatriciaTrieWritingUtils::DICT_OFFSET_FIELD_SIZE = 3;
+const int DynamicPatriciaTrieWritingUtils::MAX_DICT_OFFSET_VALUE = 0x7FFFFF;
+const int DynamicPatriciaTrieWritingUtils::MIN_DICT_OFFSET_VALUE = -0x7FFFFF;
+const int DynamicPatriciaTrieWritingUtils::DICT_OFFSET_NEGATIVE_FLAG = 0x800000;
+const int DynamicPatriciaTrieWritingUtils::PROBABILITY_FIELD_SIZE = 1;
+const int DynamicPatriciaTrieWritingUtils::NODE_FLAG_FIELD_SIZE = 1;
+
+/* static */ bool DynamicPatriciaTrieWritingUtils::writeForwardLinkPositionAndAdvancePosition(
+        BufferWithExtendableBuffer *const buffer, const int forwardLinkPos,
+        int *const forwardLinkFieldPos) {
+    return writeDictOffset(buffer, forwardLinkPos, (*forwardLinkFieldPos), forwardLinkFieldPos);
+}
+
+/* static */ bool DynamicPatriciaTrieWritingUtils::writePtNodeArraySizeAndAdvancePosition(
+        BufferWithExtendableBuffer *const buffer, const size_t arraySize,
+        int *const arraySizeFieldPos) {
+    // Currently, all array size field to be created has LARGE_PTNODE_ARRAY_SIZE_FIELD_SIZE to
+    // simplify updating process.
+    // TODO: Use SMALL_PTNODE_ARRAY_SIZE_FIELD_SIZE for small arrays.
+    /*if (arraySize <= MAX_PTNODE_ARRAY_SIZE_TO_USE_SMALL_SIZE_FIELD) {
+        return buffer->writeUintAndAdvancePosition(arraySize, SMALL_PTNODE_ARRAY_SIZE_FIELD_SIZE,
+                arraySizeFieldPos);
+    } else */
+    if (arraySize <= MAX_PTNODE_ARRAY_SIZE) {
+        uint32_t data = arraySize | LARGE_PTNODE_ARRAY_SIZE_FIELD_SIZE_FLAG;
+        return buffer->writeUintAndAdvancePosition(data, LARGE_PTNODE_ARRAY_SIZE_FIELD_SIZE,
+                arraySizeFieldPos);
+    } else {
+        AKLOGI("PtNode array size cannot be written because arraySize is too large: %zd",
+                arraySize);
+        ASSERT(false);
+        return false;
+    }
+}
+
+/* static */ bool DynamicPatriciaTrieWritingUtils::writeFlagsAndAdvancePosition(
+        BufferWithExtendableBuffer *const buffer,
+        const DynamicPatriciaTrieReadingUtils::NodeFlags nodeFlags, int *const nodeFlagsFieldPos) {
+    return buffer->writeUintAndAdvancePosition(nodeFlags, NODE_FLAG_FIELD_SIZE, nodeFlagsFieldPos);
+}
+
+// Note that parentOffset is offset from node's head position.
+/* static */ bool DynamicPatriciaTrieWritingUtils::writeParentPosOffsetAndAdvancePosition(
+        BufferWithExtendableBuffer *const buffer, const int parentPos, const int basePos,
+        int *const parentPosFieldPos) {
+    return writeDictOffset(buffer, parentPos, basePos, parentPosFieldPos);
+}
+
+/* static */ bool DynamicPatriciaTrieWritingUtils::writeCodePointsAndAdvancePosition(
+        BufferWithExtendableBuffer *const buffer, const int *const codePoints,
+        const int codePointCount, int *const codePointFieldPos) {
+    if (codePointCount <= 0) {
+        AKLOGI("code points cannot be written because codePointCount is invalid: %d",
+                codePointCount);
+        ASSERT(false);
+        return false;
+    }
+    const bool hasMultipleCodePoints = codePointCount > 1;
+    return buffer->writeCodePointsAndAdvancePosition(codePoints, codePointCount,
+            hasMultipleCodePoints, codePointFieldPos);
+}
+
+/* static */ bool DynamicPatriciaTrieWritingUtils::writeProbabilityAndAdvancePosition(
+        BufferWithExtendableBuffer *const buffer, const int probability,
+        int *const probabilityFieldPos) {
+    if (probability < 0 || probability > MAX_PROBABILITY) {
+        AKLOGI("probability cannot be written because the probability is invalid: %d",
+                probability);
+        ASSERT(false);
+        return false;
+    }
+    return buffer->writeUintAndAdvancePosition(probability, PROBABILITY_FIELD_SIZE,
+            probabilityFieldPos);
+}
+
+/* static */ bool DynamicPatriciaTrieWritingUtils::writeChildrenPositionAndAdvancePosition(
+        BufferWithExtendableBuffer *const buffer, const int childrenPosition,
+        int *const childrenPositionFieldPos) {
+    return writeDictOffset(buffer, childrenPosition, (*childrenPositionFieldPos),
+            childrenPositionFieldPos);
+}
+
+/* static */ bool DynamicPatriciaTrieWritingUtils::writeDictOffset(
+        BufferWithExtendableBuffer *const buffer, const int targetPos, const int basePos,
+        int *const offsetFieldPos) {
+    int offset = targetPos - basePos;
+    if (targetPos == NOT_A_DICT_POS) {
+        offset = DynamicPatriciaTrieReadingUtils::DICT_OFFSET_INVALID;
+    } else if (offset == 0) {
+        offset = DynamicPatriciaTrieReadingUtils::DICT_OFFSET_ZERO_OFFSET;
+    }
+    if (offset > MAX_DICT_OFFSET_VALUE || offset < MIN_DICT_OFFSET_VALUE) {
+        AKLOGI("offset cannot be written because the offset is too large or too small: %d",
+                offset);
+        ASSERT(false);
+        return false;
+    }
+    uint32_t data = 0;
+    if (offset >= 0) {
+        data = offset;
+    } else {
+        data = abs(offset) | DICT_OFFSET_NEGATIVE_FLAG;
+    }
+    return buffer->writeUintAndAdvancePosition(data, DICT_OFFSET_FIELD_SIZE, offsetFieldPos);
+}
+}
diff --git a/native/jni/src/suggest/policyimpl/dictionary/dynamic_patricia_trie_writing_utils.h b/native/jni/src/suggest/policyimpl/dictionary/dynamic_patricia_trie_writing_utils.h
new file mode 100644
index 0000000..a37e9fb
--- /dev/null
+++ b/native/jni/src/suggest/policyimpl/dictionary/dynamic_patricia_trie_writing_utils.h
@@ -0,0 +1,74 @@
+/*
+ * Copyright (C) 2013, 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.
+ */
+
+#ifndef LATINIME_DYNAMIC_PATRICIA_TRIE_WRITING_UTILS_H
+#define LATINIME_DYNAMIC_PATRICIA_TRIE_WRITING_UTILS_H
+
+#include <cstddef>
+
+#include "defines.h"
+#include "suggest/policyimpl/dictionary/dynamic_patricia_trie_reading_utils.h"
+
+namespace latinime {
+
+class BufferWithExtendableBuffer;
+
+class DynamicPatriciaTrieWritingUtils {
+ public:
+    static const int NODE_FLAG_FIELD_SIZE;
+
+    static bool writeForwardLinkPositionAndAdvancePosition(
+            BufferWithExtendableBuffer *const buffer, const int forwardLinkPos,
+            int *const forwardLinkFieldPos);
+
+    static bool writePtNodeArraySizeAndAdvancePosition(BufferWithExtendableBuffer *const buffer,
+            const size_t arraySize, int *const arraySizeFieldPos);
+
+    static bool writeFlagsAndAdvancePosition(BufferWithExtendableBuffer *const buffer,
+            const DynamicPatriciaTrieReadingUtils::NodeFlags nodeFlags,
+            int *const nodeFlagsFieldPos);
+
+    static bool writeParentPosOffsetAndAdvancePosition(BufferWithExtendableBuffer *const buffer,
+            const int parentPosition, const int basePos, int *const parentPosFieldPos);
+
+    static bool writeCodePointsAndAdvancePosition(BufferWithExtendableBuffer *const buffer,
+            const int *const codePoints, const int codePointCount, int *const codePointFieldPos);
+
+    static bool writeProbabilityAndAdvancePosition(BufferWithExtendableBuffer *const buffer,
+            const int probability, int *const probabilityFieldPos);
+
+    static bool writeChildrenPositionAndAdvancePosition(BufferWithExtendableBuffer *const buffer,
+            const int childrenPosition, int *const childrenPositionFieldPos);
+
+ private:
+    DISALLOW_IMPLICIT_CONSTRUCTORS(DynamicPatriciaTrieWritingUtils);
+
+    static const size_t MAX_PTNODE_ARRAY_SIZE_TO_USE_SMALL_SIZE_FIELD;
+    static const size_t MAX_PTNODE_ARRAY_SIZE;
+    static const int SMALL_PTNODE_ARRAY_SIZE_FIELD_SIZE;
+    static const int LARGE_PTNODE_ARRAY_SIZE_FIELD_SIZE;
+    static const int LARGE_PTNODE_ARRAY_SIZE_FIELD_SIZE_FLAG;
+    static const int DICT_OFFSET_FIELD_SIZE;
+    static const int MAX_DICT_OFFSET_VALUE;
+    static const int MIN_DICT_OFFSET_VALUE;
+    static const int DICT_OFFSET_NEGATIVE_FLAG;
+    static const int PROBABILITY_FIELD_SIZE;
+
+    static bool writeDictOffset(BufferWithExtendableBuffer *const buffer, const int targetPos,
+            const int basePos, int *const offsetFieldPos);
+};
+} // namespace latinime
+#endif /* LATINIME_DYNAMIC_PATRICIA_TRIE_WRITING_UTILS_H */
diff --git a/native/jni/src/suggest/policyimpl/dictionary/header/header_policy.cpp b/native/jni/src/suggest/policyimpl/dictionary/header/header_policy.cpp
new file mode 100644
index 0000000..47ace23
--- /dev/null
+++ b/native/jni/src/suggest/policyimpl/dictionary/header/header_policy.cpp
@@ -0,0 +1,178 @@
+/*
+ * Copyright (C) 2013, 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.
+ */
+
+#include "suggest/policyimpl/dictionary/header/header_policy.h"
+
+#include <cstddef>
+#include <cstdio>
+#include <ctime>
+
+namespace latinime {
+
+const char *const HeaderPolicy::MULTIPLE_WORDS_DEMOTION_RATE_KEY = "MULTIPLE_WORDS_DEMOTION_RATE";
+const char *const HeaderPolicy::USES_FORGETTING_CURVE_KEY = "USES_FORGETTING_CURVE";
+const char *const HeaderPolicy::LAST_UPDATED_TIME_KEY = "date";
+const float HeaderPolicy::DEFAULT_MULTIPLE_WORD_COST_MULTIPLIER = 1.0f;
+const float HeaderPolicy::MULTIPLE_WORD_COST_MULTIPLIER_SCALE = 100.0f;
+
+// Used for logging. Question mark is used to indicate that the key is not found.
+void HeaderPolicy::readHeaderValueOrQuestionMark(const char *const key, int *outValue,
+        int outValueSize) const {
+    if (outValueSize <= 0) return;
+    if (outValueSize == 1) {
+        outValue[0] = '\0';
+        return;
+    }
+    std::vector<int> keyCodePointVector;
+    insertCharactersIntoVector(key, &keyCodePointVector);
+    HeaderReadWriteUtils::AttributeMap::const_iterator it = mAttributeMap.find(keyCodePointVector);
+    if (it == mAttributeMap.end()) {
+        // The key was not found.
+        outValue[0] = '?';
+        outValue[1] = '\0';
+        return;
+    }
+    const int terminalIndex = min(static_cast<int>(it->second.size()), outValueSize - 1);
+    for (int i = 0; i < terminalIndex; ++i) {
+        outValue[i] = it->second[i];
+    }
+    outValue[terminalIndex] = '\0';
+}
+
+float HeaderPolicy::readMultipleWordCostMultiplier() const {
+    int attributeValue = 0;
+    if (getAttributeValueAsInt(MULTIPLE_WORDS_DEMOTION_RATE_KEY, &attributeValue)) {
+        if (attributeValue <= 0) {
+            return static_cast<float>(MAX_VALUE_FOR_WEIGHTING);
+        }
+        return MULTIPLE_WORD_COST_MULTIPLIER_SCALE / static_cast<float>(attributeValue);
+    } else {
+        return DEFAULT_MULTIPLE_WORD_COST_MULTIPLIER;
+    }
+}
+
+bool HeaderPolicy::readUsesForgettingCurveFlag() const {
+    int attributeValue = 0;
+    if (getAttributeValueAsInt(USES_FORGETTING_CURVE_KEY, &attributeValue)) {
+        return attributeValue != 0;
+    } else {
+        return false;
+    }
+}
+
+// Returns S_INT_MIN when the key is not found or the value is invalid.
+int HeaderPolicy::readLastUpdatedTime() const {
+    int attributeValue = 0;
+    if (getAttributeValueAsInt(LAST_UPDATED_TIME_KEY, &attributeValue)) {
+        return attributeValue;
+    } else {
+        return S_INT_MIN;
+    }
+}
+
+// Returns whether the key is found or not and stores the found value into outValue.
+bool HeaderPolicy::getAttributeValueAsInt(const char *const key, int *const outValue) const {
+    std::vector<int> keyVector;
+    insertCharactersIntoVector(key, &keyVector);
+    HeaderReadWriteUtils::AttributeMap::const_iterator it = mAttributeMap.find(keyVector);
+    if (it == mAttributeMap.end()) {
+        // The key was not found.
+        return false;
+    }
+    *outValue = parseIntAttributeValue(&(it->second));
+    return true;
+}
+
+bool HeaderPolicy::writeHeaderToBuffer(BufferWithExtendableBuffer *const bufferToWrite,
+        const bool updatesLastUpdatedTime) const {
+    int writingPos = 0;
+    if (!HeaderReadWriteUtils::writeDictionaryVersion(bufferToWrite, mDictFormatVersion,
+            &writingPos)) {
+        return false;
+    }
+    if (!HeaderReadWriteUtils::writeDictionaryFlags(bufferToWrite, mDictionaryFlags,
+            &writingPos)) {
+        return false;
+    }
+    // Temporarily writes a dummy header size.
+    int headerSizeFieldPos = writingPos;
+    if (!HeaderReadWriteUtils::writeDictionaryHeaderSize(bufferToWrite, 0 /* size */,
+            &writingPos)) {
+        return false;
+    }
+    if (updatesLastUpdatedTime) {
+        // Set current time as a last updated time.
+        HeaderReadWriteUtils::AttributeMap attributeMapTowrite(mAttributeMap);
+        std::vector<int> updatedTimekey;
+        insertCharactersIntoVector(LAST_UPDATED_TIME_KEY, &updatedTimekey);
+        const time_t currentTime = time(NULL);
+        std::vector<int> updatedTimeValue;
+        char charBuf[LARGEST_INT_DIGIT_COUNT + 1];
+        snprintf(charBuf, LARGEST_INT_DIGIT_COUNT + 1, "%ld", currentTime);
+        insertCharactersIntoVector(charBuf, &updatedTimeValue);
+        attributeMapTowrite[updatedTimekey] = updatedTimeValue;
+        if (!HeaderReadWriteUtils::writeHeaderAttributes(bufferToWrite, &attributeMapTowrite,
+                &writingPos)) {
+            return false;
+        }
+    } else {
+        if (!HeaderReadWriteUtils::writeHeaderAttributes(bufferToWrite, &mAttributeMap,
+                &writingPos)) {
+            return false;
+        }
+    }
+    // Writes an actual header size.
+    if (!HeaderReadWriteUtils::writeDictionaryHeaderSize(bufferToWrite, writingPos,
+            &headerSizeFieldPos)) {
+        return false;
+    }
+    return true;
+}
+
+/* static */ HeaderReadWriteUtils::AttributeMap
+        HeaderPolicy::createAttributeMapAndReadAllAttributes(const uint8_t *const dictBuf) {
+    HeaderReadWriteUtils::AttributeMap attributeMap;
+    HeaderReadWriteUtils::fetchAllHeaderAttributes(dictBuf, &attributeMap);
+    return attributeMap;
+}
+
+/* static */ int HeaderPolicy::parseIntAttributeValue(
+        const std::vector<int> *const attributeValue) {
+    int value = 0;
+    bool isNegative = false;
+    for (size_t i = 0; i < attributeValue->size(); ++i) {
+        if (i == 0 && attributeValue->at(i) == '-') {
+            isNegative = true;
+        } else {
+            if (!isdigit(attributeValue->at(i))) {
+                // If not a number, return S_INT_MIN
+                return S_INT_MIN;
+            }
+            value *= 10;
+            value += attributeValue->at(i) - '0';
+        }
+    }
+    return isNegative ? -value : value;
+}
+
+/* static */ void HeaderPolicy::insertCharactersIntoVector(const char *const characters,
+        std::vector<int> *const vector) {
+    for (int i = 0; characters[i]; ++i) {
+        vector->push_back(characters[i]);
+    }
+}
+
+} // namespace latinime
diff --git a/native/jni/src/suggest/policyimpl/dictionary/header/header_policy.h b/native/jni/src/suggest/policyimpl/dictionary/header/header_policy.h
new file mode 100644
index 0000000..6b396f3
--- /dev/null
+++ b/native/jni/src/suggest/policyimpl/dictionary/header/header_policy.h
@@ -0,0 +1,113 @@
+/*
+ * Copyright (C) 2013, 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.
+ */
+
+#ifndef LATINIME_HEADER_POLICY_H
+#define LATINIME_HEADER_POLICY_H
+
+#include <cctype>
+#include <stdint.h>
+
+#include "defines.h"
+#include "suggest/core/policy/dictionary_header_structure_policy.h"
+#include "suggest/policyimpl/dictionary/header/header_read_write_utils.h"
+#include "suggest/policyimpl/dictionary/utils/format_utils.h"
+
+namespace latinime {
+
+class HeaderPolicy : public DictionaryHeaderStructurePolicy {
+ public:
+    explicit HeaderPolicy(const uint8_t *const dictBuf, const int dictSize)
+            : mDictBuf(dictBuf),
+              mDictFormatVersion(FormatUtils::detectFormatVersion(dictBuf, dictSize)),
+              mDictionaryFlags(HeaderReadWriteUtils::getFlags(dictBuf)),
+              mSize(HeaderReadWriteUtils::getHeaderSize(dictBuf)),
+              mAttributeMap(createAttributeMapAndReadAllAttributes(mDictBuf)),
+              mMultiWordCostMultiplier(readMultipleWordCostMultiplier()),
+              mUsesForgettingCurve(readUsesForgettingCurveFlag()),
+              mLastUpdatedTime(readLastUpdatedTime()) {}
+
+    ~HeaderPolicy() {}
+
+    AK_FORCE_INLINE int getSize() const {
+        return mSize;
+    }
+
+    AK_FORCE_INLINE bool supportsDynamicUpdate() const {
+        return HeaderReadWriteUtils::supportsDynamicUpdate(mDictionaryFlags);
+    }
+
+    AK_FORCE_INLINE bool requiresGermanUmlautProcessing() const {
+        return HeaderReadWriteUtils::requiresGermanUmlautProcessing(mDictionaryFlags);
+    }
+
+    AK_FORCE_INLINE bool requiresFrenchLigatureProcessing() const {
+        return HeaderReadWriteUtils::requiresFrenchLigatureProcessing(mDictionaryFlags);
+    }
+
+    AK_FORCE_INLINE float getMultiWordCostMultiplier() const {
+        return mMultiWordCostMultiplier;
+    }
+
+    AK_FORCE_INLINE bool usesForgettingCurve() const {
+        return mUsesForgettingCurve;
+    }
+
+    AK_FORCE_INLINE int getLastUpdatedTime() const {
+        return mLastUpdatedTime;
+    }
+
+    void readHeaderValueOrQuestionMark(const char *const key,
+            int *outValue, int outValueSize) const;
+
+    bool writeHeaderToBuffer(BufferWithExtendableBuffer *const bufferToWrite,
+            const bool updatesLastUpdatedTime) const;
+
+ private:
+    DISALLOW_IMPLICIT_CONSTRUCTORS(HeaderPolicy);
+
+    static const char *const MULTIPLE_WORDS_DEMOTION_RATE_KEY;
+    static const char *const USES_FORGETTING_CURVE_KEY;
+    static const char *const LAST_UPDATED_TIME_KEY;
+    static const float DEFAULT_MULTIPLE_WORD_COST_MULTIPLIER;
+    static const float MULTIPLE_WORD_COST_MULTIPLIER_SCALE;
+
+    const uint8_t *const mDictBuf;
+    const FormatUtils::FORMAT_VERSION mDictFormatVersion;
+    const HeaderReadWriteUtils::DictionaryFlags mDictionaryFlags;
+    const int mSize;
+    HeaderReadWriteUtils::AttributeMap mAttributeMap;
+    const float mMultiWordCostMultiplier;
+    const bool mUsesForgettingCurve;
+    const int mLastUpdatedTime;
+
+    float readMultipleWordCostMultiplier() const;
+
+    bool readUsesForgettingCurveFlag() const;
+
+    int readLastUpdatedTime() const;
+
+    bool getAttributeValueAsInt(const char *const key, int *const outValue) const;
+
+    static HeaderReadWriteUtils::AttributeMap createAttributeMapAndReadAllAttributes(
+            const uint8_t *const dictBuf);
+
+    static int parseIntAttributeValue(const std::vector<int> *const attributeValue);
+
+    static void insertCharactersIntoVector(
+            const char *const characters, std::vector<int> *const vector);
+};
+} // namespace latinime
+#endif /* LATINIME_HEADER_POLICY_H */
diff --git a/native/jni/src/suggest/policyimpl/dictionary/header/header_read_write_utils.cpp b/native/jni/src/suggest/policyimpl/dictionary/header/header_read_write_utils.cpp
new file mode 100644
index 0000000..80fe886
--- /dev/null
+++ b/native/jni/src/suggest/policyimpl/dictionary/header/header_read_write_utils.cpp
@@ -0,0 +1,131 @@
+/*
+ * Copyright (C) 2013, 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.
+ */
+
+#include "suggest/policyimpl/dictionary/header/header_read_write_utils.h"
+
+#include <vector>
+
+#include "defines.h"
+#include "suggest/policyimpl/dictionary/utils/buffer_with_extendable_buffer.h"
+#include "suggest/policyimpl/dictionary/utils/byte_array_utils.h"
+
+namespace latinime {
+
+const int HeaderReadWriteUtils::MAX_ATTRIBUTE_KEY_LENGTH = 256;
+const int HeaderReadWriteUtils::MAX_ATTRIBUTE_VALUE_LENGTH = 256;
+
+const int HeaderReadWriteUtils::HEADER_MAGIC_NUMBER_SIZE = 4;
+const int HeaderReadWriteUtils::HEADER_DICTIONARY_VERSION_SIZE = 2;
+const int HeaderReadWriteUtils::HEADER_FLAG_SIZE = 2;
+const int HeaderReadWriteUtils::HEADER_SIZE_FIELD_SIZE = 4;
+
+const HeaderReadWriteUtils::DictionaryFlags HeaderReadWriteUtils::NO_FLAGS = 0;
+// Flags for special processing
+// Those *must* match the flags in makedict (FormatSpec#*_PROCESSING_FLAG) or
+// something very bad (like, the apocalypse) will happen. Please update both at the same time.
+const HeaderReadWriteUtils::DictionaryFlags
+        HeaderReadWriteUtils::GERMAN_UMLAUT_PROCESSING_FLAG = 0x1;
+const HeaderReadWriteUtils::DictionaryFlags
+        HeaderReadWriteUtils::SUPPORTS_DYNAMIC_UPDATE_FLAG = 0x2;
+const HeaderReadWriteUtils::DictionaryFlags
+        HeaderReadWriteUtils::FRENCH_LIGATURE_PROCESSING_FLAG = 0x4;
+
+/* static */ int HeaderReadWriteUtils::getHeaderSize(const uint8_t *const dictBuf) {
+    // See the format of the header in the comment in
+    // BinaryDictionaryFormatUtils::detectFormatVersion()
+    return ByteArrayUtils::readUint32(dictBuf, HEADER_MAGIC_NUMBER_SIZE
+            + HEADER_DICTIONARY_VERSION_SIZE + HEADER_FLAG_SIZE);
+}
+
+/* static */ HeaderReadWriteUtils::DictionaryFlags
+        HeaderReadWriteUtils::getFlags(const uint8_t *const dictBuf) {
+    return ByteArrayUtils::readUint16(dictBuf,
+            HEADER_MAGIC_NUMBER_SIZE + HEADER_DICTIONARY_VERSION_SIZE);
+}
+
+/* static */ void HeaderReadWriteUtils::fetchAllHeaderAttributes(const uint8_t *const dictBuf,
+        AttributeMap *const headerAttributes) {
+    const int headerSize = getHeaderSize(dictBuf);
+    int pos = getHeaderOptionsPosition();
+    if (pos == NOT_A_DICT_POS) {
+        // The header doesn't have header options.
+        return;
+    }
+    int keyBuffer[MAX_ATTRIBUTE_KEY_LENGTH];
+    int valueBuffer[MAX_ATTRIBUTE_VALUE_LENGTH];
+    while (pos < headerSize) {
+        const int keyLength = ByteArrayUtils::readStringAndAdvancePosition(dictBuf,
+                MAX_ATTRIBUTE_KEY_LENGTH, keyBuffer, &pos);
+        std::vector<int> key;
+        key.insert(key.end(), keyBuffer, keyBuffer + keyLength);
+        const int valueLength = ByteArrayUtils::readStringAndAdvancePosition(dictBuf,
+                MAX_ATTRIBUTE_VALUE_LENGTH, valueBuffer, &pos);
+        std::vector<int> value;
+        value.insert(value.end(), valueBuffer, valueBuffer + valueLength);
+        headerAttributes->insert(AttributeMap::value_type(key, value));
+    }
+}
+
+/* static */ bool HeaderReadWriteUtils::writeDictionaryVersion(
+        BufferWithExtendableBuffer *const buffer, const FormatUtils::FORMAT_VERSION version,
+        int *const writingPos) {
+    if (!buffer->writeUintAndAdvancePosition(FormatUtils::MAGIC_NUMBER, HEADER_MAGIC_NUMBER_SIZE,
+            writingPos)) {
+        return false;
+    }
+    switch (version) {
+        case FormatUtils::VERSION_2:
+            // Version 2 dictionary writing is not supported.
+            return false;
+        case FormatUtils::VERSION_3:
+            return buffer->writeUintAndAdvancePosition(3 /* data */,
+                    HEADER_DICTIONARY_VERSION_SIZE, writingPos);
+        default:
+            return false;
+    }
+}
+
+/* static */ bool HeaderReadWriteUtils::writeDictionaryFlags(
+        BufferWithExtendableBuffer *const buffer, const DictionaryFlags flags,
+        int *const writingPos) {
+    return buffer->writeUintAndAdvancePosition(flags, HEADER_FLAG_SIZE, writingPos);
+}
+
+/* static */ bool HeaderReadWriteUtils::writeDictionaryHeaderSize(
+        BufferWithExtendableBuffer *const buffer, const int size, int *const writingPos) {
+    return buffer->writeUintAndAdvancePosition(size, HEADER_SIZE_FIELD_SIZE, writingPos);
+}
+
+/* static */ bool HeaderReadWriteUtils::writeHeaderAttributes(
+        BufferWithExtendableBuffer *const buffer, const AttributeMap *const headerAttributes,
+        int *const writingPos) {
+    for (AttributeMap::const_iterator it = headerAttributes->begin();
+            it != headerAttributes->end(); ++it) {
+        // Write a key.
+        if (!buffer->writeCodePointsAndAdvancePosition(&(it->first.at(0)), it->first.size(),
+                true /* writesTerminator */, writingPos)) {
+            return false;
+        }
+        // Write a value.
+        if (!buffer->writeCodePointsAndAdvancePosition(&(it->second.at(0)), it->second.size(),
+                true /* writesTerminator */, writingPos)) {
+            return false;
+        }
+    }
+    return true;
+}
+
+} // namespace latinime
diff --git a/native/jni/src/suggest/policyimpl/dictionary/header/header_read_write_utils.h b/native/jni/src/suggest/policyimpl/dictionary/header/header_read_write_utils.h
new file mode 100644
index 0000000..6cce733
--- /dev/null
+++ b/native/jni/src/suggest/policyimpl/dictionary/header/header_read_write_utils.h
@@ -0,0 +1,93 @@
+/*
+ * Copyright (C) 2013, 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.
+ */
+
+#ifndef LATINIME_HEADER_READ_WRITE_UTILS_H
+#define LATINIME_HEADER_READ_WRITE_UTILS_H
+
+#include <map>
+#include <stdint.h>
+#include <vector>
+
+#include "defines.h"
+#include "suggest/policyimpl/dictionary/utils/format_utils.h"
+
+namespace latinime {
+
+class BufferWithExtendableBuffer;
+
+class HeaderReadWriteUtils {
+ public:
+    typedef uint16_t DictionaryFlags;
+    typedef std::map<std::vector<int>, std::vector<int> > AttributeMap;
+
+    static int getHeaderSize(const uint8_t *const dictBuf);
+
+    static DictionaryFlags getFlags(const uint8_t *const dictBuf);
+
+    static AK_FORCE_INLINE bool supportsDynamicUpdate(const DictionaryFlags flags) {
+        return (flags & SUPPORTS_DYNAMIC_UPDATE_FLAG) != 0;
+    }
+
+    static AK_FORCE_INLINE bool requiresGermanUmlautProcessing(const DictionaryFlags flags) {
+        return (flags & GERMAN_UMLAUT_PROCESSING_FLAG) != 0;
+    }
+
+    static AK_FORCE_INLINE bool requiresFrenchLigatureProcessing(const DictionaryFlags flags) {
+        return (flags & FRENCH_LIGATURE_PROCESSING_FLAG) != 0;
+    }
+
+    static AK_FORCE_INLINE int getHeaderOptionsPosition() {
+        return HEADER_MAGIC_NUMBER_SIZE + HEADER_DICTIONARY_VERSION_SIZE + HEADER_FLAG_SIZE
+                + HEADER_SIZE_FIELD_SIZE;
+    }
+
+    static void fetchAllHeaderAttributes(const uint8_t *const dictBuf,
+            AttributeMap *const headerAttributes);
+
+    static bool writeDictionaryVersion(BufferWithExtendableBuffer *const buffer,
+            const FormatUtils::FORMAT_VERSION version, int *const writingPos);
+
+    static bool writeDictionaryFlags(BufferWithExtendableBuffer *const buffer,
+            const DictionaryFlags flags, int *const writingPos);
+
+    static bool writeDictionaryHeaderSize(BufferWithExtendableBuffer *const buffer,
+            const int size, int *const writingPos);
+
+    static bool writeHeaderAttributes(BufferWithExtendableBuffer *const buffer,
+            const AttributeMap *const headerAttributes, int *const writingPos);
+
+ private:
+    DISALLOW_IMPLICIT_CONSTRUCTORS(HeaderReadWriteUtils);
+
+    static const int MAX_ATTRIBUTE_KEY_LENGTH;
+    static const int MAX_ATTRIBUTE_VALUE_LENGTH;
+
+    static const int HEADER_MAGIC_NUMBER_SIZE;
+    static const int HEADER_DICTIONARY_VERSION_SIZE;
+    static const int HEADER_FLAG_SIZE;
+    static const int HEADER_SIZE_FIELD_SIZE;
+
+    static const DictionaryFlags NO_FLAGS;
+    // Flags for special processing
+    // Those *must* match the flags in makedict (FormatSpec#*_PROCESSING_FLAGS) or
+    // something very bad (like, the apocalypse) will happen. Please update both at the same time.
+    static const DictionaryFlags GERMAN_UMLAUT_PROCESSING_FLAG;
+    static const DictionaryFlags SUPPORTS_DYNAMIC_UPDATE_FLAG;
+    static const DictionaryFlags FRENCH_LIGATURE_PROCESSING_FLAG;
+    static const DictionaryFlags CONTAINS_BIGRAMS_FLAG;
+};
+}
+#endif /* LATINIME_HEADER_READ_WRITE_UTILS_H */
diff --git a/native/jni/src/suggest/policyimpl/dictionary/patricia_trie_policy.cpp b/native/jni/src/suggest/policyimpl/dictionary/patricia_trie_policy.cpp
index 097f7c8..5269795 100644
--- a/native/jni/src/suggest/policyimpl/dictionary/patricia_trie_policy.cpp
+++ b/native/jni/src/suggest/policyimpl/dictionary/patricia_trie_policy.cpp
@@ -20,55 +20,312 @@
 #include "defines.h"
 #include "suggest/core/dicnode/dic_node.h"
 #include "suggest/core/dicnode/dic_node_vector.h"
-#include "suggest/core/dictionary/binary_dictionary_info.h"
-#include "suggest/core/dictionary/binary_dictionary_terminal_attributes_reading_utils.h"
-#include "suggest/policyimpl/dictionary/binary_format.h"
 #include "suggest/policyimpl/dictionary/patricia_trie_reading_utils.h"
+#include "suggest/policyimpl/dictionary/utils/probability_utils.h"
 
 namespace latinime {
 
-const PatriciaTriePolicy PatriciaTriePolicy::sInstance;
-
 void PatriciaTriePolicy::createAndGetAllChildNodes(const DicNode *const dicNode,
-        const BinaryDictionaryInfo *const binaryDictionaryInfo,
-        const NodeFilter *const nodeFilter, DicNodeVector *const childDicNodes) const {
+        DicNodeVector *const childDicNodes) const {
     if (!dicNode->hasChildren()) {
         return;
     }
     int nextPos = dicNode->getChildrenPos();
-    const int childCount = PatriciaTrieReadingUtils::getGroupCountAndAdvancePosition(
-            binaryDictionaryInfo->getDictRoot(), &nextPos);
+    const int childCount = PatriciaTrieReadingUtils::getPtNodeArraySizeAndAdvancePosition(
+            mDictRoot, &nextPos);
     for (int i = 0; i < childCount; i++) {
-        nextPos = createAndGetLeavingChildNode(dicNode, nextPos, binaryDictionaryInfo,
-                nodeFilter, childDicNodes);
+        nextPos = createAndGetLeavingChildNode(dicNode, nextPos, childDicNodes);
     }
 }
 
+// This retrieves code points and the probability of the word by its terminal position.
+// Due to the fact that words are ordered in the dictionary in a strict breadth-first order,
+// it is possible to check for this with advantageous complexity. For each node, we search
+// for PtNodes with children and compare the children position with the position we look for.
+// When we shoot the position we look for, it means the word we look for is in the children
+// of the previous PtNode. The only tricky part is the fact that if we arrive at the end of a
+// PtNode array with the last PtNode's children position still less than what we are searching for,
+// we must descend the last PtNode's children (for example, if the word we are searching for starts
+// with a z, it's the last PtNode of the root array, so all children addresses will be smaller
+// than the position we look for, and we have to descend the z node).
+/* Parameters :
+ * ptNodePos: the byte position of the terminal PtNode of the word we are searching for (this is
+ *   what is stored as the "bigram position" in each bigram)
+ * outCodePoints: an array to write the found word, with MAX_WORD_LENGTH size.
+ * outUnigramProbability: a pointer to an int to write the probability into.
+ * Return value : the code point count, of 0 if the word was not found.
+ */
+// TODO: Split this function to be more readable
 int PatriciaTriePolicy::getCodePointsAndProbabilityAndReturnCodePointCount(
-        const BinaryDictionaryInfo *const binaryDictionaryInfo,
-        const int nodePos, const int maxCodePointCount, int *const outCodePoints,
+        const int ptNodePos, const int maxCodePointCount, int *const outCodePoints,
         int *const outUnigramProbability) const {
-    return BinaryFormat::getCodePointsAndProbabilityAndReturnCodePointCount(
-            binaryDictionaryInfo->getDictRoot(), nodePos,
-            maxCodePointCount, outCodePoints, outUnigramProbability);
+    int pos = getRootPosition();
+    int wordPos = 0;
+    // One iteration of the outer loop iterates through PtNode arrays. As stated above, we will
+    // only traverse nodes that are actually a part of the terminal we are searching, so each time
+    // we enter this loop we are one depth level further than last time.
+    // The only reason we count nodes is because we want to reduce the probability of infinite
+    // looping in case there is a bug. Since we know there is an upper bound to the depth we are
+    // supposed to traverse, it does not hurt to count iterations.
+    for (int loopCount = maxCodePointCount; loopCount > 0; --loopCount) {
+        int lastCandidatePtNodePos = 0;
+        // Let's loop through PtNodes in this PtNode array searching for either the terminal
+        // or one of its ascendants.
+        for (int ptNodeCount = PatriciaTrieReadingUtils::getPtNodeArraySizeAndAdvancePosition(
+                mDictRoot, &pos); ptNodeCount > 0; --ptNodeCount) {
+            const int startPos = pos;
+            const PatriciaTrieReadingUtils::NodeFlags flags =
+                    PatriciaTrieReadingUtils::getFlagsAndAdvancePosition(mDictRoot, &pos);
+            const int character = PatriciaTrieReadingUtils::getCodePointAndAdvancePosition(
+                    mDictRoot, &pos);
+            if (ptNodePos == startPos) {
+                // We found the position. Copy the rest of the code points in the buffer and return
+                // the length.
+                outCodePoints[wordPos] = character;
+                if (PatriciaTrieReadingUtils::hasMultipleChars(flags)) {
+                    int nextChar = PatriciaTrieReadingUtils::getCodePointAndAdvancePosition(
+                            mDictRoot, &pos);
+                    // We count code points in order to avoid infinite loops if the file is broken
+                    // or if there is some other bug
+                    int charCount = maxCodePointCount;
+                    while (NOT_A_CODE_POINT != nextChar && --charCount > 0) {
+                        outCodePoints[++wordPos] = nextChar;
+                        nextChar = PatriciaTrieReadingUtils::getCodePointAndAdvancePosition(
+                                mDictRoot, &pos);
+                    }
+                }
+                *outUnigramProbability =
+                        PatriciaTrieReadingUtils::readProbabilityAndAdvancePosition(mDictRoot,
+                                &pos);
+                return ++wordPos;
+            }
+            // We need to skip past this PtNode, so skip any remaining code points after the
+            // first and possibly the probability.
+            if (PatriciaTrieReadingUtils::hasMultipleChars(flags)) {
+                PatriciaTrieReadingUtils::skipCharacters(mDictRoot, flags, MAX_WORD_LENGTH, &pos);
+            }
+            if (PatriciaTrieReadingUtils::isTerminal(flags)) {
+                PatriciaTrieReadingUtils::readProbabilityAndAdvancePosition(mDictRoot, &pos);
+            }
+            // The fact that this PtNode has children is very important. Since we already know
+            // that this PtNode does not match, if it has no children we know it is irrelevant
+            // to what we are searching for.
+            const bool hasChildren = PatriciaTrieReadingUtils::hasChildrenInFlags(flags);
+            // We will write in `found' whether we have passed the children position we are
+            // searching for. For example if we search for "beer", the children of b are less
+            // than the address we are searching for and the children of c are greater. When we
+            // come here for c, we realize this is too big, and that we should descend b.
+            bool found;
+            if (hasChildren) {
+                int currentPos = pos;
+                // Here comes the tricky part. First, read the children position.
+                const int childrenPos = PatriciaTrieReadingUtils
+                        ::readChildrenPositionAndAdvancePosition(mDictRoot, flags, &currentPos);
+                if (childrenPos > ptNodePos) {
+                    // If the children pos is greater than the position, it means the previous
+                    // PtNode, which position is stored in lastCandidatePtNodePos, was the right
+                    // one.
+                    found = true;
+                } else if (1 >= ptNodeCount) {
+                    // However if we are on the LAST PtNode of this array, and we have NOT shot the
+                    // position we should descend THIS node. So we trick the lastCandidatePtNodePos
+                    // so that we will descend this PtNode, not the previous one.
+                    lastCandidatePtNodePos = startPos;
+                    found = true;
+                } else {
+                    // Else, we should continue looking.
+                    found = false;
+                }
+            } else {
+                // Even if we don't have children here, we could still be on the last PtNode of /
+                // this array. If this is the case, we should descend the last PtNode that had
+                // children, and their position is already in lastCandidatePtNodePos.
+                found = (1 >= ptNodeCount);
+            }
+
+            if (found) {
+                // Okay, we found the PtNode we should descend. Its position is in
+                // the lastCandidatePtNodePos variable, so we just re-read it.
+                if (0 != lastCandidatePtNodePos) {
+                    const PatriciaTrieReadingUtils::NodeFlags lastFlags =
+                            PatriciaTrieReadingUtils::getFlagsAndAdvancePosition(
+                                    mDictRoot, &lastCandidatePtNodePos);
+                    const int lastChar = PatriciaTrieReadingUtils::getCodePointAndAdvancePosition(
+                            mDictRoot, &lastCandidatePtNodePos);
+                    // We copy all the characters in this PtNode to the buffer
+                    outCodePoints[wordPos] = lastChar;
+                    if (PatriciaTrieReadingUtils::hasMultipleChars(lastFlags)) {
+                        int nextChar = PatriciaTrieReadingUtils::getCodePointAndAdvancePosition(
+                                mDictRoot, &lastCandidatePtNodePos);
+                        int charCount = maxCodePointCount;
+                        while (-1 != nextChar && --charCount > 0) {
+                            outCodePoints[++wordPos] = nextChar;
+                            nextChar = PatriciaTrieReadingUtils::getCodePointAndAdvancePosition(
+                                    mDictRoot, &lastCandidatePtNodePos);
+                        }
+                    }
+                    ++wordPos;
+                    // Now we only need to branch to the children address. Skip the probability if
+                    // it's there, read pos, and break to resume the search at pos.
+                    if (PatriciaTrieReadingUtils::isTerminal(lastFlags)) {
+                        PatriciaTrieReadingUtils::readProbabilityAndAdvancePosition(mDictRoot,
+                                &lastCandidatePtNodePos);
+                    }
+                    pos = PatriciaTrieReadingUtils::readChildrenPositionAndAdvancePosition(
+                            mDictRoot, lastFlags, &lastCandidatePtNodePos);
+                    break;
+                } else {
+                    // Here is a little tricky part: we come here if we found out that all children
+                    // addresses in this PtNode are bigger than the address we are searching for.
+                    // Should we conclude the word is not in the dictionary? No! It could still be
+                    // one of the remaining PtNodes in this array, so we have to keep looking in
+                    // this array until we find it (or we realize it's not there either, in which
+                    // case it's actually not in the dictionary). Pass the end of this PtNode,
+                    // ready to start the next one.
+                    if (PatriciaTrieReadingUtils::hasChildrenInFlags(flags)) {
+                        PatriciaTrieReadingUtils::readChildrenPositionAndAdvancePosition(
+                                mDictRoot, flags, &pos);
+                    }
+                    if (PatriciaTrieReadingUtils::hasShortcutTargets(flags)) {
+                        mShortcutListPolicy.skipAllShortcuts(&pos);
+                    }
+                    if (PatriciaTrieReadingUtils::hasBigrams(flags)) {
+                        mBigramListPolicy.skipAllBigrams(&pos);
+                    }
+                }
+            } else {
+                // If we did not find it, we should record the last children address for the next
+                // iteration.
+                if (hasChildren) lastCandidatePtNodePos = startPos;
+                // Now skip the end of this PtNode (children pos and the attributes if any) so that
+                // our pos is after the end of this PtNode, at the start of the next one.
+                if (PatriciaTrieReadingUtils::hasChildrenInFlags(flags)) {
+                    PatriciaTrieReadingUtils::readChildrenPositionAndAdvancePosition(
+                            mDictRoot, flags, &pos);
+                }
+                if (PatriciaTrieReadingUtils::hasShortcutTargets(flags)) {
+                    mShortcutListPolicy.skipAllShortcuts(&pos);
+                }
+                if (PatriciaTrieReadingUtils::hasBigrams(flags)) {
+                    mBigramListPolicy.skipAllBigrams(&pos);
+                }
+            }
+
+        }
+    }
+    // If we have looked through all the PtNodes and found no match, the ptNodePos is
+    // not the position of a terminal in this dictionary.
+    return 0;
 }
 
-int PatriciaTriePolicy::getTerminalNodePositionOfWord(
-        const BinaryDictionaryInfo *const binaryDictionaryInfo, const int *const inWord,
+// This function gets the position of the terminal node of the exact matching word in the
+// dictionary. If no match is found, it returns NOT_A_DICT_POS.
+int PatriciaTriePolicy::getTerminalNodePositionOfWord(const int *const inWord,
         const int length, const bool forceLowerCaseSearch) const {
-    return BinaryFormat::getTerminalPosition(binaryDictionaryInfo->getDictRoot(), inWord,
-            length, forceLowerCaseSearch);
+    int pos = getRootPosition();
+    int wordPos = 0;
+
+    while (true) {
+        // If we already traversed the tree further than the word is long, there means
+        // there was no match (or we would have found it).
+        if (wordPos >= length) return NOT_A_DICT_POS;
+        int ptNodeCount = PatriciaTrieReadingUtils::getPtNodeArraySizeAndAdvancePosition(mDictRoot,
+                &pos);
+        const int wChar = forceLowerCaseSearch
+                ? CharUtils::toLowerCase(inWord[wordPos]) : inWord[wordPos];
+        while (true) {
+            // If there are no more PtNodes in this array, it means we could not
+            // find a matching character for this depth, therefore there is no match.
+            if (0 >= ptNodeCount) return NOT_A_DICT_POS;
+            const int ptNodePos = pos;
+            const PatriciaTrieReadingUtils::NodeFlags flags =
+                    PatriciaTrieReadingUtils::getFlagsAndAdvancePosition(mDictRoot, &pos);
+            int character = PatriciaTrieReadingUtils::getCodePointAndAdvancePosition(mDictRoot,
+                    &pos);
+            if (character == wChar) {
+                // This is the correct PtNode. Only one PtNode may start with the same char within
+                // a PtNode array, so either we found our match in this array, or there is
+                // no match and we can return NOT_A_DICT_POS. So we will check all the
+                // characters in this PtNode indeed does match.
+                if (PatriciaTrieReadingUtils::hasMultipleChars(flags)) {
+                    character = PatriciaTrieReadingUtils::getCodePointAndAdvancePosition(mDictRoot,
+                            &pos);
+                    while (NOT_A_CODE_POINT != character) {
+                        ++wordPos;
+                        // If we shoot the length of the word we search for, or if we find a single
+                        // character that does not match, as explained above, it means the word is
+                        // not in the dictionary (by virtue of this PtNode being the only one to
+                        // match the word on the first character, but not matching the whole word).
+                        if (wordPos >= length) return NOT_A_DICT_POS;
+                        if (inWord[wordPos] != character) return NOT_A_DICT_POS;
+                        character = PatriciaTrieReadingUtils::getCodePointAndAdvancePosition(
+                                mDictRoot, &pos);
+                    }
+                }
+                // If we come here we know that so far, we do match. Either we are on a terminal
+                // and we match the length, in which case we found it, or we traverse children.
+                // If we don't match the length AND don't have children, then a word in the
+                // dictionary fully matches a prefix of the searched word but not the full word.
+                ++wordPos;
+                if (PatriciaTrieReadingUtils::isTerminal(flags)) {
+                    if (wordPos == length) {
+                        return ptNodePos;
+                    }
+                    PatriciaTrieReadingUtils::readProbabilityAndAdvancePosition(mDictRoot, &pos);
+                }
+                if (!PatriciaTrieReadingUtils::hasChildrenInFlags(flags)) {
+                    return NOT_A_DICT_POS;
+                }
+                // We have children and we are still shorter than the word we are searching for, so
+                // we need to traverse children. Put the pointer on the children position, and
+                // break
+                pos = PatriciaTrieReadingUtils::readChildrenPositionAndAdvancePosition(mDictRoot,
+                        flags, &pos);
+                break;
+            } else {
+                // This PtNode does not match, so skip the remaining part and go to the next.
+                if (PatriciaTrieReadingUtils::hasMultipleChars(flags)) {
+                    PatriciaTrieReadingUtils::skipCharacters(mDictRoot, flags, MAX_WORD_LENGTH,
+                            &pos);
+                }
+                if (PatriciaTrieReadingUtils::isTerminal(flags)) {
+                    PatriciaTrieReadingUtils::readProbabilityAndAdvancePosition(mDictRoot, &pos);
+                }
+                if (PatriciaTrieReadingUtils::hasChildrenInFlags(flags)) {
+                    PatriciaTrieReadingUtils::readChildrenPositionAndAdvancePosition(mDictRoot,
+                            flags, &pos);
+                }
+                if (PatriciaTrieReadingUtils::hasShortcutTargets(flags)) {
+                    mShortcutListPolicy.skipAllShortcuts(&pos);
+                }
+                if (PatriciaTrieReadingUtils::hasBigrams(flags)) {
+                    mBigramListPolicy.skipAllBigrams(&pos);
+                }
+            }
+            --ptNodeCount;
+        }
+    }
 }
 
-int PatriciaTriePolicy::getUnigramProbability(
-        const BinaryDictionaryInfo *const binaryDictionaryInfo, const int nodePos) const {
-    if (nodePos == NOT_A_VALID_WORD_POS) {
+int PatriciaTriePolicy::getProbability(const int unigramProbability,
+        const int bigramProbability) const {
+    if (unigramProbability == NOT_A_PROBABILITY) {
+        return NOT_A_PROBABILITY;
+    } else if (bigramProbability == NOT_A_PROBABILITY) {
+        return ProbabilityUtils::backoff(unigramProbability);
+    } else {
+        return ProbabilityUtils::computeProbabilityForBigram(unigramProbability,
+                bigramProbability);
+    }
+}
+
+int PatriciaTriePolicy::getUnigramProbabilityOfPtNode(const int ptNodePos) const {
+    if (ptNodePos == NOT_A_DICT_POS) {
         return NOT_A_PROBABILITY;
     }
-    const uint8_t *const dictRoot = binaryDictionaryInfo->getDictRoot();
-    int pos = nodePos;
+    int pos = ptNodePos;
     const PatriciaTrieReadingUtils::NodeFlags flags =
-            PatriciaTrieReadingUtils::getFlagsAndAdvancePosition(dictRoot, &pos);
+            PatriciaTrieReadingUtils::getFlagsAndAdvancePosition(mDictRoot, &pos);
     if (!PatriciaTrieReadingUtils::isTerminal(flags)) {
         return NOT_A_PROBABILITY;
     }
@@ -79,90 +336,80 @@
         // for shortcuts).
         return NOT_A_PROBABILITY;
     }
-    PatriciaTrieReadingUtils::skipCharacters(dictRoot, flags, MAX_WORD_LENGTH, &pos);
-    return PatriciaTrieReadingUtils::readProbabilityAndAdvancePosition(dictRoot, &pos);
+    PatriciaTrieReadingUtils::skipCharacters(mDictRoot, flags, MAX_WORD_LENGTH, &pos);
+    return getProbability(PatriciaTrieReadingUtils::readProbabilityAndAdvancePosition(
+            mDictRoot, &pos), NOT_A_PROBABILITY);
 }
 
-int PatriciaTriePolicy::getShortcutPositionOfNode(
-        const BinaryDictionaryInfo *const binaryDictionaryInfo,
-        const int nodePos) const {
-    if (nodePos == NOT_A_VALID_WORD_POS) {
+int PatriciaTriePolicy::getShortcutPositionOfPtNode(const int ptNodePos) const {
+    if (ptNodePos == NOT_A_DICT_POS) {
         return NOT_A_DICT_POS;
     }
-    const uint8_t *const dictRoot = binaryDictionaryInfo->getDictRoot();
-    int pos = nodePos;
+    int pos = ptNodePos;
     const PatriciaTrieReadingUtils::NodeFlags flags =
-            PatriciaTrieReadingUtils::getFlagsAndAdvancePosition(dictRoot, &pos);
+            PatriciaTrieReadingUtils::getFlagsAndAdvancePosition(mDictRoot, &pos);
     if (!PatriciaTrieReadingUtils::hasShortcutTargets(flags)) {
         return NOT_A_DICT_POS;
     }
-    PatriciaTrieReadingUtils::skipCharacters(dictRoot, flags, MAX_WORD_LENGTH, &pos);
+    PatriciaTrieReadingUtils::skipCharacters(mDictRoot, flags, MAX_WORD_LENGTH, &pos);
     if (PatriciaTrieReadingUtils::isTerminal(flags)) {
-        PatriciaTrieReadingUtils::readProbabilityAndAdvancePosition(dictRoot, &pos);
+        PatriciaTrieReadingUtils::readProbabilityAndAdvancePosition(mDictRoot, &pos);
     }
     if (PatriciaTrieReadingUtils::hasChildrenInFlags(flags)) {
-        PatriciaTrieReadingUtils::readChildrenPositionAndAdvancePosition(dictRoot, flags, &pos);
+        PatriciaTrieReadingUtils::readChildrenPositionAndAdvancePosition(mDictRoot, flags, &pos);
     }
     return pos;
 }
 
-int PatriciaTriePolicy::getBigramsPositionOfNode(
-        const BinaryDictionaryInfo *const binaryDictionaryInfo,
-        const int nodePos) const {
-    if (nodePos == NOT_A_VALID_WORD_POS) {
+int PatriciaTriePolicy::getBigramsPositionOfPtNode(const int ptNodePos) const {
+    if (ptNodePos == NOT_A_DICT_POS) {
         return NOT_A_DICT_POS;
     }
-    const uint8_t *const dictRoot = binaryDictionaryInfo->getDictRoot();
-    int pos = nodePos;
+    int pos = ptNodePos;
     const PatriciaTrieReadingUtils::NodeFlags flags =
-            PatriciaTrieReadingUtils::getFlagsAndAdvancePosition(dictRoot, &pos);
+            PatriciaTrieReadingUtils::getFlagsAndAdvancePosition(mDictRoot, &pos);
     if (!PatriciaTrieReadingUtils::hasBigrams(flags)) {
         return NOT_A_DICT_POS;
     }
-    PatriciaTrieReadingUtils::skipCharacters(dictRoot, flags, MAX_WORD_LENGTH, &pos);
+    PatriciaTrieReadingUtils::skipCharacters(mDictRoot, flags, MAX_WORD_LENGTH, &pos);
     if (PatriciaTrieReadingUtils::isTerminal(flags)) {
-        PatriciaTrieReadingUtils::readProbabilityAndAdvancePosition(dictRoot, &pos);
+        PatriciaTrieReadingUtils::readProbabilityAndAdvancePosition(mDictRoot, &pos);
     }
     if (PatriciaTrieReadingUtils::hasChildrenInFlags(flags)) {
-        PatriciaTrieReadingUtils::readChildrenPositionAndAdvancePosition(dictRoot, flags, &pos);
+        PatriciaTrieReadingUtils::readChildrenPositionAndAdvancePosition(mDictRoot, flags, &pos);
     }
     if (PatriciaTrieReadingUtils::hasShortcutTargets(flags)) {
-        BinaryDictionaryTerminalAttributesReadingUtils::skipShortcuts(binaryDictionaryInfo, &pos);
+        mShortcutListPolicy.skipAllShortcuts(&pos);;
     }
     return pos;
 }
 
 int PatriciaTriePolicy::createAndGetLeavingChildNode(const DicNode *const dicNode,
-        const int nodePos, const BinaryDictionaryInfo *const binaryDictionaryInfo,
-        const NodeFilter *const childrenFilter, DicNodeVector *childDicNodes) const {
-    const uint8_t *const dictRoot = binaryDictionaryInfo->getDictRoot();
-    int pos = nodePos;
+        const int ptNodePos, DicNodeVector *childDicNodes) const {
+    int pos = ptNodePos;
     const PatriciaTrieReadingUtils::NodeFlags flags =
-            PatriciaTrieReadingUtils::getFlagsAndAdvancePosition(dictRoot, &pos);
+            PatriciaTrieReadingUtils::getFlagsAndAdvancePosition(mDictRoot, &pos);
     int mergedNodeCodePoints[MAX_WORD_LENGTH];
     const int mergedNodeCodePointCount = PatriciaTrieReadingUtils::getCharsAndAdvancePosition(
-            dictRoot, flags, MAX_WORD_LENGTH, mergedNodeCodePoints, &pos);
+            mDictRoot, flags, MAX_WORD_LENGTH, mergedNodeCodePoints, &pos);
     const int probability = (PatriciaTrieReadingUtils::isTerminal(flags))?
-            PatriciaTrieReadingUtils::readProbabilityAndAdvancePosition(dictRoot, &pos)
+            PatriciaTrieReadingUtils::readProbabilityAndAdvancePosition(mDictRoot, &pos)
                     : NOT_A_PROBABILITY;
     const int childrenPos = PatriciaTrieReadingUtils::hasChildrenInFlags(flags) ?
             PatriciaTrieReadingUtils::readChildrenPositionAndAdvancePosition(
-                    dictRoot, flags, &pos) : NOT_A_DICT_POS;
+                    mDictRoot, flags, &pos) : NOT_A_DICT_POS;
     if (PatriciaTrieReadingUtils::hasShortcutTargets(flags)) {
-        BinaryDictionaryTerminalAttributesReadingUtils::skipShortcuts(binaryDictionaryInfo, &pos);
+        getShortcutsStructurePolicy()->skipAllShortcuts(&pos);
     }
     if (PatriciaTrieReadingUtils::hasBigrams(flags)) {
-        BinaryDictionaryTerminalAttributesReadingUtils::skipExistingBigrams(
-                binaryDictionaryInfo, &pos);
+        getBigramsStructurePolicy()->skipAllBigrams(&pos);
     }
-    if (!childrenFilter->isFilteredOut(mergedNodeCodePoints[0])) {
-        childDicNodes->pushLeavingChild(dicNode, nodePos, childrenPos, probability,
-                PatriciaTrieReadingUtils::isTerminal(flags),
-                PatriciaTrieReadingUtils::hasChildrenInFlags(flags),
-                PatriciaTrieReadingUtils::isBlacklisted(flags) ||
-                        PatriciaTrieReadingUtils::isNotAWord(flags),
-                mergedNodeCodePointCount, mergedNodeCodePoints);
-    }
+    childDicNodes->pushLeavingChild(dicNode, ptNodePos, childrenPos, probability,
+            PatriciaTrieReadingUtils::isTerminal(flags),
+            PatriciaTrieReadingUtils::hasChildrenInFlags(flags),
+            PatriciaTrieReadingUtils::isBlacklisted(flags) ||
+                    PatriciaTrieReadingUtils::isNotAWord(flags),
+            mergedNodeCodePointCount, mergedNodeCodePoints);
     return pos;
 }
 
diff --git a/native/jni/src/suggest/policyimpl/dictionary/patricia_trie_policy.h b/native/jni/src/suggest/policyimpl/dictionary/patricia_trie_policy.h
index 71f256e..19155f9 100644
--- a/native/jni/src/suggest/policyimpl/dictionary/patricia_trie_policy.h
+++ b/native/jni/src/suggest/policyimpl/dictionary/patricia_trie_policy.h
@@ -17,15 +17,29 @@
 #ifndef LATINIME_PATRICIA_TRIE_POLICY_H
 #define LATINIME_PATRICIA_TRIE_POLICY_H
 
+#include <stdint.h>
+
 #include "defines.h"
-#include "suggest/core/policy/dictionary_structure_policy.h"
+#include "suggest/core/policy/dictionary_structure_with_buffer_policy.h"
+#include "suggest/policyimpl/dictionary/bigram/bigram_list_policy.h"
+#include "suggest/policyimpl/dictionary/header/header_policy.h"
+#include "suggest/policyimpl/dictionary/shortcut/shortcut_list_policy.h"
+#include "suggest/policyimpl/dictionary/utils/mmapped_buffer.h"
 
 namespace latinime {
 
-class PatriciaTriePolicy : public DictionaryStructurePolicy {
+class DicNode;
+class DicNodeVector;
+
+class PatriciaTriePolicy : public DictionaryStructureWithBufferPolicy {
  public:
-    static AK_FORCE_INLINE const PatriciaTriePolicy *getInstance() {
-        return &sInstance;
+    PatriciaTriePolicy(const MmappedBuffer *const buffer)
+            : mBuffer(buffer), mHeaderPolicy(mBuffer->getBuffer(), buffer->getBufferSize()),
+              mDictRoot(mBuffer->getBuffer() + mHeaderPolicy.getSize()),
+              mBigramListPolicy(mDictRoot), mShortcutListPolicy(mDictRoot) {}
+
+    ~PatriciaTriePolicy() {
+        delete mBuffer;
     }
 
     AK_FORCE_INLINE int getRootPosition() const {
@@ -33,37 +47,82 @@
     }
 
     void createAndGetAllChildNodes(const DicNode *const dicNode,
-            const BinaryDictionaryInfo *const binaryDictionaryInfo,
-            const NodeFilter *const nodeFilter, DicNodeVector *const childDicNodes) const;
+            DicNodeVector *const childDicNodes) const;
 
     int getCodePointsAndProbabilityAndReturnCodePointCount(
-            const BinaryDictionaryInfo *const binaryDictionaryInfo,
             const int terminalNodePos, const int maxCodePointCount, int *const outCodePoints,
             int *const outUnigramProbability) const;
 
-    int getTerminalNodePositionOfWord(
-            const BinaryDictionaryInfo *const binaryDictionaryInfo, const int *const inWord,
+    int getTerminalNodePositionOfWord(const int *const inWord,
             const int length, const bool forceLowerCaseSearch) const;
 
-    int getUnigramProbability(const BinaryDictionaryInfo *const binaryDictionaryInfo,
-            const int nodePos) const;
+    int getProbability(const int unigramProbability, const int bigramProbability) const;
 
-    int getShortcutPositionOfNode(const BinaryDictionaryInfo *const binaryDictionaryInfo,
-            const int nodePos) const;
+    int getUnigramProbabilityOfPtNode(const int ptNodePos) const;
 
-    int getBigramsPositionOfNode(const BinaryDictionaryInfo *const binaryDictionaryInfo,
-            const int nodePos) const;
+    int getShortcutPositionOfPtNode(const int ptNodePos) const;
+
+    int getBigramsPositionOfPtNode(const int ptNodePos) const;
+
+    const DictionaryHeaderStructurePolicy *getHeaderStructurePolicy() const {
+        return &mHeaderPolicy;
+    }
+
+    const DictionaryBigramsStructurePolicy *getBigramsStructurePolicy() const {
+        return &mBigramListPolicy;
+    }
+
+    const DictionaryShortcutsStructurePolicy *getShortcutsStructurePolicy() const {
+        return &mShortcutListPolicy;
+    }
+
+    bool addUnigramWord(const int *const word, const int length, const int probability) {
+        // This method should not be called for non-updatable dictionary.
+        AKLOGI("Warning: addUnigramWord() is called for non-updatable dictionary.");
+        return false;
+    }
+
+    bool addBigramWords(const int *const word0, const int length0, const int *const word1,
+            const int length1, const int probability) {
+        // This method should not be called for non-updatable dictionary.
+        AKLOGI("Warning: addBigramWords() is called for non-updatable dictionary.");
+        return false;
+    }
+
+    bool removeBigramWords(const int *const word0, const int length0, const int *const word1,
+            const int length1) {
+        // This method should not be called for non-updatable dictionary.
+        AKLOGI("Warning: removeBigramWords() is called for non-updatable dictionary.");
+        return false;
+    }
+
+    void flush(const char *const filePath) {
+        // This method should not be called for non-updatable dictionary.
+        AKLOGI("Warning: flush() is called for non-updatable dictionary.");
+    }
+
+    void flushWithGC(const char *const filePath) {
+        // This method should not be called for non-updatable dictionary.
+        AKLOGI("Warning: flushWithGC() is called for non-updatable dictionary.");
+    }
+
+    bool needsToRunGC() const {
+        // This method should not be called for non-updatable dictionary.
+        AKLOGI("Warning: needsToRunGC() is called for non-updatable dictionary.");
+        return false;
+    }
 
  private:
-    DISALLOW_COPY_AND_ASSIGN(PatriciaTriePolicy);
-    static const PatriciaTriePolicy sInstance;
+    DISALLOW_IMPLICIT_CONSTRUCTORS(PatriciaTriePolicy);
 
-    PatriciaTriePolicy() {}
-    ~PatriciaTriePolicy() {}
+    const MmappedBuffer *const mBuffer;
+    const HeaderPolicy mHeaderPolicy;
+    const uint8_t *const mDictRoot;
+    const BigramListPolicy mBigramListPolicy;
+    const ShortcutListPolicy mShortcutListPolicy;
 
-    int createAndGetLeavingChildNode(const DicNode *const dicNode, const int nodePos,
-            const BinaryDictionaryInfo *const binaryDictionaryInfo,
-            const NodeFilter *const nodeFilter, DicNodeVector *const childDicNodes) const;
+    int createAndGetLeavingChildNode(const DicNode *const dicNode, const int ptNodePos,
+            DicNodeVector *const childDicNodes) const;
 };
 } // namespace latinime
 #endif // LATINIME_PATRICIA_TRIE_POLICY_H
diff --git a/native/jni/src/suggest/policyimpl/dictionary/patricia_trie_reading_utils.cpp b/native/jni/src/suggest/policyimpl/dictionary/patricia_trie_reading_utils.cpp
index 89e981d..1316b42 100644
--- a/native/jni/src/suggest/policyimpl/dictionary/patricia_trie_reading_utils.cpp
+++ b/native/jni/src/suggest/policyimpl/dictionary/patricia_trie_reading_utils.cpp
@@ -17,21 +17,21 @@
 #include "suggest/policyimpl/dictionary/patricia_trie_reading_utils.h"
 
 #include "defines.h"
-#include "suggest/core/dictionary/byte_array_utils.h"
+#include "suggest/policyimpl/dictionary/utils/byte_array_utils.h"
 
 namespace latinime {
 
 typedef PatriciaTrieReadingUtils PtReadingUtils;
 
-const PtReadingUtils::NodeFlags PtReadingUtils::MASK_GROUP_ADDRESS_TYPE = 0xC0;
-const PtReadingUtils::NodeFlags PtReadingUtils::FLAG_GROUP_ADDRESS_TYPE_NOADDRESS = 0x00;
-const PtReadingUtils::NodeFlags PtReadingUtils::FLAG_GROUP_ADDRESS_TYPE_ONEBYTE = 0x40;
-const PtReadingUtils::NodeFlags PtReadingUtils::FLAG_GROUP_ADDRESS_TYPE_TWOBYTES = 0x80;
-const PtReadingUtils::NodeFlags PtReadingUtils::FLAG_GROUP_ADDRESS_TYPE_THREEBYTES = 0xC0;
+const PtReadingUtils::NodeFlags PtReadingUtils::MASK_CHILDREN_POSITION_TYPE = 0xC0;
+const PtReadingUtils::NodeFlags PtReadingUtils::FLAG_CHILDREN_POSITION_TYPE_NOPOSITION = 0x00;
+const PtReadingUtils::NodeFlags PtReadingUtils::FLAG_CHILDREN_POSITION_TYPE_ONEBYTE = 0x40;
+const PtReadingUtils::NodeFlags PtReadingUtils::FLAG_CHILDREN_POSITION_TYPE_TWOBYTES = 0x80;
+const PtReadingUtils::NodeFlags PtReadingUtils::FLAG_CHILDREN_POSITION_TYPE_THREEBYTES = 0xC0;
 
 // Flag for single/multiple char group
 const PtReadingUtils::NodeFlags PtReadingUtils::FLAG_HAS_MULTIPLE_CHARS = 0x20;
-// Flag for terminal groups
+// Flag for terminal PtNodes
 const PtReadingUtils::NodeFlags PtReadingUtils::FLAG_IS_TERMINAL = 0x10;
 // Flag for shortcut targets presence
 const PtReadingUtils::NodeFlags PtReadingUtils::FLAG_HAS_SHORTCUT_TARGETS = 0x08;
@@ -42,18 +42,75 @@
 // Flag for blacklist
 const PtReadingUtils::NodeFlags PtReadingUtils::FLAG_IS_BLACKLISTED = 0x01;
 
+/* static */ int PtReadingUtils::getPtNodeArraySizeAndAdvancePosition(
+        const uint8_t *const buffer, int *const pos) {
+    const uint8_t firstByte = ByteArrayUtils::readUint8AndAdvancePosition(buffer, pos);
+    if (firstByte < 0x80) {
+        return firstByte;
+    } else {
+        return ((firstByte & 0x7F) << 8) ^ ByteArrayUtils::readUint8AndAdvancePosition(
+                buffer, pos);
+    }
+}
+
+/* static */ PtReadingUtils::NodeFlags PtReadingUtils::getFlagsAndAdvancePosition(
+        const uint8_t *const buffer, int *const pos) {
+    return ByteArrayUtils::readUint8AndAdvancePosition(buffer, pos);
+}
+
+/* static */ int PtReadingUtils::getCodePointAndAdvancePosition(const uint8_t *const buffer,
+        int *const pos) {
+    return ByteArrayUtils::readCodePointAndAdvancePosition(buffer, pos);
+}
+
+// Returns the number of read characters.
+/* static */ int PtReadingUtils::getCharsAndAdvancePosition(const uint8_t *const buffer,
+        const NodeFlags flags, const int maxLength, int *const outBuffer, int *const pos) {
+    int length = 0;
+    if (hasMultipleChars(flags)) {
+        length = ByteArrayUtils::readStringAndAdvancePosition(buffer, maxLength, outBuffer,
+                pos);
+    } else {
+        if (maxLength > 0) {
+            outBuffer[0] = getCodePointAndAdvancePosition(buffer, pos);
+            length = 1;
+        }
+    }
+    return length;
+}
+
+// Returns the number of skipped characters.
+/* static */ int PtReadingUtils::skipCharacters(const uint8_t *const buffer, const NodeFlags flags,
+        const int maxLength, int *const pos) {
+    if (hasMultipleChars(flags)) {
+        return ByteArrayUtils::advancePositionToBehindString(buffer, maxLength, pos);
+    } else {
+        if (maxLength > 0) {
+            getCodePointAndAdvancePosition(buffer, pos);
+            return 1;
+        } else {
+            return 0;
+        }
+    }
+}
+
+/* static */ int PtReadingUtils::readProbabilityAndAdvancePosition(const uint8_t *const buffer,
+        int *const pos) {
+    return ByteArrayUtils::readUint8AndAdvancePosition(buffer, pos);
+}
+
 /* static */ int PtReadingUtils::readChildrenPositionAndAdvancePosition(
         const uint8_t *const buffer, const NodeFlags flags, int *const pos) {
     const int base = *pos;
     int offset = 0;
-    switch (MASK_GROUP_ADDRESS_TYPE & flags) {
-        case FLAG_GROUP_ADDRESS_TYPE_ONEBYTE:
+    switch (MASK_CHILDREN_POSITION_TYPE & flags) {
+        case FLAG_CHILDREN_POSITION_TYPE_ONEBYTE:
             offset = ByteArrayUtils::readUint8AndAdvancePosition(buffer, pos);
             break;
-        case FLAG_GROUP_ADDRESS_TYPE_TWOBYTES:
+        case FLAG_CHILDREN_POSITION_TYPE_TWOBYTES:
             offset = ByteArrayUtils::readUint16AndAdvancePosition(buffer, pos);
             break;
-        case FLAG_GROUP_ADDRESS_TYPE_THREEBYTES:
+        case FLAG_CHILDREN_POSITION_TYPE_THREEBYTES:
             offset = ByteArrayUtils::readUint24AndAdvancePosition(buffer, pos);
             break;
         default:
diff --git a/native/jni/src/suggest/policyimpl/dictionary/patricia_trie_reading_utils.h b/native/jni/src/suggest/policyimpl/dictionary/patricia_trie_reading_utils.h
index 002c3f1..8420ee9 100644
--- a/native/jni/src/suggest/policyimpl/dictionary/patricia_trie_reading_utils.h
+++ b/native/jni/src/suggest/policyimpl/dictionary/patricia_trie_reading_utils.h
@@ -20,7 +20,6 @@
 #include <stdint.h>
 
 #include "defines.h"
-#include "suggest/core/dictionary/byte_array_utils.h"
 
 namespace latinime {
 
@@ -28,62 +27,21 @@
  public:
     typedef uint8_t NodeFlags;
 
-    static AK_FORCE_INLINE int getGroupCountAndAdvancePosition(
-            const uint8_t *const buffer, int *const pos) {
-        const uint8_t firstByte = ByteArrayUtils::readUint8AndAdvancePosition(buffer, pos);
-        if (firstByte < 0x80) {
-            return firstByte;
-        } else {
-            return ((firstByte & 0x7F) << 8) ^ ByteArrayUtils::readUint8AndAdvancePosition(
-                    buffer, pos);
-        }
-    }
+    static int getPtNodeArraySizeAndAdvancePosition(const uint8_t *const buffer, int *const pos);
 
-    static AK_FORCE_INLINE NodeFlags getFlagsAndAdvancePosition(const uint8_t *const buffer,
-            int *const pos) {
-        return ByteArrayUtils::readUint8AndAdvancePosition(buffer, pos);
-    }
+    static NodeFlags getFlagsAndAdvancePosition(const uint8_t *const buffer, int *const pos);
 
-    static AK_FORCE_INLINE int getCodePointAndAdvancePosition(const uint8_t *const buffer,
-            int *const pos) {
-        return ByteArrayUtils::readCodePointAndAdvancePosition(buffer, pos);
-    }
+    static int getCodePointAndAdvancePosition(const uint8_t *const buffer, int *const pos);
 
     // Returns the number of read characters.
-    static AK_FORCE_INLINE int getCharsAndAdvancePosition(const uint8_t *const buffer,
-            const NodeFlags flags, const int maxLength, int *const outBuffer, int *const pos) {
-        int length = 0;
-        if (hasMultipleChars(flags)) {
-            length = ByteArrayUtils::readStringAndAdvancePosition(buffer, maxLength, outBuffer,
-                    pos);
-        } else {
-            if (maxLength > 0) {
-                outBuffer[0] = getCodePointAndAdvancePosition(buffer, pos);
-                length = 1;
-            }
-        }
-        return length;
-    }
+    static int getCharsAndAdvancePosition(const uint8_t *const buffer, const NodeFlags flags,
+            const int maxLength, int *const outBuffer, int *const pos);
 
     // Returns the number of skipped characters.
-    static AK_FORCE_INLINE int skipCharacters(const uint8_t *const buffer, const NodeFlags flags,
-            const int maxLength, int *const pos) {
-        if (hasMultipleChars(flags)) {
-            return ByteArrayUtils::advancePositionToBehindString(buffer, maxLength, pos);
-        } else {
-            if (maxLength > 0) {
-                getCodePointAndAdvancePosition(buffer, pos);
-                return 1;
-            } else {
-                return 0;
-            }
-        }
-    }
+    static int skipCharacters(const uint8_t *const buffer, const NodeFlags flags,
+            const int maxLength, int *const pos);
 
-    static AK_FORCE_INLINE int readProbabilityAndAdvancePosition(const uint8_t *const buffer,
-            int *const pos) {
-        return ByteArrayUtils::readUint8AndAdvancePosition(buffer, pos);
-    }
+    static int readProbabilityAndAdvancePosition(const uint8_t *const buffer, int *const pos);
 
     static int readChildrenPositionAndAdvancePosition(const uint8_t *const buffer,
             const NodeFlags flags, int *const pos);
@@ -116,17 +74,40 @@
     }
 
     static AK_FORCE_INLINE bool hasChildrenInFlags(const NodeFlags flags) {
-        return FLAG_GROUP_ADDRESS_TYPE_NOADDRESS != (MASK_GROUP_ADDRESS_TYPE & flags);
+        return FLAG_CHILDREN_POSITION_TYPE_NOPOSITION != (MASK_CHILDREN_POSITION_TYPE & flags);
+    }
+
+    static AK_FORCE_INLINE NodeFlags createAndGetFlags(const bool isBlacklisted,
+            const bool isNotAWord, const bool isTerminal, const bool hasShortcutTargets,
+            const bool hasBigrams, const bool hasMultipleChars,
+            const int childrenPositionFieldSize) {
+        NodeFlags nodeFlags = 0;
+        nodeFlags = isBlacklisted ? (nodeFlags | FLAG_IS_BLACKLISTED) : nodeFlags;
+        nodeFlags = isNotAWord ? (nodeFlags | FLAG_IS_NOT_A_WORD) : nodeFlags;
+        nodeFlags = isTerminal ? (nodeFlags | FLAG_IS_TERMINAL) : nodeFlags;
+        nodeFlags = hasShortcutTargets ? (nodeFlags | FLAG_HAS_SHORTCUT_TARGETS) : nodeFlags;
+        nodeFlags = hasBigrams ? (nodeFlags | FLAG_HAS_BIGRAMS) : nodeFlags;
+        nodeFlags = hasMultipleChars ? (nodeFlags | FLAG_HAS_MULTIPLE_CHARS) : nodeFlags;
+        if (childrenPositionFieldSize == 1) {
+            nodeFlags |= FLAG_CHILDREN_POSITION_TYPE_ONEBYTE;
+        } else if (childrenPositionFieldSize == 2) {
+            nodeFlags |= FLAG_CHILDREN_POSITION_TYPE_TWOBYTES;
+        } else if (childrenPositionFieldSize == 3) {
+            nodeFlags |= FLAG_CHILDREN_POSITION_TYPE_THREEBYTES;
+        } else {
+            nodeFlags |= FLAG_CHILDREN_POSITION_TYPE_NOPOSITION;
+        }
+        return nodeFlags;
     }
 
  private:
     DISALLOW_IMPLICIT_CONSTRUCTORS(PatriciaTrieReadingUtils);
 
-    static const NodeFlags MASK_GROUP_ADDRESS_TYPE;
-    static const NodeFlags FLAG_GROUP_ADDRESS_TYPE_NOADDRESS;
-    static const NodeFlags FLAG_GROUP_ADDRESS_TYPE_ONEBYTE;
-    static const NodeFlags FLAG_GROUP_ADDRESS_TYPE_TWOBYTES;
-    static const NodeFlags FLAG_GROUP_ADDRESS_TYPE_THREEBYTES;
+    static const NodeFlags MASK_CHILDREN_POSITION_TYPE;
+    static const NodeFlags FLAG_CHILDREN_POSITION_TYPE_NOPOSITION;
+    static const NodeFlags FLAG_CHILDREN_POSITION_TYPE_ONEBYTE;
+    static const NodeFlags FLAG_CHILDREN_POSITION_TYPE_TWOBYTES;
+    static const NodeFlags FLAG_CHILDREN_POSITION_TYPE_THREEBYTES;
 
     static const NodeFlags FLAG_HAS_MULTIPLE_CHARS;
     static const NodeFlags FLAG_IS_TERMINAL;
diff --git a/native/jni/src/suggest/policyimpl/dictionary/shortcut/dynamic_shortcut_list_policy.h b/native/jni/src/suggest/policyimpl/dictionary/shortcut/dynamic_shortcut_list_policy.h
new file mode 100644
index 0000000..bd3211f
--- /dev/null
+++ b/native/jni/src/suggest/policyimpl/dictionary/shortcut/dynamic_shortcut_list_policy.h
@@ -0,0 +1,123 @@
+/*
+ * Copyright (C) 2013 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.
+ */
+
+#ifndef LATINIME_DYNAMIC_SHORTCUT_LIST_POLICY_H
+#define LATINIME_DYNAMIC_SHORTCUT_LIST_POLICY_H
+
+#include <stdint.h>
+
+#include "defines.h"
+#include "suggest/core/policy/dictionary_shortcuts_structure_policy.h"
+#include "suggest/policyimpl/dictionary/shortcut/shortcut_list_reading_utils.h"
+#include "suggest/policyimpl/dictionary/utils/buffer_with_extendable_buffer.h"
+
+namespace latinime {
+
+/*
+ * This is a dynamic version of ShortcutListPolicy and supports an additional buffer.
+ */
+class DynamicShortcutListPolicy : public DictionaryShortcutsStructurePolicy {
+ public:
+    explicit DynamicShortcutListPolicy(const BufferWithExtendableBuffer *const buffer)
+            : mBuffer(buffer) {}
+
+    ~DynamicShortcutListPolicy() {}
+
+    int getStartPos(const int pos) const {
+        if (pos == NOT_A_DICT_POS) {
+            return NOT_A_DICT_POS;
+        }
+        return pos + ShortcutListReadingUtils::getShortcutListSizeFieldSize();
+    }
+
+    void getNextShortcut(const int maxCodePointCount, int *const outCodePoint,
+            int *const outCodePointCount, bool *const outIsWhitelist, bool *const outHasNext,
+            int *const pos) const {
+        const bool usesAdditionalBuffer = mBuffer->isInAdditionalBuffer(*pos);
+        const uint8_t *const buffer = mBuffer->getBuffer(usesAdditionalBuffer);
+        if (usesAdditionalBuffer) {
+            *pos -= mBuffer->getOriginalBufferSize();
+        }
+        const ShortcutListReadingUtils::ShortcutFlags flags =
+                ShortcutListReadingUtils::getFlagsAndForwardPointer(buffer, pos);
+        if (outHasNext) {
+            *outHasNext = ShortcutListReadingUtils::hasNext(flags);
+        }
+        if (outIsWhitelist) {
+            *outIsWhitelist = ShortcutListReadingUtils::isWhitelist(flags);
+        }
+        if (outCodePoint) {
+            *outCodePointCount = ShortcutListReadingUtils::readShortcutTarget(
+                    buffer, maxCodePointCount, outCodePoint, pos);
+        }
+        if (usesAdditionalBuffer) {
+            *pos += mBuffer->getOriginalBufferSize();
+        }
+    }
+
+    void skipAllShortcuts(int *const pos) const {
+        const bool usesAdditionalBuffer = mBuffer->isInAdditionalBuffer(*pos);
+        const uint8_t *const buffer = mBuffer->getBuffer(usesAdditionalBuffer);
+        if (usesAdditionalBuffer) {
+            *pos -= mBuffer->getOriginalBufferSize();
+        }
+        const int shortcutListSize = ShortcutListReadingUtils
+                ::getShortcutListSizeAndForwardPointer(buffer, pos);
+        *pos += shortcutListSize;
+        if (usesAdditionalBuffer) {
+            *pos += mBuffer->getOriginalBufferSize();
+        }
+    }
+
+    // Copy shortcuts from the shortcut list that starts at fromPos in mBuffer to toPos in
+    // bufferToWrite and advance these positions after the shortcut lists. This returns whether
+    // the copy was succeeded or not.
+    bool copyAllShortcutsAndReturnIfSucceededOrNot(BufferWithExtendableBuffer *const bufferToWrite,
+            int *const fromPos, int *const toPos) const {
+        const bool usesAdditionalBuffer = mBuffer->isInAdditionalBuffer(*fromPos);
+        if (usesAdditionalBuffer) {
+            *fromPos -= mBuffer->getOriginalBufferSize();
+        }
+        const int shortcutListSize = ShortcutListReadingUtils
+                ::getShortcutListSizeAndForwardPointer(mBuffer->getBuffer(usesAdditionalBuffer),
+                        fromPos);
+        // Copy shortcut list size.
+        if (!bufferToWrite->writeUintAndAdvancePosition(
+                shortcutListSize + ShortcutListReadingUtils::getShortcutListSizeFieldSize(),
+                ShortcutListReadingUtils::getShortcutListSizeFieldSize(), toPos)) {
+            return false;
+        }
+        // Copy shortcut list.
+        for (int i = 0; i < shortcutListSize; ++i) {
+            const uint8_t data = ByteArrayUtils::readUint8AndAdvancePosition(
+                    mBuffer->getBuffer(usesAdditionalBuffer), fromPos);
+            if (!bufferToWrite->writeUintAndAdvancePosition(data, 1 /* size */, toPos)) {
+                return false;
+            }
+        }
+        if (usesAdditionalBuffer) {
+            *fromPos += mBuffer->getOriginalBufferSize();
+        }
+        return true;
+    }
+
+ private:
+    DISALLOW_IMPLICIT_CONSTRUCTORS(DynamicShortcutListPolicy);
+
+    const BufferWithExtendableBuffer *const mBuffer;
+};
+} // namespace latinime
+#endif // LATINIME_DYNAMIC_SHORTCUT_LIST_POLICY_H
diff --git a/native/jni/src/suggest/policyimpl/dictionary/shortcut/shortcut_list_policy.h b/native/jni/src/suggest/policyimpl/dictionary/shortcut/shortcut_list_policy.h
new file mode 100644
index 0000000..d73f739
--- /dev/null
+++ b/native/jni/src/suggest/policyimpl/dictionary/shortcut/shortcut_list_policy.h
@@ -0,0 +1,73 @@
+/*
+ * Copyright (C) 2013 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.
+ */
+
+#ifndef LATINIME_SHORTCUT_LIST_POLICY_H
+#define LATINIME_SHORTCUT_LIST_POLICY_H
+
+#include <stdint.h>
+
+#include "defines.h"
+#include "suggest/core/policy/dictionary_shortcuts_structure_policy.h"
+#include "suggest/policyimpl/dictionary/shortcut/shortcut_list_reading_utils.h"
+
+namespace latinime {
+
+class ShortcutListPolicy : public DictionaryShortcutsStructurePolicy {
+ public:
+    explicit ShortcutListPolicy(const uint8_t *const shortcutBuf)
+            : mShortcutsBuf(shortcutBuf) {}
+
+    ~ShortcutListPolicy() {}
+
+    int getStartPos(const int pos) const {
+        if (pos == NOT_A_DICT_POS) {
+            return NOT_A_DICT_POS;
+        }
+        int listPos = pos;
+        ShortcutListReadingUtils::getShortcutListSizeAndForwardPointer(mShortcutsBuf, &listPos);
+        return listPos;
+    }
+
+    void getNextShortcut(const int maxCodePointCount, int *const outCodePoint,
+            int *const outCodePointCount, bool *const outIsWhitelist, bool *const outHasNext,
+            int *const pos) const {
+        const ShortcutListReadingUtils::ShortcutFlags flags =
+                ShortcutListReadingUtils::getFlagsAndForwardPointer(mShortcutsBuf, pos);
+        if (outHasNext) {
+            *outHasNext = ShortcutListReadingUtils::hasNext(flags);
+        }
+        if (outIsWhitelist) {
+            *outIsWhitelist = ShortcutListReadingUtils::isWhitelist(flags);
+        }
+        if (outCodePoint) {
+            *outCodePointCount = ShortcutListReadingUtils::readShortcutTarget(
+                        mShortcutsBuf, maxCodePointCount, outCodePoint, pos);
+        }
+    }
+
+    void skipAllShortcuts(int *const pos) const {
+        const int shortcutListSize = ShortcutListReadingUtils
+                ::getShortcutListSizeAndForwardPointer(mShortcutsBuf, pos);
+        *pos += shortcutListSize;
+    }
+
+ private:
+    DISALLOW_IMPLICIT_CONSTRUCTORS(ShortcutListPolicy);
+
+    const uint8_t *const mShortcutsBuf;
+};
+} // namespace latinime
+#endif // LATINIME_SHORTCUT_LIST_POLICY_H
diff --git a/native/jni/src/suggest/policyimpl/dictionary/shortcut/shortcut_list_reading_utils.cpp b/native/jni/src/suggest/policyimpl/dictionary/shortcut/shortcut_list_reading_utils.cpp
new file mode 100644
index 0000000..847dcde
--- /dev/null
+++ b/native/jni/src/suggest/policyimpl/dictionary/shortcut/shortcut_list_reading_utils.cpp
@@ -0,0 +1,51 @@
+/*
+ * Copyright (C) 2013 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.
+ */
+
+#include "suggest/policyimpl/dictionary/shortcut/shortcut_list_reading_utils.h"
+
+#include "suggest/policyimpl/dictionary/utils/byte_array_utils.h"
+
+namespace latinime {
+
+// Flag for presence of more attributes
+const ShortcutListReadingUtils::ShortcutFlags
+        ShortcutListReadingUtils::FLAG_ATTRIBUTE_HAS_NEXT = 0x80;
+// Mask for attribute probability, stored on 4 bits inside the flags byte.
+const ShortcutListReadingUtils::ShortcutFlags
+        ShortcutListReadingUtils::MASK_ATTRIBUTE_PROBABILITY = 0x0F;
+const int ShortcutListReadingUtils::SHORTCUT_LIST_SIZE_FIELD_SIZE = 2;
+// The numeric value of the shortcut probability that means 'whitelist'.
+const int ShortcutListReadingUtils::WHITELIST_SHORTCUT_PROBABILITY = 15;
+
+/* static */ ShortcutListReadingUtils::ShortcutFlags
+        ShortcutListReadingUtils::getFlagsAndForwardPointer(const uint8_t *const dictRoot,
+                int *const pos) {
+    return ByteArrayUtils::readUint8AndAdvancePosition(dictRoot, pos);
+}
+
+/* static */ int ShortcutListReadingUtils::getShortcutListSizeAndForwardPointer(
+        const uint8_t *const dictRoot, int *const pos) {
+    // readUint16andAdvancePosition() returns an offset *including* the uint16 field itself.
+    return ByteArrayUtils::readUint16AndAdvancePosition(dictRoot, pos)
+            - SHORTCUT_LIST_SIZE_FIELD_SIZE;
+}
+
+/* static */ int ShortcutListReadingUtils::readShortcutTarget(
+        const uint8_t *const dictRoot, const int maxLength,  int *const outWord, int *const pos) {
+    return ByteArrayUtils::readStringAndAdvancePosition(dictRoot, maxLength, outWord, pos);
+}
+
+} // namespace latinime
diff --git a/native/jni/src/suggest/policyimpl/dictionary/shortcut/shortcut_list_reading_utils.h b/native/jni/src/suggest/policyimpl/dictionary/shortcut/shortcut_list_reading_utils.h
new file mode 100644
index 0000000..a83ed5a
--- /dev/null
+++ b/native/jni/src/suggest/policyimpl/dictionary/shortcut/shortcut_list_reading_utils.h
@@ -0,0 +1,69 @@
+/*
+ * Copyright (C) 2013 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.
+ */
+
+#ifndef LATINIME_SHORTCUT_LIST_READING_UTILS_H
+#define LATINIME_SHORTCUT_LIST_READING_UTILS_H
+
+#include <stdint.h>
+
+#include "defines.h"
+
+namespace latinime {
+
+class ShortcutListReadingUtils {
+ public:
+    typedef uint8_t ShortcutFlags;
+
+    static ShortcutFlags getFlagsAndForwardPointer(const uint8_t *const dictRoot, int *const pos);
+
+    static AK_FORCE_INLINE int getProbabilityFromFlags(const ShortcutFlags flags) {
+        return flags & MASK_ATTRIBUTE_PROBABILITY;
+    }
+
+    static AK_FORCE_INLINE bool hasNext(const ShortcutFlags flags) {
+        return (flags & FLAG_ATTRIBUTE_HAS_NEXT) != 0;
+    }
+
+    // This method returns the size of the shortcut list region excluding the shortcut list size
+    // field at the beginning.
+    static int getShortcutListSizeAndForwardPointer(const uint8_t *const dictRoot, int *const pos);
+
+    static AK_FORCE_INLINE int getShortcutListSizeFieldSize() {
+        return SHORTCUT_LIST_SIZE_FIELD_SIZE;
+    }
+
+    static AK_FORCE_INLINE void skipShortcuts(const uint8_t *const dictRoot, int *const pos) {
+        const int shortcutListSize = getShortcutListSizeAndForwardPointer(dictRoot, pos);
+        *pos += shortcutListSize;
+    }
+
+    static AK_FORCE_INLINE bool isWhitelist(const ShortcutFlags flags) {
+        return getProbabilityFromFlags(flags) == WHITELIST_SHORTCUT_PROBABILITY;
+    }
+
+    static int readShortcutTarget(const uint8_t *const dictRoot, const int maxLength,
+            int *const outWord, int *const pos);
+
+ private:
+    DISALLOW_IMPLICIT_CONSTRUCTORS(ShortcutListReadingUtils);
+
+    static const ShortcutFlags FLAG_ATTRIBUTE_HAS_NEXT;
+    static const ShortcutFlags MASK_ATTRIBUTE_PROBABILITY;
+    static const int SHORTCUT_LIST_SIZE_FIELD_SIZE;
+    static const int WHITELIST_SHORTCUT_PROBABILITY;
+};
+} // namespace latinime
+#endif // LATINIME_SHORTCUT_LIST_READING_UTILS_H
diff --git a/native/jni/src/suggest/policyimpl/dictionary/utils/buffer_with_extendable_buffer.cpp b/native/jni/src/suggest/policyimpl/dictionary/utils/buffer_with_extendable_buffer.cpp
new file mode 100644
index 0000000..f692882
--- /dev/null
+++ b/native/jni/src/suggest/policyimpl/dictionary/utils/buffer_with_extendable_buffer.cpp
@@ -0,0 +1,103 @@
+/*
+ * Copyright (C) 2013 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.
+ */
+
+#include "suggest/policyimpl/dictionary/utils/buffer_with_extendable_buffer.h"
+
+namespace latinime {
+
+const size_t BufferWithExtendableBuffer::MAX_ADDITIONAL_BUFFER_SIZE = 1024 * 1024;
+const int BufferWithExtendableBuffer::NEAR_BUFFER_LIMIT_THRESHOLD_PERCENTILE = 90;
+// TODO: Needs to allocate larger memory corresponding to the current vector size.
+const size_t BufferWithExtendableBuffer::EXTEND_ADDITIONAL_BUFFER_SIZE_STEP = 128 * 1024;
+
+bool BufferWithExtendableBuffer::writeUintAndAdvancePosition(const uint32_t data, const int size,
+        int *const pos) {
+    if (!(size >= 1 && size <= 4)) {
+        AKLOGI("writeUintAndAdvancePosition() is called with invalid size: %d", size);
+        ASSERT(false);
+        return false;
+    }
+    if (!checkAndPrepareWriting(*pos, size)) {
+        return false;
+    }
+    const bool usesAdditionalBuffer = isInAdditionalBuffer(*pos);
+    uint8_t *const buffer = usesAdditionalBuffer ? &mAdditionalBuffer[0] : mOriginalBuffer;
+    if (usesAdditionalBuffer) {
+        *pos -= mOriginalBufferSize;
+    }
+    ByteArrayUtils::writeUintAndAdvancePosition(buffer, data, size, pos);
+    if (usesAdditionalBuffer) {
+        *pos += mOriginalBufferSize;
+    }
+    return true;
+}
+
+bool BufferWithExtendableBuffer::writeCodePointsAndAdvancePosition(const int *const codePoints,
+        const int codePointCount, const bool writesTerminator ,int *const pos) {
+    const size_t size = ByteArrayUtils::calculateRequiredByteCountToStoreCodePoints(
+            codePoints, codePointCount, writesTerminator);
+    if (!checkAndPrepareWriting(*pos, size)) {
+        return false;
+    }
+    const bool usesAdditionalBuffer = isInAdditionalBuffer(*pos);
+    uint8_t *const buffer = usesAdditionalBuffer ? &mAdditionalBuffer[0] : mOriginalBuffer;
+    if (usesAdditionalBuffer) {
+        *pos -= mOriginalBufferSize;
+    }
+    ByteArrayUtils::writeCodePointsAndAdvancePosition(buffer, codePoints, codePointCount,
+            writesTerminator, pos);
+    if (usesAdditionalBuffer) {
+        *pos += mOriginalBufferSize;
+    }
+    return true;
+}
+
+bool BufferWithExtendableBuffer::extendBuffer() {
+    const size_t sizeAfterExtending =
+            mAdditionalBuffer.size() + EXTEND_ADDITIONAL_BUFFER_SIZE_STEP;
+    if (sizeAfterExtending > mMaxAdditionalBufferSize) {
+        return false;
+    }
+    mAdditionalBuffer.resize(mAdditionalBuffer.size() + EXTEND_ADDITIONAL_BUFFER_SIZE_STEP);
+    return true;
+}
+
+bool BufferWithExtendableBuffer::checkAndPrepareWriting(const int pos, const int size) {
+    if (isInAdditionalBuffer(pos)) {
+        const int tailPosition = getTailPosition();
+        if (pos == tailPosition) {
+            // Append data to the tail.
+            if (pos + size > static_cast<int>(mAdditionalBuffer.size()) + mOriginalBufferSize) {
+                // Need to extend buffer.
+                if (!extendBuffer()) {
+                    return false;
+                }
+            }
+            mUsedAdditionalBufferSize += size;
+        } else if (pos + size > tailPosition) {
+            // The access will beyond the tail of used region.
+            return false;
+        }
+    } else {
+        if (pos < 0 || mOriginalBufferSize < pos + size) {
+            // Invalid position or violate the boundary.
+            return false;
+        }
+    }
+    return true;
+}
+
+}
diff --git a/native/jni/src/suggest/policyimpl/dictionary/utils/buffer_with_extendable_buffer.h b/native/jni/src/suggest/policyimpl/dictionary/utils/buffer_with_extendable_buffer.h
new file mode 100644
index 0000000..17d2e39
--- /dev/null
+++ b/native/jni/src/suggest/policyimpl/dictionary/utils/buffer_with_extendable_buffer.h
@@ -0,0 +1,103 @@
+/*
+ * Copyright (C) 2013, 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.
+ */
+
+#ifndef LATINIME_BUFFER_WITH_EXTENDABLE_BUFFER_H
+#define LATINIME_BUFFER_WITH_EXTENDABLE_BUFFER_H
+
+#include <cstddef>
+#include <stdint.h>
+#include <vector>
+
+#include "defines.h"
+#include "suggest/policyimpl/dictionary/utils/byte_array_utils.h"
+
+namespace latinime {
+
+// This is used as a buffer that can be extended for updatable dictionaries.
+// To optimize performance, raw pointer is directly used for reading buffer. The position has to be
+// adjusted to access additional buffer. On the other hand, this class does not provide writable
+// raw pointer but provides several methods that handle boundary checking for writing data.
+class BufferWithExtendableBuffer {
+ public:
+    BufferWithExtendableBuffer(uint8_t *const originalBuffer, const int originalBufferSize,
+            const int maxAdditionalBufferSize = MAX_ADDITIONAL_BUFFER_SIZE)
+            : mOriginalBuffer(originalBuffer), mOriginalBufferSize(originalBufferSize),
+              mAdditionalBuffer(EXTEND_ADDITIONAL_BUFFER_SIZE_STEP), mUsedAdditionalBufferSize(0),
+              mMaxAdditionalBufferSize(maxAdditionalBufferSize) {}
+
+    AK_FORCE_INLINE int getTailPosition() const {
+        return mOriginalBufferSize + mUsedAdditionalBufferSize;
+    }
+
+    /**
+     * For reading.
+     */
+    AK_FORCE_INLINE bool isInAdditionalBuffer(const int position) const {
+        return position >= mOriginalBufferSize;
+    }
+
+    // TODO: Resolve the issue that the address can be changed when the vector is resized.
+    // CAVEAT!: Be careful about array out of bound access with buffers
+    AK_FORCE_INLINE const uint8_t *getBuffer(const bool usesAdditionalBuffer) const {
+        if (usesAdditionalBuffer) {
+            return &mAdditionalBuffer[0];
+        } else {
+            return mOriginalBuffer;
+        }
+    }
+
+    AK_FORCE_INLINE int getOriginalBufferSize() const {
+        return mOriginalBufferSize;
+    }
+
+    AK_FORCE_INLINE bool isNearSizeLimit() const {
+        return mAdditionalBuffer.size() >= ((mMaxAdditionalBufferSize
+                * NEAR_BUFFER_LIMIT_THRESHOLD_PERCENTILE) / 100);
+    }
+
+    /**
+     * For writing.
+     *
+     * Writing is allowed for original buffer, already written region of additional buffer and the
+     * tail of additional buffer.
+     */
+    bool writeUintAndAdvancePosition(const uint32_t data, const int size, int *const pos);
+
+    bool writeCodePointsAndAdvancePosition(const int *const codePoints, const int codePointCount,
+            const bool writesTerminator, int *const pos);
+
+ private:
+    DISALLOW_COPY_AND_ASSIGN(BufferWithExtendableBuffer);
+
+    static const size_t MAX_ADDITIONAL_BUFFER_SIZE;
+    static const int NEAR_BUFFER_LIMIT_THRESHOLD_PERCENTILE;
+    static const size_t EXTEND_ADDITIONAL_BUFFER_SIZE_STEP;
+
+    uint8_t *const mOriginalBuffer;
+    const int mOriginalBufferSize;
+    std::vector<uint8_t> mAdditionalBuffer;
+    int mUsedAdditionalBufferSize;
+    const size_t mMaxAdditionalBufferSize;
+
+    // Return if the buffer is successfully extended or not.
+    bool extendBuffer();
+
+    // Returns if it is possible to write size-bytes from pos. When pos is at the tail position of
+    // the additional buffer, try extending the buffer.
+    bool checkAndPrepareWriting(const int pos, const int size);
+};
+}
+#endif /* LATINIME_BUFFER_WITH_EXTENDABLE_BUFFER_H */
diff --git a/native/jni/src/suggest/core/dictionary/byte_array_utils.cpp b/native/jni/src/suggest/policyimpl/dictionary/utils/byte_array_utils.cpp
similarity index 78%
rename from native/jni/src/suggest/core/dictionary/byte_array_utils.cpp
rename to native/jni/src/suggest/policyimpl/dictionary/utils/byte_array_utils.cpp
index 68b1d5d..1833e88 100644
--- a/native/jni/src/suggest/core/dictionary/byte_array_utils.cpp
+++ b/native/jni/src/suggest/policyimpl/dictionary/utils/byte_array_utils.cpp
@@ -14,11 +14,12 @@
  * limitations under the License.
  */
 
-#include "suggest/core/dictionary/byte_array_utils.h"
+#include "suggest/policyimpl/dictionary/utils/byte_array_utils.h"
 
 namespace latinime {
 
-const uint8_t ByteArrayUtils::MINIMAL_ONE_BYTE_CHARACTER_VALUE = 0x20;
+const uint8_t ByteArrayUtils::MINIMUM_ONE_BYTE_CHARACTER_VALUE = 0x20;
+const uint8_t ByteArrayUtils::MAXIMUM_ONE_BYTE_CHARACTER_VALUE = 0xFF;
 const uint8_t ByteArrayUtils::CHARACTER_ARRAY_TERMINATOR = 0x1F;
 
 } // namespace latinime
diff --git a/native/jni/src/suggest/policyimpl/dictionary/utils/byte_array_utils.h b/native/jni/src/suggest/policyimpl/dictionary/utils/byte_array_utils.h
new file mode 100644
index 0000000..0c15768
--- /dev/null
+++ b/native/jni/src/suggest/policyimpl/dictionary/utils/byte_array_utils.h
@@ -0,0 +1,261 @@
+/*
+ * Copyright (C) 2013, 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.
+ */
+
+#ifndef LATINIME_BYTE_ARRAY_UTILS_H
+#define LATINIME_BYTE_ARRAY_UTILS_H
+
+#include <stdint.h>
+
+#include "defines.h"
+
+namespace latinime {
+
+/**
+ * Utility methods for reading byte arrays.
+ */
+class ByteArrayUtils {
+ public:
+    /**
+     * Integer writing
+     *
+     * Each method write a corresponding size integer in a big endian manner.
+     */
+    static AK_FORCE_INLINE void writeUintAndAdvancePosition(uint8_t *const buffer,
+            const uint32_t data, const int size, int *const pos) {
+        // size must be in 1 to 4.
+        ASSERT(size >= 1 && size <= 4);
+        switch (size) {
+            case 1:
+                ByteArrayUtils::writeUint8AndAdvancePosition(buffer, data, pos);
+                return;
+            case 2:
+                ByteArrayUtils::writeUint16AndAdvancePosition(buffer, data, pos);
+                return;
+            case 3:
+                ByteArrayUtils::writeUint24AndAdvancePosition(buffer, data, pos);
+                return;
+            case 4:
+                ByteArrayUtils::writeUint32AndAdvancePosition(buffer, data, pos);
+                return;
+            default:
+                break;
+        }
+    }
+
+    /**
+     * Integer reading
+     *
+     * Each method read a corresponding size integer in a big endian manner.
+     */
+    static AK_FORCE_INLINE uint32_t readUint32(const uint8_t *const buffer, const int pos) {
+        return (buffer[pos] << 24) ^ (buffer[pos + 1] << 16)
+                ^ (buffer[pos + 2] << 8) ^ buffer[pos + 3];
+    }
+
+    static AK_FORCE_INLINE uint32_t readUint24(const uint8_t *const buffer, const int pos) {
+        return (buffer[pos] << 16) ^ (buffer[pos + 1] << 8) ^ buffer[pos + 2];
+    }
+
+    static AK_FORCE_INLINE uint16_t readUint16(const uint8_t *const buffer, const int pos) {
+        return (buffer[pos] << 8) ^ buffer[pos + 1];
+    }
+
+    static AK_FORCE_INLINE uint8_t readUint8(const uint8_t *const buffer, const int pos) {
+        return buffer[pos];
+    }
+
+    static AK_FORCE_INLINE uint32_t readUint32AndAdvancePosition(
+            const uint8_t *const buffer, int *const pos) {
+        const uint32_t value = readUint32(buffer, *pos);
+        *pos += 4;
+        return value;
+    }
+
+    static AK_FORCE_INLINE int readSint24AndAdvancePosition(
+            const uint8_t *const buffer, int *const pos) {
+        const uint8_t value = readUint8(buffer, *pos);
+        if (value < 0x80) {
+            return readUint24AndAdvancePosition(buffer, pos);
+        } else {
+            (*pos)++;
+            return -(((value & 0x7F) << 16) ^ readUint16AndAdvancePosition(buffer, pos));
+        }
+    }
+
+    static AK_FORCE_INLINE uint32_t readUint24AndAdvancePosition(
+            const uint8_t *const buffer, int *const pos) {
+        const uint32_t value = readUint24(buffer, *pos);
+        *pos += 3;
+        return value;
+    }
+
+    static AK_FORCE_INLINE uint16_t readUint16AndAdvancePosition(
+            const uint8_t *const buffer, int *const pos) {
+        const uint16_t value = readUint16(buffer, *pos);
+        *pos += 2;
+        return value;
+    }
+
+    static AK_FORCE_INLINE uint8_t readUint8AndAdvancePosition(
+            const uint8_t *const buffer, int *const pos) {
+        return buffer[(*pos)++];
+    }
+
+    /**
+     * Code Point Reading
+     *
+     * 1 byte = bbbbbbbb match
+     * case 000xxxxx: xxxxx << 16 + next byte << 8 + next byte
+     * else: if 00011111 (= 0x1F) : this is the terminator. This is a relevant choice because
+     *       unicode code points range from 0 to 0x10FFFF, so any 3-byte value starting with
+     *       00011111 would be outside unicode.
+     * else: iso-latin-1 code
+     * This allows for the whole unicode range to be encoded, including chars outside of
+     * the BMP. Also everything in the iso-latin-1 charset is only 1 byte, except control
+     * characters which should never happen anyway (and still work, but take 3 bytes).
+     */
+    static AK_FORCE_INLINE int readCodePoint(const uint8_t *const buffer, const int pos) {
+        int p = pos;
+        return readCodePointAndAdvancePosition(buffer, &p);
+    }
+
+    static AK_FORCE_INLINE int readCodePointAndAdvancePosition(
+            const uint8_t *const buffer, int *const pos) {
+        const uint8_t firstByte = readUint8(buffer, *pos);
+        if (firstByte < MINIMUM_ONE_BYTE_CHARACTER_VALUE) {
+            if (firstByte == CHARACTER_ARRAY_TERMINATOR) {
+                *pos += 1;
+                return NOT_A_CODE_POINT;
+            } else {
+                return readUint24AndAdvancePosition(buffer, pos);
+            }
+        } else {
+            *pos += 1;
+            return firstByte;
+        }
+    }
+
+    /**
+     * String (array of code points) Reading
+     *
+     * Reads code points until the terminator is found.
+     */
+    // Returns the length of the string.
+    static int readStringAndAdvancePosition(const uint8_t *const buffer,
+            const int maxLength, int *const outBuffer, int *const pos) {
+        int length = 0;
+        int codePoint = readCodePointAndAdvancePosition(buffer, pos);
+        while (NOT_A_CODE_POINT != codePoint && length < maxLength) {
+            outBuffer[length++] = codePoint;
+            codePoint = readCodePointAndAdvancePosition(buffer, pos);
+        }
+        return length;
+    }
+
+    // Advances the position and returns the length of the string.
+    static int advancePositionToBehindString(
+            const uint8_t *const buffer, const int maxLength, int *const pos) {
+        int length = 0;
+        int codePoint = readCodePointAndAdvancePosition(buffer, pos);
+        while (NOT_A_CODE_POINT != codePoint && length < maxLength) {
+            codePoint = readCodePointAndAdvancePosition(buffer, pos);
+            length++;
+        }
+        return length;
+    }
+
+    /**
+     * String (array of code points) Writing
+     */
+    static void writeCodePointsAndAdvancePosition(uint8_t *const buffer,
+            const int *const codePoints, const int codePointCount, const bool writesTerminator,
+            int *const pos) {
+        for (int i = 0; i < codePointCount; ++i) {
+            const int codePoint = codePoints[i];
+            if (codePoint == NOT_A_CODE_POINT || codePoint == CHARACTER_ARRAY_TERMINATOR) {
+                break;
+            } else if (codePoint < MINIMUM_ONE_BYTE_CHARACTER_VALUE
+                    || codePoint > MAXIMUM_ONE_BYTE_CHARACTER_VALUE) {
+                // three bytes character.
+                writeUint24AndAdvancePosition(buffer, codePoint, pos);
+            } else {
+                // one byte character.
+                writeUint8AndAdvancePosition(buffer, codePoint, pos);
+            }
+        }
+        if (writesTerminator) {
+            writeUint8AndAdvancePosition(buffer, CHARACTER_ARRAY_TERMINATOR, pos);
+        }
+    }
+
+    static int calculateRequiredByteCountToStoreCodePoints(const int *const codePoints,
+            const int codePointCount, const bool writesTerminator) {
+        int byteCount = 0;
+        for (int i = 0; i < codePointCount; ++i) {
+            const int codePoint = codePoints[i];
+            if (codePoint == NOT_A_CODE_POINT || codePoint == CHARACTER_ARRAY_TERMINATOR) {
+                break;
+            } else if (codePoint < MINIMUM_ONE_BYTE_CHARACTER_VALUE
+                    || codePoint > MAXIMUM_ONE_BYTE_CHARACTER_VALUE) {
+                // three bytes character.
+                byteCount += 3;
+            } else {
+                // one byte character.
+                byteCount += 1;
+            }
+        }
+        if (writesTerminator) {
+            // The terminator is one byte.
+            byteCount += 1;
+        }
+        return byteCount;
+    }
+
+ private:
+    DISALLOW_IMPLICIT_CONSTRUCTORS(ByteArrayUtils);
+
+    static const uint8_t MINIMUM_ONE_BYTE_CHARACTER_VALUE;
+    static const uint8_t MAXIMUM_ONE_BYTE_CHARACTER_VALUE;
+    static const uint8_t CHARACTER_ARRAY_TERMINATOR;
+
+    static AK_FORCE_INLINE void writeUint32AndAdvancePosition(uint8_t *const buffer,
+            const uint32_t data, int *const pos) {
+        buffer[(*pos)++] = (data >> 24) & 0xFF;
+        buffer[(*pos)++] = (data >> 16) & 0xFF;
+        buffer[(*pos)++] = (data >> 8) & 0xFF;
+        buffer[(*pos)++] = data & 0xFF;
+    }
+
+    static AK_FORCE_INLINE void writeUint24AndAdvancePosition(uint8_t *const buffer,
+            const uint32_t data, int *const pos) {
+        buffer[(*pos)++] = (data >> 16) & 0xFF;
+        buffer[(*pos)++] = (data >> 8) & 0xFF;
+        buffer[(*pos)++] = data & 0xFF;
+    }
+
+    static AK_FORCE_INLINE void writeUint16AndAdvancePosition(uint8_t *const buffer,
+            const uint16_t data, int *const pos) {
+        buffer[(*pos)++] = (data >> 8) & 0xFF;
+        buffer[(*pos)++] = data & 0xFF;
+    }
+
+    static AK_FORCE_INLINE void writeUint8AndAdvancePosition(uint8_t *const buffer,
+            const uint8_t data, int *const pos) {
+        buffer[(*pos)++] = data & 0xFF;
+    }
+};
+} // namespace latinime
+#endif /* LATINIME_BYTE_ARRAY_UTILS_H */
diff --git a/native/jni/src/suggest/policyimpl/dictionary/utils/format_utils.cpp b/native/jni/src/suggest/policyimpl/dictionary/utils/format_utils.cpp
new file mode 100644
index 0000000..1d77d5c
--- /dev/null
+++ b/native/jni/src/suggest/policyimpl/dictionary/utils/format_utils.cpp
@@ -0,0 +1,56 @@
+/*
+ * Copyright (C) 2013 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.
+ */
+
+#include "suggest/policyimpl/dictionary/utils/format_utils.h"
+
+#include "suggest/policyimpl/dictionary/utils/byte_array_utils.h"
+
+namespace latinime {
+
+const uint32_t FormatUtils::MAGIC_NUMBER = 0x9BC13AFE;
+
+// Magic number (4 bytes), version (2 bytes), flags (2 bytes), header size (4 bytes) = 12
+const int FormatUtils::DICTIONARY_MINIMUM_SIZE = 12;
+
+/* static */ FormatUtils::FORMAT_VERSION FormatUtils::detectFormatVersion(
+        const uint8_t *const dict, const int dictSize) {
+    // The magic number is stored big-endian.
+    // If the dictionary is less than 4 bytes, we can't even read the magic number, so we don't
+    // understand this format.
+    if (dictSize < DICTIONARY_MINIMUM_SIZE) {
+        return UNKNOWN_VERSION;
+    }
+    const uint32_t magicNumber = ByteArrayUtils::readUint32(dict, 0);
+    switch (magicNumber) {
+        case MAGIC_NUMBER:
+            // Version 2 header is as follows:
+            // Magic number (4 bytes) 0x9B 0xC1 0x3A 0xFE
+            // Dictionary format version number (2 bytes)
+            // Options (2 bytes)
+            // Header size (4 bytes) : integer, big endian
+            if (ByteArrayUtils::readUint16(dict, 4) == 2) {
+                return VERSION_2;
+            } else if (ByteArrayUtils::readUint16(dict, 4) == 3) {
+                return VERSION_3;
+            } else {
+                return UNKNOWN_VERSION;
+            }
+        default:
+            return UNKNOWN_VERSION;
+    }
+}
+
+} // namespace latinime
diff --git a/native/jni/src/suggest/core/dictionary/binary_dictionary_format_utils.h b/native/jni/src/suggest/policyimpl/dictionary/utils/format_utils.h
similarity index 60%
rename from native/jni/src/suggest/core/dictionary/binary_dictionary_format_utils.h
rename to native/jni/src/suggest/policyimpl/dictionary/utils/format_utils.h
index 830684c..79ed0de 100644
--- a/native/jni/src/suggest/core/dictionary/binary_dictionary_format_utils.h
+++ b/native/jni/src/suggest/policyimpl/dictionary/utils/format_utils.h
@@ -14,24 +14,19 @@
  * limitations under the License.
  */
 
-#ifndef LATINIME_BINARY_DICTIONARY_FORMAT_UTILS_H
-#define LATINIME_BINARY_DICTIONARY_FORMAT_UTILS_H
+#ifndef LATINIME_FORMAT_UTILS_H
+#define LATINIME_FORMAT_UTILS_H
 
 #include <stdint.h>
 
 #include "defines.h"
-#include "suggest/core/dictionary/byte_array_utils.h"
 
 namespace latinime {
 
 /**
  * Methods to handle binary dictionary format version.
- *
- * Currently, we have a file with a similar name, binary_format.h. binary_format.h contains binary
- * reading methods and utility methods for various purposes.
- * On the other hand, this file deals with only about dictionary format version.
  */
-class BinaryDictionaryFormatUtils {
+class FormatUtils {
  public:
     enum FORMAT_VERSION {
         VERSION_2,
@@ -39,14 +34,16 @@
         UNKNOWN_VERSION
     };
 
+    // 32 bit magic number is stored at the beginning of the dictionary header to reject
+    // unsupported or obsolete dictionary formats.
+    static const uint32_t MAGIC_NUMBER;
+
     static FORMAT_VERSION detectFormatVersion(const uint8_t *const dict, const int dictSize);
 
  private:
-    DISALLOW_IMPLICIT_CONSTRUCTORS(BinaryDictionaryFormatUtils);
+    DISALLOW_IMPLICIT_CONSTRUCTORS(FormatUtils);
 
     static const int DICTIONARY_MINIMUM_SIZE;
-    static const uint32_t HEADER_VERSION_2_MAGIC_NUMBER;
-    static const int HEADER_VERSION_2_MINIMUM_SIZE;
 };
 } // namespace latinime
-#endif /* LATINIME_BINARY_DICTIONARY_FORMAT_UTILS_H */
+#endif /* LATINIME_FORMAT_UTILS_H */
diff --git a/native/jni/src/suggest/policyimpl/dictionary/utils/mmapped_buffer.h b/native/jni/src/suggest/policyimpl/dictionary/utils/mmapped_buffer.h
new file mode 100644
index 0000000..6b69116
--- /dev/null
+++ b/native/jni/src/suggest/policyimpl/dictionary/utils/mmapped_buffer.h
@@ -0,0 +1,102 @@
+/*
+ * Copyright (C) 2013, 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.
+ */
+
+#ifndef LATINIME_MMAPPED_BUFFER_H
+#define LATINIME_MMAPPED_BUFFER_H
+
+#include <cerrno>
+#include <fcntl.h>
+#include <stdint.h>
+#include <sys/mman.h>
+#include <unistd.h>
+
+#include "defines.h"
+
+namespace latinime {
+
+class MmappedBuffer {
+ public:
+    static MmappedBuffer* openBuffer(const char *const path, const int bufferOffset,
+            const int bufferSize, const bool isUpdatable) {
+        const int openMode = isUpdatable ? O_RDWR : O_RDONLY;
+        const int mmapFd = open(path, openMode);
+        if (mmapFd < 0) {
+            AKLOGE("DICT: Can't open the source. path=%s errno=%d", path, errno);
+            return 0;
+        }
+        const int pagesize = getpagesize();
+        const int offset = bufferOffset % pagesize;
+        int alignedOffset = bufferOffset - offset;
+        int alignedSize = bufferSize + offset;
+        const int protMode = isUpdatable ? PROT_READ | PROT_WRITE : PROT_READ;
+        void *const mmappedBuffer = mmap(0, alignedSize, protMode, MAP_PRIVATE, mmapFd,
+                alignedOffset);
+        if (mmappedBuffer == MAP_FAILED) {
+            AKLOGE("DICT: Can't mmap dictionary. errno=%d", errno);
+            close(mmapFd);
+            return 0;
+        }
+        uint8_t *const buffer = static_cast<uint8_t *>(mmappedBuffer) + offset;
+        if (!buffer) {
+            AKLOGE("DICT: buffer is null");
+            close(mmapFd);
+            return 0;
+        }
+        return new MmappedBuffer(buffer, bufferSize, mmappedBuffer, alignedSize, mmapFd,
+                isUpdatable);
+    }
+
+    ~MmappedBuffer() {
+        int ret = munmap(mMmappedBuffer, mAlignedSize);
+        if (ret != 0) {
+            AKLOGE("DICT: Failure in munmap. ret=%d errno=%d", ret, errno);
+        }
+        ret = close(mMmapFd);
+        if (ret != 0) {
+            AKLOGE("DICT: Failure in close. ret=%d errno=%d", ret, errno);
+        }
+    }
+
+    AK_FORCE_INLINE uint8_t *getBuffer() const {
+        return mBuffer;
+    }
+
+    AK_FORCE_INLINE int getBufferSize() const {
+        return mBufferSize;
+    }
+
+    AK_FORCE_INLINE bool isUpdatable() const {
+        return mIsUpdatable;
+    }
+
+ private:
+    AK_FORCE_INLINE MmappedBuffer(uint8_t *const buffer, const int bufferSize,
+            void *const mmappedBuffer, const int alignedSize, const int mmapFd,
+            const bool isUpdatable)
+            : mBuffer(buffer), mBufferSize(bufferSize), mMmappedBuffer(mmappedBuffer),
+              mAlignedSize(alignedSize), mMmapFd(mmapFd), mIsUpdatable(isUpdatable) {}
+
+    DISALLOW_IMPLICIT_CONSTRUCTORS(MmappedBuffer);
+
+    uint8_t *const mBuffer;
+    const int mBufferSize;
+    void *const mMmappedBuffer;
+    const int mAlignedSize;
+    const int mMmapFd;
+    const bool mIsUpdatable;
+};
+}
+#endif /* LATINIME_MMAPPED_BUFFER_H */
diff --git a/native/jni/src/suggest/core/dictionary/probability_utils.h b/native/jni/src/suggest/policyimpl/dictionary/utils/probability_utils.h
similarity index 96%
rename from native/jni/src/suggest/core/dictionary/probability_utils.h
rename to native/jni/src/suggest/policyimpl/dictionary/utils/probability_utils.h
index f450087..21fe355 100644
--- a/native/jni/src/suggest/core/dictionary/probability_utils.h
+++ b/native/jni/src/suggest/policyimpl/dictionary/utils/probability_utils.h
@@ -41,7 +41,7 @@
         // the unigram probability to be the median value of the 17th step from the top. A value of
         // 0 for the bigram probability represents the middle of the 16th step from the top,
         // while a value of 15 represents the middle of the top step.
-        // See makedict.BinaryDictInputOutput for details.
+        // See makedict.BinaryDictEncoder#makeBigramFlags for details.
         const float stepSize = static_cast<float>(MAX_PROBABILITY - unigramProbability)
                 / (1.5f + MAX_BIGRAM_ENCODED_PROBABILITY);
         return unigramProbability
diff --git a/native/jni/src/suggest/policyimpl/typing/typing_weighting.h b/native/jni/src/suggest/policyimpl/typing/typing_weighting.h
index 7cddb08..b6aa858 100644
--- a/native/jni/src/suggest/policyimpl/typing/typing_weighting.h
+++ b/native/jni/src/suggest/policyimpl/typing/typing_weighting.h
@@ -155,7 +155,8 @@
     float getNewWordBigramLanguageCost(const DicTraverseSession *const traverseSession,
             const DicNode *const dicNode,
             MultiBigramMap *const multiBigramMap) const {
-        return DicNodeUtils::getBigramNodeImprobability(traverseSession->getBinaryDictionaryInfo(),
+        return DicNodeUtils::getBigramNodeImprobability(
+                traverseSession->getDictionaryStructurePolicy(),
                 dicNode, multiBigramMap) * ScoringParams::DISTANCE_WEIGHT_LANGUAGE;
     }
 
diff --git a/native/jni/src/utils/autocorrection_threshold_utils.cpp b/native/jni/src/utils/autocorrection_threshold_utils.cpp
index 3406e0f..1f8ee08 100644
--- a/native/jni/src/utils/autocorrection_threshold_utils.cpp
+++ b/native/jni/src/utils/autocorrection_threshold_utils.cpp
@@ -83,9 +83,12 @@
         return 0.0f;
     }
 
+    if (score <= 0 || distance >= afterLength) {
+        // normalizedScore must be 0.0f (the minimum value) if the score is less than or equal to 0,
+        // or if the edit distance is larger than or equal to afterLength.
+        return 0.0f;
+    }
     // add a weight based on edit distance.
-    // distance <= max(afterLength, beforeLength) == afterLength,
-    // so, 0 <= distance / afterLength <= 1
     const float weight = 1.0f - static_cast<float>(distance) / static_cast<float>(afterLength);
 
     // TODO: Revise the following logic thoroughly by referring to...
diff --git a/tests/src/com/android/inputmethod/keyboard/internal/MockKeyboardSwitcher.java b/tests/src/com/android/inputmethod/keyboard/internal/MockKeyboardSwitcher.java
index db39976..6e3e37a 100644
--- a/tests/src/com/android/inputmethod/keyboard/internal/MockKeyboardSwitcher.java
+++ b/tests/src/com/android/inputmethod/keyboard/internal/MockKeyboardSwitcher.java
@@ -120,6 +120,11 @@
     }
 
     @Override
+    public void setEmojiKeyboard() {
+        // Just ignore.
+    }
+
+    @Override
     public void requestUpdatingShiftState() {
         mState.onUpdateShiftState(mAutoCapsState, RecapitalizeStatus.NOT_A_RECAPITALIZE_MODE);
     }
diff --git a/tests/src/com/android/inputmethod/latin/BinaryDictionaryTests.java b/tests/src/com/android/inputmethod/latin/BinaryDictionaryTests.java
new file mode 100644
index 0000000..96a2217
--- /dev/null
+++ b/tests/src/com/android/inputmethod/latin/BinaryDictionaryTests.java
@@ -0,0 +1,653 @@
+/*
+ * Copyright (C) 2013 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.latin;
+
+import android.test.AndroidTestCase;
+import android.test.suitebuilder.annotation.LargeTest;
+import android.util.Pair;
+
+import com.android.inputmethod.latin.makedict.CodePointUtils;
+import com.android.inputmethod.latin.makedict.DictEncoder;
+import com.android.inputmethod.latin.makedict.FormatSpec;
+import com.android.inputmethod.latin.makedict.FusionDictionary;
+import com.android.inputmethod.latin.makedict.FusionDictionary.PtNodeArray;
+import com.android.inputmethod.latin.makedict.UnsupportedFormatException;
+import com.android.inputmethod.latin.makedict.Ver3DictEncoder;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.Locale;
+import java.util.Random;
+
+@LargeTest
+public class BinaryDictionaryTests extends AndroidTestCase {
+    private static final FormatSpec.FormatOptions FORMAT_OPTIONS =
+            new FormatSpec.FormatOptions(3 /* version */, true /* supportsDynamicUpdate */);
+    private static final String TEST_DICT_FILE_EXTENSION = ".testDict";
+    private static final String TEST_LOCALE = "test";
+
+    @Override
+    protected void setUp() throws Exception {
+        super.setUp();
+    }
+
+    @Override
+    protected void tearDown() throws Exception {
+        super.tearDown();
+    }
+
+    private File createEmptyDictionaryAndGetFile(final String filename) throws IOException,
+            UnsupportedFormatException {
+        final FusionDictionary dict = new FusionDictionary(new PtNodeArray(),
+                new FusionDictionary.DictionaryOptions(new HashMap<String,String>(), false, false));
+        final File file = File.createTempFile(filename, TEST_DICT_FILE_EXTENSION,
+                getContext().getCacheDir());
+        final DictEncoder dictEncoder = new Ver3DictEncoder(file);
+        dictEncoder.writeDictionary(dict, FORMAT_OPTIONS);
+        return file;
+    }
+
+    public void testIsValidDictionary() {
+        File dictFile = null;
+        try {
+            dictFile = createEmptyDictionaryAndGetFile("TestBinaryDictionary");
+        } catch (IOException e) {
+            fail("IOException while writing an initial dictionary : " + e);
+        } catch (UnsupportedFormatException e) {
+            fail("UnsupportedFormatException while writing an initial dictionary : " + e);
+        }
+        BinaryDictionary binaryDictionary = new BinaryDictionary(dictFile.getAbsolutePath(),
+                0 /* offset */, dictFile.length(), true /* useFullEditDistance */,
+                Locale.getDefault(), TEST_LOCALE, true /* isUpdatable */);
+        assertTrue("binaryDictionary must be valid for existing valid dictionary file.",
+                binaryDictionary.isValidDictionary());
+        binaryDictionary.close();
+        assertFalse("binaryDictionary must be invalid after closing.",
+                binaryDictionary.isValidDictionary());
+        dictFile.delete();
+        binaryDictionary = new BinaryDictionary(dictFile.getAbsolutePath(), 0 /* offset */,
+                dictFile.length(), true /* useFullEditDistance */, Locale.getDefault(),
+                TEST_LOCALE, true /* isUpdatable */);
+        assertFalse("binaryDictionary must be invalid for not existing dictionary file.",
+                binaryDictionary.isValidDictionary());
+        binaryDictionary.close();
+    }
+
+    public void testAddUnigramWord() {
+        File dictFile = null;
+        try {
+            dictFile = createEmptyDictionaryAndGetFile("TestBinaryDictionary");
+        } catch (IOException e) {
+            fail("IOException while writing an initial dictionary : " + e);
+        } catch (UnsupportedFormatException e) {
+            fail("UnsupportedFormatException while writing an initial dictionary : " + e);
+        }
+        BinaryDictionary binaryDictionary = new BinaryDictionary(dictFile.getAbsolutePath(),
+                0 /* offset */, dictFile.length(), true /* useFullEditDistance */,
+                Locale.getDefault(), TEST_LOCALE, true /* isUpdatable */);
+
+        final int probability = 100;
+        binaryDictionary.addUnigramWord("aaa", probability);
+        // Reallocate and create.
+        binaryDictionary.addUnigramWord("aab", probability);
+        // Insert into children.
+        binaryDictionary.addUnigramWord("aac", probability);
+        // Make terminal.
+        binaryDictionary.addUnigramWord("aa", probability);
+        // Create children.
+        binaryDictionary.addUnigramWord("aaaa", probability);
+        // Reallocate and make termianl.
+        binaryDictionary.addUnigramWord("a", probability);
+
+        final int updatedProbability = 200;
+        // Update.
+        binaryDictionary.addUnigramWord("aaa", updatedProbability);
+
+        assertEquals(probability, binaryDictionary.getFrequency("aab"));
+        assertEquals(probability, binaryDictionary.getFrequency("aac"));
+        assertEquals(probability, binaryDictionary.getFrequency("aa"));
+        assertEquals(probability, binaryDictionary.getFrequency("aaaa"));
+        assertEquals(probability, binaryDictionary.getFrequency("a"));
+        assertEquals(updatedProbability, binaryDictionary.getFrequency("aaa"));
+
+        dictFile.delete();
+    }
+
+    public void testRandomlyAddUnigramWord() {
+        final int wordCount = 1000;
+        final int codePointSetSize = 50;
+        final int seed = 123456789;
+
+        File dictFile = null;
+        try {
+            dictFile = createEmptyDictionaryAndGetFile("TestBinaryDictionary");
+        } catch (IOException e) {
+            fail("IOException while writing an initial dictionary : " + e);
+        } catch (UnsupportedFormatException e) {
+            fail("UnsupportedFormatException while writing an initial dictionary : " + e);
+        }
+        BinaryDictionary binaryDictionary = new BinaryDictionary(dictFile.getAbsolutePath(),
+                0 /* offset */, dictFile.length(), true /* useFullEditDistance */,
+                Locale.getDefault(), TEST_LOCALE, true /* isUpdatable */);
+
+        final HashMap<String, Integer> probabilityMap = new HashMap<String, Integer>();
+        // Test a word that isn't contained within the dictionary.
+        final Random random = new Random(seed);
+        final int[] codePointSet = CodePointUtils.generateCodePointSet(codePointSetSize, random);
+        for (int i = 0; i < wordCount; ++i) {
+            final String word = CodePointUtils.generateWord(random, codePointSet);
+            probabilityMap.put(word, random.nextInt(0xFF));
+        }
+        for (String word : probabilityMap.keySet()) {
+            binaryDictionary.addUnigramWord(word, probabilityMap.get(word));
+        }
+        for (String word : probabilityMap.keySet()) {
+            assertEquals(word, (int)probabilityMap.get(word), binaryDictionary.getFrequency(word));
+        }
+        dictFile.delete();
+    }
+
+    public void testAddBigramWords() {
+        File dictFile = null;
+        try {
+            dictFile = createEmptyDictionaryAndGetFile("TestBinaryDictionary");
+        } catch (IOException e) {
+            fail("IOException while writing an initial dictionary : " + e);
+        } catch (UnsupportedFormatException e) {
+            fail("UnsupportedFormatException while writing an initial dictionary : " + e);
+        }
+        BinaryDictionary binaryDictionary = new BinaryDictionary(dictFile.getAbsolutePath(),
+                0 /* offset */, dictFile.length(), true /* useFullEditDistance */,
+                Locale.getDefault(), TEST_LOCALE, true /* isUpdatable */);
+
+        final int unigramProbability = 100;
+        final int bigramProbability = 10;
+        final int updatedBigramProbability = 15;
+        binaryDictionary.addUnigramWord("aaa", unigramProbability);
+        binaryDictionary.addUnigramWord("abb", unigramProbability);
+        binaryDictionary.addUnigramWord("bcc", unigramProbability);
+        binaryDictionary.addBigramWords("aaa", "abb", bigramProbability);
+        binaryDictionary.addBigramWords("aaa", "bcc", bigramProbability);
+        binaryDictionary.addBigramWords("abb", "aaa", bigramProbability);
+        binaryDictionary.addBigramWords("abb", "bcc", bigramProbability);
+
+        final int probability = binaryDictionary.calculateProbability(unigramProbability,
+                bigramProbability);
+        assertEquals(true, binaryDictionary.isValidBigram("aaa", "abb"));
+        assertEquals(true, binaryDictionary.isValidBigram("aaa", "bcc"));
+        assertEquals(true, binaryDictionary.isValidBigram("abb", "aaa"));
+        assertEquals(true, binaryDictionary.isValidBigram("abb", "bcc"));
+        assertEquals(probability, binaryDictionary.getBigramProbability("aaa", "abb"));
+        assertEquals(probability, binaryDictionary.getBigramProbability("aaa", "bcc"));
+        assertEquals(probability, binaryDictionary.getBigramProbability("abb", "aaa"));
+        assertEquals(probability, binaryDictionary.getBigramProbability("abb", "bcc"));
+
+        binaryDictionary.addBigramWords("aaa", "abb", updatedBigramProbability);
+        final int updatedProbability = binaryDictionary.calculateProbability(unigramProbability,
+                updatedBigramProbability);
+        assertEquals(updatedProbability, binaryDictionary.getBigramProbability("aaa", "abb"));
+
+        assertEquals(false, binaryDictionary.isValidBigram("bcc", "aaa"));
+        assertEquals(false, binaryDictionary.isValidBigram("bcc", "bbc"));
+        assertEquals(false, binaryDictionary.isValidBigram("aaa", "aaa"));
+        assertEquals(Dictionary.NOT_A_PROBABILITY,
+                binaryDictionary.getBigramProbability("bcc", "aaa"));
+        assertEquals(Dictionary.NOT_A_PROBABILITY,
+                binaryDictionary.getBigramProbability("bcc", "bbc"));
+        assertEquals(Dictionary.NOT_A_PROBABILITY,
+                binaryDictionary.getBigramProbability("aaa", "aaa"));
+
+        // Testing bigram link.
+        binaryDictionary.addUnigramWord("abcde", unigramProbability);
+        binaryDictionary.addUnigramWord("fghij", unigramProbability);
+        binaryDictionary.addBigramWords("abcde", "fghij", bigramProbability);
+        binaryDictionary.addUnigramWord("fgh", unigramProbability);
+        binaryDictionary.addUnigramWord("abc", unigramProbability);
+        binaryDictionary.addUnigramWord("f", unigramProbability);
+        assertEquals(probability, binaryDictionary.getBigramProbability("abcde", "fghij"));
+        assertEquals(Dictionary.NOT_A_PROBABILITY,
+                binaryDictionary.getBigramProbability("abcde", "fgh"));
+        binaryDictionary.addBigramWords("abcde", "fghij", updatedBigramProbability);
+        assertEquals(updatedProbability, binaryDictionary.getBigramProbability("abcde", "fghij"));
+
+        dictFile.delete();
+    }
+
+    public void testRandomlyAddBigramWords() {
+        final int wordCount = 100;
+        final int bigramCount = 1000;
+        final int codePointSetSize = 50;
+        final int seed = 11111;
+
+        File dictFile = null;
+        try {
+            dictFile = createEmptyDictionaryAndGetFile("TestBinaryDictionary");
+        } catch (IOException e) {
+            fail("IOException while writing an initial dictionary : " + e);
+        } catch (UnsupportedFormatException e) {
+            fail("UnsupportedFormatException while writing an initial dictionary : " + e);
+        }
+        BinaryDictionary binaryDictionary = new BinaryDictionary(dictFile.getAbsolutePath(),
+                0 /* offset */, dictFile.length(), true /* useFullEditDistance */,
+                Locale.getDefault(), TEST_LOCALE, true /* isUpdatable */);
+        final ArrayList<String> words = new ArrayList<String>();
+        // Test a word that isn't contained within the dictionary.
+        final Random random = new Random(seed);
+        final int[] codePointSet = CodePointUtils.generateCodePointSet(codePointSetSize, random);
+        final int[] unigramProbabilities = new int[wordCount];
+        for (int i = 0; i < wordCount; ++i) {
+            final String word = CodePointUtils.generateWord(random, codePointSet);
+            words.add(word);
+            final int unigramProbability = random.nextInt(0xFF);
+            unigramProbabilities[i] = unigramProbability;
+            binaryDictionary.addUnigramWord(word, unigramProbability);
+        }
+
+        final int[][] probabilities = new int[wordCount][wordCount];
+
+        for (int i = 0; i < wordCount; ++i) {
+            for (int j = 0; j < wordCount; ++j) {
+                probabilities[i][j] = Dictionary.NOT_A_PROBABILITY;
+            }
+        }
+
+        for (int i = 0; i < bigramCount; i++) {
+            final int word0Index = random.nextInt(wordCount);
+            final int word1Index = random.nextInt(wordCount);
+            final String word0 = words.get(word0Index);
+            final String word1 = words.get(word1Index);
+            final int bigramProbability = random.nextInt(0xF);
+            probabilities[word0Index][word1Index] = binaryDictionary.calculateProbability(
+                    unigramProbabilities[word1Index], bigramProbability);
+            binaryDictionary.addBigramWords(word0, word1, bigramProbability);
+        }
+
+        for (int i = 0; i < words.size(); i++) {
+            for (int j = 0; j < words.size(); j++) {
+                assertEquals(probabilities[i][j],
+                        binaryDictionary.getBigramProbability(words.get(i), words.get(j)));
+            }
+        }
+
+        dictFile.delete();
+    }
+
+    public void testRemoveBigramWords() {
+        File dictFile = null;
+        try {
+            dictFile = createEmptyDictionaryAndGetFile("TestBinaryDictionary");
+        } catch (IOException e) {
+            fail("IOException while writing an initial dictionary : " + e);
+        } catch (UnsupportedFormatException e) {
+            fail("UnsupportedFormatException while writing an initial dictionary : " + e);
+        }
+        BinaryDictionary binaryDictionary = new BinaryDictionary(dictFile.getAbsolutePath(),
+                0 /* offset */, dictFile.length(), true /* useFullEditDistance */,
+                Locale.getDefault(), TEST_LOCALE, true /* isUpdatable */);
+        final int unigramProbability = 100;
+        final int bigramProbability = 10;
+        binaryDictionary.addUnigramWord("aaa", unigramProbability);
+        binaryDictionary.addUnigramWord("abb", unigramProbability);
+        binaryDictionary.addUnigramWord("bcc", unigramProbability);
+        binaryDictionary.addBigramWords("aaa", "abb", bigramProbability);
+        binaryDictionary.addBigramWords("aaa", "bcc", bigramProbability);
+        binaryDictionary.addBigramWords("abb", "aaa", bigramProbability);
+        binaryDictionary.addBigramWords("abb", "bcc", bigramProbability);
+
+        assertEquals(true, binaryDictionary.isValidBigram("aaa", "abb"));
+        assertEquals(true, binaryDictionary.isValidBigram("aaa", "bcc"));
+        assertEquals(true, binaryDictionary.isValidBigram("abb", "aaa"));
+        assertEquals(true, binaryDictionary.isValidBigram("abb", "bcc"));
+
+        binaryDictionary.removeBigramWords("aaa", "abb");
+        assertEquals(false, binaryDictionary.isValidBigram("aaa", "abb"));
+        binaryDictionary.addBigramWords("aaa", "abb", bigramProbability);
+        assertEquals(true, binaryDictionary.isValidBigram("aaa", "abb"));
+
+
+        binaryDictionary.removeBigramWords("aaa", "bcc");
+        assertEquals(false, binaryDictionary.isValidBigram("aaa", "bcc"));
+        binaryDictionary.removeBigramWords("abb", "aaa");
+        assertEquals(false, binaryDictionary.isValidBigram("abb", "aaa"));
+        binaryDictionary.removeBigramWords("abb", "bcc");
+        assertEquals(false, binaryDictionary.isValidBigram("abb", "bcc"));
+
+        binaryDictionary.removeBigramWords("aaa", "abb");
+        // Test remove non-existing bigram operation.
+        binaryDictionary.removeBigramWords("aaa", "abb");
+        binaryDictionary.removeBigramWords("bcc", "aaa");
+
+        dictFile.delete();
+    }
+
+    public void testFlushDictionary() {
+        File dictFile = null;
+        try {
+            dictFile = createEmptyDictionaryAndGetFile("TestBinaryDictionary");
+        } catch (IOException e) {
+            fail("IOException while writing an initial dictionary : " + e);
+        } catch (UnsupportedFormatException e) {
+            fail("UnsupportedFormatException while writing an initial dictionary : " + e);
+        }
+        BinaryDictionary binaryDictionary = new BinaryDictionary(dictFile.getAbsolutePath(),
+                0 /* offset */, dictFile.length(), true /* useFullEditDistance */,
+                Locale.getDefault(), TEST_LOCALE, true /* isUpdatable */);
+
+        final int probability = 100;
+        binaryDictionary.addUnigramWord("aaa", probability);
+        binaryDictionary.addUnigramWord("abcd", probability);
+        // Close without flushing.
+        binaryDictionary.close();
+
+        binaryDictionary = new BinaryDictionary(dictFile.getAbsolutePath(),
+                0 /* offset */, dictFile.length(), true /* useFullEditDistance */,
+                Locale.getDefault(), TEST_LOCALE, true /* isUpdatable */);
+
+        assertEquals(Dictionary.NOT_A_PROBABILITY, binaryDictionary.getFrequency("aaa"));
+        assertEquals(Dictionary.NOT_A_PROBABILITY, binaryDictionary.getFrequency("abcd"));
+
+        binaryDictionary.addUnigramWord("aaa", probability);
+        binaryDictionary.addUnigramWord("abcd", probability);
+        binaryDictionary.flush();
+        binaryDictionary.close();
+
+        binaryDictionary = new BinaryDictionary(dictFile.getAbsolutePath(),
+                0 /* offset */, dictFile.length(), true /* useFullEditDistance */,
+                Locale.getDefault(), TEST_LOCALE, true /* isUpdatable */);
+
+        assertEquals(probability, binaryDictionary.getFrequency("aaa"));
+        assertEquals(probability, binaryDictionary.getFrequency("abcd"));
+        binaryDictionary.addUnigramWord("bcde", probability);
+        binaryDictionary.flush();
+        binaryDictionary.close();
+
+        binaryDictionary = new BinaryDictionary(dictFile.getAbsolutePath(),
+                0 /* offset */, dictFile.length(), true /* useFullEditDistance */,
+                Locale.getDefault(), TEST_LOCALE, true /* isUpdatable */);
+        assertEquals(probability, binaryDictionary.getFrequency("bcde"));
+        binaryDictionary.close();
+
+        dictFile.delete();
+    }
+
+    public void testFlushWithGCDictionary() {
+        File dictFile = null;
+        try {
+            dictFile = createEmptyDictionaryAndGetFile("TestBinaryDictionary");
+        } catch (IOException e) {
+            fail("IOException while writing an initial dictionary : " + e);
+        } catch (UnsupportedFormatException e) {
+            fail("UnsupportedFormatException while writing an initial dictionary : " + e);
+        }
+        BinaryDictionary binaryDictionary = new BinaryDictionary(dictFile.getAbsolutePath(),
+                0 /* offset */, dictFile.length(), true /* useFullEditDistance */,
+                Locale.getDefault(), TEST_LOCALE, true /* isUpdatable */);
+
+        final int unigramProbability = 100;
+        final int bigramProbability = 10;
+        binaryDictionary.addUnigramWord("aaa", unigramProbability);
+        binaryDictionary.addUnigramWord("abb", unigramProbability);
+        binaryDictionary.addUnigramWord("bcc", unigramProbability);
+        binaryDictionary.addBigramWords("aaa", "abb", bigramProbability);
+        binaryDictionary.addBigramWords("aaa", "bcc", bigramProbability);
+        binaryDictionary.addBigramWords("abb", "aaa", bigramProbability);
+        binaryDictionary.addBigramWords("abb", "bcc", bigramProbability);
+        binaryDictionary.flushWithGC();
+        binaryDictionary.close();
+
+        binaryDictionary = new BinaryDictionary(dictFile.getAbsolutePath(),
+                0 /* offset */, dictFile.length(), true /* useFullEditDistance */,
+                Locale.getDefault(), TEST_LOCALE, true /* isUpdatable */);
+        final int probability = binaryDictionary.calculateProbability(unigramProbability,
+                bigramProbability);
+        assertEquals(unigramProbability, binaryDictionary.getFrequency("aaa"));
+        assertEquals(unigramProbability, binaryDictionary.getFrequency("abb"));
+        assertEquals(unigramProbability, binaryDictionary.getFrequency("bcc"));
+        assertEquals(probability, binaryDictionary.getBigramProbability("aaa", "abb"));
+        assertEquals(probability, binaryDictionary.getBigramProbability("aaa", "bcc"));
+        assertEquals(probability, binaryDictionary.getBigramProbability("abb", "aaa"));
+        assertEquals(probability, binaryDictionary.getBigramProbability("abb", "bcc"));
+        assertEquals(false, binaryDictionary.isValidBigram("bcc", "aaa"));
+        assertEquals(false, binaryDictionary.isValidBigram("bcc", "bbc"));
+        assertEquals(false, binaryDictionary.isValidBigram("aaa", "aaa"));
+        binaryDictionary.flushWithGC();
+        binaryDictionary.close();
+
+        dictFile.delete();
+    }
+
+    // TODO: Evaluate performance of GC
+    public void testAddBigramWordsAndFlashWithGC() {
+        final int wordCount = 100;
+        final int bigramCount = 1000;
+        final int codePointSetSize = 30;
+        // TODO: Use various seeds such as a current timestamp to make this test more random.
+        final int seed = 314159265;
+
+        File dictFile = null;
+        try {
+            dictFile = createEmptyDictionaryAndGetFile("TestBinaryDictionary");
+        } catch (IOException e) {
+            fail("IOException while writing an initial dictionary : " + e);
+        } catch (UnsupportedFormatException e) {
+            fail("UnsupportedFormatException while writing an initial dictionary : " + e);
+        }
+
+        BinaryDictionary binaryDictionary = new BinaryDictionary(dictFile.getAbsolutePath(),
+                0 /* offset */, dictFile.length(), true /* useFullEditDistance */,
+                Locale.getDefault(), TEST_LOCALE, true /* isUpdatable */);
+        final ArrayList<String> words = new ArrayList<String>();
+        // Test a word that isn't contained within the dictionary.
+        final Random random = new Random(seed);
+        final int[] codePointSet = CodePointUtils.generateCodePointSet(codePointSetSize, random);
+        final int[] unigramProbabilities = new int[wordCount];
+        for (int i = 0; i < wordCount; ++i) {
+            final String word = CodePointUtils.generateWord(random, codePointSet);
+            words.add(word);
+            final int unigramProbability = random.nextInt(0xFF);
+            unigramProbabilities[i] = unigramProbability;
+            binaryDictionary.addUnigramWord(word, unigramProbability);
+        }
+
+        final int[][] probabilities = new int[wordCount][wordCount];
+
+        for (int i = 0; i < wordCount; ++i) {
+            for (int j = 0; j < wordCount; ++j) {
+                probabilities[i][j] = Dictionary.NOT_A_PROBABILITY;
+            }
+        }
+
+        for (int i = 0; i < bigramCount; i++) {
+            final int word0Index = random.nextInt(wordCount);
+            final int word1Index = random.nextInt(wordCount);
+            final String word0 = words.get(word0Index);
+            final String word1 = words.get(word1Index);
+            final int bigramProbability = random.nextInt(0xF);
+            probabilities[word0Index][word1Index] = binaryDictionary.calculateProbability(
+                    unigramProbabilities[word1Index], bigramProbability);
+            binaryDictionary.addBigramWords(word0, word1, bigramProbability);
+        }
+
+        binaryDictionary.flushWithGC();
+        binaryDictionary.close();
+        binaryDictionary = new BinaryDictionary(dictFile.getAbsolutePath(),
+                0 /* offset */, dictFile.length(), true /* useFullEditDistance */,
+                Locale.getDefault(), TEST_LOCALE, true /* isUpdatable */);
+
+        for (int i = 0; i < words.size(); i++) {
+            for (int j = 0; j < words.size(); j++) {
+                assertEquals(probabilities[i][j],
+                        binaryDictionary.getBigramProbability(words.get(i), words.get(j)));
+            }
+        }
+        dictFile.delete();
+    }
+
+    public void testRandomOperetionsAndFlashWithGC() {
+        final int flashWithGCIterationCount = 50;
+        final int operationCountInEachIteration = 200;
+        final int initialUnigramCount = 100;
+        final float addUnigramProb = 0.5f;
+        final float addBigramProb = 0.8f;
+        final float removeBigramProb = 0.2f;
+        final int codePointSetSize = 30;
+        final int seed = 141421356;
+
+        final Random random = new Random(seed);
+
+        File dictFile = null;
+        try {
+            dictFile = createEmptyDictionaryAndGetFile("TestBinaryDictionary");
+        } catch (IOException e) {
+            fail("IOException while writing an initial dictionary : " + e);
+        } catch (UnsupportedFormatException e) {
+            fail("UnsupportedFormatException while writing an initial dictionary : " + e);
+        }
+
+        BinaryDictionary binaryDictionary = new BinaryDictionary(dictFile.getAbsolutePath(),
+                0 /* offset */, dictFile.length(), true /* useFullEditDistance */,
+                Locale.getDefault(), TEST_LOCALE, true /* isUpdatable */);
+        final ArrayList<String> words = new ArrayList<String>();
+        final ArrayList<Pair<String, String>> bigramWords = new ArrayList<Pair<String,String>>();
+        final int[] codePointSet = CodePointUtils.generateCodePointSet(codePointSetSize, random);
+        final HashMap<String, Integer> unigramProbabilities = new HashMap<String, Integer>();
+        final HashMap<Pair<String, String>, Integer> bigramProbabilities =
+                new HashMap<Pair<String, String>, Integer>();
+        for (int i = 0; i < initialUnigramCount; ++i) {
+            final String word = CodePointUtils.generateWord(random, codePointSet);
+            words.add(word);
+            final int unigramProbability = random.nextInt(0xFF);
+            unigramProbabilities.put(word, unigramProbability);
+            binaryDictionary.addUnigramWord(word, unigramProbability);
+        }
+        binaryDictionary.flushWithGC();
+        binaryDictionary.close();
+
+        for (int gcCount = 0; gcCount < flashWithGCIterationCount; gcCount++) {
+            binaryDictionary = new BinaryDictionary(dictFile.getAbsolutePath(),
+                    0 /* offset */, dictFile.length(), true /* useFullEditDistance */,
+                    Locale.getDefault(), TEST_LOCALE, true /* isUpdatable */);
+            for (int opCount = 0; opCount < operationCountInEachIteration; opCount++) {
+                // Add unigram.
+                if (random.nextFloat() < addUnigramProb) {
+                    final String word = CodePointUtils.generateWord(random, codePointSet);
+                    words.add(word);
+                    final int unigramProbability = random.nextInt(0xFF);
+                    unigramProbabilities.put(word, unigramProbability);
+                    binaryDictionary.addUnigramWord(word, unigramProbability);
+                }
+                // Add bigram.
+                if (random.nextFloat() < addBigramProb && words.size() > 2) {
+                    final int word0Index = random.nextInt(words.size());
+                    int word1Index = random.nextInt(words.size() - 1);
+                    if (word0Index <= word1Index) {
+                        word1Index++;
+                    }
+                    final String word0 = words.get(word0Index);
+                    final String word1 = words.get(word1Index);
+                    final int bigramProbability = random.nextInt(0xF);
+                    final Pair<String, String> bigram = new Pair<String, String>(word0, word1);
+                    bigramWords.add(bigram);
+                    bigramProbabilities.put(bigram, bigramProbability);
+                    binaryDictionary.addBigramWords(word0, word1, bigramProbability);
+                }
+                // Remove bigram.
+                if (random.nextFloat() < removeBigramProb && !bigramWords.isEmpty()) {
+                    final int bigramIndex = random.nextInt(bigramWords.size());
+                    final Pair<String, String> bigram = bigramWords.get(bigramIndex);
+                    bigramWords.remove(bigramIndex);
+                    bigramProbabilities.remove(bigram);
+                    binaryDictionary.removeBigramWords(bigram.first, bigram.second);
+                }
+            }
+
+            // Test whether the all unigram operations are collectlly handled.
+            for (int i = 0; i < words.size(); i++) {
+                final String word = words.get(i);
+                final int unigramProbability = unigramProbabilities.get(word);
+                assertEquals(word, unigramProbability, binaryDictionary.getFrequency(word));
+            }
+            // Test whether the all bigram operations are collectlly handled.
+            for (int i = 0; i < bigramWords.size(); i++) {
+                final Pair<String, String> bigram = bigramWords.get(i);
+                final int unigramProbability = unigramProbabilities.get(bigram.second);
+                final int probability;
+                if (bigramProbabilities.containsKey(bigram)) {
+                    final int bigramProbability = bigramProbabilities.get(bigram);
+                    probability = binaryDictionary.calculateProbability(unigramProbability,
+                            bigramProbability);
+                } else {
+                    probability = Dictionary.NOT_A_PROBABILITY;
+                }
+                assertEquals(probability,
+                        binaryDictionary.getBigramProbability(bigram.first, bigram.second));
+            }
+            binaryDictionary.flushWithGC();
+            binaryDictionary.close();
+        }
+
+        dictFile.delete();
+    }
+
+    public void testAddManyUnigramsAndFlushWithGC() {
+        final int flashWithGCIterationCount = 3;
+        final int codePointSetSize = 50;
+        final int seed = 22360679;
+
+        final Random random = new Random(seed);
+
+        File dictFile = null;
+        try {
+            dictFile = createEmptyDictionaryAndGetFile("TestBinaryDictionary");
+        } catch (IOException e) {
+            fail("IOException while writing an initial dictionary : " + e);
+        } catch (UnsupportedFormatException e) {
+            fail("UnsupportedFormatException while writing an initial dictionary : " + e);
+        }
+
+        final ArrayList<String> words = new ArrayList<String>();
+        final HashMap<String, Integer> unigramProbabilities = new HashMap<String, Integer>();
+        final int[] codePointSet = CodePointUtils.generateCodePointSet(codePointSetSize, random);
+
+        BinaryDictionary binaryDictionary;
+        for (int i = 0; i < flashWithGCIterationCount; i++) {
+            binaryDictionary = new BinaryDictionary(dictFile.getAbsolutePath(),
+                    0 /* offset */, dictFile.length(), true /* useFullEditDistance */,
+                    Locale.getDefault(), TEST_LOCALE, true /* isUpdatable */);
+            while(!binaryDictionary.needsToRunGC()) {
+                final String word = CodePointUtils.generateWord(random, codePointSet);
+                words.add(word);
+                final int unigramProbability = random.nextInt(0xFF);
+                unigramProbabilities.put(word, unigramProbability);
+                binaryDictionary.addUnigramWord(word, unigramProbability);
+            }
+
+            for (int j = 0; j < words.size(); j++) {
+                final String word = words.get(j);
+                final int unigramProbability = unigramProbabilities.get(word);
+                assertEquals(word, unigramProbability, binaryDictionary.getFrequency(word));
+            }
+
+            binaryDictionary.flushWithGC();
+            binaryDictionary.close();
+        }
+
+        dictFile.delete();
+    }
+}
diff --git a/tests/src/com/android/inputmethod/latin/ExpandableDictionaryTests.java b/tests/src/com/android/inputmethod/latin/ExpandableDictionaryTests.java
new file mode 100644
index 0000000..ecf3af7
--- /dev/null
+++ b/tests/src/com/android/inputmethod/latin/ExpandableDictionaryTests.java
@@ -0,0 +1,55 @@
+/*
+ * Copyright (C) 2013 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.latin;
+
+import android.test.AndroidTestCase;
+import android.test.suitebuilder.annotation.SmallTest;
+
+/**
+ * Unit test for ExpandableDictionary
+ */
+@SmallTest
+public class ExpandableDictionaryTests extends AndroidTestCase {
+
+    private final static int UNIGRAM_FREQ = 50;
+
+    public void testAddWordAndGetWordFrequency() {
+        final ExpandableDictionary dict = new ExpandableDictionary(Dictionary.TYPE_USER);
+
+        // Add words
+        dict.addWord("abcde", "abcde", UNIGRAM_FREQ);
+        dict.addWord("abcef", null, UNIGRAM_FREQ + 1);
+
+        // Check words
+        assertFalse(dict.isValidWord("abcde"));
+        assertEquals(UNIGRAM_FREQ, dict.getWordFrequency("abcde"));
+        assertTrue(dict.isValidWord("abcef"));
+        assertEquals(UNIGRAM_FREQ+1, dict.getWordFrequency("abcef"));
+
+        dict.addWord("abc", null, UNIGRAM_FREQ + 2);
+        assertTrue(dict.isValidWord("abc"));
+        assertEquals(UNIGRAM_FREQ + 2, dict.getWordFrequency("abc"));
+
+        // Add existing word with lower frequency
+        dict.addWord("abc", null, UNIGRAM_FREQ);
+        assertEquals(UNIGRAM_FREQ + 2, dict.getWordFrequency("abc"));
+
+        // Add existing word with higher frequency
+        dict.addWord("abc", null, UNIGRAM_FREQ + 3);
+        assertEquals(UNIGRAM_FREQ + 3, dict.getWordFrequency("abc"));
+    }
+}
diff --git a/tests/src/com/android/inputmethod/latin/FusionDictionaryTests.java b/tests/src/com/android/inputmethod/latin/FusionDictionaryTests.java
index 65dfd2d..cadd0f8 100644
--- a/tests/src/com/android/inputmethod/latin/FusionDictionaryTests.java
+++ b/tests/src/com/android/inputmethod/latin/FusionDictionaryTests.java
@@ -20,7 +20,7 @@
 import android.test.suitebuilder.annotation.SmallTest;
 
 import com.android.inputmethod.latin.makedict.FusionDictionary;
-import com.android.inputmethod.latin.makedict.FusionDictionary.Node;
+import com.android.inputmethod.latin.makedict.FusionDictionary.PtNodeArray;
 
 import java.util.HashMap;
 
@@ -30,21 +30,21 @@
 @SmallTest
 public class FusionDictionaryTests extends AndroidTestCase {
     public void testFindWordInTree() {
-        FusionDictionary dict = new FusionDictionary(new Node(),
+        FusionDictionary dict = new FusionDictionary(new PtNodeArray(),
                 new FusionDictionary.DictionaryOptions(new HashMap<String,String>(), false, false));
 
         dict.add("abc", 10, null, false /* isNotAWord */);
-        assertNull(FusionDictionary.findWordInTree(dict.mRoot, "aaa"));
-        assertNotNull(FusionDictionary.findWordInTree(dict.mRoot, "abc"));
+        assertNull(FusionDictionary.findWordInTree(dict.mRootNodeArray, "aaa"));
+        assertNotNull(FusionDictionary.findWordInTree(dict.mRootNodeArray, "abc"));
 
         dict.add("aa", 10, null, false /* isNotAWord */);
-        assertNull(FusionDictionary.findWordInTree(dict.mRoot, "aaa"));
-        assertNotNull(FusionDictionary.findWordInTree(dict.mRoot, "aa"));
+        assertNull(FusionDictionary.findWordInTree(dict.mRootNodeArray, "aaa"));
+        assertNotNull(FusionDictionary.findWordInTree(dict.mRootNodeArray, "aa"));
 
         dict.add("babcd", 10, null, false /* isNotAWord */);
         dict.add("bacde", 10, null, false /* isNotAWord */);
-        assertNull(FusionDictionary.findWordInTree(dict.mRoot, "ba"));
-        assertNotNull(FusionDictionary.findWordInTree(dict.mRoot, "babcd"));
-        assertNotNull(FusionDictionary.findWordInTree(dict.mRoot, "bacde"));
+        assertNull(FusionDictionary.findWordInTree(dict.mRootNodeArray, "ba"));
+        assertNotNull(FusionDictionary.findWordInTree(dict.mRootNodeArray, "babcd"));
+        assertNotNull(FusionDictionary.findWordInTree(dict.mRootNodeArray, "bacde"));
     }
 }
diff --git a/tests/src/com/android/inputmethod/latin/InputLogicTests.java b/tests/src/com/android/inputmethod/latin/InputLogicTests.java
index d27a7a9..cc2569f 100644
--- a/tests/src/com/android/inputmethod/latin/InputLogicTests.java
+++ b/tests/src/com/android/inputmethod/latin/InputLogicTests.java
@@ -17,6 +17,7 @@
 package com.android.inputmethod.latin;
 
 import android.test.suitebuilder.annotation.LargeTest;
+import android.view.inputmethod.BaseInputConnection;
 
 @LargeTest
 public class InputLogicTests extends InputTestsBase {
@@ -133,6 +134,13 @@
         assertEquals("simple auto-correct", EXPECTED_RESULT, mEditText.getText().toString());
     }
 
+    public void testAutoCorrectWithQuote() {
+        final String STRING_TO_TYPE = "didn' ";
+        final String EXPECTED_RESULT = "didn't ";
+        type(STRING_TO_TYPE);
+        assertEquals("auto-correct with quote", EXPECTED_RESULT, mEditText.getText().toString());
+    }
+
     public void testAutoCorrectWithPeriod() {
         final String STRING_TO_TYPE = "tgis.";
         final String EXPECTED_RESULT = "this.";
@@ -290,5 +298,47 @@
         }
         assertEquals("delete whole composing word", "", mEditText.getText().toString());
     }
+
+    public void testResumeSuggestionOnBackspace() {
+        final String WORD_TO_TYPE = "and this ";
+        type(WORD_TO_TYPE);
+        assertEquals("resume suggestion on backspace", -1,
+                BaseInputConnection.getComposingSpanStart(mEditText.getText()));
+        assertEquals("resume suggestion on backspace", -1,
+                BaseInputConnection.getComposingSpanEnd(mEditText.getText()));
+        type(Constants.CODE_DELETE);
+        assertEquals("resume suggestion on backspace", 4,
+                BaseInputConnection.getComposingSpanStart(mEditText.getText()));
+        assertEquals("resume suggestion on backspace", 8,
+                BaseInputConnection.getComposingSpanEnd(mEditText.getText()));
+    }
+
+    private void helperTestComposing(final String wordToType, final boolean shouldBeComposing) {
+        mEditText.setText("");
+        type(wordToType);
+        assertEquals("start composing inside text", shouldBeComposing ? 0 : -1,
+                BaseInputConnection.getComposingSpanStart(mEditText.getText()));
+        assertEquals("start composing inside text", shouldBeComposing ? wordToType.length() : -1,
+                BaseInputConnection.getComposingSpanEnd(mEditText.getText()));
+    }
+
+    public void testStartComposing() {
+        // Should start composing on a letter
+        helperTestComposing("a", true);
+        type("  "); // To reset the composing state
+        // Should not start composing on quote
+        helperTestComposing("'", false);
+        type("  ");
+        helperTestComposing("'-", false);
+        type("  ");
+        // Should not start composing on dash
+        helperTestComposing("-", false);
+        type("  ");
+        helperTestComposing("-'", false);
+        type("  ");
+        helperTestComposing("a-", true);
+        type("  ");
+        helperTestComposing("a'", true);
+    }
     // TODO: Add some tests for non-BMP characters
 }
diff --git a/tests/src/com/android/inputmethod/latin/InputLogicTestsLanguageWithoutSpaces.java b/tests/src/com/android/inputmethod/latin/InputLogicTestsLanguageWithoutSpaces.java
new file mode 100644
index 0000000..0f0ebaf
--- /dev/null
+++ b/tests/src/com/android/inputmethod/latin/InputLogicTestsLanguageWithoutSpaces.java
@@ -0,0 +1,105 @@
+/*
+ * Copyright (C) 2013 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.latin;
+
+import android.test.suitebuilder.annotation.LargeTest;
+import android.view.inputmethod.BaseInputConnection;
+
+import com.android.inputmethod.latin.suggestions.SuggestionStripView;
+
+@LargeTest
+public class InputLogicTestsLanguageWithoutSpaces extends InputTestsBase {
+    public void testAutoCorrectForLanguageWithoutSpaces() {
+        final String STRING_TO_TYPE = "tgis is";
+        final String EXPECTED_RESULT = "thisis";
+        changeKeyboardLocaleAndDictLocale("th", "en_US");
+        type(STRING_TO_TYPE);
+        assertEquals("simple auto-correct for language without spaces", EXPECTED_RESULT,
+                mEditText.getText().toString());
+    }
+
+    public void testRevertAutoCorrectForLanguageWithoutSpaces() {
+        final String STRING_TO_TYPE = "tgis ";
+        final String EXPECTED_INTERMEDIATE_RESULT = "this";
+        final String EXPECTED_FINAL_RESULT = "tgis";
+        changeKeyboardLocaleAndDictLocale("th", "en_US");
+        type(STRING_TO_TYPE);
+        assertEquals("simple auto-correct for language without spaces",
+                EXPECTED_INTERMEDIATE_RESULT, mEditText.getText().toString());
+        type(Constants.CODE_DELETE);
+        assertEquals("simple auto-correct for language without spaces",
+                EXPECTED_FINAL_RESULT, mEditText.getText().toString());
+        // Check we are back to composing the word
+        assertEquals("don't resume suggestion on backspace", 0,
+                BaseInputConnection.getComposingSpanStart(mEditText.getText()));
+        assertEquals("don't resume suggestion on backspace", 4,
+                BaseInputConnection.getComposingSpanEnd(mEditText.getText()));
+    }
+
+    public void testDontResumeSuggestionOnBackspace() {
+        final String WORD_TO_TYPE = "and this ";
+        changeKeyboardLocaleAndDictLocale("th", "en_US");
+        type(WORD_TO_TYPE);
+        assertEquals("don't resume suggestion on backspace", -1,
+                BaseInputConnection.getComposingSpanStart(mEditText.getText()));
+        assertEquals("don't resume suggestion on backspace", -1,
+                BaseInputConnection.getComposingSpanEnd(mEditText.getText()));
+        type(" ");
+        type(Constants.CODE_DELETE);
+        assertEquals("don't resume suggestion on backspace", -1,
+                BaseInputConnection.getComposingSpanStart(mEditText.getText()));
+        assertEquals("don't resume suggestion on backspace", -1,
+                BaseInputConnection.getComposingSpanEnd(mEditText.getText()));
+    }
+
+    public void testStartComposingInsideText() {
+        final String WORD_TO_TYPE = "abcdefgh ";
+        final int typedLength = WORD_TO_TYPE.length() - 1; // -1 because space gets eaten
+        final int CURSOR_POS = 4;
+        changeKeyboardLocaleAndDictLocale("th", "en_US");
+        type(WORD_TO_TYPE);
+        mLatinIME.onUpdateSelection(0, 0, typedLength, typedLength, -1, -1);
+        mInputConnection.setSelection(CURSOR_POS, CURSOR_POS);
+        mLatinIME.onUpdateSelection(typedLength, typedLength,
+                CURSOR_POS, CURSOR_POS, -1, -1);
+        sleep(DELAY_TO_WAIT_FOR_PREDICTIONS);
+        runMessages();
+        assertEquals("start composing inside text", -1,
+                BaseInputConnection.getComposingSpanStart(mEditText.getText()));
+        assertEquals("start composing inside text", -1,
+                BaseInputConnection.getComposingSpanEnd(mEditText.getText()));
+        type("xxxx");
+        assertEquals("start composing inside text", 4,
+                BaseInputConnection.getComposingSpanStart(mEditText.getText()));
+        assertEquals("start composing inside text", 8,
+                BaseInputConnection.getComposingSpanEnd(mEditText.getText()));
+    }
+
+    public void testPredictions() {
+        final String WORD_TO_TYPE = "Barack ";
+        changeKeyboardLocaleAndDictLocale("th", "en_US");
+        type(WORD_TO_TYPE);
+        sleep(DELAY_TO_WAIT_FOR_PREDICTIONS);
+        runMessages();
+        // Make sure there is no space
+        assertEquals("predictions in lang without spaces", "Barack",
+                mEditText.getText().toString());
+        // Test the first prediction is displayed
+        assertEquals("predictions in lang without spaces", "Obama",
+                mLatinIME.getFirstSuggestedWord());
+    }
+}
diff --git a/tests/src/com/android/inputmethod/latin/InputPointersTests.java b/tests/src/com/android/inputmethod/latin/InputPointersTests.java
index f0b6acc..5095f96 100644
--- a/tests/src/com/android/inputmethod/latin/InputPointersTests.java
+++ b/tests/src/com/android/inputmethod/latin/InputPointersTests.java
@@ -244,4 +244,20 @@
                     expecteds[i + expectedPos], actuals[i + actualPos]);
         }
     }
+
+    public void testShift() {
+        final InputPointers src = new InputPointers(DEFAULT_CAPACITY);
+        final int limit = 100;
+        final int shiftAmount = 20;
+        for (int i = 0; i < limit; i++) {
+            src.addPointer(i, i * 2, i * 3, i * 4);
+        }
+        src.shift(shiftAmount);
+        for (int i = 0; i < limit - shiftAmount; ++i) {
+            assertEquals("xCoordinates at " + i, i + shiftAmount, src.getXCoordinates()[i]);
+            assertEquals("yCoordinates at " + i, (i + shiftAmount) * 2, src.getYCoordinates()[i]);
+            assertEquals("pointerIds at " + i, (i + shiftAmount) * 3, src.getPointerIds()[i]);
+            assertEquals("times at " + i, (i + shiftAmount) * 4, src.getTimes()[i]);
+        }
+    }
 }
diff --git a/tests/src/com/android/inputmethod/latin/InputTestsBase.java b/tests/src/com/android/inputmethod/latin/InputTestsBase.java
index eb4f706..234bb1b 100644
--- a/tests/src/com/android/inputmethod/latin/InputTestsBase.java
+++ b/tests/src/com/android/inputmethod/latin/InputTestsBase.java
@@ -44,8 +44,10 @@
 
     private static final String PREF_DEBUG_MODE = "debug_mode";
 
-    // The message that sets the underline is posted with a 100 ms delay
+    // The message that sets the underline is posted with a 200 ms delay
     protected static final int DELAY_TO_WAIT_FOR_UNDERLINE = 200;
+    // The message that sets predictions is posted with a 200 ms delay
+    protected static final int DELAY_TO_WAIT_FOR_PREDICTIONS = 200;
 
     protected LatinIME mLatinIME;
     protected Keyboard mKeyboard;
@@ -204,17 +206,16 @@
         // view and only delegates to the parts of the code that care. So we don't include them here
         // to keep these tests as pinpoint as possible and avoid bringing it too many dependencies,
         // but keep them in mind if something breaks. Commenting them out as is should work.
-        //mLatinIME.onPressKey(codePoint);
-        for (final Key key : mKeyboard.mKeys) {
-            if (key.mCode == codePoint) {
-                final int x = key.mX + key.mWidth / 2;
-                final int y = key.mY + key.mHeight / 2;
-                mLatinIME.onCodeInput(codePoint, x, y);
-                return;
-            }
+        //mLatinIME.onPressKey(codePoint, 0 /* repeatCount */, true /* isSinglePointer */);
+        final Key key = mKeyboard.getKey(codePoint);
+        if (key != null) {
+            final int x = key.getX() + key.getWidth() / 2;
+            final int y = key.getY() + key.getHeight() / 2;
+            mLatinIME.onCodeInput(codePoint, x, y);
+            return;
         }
         mLatinIME.onCodeInput(codePoint, Constants.NOT_A_COORDINATE, Constants.NOT_A_COORDINATE);
-        //mLatinIME.onReleaseKey(codePoint, false);
+        //mLatinIME.onReleaseKey(codePoint, false /* withSliding */);
     }
 
     protected void type(final String stringToType) {
@@ -234,9 +235,6 @@
                 --remainingAttempts;
             }
         }
-        if (!mLatinIME.hasMainDictionary()) {
-            throw new RuntimeException("Can't initialize the main dictionary");
-        }
     }
 
     protected void changeLanguage(final String locale) {
@@ -248,9 +246,21 @@
         waitForDictionaryToBeLoaded();
     }
 
+    protected void changeKeyboardLocaleAndDictLocale(final String keyboardLocale,
+            final String dictLocale) {
+        changeLanguage(keyboardLocale);
+        if (!keyboardLocale.equals(dictLocale)) {
+            mLatinIME.replaceMainDictionaryForTest(
+                    LocaleUtils.constructLocaleFromString(dictLocale));
+        }
+        waitForDictionaryToBeLoaded();
+    }
+
     protected void pickSuggestionManually(final int index, final String suggestion) {
         mLatinIME.pickSuggestionManually(index, new SuggestedWordInfo(suggestion, 1,
-                SuggestedWordInfo.KIND_CORRECTION, "main"));
+                SuggestedWordInfo.KIND_CORRECTION, null /* sourceDict */,
+                SuggestedWordInfo.NOT_AN_INDEX /* indexOfTouchPointOfSecondWord */,
+                SuggestedWordInfo.NOT_A_CONFIDENCE /* autoCommitFirstWordConfidence */));
     }
 
     // Helper to avoid writing the try{}catch block each time
diff --git a/tests/src/com/android/inputmethod/latin/SuggestedWordsTests.java b/tests/src/com/android/inputmethod/latin/SuggestedWordsTests.java
index 8d0fe01..a594baf 100644
--- a/tests/src/com/android/inputmethod/latin/SuggestedWordsTests.java
+++ b/tests/src/com/android/inputmethod/latin/SuggestedWordsTests.java
@@ -34,9 +34,14 @@
         final int NUMBER_OF_ADDED_SUGGESTIONS = 5;
         final ArrayList<SuggestedWordInfo> list = CollectionUtils.newArrayList();
         list.add(new SuggestedWordInfo(TYPED_WORD, TYPED_WORD_FREQ,
-                SuggestedWordInfo.KIND_TYPED, ""));
+                SuggestedWordInfo.KIND_TYPED, null /* sourceDict */,
+                SuggestedWordInfo.NOT_AN_INDEX /* indexOfTouchPointOfSecondWord */,
+                SuggestedWordInfo.NOT_A_CONFIDENCE /* autoCommitFirstWordConfidence */));
         for (int i = 0; i < NUMBER_OF_ADDED_SUGGESTIONS; ++i) {
-            list.add(new SuggestedWordInfo("" + i, 1, SuggestedWordInfo.KIND_CORRECTION, ""));
+            list.add(new SuggestedWordInfo("" + i, 1, SuggestedWordInfo.KIND_CORRECTION,
+                    null /* sourceDict */,
+                    SuggestedWordInfo.NOT_AN_INDEX /* indexOfTouchPointOfSecondWord */,
+                    SuggestedWordInfo.NOT_A_CONFIDENCE /* autoCommitFirstWordConfidence */));
         }
 
         final SuggestedWords words = new SuggestedWords(
@@ -59,4 +64,37 @@
         assertEquals("0", wordsWithoutTyped.getWord(0));
         assertEquals(SuggestedWordInfo.KIND_CORRECTION, wordsWithoutTyped.getInfo(0).mKind);
     }
+
+    // Helper for testGetTransformedWordInfo
+    private SuggestedWordInfo createWordInfo(final String s) {
+        // Use 100 as the frequency because the numerical value does not matter as
+        // long as it's > 1 and < INT_MAX.
+        return new SuggestedWordInfo(s, 100,
+                SuggestedWordInfo.KIND_TYPED, null /* sourceDict */,
+                SuggestedWordInfo.NOT_AN_INDEX /* indexOfTouchPointOfSecondWord */,
+                SuggestedWordInfo.NOT_A_CONFIDENCE /* autoCommitFirstWordConfidence */);
+    }
+
+    // Helper for testGetTransformedWordInfo
+    private SuggestedWordInfo transformWordInfo(final String info,
+            final int trailingSingleQuotesCount) {
+        return Suggest.getTransformedSuggestedWordInfo(createWordInfo(info),
+                Locale.ENGLISH, false /* isAllUpperCase */, false /* isFirstCharCapitalized */,
+                trailingSingleQuotesCount);
+    }
+
+    public void testGetTransformedSuggestedWordInfo() {
+        SuggestedWordInfo result = transformWordInfo("word", 0);
+        assertEquals(result.mWord, "word");
+        result = transformWordInfo("word", 1);
+        assertEquals(result.mWord, "word'");
+        result = transformWordInfo("word", 3);
+        assertEquals(result.mWord, "word'''");
+        result = transformWordInfo("didn't", 0);
+        assertEquals(result.mWord, "didn't");
+        result = transformWordInfo("didn't", 1);
+        assertEquals(result.mWord, "didn't");
+        result = transformWordInfo("didn't", 3);
+        assertEquals(result.mWord, "didn't''");
+    }
 }
diff --git a/tests/src/com/android/inputmethod/latin/makedict/BinaryDictDecoderEncoderTests.java b/tests/src/com/android/inputmethod/latin/makedict/BinaryDictDecoderEncoderTests.java
new file mode 100644
index 0000000..a4d9426
--- /dev/null
+++ b/tests/src/com/android/inputmethod/latin/makedict/BinaryDictDecoderEncoderTests.java
@@ -0,0 +1,684 @@
+/*
+ * Copyright (C) 2012 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.latin.makedict;
+
+import android.test.AndroidTestCase;
+import android.test.MoreAsserts;
+import android.test.suitebuilder.annotation.LargeTest;
+import android.util.Log;
+import android.util.SparseArray;
+
+import com.android.inputmethod.latin.makedict.BinaryDictDecoderUtils.CharEncoding;
+import com.android.inputmethod.latin.makedict.BinaryDictDecoderUtils.DictBuffer;
+import com.android.inputmethod.latin.makedict.FormatSpec.FileHeader;
+import com.android.inputmethod.latin.makedict.FormatSpec.FormatOptions;
+import com.android.inputmethod.latin.makedict.FusionDictionary.DictionaryOptions;
+import com.android.inputmethod.latin.makedict.FusionDictionary.PtNode;
+import com.android.inputmethod.latin.makedict.FusionDictionary.PtNodeArray;
+import com.android.inputmethod.latin.makedict.FusionDictionary.WeightedString;
+import com.android.inputmethod.latin.utils.ByteArrayDictBuffer;
+import com.android.inputmethod.latin.utils.CollectionUtils;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map.Entry;
+import java.util.Random;
+import java.util.Set;
+import java.util.TreeMap;
+
+/**
+ * Unit tests for BinaryDictDecoderUtils and BinaryDictEncoderUtils.
+ */
+@LargeTest
+public class BinaryDictDecoderEncoderTests extends AndroidTestCase {
+    private static final String TAG = BinaryDictDecoderEncoderTests.class.getSimpleName();
+    private static final int DEFAULT_MAX_UNIGRAMS = 100;
+    private static final int DEFAULT_CODE_POINT_SET_SIZE = 50;
+    private static final int UNIGRAM_FREQ = 10;
+    private static final int BIGRAM_FREQ = 50;
+    private static final int TOLERANCE_OF_BIGRAM_FREQ = 5;
+    private static final int NUM_OF_NODES_HAVING_SHORTCUTS = 50;
+    private static final int NUM_OF_SHORTCUTS = 5;
+
+    private static final int USE_BYTE_ARRAY = 1;
+    private static final int USE_BYTE_BUFFER = 2;
+
+    private static final ArrayList<String> sWords = CollectionUtils.newArrayList();
+    private static final SparseArray<List<Integer>> sEmptyBigrams =
+            CollectionUtils.newSparseArray();
+    private static final SparseArray<List<Integer>> sStarBigrams = CollectionUtils.newSparseArray();
+    private static final SparseArray<List<Integer>> sChainBigrams =
+            CollectionUtils.newSparseArray();
+    private static final HashMap<String, List<String>> sShortcuts = CollectionUtils.newHashMap();
+
+    private static final FormatSpec.FormatOptions VERSION2 = new FormatSpec.FormatOptions(2);
+    private static final FormatSpec.FormatOptions VERSION3_WITHOUT_DYNAMIC_UPDATE =
+            new FormatSpec.FormatOptions(3, false /* supportsDynamicUpdate */);
+    private static final FormatSpec.FormatOptions VERSION3_WITH_DYNAMIC_UPDATE =
+            new FormatSpec.FormatOptions(3, true /* supportsDynamicUpdate */);
+    private static final FormatSpec.FormatOptions VERSION4_WITHOUT_DYNAMIC_UPDATE =
+            new FormatSpec.FormatOptions(4, false /* supportsDynamicUpdate */);
+    private static final FormatSpec.FormatOptions VERSION4_WITH_DYNAMIC_UPDATE =
+            new FormatSpec.FormatOptions(4, true /* supportsDynamicUpdate */);
+
+    private static final String TEST_DICT_FILE_EXTENSION = ".testDict";
+
+    public BinaryDictDecoderEncoderTests() {
+        this(System.currentTimeMillis(), DEFAULT_MAX_UNIGRAMS);
+    }
+
+    public BinaryDictDecoderEncoderTests(final long seed, final int maxUnigrams) {
+        super();
+        Log.e(TAG, "Testing dictionary: seed is " + seed);
+        final Random random = new Random(seed);
+        sWords.clear();
+        final int[] codePointSet = CodePointUtils.generateCodePointSet(DEFAULT_CODE_POINT_SET_SIZE,
+                random);
+        generateWords(maxUnigrams, random, codePointSet);
+
+        for (int i = 0; i < sWords.size(); ++i) {
+            sChainBigrams.put(i, new ArrayList<Integer>());
+            if (i > 0) {
+                sChainBigrams.get(i - 1).add(i);
+            }
+        }
+
+        sStarBigrams.put(0, new ArrayList<Integer>());
+        for (int i = 1; i < sWords.size(); ++i) {
+            sStarBigrams.get(0).add(i);
+        }
+
+        sShortcuts.clear();
+        for (int i = 0; i < NUM_OF_NODES_HAVING_SHORTCUTS; ++i) {
+            final int from = Math.abs(random.nextInt()) % sWords.size();
+            sShortcuts.put(sWords.get(from), new ArrayList<String>());
+            for (int j = 0; j < NUM_OF_SHORTCUTS; ++j) {
+                final int to = Math.abs(random.nextInt()) % sWords.size();
+                sShortcuts.get(sWords.get(from)).add(sWords.get(to));
+            }
+        }
+    }
+
+    private DictEncoder getDictEncoder(final File file, final FormatOptions formatOptions) {
+        if (formatOptions.mVersion == FormatSpec.VERSION4) {
+            return new Ver4DictEncoder(getContext().getCacheDir());
+        } else if (formatOptions.mVersion == 3 || formatOptions.mVersion == 2) {
+            return new Ver3DictEncoder(file);
+        } else {
+            throw new RuntimeException("The format option has a wrong version : "
+                    + formatOptions.mVersion);
+        }
+    }
+
+    private void generateWords(final int number, final Random random, final int[] codePointSet) {
+        final Set<String> wordSet = CollectionUtils.newHashSet();
+        while (wordSet.size() < number) {
+            wordSet.add(CodePointUtils.generateWord(random, codePointSet));
+        }
+        sWords.addAll(wordSet);
+    }
+
+    /**
+     * Adds unigrams to the dictionary.
+     */
+    private void addUnigrams(final int number, final FusionDictionary dict,
+            final List<String> words, final HashMap<String, List<String>> shortcutMap) {
+        for (int i = 0; i < number; ++i) {
+            final String word = words.get(i);
+            final ArrayList<WeightedString> shortcuts = CollectionUtils.newArrayList();
+            if (shortcutMap != null && shortcutMap.containsKey(word)) {
+                for (final String shortcut : shortcutMap.get(word)) {
+                    shortcuts.add(new WeightedString(shortcut, UNIGRAM_FREQ));
+                }
+            }
+            dict.add(word, UNIGRAM_FREQ, (shortcutMap == null) ? null : shortcuts,
+                    false /* isNotAWord */);
+        }
+    }
+
+    private void addBigrams(final FusionDictionary dict,
+            final List<String> words,
+            final SparseArray<List<Integer>> bigrams) {
+        for (int i = 0; i < bigrams.size(); ++i) {
+            final int w1 = bigrams.keyAt(i);
+            for (int w2 : bigrams.valueAt(i)) {
+                dict.setBigram(words.get(w1), words.get(w2), BIGRAM_FREQ);
+            }
+        }
+    }
+
+//    The following is useful to dump the dictionary into a textual file, but it can't compile
+//    on-device, so it's commented out.
+//    private void dumpToCombinedFileForDebug(final FusionDictionary dict, final String filename)
+//            throws IOException {
+//        com.android.inputmethod.latin.dicttool.CombinedInputOutput.writeDictionaryCombined(
+//                new java.io.FileWriter(new File(filename)), dict);
+//    }
+
+    private long timeWritingDictToFile(final File file, final FusionDictionary dict,
+            final FormatSpec.FormatOptions formatOptions) {
+
+        long now = -1, diff = -1;
+
+        try {
+            final DictEncoder dictEncoder = getDictEncoder(file, formatOptions);
+
+            now = System.currentTimeMillis();
+            // If you need to dump the dict to a textual file, uncomment the line below and the
+            // function above
+            // dumpToCombinedFileForDebug(file, "/tmp/foo");
+            dictEncoder.writeDictionary(dict, formatOptions);
+            diff = System.currentTimeMillis() - now;
+        } catch (IOException e) {
+            Log.e(TAG, "IO exception while writing file", e);
+        } catch (UnsupportedFormatException e) {
+            Log.e(TAG, "UnsupportedFormatException", e);
+        }
+
+        return diff;
+    }
+
+    private void checkDictionary(final FusionDictionary dict, final List<String> words,
+            final SparseArray<List<Integer>> bigrams,
+            final HashMap<String, List<String>> shortcutMap) {
+        assertNotNull(dict);
+
+        // check unigram
+        for (final String word : words) {
+            final PtNode ptNode = FusionDictionary.findWordInTree(dict.mRootNodeArray, word);
+            assertNotNull(ptNode);
+        }
+
+        // check bigram
+        for (int i = 0; i < bigrams.size(); ++i) {
+            final int w1 = bigrams.keyAt(i);
+            for (final int w2 : bigrams.valueAt(i)) {
+                final PtNode ptNode = FusionDictionary.findWordInTree(dict.mRootNodeArray,
+                        words.get(w1));
+                assertNotNull(words.get(w1) + "," + words.get(w2), ptNode.getBigram(words.get(w2)));
+            }
+        }
+
+        // check shortcut
+        if (shortcutMap != null) {
+            for (final Entry<String, List<String>> entry : shortcutMap.entrySet()) {
+                assertTrue(words.contains(entry.getKey()));
+                final PtNode ptNode = FusionDictionary.findWordInTree(dict.mRootNodeArray,
+                        entry.getKey());
+                for (final String word : entry.getValue()) {
+                    assertNotNull("shortcut not found: " + entry.getKey() + ", " + word,
+                            ptNode.getShortcut(word));
+                }
+            }
+        }
+    }
+
+    private String outputOptions(final int bufferType,
+            final FormatSpec.FormatOptions formatOptions) {
+        String result = " : buffer type = "
+                + ((bufferType == USE_BYTE_BUFFER) ? "byte buffer" : "byte array");
+        result += " : version = " + formatOptions.mVersion;
+        return result + ", supportsDynamicUpdate = " + formatOptions.mSupportsDynamicUpdate;
+    }
+
+    private DictionaryOptions getDictionaryOptions(final String id, final String version) {
+        final DictionaryOptions options = new DictionaryOptions(new HashMap<String, String>(),
+                false, false);
+        options.mAttributes.put("version", version);
+        options.mAttributes.put("dictionary", id);
+        return options;
+    }
+
+    private File setUpDictionaryFile(final String name, final String version) {
+        File file = null;
+        try {
+            file = new File(getContext().getCacheDir(), name + "." + version
+                    + TEST_DICT_FILE_EXTENSION);
+            file.createNewFile();
+        } catch (IOException e) {
+            // do nothing
+        }
+        assertTrue("Failed to create the dictionary file.", file.exists());
+        return file;
+    }
+
+    private DictDecoder getDictDecoder(final File file, final int bufferType,
+            final FormatOptions formatOptions, final DictionaryOptions dictOptions) {
+        if (formatOptions.mVersion == FormatSpec.VERSION4) {
+            final FileHeader header = new FileHeader(0, dictOptions, formatOptions);
+            return FormatSpec.getDictDecoder(new File(getContext().getCacheDir(),
+                    header.getId() + "." + header.getVersion()), bufferType);
+        } else {
+            return FormatSpec.getDictDecoder(file, bufferType);
+        }
+    }
+    // Tests for readDictionaryBinary and writeDictionaryBinary
+
+    private long timeReadingAndCheckDict(final File file, final List<String> words,
+            final SparseArray<List<Integer>> bigrams,
+            final HashMap<String, List<String>> shortcutMap, final int bufferType,
+            final FormatOptions formatOptions, final DictionaryOptions dictOptions) {
+        long now, diff = -1;
+
+        FusionDictionary dict = null;
+        try {
+            final DictDecoder dictDecoder = getDictDecoder(file, bufferType, formatOptions,
+                    dictOptions);
+            now = System.currentTimeMillis();
+            dict = dictDecoder.readDictionaryBinary(null, false /* deleteDictIfBroken */);
+            diff  = System.currentTimeMillis() - now;
+        } catch (IOException e) {
+            Log.e(TAG, "IOException while reading dictionary", e);
+        } catch (UnsupportedFormatException e) {
+            Log.e(TAG, "Unsupported format", e);
+        }
+
+        checkDictionary(dict, words, bigrams, shortcutMap);
+        return diff;
+    }
+
+    // Tests for readDictionaryBinary and writeDictionaryBinary
+    private String runReadAndWrite(final List<String> words,
+            final SparseArray<List<Integer>> bigrams, final HashMap<String, List<String>> shortcuts,
+            final int bufferType, final FormatSpec.FormatOptions formatOptions,
+            final String message) {
+
+        final String dictName = "runReadAndWrite";
+        final String dictVersion = Long.toString(System.currentTimeMillis());
+        final File file = setUpDictionaryFile(dictName, dictVersion);
+
+        final FusionDictionary dict = new FusionDictionary(new PtNodeArray(),
+                getDictionaryOptions(dictName, dictVersion));
+        addUnigrams(words.size(), dict, words, shortcuts);
+        addBigrams(dict, words, bigrams);
+        checkDictionary(dict, words, bigrams, shortcuts);
+
+        final long write = timeWritingDictToFile(file, dict, formatOptions);
+        final long read = timeReadingAndCheckDict(file, words, bigrams, shortcuts, bufferType,
+                formatOptions, dict.mOptions);
+
+        return "PROF: read=" + read + "ms, write=" + write + "ms :" + message
+                + " : " + outputOptions(bufferType, formatOptions);
+    }
+
+    private void runReadAndWriteTests(final List<String> results, final int bufferType,
+            final FormatSpec.FormatOptions formatOptions) {
+        results.add(runReadAndWrite(sWords, sEmptyBigrams, null /* shortcuts */, bufferType,
+                formatOptions, "unigram"));
+        results.add(runReadAndWrite(sWords, sChainBigrams, null /* shortcuts */, bufferType,
+                formatOptions, "chain"));
+        results.add(runReadAndWrite(sWords, sStarBigrams, null /* shortcuts */, bufferType,
+                formatOptions, "star"));
+        results.add(runReadAndWrite(sWords, sEmptyBigrams, sShortcuts, bufferType, formatOptions,
+                "unigram with shortcuts"));
+        results.add(runReadAndWrite(sWords, sChainBigrams, sShortcuts, bufferType, formatOptions,
+                "chain with shortcuts"));
+        results.add(runReadAndWrite(sWords, sStarBigrams, sShortcuts, bufferType, formatOptions,
+                "star with shortcuts"));
+    }
+
+    // Unit test for CharEncoding.readString and CharEncoding.writeString.
+    public void testCharEncoding() {
+        // the max length of a word in sWords is less than 50.
+        // See generateWords.
+        final byte[] buffer = new byte[50 * 3];
+        final DictBuffer dictBuffer = new ByteArrayDictBuffer(buffer);
+        for (final String word : sWords) {
+            Log.d("testReadAndWriteString", "write : " + word);
+            Arrays.fill(buffer, (byte)0);
+            CharEncoding.writeString(buffer, 0, word);
+            dictBuffer.position(0);
+            final String str = CharEncoding.readString(dictBuffer);
+            assertEquals(word, str);
+        }
+    }
+
+    public void testReadAndWriteWithByteBuffer() {
+        final List<String> results = CollectionUtils.newArrayList();
+
+        runReadAndWriteTests(results, USE_BYTE_BUFFER, VERSION2);
+        runReadAndWriteTests(results, USE_BYTE_BUFFER, VERSION3_WITHOUT_DYNAMIC_UPDATE);
+        runReadAndWriteTests(results, USE_BYTE_BUFFER, VERSION3_WITH_DYNAMIC_UPDATE);
+        runReadAndWriteTests(results, USE_BYTE_BUFFER, VERSION4_WITHOUT_DYNAMIC_UPDATE);
+        runReadAndWriteTests(results, USE_BYTE_BUFFER, VERSION4_WITH_DYNAMIC_UPDATE);
+
+        for (final String result : results) {
+            Log.d(TAG, result);
+        }
+    }
+
+    public void testReadAndWriteWithByteArray() {
+        final List<String> results = CollectionUtils.newArrayList();
+
+        runReadAndWriteTests(results, USE_BYTE_ARRAY, VERSION2);
+        runReadAndWriteTests(results, USE_BYTE_ARRAY, VERSION3_WITHOUT_DYNAMIC_UPDATE);
+        runReadAndWriteTests(results, USE_BYTE_ARRAY, VERSION3_WITH_DYNAMIC_UPDATE);
+        runReadAndWriteTests(results, USE_BYTE_ARRAY, VERSION4_WITHOUT_DYNAMIC_UPDATE);
+        runReadAndWriteTests(results, USE_BYTE_ARRAY, VERSION4_WITH_DYNAMIC_UPDATE);
+
+        for (final String result : results) {
+            Log.d(TAG, result);
+        }
+    }
+
+    // Tests for readUnigramsAndBigramsBinary
+
+    private void checkWordMap(final List<String> expectedWords,
+            final SparseArray<List<Integer>> expectedBigrams,
+            final TreeMap<Integer, String> resultWords,
+            final TreeMap<Integer, Integer> resultFrequencies,
+            final TreeMap<Integer, ArrayList<PendingAttribute>> resultBigrams) {
+        // check unigrams
+        final Set<String> actualWordsSet = new HashSet<String>(resultWords.values());
+        final Set<String> expectedWordsSet = new HashSet<String>(expectedWords);
+        assertEquals(actualWordsSet, expectedWordsSet);
+
+        for (int freq : resultFrequencies.values()) {
+            assertEquals(freq, UNIGRAM_FREQ);
+        }
+
+        // check bigrams
+        final HashMap<String, List<String>> expBigrams = new HashMap<String, List<String>>();
+        for (int i = 0; i < expectedBigrams.size(); ++i) {
+            final String word1 = expectedWords.get(expectedBigrams.keyAt(i));
+            for (int w2 : expectedBigrams.valueAt(i)) {
+                if (expBigrams.get(word1) == null) {
+                    expBigrams.put(word1, new ArrayList<String>());
+                }
+                expBigrams.get(word1).add(expectedWords.get(w2));
+            }
+        }
+
+        final HashMap<String, List<String>> actBigrams = new HashMap<String, List<String>>();
+        for (Entry<Integer, ArrayList<PendingAttribute>> entry : resultBigrams.entrySet()) {
+            final String word1 = resultWords.get(entry.getKey());
+            final int unigramFreq = resultFrequencies.get(entry.getKey());
+            for (PendingAttribute attr : entry.getValue()) {
+                final String word2 = resultWords.get(attr.mAddress);
+                if (actBigrams.get(word1) == null) {
+                    actBigrams.put(word1, new ArrayList<String>());
+                }
+                actBigrams.get(word1).add(word2);
+
+                final int bigramFreq = BinaryDictIOUtils.reconstructBigramFrequency(
+                        unigramFreq, attr.mFrequency);
+                assertTrue(Math.abs(bigramFreq - BIGRAM_FREQ) < TOLERANCE_OF_BIGRAM_FREQ);
+            }
+        }
+
+        assertEquals(actBigrams, expBigrams);
+    }
+
+    private long timeAndCheckReadUnigramsAndBigramsBinary(final File file, final List<String> words,
+            final SparseArray<List<Integer>> bigrams, final int bufferType,
+            final FormatOptions formatOptions, final DictionaryOptions dictOptions) {
+        FileInputStream inStream = null;
+
+        final TreeMap<Integer, String> resultWords = CollectionUtils.newTreeMap();
+        final TreeMap<Integer, ArrayList<PendingAttribute>> resultBigrams =
+                CollectionUtils.newTreeMap();
+        final TreeMap<Integer, Integer> resultFreqs = CollectionUtils.newTreeMap();
+
+        long now = -1, diff = -1;
+        try {
+            final DictDecoder dictDecoder = getDictDecoder(file, bufferType, formatOptions,
+                    dictOptions);
+            now = System.currentTimeMillis();
+            dictDecoder.readUnigramsAndBigramsBinary(resultWords, resultFreqs, resultBigrams);
+            diff = System.currentTimeMillis() - now;
+        } catch (IOException e) {
+            Log.e(TAG, "IOException", e);
+        } catch (UnsupportedFormatException e) {
+            Log.e(TAG, "UnsupportedFormatException", e);
+        } finally {
+            if (inStream != null) {
+                try {
+                    inStream.close();
+                } catch (IOException e) {
+                    // do nothing
+                }
+            }
+        }
+
+        checkWordMap(words, bigrams, resultWords, resultFreqs, resultBigrams);
+        return diff;
+    }
+
+    private String runReadUnigramsAndBigramsBinary(final ArrayList<String> words,
+            final SparseArray<List<Integer>> bigrams, final int bufferType,
+            final FormatSpec.FormatOptions formatOptions, final String message) {
+        final String dictName = "runReadUnigrams";
+        final String dictVersion = Long.toString(System.currentTimeMillis());
+        final File file = setUpDictionaryFile(dictName, dictVersion);
+
+        // making the dictionary from lists of words.
+        final FusionDictionary dict = new FusionDictionary(new PtNodeArray(),
+                getDictionaryOptions(dictName, dictVersion));
+        addUnigrams(words.size(), dict, words, null /* shortcutMap */);
+        addBigrams(dict, words, bigrams);
+
+        timeWritingDictToFile(file, dict, formatOptions);
+
+        long wordMap = timeAndCheckReadUnigramsAndBigramsBinary(file, words, bigrams, bufferType,
+                formatOptions, dict.mOptions);
+        long fullReading = timeReadingAndCheckDict(file, words, bigrams, null /* shortcutMap */,
+                bufferType, formatOptions, dict.mOptions);
+
+        return "readDictionaryBinary=" + fullReading + ", readUnigramsAndBigramsBinary=" + wordMap
+                + " : " + message + " : " + outputOptions(bufferType, formatOptions);
+    }
+
+    private void runReadUnigramsAndBigramsTests(final ArrayList<String> results,
+            final int bufferType, final FormatSpec.FormatOptions formatOptions) {
+        results.add(runReadUnigramsAndBigramsBinary(sWords, sEmptyBigrams, bufferType,
+                formatOptions, "unigram"));
+        results.add(runReadUnigramsAndBigramsBinary(sWords, sChainBigrams, bufferType,
+                formatOptions, "chain"));
+        results.add(runReadUnigramsAndBigramsBinary(sWords, sStarBigrams, bufferType,
+                formatOptions, "star"));
+    }
+
+    public void testReadUnigramsAndBigramsBinaryWithByteBuffer() {
+        final ArrayList<String> results = CollectionUtils.newArrayList();
+
+        runReadUnigramsAndBigramsTests(results, USE_BYTE_BUFFER, VERSION2);
+        runReadUnigramsAndBigramsTests(results, USE_BYTE_BUFFER, VERSION3_WITHOUT_DYNAMIC_UPDATE);
+        runReadUnigramsAndBigramsTests(results, USE_BYTE_BUFFER, VERSION3_WITH_DYNAMIC_UPDATE);
+        runReadUnigramsAndBigramsTests(results, USE_BYTE_BUFFER, VERSION4_WITHOUT_DYNAMIC_UPDATE);
+        runReadUnigramsAndBigramsTests(results, USE_BYTE_BUFFER, VERSION4_WITH_DYNAMIC_UPDATE);
+
+        for (final String result : results) {
+            Log.d(TAG, result);
+        }
+    }
+
+    public void testReadUnigramsAndBigramsBinaryWithByteArray() {
+        final ArrayList<String> results = CollectionUtils.newArrayList();
+
+        runReadUnigramsAndBigramsTests(results, USE_BYTE_ARRAY, VERSION2);
+        runReadUnigramsAndBigramsTests(results, USE_BYTE_ARRAY, VERSION3_WITHOUT_DYNAMIC_UPDATE);
+        runReadUnigramsAndBigramsTests(results, USE_BYTE_ARRAY, VERSION3_WITH_DYNAMIC_UPDATE);
+        runReadUnigramsAndBigramsTests(results, USE_BYTE_ARRAY, VERSION4_WITHOUT_DYNAMIC_UPDATE);
+        runReadUnigramsAndBigramsTests(results, USE_BYTE_ARRAY, VERSION4_WITH_DYNAMIC_UPDATE);
+
+        for (final String result : results) {
+            Log.d(TAG, result);
+        }
+    }
+
+    // Tests for getTerminalPosition
+    private String getWordFromBinary(final DictDecoder dictDecoder, final int address) {
+        if (dictDecoder.getPosition() != 0) dictDecoder.setPosition(0);
+
+        FileHeader fileHeader = null;
+        try {
+            fileHeader = dictDecoder.readHeader();
+        } catch (IOException e) {
+            return null;
+        } catch (UnsupportedFormatException e) {
+            return null;
+        }
+        if (fileHeader == null) return null;
+        return BinaryDictDecoderUtils.getWordAtPosition(dictDecoder, fileHeader.mHeaderSize,
+                address, fileHeader.mFormatOptions).mWord;
+    }
+
+    private long checkGetTerminalPosition(final DictDecoder dictDecoder, final String word,
+            int index, boolean contained) {
+        final int expectedFrequency = (UNIGRAM_FREQ + index) % 255;
+        long diff = -1;
+        int position = -1;
+        try {
+            final long now = System.nanoTime();
+            position = dictDecoder.getTerminalPosition(word);
+            diff = System.nanoTime() - now;
+        } catch (IOException e) {
+            Log.e(TAG, "IOException while getTerminalPosition", e);
+        } catch (UnsupportedFormatException e) {
+            Log.e(TAG, "UnsupportedFormatException while getTerminalPosition", e);
+        }
+
+        assertEquals(FormatSpec.NOT_VALID_WORD != position, contained);
+        if (contained) assertEquals(getWordFromBinary(dictDecoder, position), word);
+        return diff;
+    }
+
+    private void runGetTerminalPosition(final ArrayList<String> words,
+            final SparseArray<List<Integer>> bigrams, final int bufferType,
+            final FormatOptions formatOptions, final String message) {
+        final String dictName = "testGetTerminalPosition";
+        final String dictVersion = Long.toString(System.currentTimeMillis());
+        final File file = setUpDictionaryFile(dictName, dictVersion);
+
+        final FusionDictionary dict = new FusionDictionary(new PtNodeArray(),
+                getDictionaryOptions(dictName, dictVersion));
+        addUnigrams(sWords.size(), dict, sWords, null /* shortcutMap */);
+        addBigrams(dict, words, bigrams);
+        timeWritingDictToFile(file, dict, formatOptions);
+
+        final DictDecoder dictDecoder = getDictDecoder(file, DictDecoder.USE_BYTEARRAY,
+                formatOptions, dict.mOptions);
+        try {
+            dictDecoder.openDictBuffer();
+        } catch (IOException e) {
+            // ignore
+            Log.e(TAG, "IOException while opening the buffer", e);
+        }
+        assertTrue("Can't get the buffer", dictDecoder.isDictBufferOpen());
+
+        try {
+            // too long word
+            final String longWord = "abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyz";
+            assertEquals(FormatSpec.NOT_VALID_WORD, dictDecoder.getTerminalPosition(longWord));
+
+            // null
+            assertEquals(FormatSpec.NOT_VALID_WORD, dictDecoder.getTerminalPosition(null));
+
+            // empty string
+            assertEquals(FormatSpec.NOT_VALID_WORD, dictDecoder.getTerminalPosition(""));
+        } catch (IOException e) {
+        } catch (UnsupportedFormatException e) {
+        }
+
+        // Test a word that is contained within the dictionary.
+        long sum = 0;
+        for (int i = 0; i < sWords.size(); ++i) {
+            final long time = checkGetTerminalPosition(dictDecoder, sWords.get(i), i, true);
+            sum += time == -1 ? 0 : time;
+        }
+        Log.d(TAG, "per search : " + (((double)sum) / sWords.size() / 1000000) + " : " + message
+                + " : " + outputOptions(bufferType, formatOptions));
+
+        // Test a word that isn't contained within the dictionary.
+        final Random random = new Random((int)System.currentTimeMillis());
+        final int[] codePointSet = CodePointUtils.generateCodePointSet(DEFAULT_CODE_POINT_SET_SIZE,
+                random);
+        for (int i = 0; i < 1000; ++i) {
+            final String word = CodePointUtils.generateWord(random, codePointSet);
+            if (sWords.indexOf(word) != -1) continue;
+            checkGetTerminalPosition(dictDecoder, word, i, false);
+        }
+    }
+
+    private void runGetTerminalPositionTests(final ArrayList<String> results, final int bufferType,
+            final FormatOptions formatOptions) {
+        runGetTerminalPosition(sWords, sEmptyBigrams, bufferType, formatOptions, "unigram");
+    }
+
+    public void testGetTerminalPosition() {
+        final ArrayList<String> results = CollectionUtils.newArrayList();
+
+        runGetTerminalPositionTests(results, USE_BYTE_ARRAY, VERSION2);
+        runGetTerminalPositionTests(results, USE_BYTE_ARRAY, VERSION3_WITHOUT_DYNAMIC_UPDATE);
+        runGetTerminalPositionTests(results, USE_BYTE_ARRAY, VERSION3_WITH_DYNAMIC_UPDATE);
+        runGetTerminalPositionTests(results, USE_BYTE_ARRAY, VERSION4_WITHOUT_DYNAMIC_UPDATE);
+        runGetTerminalPositionTests(results, USE_BYTE_ARRAY, VERSION4_WITH_DYNAMIC_UPDATE);
+
+        runGetTerminalPositionTests(results, USE_BYTE_BUFFER, VERSION2);
+        runGetTerminalPositionTests(results, USE_BYTE_BUFFER, VERSION3_WITHOUT_DYNAMIC_UPDATE);
+        runGetTerminalPositionTests(results, USE_BYTE_BUFFER, VERSION3_WITH_DYNAMIC_UPDATE);
+        runGetTerminalPositionTests(results, USE_BYTE_BUFFER, VERSION4_WITHOUT_DYNAMIC_UPDATE);
+        runGetTerminalPositionTests(results, USE_BYTE_BUFFER, VERSION4_WITH_DYNAMIC_UPDATE);
+
+        for (final String result : results) {
+            Log.d(TAG, result);
+        }
+    }
+
+    public void testDeleteWord() {
+        final String dictName = "testDeleteWord";
+        final String dictVersion = Long.toString(System.currentTimeMillis());
+        final File file = setUpDictionaryFile(dictName, dictVersion);
+
+        final FusionDictionary dict = new FusionDictionary(new PtNodeArray(),
+                new FusionDictionary.DictionaryOptions(
+                        new HashMap<String, String>(), false, false));
+        addUnigrams(sWords.size(), dict, sWords, null /* shortcutMap */);
+        timeWritingDictToFile(file, dict, VERSION3_WITH_DYNAMIC_UPDATE);
+
+        final Ver3DictDecoder dictDecoder = new Ver3DictDecoder(file, DictDecoder.USE_BYTEARRAY);
+        try {
+            dictDecoder.openDictBuffer();
+        } catch (IOException e) {
+            // ignore
+            Log.e(TAG, "IOException while opening the buffer", e);
+        }
+        assertTrue("Can't get the buffer", dictDecoder.isDictBufferOpen());
+
+        try {
+            MoreAsserts.assertNotEqual(FormatSpec.NOT_VALID_WORD,
+                    dictDecoder.getTerminalPosition(sWords.get(0)));
+            DynamicBinaryDictIOUtils.deleteWord(dictDecoder, sWords.get(0));
+            assertEquals(FormatSpec.NOT_VALID_WORD,
+                    dictDecoder.getTerminalPosition(sWords.get(0)));
+
+            MoreAsserts.assertNotEqual(FormatSpec.NOT_VALID_WORD,
+                    dictDecoder.getTerminalPosition(sWords.get(5)));
+            DynamicBinaryDictIOUtils.deleteWord(dictDecoder, sWords.get(5));
+            assertEquals(FormatSpec.NOT_VALID_WORD,
+                    dictDecoder.getTerminalPosition(sWords.get(5)));
+        } catch (IOException e) {
+        } catch (UnsupportedFormatException e) {
+        }
+    }
+}
diff --git a/tests/src/com/android/inputmethod/latin/makedict/BinaryDictIOTests.java b/tests/src/com/android/inputmethod/latin/makedict/BinaryDictIOTests.java
deleted file mode 100644
index ef4ed33..0000000
--- a/tests/src/com/android/inputmethod/latin/makedict/BinaryDictIOTests.java
+++ /dev/null
@@ -1,635 +0,0 @@
-/*
- * Copyright (C) 2012 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.latin.makedict;
-
-import android.test.AndroidTestCase;
-import android.test.MoreAsserts;
-import android.test.suitebuilder.annotation.LargeTest;
-import android.util.Log;
-import android.util.SparseArray;
-
-import com.android.inputmethod.latin.makedict.BinaryDictInputOutput.FusionDictionaryBufferInterface;
-import com.android.inputmethod.latin.makedict.FormatSpec.FileHeader;
-import com.android.inputmethod.latin.makedict.FusionDictionary.CharGroup;
-import com.android.inputmethod.latin.makedict.FusionDictionary.Node;
-import com.android.inputmethod.latin.makedict.FusionDictionary.WeightedString;
-import com.android.inputmethod.latin.utils.ByteArrayWrapper;
-import com.android.inputmethod.latin.utils.CollectionUtils;
-
-import java.io.File;
-import java.io.FileInputStream;
-import java.io.FileOutputStream;
-import java.io.IOException;
-import java.nio.ByteBuffer;
-import java.nio.channels.FileChannel;
-import java.util.ArrayList;
-import java.util.HashMap;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Map;
-import java.util.Map.Entry;
-import java.util.Random;
-import java.util.Set;
-
-/**
- * Unit tests for BinaryDictInputOutput
- */
-@LargeTest
-public class BinaryDictIOTests extends AndroidTestCase {
-    private static final String TAG = BinaryDictIOTests.class.getSimpleName();
-    private static final int DEFAULT_MAX_UNIGRAMS = 100;
-    private static final int DEFAULT_CODE_POINT_SET_SIZE = 50;
-    private static final int UNIGRAM_FREQ = 10;
-    private static final int BIGRAM_FREQ = 50;
-    private static final int TOLERANCE_OF_BIGRAM_FREQ = 5;
-
-    private static final int USE_BYTE_ARRAY = 1;
-    private static final int USE_BYTE_BUFFER = 2;
-
-    private static final List<String> sWords = CollectionUtils.newArrayList();
-    private static final SparseArray<List<Integer>> sEmptyBigrams =
-            CollectionUtils.newSparseArray();
-    private static final SparseArray<List<Integer>> sStarBigrams = CollectionUtils.newSparseArray();
-    private static final SparseArray<List<Integer>> sChainBigrams =
-            CollectionUtils.newSparseArray();
-
-    private static final FormatSpec.FormatOptions VERSION2 = new FormatSpec.FormatOptions(2);
-    private static final FormatSpec.FormatOptions VERSION3_WITHOUT_DYNAMIC_UPDATE =
-            new FormatSpec.FormatOptions(3, false /* supportsDynamicUpdate */);
-    private static final FormatSpec.FormatOptions VERSION3_WITH_DYNAMIC_UPDATE =
-            new FormatSpec.FormatOptions(3, true /* supportsDynamicUpdate */);
-
-    public BinaryDictIOTests() {
-        this(System.currentTimeMillis(), DEFAULT_MAX_UNIGRAMS);
-    }
-
-    public BinaryDictIOTests(final long seed, final int maxUnigrams) {
-        super();
-        Log.e(TAG, "Testing dictionary: seed is " + seed);
-        final Random random = new Random(seed);
-        sWords.clear();
-        final int[] codePointSet = generateCodePointSet(DEFAULT_CODE_POINT_SET_SIZE, random);
-        generateWords(maxUnigrams, random, codePointSet);
-
-        for (int i = 0; i < sWords.size(); ++i) {
-            sChainBigrams.put(i, new ArrayList<Integer>());
-            if (i > 0) {
-                sChainBigrams.get(i - 1).add(i);
-            }
-        }
-
-        sStarBigrams.put(0, new ArrayList<Integer>());
-        for (int i = 1; i < sWords.size(); ++i) {
-            sStarBigrams.get(0).add(i);
-        }
-    }
-
-    private int[] generateCodePointSet(final int codePointSetSize, final Random random) {
-        final int[] codePointSet = new int[codePointSetSize];
-        for (int i = codePointSet.length - 1; i >= 0; ) {
-            final int r = Math.abs(random.nextInt());
-            if (r < 0) continue;
-            // Don't insert 0~0x20, but insert any other code point.
-            // Code points are in the range 0~0x10FFFF.
-            final int candidateCodePoint = (int)(0x20 + r % (Character.MAX_CODE_POINT - 0x20));
-            // Code points between MIN_ and MAX_SURROGATE are not valid on their own.
-            if (candidateCodePoint >= Character.MIN_SURROGATE
-                    && candidateCodePoint <= Character.MAX_SURROGATE) continue;
-            codePointSet[i] = candidateCodePoint;
-            --i;
-        }
-        return codePointSet;
-    }
-
-    // Utilities for test
-
-    /**
-     * Makes new buffer according to BUFFER_TYPE.
-     */
-    private FusionDictionaryBufferInterface getBuffer(final File file, final int bufferType) {
-        FileInputStream inStream = null;
-        try {
-            inStream = new FileInputStream(file);
-            if (bufferType == USE_BYTE_ARRAY) {
-                final byte[] array = new byte[(int)file.length()];
-                inStream.read(array);
-                return new ByteArrayWrapper(array);
-            } else if (bufferType == USE_BYTE_BUFFER){
-                final ByteBuffer buffer = inStream.getChannel().map(
-                        FileChannel.MapMode.READ_ONLY, 0, file.length());
-                return new BinaryDictInputOutput.ByteBufferWrapper(buffer);
-            }
-        } catch (IOException e) {
-            Log.e(TAG, "IOException while making buffer", e);
-        } finally {
-            if (inStream != null) {
-                try {
-                    inStream.close();
-                } catch (IOException e) {
-                    Log.e(TAG, "IOException while closing stream", e);
-                }
-            }
-        }
-        return null;
-    }
-
-    /**
-     * Generates a random word.
-     */
-    private String generateWord(final Random random, final int[] codePointSet) {
-        StringBuilder builder = new StringBuilder();
-        // 8 * 4 = 32 chars max, but we do it the following way so as to bias the random toward
-        // longer words. This should be closer to natural language, and more importantly, it will
-        // exercise the algorithms in dicttool much more.
-        final int count = 1 + (Math.abs(random.nextInt()) % 5)
-                + (Math.abs(random.nextInt()) % 5)
-                + (Math.abs(random.nextInt()) % 5)
-                + (Math.abs(random.nextInt()) % 5)
-                + (Math.abs(random.nextInt()) % 5)
-                + (Math.abs(random.nextInt()) % 5)
-                + (Math.abs(random.nextInt()) % 5)
-                + (Math.abs(random.nextInt()) % 5);
-        while (builder.length() < count) {
-            builder.appendCodePoint(codePointSet[Math.abs(random.nextInt()) % codePointSet.length]);
-        }
-        return builder.toString();
-    }
-
-    private void generateWords(final int number, final Random random, final int[] codePointSet) {
-        final Set<String> wordSet = CollectionUtils.newHashSet();
-        while (wordSet.size() < number) {
-            wordSet.add(generateWord(random, codePointSet));
-        }
-        sWords.addAll(wordSet);
-    }
-
-    /**
-     * Adds unigrams to the dictionary.
-     */
-    private void addUnigrams(final int number, final FusionDictionary dict,
-            final List<String> words, final Map<String, List<String>> shortcutMap) {
-        for (int i = 0; i < number; ++i) {
-            final String word = words.get(i);
-            final ArrayList<WeightedString> shortcuts = CollectionUtils.newArrayList();
-            if (shortcutMap != null && shortcutMap.containsKey(word)) {
-                for (final String shortcut : shortcutMap.get(word)) {
-                    shortcuts.add(new WeightedString(shortcut, UNIGRAM_FREQ));
-                }
-            }
-            dict.add(word, UNIGRAM_FREQ, (shortcutMap == null) ? null : shortcuts,
-                    false /* isNotAWord */);
-        }
-    }
-
-    private void addBigrams(final FusionDictionary dict,
-            final List<String> words,
-            final SparseArray<List<Integer>> bigrams) {
-        for (int i = 0; i < bigrams.size(); ++i) {
-            final int w1 = bigrams.keyAt(i);
-            for (int w2 : bigrams.valueAt(i)) {
-                dict.setBigram(words.get(w1), words.get(w2), BIGRAM_FREQ);
-            }
-        }
-    }
-
-//    The following is useful to dump the dictionary into a textual file, but it can't compile
-//    on-device, so it's commented out.
-//    private void dumpToCombinedFileForDebug(final FusionDictionary dict, final String filename)
-//            throws IOException {
-//        com.android.inputmethod.latin.dicttool.CombinedInputOutput.writeDictionaryCombined(
-//                new java.io.FileWriter(new File(filename)), dict);
-//    }
-
-    private long timeWritingDictToFile(final File file, final FusionDictionary dict,
-            final FormatSpec.FormatOptions formatOptions) {
-
-        long now = -1, diff = -1;
-
-        try {
-            final FileOutputStream out = new FileOutputStream(file);
-
-            now = System.currentTimeMillis();
-            // If you need to dump the dict to a textual file, uncomment the line below and the
-            // function above
-            // dumpToCombinedFileForDebug(file, "/tmp/foo");
-            BinaryDictInputOutput.writeDictionaryBinary(out, dict, formatOptions);
-            diff = System.currentTimeMillis() - now;
-
-            out.flush();
-            out.close();
-        } catch (IOException e) {
-            Log.e(TAG, "IO exception while writing file", e);
-        } catch (UnsupportedFormatException e) {
-            Log.e(TAG, "UnsupportedFormatException", e);
-        }
-
-        return diff;
-    }
-
-    private void checkDictionary(final FusionDictionary dict, final List<String> words,
-            final SparseArray<List<Integer>> bigrams, final Map<String, List<String>> shortcutMap) {
-        assertNotNull(dict);
-
-        // check unigram
-        for (final String word : words) {
-            final CharGroup cg = FusionDictionary.findWordInTree(dict.mRoot, word);
-            assertNotNull(cg);
-        }
-
-        // check bigram
-        for (int i = 0; i < bigrams.size(); ++i) {
-            final int w1 = bigrams.keyAt(i);
-            for (final int w2 : bigrams.valueAt(i)) {
-                final CharGroup cg = FusionDictionary.findWordInTree(dict.mRoot, words.get(w1));
-                assertNotNull(words.get(w1) + "," + words.get(w2), cg.getBigram(words.get(w2)));
-            }
-        }
-
-        // check shortcut
-        if (shortcutMap != null) {
-            for (final Map.Entry<String, List<String>> entry : shortcutMap.entrySet()) {
-                final CharGroup group = FusionDictionary.findWordInTree(dict.mRoot, entry.getKey());
-                for (final String word : entry.getValue()) {
-                    assertNotNull("shortcut not found: " + entry.getKey() + ", " + word,
-                            group.getShortcut(word));
-                }
-            }
-        }
-    }
-
-    private String outputOptions(final int bufferType,
-            final FormatSpec.FormatOptions formatOptions) {
-        String result = " : buffer type = "
-                + ((bufferType == USE_BYTE_BUFFER) ? "byte buffer" : "byte array");
-        result += " : version = " + formatOptions.mVersion;
-        return result + ", supportsDynamicUpdate = " + formatOptions.mSupportsDynamicUpdate;
-    }
-
-    // Tests for readDictionaryBinary and writeDictionaryBinary
-
-    private long timeReadingAndCheckDict(final File file, final List<String> words,
-            final SparseArray<List<Integer>> bigrams, final Map<String, List<String>> shortcutMap,
-            final int bufferType) {
-        long now, diff = -1;
-        final FusionDictionaryBufferInterface buffer = getBuffer(file, bufferType);
-        assertNotNull(buffer);
-
-        FusionDictionary dict = null;
-        try {
-            now = System.currentTimeMillis();
-            dict = BinaryDictInputOutput.readDictionaryBinary(buffer, null);
-            diff  = System.currentTimeMillis() - now;
-        } catch (IOException e) {
-            Log.e(TAG, "IOException while reading dictionary", e);
-        } catch (UnsupportedFormatException e) {
-            Log.e(TAG, "Unsupported format", e);
-        }
-
-        checkDictionary(dict, words, bigrams, shortcutMap);
-        return diff;
-    }
-
-    // Tests for readDictionaryBinary and writeDictionaryBinary
-    private String runReadAndWrite(final List<String> words,
-            final SparseArray<List<Integer>> bigrams, final Map<String, List<String>> shortcuts,
-            final int bufferType, final FormatSpec.FormatOptions formatOptions,
-            final String message) {
-        File file = null;
-        try {
-            file = File.createTempFile("runReadAndWrite", ".dict", getContext().getCacheDir());
-        } catch (IOException e) {
-            Log.e(TAG, "IOException", e);
-        }
-        assertNotNull(file);
-
-        final FusionDictionary dict = new FusionDictionary(new Node(),
-                new FusionDictionary.DictionaryOptions(new HashMap<String,String>(), false, false));
-        addUnigrams(words.size(), dict, words, shortcuts);
-        addBigrams(dict, words, bigrams);
-        checkDictionary(dict, words, bigrams, shortcuts);
-
-        final long write = timeWritingDictToFile(file, dict, formatOptions);
-        final long read = timeReadingAndCheckDict(file, words, bigrams, shortcuts, bufferType);
-
-        return "PROF: read=" + read + "ms, write=" + write + "ms :" + message
-                + " : " + outputOptions(bufferType, formatOptions);
-    }
-
-    private void runReadAndWriteTests(final List<String> results, final int bufferType,
-            final FormatSpec.FormatOptions formatOptions) {
-        results.add(runReadAndWrite(sWords, sEmptyBigrams, null /* shortcuts */, bufferType,
-                formatOptions, "unigram"));
-        results.add(runReadAndWrite(sWords, sChainBigrams, null /* shortcuts */, bufferType,
-                formatOptions, "chain"));
-        results.add(runReadAndWrite(sWords, sStarBigrams, null /* shortcuts */, bufferType,
-                formatOptions, "star"));
-    }
-
-    public void testReadAndWriteWithByteBuffer() {
-        final List<String> results = CollectionUtils.newArrayList();
-
-        runReadAndWriteTests(results, USE_BYTE_BUFFER, VERSION2);
-        runReadAndWriteTests(results, USE_BYTE_BUFFER, VERSION3_WITHOUT_DYNAMIC_UPDATE);
-        runReadAndWriteTests(results, USE_BYTE_BUFFER, VERSION3_WITH_DYNAMIC_UPDATE);
-
-        for (final String result : results) {
-            Log.d(TAG, result);
-        }
-    }
-
-    public void testReadAndWriteWithByteArray() {
-        final List<String> results = CollectionUtils.newArrayList();
-
-        runReadAndWriteTests(results, USE_BYTE_ARRAY, VERSION2);
-        runReadAndWriteTests(results, USE_BYTE_ARRAY, VERSION3_WITHOUT_DYNAMIC_UPDATE);
-        runReadAndWriteTests(results, USE_BYTE_ARRAY, VERSION3_WITH_DYNAMIC_UPDATE);
-
-        for (final String result : results) {
-            Log.d(TAG, result);
-        }
-    }
-
-    // Tests for readUnigramsAndBigramsBinary
-
-    private void checkWordMap(final List<String> expectedWords,
-            final SparseArray<List<Integer>> expectedBigrams,
-            final Map<Integer, String> resultWords,
-            final Map<Integer, Integer> resultFrequencies,
-            final Map<Integer, ArrayList<PendingAttribute>> resultBigrams) {
-        // check unigrams
-        final Set<String> actualWordsSet = new HashSet<String>(resultWords.values());
-        final Set<String> expectedWordsSet = new HashSet<String>(expectedWords);
-        assertEquals(actualWordsSet, expectedWordsSet);
-
-        for (int freq : resultFrequencies.values()) {
-            assertEquals(freq, UNIGRAM_FREQ);
-        }
-
-        // check bigrams
-        final Map<String, List<String>> expBigrams = new HashMap<String, List<String>>();
-        for (int i = 0; i < expectedBigrams.size(); ++i) {
-            final String word1 = expectedWords.get(expectedBigrams.keyAt(i));
-            for (int w2 : expectedBigrams.valueAt(i)) {
-                if (expBigrams.get(word1) == null) {
-                    expBigrams.put(word1, new ArrayList<String>());
-                }
-                expBigrams.get(word1).add(expectedWords.get(w2));
-            }
-        }
-
-        final Map<String, List<String>> actBigrams = new HashMap<String, List<String>>();
-        for (Entry<Integer, ArrayList<PendingAttribute>> entry : resultBigrams.entrySet()) {
-            final String word1 = resultWords.get(entry.getKey());
-            final int unigramFreq = resultFrequencies.get(entry.getKey());
-            for (PendingAttribute attr : entry.getValue()) {
-                final String word2 = resultWords.get(attr.mAddress);
-                if (actBigrams.get(word1) == null) {
-                    actBigrams.put(word1, new ArrayList<String>());
-                }
-                actBigrams.get(word1).add(word2);
-
-                final int bigramFreq = BinaryDictInputOutput.reconstructBigramFrequency(
-                        unigramFreq, attr.mFrequency);
-                assertTrue(Math.abs(bigramFreq - BIGRAM_FREQ) < TOLERANCE_OF_BIGRAM_FREQ);
-            }
-        }
-
-        assertEquals(actBigrams, expBigrams);
-    }
-
-    private long timeAndCheckReadUnigramsAndBigramsBinary(final File file, final List<String> words,
-            final SparseArray<List<Integer>> bigrams, final int bufferType) {
-        FileInputStream inStream = null;
-
-        final Map<Integer, String> resultWords = CollectionUtils.newTreeMap();
-        final Map<Integer, ArrayList<PendingAttribute>> resultBigrams =
-                CollectionUtils.newTreeMap();
-        final Map<Integer, Integer> resultFreqs = CollectionUtils.newTreeMap();
-
-        long now = -1, diff = -1;
-        final FusionDictionaryBufferInterface buffer = getBuffer(file, bufferType);
-        assertNotNull("Can't get buffer.", buffer);
-        try {
-            now = System.currentTimeMillis();
-            BinaryDictIOUtils.readUnigramsAndBigramsBinary(buffer, resultWords, resultFreqs,
-                    resultBigrams);
-            diff = System.currentTimeMillis() - now;
-        } catch (IOException e) {
-            Log.e(TAG, "IOException", e);
-        } catch (UnsupportedFormatException e) {
-            Log.e(TAG, "UnsupportedFormatException", e);
-        } finally {
-            if (inStream != null) {
-                try {
-                    inStream.close();
-                } catch (IOException e) {
-                    // do nothing
-                }
-            }
-        }
-
-        checkWordMap(words, bigrams, resultWords, resultFreqs, resultBigrams);
-        return diff;
-    }
-
-    private String runReadUnigramsAndBigramsBinary(final List<String> words,
-            final SparseArray<List<Integer>> bigrams, final int bufferType,
-            final FormatSpec.FormatOptions formatOptions, final String message) {
-        File file = null;
-        try {
-            file = File.createTempFile("runReadUnigrams", ".dict", getContext().getCacheDir());
-        } catch (IOException e) {
-            Log.e(TAG, "IOException", e);
-        }
-        assertNotNull(file);
-
-        // making the dictionary from lists of words.
-        final FusionDictionary dict = new FusionDictionary(new Node(),
-                new FusionDictionary.DictionaryOptions(
-                        new HashMap<String, String>(), false, false));
-        addUnigrams(words.size(), dict, words, null /* shortcutMap */);
-        addBigrams(dict, words, bigrams);
-
-        timeWritingDictToFile(file, dict, formatOptions);
-
-        long wordMap = timeAndCheckReadUnigramsAndBigramsBinary(file, words, bigrams, bufferType);
-        long fullReading = timeReadingAndCheckDict(file, words, bigrams, null /* shortcutMap */,
-                bufferType);
-
-        return "readDictionaryBinary=" + fullReading + ", readUnigramsAndBigramsBinary=" + wordMap
-                + " : " + message + " : " + outputOptions(bufferType, formatOptions);
-    }
-
-    private void runReadUnigramsAndBigramsTests(final List<String> results, final int bufferType,
-            final FormatSpec.FormatOptions formatOptions) {
-        results.add(runReadUnigramsAndBigramsBinary(sWords, sEmptyBigrams, bufferType,
-                formatOptions, "unigram"));
-        results.add(runReadUnigramsAndBigramsBinary(sWords, sChainBigrams, bufferType,
-                formatOptions, "chain"));
-        results.add(runReadUnigramsAndBigramsBinary(sWords, sChainBigrams, bufferType,
-                formatOptions, "star"));
-    }
-
-    public void testReadUnigramsAndBigramsBinaryWithByteBuffer() {
-        final List<String> results = CollectionUtils.newArrayList();
-
-        runReadUnigramsAndBigramsTests(results, USE_BYTE_BUFFER, VERSION2);
-        runReadUnigramsAndBigramsTests(results, USE_BYTE_BUFFER, VERSION3_WITHOUT_DYNAMIC_UPDATE);
-        runReadUnigramsAndBigramsTests(results, USE_BYTE_BUFFER, VERSION3_WITH_DYNAMIC_UPDATE);
-
-        for (final String result : results) {
-            Log.d(TAG, result);
-        }
-    }
-
-    public void testReadUnigramsAndBigramsBinaryWithByteArray() {
-        final List<String> results = CollectionUtils.newArrayList();
-
-        runReadUnigramsAndBigramsTests(results, USE_BYTE_ARRAY, VERSION2);
-        runReadUnigramsAndBigramsTests(results, USE_BYTE_ARRAY, VERSION3_WITHOUT_DYNAMIC_UPDATE);
-        runReadUnigramsAndBigramsTests(results, USE_BYTE_ARRAY, VERSION3_WITH_DYNAMIC_UPDATE);
-
-        for (final String result : results) {
-            Log.d(TAG, result);
-        }
-    }
-
-    // Tests for getTerminalPosition
-    private String getWordFromBinary(final FusionDictionaryBufferInterface buffer,
-            final int address) {
-        if (buffer.position() != 0) buffer.position(0);
-
-        FileHeader header = null;
-        try {
-            header = BinaryDictInputOutput.readHeader(buffer);
-        } catch (IOException e) {
-            return null;
-        } catch (UnsupportedFormatException e) {
-            return null;
-        }
-        if (header == null) return null;
-        return BinaryDictInputOutput.getWordAtAddress(buffer, header.mHeaderSize,
-                address - header.mHeaderSize, header.mFormatOptions).mWord;
-    }
-
-    private long runGetTerminalPosition(final FusionDictionaryBufferInterface buffer,
-            final String word, int index, boolean contained) {
-        final int expectedFrequency = (UNIGRAM_FREQ + index) % 255;
-        long diff = -1;
-        int position = -1;
-        try {
-            final long now = System.nanoTime();
-            position = BinaryDictIOUtils.getTerminalPosition(buffer, word);
-            diff = System.nanoTime() - now;
-        } catch (IOException e) {
-            Log.e(TAG, "IOException while getTerminalPosition", e);
-        } catch (UnsupportedFormatException e) {
-            Log.e(TAG, "UnsupportedFormatException while getTerminalPosition", e);
-        }
-
-        assertEquals(FormatSpec.NOT_VALID_WORD != position, contained);
-        if (contained) assertEquals(getWordFromBinary(buffer, position), word);
-        return diff;
-    }
-
-    public void testGetTerminalPosition() {
-        File file = null;
-        try {
-            file = File.createTempFile("testGetTerminalPosition", ".dict",
-                    getContext().getCacheDir());
-        } catch (IOException e) {
-            // do nothing
-        }
-        assertNotNull(file);
-
-        final FusionDictionary dict = new FusionDictionary(new Node(),
-                new FusionDictionary.DictionaryOptions(
-                        new HashMap<String, String>(), false, false));
-        addUnigrams(sWords.size(), dict, sWords, null /* shortcutMap */);
-        timeWritingDictToFile(file, dict, VERSION3_WITH_DYNAMIC_UPDATE);
-
-        final FusionDictionaryBufferInterface buffer = getBuffer(file, USE_BYTE_ARRAY);
-
-        try {
-            // too long word
-            final String longWord = "abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyz";
-            assertEquals(FormatSpec.NOT_VALID_WORD,
-                    BinaryDictIOUtils.getTerminalPosition(buffer, longWord));
-
-            // null
-            assertEquals(FormatSpec.NOT_VALID_WORD,
-                    BinaryDictIOUtils.getTerminalPosition(buffer, null));
-
-            // empty string
-            assertEquals(FormatSpec.NOT_VALID_WORD,
-                    BinaryDictIOUtils.getTerminalPosition(buffer, ""));
-        } catch (IOException e) {
-        } catch (UnsupportedFormatException e) {
-        }
-
-        // Test a word that is contained within the dictionary.
-        long sum = 0;
-        for (int i = 0; i < sWords.size(); ++i) {
-            final long time = runGetTerminalPosition(buffer, sWords.get(i), i, true);
-            sum += time == -1 ? 0 : time;
-        }
-        Log.d(TAG, "per a search : " + (((double)sum) / sWords.size() / 1000000));
-
-        // Test a word that isn't contained within the dictionary.
-        final Random random = new Random((int)System.currentTimeMillis());
-        final int[] codePointSet = generateCodePointSet(DEFAULT_CODE_POINT_SET_SIZE, random);
-        for (int i = 0; i < 1000; ++i) {
-            final String word = generateWord(random, codePointSet);
-            if (sWords.indexOf(word) != -1) continue;
-            runGetTerminalPosition(buffer, word, i, false);
-        }
-    }
-
-    public void testDeleteWord() {
-        File file = null;
-        try {
-            file = File.createTempFile("testDeleteWord", ".dict", getContext().getCacheDir());
-        } catch (IOException e) {
-            // do nothing
-        }
-        assertNotNull(file);
-
-        final FusionDictionary dict = new FusionDictionary(new Node(),
-                new FusionDictionary.DictionaryOptions(
-                        new HashMap<String, String>(), false, false));
-        addUnigrams(sWords.size(), dict, sWords, null /* shortcutMap */);
-        timeWritingDictToFile(file, dict, VERSION3_WITH_DYNAMIC_UPDATE);
-
-        final FusionDictionaryBufferInterface buffer = getBuffer(file, USE_BYTE_ARRAY);
-
-        try {
-            MoreAsserts.assertNotEqual(FormatSpec.NOT_VALID_WORD,
-                    BinaryDictIOUtils.getTerminalPosition(buffer, sWords.get(0)));
-            BinaryDictIOUtils.deleteWord(buffer, sWords.get(0));
-            assertEquals(FormatSpec.NOT_VALID_WORD,
-                    BinaryDictIOUtils.getTerminalPosition(buffer, sWords.get(0)));
-
-            MoreAsserts.assertNotEqual(FormatSpec.NOT_VALID_WORD,
-                    BinaryDictIOUtils.getTerminalPosition(buffer, sWords.get(5)));
-            BinaryDictIOUtils.deleteWord(buffer, sWords.get(5));
-            assertEquals(FormatSpec.NOT_VALID_WORD,
-                    BinaryDictIOUtils.getTerminalPosition(buffer, sWords.get(5)));
-        } catch (IOException e) {
-        } catch (UnsupportedFormatException e) {
-        }
-    }
-}
diff --git a/tests/src/com/android/inputmethod/latin/makedict/BinaryDictIOUtilsTests.java b/tests/src/com/android/inputmethod/latin/makedict/BinaryDictIOUtilsTests.java
index 9331da4..a837494 100644
--- a/tests/src/com/android/inputmethod/latin/makedict/BinaryDictIOUtilsTests.java
+++ b/tests/src/com/android/inputmethod/latin/makedict/BinaryDictIOUtilsTests.java
@@ -21,20 +21,16 @@
 import android.test.suitebuilder.annotation.LargeTest;
 import android.util.Log;
 
-import com.android.inputmethod.latin.makedict.BinaryDictInputOutput.ByteBufferWrapper;
-import com.android.inputmethod.latin.makedict.BinaryDictInputOutput.FusionDictionaryBufferInterface;
+import com.android.inputmethod.latin.makedict.BinaryDictDecoderUtils.DictBuffer;
 import com.android.inputmethod.latin.makedict.FormatSpec.FileHeader;
-import com.android.inputmethod.latin.makedict.FusionDictionary.Node;
+import com.android.inputmethod.latin.makedict.FusionDictionary.PtNodeArray;
 import com.android.inputmethod.latin.makedict.FusionDictionary.WeightedString;
 import com.android.inputmethod.latin.utils.CollectionUtils;
 
 import java.io.BufferedOutputStream;
 import java.io.File;
-import java.io.FileInputStream;
 import java.io.FileOutputStream;
 import java.io.IOException;
-import java.io.RandomAccessFile;
-import java.nio.channels.FileChannel;
 import java.util.ArrayList;
 import java.util.HashMap;
 import java.util.Random;
@@ -49,6 +45,8 @@
     public static final int DEFAULT_MAX_UNIGRAMS = 1500;
     private final int mMaxUnigrams;
 
+    private static final String TEST_DICT_FILE_EXTENSION = ".testDict";
+
     private static final String[] CHARACTERS = {
         "a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l", "m",
         "n", "o", "p", "q", "r", "s", "t", "u", "v", "w", "x", "y", "z",
@@ -86,8 +84,8 @@
         return builder.toString();
     }
 
-    private static void printCharGroup(final CharGroupInfo info) {
-        Log.d(TAG, "    CharGroup at " + info.mOriginalAddress);
+    private static void printPtNode(final PtNodeInfo info) {
+        Log.d(TAG, "    PtNode at " + info.mOriginalAddress);
         Log.d(TAG, "        flags = " + info.mFlags);
         Log.d(TAG, "        parentAddress = " + info.mParentAddress);
         Log.d(TAG, "        characters = " + new String(info.mCharacters, 0,
@@ -111,70 +109,75 @@
         Log.d(TAG, "    end address = " + info.mEndAddress);
     }
 
-    private static void printNode(final FusionDictionaryBufferInterface buffer,
+    private static void printNode(final Ver3DictDecoder dictDecoder,
             final FormatSpec.FormatOptions formatOptions) {
-        Log.d(TAG, "Node at " + buffer.position());
-        final int count = BinaryDictInputOutput.readCharGroupCount(buffer);
-        Log.d(TAG, "    charGroupCount = " + count);
+        final DictBuffer dictBuffer = dictDecoder.getDictBuffer();
+        Log.d(TAG, "Node at " + dictBuffer.position());
+        final int count = BinaryDictDecoderUtils.readPtNodeCount(dictBuffer);
+        Log.d(TAG, "    ptNodeCount = " + count);
         for (int i = 0; i < count; ++i) {
-            final CharGroupInfo currentInfo = BinaryDictInputOutput.readCharGroup(buffer,
-                    buffer.position(), formatOptions);
-            printCharGroup(currentInfo);
+            final PtNodeInfo currentInfo = dictDecoder.readPtNode(dictBuffer.position(),
+                    formatOptions);
+            printPtNode(currentInfo);
         }
         if (formatOptions.mSupportsDynamicUpdate) {
-            final int forwardLinkAddress = buffer.readUnsignedInt24();
+            final int forwardLinkAddress = dictBuffer.readUnsignedInt24();
             Log.d(TAG, "    forwardLinkAddress = " + forwardLinkAddress);
         }
     }
 
-    private static void printBinaryFile(final FusionDictionaryBufferInterface buffer)
+    @SuppressWarnings("unused")
+    private static void printBinaryFile(final Ver3DictDecoder dictDecoder)
             throws IOException, UnsupportedFormatException {
-        FileHeader header = BinaryDictInputOutput.readHeader(buffer);
-        while (buffer.position() < buffer.limit()) {
-            printNode(buffer, header.mFormatOptions);
+        final FileHeader fileHeader = dictDecoder.readHeader();
+        final DictBuffer dictBuffer = dictDecoder.getDictBuffer();
+        while (dictBuffer.position() < dictBuffer.limit()) {
+            printNode(dictDecoder, fileHeader.mFormatOptions);
         }
     }
 
     private int getWordPosition(final File file, final String word) {
         int position = FormatSpec.NOT_VALID_WORD;
-        FileInputStream inStream = null;
+
         try {
-            inStream = new FileInputStream(file);
-            final FusionDictionaryBufferInterface buffer = new ByteBufferWrapper(
-                    inStream.getChannel().map(FileChannel.MapMode.READ_ONLY, 0, file.length()));
-            position = BinaryDictIOUtils.getTerminalPosition(buffer, word);
+            final Ver3DictDecoder dictDecoder = new Ver3DictDecoder(file,
+                    DictDecoder.USE_READONLY_BYTEBUFFER);
+            position = dictDecoder.getTerminalPosition(word);
         } catch (IOException e) {
         } catch (UnsupportedFormatException e) {
-        } finally {
-            if (inStream != null) {
-                try {
-                    inStream.close();
-                } catch (IOException e) {
-                    // do nothing
-                }
-            }
         }
         return position;
     }
 
-    private CharGroupInfo findWordFromFile(final File file, final String word) {
-        FileInputStream inStream = null;
-        CharGroupInfo info = null;
+    /**
+     * Find a word using the DictDecoder.
+     *
+     * @param dictDecoder the dict decoder
+     * @param word the word searched
+     * @return the found ptNodeInfo
+     * @throws IOException
+     * @throws UnsupportedFormatException
+     */
+    private static PtNodeInfo findWordByBinaryDictReader(final DictDecoder dictDecoder,
+            final String word) throws IOException, UnsupportedFormatException {
+        int position = dictDecoder.getTerminalPosition(word);
+        if (position != FormatSpec.NOT_VALID_WORD) {
+            dictDecoder.setPosition(0);
+            final FileHeader header = dictDecoder.readHeader();
+            dictDecoder.setPosition(position);
+            return dictDecoder.readPtNode(position, header.mFormatOptions);
+        }
+        return null;
+    }
+
+    private PtNodeInfo findWordFromFile(final File file, final String word) {
+        final DictDecoder dictDecoder = FormatSpec.getDictDecoder(file);
+        PtNodeInfo info = null;
         try {
-            inStream = new FileInputStream(file);
-            final FusionDictionaryBufferInterface buffer = new ByteBufferWrapper(
-                    inStream.getChannel().map(FileChannel.MapMode.READ_ONLY, 0, file.length()));
-            info = BinaryDictIOUtils.findWordFromBuffer(buffer, word);
+            dictDecoder.openDictBuffer();
+            info = findWordByBinaryDictReader(dictDecoder, word);
         } catch (IOException e) {
         } catch (UnsupportedFormatException e) {
-        } finally {
-            if (inStream != null) {
-                try {
-                    inStream.close();
-                } catch (IOException e) {
-                    // do nothing
-                }
-            }
         }
         return info;
     }
@@ -183,42 +186,34 @@
     private long insertAndCheckWord(final File file, final String word, final int frequency,
             final boolean exist, final ArrayList<WeightedString> bigrams,
             final ArrayList<WeightedString> shortcuts) {
-        RandomAccessFile raFile = null;
         BufferedOutputStream outStream = null;
-        FusionDictionaryBufferInterface buffer = null;
         long amountOfTime = -1;
         try {
-            raFile = new RandomAccessFile(file, "rw");
-            buffer = new ByteBufferWrapper(raFile.getChannel().map(
-                    FileChannel.MapMode.READ_WRITE, 0, file.length()));
+            final Ver3DictDecoder dictDecoder = new Ver3DictDecoder(file,
+                    DictDecoder.USE_WRITABLE_BYTEBUFFER);
+            dictDecoder.openDictBuffer();
             outStream = new BufferedOutputStream(new FileOutputStream(file, true));
 
             if (!exist) {
                 assertEquals(FormatSpec.NOT_VALID_WORD, getWordPosition(file, word));
             }
             final long now = System.nanoTime();
-            BinaryDictIOUtils.insertWord(buffer, outStream, word, frequency, bigrams, shortcuts,
-                    false, false);
+            DynamicBinaryDictIOUtils.insertWord(dictDecoder, outStream, word, frequency, bigrams,
+                    shortcuts, false, false);
             amountOfTime = System.nanoTime() - now;
             outStream.flush();
             MoreAsserts.assertNotEqual(FormatSpec.NOT_VALID_WORD, getWordPosition(file, word));
             outStream.close();
-            raFile.close();
         } catch (IOException e) {
+            Log.e(TAG, "Raised an IOException while inserting a word", e);
         } catch (UnsupportedFormatException e) {
+            Log.e(TAG, "Raised an UnsupportedFormatException error while inserting a word", e);
         } finally {
             if (outStream != null) {
                 try {
                     outStream.close();
                 } catch (IOException e) {
-                    // do nothing
-                }
-            }
-            if (raFile != null) {
-                try {
-                    raFile.close();
-                } catch (IOException e) {
-                    // do nothing
+                    Log.e(TAG, "Failed to close the output stream", e);
                 }
             }
         }
@@ -226,65 +221,48 @@
     }
 
     private void deleteWord(final File file, final String word) {
-        RandomAccessFile raFile = null;
-        FusionDictionaryBufferInterface buffer = null;
         try {
-            raFile = new RandomAccessFile(file, "rw");
-            buffer = new ByteBufferWrapper(raFile.getChannel().map(
-                    FileChannel.MapMode.READ_WRITE, 0, file.length()));
-            BinaryDictIOUtils.deleteWord(buffer, word);
+            final Ver3DictDecoder dictDecoder = new Ver3DictDecoder(file,
+                    DictDecoder.USE_WRITABLE_BYTEBUFFER);
+            dictDecoder.openDictBuffer();
+            DynamicBinaryDictIOUtils.deleteWord(dictDecoder, word);
         } catch (IOException e) {
         } catch (UnsupportedFormatException e) {
-        } finally {
-            if (raFile != null) {
-                try {
-                    raFile.close();
-                } catch (IOException e) {
-                    // do nothing
-                }
-            }
         }
     }
 
     private void checkReverseLookup(final File file, final String word, final int position) {
-        FileInputStream inStream = null;
+
         try {
-            inStream = new FileInputStream(file);
-            final FusionDictionaryBufferInterface buffer = new ByteBufferWrapper(
-                    inStream.getChannel().map(FileChannel.MapMode.READ_ONLY, 0, file.length()));
-            final FileHeader header = BinaryDictInputOutput.readHeader(buffer);
-            assertEquals(word, BinaryDictInputOutput.getWordAtAddress(buffer, header.mHeaderSize,
-                    position - header.mHeaderSize, header.mFormatOptions).mWord);
+            final DictDecoder dictDecoder = FormatSpec.getDictDecoder(file);
+            final FileHeader fileHeader = dictDecoder.readHeader();
+            assertEquals(word,
+                    BinaryDictDecoderUtils.getWordAtPosition(dictDecoder, fileHeader.mHeaderSize,
+                            position, fileHeader.mFormatOptions).mWord);
         } catch (IOException e) {
+            Log.e(TAG, "Raised an IOException while looking up a word", e);
         } catch (UnsupportedFormatException e) {
-        } finally {
-            if (inStream != null) {
-                try {
-                    inStream.close();
-                } catch (IOException e) {
-                    // do nothing
-                }
-            }
+            Log.e(TAG, "Raised an UnsupportedFormatException error while looking up a word", e);
         }
     }
 
     public void testInsertWord() {
         File file = null;
         try {
-            file = File.createTempFile("testInsertWord", ".dict", getContext().getCacheDir());
+            file = File.createTempFile("testInsertWord", TEST_DICT_FILE_EXTENSION,
+                    getContext().getCacheDir());
         } catch (IOException e) {
             fail("IOException while creating temporary file: " + e);
         }
 
         // set an initial dictionary.
-        final FusionDictionary dict = new FusionDictionary(new Node(),
+        final FusionDictionary dict = new FusionDictionary(new PtNodeArray(),
                 new FusionDictionary.DictionaryOptions(new HashMap<String,String>(), false, false));
         dict.add("abcd", 10, null, false);
 
         try {
-            final FileOutputStream out = new FileOutputStream(file);
-            BinaryDictInputOutput.writeDictionaryBinary(out, dict, FORMAT_OPTIONS);
-            out.close();
+            final DictEncoder dictEncoder = new Ver3DictEncoder(file);
+            dictEncoder.writeDictionary(dict, FORMAT_OPTIONS);
         } catch (IOException e) {
             fail("IOException while writing an initial dictionary : " + e);
         } catch (UnsupportedFormatException e) {
@@ -321,22 +299,21 @@
     public void testInsertWordWithBigrams() {
         File file = null;
         try {
-            file = File.createTempFile("testInsertWordWithBigrams", ".dict",
+            file = File.createTempFile("testInsertWordWithBigrams", TEST_DICT_FILE_EXTENSION,
                     getContext().getCacheDir());
         } catch (IOException e) {
             fail("IOException while creating temporary file: " + e);
         }
 
         // set an initial dictionary.
-        final FusionDictionary dict = new FusionDictionary(new Node(),
+        final FusionDictionary dict = new FusionDictionary(new PtNodeArray(),
                 new FusionDictionary.DictionaryOptions(new HashMap<String,String>(), false, false));
         dict.add("abcd", 10, null, false);
         dict.add("efgh", 15, null, false);
 
         try {
-            final FileOutputStream out = new FileOutputStream(file);
-            BinaryDictInputOutput.writeDictionaryBinary(out, dict, FORMAT_OPTIONS);
-            out.close();
+            final DictEncoder dictEncoder = new Ver3DictEncoder(file); 
+            dictEncoder.writeDictionary(dict, FORMAT_OPTIONS);
         } catch (IOException e) {
             fail("IOException while writing an initial dictionary : " + e);
         } catch (UnsupportedFormatException e) {
@@ -349,7 +326,7 @@
         insertAndCheckWord(file, "banana", 0, false, null, null);
         insertAndCheckWord(file, "recursive", 60, true, banana, null);
 
-        final CharGroupInfo info = findWordFromFile(file, "recursive");
+        final PtNodeInfo info = findWordFromFile(file, "recursive");
         int bananaPos = getWordPosition(file, "banana");
         assertNotNull(info.mBigrams);
         assertEquals(info.mBigrams.size(), 1);
@@ -359,21 +336,21 @@
     public void testRandomWords() {
         File file = null;
         try {
-            file = File.createTempFile("testRandomWord", ".dict", getContext().getCacheDir());
+            file = File.createTempFile("testRandomWord", TEST_DICT_FILE_EXTENSION,
+                    getContext().getCacheDir());
         } catch (IOException e) {
         }
         assertNotNull(file);
 
         // set an initial dictionary.
-        final FusionDictionary dict = new FusionDictionary(new Node(),
+        final FusionDictionary dict = new FusionDictionary(new PtNodeArray(),
                 new FusionDictionary.DictionaryOptions(new HashMap<String, String>(), false,
                         false));
         dict.add("initial", 10, null, false);
 
         try {
-            final FileOutputStream out = new FileOutputStream(file);
-            BinaryDictInputOutput.writeDictionaryBinary(out, dict, FORMAT_OPTIONS);
-            out.close();
+            final DictEncoder dictEncoder = new Ver3DictEncoder(file);
+            dictEncoder.writeDictionary(dict, FORMAT_OPTIONS);
         } catch (IOException e) {
             assertTrue(false);
         } catch (UnsupportedFormatException e) {
diff --git a/tests/src/com/android/inputmethod/latin/makedict/CodePointUtils.java b/tests/src/com/android/inputmethod/latin/makedict/CodePointUtils.java
new file mode 100644
index 0000000..36b958a
--- /dev/null
+++ b/tests/src/com/android/inputmethod/latin/makedict/CodePointUtils.java
@@ -0,0 +1,65 @@
+/*
+ * Copyright (C) 2013 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.latin.makedict;
+
+import java.util.Random;
+
+// Utility methods related with code points used for tests.
+public class CodePointUtils {
+    private CodePointUtils() {
+        // This utility class is not publicly instantiable.
+    }
+
+    public static int[] generateCodePointSet(final int codePointSetSize, final Random random) {
+        final int[] codePointSet = new int[codePointSetSize];
+        for (int i = codePointSet.length - 1; i >= 0; ) {
+            final int r = Math.abs(random.nextInt());
+            if (r < 0) continue;
+            // Don't insert 0~0x20, but insert any other code point.
+            // Code points are in the range 0~0x10FFFF.
+            final int candidateCodePoint = 0x20 + r % (Character.MAX_CODE_POINT - 0x20);
+            // Code points between MIN_ and MAX_SURROGATE are not valid on their own.
+            if (candidateCodePoint >= Character.MIN_SURROGATE
+                    && candidateCodePoint <= Character.MAX_SURROGATE) continue;
+            codePointSet[i] = candidateCodePoint;
+            --i;
+        }
+        return codePointSet;
+    }
+
+    /**
+     * Generates a random word.
+     */
+    public static String generateWord(final Random random, final int[] codePointSet) {
+        StringBuilder builder = new StringBuilder();
+        // 8 * 4 = 32 chars max, but we do it the following way so as to bias the random toward
+        // longer words. This should be closer to natural language, and more importantly, it will
+        // exercise the algorithms in dicttool much more.
+        final int count = 1 + (Math.abs(random.nextInt()) % 5)
+                + (Math.abs(random.nextInt()) % 5)
+                + (Math.abs(random.nextInt()) % 5)
+                + (Math.abs(random.nextInt()) % 5)
+                + (Math.abs(random.nextInt()) % 5)
+                + (Math.abs(random.nextInt()) % 5)
+                + (Math.abs(random.nextInt()) % 5)
+                + (Math.abs(random.nextInt()) % 5);
+        while (builder.length() < count) {
+            builder.appendCodePoint(codePointSet[Math.abs(random.nextInt()) % codePointSet.length]);
+        }
+        return builder.toString();
+    }
+}
diff --git a/tests/src/com/android/inputmethod/latin/makedict/Ver3DictDecoderTests.java b/tests/src/com/android/inputmethod/latin/makedict/Ver3DictDecoderTests.java
new file mode 100644
index 0000000..9611599
--- /dev/null
+++ b/tests/src/com/android/inputmethod/latin/makedict/Ver3DictDecoderTests.java
@@ -0,0 +1,150 @@
+/*
+ * Copyright (C) 2013 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.latin.makedict;
+
+import com.android.inputmethod.latin.makedict.BinaryDictDecoderUtils.DictBuffer;
+import com.android.inputmethod.latin.makedict.DictDecoder.DictionaryBufferFactory;
+import com.android.inputmethod.latin.makedict.DictDecoder.DictionaryBufferFromByteArrayFactory;
+import com.android.inputmethod.latin.makedict.DictDecoder.
+        DictionaryBufferFromReadOnlyByteBufferFactory;
+import com.android.inputmethod.latin.makedict.DictDecoder.
+        DictionaryBufferFromWritableByteBufferFactory;
+
+import android.test.AndroidTestCase;
+import android.util.Log;
+
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+
+/**
+ * Unit tests for Ver3DictDecoder
+ */
+public class Ver3DictDecoderTests extends AndroidTestCase {
+    private static final String TAG = Ver3DictDecoderTests.class.getSimpleName();
+
+    private final byte[] data = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
+
+    // Utilities for testing
+    public void writeDataToFile(final File file) {
+        FileOutputStream outStream = null;
+        try {
+            outStream = new FileOutputStream(file);
+            outStream.write(data);
+        } catch (IOException e) {
+            fail ("Can't write data to the test file");
+        } finally {
+            if (outStream != null) {
+                try {
+                    outStream.close();
+                } catch (IOException e) {
+                    Log.e(TAG, "Failed to close the output stream", e);
+                }
+            }
+        }
+    }
+
+    @SuppressWarnings("null")
+    public void runTestOpenBuffer(final String testName, final DictionaryBufferFactory factory) {
+        File testFile = null;
+        try {
+            testFile = File.createTempFile(testName, ".tmp", getContext().getCacheDir());
+        } catch (IOException e) {
+            Log.e(TAG, "IOException while the creating temporary file", e);
+        }
+
+        assertNotNull(testFile);
+        final Ver3DictDecoder dictDecoder = new Ver3DictDecoder(testFile, factory);
+        try {
+            dictDecoder.openDictBuffer();
+        } catch (Exception e) {
+            Log.e(TAG, "Failed to open the buffer", e);
+        }
+
+        writeDataToFile(testFile);
+
+        try {
+            dictDecoder.openDictBuffer();
+        } catch (Exception e) {
+            Log.e(TAG, "Raised the exception while opening buffer", e);
+        }
+
+        assertEquals(testFile.length(), dictDecoder.getDictBuffer().capacity());
+    }
+
+    public void testOpenBufferWithByteBuffer() {
+        runTestOpenBuffer("testOpenBufferWithByteBuffer",
+                new DictionaryBufferFromReadOnlyByteBufferFactory());
+    }
+
+    public void testOpenBufferWithByteArray() {
+        runTestOpenBuffer("testOpenBufferWithByteArray",
+                new DictionaryBufferFromByteArrayFactory());
+    }
+
+    public void testOpenBufferWithWritableByteBuffer() {
+        runTestOpenBuffer("testOpenBufferWithWritableByteBuffer",
+                new DictionaryBufferFromWritableByteBufferFactory());
+    }
+
+    @SuppressWarnings("null")
+    public void runTestGetBuffer(final String testName, final DictionaryBufferFactory factory) {
+        File testFile = null;
+        try {
+            testFile = File.createTempFile(testName, ".tmp", getContext().getCacheDir());
+        } catch (IOException e) {
+            Log.e(TAG, "IOException while the creating temporary file", e);
+        }
+
+        final Ver3DictDecoder dictDecoder = new Ver3DictDecoder(testFile, factory);
+
+        // the default return value of getBuffer() must be null.
+        assertNull("the default return value of getBuffer() is not null",
+                dictDecoder.getDictBuffer());
+
+        writeDataToFile(testFile);
+        assertTrue(testFile.exists());
+        Log.d(TAG, "file length = " + testFile.length());
+
+        DictBuffer dictBuffer = null;
+        try {
+            dictBuffer = dictDecoder.openAndGetDictBuffer();
+        } catch (IOException e) {
+            Log.e(TAG, "Failed to open and get the buffer", e);
+        }
+        assertNotNull("the buffer must not be null", dictBuffer);
+
+        for (int i = 0; i < data.length; ++i) {
+            assertEquals(data[i], dictBuffer.readUnsignedByte());
+        }
+    }
+
+    public void testGetBufferWithByteBuffer() {
+        runTestGetBuffer("testGetBufferWithByteBuffer",
+                new DictionaryBufferFromReadOnlyByteBufferFactory());
+    }
+
+    public void testGetBufferWithByteArray() {
+        runTestGetBuffer("testGetBufferWithByteArray",
+                new DictionaryBufferFromByteArrayFactory());
+    }
+
+    public void testGetBufferWithWritableByteBuffer() {
+        runTestGetBuffer("testGetBufferWithWritableByteBuffer",
+                new DictionaryBufferFromWritableByteBufferFactory());
+    }
+}
diff --git a/tests/src/com/android/inputmethod/latin/personalization/UserHistoryDictionaryTests.java b/tests/src/com/android/inputmethod/latin/personalization/UserHistoryDictionaryTests.java
index b3e2ee0..d605cdb 100644
--- a/tests/src/com/android/inputmethod/latin/personalization/UserHistoryDictionaryTests.java
+++ b/tests/src/com/android/inputmethod/latin/personalization/UserHistoryDictionaryTests.java
@@ -22,6 +22,7 @@
 import android.test.suitebuilder.annotation.LargeTest;
 import android.util.Log;
 
+import com.android.inputmethod.latin.ExpandableBinaryDictionary;
 import com.android.inputmethod.latin.utils.CollectionUtils;
 
 import java.io.File;
@@ -29,6 +30,7 @@
 import java.util.List;
 import java.util.Random;
 import java.util.Set;
+import java.util.concurrent.TimeUnit;
 
 /**
  * Unit tests for UserHistoryDictionary
@@ -43,6 +45,9 @@
         "n", "o", "p", "q", "r", "s", "t", "u", "v", "w", "x", "y", "z"
     };
 
+    private static final int MIN_USER_HISTORY_DICTIONARY_FILE_SIZE = 1000;
+    private static final int WAIT_TERMINATING_IN_MILLISECONDS = 100;
+
     @Override
     public void setUp() {
         mPrefs = PreferenceManager.getDefaultSharedPreferences(getContext());
@@ -73,48 +78,74 @@
     private void addToDict(final UserHistoryPredictionDictionary dict, final List<String> words) {
         String prevWord = null;
         for (String word : words) {
-            dict.forceAddWordForTest(prevWord, word, true);
+            dict.addToPersonalizationPredictionDictionary(prevWord, word, true);
             prevWord = word;
         }
     }
 
+    /**
+     * @param checksContents if true, checks whether written words are actually in the dictionary
+     * or not.
+     */
+    private void addAndWriteRandomWords(final String testFilenameSuffix, final int numberOfWords,
+            final Random random, final boolean checksContents) {
+        final List<String> words = generateWords(numberOfWords, random);
+        final UserHistoryPredictionDictionary dict =
+                PersonalizationHelper.getUserHistoryPredictionDictionary(getContext(),
+                        testFilenameSuffix /* locale */, mPrefs);
+        // Add random words to the user history dictionary.
+        addToDict(dict, words);
+        if (checksContents) {
+            try {
+                Thread.sleep(TimeUnit.MILLISECONDS.convert(5L, TimeUnit.SECONDS));
+            } catch (InterruptedException e) {
+            }
+            // Limit word count to check when using a Java on memory dictionary.
+            final int wordCountToCheck =
+                    ExpandableBinaryDictionary.ENABLE_BINARY_DICTIONARY_DYNAMIC_UPDATE ?
+                            numberOfWords : 10;
+            for (int i = 0; i < wordCountToCheck; ++i) {
+                final String word = words.get(i);
+                // This may fail as long as we use tryLock on inserting the bigram words
+                assertTrue(dict.isInDictionaryForTests(word));
+            }
+        }
+        // write to file.
+        dict.close();
+    }
+
     public void testRandomWords() {
         File dictFile = null;
+        Log.d(TAG, "This test can be used for profiling.");
+        Log.d(TAG, "Usage: please set UserHistoryDictionary.PROFILE_SAVE_RESTORE to true.");
+        final String testFilenameSuffix = "testRandomWords" + System.currentTimeMillis();
+        final int numberOfWords = 1000;
+        final Random random = new Random(123456);
+
         try {
-            Log.d(TAG, "This test can be used for profiling.");
-            Log.d(TAG, "Usage: please set UserHistoryDictionary.PROFILE_SAVE_RESTORE to true.");
-            final int numberOfWords = 1000;
-            final Random random = new Random(123456);
-            List<String> words = generateWords(numberOfWords, random);
-
-            final String locale = "testRandomWords";
-            final String fileName = "UserHistoryDictionary." + locale + ".dict";
-            dictFile = new File(getContext().getFilesDir(), fileName);
-            final UserHistoryPredictionDictionary dict =
-                    PersonalizationDictionaryHelper.getUserHistoryPredictionDictionary(
-                            getContext(), locale, mPrefs);
-            dict.mIsTest = true;
-
-            addToDict(dict, words);
-
-            try {
-                Log.d(TAG, "waiting for adding the word ...");
-                Thread.sleep(2000);
-            } catch (InterruptedException e) {
-                Log.d(TAG, "InterruptedException: " + e);
-            }
-
-            // write to file
-            dict.close();
-
-            try {
-                Log.d(TAG, "waiting for writing ...");
-                Thread.sleep(5000);
-            } catch (InterruptedException e) {
-                Log.d(TAG, "InterruptedException: " + e);
-            }
+            addAndWriteRandomWords(testFilenameSuffix, numberOfWords, random,
+                    true /* checksContents */);
         } finally {
+            try {
+                final UserHistoryPredictionDictionary dict =
+                        PersonalizationHelper.getUserHistoryPredictionDictionary(getContext(),
+                                testFilenameSuffix, mPrefs);
+                Log.d(TAG, "waiting for writing ...");
+                dict.shutdownExecutorForTests();
+                while (!dict.isTerminatedForTests()) {
+                    Thread.sleep(WAIT_TERMINATING_IN_MILLISECONDS);
+                }
+            } catch (InterruptedException e) {
+                Log.d(TAG, "InterruptedException: " + e);
+            }
+
+            final String fileName = UserHistoryPredictionDictionary.NAME + "." + testFilenameSuffix
+                    + ExpandableBinaryDictionary.DICT_FILE_EXTENSION;
+            dictFile = new File(getContext().getFilesDir(), fileName);
+
             if (dictFile != null) {
+                assertTrue(dictFile.exists());
+                assertTrue(dictFile.length() >= MIN_USER_HISTORY_DICTIONARY_FILE_SIZE);
                 dictFile.delete();
             }
         }
@@ -122,52 +153,94 @@
 
     public void testStressTestForSwitchingLanguagesAndAddingWords() {
         final int numberOfLanguages = 2;
-        final int numberOfLanguageSwitching = 100;
-        final int numberOfWordsIntertedForEachLanguageSwitch = 100;
+        final int numberOfLanguageSwitching = 80;
+        final int numberOfWordsInsertedForEachLanguageSwitch = 100;
 
         final File dictFiles[] = new File[numberOfLanguages];
+        final String testFilenameSuffixes[] = new String[numberOfLanguages];
         try {
             final Random random = new Random(123456);
 
-            // Create locales for this test.
-            String locales[] = new String[numberOfLanguages];
+            // Create filename suffixes for this test.
             for (int i = 0; i < numberOfLanguages; i++) {
-                locales[i] = "testSwitchingLanguages" + i;
-                final String fileName = "UserHistoryDictionary." + locales[i] + ".dict";
+                testFilenameSuffixes[i] = "testSwitchingLanguages" + i;
+                final String fileName = UserHistoryPredictionDictionary.NAME + "." +
+                        testFilenameSuffixes[i] + ExpandableBinaryDictionary.DICT_FILE_EXTENSION;
                 dictFiles[i] = new File(getContext().getFilesDir(), fileName);
             }
 
-            final long now = System.currentTimeMillis();
+            final long start = System.currentTimeMillis();
 
             for (int i = 0; i < numberOfLanguageSwitching; i++) {
                 final int index = i % numberOfLanguages;
-                // Switch languages to locales[index].
-                final UserHistoryPredictionDictionary dict =
-                        PersonalizationDictionaryHelper.getUserHistoryPredictionDictionary(
-                                getContext(), locales[index], mPrefs);
-                final List<String> words = generateWords(
-                        numberOfWordsIntertedForEachLanguageSwitch, random);
-                // Add random words to the user history dictionary.
-                addToDict(dict, words);
-                // write to file
-                dict.close();
+                // Switch languages to testFilenameSuffixes[index].
+                addAndWriteRandomWords(testFilenameSuffixes[index],
+                        numberOfWordsInsertedForEachLanguageSwitch, random,
+                        false /* checksContents */);
             }
 
             final long end = System.currentTimeMillis();
             Log.d(TAG, "testStressTestForSwitchingLanguageAndAddingWords took "
-                    + (end - now) + " ms");
+                    + (end - start) + " ms");
+        } finally {
             try {
                 Log.d(TAG, "waiting for writing ...");
-                Thread.sleep(5000);
+                for (int i = 0; i < numberOfLanguages; i++) {
+                    final UserHistoryPredictionDictionary dict =
+                            PersonalizationHelper.getUserHistoryPredictionDictionary(getContext(),
+                                    testFilenameSuffixes[i], mPrefs);
+                    dict.shutdownExecutorForTests();
+                    while (!dict.isTerminatedForTests()) {
+                        Thread.sleep(WAIT_TERMINATING_IN_MILLISECONDS);
+                    }
+                }
             } catch (InterruptedException e) {
                 Log.d(TAG, "InterruptedException: " + e);
             }
-        } finally {
             for (final File file : dictFiles) {
                 if (file != null) {
+                    assertTrue(file.exists());
+                    assertTrue(file.length() >= MIN_USER_HISTORY_DICTIONARY_FILE_SIZE);
                     file.delete();
                 }
             }
         }
     }
+
+    public void testAddManyWords() {
+        File dictFile = null;
+        final String testFilenameSuffix = "testRandomWords" + System.currentTimeMillis();
+        final int numberOfWords =
+                ExpandableBinaryDictionary.ENABLE_BINARY_DICTIONARY_DYNAMIC_UPDATE ?
+                        10000 : 1000;
+        final Random random = new Random(123456);
+
+        UserHistoryPredictionDictionary dict =
+                PersonalizationHelper.getUserHistoryPredictionDictionary(getContext(),
+                        testFilenameSuffix, mPrefs);
+        try {
+            addAndWriteRandomWords(testFilenameSuffix, numberOfWords, random,
+                    true /* checksContents */);
+            dict.close();
+        } finally {
+            try {
+                Log.d(TAG, "waiting for writing ...");
+                dict.shutdownExecutorForTests();
+                while (!dict.isTerminatedForTests()) {
+                    Thread.sleep(WAIT_TERMINATING_IN_MILLISECONDS);
+                }
+            } catch (InterruptedException e) {
+                Log.d(TAG, "InterruptedException: ", e);
+            }
+            final String fileName = UserHistoryPredictionDictionary.NAME + "." + testFilenameSuffix
+                    + ExpandableBinaryDictionary.DICT_FILE_EXTENSION;
+            dictFile = new File(getContext().getFilesDir(), fileName);
+            if (dictFile != null) {
+                assertTrue(dictFile.exists());
+                assertTrue(dictFile.length() >= MIN_USER_HISTORY_DICTIONARY_FILE_SIZE);
+                dictFile.delete();
+            }
+        }
+    }
+
 }
diff --git a/tests/src/com/android/inputmethod/latin/utils/AsyncResultHolderTests.java b/tests/src/com/android/inputmethod/latin/utils/AsyncResultHolderTests.java
new file mode 100644
index 0000000..7fd1679
--- /dev/null
+++ b/tests/src/com/android/inputmethod/latin/utils/AsyncResultHolderTests.java
@@ -0,0 +1,73 @@
+/*
+ * Copyright (C) 2013 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.latin.utils;
+
+import android.test.AndroidTestCase;
+import android.test.suitebuilder.annotation.MediumTest;
+import android.util.Log;
+
+@MediumTest
+public class AsyncResultHolderTests extends AndroidTestCase {
+    private static final String TAG = AsyncResultHolderTests.class.getSimpleName();
+
+    private static final int TIMEOUT_IN_MILLISECONDS = 500;
+    private static final int MARGIN_IN_MILLISECONDS = 250;
+    private static final int DEFAULT_VALUE = 2;
+    private static final int SET_VALUE = 1;
+
+    private <T> void setAfterGivenTime(final AsyncResultHolder<T> holder, final T value,
+            final long time) {
+        new Thread(new Runnable() {
+            @Override
+            public void run() {
+                try {
+                    Thread.sleep(time);
+                } catch (InterruptedException e) {
+                    Log.d(TAG, "Exception while sleeping", e);
+                }
+                holder.set(value);
+            }
+        }).start();
+    }
+
+    public void testGetWithoutSet() {
+        final AsyncResultHolder<Integer> holder = new AsyncResultHolder<Integer>();
+        final int resultValue = holder.get(DEFAULT_VALUE, TIMEOUT_IN_MILLISECONDS);
+        assertEquals(DEFAULT_VALUE, resultValue);
+    }
+
+    public void testGetBeforeSet() {
+        final AsyncResultHolder<Integer> holder = new AsyncResultHolder<Integer>();
+        setAfterGivenTime(holder, SET_VALUE, TIMEOUT_IN_MILLISECONDS + MARGIN_IN_MILLISECONDS);
+        final int resultValue = holder.get(DEFAULT_VALUE, TIMEOUT_IN_MILLISECONDS);
+        assertEquals(DEFAULT_VALUE, resultValue);
+    }
+
+    public void testGetAfterSet() {
+        final AsyncResultHolder<Integer> holder = new AsyncResultHolder<Integer>();
+        holder.set(SET_VALUE);
+        final int resultValue = holder.get(DEFAULT_VALUE, TIMEOUT_IN_MILLISECONDS);
+        assertEquals(SET_VALUE, resultValue);
+    }
+
+    public void testGetBeforeTimeout() {
+        final AsyncResultHolder<Integer> holder = new AsyncResultHolder<Integer>();
+        setAfterGivenTime(holder, SET_VALUE, TIMEOUT_IN_MILLISECONDS - MARGIN_IN_MILLISECONDS);
+        final int resultValue = holder.get(DEFAULT_VALUE, TIMEOUT_IN_MILLISECONDS);
+        assertEquals(SET_VALUE, resultValue);
+    }
+}
diff --git a/tests/src/com/android/inputmethod/latin/utils/PrioritizedSerialExecutorTests.java b/tests/src/com/android/inputmethod/latin/utils/PrioritizedSerialExecutorTests.java
new file mode 100644
index 0000000..e075548
--- /dev/null
+++ b/tests/src/com/android/inputmethod/latin/utils/PrioritizedSerialExecutorTests.java
@@ -0,0 +1,105 @@
+/*
+ * Copyright (C) 2013 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.latin.utils;
+
+import android.test.AndroidTestCase;
+import android.test.suitebuilder.annotation.MediumTest;
+import android.util.Log;
+
+import java.util.concurrent.atomic.AtomicInteger;
+
+/**
+ * Unit tests for PrioritizedSerialExecutor.
+ * TODO: Add more detailed tests to make use of priorities, etc.
+ */
+@MediumTest
+public class PrioritizedSerialExecutorTests extends AndroidTestCase {
+    private static final String TAG = PrioritizedSerialExecutorTests.class.getSimpleName();
+
+    private static final int NUM_OF_TASKS = 10;
+    private static final int DELAY_FOR_WAITING_TASKS_MILLISECONDS = 500;
+
+    public void testExecute() {
+        final PrioritizedSerialExecutor executor = new PrioritizedSerialExecutor();
+        final AtomicInteger v = new AtomicInteger(0);
+        for (int i = 0; i < NUM_OF_TASKS; ++i) {
+            executor.execute(new Runnable() {
+                @Override
+                public void run() {
+                    v.incrementAndGet();
+                }
+            });
+        }
+        try {
+            Thread.sleep(DELAY_FOR_WAITING_TASKS_MILLISECONDS);
+        } catch (InterruptedException e) {
+            Log.d(TAG, "Exception while sleeping.", e);
+        }
+
+        assertEquals(NUM_OF_TASKS, v.get());
+    }
+
+    public void testExecutePrioritized() {
+        final PrioritizedSerialExecutor executor = new PrioritizedSerialExecutor();
+        final AtomicInteger v = new AtomicInteger(0);
+        for (int i = 0; i < NUM_OF_TASKS; ++i) {
+            executor.executePrioritized(new Runnable() {
+                @Override
+                public void run() {
+                    v.incrementAndGet();
+                }
+            });
+        }
+        try {
+            Thread.sleep(DELAY_FOR_WAITING_TASKS_MILLISECONDS);
+        } catch (InterruptedException e) {
+            Log.d(TAG, "Exception while sleeping.", e);
+        }
+
+        assertEquals(NUM_OF_TASKS, v.get());
+    }
+
+    public void testExecuteCombined() {
+        final PrioritizedSerialExecutor executor = new PrioritizedSerialExecutor();
+        final AtomicInteger v = new AtomicInteger(0);
+        for (int i = 0; i < NUM_OF_TASKS; ++i) {
+            executor.execute(new Runnable() {
+                @Override
+                public void run() {
+                    v.incrementAndGet();
+                }
+            });
+        }
+
+        for (int i = 0; i < NUM_OF_TASKS; ++i) {
+            executor.executePrioritized(new Runnable() {
+                @Override
+                public void run() {
+                    v.incrementAndGet();
+                }
+            });
+        }
+
+        try {
+            Thread.sleep(DELAY_FOR_WAITING_TASKS_MILLISECONDS);
+        } catch (InterruptedException e) {
+            Log.d(TAG, "Exception while sleeping.", e);
+        }
+
+        assertEquals(2 * NUM_OF_TASKS, v.get());
+    }
+}
diff --git a/tests/src/com/android/inputmethod/latin/utils/ResizableIntArrayTests.java b/tests/src/com/android/inputmethod/latin/utils/ResizableIntArrayTests.java
index cfff61e..cad80d5 100644
--- a/tests/src/com/android/inputmethod/latin/utils/ResizableIntArrayTests.java
+++ b/tests/src/com/android/inputmethod/latin/utils/ResizableIntArrayTests.java
@@ -340,4 +340,18 @@
                     expecteds[i + expectedPos], actuals[i + actualPos]);
         }
     }
+
+    public void testShift() {
+        final ResizableIntArray src = new ResizableIntArray(DEFAULT_CAPACITY);
+        final int limit = DEFAULT_CAPACITY * 10;
+        final int shiftAmount = 20;
+        for (int i = 0; i < limit; ++i) {
+            src.add(i, i);
+            assertEquals("length after add at " + i, i + 1, src.getLength());
+        }
+        src.shift(shiftAmount);
+        for (int i = 0; i < limit - shiftAmount; ++i) {
+            assertEquals("value at " + i, i + shiftAmount, src.get(i));
+        }
+    }
 }
diff --git a/tests/src/com/android/inputmethod/latin/utils/StringUtilsTests.java b/tests/src/com/android/inputmethod/latin/utils/StringUtilsTests.java
index 9ee8e38..eb9fb98 100644
--- a/tests/src/com/android/inputmethod/latin/utils/StringUtilsTests.java
+++ b/tests/src/com/android/inputmethod/latin/utils/StringUtilsTests.java
@@ -16,9 +16,18 @@
 
 package com.android.inputmethod.latin.utils;
 
+import com.android.inputmethod.latin.settings.SettingsValues;
+
 import android.test.AndroidTestCase;
 import android.test.suitebuilder.annotation.SmallTest;
+import android.text.style.SuggestionSpan;
+import android.text.style.URLSpan;
+import android.text.SpannableStringBuilder;
+import android.text.Spannable;
+import android.text.Spanned;
 
+import java.util.Arrays;
+import java.util.List;
 import java.util.Locale;
 
 @SmallTest
@@ -183,6 +192,18 @@
         assertTrue(StringUtils.isIdenticalAfterDowncase(""));
     }
 
+    public void testLooksValidForDictionaryInsertion() {
+        final SettingsValues settings =
+                SettingsValues.makeDummySettingsValuesForTest(Locale.ENGLISH);
+        assertTrue(StringUtils.looksValidForDictionaryInsertion("aochaueo", settings));
+        assertFalse(StringUtils.looksValidForDictionaryInsertion("", settings));
+        assertTrue(StringUtils.looksValidForDictionaryInsertion("ao-ch'aueo", settings));
+        assertFalse(StringUtils.looksValidForDictionaryInsertion("2908743256", settings));
+        assertTrue(StringUtils.looksValidForDictionaryInsertion("31aochaueo", settings));
+        assertFalse(StringUtils.looksValidForDictionaryInsertion("akeo  raeoch oerch .", settings));
+        assertFalse(StringUtils.looksValidForDictionaryInsertion("!!!", settings));
+    }
+
     private static void checkCapitalize(final String src, final String dst, final String separators,
             final Locale locale) {
         assertEquals(dst, StringUtils.capitalizeEachWord(src, separators, locale));
@@ -242,4 +263,56 @@
         // code for now True is acceptable.
         assertTrue(StringUtils.lastPartLooksLikeURL(".abc/def"));
     }
+
+    public void testHexStringUtils() {
+        final byte[] bytes = new byte[] { (byte)0x01, (byte)0x11, (byte)0x22, (byte)0x33,
+                (byte)0x55, (byte)0x88, (byte)0xEE };
+        final String bytesStr = StringUtils.byteArrayToHexString(bytes);
+        final byte[] bytes2 = StringUtils.hexStringToByteArray(bytesStr);
+        for (int i = 0; i < bytes.length; ++i) {
+            assertTrue(bytes[i] == bytes2[i]);
+        }
+        final String bytesStr2 = StringUtils.byteArrayToHexString(bytes2);
+        assertTrue(bytesStr.equals(bytesStr2));
+    }
+
+    public void testJsonStringUtils() {
+        final Object[] objs = new Object[] { 1, "aaa", "bbb", 3 };
+        final List<Object> objArray = Arrays.asList(objs);
+        final String str = StringUtils.listToJsonStr(objArray);
+        final List<Object> newObjArray = StringUtils.jsonStrToList(str);
+        for (int i = 0; i < objs.length; ++i) {
+            assertEquals(objs[i], newObjArray.get(i));
+        }
+    }
+
+    public void testConcatWithSuggestionSpansOnly() {
+        SpannableStringBuilder s = new SpannableStringBuilder("test string\ntest string\n"
+                + "test string\ntest string\ntest string\ntest string\ntest string\ntest string\n"
+                + "test string\ntest string\n");
+        final int N = 10;
+        for (int i = 0; i < N; ++i) {
+            // Put a PARAGRAPH-flagged span that should not be found in the result.
+            s.setSpan(new SuggestionSpan(getContext(),
+                    new String[] {"" + i}, Spannable.SPAN_PARAGRAPH),
+                    i * 12, i * 12 + 12, Spannable.SPAN_PARAGRAPH);
+            // Put a normal suggestion span that should be found in the result.
+            s.setSpan(new SuggestionSpan(getContext(), new String[] {"" + i}, 0), i, i * 2, 0);
+            // Put a URL span than should not be found in the result.
+            s.setSpan(new URLSpan("http://a"), i, i * 2, 0);
+        }
+
+        final CharSequence a = s.subSequence(0, 15);
+        final CharSequence b = s.subSequence(15, s.length());
+        final Spanned result =
+                (Spanned)StringUtils.concatWithNonParagraphSuggestionSpansOnly(a, b);
+
+        Object[] spans = result.getSpans(0, result.length(), SuggestionSpan.class);
+        for (int i = 0; i < spans.length; i++) {
+            final int flags = result.getSpanFlags(spans[i]);
+            assertEquals("Should not find a span with PARAGRAPH flag",
+                    flags & Spannable.SPAN_PARAGRAPH, 0);
+            assertTrue("Should be a SuggestionSpan", spans[i] instanceof SuggestionSpan);
+        }
+    }
 }
diff --git a/tests/src/com/android/inputmethod/latin/utils/SubtypeLocaleUtilsTests.java b/tests/src/com/android/inputmethod/latin/utils/SubtypeLocaleUtilsTests.java
index baebda2..856b2db 100644
--- a/tests/src/com/android/inputmethod/latin/utils/SubtypeLocaleUtilsTests.java
+++ b/tests/src/com/android/inputmethod/latin/utils/SubtypeLocaleUtilsTests.java
@@ -214,7 +214,7 @@
                         SubtypeLocaleUtils.getSubtypeDisplayNameInSystemLocale(FR_CA));
                 assertEquals("de   ", "Allemand",
                         SubtypeLocaleUtils.getSubtypeDisplayNameInSystemLocale(DE));
-                assertEquals("zz   ", "Alphabet (QWERTY)",
+                assertEquals("zz   ", "Alphabet latin (QWERTY)",
                         SubtypeLocaleUtils.getSubtypeDisplayNameInSystemLocale(ZZ));
                 return null;
             }
@@ -236,7 +236,7 @@
                         SubtypeLocaleUtils.getSubtypeDisplayNameInSystemLocale(EN_UK_DVORAK));
                 assertEquals("es_US colemak","Espagnol (États-Unis) (Colemak)",
                         SubtypeLocaleUtils.getSubtypeDisplayNameInSystemLocale(ES_US_COLEMAK));
-                assertEquals("zz pc",    "Alphabet (PC)",
+                assertEquals("zz pc",    "Alphabet latin (PC)",
                         SubtypeLocaleUtils.getSubtypeDisplayNameInSystemLocale(ZZ_PC));
                 return null;
             }
diff --git a/tests/src/com/android/inputmethod/latin/utils/UserHistoryDictIOUtilsTests.java b/tests/src/com/android/inputmethod/latin/utils/UserHistoryDictIOUtilsTests.java
index b679839..3eabe2b 100644
--- a/tests/src/com/android/inputmethod/latin/utils/UserHistoryDictIOUtilsTests.java
+++ b/tests/src/com/android/inputmethod/latin/utils/UserHistoryDictIOUtilsTests.java
@@ -21,18 +21,19 @@
 import android.test.suitebuilder.annotation.LargeTest;
 import android.util.Log;
 
+import com.android.inputmethod.latin.makedict.DictDecoder;
+import com.android.inputmethod.latin.makedict.DictEncoder;
 import com.android.inputmethod.latin.makedict.FormatSpec;
 import com.android.inputmethod.latin.makedict.FusionDictionary;
-import com.android.inputmethod.latin.makedict.FusionDictionary.CharGroup;
+import com.android.inputmethod.latin.makedict.FusionDictionary.PtNode;
+import com.android.inputmethod.latin.makedict.Ver3DictDecoder;
+import com.android.inputmethod.latin.makedict.Ver3DictEncoder;
 import com.android.inputmethod.latin.personalization.UserHistoryDictionaryBigramList;
-import com.android.inputmethod.latin.utils.ByteArrayWrapper;
 import com.android.inputmethod.latin.utils.UserHistoryDictIOUtils.BigramDictionaryInterface;
 import com.android.inputmethod.latin.utils.UserHistoryDictIOUtils.OnAddWordListener;
 
 import java.io.File;
-import java.io.FileInputStream;
 import java.io.FileNotFoundException;
-import java.io.FileOutputStream;
 import java.io.IOException;
 import java.util.ArrayList;
 import java.util.Collections;
@@ -50,6 +51,7 @@
     private static final int BIGRAM_FREQUENCY = 100;
     private static final ArrayList<String> NOT_HAVE_BIGRAM = new ArrayList<String>();
     private static final FormatSpec.FormatOptions FORMAT_OPTIONS = new FormatSpec.FormatOptions(2);
+    private static final String TEST_DICT_FILE_EXTENSION = ".testDict";
 
     /**
      * Return same frequency for all words and bigrams
@@ -87,12 +89,12 @@
 
     private void checkWordInFusionDict(final FusionDictionary dict, final String word,
             final ArrayList<String> expectedBigrams) {
-        final CharGroup group = FusionDictionary.findWordInTree(dict.mRoot, word);
-        assertNotNull(group);
-        assertTrue(group.isTerminal());
+        final PtNode ptNode = FusionDictionary.findWordInTree(dict.mRootNodeArray, word);
+        assertNotNull(ptNode);
+        assertTrue(ptNode.isTerminal());
 
         for (final String bigram : expectedBigrams) {
-            assertNotNull(group.getBigram(bigram));
+            assertNotNull(ptNode.getBigram(bigram));
         }
     }
 
@@ -136,38 +138,20 @@
 
     private void writeDictToFile(final File file,
             final UserHistoryDictionaryBigramList bigramList) {
-        try {
-            final FileOutputStream out = new FileOutputStream(file);
-            UserHistoryDictIOUtils.writeDictionaryBinary(out, this, bigramList, FORMAT_OPTIONS);
-            out.flush();
-            out.close();
-        } catch (IOException e) {
-            Log.e(TAG, "IO exception while writing file", e);
-        }
+        final DictEncoder dictEncoder = new Ver3DictEncoder(file);
+        UserHistoryDictIOUtils.writeDictionary(dictEncoder, this, bigramList, FORMAT_OPTIONS);
     }
 
     private void readDictFromFile(final File file, final OnAddWordListener listener) {
-        FileInputStream inStream = null;
-
+        final DictDecoder dictDecoder = FormatSpec.getDictDecoder(file, DictDecoder.USE_BYTEARRAY);
         try {
-            inStream = new FileInputStream(file);
-            final byte[] buffer = new byte[(int)file.length()];
-            inStream.read(buffer);
-
-            UserHistoryDictIOUtils.readDictionaryBinary(new ByteArrayWrapper(buffer), listener);
+            dictDecoder.openDictBuffer();
         } catch (FileNotFoundException e) {
             Log.e(TAG, "file not found", e);
         } catch (IOException e) {
             Log.e(TAG, "IOException", e);
-        } finally {
-            if (inStream != null) {
-                try {
-                    inStream.close();
-                } catch (IOException e) {
-                    // do nothing
-                }
-            }
         }
+        UserHistoryDictIOUtils.readDictionaryBinary(dictDecoder, listener);
     }
 
     public void testGenerateFusionDictionary() {
@@ -190,7 +174,8 @@
 
         File file = null;
         try {
-            file = File.createTempFile("testReadAndWrite", ".dict", getContext().getCacheDir());
+            file = File.createTempFile("testReadAndWrite", TEST_DICT_FILE_EXTENSION,
+                    getContext().getCacheDir());
         } catch (IOException e) {
             Log.d(TAG, "IOException while creating a temporary file", e);
         }
diff --git a/tools/dicttool/Android.mk b/tools/dicttool/Android.mk
index d06be58..3d09c05 100644
--- a/tools/dicttool/Android.mk
+++ b/tools/dicttool/Android.mk
@@ -13,27 +13,34 @@
 # See the License for the specific language governing permissions and
 # limitations under the License.
 
-LOCAL_PATH := $(call my-dir)
+LATINIME_DICTTOOL_AOSP_LOCAL_PATH := $(call my-dir)
+LOCAL_PATH := $(LATINIME_DICTTOOL_AOSP_LOCAL_PATH)
+LATINIME_HOST_NATIVE_LIBNAME := liblatinime-aosp-dicttool-host
+include $(LOCAL_PATH)/NativeLib.mk
+
+######################################
+LOCAL_PATH := $(LATINIME_DICTTOOL_AOSP_LOCAL_PATH)
 include $(CLEAR_VARS)
 
-BUILD_TOP := ../../../../..
-LATINIME_DIR := $(BUILD_TOP)/packages/inputmethods/LatinIME
-LATINIME_BASE_SOURCE_DIRECTORY := $(LATINIME_DIR)/java/src/com/android/inputmethod
-LATINIME_CORE_SOURCE_DIRECTORY := $(LATINIME_BASE_SOURCE_DIRECTORY)/latin
+LATINIME_LOCAL_DIR := ../..
+LATINIME_BASE_SOURCE_DIRECTORY := $(LATINIME_LOCAL_DIR)/java/src/com/android/inputmethod
 LATINIME_ANNOTATIONS_SOURCE_DIRECTORY := $(LATINIME_BASE_SOURCE_DIRECTORY)/annotations
+LATINIME_CORE_SOURCE_DIRECTORY := $(LATINIME_BASE_SOURCE_DIRECTORY)/latin
 MAKEDICT_CORE_SOURCE_DIRECTORY := $(LATINIME_CORE_SOURCE_DIRECTORY)/makedict
-DICTTOOL_COMPAT_TESTS_DIRECTORY := compat
-DICTTOOL_ONDEVICE_TESTS_DIRECTORY := \
-        $(LATINIME_DIR)/tests/src/com/android/inputmethod/latin/makedict/
-
 USED_TARGETTED_UTILS := \
-        $(LATINIME_CORE_SOURCE_DIRECTORY)/utils/ByteArrayWrapper.java \
-        $(LATINIME_CORE_SOURCE_DIRECTORY)/utils/CollectionUtils.java
+        $(LATINIME_CORE_SOURCE_DIRECTORY)/utils/ByteArrayDictBuffer.java \
+        $(LATINIME_CORE_SOURCE_DIRECTORY)/utils/CollectionUtils.java \
+        $(LATINIME_CORE_SOURCE_DIRECTORY)/utils/JniUtils.java
+
+DICTTOOL_ONDEVICE_TESTS_DIRECTORY := \
+        $(LATINIME_LOCAL_DIR)/tests/src/com/android/inputmethod/latin/makedict/
+DICTTOOL_COMPAT_TESTS_DIRECTORY := compat
 
 LOCAL_MAIN_SRC_FILES := $(call all-java-files-under, $(MAKEDICT_CORE_SOURCE_DIRECTORY))
 LOCAL_TOOL_SRC_FILES := $(call all-java-files-under, src)
 LOCAL_ANNOTATIONS_SRC_FILES := \
         $(call all-java-files-under, $(LATINIME_ANNOTATIONS_SOURCE_DIRECTORY))
+
 LOCAL_SRC_FILES := $(LOCAL_TOOL_SRC_FILES) \
         $(filter-out $(addprefix %/, $(notdir $(LOCAL_TOOL_SRC_FILES))), $(LOCAL_MAIN_SRC_FILES)) \
         $(LOCAL_ANNOTATIONS_SRC_FILES) \
@@ -44,9 +51,13 @@
         $(USED_TARGETTED_UTILS)
 
 LOCAL_JAVA_LIBRARIES := junit
-
+LOCAL_ADDITIONAL_DEPENDENCIES := $(LATINIME_HOST_NATIVE_LIBNAME)
 LOCAL_JAR_MANIFEST := etc/manifest.txt
 LOCAL_MODULE := dicttool_aosp
 
 include $(BUILD_HOST_JAVA_LIBRARY)
 include $(LOCAL_PATH)/etc/Android.mk
+
+# Clear our private variables
+LATINIME_DICTTOOL_AOSP_LOCAL_PATH :=
+LATINIME_LOCAL_DIR :=
diff --git a/tools/dicttool/NativeLib.mk b/tools/dicttool/NativeLib.mk
new file mode 100644
index 0000000..a3d3c02
--- /dev/null
+++ b/tools/dicttool/NativeLib.mk
@@ -0,0 +1,51 @@
+#
+# Copyright (C) 2013 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.
+
+LOCAL_PATH := $(call my-dir)
+include $(CLEAR_VARS)
+
+# Need to define the name of the library in the caller in LATINIME_HOST_NATIVE_LIBNAME
+
+LATINIME_DIR_RELATIVE_TO_DICTTOOL := ../..
+
+ifneq ($(strip $(HOST_JDK_IS_64BIT_VERSION)),)
+LOCAL_CFLAGS += -m64
+LOCAL_LDFLAGS += -m64
+endif #HOST_JDK_IS_64BIT_VERSION
+
+LOCAL_CFLAGS += -DHOST_TOOL -fPIC
+LOCAL_NO_DEFAULT_COMPILER_FLAGS := true
+
+LATINIME_NATIVE_JNI_DIR := $(LATINIME_DIR_RELATIVE_TO_DICTTOOL)/native/jni
+LATINIME_NATIVE_SRC_DIR := $(LATINIME_DIR_RELATIVE_TO_DICTTOOL)/native/jni/src
+LOCAL_C_INCLUDES := $(LOCAL_PATH)/$(LATINIME_NATIVE_SRC_DIR)
+# Used in jni_common.cpp to avoid registering useless methods.
+
+LATIN_IME_JNI_SRC_FILES := \
+    com_android_inputmethod_latin_makedict_Ver3DictDecoder.cpp \
+    jni_common.cpp
+
+LATIN_IME_CORE_SRC_FILES :=
+
+LOCAL_SRC_FILES := \
+    $(addprefix $(LATINIME_NATIVE_JNI_DIR)/, $(LATIN_IME_JNI_SRC_FILES)) \
+    $(addprefix $(LATINIME_NATIVE_SRC_DIR)/, $(LATIN_IME_CORE_SRC_FILES))
+
+LOCAL_MODULE := $(LATINIME_HOST_NATIVE_LIBNAME)
+
+include $(BUILD_HOST_SHARED_LIBRARY)
+
+# Clear our private variables
+LATINIME_DIR_RELATIVE_TO_DICTTOOL := ../..
diff --git a/tools/dicttool/compat/android/util/Log.java b/tools/dicttool/compat/android/util/Log.java
index d9df3a4..b3b6dd8 100644
--- a/tools/dicttool/compat/android/util/Log.java
+++ b/tools/dicttool/compat/android/util/Log.java
@@ -25,13 +25,13 @@
     public static void d(final String tag, final String message) {
         System.out.println(tag + " : " + message);
     }
-    public static void d(final String tag, final String message, final Exception e) {
+    public static void d(final String tag, final String message, final Throwable e) {
         System.out.println(tag + " : " + message + " : " + e);
     }
     public static void e(final String tag, final String message) {
         d(tag, message);
     }
-    public static void e(final String tag, final String message, final Exception e) {
-        e(tag, message, e);
+    public static void e(final String tag, final String message, final Throwable e) {
+        d(tag, message, e);
     }
 }
diff --git a/native/jni/src/suggest/core/dictionary/byte_array_utils.cpp b/tools/dicttool/compat/com/android/inputmethod/latin/define/JniLibName.java
similarity index 71%
copy from native/jni/src/suggest/core/dictionary/byte_array_utils.cpp
copy to tools/dicttool/compat/com/android/inputmethod/latin/define/JniLibName.java
index 68b1d5d..c68bdaa 100644
--- a/native/jni/src/suggest/core/dictionary/byte_array_utils.cpp
+++ b/tools/dicttool/compat/com/android/inputmethod/latin/define/JniLibName.java
@@ -14,11 +14,12 @@
  * limitations under the License.
  */
 
-#include "suggest/core/dictionary/byte_array_utils.h"
+package com.android.inputmethod.latin.define;
 
-namespace latinime {
+public final class JniLibName {
+    private JniLibName() {
+        // This class is not publicly instantiable.
+    }
 
-const uint8_t ByteArrayUtils::MINIMAL_ONE_BYTE_CHARACTER_VALUE = 0x20;
-const uint8_t ByteArrayUtils::CHARACTER_ARRAY_TERMINATOR = 0x1F;
-
-} // namespace latinime
+    public static final String JNI_LIB_NAME = "latinime-dicttool-host";
+}
diff --git a/tools/dicttool/etc/dicttool_aosp b/tools/dicttool/etc/dicttool_aosp
index cc7111a..65a1c3a 100755
--- a/tools/dicttool/etc/dicttool_aosp
+++ b/tools/dicttool/etc/dicttool_aosp
@@ -69,4 +69,4 @@
 fi
 
 # might need more memory, e.g. -Xmx128M
-exec java -ea -classpath "$libpath":"$jarpath" "$classname" "$@"
+exec java -ea -classpath "$libpath":"$jarpath" -Djava.library.path="$libdir" "$classname" "$@"
diff --git a/tools/dicttool/src/com/android/inputmethod/latin/dicttool/BinaryDictOffdeviceUtils.java b/tools/dicttool/src/com/android/inputmethod/latin/dicttool/BinaryDictOffdeviceUtils.java
index c2c77d6..fa80385 100644
--- a/tools/dicttool/src/com/android/inputmethod/latin/dicttool/BinaryDictOffdeviceUtils.java
+++ b/tools/dicttool/src/com/android/inputmethod/latin/dicttool/BinaryDictOffdeviceUtils.java
@@ -16,7 +16,9 @@
 
 package com.android.inputmethod.latin.dicttool;
 
-import com.android.inputmethod.latin.makedict.BinaryDictInputOutput;
+import com.android.inputmethod.latin.makedict.BinaryDictDecoderUtils;
+import com.android.inputmethod.latin.makedict.DictDecoder;
+import com.android.inputmethod.latin.makedict.FormatSpec;
 import com.android.inputmethod.latin.makedict.FusionDictionary;
 import com.android.inputmethod.latin.makedict.UnsupportedFormatException;
 
@@ -30,8 +32,6 @@
 import java.io.IOException;
 import java.io.InputStream;
 import java.io.OutputStream;
-import java.nio.ByteBuffer;
-import java.nio.channels.FileChannel;
 import java.util.ArrayList;
 
 import javax.xml.parsers.ParserConfigurationException;
@@ -98,7 +98,7 @@
         // over and over, ending in a stack overflow. Hence we limit the depth at which we try
         // decoding the file.
         if (depth > MAX_DECODE_DEPTH) return null;
-        if (BinaryDictInputOutput.isBinaryDictionary(src)) {
+        if (BinaryDictDecoderUtils.isBinaryDictionary(src)) {
             spec.mFile = src;
             return spec;
         }
@@ -185,16 +185,14 @@
                     crash(filename, new RuntimeException(
                             filename + " does not seem to be a dictionary file"));
                 } else {
-                    final FileInputStream inStream = new FileInputStream(decodedSpec.mFile);
-                    final ByteBuffer buffer = inStream.getChannel().map(
-                            FileChannel.MapMode.READ_ONLY, 0, decodedSpec.mFile.length());
+                    final DictDecoder dictDecoder = FormatSpec.getDictDecoder(file,
+                            DictDecoder.USE_BYTEARRAY);
                     if (report) {
                         System.out.println("Format : Binary dictionary format");
                         System.out.println("Packaging : " + decodedSpec.describeChain());
                         System.out.println("Uncompressed size : " + decodedSpec.mFile.length());
                     }
-                    return BinaryDictInputOutput.readDictionaryBinary(
-                            new BinaryDictInputOutput.ByteBufferWrapper(buffer), null);
+                    return dictDecoder.readDictionaryBinary(null, false /* deleteDictIfBroken */);
                 }
             }
         } catch (IOException e) {
diff --git a/tools/dicttool/src/com/android/inputmethod/latin/dicttool/CombinedInputOutput.java b/tools/dicttool/src/com/android/inputmethod/latin/dicttool/CombinedInputOutput.java
index 092ee76..4b67169 100644
--- a/tools/dicttool/src/com/android/inputmethod/latin/dicttool/CombinedInputOutput.java
+++ b/tools/dicttool/src/com/android/inputmethod/latin/dicttool/CombinedInputOutput.java
@@ -19,7 +19,7 @@
 import com.android.inputmethod.latin.makedict.FormatSpec;
 import com.android.inputmethod.latin.makedict.FusionDictionary;
 import com.android.inputmethod.latin.makedict.FusionDictionary.DictionaryOptions;
-import com.android.inputmethod.latin.makedict.FusionDictionary.Node;
+import com.android.inputmethod.latin.makedict.FusionDictionary.PtNodeArray;
 import com.android.inputmethod.latin.makedict.FusionDictionary.WeightedString;
 import com.android.inputmethod.latin.makedict.Word;
 
@@ -117,7 +117,7 @@
         final boolean processLigatures =
                 FRENCH_LIGATURE_PROCESSING_OPTION.equals(attributes.get(OPTIONS_TAG));
         attributes.remove(OPTIONS_TAG);
-        final FusionDictionary dict = new FusionDictionary(new Node(), new DictionaryOptions(
+        final FusionDictionary dict = new FusionDictionary(new PtNodeArray(), new DictionaryOptions(
                 attributes, processUmlauts, processLigatures));
 
         String line;
diff --git a/tools/dicttool/src/com/android/inputmethod/latin/dicttool/DictionaryMaker.java b/tools/dicttool/src/com/android/inputmethod/latin/dicttool/DictionaryMaker.java
index cc890f6..5c7e8b4 100644
--- a/tools/dicttool/src/com/android/inputmethod/latin/dicttool/DictionaryMaker.java
+++ b/tools/dicttool/src/com/android/inputmethod/latin/dicttool/DictionaryMaker.java
@@ -16,21 +16,22 @@
 
 package com.android.inputmethod.latin.dicttool;
 
-import com.android.inputmethod.latin.makedict.BinaryDictInputOutput;
+import com.android.inputmethod.latin.makedict.BinaryDictDecoderUtils;
+import com.android.inputmethod.latin.makedict.DictDecoder;
+import com.android.inputmethod.latin.makedict.DictEncoder;
 import com.android.inputmethod.latin.makedict.FormatSpec;
 import com.android.inputmethod.latin.makedict.FusionDictionary;
 import com.android.inputmethod.latin.makedict.MakedictLog;
 import com.android.inputmethod.latin.makedict.UnsupportedFormatException;
+import com.android.inputmethod.latin.makedict.Ver3DictEncoder;
+import com.android.inputmethod.latin.makedict.Ver4DictEncoder;
 
 import java.io.BufferedWriter;
 import java.io.File;
 import java.io.FileInputStream;
 import java.io.FileNotFoundException;
-import java.io.FileOutputStream;
 import java.io.FileWriter;
 import java.io.IOException;
-import java.nio.ByteBuffer;
-import java.nio.channels.FileChannel;
 import java.util.Arrays;
 import java.util.LinkedList;
 
@@ -44,9 +45,9 @@
 public class DictionaryMaker {
 
     static class Arguments {
-        private static final String OPTION_VERSION_1 = "-1";
         private static final String OPTION_VERSION_2 = "-2";
         private static final String OPTION_VERSION_3 = "-3";
+        private static final String OPTION_VERSION_4 = "-4";
         private static final String OPTION_INPUT_SOURCE = "-s";
         private static final String OPTION_INPUT_BIGRAM_XML = "-b";
         private static final String OPTION_INPUT_SHORTCUT_XML = "-c";
@@ -127,12 +128,12 @@
                     + "| [-s <combined format input]"
                     + "| [-s <binary input>] [-d <binary output>] [-x <xml output>] "
                     + " [-o <combined output>]"
-                    + "[-1] [-2] [-3]\n"
+                    + "[-2] [-3] [-4]\n"
                     + "\n"
                     + "  Converts a source dictionary file to one or several outputs.\n"
                     + "  Source can be an XML file, with an optional XML bigrams file, or a\n"
                     + "  binary dictionary file.\n"
-                    + "  Binary version 1 (Ice Cream Sandwich), 2 (Jelly Bean), 3, XML and\n"
+                    + "  Binary version 2 (Jelly Bean), 3, 4, XML and\n"
                     + "  combined format outputs are supported.";
         }
 
@@ -159,8 +160,8 @@
                         // Do nothing, this is the default
                     } else if (OPTION_VERSION_3.equals(arg)) {
                         outputBinaryFormatVersion = 3;
-                    } else if (OPTION_VERSION_1.equals(arg)) {
-                        outputBinaryFormatVersion = 1;
+                    } else if (OPTION_VERSION_4.equals(arg)) {
+                        outputBinaryFormatVersion = 4;
                     } else if (OPTION_HELP.equals(arg)) {
                         displayHelp();
                     } else {
@@ -176,7 +177,7 @@
                                 inputUnigramXml = filename;
                             } else if (CombinedInputOutput.isCombinedDictionary(filename)) {
                                 inputCombined = filename;
-                            } else if (BinaryDictInputOutput.isBinaryDictionary(filename)) {
+                            } else if (BinaryDictDecoderUtils.isBinaryDictionary(filename)) {
                                 inputBinary = filename;
                             } else {
                                 throw new IllegalArgumentException(
@@ -198,7 +199,7 @@
                     }
                 } else {
                     if (null == inputBinary && null == inputUnigramXml) {
-                        if (BinaryDictInputOutput.isBinaryDictionary(arg)) {
+                        if (BinaryDictDecoderUtils.isBinaryDictionary(arg)) {
                             inputBinary = arg;
                         } else if (CombinedInputOutput.isCombinedDictionary(arg)) {
                             inputCombined = arg;
@@ -265,24 +266,9 @@
      */
     private static FusionDictionary readBinaryFile(final String binaryFilename)
             throws FileNotFoundException, IOException, UnsupportedFormatException {
-        FileInputStream inStream = null;
-
-        try {
-            final File file = new File(binaryFilename);
-            inStream = new FileInputStream(file);
-            final ByteBuffer buffer = inStream.getChannel().map(
-                    FileChannel.MapMode.READ_ONLY, 0, file.length());
-            return BinaryDictInputOutput.readDictionaryBinary(
-                    new BinaryDictInputOutput.ByteBufferWrapper(buffer), null);
-        } finally {
-            if (inStream != null) {
-                try {
-                    inStream.close();
-                } catch (IOException e) {
-                    // do nothing
-                }
-            }
-        }
+        final File file = new File(binaryFilename);
+        final DictDecoder dictDecoder = FormatSpec.getDictDecoder(file);
+        return dictDecoder.readDictionaryBinary(null, false /* deleteDictIfBroken */);
     }
 
     /**
@@ -371,8 +357,13 @@
             throws FileNotFoundException, IOException, UnsupportedFormatException {
         final File outputFile = new File(outputFilename);
         final FormatSpec.FormatOptions formatOptions = new FormatSpec.FormatOptions(version);
-        BinaryDictInputOutput.writeDictionaryBinary(new FileOutputStream(outputFilename), dict,
-                formatOptions);
+        final DictEncoder dictEncoder;
+        if (version == 4) {
+            dictEncoder = new Ver4DictEncoder(outputFile);
+        } else {
+            dictEncoder = new Ver3DictEncoder(outputFile);
+        }
+        dictEncoder.writeDictionary(dict, formatOptions);
     }
 
     /**
diff --git a/tools/dicttool/src/com/android/inputmethod/latin/dicttool/Diff.java b/tools/dicttool/src/com/android/inputmethod/latin/dicttool/Diff.java
index 5c3e87e..66fd084 100644
--- a/tools/dicttool/src/com/android/inputmethod/latin/dicttool/Diff.java
+++ b/tools/dicttool/src/com/android/inputmethod/latin/dicttool/Diff.java
@@ -17,7 +17,7 @@
 package com.android.inputmethod.latin.dicttool;
 
 import com.android.inputmethod.latin.makedict.FusionDictionary;
-import com.android.inputmethod.latin.makedict.FusionDictionary.CharGroup;
+import com.android.inputmethod.latin.makedict.FusionDictionary.PtNode;
 import com.android.inputmethod.latin.makedict.FusionDictionary.WeightedString;
 import com.android.inputmethod.latin.makedict.Word;
 
@@ -121,7 +121,8 @@
     private static void diffWords(final FusionDictionary dict0, final FusionDictionary dict1) {
         boolean hasDifferences = false;
         for (final Word word0 : dict0) {
-            final CharGroup word1 = FusionDictionary.findWordInTree(dict1.mRoot, word0.mWord);
+            final PtNode word1 = FusionDictionary.findWordInTree(dict1.mRootNodeArray,
+                    word0.mWord);
             if (null == word1) {
                 // This word is not in dict1
                 System.out.println("Deleted: " + word0.mWord + " " + word0.mFrequency);
@@ -150,7 +151,8 @@
             }
         }
         for (final Word word1 : dict1) {
-            final CharGroup word0 = FusionDictionary.findWordInTree(dict0.mRoot, word1.mWord);
+            final PtNode word0 = FusionDictionary.findWordInTree(dict0.mRootNodeArray,
+                    word1.mWord);
             if (null == word0) {
                 // This word is not in dict0
                 System.out.println("Added: " + word1.mWord + " " + word1.mFrequency);
diff --git a/tools/dicttool/src/com/android/inputmethod/latin/dicttool/Info.java b/tools/dicttool/src/com/android/inputmethod/latin/dicttool/Info.java
index f289454..350f427 100644
--- a/tools/dicttool/src/com/android/inputmethod/latin/dicttool/Info.java
+++ b/tools/dicttool/src/com/android/inputmethod/latin/dicttool/Info.java
@@ -18,7 +18,7 @@
 
 import com.android.inputmethod.latin.makedict.FormatSpec;
 import com.android.inputmethod.latin.makedict.FusionDictionary;
-import com.android.inputmethod.latin.makedict.FusionDictionary.CharGroup;
+import com.android.inputmethod.latin.makedict.FusionDictionary.PtNode;
 import com.android.inputmethod.latin.makedict.FusionDictionary.WeightedString;
 import com.android.inputmethod.latin.makedict.Word;
 
@@ -65,20 +65,20 @@
 
     private static void showWordInfo(final FusionDictionary dict, final String word,
             final boolean plumbing) {
-        final CharGroup group = FusionDictionary.findWordInTree(dict.mRoot, word);
-        if (null == group) {
+        final PtNode ptNode = FusionDictionary.findWordInTree(dict.mRootNodeArray, word);
+        if (null == ptNode) {
             System.out.println(word + " is not in the dictionary");
             return;
         }
         System.out.println("Word: " + word);
-        System.out.println("  Freq: " + group.getFrequency());
-        if (group.getIsNotAWord()) {
+        System.out.println("  Freq: " + ptNode.getFrequency());
+        if (ptNode.getIsNotAWord()) {
             System.out.println("  Is not a word");
         }
-        if (group.getIsBlacklistEntry()) {
+        if (ptNode.getIsBlacklistEntry()) {
             System.out.println("  Is a blacklist entry");
         }
-        final ArrayList<WeightedString> shortcutTargets = group.getShortcutTargets();
+        final ArrayList<WeightedString> shortcutTargets = ptNode.getShortcutTargets();
         if (null == shortcutTargets || shortcutTargets.isEmpty()) {
             System.out.println("  No shortcuts");
         } else {
@@ -88,7 +88,7 @@
                                 ? "whitelist" : shortcutTarget.mFrequency) + ")");
             }
         }
-        final ArrayList<WeightedString> bigrams = group.getBigrams();
+        final ArrayList<WeightedString> bigrams = ptNode.getBigrams();
         if (null == bigrams || bigrams.isEmpty()) {
             System.out.println("  No bigrams");
         } else {
diff --git a/tools/dicttool/src/com/android/inputmethod/latin/dicttool/Test.java b/tools/dicttool/src/com/android/inputmethod/latin/dicttool/Test.java
index 972b6e7..9174238 100644
--- a/tools/dicttool/src/com/android/inputmethod/latin/dicttool/Test.java
+++ b/tools/dicttool/src/com/android/inputmethod/latin/dicttool/Test.java
@@ -16,9 +16,9 @@
 
 package com.android.inputmethod.latin.dicttool;
 
-import com.android.inputmethod.latin.makedict.BinaryDictIOTests;
+import com.android.inputmethod.latin.makedict.BinaryDictDecoderEncoderTests;
+import com.android.inputmethod.latin.makedict.BinaryDictEncoderFlattenTreeTests;
 import com.android.inputmethod.latin.makedict.BinaryDictIOUtilsTests;
-import com.android.inputmethod.latin.makedict.BinaryDictInputOutputTest;
 import com.android.inputmethod.latin.makedict.FusionDictionaryTest;
 
 import java.lang.reflect.Constructor;
@@ -37,9 +37,9 @@
     private static final Class<?>[] sClassesToTest = {
         BinaryDictOffdeviceUtilsTests.class,
         FusionDictionaryTest.class,
-        BinaryDictInputOutputTest.class,
-        BinaryDictIOUtilsTests.class,
-        BinaryDictIOTests.class
+        BinaryDictDecoderEncoderTests.class,
+        BinaryDictEncoderFlattenTreeTests.class,
+        BinaryDictIOUtilsTests.class
     };
     private ArrayList<Method> mAllTestMethods = new ArrayList<Method>();
     private ArrayList<String> mUsedTestMethods = new ArrayList<String>();
diff --git a/tools/dicttool/src/com/android/inputmethod/latin/dicttool/XmlDictInputOutput.java b/tools/dicttool/src/com/android/inputmethod/latin/dicttool/XmlDictInputOutput.java
index 1fd2cba..4e99bf9 100644
--- a/tools/dicttool/src/com/android/inputmethod/latin/dicttool/XmlDictInputOutput.java
+++ b/tools/dicttool/src/com/android/inputmethod/latin/dicttool/XmlDictInputOutput.java
@@ -18,7 +18,7 @@
 
 import com.android.inputmethod.latin.makedict.FusionDictionary;
 import com.android.inputmethod.latin.makedict.FusionDictionary.DictionaryOptions;
-import com.android.inputmethod.latin.makedict.FusionDictionary.Node;
+import com.android.inputmethod.latin.makedict.FusionDictionary.PtNodeArray;
 import com.android.inputmethod.latin.makedict.FusionDictionary.WeightedString;
 import com.android.inputmethod.latin.makedict.Word;
 
@@ -124,8 +124,8 @@
                         GERMAN_UMLAUT_PROCESSING_OPTION.equals(optionsString);
                 final boolean processLigatures =
                         FRENCH_LIGATURE_PROCESSING_OPTION.equals(optionsString);
-                mDictionary = new FusionDictionary(new Node(), new DictionaryOptions(attributes,
-                        processUmlauts, processLigatures));
+                mDictionary = new FusionDictionary(new PtNodeArray(),
+                        new DictionaryOptions(attributes, processUmlauts, processLigatures));
             } else {
                 mState = UNKNOWN;
             }
diff --git a/tools/dicttool/tests/com/android/inputmethod/latin/dicttool/BinaryDictOffdeviceUtilsTests.java b/tools/dicttool/tests/com/android/inputmethod/latin/dicttool/BinaryDictOffdeviceUtilsTests.java
index 554bd24..1eff497 100644
--- a/tools/dicttool/tests/com/android/inputmethod/latin/dicttool/BinaryDictOffdeviceUtilsTests.java
+++ b/tools/dicttool/tests/com/android/inputmethod/latin/dicttool/BinaryDictOffdeviceUtilsTests.java
@@ -16,35 +16,34 @@
 
 package com.android.inputmethod.latin.dicttool;
 
-import com.android.inputmethod.latin.makedict.BinaryDictInputOutput;
+import com.android.inputmethod.latin.makedict.DictDecoder;
+import com.android.inputmethod.latin.makedict.DictEncoder;
+import com.android.inputmethod.latin.makedict.FormatSpec;
 import com.android.inputmethod.latin.makedict.FormatSpec.FormatOptions;
 import com.android.inputmethod.latin.makedict.FusionDictionary;
 import com.android.inputmethod.latin.makedict.FusionDictionary.DictionaryOptions;
-import com.android.inputmethod.latin.makedict.FusionDictionary.Node;
+import com.android.inputmethod.latin.makedict.FusionDictionary.PtNodeArray;
 import com.android.inputmethod.latin.makedict.UnsupportedFormatException;
+import com.android.inputmethod.latin.makedict.Ver3DictEncoder;
 
 import junit.framework.TestCase;
 
 import java.io.File;
 import java.io.BufferedOutputStream;
-import java.io.FileInputStream;
 import java.io.FileOutputStream;
 import java.io.IOException;
 import java.io.OutputStream;
-import java.nio.ByteBuffer;
-import java.nio.channels.FileChannel;
-import java.util.ArrayList;
 import java.util.HashMap;
 
 /**
- * Unit tests for BinaryDictOffdeviceUtilsTests
+ * Unit tests for BinaryDictOffdeviceUtils
  */
 public class BinaryDictOffdeviceUtilsTests extends TestCase {
     private static final int TEST_FREQ = 37; // Some arbitrary value unlikely to happen by chance
 
     public void testGetRawDictWorks() throws IOException, UnsupportedFormatException {
         // Create a thrice-compressed dictionary file.
-        final FusionDictionary dict = new FusionDictionary(new Node(),
+        final FusionDictionary dict = new FusionDictionary(new PtNodeArray(),
                 new DictionaryOptions(new HashMap<String, String>(),
                         false /* germanUmlautProcessing */, false /* frenchLigatureProcessing */));
         dict.add("foo", TEST_FREQ, null, false /* isNotAWord */);
@@ -55,12 +54,13 @@
 
         final File dst = File.createTempFile("testGetRawDict", ".tmp");
         dst.deleteOnExit();
+
         final OutputStream out = Compress.getCompressedStream(
                 Compress.getCompressedStream(
                         Compress.getCompressedStream(
                                 new BufferedOutputStream(new FileOutputStream(dst)))));
-
-        BinaryDictInputOutput.writeDictionaryBinary(out, dict, new FormatOptions(2, false));
+        final DictEncoder dictEncoder = new Ver3DictEncoder(out);
+        dictEncoder.writeDictionary(dict, new FormatOptions(2, false));
 
         // Test for an actually compressed dictionary and its contents
         final BinaryDictOffdeviceUtils.DecoderChainSpec decodeSpec =
@@ -69,14 +69,13 @@
             assertEquals("Wrong decode spec", BinaryDictOffdeviceUtils.COMPRESSION, step);
         }
         assertEquals("Wrong decode spec", 3, decodeSpec.mDecoderSpec.size());
-        final FileInputStream inStream = new FileInputStream(decodeSpec.mFile);
-        final ByteBuffer buffer = inStream.getChannel().map(
-                FileChannel.MapMode.READ_ONLY, 0, decodeSpec.mFile.length());
-        final FusionDictionary resultDict = BinaryDictInputOutput.readDictionaryBinary(
-                new BinaryDictInputOutput.ByteBufferWrapper(buffer),
-                null /* dict : an optional dictionary to add words to, or null */);
+        final DictDecoder dictDecoder = FormatSpec.getDictDecoder(decodeSpec.mFile);
+        final FusionDictionary resultDict = dictDecoder.readDictionaryBinary(
+                null /* dict : an optional dictionary to add words to, or null */,
+                false /* deleteDictIfBroken */);
         assertEquals("Dictionary can't be read back correctly",
-                resultDict.findWordInTree(resultDict.mRoot, "foo").getFrequency(), TEST_FREQ);
+                FusionDictionary.findWordInTree(resultDict.mRootNodeArray, "foo").getFrequency(),
+                TEST_FREQ);
     }
 
     public void testGetRawDictFails() throws IOException {
diff --git a/tools/dicttool/tests/com/android/inputmethod/latin/makedict/BinaryDictInputOutputTest.java b/tools/dicttool/tests/com/android/inputmethod/latin/makedict/BinaryDictEncoderFlattenTreeTests.java
similarity index 78%
rename from tools/dicttool/tests/com/android/inputmethod/latin/makedict/BinaryDictInputOutputTest.java
rename to tools/dicttool/tests/com/android/inputmethod/latin/makedict/BinaryDictEncoderFlattenTreeTests.java
index 0969028..5505823 100644
--- a/tools/dicttool/tests/com/android/inputmethod/latin/makedict/BinaryDictInputOutputTest.java
+++ b/tools/dicttool/tests/com/android/inputmethod/latin/makedict/BinaryDictEncoderFlattenTreeTests.java
@@ -17,7 +17,7 @@
 package com.android.inputmethod.latin.makedict;
 
 import com.android.inputmethod.latin.makedict.FusionDictionary.DictionaryOptions;
-import com.android.inputmethod.latin.makedict.FusionDictionary.Node;
+import com.android.inputmethod.latin.makedict.FusionDictionary.PtNodeArray;
 
 import junit.framework.TestCase;
 
@@ -25,13 +25,13 @@
 import java.util.HashMap;
 
 /**
- * Unit tests for BinaryDictInputOutput.
+ * Unit tests for BinaryDictEncoderUtils.flattenTree().
  */
-public class BinaryDictInputOutputTest extends TestCase {
+public class BinaryDictEncoderFlattenTreeTests extends TestCase {
     // Test the flattened array contains the expected number of nodes, and
     // that it does not contain any duplicates.
     public void testFlattenNodes() {
-        final FusionDictionary dict = new FusionDictionary(new Node(),
+        final FusionDictionary dict = new FusionDictionary(new PtNodeArray(),
                 new DictionaryOptions(new HashMap<String, String>(),
                         false /* germanUmlautProcessing */, false /* frenchLigatureProcessing */));
         dict.add("foo", 1, null, false /* isNotAWord */);
@@ -39,10 +39,11 @@
         dict.add("ftb", 1, null, false /* isNotAWord */);
         dict.add("bar", 1, null, false /* isNotAWord */);
         dict.add("fool", 1, null, false /* isNotAWord */);
-        final ArrayList<Node> result = BinaryDictInputOutput.flattenTree(dict.mRoot);
+        final ArrayList<PtNodeArray> result =
+                BinaryDictEncoderUtils.flattenTree(dict.mRootNodeArray);
         assertEquals(4, result.size());
         while (!result.isEmpty()) {
-            final Node n = result.remove(0);
+            final PtNodeArray n = result.remove(0);
             assertFalse("Flattened array contained the same node twice", result.contains(n));
         }
     }
diff --git a/tools/dicttool/tests/com/android/inputmethod/latin/makedict/FusionDictionaryTest.java b/tools/dicttool/tests/com/android/inputmethod/latin/makedict/FusionDictionaryTest.java
index 7607113..659650a 100644
--- a/tools/dicttool/tests/com/android/inputmethod/latin/makedict/FusionDictionaryTest.java
+++ b/tools/dicttool/tests/com/android/inputmethod/latin/makedict/FusionDictionaryTest.java
@@ -17,9 +17,9 @@
 package com.android.inputmethod.latin.makedict;
 
 import com.android.inputmethod.latin.makedict.FusionDictionary;
-import com.android.inputmethod.latin.makedict.FusionDictionary.CharGroup;
+import com.android.inputmethod.latin.makedict.FusionDictionary.PtNode;
 import com.android.inputmethod.latin.makedict.FusionDictionary.DictionaryOptions;
-import com.android.inputmethod.latin.makedict.FusionDictionary.Node;
+import com.android.inputmethod.latin.makedict.FusionDictionary.PtNodeArray;
 import com.android.inputmethod.latin.makedict.Word;
 
 import junit.framework.TestCase;
@@ -29,7 +29,7 @@
 import java.util.Random;
 
 /**
- * Unit tests for BinaryDictInputOutput.
+ * Unit tests for FusionDictionary.
  */
 public class FusionDictionaryTest extends TestCase {
     private static final ArrayList<String> sWords = new ArrayList<String>();
@@ -72,8 +72,8 @@
         assertNotNull(dict);
         for (final String word : words) {
             if (--limit < 0) return;
-            final CharGroup cg = FusionDictionary.findWordInTree(dict.mRoot, word);
-            assertNotNull(cg);
+            final PtNode ptNode = FusionDictionary.findWordInTree(dict.mRootNodeArray, word);
+            assertNotNull(ptNode);
         }
     }
 
@@ -95,7 +95,7 @@
     // Test the flattened array contains the expected number of nodes, and
     // that it does not contain any duplicates.
     public void testFusion() {
-        final FusionDictionary dict = new FusionDictionary(new Node(),
+        final FusionDictionary dict = new FusionDictionary(new PtNodeArray(),
                 new DictionaryOptions(new HashMap<String, String>(),
                         false /* germanUmlautProcessing */, false /* frenchLigatureProcessing */));
         final long time = System.currentTimeMillis();
diff --git a/tools/dicttool/tests/etc/test-dicttool.sh b/tools/dicttool/tests/etc/test-dicttool.sh
index 0921207..5eb44fc 100755
--- a/tools/dicttool/tests/etc/test-dicttool.sh
+++ b/tools/dicttool/tests/etc/test-dicttool.sh
@@ -24,5 +24,5 @@
 find out -name "dicttool_aosp*" -exec rm -rf {} \; > /dev/null 2>&1
 mmm -j8 external/junit
 DICTTOOL_UNITTEST=true mmm -j8 packages/inputmethods/LatinIME/tools/dicttool
-java -classpath ${ANDROID_HOST_OUT}/framework/junit.jar:${ANDROID_HOST_OUT}/framework/dicttool_aosp.jar junit.textui.TestRunner com.android.inputmethod.latin.makedict.BinaryDictInputOutputTest
+java -classpath ${ANDROID_HOST_OUT}/framework/junit.jar:${ANDROID_HOST_OUT}/framework/dicttool_aosp.jar junit.textui.TestRunner com.android.inputmethod.latin.makedict.BinaryDictEncoderFlattenTreeTests
 java -classpath ${ANDROID_HOST_OUT}/framework/junit.jar:${ANDROID_HOST_OUT}/framework/dicttool_aosp.jar junit.textui.TestRunner com.android.inputmethod.latin.dicttool.BinaryDictOffdeviceUtilsTests
diff --git a/tools/maketext/Android.mk b/tools/make-keyboard-text/Android.mk
similarity index 95%
rename from tools/maketext/Android.mk
rename to tools/make-keyboard-text/Android.mk
index 77914ca..8760148 100644
--- a/tools/maketext/Android.mk
+++ b/tools/make-keyboard-text/Android.mk
@@ -19,7 +19,7 @@
 LOCAL_SRC_FILES += $(call all-java-files-under,src)
 LOCAL_JAR_MANIFEST := etc/manifest.txt
 LOCAL_JAVA_RESOURCE_DIRS := res
-LOCAL_MODULE := maketext
+LOCAL_MODULE := make-keyboard-text
 
 include $(BUILD_HOST_JAVA_LIBRARY)
 include $(LOCAL_PATH)/etc/Android.mk
diff --git a/tools/maketext/etc/Android.mk b/tools/make-keyboard-text/etc/Android.mk
similarity index 93%
rename from tools/maketext/etc/Android.mk
rename to tools/make-keyboard-text/etc/Android.mk
index 475676b..0fbf4ff 100644
--- a/tools/maketext/etc/Android.mk
+++ b/tools/make-keyboard-text/etc/Android.mk
@@ -15,6 +15,6 @@
 LOCAL_PATH := $(call my-dir)
 include $(CLEAR_VARS)
 
-LOCAL_PREBUILT_EXECUTABLES := maketext
+LOCAL_PREBUILT_EXECUTABLES := make-keyboard-text
 
 include $(BUILD_HOST_PREBUILT)
diff --git a/tools/maketext/etc/maketext b/tools/make-keyboard-text/etc/make-keyboard-text
similarity index 97%
rename from tools/maketext/etc/maketext
rename to tools/make-keyboard-text/etc/make-keyboard-text
index 0edd360..156f9ec 100755
--- a/tools/maketext/etc/maketext
+++ b/tools/make-keyboard-text/etc/make-keyboard-text
@@ -33,7 +33,7 @@
 prog="${progdir}"/`basename "${prog}"`
 cd "${oldwd}"
 
-jarfile=maketext.jar
+jarfile=make-keyboard-text.jar
 frameworkdir="$progdir"
 if [ ! -r "$frameworkdir/$jarfile" ]
 then
diff --git a/tools/make-keyboard-text/etc/manifest.txt b/tools/make-keyboard-text/etc/manifest.txt
new file mode 100644
index 0000000..8ad4db0
--- /dev/null
+++ b/tools/make-keyboard-text/etc/manifest.txt
@@ -0,0 +1 @@
+Main-Class: com.android.inputmethod.keyboard.tools.MakeKeyboardText
diff --git a/tools/maketext/res/com/android/inputmethod/keyboard/internal/KeyboardTextsSet.tmpl b/tools/make-keyboard-text/res/com/android/inputmethod/keyboard/internal/KeyboardTextsSet.tmpl
similarity index 91%
rename from tools/maketext/res/com/android/inputmethod/keyboard/internal/KeyboardTextsSet.tmpl
rename to tools/make-keyboard-text/res/com/android/inputmethod/keyboard/internal/KeyboardTextsSet.tmpl
index 479a766..4cd9c23 100644
--- a/tools/maketext/res/com/android/inputmethod/keyboard/internal/KeyboardTextsSet.tmpl
+++ b/tools/make-keyboard-text/res/com/android/inputmethod/keyboard/internal/KeyboardTextsSet.tmpl
@@ -27,18 +27,18 @@
 /**
  * !!!!! DO NOT EDIT THIS FILE !!!!!
  *
- * This file is generated by tools/maketext. The base template file is
- *   tools/maketext/res/com/android/inputmethod/keyboard/internal/KeyboardTextsSet.tmpl
+ * This file is generated by tools/make-keyboard-text. The base template file is
+ *   tools/make-keyboard-text/res/com/android/inputmethod/keyboard/internal/KeyboardTextsSet.tmpl
  *
  * This file must be updated when any text resources in keyboard layout files have been changed.
  * These text resources are referred as "!text/<resource_name>" in keyboard XML definitions,
  * and should be defined in
- *   tools/maketext/res/values-<locale>/donottranslate-more-keys.xml
+ *   tools/make-keyboard-text/res/values-<locale>/donottranslate-more-keys.xml
  *
  * To update this file, please run the following commands.
  *   $ cd $ANDROID_BUILD_TOP
- *   $ mmm packages/inputmethods/LatinIME/tools/maketext
- *   $ maketext -java packages/inputmethods/LatinIME/java/src
+ *   $ mmm packages/inputmethods/LatinIME/tools/make-keyboard-text
+ *   $ make-keyboard-text -java packages/inputmethods/LatinIME/java/src
  *
  * The updated source file will be generated to the following path (this file).
  *   packages/inputmethods/LatinIME/java/src/com/android/inputmethod/keyboard/internal/
diff --git a/tools/maketext/res/values-af/donottranslate-more-keys.xml b/tools/make-keyboard-text/res/values-af/donottranslate-more-keys.xml
similarity index 100%
rename from tools/maketext/res/values-af/donottranslate-more-keys.xml
rename to tools/make-keyboard-text/res/values-af/donottranslate-more-keys.xml
diff --git a/tools/maketext/res/values-ar/donottranslate-more-keys.xml b/tools/make-keyboard-text/res/values-ar/donottranslate-more-keys.xml
similarity index 98%
rename from tools/maketext/res/values-ar/donottranslate-more-keys.xml
rename to tools/make-keyboard-text/res/values-ar/donottranslate-more-keys.xml
index cace240..8b86b1b 100644
--- a/tools/maketext/res/values-ar/donottranslate-more-keys.xml
+++ b/tools/make-keyboard-text/res/values-ar/donottranslate-more-keys.xml
@@ -81,7 +81,7 @@
     <!-- U+061F: "؟" ARABIC QUESTION MARK
          U+060C: "،" ARABIC COMMA
          U+061B: "؛" ARABIC SEMICOLON -->
-    <string name="more_keys_for_punctuation">"!fixedColumnOrder!8,\",\',#,-,:,!,&#x060C;,&#x061F;,\@,&amp;,\\%,+,&#x061B;,/,(,)"</string>
+    <string name="more_keys_for_punctuation">"!fixedColumnOrder!8,\",\',#,-,:,!,&#x060C;,&#x061F;,\@,&amp;,\\%,+,&#x061B;,/,(|),)|("</string>
     <string name="more_keys_for_apostrophe">"&#x061F;,&#x061B;,!,:,-,/,\',\""</string>
     <!-- U+266A: "♪" EIGHTH NOTE -->
     <string name="more_keys_for_bullet">&#x266A;</string>
diff --git a/tools/maketext/res/values-az/donottranslate-more-keys.xml b/tools/make-keyboard-text/res/values-az/donottranslate-more-keys.xml
similarity index 100%
rename from tools/maketext/res/values-az/donottranslate-more-keys.xml
rename to tools/make-keyboard-text/res/values-az/donottranslate-more-keys.xml
diff --git a/tools/maketext/res/values-be/donottranslate-more-keys.xml b/tools/make-keyboard-text/res/values-be/donottranslate-more-keys.xml
similarity index 100%
rename from tools/maketext/res/values-be/donottranslate-more-keys.xml
rename to tools/make-keyboard-text/res/values-be/donottranslate-more-keys.xml
diff --git a/tools/maketext/res/values-bg/donottranslate-more-keys.xml b/tools/make-keyboard-text/res/values-bg/donottranslate-more-keys.xml
similarity index 100%
rename from tools/maketext/res/values-bg/donottranslate-more-keys.xml
rename to tools/make-keyboard-text/res/values-bg/donottranslate-more-keys.xml
diff --git a/tools/maketext/res/values-ca/donottranslate-more-keys.xml b/tools/make-keyboard-text/res/values-ca/donottranslate-more-keys.xml
similarity index 98%
rename from tools/maketext/res/values-ca/donottranslate-more-keys.xml
rename to tools/make-keyboard-text/res/values-ca/donottranslate-more-keys.xml
index 8624dfb..9728c99 100644
--- a/tools/maketext/res/values-ca/donottranslate-more-keys.xml
+++ b/tools/make-keyboard-text/res/values-ca/donottranslate-more-keys.xml
@@ -71,7 +71,7 @@
          U+0142: "ł" LATIN SMALL LETTER L WITH STROKE -->
     <string name="more_keys_for_l">l&#x00B7;l,&#x0142;</string>
     <!-- U+00B7: "·" MIDDLE DOT -->
-    <string name="more_keys_for_punctuation">"!fixedColumnOrder!9,&#x00B7;,\",\',#,-,:,!,\\,,\?,\@,&amp;,\\%,+,;,/,(,)"</string>
+    <string name="more_keys_for_punctuation">"!fixedColumnOrder!4,&#x00B7;,!,\\,,\?,:,;,\@"</string>
     <string name="more_keys_for_tablet_period">\?,&#x00B7;</string>
     <!-- U+00E7: "ç" LATIN SMALL LETTER C WITH CEDILLA -->
     <string name="keylabel_for_spanish_row2_10">&#x00E7;</string>
diff --git a/tools/maketext/res/values-cs/donottranslate-more-keys.xml b/tools/make-keyboard-text/res/values-cs/donottranslate-more-keys.xml
similarity index 100%
rename from tools/maketext/res/values-cs/donottranslate-more-keys.xml
rename to tools/make-keyboard-text/res/values-cs/donottranslate-more-keys.xml
diff --git a/tools/maketext/res/values-da/donottranslate-more-keys.xml b/tools/make-keyboard-text/res/values-da/donottranslate-more-keys.xml
similarity index 100%
rename from tools/maketext/res/values-da/donottranslate-more-keys.xml
rename to tools/make-keyboard-text/res/values-da/donottranslate-more-keys.xml
diff --git a/tools/maketext/res/values-de/donottranslate-more-keys.xml b/tools/make-keyboard-text/res/values-de/donottranslate-more-keys.xml
similarity index 100%
rename from tools/maketext/res/values-de/donottranslate-more-keys.xml
rename to tools/make-keyboard-text/res/values-de/donottranslate-more-keys.xml
diff --git a/tools/maketext/res/values-el/donottranslate-more-keys.xml b/tools/make-keyboard-text/res/values-el/donottranslate-more-keys.xml
similarity index 100%
rename from tools/maketext/res/values-el/donottranslate-more-keys.xml
rename to tools/make-keyboard-text/res/values-el/donottranslate-more-keys.xml
diff --git a/tools/maketext/res/values-en/donottranslate-more-keys.xml b/tools/make-keyboard-text/res/values-en/donottranslate-more-keys.xml
similarity index 100%
rename from tools/maketext/res/values-en/donottranslate-more-keys.xml
rename to tools/make-keyboard-text/res/values-en/donottranslate-more-keys.xml
diff --git a/tools/maketext/res/values-eo/donottranslate-more-keys.xml b/tools/make-keyboard-text/res/values-eo/donottranslate-more-keys.xml
similarity index 100%
rename from tools/maketext/res/values-eo/donottranslate-more-keys.xml
rename to tools/make-keyboard-text/res/values-eo/donottranslate-more-keys.xml
diff --git a/tools/maketext/res/values-es/donottranslate-more-keys.xml b/tools/make-keyboard-text/res/values-es/donottranslate-more-keys.xml
similarity index 98%
rename from tools/maketext/res/values-es/donottranslate-more-keys.xml
rename to tools/make-keyboard-text/res/values-es/donottranslate-more-keys.xml
index 0e58c14..8494296 100644
--- a/tools/maketext/res/values-es/donottranslate-more-keys.xml
+++ b/tools/make-keyboard-text/res/values-es/donottranslate-more-keys.xml
@@ -71,7 +71,7 @@
     <string name="keylabel_for_spanish_row2_10">&#x00F1;</string>
     <!-- U+00A1: "¡" INVERTED EXCLAMATION MARK
          U+00BF: "¿" INVERTED QUESTION MARK -->
-    <string name="more_keys_for_punctuation">"!fixedColumnOrder!9,&#x00A1;,\",\',#,-,:,!,\\,,\?,&#x00BF;,\@,&amp;,\\%,+,;,/,(,)"</string>
+    <string name="more_keys_for_punctuation">"!fixedColumnOrder!4,;,!,\\,,\?,:,&#x00A1;,\@,&#x00BF;"</string>
     <!-- U+00A1: "¡" INVERTED EXCLAMATION MARK -->
     <string name="more_keys_for_tablet_comma">"!,&#x00A1;"</string>
     <!-- U+00BF: "¿" INVERTED QUESTION MARK -->
diff --git a/tools/maketext/res/values-et/donottranslate-more-keys.xml b/tools/make-keyboard-text/res/values-et/donottranslate-more-keys.xml
similarity index 100%
rename from tools/maketext/res/values-et/donottranslate-more-keys.xml
rename to tools/make-keyboard-text/res/values-et/donottranslate-more-keys.xml
diff --git a/tools/maketext/res/values-fa/donottranslate-more-keys.xml b/tools/make-keyboard-text/res/values-fa/donottranslate-more-keys.xml
similarity index 97%
rename from tools/maketext/res/values-fa/donottranslate-more-keys.xml
rename to tools/make-keyboard-text/res/values-fa/donottranslate-more-keys.xml
index 7c8496d..5a03c80 100644
--- a/tools/maketext/res/values-fa/donottranslate-more-keys.xml
+++ b/tools/make-keyboard-text/res/values-fa/donottranslate-more-keys.xml
@@ -86,10 +86,12 @@
     <string name="keylabel_for_apostrophe">&#x060C;</string>
     <string name="keyhintlabel_for_apostrophe">&#x061F;</string>
     <string name="more_keys_for_apostrophe">"!fixedColumnOrder!4,:,!,&#x061F;,&#x061B;,-,/,&#x00AB;|&#x00BB;,&#x00BB;|&#x00AB;"</string>
+    <!-- U+FDFC: "﷼" RIAL SIGN -->
+    <string name="keylabel_for_currency">&#xFDFC;</string>
     <!-- U+061F: "؟" ARABIC QUESTION MARK
          U+060C: "،" ARABIC COMMA
          U+061B: "؛" ARABIC SEMICOLON -->
-    <string name="more_keys_for_punctuation">"!fixedColumnOrder!8,\",\',#,-,:,!,&#x060C;,&#x061F;,\@,&amp;,\\%,+,&#x061B;,/,(,)"</string>
+    <string name="more_keys_for_punctuation">"!fixedColumnOrder!8,\",\',#,-,:,!,&#x060C;,&#x061F;,\@,&amp;,\\%,+,&#x061B;,/,(|),)|("</string>
     <!-- U+266A: "♪" EIGHTH NOTE -->
     <string name="more_keys_for_bullet">&#x266A;</string>
     <!-- U+2605: "★" BLACK STAR
diff --git a/tools/maketext/res/values-fi/donottranslate-more-keys.xml b/tools/make-keyboard-text/res/values-fi/donottranslate-more-keys.xml
similarity index 100%
rename from tools/maketext/res/values-fi/donottranslate-more-keys.xml
rename to tools/make-keyboard-text/res/values-fi/donottranslate-more-keys.xml
diff --git a/tools/maketext/res/values-fr/donottranslate-more-keys.xml b/tools/make-keyboard-text/res/values-fr/donottranslate-more-keys.xml
similarity index 100%
rename from tools/maketext/res/values-fr/donottranslate-more-keys.xml
rename to tools/make-keyboard-text/res/values-fr/donottranslate-more-keys.xml
diff --git a/tools/maketext/res/values-hi/donottranslate-more-keys.xml b/tools/make-keyboard-text/res/values-hi/donottranslate-more-keys.xml
similarity index 97%
rename from tools/maketext/res/values-hi/donottranslate-more-keys.xml
rename to tools/make-keyboard-text/res/values-hi/donottranslate-more-keys.xml
index 98ad2cb..b0d010f 100644
--- a/tools/maketext/res/values-hi/donottranslate-more-keys.xml
+++ b/tools/make-keyboard-text/res/values-hi/donottranslate-more-keys.xml
@@ -59,5 +59,5 @@
     <string name="additional_more_keys_for_symbols_9">9</string>
     <string name="additional_more_keys_for_symbols_0">0</string>
     <!-- U+20B9: "₹" INDIAN RUPEE SIGN -->
-    <string name="keylabel_for_currency_generic">&#x20B9;</string>
+    <string name="keylabel_for_currency">&#x20B9;</string>
 </resources>
diff --git a/tools/maketext/res/values-hr/donottranslate-more-keys.xml b/tools/make-keyboard-text/res/values-hr/donottranslate-more-keys.xml
similarity index 100%
rename from tools/maketext/res/values-hr/donottranslate-more-keys.xml
rename to tools/make-keyboard-text/res/values-hr/donottranslate-more-keys.xml
diff --git a/tools/maketext/res/values-hu/donottranslate-more-keys.xml b/tools/make-keyboard-text/res/values-hu/donottranslate-more-keys.xml
similarity index 100%
rename from tools/maketext/res/values-hu/donottranslate-more-keys.xml
rename to tools/make-keyboard-text/res/values-hu/donottranslate-more-keys.xml
diff --git a/tools/make-keyboard-text/res/values-hy/donottranslate-more-keys.xml b/tools/make-keyboard-text/res/values-hy/donottranslate-more-keys.xml
new file mode 100644
index 0000000..2f34128
--- /dev/null
+++ b/tools/make-keyboard-text/res/values-hy/donottranslate-more-keys.xml
@@ -0,0 +1,40 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+**
+** Copyright 2013, 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">
+    <!-- U+058A: "֊" ARMENIAN HYPHEN -->
+    <!-- U+055C: "՜" ARMENIAN EXCLAMATION MARK -->
+    <!-- U+055D: "՝" ARMENIAN COMMA -->
+    <!-- U+055E: "՞" ARMENIAN QUESTION MARK -->
+    <!-- U+0559: "ՙ" ARMENIAN MODIFIER LETTER LEFT HALF RING -->
+    <!-- U+055A: "՚" ARMENIAN APOSTROPHE -->
+    <!-- U+055B: "՛" ARMENIAN EMPHASIS MARK -->
+    <!-- U+055F: "՟" ARMENIAN ABBREVIATION MARK -->
+    <string name="more_keys_for_punctuation">"!fixedColumnOrder!8,!,?,\\,,.,&#x058A;,&#x055C;,&#x055D;,&#x055E;,:,;,\@,&#x0559;,&#x055A;,&#x055B;,&#x055F;"</string>
+    <!-- U+055E: "՞" ARMENIAN QUESTION MARK -->
+    <!-- U+00BF: "¿" INVERTED QUESTION MARK -->
+    <string name="more_keys_for_symbols_question">&#x055E;,&#x00BF;</string>
+    <!-- U+055C: "՜" ARMENIAN EXCLAMATION MARK -->
+    <!-- U+00A1: "¡" INVERTED EXCLAMATION MARK -->
+    <string name="more_keys_for_symbols_exclamation">&#x055C;,&#x00A1;</string>
+    <!-- U+058F: "֏" ARMENIAN DRAM SIGN -->
+    <!-- TODO: Enable this when we have glyph for the following letter
+         <string name="keylabel_for_currency">&#x058F;</string>
+    -->
+</resources>
diff --git a/tools/maketext/res/values-is/donottranslate-more-keys.xml b/tools/make-keyboard-text/res/values-is/donottranslate-more-keys.xml
similarity index 100%
rename from tools/maketext/res/values-is/donottranslate-more-keys.xml
rename to tools/make-keyboard-text/res/values-is/donottranslate-more-keys.xml
diff --git a/tools/maketext/res/values-it/donottranslate-more-keys.xml b/tools/make-keyboard-text/res/values-it/donottranslate-more-keys.xml
similarity index 100%
rename from tools/maketext/res/values-it/donottranslate-more-keys.xml
rename to tools/make-keyboard-text/res/values-it/donottranslate-more-keys.xml
diff --git a/tools/maketext/res/values-iw/donottranslate-more-keys.xml b/tools/make-keyboard-text/res/values-iw/donottranslate-more-keys.xml
similarity index 89%
rename from tools/maketext/res/values-iw/donottranslate-more-keys.xml
rename to tools/make-keyboard-text/res/values-iw/donottranslate-more-keys.xml
index 64d4227..feaed4c 100644
--- a/tools/maketext/res/values-iw/donottranslate-more-keys.xml
+++ b/tools/make-keyboard-text/res/values-iw/donottranslate-more-keys.xml
@@ -51,4 +51,10 @@
     <string name="double_quotes">&#x201C;,&#x201D;,&#x201E;</string>
     <string name="single_angle_quotes">!text/single_laqm_raqm_rtl</string>
     <string name="double_angle_quotes">!text/double_laqm_raqm_rtl</string>
+    <!-- U+20AA: "₪" NEW SHEQEL SIGN -->
+    <string name="keylabel_for_currency">&#x20AA;</string>
+    <string name="keyhintlabel_for_tablet_comma">!</string>
+    <string name="more_keys_for_tablet_comma">!</string>
+    <string name="keyhintlabel_for_tablet_period">\?</string>
+    <string name="more_keys_for_tablet_period">\?</string>
 </resources>
diff --git a/tools/maketext/res/values-ka/donottranslate-more-keys.xml b/tools/make-keyboard-text/res/values-ka/donottranslate-more-keys.xml
similarity index 100%
rename from tools/maketext/res/values-ka/donottranslate-more-keys.xml
rename to tools/make-keyboard-text/res/values-ka/donottranslate-more-keys.xml
diff --git a/tools/maketext/res/values-kk/donottranslate-more-keys.xml b/tools/make-keyboard-text/res/values-kk/donottranslate-more-keys.xml
similarity index 100%
rename from tools/maketext/res/values-kk/donottranslate-more-keys.xml
rename to tools/make-keyboard-text/res/values-kk/donottranslate-more-keys.xml
diff --git a/tools/maketext/res/values-mn/donottranslate-more-keys.xml b/tools/make-keyboard-text/res/values-km/donottranslate-more-keys.xml
similarity index 64%
copy from tools/maketext/res/values-mn/donottranslate-more-keys.xml
copy to tools/make-keyboard-text/res/values-km/donottranslate-more-keys.xml
index fd1853e..c33831c 100644
--- a/tools/maketext/res/values-mn/donottranslate-more-keys.xml
+++ b/tools/make-keyboard-text/res/values-km/donottranslate-more-keys.xml
@@ -2,7 +2,7 @@
 <!--
 /*
 **
-** Copyright 2012, The Android Open Source Project
+** Copyright 2013, 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.
@@ -19,10 +19,11 @@
 -->
 <resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <!-- Label for "switch to alphabetic" key.
-         U+0410: "А" CYRILLIC CAPITAL LETTER A
-         U+0411: "Б" CYRILLIC CAPITAL LETTER BE
-         U+0412: "В" CYRILLIC CAPITAL LETTER VE -->
-    <string name="label_to_alpha_key">&#x0410;&#x0411;&#x0412;</string>
-    <!-- U+20AE: "₮" TUGRIK SIGN -->
-    <string name="keylabel_for_currency_generic">&#x20AE;</string>
+         U+1780: "ក" KHMER LETTER KA
+         U+1781: "ខ" KHMER LETTER KHA
+         U+1782: "គ" KHMER LETTER KO -->
+    <string name="label_to_alpha_key">&#x1780;&#x1781;&#x1782;</string>
+    <!-- U+17DB: "៛" KHMER CURRENCY SYMBOL RIEL -->
+    <string name="more_keys_for_currency_dollar">&#x17DB;,&#x00A2;,&#x00A3;,&#x20AC;,&#x00A5;,&#x20B1;</string>
+
 </resources>
diff --git a/tools/maketext/res/values-ky/donottranslate-more-keys.xml b/tools/make-keyboard-text/res/values-ky/donottranslate-more-keys.xml
similarity index 100%
rename from tools/maketext/res/values-ky/donottranslate-more-keys.xml
rename to tools/make-keyboard-text/res/values-ky/donottranslate-more-keys.xml
diff --git a/tools/maketext/res/values-mn/donottranslate-more-keys.xml b/tools/make-keyboard-text/res/values-lo/donottranslate-more-keys.xml
similarity index 66%
copy from tools/maketext/res/values-mn/donottranslate-more-keys.xml
copy to tools/make-keyboard-text/res/values-lo/donottranslate-more-keys.xml
index fd1853e..1d8ffa8 100644
--- a/tools/maketext/res/values-mn/donottranslate-more-keys.xml
+++ b/tools/make-keyboard-text/res/values-lo/donottranslate-more-keys.xml
@@ -2,7 +2,7 @@
 <!--
 /*
 **
-** Copyright 2012, The Android Open Source Project
+** Copyright 2013, 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.
@@ -19,10 +19,10 @@
 -->
 <resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <!-- Label for "switch to alphabetic" key.
-         U+0410: "А" CYRILLIC CAPITAL LETTER A
-         U+0411: "Б" CYRILLIC CAPITAL LETTER BE
-         U+0412: "В" CYRILLIC CAPITAL LETTER VE -->
-    <string name="label_to_alpha_key">&#x0410;&#x0411;&#x0412;</string>
-    <!-- U+20AE: "₮" TUGRIK SIGN -->
-    <string name="keylabel_for_currency_generic">&#x20AE;</string>
+         U+0E81: "ກ" LAO LETTER KO
+         U+0E82: "ຂ" LAO LETTER KHO SUNG
+         U+0E84: "ຄ" LAO LETTER KHO TAM -->
+    <string name="label_to_alpha_key">&#x0E81;&#x0E82;&#x0E84;</string>
+    <!-- U+20AD: "₭" KIP SIGN -->
+    <string name="keylabel_for_currency">&#x20AD;</string>
 </resources>
diff --git a/tools/maketext/res/values-lt/donottranslate-more-keys.xml b/tools/make-keyboard-text/res/values-lt/donottranslate-more-keys.xml
similarity index 100%
rename from tools/maketext/res/values-lt/donottranslate-more-keys.xml
rename to tools/make-keyboard-text/res/values-lt/donottranslate-more-keys.xml
diff --git a/tools/maketext/res/values-lv/donottranslate-more-keys.xml b/tools/make-keyboard-text/res/values-lv/donottranslate-more-keys.xml
similarity index 100%
rename from tools/maketext/res/values-lv/donottranslate-more-keys.xml
rename to tools/make-keyboard-text/res/values-lv/donottranslate-more-keys.xml
diff --git a/tools/maketext/res/values-mk/donottranslate-more-keys.xml b/tools/make-keyboard-text/res/values-mk/donottranslate-more-keys.xml
similarity index 100%
rename from tools/maketext/res/values-mk/donottranslate-more-keys.xml
rename to tools/make-keyboard-text/res/values-mk/donottranslate-more-keys.xml
diff --git a/tools/maketext/res/values-mn/donottranslate-more-keys.xml b/tools/make-keyboard-text/res/values-mn/donottranslate-more-keys.xml
similarity index 93%
rename from tools/maketext/res/values-mn/donottranslate-more-keys.xml
rename to tools/make-keyboard-text/res/values-mn/donottranslate-more-keys.xml
index fd1853e..a7f3666 100644
--- a/tools/maketext/res/values-mn/donottranslate-more-keys.xml
+++ b/tools/make-keyboard-text/res/values-mn/donottranslate-more-keys.xml
@@ -24,5 +24,5 @@
          U+0412: "В" CYRILLIC CAPITAL LETTER VE -->
     <string name="label_to_alpha_key">&#x0410;&#x0411;&#x0412;</string>
     <!-- U+20AE: "₮" TUGRIK SIGN -->
-    <string name="keylabel_for_currency_generic">&#x20AE;</string>
+    <string name="keylabel_for_currency">&#x20AE;</string>
 </resources>
diff --git a/tools/maketext/res/values-nb/donottranslate-more-keys.xml b/tools/make-keyboard-text/res/values-nb/donottranslate-more-keys.xml
similarity index 100%
rename from tools/maketext/res/values-nb/donottranslate-more-keys.xml
rename to tools/make-keyboard-text/res/values-nb/donottranslate-more-keys.xml
diff --git a/tools/maketext/res/values-hi/donottranslate-more-keys.xml b/tools/make-keyboard-text/res/values-ne/donottranslate-more-keys.xml
similarity index 93%
copy from tools/maketext/res/values-hi/donottranslate-more-keys.xml
copy to tools/make-keyboard-text/res/values-ne/donottranslate-more-keys.xml
index 98ad2cb..9205e53 100644
--- a/tools/maketext/res/values-hi/donottranslate-more-keys.xml
+++ b/tools/make-keyboard-text/res/values-ne/donottranslate-more-keys.xml
@@ -2,7 +2,7 @@
 <!--
 /*
 **
-** Copyright 2012, The Android Open Source Project
+** Copyright 2013, 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.
@@ -58,6 +58,6 @@
     <string name="additional_more_keys_for_symbols_8">8</string>
     <string name="additional_more_keys_for_symbols_9">9</string>
     <string name="additional_more_keys_for_symbols_0">0</string>
-    <!-- U+20B9: "₹" INDIAN RUPEE SIGN -->
-    <string name="keylabel_for_currency_generic">&#x20B9;</string>
+    <!-- U+0930/U+0941/U+002E "रु." NEPALESE RUPEE SIGN -->
+    <string name="keylabel_for_currency">&#x0930;&#x0941;&#x002E;</string>
 </resources>
diff --git a/tools/maketext/res/values-nl/donottranslate-more-keys.xml b/tools/make-keyboard-text/res/values-nl/donottranslate-more-keys.xml
similarity index 100%
rename from tools/maketext/res/values-nl/donottranslate-more-keys.xml
rename to tools/make-keyboard-text/res/values-nl/donottranslate-more-keys.xml
diff --git a/tools/maketext/res/values-pl/donottranslate-more-keys.xml b/tools/make-keyboard-text/res/values-pl/donottranslate-more-keys.xml
similarity index 100%
rename from tools/maketext/res/values-pl/donottranslate-more-keys.xml
rename to tools/make-keyboard-text/res/values-pl/donottranslate-more-keys.xml
diff --git a/tools/maketext/res/values-pt/donottranslate-more-keys.xml b/tools/make-keyboard-text/res/values-pt/donottranslate-more-keys.xml
similarity index 100%
rename from tools/maketext/res/values-pt/donottranslate-more-keys.xml
rename to tools/make-keyboard-text/res/values-pt/donottranslate-more-keys.xml
diff --git a/tools/maketext/res/values-rm/donottranslate-more-keys.xml b/tools/make-keyboard-text/res/values-rm/donottranslate-more-keys.xml
similarity index 100%
rename from tools/maketext/res/values-rm/donottranslate-more-keys.xml
rename to tools/make-keyboard-text/res/values-rm/donottranslate-more-keys.xml
diff --git a/tools/maketext/res/values-ro/donottranslate-more-keys.xml b/tools/make-keyboard-text/res/values-ro/donottranslate-more-keys.xml
similarity index 100%
rename from tools/maketext/res/values-ro/donottranslate-more-keys.xml
rename to tools/make-keyboard-text/res/values-ro/donottranslate-more-keys.xml
diff --git a/tools/maketext/res/values-ru/donottranslate-more-keys.xml b/tools/make-keyboard-text/res/values-ru/donottranslate-more-keys.xml
similarity index 100%
rename from tools/maketext/res/values-ru/donottranslate-more-keys.xml
rename to tools/make-keyboard-text/res/values-ru/donottranslate-more-keys.xml
diff --git a/tools/maketext/res/values-sk/donottranslate-more-keys.xml b/tools/make-keyboard-text/res/values-sk/donottranslate-more-keys.xml
similarity index 100%
rename from tools/maketext/res/values-sk/donottranslate-more-keys.xml
rename to tools/make-keyboard-text/res/values-sk/donottranslate-more-keys.xml
diff --git a/tools/maketext/res/values-sl/donottranslate-more-keys.xml b/tools/make-keyboard-text/res/values-sl/donottranslate-more-keys.xml
similarity index 100%
rename from tools/maketext/res/values-sl/donottranslate-more-keys.xml
rename to tools/make-keyboard-text/res/values-sl/donottranslate-more-keys.xml
diff --git a/tools/maketext/res/values-sr/donottranslate-more-keys.xml b/tools/make-keyboard-text/res/values-sr/donottranslate-more-keys.xml
similarity index 100%
rename from tools/maketext/res/values-sr/donottranslate-more-keys.xml
rename to tools/make-keyboard-text/res/values-sr/donottranslate-more-keys.xml
diff --git a/tools/maketext/res/values-sv/donottranslate-more-keys.xml b/tools/make-keyboard-text/res/values-sv/donottranslate-more-keys.xml
similarity index 100%
rename from tools/maketext/res/values-sv/donottranslate-more-keys.xml
rename to tools/make-keyboard-text/res/values-sv/donottranslate-more-keys.xml
diff --git a/tools/maketext/res/values-sw/donottranslate-more-keys.xml b/tools/make-keyboard-text/res/values-sw/donottranslate-more-keys.xml
similarity index 100%
rename from tools/maketext/res/values-sw/donottranslate-more-keys.xml
rename to tools/make-keyboard-text/res/values-sw/donottranslate-more-keys.xml
diff --git a/tools/maketext/res/values-th/donottranslate-more-keys.xml b/tools/make-keyboard-text/res/values-th/donottranslate-more-keys.xml
similarity index 93%
rename from tools/maketext/res/values-th/donottranslate-more-keys.xml
rename to tools/make-keyboard-text/res/values-th/donottranslate-more-keys.xml
index 6350d4b..070c915 100644
--- a/tools/maketext/res/values-th/donottranslate-more-keys.xml
+++ b/tools/make-keyboard-text/res/values-th/donottranslate-more-keys.xml
@@ -24,5 +24,5 @@
          U+0E04: "ค" THAI CHARACTER KHO KHWAI -->
     <string name="label_to_alpha_key">&#x0E01;&#x0E02;&#x0E04;</string>
     <!-- U+0E3F: "฿" THAI CURRENCY SYMBOL BAHT -->
-    <string name="keylabel_for_currency_generic">&#x0E3F;</string>
+    <string name="keylabel_for_currency">&#x0E3F;</string>
 </resources>
diff --git a/tools/maketext/res/values-tl/donottranslate-more-keys.xml b/tools/make-keyboard-text/res/values-tl/donottranslate-more-keys.xml
similarity index 100%
rename from tools/maketext/res/values-tl/donottranslate-more-keys.xml
rename to tools/make-keyboard-text/res/values-tl/donottranslate-more-keys.xml
diff --git a/tools/maketext/res/values-tr/donottranslate-more-keys.xml b/tools/make-keyboard-text/res/values-tr/donottranslate-more-keys.xml
similarity index 100%
rename from tools/maketext/res/values-tr/donottranslate-more-keys.xml
rename to tools/make-keyboard-text/res/values-tr/donottranslate-more-keys.xml
diff --git a/tools/maketext/res/values-uk/donottranslate-more-keys.xml b/tools/make-keyboard-text/res/values-uk/donottranslate-more-keys.xml
similarity index 96%
rename from tools/maketext/res/values-uk/donottranslate-more-keys.xml
rename to tools/make-keyboard-text/res/values-uk/donottranslate-more-keys.xml
index cc05cc6..6ee34e3 100644
--- a/tools/maketext/res/values-uk/donottranslate-more-keys.xml
+++ b/tools/make-keyboard-text/res/values-uk/donottranslate-more-keys.xml
@@ -35,7 +35,7 @@
     <!-- U+044A: "ъ" CYRILLIC SMALL LETTER HARD SIGN -->
     <string name="more_keys_for_cyrillic_soft_sign">&#x044A;</string>
     <!-- U+20B4: "₴" HRYVNIA SIGN -->
-    <string name="keylabel_for_currency_generic">&#x20B4;</string>
+    <string name="keylabel_for_currency">&#x20B4;</string>
     <!-- Label for "switch to alphabetic" key.
          U+0410: "А" CYRILLIC CAPITAL LETTER A
          U+0411: "Б" CYRILLIC CAPITAL LETTER BE
diff --git a/tools/maketext/res/values-vi/donottranslate-more-keys.xml b/tools/make-keyboard-text/res/values-vi/donottranslate-more-keys.xml
similarity index 98%
rename from tools/maketext/res/values-vi/donottranslate-more-keys.xml
rename to tools/make-keyboard-text/res/values-vi/donottranslate-more-keys.xml
index fa98ea9..f01f068 100644
--- a/tools/maketext/res/values-vi/donottranslate-more-keys.xml
+++ b/tools/make-keyboard-text/res/values-vi/donottranslate-more-keys.xml
@@ -93,5 +93,5 @@
     <!-- U+0111: "đ" LATIN SMALL LETTER D WITH STROKE -->
     <string name="more_keys_for_d">&#x0111;</string>
     <!-- U+20AB: "₫" DONG SIGN -->
-    <string name="keylabel_for_currency_generic">&#x20AB;</string>
+    <string name="keylabel_for_currency">&#x20AB;</string>
 </resources>
diff --git a/tools/maketext/res/values-zu/donottranslate-more-keys.xml b/tools/make-keyboard-text/res/values-zu/donottranslate-more-keys.xml
similarity index 100%
rename from tools/maketext/res/values-zu/donottranslate-more-keys.xml
rename to tools/make-keyboard-text/res/values-zu/donottranslate-more-keys.xml
diff --git a/tools/maketext/res/values-zz/donottranslate-more-keys.xml b/tools/make-keyboard-text/res/values-zz/donottranslate-more-keys.xml
similarity index 100%
rename from tools/maketext/res/values-zz/donottranslate-more-keys.xml
rename to tools/make-keyboard-text/res/values-zz/donottranslate-more-keys.xml
diff --git a/tools/maketext/res/values/donottranslate-more-keys.xml b/tools/make-keyboard-text/res/values/donottranslate-more-keys.xml
similarity index 95%
rename from tools/maketext/res/values/donottranslate-more-keys.xml
rename to tools/make-keyboard-text/res/values/donottranslate-more-keys.xml
index 4cf2650..cc09f7f 100644
--- a/tools/maketext/res/values/donottranslate-more-keys.xml
+++ b/tools/make-keyboard-text/res/values/donottranslate-more-keys.xml
@@ -75,9 +75,9 @@
          U+00A5: "¥" YEN SIGN
          U+20B1: "₱" PESO SIGN -->
     <string name="more_keys_for_currency_dollar">&#x00A2;,&#x00A3;,&#x20AC;,&#x00A5;,&#x20B1;</string>
-    <string name="keylabel_for_currency_generic">$</string>
-    <string name="more_keys_for_currency_generic">$,&#x00A2;,&#x20AC;,&#x00A3;,&#x00A5;,&#x20B1;</string>
-    <string name="more_keys_for_punctuation">"!fixedColumnOrder!8,\",\',#,-,:,!,\\,,\?,\@,&amp;,\\%,+,;,/,(,)"</string>
+    <string name="keylabel_for_currency">$</string>
+    <string name="more_keys_for_currency">$,&#x00A2;,&#x20AC;,&#x00A3;,&#x00A5;,&#x20B1;</string>
+    <string name="more_keys_for_punctuation">"!fixedColumnOrder!3,!,\\,,\?,:,;,\@"</string>
     <!-- U+2020: "†" DAGGER
          U+2021: "‡" DOUBLE DAGGER
          U+2605: "★" BLACK STAR -->
@@ -167,10 +167,11 @@
     <!-- U+2030: "‰" PER MILLE SIGN -->
     <string name="more_keys_for_symbols_percent">&#x2030;</string>
     <string name="keylabel_for_tablet_comma">,</string>
-    <string name="keyhintlabel_for_tablet_comma">!</string>
-    <string name="more_keys_for_tablet_comma">!</string>
-    <string name="keyhintlabel_for_tablet_period">\?</string>
-    <string name="more_keys_for_tablet_period">\?</string>
+    <string name="keyhintlabel_for_tablet_comma"></string>
+    <string name="more_keys_for_tablet_comma"></string>
+    <string name="keyhintlabel_for_tablet_period"></string>
+    <!-- U+2026: "…" HORIZONTAL ELLIPSIS -->
+    <string name="more_keys_for_tablet_period">&#x2026;</string>
     <string name="keylabel_for_apostrophe">\'</string>
     <string name="keyhintlabel_for_apostrophe">\"</string>
     <string name="more_keys_for_apostrophe">\"</string>
@@ -189,7 +190,7 @@
     <!-- Label for "switch to more symbol" modifier key.  Must be short to fit on key! -->
     <string name="label_to_more_symbol_key">= \\ &lt;</string>
     <!-- Label for "switch to more symbol" modifier key on tablets.  Must be short to fit on key! -->
-    <string name="label_to_more_symbol_for_tablet_key">~ \\ {</string>
+    <string name="label_to_more_symbol_for_tablet_key">~ [ &lt;</string>
     <!-- Label for "Tab" key.  Must be short to fit on key! -->
     <string name="label_tab_key">Tab</string>
     <!-- Label for "switch to phone numeric" key.  Must be short to fit on key! -->
@@ -202,8 +203,6 @@
     <string name="label_time_am">"AM"</string>
     <!-- Key label for "post meridiem" -->
     <string name="label_time_pm">"PM"</string>
-    <!-- Label for "switch to symbols" key on PC QWERTY layout -->
-    <string name="label_to_symbol_key_pcqwerty">Sym</string>
     <string name="keylabel_for_popular_domain">".com"</string>
     <!-- popular web domains for the locale - most popular, displayed on the keyboard -->
     <string name="more_keys_for_popular_domain">"!hasLabels!,.net,.org,.gov,.edu"</string>
diff --git a/tools/maketext/src/com/android/inputmethod/latin/maketext/ArrayInitializerFormatter.java b/tools/make-keyboard-text/src/com/android/inputmethod/keyboard/tools/ArrayInitializerFormatter.java
similarity index 97%
rename from tools/maketext/src/com/android/inputmethod/latin/maketext/ArrayInitializerFormatter.java
rename to tools/make-keyboard-text/src/com/android/inputmethod/keyboard/tools/ArrayInitializerFormatter.java
index 3365c72..331003e 100644
--- a/tools/maketext/src/com/android/inputmethod/latin/maketext/ArrayInitializerFormatter.java
+++ b/tools/make-keyboard-text/src/com/android/inputmethod/keyboard/tools/ArrayInitializerFormatter.java
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package com.android.inputmethod.latin.maketext;
+package com.android.inputmethod.keyboard.tools;
 
 import java.io.PrintStream;
 
diff --git a/tools/maketext/src/com/android/inputmethod/latin/maketext/JarUtils.java b/tools/make-keyboard-text/src/com/android/inputmethod/keyboard/tools/JarUtils.java
similarity index 98%
rename from tools/maketext/src/com/android/inputmethod/latin/maketext/JarUtils.java
rename to tools/make-keyboard-text/src/com/android/inputmethod/keyboard/tools/JarUtils.java
index 6d6bc0e..a74096e 100644
--- a/tools/maketext/src/com/android/inputmethod/latin/maketext/JarUtils.java
+++ b/tools/make-keyboard-text/src/com/android/inputmethod/keyboard/tools/JarUtils.java
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package com.android.inputmethod.latin.maketext;
+package com.android.inputmethod.keyboard.tools;
 
 import java.io.IOException;
 import java.io.InputStream;
diff --git a/tools/maketext/src/com/android/inputmethod/latin/maketext/LabelText.java b/tools/make-keyboard-text/src/com/android/inputmethod/keyboard/tools/MakeKeyboardText.java
similarity index 88%
rename from tools/maketext/src/com/android/inputmethod/latin/maketext/LabelText.java
rename to tools/make-keyboard-text/src/com/android/inputmethod/keyboard/tools/MakeKeyboardText.java
index 4a92369..36a03f8 100644
--- a/tools/maketext/src/com/android/inputmethod/latin/maketext/LabelText.java
+++ b/tools/make-keyboard-text/src/com/android/inputmethod/keyboard/tools/MakeKeyboardText.java
@@ -14,14 +14,14 @@
  * the License.
  */
 
-package com.android.inputmethod.latin.maketext;
+package com.android.inputmethod.keyboard.tools;
 
 import java.util.Arrays;
 import java.util.LinkedList;
 import java.util.NoSuchElementException;
 import java.util.jar.JarFile;
 
-public class LabelText {
+public class MakeKeyboardText {
     static class Options {
         private static final String OPTION_JAVA = "-java";
 
@@ -31,7 +31,7 @@
             if (message != null) {
                 System.err.println(message);
             }
-            System.err.println("usage: makelabel " + OPTION_JAVA + " <java_output_dir>");
+            System.err.println("usage: make-keyboard-text " + OPTION_JAVA + " <java_output_dir>");
             System.exit(1);
         }
 
@@ -58,7 +58,7 @@
 
     public static void main(final String[] args) {
         final Options options = new Options(args);
-        final JarFile jar = JarUtils.getJarFile(LabelText.class);
+        final JarFile jar = JarUtils.getJarFile(MakeKeyboardText.class);
         final MoreKeysResources resources = new MoreKeysResources(jar);
         resources.writeToJava(options.mJava);
     }
diff --git a/tools/maketext/src/com/android/inputmethod/latin/maketext/MoreKeysResources.java b/tools/make-keyboard-text/src/com/android/inputmethod/keyboard/tools/MoreKeysResources.java
similarity index 99%
rename from tools/maketext/src/com/android/inputmethod/latin/maketext/MoreKeysResources.java
rename to tools/make-keyboard-text/src/com/android/inputmethod/keyboard/tools/MoreKeysResources.java
index fd42702..2643e01 100644
--- a/tools/maketext/src/com/android/inputmethod/latin/maketext/MoreKeysResources.java
+++ b/tools/make-keyboard-text/src/com/android/inputmethod/keyboard/tools/MoreKeysResources.java
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package com.android.inputmethod.latin.maketext;
+package com.android.inputmethod.keyboard.tools;
 
 import java.io.Closeable;
 import java.io.File;
diff --git a/tools/maketext/src/com/android/inputmethod/latin/maketext/StringResource.java b/tools/make-keyboard-text/src/com/android/inputmethod/keyboard/tools/StringResource.java
similarity index 94%
rename from tools/maketext/src/com/android/inputmethod/latin/maketext/StringResource.java
rename to tools/make-keyboard-text/src/com/android/inputmethod/keyboard/tools/StringResource.java
index 568a896..a49b8fe 100644
--- a/tools/maketext/src/com/android/inputmethod/latin/maketext/StringResource.java
+++ b/tools/make-keyboard-text/src/com/android/inputmethod/keyboard/tools/StringResource.java
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package com.android.inputmethod.latin.maketext;
+package com.android.inputmethod.keyboard.tools;
 
 public class StringResource {
     public final String mName;
diff --git a/tools/maketext/src/com/android/inputmethod/latin/maketext/StringResourceMap.java b/tools/make-keyboard-text/src/com/android/inputmethod/keyboard/tools/StringResourceMap.java
similarity index 98%
rename from tools/maketext/src/com/android/inputmethod/latin/maketext/StringResourceMap.java
rename to tools/make-keyboard-text/src/com/android/inputmethod/keyboard/tools/StringResourceMap.java
index ff13342..cc7ff6a 100644
--- a/tools/maketext/src/com/android/inputmethod/latin/maketext/StringResourceMap.java
+++ b/tools/make-keyboard-text/src/com/android/inputmethod/keyboard/tools/StringResourceMap.java
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package com.android.inputmethod.latin.maketext;
+package com.android.inputmethod.keyboard.tools;
 
 import org.xml.sax.Attributes;
 import org.xml.sax.SAXException;
diff --git a/tools/maketext/etc/manifest.txt b/tools/maketext/etc/manifest.txt
deleted file mode 100644
index bfd1a52..0000000
--- a/tools/maketext/etc/manifest.txt
+++ /dev/null
@@ -1 +0,0 @@
-Main-Class: com.android.inputmethod.latin.maketext.LabelText