Merge "Periodic wipe of the personalization dict."
diff --git a/java/AndroidManifest.xml b/java/AndroidManifest.xml
index 0d80c03..a7e64bf 100644
--- a/java/AndroidManifest.xml
+++ b/java/AndroidManifest.xml
@@ -34,7 +34,8 @@
     <application android:label="@string/english_ime_name"
             android:icon="@drawable/ic_launcher_keyboard"
             android:killAfterRestore="false"
-            android:supportsRtl="true">
+            android:supportsRtl="true"
+            android:allowBackup="true">
 
         <service android:name="LatinIME"
                 android:label="@string/english_ime_name"
@@ -110,13 +111,14 @@
             </intent-filter>
         </receiver>
 
-        <receiver android:name=".personalization.DictionaryDecayBroadcastReciever">
+        <receiver android:name=".personalization.DictionaryDecayBroadcastReciever"
+            android:exported="false">
             <intent-filter>
                 <action android:name="com.android.inputmethod.latin.personalization.DICT_DECAY" />
             </intent-filter>
         </receiver>
 
-        <receiver android:name=".DictionaryPackInstallBroadcastReceiver">
+        <receiver android:name=".DictionaryPackInstallBroadcastReceiver" android:exported="false">
             <intent-filter>
                 <action android:name="com.android.inputmethod.dictionarypack.aosp.UNKNOWN_CLIENT" />
             </intent-filter>
diff --git a/java/res/layout/research_feedback_fragment_layout.xml b/java/res/layout/research_feedback_fragment_layout.xml
index 505a1e8..fb5c278 100644
--- a/java/res/layout/research_feedback_fragment_layout.xml
+++ b/java/res/layout/research_feedback_fragment_layout.xml
@@ -84,40 +84,32 @@
             android:checked="false"
             android:text="@string/research_feedback_include_recording_label" />
         <LinearLayout
+            style="?android:attr/buttonBarStyle"
             android:layout_width="match_parent"
             android:layout_height="wrap_content"
-            android:orientation="vertical"
-            android:divider="?android:attr/dividerHorizontal"
-            android:showDividers="beginning"
-            android:dividerPadding="0dip">
-            <LinearLayout
-                style="?android:attr/buttonBarStyle"
-                android:layout_width="match_parent"
-                android:layout_height="wrap_content"
-                android:orientation="horizontal"
-                android:layoutDirection="locale"
-                android:measureWithLargestChild="true">
-                <Button
-                    android:id="@+id/research_feedback_cancel_button"
-                    android:layout_width="wrap_content"
-                    android:layout_gravity="left"
-                    android:layout_weight="1"
-                    android:maxLines="2"
-                    style="?android:attr/buttonBarButtonStyle"
-                    android:textSize="14sp"
-                    android:text="@string/research_feedback_cancel"
-                    android:layout_height="wrap_content" />
-                <Button
-                    android:id="@+id/research_feedback_send_button"
-                    android:layout_width="wrap_content"
-                    android:layout_gravity="right"
-                    android:layout_weight="1"
-                    android:maxLines="2"
-                    style="?android:attr/buttonBarButtonStyle"
-                    android:textSize="14sp"
-                    android:text="@string/research_feedback_send"
-                    android:layout_height="wrap_content" />
-            </LinearLayout>
+            android:orientation="horizontal"
+            android:layoutDirection="locale"
+            android:measureWithLargestChild="true">
+            <Button
+                android:id="@+id/research_feedback_cancel_button"
+                android:layout_width="wrap_content"
+                android:layout_gravity="left"
+                android:layout_weight="1"
+                android:maxLines="2"
+                style="?android:attr/buttonBarButtonStyle"
+                android:textSize="14sp"
+                android:text="@string/research_feedback_cancel"
+                android:layout_height="wrap_content" />
+            <Button
+                android:id="@+id/research_feedback_send_button"
+                android:layout_width="wrap_content"
+                android:layout_gravity="right"
+                android:layout_weight="1"
+                android:maxLines="2"
+                style="?android:attr/buttonBarButtonStyle"
+                android:textSize="14sp"
+                android:text="@string/research_feedback_send"
+                android:layout_height="wrap_content" />
         </LinearLayout>
     </LinearLayout>
 </ScrollView>
diff --git a/java/res/layout/seek_bar_dialog.xml b/java/res/layout/seek_bar_dialog.xml
index a47e9a0..e723ad9 100644
--- a/java/res/layout/seek_bar_dialog.xml
+++ b/java/res/layout/seek_bar_dialog.xml
@@ -33,7 +33,7 @@
         <TextView android:id="@+id/seek_bar_dialog_value"
             android:layout_width="wrap_content"
             android:layout_height="wrap_content"
-            android:textSize="20dp"/>
+            android:textSize="20sp"/>
     </LinearLayout>
     <SeekBar
         android:id="@+id/seek_bar_dialog_bar"
diff --git a/java/res/layout/setup_steps_title.xml b/java/res/layout/setup_steps_title.xml
index e3694bf..9ee8693 100644
--- a/java/res/layout/setup_steps_title.xml
+++ b/java/res/layout/setup_steps_title.xml
@@ -21,7 +21,5 @@
 <merge xmlns:android="http://schemas.android.com/apk/res/android">
     <TextView
         android:id="@+id/setup_title"
-        style="@style/setupTitleStyle"
-        android:layout_alignParentLeft="true"
-        android:layout_alignParentTop="true" />
+        style="@style/setupTitleStyle" />
 </merge>
diff --git a/java/res/layout/setup_welcome_title.xml b/java/res/layout/setup_welcome_title.xml
index af7053a..2c3b489 100644
--- a/java/res/layout/setup_welcome_title.xml
+++ b/java/res/layout/setup_welcome_title.xml
@@ -21,9 +21,7 @@
 <merge xmlns:android="http://schemas.android.com/apk/res/android">
     <TextView
         android:id="@+id/setup_welcome_title"
-        style="@style/setupTitleStyle"
-        android:layout_alignParentLeft="true"
-        android:layout_alignParentTop="true" />
+        style="@style/setupTitleStyle" />
     <TextView
         android:id="@+id/setup_welcome_description"
         android:text="@string/setup_welcome_additional_description"
diff --git a/java/res/layout/suggestions_strip.xml b/java/res/layout/suggestions_strip.xml
index cbf31e6..908e305 100644
--- a/java/res/layout/suggestions_strip.xml
+++ b/java/res/layout/suggestions_strip.xml
@@ -19,9 +19,7 @@
 -->
 
 <merge
-    xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:latin="http://schemas.android.com/apk/res/com.android.inputmethod.latin"
->
+    xmlns:android="http://schemas.android.com/apk/res/android">
     <LinearLayout
         android:id="@+id/suggestions_strip"
         android:orientation="horizontal"
diff --git a/java/res/layout/user_dictionary_add_word.xml b/java/res/layout/user_dictionary_add_word.xml
index 607f5c4..615fde5 100644
--- a/java/res/layout/user_dictionary_add_word.xml
+++ b/java/res/layout/user_dictionary_add_word.xml
@@ -58,42 +58,33 @@
     </EditText>
 
     <LinearLayout
+        style="?android:attr/buttonBarStyle"
         android:layout_width="match_parent"
         android:layout_height="wrap_content"
-        android:divider="?android:attr/dividerHorizontal"
-        android:dividerPadding="0dip"
-        android:orientation="vertical"
-        android:showDividers="beginning" >
+        android:measureWithLargestChild="true"
+        android:orientation="horizontal" >
 
-        <LinearLayout
-            style="?android:attr/buttonBarStyle"
-            android:layout_width="match_parent"
+        <Button
+            style="?android:attr/buttonBarButtonStyle"
+            android:layout_width="0dip"
             android:layout_height="wrap_content"
-            android:measureWithLargestChild="true"
-            android:orientation="horizontal" >
+            android:layout_gravity="start"
+            android:layout_weight="1"
+            android:maxLines="2"
+            android:onClick="onClickCancel"
+            android:text="@string/cancel"
+            android:textSize="14sp" />
 
-            <Button
-                style="?android:attr/buttonBarButtonStyle"
-                android:layout_width="0dip"
-                android:layout_height="wrap_content"
-                android:layout_gravity="start"
-                android:layout_weight="1"
-                android:maxLines="2"
-                android:onClick="onClickCancel"
-                android:text="@string/cancel"
-                android:textSize="14sp" />
-
-            <Button
-                style="?android:attr/buttonBarButtonStyle"
-                android:layout_width="0dip"
-                android:layout_height="wrap_content"
-                android:layout_gravity="end"
-                android:layout_weight="1"
-                android:maxLines="2"
-                android:onClick="onClickConfirm"
-                android:text="@string/user_dict_settings_add_dialog_confirm"
-                android:textSize="14sp" />
-        </LinearLayout>
+        <Button
+            style="?android:attr/buttonBarButtonStyle"
+            android:layout_width="0dip"
+            android:layout_height="wrap_content"
+            android:layout_gravity="end"
+            android:layout_weight="1"
+            android:maxLines="2"
+            android:onClick="onClickConfirm"
+            android:text="@string/user_dict_settings_add_dialog_confirm"
+            android:textSize="14sp" />
     </LinearLayout>
 
 </LinearLayout>
\ No newline at end of file
diff --git a/java/res/layout/user_dictionary_item.xml b/java/res/layout/user_dictionary_item.xml
index 56bad77..b8d48b5 100644
--- a/java/res/layout/user_dictionary_item.xml
+++ b/java/res/layout/user_dictionary_item.xml
@@ -19,10 +19,11 @@
     android:background="?android:attr/selectableItemBackground"
     android:gravity="center_vertical"
     android:minHeight="?android:attr/listPreferredItemHeight"
-    android:paddingEnd="?android:attr/scrollbarSize" >
+    android:paddingEnd="?android:attr/scrollbarSize"
+    android:baselineAligned="false" >
 
     <RelativeLayout
-        android:layout_width="wrap_content"
+        android:layout_width="0dp"
         android:layout_height="wrap_content"
         android:padding="6dip"
         android:layout_weight="1" >
diff --git a/java/res/values-af/strings.xml b/java/res/values-af/strings.xml
index 8600b72..92f2466 100644
--- a/java/res/values-af/strings.xml
+++ b/java/res/values-af/strings.xml
@@ -119,8 +119,7 @@
     <string name="keyboard_mode_time" msgid="4381856885582143277">"tyd"</string>
     <string name="keyboard_mode_url" msgid="1519819835514911218">"URL"</string>
     <string name="voice_input" msgid="3583258583521397548">"Steminvoerinstellings"</string>
-    <!-- no translation found for voice_input_disabled_summary (8141750303464726129) -->
-    <skip />
+    <string name="voice_input_disabled_summary" msgid="8141750303464726129">"Geen steminvoermetodes geaktiveer nie. Gaan taal- en invoerinstellings na."</string>
     <string name="configure_input_method" msgid="373356270290742459">"Stel invoermetodes op"</string>
     <string name="language_selection_title" msgid="1651299598555326750">"Invoertale"</string>
     <string name="send_feedback" msgid="1780431884109392046">"Stuur terugvoer"</string>
diff --git a/java/res/values-ar/strings.xml b/java/res/values-ar/strings.xml
index b42cd29..fa9309f 100644
--- a/java/res/values-ar/strings.xml
+++ b/java/res/values-ar/strings.xml
@@ -119,8 +119,7 @@
     <string name="keyboard_mode_time" msgid="4381856885582143277">"الوقت"</string>
     <string name="keyboard_mode_url" msgid="1519819835514911218">"‏عنوان URL"</string>
     <string name="voice_input" msgid="3583258583521397548">"مفتاح الإدخال الصوتي"</string>
-    <!-- no translation found for voice_input_disabled_summary (8141750303464726129) -->
-    <skip />
+    <string name="voice_input_disabled_summary" msgid="8141750303464726129">"لم يتم تمكين أي أساليب إدخال صوتي. تحقق من إعدادات اللغة والإدخال."</string>
     <string name="configure_input_method" msgid="373356270290742459">"تهيئة طرق الإدخال"</string>
     <string name="language_selection_title" msgid="1651299598555326750">"لغات الإدخال"</string>
     <string name="send_feedback" msgid="1780431884109392046">"إرسال تعليقات"</string>
diff --git a/java/res/values-ca/strings.xml b/java/res/values-ca/strings.xml
index 21dd7d5..9aff403 100644
--- a/java/res/values-ca/strings.xml
+++ b/java/res/values-ca/strings.xml
@@ -119,8 +119,7 @@
     <string name="keyboard_mode_time" msgid="4381856885582143277">"hora"</string>
     <string name="keyboard_mode_url" msgid="1519819835514911218">"URL"</string>
     <string name="voice_input" msgid="3583258583521397548">"Tecla d\'entrada de veu"</string>
-    <!-- no translation found for voice_input_disabled_summary (8141750303464726129) -->
-    <skip />
+    <string name="voice_input_disabled_summary" msgid="8141750303464726129">"No hi ha cap mètode d\'introducció activat. Comprova la configuració d\'Idioma i introducció de text."</string>
     <string name="configure_input_method" msgid="373356270290742459">"Configura mètodes d\'entrada"</string>
     <string name="language_selection_title" msgid="1651299598555326750">"Idiomes"</string>
     <string name="send_feedback" msgid="1780431884109392046">"Envia comentaris"</string>
diff --git a/java/res/values-cs/strings.xml b/java/res/values-cs/strings.xml
index 8140615..77bae55 100644
--- a/java/res/values-cs/strings.xml
+++ b/java/res/values-cs/strings.xml
@@ -119,8 +119,7 @@
     <string name="keyboard_mode_time" msgid="4381856885582143277">"čas"</string>
     <string name="keyboard_mode_url" msgid="1519819835514911218">"adresy URL"</string>
     <string name="voice_input" msgid="3583258583521397548">"Klávesa hlasového vstupu"</string>
