Merge "[Rlog27] Refactor LogBuffer"
diff --git a/dictionaries/fr_wordlist.combined.gz b/dictionaries/fr_wordlist.combined.gz
index b7a4e35..ccc31c8 100644
--- a/dictionaries/fr_wordlist.combined.gz
+++ b/dictionaries/fr_wordlist.combined.gz
Binary files differ
diff --git a/dictionaries/pl_wordlist.combined.gz b/dictionaries/pl_wordlist.combined.gz
index 12d5238..bf02298 100644
--- a/dictionaries/pl_wordlist.combined.gz
+++ b/dictionaries/pl_wordlist.combined.gz
Binary files differ
diff --git a/java/res/layout/sound_effect_volume_dialog.xml b/java/res/layout/seek_bar_dialog.xml
similarity index 87%
rename from java/res/layout/sound_effect_volume_dialog.xml
rename to java/res/layout/seek_bar_dialog.xml
index 2946630..a47e9a0 100644
--- a/java/res/layout/sound_effect_volume_dialog.xml
+++ b/java/res/layout/seek_bar_dialog.xml
@@ -2,7 +2,7 @@
 <!--
 /*
 **
-** Copyright 2011, The Android Open Source Project
+** 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.
@@ -30,15 +30,14 @@
         android:layout_height="wrap_content"
         android:gravity="center_horizontal"
         android:layout_margin="10dp">
-        <TextView android:id="@+id/sound_effect_volume_value"
+        <TextView android:id="@+id/seek_bar_dialog_value"
             android:layout_width="wrap_content"
             android:layout_height="wrap_content"
             android:textSize="20dp"/>
     </LinearLayout>
     <SeekBar
-        android:id="@+id/sound_effect_volume_bar"
+        android:id="@+id/seek_bar_dialog_bar"
         android:layout_width="match_parent"
         android:layout_height="wrap_content"
-        android:max="100"
         android:layout_margin="10dp"/>
 </LinearLayout>
diff --git a/java/res/layout/vibration_settings_dialog.xml b/java/res/layout/vibration_settings_dialog.xml
deleted file mode 100644
index c9fb6ec..0000000
--- a/java/res/layout/vibration_settings_dialog.xml
+++ /dev/null
@@ -1,49 +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.
-*/
--->
-
-<LinearLayout
-        xmlns:android="http://schemas.android.com/apk/res/android"
-        android:orientation="vertical"
-        android:layout_width="match_parent"
-        android:layout_height="wrap_content"
-        android:layout_margin="10dp">
-    <LinearLayout
-        android:orientation="horizontal"
-        android:layout_width="match_parent"
-        android:layout_height="wrap_content"
-        android:gravity="center_horizontal"
-        android:layout_margin="10dp">
-        <TextView android:id="@+id/vibration_value"
-            android:layout_width="wrap_content"
-            android:layout_height="wrap_content"
-            android:textSize="20dp"/>
-        <TextView
-            android:layout_width="wrap_content"
-            android:layout_height="wrap_content"
-            android:text="@string/settings_ms"
-            android:textSize="20dp"/>
-    </LinearLayout>
-    <SeekBar
-        android:id="@+id/vibration_settings"
-        android:layout_width="match_parent"
-        android:layout_height="wrap_content"
-        android:max="250"
-        android:layout_margin="10dp"/>
-</LinearLayout>
diff --git a/java/res/raw/main_fr.dict b/java/res/raw/main_fr.dict
index 5aed479..c2941b7 100644
--- a/java/res/raw/main_fr.dict
+++ b/java/res/raw/main_fr.dict
Binary files differ
diff --git a/java/res/values-af/strings.xml b/java/res/values-af/strings.xml
index 18e63f3..80b9232 100644
--- a/java/res/values-af/strings.xml
+++ b/java/res/values-af/strings.xml
@@ -42,6 +42,8 @@
     <string name="key_preview_popup_dismiss_delay" msgid="6213164897443068248">"Sleutelopspringer-wagperiode"</string>
     <string name="key_preview_popup_dismiss_no_delay" msgid="2096123151571458064">"Geen wagperiode nie"</string>
     <string name="key_preview_popup_dismiss_default_delay" msgid="2166964333903906734">"Verstek"</string>
+    <!-- no translation found for settings_keypress_vibration_duration (489402970497503329) -->
+    <skip />
     <string name="use_contacts_dict" msgid="4435317977804180815">"Stel kontakname voor"</string>
     <string name="use_contacts_dict_summary" msgid="6599983334507879959">"Gebruik name van kontakte vir voorstelle en korreksies"</string>
     <string name="use_double_space_period" msgid="8781529969425082860">"Dubbelspasie-punt"</string>
diff --git a/java/res/values-am/strings.xml b/java/res/values-am/strings.xml
index 4f66a16..a59e757 100644
--- a/java/res/values-am/strings.xml
+++ b/java/res/values-am/strings.xml
@@ -42,6 +42,8 @@
     <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>
+    <!-- no translation found for settings_keypress_vibration_duration (489402970497503329) -->
+    <skip />
     <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>
diff --git a/java/res/values-ar/strings.xml b/java/res/values-ar/strings.xml
index a3877ed..c3691cd 100644
--- a/java/res/values-ar/strings.xml
+++ b/java/res/values-ar/strings.xml
@@ -42,6 +42,8 @@
     <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>
+    <!-- no translation found for settings_keypress_vibration_duration (489402970497503329) -->
+    <skip />
     <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>
diff --git a/java/res/values-be/strings.xml b/java/res/values-be/strings.xml
index bf39694..edbd9c3 100644
--- a/java/res/values-be/strings.xml
+++ b/java/res/values-be/strings.xml
@@ -42,6 +42,8 @@
     <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>
+    <!-- no translation found for settings_keypress_vibration_duration (489402970497503329) -->
+    <skip />
     <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">"Падвойны iнтэрвал"</string>
diff --git a/java/res/values-bg/strings.xml b/java/res/values-bg/strings.xml
index 27d3ac7..25030d2 100644
--- a/java/res/values-bg/strings.xml
+++ b/java/res/values-bg/strings.xml
@@ -42,6 +42,8 @@
     <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>
+    <!-- no translation found for settings_keypress_vibration_duration (489402970497503329) -->
+    <skip />
     <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>
diff --git a/java/res/values-ca/strings.xml b/java/res/values-ca/strings.xml
index 0182dd4..3f73e90 100644
--- a/java/res/values-ca/strings.xml
+++ b/java/res/values-ca/strings.xml
@@ -42,6 +42,8 @@
     <string name="key_preview_popup_dismiss_delay" msgid="6213164897443068248">"Retard en ampliar tecla"</string>
     <string name="key_preview_popup_dismiss_no_delay" msgid="2096123151571458064">"Sense retard"</string>
     <string name="key_preview_popup_dismiss_default_delay" msgid="2166964333903906734">"Predeterminat"</string>
+    <!-- no translation found for settings_keypress_vibration_duration (489402970497503329) -->
+    <skip />
     <string name="use_contacts_dict" msgid="4435317977804180815">"Suggereix noms de contactes"</string>
     <string name="use_contacts_dict_summary" msgid="6599983334507879959">"Utilitza els noms de contactes per fer suggeriments i correccions"</string>
     <string name="use_double_space_period" msgid="8781529969425082860">"Punt amb doble espai"</string>
diff --git a/java/res/values-cs/strings.xml b/java/res/values-cs/strings.xml
index 806ac12..d438ad6 100644
--- a/java/res/values-cs/strings.xml
+++ b/java/res/values-cs/strings.xml
@@ -42,6 +42,8 @@
     <string name="key_preview_popup_dismiss_delay" msgid="6213164897443068248">"Prodleva vysk. okna klávesnice"</string>
     <string name="key_preview_popup_dismiss_no_delay" msgid="2096123151571458064">"Bez prodlevy"</string>
     <string name="key_preview_popup_dismiss_default_delay" msgid="2166964333903906734">"Výchozí"</string>
+    <!-- no translation found for settings_keypress_vibration_duration (489402970497503329) -->
+    <skip />
     <string name="use_contacts_dict" msgid="4435317977804180815">"Navrhovat jména kontaktů"</string>
     <string name="use_contacts_dict_summary" msgid="6599983334507879959">"Použít jména ze seznamu kontaktů k návrhům a opravám"</string>
     <string name="use_double_space_period" msgid="8781529969425082860">"Tečka dvojitým mezerníkem"</string>
diff --git a/java/res/values-da/strings.xml b/java/res/values-da/strings.xml
index 4fbfe90..da83d1c 100644
--- a/java/res/values-da/strings.xml
+++ b/java/res/values-da/strings.xml
@@ -42,6 +42,8 @@
     <string name="key_preview_popup_dismiss_delay" msgid="6213164897443068248">"Forsink. afvis. af taste-pop op"</string>
     <string name="key_preview_popup_dismiss_no_delay" msgid="2096123151571458064">"Ingen forsink."</string>
     <string name="key_preview_popup_dismiss_default_delay" msgid="2166964333903906734">"Standard"</string>
+    <!-- no translation found for settings_keypress_vibration_duration (489402970497503329) -->
+    <skip />
     <string name="use_contacts_dict" msgid="4435317977804180815">"Foreslå navne på kontakter"</string>
     <string name="use_contacts_dict_summary" msgid="6599983334507879959">"Brug navne fra Kontaktpersoner til forslag og rettelser"</string>
     <string name="use_double_space_period" msgid="8781529969425082860">"To mellemrum for punktum"</string>
diff --git a/java/res/values-de/strings.xml b/java/res/values-de/strings.xml
index 1fad9e7..3cdb039 100644
--- a/java/res/values-de/strings.xml
+++ b/java/res/values-de/strings.xml
@@ -42,6 +42,8 @@
     <string name="key_preview_popup_dismiss_delay" msgid="6213164897443068248">"Tasten-Pop-up"</string>
     <string name="key_preview_popup_dismiss_no_delay" msgid="2096123151571458064">"Keine Verzögerung"</string>
     <string name="key_preview_popup_dismiss_default_delay" msgid="2166964333903906734">"Standard"</string>
+    <!-- no translation found for settings_keypress_vibration_duration (489402970497503329) -->
+    <skip />
     <string name="use_contacts_dict" msgid="4435317977804180815">"Kontakte vorschlagen"</string>
     <string name="use_contacts_dict_summary" msgid="6599983334507879959">"Namen aus \"Kontakte\" als Vorschläge und Korrekturmöglichkeiten anzeigen"</string>
     <string name="use_double_space_period" msgid="8781529969425082860">"Punkt plus Leerzeichen"</string>
diff --git a/java/res/values-el/strings.xml b/java/res/values-el/strings.xml
index 2b99e7e..8dcc202 100644
--- a/java/res/values-el/strings.xml
+++ b/java/res/values-el/strings.xml
@@ -42,6 +42,8 @@
     <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>
+    <!-- no translation found for settings_keypress_vibration_duration (489402970497503329) -->
+    <skip />
     <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>
diff --git a/java/res/values-en-rGB/strings.xml b/java/res/values-en-rGB/strings.xml
index 652d5aa..7a3b3e6 100644
--- a/java/res/values-en-rGB/strings.xml
+++ b/java/res/values-en-rGB/strings.xml
@@ -42,12 +42,12 @@
     <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>
+    <!-- no translation found for settings_keypress_vibration_duration (489402970497503329) -->
+    <skip />
     <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>
-    <!-- no translation found for use_double_space_period (8781529969425082860) -->
-    <skip />
-    <!-- no translation found for use_double_space_period_summary (6532892187247952799) -->
-    <skip />
+    <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="configure_dictionaries_title" msgid="4238652338556902049">"Add-on dictionaries"</string>
diff --git a/java/res/values-es-rUS/strings.xml b/java/res/values-es-rUS/strings.xml
index 9e87970..ec9614f 100644
--- a/java/res/values-es-rUS/strings.xml
+++ b/java/res/values-es-rUS/strings.xml
@@ -42,6 +42,8 @@
     <string name="key_preview_popup_dismiss_delay" msgid="6213164897443068248">"Retraso en rechazo de alerta de tecla"</string>
     <string name="key_preview_popup_dismiss_no_delay" msgid="2096123151571458064">"Sin demora"</string>
     <string name="key_preview_popup_dismiss_default_delay" msgid="2166964333903906734">"Predeterminada"</string>
+    <!-- no translation found for settings_keypress_vibration_duration (489402970497503329) -->
+    <skip />
     <string name="use_contacts_dict" msgid="4435317977804180815">"Sugerir nombres de contacto"</string>
     <string name="use_contacts_dict_summary" msgid="6599983334507879959">"Usar nombres de los contactos para sugerencias y correcciones"</string>
     <string name="use_double_space_period" msgid="8781529969425082860">"Punto y doble espacio"</string>
@@ -63,7 +65,7 @@
     <string name="auto_correction_threshold_mode_very_aggeressive" msgid="3386782235540547678">"Muy agresivo"</string>
     <string name="bigram_prediction" msgid="1084449187723948550">"Sugerencias para la palabra siguiente"</string>
     <string name="bigram_prediction_summary" msgid="3896362682751109677">"Usa la palabra anterior para hacer sugerencias"</string>
-    <string name="gesture_input" msgid="826951152254563827">"Habilitar escritura gestual"</string>
+    <string name="gesture_input" msgid="826951152254563827">"Activar escritura gestual"</string>
     <string name="gesture_input_summary" msgid="9180350639305731231">"Ingresa una palabra al deslizarte sobre las letras."</string>
     <string name="gesture_preview_trail" msgid="3802333369335722221">"Mostrar recorrido de gesto"</string>
     <string name="gesture_floating_preview_text" msgid="4443240334739381053">"Vista previa dinámica flotante"</string>
diff --git a/java/res/values-es/strings.xml b/java/res/values-es/strings.xml
index 24335e6..061516a 100644
--- a/java/res/values-es/strings.xml
+++ b/java/res/values-es/strings.xml
@@ -42,6 +42,8 @@
     <string name="key_preview_popup_dismiss_delay" msgid="6213164897443068248">"Retraso al ampliar tecla"</string>
     <string name="key_preview_popup_dismiss_no_delay" msgid="2096123151571458064">"Sin retraso"</string>
     <string name="key_preview_popup_dismiss_default_delay" msgid="2166964333903906734">"Predeterminado"</string>
+    <!-- no translation found for settings_keypress_vibration_duration (489402970497503329) -->
+    <skip />
     <string name="use_contacts_dict" msgid="4435317977804180815">"Sugerir contactos"</string>
     <string name="use_contacts_dict_summary" msgid="6599983334507879959">"Utilizar nombres de contactos para sugerencias y correcciones"</string>
     <string name="use_double_space_period" msgid="8781529969425082860">"Punto y espacio"</string>
diff --git a/java/res/values-et/strings.xml b/java/res/values-et/strings.xml
index 25dcdca..1158a21 100644
--- a/java/res/values-et/strings.xml
+++ b/java/res/values-et/strings.xml
@@ -42,6 +42,8 @@
     <string name="key_preview_popup_dismiss_delay" msgid="6213164897443068248">"Hüpiku loobumisviivitus"</string>
     <string name="key_preview_popup_dismiss_no_delay" msgid="2096123151571458064">"Viivituseta"</string>
     <string name="key_preview_popup_dismiss_default_delay" msgid="2166964333903906734">"Vaikeseade"</string>
+    <!-- no translation found for settings_keypress_vibration_duration (489402970497503329) -->
+    <skip />
     <string name="use_contacts_dict" msgid="4435317977804180815">"Soovita kontaktkirjeid"</string>
     <string name="use_contacts_dict_summary" msgid="6599983334507879959">"Kasuta soovitusteks ja parandusteks nimesid kontaktiloendist"</string>
     <string name="use_double_space_period" msgid="8781529969425082860">"Punkt tühikuklahviga"</string>
diff --git a/java/res/values-fa/strings.xml b/java/res/values-fa/strings.xml
index d4a0e66..1fb577c 100644
--- a/java/res/values-fa/strings.xml
+++ b/java/res/values-fa/strings.xml
@@ -42,6 +42,8 @@
     <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>
+    <!-- no translation found for settings_keypress_vibration_duration (489402970497503329) -->
+    <skip />
     <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>
diff --git a/java/res/values-fi/strings.xml b/java/res/values-fi/strings.xml
index 36890d9..9064e4a 100644
--- a/java/res/values-fi/strings.xml
+++ b/java/res/values-fi/strings.xml
@@ -42,6 +42,8 @@
     <string name="key_preview_popup_dismiss_delay" msgid="6213164897443068248">"Näppäimen hylkäysviive"</string>
     <string name="key_preview_popup_dismiss_no_delay" msgid="2096123151571458064">"Ei viivettä"</string>
     <string name="key_preview_popup_dismiss_default_delay" msgid="2166964333903906734">"Oletus"</string>
+    <!-- no translation found for settings_keypress_vibration_duration (489402970497503329) -->
+    <skip />
     <string name="use_contacts_dict" msgid="4435317977804180815">"Ehdota yhteystietojen nimiä"</string>
     <string name="use_contacts_dict_summary" msgid="6599983334507879959">"Käytä yhteystietojen nimiä ehdotuksissa ja korjauksissa"</string>
     <string name="use_double_space_period" msgid="8781529969425082860">"Kaksoisvälilyönti = piste"</string>
diff --git a/java/res/values-fr/strings.xml b/java/res/values-fr/strings.xml
index 3f89532..c251d27 100644
--- a/java/res/values-fr/strings.xml
+++ b/java/res/values-fr/strings.xml
@@ -42,6 +42,8 @@
     <string name="key_preview_popup_dismiss_delay" msgid="6213164897443068248">"Masquer touche agrandie"</string>
     <string name="key_preview_popup_dismiss_no_delay" msgid="2096123151571458064">"Sans délai"</string>
     <string name="key_preview_popup_dismiss_default_delay" msgid="2166964333903906734">"Par défaut"</string>
+    <!-- no translation found for settings_keypress_vibration_duration (489402970497503329) -->
+    <skip />
     <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>
diff --git a/java/res/values-hi/strings.xml b/java/res/values-hi/strings.xml
index 6537b85..7383682 100644
--- a/java/res/values-hi/strings.xml
+++ b/java/res/values-hi/strings.xml
@@ -42,6 +42,8 @@
     <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>
+    <!-- no translation found for settings_keypress_vibration_duration (489402970497503329) -->
+    <skip />
     <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>
diff --git a/java/res/values-hr/strings.xml b/java/res/values-hr/strings.xml
index d788c1f..014481a 100644
--- a/java/res/values-hr/strings.xml
+++ b/java/res/values-hr/strings.xml
@@ -42,6 +42,8 @@
     <string name="key_preview_popup_dismiss_delay" msgid="6213164897443068248">"Odgoda prikaza tipki"</string>
     <string name="key_preview_popup_dismiss_no_delay" msgid="2096123151571458064">"Bez odgode"</string>
     <string name="key_preview_popup_dismiss_default_delay" msgid="2166964333903906734">"Zadano"</string>
+    <!-- no translation found for settings_keypress_vibration_duration (489402970497503329) -->
+    <skip />
     <string name="use_contacts_dict" msgid="4435317977804180815">"Predlaži imena kontakata"</string>
     <string name="use_contacts_dict_summary" msgid="6599983334507879959">"Upotreba imena iz Kontakata za prijedloge i ispravke"</string>
     <string name="use_double_space_period" msgid="8781529969425082860">"Točka s dva razmaka"</string>
diff --git a/java/res/values-hu/strings.xml b/java/res/values-hu/strings.xml
index 1df8639..f146c82 100644
--- a/java/res/values-hu/strings.xml
+++ b/java/res/values-hu/strings.xml
@@ -42,6 +42,8 @@
     <string name="key_preview_popup_dismiss_delay" msgid="6213164897443068248">"Gombeltüntetés késése"</string>
     <string name="key_preview_popup_dismiss_no_delay" msgid="2096123151571458064">"Nincs késés"</string>
     <string name="key_preview_popup_dismiss_default_delay" msgid="2166964333903906734">"Alapbeállítás"</string>
+    <!-- no translation found for settings_keypress_vibration_duration (489402970497503329) -->
+    <skip />
     <string name="use_contacts_dict" msgid="4435317977804180815">"Javasolt névjegyek"</string>
     <string name="use_contacts_dict_summary" msgid="6599983334507879959">"A névjegyek használata a javaslatokhoz és javításokhoz"</string>
     <string name="use_double_space_period" msgid="8781529969425082860">"Dupla szóköz: pont"</string>
diff --git a/java/res/values-in/strings.xml b/java/res/values-in/strings.xml
index f26ad38..d3ae9a1 100644
--- a/java/res/values-in/strings.xml
+++ b/java/res/values-in/strings.xml
@@ -42,6 +42,8 @@
     <string name="key_preview_popup_dismiss_delay" msgid="6213164897443068248">"Tundaan singkir munculan kunci"</string>
     <string name="key_preview_popup_dismiss_no_delay" msgid="2096123151571458064">"Tanpa penundaan"</string>
     <string name="key_preview_popup_dismiss_default_delay" msgid="2166964333903906734">"Default"</string>
+    <!-- no translation found for settings_keypress_vibration_duration (489402970497503329) -->
+    <skip />
     <string name="use_contacts_dict" msgid="4435317977804180815">"Sarankan nama Kontak"</string>
     <string name="use_contacts_dict_summary" msgid="6599983334507879959">"Menggunakan nama dari Kontak untuk saran dan koreksi"</string>
     <string name="use_double_space_period" msgid="8781529969425082860">"Titik spasi ganda"</string>
diff --git a/java/res/values-is/strings.xml b/java/res/values-is/strings.xml
index 8ea7461..016a1d1 100644
--- a/java/res/values-is/strings.xml
+++ b/java/res/values-is/strings.xml
@@ -64,6 +64,8 @@
     <skip />
     <!-- no translation found for key_preview_popup_dismiss_default_delay (2166964333903906734) -->
     <skip />
+    <!-- no translation found for settings_keypress_vibration_duration (489402970497503329) -->
+    <skip />
     <!-- no translation found for use_contacts_dict (4435317977804180815) -->
     <skip />
     <!-- no translation found for use_contacts_dict_summary (6599983334507879959) -->
diff --git a/java/res/values-it/strings.xml b/java/res/values-it/strings.xml
index a78c5c5..3efed91 100644
--- a/java/res/values-it/strings.xml
+++ b/java/res/values-it/strings.xml
@@ -42,6 +42,8 @@
     <string name="key_preview_popup_dismiss_delay" msgid="6213164897443068248">"Ritardo eliminaz. popup tasto"</string>
     <string name="key_preview_popup_dismiss_no_delay" msgid="2096123151571458064">"Nessun ritardo"</string>
     <string name="key_preview_popup_dismiss_default_delay" msgid="2166964333903906734">"Predefinito"</string>
+    <!-- no translation found for settings_keypress_vibration_duration (489402970497503329) -->
+    <skip />
     <string name="use_contacts_dict" msgid="4435317977804180815">"Suggerisci nomi di contatti"</string>
     <string name="use_contacts_dict_summary" msgid="6599983334507879959">"Utilizza nomi di Contatti per suggerimenti e correzioni"</string>
     <string name="use_double_space_period" msgid="8781529969425082860">"Doppio spazio per punto"</string>
diff --git a/java/res/values-iw/strings.xml b/java/res/values-iw/strings.xml
index 6542b9f..2fec994 100644
--- a/java/res/values-iw/strings.xml
+++ b/java/res/values-iw/strings.xml
@@ -42,6 +42,8 @@
     <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>
+    <!-- no translation found for settings_keypress_vibration_duration (489402970497503329) -->
+    <skip />
     <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>
diff --git a/java/res/values-ja/strings.xml b/java/res/values-ja/strings.xml
index 2b341e1..8d01e75 100644
--- a/java/res/values-ja/strings.xml
+++ b/java/res/values-ja/strings.xml
@@ -42,6 +42,8 @@
     <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>
+    <!-- no translation found for settings_keypress_vibration_duration (489402970497503329) -->
+    <skip />
     <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>
diff --git a/java/res/values-ka/strings.xml b/java/res/values-ka/strings.xml
index ef30144..b9ffbf6 100644
--- a/java/res/values-ka/strings.xml
+++ b/java/res/values-ka/strings.xml
@@ -64,6 +64,8 @@
     <skip />
     <!-- no translation found for key_preview_popup_dismiss_default_delay (2166964333903906734) -->
     <skip />
+    <!-- no translation found for settings_keypress_vibration_duration (489402970497503329) -->
+    <skip />
     <!-- no translation found for use_contacts_dict (4435317977804180815) -->
     <skip />
     <!-- no translation found for use_contacts_dict_summary (6599983334507879959) -->
diff --git a/java/res/values-ko/strings.xml b/java/res/values-ko/strings.xml
index e7b9d5a..c0c313a 100644
--- a/java/res/values-ko/strings.xml
+++ b/java/res/values-ko/strings.xml
@@ -42,6 +42,8 @@
     <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>
+    <!-- no translation found for settings_keypress_vibration_duration (489402970497503329) -->
+    <skip />
     <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>
diff --git a/java/res/values-lt/strings.xml b/java/res/values-lt/strings.xml
index 5c66e1f..39fbda6 100644
--- a/java/res/values-lt/strings.xml
+++ b/java/res/values-lt/strings.xml
@@ -42,6 +42,8 @@
     <string name="key_preview_popup_dismiss_delay" msgid="6213164897443068248">"Pagr. išš. l. atsis. d."</string>
     <string name="key_preview_popup_dismiss_no_delay" msgid="2096123151571458064">"Be delsos"</string>
     <string name="key_preview_popup_dismiss_default_delay" msgid="2166964333903906734">"Numatytasis"</string>
+    <!-- no translation found for settings_keypress_vibration_duration (489402970497503329) -->
+    <skip />
     <string name="use_contacts_dict" msgid="4435317977804180815">"Siūlyti kontaktų vardus"</string>
     <string name="use_contacts_dict_summary" msgid="6599983334507879959">"Siūlant ir taisant naudoti vardus iš „Kontaktų“"</string>
     <string name="use_double_space_period" msgid="8781529969425082860">"Tšk. ir tarp. pal. dukart"</string>
diff --git a/java/res/values-lv/strings.xml b/java/res/values-lv/strings.xml
index a0cf78e..6bc4771 100644
--- a/java/res/values-lv/strings.xml
+++ b/java/res/values-lv/strings.xml
@@ -42,6 +42,8 @@
     <string name="key_preview_popup_dismiss_delay" msgid="6213164897443068248">"Taust. uzn. loga noraid. aizk."</string>
     <string name="key_preview_popup_dismiss_no_delay" msgid="2096123151571458064">"Bez aizkaves"</string>
     <string name="key_preview_popup_dismiss_default_delay" msgid="2166964333903906734">"Noklusējums"</string>
+    <!-- no translation found for settings_keypress_vibration_duration (489402970497503329) -->
+    <skip />
     <string name="use_contacts_dict" msgid="4435317977804180815">"Ieteikt kontaktp. vārdus"</string>
     <string name="use_contacts_dict_summary" msgid="6599983334507879959">"Izmantot kontaktpersonu vārdus kā ieteikumus un labojumus"</string>
     <string name="use_double_space_period" msgid="8781529969425082860">"Dubultpiesk. = punkts"</string>
diff --git a/java/res/values-mk/strings.xml b/java/res/values-mk/strings.xml
index e199e03..7758dbb 100644
--- a/java/res/values-mk/strings.xml
+++ b/java/res/values-mk/strings.xml
@@ -64,6 +64,8 @@
     <skip />
     <!-- no translation found for key_preview_popup_dismiss_default_delay (2166964333903906734) -->
     <skip />
+    <!-- no translation found for settings_keypress_vibration_duration (489402970497503329) -->
+    <skip />
     <!-- no translation found for use_contacts_dict (4435317977804180815) -->
     <skip />
     <!-- no translation found for use_contacts_dict_summary (6599983334507879959) -->
diff --git a/java/res/values-mn/strings.xml b/java/res/values-mn/strings.xml
index 41bf551..4d38079 100644
--- a/java/res/values-mn/strings.xml
+++ b/java/res/values-mn/strings.xml
@@ -64,6 +64,8 @@
     <skip />
     <!-- no translation found for key_preview_popup_dismiss_default_delay (2166964333903906734) -->
     <skip />
+    <!-- no translation found for settings_keypress_vibration_duration (489402970497503329) -->
+    <skip />
     <!-- no translation found for use_contacts_dict (4435317977804180815) -->
     <skip />
     <!-- no translation found for use_contacts_dict_summary (6599983334507879959) -->
diff --git a/java/res/values-ms/strings.xml b/java/res/values-ms/strings.xml
index c1a2f0d..a0b4983 100644
--- a/java/res/values-ms/strings.xml
+++ b/java/res/values-ms/strings.xml
@@ -42,6 +42,8 @@
     <string name="key_preview_popup_dismiss_delay" msgid="6213164897443068248">"Pop tmbl knci ketpkn lengah"</string>
     <string name="key_preview_popup_dismiss_no_delay" msgid="2096123151571458064">"Tiada lengah"</string>
     <string name="key_preview_popup_dismiss_default_delay" msgid="2166964333903906734">"Lalai"</string>
+    <!-- no translation found for settings_keypress_vibration_duration (489402970497503329) -->
+    <skip />
     <string name="use_contacts_dict" msgid="4435317977804180815">"Cadangkan nama Kenalan"</string>
     <string name="use_contacts_dict_summary" msgid="6599983334507879959">"Menggunakan nama daripada Kenalan untuk cadangan dan pembetulan"</string>
     <string name="use_double_space_period" msgid="8781529969425082860">"Titik ruang berganda"</string>
diff --git a/java/res/values-nb/strings.xml b/java/res/values-nb/strings.xml
index f56ce11..5bcedc1 100644
--- a/java/res/values-nb/strings.xml
+++ b/java/res/values-nb/strings.xml
@@ -42,6 +42,8 @@
     <string name="key_preview_popup_dismiss_delay" msgid="6213164897443068248">"Tregt tastevindu"</string>
     <string name="key_preview_popup_dismiss_no_delay" msgid="2096123151571458064">"U/ forsinkelse"</string>
     <string name="key_preview_popup_dismiss_default_delay" msgid="2166964333903906734">"Standard"</string>
+    <!-- no translation found for settings_keypress_vibration_duration (489402970497503329) -->
+    <skip />
     <string name="use_contacts_dict" msgid="4435317977804180815">"Foreslå kontaktnavn"</string>
     <string name="use_contacts_dict_summary" msgid="6599983334507879959">"Bruk navn fra Kontakter til forslag og korrigeringer"</string>
     <string name="use_double_space_period" msgid="8781529969425082860">"Punktum ved doble mellomrom"</string>
diff --git a/java/res/values-nl/strings.xml b/java/res/values-nl/strings.xml
index 6bd474a..034b352 100644
--- a/java/res/values-nl/strings.xml
+++ b/java/res/values-nl/strings.xml
@@ -42,6 +42,8 @@
     <string name="key_preview_popup_dismiss_delay" msgid="6213164897443068248">"Afwijz.vertr. toetspop-up"</string>
     <string name="key_preview_popup_dismiss_no_delay" msgid="2096123151571458064">"Geen vertraging"</string>
     <string name="key_preview_popup_dismiss_default_delay" msgid="2166964333903906734">"Standaard"</string>
+    <!-- no translation found for settings_keypress_vibration_duration (489402970497503329) -->
+    <skip />
     <string name="use_contacts_dict" msgid="4435317977804180815">"Contactnamen suggereren"</string>
     <string name="use_contacts_dict_summary" msgid="6599983334507879959">"Namen uit Contacten gebruiken voor suggesties en correcties"</string>
     <string name="use_double_space_period" msgid="8781529969425082860">"Dubbeltik is punt, spatie"</string>
diff --git a/java/res/values-pl/strings-appname.xml b/java/res/values-pl/strings-appname.xml
index e460644..4d244d7 100644
--- a/java/res/values-pl/strings-appname.xml
+++ b/java/res/values-pl/strings-appname.xml
@@ -21,10 +21,7 @@
 <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="178705338187710493">"Klawiatura Android"</string>
-    <!-- no translation found for spell_checker_service_name (6268342166872202903) -->
-    <skip />
-    <!-- no translation found for english_ime_settings (7470027018752707691) -->
-    <skip />
-    <!-- no translation found for android_spell_checker_settings (8397842018475560441) -->
-    <skip />
+    <string name="spell_checker_service_name" msgid="6268342166872202903">"Sprawdzanie pisowni w Androidzie"</string>
+    <string name="english_ime_settings" msgid="7470027018752707691">"Ustawienia klawiatury Android"</string>
+    <string name="android_spell_checker_settings" msgid="8397842018475560441">"Ustawienia sprawdzania pisowni"</string>
 </resources>
diff --git a/java/res/values-pl/strings.xml b/java/res/values-pl/strings.xml
index 27fcf60..4ddfa8d 100644
--- a/java/res/values-pl/strings.xml
+++ b/java/res/values-pl/strings.xml
@@ -42,6 +42,8 @@
     <string name="key_preview_popup_dismiss_delay" msgid="6213164897443068248">"Opóźnienie znikania klawiszy"</string>
     <string name="key_preview_popup_dismiss_no_delay" msgid="2096123151571458064">"Bez opóźnienia"</string>
     <string name="key_preview_popup_dismiss_default_delay" msgid="2166964333903906734">"Wartość domyślna"</string>
+    <!-- no translation found for settings_keypress_vibration_duration (489402970497503329) -->
+    <skip />
     <string name="use_contacts_dict" msgid="4435317977804180815">"Proponuj osoby z kontaktów"</string>
     <string name="use_contacts_dict_summary" msgid="6599983334507879959">"W propozycjach i poprawkach użyj nazwisk z kontaktów"</string>
     <string name="use_double_space_period" msgid="8781529969425082860">"Szybka kropka ze spacją"</string>
diff --git a/java/res/values-pt-rPT/strings.xml b/java/res/values-pt-rPT/strings.xml
index 46bd34d..136470b 100644
--- a/java/res/values-pt-rPT/strings.xml
+++ b/java/res/values-pt-rPT/strings.xml
@@ -42,6 +42,8 @@
     <string name="key_preview_popup_dismiss_delay" msgid="6213164897443068248">"Atraso p/ ignorar pop-up"</string>
     <string name="key_preview_popup_dismiss_no_delay" msgid="2096123151571458064">"Sem atraso"</string>
     <string name="key_preview_popup_dismiss_default_delay" msgid="2166964333903906734">"Predefinido"</string>
+    <!-- no translation found for settings_keypress_vibration_duration (489402970497503329) -->
+    <skip />
     <string name="use_contacts_dict" msgid="4435317977804180815">"Sugerir nomes de Contactos"</string>
     <string name="use_contacts_dict_summary" msgid="6599983334507879959">"Utilizar nomes dos Contactos para sugestões e correções"</string>
     <string name="use_double_space_period" msgid="8781529969425082860">"Ponto de espaço duplo"</string>
diff --git a/java/res/values-pt/strings.xml b/java/res/values-pt/strings.xml
index ce61e0c..3408dde 100644
--- a/java/res/values-pt/strings.xml
+++ b/java/res/values-pt/strings.xml
@@ -42,6 +42,8 @@
     <string name="key_preview_popup_dismiss_delay" msgid="6213164897443068248">"Dispens. atraso chave princ."</string>
     <string name="key_preview_popup_dismiss_no_delay" msgid="2096123151571458064">"Sem atraso"</string>
     <string name="key_preview_popup_dismiss_default_delay" msgid="2166964333903906734">"Padrão"</string>
+    <!-- no translation found for settings_keypress_vibration_duration (489402970497503329) -->
+    <skip />
     <string name="use_contacts_dict" msgid="4435317977804180815">"Sugerir nomes de contato"</string>
     <string name="use_contacts_dict_summary" msgid="6599983334507879959">"Usar nomes dos Contatos para sugestões e correções"</string>
     <string name="use_double_space_period" msgid="8781529969425082860">"Duplo espaço p/ ponto"</string>
diff --git a/java/res/values-rm/strings.xml b/java/res/values-rm/strings.xml
index 5a315f5..90d7357 100644
--- a/java/res/values-rm/strings.xml
+++ b/java/res/values-rm/strings.xml
@@ -61,6 +61,8 @@
     <skip />
     <!-- no translation found for key_preview_popup_dismiss_default_delay (2166964333903906734) -->
     <skip />
+    <!-- no translation found for settings_keypress_vibration_duration (489402970497503329) -->
+    <skip />
     <!-- no translation found for use_contacts_dict (4435317977804180815) -->
     <skip />
     <!-- no translation found for use_contacts_dict_summary (6599983334507879959) -->
diff --git a/java/res/values-ro/strings.xml b/java/res/values-ro/strings.xml
index 99b82a5..1b01768 100644
--- a/java/res/values-ro/strings.xml
+++ b/java/res/values-ro/strings.xml
@@ -42,6 +42,8 @@
     <string name="key_preview_popup_dismiss_delay" msgid="6213164897443068248">"Înt. înch. pop-up esenţ."</string>
     <string name="key_preview_popup_dismiss_no_delay" msgid="2096123151571458064">"Fără întârziere"</string>
     <string name="key_preview_popup_dismiss_default_delay" msgid="2166964333903906734">"Prestabilit"</string>
+    <!-- no translation found for settings_keypress_vibration_duration (489402970497503329) -->
+    <skip />
     <string name="use_contacts_dict" msgid="4435317977804180815">"Sugeraţi nume din Agendă"</string>
     <string name="use_contacts_dict_summary" msgid="6599983334507879959">"Utilizaţi numele din Agendă pentru sugestii şi corecţii"</string>
     <string name="use_double_space_period" msgid="8781529969425082860">"Inserează punct spațiu"</string>
diff --git a/java/res/values-ru/strings.xml b/java/res/values-ru/strings.xml
index 1075d29..2f1f7e6 100644
--- a/java/res/values-ru/strings.xml
+++ b/java/res/values-ru/strings.xml
@@ -42,6 +42,8 @@
     <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>
+    <!-- no translation found for settings_keypress_vibration_duration (489402970497503329) -->
+    <skip />
     <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>
diff --git a/java/res/values-sk/strings.xml b/java/res/values-sk/strings.xml
index 1ee4f33..55fae95 100644
--- a/java/res/values-sk/strings.xml
+++ b/java/res/values-sk/strings.xml
@@ -42,6 +42,8 @@
     <string name="key_preview_popup_dismiss_delay" msgid="6213164897443068248">"Onesk. zrušenia kľúč. kon. okna"</string>
     <string name="key_preview_popup_dismiss_no_delay" msgid="2096123151571458064">"Bez oneskorenia"</string>
     <string name="key_preview_popup_dismiss_default_delay" msgid="2166964333903906734">"Predvolená"</string>
+    <!-- no translation found for settings_keypress_vibration_duration (489402970497503329) -->
+    <skip />
     <string name="use_contacts_dict" msgid="4435317977804180815">"Navrhnúť mená kontaktov"</string>
     <string name="use_contacts_dict_summary" msgid="6599983334507879959">"Používať mená z Kontaktov na návrhy a opravy"</string>
     <string name="use_double_space_period" msgid="8781529969425082860">"Bodka s medzerou"</string>