-    <!-- no translation found for voice_input_disabled_summary (8141750303464726129) -->
-    <skip />
+    <string name="voice_input_disabled_summary" msgid="8141750303464726129">"Nejsou povoleny žádné metody hlasového vstupu. Zkontrolujte nastavení Jazyk a vstup."</string>
     <string name="configure_input_method" msgid="373356270290742459">"Konfigurace metod zadávání"</string>
     <string name="language_selection_title" msgid="1651299598555326750">"Vstupní jazyky"</string>
     <string name="send_feedback" msgid="1780431884109392046">"Odeslat zpětnou vazbu"</string>
diff --git a/java/res/values-da/strings.xml b/java/res/values-da/strings.xml
index b7f75b3..cd3a667 100644
--- a/java/res/values-da/strings.xml
+++ b/java/res/values-da/strings.xml
@@ -119,8 +119,7 @@
     <string name="keyboard_mode_time" msgid="4381856885582143277">"klokkeslæt"</string>
     <string name="keyboard_mode_url" msgid="1519819835514911218">"Webadresse"</string>
     <string name="voice_input" msgid="3583258583521397548">"Nøgle til stemmeinput"</string>
-    <!-- no translation found for voice_input_disabled_summary (8141750303464726129) -->
-    <skip />
+    <string name="voice_input_disabled_summary" msgid="8141750303464726129">"Der er ingen aktiverede stemmeinputmetoder. Kontrollér Indstillinger for sprog og input."</string>
     <string name="configure_input_method" msgid="373356270290742459">"Konfigurer inputmetoder"</string>
     <string name="language_selection_title" msgid="1651299598555326750">"Inputsprog"</string>
     <string name="send_feedback" msgid="1780431884109392046">"Send feedback"</string>
diff --git a/java/res/values-de/strings.xml b/java/res/values-de/strings.xml
index 178db3c..1d0a549 100644
--- a/java/res/values-de/strings.xml
+++ b/java/res/values-de/strings.xml
@@ -119,8 +119,7 @@
     <string name="keyboard_mode_time" msgid="4381856885582143277">"Zeit"</string>
     <string name="keyboard_mode_url" msgid="1519819835514911218">"URL"</string>
     <string name="voice_input" msgid="3583258583521397548">"Taste für Spracheingabe"</string>
-    <!-- no translation found for voice_input_disabled_summary (8141750303464726129) -->
-    <skip />
+    <string name="voice_input_disabled_summary" msgid="8141750303464726129">"Keine Spracheingabemethoden aktiviert. Rufen Sie die Einstellungen für \"Sprache &amp; Eingabe\" auf."</string>
     <string name="configure_input_method" msgid="373356270290742459">"Eingabemethoden konfigurieren"</string>
     <string name="language_selection_title" msgid="1651299598555326750">"Eingabesprachen"</string>
     <string name="send_feedback" msgid="1780431884109392046">"Feedback geben"</string>
diff --git a/java/res/values-el/strings.xml b/java/res/values-el/strings.xml
index 6875f68..c9086fd 100644
--- a/java/res/values-el/strings.xml
+++ b/java/res/values-el/strings.xml
@@ -119,8 +119,7 @@
     <string name="keyboard_mode_time" msgid="4381856885582143277">"ώρα"</string>
     <string name="keyboard_mode_url" msgid="1519819835514911218">"διεύθυνση URL"</string>
     <string name="voice_input" msgid="3583258583521397548">"Κλειδί φωνητικής εξόδου"</string>
-    <!-- no translation found for voice_input_disabled_summary (8141750303464726129) -->
-    <skip />
+    <string name="voice_input_disabled_summary" msgid="8141750303464726129">"Δεν έχουν ενεργοποιηθεί μέθοδοι φωνητικής εισαγωγής. Ελέγξτε τις Ρυθμίσεις Γλώσσας και εισαγωγής."</string>
     <string name="configure_input_method" msgid="373356270290742459">"Διαμόρφωση μεθόδων εισαγωγής"</string>
     <string name="language_selection_title" msgid="1651299598555326750">"Γλώσσες εισόδου"</string>
     <string name="send_feedback" msgid="1780431884109392046">"Αποστολή σχολίων"</string>
diff --git a/java/res/values-en-rGB/strings.xml b/java/res/values-en-rGB/strings.xml
index 7e5e0e1..f344e73 100644
--- a/java/res/values-en-rGB/strings.xml
+++ b/java/res/values-en-rGB/strings.xml
@@ -119,8 +119,7 @@
     <string name="keyboard_mode_time" msgid="4381856885582143277">"time"</string>
     <string name="keyboard_mode_url" msgid="1519819835514911218">"URL"</string>
     <string name="voice_input" msgid="3583258583521397548">"Voice input key"</string>
-    <!-- no translation found for voice_input_disabled_summary (8141750303464726129) -->
-    <skip />
+    <string name="voice_input_disabled_summary" msgid="8141750303464726129">"No voice input methods enabled. Check Language &amp; input settings."</string>
     <string name="configure_input_method" msgid="373356270290742459">"Configure input methods"</string>
     <string name="language_selection_title" msgid="1651299598555326750">"Input languages"</string>
     <string name="send_feedback" msgid="1780431884109392046">"Send feedback"</string>
diff --git a/java/res/values-en-rIN/strings.xml b/java/res/values-en-rIN/strings.xml
index 7e5e0e1..f344e73 100644
--- a/java/res/values-en-rIN/strings.xml
+++ b/java/res/values-en-rIN/strings.xml
@@ -119,8 +119,7 @@
     <string name="keyboard_mode_time" msgid="4381856885582143277">"time"</string>
     <string name="keyboard_mode_url" msgid="1519819835514911218">"URL"</string>
     <string name="voice_input" msgid="3583258583521397548">"Voice input key"</string>
-    <!-- no translation found for voice_input_disabled_summary (8141750303464726129) -->
-    <skip />
+    <string name="voice_input_disabled_summary" msgid="8141750303464726129">"No voice input methods enabled. Check Language &amp; input settings."</string>
     <string name="configure_input_method" msgid="373356270290742459">"Configure input methods"</string>
     <string name="language_selection_title" msgid="1651299598555326750">"Input languages"</string>
     <string name="send_feedback" msgid="1780431884109392046">"Send feedback"</string>
diff --git a/java/res/values-es/strings.xml b/java/res/values-es/strings.xml
index cfcb988..5427349 100644
--- a/java/res/values-es/strings.xml
+++ b/java/res/values-es/strings.xml
@@ -119,8 +119,7 @@
     <string name="keyboard_mode_time" msgid="4381856885582143277">"hora"</string>
     <string name="keyboard_mode_url" msgid="1519819835514911218">"URL"</string>
     <string name="voice_input" msgid="3583258583521397548">"Tecla de entrada de voz"</string>
-    <!-- no translation found for voice_input_disabled_summary (8141750303464726129) -->
-    <skip />
+    <string name="voice_input_disabled_summary" msgid="8141750303464726129">"Sin métodos de introducción de voz habilitados. Comprueba ajustes de Idioma e introducción de texto."</string>
     <string name="configure_input_method" msgid="373356270290742459">"Configurar métodos de entrada"</string>
     <string name="language_selection_title" msgid="1651299598555326750">"Idiomas"</string>
     <string name="send_feedback" msgid="1780431884109392046">"Danos tu opinión"</string>
diff --git a/java/res/values-et-rEE/strings.xml b/java/res/values-et-rEE/strings.xml
index 68be2f2..2c94c77 100644
--- a/java/res/values-et-rEE/strings.xml
+++ b/java/res/values-et-rEE/strings.xml
@@ -119,8 +119,7 @@
     <string name="keyboard_mode_time" msgid="4381856885582143277">"aeg"</string>
     <string name="keyboard_mode_url" msgid="1519819835514911218">"URL"</string>
     <string name="voice_input" msgid="3583258583521397548">"Häälesisendi klahv"</string>
-    <!-- no translation found for voice_input_disabled_summary (8141750303464726129) -->
-    <skip />
+    <string name="voice_input_disabled_summary" msgid="8141750303464726129">"Ühtegi häälsisendmeetodit pole lubatud. Kontrollige keele- ja sisendiseadeid."</string>
     <string name="configure_input_method" msgid="373356270290742459">"Sisestusmeetodite seadistamine"</string>
     <string name="language_selection_title" msgid="1651299598555326750">"Sisestuskeeled"</string>
     <string name="send_feedback" msgid="1780431884109392046">"Saatke tagasisidet"</string>
diff --git a/java/res/values-fi/strings.xml b/java/res/values-fi/strings.xml
index 2e4a2c7..b729c2b 100644
--- a/java/res/values-fi/strings.xml
+++ b/java/res/values-fi/strings.xml
@@ -119,8 +119,7 @@
     <string name="keyboard_mode_time" msgid="4381856885582143277">"aika"</string>
     <string name="keyboard_mode_url" msgid="1519819835514911218">"URL-osoite"</string>
     <string name="voice_input" msgid="3583258583521397548">"Äänisyöteavain"</string>
-    <!-- no translation found for voice_input_disabled_summary (8141750303464726129) -->
-    <skip />
+    <string name="voice_input_disabled_summary" msgid="8141750303464726129">"Äänen syöttötapoja ei ole otettu käyttöön. Tarkista Kieli ja syöttötapa -asetukset."</string>
     <string name="configure_input_method" msgid="373356270290742459">"Määritä syöttötavat"</string>
     <string name="language_selection_title" msgid="1651299598555326750">"Syöttökielet"</string>
     <string name="send_feedback" msgid="1780431884109392046">"Lähetä palautetta"</string>
diff --git a/java/res/values-fr-rCA/strings.xml b/java/res/values-fr-rCA/strings.xml
index 1427202..d767165 100644
--- a/java/res/values-fr-rCA/strings.xml
+++ b/java/res/values-fr-rCA/strings.xml
@@ -119,8 +119,7 @@
     <string name="keyboard_mode_time" msgid="4381856885582143277">"Heure"</string>
     <string name="keyboard_mode_url" msgid="1519819835514911218">"URL"</string>
     <string name="voice_input" msgid="3583258583521397548">"Touche de saisie vocale"</string>
-    <!-- no translation found for voice_input_disabled_summary (8141750303464726129) -->
-    <skip />
+    <string name="voice_input_disabled_summary" msgid="8141750303464726129">"Aucun mode d\'entrée vocale n\'a été activé. Vérifiez les paramètres de langues et d\'entrée de texte."</string>
     <string name="configure_input_method" msgid="373356270290742459">"Configurer les modes de saisie"</string>
     <string name="language_selection_title" msgid="1651299598555326750">"Langues de saisie"</string>
     <string name="send_feedback" msgid="1780431884109392046">"Envoyer des commentaires"</string>
diff --git a/java/res/values-hi/strings.xml b/java/res/values-hi/strings.xml
index 7336156..7b9cbfa 100644
--- a/java/res/values-hi/strings.xml
+++ b/java/res/values-hi/strings.xml
@@ -119,8 +119,7 @@
     <string name="keyboard_mode_time" msgid="4381856885582143277">"समय"</string>
     <string name="keyboard_mode_url" msgid="1519819835514911218">"URL"</string>
     <string name="voice_input" msgid="3583258583521397548">"ध्‍वनि‍ इनपुट कुंजी"</string>
-    <!-- no translation found for voice_input_disabled_summary (8141750303464726129) -->
-    <skip />
+    <string name="voice_input_disabled_summary" msgid="8141750303464726129">"कोई ध्वनि इनपुट पद्धति सक्षम नहीं है. भाषा और इनपुट सेटिंग जांचें."</string>
     <string name="configure_input_method" msgid="373356270290742459">"इनपुट पद्धति कॉन्‍फ़िगर करें"</string>
     <string name="language_selection_title" msgid="1651299598555326750">"इनपुट भाषा"</string>
     <string name="send_feedback" msgid="1780431884109392046">"सुझाव भेजें"</string>
diff --git a/java/res/values-hr/strings.xml b/java/res/values-hr/strings.xml
index 1afe99e..cb8dda1 100644
--- a/java/res/values-hr/strings.xml
+++ b/java/res/values-hr/strings.xml
@@ -119,8 +119,7 @@
     <string name="keyboard_mode_time" msgid="4381856885582143277">"vrijeme"</string>
     <string name="keyboard_mode_url" msgid="1519819835514911218">"URL"</string>
     <string name="voice_input" msgid="3583258583521397548">"Tipka za glasovni unos"</string>
-    <!-- no translation found for voice_input_disabled_summary (8141750303464726129) -->
-    <skip />
+    <string name="voice_input_disabled_summary" msgid="8141750303464726129">"Nije omogućen nijedan način glasovnog unosa. Provjerite postavke jezika i unosa."</string>
     <string name="configure_input_method" msgid="373356270290742459">"Konfiguriraj načine ulaza"</string>
     <string name="language_selection_title" msgid="1651299598555326750">"Jezici unosa"</string>
     <string name="send_feedback" msgid="1780431884109392046">"Slanje povratnih informacija"</string>
diff --git a/java/res/values-hu/strings.xml b/java/res/values-hu/strings.xml
index 8ed477a..2948826 100644
--- a/java/res/values-hu/strings.xml
+++ b/java/res/values-hu/strings.xml
@@ -119,8 +119,7 @@
     <string name="keyboard_mode_time" msgid="4381856885582143277">"idő"</string>
     <string name="keyboard_mode_url" msgid="1519819835514911218">"URL"</string>
     <string name="voice_input" msgid="3583258583521397548">"Hangbeviteli gomb"</string>
-    <!-- no translation found for voice_input_disabled_summary (8141750303464726129) -->
-    <skip />
+    <string name="voice_input_disabled_summary" msgid="8141750303464726129">"Nincs engedélyezett hangbeviteli módszer. Nézze meg a Nyelvi és beviteli beállításokat."</string>
     <string name="configure_input_method" msgid="373356270290742459">"Beviteli módok beállítása"</string>
     <string name="language_selection_title" msgid="1651299598555326750">"Beviteli nyelvek"</string>
     <string name="send_feedback" msgid="1780431884109392046">"Visszajelzés küldése"</string>
diff --git a/java/res/values-in/strings.xml b/java/res/values-in/strings.xml
index 6957700..96fa1f8 100644
--- a/java/res/values-in/strings.xml
+++ b/java/res/values-in/strings.xml
@@ -119,8 +119,7 @@
     <string name="keyboard_mode_time" msgid="4381856885582143277">"waktu"</string>
     <string name="keyboard_mode_url" msgid="1519819835514911218">"URL"</string>
     <string name="voice_input" msgid="3583258583521397548">"Tombol masukan suara"</string>
-    <!-- no translation found for voice_input_disabled_summary (8141750303464726129) -->
-    <skip />
+    <string name="voice_input_disabled_summary" msgid="8141750303464726129">"Tidak ada metode masukan suara yang diaktifkan. Periksa setelan Bahasan &amp; masukan."</string>
     <string name="configure_input_method" msgid="373356270290742459">"Konfigurasikan metode masukan"</string>
     <string name="language_selection_title" msgid="1651299598555326750">"Bahasa masukan"</string>
     <string name="send_feedback" msgid="1780431884109392046">"Kirim masukan"</string>
diff --git a/java/res/values-it/strings.xml b/java/res/values-it/strings.xml
index 12f5004..ea8bf02 100644
--- a/java/res/values-it/strings.xml
+++ b/java/res/values-it/strings.xml
@@ -119,8 +119,7 @@
     <string name="keyboard_mode_time" msgid="4381856885582143277">"ora"</string>
     <string name="keyboard_mode_url" msgid="1519819835514911218">"URL"</string>
     <string name="voice_input" msgid="3583258583521397548">"Tasto input vocale"</string>
-    <!-- no translation found for voice_input_disabled_summary (8141750303464726129) -->
-    <skip />
+    <string name="voice_input_disabled_summary" msgid="8141750303464726129">"Nessun metodo di immissione vocale abilitato. Controlla le impostazioni Lingua e input."</string>
     <string name="configure_input_method" msgid="373356270290742459">"Configura metodi di immissione"</string>
     <string name="language_selection_title" msgid="1651299598555326750">"Lingue comandi"</string>
     <string name="send_feedback" msgid="1780431884109392046">"Invia feedback"</string>
diff --git a/java/res/values-iw/strings.xml b/java/res/values-iw/strings.xml
index 0604eb7..8899cec 100644
--- a/java/res/values-iw/strings.xml
+++ b/java/res/values-iw/strings.xml
@@ -119,8 +119,7 @@
     <string name="keyboard_mode_time" msgid="4381856885582143277">"זמן"</string>
     <string name="keyboard_mode_url" msgid="1519819835514911218">"כתובות אתרים"</string>
     <string name="voice_input" msgid="3583258583521397548">"מקש קלט קולי"</string>
-    <!-- no translation found for voice_input_disabled_summary (8141750303464726129) -->
-    <skip />
+    <string name="voice_input_disabled_summary" msgid="8141750303464726129">"לא הופעלו שיטות של קלט קולי. בדוק את הגדרות השפה והקלט."</string>
     <string name="configure_input_method" msgid="373356270290742459">"הגדרת שיטות קלט"</string>
     <string name="language_selection_title" msgid="1651299598555326750">"שפות קלט"</string>
     <string name="send_feedback" msgid="1780431884109392046">"שלח משוב"</string>
diff --git a/java/res/values-km-rKH/strings.xml b/java/res/values-km-rKH/strings.xml
index e98afd2..4fdb7d2 100644
--- a/java/res/values-km-rKH/strings.xml
+++ b/java/res/values-km-rKH/strings.xml
@@ -119,8 +119,7 @@
     <string name="keyboard_mode_time" msgid="4381856885582143277">"ពេលវេលា"</string>
     <string name="keyboard_mode_url" msgid="1519819835514911218">"URL"</string>
     <string name="voice_input" msgid="3583258583521397548">"គ្រាប់​ចុច​បញ្ចូល​​សំឡេង"</string>
-    <!-- no translation found for voice_input_disabled_summary (8141750303464726129) -->
-    <skip />
+    <string name="voice_input_disabled_summary" msgid="8141750303464726129">"គ្មាន​វិធីសាស្ត្រ​បញ្ចូល​សំឡេង​បាន​បើក។ ពិនិត្យ​មើល​ការ​កំណត់​ភាសា &amp; ការ​បញ្ចូល។"</string>
     <string name="configure_input_method" msgid="373356270290742459">"កំណត់​រចនាសម្ព័ន្ធ​វិធីសាស្ត្រ​បញ្ចូល"</string>
     <string name="language_selection_title" msgid="1651299598555326750">"បញ្ចូល​ភាសា"</string>
     <string name="send_feedback" msgid="1780431884109392046">"ផ្ញើ​មតិ​អ្នក​ប្រើ"</string>
diff --git a/java/res/values-lt/strings.xml b/java/res/values-lt/strings.xml
index 714205e0..62460b2 100644
--- a/java/res/values-lt/strings.xml
+++ b/java/res/values-lt/strings.xml
@@ -119,8 +119,7 @@
     <string name="keyboard_mode_time" msgid="4381856885582143277">"laiko"</string>
     <string name="keyboard_mode_url" msgid="1519819835514911218">"URL"</string>
     <string name="voice_input" msgid="3583258583521397548">"Įvesties balsu klavišas"</string>
-    <!-- no translation found for voice_input_disabled_summary (8141750303464726129) -->
-    <skip />
+    <string name="voice_input_disabled_summary" msgid="8141750303464726129">"Nėra jokių įgalintų įvesties balsu metodų. Patikrinkite kalbos ir įvesties nustatymus."</string>
     <string name="configure_input_method" msgid="373356270290742459">"Konfigūruoti įvesties metodus"</string>
     <string name="language_selection_title" msgid="1651299598555326750">"Įvesties kalbos"</string>
     <string name="send_feedback" msgid="1780431884109392046">"Siųsti atsiliepimą"</string>
diff --git a/java/res/values-mn-rMN/strings.xml b/java/res/values-mn-rMN/strings.xml
index 2f74dfa..e015752 100644
--- a/java/res/values-mn-rMN/strings.xml
+++ b/java/res/values-mn-rMN/strings.xml
@@ -119,8 +119,7 @@
     <string name="keyboard_mode_time" msgid="4381856885582143277">"цаг"</string>
     <string name="keyboard_mode_url" msgid="1519819835514911218">"URL"</string>
     <string name="voice_input" msgid="3583258583521397548">"Дуун оруулгын товч"</string>
-    <!-- no translation found for voice_input_disabled_summary (8141750303464726129) -->
-    <skip />
+    <string name="voice_input_disabled_summary" msgid="8141750303464726129">"Ямар ч дуу оруулах хэрэглүүр идэвхжээгүй байна. Хэл болон оруулалтын тохиргоог шалгана уу."</string>
     <string name="configure_input_method" msgid="373356270290742459">"Оруулах аргуудын тохиргоо"</string>
     <string name="language_selection_title" msgid="1651299598555326750">"Оруулах хэл"</string>
     <string name="send_feedback" msgid="1780431884109392046">"Санал хүсэлт илгээх"</string>
diff --git a/java/res/values-nb/strings.xml b/java/res/values-nb/strings.xml
index 16ccadc..ed85364 100644
--- a/java/res/values-nb/strings.xml
+++ b/java/res/values-nb/strings.xml
@@ -119,8 +119,7 @@
     <string name="keyboard_mode_time" msgid="4381856885582143277">"tid"</string>
     <string name="keyboard_mode_url" msgid="1519819835514911218">"Nettadresse"</string>
     <string name="voice_input" msgid="3583258583521397548">"Tast for taleinndata"</string>
-    <!-- no translation found for voice_input_disabled_summary (8141750303464726129) -->
-    <skip />
+    <string name="voice_input_disabled_summary" msgid="8141750303464726129">"Ingen taleinndatametoder er aktivert. Sjekk Språk og inndata-innstillingene."</string>
     <string name="configure_input_method" msgid="373356270290742459">"Konfigurer inndatametoder"</string>
     <string name="language_selection_title" msgid="1651299598555326750">"Inndataspråk"</string>
     <string name="send_feedback" msgid="1780431884109392046">"Send tilbakemelding"</string>
diff --git a/java/res/values-nl/strings.xml b/java/res/values-nl/strings.xml
index 81791fa..57511f9 100644
--- a/java/res/values-nl/strings.xml
+++ b/java/res/values-nl/strings.xml
@@ -119,8 +119,7 @@
     <string name="keyboard_mode_time" msgid="4381856885582143277">"tijd"</string>
     <string name="keyboard_mode_url" msgid="1519819835514911218">"URL"</string>
     <string name="voice_input" msgid="3583258583521397548">"Toets voor spraakinvoer"</string>
-    <!-- no translation found for voice_input_disabled_summary (8141750303464726129) -->
-    <skip />
+    <string name="voice_input_disabled_summary" msgid="8141750303464726129">"Geen spraakinvoermethoden ingeschakeld. Ga naar \'Instellingen voor taal en invoer\'."</string>
     <string name="configure_input_method" msgid="373356270290742459">"Invoermethoden configureren"</string>
     <string name="language_selection_title" msgid="1651299598555326750">"Invoertalen"</string>
     <string name="send_feedback" msgid="1780431884109392046">"Feedback verzenden"</string>
diff --git a/java/res/values-pl/strings.xml b/java/res/values-pl/strings.xml
index cfc6c7c..257fc2a 100644
--- a/java/res/values-pl/strings.xml
+++ b/java/res/values-pl/strings.xml
@@ -119,8 +119,7 @@
     <string name="keyboard_mode_time" msgid="4381856885582143277">"godzina"</string>
     <string name="keyboard_mode_url" msgid="1519819835514911218">"URL"</string>
     <string name="voice_input" msgid="3583258583521397548">"Klawisz rozpoznawania mowy"</string>
-    <!-- no translation found for voice_input_disabled_summary (8141750303464726129) -->
-    <skip />
+    <string name="voice_input_disabled_summary" msgid="8141750303464726129">"Nie włączono żadnych metod wprowadzania głosowego. Sprawdź ustawienia języka i wprowadzania."</string>
     <string name="configure_input_method" msgid="373356270290742459">"Konfiguruj metody wprowadzania"</string>
     <string name="language_selection_title" msgid="1651299598555326750">"Języki wprowadzania"</string>
     <string name="send_feedback" msgid="1780431884109392046">"Prześlij opinię"</string>
diff --git a/java/res/values-pt-rPT/strings.xml b/java/res/values-pt-rPT/strings.xml
index c247d3b..de4bada 100644
--- a/java/res/values-pt-rPT/strings.xml
+++ b/java/res/values-pt-rPT/strings.xml
@@ -119,8 +119,7 @@
     <string name="keyboard_mode_time" msgid="4381856885582143277">"hora"</string>
     <string name="keyboard_mode_url" msgid="1519819835514911218">"URLs"</string>
     <string name="voice_input" msgid="3583258583521397548">"Chave de entrada de voz"</string>
-    <!-- no translation found for voice_input_disabled_summary (8141750303464726129) -->
-    <skip />
+    <string name="voice_input_disabled_summary" msgid="8141750303464726129">"Nenhum método de entrada de texto por voz ativado. Verifique as definições de Idioma e introdução."</string>
     <string name="configure_input_method" msgid="373356270290742459">"Configurar métodos de introdução"</string>
     <string name="language_selection_title" msgid="1651299598555326750">"Idiomas de entrada"</string>
     <string name="send_feedback" msgid="1780431884109392046">"Enviar comentários"</string>
diff --git a/java/res/values-sl/strings.xml b/java/res/values-sl/strings.xml
index f330127..cbbf758 100644
--- a/java/res/values-sl/strings.xml
+++ b/java/res/values-sl/strings.xml
@@ -119,8 +119,7 @@
     <string name="keyboard_mode_time" msgid="4381856885582143277">"ura"</string>
     <string name="keyboard_mode_url" msgid="1519819835514911218">"URL"</string>
     <string name="voice_input" msgid="3583258583521397548">"Tipka za glasovni vnos"</string>
-    <!-- no translation found for voice_input_disabled_summary (8141750303464726129) -->
-    <skip />
+    <string name="voice_input_disabled_summary" msgid="8141750303464726129">"Ni omogočenih glasovnih načinov vnosa. Preverite nastavitve v razdelku »Jezik in vnos«."</string>
     <string name="configure_input_method" msgid="373356270290742459">"Nastavitev načinov vnosa"</string>
     <string name="language_selection_title" msgid="1651299598555326750">"Jeziki vnosa"</string>
     <string name="send_feedback" msgid="1780431884109392046">"Pošljite povratne informacije"</string>
diff --git a/java/res/values-sr/strings.xml b/java/res/values-sr/strings.xml
index 42f7b3b..00f0031 100644
--- a/java/res/values-sr/strings.xml
+++ b/java/res/values-sr/strings.xml
@@ -119,8 +119,7 @@
     <string name="keyboard_mode_time" msgid="4381856885582143277">"време"</string>
     <string name="keyboard_mode_url" msgid="1519819835514911218">"URL"</string>
     <string name="voice_input" msgid="3583258583521397548">"Тастер за гласовни унос"</string>