diff --git a/java/res/values-sl/strings.xml b/java/res/values-sl/strings.xml
index 6ac7406..f8fd821 100644
--- a/java/res/values-sl/strings.xml
+++ b/java/res/values-sl/strings.xml
@@ -42,6 +42,8 @@
     <string name="key_preview_popup_dismiss_delay" msgid="6213164897443068248">"Trajanje povečanja tipke"</string>
     <string name="key_preview_popup_dismiss_no_delay" msgid="2096123151571458064">"Brez zakasnitve"</string>
     <string name="key_preview_popup_dismiss_default_delay" msgid="2166964333903906734">"Privzeto"</string>
+    <!-- no translation found for settings_keypress_vibration_duration (489402970497503329) -->
+    <skip />
     <string name="use_contacts_dict" msgid="4435317977804180815">"Predlagaj imena stikov"</string>
     <string name="use_contacts_dict_summary" msgid="6599983334507879959">"Uporaba imen iz stikov za predloge in popravke"</string>
     <string name="use_double_space_period" msgid="8781529969425082860">"Dva presl. za vnos pike"</string>
diff --git a/java/res/values-sr/strings.xml b/java/res/values-sr/strings.xml
index 1456ae9..f22fc2f 100644
--- a/java/res/values-sr/strings.xml
+++ b/java/res/values-sr/strings.xml
@@ -42,6 +42,8 @@
     <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>
+    <!-- no translation found for settings_keypress_vibration_duration (489402970497503329) -->
+    <skip />
     <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>
diff --git a/java/res/values-sv/strings.xml b/java/res/values-sv/strings.xml
index f6426f9..ff72c1a 100644
--- a/java/res/values-sv/strings.xml
+++ b/java/res/values-sv/strings.xml
@@ -42,6 +42,8 @@
     <string name="key_preview_popup_dismiss_delay" msgid="6213164897443068248">"Ta bort popup-fördröjning"</string>
     <string name="key_preview_popup_dismiss_no_delay" msgid="2096123151571458064">"Fördröj inte"</string>
     <string name="key_preview_popup_dismiss_default_delay" msgid="2166964333903906734">"Standard"</string>
+    <!-- no translation found for settings_keypress_vibration_duration (489402970497503329) -->
+    <skip />
     <string name="use_contacts_dict" msgid="4435317977804180815">"Föreslå kontaktnamn"</string>
     <string name="use_contacts_dict_summary" msgid="6599983334507879959">"Använd namn från Kontakter för förslag och korrigeringar"</string>
     <string name="use_double_space_period" msgid="8781529969425082860">"Dubbelt blanksteg = punkt"</string>
diff --git a/java/res/values-sw/strings.xml b/java/res/values-sw/strings.xml
index 81b9003..5d84409 100644
--- a/java/res/values-sw/strings.xml
+++ b/java/res/values-sw/strings.xml
@@ -42,6 +42,8 @@
     <string name="key_preview_popup_dismiss_delay" msgid="6213164897443068248">"Kuchelewesha kutupa kitufe ibukizi"</string>
     <string name="key_preview_popup_dismiss_no_delay" msgid="2096123151571458064">"Hakuna kuchelewa"</string>
     <string name="key_preview_popup_dismiss_default_delay" msgid="2166964333903906734">"Chaguo-msingi"</string>
+    <!-- no translation found for settings_keypress_vibration_duration (489402970497503329) -->
+    <skip />
     <string name="use_contacts_dict" msgid="4435317977804180815">"Pendekeza majini ya Anwani"</string>
     <string name="use_contacts_dict_summary" msgid="6599983334507879959">"Tumia majina kutoka kwa Anwani kwa mapendekezo na marekebisho"</string>
     <string name="use_double_space_period" msgid="8781529969425082860">"Kitone baada ya nafasi mbili"</string>
diff --git a/java/res/values-th/strings.xml b/java/res/values-th/strings.xml
index d089098..6414e8f 100644
--- a/java/res/values-th/strings.xml
+++ b/java/res/values-th/strings.xml
@@ -42,6 +42,8 @@
     <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>
+    <!-- no translation found for settings_keypress_vibration_duration (489402970497503329) -->
+    <skip />
     <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>
diff --git a/java/res/values-tl/strings.xml b/java/res/values-tl/strings.xml
index a16032e..4c6c532 100644
--- a/java/res/values-tl/strings.xml
+++ b/java/res/values-tl/strings.xml
@@ -42,6 +42,8 @@
     <string name="key_preview_popup_dismiss_delay" msgid="6213164897443068248">"Balewala antala key popup"</string>
     <string name="key_preview_popup_dismiss_no_delay" msgid="2096123151571458064">"Walang antala"</string>
     <string name="key_preview_popup_dismiss_default_delay" msgid="2166964333903906734">"Default"</string>
+    <!-- no translation found for settings_keypress_vibration_duration (489402970497503329) -->
+    <skip />
     <string name="use_contacts_dict" msgid="4435317977804180815">"Mungkahi pangalan Contact"</string>
     <string name="use_contacts_dict_summary" msgid="6599983334507879959">"Gamitin pangalan mula Mga Contact sa mga mungkahi\'t pagwawasto"</string>
     <string name="use_double_space_period" msgid="8781529969425082860">"Double-space period"</string>
diff --git a/java/res/values-tr/strings.xml b/java/res/values-tr/strings.xml
index 7b3748c..462f7c9 100644
--- a/java/res/values-tr/strings.xml
+++ b/java/res/values-tr/strings.xml
@@ -42,6 +42,8 @@
     <string name="key_preview_popup_dismiss_delay" msgid="6213164897443068248">"Tuş popup\'ının kapatılmasını geciktirme"</string>
     <string name="key_preview_popup_dismiss_no_delay" msgid="2096123151571458064">"Gecikme yok"</string>
     <string name="key_preview_popup_dismiss_default_delay" msgid="2166964333903906734">"Varsayılan"</string>
+    <!-- no translation found for settings_keypress_vibration_duration (489402970497503329) -->
+    <skip />
     <string name="use_contacts_dict" msgid="4435317977804180815">"Kişi Adları öner"</string>
     <string name="use_contacts_dict_summary" msgid="6599983334507879959">"Öneri ve düzeltmeler için Kişiler\'deki adları kullan"</string>
     <string name="use_double_space_period" msgid="8781529969425082860">"Çift boşlukla nokta ekleme"</string>
diff --git a/java/res/values-uk/strings.xml b/java/res/values-uk/strings.xml
index 532f671..819b6ea 100644
--- a/java/res/values-uk/strings.xml
+++ b/java/res/values-uk/strings.xml
@@ -42,6 +42,8 @@
     <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>
+    <!-- no translation found for settings_keypress_vibration_duration (489402970497503329) -->
+    <skip />
     <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>
diff --git a/java/res/values-vi/strings.xml b/java/res/values-vi/strings.xml
index 2b29120..052f60c 100644
--- a/java/res/values-vi/strings.xml
+++ b/java/res/values-vi/strings.xml
@@ -42,6 +42,8 @@
     <string name="key_preview_popup_dismiss_delay" msgid="6213164897443068248">"Loại bỏ hiển thị phím trễ"</string>
     <string name="key_preview_popup_dismiss_no_delay" msgid="2096123151571458064">"Không có tgian trễ"</string>
     <string name="key_preview_popup_dismiss_default_delay" msgid="2166964333903906734">"Mặc định"</string>
+    <!-- no translation found for settings_keypress_vibration_duration (489402970497503329) -->
+    <skip />
     <string name="use_contacts_dict" msgid="4435317977804180815">"Đề xuất tên liên hệ"</string>
     <string name="use_contacts_dict_summary" msgid="6599983334507879959">"Sử dụng tên từ Danh bạ cho các đề xuất và chỉnh sửa"</string>
     <string name="use_double_space_period" msgid="8781529969425082860">"Dấu cách đôi"</string>
diff --git a/java/res/values-zh-rCN/strings.xml b/java/res/values-zh-rCN/strings.xml
index ed663b7..b119ccd 100644
--- a/java/res/values-zh-rCN/strings.xml
+++ b/java/res/values-zh-rCN/strings.xml
@@ -42,6 +42,8 @@
     <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>
+    <!-- no translation found for settings_keypress_vibration_duration (489402970497503329) -->
+    <skip />
     <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>
diff --git a/java/res/values-zh-rTW/strings.xml b/java/res/values-zh-rTW/strings.xml
index 28d32cf..02737c4 100644
--- a/java/res/values-zh-rTW/strings.xml
+++ b/java/res/values-zh-rTW/strings.xml
@@ -42,6 +42,8 @@
     <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>
+    <!-- no translation found for settings_keypress_vibration_duration (489402970497503329) -->
+    <skip />
     <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>
diff --git a/java/res/values-zu/strings.xml b/java/res/values-zu/strings.xml
index 575a687..65d2351 100644
--- a/java/res/values-zu/strings.xml
+++ b/java/res/values-zu/strings.xml
@@ -42,6 +42,8 @@
     <string name="key_preview_popup_dismiss_delay" msgid="6213164897443068248">"Ukuvela kokhiye cashisa ukulibazisa"</string>
     <string name="key_preview_popup_dismiss_no_delay" msgid="2096123151571458064">"Cha ukulibazisa"</string>
     <string name="key_preview_popup_dismiss_default_delay" msgid="2166964333903906734">"Okuzenzakalelayo"</string>
+    <!-- no translation found for settings_keypress_vibration_duration (489402970497503329) -->
+    <skip />
     <string name="use_contacts_dict" msgid="4435317977804180815">"Sikisela amagama Othintana nabo"</string>
     <string name="use_contacts_dict_summary" msgid="6599983334507879959">"Amagama abasebenzisi kusuka Kothintana nabo bokusikisela nokulungisa"</string>
     <string name="use_double_space_period" msgid="8781529969425082860">"Isikhathi se-Double-space"</string>
diff --git a/java/res/values/donottranslate.xml b/java/res/values/donottranslate.xml
index a6ea8a0..193a019 100644
--- a/java/res/values/donottranslate.xml
+++ b/java/res/values/donottranslate.xml
@@ -233,6 +233,5 @@
     <!-- dictionary pack package name /settings activity (for shared prefs and settings) -->
     <string name="dictionary_pack_package_name">com.google.android.inputmethod.latin.dictionarypack</string>
     <string name="dictionary_pack_settings_activity">com.google.android.inputmethod.latin.dictionarypack.DictionarySettingsActivity</string>
-    <string name="settings_ms">ms</string>
     <string name="settings_warning_researcher_mode">Attention!  You are using the special keyboard for research purposes.</string>
 </resources>
diff --git a/java/res/values/keyboard-heights.xml b/java/res/values/keyboard-heights.xml
index 7d85994..418d3e5 100644
--- a/java/res/values/keyboard-heights.xml
+++ b/java/res/values/keyboard-heights.xml
@@ -33,5 +33,7 @@
     <!-- Preferable keyboard height in absolute scale: 48.0mm -->
         <!-- Xoom -->
         <item>stingray,283.1337</item>
+    <!-- Default value for unknown device: empty string -->
+        <item>DEFAULT,</item>
     </string-array>
 </resources>
diff --git a/java/res/values/keypress-vibration-durations.xml b/java/res/values/keypress-vibration-durations.xml
index 370959c..9b1d543 100644
--- a/java/res/values/keypress-vibration-durations.xml
+++ b/java/res/values/keypress-vibration-durations.xml
@@ -24,5 +24,7 @@
         <item>tuna,5</item>
         <item>mako,5</item>
         <item>manta,16</item>
+        <!-- Default value for unknown device -->
+        <item>DEFAULT,10</item>
     </string-array>
 </resources>
diff --git a/java/res/values/keypress-volumes.xml b/java/res/values/keypress-volumes.xml
index d112069..047fe0c 100644
--- a/java/res/values/keypress-volumes.xml
+++ b/java/res/values/keypress-volumes.xml
@@ -20,11 +20,13 @@
 <resources>
     <string-array name="keypress_volumes" translatable="false">
         <!-- Build.HARDWARE,volume -->
-        <item>herring,0.5</item>
-        <item>tuna,0.5</item>
-        <item>stingray,0.4</item>
-        <item>grouper,0.3</item>
-        <item>mako,0.3</item>
-        <item>manta,0.2</item>
+        <item>herring,0.5f</item>
+        <item>tuna,0.5f</item>
+        <item>stingray,0.4f</item>
+        <item>grouper,0.3f</item>
+        <item>mako,0.3f</item>
+        <item>manta,0.2f</item>
+        <!-- Default value for unknown device -->
+        <item>DEFAULT,0.2f</item>
     </string-array>
 </resources>
diff --git a/java/res/values/phantom-sudden-move-event-device-list.xml b/java/res/values/phantom-sudden-move-event-device-list.xml
index 63d12e9..22f5102 100644
--- a/java/res/values/phantom-sudden-move-event-device-list.xml
+++ b/java/res/values/phantom-sudden-move-event-device-list.xml
@@ -21,6 +21,9 @@
     <string-array name="phantom_sudden_move_event_device_list" translatable="false">
         <!-- "Build.HARDWARE,true" that needs "phantom sudden move event" hack.
              See {@link com.android.inputmethod.keyboard.PointerTracker}. -->
-        <item>stingray,true</item> <!-- Xoom -->
+        <!-- Xoom -->
+        <item>stingray,true</item>
+        <!-- Default value for unknown device -->
+        <item>DEFAULT,false</item>
     </string-array>
 </resources>
diff --git a/java/res/values/strings.xml b/java/res/values/strings.xml
index 2affdeb..5c54427 100644
--- a/java/res/values/strings.xml
+++ b/java/res/values/strings.xml
@@ -76,6 +76,9 @@
     <!-- Description for delay for dismissing a popup on screen: default value of the delay [CHAR LIMIT=15] -->
     <string name="key_preview_popup_dismiss_default_delay">Default</string>
 
+    <!-- Units abbreviation for the keypress vibration duration (milliseconds) [CHAR LIMIT=10] -->
+    <string name="settings_keypress_vibration_duration"><xliff:g id="milliseconds">%s</xliff:g>ms</string>
+
     <!-- Option name for enabling or disabling the use of names of people in Contacts for suggestion and correction [CHAR LIMIT=25] -->
     <string name="use_contacts_dict">Suggest Contact names</string>
     <!-- Description for option enabling or disabling the use of names of people in Contacts for suggestion and correction [CHAR LIMIT=65] -->
diff --git a/java/res/values/sudden-jumping-touch-event-device-list.xml b/java/res/values/sudden-jumping-touch-event-device-list.xml
index 543992a..3fdc0c7 100644
--- a/java/res/values/sudden-jumping-touch-event-device-list.xml
+++ b/java/res/values/sudden-jumping-touch-event-device-list.xml
@@ -21,7 +21,11 @@
     <string-array name="sudden_jumping_touch_event_device_list" translatable="false">
         <!-- "Build.HARDWARE,true" that needs "sudden jump touch event" hack.
              See {@link com.android.inputmethod.keyboard.SuddenJumpingTouchEventHandler}. -->
-        <item>mahimahi,true</item> <!-- Nexus One -->
-        <item>sholes,true</item> <!-- Droid -->
+        <!-- Nexus One -->
+        <item>mahimahi,true</item>
+        <!-- Droid -->
+        <item>sholes,true</item>
+        <!-- Default value for unknown device -->
+        <item>DEFAULT,false</item>
     </string-array>
 </resources>
diff --git a/java/src/com/android/inputmethod/accessibility/AccessibilityEntityProvider.java b/java/src/com/android/inputmethod/accessibility/AccessibilityEntityProvider.java
index 0ab84f7..0576f66 100644
--- a/java/src/com/android/inputmethod/accessibility/AccessibilityEntityProvider.java
+++ b/java/src/com/android/inputmethod/accessibility/AccessibilityEntityProvider.java
@@ -71,12 +71,11 @@
     /** The current keyboard view. */
     private KeyboardView mKeyboardView;
 