-    <!-- no translation found for voice_input_disabled_summary (8141750303464726129) -->
-    <skip />
+    <string name="voice_input_disabled_summary" msgid="8141750303464726129">"Ниједан метод гласовног уноса није омогућен. Проверите Подешавања језика и уноса."</string>
     <string name="configure_input_method" msgid="373356270290742459">"Конфигурисање метода уноса"</string>
     <string name="language_selection_title" msgid="1651299598555326750">"Језици за унос"</string>
     <string name="send_feedback" msgid="1780431884109392046">"Пошаљи повратне информације"</string>
diff --git a/java/res/values-sv/strings.xml b/java/res/values-sv/strings.xml
index bc10054..6a16d33 100644
--- a/java/res/values-sv/strings.xml
+++ b/java/res/values-sv/strings.xml
@@ -119,8 +119,7 @@
     <string name="keyboard_mode_time" msgid="4381856885582143277">"klockslag"</string>
     <string name="keyboard_mode_url" msgid="1519819835514911218">"webbadresser"</string>
     <string name="voice_input" msgid="3583258583521397548">"Röstinmatningsknapp"</string>
-    <!-- no translation found for voice_input_disabled_summary (8141750303464726129) -->
-    <skip />
+    <string name="voice_input_disabled_summary" msgid="8141750303464726129">"Ingen röstinmatningsmetod har aktiverats. Kontrollera språk- och inmatningsinställningarna."</string>
     <string name="configure_input_method" msgid="373356270290742459">"Konfigurera inmatningsmetoder"</string>
     <string name="language_selection_title" msgid="1651299598555326750">"Inmatningsspråk"</string>
     <string name="send_feedback" msgid="1780431884109392046">"Skicka feedback"</string>
diff --git a/java/res/values-uk/strings.xml b/java/res/values-uk/strings.xml
index eb249fb..3463004 100644
--- a/java/res/values-uk/strings.xml
+++ b/java/res/values-uk/strings.xml
@@ -119,8 +119,7 @@
     <string name="keyboard_mode_time" msgid="4381856885582143277">"час"</string>
     <string name="keyboard_mode_url" msgid="1519819835514911218">"URL-адреси"</string>
     <string name="voice_input" msgid="3583258583521397548">"Ключ голосового вводу"</string>
-    <!-- no translation found for voice_input_disabled_summary (8141750303464726129) -->
-    <skip />
+    <string name="voice_input_disabled_summary" msgid="8141750303464726129">"Способи голосового вводу не ввімкнено. Перейдіть у налаштування \"Мова та введення\"."</string>
     <string name="configure_input_method" msgid="373356270290742459">"Налаштування методів введення"</string>
     <string name="language_selection_title" msgid="1651299598555326750">"Мови вводу"</string>
     <string name="send_feedback" msgid="1780431884109392046">"Надіслати відгук"</string>
diff --git a/java/res/values-zu/strings.xml b/java/res/values-zu/strings.xml
index 9cf6cb9..0afb3b9 100644
--- a/java/res/values-zu/strings.xml
+++ b/java/res/values-zu/strings.xml
@@ -119,8 +119,7 @@
     <string name="keyboard_mode_time" msgid="4381856885582143277">"isikhathi"</string>
     <string name="keyboard_mode_url" msgid="1519819835514911218">"I-URL"</string>
     <string name="voice_input" msgid="3583258583521397548">"Inkinobho yokufaka izwi"</string>
-    <!-- no translation found for voice_input_disabled_summary (8141750303464726129) -->
-    <skip />
+    <string name="voice_input_disabled_summary" msgid="8141750303464726129">"Azikho izindlela zokufaka zezwi ezinikwe amandla. Hlola izilungiselelo zolimi kanye nezokufaka."</string>
     <string name="configure_input_method" msgid="373356270290742459">"Misa izindlela zokufakwayo"</string>
     <string name="language_selection_title" msgid="1651299598555326750">"Izilimi zokufakwayo"</string>
     <string name="send_feedback" msgid="1780431884109392046">"Thumela impendulo"</string>
diff --git a/java/src/com/android/inputmethod/dictionarypack/CommonPreferences.java b/java/src/com/android/inputmethod/dictionarypack/CommonPreferences.java
index 7c27e6d..3d0e29e 100644
--- a/java/src/com/android/inputmethod/dictionarypack/CommonPreferences.java
+++ b/java/src/com/android/inputmethod/dictionarypack/CommonPreferences.java
@@ -23,7 +23,7 @@
     private static final String COMMON_PREFERENCES_NAME = "LatinImeDictPrefs";
 
     public static SharedPreferences getCommonPreferences(final Context context) {
-        return context.getSharedPreferences(COMMON_PREFERENCES_NAME, Context.MODE_WORLD_READABLE);
+        return context.getSharedPreferences(COMMON_PREFERENCES_NAME, 0);
     }
 
     public static void enable(final SharedPreferences pref, final String id) {
diff --git a/java/src/com/android/inputmethod/dictionarypack/DictionaryDownloadProgressBar.java b/java/src/com/android/inputmethod/dictionarypack/DictionaryDownloadProgressBar.java
index 88b5032..384ee3e 100644
--- a/java/src/com/android/inputmethod/dictionarypack/DictionaryDownloadProgressBar.java
+++ b/java/src/com/android/inputmethod/dictionarypack/DictionaryDownloadProgressBar.java
@@ -100,6 +100,7 @@
 
     @Override
     protected void onDetachedFromWindow() {
+        super.onDetachedFromWindow();
         mIsCurrentlyAttachedToWindow = false;
         updateReporterThreadRunningStatusAccordingToVisibility();
     }
diff --git a/java/src/com/android/inputmethod/keyboard/EmojiPalettesView.java b/java/src/com/android/inputmethod/keyboard/EmojiPalettesView.java
index ff0d538..8dea908 100644
--- a/java/src/com/android/inputmethod/keyboard/EmojiPalettesView.java
+++ b/java/src/com/android/inputmethod/keyboard/EmojiPalettesView.java
@@ -71,8 +71,8 @@
  * Because of the above reasons, this class doesn't extend {@link KeyboardView}.
  */
 public final class EmojiPalettesView extends LinearLayout implements OnTabChangeListener,
-        ViewPager.OnPageChangeListener, View.OnClickListener,
-        EmojiPageKeyboardView.OnKeyClickListener {
+        ViewPager.OnPageChangeListener, View.OnTouchListener,
+        EmojiPageKeyboardView.OnKeyEventListener {
     static final String TAG = EmojiPalettesView.class.getSimpleName();
     private static final boolean DEBUG_PAGER = false;
     private final int mKeyBackgroundId;
@@ -486,16 +486,16 @@
         final ImageView alphabetKey = (ImageView)findViewById(R.id.emoji_keyboard_alphabet);
         alphabetKey.setBackgroundResource(mEmojiFunctionalKeyBackgroundId);
         alphabetKey.setTag(Constants.CODE_SWITCH_ALPHA_SYMBOL);
-        alphabetKey.setOnClickListener(this);
+        alphabetKey.setOnTouchListener(this);
         final ImageView spaceKey = (ImageView)findViewById(R.id.emoji_keyboard_space);
         spaceKey.setBackgroundResource(mKeyBackgroundId);
         spaceKey.setTag(Constants.CODE_SPACE);
-        spaceKey.setOnClickListener(this);
+        spaceKey.setOnTouchListener(this);
         mEmojiLayoutParams.setKeyProperties(spaceKey);
         final ImageView alphabetKey2 = (ImageView)findViewById(R.id.emoji_keyboard_alphabet2);
         alphabetKey2.setBackgroundResource(mEmojiFunctionalKeyBackgroundId);
         alphabetKey2.setTag(Constants.CODE_SWITCH_ALPHA_SYMBOL);
-        alphabetKey2.setOnClickListener(this);
+        alphabetKey2.setOnTouchListener(this);
     }
 
     @Override
@@ -543,31 +543,51 @@
         }
     }
 
+    // Called from {@link EmojiPageKeyboardView} through {@link View.OnTouchListener} interface to
+    // handle touch events from View-based elements such as the space bar.
     @Override
-    public void onClick(final View v) {
-        if (v.getTag() instanceof Integer) {
-            final int code = (Integer)v.getTag();
-            registerCode(code);
-            return;
+    public boolean onTouch(final View v, final MotionEvent event) {
+        final Object tag = v.getTag();
+        if (!(tag instanceof Integer)) {
+            return false;
         }
+        final int code = (Integer) tag;
+        switch (event.getAction()) {
+            case MotionEvent.ACTION_DOWN:
+                mKeyboardActionListener.onPressKey(
+                        code, 0 /* repeatCount */, true /* isSinglePointer */);
+                break;
+            case MotionEvent.ACTION_UP:
+                mKeyboardActionListener.onCodeInput(code, NOT_A_COORDINATE, NOT_A_COORDINATE);
+                mKeyboardActionListener.onReleaseKey(code, false /* withSliding */);
+                break;
+        }
+        return false;
     }
 
-    private void registerCode(final int code) {
-        mKeyboardActionListener.onPressKey(code, 0 /* repeatCount */, true /* isSinglePointer */);
-        mKeyboardActionListener.onCodeInput(code, NOT_A_COORDINATE, NOT_A_COORDINATE);
-        mKeyboardActionListener.onReleaseKey(code, false /* withSliding */);
-    }
-
+    // Called from {@link EmojiPageKeyboardView} through
+    // {@link EmojiPageKeyboardView.OnKeyEventListener} interface to handle touch events from
+    // non-View-based elements like typical Emoji characters.
     @Override
-    public void onKeyClick(final Key key) {
+    public void onPressKey(final Key key) {
+        final int code = key.getCode();
+        mKeyboardActionListener.onPressKey(code, 0 /* repeatCount */, true /* isSinglePointer */);
+    }
+
+    // Called from {@link EmojiPageKeyboardView} through
+    // {@link EmojiPageKeyboardView.OnKeyEventListener} interface to handle touch events from
+    // non-View-based elements like typical Emoji characters.
+    @Override
+    public void onReleaseKey(final Key key) {
         mEmojiPalettesAdapter.addRecentKey(key);
         mEmojiCategory.saveLastTypedCategoryPage();
         final int code = key.getCode();
         if (code == Constants.CODE_OUTPUT_TEXT) {
             mKeyboardActionListener.onTextInput(key.getOutputText());
-            return;
+        } else {
+            mKeyboardActionListener.onCodeInput(code, NOT_A_COORDINATE, NOT_A_COORDINATE);
         }
-        registerCode(code);
+        mKeyboardActionListener.onReleaseKey(code, false /* withSliding */);
     }
 
     public void setHardwareAcceleratedDrawingEnabled(final boolean enabled) {
@@ -630,7 +650,7 @@
     }
 
     private static class EmojiPalettesAdapter extends PagerAdapter {
-        private final EmojiPageKeyboardView.OnKeyClickListener mListener;
+        private final EmojiPageKeyboardView.OnKeyEventListener mListener;
         private final DynamicGridKeyboard mRecentsKeyboard;
         private final SparseArray<EmojiPageKeyboardView> mActiveKeyboardViews =
                 CollectionUtils.newSparseArray();
@@ -638,7 +658,7 @@
         private int mActivePosition = 0;
 
         public EmojiPalettesAdapter(final EmojiCategory emojiCategory,
-                final EmojiPageKeyboardView.OnKeyClickListener listener) {
+                final EmojiPageKeyboardView.OnKeyEventListener listener) {
             mEmojiCategory = emojiCategory;
             mListener = listener;
             mRecentsKeyboard = mEmojiCategory.getKeyboard(CATEGORY_ID_RECENTS, 0);
@@ -702,7 +722,7 @@
             final EmojiPageKeyboardView keyboardView = (EmojiPageKeyboardView)inflater.inflate(
                     R.layout.emoji_keyboard_page, container, false /* attachToRoot */);
             keyboardView.setKeyboard(keyboard);
-            keyboardView.setOnKeyClickListener(mListener);
+            keyboardView.setOnKeyEventListener(mListener);
             container.addView(keyboardView);
             mActiveKeyboardViews.put(position, keyboardView);
             return keyboardView;
diff --git a/java/src/com/android/inputmethod/keyboard/MainKeyboardView.java b/java/src/com/android/inputmethod/keyboard/MainKeyboardView.java
index e1c841d..810bd91 100644
--- a/java/src/com/android/inputmethod/keyboard/MainKeyboardView.java
+++ b/java/src/com/android/inputmethod/keyboard/MainKeyboardView.java
@@ -424,8 +424,8 @@
      */
     @Override
     public void setKeyboard(final Keyboard keyboard) {
-        // Remove any pending messages.
-        mKeyTimerHandler.cancelAllKeyTimers();
+        // Remove any pending messages, except dismissing preview and key repeat.
+        mKeyTimerHandler.cancelLongPressTimers();
         super.setKeyboard(keyboard);
         mKeyDetector.setKeyboard(
                 keyboard, -getPaddingLeft(), -getPaddingTop() + getVerticalCorrection());
diff --git a/java/src/com/android/inputmethod/keyboard/internal/EmojiPageKeyboardView.java b/java/src/com/android/inputmethod/keyboard/internal/EmojiPageKeyboardView.java
index 2e80f79..5c7c6e3 100644
--- a/java/src/com/android/inputmethod/keyboard/internal/EmojiPageKeyboardView.java
+++ b/java/src/com/android/inputmethod/keyboard/internal/EmojiPageKeyboardView.java
@@ -35,16 +35,19 @@
 // TODO: Implement key popup preview.
 public final class EmojiPageKeyboardView extends KeyboardView implements
         GestureDetector.OnGestureListener {
-    public interface OnKeyClickListener {
-        public void onKeyClick(Key key);
+    public interface OnKeyEventListener {
+        public void onPressKey(Key key);
+        public void onReleaseKey(Key key);
     }
 
-    private static final OnKeyClickListener EMPTY_LISTENER = new OnKeyClickListener() {
-        @Override
-        public void onKeyClick(final Key key) {}
+    private static final OnKeyEventListener EMPTY_LISTENER = new OnKeyEventListener() {
+      @Override
+      public void onPressKey(final Key key) {}
+      @Override
+      public void onReleaseKey(final Key key) {}
     };
 
-    private OnKeyClickListener mListener = EMPTY_LISTENER;
+    private OnKeyEventListener mListener = EMPTY_LISTENER;
     private final KeyDetector mKeyDetector = new KeyDetector(0.0f /*keyHysteresisDistance */);
     private final GestureDetector mGestureDetector;
 
@@ -59,7 +62,7 @@
         mGestureDetector.setIsLongpressEnabled(false /* isLongpressEnabled */);
     }
 
-    public void setOnKeyClickListener(final OnKeyClickListener listener) {
+    public void setOnKeyEventListener(final OnKeyEventListener listener) {
         mListener = listener;
     }
 
@@ -115,9 +118,9 @@
         if (key == null) {
             return false;
         }
-        // TODO: May call {@link KeyboardActionListener#onPressKey(int,int,boolean)}.
         key.onPressed();
         invalidateKey(key);
+        mListener.onPressKey(key);
         return false;
     }
 
@@ -133,10 +136,9 @@
         if (key == null) {
             return false;
         }
-        // TODO: May call {@link KeyboardActionListener#onReleaseKey(int,boolean)}.
         key.onReleased();
         invalidateKey(key);
-        mListener.onKeyClick(key);
+        mListener.onReleaseKey(key);
         return true;
     }
 
diff --git a/java/src/com/android/inputmethod/keyboard/internal/TimerHandler.java b/java/src/com/android/inputmethod/keyboard/internal/TimerHandler.java
index 3298a3f..ec7b9b0 100644
--- a/java/src/com/android/inputmethod/keyboard/internal/TimerHandler.java
+++ b/java/src/com/android/inputmethod/keyboard/internal/TimerHandler.java
@@ -126,7 +126,7 @@
         removeMessages(MSG_LONGPRESS_SHIFT_KEY);
     }
 
-    private void cancelLongPressTimers() {
+    public void cancelLongPressTimers() {
         removeMessages(MSG_LONGPRESS_KEY);
         removeMessages(MSG_LONGPRESS_SHIFT_KEY);
     }
diff --git a/java/src/com/android/inputmethod/latin/BinaryDictionaryGetter.java b/java/src/com/android/inputmethod/latin/BinaryDictionaryGetter.java
index 181ad17..7e97802 100644
--- a/java/src/com/android/inputmethod/latin/BinaryDictionaryGetter.java
+++ b/java/src/com/android/inputmethod/latin/BinaryDictionaryGetter.java
@@ -112,7 +112,7 @@
         public DictPackSettings(final Context context) {
             mDictPreferences = null == context ? null
                     : context.getSharedPreferences(COMMON_PREFERENCES_NAME,
-                            Context.MODE_WORLD_READABLE | Context.MODE_MULTI_PROCESS);
+                            Context.MODE_MULTI_PROCESS);
         }
         public boolean isWordListActive(final String dictId) {
             if (null == mDictPreferences) {
diff --git a/java/src/com/android/inputmethod/latin/LatinIME.java b/java/src/com/android/inputmethod/latin/LatinIME.java
index aadb651..3984c19 100644
--- a/java/src/com/android/inputmethod/latin/LatinIME.java
+++ b/java/src/com/android/inputmethod/latin/LatinIME.java
@@ -1396,7 +1396,8 @@
                 // word. If we are composing a word we should have the second word before the cursor
                 // memorized, otherwise we should have the first.
                 final String rereadPrevWord = mInputLogic.getNthPreviousWordForSuggestion(
-                        currentSettings, mInputLogic.mWordComposer.isComposingWord() ? 2 : 1);
+                        currentSettings.mSpacingAndPunctuations,
+                        mInputLogic.mWordComposer.isComposingWord() ? 2 : 1);
                 if (!TextUtils.equals(previousWord, rereadPrevWord)) {
                     throw new RuntimeException("Unexpected previous word: "
                             + previousWord + " <> " + rereadPrevWord);
diff --git a/java/src/com/android/inputmethod/latin/RichInputConnection.java b/java/src/com/android/inputmethod/latin/RichInputConnection.java
index 7cf64a3..79d6674 100644
--- a/java/src/com/android/inputmethod/latin/RichInputConnection.java
+++ b/java/src/com/android/inputmethod/latin/RichInputConnection.java
@@ -28,6 +28,7 @@
 
 import com.android.inputmethod.latin.define.ProductionFlag;
 import com.android.inputmethod.latin.settings.SettingsValues;
+import com.android.inputmethod.latin.settings.SpacingAndPunctuations;
 import com.android.inputmethod.latin.utils.CapsModeUtils;
 import com.android.inputmethod.latin.utils.DebugLogUtils;
 import com.android.inputmethod.latin.utils.SpannableStringUtils;
@@ -35,7 +36,6 @@
 import com.android.inputmethod.latin.utils.TextRange;
 import com.android.inputmethod.research.ResearchLogger;
 
-import java.util.Locale;
 import java.util.regex.Pattern;
 
 /**
@@ -174,13 +174,15 @@
         }
         final int lengthOfTextBeforeCursor = mCommittedTextBeforeComposingText.length();
         if (lengthOfTextBeforeCursor > newSelStart
-                || (lengthOfTextBeforeCursor < Constants.EDITOR_CONTENTS_CACHE_SIZE
+                || (newSelStart != lengthOfTextBeforeCursor
+                        && lengthOfTextBeforeCursor < Constants.EDITOR_CONTENTS_CACHE_SIZE
                         && newSelStart < Constants.EDITOR_CONTENTS_CACHE_SIZE)) {
             // newSelStart and newSelEnd may be lying -- when rotating the device (probably a
-            // framework bug). If we have less chars than we asked for, then we know how many chars
-            // we have, and if we got more than newSelStart says, then we know it was lying. In both
-            // cases the length is more reliable.  Note that we only have to check newSelStart (not
-            // newSelEnd) since if newSelEnd is wrong, the newSelStart will be wrong as well.
+            // framework bug). If the values don't agree and we have less chars than we asked
+            // for, then we know how many chars we have. If we got more than newSelStart says, then
+            // we also know it was lying. In both cases the length is more reliable. Note that we
+            // only have to check newSelStart (not newSelEnd) since if newSelEnd is wrong, then
+            // newSelStart will be wrong as well.
             mExpectedSelStart = lengthOfTextBeforeCursor;
             mExpectedSelEnd = lengthOfTextBeforeCursor;
         }
@@ -302,7 +304,7 @@
         // This never calls InputConnection#getCapsMode - in fact, it's a static method that
         // never blocks or initiates IPC.
         return CapsModeUtils.getCapsMode(mCommittedTextBeforeComposingText, inputType,
-                settingsValues, hasSpaceBefore);
+                settingsValues.mSpacingAndPunctuations, hasSpaceBefore);
     }
 
     public int getCodePointBeforeCursor() {
@@ -538,7 +540,8 @@
     }
 
     @SuppressWarnings("unused")
-    public String getNthPreviousWord(final SettingsValues currentSettingsValues, final int n) {
+    public String getNthPreviousWord(final SpacingAndPunctuations spacingAndPunctuations,
+            final int n) {
         mIC = mParent.getCurrentInputConnection();
         if (null == mIC) return null;
         final CharSequence prev = getTextBeforeCursor(LOOKBACK_CHARACTER_NUM, 0);
@@ -557,7 +560,7 @@
                 }
             }
         }
-        return getNthPreviousWord(prev, currentSettingsValues, n);
+        return getNthPreviousWord(prev, spacingAndPunctuations, n);
     }
 
     private static boolean isSeparator(int code, String sep) {
@@ -581,7 +584,7 @@
     // (n = 2) "abc |" -> null
     // (n = 2) "abc. def|" -> null
     public static String getNthPreviousWord(final CharSequence prev,
-            final SettingsValues currentSettingsValues, final int n) {
+            final SpacingAndPunctuations spacingAndPunctuations, final int n) {
         if (prev == null) return null;
         final String[] w = spaceRegex.split(prev);
 
@@ -593,8 +596,8 @@
 
         // If ends in a separator, return null
         final char lastChar = nthPrevWord.charAt(length - 1);
-        if (currentSettingsValues.isWordSeparator(lastChar)
-                || currentSettingsValues.isWordConnector(lastChar)) return null;
+        if (spacingAndPunctuations.isWordSeparator(lastChar)
+                || spacingAndPunctuations.isWordConnector(lastChar)) return null;
 
         return nthPrevWord;
     }
@@ -782,17 +785,17 @@
      */
     public boolean isBelatedExpectedUpdate(final int oldSelStart, final int newSelStart,
             final int oldSelEnd, final int newSelEnd) {
-        // This update is "belated" if we are expecting it.  That is, mExpectedSelStart and
+        // This update is "belated" if we are expecting it. That is, mExpectedSelStart and
         // mExpectedSelEnd match the new values that the TextView is updating TO.
         if (mExpectedSelStart == newSelStart && mExpectedSelEnd == newSelEnd) return true;
-        // This update is not belated if mExpectedSelStart and mExpeectedSelend match the old
-        // values, and one of newSelStart or newSelEnd is updated to a different value.  In this
-        // case, there is likely something other than the IME that has moved the selection endpoint
+        // This update is not belated if mExpectedSelStart and mExpectedSelEnd match the old
+        // values, and one of newSelStart or newSelEnd is updated to a different value. In this
+        // case, there is likely something other than the IME has moved the selection endpoint
         // to the new value.
         if (mExpectedSelStart == oldSelStart && mExpectedSelEnd == oldSelEnd
                 && (oldSelStart != newSelStart || oldSelEnd != newSelEnd)) return false;
         // If nether of the above two cases holds, then the system may be having trouble keeping up
-        // with updates.  If 1) the selection is a cursor, 2) newSelStart is between oldSelStart
+        // with updates. If 1) the selection is a cursor, 2) newSelStart is between oldSelStart
         // and mExpectedSelStart, and 3) newSelEnd is between oldSelEnd and mExpectedSelEnd, then
         // assume a belated update.
         return (newSelStart == newSelEnd)
@@ -810,4 +813,54 @@
     public boolean textBeforeCursorLooksLikeURL() {
         return StringUtils.lastPartLooksLikeURL(mCommittedTextBeforeComposingText);
     }
+
+    /**
+     * Try to get the text from the editor to expose lies the framework may have been
+     * telling us. Concretely, when the device rotates, the frameworks tells us about where the
+     * cursor used to be initially in the editor at the time it first received the focus; this
+     * may be completely different from the place it is upon rotation. Since we don't have any
+     * means to get the real value, try at least to ask the text view for some characters and
+     * detect the most damaging cases: when the cursor position is declared to be much smaller
+     * than it really is.
+     */
+    public void tryFixLyingCursorPosition() {
+        final CharSequence textBeforeCursor = getTextBeforeCursor(
+                Constants.EDITOR_CONTENTS_CACHE_SIZE, 0);
+        if (null == textBeforeCursor) {
+            mExpectedSelStart = mExpectedSelEnd = Constants.NOT_A_CURSOR_POSITION;
+        } else {
+            final int textLength = textBeforeCursor.length();
+            if (textLength > mExpectedSelStart
+                    || (textLength < Constants.EDITOR_CONTENTS_CACHE_SIZE
+                            && mExpectedSelStart < Constants.EDITOR_CONTENTS_CACHE_SIZE)) {
+                // It should not be possible to have only one of those variables be
+                // NOT_A_CURSOR_POSITION, so if they are equal, either the selection is zero-sized
+                // (simple cursor, no selection) or there is no cursor/we don't know its pos
+                final boolean wasEqual = mExpectedSelStart == mExpectedSelEnd;
+                mExpectedSelStart = textLength;
+                // We can't figure out the value of mLastSelectionEnd :(
+                // But at least if it's smaller than mLastSelectionStart something is wrong,
+                // and if they used to be equal we also don't want to make it look like there is a
+                // selection.
+                if (wasEqual || mExpectedSelStart > mExpectedSelEnd) {
+                    mExpectedSelEnd = mExpectedSelStart;
+                }
+            }
+        }
+    }
+
+    public int getExpectedSelectionStart() {
+        return mExpectedSelStart;
+    }
+
+    public int getExpectedSelectionEnd() {
+        return mExpectedSelEnd;
+    }
+
+    /**
+     * @return whether there is a selection currently active.
+     */
+    public boolean hasSelection() {
+        return mExpectedSelEnd != mExpectedSelStart;
+    }
 }
diff --git a/java/src/com/android/inputmethod/latin/inputlogic/InputLogic.java b/java/src/com/android/inputmethod/latin/inputlogic/InputLogic.java
index 968129a..de10a29 100644
--- a/java/src/com/android/inputmethod/latin/inputlogic/InputLogic.java
+++ b/java/src/com/android/inputmethod/latin/inputlogic/InputLogic.java
@@ -46,6 +46,7 @@
 import com.android.inputmethod.latin.define.ProductionFlag;
 import com.android.inputmethod.latin.settings.Settings;
 import com.android.inputmethod.latin.settings.SettingsValues;
+import com.android.inputmethod.latin.settings.SpacingAndPunctuations;
 import com.android.inputmethod.latin.suggestions.SuggestionStripView;
 import com.android.inputmethod.latin.utils.AsyncResultHolder;
 import com.android.inputmethod.latin.utils.CollectionUtils;
@@ -68,7 +69,8 @@
     // TODO : Remove this member when we can.
     private final LatinIME mLatinIME;
 
-    private InputLogicHandler mInputLogicHandler;
+    // Never null.
+    private InputLogicHandler mInputLogicHandler = InputLogicHandler.NULL_HANDLER;
 
     // TODO : make all these fields private as soon as possible.
     // Current space state of the input method. This can be any of the above constants.
@@ -104,7 +106,7 @@
         mWordComposer = new WordComposer();
         mEventInterpreter = new EventInterpreter(latinIME);
         mConnection = new RichInputConnection(latinIME);
-        mInputLogicHandler = null;
+        mInputLogicHandler = InputLogicHandler.NULL_HANDLER;
     }
 
     /**
@@ -131,6 +133,7 @@
         mLastSelectionEnd = editorInfo.initialSelEnd;
         // In some cases (namely, after rotation of the device) editorInfo.initialSelStart is lying
         // so we try using some heuristics to find out about these and fix them.
+        mConnection.tryFixLyingCursorPosition();
         tryFixLyingCursorPosition();
         mInputLogicHandler = new InputLogicHandler(mLatinIME, this);
     }
@@ -144,7 +147,7 @@
         }
         resetComposingState(true /* alsoResetLastComposedWord */);
         mInputLogicHandler.destroy();
-        mInputLogicHandler = null;
+        mInputLogicHandler = InputLogicHandler.NULL_HANDLER;
     }
 
     /**
@@ -369,7 +372,8 @@
         mWordComposer.setCapitalizedModeAndPreviousWordAtStartComposingTime(
                 getActualCapsMode(settingsValues, keyboardSwitcher.getKeyboardShiftMode()),
                 // Prev word is 1st word before cursor
-                getNthPreviousWordForSuggestion(settingsValues, 1 /* nthPreviousWord */));
+                getNthPreviousWordForSuggestion(
+                        settingsValues.mSpacingAndPunctuations, 1 /* nthPreviousWord */));
     }
 
     /* The sequence number member is only used in onUpdateBatchInput. It is increased each time
@@ -555,7 +559,8 @@
                 // yet, so the word we want is the 1st word before the cursor.
                 mWordComposer.setCapitalizedModeAndPreviousWordAtStartComposingTime(
                         getActualCapsMode(settingsValues, keyboardSwitcher.getKeyboardShiftMode()),
-                        getNthPreviousWordForSuggestion(settingsValues, 1 /* nthPreviousWord */));
+                        getNthPreviousWordForSuggestion(
+                                settingsValues.mSpacingAndPunctuations, 1 /* nthPreviousWord */));
             }
             mConnection.setComposingText(getTextWithUnderline(
                     mWordComposer.getTypedWord()), 1);