-    public AccessibilityEntityProvider(KeyboardView keyboardView, InputMethodService inputMethod) {
+    public AccessibilityEntityProvider(final KeyboardView keyboardView,
+            final InputMethodService inputMethod) {
         mInputMethodService = inputMethod;
-
         mKeyCodeDescriptionMapper = KeyCodeDescriptionMapper.getInstance();
         mAccessibilityUtils = AccessibilityUtils.getInstance();
-
         setView(keyboardView);
     }
 
@@ -85,21 +84,19 @@
      *
      * @param keyboardView The keyboard view to represent.
      */
-    public void setView(KeyboardView keyboardView) {
+    public void setView(final KeyboardView keyboardView) {
         mKeyboardView = keyboardView;
         updateParentLocation();
 
         // Since this class is constructed lazily, we might not get a subsequent
         // call to setKeyboard() and therefore need to call it now.
-        setKeyboard(mKeyboardView.getKeyboard());
+        setKeyboard();
     }
 
     /**
      * Sets the keyboard represented by this node provider.
-     *
-     * @param keyboard The keyboard to represent.
      */
-    public void setKeyboard(Keyboard keyboard) {
+    public void setKeyboard() {
         assignVirtualViewIds();
     }
 
@@ -112,19 +109,16 @@
      * @return A populated {@link AccessibilityEvent} for the key.
      * @see AccessibilityEvent
      */
-    public AccessibilityEvent createAccessibilityEvent(Key key, int eventType) {
+    public AccessibilityEvent createAccessibilityEvent(final Key key, final int eventType) {
         final int virtualViewId = generateVirtualViewIdForKey(key);
         final String keyDescription = getKeyDescription(key);
-
         final AccessibilityEvent event = AccessibilityEvent.obtain(eventType);
         event.setPackageName(mKeyboardView.getContext().getPackageName());
         event.setClassName(key.getClass().getName());
         event.setContentDescription(keyDescription);
         event.setEnabled(true);
-
         final AccessibilityRecordCompat record = new AccessibilityRecordCompat(event);
         record.setSource(mKeyboardView, virtualViewId);
-
         return event;
     }
 
@@ -145,68 +139,65 @@
      * </p>
      *
      * @param virtualViewId A client defined virtual view id.
-     * @return A populated {@link AccessibilityNodeInfoCompat} for a virtual
-     *         descendant or the host View.
+     * @return A populated {@link AccessibilityNodeInfoCompat} for a virtual descendant or the host
+     * View.
      * @see AccessibilityNodeInfoCompat
      */
     @Override
-    public AccessibilityNodeInfoCompat createAccessibilityNodeInfo(int virtualViewId) {
-        AccessibilityNodeInfoCompat info = null;
-
+    public AccessibilityNodeInfoCompat createAccessibilityNodeInfo(final int virtualViewId) {
         if (virtualViewId == UNDEFINED) {
             return null;
-        } else  if (virtualViewId == View.NO_ID) {
+        }
+        if (virtualViewId == View.NO_ID) {
             // We are requested to create an AccessibilityNodeInfo describing
             // this View, i.e. the root of the virtual sub-tree.
-            info = AccessibilityNodeInfoCompat.obtain(mKeyboardView);
-            ViewCompat.onInitializeAccessibilityNodeInfo(mKeyboardView, info);
+            final AccessibilityNodeInfoCompat rootInfo =
+                    AccessibilityNodeInfoCompat.obtain(mKeyboardView);
+            ViewCompat.onInitializeAccessibilityNodeInfo(mKeyboardView, rootInfo);
 
             // Add the virtual children of the root View.
             final Keyboard keyboard = mKeyboardView.getKeyboard();
             final Key[] keys = keyboard.mKeys;
             for (Key key : keys) {
                 final int childVirtualViewId = generateVirtualViewIdForKey(key);
-                info.addChild(mKeyboardView, childVirtualViewId);
+                rootInfo.addChild(mKeyboardView, childVirtualViewId);
             }
-        } else {
-            // Find the view that corresponds to the given id.
-            final Key key = mVirtualViewIdToKey.get(virtualViewId);
-            if (key == null) {
-                Log.e(TAG, "Invalid virtual view ID: " + virtualViewId);
-                return null;
-            }
-
-            final String keyDescription = getKeyDescription(key);
-            final Rect boundsInParent = key.mHitBox;
-
-            // Calculate the key's in-screen bounds.
-            mTempBoundsInScreen.set(boundsInParent);
-            mTempBoundsInScreen.offset(
-                    CoordinateUtils.x(mParentLocation), CoordinateUtils.y(mParentLocation));
-
-            final Rect boundsInScreen = mTempBoundsInScreen;
-
-            // Obtain and initialize an AccessibilityNodeInfo with
-            // information about the virtual view.
-            info = AccessibilityNodeInfoCompat.obtain();
-            info.setPackageName(mKeyboardView.getContext().getPackageName());
-            info.setClassName(key.getClass().getName());
-            info.setContentDescription(keyDescription);
-            info.setBoundsInParent(boundsInParent);
-            info.setBoundsInScreen(boundsInScreen);
-            info.setParent(mKeyboardView);
-            info.setSource(mKeyboardView, virtualViewId);
-            info.setBoundsInScreen(boundsInScreen);
-            info.setEnabled(true);
-            info.setVisibleToUser(true);
-
-            if (mAccessibilityFocusedView == virtualViewId) {
-                info.addAction(AccessibilityNodeInfoCompat.ACTION_CLEAR_ACCESSIBILITY_FOCUS);
-            } else {
-                info.addAction(AccessibilityNodeInfoCompat.ACTION_ACCESSIBILITY_FOCUS);
-            }
+            return rootInfo;
         }
 
+        // Find the view that corresponds to the given id.
+        final Key key = mVirtualViewIdToKey.get(virtualViewId);
+        if (key == null) {
+            Log.e(TAG, "Invalid virtual view ID: " + virtualViewId);
+            return null;
+        }
+        final String keyDescription = getKeyDescription(key);
+        final Rect boundsInParent = key.mHitBox;
+
+        // Calculate the key's in-screen bounds.
+        mTempBoundsInScreen.set(boundsInParent);
+        mTempBoundsInScreen.offset(
+                CoordinateUtils.x(mParentLocation), CoordinateUtils.y(mParentLocation));
+        final Rect boundsInScreen = mTempBoundsInScreen;
+
+        // Obtain and initialize an AccessibilityNodeInfo with information about the virtual view.
+        final AccessibilityNodeInfoCompat info = AccessibilityNodeInfoCompat.obtain();
+        info.setPackageName(mKeyboardView.getContext().getPackageName());
+        info.setClassName(key.getClass().getName());
+        info.setContentDescription(keyDescription);
+        info.setBoundsInParent(boundsInParent);
+        info.setBoundsInScreen(boundsInScreen);
+        info.setParent(mKeyboardView);
+        info.setSource(mKeyboardView, virtualViewId);
+        info.setBoundsInScreen(boundsInScreen);
+        info.setEnabled(true);
+        info.setVisibleToUser(true);
+
+        if (mAccessibilityFocusedView == virtualViewId) {
+            info.addAction(AccessibilityNodeInfoCompat.ACTION_CLEAR_ACCESSIBILITY_FOCUS);
+        } else {
+            info.addAction(AccessibilityNodeInfoCompat.ACTION_ACCESSIBILITY_FOCUS);
+        }
         return info;
     }
 
@@ -216,7 +207,7 @@
      *
      * @param key The key to press.
      */
-    void simulateKeyPress(Key key) {
+    void simulateKeyPress(final Key key) {
         final int x = key.mHitBox.centerX();
         final int y = key.mHitBox.centerY();
         final long downTime = SystemClock.uptimeMillis();
@@ -227,19 +218,17 @@
 
         mKeyboardView.onTouchEvent(downEvent);
         mKeyboardView.onTouchEvent(upEvent);
-
         downEvent.recycle();
         upEvent.recycle();
     }
 
     @Override
-    public boolean performAction(int virtualViewId, int action, Bundle arguments) {
+    public boolean performAction(final int virtualViewId, final int action,
+            final Bundle arguments) {
         final Key key = mVirtualViewIdToKey.get(virtualViewId);
-
         if (key == null) {
             return false;
         }
-
         return performActionForKey(key, action, arguments);
     }
 
@@ -249,10 +238,9 @@
      * @param key The on which to perform the action.
      * @param action The action to perform.
      * @param arguments The action's arguments.
-     * @return The result of performing the action, or false if the action is
-     *         not supported.
+     * @return The result of performing the action, or false if the action is not supported.
      */
-    boolean performActionForKey(Key key, int action, Bundle arguments) {
+    boolean performActionForKey(final Key key, final int action, final Bundle arguments) {
         final int virtualViewId = generateVirtualViewIdForKey(key);
 
         switch (action) {
@@ -272,9 +260,9 @@
             sendAccessibilityEventForKey(
                     key, AccessibilityEventCompat.TYPE_VIEW_ACCESSIBILITY_FOCUS_CLEARED);
             return true;
+        default:
+            return false;
         }
-
-        return false;
     }
 
     /**
@@ -283,7 +271,7 @@
      * @param key The key that's sending the event.
      * @param eventType The type of event to send.
      */
-    void sendAccessibilityEventForKey(Key key, int eventType) {
+    void sendAccessibilityEventForKey(final Key key, final int eventType) {
         final AccessibilityEvent event = createAccessibilityEvent(key, eventType);
         mAccessibilityUtils.requestSendAccessibilityEvent(event);
     }
@@ -294,12 +282,11 @@
      * @param key The key to describe.
      * @return The context-specific description of the key.
      */
-    private String getKeyDescription(Key key) {
+    private String getKeyDescription(final Key key) {
         final EditorInfo editorInfo = mInputMethodService.getCurrentInputEditorInfo();
         final boolean shouldObscure = mAccessibilityUtils.shouldObscureInput(editorInfo);
         final String keyDescription = mKeyCodeDescriptionMapper.getDescriptionForKey(
                 mKeyboardView.getContext(), mKeyboardView.getKeyboard(), key, shouldObscure);
-
         return keyDescription;
     }
 
@@ -311,7 +298,6 @@
         if (keyboard == null) {
             return;
         }
-
         mVirtualViewIdToKey.clear();
 
         final Key[] keys = keyboard.mKeys;
@@ -335,7 +321,7 @@
      * @param key The key to identify.
      * @return A virtual view identifier.
      */
-    private static int generateVirtualViewIdForKey(Key key) {
+    private static int generateVirtualViewIdForKey(final Key key) {
         // 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.
diff --git a/java/src/com/android/inputmethod/accessibility/AccessibilityUtils.java b/java/src/com/android/inputmethod/accessibility/AccessibilityUtils.java
index 0a700bd..bf1cea9 100644
--- a/java/src/com/android/inputmethod/accessibility/AccessibilityUtils.java
+++ b/java/src/com/android/inputmethod/accessibility/AccessibilityUtils.java
@@ -55,7 +55,7 @@
      */
     private static final boolean ENABLE_ACCESSIBILITY = true;
 
-    public static void init(InputMethodService inputMethod) {
+    public static void init(final InputMethodService inputMethod) {
         if (!ENABLE_ACCESSIBILITY) return;
 
         // These only need to be initialized if the kill switch is off.
@@ -72,7 +72,7 @@
         // This class is not publicly instantiable.
     }
 
-    private void initInternal(Context context) {
+    private void initInternal(final Context context) {
         mContext = context;
         mAccessibilityManager =
                 (AccessibilityManager) context.getSystemService(Context.ACCESSIBILITY_SERVICE);
@@ -100,9 +100,8 @@
      * @param event The event to check.
      * @return {@true} is the event is a touch exploration event
      */
-    public boolean isTouchExplorationEvent(MotionEvent event) {
+    public boolean isTouchExplorationEvent(final MotionEvent event) {
         final int action = event.getAction();
-
         return action == MotionEvent.ACTION_HOVER_ENTER
                 || action == MotionEvent.ACTION_HOVER_EXIT
                 || action == MotionEvent.ACTION_HOVER_MOVE;
@@ -114,7 +113,7 @@
      *
      * @return {@code true} if the device should obscure password characters.
      */
-    public boolean shouldObscureInput(EditorInfo editorInfo) {
+    public boolean shouldObscureInput(final EditorInfo editorInfo) {
         if (editorInfo == null) return false;
 
         // The user can optionally force speaking passwords.
@@ -140,7 +139,7 @@
      * @param view The source view.
      * @param text The text to speak.
      */
-    public void announceForAccessibility(View view, CharSequence text) {
+    public void announceForAccessibility(final View view, final CharSequence text) {
         if (!mAccessibilityManager.isEnabled()) {
             Log.e(TAG, "Attempted to speak when accessibility was disabled!");
             return;
@@ -157,8 +156,9 @@
         event.setEnabled(true);
         event.getText().add(text);
 
-        // Platforms starting at SDK 16 should use announce events.
-        if (Build.VERSION.SDK_INT >= 16) {
+        // Platforms starting at SDK version 16 (Build.VERSION_CODES.JELLY_BEAN) should use
+        // announce events.
+        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {
             event.setEventType(AccessibilityEventCompat.TYPE_ANNOUNCEMENT);
         } else {
             event.setEventType(AccessibilityEvent.TYPE_VIEW_FOCUSED);
@@ -181,7 +181,8 @@
      * @param editorInfo The input connection's editor info attribute.
      * @param restarting Whether the connection is being restarted.
      */
-    public void onStartInputViewInternal(View view, EditorInfo editorInfo, boolean restarting) {
+    public void onStartInputViewInternal(final View view, final EditorInfo editorInfo,
+            final boolean restarting) {
         if (shouldObscureInput(editorInfo)) {
             final CharSequence text = mContext.getText(R.string.spoken_use_headphones);
             announceForAccessibility(view, text);
@@ -194,7 +195,7 @@
      *
      * @param event The event to send.
      */
-    public void requestSendAccessibilityEvent(AccessibilityEvent event) {
+    public void requestSendAccessibilityEvent(final AccessibilityEvent event) {
         if (mAccessibilityManager.isEnabled()) {
             mAccessibilityManager.sendAccessibilityEvent(event);
         }
diff --git a/java/src/com/android/inputmethod/accessibility/AccessibleKeyboardViewProxy.java b/java/src/com/android/inputmethod/accessibility/AccessibleKeyboardViewProxy.java
index fcfa6d4..d73924d 100644
--- a/java/src/com/android/inputmethod/accessibility/AccessibleKeyboardViewProxy.java
+++ b/java/src/com/android/inputmethod/accessibility/AccessibleKeyboardViewProxy.java
@@ -42,12 +42,11 @@
     private Key mLastHoverKey = null;
 
     /**
-     * Inset in pixels to look for keys when the user's finger exits the
-     * keyboard area.
+     * Inset in pixels to look for keys when the user's finger exits the keyboard area.
      */
     private int mEdgeSlop;
 
-    public static void init(InputMethodService inputMethod) {
+    public static void init(final InputMethodService inputMethod) {
         sInstance.initInternal(inputMethod);
     }
 
@@ -59,7 +58,7 @@
         // Not publicly instantiable.
     }
 
-    private void initInternal(InputMethodService inputMethod) {
+    private void initInternal(final InputMethodService inputMethod) {
         mInputMethod = inputMethod;
         mEdgeSlop = inputMethod.getResources().getDimensionPixelSize(
                 R.dimen.accessibility_edge_slop);
@@ -70,61 +69,61 @@
      *
      * @param view The view to wrap.
      */
-    public void setView(MainKeyboardView view) {
+    public void setView(final MainKeyboardView view) {
         if (view == null) {
             // Ignore null views.
             return;
         }
-
         mView = view;
 
         // Ensure that the view has an accessibility delegate.
         ViewCompat.setAccessibilityDelegate(view, this);
 
-        if (mAccessibilityNodeProvider != null) {
-            mAccessibilityNodeProvider.setView(view);
+        if (mAccessibilityNodeProvider == null) {
+            return;
         }
+        mAccessibilityNodeProvider.setView(view);
     }
 
-    public void setKeyboard(Keyboard keyboard) {
-        if (mAccessibilityNodeProvider != null) {
-            mAccessibilityNodeProvider.setKeyboard(keyboard);
+    public void setKeyboard() {
+        if (mAccessibilityNodeProvider == null) {
+            return;
         }
+        mAccessibilityNodeProvider.setKeyboard();
     }
 
     /**
-     * Proxy method for View.getAccessibilityNodeProvider(). This method is
-     * called in SDK version 15 and higher to obtain the virtual node hierarchy
-     * provider.
+     * Proxy method for View.getAccessibilityNodeProvider(). This method is called in SDK
+     * version 15 (Build.VERSION_CODES.ICE_CREAM_SANDWICH_MR1) and higher to obtain the virtual
+     * node hierarchy provider.
      *
      * @return The accessibility node provider for the current keyboard.
      */
     @Override
-    public AccessibilityEntityProvider getAccessibilityNodeProvider(View host) {
+    public AccessibilityEntityProvider getAccessibilityNodeProvider(final View host) {
         return getAccessibilityNodeProvider();
     }
 
     /**
-     * Intercepts touch events before dispatch when touch exploration is turned
-     * on in ICS and higher.
+     * Intercepts touch events before dispatch when touch exploration is turned on in ICS and
+     * higher.
      *
      * @param event The motion event being dispatched.
      * @return {@code true} if the event is handled
      */
-    public boolean dispatchTouchEvent(MotionEvent event) {
+    public boolean dispatchTouchEvent(final MotionEvent event) {
         // To avoid accidental key presses during touch exploration, always drop
         // touch events generated by the user.
         return false;
     }
 
     /**
-     * Receives hover events when touch exploration is turned on in SDK versions
-     * ICS and higher.
+     * Receives hover events when touch exploration is turned on in SDK versions ICS and higher.
      *
      * @param event The hover event.
      * @return {@code true} if the event is handled
      */
-    public boolean dispatchHoverEvent(MotionEvent event, PointerTracker tracker) {
+    public boolean dispatchHoverEvent(final MotionEvent event, final PointerTracker tracker) {
         final int x = (int) event.getX();
         final int y = (int) event.getY();
         final Key previousKey = mLastHoverKey;
@@ -135,7 +134,6 @@
         } else {
             key = null;
         }
-
         mLastHoverKey = key;
 
         switch (event.getAction()) {
@@ -173,30 +171,29 @@
     }
 
     /**
-     * Utility method to determine whether the given point, in local
-     * coordinates, is inside the view, where the area of the view is contracted
-     * by the edge slop factor.
+     * Utility method to determine whether the given point, in local coordinates, is inside the
+     * view, where the area of the view is contracted by the edge slop factor.
      *
      * @param localX The local x-coordinate.
      * @param localY The local y-coordinate.
      */
-    private boolean pointInView(int localX, int localY) {
+    private boolean pointInView(final int localX, final int localY) {
         return (localX >= mEdgeSlop) && (localY >= mEdgeSlop)
                 && (localX < (mView.getWidth() - mEdgeSlop))
                 && (localY < (mView.getHeight() - mEdgeSlop));
     }
 
     /**
-     * Simulates a transition between two {@link Key}s by sending a HOVER_EXIT
-     * on the previous key, a HOVER_ENTER on the current key, and a HOVER_MOVE
-     * on the current key.
+     * Simulates a transition between two {@link Key}s by sending a HOVER_EXIT on the previous key,
+     * a HOVER_ENTER on the current key, and a HOVER_MOVE on the current key.
      *
      * @param currentKey The currently hovered key.
      * @param previousKey The previously hovered key.
      * @param event The event that triggered the transition.
      * @return {@code true} if the event was handled.
      */
-    private boolean onTransitionKey(Key currentKey, Key previousKey, MotionEvent event) {
+    private boolean onTransitionKey(final Key currentKey, final Key previousKey,
+            final MotionEvent event) {
         final int savedAction = event.getAction();
 
         event.setAction(MotionEvent.ACTION_HOVER_EXIT);
@@ -214,19 +211,18 @@
     }
 
     /**
-     * Handles a hover event on a key. If {@link Key} extended View, this would
-     * be analogous to calling View.onHoverEvent(MotionEvent).
+     * Handles a hover event on a key. If {@link Key} extended View, this would be analogous to
+     * calling View.onHoverEvent(MotionEvent).
      *
      * @param key The currently hovered key.
      * @param event The hover event.
      * @return {@code true} if the event was handled.
      */
-    private boolean onHoverKey(Key key, MotionEvent event) {
+    private boolean onHoverKey(final Key key, final MotionEvent event) {
         // Null keys can't receive events.
         if (key == null) {
             return false;
         }
-
         final AccessibilityEntityProvider provider = getAccessibilityNodeProvider();
 
         switch (event.getAction()) {
@@ -241,7 +237,6 @@
                     key, AccessibilityEventCompat.TYPE_VIEW_HOVER_EXIT);
             break;
         }
-
         return true;
     }
 
@@ -268,7 +263,6 @@
         default:
             text = context.getText(R.string.spoken_description_shiftmode_off);
         }
-
         AccessibilityUtils.getInstance().announceForAccessibility(mView, text);
     }
 
@@ -307,7 +301,6 @@
         if (resId < 0) {
             return;
         }
-
         final String text = context.getString(resId);
         AccessibilityUtils.getInstance().announceForAccessibility(mView, text);
     }
diff --git a/java/src/com/android/inputmethod/accessibility/KeyCodeDescriptionMapper.java b/java/src/com/android/inputmethod/accessibility/KeyCodeDescriptionMapper.java
index b87ae3a..6a01b01 100644
--- a/java/src/com/android/inputmethod/accessibility/KeyCodeDescriptionMapper.java
+++ b/java/src/com/android/inputmethod/accessibility/KeyCodeDescriptionMapper.java
@@ -93,17 +93,17 @@
      * @param keyboard The keyboard on which the key resides.
      * @param key The key from which to obtain a description.
      * @param shouldObscure {@true} if text (e.g. non-control) characters should be obscured.
-     * @return a character sequence describing the action performed by pressing
-     *         the key
+     * @return a character sequence describing the action performed by pressing the key
      */
-    public String getDescriptionForKey(Context context, Keyboard keyboard, Key key,
-            boolean shouldObscure) {
+    public String getDescriptionForKey(final Context context, final Keyboard keyboard,
+            final Key key, final boolean shouldObscure) {
         final int code = key.mCode;
 
         if (code == Constants.CODE_SWITCH_ALPHA_SYMBOL) {
             final String description = getDescriptionForSwitchAlphaSymbol(context, keyboard);
-            if (description != null)
+            if (description != null) {
                 return description;
+            }
         }
 
         if (code == Constants.CODE_SHIFT) {
@@ -127,7 +127,6 @@
         if (key.mCode != Constants.CODE_UNSPECIFIED) {
             return getDescriptionForKeyCode(context, keyboard, key, shouldObscure);
         }
-
         return null;
     }
 
@@ -138,10 +137,10 @@
      *
      * @param context The package's context.
      * @param keyboard The keyboard on which the key resides.
-     * @return a character sequence describing the action performed by pressing
-     *         the key
+     * @return a character sequence describing the action performed by pressing the key
      */
-    private String getDescriptionForSwitchAlphaSymbol(Context context, Keyboard keyboard) {
+    private String getDescriptionForSwitchAlphaSymbol(final Context context,
+            final Keyboard keyboard) {
         final KeyboardId keyboardId = keyboard.mId;
         final int elementId = keyboardId.mElementId;
         final int resId;
@@ -168,7 +167,6 @@
             Log.e(TAG, "Missing description for keyboard element ID:" + elementId);
             return null;
         }
-
         return context.getString(resId);
     }
 
@@ -179,7 +177,7 @@
      * @param keyboard The keyboard on which the key resides.
      * @return A context-sensitive description of the "Shift" key.
      */
-    private String getDescriptionForShiftKey(Context context, Keyboard keyboard) {
+    private String getDescriptionForShiftKey(final Context context, final Keyboard keyboard) {
         final KeyboardId keyboardId = keyboard.mId;
         final int elementId = keyboardId.mElementId;
         final int resId;
@@ -197,7 +195,6 @@
         default:
             resId = R.string.spoken_description_shift;
         }
-
         return context.getString(resId);
     }
 
@@ -207,10 +204,10 @@
      * @param context The package's context.
      * @param keyboard The keyboard on which the key resides.
      * @param key The key to describe.
-     * @return Returns a context-sensitive description of the "Enter" action
-     *         key.
+     * @return Returns a context-sensitive description of the "Enter" action key.
      */
-    private String getDescriptionForActionKey(Context context, Keyboard keyboard, Key key) {
+    private String getDescriptionForActionKey(final Context context, final Keyboard keyboard,
+            final Key key) {
         final KeyboardId keyboardId = keyboard.mId;
         final int actionId = keyboardId.imeActionId();
         final int resId;
@@ -243,7 +240,6 @@
         default:
             resId = R.string.spoken_description_return;
         }
-
         return context.getString(resId);
     }
 
@@ -265,11 +261,10 @@
      * @param keyboard The keyboard on which the key resides.
      * @param key The key from which to obtain a description.
      * @param shouldObscure {@true} if text (e.g. non-control) characters should be obscured.
-     * @return a character sequence describing the action performed by pressing
-     *         the key
+     * @return a character sequence describing the action performed by pressing the key
      */
-    private String getDescriptionForKeyCode(Context context, Keyboard keyboard, Key key,
-            boolean shouldObscure) {
+    private String getDescriptionForKeyCode(final Context context, final Keyboard keyboard,
+            final Key key, final boolean shouldObscure) {
         final int code = key.mCode;
 
         // If the key description should be obscured, now is the time to do it.
@@ -277,15 +272,15 @@
         if (shouldObscure && isDefinedNonCtrl) {
             return context.getString(OBSCURED_KEY_RES_ID);
         }
-
         if (mKeyCodeMap.indexOfKey(code) >= 0) {
             return context.getString(mKeyCodeMap.get(code));
-        } else if (isDefinedNonCtrl) {
-            return Character.toString((char) code);
-        } else if (!TextUtils.isEmpty(key.mLabel)) {
-            return key.mLabel;
-        } else {
-            return context.getString(R.string.spoken_description_unknown, code);
         }
+        if (isDefinedNonCtrl) {
+            return Character.toString((char) code);
+        }
+        if (!TextUtils.isEmpty(key.mLabel)) {
+            return key.mLabel;
+        }
+        return context.getString(R.string.spoken_description_unknown, code);
     }
 }
diff --git a/java/src/com/android/inputmethod/compat/CompatUtils.java b/java/src/com/android/inputmethod/compat/CompatUtils.java
index ffed6ec..a82103a 100644
--- a/java/src/com/android/inputmethod/compat/CompatUtils.java
+++ b/java/src/com/android/inputmethod/compat/CompatUtils.java
@@ -31,8 +31,8 @@
     private static final String INPUT_METHOD_SUBTYPE_SETTINGS =
             "android.settings.INPUT_METHOD_SUBTYPE_SETTINGS";
 
-    public static Intent getInputLanguageSelectionIntent(String inputMethodId,
-            int flagsForSubtypeSettings) {
+    public static Intent getInputLanguageSelectionIntent(final String inputMethodId,
+            final int flagsForSubtypeSettings) {
         // Refer to android.provider.Settings.ACTION_INPUT_METHOD_SUBTYPE_SETTINGS
         final String action = INPUT_METHOD_SUBTYPE_SETTINGS;
         final Intent intent = new Intent(action);
@@ -45,7 +45,7 @@
         return intent;
     }
 
-    public static Class<?> getClass(String className) {
+    public static Class<?> getClass(final String className) {
         try {
             return Class.forName(className);
         } catch (ClassNotFoundException e) {
@@ -53,8 +53,8 @@
         }
     }
 
-    public static Method getMethod(Class<?> targetClass, String name,
-            Class<?>... parameterTypes) {
+    public static Method getMethod(final Class<?> targetClass, final String name,
+            final Class<?>... parameterTypes) {
         if (targetClass == null || TextUtils.isEmpty(name)) return null;
         try {
             return targetClass.getMethod(name, parameterTypes);
@@ -66,7 +66,7 @@
         return null;
     }
 
-    public static Field getField(Class<?> targetClass, String name) {
+    public static Field getField(final Class<?> targetClass, final String name) {
         if (targetClass == null || TextUtils.isEmpty(name)) return null;
         try {
             return targetClass.getField(name);
@@ -78,7 +78,8 @@
         return null;
     }
 
-    public static Constructor<?> getConstructor(Class<?> targetClass, Class<?> ... types) {
+    public static Constructor<?> getConstructor(final Class<?> targetClass,
+            final Class<?> ... types) {
         if (targetClass == null || types == null) return null;
         try {
             return targetClass.getConstructor(types);
@@ -90,7 +91,7 @@
         return null;
     }
 
-    public static Object newInstance(Constructor<?> constructor, Object ... args) {
+    public static Object newInstance(final Constructor<?> constructor, final Object ... args) {
         if (constructor == null) return null;
         try {
             return constructor.newInstance(args);
@@ -100,8 +101,8 @@
         return null;
     }
 
-    public static Object invoke(
-            Object receiver, Object defaultValue, Method method, Object... args) {
+    public static Object invoke(final Object receiver, final Object defaultValue,
+            final Method method, final Object... args) {
         if (method == null) return defaultValue;
         try {
             return method.invoke(receiver, args);
@@ -111,7 +112,8 @@
         return defaultValue;
     }
 
-    public static Object getFieldValue(Object receiver, Object defaultValue, Field field) {
+    public static Object getFieldValue(final Object receiver, final Object defaultValue,
+            final Field field) {
         if (field == null) return defaultValue;
         try {
             return field.get(receiver);
@@ -121,7 +123,7 @@
         return defaultValue;
     }
 
-    public static void setFieldValue(Object receiver, Field field, Object value) {
+    public static void setFieldValue(final Object receiver, final Field field, final Object value) {
         if (field == null) return;
         try {
             field.set(receiver, value);
diff --git a/java/src/com/android/inputmethod/compat/EditorInfoCompatUtils.java b/java/src/com/android/inputmethod/compat/EditorInfoCompatUtils.java
index 210058b..7eefa22 100644
--- a/java/src/com/android/inputmethod/compat/EditorInfoCompatUtils.java
+++ b/java/src/com/android/inputmethod/compat/EditorInfoCompatUtils.java
@@ -21,23 +21,23 @@
 import java.lang.reflect.Field;
 
 public final class EditorInfoCompatUtils {
-    // EditorInfo.IME_FLAG_FORCE_ASCII has been introduced since API#16 (JellyBean).
+    // Note that EditorInfo.IME_FLAG_FORCE_ASCII has been introduced
+    // in API level 16 (Build.VERSION_CODES.JELLY_BEAN).
     private static final Field FIELD_IME_FLAG_FORCE_ASCII = CompatUtils.getField(
             EditorInfo.class, "IME_FLAG_FORCE_ASCII");
-    private static final Integer OBJ_IME_FLAG_FORCE_ASCII = (Integer) CompatUtils
-            .getFieldValue(null, null, FIELD_IME_FLAG_FORCE_ASCII);
+    private static final Integer OBJ_IME_FLAG_FORCE_ASCII = (Integer) CompatUtils.getFieldValue(
+            null /* receiver */, null /* defaultValue */, FIELD_IME_FLAG_FORCE_ASCII);
 
     private EditorInfoCompatUtils() {
         // This utility class is not publicly instantiable.
     }
 
-    public static boolean hasFlagForceAscii(int imeOptions) {
-        if (OBJ_IME_FLAG_FORCE_ASCII == null)
-            return false;
+    public static boolean hasFlagForceAscii(final int imeOptions) {
+        if (OBJ_IME_FLAG_FORCE_ASCII == null) return false;
         return (imeOptions & OBJ_IME_FLAG_FORCE_ASCII) != 0;
     }
 
-    public static String imeActionName(int imeOptions) {
+    public static String imeActionName(final int imeOptions) {
         final int actionId = imeOptions & EditorInfo.IME_MASK_ACTION;
         switch (actionId) {
         case EditorInfo.IME_ACTION_UNSPECIFIED:
@@ -61,7 +61,7 @@
         }
     }
 
-    public static String imeOptionsName(int imeOptions) {
+    public static String imeOptionsName(final int imeOptions) {
         final String action = imeActionName(imeOptions);
         final StringBuilder flags = new StringBuilder();
         if ((imeOptions & EditorInfo.IME_FLAG_NO_ENTER_ACTION) != 0) {
diff --git a/java/src/com/android/inputmethod/compat/InputMethodManagerCompatWrapper.java b/java/src/com/android/inputmethod/compat/InputMethodManagerCompatWrapper.java
index 8bd1e52..a80c3fe 100644
--- a/java/src/com/android/inputmethod/compat/InputMethodManagerCompatWrapper.java
+++ b/java/src/com/android/inputmethod/compat/InputMethodManagerCompatWrapper.java
@@ -23,6 +23,8 @@
 import java.lang.reflect.Method;
 
 public final class InputMethodManagerCompatWrapper {
+    // Note that InputMethodManager.switchToNextInputMethod() has been introduced
+    // in API level 16 (Build.VERSION_CODES.JELLY_BEAN).
     private static final Method METHOD_switchToNextInputMethod = CompatUtils.getMethod(
             InputMethodManager.class, "switchToNextInputMethod", IBinder.class, Boolean.TYPE);
 
@@ -33,7 +35,7 @@
     }
 
     public boolean switchToNextInputMethod(final IBinder token, final boolean onlyCurrentIme) {
-        return (Boolean)CompatUtils.invoke(mImm, false, METHOD_switchToNextInputMethod, token,
-                onlyCurrentIme);
+        return (Boolean)CompatUtils.invoke(mImm, false /* defaultValue */,
+                METHOD_switchToNextInputMethod, token, onlyCurrentIme);
     }
 }
diff --git a/java/src/com/android/inputmethod/compat/InputMethodServiceCompatUtils.java b/java/src/com/android/inputmethod/compat/InputMethodServiceCompatUtils.java
index 8eea31e..14ee654 100644
--- a/java/src/com/android/inputmethod/compat/InputMethodServiceCompatUtils.java
+++ b/java/src/com/android/inputmethod/compat/InputMethodServiceCompatUtils.java
@@ -21,6 +21,8 @@
 import java.lang.reflect.Method;
 
 public final class InputMethodServiceCompatUtils {
+    // Note that InputMethodService.enableHardwareAcceleration() has been introduced
+    // in API level 17 (Build.VERSION_CODES.JELLY_BEAN_MR1).
     private static final Method METHOD_enableHardwareAcceleration =
             CompatUtils.getMethod(InputMethodService.class, "enableHardwareAcceleration");
 
@@ -28,7 +30,8 @@
         // This utility class is not publicly instantiable.
     }
 
-    public static boolean enableHardwareAcceleration(InputMethodService ims) {
-        return (Boolean)CompatUtils.invoke(ims, false, METHOD_enableHardwareAcceleration);
+    public static boolean enableHardwareAcceleration(final InputMethodService ims) {
+        return (Boolean)CompatUtils.invoke(ims, false /* defaultValue */,
+                METHOD_enableHardwareAcceleration);
     }
 }
diff --git a/java/src/com/android/inputmethod/compat/SettingsSecureCompatUtils.java b/java/src/com/android/inputmethod/compat/SettingsSecureCompatUtils.java
index db5abd0..cfaf7ec 100644
--- a/java/src/com/android/inputmethod/compat/SettingsSecureCompatUtils.java
+++ b/java/src/com/android/inputmethod/compat/SettingsSecureCompatUtils.java
@@ -19,6 +19,8 @@
 import java.lang.reflect.Field;
 
 public final class SettingsSecureCompatUtils {
+    // Note that Settings.Secure.ACCESSIBILITY_SPEAK_PASSWORD has been introduced
+    // in API level 15 (Build.VERSION_CODES.ICE_CREAM_SANDWICH_MR1).
     private static final Field FIELD_ACCESSIBILITY_SPEAK_PASSWORD = CompatUtils.getField(
             android.provider.Settings.Secure.class, "ACCESSIBILITY_SPEAK_PASSWORD");
 
@@ -30,5 +32,5 @@
      * Whether to speak passwords while in accessibility mode.
      */
     public static final String ACCESSIBILITY_SPEAK_PASSWORD = (String) CompatUtils.getFieldValue(
-            null, null, FIELD_ACCESSIBILITY_SPEAK_PASSWORD);
+            null /* receiver */, null /* defaultValue */, FIELD_ACCESSIBILITY_SPEAK_PASSWORD);
 }
diff --git a/java/src/com/android/inputmethod/compat/SuggestionSpanUtils.java b/java/src/com/android/inputmethod/compat/SuggestionSpanUtils.java
index 0e3634d..141e08a 100644
--- a/java/src/com/android/inputmethod/compat/SuggestionSpanUtils.java
+++ b/java/src/com/android/inputmethod/compat/SuggestionSpanUtils.java
@@ -34,11 +34,12 @@
 public final class SuggestionSpanUtils {
     private static final String TAG = SuggestionSpanUtils.class.getSimpleName();
 
-    // Note that SuggestionSpan.FLAG_AUTO_CORRECTION was added in API level 15.
-    public static final Field FIELD_FLAG_AUTO_CORRECTION =
-            CompatUtils.getField(SuggestionSpan.class, "FLAG_AUTO_CORRECTION");
-    public static final Integer OBJ_FLAG_AUTO_CORRECTION =
-            (Integer) CompatUtils.getFieldValue(null, null, FIELD_FLAG_AUTO_CORRECTION);
+    // Note that SuggestionSpan.FLAG_AUTO_CORRECTION has been introduced
+    // in API level 15 (Build.VERSION_CODES.ICE_CREAM_SANDWICH_MR1).
+    public static final Field FIELD_FLAG_AUTO_CORRECTION = CompatUtils.getField(
+            SuggestionSpan.class, "FLAG_AUTO_CORRECTION");
+    public static final Integer OBJ_FLAG_AUTO_CORRECTION = (Integer) CompatUtils.getFieldValue(
+            null /* receiver */, null /* defaultValue */, FIELD_FLAG_AUTO_CORRECTION);
 
     static {
         if (LatinImeLogger.sDBG) {
@@ -58,8 +59,9 @@
             return text;
         }
         final Spannable spannable = new SpannableString(text);
-        final SuggestionSpan suggestionSpan = new SuggestionSpan(context, null, new String[] {},
-                (int)OBJ_FLAG_AUTO_CORRECTION, SuggestionSpanPickedNotificationReceiver.class);
+        final SuggestionSpan suggestionSpan = new SuggestionSpan(context, null /* locale */,
+                new String[] {} /* suggestions */, (int)OBJ_FLAG_AUTO_CORRECTION,
+                SuggestionSpanPickedNotificationReceiver.class);
         spannable.setSpan(suggestionSpan, 0, text.length(),
                 Spanned.SPAN_EXCLUSIVE_EXCLUSIVE | Spanned.SPAN_COMPOSING);
         return spannable;
@@ -87,8 +89,8 @@
 
         // TODO: We should avoid adding suggestion span candidates that came from the bigram
         // prediction.
-        final SuggestionSpan suggestionSpan = new SuggestionSpan(context, null,
-                suggestionsList.toArray(new String[suggestionsList.size()]), 0,
+        final SuggestionSpan suggestionSpan = new SuggestionSpan(context, null /* locale */,
+                suggestionsList.toArray(new String[suggestionsList.size()]), 0 /* flags */,
                 SuggestionSpanPickedNotificationReceiver.class);
         spannable.setSpan(suggestionSpan, 0, pickedWord.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
         return spannable;
diff --git a/java/src/com/android/inputmethod/compat/SuggestionsInfoCompatUtils.java b/java/src/com/android/inputmethod/compat/SuggestionsInfoCompatUtils.java
index 8314212..d8d2be0 100644
--- a/java/src/com/android/inputmethod/compat/SuggestionsInfoCompatUtils.java
+++ b/java/src/com/android/inputmethod/compat/SuggestionsInfoCompatUtils.java
@@ -21,10 +21,13 @@
 import java.lang.reflect.Field;
 
 public final class SuggestionsInfoCompatUtils {
-    private static final Field FIELD_RESULT_ATTR_HAS_RECOMMENDED_SUGGESTIONS = CompatUtils.getField(
-            SuggestionsInfo.class, "RESULT_ATTR_HAS_RECOMMENDED_SUGGESTIONS");
-    private static final Integer OBJ_RESULT_ATTR_HAS_RECOMMENDED_SUGGESTIONS = (Integer) CompatUtils
-            .getFieldValue(null, null, FIELD_RESULT_ATTR_HAS_RECOMMENDED_SUGGESTIONS);
+    // Note that SuggestionsInfo.RESULT_ATTR_HAS_RECOMMENDED_SUGGESTIONS has been introduced
+    // in API level 15 (Build.VERSION_CODES.ICE_CREAM_SANDWICH_MR1).
+    private static final Field FIELD_RESULT_ATTR_HAS_RECOMMENDED_SUGGESTIONS =
+            CompatUtils.getField(SuggestionsInfo.class, "RESULT_ATTR_HAS_RECOMMENDED_SUGGESTIONS");
+    private static final Integer OBJ_RESULT_ATTR_HAS_RECOMMENDED_SUGGESTIONS =
+            (Integer) CompatUtils.getFieldValue(null /* receiver */, null /* defaultValue */,
+                    FIELD_RESULT_ATTR_HAS_RECOMMENDED_SUGGESTIONS);
     private static final int RESULT_ATTR_HAS_RECOMMENDED_SUGGESTIONS =
             OBJ_RESULT_ATTR_HAS_RECOMMENDED_SUGGESTIONS != null
                     ? OBJ_RESULT_ATTR_HAS_RECOMMENDED_SUGGESTIONS : 0;
diff --git a/java/src/com/android/inputmethod/event/Event.java b/java/src/com/android/inputmethod/event/Event.java
index c96a336..215e4de 100644
--- a/java/src/com/android/inputmethod/event/Event.java
+++ b/java/src/com/android/inputmethod/event/Event.java
@@ -29,8 +29,64 @@
  * The combiner should figure out what to do with this.
  */
 public class Event {
+    // Should the types below be represented by separate classes instead? It would be cleaner
+    // but probably a bit too much
+    // An event we don't handle in Latin IME, for example pressing Ctrl on a hardware keyboard.
+    final public static int EVENT_NOT_HANDLED = 0;
+    // A character that is already final, for example pressing an alphabetic character on a
+    // hardware qwerty keyboard.
+    final public static int EVENT_COMMITTABLE = 1;
+    // A dead key, which means a character that should combine with what is coming next. Examples
+    // include the "^" character on an azerty keyboard which combines with "e" to make "ê", or
+    // AltGr+' on a dvorak international keyboard which combines with "e" to make "é". This is
+    // true regardless of the language or combining mode, and should be seen as a property of the
+    // key - a dead key followed by another key with which it can combine should be regarded as if
+    // the keyboard actually had such a key.
+    final public static int EVENT_DEAD = 2;
+    // A toggle event is triggered by a key that affects the previous character. An example would
+    // be a numeric key on a 10-key keyboard, which would toggle between 1 - a - b - c with
+    // repeated presses.
+    final public static int EVENT_TOGGLE = 3;
+    // A mode event instructs the combiner to change modes. The canonical example would be the
+    // hankaku/zenkaku key on a Japanese keyboard, or even the caps lock key on a qwerty keyboard
+    // if handled at the combiner level.
+    final public static int EVENT_MODE_KEY = 4;
+
+    final private static int NOT_A_CODE_POINT = 0;
+
+    private int mType; // The type of event - one of the constants above
+    // The code point associated with the event, if relevant. This is a unicode code point, and
+    // has nothing to do with other representations of the key. It is only relevant if this event
+    // is the right type: COMMITTABLE or DEAD or TOGGLE, but for a mode key like hankaku/zenkaku or
+    // ctrl, there is no code point associated so this should be NOT_A_CODE_POINT to avoid
+    // unintentional use of its value when it's not relevant.
+    private int mCodePoint;
+
     static Event obtainEvent() {
         // TODO: create an event pool instead
         return new Event();
     }
+
+    public void setDeadEvent(final int codePoint) {
+        mType = EVENT_DEAD;
+        mCodePoint = codePoint;
+    }
+
+    public void setCommittableEvent(final int codePoint) {
+        mType = EVENT_COMMITTABLE;
+        mCodePoint = codePoint;
+    }
+
+    public void setNotHandledEvent() {
+        mType = EVENT_NOT_HANDLED;
+        mCodePoint = NOT_A_CODE_POINT; // Just in case
+    }
+
+    public boolean isCommittable() {
+        return EVENT_COMMITTABLE == mType;
+    }
+
+    public int getCodePoint() {
+        return mCodePoint;
+    }
 }
diff --git a/java/src/com/android/inputmethod/event/EventInterpreter.java b/java/src/com/android/inputmethod/event/EventInterpreter.java
index 443c269..1bd0cca 100644
--- a/java/src/com/android/inputmethod/event/EventInterpreter.java
+++ b/java/src/com/android/inputmethod/event/EventInterpreter.java
@@ -16,8 +16,12 @@
 
 package com.android.inputmethod.event;
 
+import android.util.SparseArray;
 import android.view.KeyEvent;
 
+import com.android.inputmethod.latin.Constants;
+import com.android.inputmethod.latin.LatinIME;
+
 /**
  * This class implements the logic between receiving events and generating code points.
  *
@@ -32,18 +36,45 @@
     // TODO: Create an object type to represent input material + visual feedback + decoding state
     // TODO: Create an interface to call back to Latin IME through the above object
 
-    // TODO: replace this with an associative container to bind device id -> decoder
-    HardwareEventDecoder mHardwareEventDecoder;
-    SoftwareEventDecoder mSoftwareEventDecoder;
+    final EventDecoderSpec mDecoderSpec;
+    final SparseArray<HardwareEventDecoder> mHardwareEventDecoders;
+    final SoftwareEventDecoder mSoftwareEventDecoder;
+    final LatinIME mLatinIme;
 
-    public EventInterpreter() {
-        this(null);
+    /**
+     * Create a default interpreter.
+     *
+     * This creates a default interpreter that does nothing. A default interpreter should normally
+     * only be used for fallback purposes, when we really don't know what we want to do with input.
+     *
+     * @param latinIme a reference to the ime.
+     */
+    public EventInterpreter(final LatinIME latinIme) {
+        this(null, latinIme);
     }
 
-    public EventInterpreter(final EventDecoderSpec specification) {
-        // TODO: create the decoding chain from a specification. The decoders should be
-        // created lazily
-        mHardwareEventDecoder = new HardwareKeyboardEventDecoder(0);
+    /**
+     * Create an event interpreter according to a specification.
+     *
+     * The specification contains information about what to do with events. Typically, it will
+     * contain information about the type of keyboards - for example, if hardware keyboard(s) is/are
+     * attached, their type will be included here so that the decoder knows what to do with each
+     * keypress (a 10-key keyboard is not handled like a qwerty-ish keyboard).
+     * It also contains information for combining characters. For example, if the input language
+     * is Japanese, the specification will typically request kana conversion.
+     * Also note that the specification can be null. This means that we need to create a default
+     * interpreter that does no specific combining, and assumes the most common cases.
+     *
+     * @param specification the specification for event interpretation. null for default.
+     * @param latinIme a reference to the ime.
+     */
+    public EventInterpreter(final EventDecoderSpec specification, final LatinIME latinIme) {
+        mDecoderSpec = null != specification ? specification : new EventDecoderSpec();
+        // For both, we expect to have only one decoder in almost all cases, hence the default
+        // capacity of 1.
+        mHardwareEventDecoders = new SparseArray<HardwareEventDecoder>(1);
+        mSoftwareEventDecoder = new SoftwareKeyboardEventDecoder();
+        mLatinIme = latinIme;
     }
 
     // Helper method to decode a hardware key event into a generic event, and execute any
@@ -60,15 +91,26 @@
     }
 
     private HardwareEventDecoder getHardwareKeyEventDecoder(final int deviceId) {
-        // TODO: look up the decoder by device id. It should be created lazily
-        return mHardwareEventDecoder;
+        final HardwareEventDecoder decoder = mHardwareEventDecoders.get(deviceId);
+        if (null != decoder) return decoder;
+        // TODO: create the decoder according to the specification
+        final HardwareEventDecoder newDecoder = new HardwareKeyboardEventDecoder(deviceId);
+        mHardwareEventDecoders.put(deviceId, newDecoder);
+        return newDecoder;
     }
 
     private SoftwareEventDecoder getSoftwareEventDecoder() {
+        // Within the context of Latin IME, since we never present several software interfaces
+        // at the time, we should never need multiple software event decoders at a time.
         return mSoftwareEventDecoder;
     }
 
     private boolean onEvent(final Event event) {
+        if (event.isCommittable()) {
+            mLatinIme.onCodeInput(event.getCodePoint(),
+                    Constants.EXTERNAL_KEYBOARD_COORDINATE, Constants.EXTERNAL_KEYBOARD_COORDINATE);
+            return true;
+        }
         // TODO: Classify the event - input or non-input (see design doc)
         // TODO: IF action event
         //          Send decoded action back to LatinIME
diff --git a/java/src/com/android/inputmethod/event/HardwareKeyboardEventDecoder.java b/java/src/com/android/inputmethod/event/HardwareKeyboardEventDecoder.java
index 9aa6a27..2dbc9f0 100644
--- a/java/src/com/android/inputmethod/event/HardwareKeyboardEventDecoder.java
+++ b/java/src/com/android/inputmethod/event/HardwareKeyboardEventDecoder.java
@@ -16,10 +16,17 @@
 
 package com.android.inputmethod.event;
 
+import android.view.KeyCharacterMap;
 import android.view.KeyEvent;
 
+import com.android.inputmethod.latin.Constants;
+
 /**
  * A hardware event decoder for a hardware qwerty-ish keyboard.
+ *
+ * The events are always hardware keypresses, but they can be key down or key up events, they
+ * can be dead keys, they can be meta keys like shift or ctrl... This does not deal with
+ * 10-key like keyboards; a different decoder is used for this.
  */
 public class HardwareKeyboardEventDecoder implements HardwareEventDecoder {
     final int mDeviceId;
@@ -30,7 +37,33 @@
     }
 
     @Override
-    public Event decodeHardwareKey(KeyEvent keyEvent) {
-        return Event.obtainEvent();
+    public Event decodeHardwareKey(final KeyEvent keyEvent) {
+        final Event event = Event.obtainEvent();
+        // KeyEvent#getUnicodeChar() does not exactly returns a unicode char, but rather a value
+        // that includes both the unicode char in the lower 21 bits and flags in the upper bits,
+        // hence the name "codePointAndFlags". {@see KeyEvent#getUnicodeChar()} for more info.
+        final int codePointAndFlags = keyEvent.getUnicodeChar();
+        // The keyCode is the abstraction used by the KeyEvent to represent different keys that
+        // do not necessarily map to a unicode character. This represents a physical key, like
+        // the key for 'A' or Space, but also Backspace or Ctrl or Caps Lock.
+        final int keyCode = keyEvent.getKeyCode();
+        if (KeyEvent.KEYCODE_DEL == keyCode) {
+            event.setCommittableEvent(Constants.CODE_DELETE);
+            return event;
+        }
+        if (keyEvent.isPrintingKey() || KeyEvent.KEYCODE_SPACE == keyCode
+                || KeyEvent.KEYCODE_ENTER == keyCode) {
+            if (0 != (codePointAndFlags & KeyCharacterMap.COMBINING_ACCENT)) {
+                // A dead key.
+                event.setDeadEvent(codePointAndFlags & KeyCharacterMap.COMBINING_ACCENT_MASK);
+            } else {
+                // A committable character. This should be committed right away, taking into
+                // account the current state.
+                event.setCommittableEvent(codePointAndFlags);
+            }
+        } else {
+            event.setNotHandledEvent();
+        }
+        return event;
     }
 }
diff --git a/java/src/com/android/inputmethod/event/SoftwareEventDecoder.java b/java/src/com/android/inputmethod/event/SoftwareEventDecoder.java
index 0b80d5c..d81ee0b 100644
--- a/java/src/com/android/inputmethod/event/SoftwareEventDecoder.java
+++ b/java/src/com/android/inputmethod/event/SoftwareEventDecoder.java
@@ -17,7 +17,12 @@
 package com.android.inputmethod.event;
 
 /**
- * An event decoder for software events.
+ * An event decoder for events out of a software keyboard.
+ *
+ * This defines the interface for an event decoder that supports events out of a software keyboard.
+ * This differs significantly from hardware keyboard event decoders in several respects. First,
+ * a software keyboard does not have a scancode/layout system; the keypresses that insert
+ * characters output unicode characters directly.
  */
 public interface SoftwareEventDecoder extends EventDecoder {
     public Event decodeSoftwareEvent();
diff --git a/java/src/com/android/inputmethod/event/SoftwareKeyboardEventDecoder.java b/java/src/com/android/inputmethod/event/SoftwareKeyboardEventDecoder.java
new file mode 100644
index 0000000..de91567
--- /dev/null
+++ b/java/src/com/android/inputmethod/event/SoftwareKeyboardEventDecoder.java
@@ -0,0 +1,27 @@
+/*
+ * 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.event;
+
+/**
+ * A decoder for events from software keyboard, like the ones displayed by Latin IME.
+ */
+public class SoftwareKeyboardEventDecoder implements SoftwareEventDecoder {
+    @Override
+    public Event decodeSoftwareEvent() {
+        return null;
+    }
+}
diff --git a/java/src/com/android/inputmethod/keyboard/KeyboardSwitcher.java b/java/src/com/android/inputmethod/keyboard/KeyboardSwitcher.java
index c1c105e..1088fda 100644
--- a/java/src/com/android/inputmethod/keyboard/KeyboardSwitcher.java
+++ b/java/src/com/android/inputmethod/keyboard/KeyboardSwitcher.java
@@ -19,6 +19,7 @@
 import android.content.Context;
 import android.content.SharedPreferences;
 import android.content.res.Resources;
+import android.preference.PreferenceManager;
 import android.util.Log;
 import android.view.ContextThemeWrapper;
 import android.view.LayoutInflater;
@@ -50,7 +51,7 @@
 
         // Note: The themeId should be aligned with "themeId" attribute of Keyboard style
         // in values/style.xml.
-        public KeyboardTheme(int themeId, int styleId) {
+        public KeyboardTheme(final int themeId, final int styleId) {
             mThemeId = themeId;
             mStyleId = styleId;
         }
@@ -65,7 +66,8 @@
         new KeyboardTheme(5, R.style.KeyboardTheme_IceCreamSandwich),
     };
 
-    private AudioAndHapticFeedbackManager mFeedbackManager;
+    private final AudioAndHapticFeedbackManager mFeedbackManager =
+            AudioAndHapticFeedbackManager.getInstance();
     private SubtypeSwitcher mSubtypeSwitcher;
     private SharedPreferences mPrefs;
 
@@ -95,21 +97,22 @@
         // Intentional empty constructor for singleton.
     }
 
-    public static void init(LatinIME latinIme, SharedPreferences prefs) {
+    public static void init(final LatinIME latinIme) {
+        final SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(latinIme);
         sInstance.initInternal(latinIme, prefs);
     }
 
-    private void initInternal(LatinIME latinIme, SharedPreferences prefs) {
+    private void initInternal(final LatinIME latinIme, final SharedPreferences prefs) {
         mLatinIME = latinIme;
         mResources = latinIme.getResources();
-        mFeedbackManager = new AudioAndHapticFeedbackManager(latinIme);
         mPrefs = prefs;
         mSubtypeSwitcher = SubtypeSwitcher.getInstance();
         mState = new KeyboardState(this);
         setContextThemeWrapper(latinIme, getKeyboardTheme(latinIme, prefs));
     }
 
-    private static KeyboardTheme getKeyboardTheme(Context context, SharedPreferences prefs) {
+    private static KeyboardTheme getKeyboardTheme(final Context context,
+            final SharedPreferences prefs) {
         final String defaultIndex = context.getString(R.string.config_default_keyboard_theme_index);
         final String themeIndex = prefs.getString(PREF_KEYBOARD_LAYOUT, defaultIndex);
         try {
@@ -124,7 +127,7 @@
         return KEYBOARD_THEMES[0];
     }
 
-    private void setContextThemeWrapper(Context context, KeyboardTheme keyboardTheme) {
+    private void setContextThemeWrapper(final Context context, final KeyboardTheme keyboardTheme) {
         if (mThemeContext == null || mKeyboardTheme.mThemeId != keyboardTheme.mThemeId) {
             mKeyboardTheme = keyboardTheme;
             mThemeContext = new ContextThemeWrapper(context, keyboardTheme.mStyleId);
@@ -132,7 +135,7 @@
         }
     }
 
-    public void loadKeyboard(EditorInfo editorInfo, SettingsValues settingsValues) {
+    public void loadKeyboard(final EditorInfo editorInfo, final SettingsValues settingsValues) {
         final KeyboardLayoutSet.Builder builder = new KeyboardLayoutSet.Builder(
                 mThemeContext, editorInfo);
         final Resources res = mThemeContext.getResources();
@@ -210,14 +213,14 @@
         mState.onResetKeyboardStateToAlphabet();
     }
 
-    public void onPressKey(int code) {
+    public void onPressKey(final int code) {
         if (isVibrateAndSoundFeedbackRequired()) {
             mFeedbackManager.hapticAndAudioFeedback(code, mKeyboardView);
         }
         mState.onPressKey(code, isSinglePointer(), mLatinIME.getCurrentAutoCapsState());
     }
 
-    public void onReleaseKey(int code, boolean withSliding) {
+    public void onReleaseKey(final int code, final boolean withSliding) {
         mState.onReleaseKey(code, withSliding);
     }
 
@@ -303,7 +306,7 @@
 
     // Implements {@link KeyboardState.SwitchActions}.
     @Override
-    public void startLongPressTimer(int code) {
+    public void startLongPressTimer(final int code) {
         final MainKeyboardView keyboardView = getMainKeyboardView();
         if (keyboardView != null) {
             final TimerProxy timer = keyboardView.getTimerProxy();
@@ -323,11 +326,11 @@
 
     // Implements {@link KeyboardState.SwitchActions}.
     @Override
-    public void hapticAndAudioFeedback(int code) {
+    public void hapticAndAudioFeedback(final int code) {
         mFeedbackManager.hapticAndAudioFeedback(code, mKeyboardView);
     }
 
-    public void onLongPressTimeout(int code) {
+    public void onLongPressTimeout(final int code) {
         mState.onLongPressTimeout(code);
     }
 
@@ -346,7 +349,7 @@
     /**
      * Updates state machine to figure out when to automatically switch back to the previous mode.
      */
-    public void onCodeInput(int code) {
+    public void onCodeInput(final int code) {
         mState.onCodeInput(code, isSinglePointer(), mLatinIME.getCurrentAutoCapsState());
     }
 
@@ -354,7 +357,7 @@
         return mKeyboardView;
     }
 
-    public View onCreateInputView(boolean isHardwareAcceleratedDrawingEnabled) {
+    public View onCreateInputView(final boolean isHardwareAcceleratedDrawingEnabled) {
         if (mKeyboardView != null) {
             mKeyboardView.closing();
         }
@@ -383,7 +386,7 @@
         }
     }
 
-    public void onAutoCorrectionStateChanged(boolean isAutoCorrection) {
+    public void onAutoCorrectionStateChanged(final boolean isAutoCorrection) {
         if (mIsAutoCorrectionActive != isAutoCorrection) {
             mIsAutoCorrectionActive = isAutoCorrection;
             if (mKeyboardView != null) {
diff --git a/java/src/com/android/inputmethod/keyboard/KeyboardView.java b/java/src/com/android/inputmethod/keyboard/KeyboardView.java
index 61d3874..b7bee34 100644
--- a/java/src/com/android/inputmethod/keyboard/KeyboardView.java
+++ b/java/src/com/android/inputmethod/keyboard/KeyboardView.java
@@ -50,6 +50,7 @@
 import com.android.inputmethod.latin.R;
 import com.android.inputmethod.latin.StaticInnerHandlerWrapper;
 import com.android.inputmethod.latin.StringUtils;
+import com.android.inputmethod.latin.SuggestedWords;
 import com.android.inputmethod.latin.define.ProductionFlag;
 import com.android.inputmethod.research.ResearchLogger;
 
@@ -870,9 +871,9 @@
         mPreviewPlacerView.dismissSlidingKeyInputPreview();
     }
 
-    public void showGestureFloatingPreviewText(final String gestureFloatingPreviewText) {
+    public void showGestureFloatingPreviewText(final SuggestedWords suggestedWords) {
         locatePreviewPlacerView();
-        mPreviewPlacerView.setGestureFloatingPreviewText(gestureFloatingPreviewText);
+        mPreviewPlacerView.setGestureFloatingPreviewText(suggestedWords);
     }
 
     public void dismissGestureFloatingPreviewText() {
diff --git a/java/src/com/android/inputmethod/keyboard/MainKeyboardView.java b/java/src/com/android/inputmethod/keyboard/MainKeyboardView.java
index 584d2fe..79aab9c 100644
--- a/java/src/com/android/inputmethod/keyboard/MainKeyboardView.java
+++ b/java/src/com/android/inputmethod/keyboard/MainKeyboardView.java
@@ -393,8 +393,8 @@
         mHasDistinctMultitouch = hasDistinctMultitouch && !forceNonDistinctMultitouch;
         final Resources res = getResources();
         final boolean needsPhantomSuddenMoveEventHack = Boolean.parseBoolean(
-                ResourceUtils.getDeviceOverrideValue(res,
-                        R.array.phantom_sudden_move_event_device_list, "false"));
+                ResourceUtils.getDeviceOverrideValue(
+                        res, R.array.phantom_sudden_move_event_device_list));
         PointerTracker.init(needsPhantomSuddenMoveEventHack);
 
         final TypedArray a = context.obtainStyledAttributes(
@@ -530,7 +530,7 @@
 
         // This always needs to be set since the accessibility state can
         // potentially change without the keyboard being set again.
-        AccessibleKeyboardViewProxy.getInstance().setKeyboard(keyboard);
+        AccessibleKeyboardViewProxy.getInstance().setKeyboard();
     }
 
     // Note that this method is called from a non-UI thread.
diff --git a/java/src/com/android/inputmethod/keyboard/internal/AbstractDrawingPreview.java b/java/src/com/android/inputmethod/keyboard/internal/AbstractDrawingPreview.java
new file mode 100644
index 0000000..8a3f064
--- /dev/null
+++ b/java/src/com/android/inputmethod/keyboard/internal/AbstractDrawingPreview.java
@@ -0,0 +1,49 @@
+/*
+ * 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.keyboard.internal;
+
+import android.graphics.Canvas;
+
+import com.android.inputmethod.keyboard.PointerTracker;
+
+/**
+ * Abstract base class for previews that are drawn on PreviewPlacerView, e.g.,
+ * GestureFloatingPrevewText, GestureTrail, and SlidingKeyInputPreview.
+ */
+public abstract class AbstractDrawingPreview {
+    private boolean mPreviewEnabled;
+
+    public void setPreviewEnabled(final boolean enabled) {
+        mPreviewEnabled = enabled;
+    }
+
+    public boolean isPreviewEnabled() {
+        return mPreviewEnabled;
+    }
+
+    /**
+     * Draws the preview
+     * @param canvas The canvas where the preview is drawn.
+     */
+    public abstract void onDraw(final Canvas canvas);
+
+    /**
+     * Set the position of the preview.
+     * @param pt The new location of the preview is based on the points in PointerTracker pt.
+     */
+    public abstract void setPreviewPosition(final PointerTracker pt);
+}
diff --git a/java/src/com/android/inputmethod/keyboard/internal/GestureFloatingPreviewText.java b/java/src/com/android/inputmethod/keyboard/internal/GestureFloatingPreviewText.java
new file mode 100644
index 0000000..84cfb38
--- /dev/null
+++ b/java/src/com/android/inputmethod/keyboard/internal/GestureFloatingPreviewText.java
@@ -0,0 +1,179 @@
+/*
+ * 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.keyboard.internal;
+
+import android.content.Context;
+import android.content.res.TypedArray;
+import android.graphics.Canvas;
+import android.graphics.Color;
+import android.graphics.Paint;
+import android.graphics.Paint.Align;
+import android.graphics.Rect;
+import android.graphics.RectF;
+import android.text.TextUtils;
+
+import com.android.inputmethod.keyboard.PointerTracker;
+import com.android.inputmethod.latin.CoordinateUtils;
+import com.android.inputmethod.latin.R;
+import com.android.inputmethod.latin.ResizableIntArray;
+import com.android.inputmethod.latin.SuggestedWords;
+
+/**
+ * The class for single gesture preview text. The class for multiple gesture preview text will be
+ * derived from it.
+ */
+public class GestureFloatingPreviewText extends AbstractDrawingPreview {
+    private static final class GesturePreviewTextParams {
+        public final int mGesturePreviewTextSize;
+        public final int mGesturePreviewTextColor;
+        public final int mGesturePreviewTextDimmedColor;
+        public final int mGesturePreviewTextOffset;
+        public final int mGesturePreviewTextHeight;
+        public final int mGesturePreviewColor;
+        public final float mGesturePreviewHorizontalPadding;
+        public final float mGesturePreviewVerticalPadding;
+        public final float mGesturePreviewRoundRadius;
+        public final Paint mTextPaint;
+
+        private static final char[] TEXT_HEIGHT_REFERENCE_CHAR = { 'M' };
+
+        public GesturePreviewTextParams(final TypedArray keyboardViewAttr) {
+            mGesturePreviewTextSize = keyboardViewAttr.getDimensionPixelSize(
+                    R.styleable.KeyboardView_gestureFloatingPreviewTextSize, 0);
+            mGesturePreviewTextColor = keyboardViewAttr.getColor(
+                    R.styleable.KeyboardView_gestureFloatingPreviewTextColor, 0);
+            mGesturePreviewTextOffset = keyboardViewAttr.getDimensionPixelOffset(
+                    R.styleable.KeyboardView_gestureFloatingPreviewTextOffset, 0);
+            mGesturePreviewColor = keyboardViewAttr.getColor(
+                    R.styleable.KeyboardView_gestureFloatingPreviewColor, 0);
+            mGesturePreviewHorizontalPadding = keyboardViewAttr.getDimension(
+                    R.styleable.KeyboardView_gestureFloatingPreviewHorizontalPadding, 0.0f);
+            mGesturePreviewVerticalPadding = keyboardViewAttr.getDimension(
+                    R.styleable.KeyboardView_gestureFloatingPreviewVerticalPadding, 0.0f);
+            mGesturePreviewRoundRadius = keyboardViewAttr.getDimension(
+                    R.styleable.KeyboardView_gestureFloatingPreviewRoundRadius, 0.0f);
+            mGesturePreviewTextDimmedColor = Color.GRAY;
+
+            final Paint textPaint = new Paint();
+            textPaint.setAntiAlias(true);
+            textPaint.setTextAlign(Align.CENTER);
+            textPaint.setTextSize(mGesturePreviewTextSize);
+            mTextPaint = textPaint;
+            final Rect textRect = new Rect();
+            textPaint.getTextBounds(TEXT_HEIGHT_REFERENCE_CHAR, 0, 1, textRect);
+            mGesturePreviewTextHeight = textRect.height();
+        }
+    }
+
+    protected final GesturePreviewTextParams mParams;
+    protected int mPreviewWordNum;
+    protected final RectF mGesturePreviewRectangle = new RectF();
+    protected int mHighlightedWordIndex;
+
+    private static final int PREVIEW_TEXT_ARRAY_CAPACITY = 10;
+    // These variables store the positions of preview words. In multi-preview mode, the gesture
+    // floating preview at most shows PREVIEW_TEXT_ARRAY_CAPACITY words.
+    protected final ResizableIntArray mPreviewTextXArray = new ResizableIntArray(
+            PREVIEW_TEXT_ARRAY_CAPACITY);
+    protected final ResizableIntArray mPreviewTextYArray = new ResizableIntArray(
+            PREVIEW_TEXT_ARRAY_CAPACITY);
+
+    protected SuggestedWords mSuggestedWords = SuggestedWords.EMPTY;
+    protected final Context mContext;
+    public final int[] mLastPointerCoords = CoordinateUtils.newInstance();
+
+    public GestureFloatingPreviewText(final TypedArray typedArray, final Context context) {
+        mParams = new GesturePreviewTextParams(typedArray);
+        mHighlightedWordIndex = 0;
+        mContext = context;
+    }
+
+    public void setSuggetedWords(final SuggestedWords suggestedWords) {
+        if (suggestedWords == null) {
+            mSuggestedWords = SuggestedWords.EMPTY;
+        } else {
+            mSuggestedWords = suggestedWords;
+        }
+        updatePreviewPosition();
+    }
+
+    protected void drawText(final Canvas canvas, final String text, final float textX,
+            final float textY, final int color) {
+        final Paint paint = mParams.mTextPaint;
+        paint.setColor(color);
+        canvas.drawText(text, textX, textY, paint);
+    }
+
+    @Override
+    public void setPreviewPosition(final PointerTracker pt) {
+        pt.getLastCoordinates(mLastPointerCoords);
+        updatePreviewPosition();
+    }
+
+    /**
+     * Draws gesture preview text
+     * @param canvas The canvas where preview text is drawn.
+     */
+    @Override
+    public void onDraw(final Canvas canvas) {
+        if (!isPreviewEnabled() || mSuggestedWords.isEmpty()
+                || TextUtils.isEmpty(mSuggestedWords.getWord(0))) {
+            return;
+        }
+        final Paint paint = mParams.mTextPaint;
+        paint.setColor(mParams.mGesturePreviewColor);
+        final float round = mParams.mGesturePreviewRoundRadius;
+        canvas.drawRoundRect(mGesturePreviewRectangle, round, round, paint);
+        final String text = mSuggestedWords.getWord(0);
+        final int textX = mPreviewTextXArray.get(0);
+        final int textY = mPreviewTextYArray.get(0);
+        drawText(canvas, text, textX, textY, mParams.mGesturePreviewTextColor);
+    }
+
+    /**
+     * Updates gesture preview text position based on mLastPointerCoords.
+     */
+    protected void updatePreviewPosition() {
+        if (mSuggestedWords.isEmpty() || TextUtils.isEmpty(mSuggestedWords.getWord(0))) {
+            return;
+        }
+        final String text = mSuggestedWords.getWord(0);
+
+        final Paint paint = mParams.mTextPaint;
+        final RectF rectangle = mGesturePreviewRectangle;
+
+        final int textHeight = mParams.mGesturePreviewTextHeight;
+        final float textWidth = paint.measureText(text);
+        final float hPad = mParams.mGesturePreviewHorizontalPadding;
+        final float vPad = mParams.mGesturePreviewVerticalPadding;
+        final float rectWidth = textWidth + hPad * 2.0f;
+        final float rectHeight = textHeight + vPad * 2.0f;
+
+        final int displayWidth = mContext.getResources().getDisplayMetrics().widthPixels;
+        final float rectX = Math.min(
+                Math.max(CoordinateUtils.x(mLastPointerCoords) - rectWidth / 2.0f, 0.0f),
+                displayWidth - rectWidth);
+        final float rectY = CoordinateUtils.y(mLastPointerCoords)
+                - mParams.mGesturePreviewTextOffset - rectHeight;
+        rectangle.set(rectX, rectY, rectX + rectWidth, rectY + rectHeight);
+
+        final int textX = (int)(rectX + hPad + textWidth / 2.0f);
+        final int textY = (int)(rectY + vPad) + textHeight;
+        mPreviewTextXArray.add(0, textX);
+        mPreviewTextYArray.add(0, textY);
+    }
+}
diff --git a/java/src/com/android/inputmethod/keyboard/internal/GesturePreviewTrail.java b/java/src/com/android/inputmethod/keyboard/internal/GesturePreviewTrail.java
index 3a57f67..f8949b2 100644
--- a/java/src/com/android/inputmethod/keyboard/internal/GesturePreviewTrail.java
+++ b/java/src/com/android/inputmethod/keyboard/internal/GesturePreviewTrail.java
@@ -101,6 +101,16 @@
         }
     }
 
+    /**
+     * Calculate the alpha of a gesture trail.
+     * A gesture trail starts from fully opaque. After mFadeStartDelay has been passed, the alpha
+     * of a trail reduces in proportion to the elapsed time. Then after mFadeDuration has been
+     * passed, a trail becomes fully transparent.
+     *
+     * @param elapsedTime the elapsed time since a trail has been made.
+     * @param params gesture trail display parameters
+     * @return the width of a gesture trail
+     */
     private static int getAlpha(final int elapsedTime, final Params params) {
         if (elapsedTime < params.mFadeoutStartDelay) {
             return Constants.Color.ALPHA_OPAQUE;
@@ -111,10 +121,19 @@
         return Constants.Color.ALPHA_OPAQUE - decreasingAlpha;
     }
 
+    /**
+     * Calculate the width of a gesture trail.
+     * A gesture trail starts from the width of mTrailStartWidth and reduces its width in proportion
+     * to the elapsed time. After mTrailEndWidth has been passed, the width becomes mTraiLEndWidth.
+     *
+     * @param elapsedTime the elapsed time since a trail has been made.
+     * @param params gesture trail display parameters
+     * @return the width of a gesture trail
+     */
     private static float getWidth(final int elapsedTime, final Params params) {
-        return Math.max((params.mTrailLingerDuration - elapsedTime)
-                * (params.mTrailStartWidth - params.mTrailEndWidth)
-                / params.mTrailLingerDuration, 0.0f);
+        final int deltaTime = params.mTrailLingerDuration - elapsedTime;
+        final float deltaWidth = params.mTrailStartWidth - params.mTrailEndWidth;
+        return (deltaTime * deltaWidth) / params.mTrailLingerDuration + params.mTrailEndWidth;
     }
 
     private final RoundedLine mRoundedLine = new RoundedLine();
@@ -154,7 +173,7 @@
             final RoundedLine line = mRoundedLine;
             int p1x = getXCoordValue(xCoords[startIndex]);
             int p1y = yCoords[startIndex];
-            int lastTime = sinceDown - eventTimes[startIndex];
+            final int lastTime = sinceDown - eventTimes[startIndex];
             float maxWidth = getWidth(lastTime, params);
             float r1 = maxWidth / 2.0f;
             // Initialize bounds rectangle.
@@ -167,20 +186,19 @@
                 final float r2 = width / 2.0f;
                 // Draw trail line only when the current point isn't a down point.
                 if (!isDownEventXCoord(xCoords[i])) {
-                    final int alpha = getAlpha(elapsedTime, params);
-                    paint.setAlpha(alpha);
                     final Path path = line.makePath(p1x, p1y, r1, p2x, p2y, r2);
                     if (path != null) {
+                        final int alpha = getAlpha(elapsedTime, params);
+                        paint.setAlpha(alpha);
                         canvas.drawPath(path, paint);
+                        // Take union for the bounds.
                         outBoundsRect.union(p2x, p2y);
+                        maxWidth = Math.max(maxWidth, width);
                     }
-                    // Take union for the bounds.
-                    maxWidth = Math.max(maxWidth, width);
                 }
                 p1x = p2x;
                 p1y = p2y;
                 r1 = r2;
-                lastTime = elapsedTime;
             }
             // Take care of trail line width.
             final int inset = -((int)maxWidth + 1);
diff --git a/java/src/com/android/inputmethod/keyboard/internal/KeyboardBuilder.java b/java/src/com/android/inputmethod/keyboard/internal/KeyboardBuilder.java
index da418f4..0f1d5cc 100644
--- a/java/src/com/android/inputmethod/keyboard/internal/KeyboardBuilder.java
+++ b/java/src/com/android/inputmethod/keyboard/internal/KeyboardBuilder.java
@@ -20,6 +20,7 @@
 import android.content.res.Resources;
 import android.content.res.TypedArray;
 import android.content.res.XmlResourceParser;
+import android.text.TextUtils;
 import android.util.AttributeSet;
 import android.util.DisplayMetrics;
 import android.util.Log;
@@ -240,14 +241,14 @@
         try {
             final int displayHeight = mDisplayMetrics.heightPixels;
             final String keyboardHeightString = ResourceUtils.getDeviceOverrideValue(
-                    mResources, R.array.keyboard_heights, null);
+                    mResources, R.array.keyboard_heights);
             final float keyboardHeight;
-            if (keyboardHeightString != null) {
-                keyboardHeight = Float.parseFloat(keyboardHeightString)
-                        * mDisplayMetrics.density;
-            } else {
+            if (TextUtils.isEmpty(keyboardHeightString)) {
                 keyboardHeight = keyboardAttr.getDimension(
                         R.styleable.Keyboard_keyboardHeight, displayHeight / 2);
+            } else {
+                keyboardHeight = Float.parseFloat(keyboardHeightString)
+                        * mDisplayMetrics.density;
             }
             final float maxKeyboardHeight = ResourceUtils.getDimensionOrFraction(keyboardAttr,
                     R.styleable.Keyboard_maxKeyboardHeight, displayHeight, displayHeight / 2);
diff --git a/java/src/com/android/inputmethod/keyboard/internal/PreviewPlacerView.java b/java/src/com/android/inputmethod/keyboard/internal/PreviewPlacerView.java
index bc734b0..a005dc9 100644
--- a/java/src/com/android/inputmethod/keyboard/internal/PreviewPlacerView.java
+++ b/java/src/com/android/inputmethod/keyboard/internal/PreviewPlacerView.java
@@ -22,13 +22,10 @@
 import android.graphics.Canvas;
 import android.graphics.Color;
 import android.graphics.Paint;
-import android.graphics.Paint.Align;
 import android.graphics.PorterDuff;
 import android.graphics.PorterDuffXfermode;
 import android.graphics.Rect;
-import android.graphics.RectF;
 import android.os.Message;
-import android.text.TextUtils;
 import android.util.AttributeSet;
 import android.util.SparseArray;
 import android.widget.RelativeLayout;
@@ -39,15 +36,9 @@
 import com.android.inputmethod.latin.CoordinateUtils;
 import com.android.inputmethod.latin.R;
 import com.android.inputmethod.latin.StaticInnerHandlerWrapper;
+import com.android.inputmethod.latin.SuggestedWords;
 
 public final class PreviewPlacerView extends RelativeLayout {
-    private final int mGestureFloatingPreviewTextColor;
-    private final int mGestureFloatingPreviewTextOffset;
-    private final int mGestureFloatingPreviewColor;
-    private final float mGestureFloatingPreviewHorizontalPadding;
-    private final float mGestureFloatingPreviewVerticalPadding;
-    private final float mGestureFloatingPreviewRoundRadius;
-
     private final int[] mKeyboardViewOrigin = CoordinateUtils.newInstance();
 
     private final SparseArray<GesturePreviewTrail> mGesturePreviewTrails =
@@ -62,16 +53,7 @@
     private final Canvas mOffscreenCanvas = new Canvas();
     private final Rect mOffscreenDirtyRect = new Rect();
     private final Rect mGesturePreviewTrailBoundsRect = new Rect(); // per trail
-
-    private final Paint mTextPaint;
-    private String mGestureFloatingPreviewText;
-    private final int mGestureFloatingPreviewTextHeight;
-    // {@link RectF} is needed for {@link Canvas#drawRoundRect(RectF, float, float, Paint)}.
-    private final RectF mGestureFloatingPreviewRectangle = new RectF();
-    private final int[] mLastPointerCoords = CoordinateUtils.newInstance();
-    private static final char[] TEXT_HEIGHT_REFERENCE_CHAR = { 'M' };
-    private boolean mDrawsGestureFloatingPreviewText;
-
+    private final GestureFloatingPreviewText mGestureFloatingPreviewText;
     private boolean mShowSlidingKeyInputPreview;
     private final int[] mRubberBandFrom = CoordinateUtils.newInstance();
     private final int[] mRubberBandTo = CoordinateUtils.newInstance();
@@ -130,22 +112,11 @@
 
         final TypedArray keyboardViewAttr = context.obtainStyledAttributes(
                 attrs, R.styleable.KeyboardView, defStyle, R.style.KeyboardView);
-        final int gestureFloatingPreviewTextSize = keyboardViewAttr.getDimensionPixelSize(
-                R.styleable.KeyboardView_gestureFloatingPreviewTextSize, 0);
-        mGestureFloatingPreviewTextColor = keyboardViewAttr.getColor(
-                R.styleable.KeyboardView_gestureFloatingPreviewTextColor, 0);
-        mGestureFloatingPreviewTextOffset = keyboardViewAttr.getDimensionPixelOffset(
-                R.styleable.KeyboardView_gestureFloatingPreviewTextOffset, 0);
-        mGestureFloatingPreviewColor = keyboardViewAttr.getColor(
-                R.styleable.KeyboardView_gestureFloatingPreviewColor, 0);
-        mGestureFloatingPreviewHorizontalPadding = keyboardViewAttr.getDimension(
-                R.styleable.KeyboardView_gestureFloatingPreviewHorizontalPadding, 0.0f);
-        mGestureFloatingPreviewVerticalPadding = keyboardViewAttr.getDimension(
-                R.styleable.KeyboardView_gestureFloatingPreviewVerticalPadding, 0.0f);
-        mGestureFloatingPreviewRoundRadius = keyboardViewAttr.getDimension(
-                R.styleable.KeyboardView_gestureFloatingPreviewRoundRadius, 0.0f);
         final int gestureFloatingPreviewTextLingerTimeout = keyboardViewAttr.getInt(
                 R.styleable.KeyboardView_gestureFloatingPreviewTextLingerTimeout, 0);
+        // TODO: mGestureFloatingPreviewText could be an instance of GestureFloatingPreviewText or
+        // MultiGesturePreviewText, depending on the user's choice in the settings.
+        mGestureFloatingPreviewText = new GestureFloatingPreviewText(keyboardViewAttr, context);
         mGesturePreviewTrailParams = new Params(keyboardViewAttr);
         keyboardViewAttr.recycle();
 
@@ -157,15 +128,6 @@
         gesturePaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC));
         mGesturePaint = gesturePaint;
 
-        final Paint textPaint = new Paint();
-        textPaint.setAntiAlias(true);
-        textPaint.setTextAlign(Align.CENTER);
-        textPaint.setTextSize(gestureFloatingPreviewTextSize);
-        mTextPaint = textPaint;
-        final Rect textRect = new Rect();
-        textPaint.getTextBounds(TEXT_HEIGHT_REFERENCE_CHAR, 0, 1, textRect);
-        mGestureFloatingPreviewTextHeight = textRect.height();
-
         final Paint layerPaint = new Paint();
         layerPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC_OVER));
         setLayerType(LAYER_TYPE_HARDWARE, layerPaint);
@@ -181,14 +143,14 @@
     public void setGesturePreviewMode(final boolean drawsGesturePreviewTrail,
             final boolean drawsGestureFloatingPreviewText) {
         mDrawsGesturePreviewTrail = drawsGesturePreviewTrail;
-        mDrawsGestureFloatingPreviewText = drawsGestureFloatingPreviewText;
+        mGestureFloatingPreviewText.setPreviewEnabled(drawsGestureFloatingPreviewText);
     }
 
     public void invalidatePointer(final PointerTracker tracker, final boolean isOldestTracker) {
         final boolean needsToUpdateLastPointer =
-                isOldestTracker && mDrawsGestureFloatingPreviewText;
+                isOldestTracker && mGestureFloatingPreviewText.isPreviewEnabled();
         if (needsToUpdateLastPointer) {
-            tracker.getLastCoordinates(mLastPointerCoords);
+            mGestureFloatingPreviewText.setPreviewPosition(tracker);
         }
 
         if (mDrawsGesturePreviewTrail) {
@@ -252,6 +214,7 @@
         super.onDraw(canvas);
         final int originX = CoordinateUtils.x(mKeyboardViewOrigin);
         final int originY = CoordinateUtils.y(mKeyboardViewOrigin);
+        canvas.translate(originX, originY);
         if (mDrawsGesturePreviewTrail) {
             mayAllocateOffscreenBuffer();
             // Draw gesture trails to offscreen buffer.
@@ -259,11 +222,10 @@
                     mOffscreenCanvas, mGesturePaint, mOffscreenDirtyRect);
             // Transfer offscreen buffer to screen.
             if (!mOffscreenDirtyRect.isEmpty()) {
-                final int offsetY = originY - mOffscreenOffsetY;
-                canvas.translate(originX, offsetY);
+                canvas.translate(0, - mOffscreenOffsetY);
                 canvas.drawBitmap(mOffscreenBuffer, mOffscreenDirtyRect, mOffscreenDirtyRect,
                         mGesturePaint);
-                canvas.translate(-originX, -offsetY);
+                canvas.translate(0, mOffscreenOffsetY);
                 // Note: Defer clearing the dirty rectangle here because we will get cleared
                 // rectangle on the canvas.
             }
@@ -271,16 +233,11 @@
                 mDrawingHandler.postUpdateGestureTrailPreview();
             }
         }
-        if (mDrawsGestureFloatingPreviewText) {
-            canvas.translate(originX, originY);
-            drawGestureFloatingPreviewText(canvas, mGestureFloatingPreviewText);
-            canvas.translate(-originX, -originY);
-        }
+        mGestureFloatingPreviewText.onDraw(canvas);
         if (mShowSlidingKeyInputPreview) {
-            canvas.translate(originX, originY);
             drawSlidingKeyInputPreview(canvas);
-            canvas.translate(-originX, -originY);
         }
+        canvas.translate(-originX, -originY);
     }
 
     private boolean drawGestureTrails(final Canvas offscreenCanvas, final Paint paint,
@@ -322,9 +279,9 @@
                 Math.min(out.bottom, bottom));
     }
 
-    public void setGestureFloatingPreviewText(final String gestureFloatingPreviewText) {
-        if (!mDrawsGestureFloatingPreviewText) return;
-        mGestureFloatingPreviewText = gestureFloatingPreviewText;
+    public void setGestureFloatingPreviewText(final SuggestedWords suggestedWords) {
+        if (!mGestureFloatingPreviewText.isPreviewEnabled()) return;
+        mGestureFloatingPreviewText.setSuggetedWords(suggestedWords);
         invalidate();
     }
 
@@ -332,39 +289,6 @@
         mDrawingHandler.dismissGestureFloatingPreviewText();
     }
 
-    private void drawGestureFloatingPreviewText(final Canvas canvas,
-            final String gestureFloatingPreviewText) {
-        if (TextUtils.isEmpty(gestureFloatingPreviewText)) {
-            return;
-        }
-
-        final Paint paint = mTextPaint;
-        final RectF rectangle = mGestureFloatingPreviewRectangle;
-
-        // Paint the round rectangle background.
-        final int textHeight = mGestureFloatingPreviewTextHeight;
-        final float textWidth = paint.measureText(gestureFloatingPreviewText);
-        final float hPad = mGestureFloatingPreviewHorizontalPadding;
-        final float vPad = mGestureFloatingPreviewVerticalPadding;
-        final float rectWidth = textWidth + hPad * 2.0f;
-        final float rectHeight = textHeight + vPad * 2.0f;
-        final int canvasWidth = canvas.getWidth();
-        final float rectX = Math.min(
-                Math.max(CoordinateUtils.x(mLastPointerCoords) - rectWidth / 2.0f, 0.0f),
-                canvasWidth - rectWidth);
-        final float rectY = CoordinateUtils.y(mLastPointerCoords)
-                - mGestureFloatingPreviewTextOffset - rectHeight;
-        rectangle.set(rectX, rectY, rectX + rectWidth, rectY + rectHeight);
-        final float round = mGestureFloatingPreviewRoundRadius;
-        paint.setColor(mGestureFloatingPreviewColor);
-        canvas.drawRoundRect(rectangle, round, round, paint);
-        // Paint the text preview
-        paint.setColor(mGestureFloatingPreviewTextColor);
-        final float textX = rectX + hPad + textWidth / 2.0f;
-        final float textY = rectY + vPad + textHeight;
-        canvas.drawText(gestureFloatingPreviewText, textX, textY, paint);
-    }
-
     private void drawSlidingKeyInputPreview(final Canvas canvas) {
         // TODO: Implement rubber band preview
     }
diff --git a/java/src/com/android/inputmethod/keyboard/internal/TouchScreenRegulator.java b/java/src/com/android/inputmethod/keyboard/internal/TouchScreenRegulator.java
index c795d53..e7a0a70 100644
--- a/java/src/com/android/inputmethod/keyboard/internal/TouchScreenRegulator.java
+++ b/java/src/com/android/inputmethod/keyboard/internal/TouchScreenRegulator.java
@@ -55,7 +55,7 @@
     public TouchScreenRegulator(final Context context, final ProcessMotionEvent view) {
         mView = view;
         mNeedsSuddenJumpingHack = Boolean.parseBoolean(ResourceUtils.getDeviceOverrideValue(
-                context.getResources(), R.array.sudden_jumping_touch_event_device_list, "false"));
+                context.getResources(), R.array.sudden_jumping_touch_event_device_list));
     }
 
     public void setKeyboardGeometry(final int keyboardWidth) {
diff --git a/java/src/com/android/inputmethod/latin/AdditionalSubtype.java b/java/src/com/android/inputmethod/latin/AdditionalSubtype.java
index 509fc1b..820733b 100644
--- a/java/src/com/android/inputmethod/latin/AdditionalSubtype.java
+++ b/java/src/com/android/inputmethod/latin/AdditionalSubtype.java
@@ -45,7 +45,7 @@
             final String keyboardLayoutSetName, final String extraValue) {
         final String layoutExtraValue = KEYBOARD_LAYOUT_SET + "=" + keyboardLayoutSetName;
         final String layoutDisplayNameExtraValue;
-        if (Build.VERSION.SDK_INT >= /* JELLY_BEAN */ 15
+        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN
                 && SubtypeLocale.isExceptionalLocale(localeString)) {
             final String layoutDisplayName = SubtypeLocale.getKeyboardLayoutSetDisplayName(
                     keyboardLayoutSetName);
diff --git a/java/src/com/android/inputmethod/latin/AdditionalSubtypeSettings.java b/java/src/com/android/inputmethod/latin/AdditionalSubtypeSettings.java
index 08f08d2..6ac5a9b 100644
--- a/java/src/com/android/inputmethod/latin/AdditionalSubtypeSettings.java
+++ b/java/src/com/android/inputmethod/latin/AdditionalSubtypeSettings.java
@@ -387,7 +387,7 @@
         super.onCreate(savedInstanceState);
 
         mPrefs = getPreferenceManager().getSharedPreferences();
-        RichInputMethodManager.init(getActivity(), mPrefs);
+        RichInputMethodManager.init(getActivity());
         mRichImm = RichInputMethodManager.getInstance();
         addPreferencesFromResource(R.xml.additional_subtype_settings);
         setHasOptionsMenu(true);
diff --git a/java/src/com/android/inputmethod/latin/AudioAndHapticFeedbackManager.java b/java/src/com/android/inputmethod/latin/AudioAndHapticFeedbackManager.java
index 0247263..6367156 100644
--- a/java/src/com/android/inputmethod/latin/AudioAndHapticFeedbackManager.java
+++ b/java/src/com/android/inputmethod/latin/AudioAndHapticFeedbackManager.java
@@ -18,11 +18,10 @@
 
 import android.content.Context;
 import android.media.AudioManager;
+import android.os.Vibrator;
 import android.view.HapticFeedbackConstants;
 import android.view.View;
 
-import com.android.inputmethod.latin.VibratorUtils;
-
 /**
  * This class gathers audio feedback and haptic feedback functions.
  *
@@ -30,34 +29,63 @@
  * complexity of settings and the like.
  */
 public final class AudioAndHapticFeedbackManager {
-    private final AudioManager mAudioManager;
-    private final VibratorUtils mVibratorUtils;
+    public static final int MAX_KEYPRESS_VIBRATION_DURATION = 250; // millisecond
+
+    private AudioManager mAudioManager;
+    private Vibrator mVibrator;
 
     private SettingsValues mSettingsValues;
     private boolean mSoundOn;
 
-    public AudioAndHapticFeedbackManager(final LatinIME latinIme) {
-        mVibratorUtils = VibratorUtils.getInstance(latinIme);
-        mAudioManager = (AudioManager) latinIme.getSystemService(Context.AUDIO_SERVICE);
+    private static final AudioAndHapticFeedbackManager sInstance =
+            new AudioAndHapticFeedbackManager();
+
+    public static AudioAndHapticFeedbackManager getInstance() {
+        return sInstance;
+    }
+
+    private AudioAndHapticFeedbackManager() {
+        // Intentional empty constructor for singleton.
+    }
+
+    public static void init(final Context context) {
+        sInstance.initInternal(context);
+    }
+
+    private void initInternal(final Context context) {
+        mAudioManager = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE);
+        mVibrator = (Vibrator) context.getSystemService(Context.VIBRATOR_SERVICE);
     }
 
     public void hapticAndAudioFeedback(final int primaryCode,
             final View viewToPerformHapticFeedbackOn) {
-        vibrate(viewToPerformHapticFeedbackOn);
+        vibrateInternal(viewToPerformHapticFeedbackOn);
         playKeyClick(primaryCode);
     }
 
+    public boolean hasVibrator() {
+        return mVibrator != null && mVibrator.hasVibrator();
+    }
+
+    public void vibrate(final long milliseconds) {
+        if (mVibrator == null) {
+            return;
+        }
+        mVibrator.vibrate(milliseconds);
+    }
+
     private boolean reevaluateIfSoundIsOn() {
         if (mSettingsValues == null || !mSettingsValues.mSoundOn || mAudioManager == null) {
             return false;
-        } else {
-            return mAudioManager.getRingerMode() == AudioManager.RINGER_MODE_NORMAL;
         }
+        return mAudioManager.getRingerMode() == AudioManager.RINGER_MODE_NORMAL;
     }
 
-    private void playKeyClick(int primaryCode) {
+    private void playKeyClick(final int primaryCode) {
         // if mAudioManager is null, we can't play a sound anyway, so return
-        if (mAudioManager == null) return;
+        if (mAudioManager == null) {
+            return;
+        }
         if (mSoundOn) {
             final int sound;
             switch (primaryCode) {
@@ -78,7 +106,7 @@
         }
     }
 
-    private void vibrate(final View viewToPerformHapticFeedbackOn) {
+    private void vibrateInternal(final View viewToPerformHapticFeedbackOn) {
         if (!mSettingsValues.mVibrateOn) {
             return;
         }
@@ -89,9 +117,9 @@
                         HapticFeedbackConstants.KEYBOARD_TAP,
                         HapticFeedbackConstants.FLAG_IGNORE_GLOBAL_SETTING);
             }
-        } else if (mVibratorUtils != null) {
-            mVibratorUtils.vibrate(mSettingsValues.mKeypressVibrationDuration);
+            return;
         }
+        vibrate(mSettingsValues.mKeypressVibrationDuration);
     }
 
     public void onSettingsChanged(final SettingsValues settingsValues) {
diff --git a/java/src/com/android/inputmethod/latin/Constants.java b/java/src/com/android/inputmethod/latin/Constants.java
index 16ec5b5..3a77724 100644
--- a/java/src/com/android/inputmethod/latin/Constants.java
+++ b/java/src/com/android/inputmethod/latin/Constants.java
@@ -111,7 +111,7 @@
         }
     }
 
-    public static class TextUtils {
+    public static final class TextUtils {
         /**
          * Capitalization mode for {@link android.text.TextUtils#getCapsMode}: don't capitalize
          * characters.  This value may be used with
@@ -126,7 +126,7 @@
         }
     }
 
-    public static class Dictionary {
+    public static final class Dictionary {
         public static final int MAX_WORD_LENGTH = 48;
 
         private Dictionary() {
@@ -139,6 +139,7 @@
     public static final int NOT_A_COORDINATE = -1;
     public static final int SUGGESTION_STRIP_COORDINATE = -2;
     public static final int SPELL_CHECKER_COORDINATE = -3;
+    public static final int EXTERNAL_KEYBOARD_COORDINATE = -4;
 
     public static boolean isValidCoordinate(final int coordinate) {
         // Detect {@link NOT_A_COORDINATE}, {@link SUGGESTION_STRIP_COORDINATE},
diff --git a/java/src/com/android/inputmethod/latin/DictionaryCollection.java b/java/src/com/android/inputmethod/latin/DictionaryCollection.java
index 7f78ac8..9a771cf 100644
--- a/java/src/com/android/inputmethod/latin/DictionaryCollection.java
+++ b/java/src/com/android/inputmethod/latin/DictionaryCollection.java
@@ -38,7 +38,7 @@
         mDictionaries = CollectionUtils.newCopyOnWriteArrayList();
     }
 
-    public DictionaryCollection(final String dictType, Dictionary... dictionaries) {
+    public DictionaryCollection(final String dictType, final Dictionary... dictionaries) {
         super(dictType);
         if (null == dictionaries) {
             mDictionaries = CollectionUtils.newCopyOnWriteArrayList();
@@ -48,7 +48,7 @@
         }
     }
 
-    public DictionaryCollection(final String dictType, Collection<Dictionary> dictionaries) {
+    public DictionaryCollection(final String dictType, final Collection<Dictionary> dictionaries) {
         super(dictType);
         mDictionaries = CollectionUtils.newCopyOnWriteArrayList(dictionaries);
         mDictionaries.removeAll(Collections.singleton(null));
diff --git a/java/src/com/android/inputmethod/latin/LatinIME.java b/java/src/com/android/inputmethod/latin/LatinIME.java
index dcc131f..28994dc 100644
--- a/java/src/com/android/inputmethod/latin/LatinIME.java
+++ b/java/src/com/android/inputmethod/latin/LatinIME.java
@@ -35,6 +35,7 @@
 import android.inputmethodservice.InputMethodService;
 import android.media.AudioManager;
 import android.net.ConnectivityManager;
+import android.os.Build.VERSION_CODES;
 import android.os.Debug;
 import android.os.Handler;
 import android.os.HandlerThread;
@@ -72,7 +73,6 @@
 import com.android.inputmethod.keyboard.KeyboardSwitcher;
 import com.android.inputmethod.keyboard.KeyboardView;
 import com.android.inputmethod.keyboard.MainKeyboardView;
-import com.android.inputmethod.latin.LocaleUtils.RunInLocale;
 import com.android.inputmethod.latin.Utils.Stats;
 import com.android.inputmethod.latin.define.ProductionFlag;
 import com.android.inputmethod.latin.suggestions.SuggestionStripView;
@@ -127,7 +127,7 @@
     // Current space state of the input method. This can be any of the above constants.
     private int mSpaceState;
 
-    private SettingsValues mCurrentSettings;
+    private final Settings mSettings;
 
     private View mExtractArea;
     private View mKeyPreviewBackingView;
@@ -138,14 +138,12 @@
     private ApplicationInfo mTargetApplicationInfo;
 
     private RichInputMethodManager mRichImm;
-    private Resources mResources;
-    private SharedPreferences mPrefs;
     @UsedForTesting final KeyboardSwitcher mKeyboardSwitcher;
     private final SubtypeSwitcher mSubtypeSwitcher;
     private final SubtypeState mSubtypeState = new SubtypeState();
     // At start, create a default event interpreter that does nothing by passing it no decoder spec.
     // The event interpreter should never be null.
-    private EventInterpreter mEventInterpreter = new EventInterpreter();
+    private EventInterpreter mEventInterpreter = new EventInterpreter(this);
 
     private boolean mIsMainDictionaryAvailable;
     private UserBinaryDictionary mUserDictionary;
@@ -400,6 +398,7 @@
 
     public LatinIME() {
         super();
+        mSettings = Settings.getInstance();
         mSubtypeSwitcher = SubtypeSwitcher.getInstance();
         mKeyboardSwitcher = KeyboardSwitcher.getInstance();
         mIsHardwareAcceleratedDrawingEnabled =
@@ -409,17 +408,16 @@
 
     @Override
     public void onCreate() {
-        mPrefs = PreferenceManager.getDefaultSharedPreferences(this);
-        mResources = getResources();
-
-        LatinImeLogger.init(this, mPrefs);
+        Settings.init(this);
+        LatinImeLogger.init(this);
         if (ProductionFlag.IS_EXPERIMENTAL) {
-            ResearchLogger.getInstance().init(this, mPrefs);
+            ResearchLogger.getInstance().init(this);
         }
-        RichInputMethodManager.init(this, mPrefs);
+        RichInputMethodManager.init(this);
         mRichImm = RichInputMethodManager.getInstance();
         SubtypeSwitcher.init(this);
-        KeyboardSwitcher.init(this, mPrefs);
+        KeyboardSwitcher.init(this);
+        AudioAndHapticFeedbackManager.init(this);
         AccessibilityUtils.init(this);
 
         super.onCreate();
@@ -430,7 +428,7 @@
         loadSettings();
         initSuggest();
 
-        mDisplayOrientation = mResources.getConfiguration().orientation;
+        mDisplayOrientation = getResources().getConfiguration().orientation;
 
         // Register to receive ringer mode change and network state change.
         // Also receive installation and removal of a dictionary pack.
@@ -457,18 +455,10 @@
     // Has to be package-visible for unit tests
     @UsedForTesting
     void loadSettings() {
-        // Note that the calling sequence of onCreate() and onCurrentInputMethodSubtypeChanged()
-        // is not guaranteed. It may even be called at the same time on a different thread.
-        if (null == mPrefs) mPrefs = PreferenceManager.getDefaultSharedPreferences(this);
+        final Locale locale = mSubtypeSwitcher.getCurrentSubtypeLocale();
         final InputAttributes inputAttributes =
                 new InputAttributes(getCurrentInputEditorInfo(), isFullscreenMode());
-        final RunInLocale<SettingsValues> job = new RunInLocale<SettingsValues>() {
-            @Override
-            protected SettingsValues job(Resources res) {
-                return new SettingsValues(mPrefs, inputAttributes, LatinIME.this);
-            }
-        };
-        mCurrentSettings = job.runInLocale(mResources, mSubtypeSwitcher.getCurrentSubtypeLocale());
+        mSettings.loadSettings(locale, inputAttributes);
         resetContactsDictionary(null == mSuggest ? null : mSuggest.getContactsDictionary());
     }
 
@@ -495,8 +485,8 @@
         }
         mSuggest = new Suggest(this /* Context */, subtypeLocale,
                 this /* SuggestInitializationListener */);
-        if (mCurrentSettings.mCorrectionEnabled) {
-            mSuggest.setAutoCorrectionThreshold(mCurrentSettings.mAutoCorrectionThreshold);
+        if (mSettings.getCurrent().mCorrectionEnabled) {
+            mSuggest.setAutoCorrectionThreshold(mSettings.getCurrent().mAutoCorrectionThreshold);
         }
 
         mIsMainDictionaryAvailable = DictionaryFactory.isDictionaryAvailable(this, subtypeLocale);
@@ -510,10 +500,8 @@
 
         resetContactsDictionary(oldContactsDictionary);
 
-        // Note that the calling sequence of onCreate() and onCurrentInputMethodSubtypeChanged()
-        // is not guaranteed. It may even be called at the same time on a different thread.
-        if (null == mPrefs) mPrefs = PreferenceManager.getDefaultSharedPreferences(this);
-        mUserHistoryDictionary = UserHistoryDictionary.getInstance(this, localeStr, mPrefs);
+        final SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(this);
+        mUserHistoryDictionary = UserHistoryDictionary.getInstance(this, localeStr, prefs);
         mSuggest.setUserHistoryDictionary(mUserHistoryDictionary);
     }
 
@@ -526,7 +514,8 @@
      * @param oldContactsDictionary an optional dictionary to use, or null
      */
     private void resetContactsDictionary(final ContactsBinaryDictionary oldContactsDictionary) {
-        final boolean shouldSetDictionary = (null != mSuggest && mCurrentSettings.mUseContactsDict);
+        final boolean shouldSetDictionary =
+                (null != mSuggest && mSettings.getCurrent().mUseContactsDict);
 
         final ContactsBinaryDictionary dictionaryToUse;
         if (!shouldSetDictionary) {
@@ -570,6 +559,7 @@
             mSuggest.close();
             mSuggest = null;
         }
+        mSettings.onDestroy();
         unregisterReceiver(mReceiver);
         // TODO: The experimental version is not supported by the Dictionary Pack Service yet.
         if (!ProductionFlag.IS_EXPERIMENTAL) {
@@ -582,10 +572,6 @@
 
     @Override
     public void onConfigurationChanged(final Configuration conf) {
-        // System locale has been changed. Needs to reload keyboard.
-        if (mSubtypeSwitcher.onConfigurationChanged(conf)) {
-            loadKeyboard();
-        }
         // If orientation changed while predicting, commit the change
         if (mDisplayOrientation != conf.orientation) {
             mDisplayOrientation = conf.orientation;
@@ -651,7 +637,7 @@
     public void onCurrentInputMethodSubtypeChanged(final InputMethodSubtype subtype) {
         // Note that the calling sequence of onCreate() and onCurrentInputMethodSubtypeChanged()
         // is not guaranteed. It may even be called at the same time on a different thread.
-        mSubtypeSwitcher.updateSubtype(subtype);
+        mSubtypeSwitcher.onSubtypeChanged(subtype);
         loadKeyboard();
     }
 
@@ -684,7 +670,8 @@
                     + ((editorInfo.inputType & InputType.TYPE_TEXT_FLAG_CAP_WORDS) != 0));
         }
         if (ProductionFlag.IS_EXPERIMENTAL) {
-            ResearchLogger.latinIME_onStartInputViewInternal(editorInfo, mPrefs);
+            final SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(this);
+            ResearchLogger.latinIME_onStartInputViewInternal(editorInfo, prefs);
         }
         if (InputAttributes.inPrivateImeOptions(null, NO_MICROPHONE_COMPAT, editorInfo)) {
             Log.w(TAG, "Deprecated private IME option specified: "
@@ -716,18 +703,10 @@
             accessUtils.onStartInputViewInternal(mainKeyboardView, editorInfo, restarting);
         }
 
-        final boolean inputTypeChanged = !mCurrentSettings.isSameInputType(editorInfo);
+        final boolean inputTypeChanged = !mSettings.getCurrent().isSameInputType(editorInfo);
         final boolean isDifferentTextField = !restarting || inputTypeChanged;
         if (isDifferentTextField) {
-            final boolean currentSubtypeEnabled = mSubtypeSwitcher
-                    .updateParametersOnStartInputViewAndReturnIfCurrentSubtypeEnabled();
-            if (!currentSubtypeEnabled) {
-                // Current subtype is disabled. Needs to update subtype and keyboard.
-                final InputMethodSubtype newSubtype = mRichImm.getCurrentInputMethodSubtype(
-                        mSubtypeSwitcher.getNoLanguageSubtype());
-                mSubtypeSwitcher.updateSubtype(newSubtype);
-                loadKeyboard();
-            }
+            mSubtypeSwitcher.updateParametersOnStartInputView();
         }
 
         // The EditorInfo might have a flag that affects fullscreen mode.
@@ -754,11 +733,12 @@
             mainKeyboardView.closing();
             loadSettings();
 
-            if (mSuggest != null && mCurrentSettings.mCorrectionEnabled) {
-                mSuggest.setAutoCorrectionThreshold(mCurrentSettings.mAutoCorrectionThreshold);
+            if (mSuggest != null && mSettings.getCurrent().mCorrectionEnabled) {
+                mSuggest.setAutoCorrectionThreshold(
+                        mSettings.getCurrent().mAutoCorrectionThreshold);
             }
 
-            switcher.loadKeyboard(editorInfo, mCurrentSettings);
+            switcher.loadKeyboard(editorInfo, mSettings.getCurrent());
         } else if (restarting) {
             // TODO: Come up with a more comprehensive way to reset the keyboard layout when
             // a keyboard layout set doesn't get reloaded in this method.
@@ -779,11 +759,12 @@
         mHandler.cancelDoubleSpacePeriodTimer();
 
         mainKeyboardView.setMainDictionaryAvailability(mIsMainDictionaryAvailable);
-        mainKeyboardView.setKeyPreviewPopupEnabled(mCurrentSettings.mKeyPreviewPopupOn,
-                mCurrentSettings.mKeyPreviewPopupDismissDelay);
-        mainKeyboardView.setGestureHandlingEnabledByUser(mCurrentSettings.mGestureInputEnabled);
-        mainKeyboardView.setGesturePreviewMode(mCurrentSettings.mGesturePreviewTrailEnabled,
-                mCurrentSettings.mGestureFloatingPreviewTextEnabled);
+        mainKeyboardView.setKeyPreviewPopupEnabled(mSettings.getCurrent().mKeyPreviewPopupOn,
+                mSettings.getCurrent().mKeyPreviewPopupDismissDelay);
+        mainKeyboardView.setGestureHandlingEnabledByUser(
+                mSettings.getCurrent().mGestureInputEnabled);
+        mainKeyboardView.setGesturePreviewMode(mSettings.getCurrent().mGesturePreviewTrailEnabled,
+                mSettings.getCurrent().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
@@ -941,7 +922,7 @@
      */
     @Override
     public void onExtractedTextClicked() {
-        if (mCurrentSettings.isSuggestionsRequested(mDisplayOrientation)) return;
+        if (mSettings.getCurrent().isSuggestionsRequested(mDisplayOrientation)) return;
 
         super.onExtractedTextClicked();
     }
@@ -957,7 +938,7 @@
      */
     @Override
     public void onExtractedCursorMovement(final int dx, final int dy) {
-        if (mCurrentSettings.isSuggestionsRequested(mDisplayOrientation)) return;
+        if (mSettings.getCurrent().isSuggestionsRequested(mDisplayOrientation)) return;
 
         super.onExtractedCursorMovement(dx, dy);
     }
@@ -985,7 +966,7 @@
                 }
             }
         }
-        if (!mCurrentSettings.isApplicationSpecifiedCompletionsOn()) return;
+        if (!mSettings.getCurrent().isApplicationSpecifiedCompletionsOn()) return;
         mApplicationSpecifiedCompletions = applicationSpecifiedCompletions;
         if (applicationSpecifiedCompletions == null) {
             clearSuggestionStrip();
@@ -1009,9 +990,6 @@
         final boolean isAutoCorrection = false;
         setSuggestionStrip(suggestedWords, isAutoCorrection);
         setAutoCorrectionIndicator(isAutoCorrection);
-        // TODO: is this the right thing to do? What should we auto-correct to in
-        // this case? This says to keep whatever the user typed.
-        mWordComposer.setAutoCorrection(mWordComposer.getTypedWord());
         setSuggestionStripShown(true);
         if (ProductionFlag.IS_EXPERIMENTAL) {
             ResearchLogger.latinIME_onDisplayCompletions(applicationSpecifiedCompletions);
@@ -1053,7 +1031,7 @@
         }
         final int keyboardHeight = mainKeyboardView.getHeight();
         final int suggestionsHeight = mSuggestionsContainer.getHeight();
-        final int displayHeight = mResources.getDisplayMetrics().heightPixels;
+        final int displayHeight = getResources().getDisplayMetrics().heightPixels;
         final Rect rect = new Rect();
         mKeyPreviewBackingView.getWindowVisibleDisplayFrame(rect);
         final int notificationBarHeight = rect.top;
@@ -1132,10 +1110,10 @@
     // the composing word, reset the last composed word, tell the inputconnection about it.
     private void resetEntireInputState(final int newCursorPosition) {
         resetComposingState(true /* alsoResetLastComposedWord */);
-        if (mCurrentSettings.mBigramPredictionEnabled) {
+        if (mSettings.getCurrent().mBigramPredictionEnabled) {
             clearSuggestionStrip();
         } else {
-            setSuggestionStrip(mCurrentSettings.mSuggestPuncList, false);
+            setSuggestionStrip(mSettings.getCurrent().mSuggestPuncList, false);
         }
         mConnection.resetCachesUponCursorMove(newCursorPosition);
     }
@@ -1152,13 +1130,16 @@
         if (typedWord.length() > 0) {
             commitChosenWord(typedWord, LastComposedWord.COMMIT_TYPE_USER_TYPED_WORD,
                     separatorString);
+            if (ProductionFlag.IS_EXPERIMENTAL) {
+                ResearchLogger.getInstance().onWordComplete(typedWord, Long.MAX_VALUE);
+            }
         }
     }
 
     // Called from the KeyboardSwitcher which needs to know auto caps state to display
     // the right layout.
     public int getCurrentAutoCapsState() {
-        if (!mCurrentSettings.mAutoCap) return Constants.TextUtils.CAP_MODE_OFF;
+        if (!mSettings.getCurrent().mAutoCap) return Constants.TextUtils.CAP_MODE_OFF;
 
         final EditorInfo ei = getCurrentInputEditorInfo();
         if (ei == null) return Constants.TextUtils.CAP_MODE_OFF;
@@ -1187,8 +1168,10 @@
         if (lastTwo != null && lastTwo.length() == 2
                 && lastTwo.charAt(0) == Constants.CODE_SPACE) {
             mConnection.deleteSurroundingText(2, 0);
-            mConnection.commitText(lastTwo.charAt(1) + " ", 1);
+            final String text = lastTwo.charAt(1) + " ";
+            mConnection.commitText(text, 1);
             if (ProductionFlag.IS_EXPERIMENTAL) {
+                ResearchLogger.getInstance().onWordComplete(text, Long.MAX_VALUE);
                 ResearchLogger.latinIME_swapSwapperAndSpace();
             }
             mKeyboardSwitcher.updateShiftState();
@@ -1196,8 +1179,8 @@
     }
 
     private boolean maybeDoubleSpacePeriod() {
-        if (!mCurrentSettings.mCorrectionEnabled) return false;
-        if (!mCurrentSettings.mUseDoubleSpacePeriod) return false;
+        if (!mSettings.getCurrent().mCorrectionEnabled) return false;
+        if (!mSettings.getCurrent().mUseDoubleSpacePeriod) return false;
         if (!mHandler.isAcceptingDoubleSpacePeriod()) return false;
         final CharSequence lastThree = mConnection.getTextBeforeCursor(3, 0);
         if (lastThree != null && lastThree.length() == 3
@@ -1206,7 +1189,11 @@
                 && lastThree.charAt(2) == Constants.CODE_SPACE) {
             mHandler.cancelDoubleSpacePeriodTimer();
             mConnection.deleteSurroundingText(2, 0);
-            mConnection.commitText(". ", 1);
+            final String textToInsert = ". ";
+            mConnection.commitText(textToInsert, 1);
+            if (ProductionFlag.IS_EXPERIMENTAL) {
+                ResearchLogger.getInstance().onWordComplete(textToInsert, Long.MAX_VALUE);
+            }
             mKeyboardSwitcher.updateShiftState();
             return true;
         }
@@ -1296,7 +1283,7 @@
     // TODO: Revise the language switch key behavior to make it much smarter and more reasonable.
     private void handleLanguageSwitchKey() {
         final IBinder token = getWindow().getWindow().getAttributes().token;
-        if (mCurrentSettings.mIncludesOtherImesInLanguageSwitchList) {
+        if (mSettings.getCurrent().mIncludesOtherImesInLanguageSwitchList) {
             mRichImm.switchToNextInputMethod(token, false /* onlyCurrentIme */);
             return;
         }
@@ -1324,10 +1311,8 @@
             return;
         }
 
-        // 16 is android.os.Build.VERSION_CODES.JELLY_BEAN but we can't write it because
-        // we want to be able to compile against the Ice Cream Sandwich SDK.
         if (Constants.CODE_ENTER == code && mTargetApplicationInfo != null
-                && mTargetApplicationInfo.targetSdkVersion < 16) {
+                && mTargetApplicationInfo.targetSdkVersion < VERSION_CODES.JELLY_BEAN) {
             // Backward compatibility mode. Before Jelly bean, the keyboard would simulate
             // a hardware keyboard event on pressing enter or delete. This is bad for many
             // reasons (there are race conditions with commits) but some applications are
@@ -1400,7 +1385,7 @@
             break;
         default:
             mSpaceState = SPACE_STATE_NONE;
-            if (mCurrentSettings.isWordSeparator(primaryCode)) {
+            if (mSettings.getCurrent().isWordSeparator(primaryCode)) {
                 didAutoCorrect = handleSeparator(primaryCode, x, y, spaceState);
             } else {
                 if (SPACE_STATE_PHANTOM == spaceState) {
@@ -1445,7 +1430,7 @@
     public void onTextInput(final String rawText) {
         mConnection.beginBatchEdit();
         if (mWordComposer.isComposingWord()) {
-            commitCurrentAutoCorrection(rawText.toString());
+            commitCurrentAutoCorrection(rawText);
         } else {
             resetComposingState(true /* alsoResetLastComposedWord */);
         }
@@ -1455,6 +1440,9 @@
             promotePhantomSpace();
         }
         mConnection.commitText(text, 1);
+        if (ProductionFlag.IS_EXPERIMENTAL) {
+            ResearchLogger.getInstance().onWordComplete(text, Long.MAX_VALUE);
+        }
         mConnection.endBatchEdit();
         // Space state must be updated before calling updateShiftState
         mSpaceState = SPACE_STATE_NONE;
@@ -1495,8 +1483,8 @@
             // should usually be followed by a space, and it should be more readable.
             if (Constants.NOT_A_CODE != codePointBeforeCursor
                     && !Character.isWhitespace(codePointBeforeCursor)
-                    && !mCurrentSettings.isPhantomSpacePromotingSymbol(codePointBeforeCursor)
-                    && !mCurrentSettings.isWeakSpaceStripper(codePointBeforeCursor)) {
+                    && !mSettings.getCurrent().isPhantomSpacePromotingSymbol(codePointBeforeCursor)
+                    && !mSettings.getCurrent().isWeakSpaceStripper(codePointBeforeCursor)) {
                 mSpaceState = SPACE_STATE_PHANTOM;
             }
         }
@@ -1603,9 +1591,7 @@
         if (dismissGestureFloatingPreviewText) {
             mainKeyboardView.dismissGestureFloatingPreviewText();
         } else {
-            final String batchInputText = suggestedWords.isEmpty()
-                    ? null : suggestedWords.getWord(0);
-            mainKeyboardView.showGestureFloatingPreviewText(batchInputText);
+            mainKeyboardView.showGestureFloatingPreviewText(suggestedWords);
         }
     }
 
@@ -1739,10 +1725,8 @@
                     // This should never happen.
                     Log.e(TAG, "Backspace when we don't know the selection position");
                 }
-                // 16 is android.os.Build.VERSION_CODES.JELLY_BEAN but we can't write it because
-                // we want to be able to compile against the Ice Cream Sandwich SDK.
                 if (mTargetApplicationInfo != null
-                        && mTargetApplicationInfo.targetSdkVersion < 16) {
+                        && mTargetApplicationInfo.targetSdkVersion < VERSION_CODES.JELLY_BEAN) {
                     // Backward compatibility mode. Before Jelly bean, the keyboard would simulate
                     // a hardware keyboard event on pressing enter or delete. This is bad for many
                     // reasons (there are race conditions with commits) but some applications are
@@ -1755,7 +1739,7 @@
                     mConnection.deleteSurroundingText(1, 0);
                 }
             }
-            if (mCurrentSettings.isSuggestionsRequested(mDisplayOrientation)) {
+            if (mSettings.getCurrent().isSuggestionsRequested(mDisplayOrientation)) {
                 restartSuggestionsOnWordBeforeCursorIfAtEndOfWord();
             }
         }
@@ -1769,10 +1753,10 @@
         } else if ((SPACE_STATE_WEAK == spaceState
                 || SPACE_STATE_SWAP_PUNCTUATION == spaceState)
                 && isFromSuggestionStrip) {
-            if (mCurrentSettings.isWeakSpaceSwapper(code)) {
+            if (mSettings.getCurrent().isWeakSpaceSwapper(code)) {
                 return true;
             } else {
-                if (mCurrentSettings.isWeakSpaceStripper(code)) {
+                if (mSettings.getCurrent().isWeakSpaceStripper(code)) {
                     mConnection.removeTrailingSpace();
                 }
                 return false;
@@ -1787,7 +1771,7 @@
         boolean isComposingWord = mWordComposer.isComposingWord();
 
         if (SPACE_STATE_PHANTOM == spaceState &&
-                !mCurrentSettings.isSymbolExcludedFromWordSeparators(primaryCode)) {
+                !mSettings.getCurrent().isSymbolExcludedFromWordSeparators(primaryCode)) {
             if (isComposingWord) {
                 // Sanity check
                 throw new RuntimeException("Should not be composing here");
@@ -1799,9 +1783,9 @@
         // dozen milliseconds. Avoid calling it as much as possible, since we are on the UI
         // thread here.
         if (!isComposingWord && (isAlphabet(primaryCode)
-                || mCurrentSettings.isSymbolExcludedFromWordSeparators(primaryCode))
-                && mCurrentSettings.isSuggestionsRequested(mDisplayOrientation) &&
-                !mConnection.isCursorTouchingWord(mCurrentSettings)) {
+                || mSettings.getCurrent().isSymbolExcludedFromWordSeparators(primaryCode))
+                && mSettings.getCurrent().isSuggestionsRequested(mDisplayOrientation) &&
+                !mConnection.isCursorTouchingWord(mSettings.getCurrent())) {
             // 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
@@ -1855,7 +1839,7 @@
         boolean didAutoCorrect = false;
         // Handle separator
         if (mWordComposer.isComposingWord()) {
-            if (mCurrentSettings.mCorrectionEnabled) {
+            if (mSettings.getCurrent().mCorrectionEnabled) {
                 // TODO: maybe cache Strings in an <String> sparse array or something
                 commitCurrentAutoCorrection(new String(new int[]{primaryCode}, 0, 1));
                 didAutoCorrect = true;
@@ -1868,13 +1852,13 @@
                 Constants.SUGGESTION_STRIP_COORDINATE == x);
 
         if (SPACE_STATE_PHANTOM == spaceState &&
-                mCurrentSettings.isPhantomSpacePromotingSymbol(primaryCode)) {
+                mSettings.getCurrent().isPhantomSpacePromotingSymbol(primaryCode)) {
             promotePhantomSpace();
         }
         sendKeyCodePoint(primaryCode);
 
         if (Constants.CODE_SPACE == primaryCode) {
-            if (mCurrentSettings.isSuggestionsRequested(mDisplayOrientation)) {
+            if (mSettings.getCurrent().isSuggestionsRequested(mDisplayOrientation)) {
                 if (maybeDoubleSpacePeriod()) {
                     mSpaceState = SPACE_STATE_DOUBLE;
                 } else if (!isShowingPunctuationList()) {
@@ -1883,7 +1867,7 @@
             }
 
             mHandler.startDoubleSpacePeriodTimer();
-            if (!mConnection.isCursorTouchingWord(mCurrentSettings)) {
+            if (!mConnection.isCursorTouchingWord(mSettings.getCurrent())) {
                 mHandler.postUpdateSuggestionStrip();
             }
         } else {
@@ -1891,8 +1875,8 @@
                 swapSwapperAndSpace();
                 mSpaceState = SPACE_STATE_SWAP_PUNCTUATION;
             } else if (SPACE_STATE_PHANTOM == spaceState
-                    && !mCurrentSettings.isWeakSpaceStripper(primaryCode)
-                    && !mCurrentSettings.isPhantomSpacePromotingSymbol(primaryCode)) {
+                    && !mSettings.getCurrent().isWeakSpaceStripper(primaryCode)
+                    && !mSettings.getCurrent().isPhantomSpacePromotingSymbol(primaryCode)) {
                 // If we are in phantom space state, and the user presses a separator, we want to
                 // stay in phantom space state so that the next keypress has a chance to add the
                 // space. For example, if I type "Good dat", pick "day" from the suggestion strip
@@ -1925,6 +1909,7 @@
     }
 
     private void handleClose() {
+        // TODO: Verify that words are logged properly when IME is closed.
         commitTyped(LastComposedWord.NOT_A_SEPARATOR);
         requestHideSelf(0);
         final MainKeyboardView mainKeyboardView = mKeyboardSwitcher.getMainKeyboardView();
@@ -1938,7 +1923,7 @@
     @UsedForTesting
     boolean isShowingPunctuationList() {
         if (mSuggestionStripView == null) return false;
-        return mCurrentSettings.mSuggestPuncList == mSuggestionStripView.getSuggestions();
+        return mSettings.getCurrent().mSuggestPuncList == mSuggestionStripView.getSuggestions();
     }
 
     private boolean isSuggestionsStripVisible() {
@@ -1946,11 +1931,11 @@
             return false;
         if (mSuggestionStripView.isShowingAddToDictionaryHint())
             return true;
-        if (!mCurrentSettings.isSuggestionStripVisibleInOrientation(mDisplayOrientation))
+        if (!mSettings.getCurrent().isSuggestionStripVisibleInOrientation(mDisplayOrientation))
             return false;
-        if (mCurrentSettings.isApplicationSpecifiedCompletionsOn())
+        if (mSettings.getCurrent().isApplicationSpecifiedCompletionsOn())
             return true;
-        return mCurrentSettings.isSuggestionsRequested(mDisplayOrientation);
+        return mSettings.getCurrent().isSuggestionsRequested(mDisplayOrientation);
     }
 
     private void clearSuggestionStrip() {
@@ -1984,16 +1969,16 @@
         mHandler.cancelUpdateSuggestionStrip();
 
         // Check if we have a suggestion engine attached.
-        if (mSuggest == null || !mCurrentSettings.isSuggestionsRequested(mDisplayOrientation)) {
+        if (mSuggest == null
+                || !mSettings.getCurrent().isSuggestionsRequested(mDisplayOrientation)) {
             if (mWordComposer.isComposingWord()) {
                 Log.w(TAG, "Called updateSuggestionsOrPredictions but suggestions were not "
                         + "requested!");
-                mWordComposer.setAutoCorrection(mWordComposer.getTypedWord());
             }
             return;
         }
 
-        if (!mWordComposer.isComposingWord() && !mCurrentSettings.mBigramPredictionEnabled) {
+        if (!mWordComposer.isComposingWord() && !mSettings.getCurrent().mBigramPredictionEnabled) {
             setPunctuationSuggestions();
             return;
         }
@@ -2014,10 +1999,10 @@
         // should just skip whitespace if any, so 1.
         // TODO: this is slow (2-way IPC) - we should probably cache this instead.
         final String prevWord =
-                mConnection.getNthPreviousWord(mCurrentSettings.mWordSeparators,
+                mConnection.getNthPreviousWord(mSettings.getCurrent().mWordSeparators,
                 mWordComposer.isComposingWord() ? 2 : 1);
         final SuggestedWords suggestedWords = mSuggest.getSuggestedWords(mWordComposer,
-                prevWord, keyboard.getProximityInfo(), mCurrentSettings.mCorrectionEnabled,
+                prevWord, keyboard.getProximityInfo(), mSettings.getCurrent().mCorrectionEnabled,
                 sessionId);
         return maybeRetrieveOlderSuggestions(typedWord, suggestedWords);
     }
@@ -2042,7 +2027,7 @@
 
     private SuggestedWords getOlderSuggestions(final String typedWord) {
         SuggestedWords previousSuggestions = mSuggestionStripView.getSuggestions();
-        if (previousSuggestions == mCurrentSettings.mSuggestPuncList) {
+        if (previousSuggestions == mSettings.getCurrent().mSuggestPuncList) {
             previousSuggestions = SuggestedWords.EMPTY;
         }
         if (typedWord == null) {
@@ -2091,8 +2076,7 @@
                         + "is empty? Impossible! I must commit suicide.");
             }
             if (ProductionFlag.IS_INTERNAL) {
-                Stats.onAutoCorrection(
-                        typedWord, autoCorrection.toString(), separatorString, mWordComposer);
+                Stats.onAutoCorrection(typedWord, autoCorrection, separatorString, mWordComposer);
             }
             if (ProductionFlag.IS_EXPERIMENTAL) {
                 ResearchLogger.latinIme_commitCurrentAutoCorrection(typedWord, autoCorrection,
@@ -2124,7 +2108,7 @@
         if (suggestion.length() == 1 && isShowingPunctuationList()) {
             // Word separators are suggested before the user inputs something.
             // So, LatinImeLogger logs "" as a user's input.
-            LatinImeLogger.logOnManualSuggestion("", suggestion.toString(), index, suggestedWords);
+            LatinImeLogger.logOnManualSuggestion("", suggestion, index, suggestedWords);
             // Rely on onCodeInput to do the complicated swapping/stripping logic consistently.
             final int primaryCode = suggestion.charAt(0);
             onCodeInput(primaryCode,
@@ -2141,13 +2125,13 @@
                 // the current batch input text and there is no need for a phantom space.
                 && !mWordComposer.isBatchMode()) {
             int firstChar = Character.codePointAt(suggestion, 0);
-            if ((!mCurrentSettings.isWeakSpaceStripper(firstChar))
-                    && (!mCurrentSettings.isWeakSpaceSwapper(firstChar))) {
+            if ((!mSettings.getCurrent().isWeakSpaceStripper(firstChar))
+                    && (!mSettings.getCurrent().isWeakSpaceSwapper(firstChar))) {
                 promotePhantomSpace();
             }
         }
 
-        if (mCurrentSettings.isApplicationSpecifiedCompletionsOn()
+        if (mSettings.getCurrent().isApplicationSpecifiedCompletionsOn()
                 && mApplicationSpecifiedCompletions != null
                 && index >= 0 && index < mApplicationSpecifiedCompletions.length) {
             if (mSuggestionStripView != null) {
@@ -2163,9 +2147,8 @@
 
         // We need to log before we commit, because the word composer will store away the user
         // typed word.
-        final String replacedWord = mWordComposer.getTypedWord().toString();
-        LatinImeLogger.logOnManualSuggestion(replacedWord,
-                suggestion.toString(), index, suggestedWords);
+        final String replacedWord = mWordComposer.getTypedWord();
+        LatinImeLogger.logOnManualSuggestion(replacedWord, suggestion, index, suggestedWords);
         mExpectingUpdateSelection = true;
         commitChosenWord(suggestion, LastComposedWord.COMMIT_TYPE_MANUAL_PICK,
                 LastComposedWord.NOT_A_SEPARATOR);
@@ -2193,7 +2176,7 @@
         }
         if (showingAddToDictionaryHint && mIsUserDictionaryAvailable) {
             mSuggestionStripView.showAddToDictionaryHint(
-                    suggestion, mCurrentSettings.mHintToSaveText);
+                    suggestion, mSettings.getCurrent().mHintToSaveText);
         } else {
             // If we're not showing the "Touch again to save", then update the suggestion strip.
             mHandler.postUpdateSuggestionStrip();
@@ -2214,15 +2197,15 @@
         // what user typed. Note: currently this is done much later in
         // LastComposedWord#didCommitTypedWord by string equality of the remembered
         // strings.
-        mLastComposedWord = mWordComposer.commitWord(commitType, chosenWord.toString(),
-                separatorString, prevWord);
+        mLastComposedWord = mWordComposer.commitWord(commitType, chosenWord, separatorString,
+                prevWord);
     }
 
     private void setPunctuationSuggestions() {
-        if (mCurrentSettings.mBigramPredictionEnabled) {
+        if (mSettings.getCurrent().mBigramPredictionEnabled) {
             clearSuggestionStrip();
         } else {
-            setSuggestionStrip(mCurrentSettings.mSuggestPuncList, false);
+            setSuggestionStrip(mSettings.getCurrent().mSuggestPuncList, false);
         }
         setAutoCorrectionIndicator(false);
         setSuggestionStripShown(isSuggestionsStripVisible());
@@ -2235,12 +2218,12 @@
         // If correction is not enabled, we don't add words to the user history dictionary.
         // That's to avoid unintended additions in some sensitive fields, or fields that
         // expect to receive non-words.
-        if (!mCurrentSettings.mCorrectionEnabled) return null;
+        if (!mSettings.getCurrent().mCorrectionEnabled) return null;
 
         final UserHistoryDictionary userHistoryDictionary = mUserHistoryDictionary;
         if (userHistoryDictionary != null) {
-            final CharSequence prevWord
-                    = mConnection.getNthPreviousWord(mCurrentSettings.mWordSeparators, 2);
+            final String prevWord
+                    = mConnection.getNthPreviousWord(mSettings.getCurrent().mWordSeparators, 2);
             final String secondWord;
             if (mWordComposer.wasAutoCapitalized() && !mWordComposer.isMostlyCaps()) {
                 secondWord = suggestion.toLowerCase(mSubtypeSwitcher.getCurrentSubtypeLocale());
@@ -2252,9 +2235,8 @@
             final int maxFreq = AutoCorrection.getMaxFrequency(
                     mSuggest.getUnigramDictionaries(), suggestion);
             if (maxFreq == 0) return null;
-            final String prevWordString = (null == prevWord) ? null : prevWord.toString();
-            userHistoryDictionary.addToUserHistory(prevWordString, secondWord, maxFreq > 0);
-            return prevWordString;
+            userHistoryDictionary.addToUserHistory(prevWord, secondWord, maxFreq > 0);
+            return prevWord;
         }
         return null;
     }
@@ -2264,7 +2246,8 @@
      * word, else do nothing.
      */
     private void restartSuggestionsOnWordBeforeCursorIfAtEndOfWord() {
-        final CharSequence word = mConnection.getWordBeforeCursorIfAtEndOfWord(mCurrentSettings);
+        final CharSequence word =
+                mConnection.getWordBeforeCursorIfAtEndOfWord(mSettings.getCurrent());
         if (null != word) {
             restartSuggestionsOnWordBeforeCursor(word);
         }
@@ -2302,8 +2285,7 @@
         }
         mConnection.deleteSurroundingText(deleteLength, 0);
         if (!TextUtils.isEmpty(previousWord) && !TextUtils.isEmpty(committedWord)) {
-            mUserHistoryDictionary.cancelAddingUserHistory(
-                    previousWord.toString(), committedWord.toString());
+            mUserHistoryDictionary.cancelAddingUserHistory(previousWord, committedWord);
         }
         mConnection.commitText(originallyTypedWord + mLastComposedWord.mSeparatorString, 1);
         if (ProductionFlag.IS_INTERNAL) {
@@ -2311,7 +2293,8 @@
                     Constants.NOT_A_COORDINATE, Constants.NOT_A_COORDINATE);
         }
         if (ProductionFlag.IS_EXPERIMENTAL) {
-            ResearchLogger.latinIME_revertCommit(originallyTypedWord);
+            ResearchLogger.latinIME_revertCommit(committedWord, originallyTypedWord);
+            ResearchLogger.getInstance().onWordComplete(originallyTypedWord, Long.MAX_VALUE);
         }
         // Don't restart suggestion yet. We'll restart if the user deletes the
         // separator.
@@ -2322,14 +2305,14 @@
 
     // This essentially inserts a space, and that's it.
     public void promotePhantomSpace() {
-        if (mCurrentSettings.shouldInsertSpacesAutomatically()) {
+        if (mSettings.getCurrent().shouldInsertSpacesAutomatically()) {
             sendKeyCodePoint(Constants.CODE_SPACE);
         }
     }
 
     // Used by the RingCharBuffer
     public boolean isWordSeparator(final int code) {
-        return mCurrentSettings.isWordSeparator(code);
+        return mSettings.getCurrent().isWordSeparator(code);
     }
 
     // TODO: Make this private
@@ -2342,7 +2325,7 @@
         loadSettings();
         if (mKeyboardSwitcher.getMainKeyboardView() != null) {
             // Reload keyboard because the current language has been changed.
-            mKeyboardSwitcher.loadKeyboard(getCurrentInputEditorInfo(), mCurrentSettings);
+            mKeyboardSwitcher.loadKeyboard(getCurrentInputEditorInfo(), mSettings.getCurrent());
         }
         // Since we just changed languages, we should re-evaluate suggestions with whatever word
         // we are currently composing. If we are not composing anything, we may want to display
@@ -2399,7 +2382,6 @@
 
     @Override
     public boolean onKeyUp(final int keyCode, final KeyEvent event) {
-        if (mEventInterpreter.onHardwareKeyEvent(event)) return true;
         return super.onKeyUp(keyCode, event);
     }
 
@@ -2504,7 +2486,7 @@
                 .append("\nPackage : ").append(mTargetApplicationInfo.packageName)
                 .append("\nTarget app sdk version : ")
                 .append(mTargetApplicationInfo.targetSdkVersion)
-                .append("\nAttributes : ").append(mCurrentSettings.getInputAttributesDebugString())
+                .append("\nAttributes : ").append(mSettings.getCurrent().mInputAttributes)
                 .append("\nContext : ").append(context);
         throw new RuntimeException(s.toString());
     }
@@ -2518,13 +2500,14 @@
         final Keyboard keyboard = mKeyboardSwitcher.getKeyboard();
         final int keyboardMode = keyboard != null ? keyboard.mId.mMode : -1;
         p.println("  Keyboard mode = " + keyboardMode);
+        final SettingsValues settingsValues = mSettings.getCurrent();
         p.println("  mIsSuggestionsSuggestionsRequested = "
-                + mCurrentSettings.isSuggestionsRequested(mDisplayOrientation));
-        p.println("  mCorrectionEnabled=" + mCurrentSettings.mCorrectionEnabled);
+                + settingsValues.isSuggestionsRequested(mDisplayOrientation));
+        p.println("  mCorrectionEnabled=" + settingsValues.mCorrectionEnabled);
         p.println("  isComposingWord=" + mWordComposer.isComposingWord());
-        p.println("  mSoundOn=" + mCurrentSettings.mSoundOn);
-        p.println("  mVibrateOn=" + mCurrentSettings.mVibrateOn);
-        p.println("  mKeyPreviewPopupOn=" + mCurrentSettings.mKeyPreviewPopupOn);
-        p.println("  inputAttributes=" + mCurrentSettings.getInputAttributesDebugString());
+        p.println("  mSoundOn=" + settingsValues.mSoundOn);
+        p.println("  mVibrateOn=" + settingsValues.mVibrateOn);
+        p.println("  mKeyPreviewPopupOn=" + settingsValues.mKeyPreviewPopupOn);
+        p.println("  inputAttributes=" + settingsValues.mInputAttributes);
     }
 }
diff --git a/java/src/com/android/inputmethod/latin/LatinImeLogger.java b/java/src/com/android/inputmethod/latin/LatinImeLogger.java
index 394a9c7..e4e8b94 100644
--- a/java/src/com/android/inputmethod/latin/LatinImeLogger.java
+++ b/java/src/com/android/inputmethod/latin/LatinImeLogger.java
@@ -31,7 +31,7 @@
     public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key) {
     }
 
-    public static void init(LatinIME context, SharedPreferences prefs) {
+    public static void init(LatinIME context) {
     }
 
     public static void commit() {
diff --git a/java/src/com/android/inputmethod/latin/PositionalInfoForUserDictPendingAddition.java b/java/src/com/android/inputmethod/latin/PositionalInfoForUserDictPendingAddition.java
index 8a2d222..1fd2563 100644
--- a/java/src/com/android/inputmethod/latin/PositionalInfoForUserDictPendingAddition.java
+++ b/java/src/com/android/inputmethod/latin/PositionalInfoForUserDictPendingAddition.java
@@ -29,7 +29,7 @@
  * the IME needs to take a note of what it has to replace and where it is.
  * This class encapsulates this data.
  */
-public class PositionalInfoForUserDictPendingAddition {
+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
diff --git a/java/src/com/android/inputmethod/latin/ResourceUtils.java b/java/src/com/android/inputmethod/latin/ResourceUtils.java
index 5021ad3..b74b979 100644
--- a/java/src/com/android/inputmethod/latin/ResourceUtils.java
+++ b/java/src/com/android/inputmethod/latin/ResourceUtils.java
@@ -19,11 +19,15 @@
 import android.content.res.Resources;
 import android.content.res.TypedArray;
 import android.os.Build;
+import android.util.Log;
 import android.util.TypedValue;
 
 import java.util.HashMap;
 
 public final class ResourceUtils {
+    private static final String TAG = ResourceUtils.class.getSimpleName();
+    private static final boolean DEBUG = false;
+
     public static final float UNDEFINED_RATIO = -1.0f;
     public static final int UNDEFINED_DIMENSION = -1;
 
@@ -31,24 +35,44 @@
         // This utility class is not publicly instantiable.
     }
 
+    private static final String DEFAULT_PREFIX = "DEFAULT,";
     private static final String HARDWARE_PREFIX = Build.HARDWARE + ",";
     private static final HashMap<String, String> sDeviceOverrideValueMap =
             CollectionUtils.newHashMap();
 
-    public static String getDeviceOverrideValue(Resources res, int overrideResId, String defValue) {
+    public static String getDeviceOverrideValue(final Resources res, final int overrideResId) {
         final int orientation = res.getConfiguration().orientation;
         final String key = overrideResId + "-" + orientation;
-        if (!sDeviceOverrideValueMap.containsKey(key)) {
-            String overrideValue = defValue;
-            for (final String element : res.getStringArray(overrideResId)) {
-                if (element.startsWith(HARDWARE_PREFIX)) {
-                    overrideValue = element.substring(HARDWARE_PREFIX.length());
-                    break;
-                }
+        if (sDeviceOverrideValueMap.containsKey(key)) {
+            return sDeviceOverrideValueMap.get(key);
+        }
+
+        final String[] overrideArray = res.getStringArray(overrideResId);
+        final String overrideValue = StringUtils.findPrefixedString(HARDWARE_PREFIX, overrideArray);
+        // The overrideValue might be an empty string.
+        if (overrideValue != null) {
+            if (DEBUG) {
+                Log.d(TAG, "Find override value:"
+                        + " resource="+ res.getResourceEntryName(overrideResId)
+                        + " Build.HARDWARE=" + Build.HARDWARE + " override=" + overrideValue);
             }
             sDeviceOverrideValueMap.put(key, overrideValue);
+            return overrideValue;
         }
-        return sDeviceOverrideValueMap.get(key);
+
+        final String defaultValue = StringUtils.findPrefixedString(DEFAULT_PREFIX, overrideArray);
+        // The defaultValue might be an empty string.
+        if (defaultValue == null) {
+            Log.w(TAG, "Couldn't find override value nor default value:"
+                    + " resource="+ res.getResourceEntryName(overrideResId)
+                    + " Build.HARDWARE=" + Build.HARDWARE);
+        } else if (DEBUG) {
+            Log.d(TAG, "Found default value:"
+                + " resource="+ res.getResourceEntryName(overrideResId)
+                + " Build.HARDWARE=" + Build.HARDWARE + " default=" + defaultValue);
+        }
+        sDeviceOverrideValueMap.put(key, defaultValue);
+        return defaultValue;
     }
 
     public static boolean isValidFraction(final float fraction) {
@@ -85,8 +109,8 @@
         return a.getDimensionPixelSize(index, ResourceUtils.UNDEFINED_DIMENSION);
     }
 
-    public static float getDimensionOrFraction(TypedArray a, int index, int base,
-            float defValue) {
+    public static float getDimensionOrFraction(final TypedArray a, final int index, final int base,
+            final float defValue) {
         final TypedValue value = a.peekValue(index);
         if (value == null) {
             return defValue;
@@ -99,7 +123,7 @@
         return defValue;
     }
 
-    public static int getEnumValue(TypedArray a, int index, int defValue) {
+    public static int getEnumValue(final TypedArray a, final int index, final int defValue) {
         final TypedValue value = a.peekValue(index);
         if (value == null) {
             return defValue;
@@ -110,19 +134,19 @@
         return defValue;
     }
 
-    public static boolean isFractionValue(TypedValue v) {
+    public static boolean isFractionValue(final TypedValue v) {
         return v.type == TypedValue.TYPE_FRACTION;
     }
 
-    public static boolean isDimensionValue(TypedValue v) {
+    public static boolean isDimensionValue(final TypedValue v) {
         return v.type == TypedValue.TYPE_DIMENSION;
     }
 
-    public static boolean isIntegerValue(TypedValue v) {
+    public static boolean isIntegerValue(final TypedValue v) {
         return v.type >= TypedValue.TYPE_FIRST_INT && v.type <= TypedValue.TYPE_LAST_INT;
     }
 
-    public static boolean isStringValue(TypedValue v) {
+    public static boolean isStringValue(final TypedValue v) {
         return v.type == TypedValue.TYPE_STRING;
     }
 }
diff --git a/java/src/com/android/inputmethod/latin/RichInputConnection.java b/java/src/com/android/inputmethod/latin/RichInputConnection.java
index e9c81da..9cb24b5 100644
--- a/java/src/com/android/inputmethod/latin/RichInputConnection.java
+++ b/java/src/com/android/inputmethod/latin/RichInputConnection.java
@@ -178,9 +178,6 @@
         mComposingText.setLength(0);
         if (null != mIC) {
             mIC.commitText(text, i);
-            if (ProductionFlag.IS_EXPERIMENTAL) {
-                ResearchLogger.richInputConnection_commitText(text, i);
-            }
         }
     }
 
@@ -287,40 +284,40 @@
         if (DEBUG_BATCH_NESTING) checkBatchEdit();
         if (keyEvent.getAction() == KeyEvent.ACTION_DOWN) {
             if (DEBUG_PREVIOUS_TEXT) checkConsistencyForDebug();
-            // This method is only called for enter or backspace when speaking to old
-            // applications (target SDK <= 15), or for digits.
+            // This method is only called for enter or backspace when speaking to old applications
+            // (target SDK <= 15 (Build.VERSION_CODES.ICE_CREAM_SANDWICH_MR1)), or for digits.
             // When talking to new applications we never use this method because it's inherently
             // racy and has unpredictable results, but for backward compatibility we continue
             // sending the key events for only Enter and Backspace because some applications
             // mistakenly catch them to do some stuff.
             switch (keyEvent.getKeyCode()) {
-                case KeyEvent.KEYCODE_ENTER:
-                    mCommittedTextBeforeComposingText.append("\n");
-                    mCurrentCursorPosition += 1;
-                    break;
-                case KeyEvent.KEYCODE_DEL:
-                    if (0 == mComposingText.length()) {
-                        if (mCommittedTextBeforeComposingText.length() > 0) {
-                            mCommittedTextBeforeComposingText.delete(
-                                    mCommittedTextBeforeComposingText.length() - 1,
-                                    mCommittedTextBeforeComposingText.length());
-                        }
-                    } else {
-                        mComposingText.delete(mComposingText.length() - 1, mComposingText.length());
+            case KeyEvent.KEYCODE_ENTER:
+                mCommittedTextBeforeComposingText.append("\n");
+                mCurrentCursorPosition += 1;
+                break;
+            case KeyEvent.KEYCODE_DEL:
+                if (0 == mComposingText.length()) {
+                    if (mCommittedTextBeforeComposingText.length() > 0) {
+                        mCommittedTextBeforeComposingText.delete(
+                                mCommittedTextBeforeComposingText.length() - 1,
+                                mCommittedTextBeforeComposingText.length());
                     }
-                    if (mCurrentCursorPosition > 0) mCurrentCursorPosition -= 1;
-                    break;
-                case KeyEvent.KEYCODE_UNKNOWN:
-                    if (null != keyEvent.getCharacters()) {
-                        mCommittedTextBeforeComposingText.append(keyEvent.getCharacters());
-                        mCurrentCursorPosition += keyEvent.getCharacters().length();
-                    }
-                    break;
-                default:
-                    final String text = new String(new int[] { keyEvent.getUnicodeChar() }, 0, 1);
-                    mCommittedTextBeforeComposingText.append(text);
-                    mCurrentCursorPosition += text.length();
-                    break;
+                } else {
+                    mComposingText.delete(mComposingText.length() - 1, mComposingText.length());
+                }
+                if (mCurrentCursorPosition > 0) mCurrentCursorPosition -= 1;
+                break;
+            case KeyEvent.KEYCODE_UNKNOWN:
+                if (null != keyEvent.getCharacters()) {
+                    mCommittedTextBeforeComposingText.append(keyEvent.getCharacters());
+                    mCurrentCursorPosition += keyEvent.getCharacters().length();
+                }
+                break;
+            default:
+                final String text = new String(new int[] { keyEvent.getUnicodeChar() }, 0, 1);
+                mCommittedTextBeforeComposingText.append(text);
+                mCurrentCursorPosition += text.length();
+                break;
             }
         }
         if (null != mIC) {
@@ -537,17 +534,17 @@
         // Going backward, alternate skipping non-separators and separators until enough words
         // have been read.
         int count = additionalPrecedingWordsCount;
-        int start = before.length();
+        int startIndexInBefore = before.length();
         boolean isStoppingAtWhitespace = true;  // toggles to indicate what to stop at
         while (true) { // see comments below for why this is guaranteed to halt
-            while (start > 0) {
-                final int codePoint = Character.codePointBefore(before, start);
+            while (startIndexInBefore > 0) {
+                final int codePoint = Character.codePointBefore(before, startIndexInBefore);
                 if (isStoppingAtWhitespace == isSeparator(codePoint, sep)) {
                     break;  // inner loop
                 }
-                --start;
+                --startIndexInBefore;
                 if (Character.isSupplementaryCodePoint(codePoint)) {
-                    --start;
+                    --startIndexInBefore;
                 }
             }
             // isStoppingAtWhitespace is true every other time through the loop,
@@ -560,25 +557,20 @@
         }
 
         // Find last word separator after the cursor
-        int end = -1;
-        while (++end < after.length()) {
-            final int codePoint = Character.codePointAt(after, end);
+        int endIndexInAfter = -1;
+        while (++endIndexInAfter < after.length()) {
+            final int codePoint = Character.codePointAt(after, endIndexInAfter);
             if (isSeparator(codePoint, sep)) {
                 break;
             }
             if (Character.isSupplementaryCodePoint(codePoint)) {
-                ++end;
+                ++endIndexInAfter;
             }
         }
 
-        final int cursor = getCursorPosition();
-        if (start >= 0 && cursor + end <= after.length() + before.length()) {
-            String word = before.toString().substring(start, before.length())
-                    + after.toString().substring(0, end);
-            return new Range(before.length() - start, end, word);
-        }
-
-        return null;
+        final String word = before.toString().substring(startIndexInBefore, before.length())
+                + after.toString().substring(0, endIndexInAfter);
+        return new Range(before.length() - startIndexInBefore, endIndexInAfter, word);
     }
 
     public boolean isCursorTouchingWord(final SettingsValues settingsValues) {
@@ -665,7 +657,11 @@
             return false;
         }
         deleteSurroundingText(2, 0);
-        commitText("  ", 1);
+        final String doubleSpace = "  ";
+        commitText(doubleSpace, 1);
+        if (ProductionFlag.IS_EXPERIMENTAL) {
+            ResearchLogger.getInstance().onWordComplete(doubleSpace, Long.MAX_VALUE);
+        }
         return true;
     }
 
@@ -686,7 +682,11 @@
             return false;
         }
         deleteSurroundingText(2, 0);
-        commitText(" " + textBeforeCursor.subSequence(0, 1), 1);
+        final String text = " " + textBeforeCursor.subSequence(0, 1);
+        commitText(text, 1);
+        if (ProductionFlag.IS_EXPERIMENTAL) {
+            ResearchLogger.getInstance().onWordComplete(text, Long.MAX_VALUE);
+        }
         return true;
     }
 
diff --git a/java/src/com/android/inputmethod/latin/RichInputMethodManager.java b/java/src/com/android/inputmethod/latin/RichInputMethodManager.java
index af0d61c..637916f 100644
--- a/java/src/com/android/inputmethod/latin/RichInputMethodManager.java
+++ b/java/src/com/android/inputmethod/latin/RichInputMethodManager.java
@@ -21,6 +21,7 @@
 import android.content.Context;
 import android.content.SharedPreferences;
 import android.os.IBinder;
+import android.preference.PreferenceManager;
 import android.view.inputmethod.InputMethodInfo;
 import android.view.inputmethod.InputMethodManager;
 import android.view.inputmethod.InputMethodSubtype;
@@ -50,7 +51,8 @@
         return sInstance;
     }
 
-    public static void init(final Context context, final SharedPreferences prefs) {
+    public static void init(final Context context) {
+        final SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context);
         sInstance.initInternal(context, prefs);
     }
 
diff --git a/java/src/com/android/inputmethod/latin/SeekBarDialog.java b/java/src/com/android/inputmethod/latin/SeekBarDialog.java
new file mode 100644
index 0000000..e576c09
--- /dev/null
+++ b/java/src/com/android/inputmethod/latin/SeekBarDialog.java
@@ -0,0 +1,179 @@
+/*
+ * 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;
+
+import android.app.AlertDialog;
+import android.content.Context;
+import android.content.DialogInterface;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.widget.SeekBar;
+import android.widget.SeekBar.OnSeekBarChangeListener;
+import android.widget.TextView;
+
+public final class SeekBarDialog implements DialogInterface.OnClickListener,
+        OnSeekBarChangeListener {
+    public interface Listener {
+        public void onPositiveButtonClick(final SeekBarDialog dialog);
+        public void onNegativeButtonClick(final SeekBarDialog dialog);
+        public void onProgressChanged(final SeekBarDialog dialog);
+        public void onStartTrackingTouch(final SeekBarDialog dialog);
+        public void onStopTrackingTouch(final SeekBarDialog dialog);
+    }
+
+    public static class Adapter implements Listener {
+        @Override
+        public void onPositiveButtonClick(final SeekBarDialog dialog) {}
+        @Override
+        public void onNegativeButtonClick(final SeekBarDialog dialog) { dialog.dismiss(); }
+        @Override
+        public void onProgressChanged(final SeekBarDialog dialog) {}
+        @Override
+        public void onStartTrackingTouch(final SeekBarDialog dialog) {}
+        @Override
+        public void onStopTrackingTouch(final SeekBarDialog dialog) {}
+    }
+
+    private static final Listener EMPTY_ADAPTER = new Adapter();
+
+    private final AlertDialog mDialog;
+    private final Listener mListener;
+    private final TextView mValueView;
+    private final SeekBar mSeekBar;
+    private final String mValueFormat;
+
+    private int mValue;
+
+    private SeekBarDialog(final Builder builder) {
+        final AlertDialog.Builder dialogBuilder = builder.mDialogBuilder;
+        dialogBuilder.setView(builder.mView);
+        dialogBuilder.setPositiveButton(android.R.string.ok, this);
+        dialogBuilder.setNegativeButton(android.R.string.cancel, this);
+        mDialog = dialogBuilder.create();
+        mListener = (builder.mListener == null) ? EMPTY_ADAPTER : builder.mListener;
+        mValueView = (TextView)builder.mView.findViewById(R.id.seek_bar_dialog_value);
+        mSeekBar = (SeekBar)builder.mView.findViewById(R.id.seek_bar_dialog_bar);
+        mSeekBar.setMax(builder.mMaxValue);
+        mSeekBar.setOnSeekBarChangeListener(this);
+        if (builder.mValueFormatResId == 0) {
+            mValueFormat = "%s";
+        } else {
+            mValueFormat = mDialog.getContext().getString(builder.mValueFormatResId);
+        }
+    }
+
+    public void setValue(final int value, final boolean fromUser) {
+        mValue = value;
+        mValueView.setText(String.format(mValueFormat, value));
+        if (!fromUser) {
+            mSeekBar.setProgress(value);
+        }
+    }
+
+    public int getValue() {
+        return mValue;
+    }
+
+    public CharSequence getValueText() {
+        return mValueView.getText();
+    }
+
+    public void show() {
+        mDialog.show();
+    }
+
+    public void dismiss() {
+        mDialog.dismiss();
+    }
+
+    @Override
+    public void onClick(final DialogInterface dialog, int which) {
+        if (which == DialogInterface.BUTTON_POSITIVE) {
+            mListener.onPositiveButtonClick(this);
+            return;
+        }
+        if (which == DialogInterface.BUTTON_NEGATIVE) {
+            mListener.onNegativeButtonClick(this);
+            return;
+        }
+    }
+
+    @Override
+    public void onProgressChanged(final SeekBar seekBar, final int progress,
+            final boolean fromUser) {
+        setValue(progress, fromUser);
+        if (fromUser) {
+            mListener.onProgressChanged(this);
+        }
+    }
+
+    @Override
+    public void onStartTrackingTouch(final SeekBar seekBar) {
+        mListener.onStartTrackingTouch(this);
+    }
+
+    @Override
+    public void onStopTrackingTouch(final SeekBar seekBar) {
+        mListener.onStopTrackingTouch(this);
+    }
+
+    public static final class Builder {
+        final AlertDialog.Builder mDialogBuilder;
+        final View mView;
+
+        int mMaxValue;
+        int mValueFormatResId;
+        int mValue;
+        Listener mListener;
+
+        public Builder(final Context context) {
+            mDialogBuilder = new AlertDialog.Builder(context);
+            mView = LayoutInflater.from(context).inflate(R.layout.seek_bar_dialog, null);
+        }
+
+        public Builder setTitle(final int resId) {
+            mDialogBuilder.setTitle(resId);
+            return this;
+        }
+
+        public Builder setMaxValue(final int max) {
+            mMaxValue = max;
+            return this;
+        }
+
+        public Builder setValueFromat(final int resId) {
+            mValueFormatResId = resId;
+            return this;
+        }
+
+        public Builder setValue(final int value) {
+            mValue = value;
+            return this;
+        }
+
+        public Builder setListener(final Listener listener) {
+            mListener = listener;
+            return this;
+        }
+
+        public SeekBarDialog create() {
+            final SeekBarDialog dialog = new SeekBarDialog(this);
+            dialog.setValue(mValue, false /* fromUser */);
+            return dialog;
+        }
+    }
+}
diff --git a/java/src/com/android/inputmethod/latin/Settings.java b/java/src/com/android/inputmethod/latin/Settings.java
index 7a73cad..1d9d85b 100644
--- a/java/src/com/android/inputmethod/latin/Settings.java
+++ b/java/src/com/android/inputmethod/latin/Settings.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2008 The Android Open Source Project
+ * 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
@@ -16,34 +16,16 @@
 
 package com.android.inputmethod.latin;
 
-import android.app.AlertDialog;
-import android.app.backup.BackupManager;
 import android.content.Context;
-import android.content.DialogInterface;
-import android.content.Intent;
 import android.content.SharedPreferences;
 import android.content.res.Resources;
-import android.media.AudioManager;
-import android.os.Bundle;
-import android.preference.CheckBoxPreference;
-import android.preference.ListPreference;
-import android.preference.Preference;
-import android.preference.Preference.OnPreferenceClickListener;
-import android.preference.PreferenceGroup;
-import android.preference.PreferenceScreen;
-import android.view.LayoutInflater;
-import android.view.View;
-import android.view.inputmethod.InputMethodSubtype;
-import android.widget.SeekBar;
-import android.widget.SeekBar.OnSeekBarChangeListener;
-import android.widget.TextView;
+import android.preference.PreferenceManager;
 
-import com.android.inputmethod.latin.define.ProductionFlag;
-import com.android.inputmethodcommon.InputMethodSettingsFragment;
+import com.android.inputmethod.latin.LocaleUtils.RunInLocale;
 
-public final class Settings extends InputMethodSettingsFragment
-        implements SharedPreferences.OnSharedPreferenceChangeListener {
+import java.util.Locale;
 
+public final class Settings implements SharedPreferences.OnSharedPreferenceChangeListener {
     // In the same order as xml/prefs.xml
     public static final String PREF_GENERAL_SETTINGS = "general_settings";
     public static final String PREF_AUTO_CAP = "auto_cap";
@@ -84,387 +66,54 @@
     public static final String PREF_SELECTED_LANGUAGES = "selected_languages";
     public static final String PREF_DEBUG_SETTINGS = "debug_settings";
 
-    private PreferenceScreen mKeypressVibrationDurationSettingsPref;
-    private PreferenceScreen mKeypressSoundVolumeSettingsPref;
-    private ListPreference mVoicePreference;
-    private ListPreference mShowCorrectionSuggestionsPreference;
-    private ListPreference mAutoCorrectionThresholdPreference;
-    private ListPreference mKeyPreviewPopupDismissDelay;
-    // Use bigrams to predict the next word when there is no input for it yet
-    private CheckBoxPreference mBigramPrediction;
-    private Preference mDebugSettingsPreference;
+    private Resources mRes;
+    private SharedPreferences mPrefs;
+    private Locale mCurrentLocale;
+    private SettingsValues mSettingsValues;
 
-    private TextView mKeypressVibrationDurationSettingsTextView;
-    private TextView mKeypressSoundVolumeSettingsTextView;
+    private static final Settings sInstance = new Settings();
 
-    private static void setPreferenceEnabled(final Preference preference, final boolean enabled) {
-        if (preference != null) {
-            preference.setEnabled(enabled);
-        }
+    public static Settings getInstance() {
+        return sInstance;
     }
 
-    private void ensureConsistencyOfAutoCorrectionSettings() {
-        final String autoCorrectionOff = getResources().getString(
-                R.string.auto_correction_threshold_mode_index_off);
-        final String currentSetting = mAutoCorrectionThresholdPreference.getValue();
-        setPreferenceEnabled(mBigramPrediction, !currentSetting.equals(autoCorrectionOff));
+    public static void init(final Context context) {
+        sInstance.onCreate(context);
     }
 
-    @Override
-    public void onCreate(final Bundle icicle) {
-        super.onCreate(icicle);
-        setInputMethodSettingsCategoryTitle(R.string.language_selection_title);
-        setSubtypeEnablerTitle(R.string.select_language);
-        addPreferencesFromResource(R.xml.prefs);
-
-        final Resources res = getResources();
-        final Context context = getActivity();
-
-        // When we are called from the Settings application but we are not already running, the
-        // {@link SubtypeLocale} class may not have been initialized. It is safe to call
-        // {@link SubtypeLocale#init(Context)} multiple times.
-        SubtypeLocale.init(context);
-        mVoicePreference = (ListPreference) findPreference(PREF_VOICE_MODE);
-        mShowCorrectionSuggestionsPreference =
-                (ListPreference) findPreference(PREF_SHOW_SUGGESTIONS_SETTING);
-        SharedPreferences prefs = getPreferenceManager().getSharedPreferences();
-        prefs.registerOnSharedPreferenceChangeListener(this);
-
-        mAutoCorrectionThresholdPreference =
-                (ListPreference) findPreference(PREF_AUTO_CORRECTION_THRESHOLD);
-        mBigramPrediction = (CheckBoxPreference) findPreference(PREF_BIGRAM_PREDICTIONS);
-        ensureConsistencyOfAutoCorrectionSettings();
-
-        final PreferenceGroup generalSettings =
-                (PreferenceGroup) findPreference(PREF_GENERAL_SETTINGS);
-        final PreferenceGroup textCorrectionGroup =
-                (PreferenceGroup) findPreference(PREF_CORRECTION_SETTINGS);
-        final PreferenceGroup gestureTypingSettings =
-                (PreferenceGroup) findPreference(PREF_GESTURE_SETTINGS);
-        final PreferenceGroup miscSettings =
-                (PreferenceGroup) findPreference(PREF_MISC_SETTINGS);
-
-        mDebugSettingsPreference = findPreference(PREF_DEBUG_SETTINGS);
-        if (mDebugSettingsPreference != null) {
-            if (ProductionFlag.IS_INTERNAL) {
-                final Intent debugSettingsIntent = new Intent(Intent.ACTION_MAIN);
-                debugSettingsIntent.setClassName(
-                        context.getPackageName(), DebugSettingsActivity.class.getName());
-                mDebugSettingsPreference.setIntent(debugSettingsIntent);
-            } else {
-                miscSettings.removePreference(mDebugSettingsPreference);
-            }
-        }
-
-        final boolean showVoiceKeyOption = res.getBoolean(
-                R.bool.config_enable_show_voice_key_option);
-        if (!showVoiceKeyOption) {
-            generalSettings.removePreference(mVoicePreference);
-        }
-
-        final PreferenceGroup advancedSettings =
-                (PreferenceGroup) findPreference(PREF_ADVANCED_SETTINGS);
-        if (!VibratorUtils.getInstance(context).hasVibrator()) {
-            generalSettings.removePreference(findPreference(PREF_VIBRATE_ON));
-            if (null != advancedSettings) { // Theoretically advancedSettings cannot be null
-                advancedSettings.removePreference(findPreference(PREF_VIBRATION_DURATION_SETTINGS));
-            }
-        }
-
-        final boolean showKeyPreviewPopupOption = res.getBoolean(
-                R.bool.config_enable_show_popup_on_keypress_option);
-        mKeyPreviewPopupDismissDelay =
-                (ListPreference) findPreference(PREF_KEY_PREVIEW_POPUP_DISMISS_DELAY);
-        if (!showKeyPreviewPopupOption) {
-            generalSettings.removePreference(findPreference(PREF_POPUP_ON));
-            if (null != advancedSettings) { // Theoretically advancedSettings cannot be null
-                advancedSettings.removePreference(mKeyPreviewPopupDismissDelay);
-            }
-        } else {
-            final String[] entries = new String[] {
-                    res.getString(R.string.key_preview_popup_dismiss_no_delay),
-                    res.getString(R.string.key_preview_popup_dismiss_default_delay),
-            };
-            final String popupDismissDelayDefaultValue = Integer.toString(res.getInteger(
-                    R.integer.config_key_preview_linger_timeout));
-            mKeyPreviewPopupDismissDelay.setEntries(entries);
-            mKeyPreviewPopupDismissDelay.setEntryValues(
-                    new String[] { "0", popupDismissDelayDefaultValue });
-            if (null == mKeyPreviewPopupDismissDelay.getValue()) {
-                mKeyPreviewPopupDismissDelay.setValue(popupDismissDelayDefaultValue);
-            }
-            setPreferenceEnabled(mKeyPreviewPopupDismissDelay,
-                    SettingsValues.isKeyPreviewPopupEnabled(prefs, res));
-        }
-
-        setPreferenceEnabled(findPreference(PREF_INCLUDE_OTHER_IMES_IN_LANGUAGE_SWITCH_LIST),
-                SettingsValues.showsLanguageSwitchKey(prefs));
-
-        final PreferenceScreen dictionaryLink =
-                (PreferenceScreen) findPreference(PREF_CONFIGURE_DICTIONARIES_KEY);
-        final Intent intent = dictionaryLink.getIntent();
-
-        final int number = context.getPackageManager().queryIntentActivities(intent, 0).size();
-        // TODO: The experimental version is not supported by the Dictionary Pack Service yet
-        if (ProductionFlag.IS_EXPERIMENTAL || 0 >= number) {
-            textCorrectionGroup.removePreference(dictionaryLink);
-        }
-
-        final boolean gestureInputEnabledByBuildConfig = res.getBoolean(
-                R.bool.config_gesture_input_enabled_by_build_config);
-        if (!gestureInputEnabledByBuildConfig) {
-            getPreferenceScreen().removePreference(gestureTypingSettings);
-        }
-
-        mKeypressVibrationDurationSettingsPref =
-                (PreferenceScreen) findPreference(PREF_VIBRATION_DURATION_SETTINGS);
-        if (mKeypressVibrationDurationSettingsPref != null) {
-            mKeypressVibrationDurationSettingsPref.setOnPreferenceClickListener(
-                    new OnPreferenceClickListener() {
-                        @Override
-                        public boolean onPreferenceClick(Preference arg0) {
-                            showKeypressVibrationDurationSettingsDialog();
-                            return true;
-                        }
-                    });
-            updateKeypressVibrationDurationSettingsSummary(prefs, res);
-        }
-
-        mKeypressSoundVolumeSettingsPref =
-                (PreferenceScreen) findPreference(PREF_KEYPRESS_SOUND_VOLUME);
-        if (mKeypressSoundVolumeSettingsPref != null) {
-            mKeypressSoundVolumeSettingsPref.setOnPreferenceClickListener(
-                    new OnPreferenceClickListener() {
-                        @Override
-                        public boolean onPreferenceClick(Preference arg0) {
-                            showKeypressSoundVolumeSettingDialog();
-                            return true;
-                        }
-                    });
-            updateKeypressSoundVolumeSummary(prefs, res);
-        }
-        refreshEnablingsOfKeypressSoundAndVibrationSettings(prefs, res);
+    private Settings() {
+        // Intentional empty constructor for singleton.
     }
 
-    @Override
-    public void onResume() {
-        super.onResume();
-        final boolean isShortcutImeEnabled = SubtypeSwitcher.getInstance().isShortcutImeEnabled();
-        if (isShortcutImeEnabled) {
-            updateVoiceModeSummary();
-        } else {
-            getPreferenceScreen().removePreference(mVoicePreference);
-        }
-        updateShowCorrectionSuggestionsSummary();
-        updateKeyPreviewPopupDelaySummary();
-        updateCustomInputStylesSummary();
+    private void onCreate(final Context context) {
+        mRes = context.getResources();
+        mPrefs = PreferenceManager.getDefaultSharedPreferences(context);
+        mPrefs.registerOnSharedPreferenceChangeListener(this);
     }
 
-    @Override
     public void onDestroy() {
-        getPreferenceManager().getSharedPreferences().unregisterOnSharedPreferenceChangeListener(
-                this);
-        super.onDestroy();
+        mPrefs.unregisterOnSharedPreferenceChangeListener(this);
     }
 
     @Override
     public void onSharedPreferenceChanged(final SharedPreferences prefs, final String key) {
-        (new BackupManager(getActivity())).dataChanged();
-        if (key.equals(PREF_POPUP_ON)) {
-            setPreferenceEnabled(findPreference(PREF_KEY_PREVIEW_POPUP_DISMISS_DELAY),
-                    prefs.getBoolean(PREF_POPUP_ON, true));
-        } else if (key.equals(PREF_SHOW_LANGUAGE_SWITCH_KEY)) {
-            setPreferenceEnabled(findPreference(PREF_INCLUDE_OTHER_IMES_IN_LANGUAGE_SWITCH_LIST),
-                    SettingsValues.showsLanguageSwitchKey(prefs));
-        } else if (key.equals(PREF_GESTURE_INPUT)) {
-            final boolean gestureInputEnabledByConfig = getResources().getBoolean(
-                    R.bool.config_gesture_input_enabled_by_build_config);
-            if (gestureInputEnabledByConfig) {
-                final boolean gestureInputEnabledByUser = prefs.getBoolean(
-                        PREF_GESTURE_INPUT, true);
-                setPreferenceEnabled(findPreference(PREF_GESTURE_PREVIEW_TRAIL),
-                        gestureInputEnabledByUser);
-                setPreferenceEnabled(findPreference(PREF_GESTURE_FLOATING_PREVIEW_TEXT),
-                        gestureInputEnabledByUser);
-            }
-        }
-        ensureConsistencyOfAutoCorrectionSettings();
-        updateVoiceModeSummary();
-        updateShowCorrectionSuggestionsSummary();
-        updateKeyPreviewPopupDelaySummary();
-        refreshEnablingsOfKeypressSoundAndVibrationSettings(prefs, getResources());
+        loadSettings(mCurrentLocale, mSettingsValues.mInputAttributes);
     }
 
-    private void updateShowCorrectionSuggestionsSummary() {
-        mShowCorrectionSuggestionsPreference.setSummary(
-                getResources().getStringArray(R.array.prefs_suggestion_visibilities)
-                [mShowCorrectionSuggestionsPreference.findIndexOfValue(
-                        mShowCorrectionSuggestionsPreference.getValue())]);
+    public void loadSettings(final Locale locale, final InputAttributes inputAttributes) {
+        mCurrentLocale = locale;
+        final SharedPreferences prefs = mPrefs;
+        final RunInLocale<SettingsValues> job = new RunInLocale<SettingsValues>() {
+            @Override
+            protected SettingsValues job(final Resources res) {
+                return new SettingsValues(prefs, res, inputAttributes);
+            }
+        };
+        mSettingsValues = job.runInLocale(mRes, locale);
     }
 
-    private void updateCustomInputStylesSummary() {
-        final PreferenceScreen customInputStyles =
-                (PreferenceScreen)findPreference(PREF_CUSTOM_INPUT_STYLES);
-        final SharedPreferences prefs = getPreferenceManager().getSharedPreferences();
-        final Resources res = getResources();
-        final String prefSubtype = SettingsValues.getPrefAdditionalSubtypes(prefs, res);
-        final InputMethodSubtype[] subtypes =
-                AdditionalSubtype.createAdditionalSubtypesArray(prefSubtype);
-        final StringBuilder styles = new StringBuilder();
-        for (final InputMethodSubtype subtype : subtypes) {
-            if (styles.length() > 0) styles.append(", ");
-            styles.append(SubtypeLocale.getSubtypeDisplayName(subtype, res));
-        }
-        customInputStyles.setSummary(styles);
-    }
-
-    private void updateKeyPreviewPopupDelaySummary() {
-        final ListPreference lp = mKeyPreviewPopupDismissDelay;
-        final CharSequence[] entries = lp.getEntries();
-        if (entries == null || entries.length <= 0) return;
-        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) {
-        if (mKeypressVibrationDurationSettingsPref != null) {
-            final boolean hasVibratorHardware = VibratorUtils.getInstance(getActivity())
-                    .hasVibrator();
-            final boolean vibrateOnByUser = sp.getBoolean(Settings.PREF_VIBRATE_ON,
-                    res.getBoolean(R.bool.config_default_vibration_enabled));
-            setPreferenceEnabled(mKeypressVibrationDurationSettingsPref,
-                    hasVibratorHardware && vibrateOnByUser);
-        }
-
-        if (mKeypressSoundVolumeSettingsPref != null) {
-            final boolean soundOn = sp.getBoolean(Settings.PREF_SOUND_ON,
-                    res.getBoolean(R.bool.config_default_sound_enabled));
-            setPreferenceEnabled(mKeypressSoundVolumeSettingsPref, soundOn);
-        }
-    }
-
-    private void updateKeypressVibrationDurationSettingsSummary(
-            final SharedPreferences sp, final Resources res) {
-        if (mKeypressVibrationDurationSettingsPref != null) {
-            mKeypressVibrationDurationSettingsPref.setSummary(
-                    SettingsValues.getCurrentVibrationDuration(sp, res)
-                            + res.getString(R.string.settings_ms));
-        }
-    }
-
-    private void showKeypressVibrationDurationSettingsDialog() {
-        final SharedPreferences sp = getPreferenceManager().getSharedPreferences();
-        final Context context = getActivity();
-        final Resources res = context.getResources();
-        final AlertDialog.Builder builder = new AlertDialog.Builder(context);
-        builder.setTitle(R.string.prefs_keypress_vibration_duration_settings);
-        builder.setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() {
-            @Override
-            public void onClick(DialogInterface dialog, int whichButton) {
-                final int ms = Integer.valueOf(
-                        mKeypressVibrationDurationSettingsTextView.getText().toString());
-                sp.edit().putInt(Settings.PREF_VIBRATION_DURATION_SETTINGS, ms).apply();
-                updateKeypressVibrationDurationSettingsSummary(sp, res);
-            }
-        });
-        builder.setNegativeButton(android.R.string.cancel,  new DialogInterface.OnClickListener() {
-            @Override
-            public void onClick(DialogInterface dialog, int whichButton) {
-                dialog.dismiss();
-            }
-        });
-        final View v = LayoutInflater.from(context).inflate(
-                R.layout.vibration_settings_dialog, null);
-        final int currentMs = SettingsValues.getCurrentVibrationDuration(
-                getPreferenceManager().getSharedPreferences(), getResources());
-        mKeypressVibrationDurationSettingsTextView = (TextView)v.findViewById(R.id.vibration_value);
-        final SeekBar sb = (SeekBar)v.findViewById(R.id.vibration_settings);
-        sb.setOnSeekBarChangeListener(new OnSeekBarChangeListener() {
-            @Override
-            public void onProgressChanged(SeekBar arg0, int arg1, boolean arg2) {
-                final int tempMs = arg1;
-                mKeypressVibrationDurationSettingsTextView.setText(String.valueOf(tempMs));
-            }
-
-            @Override
-            public void onStartTrackingTouch(SeekBar arg0) {
-            }
-
-            @Override
-            public void onStopTrackingTouch(SeekBar arg0) {
-                final int tempMs = arg0.getProgress();
-                VibratorUtils.getInstance(context).vibrate(tempMs);
-            }
-        });
-        sb.setProgress(currentMs);
-        mKeypressVibrationDurationSettingsTextView.setText(String.valueOf(currentMs));
-        builder.setView(v);
-        builder.create().show();
-    }
-
-    private void updateKeypressSoundVolumeSummary(final SharedPreferences sp, final Resources res) {
-        if (mKeypressSoundVolumeSettingsPref != null) {
-            mKeypressSoundVolumeSettingsPref.setSummary(String.valueOf(
-                    (int)(SettingsValues.getCurrentKeypressSoundVolume(sp, res) * 100)));
-        }
-    }
-
-    private void showKeypressSoundVolumeSettingDialog() {
-        final Context context = getActivity();
-        final AudioManager am = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE);
-        final SharedPreferences sp = getPreferenceManager().getSharedPreferences();
-        final Resources res = context.getResources();
-        final AlertDialog.Builder builder = new AlertDialog.Builder(context);
-        builder.setTitle(R.string.prefs_keypress_sound_volume_settings);
-        builder.setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() {
-            @Override
-            public void onClick(DialogInterface dialog, int whichButton) {
-                final float volume =
-                        ((float)Integer.valueOf(
-                                mKeypressSoundVolumeSettingsTextView.getText().toString())) / 100;
-                sp.edit().putFloat(Settings.PREF_KEYPRESS_SOUND_VOLUME, volume).apply();
-                updateKeypressSoundVolumeSummary(sp, res);
-            }
-        });
-        builder.setNegativeButton(android.R.string.cancel,  new DialogInterface.OnClickListener() {
-            @Override
-            public void onClick(DialogInterface dialog, int whichButton) {
-                dialog.dismiss();
-            }
-        });
-        final View v = LayoutInflater.from(context).inflate(
-                R.layout.sound_effect_volume_dialog, null);
-        final int currentVolumeInt =
-                (int)(SettingsValues.getCurrentKeypressSoundVolume(sp, res) * 100);
-        mKeypressSoundVolumeSettingsTextView =
-                (TextView)v.findViewById(R.id.sound_effect_volume_value);
-        final SeekBar sb = (SeekBar)v.findViewById(R.id.sound_effect_volume_bar);
-        sb.setOnSeekBarChangeListener(new OnSeekBarChangeListener() {
-            @Override
-            public void onProgressChanged(SeekBar arg0, int arg1, boolean arg2) {
-                final int tempVolume = arg1;
-                mKeypressSoundVolumeSettingsTextView.setText(String.valueOf(tempVolume));
-            }
-
-            @Override
-            public void onStartTrackingTouch(SeekBar arg0) {
-            }
-
-            @Override
-            public void onStopTrackingTouch(SeekBar arg0) {
-                final float tempVolume = ((float)arg0.getProgress()) / 100;
-                am.playSoundEffect(AudioManager.FX_KEYPRESS_STANDARD, tempVolume);
-            }
-        });
-        sb.setProgress(currentVolumeInt);
-        mKeypressSoundVolumeSettingsTextView.setText(String.valueOf(currentVolumeInt));
-        builder.setView(v);
-        builder.create().show();
+    // TODO: Remove this method and add proxy method to SettingsValues.
+    public SettingsValues getCurrent() {
+        return mSettingsValues;
     }
 }
diff --git a/java/src/com/android/inputmethod/latin/SettingsActivity.java b/java/src/com/android/inputmethod/latin/SettingsActivity.java
index 0d3c8eb..3aeb101 100644
--- a/java/src/com/android/inputmethod/latin/SettingsActivity.java
+++ b/java/src/com/android/inputmethod/latin/SettingsActivity.java
@@ -20,7 +20,7 @@
 import android.preference.PreferenceActivity;
 
 public final class SettingsActivity extends PreferenceActivity {
-    private static final String DEFAULT_FRAGMENT = Settings.class.getName();
+    private static final String DEFAULT_FRAGMENT = SettingsFragment.class.getName();
 
     @Override
     public Intent getIntent() {
diff --git a/java/src/com/android/inputmethod/latin/SettingsFragment.java b/java/src/com/android/inputmethod/latin/SettingsFragment.java
new file mode 100644
index 0000000..a2980bf
--- /dev/null
+++ b/java/src/com/android/inputmethod/latin/SettingsFragment.java
@@ -0,0 +1,375 @@
+/*
+ * 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.
+ */
+
+package com.android.inputmethod.latin;
+
+import android.app.backup.BackupManager;
+import android.content.Context;
+import android.content.Intent;
+import android.content.SharedPreferences;
+import android.content.res.Resources;
+import android.media.AudioManager;
+import android.os.Bundle;
+import android.preference.CheckBoxPreference;
+import android.preference.ListPreference;
+import android.preference.Preference;
+import android.preference.Preference.OnPreferenceClickListener;
+import android.preference.PreferenceGroup;
+import android.preference.PreferenceScreen;
+import android.view.inputmethod.InputMethodSubtype;
+
+import com.android.inputmethod.latin.define.ProductionFlag;
+import com.android.inputmethodcommon.InputMethodSettingsFragment;
+
+public final class SettingsFragment extends InputMethodSettingsFragment
+        implements SharedPreferences.OnSharedPreferenceChangeListener {
+    private PreferenceScreen mKeypressVibrationDurationSettingsPref;
+    private PreferenceScreen mKeypressSoundVolumeSettingsPref;
+    private ListPreference mVoicePreference;
+    private ListPreference mShowCorrectionSuggestionsPreference;
+    private ListPreference mAutoCorrectionThresholdPreference;
+    private ListPreference mKeyPreviewPopupDismissDelay;
+    // Use bigrams to predict the next word when there is no input for it yet
+    private CheckBoxPreference mBigramPrediction;
+    private Preference mDebugSettingsPreference;
+
+    private void setPreferenceEnabled(final String preferenceKey, final boolean enabled) {
+        final Preference preference = findPreference(preferenceKey);
+        if (preference != null) {
+            preference.setEnabled(enabled);
+        }
+    }
+
+    private void ensureConsistencyOfAutoCorrectionSettings() {
+        final String autoCorrectionOff = getResources().getString(
+                R.string.auto_correction_threshold_mode_index_off);
+        final String currentSetting = mAutoCorrectionThresholdPreference.getValue();
+        mBigramPrediction.setEnabled(!currentSetting.equals(autoCorrectionOff));
+    }
+
+    @Override
+    public void onCreate(final Bundle icicle) {
+        super.onCreate(icicle);
+        setInputMethodSettingsCategoryTitle(R.string.language_selection_title);
+        setSubtypeEnablerTitle(R.string.select_language);
+        addPreferencesFromResource(R.xml.prefs);
+
+        final Resources res = getResources();
+        final Context context = getActivity();
+
+        // When we are called from the Settings application but we are not already running, the
+        // {@link SubtypeLocale} class may not have been initialized. It is safe to call
+        // {@link SubtypeLocale#init(Context)} multiple times.
+        SubtypeLocale.init(context);
+        mVoicePreference = (ListPreference) findPreference(Settings.PREF_VOICE_MODE);
+        mShowCorrectionSuggestionsPreference =
+                (ListPreference) findPreference(Settings.PREF_SHOW_SUGGESTIONS_SETTING);
+        final SharedPreferences prefs = getPreferenceManager().getSharedPreferences();
+        prefs.registerOnSharedPreferenceChangeListener(this);
+
+        mAutoCorrectionThresholdPreference =
+                (ListPreference) findPreference(Settings.PREF_AUTO_CORRECTION_THRESHOLD);
+        mBigramPrediction = (CheckBoxPreference) findPreference(Settings.PREF_BIGRAM_PREDICTIONS);
+        ensureConsistencyOfAutoCorrectionSettings();
+
+        final PreferenceGroup generalSettings =
+                (PreferenceGroup) findPreference(Settings.PREF_GENERAL_SETTINGS);
+        final PreferenceGroup textCorrectionGroup =
+                (PreferenceGroup) findPreference(Settings.PREF_CORRECTION_SETTINGS);
+        final PreferenceGroup gestureTypingSettings =
+                (PreferenceGroup) findPreference(Settings.PREF_GESTURE_SETTINGS);
+        final PreferenceGroup miscSettings =
+                (PreferenceGroup) findPreference(Settings.PREF_MISC_SETTINGS);
+
+        mDebugSettingsPreference = findPreference(Settings.PREF_DEBUG_SETTINGS);
+        if (mDebugSettingsPreference != null) {
+            if (ProductionFlag.IS_INTERNAL) {
+                final Intent debugSettingsIntent = new Intent(Intent.ACTION_MAIN);
+                debugSettingsIntent.setClassName(
+                        context.getPackageName(), DebugSettingsActivity.class.getName());
+                mDebugSettingsPreference.setIntent(debugSettingsIntent);
+            } else {
+                miscSettings.removePreference(mDebugSettingsPreference);
+            }
+        }
+
+        final boolean showVoiceKeyOption = res.getBoolean(
+                R.bool.config_enable_show_voice_key_option);
+        if (!showVoiceKeyOption) {
+            generalSettings.removePreference(mVoicePreference);
+        }
+
+        final PreferenceGroup advancedSettings =
+                (PreferenceGroup) findPreference(Settings.PREF_ADVANCED_SETTINGS);
+        if (!AudioAndHapticFeedbackManager.getInstance().hasVibrator()) {
+            generalSettings.removePreference(findPreference(Settings.PREF_VIBRATE_ON));
+            if (null != advancedSettings) { // Theoretically advancedSettings cannot be null
+                advancedSettings.removePreference(
+                        findPreference(Settings.PREF_VIBRATION_DURATION_SETTINGS));
+            }
+        }
+
+        final boolean showKeyPreviewPopupOption = res.getBoolean(
+                R.bool.config_enable_show_popup_on_keypress_option);
+        mKeyPreviewPopupDismissDelay =
+                (ListPreference) findPreference(Settings.PREF_KEY_PREVIEW_POPUP_DISMISS_DELAY);
+        if (!showKeyPreviewPopupOption) {
+            generalSettings.removePreference(findPreference(Settings.PREF_POPUP_ON));
+            if (null != advancedSettings) { // Theoretically advancedSettings cannot be null
+                advancedSettings.removePreference(mKeyPreviewPopupDismissDelay);
+            }
+        } else {
+            final String[] entries = new String[] {
+                    res.getString(R.string.key_preview_popup_dismiss_no_delay),
+                    res.getString(R.string.key_preview_popup_dismiss_default_delay),
+            };
+            final String popupDismissDelayDefaultValue = Integer.toString(res.getInteger(
+                    R.integer.config_key_preview_linger_timeout));
+            mKeyPreviewPopupDismissDelay.setEntries(entries);
+            mKeyPreviewPopupDismissDelay.setEntryValues(
+                    new String[] { "0", popupDismissDelayDefaultValue });
+            if (null == mKeyPreviewPopupDismissDelay.getValue()) {
+                mKeyPreviewPopupDismissDelay.setValue(popupDismissDelayDefaultValue);
+            }
+            mKeyPreviewPopupDismissDelay.setEnabled(
+                    SettingsValues.isKeyPreviewPopupEnabled(prefs, res));
+        }
+
+        setPreferenceEnabled(Settings.PREF_INCLUDE_OTHER_IMES_IN_LANGUAGE_SWITCH_LIST,
+                SettingsValues.showsLanguageSwitchKey(prefs));
+
+        final PreferenceScreen dictionaryLink =
+                (PreferenceScreen) findPreference(Settings.PREF_CONFIGURE_DICTIONARIES_KEY);
+        final Intent intent = dictionaryLink.getIntent();
+
+        final int number = context.getPackageManager().queryIntentActivities(intent, 0).size();
+        // TODO: The experimental version is not supported by the Dictionary Pack Service yet
+        if (ProductionFlag.IS_EXPERIMENTAL || 0 >= number) {
+            textCorrectionGroup.removePreference(dictionaryLink);
+        }
+
+        final boolean gestureInputEnabledByBuildConfig = res.getBoolean(
+                R.bool.config_gesture_input_enabled_by_build_config);
+        if (!gestureInputEnabledByBuildConfig) {
+            getPreferenceScreen().removePreference(gestureTypingSettings);
+        }
+
+        mKeypressVibrationDurationSettingsPref =
+                (PreferenceScreen) findPreference(Settings.PREF_VIBRATION_DURATION_SETTINGS);
+        if (mKeypressVibrationDurationSettingsPref != null) {
+            mKeypressVibrationDurationSettingsPref.setOnPreferenceClickListener(
+                    new OnPreferenceClickListener() {
+                        @Override
+                        public boolean onPreferenceClick(Preference arg0) {
+                            showKeypressVibrationDurationSettingsDialog();
+                            return true;
+                        }
+                    });
+            mKeypressVibrationDurationSettingsPref.setSummary(
+                    res.getString(R.string.settings_keypress_vibration_duration,
+                            SettingsValues.getCurrentVibrationDuration(prefs, res)));
+        }
+
+        mKeypressSoundVolumeSettingsPref =
+                (PreferenceScreen) findPreference(Settings.PREF_KEYPRESS_SOUND_VOLUME);
+        if (mKeypressSoundVolumeSettingsPref != null) {
+            mKeypressSoundVolumeSettingsPref.setOnPreferenceClickListener(
+                    new OnPreferenceClickListener() {
+                        @Override
+                        public boolean onPreferenceClick(Preference arg0) {
+                            showKeypressSoundVolumeSettingDialog();
+                            return true;
+                        }
+                    });
+            mKeypressSoundVolumeSettingsPref.setSummary(String.valueOf(
+                    getCurrentKeyPressSoundVolumePercent(prefs, res)));
+        }
+        refreshEnablingsOfKeypressSoundAndVibrationSettings(prefs, res);
+    }
+
+    @Override
+    public void onResume() {
+        super.onResume();
+        final boolean isShortcutImeEnabled = SubtypeSwitcher.getInstance().isShortcutImeEnabled();
+        if (isShortcutImeEnabled) {
+            updateVoiceModeSummary();
+        } else {
+            getPreferenceScreen().removePreference(mVoicePreference);
+        }
+        updateShowCorrectionSuggestionsSummary();
+        updateKeyPreviewPopupDelaySummary();
+        updateCustomInputStylesSummary();
+    }
+
+    @Override
+    public void onDestroy() {
+        getPreferenceManager().getSharedPreferences().unregisterOnSharedPreferenceChangeListener(
+                this);
+        super.onDestroy();
+    }
+
+    @Override
+    public void onSharedPreferenceChanged(final SharedPreferences prefs, final String key) {
+        (new BackupManager(getActivity())).dataChanged();
+        if (key.equals(Settings.PREF_POPUP_ON)) {
+            setPreferenceEnabled(Settings.PREF_KEY_PREVIEW_POPUP_DISMISS_DELAY,
+                    prefs.getBoolean(Settings.PREF_POPUP_ON, true));
+        } else if (key.equals(Settings.PREF_SHOW_LANGUAGE_SWITCH_KEY)) {
+            setPreferenceEnabled(Settings.PREF_INCLUDE_OTHER_IMES_IN_LANGUAGE_SWITCH_LIST,
+                    SettingsValues.showsLanguageSwitchKey(prefs));
+        } else if (key.equals(Settings.PREF_GESTURE_INPUT)) {
+            final boolean gestureInputEnabledByConfig = getResources().getBoolean(
+                    R.bool.config_gesture_input_enabled_by_build_config);
+            if (gestureInputEnabledByConfig) {
+                final boolean gestureInputEnabledByUser = prefs.getBoolean(
+                        Settings.PREF_GESTURE_INPUT, true);
+                setPreferenceEnabled(Settings.PREF_GESTURE_PREVIEW_TRAIL,
+                        gestureInputEnabledByUser);
+                setPreferenceEnabled(Settings.PREF_GESTURE_FLOATING_PREVIEW_TEXT,
+                        gestureInputEnabledByUser);
+            }
+        }
+        ensureConsistencyOfAutoCorrectionSettings();
+        updateVoiceModeSummary();
+        updateShowCorrectionSuggestionsSummary();
+        updateKeyPreviewPopupDelaySummary();
+        refreshEnablingsOfKeypressSoundAndVibrationSettings(prefs, getResources());
+    }
+
+    private void updateShowCorrectionSuggestionsSummary() {
+        mShowCorrectionSuggestionsPreference.setSummary(
+                getResources().getStringArray(R.array.prefs_suggestion_visibilities)
+                [mShowCorrectionSuggestionsPreference.findIndexOfValue(
+                        mShowCorrectionSuggestionsPreference.getValue())]);
+    }
+
+    private void updateCustomInputStylesSummary() {
+        final PreferenceScreen customInputStyles =
+                (PreferenceScreen)findPreference(Settings.PREF_CUSTOM_INPUT_STYLES);
+        final SharedPreferences prefs = getPreferenceManager().getSharedPreferences();
+        final Resources res = getResources();
+        final String prefSubtype = SettingsValues.getPrefAdditionalSubtypes(prefs, res);
+        final InputMethodSubtype[] subtypes =
+                AdditionalSubtype.createAdditionalSubtypesArray(prefSubtype);
+        final StringBuilder styles = new StringBuilder();
+        for (final InputMethodSubtype subtype : subtypes) {
+            if (styles.length() > 0) styles.append(", ");
+            styles.append(SubtypeLocale.getSubtypeDisplayName(subtype, res));
+        }
+        customInputStyles.setSummary(styles);
+    }
+
+    private void updateKeyPreviewPopupDelaySummary() {
+        final ListPreference lp = mKeyPreviewPopupDismissDelay;
+        final CharSequence[] entries = lp.getEntries();
+        if (entries == null || entries.length <= 0) return;
+        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) {
+        if (mKeypressVibrationDurationSettingsPref != null) {
+            final boolean hasVibratorHardware =
+                    AudioAndHapticFeedbackManager.getInstance().hasVibrator();
+            final boolean vibrateOnByUser = sp.getBoolean(Settings.PREF_VIBRATE_ON,
+                    res.getBoolean(R.bool.config_default_vibration_enabled));
+            mKeypressVibrationDurationSettingsPref.setEnabled(
+                    hasVibratorHardware && vibrateOnByUser);
+        }
+
+        if (mKeypressSoundVolumeSettingsPref != null) {
+            final boolean soundOn = sp.getBoolean(Settings.PREF_SOUND_ON,
+                    res.getBoolean(R.bool.config_default_sound_enabled));
+            mKeypressSoundVolumeSettingsPref.setEnabled(soundOn);
+        }
+    }
+
+    private void showKeypressVibrationDurationSettingsDialog() {
+        final SharedPreferences sp = getPreferenceManager().getSharedPreferences();
+        final Context context = getActivity();
+        final PreferenceScreen settingsPref = mKeypressVibrationDurationSettingsPref;
+        final SeekBarDialog.Listener listener = new SeekBarDialog.Adapter() {
+            @Override
+            public void onPositiveButtonClick(final SeekBarDialog dialog) {
+                final int ms = dialog.getValue();
+                sp.edit().putInt(Settings.PREF_VIBRATION_DURATION_SETTINGS, ms).apply();
+                if (settingsPref != null) {
+                    settingsPref.setSummary(dialog.getValueText());
+                }
+            }
+
+            @Override
+            public void onStopTrackingTouch(final SeekBarDialog dialog) {
+                final int ms = dialog.getValue();
+                AudioAndHapticFeedbackManager.getInstance().vibrate(ms);
+            }
+        };
+        final int currentMs = SettingsValues.getCurrentVibrationDuration(sp, getResources());
+        final SeekBarDialog.Builder builder = new SeekBarDialog.Builder(context);
+        builder.setTitle(R.string.prefs_keypress_vibration_duration_settings)
+                .setListener(listener)
+                .setMaxValue(AudioAndHapticFeedbackManager.MAX_KEYPRESS_VIBRATION_DURATION)
+                .setValueFromat(R.string.settings_keypress_vibration_duration)
+                .setValue(currentMs)
+                .create()
+                .show();
+    }
+
+    private static final int PERCENT_INT = 100;
+    private static final float PERCENT_FLOAT = 100.0f;
+
+    private static int getCurrentKeyPressSoundVolumePercent(final SharedPreferences sp,
+            final Resources res) {
+        return (int)(SettingsValues.getCurrentKeypressSoundVolume(sp, res) * PERCENT_FLOAT);
+    }
+
+    private void showKeypressSoundVolumeSettingDialog() {
+        final SharedPreferences sp = getPreferenceManager().getSharedPreferences();
+        final Context context = getActivity();
+        final AudioManager am = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE);
+        final PreferenceScreen settingsPref = mKeypressSoundVolumeSettingsPref;
+        final SeekBarDialog.Listener listener = new SeekBarDialog.Adapter() {
+            @Override
+            public void onPositiveButtonClick(final SeekBarDialog dialog) {
+                final float volume = dialog.getValue() / PERCENT_FLOAT;
+                sp.edit().putFloat(Settings.PREF_KEYPRESS_SOUND_VOLUME, volume).apply();
+                if (settingsPref != null) {
+                    settingsPref.setSummary(dialog.getValueText());
+                }
+            }
+
+            @Override
+            public void onStopTrackingTouch(final SeekBarDialog dialog) {
+                final float volume = dialog.getValue() / PERCENT_FLOAT;
+                am.playSoundEffect(AudioManager.FX_KEYPRESS_STANDARD, volume);
+            }
+        };
+        final SeekBarDialog.Builder builder = new SeekBarDialog.Builder(context);
+        final int currentVolumeInt = getCurrentKeyPressSoundVolumePercent(sp, getResources());
+        builder.setTitle(R.string.prefs_keypress_sound_volume_settings)
+                .setListener(listener)
+                .setMaxValue(PERCENT_INT)
+                .setValue(currentVolumeInt)
+                .create()
+                .show();
+    }
+}
diff --git a/java/src/com/android/inputmethod/latin/SettingsValues.java b/java/src/com/android/inputmethod/latin/SettingsValues.java
index a238767..3940662 100644
--- a/java/src/com/android/inputmethod/latin/SettingsValues.java
+++ b/java/src/com/android/inputmethod/latin/SettingsValues.java
@@ -16,7 +16,6 @@
 
 package com.android.inputmethod.latin;
 
-import android.content.Context;
 import android.content.SharedPreferences;
 import android.content.res.Configuration;
 import android.content.res.Resources;
@@ -78,16 +77,12 @@
     public final boolean mUseDoubleSpacePeriod;
     // Use bigrams to predict the next word when there is no input for it yet
     public final boolean mBigramPredictionEnabled;
-    @SuppressWarnings("unused") // TODO: Use this
-    private final int mVibrationDurationSettingsRawValue;
-    @SuppressWarnings("unused") // TODO: Use this
-    private final float mKeypressSoundVolumeRawValue;
     public final boolean mGestureInputEnabled;
     public final boolean mGesturePreviewTrailEnabled;
     public final boolean mGestureFloatingPreviewTextEnabled;
 
     // From the input box
-    private final InputAttributes mInputAttributes;
+    public final InputAttributes mInputAttributes;
 
     // Deduced settings
     public final int mKeypressVibrationDuration;
@@ -100,10 +95,8 @@
     private final boolean mVoiceKeyEnabled;
     private final boolean mVoiceKeyOnMain;
 
-    public SettingsValues(final SharedPreferences prefs, final InputAttributes inputAttributes,
-            final Context context) {
-        final Resources res = context.getResources();
-
+    public SettingsValues(final SharedPreferences prefs, final Resources res,
+            final InputAttributes inputAttributes) {
         // Get the resources
         mDelayUpdateOldSuggestions = res.getInteger(R.integer.config_delay_update_old_suggestions);
         mWeakSpaceStrippers = res.getString(R.string.weak_space_stripping_symbols);
@@ -125,7 +118,7 @@
                 res.getString(R.string.symbols_excluded_from_word_separators);
         mWordSeparators = createWordSeparators(mWeakSpaceStrippers, mWeakSpaceSwappers,
                 mSymbolsExcludedFromWordSeparators, res);
-        mHintToSaveText = context.getText(R.string.hint_add_to_dictionary);
+        mHintToSaveText = res.getText(R.string.hint_add_to_dictionary);
 
         // Store the input attributes
         if (null == inputAttributes) {
@@ -136,7 +129,7 @@
 
         // Get the settings preferences
         mAutoCap = prefs.getBoolean(Settings.PREF_AUTO_CAP, true);
-        mVibrateOn = isVibrateOn(context, prefs, res);
+        mVibrateOn = isVibrateOn(prefs, res);
         mSoundOn = prefs.getBoolean(Settings.PREF_SOUND_ON,
                 res.getBoolean(R.bool.config_default_sound_enabled));
         mKeyPreviewPopupOn = isKeyPreviewPopupEnabled(prefs, res);
@@ -158,9 +151,6 @@
         mUseDoubleSpacePeriod = prefs.getBoolean(Settings.PREF_KEY_USE_DOUBLE_SPACE_PERIOD, true);
         mAutoCorrectEnabled = isAutoCorrectEnabled(res, mAutoCorrectionThresholdRawValue);
         mBigramPredictionEnabled = isBigramPredictionEnabled(prefs, res);
-        mVibrationDurationSettingsRawValue =
-                prefs.getInt(Settings.PREF_VIBRATION_DURATION_SETTINGS, -1);
-        mKeypressSoundVolumeRawValue = prefs.getFloat(Settings.PREF_KEYPRESS_SOUND_VOLUME, -1.0f);
 
         // Compute other readable settings
         mKeypressVibrationDuration = getCurrentVibrationDuration(prefs, res);
@@ -221,9 +211,8 @@
         throw new RuntimeException("Bug: visibility string is not configured correctly");
     }
 
-    private static boolean isVibrateOn(final Context context, final SharedPreferences prefs,
-            final Resources res) {
-        final boolean hasVibrator = VibratorUtils.getInstance(context).hasVibrator();
+    private static boolean isVibrateOn(final SharedPreferences prefs, final Resources res) {
+        final boolean hasVibrator = AudioAndHapticFeedbackManager.getInstance().hasVibrator();
         return hasVibrator && prefs.getBoolean(Settings.PREF_VIBRATE_ON,
                 res.getBoolean(R.bool.config_default_vibration_enabled));
     }
@@ -280,6 +269,7 @@
         return !currentAutoCorrectionSetting.equals(autoCorrectionOff);
     }
 
+    // TODO: Clean up and move public helper methods to Settings class.
     // Public to access from KeyboardSwitcher. Should it have access to some
     // process-global instance instead?
     public static boolean isKeyPreviewPopupEnabled(final SharedPreferences prefs,
@@ -383,27 +373,23 @@
     // Accessed from the settings interface, hence public
     public static float getCurrentKeypressSoundVolume(final SharedPreferences prefs,
             final Resources res) {
-        // TODO: use mVibrationDurationSettingsRawValue instead of reading it again here
         final float volume = prefs.getFloat(Settings.PREF_KEYPRESS_SOUND_VOLUME, -1.0f);
         if (volume >= 0) {
             return volume;
         }
-
-        return Float.parseFloat(ResourceUtils.getDeviceOverrideValue(
-                res, R.array.keypress_volumes, "-1.0f"));
+        return Float.parseFloat(
+                ResourceUtils.getDeviceOverrideValue(res, R.array.keypress_volumes));
     }
 
     // Likewise
     public static int getCurrentVibrationDuration(final SharedPreferences prefs,
             final Resources res) {
-        // TODO: use mKeypressVibrationDuration instead of reading it again here
         final int ms = prefs.getInt(Settings.PREF_VIBRATION_DURATION_SETTINGS, -1);
         if (ms >= 0) {
             return ms;
         }
-
-        return Integer.parseInt(ResourceUtils.getDeviceOverrideValue(
-                res, R.array.keypress_vibration_durations, "-1"));
+        return Integer.parseInt(
+                ResourceUtils.getDeviceOverrideValue(res, R.array.keypress_vibration_durations));
     }
 
     // Likewise
@@ -434,9 +420,4 @@
     public boolean isSameInputType(final EditorInfo editorInfo) {
         return mInputAttributes.isSameInputType(editorInfo);
     }
-
-    // For debug.
-    public String getInputAttributesDebugString() {
-        return mInputAttributes.toString();
-    }
 }
diff --git a/java/src/com/android/inputmethod/latin/StringUtils.java b/java/src/com/android/inputmethod/latin/StringUtils.java
index 043043c..ddaa5ff 100644
--- a/java/src/com/android/inputmethod/latin/StringUtils.java
+++ b/java/src/com/android/inputmethod/latin/StringUtils.java
@@ -62,6 +62,23 @@
     }
 
     /**
+     * Find a string that start with specified prefix from an array.
+     *
+     * @param prefix a prefix string to find.
+     * @param array an string array to be searched.
+     * @return the rest part of the string that starts with the prefix.
+     * Returns null if it couldn't be found.
+     */
+    public static String findPrefixedString(final String prefix, final String[] array) {
+        for (final String element : array) {
+            if (element.startsWith(prefix)) {
+                return element.substring(prefix.length());
+            }
+        }
+        return null;
+    }
+
+    /**
      * Remove duplicates from an array of strings.
      *
      * This method will always keep the first occurrence of all strings at their position
diff --git a/java/src/com/android/inputmethod/latin/SubtypeLocale.java b/java/src/com/android/inputmethod/latin/SubtypeLocale.java
index 5d8c0b1..370a659 100644
--- a/java/src/com/android/inputmethod/latin/SubtypeLocale.java
+++ b/java/src/com/android/inputmethod/latin/SubtypeLocale.java
@@ -130,7 +130,8 @@
     }
 
     public static int getSubtypeNameId(String localeString, String keyboardLayoutName) {
-        if (Build.VERSION.SDK_INT >= /* JELLY_BEAN */ 15 && isExceptionalLocale(localeString)) {
+        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN
+                && isExceptionalLocale(localeString)) {
             return sExceptionalLocaleToWithLayoutNameIdsMap.get(localeString);
         }
         final String key = localeString.equals(NO_LANGUAGE)
@@ -166,8 +167,9 @@
     //  zz    azerty  T  No language (AZERTY)    in system locale
 
     public static String getSubtypeDisplayName(final InputMethodSubtype subtype, Resources res) {
-        final String replacementString = (Build.VERSION.SDK_INT >= /* JELLY_BEAN */ 15
-                && subtype.containsExtraValueKey(UNTRANSLATABLE_STRING_IN_SUBTYPE_NAME))
+        final String replacementString =
+                (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN
+                        && subtype.containsExtraValueKey(UNTRANSLATABLE_STRING_IN_SUBTYPE_NAME))
                 ? subtype.getExtraValueOf(UNTRANSLATABLE_STRING_IN_SUBTYPE_NAME)
                 : getSubtypeLocaleDisplayName(subtype.getLocale());
         final int nameResId = subtype.getNameResId();
diff --git a/java/src/com/android/inputmethod/latin/SubtypeSwitcher.java b/java/src/com/android/inputmethod/latin/SubtypeSwitcher.java
index 8f2e275..fe29084 100644
--- a/java/src/com/android/inputmethod/latin/SubtypeSwitcher.java
+++ b/java/src/com/android/inputmethod/latin/SubtypeSwitcher.java
@@ -20,7 +20,6 @@
 
 import android.content.Context;
 import android.content.Intent;
-import android.content.res.Configuration;
 import android.content.res.Resources;
 import android.inputmethodservice.InputMethodService;
 import android.net.ConnectivityManager;
@@ -53,9 +52,6 @@
     private InputMethodInfo mShortcutInputMethodInfo;
     private InputMethodSubtype mShortcutSubtype;
     private InputMethodSubtype mNoLanguageSubtype;
-    // Note: This variable is always non-null after {@link #initialize(LatinIME)}.
-    private InputMethodSubtype mCurrentSubtype;
-    private Locale mCurrentSystemLocale;
     /*-----------------------------------------------------------*/
 
     private boolean mIsNetworkConnected;
@@ -84,7 +80,6 @@
     public static void init(final Context context) {
         SubtypeLocale.init(context);
         sInstance.initialize(context);
-        sInstance.updateAllParameters();
     }
 
     private SubtypeSwitcher() {
@@ -96,60 +91,28 @@
         mRichImm = RichInputMethodManager.getInstance();
         mConnectivityManager = (ConnectivityManager) service.getSystemService(
                 Context.CONNECTIVITY_SERVICE);
-        mCurrentSystemLocale = mResources.getConfiguration().locale;
         mNoLanguageSubtype = mRichImm.findSubtypeByLocaleAndKeyboardLayoutSet(
                 SubtypeLocale.NO_LANGUAGE, SubtypeLocale.QWERTY);
-        mCurrentSubtype = mRichImm.getCurrentInputMethodSubtype(mNoLanguageSubtype);
         if (mNoLanguageSubtype == null) {
             throw new RuntimeException("Can't find no lanugage with QWERTY subtype");
         }
 
         final NetworkInfo info = mConnectivityManager.getActiveNetworkInfo();
         mIsNetworkConnected = (info != null && info.isConnected());
-    }
 
-    // Update all parameters stored in SubtypeSwitcher.
-    // Only configuration changed event is allowed to call this because this is heavy.
-    private void updateAllParameters() {
-        mCurrentSystemLocale = mResources.getConfiguration().locale;
-        updateSubtype(mRichImm.getCurrentInputMethodSubtype(mNoLanguageSubtype));
-        updateParametersOnStartInputViewAndReturnIfCurrentSubtypeEnabled();
+        onSubtypeChanged(getCurrentSubtype());
+        updateParametersOnStartInputView();
     }
 
     /**
-     * Update parameters which are changed outside LatinIME. This parameters affect UI so they
-     * should be updated every time onStartInputView.
-     *
-     * @return true if the current subtype is enabled.
+     * Update parameters which are changed outside LatinIME. This parameters affect UI so that they
+     * should be updated every time onStartInputView is called.
      */
-    public boolean updateParametersOnStartInputViewAndReturnIfCurrentSubtypeEnabled() {
-        final boolean currentSubtypeEnabled =
-                updateEnabledSubtypesAndReturnIfEnabled(mCurrentSubtype);
-        updateShortcutIME();
-        return currentSubtypeEnabled;
-    }
-
-    /**
-     * Update enabled subtypes from the framework.
-     *
-     * @param subtype the subtype to be checked
-     * @return true if the {@code subtype} is enabled.
-     */
-    private boolean updateEnabledSubtypesAndReturnIfEnabled(final InputMethodSubtype subtype) {
+    public void updateParametersOnStartInputView() {
         final List<InputMethodSubtype> enabledSubtypesOfThisIme =
                 mRichImm.getInputMethodManager().getEnabledInputMethodSubtypeList(null, true);
         mNeedsToDisplayLanguage.updateEnabledSubtypeCount(enabledSubtypesOfThisIme.size());
-
-        for (final InputMethodSubtype ims : enabledSubtypesOfThisIme) {
-            if (ims.equals(subtype)) {
-                return true;
-            }
-        }
-        if (DBG) {
-            Log.w(TAG, "Subtype: " + subtype.getLocale() + "/" + subtype.getExtraValue()
-                    + " was disabled");
-        }
-        return false;
+        updateShortcutIME();
     }
 
     private void updateShortcutIME() {
@@ -185,25 +148,21 @@
     }
 
     // Update the current subtype. LatinIME.onCurrentInputMethodSubtypeChanged calls this function.
-    public void updateSubtype(InputMethodSubtype newSubtype) {
+    public void onSubtypeChanged(final InputMethodSubtype newSubtype) {
         if (DBG) {
-            Log.w(TAG, "onCurrentInputMethodSubtypeChanged: to: "
-                    + newSubtype.getLocale() + "/" + newSubtype.getExtraValue() + ", from: "
-                    + mCurrentSubtype.getLocale() + "/" + mCurrentSubtype.getExtraValue());
+            Log.w(TAG, "onSubtypeChanged: " + SubtypeLocale.getSubtypeDisplayName(
+                    newSubtype, mResources));
         }
 
         final Locale newLocale = SubtypeLocale.getSubtypeLocale(newSubtype);
-        final boolean sameLocale = mCurrentSystemLocale.equals(newLocale);
-        final boolean sameLanguage = mCurrentSystemLocale.getLanguage().equals(
-                newLocale.getLanguage());
+        final Locale systemLocale = mResources.getConfiguration().locale;
+        final boolean sameLocale = systemLocale.equals(newLocale);
+        final boolean sameLanguage = systemLocale.getLanguage().equals(newLocale.getLanguage());
         final boolean implicitlyEnabled =
                 mRichImm.checkIfSubtypeBelongsToThisImeAndImplicitlyEnabled(newSubtype);
         mNeedsToDisplayLanguage.updateIsSystemLanguageSameAsInputLanguage(
                 sameLocale || (sameLanguage && implicitlyEnabled));
 
-        if (newSubtype.equals(mCurrentSubtype)) return;
-
-        mCurrentSubtype = newSubtype;
         updateShortcutIME();
     }
 
@@ -281,21 +240,11 @@
     }
 
     public Locale getCurrentSubtypeLocale() {
-        return SubtypeLocale.getSubtypeLocale(mCurrentSubtype);
-    }
-
-    public boolean onConfigurationChanged(final Configuration conf) {
-        final Locale systemLocale = conf.locale;
-        final boolean systemLocaleChanged = !systemLocale.equals(mCurrentSystemLocale);
-        // If system configuration was changed, update all parameters.
-        if (systemLocaleChanged) {
-            updateAllParameters();
-        }
-        return systemLocaleChanged;
+        return SubtypeLocale.getSubtypeLocale(getCurrentSubtype());
     }
 
     public InputMethodSubtype getCurrentSubtype() {
-        return mCurrentSubtype;
+        return mRichImm.getCurrentInputMethodSubtype(mNoLanguageSubtype);
     }
 
     public InputMethodSubtype getNoLanguageSubtype() {
diff --git a/java/src/com/android/inputmethod/latin/UserBinaryDictionary.java b/java/src/com/android/inputmethod/latin/UserBinaryDictionary.java
index ddae5ac..a167849 100644
--- a/java/src/com/android/inputmethod/latin/UserBinaryDictionary.java
+++ b/java/src/com/android/inputmethod/latin/UserBinaryDictionary.java
@@ -24,27 +24,28 @@
 import android.database.ContentObserver;
 import android.database.Cursor;
 import android.net.Uri;
+import android.os.Build;
 import android.provider.UserDictionary.Words;
 import android.text.TextUtils;
 
 import java.util.Arrays;
 
 /**
- * An expandable dictionary that stores the words in the user unigram dictionary.
- *
- * Largely a copy of UserDictionary, will replace that class in the future.
+ * An expandable dictionary that stores the words in the user dictionary provider into a binary
+ * dictionary file to use it from native code.
  */
 public class UserBinaryDictionary extends ExpandableBinaryDictionary {
 
     // The user dictionary provider uses an empty string to mean "all languages".
     private static final String USER_DICTIONARY_ALL_LANGUAGES = "";
+    private static final int HISTORICAL_DEFAULT_USER_DICTIONARY_FREQUENCY = 250;
+    private static final int LATINIME_DEFAULT_USER_DICTIONARY_FREQUENCY = 160;
 
     // TODO: use Words.SHORTCUT when we target JellyBean or above
     final static String SHORTCUT = "shortcut";
     private static final String[] PROJECTION_QUERY;
     static {
-        // 16 is JellyBean, but we want this to compile against ICS.
-        if (android.os.Build.VERSION.SDK_INT >= 16) {
+        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {
             PROJECTION_QUERY = new String[] {
                 Words.WORD,
                 SHORTCUT,
@@ -90,13 +91,14 @@
         mObserver = new ContentObserver(null) {
             @Override
             public void onChange(final boolean self) {
-                // This hook is deprecated as of API level 16, but should still be supported for
-                // cases where the IME is running on an older version of the platform.
+                // This hook is deprecated as of API level 16 (Build.VERSION_CODES.JELLY_BEAN),
+                // but should still be supported for cases where the IME is running on an older
+                // version of the platform.
                 onChange(self, null);
             }
-            // The following hook is only available as of API level 16, and as such it will only
-            // work on JellyBean+ devices. On older versions of the platform, the hook
-            // above will be called instead.
+            // The following hook is only available as of API level 16
+            // (Build.VERSION_CODES.JELLY_BEAN), and as such it will only work on JellyBean+
+            // devices. On older versions of the platform, the hook above will be called instead.
             @Override
             public void onChange(final boolean self, final Uri uri) {
                 setRequiresReload(true);
@@ -232,9 +234,21 @@
         mContext.startActivity(intent);
     }
 
+    private int scaleFrequencyFromDefaultToLatinIme(final int defaultFrequency) {
+        // The default frequency for the user dictionary is 250 for historical reasons.
+        // Latin IME considers a good value for the default user dictionary frequency
+        // is about 160 considering the scale we use. So we are scaling down the values.
+        if (defaultFrequency > Integer.MAX_VALUE / LATINIME_DEFAULT_USER_DICTIONARY_FREQUENCY) {
+            return (defaultFrequency / HISTORICAL_DEFAULT_USER_DICTIONARY_FREQUENCY)
+                    * LATINIME_DEFAULT_USER_DICTIONARY_FREQUENCY;
+        } else {
+            return (defaultFrequency * LATINIME_DEFAULT_USER_DICTIONARY_FREQUENCY)
+                    / HISTORICAL_DEFAULT_USER_DICTIONARY_FREQUENCY;
+        }
+    }
+
     private void addWords(final Cursor cursor) {
-        // 16 is JellyBean, but we want this to compile against ICS.
-        final boolean hasShortcutColumn = android.os.Build.VERSION.SDK_INT >= 16;
+        final boolean hasShortcutColumn = Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN;
         clearFusionDictionary();
         if (cursor == null) return;
         if (cursor.moveToFirst()) {
@@ -245,12 +259,13 @@
                 final String word = cursor.getString(indexWord);
                 final String shortcut = hasShortcutColumn ? cursor.getString(indexShortcut) : null;
                 final int frequency = cursor.getInt(indexFrequency);
+                final int adjustedFrequency = scaleFrequencyFromDefaultToLatinIme(frequency);
                 // Safeguard against adding really long words.
                 if (word.length() < MAX_WORD_LENGTH) {
-                    super.addWord(word, null, frequency);
+                    super.addWord(word, null, adjustedFrequency);
                 }
                 if (null != shortcut && shortcut.length() < MAX_WORD_LENGTH) {
-                    super.addWord(shortcut, word, frequency);
+                    super.addWord(shortcut, word, adjustedFrequency);
                 }
                 cursor.moveToNext();
             }
diff --git a/java/src/com/android/inputmethod/latin/UserHistoryDictIOUtils.java b/java/src/com/android/inputmethod/latin/UserHistoryDictIOUtils.java
index 100e377..4fa3d7d 100644
--- a/java/src/com/android/inputmethod/latin/UserHistoryDictIOUtils.java
+++ b/java/src/com/android/inputmethod/latin/UserHistoryDictIOUtils.java
@@ -48,6 +48,7 @@
         public void setBigram(final String word1, final String word2, final int frequency);
     }
 
+    @UsedForTesting
     public interface BigramDictionaryInterface {
         public int getFrequency(final String word1, final String word2);
     }
@@ -214,4 +215,4 @@
         }
 
     }
-}
\ No newline at end of file
+}
diff --git a/java/src/com/android/inputmethod/latin/UserHistoryDictionaryBigramList.java b/java/src/com/android/inputmethod/latin/UserHistoryDictionaryBigramList.java
index df44948..316f096 100644
--- a/java/src/com/android/inputmethod/latin/UserHistoryDictionaryBigramList.java
+++ b/java/src/com/android/inputmethod/latin/UserHistoryDictionaryBigramList.java
@@ -18,6 +18,8 @@
 
 import android.util.Log;
 
+import com.android.inputmethod.annotations.UsedForTesting;
+
 import java.util.HashMap;
 import java.util.Set;
 
@@ -26,6 +28,7 @@
  * All bigrams including stale ones in SQL DB should be stored in this class to avoid adding stale
  * bigrams when we write to the SQL DB.
  */
+@UsedForTesting
 public final class UserHistoryDictionaryBigramList {
     public static final byte FORGETTING_CURVE_INITIAL_VALUE = 0;
     private static final String TAG = UserHistoryDictionaryBigramList.class.getSimpleName();
diff --git a/java/src/com/android/inputmethod/latin/Utils.java b/java/src/com/android/inputmethod/latin/Utils.java
index 3eac6a2..acfcd53 100644
--- a/java/src/com/android/inputmethod/latin/Utils.java
+++ b/java/src/com/android/inputmethod/latin/Utils.java
@@ -61,7 +61,7 @@
         }
     }
 
-    /* package */ static class RingCharBuffer {
+    /* package */ static final class RingCharBuffer {
         private static RingCharBuffer sRingCharBuffer = new RingCharBuffer();
         private static final char PLACEHOLDER_DELIMITER_CHAR = '\uFFFC';
         private static final int INVALID_COORDINATE = -2;
@@ -203,7 +203,7 @@
         }
 
         // Initialization-on-demand holder
-        private static class OnDemandInitializationHolder {
+        private static final class OnDemandInitializationHolder {
             public static final UsabilityStudyLogUtils sInstance = new UsabilityStudyLogUtils();
         }
 
diff --git a/java/src/com/android/inputmethod/latin/VibratorUtils.java b/java/src/com/android/inputmethod/latin/VibratorUtils.java
deleted file mode 100644
index b6696ce..0000000
--- a/java/src/com/android/inputmethod/latin/VibratorUtils.java
+++ /dev/null
@@ -1,50 +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;
-
-import android.content.Context;
-import android.os.Vibrator;
-
-public final class VibratorUtils {
-    private static final VibratorUtils sInstance = new VibratorUtils();
-    private Vibrator mVibrator;
-
-    private VibratorUtils() {
-        // This utility class is not publicly instantiable.
-    }
-
-    public static VibratorUtils getInstance(Context context) {
-        if (sInstance.mVibrator == null) {
-            sInstance.mVibrator = (Vibrator) context.getSystemService(Context.VIBRATOR_SERVICE);
-        }
-        return sInstance;
-    }
-
-    public boolean hasVibrator() {
-        if (mVibrator == null) {
-            return false;
-        }
-        return mVibrator.hasVibrator();
-    }
-
-    public void vibrate(long milliseconds) {
-        if (mVibrator == null) {
-            return;
-        }
-        mVibrator.vibrate(milliseconds);
-    }
-}
diff --git a/java/src/com/android/inputmethod/latin/makedict/BinaryDictInputOutput.java b/java/src/com/android/inputmethod/latin/makedict/BinaryDictInputOutput.java
index f1a7e97..937d7ab 100644
--- a/java/src/com/android/inputmethod/latin/makedict/BinaryDictInputOutput.java
+++ b/java/src/com/android/inputmethod/latin/makedict/BinaryDictInputOutput.java
@@ -56,6 +56,7 @@
     private static final int MAX_PASSES = 24;
     private static final int MAX_JUMPS = 12;
 
+    @UsedForTesting
     public interface FusionDictionaryBufferInterface {
         public int readUnsignedByte();
         public int readUnsignedShort();
diff --git a/java/src/com/android/inputmethod/latin/makedict/FusionDictionary.java b/java/src/com/android/inputmethod/latin/makedict/FusionDictionary.java
index f7cc693..bfc275d 100644
--- a/java/src/com/android/inputmethod/latin/makedict/FusionDictionary.java
+++ b/java/src/com/android/inputmethod/latin/makedict/FusionDictionary.java
@@ -16,6 +16,7 @@
 
 package com.android.inputmethod.latin.makedict;
 
+import com.android.inputmethod.annotations.UsedForTesting;
 import com.android.inputmethod.latin.Constants;
 
 import java.util.ArrayList;
@@ -29,6 +30,7 @@
 /**
  * A dictionary that can fusion heads and tails of words for more compression.
  */
+@UsedForTesting
 public final class FusionDictionary implements Iterable<Word> {
     private static final boolean DBG = MakedictLog.DBG;
 
diff --git a/java/src/com/android/inputmethod/research/ResearchLogger.java b/java/src/com/android/inputmethod/research/ResearchLogger.java
index f5465d7..2fc18ea 100644
--- a/java/src/com/android/inputmethod/research/ResearchLogger.java
+++ b/java/src/com/android/inputmethod/research/ResearchLogger.java
@@ -38,6 +38,7 @@
 import android.os.Build;
 import android.os.IBinder;
 import android.os.SystemClock;
+import android.preference.PreferenceManager;
 import android.text.TextUtils;
 import android.text.format.DateUtils;
 import android.util.Log;
@@ -161,7 +162,7 @@
         return sInstance;
     }
 
-    public void init(final LatinIME latinIME, final SharedPreferences prefs) {
+    public void init(final LatinIME latinIME) {
         assert latinIME != null;
         if (latinIME == null) {
             Log.w(TAG, "IMS is null; logging is off");
@@ -171,6 +172,7 @@
                 Log.w(TAG, "IME storage directory does not exist.");
             }
         }
+        final SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(latinIME);
         if (prefs != null) {
             mUUIDString = getUUID(prefs);
             if (!prefs.contains(PREF_USABILITY_STUDY_MODE)) {
@@ -741,7 +743,7 @@
 
     private static final LogStatement LOGSTATEMENT_COMMIT_RECORD_SPLIT_WORDS =
             new LogStatement("recordSplitWords", true, false);
-    private void onWordComplete(final String word, final long maxTime, final boolean isSplitWords) {
+    public void onWordComplete(final String word, final long maxTime) {
         final Dictionary dictionary = getDictionary();
         if (word != null && word.length() > 0 && hasLetters(word)) {
             mCurrentLogUnit.setWord(word);
@@ -751,11 +753,6 @@
         }
         final LogUnit newLogUnit = mCurrentLogUnit.splitByTime(maxTime);
         enqueueCommitText(word);
-        if (isSplitWords) {
-            enqueueEvent(LOGSTATEMENT_COMMIT_RECORD_SPLIT_WORDS);
-            enqueueCommitText(" ");
-            mStatistics.recordSplitWords();
-        }
         commitCurrentLogUnit();
         mCurrentLogUnit = newLogUnit;
     }
@@ -1049,12 +1046,15 @@
             new LogStatement("LatinIMEPickSuggestionManually", true, false, "replacedWord", "index",
                     "suggestion", "x", "y");
     public static void latinIME_pickSuggestionManually(final String replacedWord,
-            final int index, CharSequence suggestion) {
+            final int index, final String suggestion) {
+        final String scrubbedWord = scrubDigitsFromString(suggestion);
         final ResearchLogger researchLogger = getInstance();
         researchLogger.enqueueEvent(LOGSTATEMENT_LATINIME_PICKSUGGESTIONMANUALLY,
                 scrubDigitsFromString(replacedWord), index,
-                suggestion == null ? null : scrubDigitsFromString(suggestion.toString()),
-                Constants.SUGGESTION_STRIP_COORDINATE, Constants.SUGGESTION_STRIP_COORDINATE);
+                suggestion == null ? null : scrubbedWord, Constants.SUGGESTION_STRIP_COORDINATE,
+                Constants.SUGGESTION_STRIP_COORDINATE);
+        researchLogger.onWordComplete(scrubbedWord, Long.MAX_VALUE);
+        researchLogger.mStatistics.recordManualSuggestion();
     }
 
     /**
@@ -1065,10 +1065,11 @@
     private static final LogStatement LOGSTATEMENT_LATINIME_PUNCTUATIONSUGGESTION =
             new LogStatement("LatinIMEPunctuationSuggestion", false, false, "index", "suggestion",
                     "x", "y");
-    public static void latinIME_punctuationSuggestion(final int index,
-            final CharSequence suggestion) {
-        getInstance().enqueueEvent(LOGSTATEMENT_LATINIME_PUNCTUATIONSUGGESTION, index, suggestion,
+    public static void latinIME_punctuationSuggestion(final int index, final String suggestion) {
+        final ResearchLogger researchLogger = getInstance();
+        researchLogger.enqueueEvent(LOGSTATEMENT_LATINIME_PUNCTUATIONSUGGESTION, index, suggestion,
                 Constants.SUGGESTION_STRIP_COORDINATE, Constants.SUGGESTION_STRIP_COORDINATE);
+        researchLogger.onWordComplete(suggestion, Long.MAX_VALUE);
     }
 
     /**
@@ -1147,9 +1148,14 @@
      * backspace.
      */
     private static final LogStatement LOGSTATEMENT_LATINIME_REVERTCOMMIT =
-            new LogStatement("LatinIMERevertCommit", true, false, "originallyTypedWord");
-    public static void latinIME_revertCommit(final String originallyTypedWord) {
-        getInstance().enqueueEvent(LOGSTATEMENT_LATINIME_REVERTCOMMIT, originallyTypedWord);
+            new LogStatement("LatinIMERevertCommit", true, false, "committedWord",
+                    "originallyTypedWord");
+    public static void latinIME_revertCommit(final String committedWord,
+            final String originallyTypedWord) {
+        final ResearchLogger researchLogger = getInstance();
+        researchLogger.enqueueEvent(LOGSTATEMENT_LATINIME_REVERTCOMMIT, committedWord,
+                originallyTypedWord);
+        researchLogger.mStatistics.recordRevertCommit();
     }
 
     /**
@@ -1259,17 +1265,27 @@
         final ResearchLogger researchLogger = getInstance();
         researchLogger.enqueueEvent(LOGSTATEMENT_LATINIME_COMMITCURRENTAUTOCORRECTION,
                 scrubbedTypedWord, scrubbedAutoCorrection, separatorString);
-        researchLogger.onWordComplete(scrubbedAutoCorrection, Long.MAX_VALUE,
-                false /* isPartial */);
+        researchLogger.onWordComplete(scrubbedAutoCorrection, Long.MAX_VALUE);
     }
 
     private boolean isExpectingCommitText = false;
+    /**
+     * Log a call to RichInputConnection.commitPartialText
+     *
+     * SystemResponse: The IME is committing part of a word.  This happens if a space is
+     * automatically inserted to split a single typed string into two or more words.
+     */
+    // TODO: This method is currently unused.  Find where it should be called from in the IME and
+    // add invocations.
+    private static final LogStatement LOGSTATEMENT_LATINIME_COMMIT_PARTIAL_TEXT =
+            new LogStatement("LatinIMECommitPartialText", true, false, "newCursorPosition");
     public static void latinIME_commitPartialText(final CharSequence committedWord,
             final long lastTimestampOfWordData) {
         final ResearchLogger researchLogger = getInstance();
         final String scrubbedWord = scrubDigitsFromString(committedWord.toString());
-        researchLogger.onWordComplete(scrubbedWord, lastTimestampOfWordData, true /* isPartial */);
-        researchLogger.isExpectingCommitText = true;
+        researchLogger.enqueueEvent(LOGSTATEMENT_LATINIME_COMMIT_PARTIAL_TEXT);
+        researchLogger.onWordComplete(scrubbedWord, lastTimestampOfWordData);
+        researchLogger.mStatistics.recordSplitWords();
     }
 
     /**
@@ -1287,7 +1303,7 @@
         if (!researchLogger.isExpectingCommitText) {
             researchLogger.enqueueEvent(LOGSTATEMENT_RICHINPUTCONNECTIONCOMMITTEXT,
                     newCursorPosition);
-            researchLogger.onWordComplete(scrubbedWord, Long.MAX_VALUE, false /* isPartial */);
+            researchLogger.onWordComplete(scrubbedWord, Long.MAX_VALUE);
         }
         researchLogger.isExpectingCommitText = false;
     }
@@ -1465,7 +1481,8 @@
                     "isEmptinessStateKnown", "averageTimeBetweenKeys", "averageTimeBeforeDelete",
                     "averageTimeDuringRepeatedDelete", "averageTimeAfterDelete",
                     "dictionaryWordCount", "splitWordsCount", "gestureInputCount",
-                    "gestureCharsCount", "gesturesDeletedCount");
+                    "gestureCharsCount", "gesturesDeletedCount", "manualSuggestionsCount",
+                    "revertCommitsCount");
     private static void logStatistics() {
         final ResearchLogger researchLogger = getInstance();
         final Statistics statistics = researchLogger.mStatistics;
@@ -1477,8 +1494,8 @@
                 statistics.mDuringRepeatedDeleteKeysCounter.getAverageTime(),
                 statistics.mAfterDeleteKeyCounter.getAverageTime(),
                 statistics.mDictionaryWordCount, statistics.mSplitWordsCount,
-                statistics.mGesturesInputCount,
-                statistics.mGesturesCharsCount,
-                statistics.mGesturesDeletedCount);
+                statistics.mGesturesInputCount, statistics.mGesturesCharsCount,
+                statistics.mGesturesDeletedCount, statistics.mManualSuggestionsCount,
+                statistics.mRevertCommitsCount);
     }
 }
diff --git a/java/src/com/android/inputmethod/research/Statistics.java b/java/src/com/android/inputmethod/research/Statistics.java
index a02aabf..e9c02c9 100644
--- a/java/src/com/android/inputmethod/research/Statistics.java
+++ b/java/src/com/android/inputmethod/research/Statistics.java
@@ -47,6 +47,10 @@
     int mGesturesDeletedCount;
     // Total number of characters in words entered by gesture.
     int mGesturesCharsCount;
+    // Number of manual suggestions chosen.
+    int mManualSuggestionsCount;
+    // Number of times a commit was reverted in this session.
+    int mRevertCommitsCount;
     // Whether the text field was empty upon editing
     boolean mIsEmptyUponStarting;
     boolean mIsEmptinessStateKnown;
@@ -111,6 +115,8 @@
         mSplitWordsCount = 0;
         mGesturesInputCount = 0;
         mGesturesDeletedCount = 0;
+        mManualSuggestionsCount = 0;
+        mRevertCommitsCount = 0;
         mIsEmptyUponStarting = true;
         mIsEmptinessStateKnown = false;
         mKeyCounter.reset();
@@ -184,4 +190,11 @@
     public void recordGestureDelete() {
         mGesturesDeletedCount++;
     }
+    public void recordManualSuggestion() {
+        mManualSuggestionsCount++;
+    }
+
+    public void recordRevertCommit() {
+        mRevertCommitsCount++;
+    }
 }
diff --git a/native/jni/Android.mk b/native/jni/Android.mk
index a8a8871..c99f291 100644
--- a/native/jni/Android.mk
+++ b/native/jni/Android.mk
@@ -102,6 +102,7 @@
 
 LOCAL_SDK_VERSION := 14
 LOCAL_NDK_STL_VARIANT := stlport_static
+LOCAL_LDFLAGS += -ldl
 
 include $(BUILD_SHARED_LIBRARY)
 
diff --git a/native/jni/com_android_inputmethod_keyboard_ProximityInfo.cpp b/native/jni/com_android_inputmethod_keyboard_ProximityInfo.cpp
index 2423bb5..4c83c58 100644
--- a/native/jni/com_android_inputmethod_keyboard_ProximityInfo.cpp
+++ b/native/jni/com_android_inputmethod_keyboard_ProximityInfo.cpp
@@ -1,11 +1,11 @@
 /*
- * Copyright (C) 2011, The Android Open Source Project
+ * 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
+ *      http://www.apache.org/licenses/LICENSE-2.0
  *
  * Unless required by applicable law or agreed to in writing, software
  * distributed under the License is distributed on an "AS IS" BASIS,
diff --git a/native/jni/com_android_inputmethod_keyboard_ProximityInfo.h b/native/jni/com_android_inputmethod_keyboard_ProximityInfo.h
index 51fa895..c3503c8 100644
--- a/native/jni/com_android_inputmethod_keyboard_ProximityInfo.h
+++ b/native/jni/com_android_inputmethod_keyboard_ProximityInfo.h
@@ -1,11 +1,11 @@
 /*
- * Copyright (C) 2011, The Android Open Source Project
+ * 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
+ *      http://www.apache.org/licenses/LICENSE-2.0
  *
  * Unless required by applicable law or agreed to in writing, software
  * distributed under the License is distributed on an "AS IS" BASIS,
@@ -20,8 +20,6 @@
 #include "jni.h"
 
 namespace latinime {
-
 int register_ProximityInfo(JNIEnv *env);
-
 } // namespace latinime
 #endif // _COM_ANDROID_INPUTMETHOD_KEYBOARD_PROXIMITYINFO_H
diff --git a/native/jni/com_android_inputmethod_latin_BinaryDictionary.cpp b/native/jni/com_android_inputmethod_latin_BinaryDictionary.cpp
index c0b858e..4e34f98 100644
--- a/native/jni/com_android_inputmethod_latin_BinaryDictionary.cpp
+++ b/native/jni/com_android_inputmethod_latin_BinaryDictionary.cpp
@@ -1,11 +1,11 @@
 /*
- * Copyright (C) 2009, The Android Open Source Project
+ * 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
+ *      http://www.apache.org/licenses/LICENSE-2.0
  *
  * Unless required by applicable law or agreed to in writing, software
  * distributed under the License is distributed on an "AS IS" BASIS,
@@ -14,7 +14,6 @@
  * limitations under the License.
  */
 
-
 #include <cstring> // for memset()
 
 #define LOG_TAG "LatinIME: jni: BinaryDictionary"
diff --git a/native/jni/com_android_inputmethod_latin_BinaryDictionary.h b/native/jni/com_android_inputmethod_latin_BinaryDictionary.h
index b9e944f..2a07f99 100644
--- a/native/jni/com_android_inputmethod_latin_BinaryDictionary.h
+++ b/native/jni/com_android_inputmethod_latin_BinaryDictionary.h
@@ -1,11 +1,11 @@
 /*
- * Copyright (C) 2011, The Android Open Source Project
+ * 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
+ *      http://www.apache.org/licenses/LICENSE-2.0
  *
  * Unless required by applicable law or agreed to in writing, software
  * distributed under the License is distributed on an "AS IS" BASIS,
@@ -20,8 +20,6 @@
 #include "jni.h"
 
 namespace latinime {
-
 int register_BinaryDictionary(JNIEnv *env);
-
 } // namespace latinime
 #endif // _COM_ANDROID_INPUTMETHOD_LATIN_BINARYDICTIONARY_H
diff --git a/native/jni/com_android_inputmethod_latin_DicTraverseSession.cpp b/native/jni/com_android_inputmethod_latin_DicTraverseSession.cpp
index 7bb8dc5..73ac84c 100644
--- a/native/jni/com_android_inputmethod_latin_DicTraverseSession.cpp
+++ b/native/jni/com_android_inputmethod_latin_DicTraverseSession.cpp
@@ -1,11 +1,11 @@
 /*
- * Copyright (C) 2012, The Android Open Source Project
+ * 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
+ *      http://www.apache.org/licenses/LICENSE-2.0
  *
  * Unless required by applicable law or agreed to in writing, software
  * distributed under the License is distributed on an "AS IS" BASIS,
diff --git a/native/jni/com_android_inputmethod_latin_DicTraverseSession.h b/native/jni/com_android_inputmethod_latin_DicTraverseSession.h
index 37531e9..badcbb9 100644
--- a/native/jni/com_android_inputmethod_latin_DicTraverseSession.h
+++ b/native/jni/com_android_inputmethod_latin_DicTraverseSession.h
@@ -1,11 +1,11 @@
 /*
- * Copyright (C) 2012, The Android Open Source Project
+ * 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
+ *      http://www.apache.org/licenses/LICENSE-2.0
  *
  * Unless required by applicable law or agreed to in writing, software
  * distributed under the License is distributed on an "AS IS" BASIS,
@@ -17,7 +17,6 @@
 #ifndef _COM_ANDROID_INPUTMETHOD_LATIN_DICTRAVERSESESSION_H
 #define _COM_ANDROID_INPUTMETHOD_LATIN_DICTRAVERSESESSION_H
 
-#include "defines.h"
 #include "jni.h"
 
 namespace latinime {
diff --git a/native/jni/jni_common.cpp b/native/jni/jni_common.cpp
index 0da1669..7b97cf4 100644
--- a/native/jni/jni_common.cpp
+++ b/native/jni/jni_common.cpp
@@ -1,11 +1,11 @@
 /*
- * Copyright (C) 2011, The Android Open Source Project
+ * 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
+ *      http://www.apache.org/licenses/LICENSE-2.0
  *
  * Unless required by applicable law or agreed to in writing, software
  * distributed under the License is distributed on an "AS IS" BASIS,
@@ -25,45 +25,38 @@
 #include "jni.h"
 #include "jni_common.h"
 
-using namespace latinime;
-
 /*
  * Returns the JNI version on success, -1 on failure.
  */
 jint JNI_OnLoad(JavaVM *vm, void *reserved) {
     JNIEnv *env = 0;
-    jint result = -1;
 
     if (vm->GetEnv(reinterpret_cast<void **>(&env), JNI_VERSION_1_6) != JNI_OK) {
         AKLOGE("ERROR: GetEnv failed");
-        goto bail;
+        return -1;
     }
     assert(env);
-
-    if (!register_BinaryDictionary(env)) {
+    if (!env) {
+        AKLOGE("ERROR: JNIEnv is invalid");
+        return -1;
+    }
+    if (!latinime::register_BinaryDictionary(env)) {
         AKLOGE("ERROR: BinaryDictionary native registration failed");
-        goto bail;
+        return -1;
     }
-
-    if (!register_DicTraverseSession(env)) {
+    if (!latinime::register_DicTraverseSession(env)) {
         AKLOGE("ERROR: DicTraverseSession native registration failed");
-        goto bail;
+        return -1;
     }
-
-    if (!register_ProximityInfo(env)) {
+    if (!latinime::register_ProximityInfo(env)) {
         AKLOGE("ERROR: ProximityInfo native registration failed");
-        goto bail;
+        return -1;
     }
-
     /* success -- return valid version number */
-    result = JNI_VERSION_1_6;
-
-bail:
-    return result;
+    return JNI_VERSION_1_6;
 }
 
 namespace latinime {
-
 int registerNativeMethods(JNIEnv *env, const char *className, JNINativeMethod *methods,
         int numMethods) {
     jclass clazz = env->FindClass(className);
diff --git a/native/jni/jni_common.h b/native/jni/jni_common.h
index 993f97e..f960b05 100644
--- a/native/jni/jni_common.h
+++ b/native/jni/jni_common.h
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2011, The Android Open Source Project
+ * 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.
@@ -20,9 +20,7 @@
 #include "jni.h"
 
 namespace latinime {
-
 int registerNativeMethods(JNIEnv *env, const char *className, JNINativeMethod *methods,
         int numMethods);
-
 } // namespace latinime
 #endif // LATINIME_JNI_COMMON_H
diff --git a/native/jni/src/correction.cpp b/native/jni/src/correction.cpp
index 46ca911..24221c9 100644
--- a/native/jni/src/correction.cpp
+++ b/native/jni/src/correction.cpp
@@ -71,8 +71,7 @@
     mTotalTraverseCount = 0;
 }
 
-void Correction::initCorrection(const ProximityInfo *pi, const int inputSize,
-        const int maxDepth) {
+void Correction::initCorrection(const ProximityInfo *pi, const int inputSize, const int maxDepth) {
     mProximityInfo = pi;
     mInputSize = inputSize;
     mMaxDepth = maxDepth;
@@ -168,8 +167,7 @@
     return true;
 }
 
-int Correction::goDownTree(
-        const int parentIndex, const int childCount, const int firstChildPos) {
+int Correction::goDownTree(const int parentIndex, const int childCount, const int firstChildPos) {
     mCorrectionStates[mOutputIndex].mParentIndex = parentIndex;
     mCorrectionStates[mOutputIndex].mChildCount = childCount;
     mCorrectionStates[mOutputIndex].mSiblingPos = firstChildPos;
@@ -532,8 +530,7 @@
 // RankingAlgorithm //
 //////////////////////
 
-/* static */
-int Correction::RankingAlgorithm::calculateFinalProbability(const int inputIndex,
+/* static */ int Correction::RankingAlgorithm::calculateFinalProbability(const int inputIndex,
         const int outputIndex, const int freq, int *editDistanceTable, const Correction *correction,
         const int inputSize) {
     const int excessivePos = correction->getExcessivePos();
@@ -794,10 +791,9 @@
     return finalFreq;
 }
 
-/* static */
-int Correction::RankingAlgorithm::calcFreqForSplitMultipleWords(
-        const int *freqArray, const int *wordLengthArray, const int wordCount,
-        const Correction *correction, const bool isSpaceProximity, const int *word) {
+/* static */ int Correction::RankingAlgorithm::calcFreqForSplitMultipleWords(const int *freqArray,
+        const int *wordLengthArray, const int wordCount, const Correction *correction,
+        const bool isSpaceProximity, const int *word) {
     const int typedLetterMultiplier = correction->TYPED_LETTER_MULTIPLIER;
 
     bool firstCapitalizedWordDemotion = false;
@@ -965,8 +961,8 @@
     return dp[li * lo - 1];
 }
 
-int Correction::RankingAlgorithm::editDistance(const int *before, const int beforeLength,
-        const int *after, const int afterLength) {
+/* static */ int Correction::RankingAlgorithm::editDistance(const int *before,
+        const int beforeLength, const int *after, const int afterLength) {
     int table[(beforeLength + 1) * (afterLength + 1)];
     return editDistanceInternal(table, before, beforeLength, after, afterLength);
 }
@@ -993,9 +989,8 @@
 // the result.
 // So, we can normalize original score by dividing powf(2, min(b.l(),a.l())) * 255 * 2.
 
-/* static */
-float Correction::RankingAlgorithm::calcNormalizedScore(const int *before, const int beforeLength,
-        const int *after, const int afterLength, const int score) {
+/* static */ float Correction::RankingAlgorithm::calcNormalizedScore(const int *before,
+        const int beforeLength, const int *after, const int afterLength, const int score) {
     if (0 == beforeLength || 0 == afterLength) {
         return 0.0f;
     }
diff --git a/native/jni/src/correction.h b/native/jni/src/correction.h
index 4184c64..0469355 100644
--- a/native/jni/src/correction.h
+++ b/native/jni/src/correction.h
@@ -58,8 +58,7 @@
     // Non virtual inline destructor -- never inherit this class
     ~Correction() {}
     void resetCorrection();
-    void initCorrection(
-            const ProximityInfo *pi, const int inputSize, const int maxWordLength);
+    void initCorrection(const ProximityInfo *pi, const int inputSize, const int maxWordLength);
     void initCorrectionState(const int rootPos, const int childCount, const bool traverseAll);
 
     // TODO: remove
diff --git a/native/jni/src/defines.h b/native/jni/src/defines.h
index 7b127a2..46595d8 100644
--- a/native/jni/src/defines.h
+++ b/native/jni/src/defines.h
@@ -1,11 +1,11 @@
 /*
- * Copyright (C) 2010, The Android Open Source Project
+ * 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
+ *      http://www.apache.org/licenses/LICENSE-2.0
  *
  * Unless required by applicable law or agreed to in writing, software
  * distributed under the License is distributed on an "AS IS" BASIS,
diff --git a/native/jni/src/dic_traverse_wrapper.h b/native/jni/src/dic_traverse_wrapper.h
index 22cf1b1..9a1db38 100644
--- a/native/jni/src/dic_traverse_wrapper.h
+++ b/native/jni/src/dic_traverse_wrapper.h
@@ -1,11 +1,11 @@
 /*
- * Copyright (C) 2012, The Android Open Source Project
+ * 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
+ *      http://www.apache.org/licenses/LICENSE-2.0
  *
  * Unless required by applicable law or agreed to in writing, software
  * distributed under the License is distributed on an "AS IS" BASIS,
diff --git a/native/jni/src/geometry_utils.h b/native/jni/src/geometry_utils.h
index 38b91cc..4060a7b 100644
--- a/native/jni/src/geometry_utils.h
+++ b/native/jni/src/geometry_utils.h
@@ -54,7 +54,7 @@
 static AK_FORCE_INLINE float getAngle(const int x1, const int y1, const int x2, const int y2) {
     const int dx = x1 - x2;
     const int dy = y1 - y2;
-    if (dx == 0 && dy == 0) return 0;
+    if (dx == 0 && dy == 0) return 0.0f;
     return atan2f(static_cast<float>(dy), static_cast<float>(dx));
 }
 
@@ -96,6 +96,7 @@
 
 // Normal distribution N(u, sigma^2).
 struct NormalDistribution {
+ public:
     NormalDistribution(const float u, const float sigma)
             : mU(u), mSigma(sigma),
               mPreComputedNonExpPart(1.0f / sqrtf(2.0f * M_PI_F * SQUARE_FLOAT(sigma))),
@@ -108,10 +109,10 @@
 
 private:
     DISALLOW_IMPLICIT_CONSTRUCTORS(NormalDistribution);
-    float mU; // mean value
-    float mSigma; // standard deviation
-    float mPreComputedNonExpPart; // = 1 / sqrt(2 * PI * sigma^2)
-    float mPreComputedExponentPart; // = -1 / (2 * sigma^2)
+    const float mU; // mean value
+    const float mSigma; // standard deviation
+    const float mPreComputedNonExpPart; // = 1 / sqrt(2 * PI * sigma^2)
+    const float mPreComputedExponentPart; // = -1 / (2 * sigma^2)
 }; // struct NormalDistribution
 } // namespace latinime
 #endif // LATINIME_GEOMETRY_UTILS_H
diff --git a/native/jni/src/hash_map_compat.h b/native/jni/src/hash_map_compat.h
index 116359a..a1e982b 100644
--- a/native/jni/src/hash_map_compat.h
+++ b/native/jni/src/hash_map_compat.h
@@ -1,11 +1,11 @@
 /*
- * Copyright (C) 2012, The Android Open Source Project
+ * 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
+ *      http://www.apache.org/licenses/LICENSE-2.0
  *
  * Unless required by applicable law or agreed to in writing, software
  * distributed under the License is distributed on an "AS IS" BASIS,
diff --git a/native/jni/src/suggest/suggest_interface.h b/native/jni/src/suggest/suggest_interface.h
index de58e79..0fb5426 100644
--- a/native/jni/src/suggest/suggest_interface.h
+++ b/native/jni/src/suggest/suggest_interface.h
@@ -28,8 +28,8 @@
     virtual int getSuggestions(ProximityInfo *pInfo, void *traverseSession, int *inputXs,
             int *inputYs, int *times, int *pointerIds, int *codes, int inputSize, int commitPoint,
             int *outWords, int *frequencies, int *outputIndices, int *outputTypes) const = 0;
-    SuggestInterface() {};
-    virtual ~SuggestInterface() {};
+    SuggestInterface() {}
+    virtual ~SuggestInterface() {}
  private:
     DISALLOW_COPY_AND_ASSIGN(SuggestInterface);
 };
diff --git a/native/jni/src/unigram_dictionary.cpp b/native/jni/src/unigram_dictionary.cpp
index def4a5b..ebeef13 100644
--- a/native/jni/src/unigram_dictionary.cpp
+++ b/native/jni/src/unigram_dictionary.cpp
@@ -365,7 +365,7 @@
 }
 
 void UnigramDictionary::onTerminal(const int probability,
-        const TerminalAttributes& terminalAttributes, Correction *correction,
+        const TerminalAttributes &terminalAttributes, Correction *correction,
         WordsPriorityQueuePool *queuePool, const bool addToMasterQueue,
         const int currentWordIndex) const {
     const int inputIndex = correction->getInputIndex();
@@ -390,8 +390,7 @@
 
         const int shortcutProbability = finalProbability > 0 ? finalProbability - 1 : 0;
         // Please note that the shortcut candidates will be added to the master queue only.
-        TerminalAttributes::ShortcutIterator iterator =
-                terminalAttributes.getShortcutIterator();
+        TerminalAttributes::ShortcutIterator iterator = terminalAttributes.getShortcutIterator();
         while (iterator.hasNextShortcutTarget()) {
             // TODO: addWord only supports weak ordering, meaning we have no means
             // to control the order of the shortcuts relative to one another or to the word.
diff --git a/native/jni/src/unigram_dictionary.h b/native/jni/src/unigram_dictionary.h
index 3162e46..f5850b4 100644
--- a/native/jni/src/unigram_dictionary.h
+++ b/native/jni/src/unigram_dictionary.h
@@ -59,14 +59,13 @@
             WordsPriorityQueuePool *queuePool) const;
     int getDigraphReplacement(const int *codes, const int i, const int codesSize,
             const digraph_t *const digraphs, const unsigned int digraphsSize) const;
-    void getWordWithDigraphSuggestionsRec(ProximityInfo *proximityInfo,
-        const int *xcoordinates, const int *ycoordinates, const int *codesBuffer,
-        int *xCoordinatesBuffer, int *yCoordinatesBuffer, const int codesBufferSize,
-        const std::map<int, int> *bigramMap, const uint8_t *bigramFilter,
-        const bool useFullEditDistance, const int *codesSrc, const int codesRemain,
-        const int currentDepth, int *codesDest, Correction *correction,
-        WordsPriorityQueuePool *queuePool, const digraph_t *const digraphs,
-        const unsigned int digraphsSize) const;
+    void getWordWithDigraphSuggestionsRec(ProximityInfo *proximityInfo, const int *xcoordinates,
+            const int *ycoordinates, const int *codesBuffer, int *xCoordinatesBuffer,
+            int *yCoordinatesBuffer, const int codesBufferSize, const std::map<int, int> *bigramMap,
+            const uint8_t *bigramFilter, const bool useFullEditDistance, const int *codesSrc,
+            const int codesRemain, const int currentDepth, int *codesDest, Correction *correction,
+            WordsPriorityQueuePool *queuePool, const digraph_t *const digraphs,
+            const unsigned int digraphsSize) const;
     void initSuggestions(ProximityInfo *proximityInfo, const int *xcoordinates,
             const int *ycoordinates, const int *codes, const int codesSize,
             Correction *correction) const;
@@ -79,12 +78,11 @@
             const std::map<int, int> *bigramMap, const uint8_t *bigramFilter,
             Correction *correction, WordsPriorityQueuePool *queuePool, const bool doAutoCompletion,
             const int maxErrors, const int currentWordIndex) const;
-    void getSplitMultipleWordsSuggestions(ProximityInfo *proximityInfo,
-            const int *xcoordinates, const int *ycoordinates, const int *codes,
-            const bool useFullEditDistance, const int inputSize,
-            Correction *correction, WordsPriorityQueuePool *queuePool,
+    void getSplitMultipleWordsSuggestions(ProximityInfo *proximityInfo, const int *xcoordinates,
+            const int *ycoordinates, const int *codes, const bool useFullEditDistance,
+            const int inputSize, Correction *correction, WordsPriorityQueuePool *queuePool,
             const bool hasAutoCorrectionCandidate) const;
-    void onTerminal(const int freq, const TerminalAttributes& terminalAttributes,
+    void onTerminal(const int freq, const TerminalAttributes &terminalAttributes,
             Correction *correction, WordsPriorityQueuePool *queuePool, const bool addToMasterQueue,
             const int currentWordIndex) const;
     // Process a node by considering proximity, missing and excessive character
@@ -96,14 +94,13 @@
             Correction *correction, int *word) const;
     int getMostFrequentWordLikeInner(const int *const inWord, const int inputSize,
             int *outWord) const;
-    int getSubStringSuggestion(
-            ProximityInfo *proximityInfo, const int *xcoordinates, const int *ycoordinates,
-            const int *codes, const bool useFullEditDistance, Correction *correction,
-            WordsPriorityQueuePool *queuePool, const int inputSize,
+    int getSubStringSuggestion(ProximityInfo *proximityInfo, const int *xcoordinates,
+            const int *ycoordinates, const int *codes, const bool useFullEditDistance,
+            Correction *correction, WordsPriorityQueuePool *queuePool, const int inputSize,
             const bool hasAutoCorrectionCandidate, const int currentWordIndex,
-            const int inputWordStartPos, const int inputWordLength,
-            const int outputWordStartPos, const bool isSpaceProximity, int *freqArray,
-            int *wordLengthArray, int *outputWord, int *outputWordLength) const;
+            const int inputWordStartPos, const int inputWordLength, const int outputWordStartPos,
+            const bool isSpaceProximity, int *freqArray, int *wordLengthArray, int *outputWord,
+            int *outputWordLength) const;
     void getMultiWordsSuggestionRec(ProximityInfo *proximityInfo, const int *xcoordinates,
             const int *ycoordinates, const int *codes, const bool useFullEditDistance,
             const int inputSize, Correction *correction, WordsPriorityQueuePool *queuePool,
diff --git a/native/jni/src/words_priority_queue.h b/native/jni/src/words_priority_queue.h
index e613d2f..84b4b48 100644
--- a/native/jni/src/words_priority_queue.h
+++ b/native/jni/src/words_priority_queue.h
@@ -28,7 +28,7 @@
 class WordsPriorityQueue {
  public:
     class SuggestedWord {
-    public:
+     public:
         int mScore;
         int mWord[MAX_WORD_LENGTH_INTERNAL];
         int mWordLength;
@@ -122,7 +122,7 @@
     AK_FORCE_INLINE float getHighestNormalizedScore(const int *before, const int beforeLength,
             int **outWord, int *outScore, int *outLength) {
         if (!mHighestSuggestedWord) {
-            return 0.0;
+            return 0.0f;
         }
         return getNormalizedScore(mHighestSuggestedWord, before, beforeLength, outWord, outScore,
                 outLength);
diff --git a/tests/src/com/android/inputmethod/keyboard/SpacebarTextTests.java b/tests/src/com/android/inputmethod/keyboard/SpacebarTextTests.java
index de323b0..015c271 100644
--- a/tests/src/com/android/inputmethod/keyboard/SpacebarTextTests.java
+++ b/tests/src/com/android/inputmethod/keyboard/SpacebarTextTests.java
@@ -18,7 +18,6 @@
 
 import android.content.Context;
 import android.content.res.Resources;
-import android.preference.PreferenceManager;
 import android.test.AndroidTestCase;
 import android.view.inputmethod.InputMethodSubtype;
 
@@ -42,8 +41,7 @@
     protected void setUp() throws Exception {
         super.setUp();
         final Context context = getContext();
-        RichInputMethodManager.init(
-                context, PreferenceManager.getDefaultSharedPreferences(context));
+        RichInputMethodManager.init(context);
         mRichImm = RichInputMethodManager.getInstance();
         mRes = context.getResources();
         SubtypeLocale.init(context);
diff --git a/tests/src/com/android/inputmethod/latin/InputTestsBase.java b/tests/src/com/android/inputmethod/latin/InputTestsBase.java
index 7033050..8629867 100644
--- a/tests/src/com/android/inputmethod/latin/InputTestsBase.java
+++ b/tests/src/com/android/inputmethod/latin/InputTestsBase.java
@@ -275,7 +275,7 @@
         if (subtype == null) {
             fail("InputMethodSubtype for locale " + locale + " is not enabled");
         }
-        SubtypeSwitcher.getInstance().updateSubtype(subtype);
+        SubtypeSwitcher.getInstance().onSubtypeChanged(subtype);
         mLatinIME.loadKeyboard();
         mKeyboard = mLatinIME.mKeyboardSwitcher.getKeyboard();
         waitForDictionaryToBeLoaded();
diff --git a/tests/src/com/android/inputmethod/latin/SubtypeLocaleTests.java b/tests/src/com/android/inputmethod/latin/SubtypeLocaleTests.java
index 0f6fb73..4e81de6 100644
--- a/tests/src/com/android/inputmethod/latin/SubtypeLocaleTests.java
+++ b/tests/src/com/android/inputmethod/latin/SubtypeLocaleTests.java
@@ -18,7 +18,6 @@
 
 import android.content.Context;
 import android.content.res.Resources;
-import android.preference.PreferenceManager;
 import android.test.AndroidTestCase;
 import android.view.inputmethod.InputMethodSubtype;
 
@@ -38,8 +37,7 @@
     protected void setUp() throws Exception {
         super.setUp();
         final Context context = getContext();
-        RichInputMethodManager.init(
-                context, PreferenceManager.getDefaultSharedPreferences(context));
+        RichInputMethodManager.init(context);
         mRichImm = RichInputMethodManager.getInstance();
         mRes = context.getResources();
         SubtypeLocale.init(context);