@@ -1107,7 +1112,7 @@
             }
         }
         mWordComposer.setComposingWord(typedWord,
-                getNthPreviousWordForSuggestion(settingsValues,
+                getNthPreviousWordForSuggestion(settingsValues.mSpacingAndPunctuations,
                         // We want the previous word for suggestion. If we have chars in the word
                         // before the cursor, then we want the word before that, hence 2; otherwise,
                         // we want the word immediately before the cursor, hence 1.
@@ -1301,17 +1306,17 @@
 
     /**
      * Get the nth previous word before the cursor as context for the suggestion process.
-     * @param currentSettings the current settings values.
+     * @param spacingAndPunctuations the current spacing and punctuations settings.
      * @param nthPreviousWord reverse index of the word to get (1-indexed)
      * @return the nth previous word before the cursor.
      */
     // TODO: Make this private
-    public String getNthPreviousWordForSuggestion(final SettingsValues currentSettings,
-            final int nthPreviousWord) {
-        if (currentSettings.mSpacingAndPunctuations.mCurrentLanguageHasSpaces) {
+    public String getNthPreviousWordForSuggestion(
+            final SpacingAndPunctuations spacingAndPunctuations, final int nthPreviousWord) {
+        if (spacingAndPunctuations.mCurrentLanguageHasSpaces) {
             // If we are typing in a language with spaces we can just look up the previous
             // word from textview.
-            return mConnection.getNthPreviousWord(currentSettings, nthPreviousWord);
+            return mConnection.getNthPreviousWord(spacingAndPunctuations, nthPreviousWord);
         } else {
             return LastComposedWord.NOT_A_COMPOSED_WORD == mLastComposedWord ? null
                     : mLastComposedWord.mCommittedWord;
@@ -1659,7 +1664,8 @@
         mConnection.commitText(SuggestionSpanUtils.getTextWithSuggestionSpan(mLatinIME, chosenWord,
                 suggestedWords), 1);
         // TODO: we pass 2 here, but would it be better to move this above and pass 1 instead?
-        final String prevWord = mConnection.getNthPreviousWord(settingsValues, 2);
+        final String prevWord = mConnection.getNthPreviousWord(
+                settingsValues.mSpacingAndPunctuations, 2);
         // Add the word to the user history dictionary
         performAdditionToUserHistoryDictionary(settingsValues, chosenWord, prevWord);
         // TODO: figure out here if this is an auto-correct or if the best word is actually
@@ -1745,6 +1751,7 @@
             // If remainingTries is 0, we should stop waiting for new tries, but it's still
             // better to load the keyboard (less things will be broken).
         }
+        mConnection.tryFixLyingCursorPosition();
         tryFixLyingCursorPosition();
         keyboardSwitcher.loadKeyboard(getCurrentInputEditorInfo(), settingsValues);
         if (tryResumeSuggestions) {
diff --git a/java/src/com/android/inputmethod/latin/inputlogic/InputLogicHandler.java b/java/src/com/android/inputmethod/latin/inputlogic/InputLogicHandler.java
index 3258dcd..ea010b6 100644
--- a/java/src/com/android/inputmethod/latin/inputlogic/InputLogicHandler.java
+++ b/java/src/com/android/inputmethod/latin/inputlogic/InputLogicHandler.java
@@ -40,6 +40,33 @@
 
     private static final int MSG_GET_SUGGESTED_WORDS = 1;
 
+    // A handler that never does anything. This is used for cases where events come before anything
+    // is initialized, though probably only the monkey can actually do this.
+    public static final InputLogicHandler NULL_HANDLER = new InputLogicHandler() {
+        @Override
+        public void destroy() {}
+        @Override
+        public boolean handleMessage(final Message msg) { return true; }
+        @Override
+        public void onStartBatchInput() {}
+        @Override
+        public void onUpdateBatchInput(final InputPointers batchPointers,
+                final int sequenceNumber) {}
+        @Override
+        public void onCancelBatchInput() {}
+        @Override
+        public void onEndBatchInput(final InputPointers batchPointers, final int sequenceNumber) {}
+        @Override
+        public void getSuggestedWords(final int sessionId, final int sequenceNumber,
+                final OnGetSuggestedWordsCallback callback) {}
+    };
+
+    private InputLogicHandler() {
+        mNonUIThreadHandler = null;
+        mLatinIME = null;
+        mInputLogic = null;
+    }
+
     public InputLogicHandler(final LatinIME latinIME, final InputLogic inputLogic) {
         final HandlerThread handlerThread = new HandlerThread(
                 InputLogicHandler.class.getSimpleName());
diff --git a/java/src/com/android/inputmethod/latin/settings/Settings.java b/java/src/com/android/inputmethod/latin/settings/Settings.java
index 7fb5183..323e612 100644
--- a/java/src/com/android/inputmethod/latin/settings/Settings.java
+++ b/java/src/com/android/inputmethod/latin/settings/Settings.java
@@ -159,7 +159,7 @@
             final RunInLocale<SettingsValues> job = new RunInLocale<SettingsValues>() {
                 @Override
                 protected SettingsValues job(final Resources res) {
-                    return new SettingsValues(context, prefs, locale, res, inputAttributes);
+                    return new SettingsValues(context, prefs, res, inputAttributes);
                 }
             };
             mSettingsValues = job.runInLocale(mRes, locale);
diff --git a/java/src/com/android/inputmethod/latin/settings/SettingsValues.java b/java/src/com/android/inputmethod/latin/settings/SettingsValues.java
index 3fa686b..e4ae64f 100644
--- a/java/src/com/android/inputmethod/latin/settings/SettingsValues.java
+++ b/java/src/com/android/inputmethod/latin/settings/SettingsValues.java
@@ -24,7 +24,6 @@
 import android.util.Log;
 import android.view.inputmethod.EditorInfo;
 
-import com.android.inputmethod.annotations.UsedForTesting;
 import com.android.inputmethod.compat.AppWorkaroundsUtils;
 import com.android.inputmethod.latin.InputAttributes;
 import com.android.inputmethod.latin.R;
@@ -96,9 +95,9 @@
     // Debug settings
     public final boolean mIsInternal;
 
-    public SettingsValues(final Context context, final SharedPreferences prefs, final Locale locale,
-            final Resources res, final InputAttributes inputAttributes) {
-        mLocale = locale;
+    public SettingsValues(final Context context, final SharedPreferences prefs, final Resources res,
+            final InputAttributes inputAttributes) {
+        mLocale = res.getConfiguration().locale;
         // Get the resources
         mDelayUpdateOldSuggestions = res.getInteger(R.integer.config_delay_update_old_suggestions);
         mSpacingAndPunctuations = new SpacingAndPunctuations(res);
@@ -166,51 +165,6 @@
         }
     }
 
-    // Only for tests
-    private SettingsValues(final Locale locale) {
-        // TODO: locale is saved, but not used yet. May have to change this if tests require.
-        mLocale = locale;
-        mDelayUpdateOldSuggestions = 0;
-        mSpacingAndPunctuations = SpacingAndPunctuations.DEFAULT;
-        mHintToSaveText = "Touch again to save";
-        mInputAttributes = new InputAttributes(null, false /* isFullscreenMode */);
-        mAutoCap = true;
-        mVibrateOn = true;
-        mSoundOn = true;
-        mKeyPreviewPopupOn = true;
-        mSlidingKeyInputPreviewEnabled = true;
-        mShowsVoiceInputKey = true;
-        mIncludesOtherImesInLanguageSwitchList = false;
-        mShowsLanguageSwitchKey = true;
-        mUseContactsDict = true;
-        mUsePersonalizedDicts = true;
-        mUseDoubleSpacePeriod = true;
-        mBlockPotentiallyOffensive = true;
-        mAutoCorrectEnabled = true;
-        mBigramPredictionEnabled = true;
-        mKeyLongpressTimeout = 300;
-        mKeypressVibrationDuration = 5;
-        mKeypressSoundVolume = 1;
-        mKeyPreviewPopupDismissDelay = 70;
-        mAutoCorrectionThreshold = 1;
-        mGestureInputEnabled = true;
-        mGestureTrailEnabled = true;
-        mGestureFloatingPreviewTextEnabled = true;
-        mPhraseGestureEnabled = true;
-        mCorrectionEnabled = mAutoCorrectEnabled && !mInputAttributes.mInputTypeNoAutoCorrect;
-        mSuggestionVisibility = 0;
-        mIsInternal = false;
-        mUseOnlyPersonalizationDictionaryForDebug = false;
-        mDisplayOrientation = Configuration.ORIENTATION_PORTRAIT;
-        mAppWorkarounds = new AsyncResultHolder<AppWorkaroundsUtils>();
-        mAppWorkarounds.set(null);
-    }
-
-    @UsedForTesting
-    public static SettingsValues makeDummySettingsValuesForTest(final Locale locale) {
-        return new SettingsValues(locale);
-    }
-
     public boolean isApplicationSpecifiedCompletionsOn() {
         return mInputAttributes.mApplicationSpecifiedCompletionOn;
     }
diff --git a/java/src/com/android/inputmethod/latin/settings/SpacingAndPunctuations.java b/java/src/com/android/inputmethod/latin/settings/SpacingAndPunctuations.java
index 0500f4a..dbe30e2 100644
--- a/java/src/com/android/inputmethod/latin/settings/SpacingAndPunctuations.java
+++ b/java/src/com/android/inputmethod/latin/settings/SpacingAndPunctuations.java
@@ -29,6 +29,7 @@
 
 import java.util.ArrayList;
 import java.util.Arrays;
+import java.util.Locale;
 
 public final class SpacingAndPunctuations {
     private final int[] mSymbolsPrecededBySpace;
@@ -39,23 +40,7 @@
     private final int mSentenceSeparator;
     public final String mSentenceSeparatorAndSpace;
     public final boolean mCurrentLanguageHasSpaces;
-
-    public static final SpacingAndPunctuations DEFAULT = new SpacingAndPunctuations();
-
-    private SpacingAndPunctuations() {
-        mSymbolsPrecededBySpace = new int[] { '(', '[', '{', '&' };
-        Arrays.sort(mSymbolsPrecededBySpace);
-        mSymbolsFollowedBySpace = new int[] { '.', ',', ';', ':', '!', '?', ')', ']', '}', '&' };
-        Arrays.sort(mSymbolsFollowedBySpace);
-        mWordConnectors = new int[] { '\'', '-' };
-        Arrays.sort(mWordConnectors);
-        mSentenceSeparator = Constants.CODE_PERIOD;
-        mSentenceSeparatorAndSpace = ". ";
-        final String[] suggestPuncsSpec = new String[] { "!", "?", ",", ":", ";" };
-        mSuggestPuncList = createSuggestPuncList(suggestPuncsSpec);
-        mWordSeparators = "&\t \n()[]{}*&<>+=|.,;:!?/_\"";
-        mCurrentLanguageHasSpaces = true;
-    }
+    public final boolean mUsesAmericanTypography;
 
     public SpacingAndPunctuations(final Resources res) {
         mSymbolsPrecededBySpace =
@@ -75,6 +60,10 @@
         mSentenceSeparatorAndSpace = new String(new int[] {
                 mSentenceSeparator, Constants.CODE_SPACE }, 0, 2);
         mCurrentLanguageHasSpaces = res.getBoolean(R.bool.current_language_has_spaces);
+        final Locale locale = res.getConfiguration().locale;
+        // Heuristic: we use American Typography rules because it's the most common rules for all
+        // English variants.
+        mUsesAmericanTypography = Locale.ENGLISH.getLanguage().equals(locale.getLanguage());
     }
 
     // Helper functions to create member values.
diff --git a/java/src/com/android/inputmethod/latin/utils/CapsModeUtils.java b/java/src/com/android/inputmethod/latin/utils/CapsModeUtils.java
index 6ad5c77..057e332 100644
--- a/java/src/com/android/inputmethod/latin/utils/CapsModeUtils.java
+++ b/java/src/com/android/inputmethod/latin/utils/CapsModeUtils.java
@@ -21,7 +21,7 @@
 
 import com.android.inputmethod.latin.Constants;
 import com.android.inputmethod.latin.WordComposer;
-import com.android.inputmethod.latin.settings.SettingsValues;
+import com.android.inputmethod.latin.settings.SpacingAndPunctuations;
 
 import java.util.Locale;
 
@@ -74,7 +74,7 @@
      * @param reqModes The modes to be checked: may be any combination of
      * {@link TextUtils#CAP_MODE_CHARACTERS}, {@link TextUtils#CAP_MODE_WORDS}, and
      * {@link TextUtils#CAP_MODE_SENTENCES}.
-     * @param settingsValues The current settings values.
+     * @param spacingAndPunctuations The current spacing and punctuations settings.
      * @param hasSpaceBefore Whether we should consider there is a space inserted at the end of cs
      *
      * @return Returns the actual capitalization modes that can be in effect
@@ -83,7 +83,7 @@
      * {@link TextUtils#CAP_MODE_SENTENCES}.
      */
     public static int getCapsMode(final CharSequence cs, final int reqModes,
-            final SettingsValues settingsValues, final boolean hasSpaceBefore) {
+            final SpacingAndPunctuations spacingAndPunctuations, final boolean hasSpaceBefore) {
         // Quick description of what we want to do:
         // CAP_MODE_CHARACTERS is always on.
         // CAP_MODE_WORDS is on if there is some whitespace before the cursor.
@@ -167,8 +167,7 @@
         // No other language has such a rule as far as I know, instead putting inside the quotation
         // mark as the exact thing quoted and handling the surrounding punctuation independently,
         // e.g. <<Did he say, "let's go home"?>>
-        // Hence, specifically for English, we treat this special case here.
-        if (Locale.ENGLISH.getLanguage().equals(settingsValues.mLocale.getLanguage())) {
+        if (spacingAndPunctuations.mUsesAmericanTypography) {
             for (; j > 0; j--) {
                 // Here we look to go over any closing punctuation. This is because in dominant
                 // variants of English, the final period is placed within double quotes and maybe
@@ -191,7 +190,7 @@
         if (c == Constants.CODE_QUESTION_MARK || c == Constants.CODE_EXCLAMATION_MARK) {
             return (TextUtils.CAP_MODE_CHARACTERS | TextUtils.CAP_MODE_SENTENCES) & reqModes;
         }
-        if (!settingsValues.mSpacingAndPunctuations.isSentenceSeparator(c) || j <= 0) {
+        if (!spacingAndPunctuations.isSentenceSeparator(c) || j <= 0) {
             return (TextUtils.CAP_MODE_CHARACTERS | TextUtils.CAP_MODE_WORDS) & reqModes;
         }
 
@@ -241,7 +240,7 @@
             case WORD:
                 if (Character.isLetter(c)) {
                     state = WORD;
-                } else if (settingsValues.mSpacingAndPunctuations.isSentenceSeparator(c)) {
+                } else if (spacingAndPunctuations.isSentenceSeparator(c)) {
                     state = PERIOD;
                 } else {
                     return caps;
@@ -257,7 +256,7 @@
             case LETTER:
                 if (Character.isLetter(c)) {
                     state = LETTER;
-                } else if (settingsValues.mSpacingAndPunctuations.isSentenceSeparator(c)) {
+                } else if (spacingAndPunctuations.isSentenceSeparator(c)) {
                     state = PERIOD;
                 } else {
                     return noCaps;
diff --git a/java/src/com/android/inputmethod/latin/utils/DictionaryInfoUtils.java b/java/src/com/android/inputmethod/latin/utils/DictionaryInfoUtils.java
index 3daa63f..b3c787e 100644
--- a/java/src/com/android/inputmethod/latin/utils/DictionaryInfoUtils.java
+++ b/java/src/com/android/inputmethod/latin/utils/DictionaryInfoUtils.java
@@ -20,13 +20,16 @@
 import android.content.Context;
 import android.content.res.AssetManager;
 import android.content.res.Resources;
+import android.text.TextUtils;
 import android.util.Log;
 
+import com.android.inputmethod.annotations.UsedForTesting;
 import com.android.inputmethod.latin.AssetFileAddress;
 import com.android.inputmethod.latin.BinaryDictionaryGetter;
 import com.android.inputmethod.latin.R;
 import com.android.inputmethod.latin.makedict.BinaryDictIOUtils;
 import com.android.inputmethod.latin.makedict.FormatSpec.FileHeader;
+import com.android.inputmethod.latin.settings.SpacingAndPunctuations;
 
 import java.io.File;
 import java.util.ArrayList;
@@ -364,4 +367,29 @@
 
         return dictList;
     }
+
+    @UsedForTesting
+    public static boolean looksValidForDictionaryInsertion(final CharSequence text,
+            final SpacingAndPunctuations spacingAndPunctuations) {
+        if (TextUtils.isEmpty(text)) return false;
+        final int length = text.length();
+        int i = 0;
+        int digitCount = 0;
+        while (i < length) {
+            final int codePoint = Character.codePointAt(text, i);
+            final int charCount = Character.charCount(codePoint);
+            i += charCount;
+            if (Character.isDigit(codePoint)) {
+                // Count digits: see below
+                digitCount += charCount;
+                continue;
+            }
+            if (!spacingAndPunctuations.isWordCodePoint(codePoint)) return false;
+        }
+        // We reject strings entirely comprised of digits to avoid using PIN codes or credit
+        // card numbers. It would come in handy for word prediction though; a good example is
+        // when writing one's address where the street number is usually quite discriminative,
+        // as well as the postal code.
+        return digitCount < length;
+    }
 }
diff --git a/java/src/com/android/inputmethod/latin/utils/StringUtils.java b/java/src/com/android/inputmethod/latin/utils/StringUtils.java
index 85f4454..c5ed393 100644
--- a/java/src/com/android/inputmethod/latin/utils/StringUtils.java
+++ b/java/src/com/android/inputmethod/latin/utils/StringUtils.java
@@ -17,15 +17,11 @@
 package com.android.inputmethod.latin.utils;
 
 import android.text.TextUtils;
-import android.util.Log;
 
 import com.android.inputmethod.annotations.UsedForTesting;
 import com.android.inputmethod.latin.Constants;
-import com.android.inputmethod.latin.settings.SettingsValues;
 
-import java.io.IOException;
 import java.util.ArrayList;
-import java.util.List;
 import java.util.Locale;
 
 public final class StringUtils {
@@ -268,31 +264,6 @@
         return true;
     }
 
-    @UsedForTesting
-    public static boolean looksValidForDictionaryInsertion(final CharSequence text,
-            final SettingsValues settings) {
-        if (TextUtils.isEmpty(text)) return false;
-        final int length = text.length();
-        int i = 0;
-        int digitCount = 0;
-        while (i < length) {
-            final int codePoint = Character.codePointAt(text, i);
-            final int charCount = Character.charCount(codePoint);
-            i += charCount;
-            if (Character.isDigit(codePoint)) {
-                // Count digits: see below
-                digitCount += charCount;
-                continue;
-            }
-            if (!settings.isWordCodePoint(codePoint)) return false;
-        }
-        // We reject strings entirely comprised of digits to avoid using PIN codes or credit
-        // card numbers. It would come in handy for word prediction though; a good example is
-        // when writing one's address where the street number is usually quite discriminative,
-        // as well as the postal code.
-        return digitCount < length;
-    }
-
     public static boolean isIdenticalAfterCapitalizeEachWord(final String text,
             final String separators) {
         boolean needCapsNext = true;
diff --git a/tests/src/com/android/inputmethod/latin/InputLogicTests.java b/tests/src/com/android/inputmethod/latin/InputLogicTests.java
index 039f581..1a249ce 100644
--- a/tests/src/com/android/inputmethod/latin/InputLogicTests.java
+++ b/tests/src/com/android/inputmethod/latin/InputLogicTests.java
@@ -368,8 +368,8 @@
         // Choose the auto-correction, which is always in position 0. For "Barack", the
         // auto-correction should be "Barack".
         pickSuggestionManually(0, WORD_TO_TYPE);
-        runMessages();
         sleep(DELAY_TO_WAIT_FOR_PREDICTIONS);
+        runMessages();
         // Test the first prediction is displayed
         final SuggestedWords suggestedWords = mLatinIME.getSuggestedWords();
         assertEquals("predictions after manual pick", "Obama",
diff --git a/tests/src/com/android/inputmethod/latin/RichInputConnectionAndTextRangeTests.java b/tests/src/com/android/inputmethod/latin/RichInputConnectionAndTextRangeTests.java
index 6ad1250..f19d185 100644
--- a/tests/src/com/android/inputmethod/latin/RichInputConnectionAndTextRangeTests.java
+++ b/tests/src/com/android/inputmethod/latin/RichInputConnectionAndTextRangeTests.java
@@ -16,16 +16,13 @@
 
 package com.android.inputmethod.latin;
 
-import com.android.inputmethod.latin.settings.SettingsValues;
-import com.android.inputmethod.latin.utils.TextRange;
-
+import android.content.res.Resources;
 import android.inputmethodservice.InputMethodService;
 import android.os.Parcel;
 import android.test.AndroidTestCase;
 import android.test.MoreAsserts;
 import android.test.suitebuilder.annotation.SmallTest;
 import android.text.SpannableString;
-import android.text.Spanned;
 import android.text.TextUtils;
 import android.text.style.SuggestionSpan;
 import android.view.inputmethod.ExtractedText;
@@ -33,6 +30,10 @@
 import android.view.inputmethod.InputConnection;
 import android.view.inputmethod.InputConnectionWrapper;
 
+import com.android.inputmethod.latin.settings.SpacingAndPunctuations;
+import com.android.inputmethod.latin.utils.RunInLocale;
+import com.android.inputmethod.latin.utils.TextRange;
+
 import java.util.Locale;
 
 @SmallTest
@@ -40,12 +41,19 @@
 
     // The following is meant to be a reasonable default for
     // the "word_separators" resource.
-    private static final SettingsValues sSettings =
-            SettingsValues.makeDummySettingsValuesForTest(Locale.ENGLISH);
+    private SpacingAndPunctuations mSpacingAndPunctuations;
 
     @Override
     protected void setUp() throws Exception {
         super.setUp();
+        final RunInLocale<SpacingAndPunctuations> job = new RunInLocale<SpacingAndPunctuations>() {
+            @Override
+            protected SpacingAndPunctuations job(final Resources res) {
+                return new SpacingAndPunctuations(res);
+            }
+        };
+        final Resources res = getContext().getResources();
+        mSpacingAndPunctuations = job.runInLocale(res, Locale.ENGLISH);
     }
 
     private class MockConnection extends InputConnectionWrapper {
@@ -139,9 +147,12 @@
      */
     public void testGetPreviousWord() {
         // If one of the following cases breaks, the bigram suggestions won't work.
-        assertEquals(RichInputConnection.getNthPreviousWord("abc def", sSettings, 2), "abc");
-        assertNull(RichInputConnection.getNthPreviousWord("abc", sSettings, 2));
-        assertNull(RichInputConnection.getNthPreviousWord("abc. def", sSettings, 2));
+        assertEquals(RichInputConnection.getNthPreviousWord(
+                "abc def", mSpacingAndPunctuations, 2), "abc");
+        assertNull(RichInputConnection.getNthPreviousWord(
+                "abc", mSpacingAndPunctuations, 2));
+        assertNull(RichInputConnection.getNthPreviousWord(
+                "abc. def", mSpacingAndPunctuations, 2));
 
         // The following tests reflect the current behavior of the function
         // RichInputConnection#getNthPreviousWord.
@@ -150,15 +161,23 @@
         // this function if needed - especially since it does not seem very
         // logical. These tests are just there to catch any unintentional
         // changes in the behavior of the RichInputConnection#getPreviousWord method.
-        assertEquals(RichInputConnection.getNthPreviousWord("abc def ", sSettings, 2), "abc");
-        assertEquals(RichInputConnection.getNthPreviousWord("abc def.", sSettings, 2), "abc");
-        assertEquals(RichInputConnection.getNthPreviousWord("abc def .", sSettings, 2), "def");
-        assertNull(RichInputConnection.getNthPreviousWord("abc ", sSettings, 2));
+        assertEquals(RichInputConnection.getNthPreviousWord(
+                "abc def ", mSpacingAndPunctuations, 2), "abc");
+        assertEquals(RichInputConnection.getNthPreviousWord(
+                "abc def.", mSpacingAndPunctuations, 2), "abc");
+        assertEquals(RichInputConnection.getNthPreviousWord(
+                "abc def .", mSpacingAndPunctuations, 2), "def");
+        assertNull(RichInputConnection.getNthPreviousWord(
+                "abc ", mSpacingAndPunctuations, 2));
 
-        assertEquals(RichInputConnection.getNthPreviousWord("abc def", sSettings, 1), "def");
-        assertEquals(RichInputConnection.getNthPreviousWord("abc def ", sSettings, 1), "def");
-        assertNull(RichInputConnection.getNthPreviousWord("abc def.", sSettings, 1));
-        assertNull(RichInputConnection.getNthPreviousWord("abc def .", sSettings, 1));
+        assertEquals(RichInputConnection.getNthPreviousWord(
+                "abc def", mSpacingAndPunctuations, 1), "def");
+        assertEquals(RichInputConnection.getNthPreviousWord(
+                "abc def ", mSpacingAndPunctuations, 1), "def");
+        assertNull(RichInputConnection.getNthPreviousWord(
+                "abc def.", mSpacingAndPunctuations, 1));
+        assertNull(RichInputConnection.getNthPreviousWord(
+                "abc def .", mSpacingAndPunctuations, 1));
     }
 
     /**
diff --git a/tests/src/com/android/inputmethod/latin/utils/CapsModeUtilsTests.java b/tests/src/com/android/inputmethod/latin/utils/CapsModeUtilsTests.java
index 1fd5c98..40a103b 100644
--- a/tests/src/com/android/inputmethod/latin/utils/CapsModeUtilsTests.java
+++ b/tests/src/com/android/inputmethod/latin/utils/CapsModeUtilsTests.java
@@ -16,75 +16,83 @@
 
 package com.android.inputmethod.latin.utils;
 
-import com.android.inputmethod.latin.settings.SettingsValues;
-
+import android.content.res.Resources;
 import android.test.AndroidTestCase;
 import android.test.suitebuilder.annotation.SmallTest;
 import android.text.TextUtils;
 
+import com.android.inputmethod.latin.settings.SpacingAndPunctuations;
+
 import java.util.Locale;
 
 @SmallTest
 public class CapsModeUtilsTests extends AndroidTestCase {
     private static void onePathForCaps(final CharSequence cs, final int expectedResult,
-            final int mask, final SettingsValues sv, final boolean hasSpaceBefore) {
-        int oneTimeResult = expectedResult & mask;
+            final int mask, final SpacingAndPunctuations sp, final boolean hasSpaceBefore) {
+        final int oneTimeResult = expectedResult & mask;
         assertEquals("After >" + cs + "<", oneTimeResult,
-                CapsModeUtils.getCapsMode(cs, mask, sv, hasSpaceBefore));
+                CapsModeUtils.getCapsMode(cs, mask, sp, hasSpaceBefore));
     }
 
     private static void allPathsForCaps(final CharSequence cs, final int expectedResult,
-            final SettingsValues sv, final boolean hasSpaceBefore) {
+            final SpacingAndPunctuations sp, final boolean hasSpaceBefore) {
         final int c = TextUtils.CAP_MODE_CHARACTERS;
         final int w = TextUtils.CAP_MODE_WORDS;
         final int s = TextUtils.CAP_MODE_SENTENCES;
-        onePathForCaps(cs, expectedResult, c | w | s, sv, hasSpaceBefore);
-        onePathForCaps(cs, expectedResult, w | s, sv, hasSpaceBefore);
-        onePathForCaps(cs, expectedResult, c | s, sv, hasSpaceBefore);
-        onePathForCaps(cs, expectedResult, c | w, sv, hasSpaceBefore);
-        onePathForCaps(cs, expectedResult, c, sv, hasSpaceBefore);
-        onePathForCaps(cs, expectedResult, w, sv, hasSpaceBefore);
-        onePathForCaps(cs, expectedResult, s, sv, hasSpaceBefore);
+        onePathForCaps(cs, expectedResult, c | w | s, sp, hasSpaceBefore);
+        onePathForCaps(cs, expectedResult, w | s, sp, hasSpaceBefore);
+        onePathForCaps(cs, expectedResult, c | s, sp, hasSpaceBefore);
+        onePathForCaps(cs, expectedResult, c | w, sp, hasSpaceBefore);
+        onePathForCaps(cs, expectedResult, c, sp, hasSpaceBefore);
+        onePathForCaps(cs, expectedResult, w, sp, hasSpaceBefore);
+        onePathForCaps(cs, expectedResult, s, sp, hasSpaceBefore);
     }
 
     public void testGetCapsMode() {
         final int c = TextUtils.CAP_MODE_CHARACTERS;
         final int w = TextUtils.CAP_MODE_WORDS;
         final int s = TextUtils.CAP_MODE_SENTENCES;
-        SettingsValues sv = SettingsValues.makeDummySettingsValuesForTest(Locale.ENGLISH);
-        allPathsForCaps("", c | w | s, sv, false);
-        allPathsForCaps("Word", c, sv, false);
-        allPathsForCaps("Word.", c, sv, false);
-        allPathsForCaps("Word ", c | w, sv, false);
-        allPathsForCaps("Word. ", c | w | s, sv, false);
-        allPathsForCaps("Word..", c, sv, false);
-        allPathsForCaps("Word.. ", c | w | s, sv, false);
-        allPathsForCaps("Word... ", c | w | s, sv, false);
-        allPathsForCaps("Word ... ", c | w | s, sv, false);
-        allPathsForCaps("Word . ", c | w, sv, false);
-        allPathsForCaps("In the U.S ", c | w, sv, false);
-        allPathsForCaps("In the U.S. ", c | w, sv, false);
-        allPathsForCaps("Some stuff (e.g. ", c | w, sv, false);
-        allPathsForCaps("In the U.S.. ", c | w | s, sv, false);
-        allPathsForCaps("\"Word.\" ", c | w | s, sv, false);
-        allPathsForCaps("\"Word\". ", c | w | s, sv, false);
-        allPathsForCaps("\"Word\" ", c | w, sv, false);
+        final RunInLocale<SpacingAndPunctuations> job = new RunInLocale<SpacingAndPunctuations>() {
+            @Override
+            protected SpacingAndPunctuations job(final Resources res) {
+                return new SpacingAndPunctuations(res);
+            }
+        };
+        final Resources res = getContext().getResources();
+        SpacingAndPunctuations sp = job.runInLocale(res, Locale.ENGLISH);
+        allPathsForCaps("", c | w | s, sp, false);
+        allPathsForCaps("Word", c, sp, false);
+        allPathsForCaps("Word.", c, sp, false);
+        allPathsForCaps("Word ", c | w, sp, false);
+        allPathsForCaps("Word. ", c | w | s, sp, false);
+        allPathsForCaps("Word..", c, sp, false);
+        allPathsForCaps("Word.. ", c | w | s, sp, false);
+        allPathsForCaps("Word... ", c | w | s, sp, false);
+        allPathsForCaps("Word ... ", c | w | s, sp, false);
+        allPathsForCaps("Word . ", c | w, sp, false);
+        allPathsForCaps("In the U.S ", c | w, sp, false);
+        allPathsForCaps("In the U.S. ", c | w, sp, false);
+        allPathsForCaps("Some stuff (e.g. ", c | w, sp, false);
+        allPathsForCaps("In the U.S.. ", c | w | s, sp, false);
+        allPathsForCaps("\"Word.\" ", c | w | s, sp, false);
+        allPathsForCaps("\"Word\". ", c | w | s, sp, false);
+        allPathsForCaps("\"Word\" ", c | w, sp, false);
 
         // Test for phantom space
-        allPathsForCaps("Word", c | w, sv, true);
-        allPathsForCaps("Word.", c | w | s, sv, true);
+        allPathsForCaps("Word", c | w, sp, true);
+        allPathsForCaps("Word.", c | w | s, sp, true);
 
         // Tests after some whitespace
-        allPathsForCaps("Word\n", c | w | s, sv, false);
-        allPathsForCaps("Word\n", c | w | s, sv, true);
-        allPathsForCaps("Word\n ", c | w | s, sv, true);
-        allPathsForCaps("Word.\n", c | w | s, sv, false);
-        allPathsForCaps("Word.\n", c | w | s, sv, true);
-        allPathsForCaps("Word.\n ", c | w | s, sv, true);
+        allPathsForCaps("Word\n", c | w | s, sp, false);
+        allPathsForCaps("Word\n", c | w | s, sp, true);
+        allPathsForCaps("Word\n ", c | w | s, sp, true);
+        allPathsForCaps("Word.\n", c | w | s, sp, false);
+        allPathsForCaps("Word.\n", c | w | s, sp, true);
+        allPathsForCaps("Word.\n ", c | w | s, sp, true);
 
-        sv = SettingsValues.makeDummySettingsValuesForTest(Locale.FRENCH);
-        allPathsForCaps("\"Word.\" ", c | w, sv, false);
-        allPathsForCaps("\"Word\". ", c | w | s, sv, false);
-        allPathsForCaps("\"Word\" ", c | w, sv, false);
+        sp = job.runInLocale(res, Locale.FRENCH);
+        allPathsForCaps("\"Word.\" ", c | w, sp, false);
+        allPathsForCaps("\"Word\". ", c | w | s, sp, false);
+        allPathsForCaps("\"Word\" ", c | w, sp, false);
     }
 }
diff --git a/tests/src/com/android/inputmethod/latin/utils/DictionaryInfoUtilsTests.java b/tests/src/com/android/inputmethod/latin/utils/DictionaryInfoUtilsTests.java
new file mode 100644
index 0000000..6e71607
--- /dev/null
+++ b/tests/src/com/android/inputmethod/latin/utils/DictionaryInfoUtilsTests.java
@@ -0,0 +1,47 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.inputmethod.latin.utils;
+
+import android.content.res.Resources;
+import android.test.AndroidTestCase;
+import android.test.suitebuilder.annotation.SmallTest;
+
+import com.android.inputmethod.latin.settings.SpacingAndPunctuations;
+
+import java.util.Locale;
+
+@SmallTest
+public class DictionaryInfoUtilsTests extends AndroidTestCase {
+    public void testLooksValidForDictionaryInsertion() {
+        final RunInLocale<SpacingAndPunctuations> job = new RunInLocale<SpacingAndPunctuations>() {
+            @Override
+            protected SpacingAndPunctuations job(final Resources res) {
+                return new SpacingAndPunctuations(res);
+            }
+        };
+        final Resources res = getContext().getResources();
+        final SpacingAndPunctuations sp = job.runInLocale(res, Locale.ENGLISH);
+        assertTrue(DictionaryInfoUtils.looksValidForDictionaryInsertion("aochaueo", sp));
+        assertFalse(DictionaryInfoUtils.looksValidForDictionaryInsertion("", sp));
+        assertTrue(DictionaryInfoUtils.looksValidForDictionaryInsertion("ao-ch'aueo", sp));
+        assertFalse(DictionaryInfoUtils.looksValidForDictionaryInsertion("2908743256", sp));
+        assertTrue(DictionaryInfoUtils.looksValidForDictionaryInsertion("31aochaueo", sp));
+        assertFalse(DictionaryInfoUtils.looksValidForDictionaryInsertion("akeo  raeoch oerch .",
+                sp));
+        assertFalse(DictionaryInfoUtils.looksValidForDictionaryInsertion("!!!", sp));
+    }
+}
diff --git a/tests/src/com/android/inputmethod/latin/utils/StringAndJsonUtilsTests.java b/tests/src/com/android/inputmethod/latin/utils/StringAndJsonUtilsTests.java
index 0c88f34..7196612 100644
--- a/tests/src/com/android/inputmethod/latin/utils/StringAndJsonUtilsTests.java
+++ b/tests/src/com/android/inputmethod/latin/utils/StringAndJsonUtilsTests.java
@@ -16,8 +16,6 @@
 
 package com.android.inputmethod.latin.utils;
 
-import com.android.inputmethod.latin.settings.SettingsValues;
-
 import android.test.AndroidTestCase;
 import android.test.suitebuilder.annotation.SmallTest;
 
@@ -208,18 +206,6 @@
         assertTrue(StringUtils.isIdenticalAfterDowncase(""));
     }
 
-    public void testLooksValidForDictionaryInsertion() {
-        final SettingsValues settings =
-                SettingsValues.makeDummySettingsValuesForTest(Locale.ENGLISH);
-        assertTrue(StringUtils.looksValidForDictionaryInsertion("aochaueo", settings));
-        assertFalse(StringUtils.looksValidForDictionaryInsertion("", settings));
-        assertTrue(StringUtils.looksValidForDictionaryInsertion("ao-ch'aueo", settings));
-        assertFalse(StringUtils.looksValidForDictionaryInsertion("2908743256", settings));
-        assertTrue(StringUtils.looksValidForDictionaryInsertion("31aochaueo", settings));
-        assertFalse(StringUtils.looksValidForDictionaryInsertion("akeo  raeoch oerch .", settings));
-        assertFalse(StringUtils.looksValidForDictionaryInsertion("!!!", settings));
-    }
-
     private static void checkCapitalize(final String src, final String dst, final String separators,
             final Locale locale) {
         assertEquals(dst, StringUtils.capitalizeEachWord(src, separators, locale));
diff --git a/tools/dicttool/compat/com/android/inputmethod/latin/settings/SettingsValues.java b/tools/dicttool/compat/com/android/inputmethod/latin/settings/SettingsValues.java
deleted file mode 100644
index 0a84cde..0000000
--- a/tools/dicttool/compat/com/android/inputmethod/latin/settings/SettingsValues.java
+++ /dev/null
@@ -1,23 +0,0 @@
-/*
- * Copyright (C) 2013 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.inputmethod.latin.settings;
-
-public class SettingsValues {
-    public boolean isWordCodePoint(final int code) {
-        return Character.isLetter(code);
-    }
-}